resolve merge conflicts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
14931c541f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Ignore the actual .bashrc_local with real passwords
|
||||||
|
dotfiles/.bashrc_local
|
||||||
@ -29,6 +29,7 @@ After running `setup_env.sh`, activate plugins:
|
|||||||
- **Tmux**: Press `prefix + I` (Ctrl-b + I) to install TPM plugins
|
- **Tmux**: Press `prefix + I` (Ctrl-b + I) to install TPM plugins
|
||||||
- **Vim**: Run `:PluginInstall` inside vim to install Vundle plugins
|
- **Vim**: Run `:PluginInstall` inside vim to install Vundle plugins
|
||||||
|
|
||||||
|
|
||||||
### Git Operations
|
### Git Operations
|
||||||
```bash
|
```bash
|
||||||
# Automated commit and push (defined in .bashrc)
|
# Automated commit and push (defined in .bashrc)
|
||||||
@ -48,6 +49,7 @@ The repository uses **symlink-based configuration deployment**. When `setup_env.
|
|||||||
|
|
||||||
All dotfiles are symlinked, not copied. Editing `~/.<file>` directly modifies files in the repository.
|
All dotfiles are symlinked, not copied. Editing `~/.<file>` directly modifies files in the repository.
|
||||||
|
|
||||||
|
|
||||||
**Critical Files:**
|
**Critical Files:**
|
||||||
- `dotfiles/.bashrc` - Main bash configuration with extensive aliases and functions
|
- `dotfiles/.bashrc` - Main bash configuration with extensive aliases and functions
|
||||||
- `dotfiles/.bashrc_local` - Machine-specific environment variables (PG, MS connection strings)
|
- `dotfiles/.bashrc_local` - Machine-specific environment variables (PG, MS connection strings)
|
||||||
@ -61,6 +63,8 @@ All dotfiles are symlinked, not copied. Editing `~/.<file>` directly modifies fi
|
|||||||
|
|
||||||
### Custom Bash Workflow
|
### Custom Bash Workflow
|
||||||
|
|
||||||
|
The `.bashrc` contains a sophisticated workflow for working with:
|
||||||
|
|
||||||
**Database Query Management:**
|
**Database Query Management:**
|
||||||
|
|
||||||
PostgreSQL workflow:
|
PostgreSQL workflow:
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
#export IPTOKEN=
|
|
||||||
#export PG="psql -U ptrowbridge -d ubm -p 5432 -h usmidsap01"
|
|
||||||
#export MS="sqlcmd.exe -S mid-sql02 -i"
|
|
||||||
#export JAVA_HOME=/opt/jdk-19.0.1
|
|
||||||
#export PATH=$PATH:$JAVA_HOME/bin
|
|
||||||
#export PATH=$PATH:/opt/gradle/gradle-7.6/bin
|
|
||||||
#export RUNNER_PATH=/opt/runner/
|
|
||||||
#export DB2PW=
|
|
||||||
#export PGPW=
|
|
||||||
44
dotfiles/.bashrc_local_example
Normal file
44
dotfiles/.bashrc_local_example
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# .bashrc_local - Machine-specific environment variables
|
||||||
|
# Copy this file to .bashrc_local and fill in your actual values
|
||||||
|
|
||||||
|
# Token for IP services (if needed)
|
||||||
|
#export IPTOKEN=
|
||||||
|
|
||||||
|
# PostgreSQL connection string
|
||||||
|
export PG="psql -U username -d database -p 5432 -h hostname"
|
||||||
|
|
||||||
|
# SQL Server connection strings
|
||||||
|
export MS="sqlcmd -U username -C -S servername"
|
||||||
|
export MSC="sqlcmd -U username -S servername -C -s \| -W"
|
||||||
|
|
||||||
|
# Java and Gradle paths
|
||||||
|
export JAVA_HOME=/opt/jdk-20.0.1
|
||||||
|
export PATH=$PATH:$JAVA_HOME/bin
|
||||||
|
export PATH=$PATH:/opt/gradle/gradle-8.1/bin
|
||||||
|
export PATH=$PATH:/opt/mssql-tools18/bin
|
||||||
|
|
||||||
|
# Runner configuration path
|
||||||
|
export RUNNER_PATH=/opt/jrunner_conf/
|
||||||
|
|
||||||
|
# Database passwords (fill in your actual passwords)
|
||||||
|
export DB2PW=your_db2_password_here
|
||||||
|
export PGPW=your_postgres_password_here
|
||||||
|
export SQLCMDPASSWORD='your_sqlcmd_password_here'
|
||||||
|
|
||||||
|
# Windows SQL Server connection (if needed)
|
||||||
|
# export MSW="sqlcmd.exe -S servername -C "
|
||||||
|
|
||||||
|
# Alternative Java/Gradle versions (commented out)
|
||||||
|
#export JAVA_HOME=/opt/jdk-19.0.1
|
||||||
|
#export PATH=$PATH:$JAVA_HOME/bin
|
||||||
|
#export PATH=$PATH:/opt/gradle/gradle-7.6/bin
|
||||||
|
#export RUNNER_PATH=/opt/runner/
|
||||||
|
|
||||||
|
# Deno installation
|
||||||
|
export DENO_INSTALL="$HOME/.deno"
|
||||||
|
export PATH="$DENO_INSTALL/bin:$PATH"
|
||||||
|
|
||||||
|
# NVM (Node Version Manager)
|
||||||
|
export NVM_DIR="$HOME/.nvm"
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||||
|
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
||||||
@ -1,9 +1,16 @@
|
|||||||
-- Switch pagers with :x and :xx commands
|
-- Switch pagers with :x and :xx commands
|
||||||
\set x '\\setenv PAGER ''less -S'''
|
\set x '\\setenv PAGER ''less -S'''
|
||||||
\set xx '\\setenv PAGER \'pspg -bX --no-mouse\''
|
\set xx '\\setenv PAGER \'pspg -bX --no-mouse\''
|
||||||
\timing on
|
-- \timing on
|
||||||
\set QUIET 1
|
\set QUIET 1
|
||||||
\pset linestyle unicode
|
\pset linestyle unicode
|
||||||
-- \pset border 2
|
-- \pset border 2
|
||||||
\pset null ∅
|
\pset null ∅
|
||||||
\unset QUIET
|
\unset QUIET
|
||||||
|
-- work with vd
|
||||||
|
-- \setenv PAGER 'vd -'
|
||||||
|
-- \pset format csv
|
||||||
|
-- \pset footer off
|
||||||
|
-- \setenv PSQL_PAGER_ALWAYS 1
|
||||||
|
-- \pset paget on
|
||||||
|
|
||||||
|
|||||||
94
dotfiles/README.md
Normal file
94
dotfiles/README.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# dotfiles
|
||||||
|
|
||||||
|
Symlink-deployed config. The parent `~/setup_env/setup_env.sh` installs each file here to its destination path — edit the file in this repo, the change goes live.
|
||||||
|
|
||||||
|
| Source in this repo | Deployed to | Deployed by |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `.bashrc`, `.vimrc`, `.gitconfig`, `.pspgconf`, `.psqlrc`, `.tmux.conf`, `.bashrc_local` | `~/<filename>` | `deploy_configs` |
|
||||||
|
| `bin/*` | `~/.local/bin/<name>` | `deploy_bin` |
|
||||||
|
| `nvim/*.lua` | `~/.config/nvim/lua/<name>.lua` | `deploy_nvim` |
|
||||||
|
|
||||||
|
On a fresh machine: clone the `setup_env` repo, then from `~/setup_env/` run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./install_neovim.sh
|
||||||
|
./install_nvchad.sh
|
||||||
|
./install_python3.sh
|
||||||
|
./setup_env.sh # creates all the symlinks + installs apt packages
|
||||||
|
```
|
||||||
|
|
||||||
|
After install you still need to add one line to the NvChad-provided `~/.config/nvim/lua/mappings.lua` (it's not tracked here because NvChad owns that file):
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require("td_mappings")
|
||||||
|
```
|
||||||
|
|
||||||
|
Everything else picks up automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## td — markdown todo time tracking
|
||||||
|
|
||||||
|
Track time on markdown todos identified by an Obsidian block-ref `^tid-*` at the end of the task line. Data lives in `time.csv` at the vault root; reports cross-join time with git history to show what was created vs. completed.
|
||||||
|
|
||||||
|
### The three surfaces (one script)
|
||||||
|
|
||||||
|
All logic is in `bin/td` — a Python 3 stdlib script. The other surfaces are thin wrappers so the script is reachable from each editing context.
|
||||||
|
|
||||||
|
| Surface | Location | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Python script | `bin/td` → `~/.local/bin/td` | Single source of truth. All subcommands. |
|
||||||
|
| Shell wrappers | `.bashrc` (`tstart` / `tstop` / `treport` / `tweek`) | Bypass the `td` alias (see gotcha). |
|
||||||
|
| Nvim module | `nvim/td.lua` + `nvim/td_mappings.lua` | `:TdStart` / `:TdStop` / `:TdReport` / `:TdWeek` commands + `<leader>t{s,p,r,w}` keymaps. |
|
||||||
|
|
||||||
|
### Data model
|
||||||
|
|
||||||
|
`time.csv` at the vault root:
|
||||||
|
|
||||||
|
```
|
||||||
|
started_at, stopped_at, tid, file, description
|
||||||
|
```
|
||||||
|
|
||||||
|
- `tid` — block-ref identifier, format `tid-YYYYMMDD-HHMMSS`, appended as `^tid-...` to the task line in the markdown file
|
||||||
|
- `started_at` / `stopped_at` — ISO-8601 local timestamps; a running entry has an empty `stopped_at`
|
||||||
|
- one row per time segment; `td stop` fills in `stopped_at` on the open row
|
||||||
|
|
||||||
|
Vault root is discovered by walking up from the current buffer file looking for `time.csv` or `.obsidian/`.
|
||||||
|
|
||||||
|
### Subcommands
|
||||||
|
|
||||||
|
| Command | What it does |
|
||||||
|
| --- | --- |
|
||||||
|
| `td start <tid> [--file PATH] [--desc TEXT]` | Start a timer. Auto-stops any running entry first. If `--file` / `--desc` aren't given, greps the vault for the tid to populate them. |
|
||||||
|
| `td stop` | Close the open entry. |
|
||||||
|
| `td report [FILTER]` | Total time per tid (filter matches tid or file substring). |
|
||||||
|
| `td current` | Print the currently-running tid (or nothing). |
|
||||||
|
| `td tidgen` | Print a fresh `tid-YYYYMMDD-HHMMSS`. |
|
||||||
|
| `td week [--since DATE]` | Task create/complete events from `git log -p`, joined with `time.csv`. Default range: since Monday 00:00 of this week. |
|
||||||
|
|
||||||
|
### How `td week` works
|
||||||
|
|
||||||
|
Scans `git log -p --reverse --no-renames` from cwd, pairs `-`/`+` task-line diffs within each commit by tid, classifies each event:
|
||||||
|
|
||||||
|
- `[x]` **done** — `- [ ]` → `- [x]` transition in one commit
|
||||||
|
- `[ ]` **new** — added `- [ ]` line with a fresh tid
|
||||||
|
- `[+]` **done+new** — `- [x]` added with no prior `- [ ]` for that tid (created and completed in the same commit)
|
||||||
|
- `[o]` **reopen** — `- [x]` → `- [ ]` transition
|
||||||
|
|
||||||
|
Event timestamp is commit time, not toggle time — batched commits share one timestamp. Fine for weekly "what did I get done" reports; not precise enough for hourly auditing.
|
||||||
|
|
||||||
|
### Nvim integration
|
||||||
|
|
||||||
|
`<leader>ts` / `:TdStart` on a `- [ ]` task line:
|
||||||
|
1. If the line has no `^tid-*` block ref, auto-generates `tid-YYYYMMDD-HHMMSS`, appends it, saves the buffer.
|
||||||
|
2. Starts the timer with the file path (relative to vault root) and the task text (minus checkbox + block ref) as description.
|
||||||
|
|
||||||
|
Other mappings: `<leader>tp` stop, `<leader>tr` report float, `<leader>tw` week float. `q` or `<Esc>` closes a float.
|
||||||
|
|
||||||
|
Known limitation: NvChad defers `mappings.lua` via `vim.schedule`, so `:TdStart` etc. aren't available inside `-c` arguments on headless invocations. Interactive use is fine.
|
||||||
|
|
||||||
|
### Gotchas
|
||||||
|
|
||||||
|
- **`td` alone is aliased to `rg`** (for the `td` / `tdp` / `tdo` todo-grep family, defined in `.bashrc`). So `td week` runs `rg "\- \[[^(x|~)]\]" week` and errors out. Use `tweek` (shell wrapper that calls `command td week`) or `command td week` directly.
|
||||||
|
- **CSV is cwd-scoped.** `$TD_LOG` defaults to `./time.csv`, so subcommands must be run from (or under) the vault root. The nvim wrapper handles this by walking up to find the vault root; the shell wrappers trust your cwd.
|
||||||
|
- **Data-model changes go in `bin/td`.** The shell and nvim surfaces should stay thin — if a new subcommand is useful in nvim too, add it to `bin/td`, then a one-line `tfoo()` wrapper in `.bashrc` and a `M.foo` + mapping in `nvim/td.lua` / `nvim/td_mappings.lua`.
|
||||||
228
dotfiles/bin/td
Executable file
228
dotfiles/bin/td
Executable file
@ -0,0 +1,228 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""td — time-track markdown todos identified by ^tid block-refs.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
td start <tid> [--file PATH] [--desc TEXT]
|
||||||
|
td stop
|
||||||
|
td report [FILTER] # FILTER matches tid or file substring
|
||||||
|
td current # print the currently-running tid (or nothing)
|
||||||
|
td tidgen # print a new unique tid
|
||||||
|
td week [--since DATE] # task create/complete events from git log, joined with time.csv
|
||||||
|
|
||||||
|
Log location: $TD_LOG (default ./time.csv)
|
||||||
|
"""
|
||||||
|
import argparse, csv, os, re, subprocess, sys
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
LOG = os.environ.get("TD_LOG", "./time.csv")
|
||||||
|
FIELDS = ["started_at", "stopped_at", "tid", "file", "description"]
|
||||||
|
|
||||||
|
def now():
|
||||||
|
return datetime.now().isoformat(timespec="seconds")
|
||||||
|
|
||||||
|
def read_rows():
|
||||||
|
if not os.path.exists(LOG):
|
||||||
|
return []
|
||||||
|
with open(LOG, newline="") as f:
|
||||||
|
return list(csv.DictReader(f))
|
||||||
|
|
||||||
|
def write_rows(rows):
|
||||||
|
with open(LOG, "w", newline="") as f:
|
||||||
|
w = csv.DictWriter(f, fieldnames=FIELDS)
|
||||||
|
w.writeheader()
|
||||||
|
w.writerows(rows)
|
||||||
|
|
||||||
|
def lookup_task(tid):
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output(
|
||||||
|
["rg", "--no-heading", "--with-filename", rf"\^{tid}\b", "."],
|
||||||
|
text=True, stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
return "", ""
|
||||||
|
first = out.splitlines()[0] if out else ""
|
||||||
|
if not first:
|
||||||
|
return "", ""
|
||||||
|
path, _, line = first.partition(":")
|
||||||
|
file = path.removeprefix("./")
|
||||||
|
desc = re.sub(r"^\s*-\s*\[.\]\s*", "", line)
|
||||||
|
desc = re.sub(r"\s*\^\S+\s*$", "", desc).strip()
|
||||||
|
return file, desc
|
||||||
|
|
||||||
|
def cmd_start(args):
|
||||||
|
rows = read_rows()
|
||||||
|
ts = now()
|
||||||
|
stopped = [r["tid"] for r in rows if not r["stopped_at"]]
|
||||||
|
for r in rows:
|
||||||
|
if not r["stopped_at"]:
|
||||||
|
r["stopped_at"] = ts
|
||||||
|
file, desc = args.file or "", args.desc or ""
|
||||||
|
if not (file or desc):
|
||||||
|
file, desc = lookup_task(args.tid)
|
||||||
|
if not file:
|
||||||
|
print(f"warning: ^{args.tid} not found in {os.getcwd()} — logging with empty file/desc", file=sys.stderr)
|
||||||
|
rows.append({"started_at": ts, "stopped_at": "", "tid": args.tid, "file": file, "description": desc})
|
||||||
|
write_rows(rows)
|
||||||
|
if stopped:
|
||||||
|
print(f" stopped {', '.join(stopped)} first")
|
||||||
|
snippet = f" ({file}: {desc[:50]})" if desc else ""
|
||||||
|
print(f"started {args.tid}{snippet}")
|
||||||
|
|
||||||
|
def cmd_stop(args):
|
||||||
|
rows = read_rows()
|
||||||
|
if not rows:
|
||||||
|
print(f"no log at {LOG}"); sys.exit(1)
|
||||||
|
ts = now()
|
||||||
|
stopped = []
|
||||||
|
for r in rows:
|
||||||
|
if not r["stopped_at"]:
|
||||||
|
r["stopped_at"] = ts
|
||||||
|
stopped.append(r["tid"])
|
||||||
|
if not stopped:
|
||||||
|
print("nothing running"); sys.exit(1)
|
||||||
|
write_rows(rows)
|
||||||
|
print(f"stopped {', '.join(stopped)}")
|
||||||
|
|
||||||
|
def cmd_report(args):
|
||||||
|
rows = read_rows()
|
||||||
|
if not rows:
|
||||||
|
print(f"no log at {LOG}"); sys.exit(1)
|
||||||
|
totals, files, running = defaultdict(int), {}, []
|
||||||
|
for r in rows:
|
||||||
|
if args.filter and args.filter not in r["tid"] and args.filter not in r["file"]:
|
||||||
|
continue
|
||||||
|
start = datetime.fromisoformat(r["started_at"])
|
||||||
|
files[r["tid"]] = r["file"]
|
||||||
|
if r["stopped_at"]:
|
||||||
|
stop = datetime.fromisoformat(r["stopped_at"])
|
||||||
|
totals[r["tid"]] += int((stop - start).total_seconds())
|
||||||
|
else:
|
||||||
|
running.append((r["tid"], start, r["file"], r["description"]))
|
||||||
|
for tid in sorted(totals):
|
||||||
|
t = totals[tid]
|
||||||
|
h, m = t // 3600, (t % 3600) // 60
|
||||||
|
print(f"{tid:<24} {h:>3}h{m:02d}m {files.get(tid,'')}")
|
||||||
|
for tid, start, file, desc in running:
|
||||||
|
print(f"{tid:<24} RUNNING since {start.strftime('%H:%M')} {file}: {desc[:40]}")
|
||||||
|
|
||||||
|
def cmd_current(args):
|
||||||
|
for r in read_rows():
|
||||||
|
if not r["stopped_at"]:
|
||||||
|
print(r["tid"]); return
|
||||||
|
|
||||||
|
def cmd_tidgen(args):
|
||||||
|
print(f"tid-{datetime.now().strftime('%Y%m%d-%H%M%S')}")
|
||||||
|
|
||||||
|
TID_RE = re.compile(r"\^(tid-[\w-]+)")
|
||||||
|
TASK_ADD_RE = re.compile(r"^\+(?!\+\+)\s*-\s*\[([ xX])\]\s*(.*)$")
|
||||||
|
TASK_REM_RE = re.compile(r"^-(?!--)\s*-\s*\[([ xX])\]\s*(.*)$")
|
||||||
|
|
||||||
|
def _tid_totals():
|
||||||
|
totals = defaultdict(int)
|
||||||
|
for r in read_rows():
|
||||||
|
if r["stopped_at"]:
|
||||||
|
start = datetime.fromisoformat(r["started_at"])
|
||||||
|
stop = datetime.fromisoformat(r["stopped_at"])
|
||||||
|
totals[r["tid"]] += int((stop - start).total_seconds())
|
||||||
|
return totals
|
||||||
|
|
||||||
|
def _scan_git(since):
|
||||||
|
SEP = "---TDWEEK-COMMIT---"
|
||||||
|
try:
|
||||||
|
out = subprocess.check_output(
|
||||||
|
["git", "log", f"--since={since}", "--reverse",
|
||||||
|
f"--format={SEP}%n%cI", "-p", "--no-color", "--no-renames"],
|
||||||
|
text=True, stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
return
|
||||||
|
for chunk in out.split(SEP + "\n"):
|
||||||
|
if not chunk.strip():
|
||||||
|
continue
|
||||||
|
lines = chunk.split("\n")
|
||||||
|
ts = lines[0]
|
||||||
|
current_file = None
|
||||||
|
adds, removes = [], []
|
||||||
|
for ln in lines[1:]:
|
||||||
|
if ln.startswith("+++ b/"):
|
||||||
|
current_file = ln[6:].split("\t")[0].rstrip(); continue
|
||||||
|
if ln.startswith("--- ") or ln.startswith("diff --git") or ln.startswith("index ") or ln.startswith("@@"):
|
||||||
|
continue
|
||||||
|
if not current_file or current_file == "/dev/null":
|
||||||
|
continue
|
||||||
|
m = TASK_ADD_RE.match(ln)
|
||||||
|
if m:
|
||||||
|
status = m.group(1).lower().strip() or " "
|
||||||
|
tm = TID_RE.search(m.group(2))
|
||||||
|
if tm:
|
||||||
|
desc = TID_RE.sub("", m.group(2)).strip()
|
||||||
|
adds.append((current_file, status, tm.group(1), desc))
|
||||||
|
continue
|
||||||
|
m = TASK_REM_RE.match(ln)
|
||||||
|
if m:
|
||||||
|
status = m.group(1).lower().strip() or " "
|
||||||
|
tm = TID_RE.search(m.group(2))
|
||||||
|
if tm:
|
||||||
|
removes.append((current_file, status, tm.group(1)))
|
||||||
|
rem_by_tid = {t[2]: t for t in removes}
|
||||||
|
for file, status, tid, desc in adds:
|
||||||
|
prior = rem_by_tid.get(tid)
|
||||||
|
if prior:
|
||||||
|
if prior[1] == " " and status == "x":
|
||||||
|
yield (ts, "done", tid, file, desc)
|
||||||
|
elif prior[1] == "x" and status == " ":
|
||||||
|
yield (ts, "reopen", tid, file, desc)
|
||||||
|
else:
|
||||||
|
yield (ts, "done+new" if status == "x" else "new", tid, file, desc)
|
||||||
|
|
||||||
|
def _default_since():
|
||||||
|
today = datetime.now()
|
||||||
|
monday = today - timedelta(days=today.weekday())
|
||||||
|
return monday.strftime("%Y-%m-%d 00:00")
|
||||||
|
|
||||||
|
def _fmt_dur(secs):
|
||||||
|
return f"{secs // 3600}h{(secs % 3600) // 60:02d}m"
|
||||||
|
|
||||||
|
def cmd_week(args):
|
||||||
|
since = args.since or _default_since()
|
||||||
|
events = list(_scan_git(since))
|
||||||
|
totals = _tid_totals()
|
||||||
|
if not events:
|
||||||
|
print(f"no task events since {since}"); return
|
||||||
|
by_day = defaultdict(list)
|
||||||
|
for ts, kind, tid, file, desc in events:
|
||||||
|
day = ts[:10]
|
||||||
|
by_day[day].append((ts, kind, tid, file, desc))
|
||||||
|
marker = {"done": "[x]", "new": "[ ]", "done+new": "[+]", "reopen": "[o]"}
|
||||||
|
n_done = n_new = total_secs = 0
|
||||||
|
counted_tids = set()
|
||||||
|
print(f"Week since {since}\n")
|
||||||
|
for day in sorted(by_day):
|
||||||
|
dt = datetime.fromisoformat(day)
|
||||||
|
print(f"{dt.strftime('%a %Y-%m-%d')}")
|
||||||
|
for ts, kind, tid, file, desc in by_day[day]:
|
||||||
|
t = totals.get(tid, 0)
|
||||||
|
dur = _fmt_dur(t) if t else " "
|
||||||
|
print(f" {marker.get(kind,'? ')} {tid:<24} {dur:>6} {file} — {desc[:60]}")
|
||||||
|
if kind in ("done", "done+new"): n_done += 1
|
||||||
|
if kind in ("new", "done+new"): n_new += 1
|
||||||
|
if tid not in counted_tids:
|
||||||
|
counted_tids.add(tid); total_secs += t
|
||||||
|
print()
|
||||||
|
print(f"Totals: completed {n_done} created {n_new} time {_fmt_dur(total_secs)}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser(prog="td", description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
sp = p.add_subparsers(dest="cmd", required=True)
|
||||||
|
s = sp.add_parser("start"); s.add_argument("tid"); s.add_argument("--file", default=""); s.add_argument("--desc", default=""); s.set_defaults(func=cmd_start)
|
||||||
|
sp.add_parser("stop").set_defaults(func=cmd_stop)
|
||||||
|
s = sp.add_parser("report"); s.add_argument("filter", nargs="?"); s.set_defaults(func=cmd_report)
|
||||||
|
sp.add_parser("current").set_defaults(func=cmd_current)
|
||||||
|
sp.add_parser("tidgen").set_defaults(func=cmd_tidgen)
|
||||||
|
s = sp.add_parser("week"); s.add_argument("--since", default=None); s.set_defaults(func=cmd_week)
|
||||||
|
args = p.parse_args()
|
||||||
|
args.func(args)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
119
dotfiles/nvim/td.lua
Normal file
119
dotfiles/nvim/td.lua
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function vault_root()
|
||||||
|
local file = vim.api.nvim_buf_get_name(0)
|
||||||
|
local dir = file ~= "" and vim.fn.fnamemodify(file, ":p:h") or vim.fn.getcwd()
|
||||||
|
local probe = dir
|
||||||
|
while probe ~= "/" and probe ~= "" do
|
||||||
|
if vim.loop.fs_stat(probe .. "/time.csv") or vim.loop.fs_stat(probe .. "/.obsidian") then
|
||||||
|
return probe
|
||||||
|
end
|
||||||
|
local parent = vim.fn.fnamemodify(probe, ":h")
|
||||||
|
if parent == probe then break end
|
||||||
|
probe = parent
|
||||||
|
end
|
||||||
|
return vim.fn.getcwd()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function relpath(abs, base)
|
||||||
|
local a = vim.fn.fnamemodify(abs, ":p")
|
||||||
|
local b = vim.fn.fnamemodify(base, ":p"):gsub("/$", "")
|
||||||
|
if a:sub(1, #b + 1) == b .. "/" then return a:sub(#b + 2) end
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run(cmd, cwd, on_done)
|
||||||
|
local stdout, stderr = {}, {}
|
||||||
|
vim.fn.jobstart(cmd, {
|
||||||
|
cwd = cwd,
|
||||||
|
stdout_buffered = true,
|
||||||
|
stderr_buffered = true,
|
||||||
|
on_stdout = function(_, d) for _, l in ipairs(d) do if l ~= "" then stdout[#stdout + 1] = l end end end,
|
||||||
|
on_stderr = function(_, d) for _, l in ipairs(d) do if l ~= "" then stderr[#stderr + 1] = l end end end,
|
||||||
|
on_exit = function(_, code)
|
||||||
|
vim.schedule(function() if on_done then on_done(stdout, stderr, code) end end)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function notify(lines, level)
|
||||||
|
if #lines == 0 then return end
|
||||||
|
vim.notify(table.concat(lines, "\n"), level or vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function extract_desc(line)
|
||||||
|
return (line:gsub("^%s*%-%s*%[.%]%s*", ""):gsub("%s*%^%S+%s*$", ""):gsub("%s+$", ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensure_tid_on_line()
|
||||||
|
local line = vim.api.nvim_get_current_line()
|
||||||
|
local tid = line:match("%^(tid%-%S+)")
|
||||||
|
if tid then return tid end
|
||||||
|
if not line:match("^%s*%-%s*%[.%]") then
|
||||||
|
vim.notify("td: not on a todo line (- [ ] ...)", vim.log.levels.WARN)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
tid = os.date("tid-%Y%m%d-%H%M%S")
|
||||||
|
local new = line:gsub("%s+$", "") .. " ^" .. tid
|
||||||
|
vim.api.nvim_set_current_line(new)
|
||||||
|
vim.cmd("silent! write")
|
||||||
|
return tid
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.start()
|
||||||
|
local tid = ensure_tid_on_line()
|
||||||
|
if not tid then return end
|
||||||
|
local cwd = vault_root()
|
||||||
|
local file = relpath(vim.api.nvim_buf_get_name(0), cwd)
|
||||||
|
local desc = extract_desc(vim.api.nvim_get_current_line())
|
||||||
|
run({ "td", "start", tid, "--file", file, "--desc", desc }, cwd, function(out, err)
|
||||||
|
notify(out); notify(err, vim.log.levels.WARN)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.stop()
|
||||||
|
run({ "td", "stop" }, vault_root(), function(out, err)
|
||||||
|
notify(out); notify(err, vim.log.levels.WARN)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function float(title, lines)
|
||||||
|
local buf = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||||
|
vim.api.nvim_buf_set_option(buf, "modifiable", false)
|
||||||
|
local width = math.min(120, vim.o.columns - 4)
|
||||||
|
local height = math.min(30, #lines + 2)
|
||||||
|
vim.api.nvim_open_win(buf, true, {
|
||||||
|
relative = "editor",
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
row = math.floor((vim.o.lines - height) / 2),
|
||||||
|
col = math.floor((vim.o.columns - width) / 2),
|
||||||
|
style = "minimal",
|
||||||
|
border = "rounded",
|
||||||
|
title = " " .. title .. " ",
|
||||||
|
title_pos = "center",
|
||||||
|
})
|
||||||
|
vim.api.nvim_buf_set_keymap(buf, "n", "q", "<cmd>close<cr>", { noremap = true, silent = true })
|
||||||
|
vim.api.nvim_buf_set_keymap(buf, "n", "<Esc>", "<cmd>close<cr>", { noremap = true, silent = true })
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.report()
|
||||||
|
run({ "td", "report" }, vault_root(), function(out, err)
|
||||||
|
if #out == 0 then notify(err, vim.log.levels.WARN); return end
|
||||||
|
float("td report", out)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.week(opts)
|
||||||
|
local cmd = { "td", "week" }
|
||||||
|
if opts and opts.args and opts.args ~= "" then
|
||||||
|
for w in opts.args:gmatch("%S+") do cmd[#cmd + 1] = w end
|
||||||
|
end
|
||||||
|
run(cmd, vault_root(), function(out, err)
|
||||||
|
if #out == 0 then notify(err, vim.log.levels.WARN); return end
|
||||||
|
float("td week", out)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
11
dotfiles/nvim/td_mappings.lua
Normal file
11
dotfiles/nvim/td_mappings.lua
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-- td: time-track markdown todos (logic lives in ~/.local/bin/td and td.lua)
|
||||||
|
-- Loaded from ~/.config/nvim/lua/mappings.lua via require("td_mappings").
|
||||||
|
local td = require("td")
|
||||||
|
vim.api.nvim_create_user_command("TdStart", td.start, {})
|
||||||
|
vim.api.nvim_create_user_command("TdStop", td.stop, {})
|
||||||
|
vim.api.nvim_create_user_command("TdReport", td.report, {})
|
||||||
|
vim.api.nvim_create_user_command("TdWeek", td.week, { nargs = "*" })
|
||||||
|
vim.keymap.set("n", "<leader>ts", td.start, { desc = "td: start timer on current task" })
|
||||||
|
vim.keymap.set("n", "<leader>tp", td.stop, { desc = "td: stop (pause) timer" })
|
||||||
|
vim.keymap.set("n", "<leader>tr", td.report, { desc = "td: report floating window" })
|
||||||
|
vim.keymap.set("n", "<leader>tw", td.week, { desc = "td: week (tasks from git log)" })
|
||||||
@ -1,3 +1,81 @@
|
|||||||
sudo apt install zip
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
curl -s "https://get.sdkman.io" | bash
|
echo "============================================"
|
||||||
|
echo "SDKMAN (Java Development) Installation Script"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will run the following commands with sudo:"
|
||||||
|
echo " - apt-get update"
|
||||||
|
echo " - apt-get install -y zip unzip curl"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue with installation? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Starting installation (commands will be shown as they run)..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Enable command tracing
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Install prerequisites
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y zip unzip curl
|
||||||
|
|
||||||
|
set +x
|
||||||
|
|
||||||
|
# Check if SDKMAN is already installed
|
||||||
|
if [ -d "$HOME/.sdkman" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "SDKMAN is already installed at ~/.sdkman"
|
||||||
|
echo "To update SDKMAN, run: sdk selfupdate"
|
||||||
|
echo "============================================"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install SDKMAN
|
||||||
|
echo "Installing SDKMAN..."
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if ! curl -s "https://get.sdkman.io" | bash; then
|
||||||
|
set +x
|
||||||
|
echo "Error: SDKMAN installation failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
set +x
|
||||||
|
|
||||||
|
# Source SDKMAN to make it available in current session
|
||||||
|
export SDKMAN_DIR="$HOME/.sdkman"
|
||||||
|
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
# Verify installation
|
||||||
|
if [ -d "$HOME/.sdkman" ] && [ -f "$HOME/.sdkman/bin/sdkman-init.sh" ]; then
|
||||||
|
echo "SDKMAN installed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "To start using SDKMAN, either:"
|
||||||
|
echo " 1. Restart your shell, or"
|
||||||
|
echo " 2. Run: source ~/.bashrc"
|
||||||
|
echo ""
|
||||||
|
echo "Then you can install Java with:"
|
||||||
|
echo " sdk list java # List available Java versions"
|
||||||
|
echo " sdk install java # Install latest Java"
|
||||||
|
echo " sdk install java 21.0.1-tem # Install specific version"
|
||||||
|
echo ""
|
||||||
|
echo "Other useful SDKMAN commands:"
|
||||||
|
echo " sdk install gradle # Install Gradle"
|
||||||
|
echo " sdk install maven # Install Maven"
|
||||||
|
echo " sdk list # List all available SDKs"
|
||||||
|
else
|
||||||
|
echo "Error: SDKMAN installation verification failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "============================================"
|
||||||
|
|||||||
@ -1,5 +1,79 @@
|
|||||||
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz
|
#!/bin/bash
|
||||||
sudo rm -rf /opt/nvim
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo "Neovim Latest Installation Script"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will run the following commands with sudo:"
|
||||||
|
echo " - apt-get update (if curl not installed)"
|
||||||
|
echo " - apt-get install -y curl (if needed)"
|
||||||
|
echo " - rm -rf /opt/nvim-linux64 (if old installation exists)"
|
||||||
|
echo " - tar -C /opt -xzf nvim-linux64.tar.gz"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue with installation? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Starting installation (commands will be shown as they run)..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Enable command tracing
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Check for required tools
|
||||||
|
if ! command -v curl &> /dev/null; then
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disable tracing temporarily for cleaner download messages
|
||||||
|
set +x
|
||||||
|
|
||||||
|
# Download latest Neovim
|
||||||
|
echo "Downloading Neovim..."
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
cd "$TEMP_DIR"
|
||||||
|
|
||||||
|
if ! curl -fsSL -o nvim-linux64.tar.gz https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz; then
|
||||||
|
echo "Error: Failed to download Neovim" >&2
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Download complete."
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Remove old installation if it exists
|
||||||
|
if [ -d /opt/nvim-linux64 ]; then
|
||||||
|
sudo rm -rf /opt/nvim-linux64
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract to /opt
|
||||||
sudo tar -C /opt -xzf nvim-linux64.tar.gz
|
sudo tar -C /opt -xzf nvim-linux64.tar.gz
|
||||||
|
|
||||||
export PATH="$PATH:/opt/nvim-linux64/bin"
|
set +x
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
cd - > /dev/null
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
# Verify installation
|
||||||
|
if [ -x /opt/nvim-linux64/bin/nvim ]; then
|
||||||
|
echo "Neovim installed successfully!"
|
||||||
|
/opt/nvim-linux64/bin/nvim --version | head -n1
|
||||||
|
echo ""
|
||||||
|
echo "Neovim is installed at: /opt/nvim-linux64/bin/nvim"
|
||||||
|
echo "Add to PATH by adding this to your ~/.bashrc:"
|
||||||
|
echo ' export PATH="$PATH:/opt/nvim-linux64/bin"'
|
||||||
|
else
|
||||||
|
echo "Error: Neovim installation failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "============================================"
|
||||||
|
|||||||
113
install_nvchad.sh
Executable file
113
install_nvchad.sh
Executable file
@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo "NvChad Configuration Installation Script"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will:"
|
||||||
|
echo " - Backup existing ~/.config/nvim to ~/.config/nvim.backup (if exists)"
|
||||||
|
echo " - Clone your NvChad config from git@gitea.hptrow.me:pt/nvchad.git (customize branch)"
|
||||||
|
echo " - Launch nvim to auto-install lazy.nvim and all plugins"
|
||||||
|
echo ""
|
||||||
|
echo "Prerequisites:"
|
||||||
|
echo " - Neovim 0.9.5+ must be installed (run ./install_neovim.sh if needed)"
|
||||||
|
echo " - Git must be installed"
|
||||||
|
echo " - SSH key must be set up for gitea.hptrow.me"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue with installation? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Starting installation..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
if ! command -v nvim &> /dev/null; then
|
||||||
|
echo "Error: Neovim is not installed or not in PATH" >&2
|
||||||
|
echo "Please run ./install_neovim.sh first" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v git &> /dev/null; then
|
||||||
|
echo "Error: Git is not installed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify Neovim version
|
||||||
|
NVIM_VERSION=$(nvim --version | head -n1 | grep -oP 'v\K[0-9]+\.[0-9]+' || echo "0.0")
|
||||||
|
REQUIRED_VERSION="0.9"
|
||||||
|
if [ "$(printf '%s\n' "$REQUIRED_VERSION" "$NVIM_VERSION" | sort -V | head -n1)" != "$REQUIRED_VERSION" ]; then
|
||||||
|
echo "Error: Neovim version $NVIM_VERSION is too old (need 0.9.5+)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Neovim version: $(nvim --version | head -n1)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Backup existing config if it exists
|
||||||
|
if [ -d ~/.config/nvim ]; then
|
||||||
|
echo "Backing up existing ~/.config/nvim to ~/.config/nvim.backup"
|
||||||
|
if [ -d ~/.config/nvim.backup ]; then
|
||||||
|
rm -rf ~/.config/nvim.backup
|
||||||
|
fi
|
||||||
|
mv ~/.config/nvim ~/.config/nvim.backup
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clone the config
|
||||||
|
echo "Cloning NvChad config from gitea (customize branch)..."
|
||||||
|
set -x
|
||||||
|
git clone -b customize git@gitea.hptrow.me:pt/nvchad.git ~/.config/nvim
|
||||||
|
set +x
|
||||||
|
|
||||||
|
if [ ! -d ~/.config/nvim ]; then
|
||||||
|
echo "Error: Failed to clone config" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Config cloned successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "First Launch Setup"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Neovim will now launch and automatically:"
|
||||||
|
echo " 1. Bootstrap lazy.nvim plugin manager"
|
||||||
|
echo " 2. Install NvChad (as a plugin)"
|
||||||
|
echo " 3. Install all configured plugins"
|
||||||
|
echo ""
|
||||||
|
echo "This may take a few minutes on first run."
|
||||||
|
echo "After installation completes, close nvim with :q"
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter to launch nvim and complete setup..." -r
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Launch nvim to trigger plugin installation
|
||||||
|
nvim +q
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "Installation Complete!"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Your NvChad configuration is installed at: ~/.config/nvim"
|
||||||
|
echo ""
|
||||||
|
echo "Key customizations in this config:"
|
||||||
|
echo " - Theme: vscode_dark"
|
||||||
|
echo " - Tab width: 4 spaces"
|
||||||
|
echo " - Obsidian.nvim integration"
|
||||||
|
echo " - SQL development tools (pg_format, sqlfluff)"
|
||||||
|
echo " - Mason for installing formatters"
|
||||||
|
echo " - Custom keybindings: g+w (format), <leader>gb (blame), more in lua/mappings.lua"
|
||||||
|
echo ""
|
||||||
|
echo "Next time you launch nvim, everything will be ready!"
|
||||||
|
echo ""
|
||||||
|
if [ -d ~/.config/nvim.backup ]; then
|
||||||
|
echo "Note: Your old config was backed up to ~/.config/nvim.backup"
|
||||||
|
fi
|
||||||
|
echo "============================================"
|
||||||
@ -1,12 +1,61 @@
|
|||||||
# Create the file repository configuration:
|
#!/bin/bash
|
||||||
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
set -euo pipefail
|
||||||
|
|
||||||
# Import the repository signing key:
|
echo "============================================"
|
||||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
echo "PostgreSQL Installation Script"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will run the following commands with sudo:"
|
||||||
|
echo " - mkdir -p /etc/apt/keyrings"
|
||||||
|
echo " - curl ... | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg"
|
||||||
|
echo " - tee /etc/apt/sources.list.d/pgdg.list"
|
||||||
|
echo " - apt-get update"
|
||||||
|
echo " - apt-get install -y postgresql"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue with installation? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Update the package lists:
|
echo ""
|
||||||
|
echo "Starting installation (commands will be shown as they run)..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Enable command tracing
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Create directory for keyrings if it doesn't exist
|
||||||
|
sudo mkdir -p /etc/apt/keyrings
|
||||||
|
|
||||||
|
# Download and install the PostgreSQL GPG key
|
||||||
|
if [ ! -f /etc/apt/keyrings/postgresql.gpg ]; then
|
||||||
|
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
|
||||||
|
sudo gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add PostgreSQL repository
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/pgdg.list > /dev/null
|
||||||
|
|
||||||
|
# Update package lists
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
|
||||||
# Install the latest version of PostgreSQL.
|
# Install latest PostgreSQL
|
||||||
# If you want a specific version, use 'postgresql-12' or similar instead of 'postgresql':
|
sudo apt-get install -y postgresql
|
||||||
sudo apt-get -y install postgresql
|
|
||||||
|
# Disable command tracing for cleaner output
|
||||||
|
set +x
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
# Verify installation
|
||||||
|
if command -v psql &> /dev/null; then
|
||||||
|
echo "PostgreSQL installed successfully!"
|
||||||
|
psql --version
|
||||||
|
else
|
||||||
|
echo "Error: PostgreSQL installation failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "============================================"
|
||||||
|
|||||||
@ -1,24 +1,83 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# Update the package list
|
echo "============================================"
|
||||||
|
echo "Python 3 Latest Installation Script"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will run the following commands with sudo:"
|
||||||
|
echo " - apt-get update"
|
||||||
|
echo " - apt-get install -y software-properties-common"
|
||||||
|
echo " - add-apt-repository -y ppa:deadsnakes/ppa"
|
||||||
|
echo " - apt-get update"
|
||||||
|
echo " - apt-get install -y python3.X python3.X-venv python3.X-dev python3.X-distutils"
|
||||||
|
echo " - <latest-python> (to install pip via get-pip.py)"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue with installation? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Starting installation (commands will be shown as they run)..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Enable command tracing
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Install prerequisites
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y software-properties-common
|
||||||
|
|
||||||
|
# Add deadsnakes PPA
|
||||||
|
sudo add-apt-repository -y ppa:deadsnakes/ppa
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
|
||||||
# Install the software-properties-common package
|
# Disable tracing temporarily to find latest version cleanly
|
||||||
sudo apt-get install software-properties-common
|
set +x
|
||||||
|
|
||||||
# Add the deadsnakes PPA to the sources list
|
# Find the latest Python 3 version available
|
||||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
echo "Finding latest Python 3 version..."
|
||||||
|
LATEST_PYTHON=$(apt-cache search --names-only '^python3\.[0-9]+$' | \
|
||||||
|
grep -oP 'python3\.\d+' | \
|
||||||
|
sort -V | \
|
||||||
|
tail -1)
|
||||||
|
|
||||||
# Update the package list again
|
if [ -z "$LATEST_PYTHON" ]; then
|
||||||
sudo apt-get update
|
echo "Error: Could not determine latest Python 3 version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Check the latest version of Python 3 available
|
echo "Installing $LATEST_PYTHON..."
|
||||||
latest_version=$(apt-cache madison python3 | awk '{print $3}' | grep "^3\." | sort -V | tail -1)
|
set -x
|
||||||
|
|
||||||
# Install the latest version of Python 3
|
sudo apt-get install -y \
|
||||||
sudo apt-get install -y python3=$latest_version
|
"$LATEST_PYTHON" \
|
||||||
|
"$LATEST_PYTHON-venv" \
|
||||||
|
"$LATEST_PYTHON-dev" \
|
||||||
|
"$LATEST_PYTHON-distutils"
|
||||||
|
|
||||||
# Verify the installation
|
# Install pip for the new Python version
|
||||||
python3 --version
|
curl -sS https://bootstrap.pypa.io/get-pip.py | sudo "$LATEST_PYTHON"
|
||||||
which python3
|
|
||||||
|
|
||||||
|
# Disable command tracing for cleaner output
|
||||||
|
set +x
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
# Verify installation
|
||||||
|
if command -v "$LATEST_PYTHON" &> /dev/null; then
|
||||||
|
echo "$LATEST_PYTHON installed successfully!"
|
||||||
|
"$LATEST_PYTHON" --version
|
||||||
|
"$LATEST_PYTHON" -m pip --version
|
||||||
|
else
|
||||||
|
echo "Error: $LATEST_PYTHON installation failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "Note: Use '$LATEST_PYTHON' to run this version"
|
||||||
|
echo "To make it the default python3, run:"
|
||||||
|
echo " sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/$LATEST_PYTHON 1"
|
||||||
|
echo "============================================"
|
||||||
|
|||||||
@ -1,3 +1,68 @@
|
|||||||
pip3 install visidata
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# https://www.visidata.org/install/
|
echo "============================================"
|
||||||
|
echo "VisiData Installation Script"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will run the following commands with sudo:"
|
||||||
|
echo " - apt-get update (if pipx not installed)"
|
||||||
|
echo " - apt-get install -y pipx (if needed)"
|
||||||
|
echo ""
|
||||||
|
read -p "Continue with installation? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Starting installation (commands will be shown as they run)..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Enable command tracing
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Check if python3 is available
|
||||||
|
if ! command -v python3 &> /dev/null; then
|
||||||
|
set +x
|
||||||
|
echo "Error: python3 is not installed. Please install Python 3 first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install pipx if not available (recommended way to install VisiData)
|
||||||
|
if ! command -v pipx &> /dev/null; then
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y pipx
|
||||||
|
set +x
|
||||||
|
pipx ensurepath
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
set +x
|
||||||
|
|
||||||
|
# Install VisiData using pipx
|
||||||
|
echo "Installing VisiData via pipx..."
|
||||||
|
set -x
|
||||||
|
pipx install visidata
|
||||||
|
|
||||||
|
# Disable command tracing for cleaner output
|
||||||
|
set +x
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
# Verify installation
|
||||||
|
if command -v vd &> /dev/null; then
|
||||||
|
echo "VisiData installed successfully!"
|
||||||
|
vd --version
|
||||||
|
echo ""
|
||||||
|
echo "Note: If 'vd' command is not found, you may need to:"
|
||||||
|
echo " 1. Restart your shell, or"
|
||||||
|
echo " 2. Run: source ~/.bashrc"
|
||||||
|
else
|
||||||
|
echo "Warning: VisiData installed but 'vd' command not in PATH" >&2
|
||||||
|
echo "You may need to restart your shell or run: source ~/.bashrc" >&2
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "Documentation: https://www.visidata.org/"
|
||||||
|
echo "============================================"
|
||||||
|
|||||||
31
setup_env.sh
31
setup_env.sh
@ -65,6 +65,35 @@ deploy_configs() {
|
|||||||
source ~/.bashrc
|
source ~/.bashrc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Deploy executable scripts from dotfiles/bin/ into ~/.local/bin/
|
||||||
|
deploy_bin() {
|
||||||
|
echo "Deploying scripts to ~/.local/bin/ ..."
|
||||||
|
local src_dir="$(pwd)/dotfiles/bin"
|
||||||
|
[[ -d "$src_dir" ]] || return 0
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
for script in "$src_dir"/*; do
|
||||||
|
[[ -f "$script" ]] || continue
|
||||||
|
create_symlink "$script" ~/.local/bin/"$(basename "$script")"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deploy nvim lua modules from dotfiles/nvim/ into ~/.config/nvim/lua/
|
||||||
|
# NOTE: requires ~/.config/nvim/ to already exist (see install_neovim.sh + install_nvchad.sh).
|
||||||
|
# Modules are required by name from ~/.config/nvim/lua/mappings.lua — e.g. require("td_mappings").
|
||||||
|
deploy_nvim() {
|
||||||
|
echo "Deploying nvim lua modules to ~/.config/nvim/lua/ ..."
|
||||||
|
local src_dir="$(pwd)/dotfiles/nvim"
|
||||||
|
[[ -d "$src_dir" ]] || return 0
|
||||||
|
if [[ ! -d ~/.config/nvim/lua ]]; then
|
||||||
|
echo " ~/.config/nvim/lua missing — run install_neovim.sh + install_nvchad.sh first. Skipping."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
for mod in "$src_dir"/*.lua; do
|
||||||
|
[[ -f "$mod" ]] || continue
|
||||||
|
create_symlink "$mod" ~/.config/nvim/lua/"$(basename "$mod")"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Main script
|
# Main script
|
||||||
main() {
|
main() {
|
||||||
install_packages
|
install_packages
|
||||||
@ -72,6 +101,8 @@ main() {
|
|||||||
install_vundle
|
install_vundle
|
||||||
install_git_bash_prompt
|
install_git_bash_prompt
|
||||||
deploy_configs
|
deploy_configs
|
||||||
|
deploy_bin
|
||||||
|
deploy_nvim
|
||||||
echo "Setup complete! Please restart your shell or run 'source ~/.bashrc' for changes to take effect."
|
echo "Setup complete! Please restart your shell or run 'source ~/.bashrc' for changes to take effect."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user