mirror of
https://github.com/chenasraf/dotfiles.git
synced 2026-05-17 17:28:07 +00:00
feat: install wand + deprecate js utils
This commit is contained in:
@@ -248,6 +248,23 @@ install:
|
||||
strategy: tar
|
||||
download_filename: tx-linux-{{ .Arch }}.tar.gz
|
||||
|
||||
- name: wand
|
||||
type: group
|
||||
steps:
|
||||
- name: wand
|
||||
type: brew
|
||||
opts:
|
||||
tap: chenasraf/tap
|
||||
- name: wand
|
||||
type: github-release
|
||||
platforms:
|
||||
only: ['linux']
|
||||
opts:
|
||||
repository: chenasraf/wand
|
||||
destination: ~/.local/bin
|
||||
strategy: tar
|
||||
download_filename: wand-linux-{{ .Arch }}.tar.gz
|
||||
|
||||
- name: cospend-cli
|
||||
type: group
|
||||
steps:
|
||||
@@ -457,14 +474,6 @@ install:
|
||||
- name: bat
|
||||
type: brew
|
||||
|
||||
- name: home
|
||||
type: shell
|
||||
opts:
|
||||
bin_name: tx
|
||||
command: |
|
||||
cd $HOME/.config/dotfiles/utils
|
||||
pnpm i && pnpm build && pnpm ginst
|
||||
|
||||
- name: pandoc
|
||||
type: group
|
||||
steps:
|
||||
|
||||
@@ -99,11 +99,6 @@ unaconfig:
|
||||
windows:
|
||||
- ./
|
||||
|
||||
wand:
|
||||
root: ~/Dev/wand
|
||||
windows:
|
||||
- ./
|
||||
|
||||
# ovalix:
|
||||
# root: ~/Dev/ovalix_webapp
|
||||
# windows:
|
||||
@@ -160,6 +155,11 @@ flexy:
|
||||
windows:
|
||||
- ./
|
||||
|
||||
wand:
|
||||
root: ~/Dev/go/src/github.com/chenasraf/wand
|
||||
windows:
|
||||
- ./
|
||||
|
||||
sofmani:
|
||||
root: ~/Dev/go/src/github.com/chenasraf/sofmani
|
||||
aliases: [sm]
|
||||
|
||||
101
.config/wand.yml
Normal file
101
.config/wand.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
brew:
|
||||
description: Manage Brewfile
|
||||
children:
|
||||
backup:
|
||||
description: Backup brew state to Brewfile
|
||||
flags:
|
||||
no-push:
|
||||
description: Skip pushing changes to git
|
||||
type: bool
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
if [ "$(uname)" != "Darwin" ]; then
|
||||
echo "Not on macOS, skipping backup."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DEVICE_UID=$(cat "$DOTFILES/.device_uid")
|
||||
SYNC_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
BREW_DIR="$DOTFILES/brew/$DEVICE_UID"
|
||||
|
||||
if [ "$WAND_FLAG_NO_PUSH" != "true" ]; then
|
||||
if ! git -C "$DOTFILES" diff --quiet; then
|
||||
echo "There are other changes waiting to be pushed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$BREW_DIR"
|
||||
cd "$BREW_DIR"
|
||||
brew bundle dump --formula --cask --tap --describe --force
|
||||
|
||||
if [ "$WAND_FLAG_NO_PUSH" != "true" ]; then
|
||||
git -C "$DOTFILES" add "$BREW_DIR/Brewfile"
|
||||
git -C "$DOTFILES" commit -m "backup(brew): Update Brewfile for $DEVICE_UID ($SYNC_DATE)"
|
||||
git -C "$DOTFILES" push
|
||||
fi
|
||||
|
||||
restore:
|
||||
description: Restore brew state from Brewfile
|
||||
flags:
|
||||
arch:
|
||||
alias: a
|
||||
description: Architecture to use
|
||||
default: arm64
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
DEVICE_UID=$(cat "$DOTFILES/.device_uid")
|
||||
BREW_DIR="$DOTFILES/brew/$DEVICE_UID"
|
||||
echo "Restoring Brewfile for $DEVICE_UID ($WAND_FLAG_ARCH)"
|
||||
cd "$BREW_DIR"
|
||||
if [ "$WAND_FLAG_ARCH" = "arm64" ]; then
|
||||
arch -arm64 brew bundle
|
||||
else
|
||||
brew bundle
|
||||
fi
|
||||
|
||||
mush:
|
||||
description: Backup/restore Mushclient profile
|
||||
children:
|
||||
backup:
|
||||
description: Backup Mushclient profile
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
LOCAL_BASE="$HOME/Library/Application Support/CrossOver/Bottles/MushClient/drive_c/users/crossover"
|
||||
LOCAL_DIR="$LOCAL_BASE/MUSHclient"
|
||||
SYNCED_DIR="$HOME/Nextcloud/synced"
|
||||
SYNCED_BACKUP="$SYNCED_DIR/MUSHclient"
|
||||
|
||||
echo "Dumping Aardwolf.db to Aardwolf.dump.sql"
|
||||
sqlite3 "$SYNCED_BACKUP/Aardwolf.db" .dump > "$LOCAL_DIR/Aardwolf.dump.sql"
|
||||
echo "Copying to sync dir..."
|
||||
rsync -vtr "$LOCAL_DIR" "$SYNCED_DIR"
|
||||
echo "Backed up $LOCAL_DIR to $SYNCED_DIR"
|
||||
|
||||
restore:
|
||||
description: Restore Mushclient profile
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
LOCAL_BASE="$HOME/Library/Application Support/CrossOver/Bottles/MushClient/drive_c/users/crossover"
|
||||
SYNCED_DIR="$HOME/Nextcloud/synced"
|
||||
SYNCED_BACKUP="$SYNCED_DIR/MUSHclient"
|
||||
|
||||
echo "Copying $SYNCED_BACKUP to $LOCAL_BASE"
|
||||
rsync -vtr --exclude .git "$SYNCED_BACKUP" "$LOCAL_BASE/"
|
||||
echo "Restored $SYNCED_BACKUP to $LOCAL_BASE"
|
||||
|
||||
map-restore:
|
||||
description: Restore Mushclient map database
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
LOCAL_BASE="$HOME/Library/Application Support/CrossOver/Bottles/MushClient/drive_c/users/crossover"
|
||||
SRC="Aardwolf.db.Backup_Manual"
|
||||
BK="Aardwolf.db.$(date -u +"%Y-%m-%dT%H-%M-%SZ").bk"
|
||||
DEST="Aardwolf.db"
|
||||
|
||||
cd "$LOCAL_BASE"
|
||||
echo "Renaming $DEST to $BK"
|
||||
mv "$DEST" "$BK"
|
||||
echo "Copying db_backups/$SRC to $DEST"
|
||||
cp "db_backups/$SRC" "$DEST"
|
||||
echo "Done."
|
||||
@@ -11,7 +11,6 @@ _local\.zsh
|
||||
autoload_completions\.zsh
|
||||
completions
|
||||
_plugins
|
||||
utils
|
||||
brew
|
||||
node_modules
|
||||
pnpm-lock\.yaml
|
||||
@@ -20,5 +19,3 @@ README\.md
|
||||
\.prettierignore
|
||||
\.eslintignore
|
||||
\.shellcheckrc
|
||||
\.config/tmux_m1\.yml
|
||||
\.config/tmux_planck\.yml
|
||||
|
||||
@@ -140,7 +140,6 @@ alias ip="iplocal; ip4; ip6"
|
||||
# package management
|
||||
alias pkgupdate="brew update; brew upgrade; brew cleanup; pnpm i -g pnpm; pnpm up -g --latest; sudo \$SHELL -c \"gem update; gem cleanup\""
|
||||
alias pi="platform_install"
|
||||
alias install-utils="pushd \$DOTFILES/utils; pnpm install && pnpm build && pnpm ginst; popd"
|
||||
alias install-wezterm="brew tap homebrew/cask-versions;brew install --cask wezterm@nightly --force"
|
||||
alias update-wezterm="brew upgrade --cask wezterm-nightly --no-quarantine --greedy-latest"
|
||||
|
||||
@@ -206,7 +205,6 @@ fi
|
||||
alias lssh="lazyssh"
|
||||
alias lvim="nvim -c':e#<1'"
|
||||
alias cfg-reload="cp ~/.dotfiles/.config/sofmani.yml ~/.config/"
|
||||
alias utils-rebuild="pushd \\$DOTFILES/utils; pnpm ginst; popd"
|
||||
alias sm="sofmani"
|
||||
alias gop="git open"
|
||||
alias exp="cospend -p home-2026"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[*]
|
||||
tab_width = 2
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
3
utils/.gitignore
vendored
3
utils/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
build/
|
||||
@@ -1 +0,0 @@
|
||||
gen/
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.md",
|
||||
"options": {
|
||||
"printWidth": 100,
|
||||
"proseWrap": "always"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import eslint from '@eslint/js'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default [
|
||||
...tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended),
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
},
|
||||
ignores: ['node_modules/', 'build/', 'dist/', 'gen/'],
|
||||
},
|
||||
]
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "utils",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"hutil": "home/home.js",
|
||||
"tblf": "tblf.js"
|
||||
},
|
||||
"scripts": {
|
||||
"h": "ts-node src/home/home.ts",
|
||||
"tblf": "ts-node src/tblf.ts",
|
||||
"build": "tsc && cp package.json build/",
|
||||
"ginst": "pnpm build && pnpm i -g utils@file:$(pwd)/build",
|
||||
"dev": "tsc -w"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/node": "^25.0.10",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
"massarg": "2.1.1",
|
||||
"prettier": "^3.8.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"typescript-eslint": "^8.53.1"
|
||||
}
|
||||
}
|
||||
1183
utils/pnpm-lock.yaml
generated
1183
utils/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
||||
import { MassargCommand } from 'massarg/command'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
// Custom error class for user-friendly errors
|
||||
export class UserError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message)
|
||||
this.name = 'UserError'
|
||||
}
|
||||
}
|
||||
|
||||
export type Opts = {
|
||||
key: string
|
||||
verbose: boolean
|
||||
dry: boolean
|
||||
}
|
||||
|
||||
export function withDefaultOpts<T extends Opts, OT extends object = object>(
|
||||
command: MassargCommand<OT>,
|
||||
): MassargCommand<OT & T> {
|
||||
return (command as MassargCommand<OT & T>)
|
||||
.flag({
|
||||
name: 'verbose',
|
||||
description: 'Verbose output',
|
||||
aliases: ['v'],
|
||||
})
|
||||
.flag({
|
||||
name: 'dry',
|
||||
description: 'Dry run',
|
||||
aliases: ['d'],
|
||||
})
|
||||
.help({
|
||||
bindOption: true,
|
||||
bindCommand: true,
|
||||
})
|
||||
}
|
||||
|
||||
export function log({ verbose, dry }: Opts, ...content: unknown[]) {
|
||||
if (!verbose && !dry) return
|
||||
console.log(...content)
|
||||
}
|
||||
|
||||
export async function runCommand(
|
||||
opts: Opts,
|
||||
command: string | (string | false | number | null | undefined)[],
|
||||
): Promise<number> {
|
||||
if (Array.isArray(command)) {
|
||||
command = command.filter(Boolean).join('; ')
|
||||
}
|
||||
const [cmd, ...args] = command.split(' ')
|
||||
log(opts, '$ ' + command)
|
||||
if (opts.dry) return 0
|
||||
const proc = spawn(cmd, args, { stdio: 'inherit', shell: '/bin/zsh' })
|
||||
return new Promise((resolve, reject) => {
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0 || code == null) {
|
||||
resolve(code ?? 0)
|
||||
} else {
|
||||
reject(new Error(`Command: \`${cmd} ${args.join(' ')}\` exited with code: ${code}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function getCommandOutput(
|
||||
opts: Opts,
|
||||
command: string | string[],
|
||||
): Promise<{ code: number; output: string }> {
|
||||
if (Array.isArray(command)) {
|
||||
command = command.join('; ')
|
||||
}
|
||||
const [cmd, ...args] = command.split(' ')
|
||||
log(opts, '$ ' + command)
|
||||
if (opts.dry) return { code: 0, output: '' }
|
||||
const proc = spawn(cmd, args, { stdio: 'pipe', shell: '/bin/zsh' })
|
||||
return new Promise<{ code: number; output: string }>((resolve, reject) => {
|
||||
let output = ''
|
||||
proc.stdout.on('data', (data) => {
|
||||
output += data.toString()
|
||||
})
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve({ code, output })
|
||||
} else {
|
||||
reject(code)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`
|
||||
@@ -1,95 +0,0 @@
|
||||
import * as os from 'node:os'
|
||||
import { MassargCommand } from 'massarg/command'
|
||||
import { DF_DIR, HomeOpts, checkGitChanges, getDeviceUID } from './common'
|
||||
import { massarg } from 'massarg'
|
||||
import { UserError, runCommand } from '../common'
|
||||
|
||||
export type BrewOpts = HomeOpts & { push: boolean; arch?: string }
|
||||
|
||||
async function backup(opts: BrewOpts) {
|
||||
const isMacOS = os.platform() === 'darwin'
|
||||
if (!isMacOS) {
|
||||
console.log('Not on MacOS, skipping backup.')
|
||||
return
|
||||
}
|
||||
const syncDate = new Date().toISOString()
|
||||
const DEVICE_UID = await getDeviceUID()
|
||||
const gitChanges = await checkGitChanges(opts)
|
||||
if (gitChanges) {
|
||||
throw new UserError('There are other changes waiting to be pushed')
|
||||
}
|
||||
await runCommand(opts, [
|
||||
`mkdir -p "${DF_DIR}/brew/${DEVICE_UID}"`,
|
||||
`pushd "${DF_DIR}/brew/${DEVICE_UID}"`,
|
||||
`brew bundle dump --formula --cask --tap --describe --force`,
|
||||
...(opts.push
|
||||
? [
|
||||
`git add Brewfile`,
|
||||
`git commit -m "backup(brew): Update Brewfile for ${DEVICE_UID} (${syncDate})"`,
|
||||
`git push`,
|
||||
]
|
||||
: []),
|
||||
`popd`,
|
||||
])
|
||||
}
|
||||
|
||||
async function restore(opts: BrewOpts) {
|
||||
const DEVICE_UID = await getDeviceUID()
|
||||
console.log(`Restoring Brewfile for ${DEVICE_UID} (${opts.arch})`)
|
||||
await runCommand(opts, [
|
||||
`pushd "${DF_DIR}/brew/${DEVICE_UID}"`,
|
||||
`${opts.arch === 'arm64' ? 'arch -arm64' : ''} brew bundle`,
|
||||
`popd`,
|
||||
])
|
||||
}
|
||||
|
||||
// NOTE backup
|
||||
const backupCommand = new MassargCommand<BrewOpts>({
|
||||
name: 'backup',
|
||||
aliases: ['b', 'p'],
|
||||
description: 'Backup brew state to Brewfile',
|
||||
run: backup,
|
||||
})
|
||||
.help({
|
||||
bindOption: true,
|
||||
bindCommand: true,
|
||||
})
|
||||
.flag({
|
||||
name: 'push',
|
||||
aliases: ['p'],
|
||||
description: 'Push changes to git',
|
||||
negatable: true,
|
||||
defaultValue: true,
|
||||
})
|
||||
|
||||
// NOTE restore
|
||||
const restoreCommand = new MassargCommand<BrewOpts>({
|
||||
name: 'restore',
|
||||
aliases: ['r', 'l'],
|
||||
description: 'Restore brew state from Brewfile',
|
||||
run: restore,
|
||||
})
|
||||
.help({
|
||||
bindOption: true,
|
||||
bindCommand: true,
|
||||
})
|
||||
.option({
|
||||
name: 'arch',
|
||||
aliases: ['a'],
|
||||
defaultValue: 'arm64',
|
||||
description: 'Architecture to use',
|
||||
})
|
||||
|
||||
// NOTE main
|
||||
export const brewCommand = massarg<BrewOpts>({
|
||||
name: 'brew',
|
||||
aliases: ['b'],
|
||||
description: 'Manage Brewfile',
|
||||
})
|
||||
.main(backup)
|
||||
.command(backupCommand)
|
||||
.command(restoreCommand)
|
||||
.help({
|
||||
bindCommand: true,
|
||||
bindOption: true,
|
||||
})
|
||||
@@ -1,32 +0,0 @@
|
||||
import os from 'node:os'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { Opts, runCommand } from '../common'
|
||||
|
||||
export type HomeOpts = Opts
|
||||
|
||||
export const DF_DIR = path.join(os.homedir(), '/.dotfiles')
|
||||
|
||||
export const DEVICE_UID_FILE = path.join(DF_DIR, '.device_uid')
|
||||
|
||||
let DEVICE_UID: string
|
||||
|
||||
export async function getDeviceUID() {
|
||||
if (!DEVICE_UID) {
|
||||
try {
|
||||
DEVICE_UID = (await fs.readFile(DEVICE_UID_FILE)).toString().trim()
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Problem getting specific device UID. Make sure you create the file ${DEVICE_UID_FILE} and write your device UID inside.`,
|
||||
)
|
||||
console.error('Original error:')
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
return DEVICE_UID
|
||||
}
|
||||
|
||||
export async function checkGitChanges(opts: HomeOpts): Promise<boolean> {
|
||||
const code = await runCommand(opts, `git -C ${DF_DIR} diff --quiet`).catch((code) => code)
|
||||
return code !== 0
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { CommandConfig, MassargCommand } from 'massarg/command'
|
||||
import { DF_DIR, HomeOpts, checkGitChanges } from './common'
|
||||
import { runCommand } from '../common'
|
||||
|
||||
type PushOpts = HomeOpts & {
|
||||
message?: string
|
||||
}
|
||||
|
||||
type GitOpts = HomeOpts & {
|
||||
args: string[]
|
||||
}
|
||||
|
||||
function createGitCommand<O extends HomeOpts>(
|
||||
name: string,
|
||||
commands?: (opts: O) => string[],
|
||||
config?: Partial<CommandConfig<O>>,
|
||||
): MassargCommand<O> {
|
||||
return new MassargCommand<O>({
|
||||
name,
|
||||
description: `Run git ${name}`,
|
||||
aliases: [name[0].toLowerCase()],
|
||||
run: async (opts) => {
|
||||
if (commands) {
|
||||
for (const command of commands(opts)) {
|
||||
await runCommand(opts, `git -C ${DF_DIR} ${command}`)
|
||||
}
|
||||
} else {
|
||||
runCommand(opts, `git -C ${DF_DIR} ${name}`)
|
||||
}
|
||||
},
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
// NOTE git
|
||||
export const gitCommand = createGitCommand<GitOpts>(
|
||||
'git',
|
||||
(opts) => [opts.args.map((e) => (e.includes(' ') ? JSON.stringify(e) : e)).join(' ')],
|
||||
{ description: 'Run git command' },
|
||||
).option({
|
||||
name: 'args',
|
||||
description: 'Arguments to pass to git',
|
||||
aliases: ['a'],
|
||||
array: true,
|
||||
isDefault: true,
|
||||
required: true,
|
||||
})
|
||||
|
||||
// NOTE push
|
||||
export const pushCommand = new MassargCommand<PushOpts>({
|
||||
name: 'push',
|
||||
run: async (opts) => {
|
||||
const gitHasChanges = await checkGitChanges(opts)
|
||||
if (gitHasChanges) {
|
||||
await runCommand(opts, `git -C ${DF_DIR} add .`)
|
||||
await runCommand(
|
||||
opts,
|
||||
`git -C ${DF_DIR} commit ${opts.message ? `-m "${opts.message}"` : ''}`,
|
||||
)
|
||||
}
|
||||
await runCommand(opts, `git -C ${DF_DIR} push`)
|
||||
},
|
||||
description: 'Push all (incl. uncommitted) changes to remote',
|
||||
}).option({
|
||||
name: 'message',
|
||||
description: 'Commit message',
|
||||
aliases: ['m'],
|
||||
})
|
||||
|
||||
// NOTE pull
|
||||
export const pullCommand = createGitCommand('pull', undefined, { aliases: ['l'] })
|
||||
|
||||
// NOTE status
|
||||
export const statusCommand = createGitCommand('status', undefined, {
|
||||
description: 'Show git status',
|
||||
})
|
||||
@@ -1,15 +0,0 @@
|
||||
import { massarg } from 'massarg'
|
||||
import { withDefaultOpts } from '../common'
|
||||
import { HomeOpts } from './common'
|
||||
import { mushCommand } from './mush_cmd'
|
||||
import { brewCommand } from './brew_cmd'
|
||||
|
||||
withDefaultOpts(
|
||||
massarg<HomeOpts>({
|
||||
name: 'hutil',
|
||||
description: 'Dotfiles utilities',
|
||||
}),
|
||||
)
|
||||
.command(mushCommand)
|
||||
.command(brewCommand)
|
||||
.parse()
|
||||
@@ -1,80 +0,0 @@
|
||||
import * as os from 'node:os'
|
||||
import { MassargCommand } from 'massarg/command'
|
||||
import { HomeOpts } from './common'
|
||||
import { massarg } from 'massarg'
|
||||
import { runCommand, yellow } from '../common'
|
||||
|
||||
const home = os.homedir()
|
||||
const localBaseDir = `${home}/Library/Application Support/CrossOver/Bottles/MushClient/drive_c/users/crossover`
|
||||
const localDir = `${localBaseDir}/MUSHclient`
|
||||
const syncedDir = `${home}/Nextcloud/synced`
|
||||
const syncedBackupDir = `${syncedDir}/MUSHclient`
|
||||
|
||||
const backup = async (opts: HomeOpts) => {
|
||||
await runCommand(opts, [
|
||||
`echo "${yellow(`Dumping "Aardwolf.db" to "Aardwolf.dump.sql"`)}"`,
|
||||
`sqlite3 "${syncedBackupDir}/Aardwolf.db" .dump > "${localDir}/Aardwolf.dump.sql"`,
|
||||
`echo "${yellow(`Copying to sync dir...`)}"`,
|
||||
`rsync -vtr "${localDir}" "${syncedDir}"`,
|
||||
`echo "${yellow(`Backed up "${localDir}" to "${syncedDir}"`)}"`,
|
||||
])
|
||||
}
|
||||
|
||||
// NOTE backup
|
||||
const backupCommand = new MassargCommand<HomeOpts>({
|
||||
name: 'backup',
|
||||
aliases: ['b', 'p'],
|
||||
description: 'Backup Mushclient profile',
|
||||
run: backup,
|
||||
})
|
||||
|
||||
// NOTE restore
|
||||
const restoreCommand = new MassargCommand<HomeOpts>({
|
||||
name: 'restore',
|
||||
aliases: ['r', 'l'],
|
||||
description: 'Restore Mushclient profile',
|
||||
run: async (opts: HomeOpts) => {
|
||||
await runCommand(opts, [
|
||||
`echo "${yellow(`Copying "${syncedBackupDir}" to "${localBaseDir}"`)}"`,
|
||||
`rsync -vtr --exclude .git "${syncedBackupDir}" "${localBaseDir}/"`,
|
||||
`echo "${yellow(`Restored "${syncedBackupDir}" to "${localBaseDir}"`)}"`,
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
// NOTE map-restore
|
||||
const mapRestoreCommand = new MassargCommand<HomeOpts>({
|
||||
name: 'map-restore',
|
||||
aliases: ['mr', 'm'],
|
||||
description: 'Restore Mushclient map database',
|
||||
run: async (opts: HomeOpts) => {
|
||||
const src = 'Aardwolf.db.Backup_Manual'
|
||||
const bk = `Aardwolf.db.${new Date().toISOString().replace(/:/g, '-')}.bk`
|
||||
const dest = 'Aardwolf.db'
|
||||
|
||||
await runCommand(opts, [
|
||||
`pushd "${localBaseDir}"`,
|
||||
`echo "${yellow(`Renaming ${dest} to ${bk}`)}"`,
|
||||
`mv "${dest}" "${bk}"`,
|
||||
`echo "${yellow(`Copying ${localBaseDir}/db_backups/${src} to ${localBaseDir}/${dest}`)}"`,
|
||||
`cp "db_backups/${src}" "${localBaseDir}/${dest}"`,
|
||||
`echo "${yellow('Done.')}"`,
|
||||
'popd',
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
// NOTE main
|
||||
export const mushCommand = massarg<HomeOpts>({
|
||||
name: 'mush',
|
||||
aliases: ['m'],
|
||||
description: 'Backup/restore Mushclient profile',
|
||||
})
|
||||
.main(backup)
|
||||
.command(backupCommand)
|
||||
.command(restoreCommand)
|
||||
.command(mapRestoreCommand)
|
||||
.help({
|
||||
bindCommand: true,
|
||||
bindOption: true,
|
||||
})
|
||||
@@ -1,97 +0,0 @@
|
||||
import { massarg } from 'massarg'
|
||||
import { Opts as _Opts, UserError, log } from './common'
|
||||
|
||||
type Opts = _Opts & {
|
||||
input?: string
|
||||
header?: string
|
||||
separator?: string
|
||||
outputSeparator?: string
|
||||
}
|
||||
|
||||
async function main(opts: Opts) {
|
||||
let input = opts.input || ''
|
||||
if (!opts.input) {
|
||||
// read from stdin
|
||||
log(opts, 'Reading from stdin...')
|
||||
input = await new Promise((resolve) => {
|
||||
let data = ''
|
||||
process.stdin.on('data', (chunk) => {
|
||||
data += chunk
|
||||
})
|
||||
process.stdin.on('end', () => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
input = input.replaceAll('\\n', '\n')
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
throw new UserError('No input provided')
|
||||
}
|
||||
|
||||
log(opts, 'Input:', input + '\n')
|
||||
|
||||
const lines = input.split('\n')
|
||||
if (opts.header) {
|
||||
lines.unshift(opts.header)
|
||||
}
|
||||
const separator = opts.separator ? new RegExp(opts.separator) : /[\s\t]+/
|
||||
const outputSeparator = opts.outputSeparator || ' '
|
||||
const rows = lines.map((line) => line.split(separator))
|
||||
const columnMaxes = new Array(rows[0].length).fill(0)
|
||||
|
||||
for (const row of rows) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
columnMaxes[i] = Math.max(columnMaxes[i], row[i].length)
|
||||
}
|
||||
}
|
||||
|
||||
let output = ''
|
||||
for (const row of rows) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
const column = row[i]
|
||||
output += column.padEnd(columnMaxes[i]) + outputSeparator
|
||||
}
|
||||
output += '\n'
|
||||
}
|
||||
|
||||
console.log(output.trimEnd())
|
||||
}
|
||||
|
||||
massarg<Opts>({
|
||||
name: 'tblf',
|
||||
description: 'Generate a table from a file or stdin.',
|
||||
})
|
||||
.main(main)
|
||||
.option({
|
||||
name: 'input',
|
||||
description: 'The input file to read from.',
|
||||
aliases: ['i'],
|
||||
isDefault: true,
|
||||
})
|
||||
.option({
|
||||
name: 'header',
|
||||
description: 'The header to prepend to the input, which will be aligned.',
|
||||
aliases: ['th'],
|
||||
})
|
||||
.option({
|
||||
name: 'separator',
|
||||
description: 'The separator to use when reading from stdin.',
|
||||
aliases: ['s'],
|
||||
})
|
||||
.option({
|
||||
name: 'output-separator',
|
||||
description: 'The separator to use when writing to stdout.',
|
||||
aliases: ['o'],
|
||||
})
|
||||
.flag({
|
||||
name: 'verbose',
|
||||
aliases: ['v'],
|
||||
description: 'Print verbose output.',
|
||||
})
|
||||
.help({
|
||||
bindOption: true,
|
||||
bindCommand: true,
|
||||
})
|
||||
.parse()
|
||||
@@ -1,96 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
/* Language and Environment */
|
||||
"target": "ESNext", // /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
/* Modules */
|
||||
"module": "NodeNext", // /* Specify what module code is generated. */
|
||||
"rootDir": "./src", // /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "NodeNext", // /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./build", // /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, // /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, // /* Ensure that casing is correct in imports. */
|
||||
/* Type Checking */
|
||||
"strict": true, // /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true // /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user