From 7f8b055f1e507b1a6559d889ec6de757aacd414a Mon Sep 17 00:00:00 2001 From: aguil Date: Sat, 8 Aug 2026 19:00:08 -0600 Subject: [PATCH 1/4] ci: move pre-commit workflow to GitHub Actions The dotfiles source is moving back to github.com/aguil/dotfiles. Codeberg hosted runners forced a codeberg-medium label with a 10-minute cap and a full-URL pin for jdx/mise-action, since code.forgejo.org does not mirror it. Both workarounds are unnecessary on GitHub-hosted runners. Codeberg auth (fj, SSH key, 1Password refs) stays in place for other repos. --- {.forgejo => .github}/workflows/pre-commit.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) rename {.forgejo => .github}/workflows/pre-commit.yaml (75%) diff --git a/.forgejo/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml similarity index 75% rename from .forgejo/workflows/pre-commit.yaml rename to .github/workflows/pre-commit.yaml index a832260..037d8d0 100644 --- a/.forgejo/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -7,9 +7,7 @@ on: jobs: pre-commit: - # Codeberg hosted runners do not serve ubuntu-latest; medium has the - # 10-minute runtime cap needed for an uncached mise toolchain install. - runs-on: codeberg-medium + runs-on: ubuntu-latest env: # CI/CD hardening — see dot_agents/skills/mise-security/SKILL.md MISE_EXPERIMENTAL: "0" @@ -21,9 +19,7 @@ jobs: steps: - uses: actions/checkout@v7 - # Full URL: code.forgejo.org (the runner's default action source) - # does not mirror jdx/mise-action. - - uses: https://github.com/jdx/mise-action@v4 + - uses: jdx/mise-action@v4 with: install: true cache: true From 2900eb3201473d41197f0511b49b43bcd04603ee Mon Sep 17 00:00:00 2001 From: aguil Date: Sat, 8 Aug 2026 19:01:01 -0600 Subject: [PATCH 2/4] refactor(chezmoi): stop routing chezmoi source to fj The chezmoi source origin moves to github.com/aguil/dotfiles, so gh owns it. The gh wrapper already guards its chezmoi-source branch on the remote matching github.com; the fj wrapper claimed the source unconditionally. Drop that branch. It was already redundant with the unconditional fallback below it, so fj still reaches Codeberg from anywhere. Codeberg auth, the ~/dev/repos// route, and fj-routes.d overlays are untouched. --- dot_config/chezmoi/profile.d/04-fj-wrapper.sh.tmpl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dot_config/chezmoi/profile.d/04-fj-wrapper.sh.tmpl b/dot_config/chezmoi/profile.d/04-fj-wrapper.sh.tmpl index e9002d7..3e3e039 100644 --- a/dot_config/chezmoi/profile.d/04-fj-wrapper.sh.tmpl +++ b/dot_config/chezmoi/profile.d/04-fj-wrapper.sh.tmpl @@ -59,7 +59,6 @@ _chez_fj_route_data_home () { } fj () { - local chezmoi_source local route_data_home local fj_codeberg_host='{{ if hasKey . "codeberg" }}{{ .codeberg.host }}{{ else }}codeberg.org{{ end }}' local fj_codeberg_repo_root="$HOME/dev/repos/$fj_codeberg_host/{{ if hasKey . "codeberg" }}{{ .codeberg.user }}{{ else }}{{ .git.personal.githubUser }}{{ end }}" @@ -77,16 +76,6 @@ fj () { return $? ;; esac - if command -v chezmoi >/dev/null 2>&1; then - if chezmoi_source="$(chezmoi source-path 2>/dev/null)"; then - case "$PWD/" in - "$chezmoi_source"/*|"$chezmoi_source"/) - _chez_fj_run "$fj_data_home" "$@" - return $? - ;; - esac - fi - fi _chez_fj_run "$fj_data_home" "$@" return $? fi From 86c6bd5dc8cfbcb843804071676c06807fbc94eb Mon Sep 17 00:00:00 2001 From: aguil Date: Sat, 8 Aug 2026 19:01:51 -0600 Subject: [PATCH 3/4] docs: point chezmoi source tooling at GitHub The dotfiles source now lives on github.com/aguil/dotfiles, so gh (not fj) owns it. Update the forgejo/github CLI skills, CI references, and the Codeberg sections of the README and repo-maintenance docs. Codeberg remains a supported forge for other repos; only the claim that the chezmoi source is Codeberg-hosted is removed. --- README.md | 18 +++++++++++------- docs/repo-maintenance.md | 6 +++--- .../skills/dotfiles-forgejo-cli/SKILL.md | 13 +++++++------ dot_agents/skills/dotfiles-github-cli/SKILL.md | 11 +++++------ dot_agents/skills/mise-security/SKILL.md | 10 +++++----- install.sh | 2 -- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index bf93f15..438b91e 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,8 @@ pre-commit hook setup** (`just repos::pre-commit-install`), and the recommended `just repos::pre-commit-verify` check before pushes or PRs. Shell QA commands are available via `just -f qa.just ...` after `mise install`. -Forgejo Actions runs pre-commit on PRs automatically (see -`.forgejo/workflows/pre-commit.yaml`); that does **not** install hooks on your +GitHub Actions runs pre-commit on PRs automatically (see +`.github/workflows/pre-commit.yaml`); that does **not** install hooks on your computer until you run `just repos::pre-commit-install` once in the chezmoi source directory. @@ -340,9 +340,12 @@ scripts) use the same authenticated config as your shell: - **personal** profile: `~/.config/gh-personal` - **work** profile: `~/.config/gh` -The shell `gh` wrapper (`02-gh-wrapper.sh`) applies `gh-routes.d` and routes -`~/dev/repos/github.com//` (and chezmoi source while `origin` is still on -GitHub). +The shell `gh` wrapper (`02-gh-wrapper.sh`) applies `gh-routes.d`, then routes +automatically when your current directory is either: + +- under `~/dev/repos/github.com//`, or +- inside your chezmoi source path (`chezmoi source-path`), whose `origin` is on + GitHub. ### Codeberg (`fj`) @@ -351,8 +354,9 @@ Install [forgejo-cli](https://codeberg.org/forgejo-contrib/forgejo-cli) `03-fj-default-config.sh` exports **`CHEZMOI_FJ_DATA_HOME`** (personal default `~/.local/share/fj-personal`). The `fj()` wrapper (`04-fj-wrapper.sh`) sets -**`XDG_DATA_HOME`** there under `~/dev/repos///` and in -chezmoi source when `origin` is on Codeberg. Agents should use the same prefix: +**`XDG_DATA_HOME`** there under `~/dev/repos///`. The +chezmoi source is **not** routed to `fj` — its `origin` is on GitHub, so `gh` +owns it. Agents should use the same prefix: ```bash XDG_DATA_HOME="${CHEZMOI_FJ_DATA_HOME:-$HOME/.local/share/fj-personal}" fj pr list diff --git a/docs/repo-maintenance.md b/docs/repo-maintenance.md index a99f807..7a452c2 100644 --- a/docs/repo-maintenance.md +++ b/docs/repo-maintenance.md @@ -109,8 +109,8 @@ Boundary conventions: ## Pre-commit (markdown) -Forgejo Actions runs **[pre-commit](https://pre-commit.com)** on pull requests -and pushes to `master`/`main` via `.forgejo/workflows/pre-commit.yaml` (mise, +GitHub Actions runs **[pre-commit](https://pre-commit.com)** on pull requests +and pushes to `master`/`main` via `.github/workflows/pre-commit.yaml` (mise, pre-commit cache, and Neovim headless tests). That job uses `mise exec --locked` (same as local QA); it does **not** configure your machine. @@ -144,7 +144,7 @@ just repos::pre-commit-verify # pre-commit run --all-files ``` Use this even when the local hook already passed. It gives you the same -repository-wide check that Forgejo Actions runs on the PR. +repository-wide check that GitHub Actions runs on the PR. Re-run `just repos::pre-commit-install` after cloning on a new machine or if you replace `.git/hooks`. diff --git a/dot_agents/skills/dotfiles-forgejo-cli/SKILL.md b/dot_agents/skills/dotfiles-forgejo-cli/SKILL.md index e192889..4040c67 100644 --- a/dot_agents/skills/dotfiles-forgejo-cli/SKILL.md +++ b/dot_agents/skills/dotfiles-forgejo-cli/SKILL.md @@ -3,14 +3,14 @@ name: dotfiles-forgejo-cli description: >- Use fj (forgejo-cli) for Codeberg and other Forgejo hosts in this chezmoi setup. Use when creating or reviewing pull requests, issues, releases, or CI - on Codeberg, including the chezmoi source tree after migration from GitHub. - Prefer fj over gh for those hosts. + on Codeberg. Prefer fj over gh for those hosts. --- # Forgejo CLI (fj) for Codeberg -When the task involves **Codeberg** (or another Forgejo host) or the **chezmoi -source** on a personal machine, use **`fj`**, not **`gh`**. +When the task involves **Codeberg** (or another Forgejo host), use **`fj`**, not +**`gh`**. The chezmoi source is **not** one of those hosts — its `origin` is on +GitHub, so use **`gh`** there (see **dotfiles-github-cli**). ## Use `fj` for @@ -43,8 +43,9 @@ XDG_DATA_HOME="${CHEZMOI_FJ_DATA_HOME:-$HOME/.local/share/fj-personal}" \ ## GitHub -For **`github.com`** remotes and `~/dev/repos/github.com//`, use **`gh`** -and the **dotfiles-github-cli** skill instead. +For **`github.com`** remotes, `~/dev/repos/github.com//`, and the +**chezmoi source** (`~/.local/share/chezmoi`, origin `aguil/dotfiles`), use +**`gh`** and the **dotfiles-github-cli** skill instead. ## Overlay routes diff --git a/dot_agents/skills/dotfiles-github-cli/SKILL.md b/dot_agents/skills/dotfiles-github-cli/SKILL.md index 5b04171..6549b62 100644 --- a/dot_agents/skills/dotfiles-github-cli/SKILL.md +++ b/dot_agents/skills/dotfiles-github-cli/SKILL.md @@ -63,15 +63,14 @@ GitHub. For **Codeberg / Forgejo**, use **`fj`** (see **dotfiles-forgejo-cli** skill): repo-scoped `fj()` sets `XDG_DATA_HOME` to `CHEZMOI_FJ_DATA_HOME` under -`~/dev/repos///` and in the chezmoi source when `origin` is -on Codeberg. **`gh` does not support Forgejo hosts.** +`~/dev/repos///`. **`gh` does not support Forgejo hosts.** Typical split (adjust paths and hostnames to your layout): -| Role | Chezmoi source (example) | Use the `gh` login that owns this remote | -| ---------------------------- | ---------------------------------- | ----------------------------------------------- | -| Primary dotfiles | `~/.local/share/chezmoi` | `fj` if origin is Codeberg; else `gh` on GitHub | -| Secondary overlay (optional) | e.g. `~/.local/share/chezmoi-work` | Employer or second user on GitHub | +| Role | Chezmoi source (example) | Use the `gh` login that owns this remote | +| ---------------------------- | ---------------------------------- | ---------------------------------------- | +| Primary dotfiles | `~/.local/share/chezmoi` | `gh` on GitHub | +| Secondary overlay (optional) | e.g. `~/.local/share/chezmoi-work` | Employer or second user on GitHub | ## Fallback diff --git a/dot_agents/skills/mise-security/SKILL.md b/dot_agents/skills/mise-security/SKILL.md index 90f4c4d..1363c67 100644 --- a/dot_agents/skills/mise-security/SKILL.md +++ b/dot_agents/skills/mise-security/SKILL.md @@ -54,11 +54,11 @@ Commit updated lockfiles with the version bump. ## CI/CD hardening -| Action | Status here | -| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Disable experimental features** — `MISE_EXPERIMENTAL=0` | Profile.d default; **Forgejo** `.forgejo/workflows/pre-commit.yaml` job `env` | -| **Paranoid + trust (CI)** — same posture as interactive shells, scoped to the checkout | Forgejo workflow: `MISE_PARANOID=1`, `MISE_TRUSTED_CONFIG_PATHS` = `${{ github.workspace }}`. No `MISE_CEILING_PATHS`: mise excludes the ceiling dir itself, so pointing it at the workspace hides the repo's own `.mise.toml`; paranoid mode already hard-fails on configs outside the trusted path | -| **Locked tool runs in CI** — `mise exec --locked` | Forgejo pre-commit + Neovim steps; `qa.just` / `.pre-commit-config.yaml` locally | +| Action | Status here | +| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Disable experimental features** — `MISE_EXPERIMENTAL=0` | Profile.d default; **GitHub Actions** `.github/workflows/pre-commit.yaml` job `env` | +| **Paranoid + trust (CI)** — same posture as interactive shells, scoped to the checkout | GitHub workflow: `MISE_PARANOID=1`, `MISE_TRUSTED_CONFIG_PATHS` = `${{ github.workspace }}`. No `MISE_CEILING_PATHS`: mise excludes the ceiling dir itself, so pointing it at the workspace hides the repo's own `.mise.toml`; paranoid mode already hard-fails on configs outside the trusted path | +| **Locked tool runs in CI** — `mise exec --locked` | GitHub pre-commit + Neovim steps; `qa.just` / `.pre-commit-config.yaml` locally | In CI scripts that invoke mise, export the same vars (or the workspace-scoped equivalents above on shared runners) and use `mise exec --locked`. diff --git a/install.sh b/install.sh index 64d35e3..ea615bb 100755 --- a/install.sh +++ b/install.sh @@ -12,9 +12,7 @@ if [ -z "$REPO_URL" ]; then printf '%s\n' "Unable to determine dotfiles repo URL." >&2 printf '%s\n' "Set REPO_URL (or CHEZMOI_REPO_URL) and rerun." >&2 printf '%s\n' "Example SSH: REPO_URL='git@github.com:/dotfiles.git' ./install.sh" >&2 - printf '%s\n' "Example SSH: REPO_URL='git@codeberg.org:/dotfiles.git' ./install.sh" >&2 printf '%s\n' "Example HTTPS: REPO_URL='https://github.com//dotfiles.git' ./install.sh" >&2 - printf '%s\n' "Example HTTPS: REPO_URL='https://codeberg.org//dotfiles.git' ./install.sh" >&2 exit 1 fi From 74b83d634bf438e7f3ee9a411cf80496b6f25f6a Mon Sep 17 00:00:00 2001 From: aguil Date: Sat, 8 Aug 2026 19:06:28 -0600 Subject: [PATCH 4/4] fix(chezmoi): keep Codeberg config stable when origin is not Codeberg The Codeberg user/host/op-refs were only ever supplied by remote inference or env vars. Their promptStringOnce fallbacks check root keys (.gitPersonalCodebergUser, .codebergHost, ...) that the template never writes back into [data], so 'Once' never engaged and the prompt was simply unreachable while origin was on Codeberg. With origin on GitHub, inference goes empty and the prompts become reachable, so 'chezmoi init' now blocks on a TTY and fails non-interactively. Fall back to the existing [data.codeberg] and [data.ssh.personal] values before prompting, so an already-configured machine re-inits unattended and Codeberg auth survives the origin move. --- .chezmoi.toml.tmpl | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/.chezmoi.toml.tmpl b/.chezmoi.toml.tmpl index eb61d81..39c4312 100644 --- a/.chezmoi.toml.tmpl +++ b/.chezmoi.toml.tmpl @@ -75,17 +75,41 @@ {{- else -}} {{- $personalGithubHostAlias = promptStringOnce . "gitPersonalGithubHostAlias" "Personal GitHub SSH host alias" "github-personal" -}} {{- end -}} +{{- /* Reuse existing [data.codeberg] before prompting. The promptStringOnce + fallbacks below key off root names this template never writes back into + [data], so "Once" never engages; while origin was on Codeberg the + prompts were simply unreachable because inference always won. With + origin elsewhere, inference is empty and a configured machine would + re-prompt (and fail non-interactively) on every init. */ -}} +{{- $existingCodebergUser := "" -}} +{{- $existingCodebergHost := "" -}} +{{- $existingCodebergTokenOpRef := "" -}} +{{- $existingCodebergSshKeyOpRef := "" -}} +{{- if hasKey . "codeberg" -}} +{{- if hasKey .codeberg "user" -}}{{- $existingCodebergUser = .codeberg.user -}}{{- end -}} +{{- if hasKey .codeberg "host" -}}{{- $existingCodebergHost = .codeberg.host -}}{{- end -}} +{{- if hasKey .codeberg "tokenOpRef" -}}{{- $existingCodebergTokenOpRef = .codeberg.tokenOpRef -}}{{- end -}} +{{- if hasKey .codeberg "sshKeyOpRef" -}}{{- $existingCodebergSshKeyOpRef = .codeberg.sshKeyOpRef -}}{{- end -}} +{{- end -}} +{{- $existingCodebergIdentityFile := "" -}} +{{- if and (hasKey . "ssh") (hasKey .ssh "personal") (hasKey .ssh.personal "codebergIdentityFile") -}} +{{- $existingCodebergIdentityFile = .ssh.personal.codebergIdentityFile -}} +{{- end -}} {{- $envPersonalCodebergUser := env "CHEZMOI_GIT_CODEBERG_USER" -}} {{- $personalCodebergUser := "" -}} {{- if ne $envPersonalCodebergUser "" -}} {{- $personalCodebergUser = $envPersonalCodebergUser -}} {{- else if ne $inferredPersonalCodebergUser "" -}} {{- $personalCodebergUser = $inferredPersonalCodebergUser -}} +{{- else if ne $existingCodebergUser "" -}} +{{- $personalCodebergUser = $existingCodebergUser -}} {{- else -}} {{- $personalCodebergUser = promptStringOnce . "gitPersonalCodebergUser" "Codeberg username for repo routing" "codeberg-user" -}} {{- end -}} {{- $defaultCodebergHost := "codeberg.org" -}} -{{- if ne $inferredPersonalGhHost "" -}} +{{- if ne $existingCodebergHost "" -}} +{{- $defaultCodebergHost = $existingCodebergHost -}} +{{- else if ne $inferredPersonalGhHost "" -}} {{- $defaultCodebergHost = $inferredPersonalGhHost -}} {{- end -}} [data] @@ -96,14 +120,14 @@ tokenOpRef = "{{ if eq $profile "personal" }}{{ if ne (env "CHEZMOI_GH_TOKEN_OP_REF") "" }}{{ env "CHEZMOI_GH_TOKEN_OP_REF" }}{{ else }}{{ promptStringOnce . "ghTokenOpRefPersonal" "1Password ref for personal GitHub CLI token" "op://Private/GitHub CLI/token" }}{{ end }}{{ else }}{{ env "CHEZMOI_GH_TOKEN_OP_REF" }}{{ end }}" [data.codeberg] - host = "{{ if ne (env "CHEZMOI_CODEBERG_HOST") "" }}{{ env "CHEZMOI_CODEBERG_HOST" }}{{ else }}{{ promptStringOnce . "codebergHost" "Codeberg hostname" $defaultCodebergHost }}{{ end }}" + host = "{{ if ne (env "CHEZMOI_CODEBERG_HOST") "" }}{{ env "CHEZMOI_CODEBERG_HOST" }}{{ else if ne $existingCodebergHost "" }}{{ $existingCodebergHost }}{{ else }}{{ promptStringOnce . "codebergHost" "Codeberg hostname" $defaultCodebergHost }}{{ end }}" user = "{{ $personalCodebergUser }}" - tokenOpRef = "{{ if eq $profile "personal" }}{{ if ne (env "CHEZMOI_CODEBERG_TOKEN_OP_REF") "" }}{{ env "CHEZMOI_CODEBERG_TOKEN_OP_REF" }}{{ else }}{{ promptStringOnce . "codebergTokenOpRefPersonal" "1Password ref for Codeberg API token (fj)" "op://Private/Codeberg/token" }}{{ end }}{{ else }}{{ env "CHEZMOI_CODEBERG_TOKEN_OP_REF" }}{{ end }}" - sshKeyOpRef = "{{ if eq $profile "personal" }}{{ if ne (env "CHEZMOI_CODEBERG_SSH_KEY_OP_REF") "" }}{{ env "CHEZMOI_CODEBERG_SSH_KEY_OP_REF" }}{{ else }}{{ promptStringOnce . "codebergSshKeyOpRefPersonal" "1Password ref for Codeberg SSH private key" "op://Private/Codeberg SSH/private key" }}{{ end }}{{ else }}{{ env "CHEZMOI_CODEBERG_SSH_KEY_OP_REF" }}{{ end }}" + tokenOpRef = "{{ if eq $profile "personal" }}{{ if ne (env "CHEZMOI_CODEBERG_TOKEN_OP_REF") "" }}{{ env "CHEZMOI_CODEBERG_TOKEN_OP_REF" }}{{ else if ne $existingCodebergTokenOpRef "" }}{{ $existingCodebergTokenOpRef }}{{ else }}{{ promptStringOnce . "codebergTokenOpRefPersonal" "1Password ref for Codeberg API token (fj)" "op://Private/Codeberg/token" }}{{ end }}{{ else }}{{ env "CHEZMOI_CODEBERG_TOKEN_OP_REF" }}{{ end }}" + sshKeyOpRef = "{{ if eq $profile "personal" }}{{ if ne (env "CHEZMOI_CODEBERG_SSH_KEY_OP_REF") "" }}{{ env "CHEZMOI_CODEBERG_SSH_KEY_OP_REF" }}{{ else if ne $existingCodebergSshKeyOpRef "" }}{{ $existingCodebergSshKeyOpRef }}{{ else }}{{ promptStringOnce . "codebergSshKeyOpRefPersonal" "1Password ref for Codeberg SSH private key" "op://Private/Codeberg SSH/private key" }}{{ end }}{{ else }}{{ env "CHEZMOI_CODEBERG_SSH_KEY_OP_REF" }}{{ end }}" [data.ssh.personal] githubIdentityFile = "{{ env "CHEZMOI_SSH_PERSONAL_GITHUB_IDENTITY_FILE" }}" - codebergIdentityFile = "{{ if ne (env "CHEZMOI_SSH_PERSONAL_CODEBERG_IDENTITY_FILE") "" }}{{ env "CHEZMOI_SSH_PERSONAL_CODEBERG_IDENTITY_FILE" }}{{ else }}{{ promptStringOnce . "sshPersonalCodebergIdentityFile" "Path to personal Codeberg SSH private key" "~/.ssh/id_ed25519_codeberg" }}{{ end }}" + codebergIdentityFile = "{{ if ne (env "CHEZMOI_SSH_PERSONAL_CODEBERG_IDENTITY_FILE") "" }}{{ env "CHEZMOI_SSH_PERSONAL_CODEBERG_IDENTITY_FILE" }}{{ else if ne $existingCodebergIdentityFile "" }}{{ $existingCodebergIdentityFile }}{{ else }}{{ promptStringOnce . "sshPersonalCodebergIdentityFile" "Path to personal Codeberg SSH private key" "~/.ssh/id_ed25519_codeberg" }}{{ end }}" [data.git.work] name = "Jason Aguilon"