184 lines
7.6 KiB
Text
184 lines
7.6 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
# Private dev-internal overlay manager.
|
||
|
|
#
|
||
|
|
# Tracks the gitignored .coder/ and .claude/ of a project in a SEPARATE
|
||
|
|
# "bare-overlay" repo: its git-dir lives apart (<worktree>/.internal.git, itself
|
||
|
|
# gitignored), its work-tree is the project root, and it tracks only the two
|
||
|
|
# private dirs. The public repo and the overlay coexist on one working tree
|
||
|
|
# without colliding (distinct git-dirs). The overlay pushes ONLY to a private
|
||
|
|
# remote and is NEVER mirrored — same boundary as vault (ADR-062).
|
||
|
|
#
|
||
|
|
# Generic: works for any project using the same .coder/.claude layout.
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# nu internal-overlay.nu setup <worktree> [--remote <forgejo-priv-url>] [--push]
|
||
|
|
# nu internal-overlay.nu onboard <worktree> --remote <forgejo-priv-url>
|
||
|
|
# nu internal-overlay.nu run <worktree> -- <git args...>
|
||
|
|
# nu internal-overlay.nu status <worktree>
|
||
|
|
|
||
|
|
# Absolute path of the overlay's bare git-dir for a given work-tree.
|
||
|
|
def overlay-gitdir [wt: path] {
|
||
|
|
$wt | path join ".internal.git"
|
||
|
|
}
|
||
|
|
|
||
|
|
# The git CLI prefix (cwd into the work-tree, pin git-dir + work-tree).
|
||
|
|
def git-prefix [wt: path] {
|
||
|
|
["-C" $wt "--git-dir" (overlay-gitdir $wt) "--work-tree" $wt]
|
||
|
|
}
|
||
|
|
|
||
|
|
# Resolve the overlay work-tree for daily commands (commit/run/status), in order:
|
||
|
|
# 1. explicit --worktree flag
|
||
|
|
# 2. $env.ONTOREF_INTERNAL_ROOT (for agents in a jj workspace — anchors the
|
||
|
|
# overlay to the MAIN checkout, since ignored .coder/.claude are NOT
|
||
|
|
# materialized in a fresh workspace)
|
||
|
|
# 3. walk up from $env.PWD to the nearest .internal.git
|
||
|
|
# The overlay must already exist (setup/onboard create it).
|
||
|
|
def resolve-wt [given: string] {
|
||
|
|
if ($given | is-not-empty) { return ($given | path expand) }
|
||
|
|
let er = ($env.ONTOREF_INTERNAL_ROOT? | default "")
|
||
|
|
if ($er | is-not-empty) {
|
||
|
|
let p = ($er | path expand)
|
||
|
|
if not (overlay-gitdir $p | path exists) {
|
||
|
|
print --stderr $"ONTOREF_INTERNAL_ROOT=($p) but no overlay at (overlay-gitdir $p)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
return $p
|
||
|
|
}
|
||
|
|
mut d = $env.PWD
|
||
|
|
while $d != "/" {
|
||
|
|
if (overlay-gitdir $d | path exists) { return $d }
|
||
|
|
$d = ($d | path dirname)
|
||
|
|
}
|
||
|
|
print --stderr "no overlay: pass --worktree, set ONTOREF_INTERNAL_ROOT, or cd into an overlaid project"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
def main [] {
|
||
|
|
print "internal-overlay — private dev-internal overlay (.coder/.claude), never mirrored"
|
||
|
|
print ""
|
||
|
|
print " setup <worktree> [--remote <url>] [--push] create overlay, capture current .coder/.claude"
|
||
|
|
print " onboard <worktree> --remote <url> clone the overlay into an existing checkout"
|
||
|
|
print " commit <message> [--worktree <dir>] force-add .coder/.claude and commit"
|
||
|
|
print " run \"<git args>\" [--worktree <dir>] run any git command against the overlay"
|
||
|
|
print " status [--worktree <dir>] short status of the overlay"
|
||
|
|
print ""
|
||
|
|
print " commit/run/status resolve the work-tree as: --worktree -> \$ONTOREF_INTERNAL_ROOT -> walk up from cwd."
|
||
|
|
print " In a jj agent workspace, set ONTOREF_INTERNAL_ROOT to the MAIN checkout (ignored .coder/.claude"
|
||
|
|
print " are not materialized in a fresh workspace). The overlay never gets a jj/rad/github remote."
|
||
|
|
}
|
||
|
|
|
||
|
|
def "main setup" [
|
||
|
|
worktree: path # project/member root to overlay
|
||
|
|
--remote: string = "" # forgejo PRIVATE url (optional)
|
||
|
|
--push # push after commit (default: you push)
|
||
|
|
...paths: string # dirs to track (default: .coder .claude)
|
||
|
|
] {
|
||
|
|
let paths = (if ($paths | is-empty) { [".coder" ".claude"] } else { $paths })
|
||
|
|
let wt = ($worktree | path expand)
|
||
|
|
let gd = (overlay-gitdir $wt)
|
||
|
|
|
||
|
|
if not ($wt | path exists) {
|
||
|
|
print --stderr $"work-tree does not exist: ($wt)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
if ($gd | path exists) {
|
||
|
|
print --stderr $"overlay already exists at ($gd) — use 'run' or 'status'"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
# PRE-1 privacy: refuse if the dirs are already tracked in the PUBLIC repo —
|
||
|
|
# they would otherwise reach the public mirror through that history.
|
||
|
|
let pub = (^git -C $wt ls-files | complete)
|
||
|
|
if $pub.exit_code == 0 {
|
||
|
|
let leaked = ($pub.stdout | lines | where ($it =~ '^\.(coder|claude)/'))
|
||
|
|
if ($leaked | length) > 0 {
|
||
|
|
print --stderr $"PRE-1 FAILED: ($leaked | length) files under .coder/.claude are TRACKED in the public repo and would be mirrored."
|
||
|
|
print --stderr $" fix: git -C ($wt) rm -r --cached .coder .claude then scrub history before creating the overlay."
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# PRE-2 privacy: the public repo must ignore the private dirs AND the overlay git-dir.
|
||
|
|
let gi = ($wt | path join ".gitignore")
|
||
|
|
let want = [".coder/" ".claude/" ".internal.git/"]
|
||
|
|
let have = (if ($gi | path exists) { open --raw $gi | lines } else { [] })
|
||
|
|
let missing = ($want | where ($it not-in $have))
|
||
|
|
if ($missing | length) > 0 {
|
||
|
|
$"(char nl)($missing | str join (char nl))(char nl)" | save --append --raw $gi
|
||
|
|
print $"PRE-2: added to .gitignore -> ($missing | str join ', ')"
|
||
|
|
}
|
||
|
|
|
||
|
|
let pre = (git-prefix $wt)
|
||
|
|
^git init --bare $gd
|
||
|
|
^git ...($pre ++ ["config" "status.showUntrackedFiles" "no"])
|
||
|
|
# -f is required: the work-tree's .gitignore ignores these dirs (PRE-2), and the
|
||
|
|
# overlay's whole purpose is to track exactly those ignored paths.
|
||
|
|
^git ...($pre ++ ["add" "-f"] ++ $paths)
|
||
|
|
^git ...($pre ++ ["commit" "-m" "internal: dev-internal overlay (.coder + .claude)"])
|
||
|
|
|
||
|
|
if ($remote | is-not-empty) {
|
||
|
|
^git ...($pre ++ ["remote" "add" "forgejo-priv" $remote])
|
||
|
|
if $push {
|
||
|
|
^git ...($pre ++ ["push" "-u" "forgejo-priv" "HEAD"])
|
||
|
|
} else {
|
||
|
|
print $"remote 'forgejo-priv' set. Push yourself: nu internal-overlay.nu run ($wt) -- push -u forgejo-priv HEAD"
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
print $"overlay created at ($gd). Add a private remote later: nu internal-overlay.nu run ($wt) -- remote add forgejo-priv <url>"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
def "main onboard" [
|
||
|
|
worktree: path # existing checkout of the PUBLIC repo
|
||
|
|
--remote: string = "" # private overlay url (required)
|
||
|
|
] {
|
||
|
|
let wt = ($worktree | path expand)
|
||
|
|
let gd = (overlay-gitdir $wt)
|
||
|
|
|
||
|
|
if ($remote | is-empty) {
|
||
|
|
print --stderr "--remote <private-overlay-url> is required for onboard"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
if ($gd | path exists) {
|
||
|
|
print --stderr $"overlay git-dir already present at ($gd)"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
^git clone --bare $remote $gd
|
||
|
|
let pre = (git-prefix $wt)
|
||
|
|
# a bare clone leaves core.bare=true and drops local config — restore both.
|
||
|
|
^git ...($pre ++ ["config" "core.bare" "false"])
|
||
|
|
^git ...($pre ++ ["config" "status.showUntrackedFiles" "no"])
|
||
|
|
^git ...($pre ++ ["checkout" "-f"])
|
||
|
|
print $"onboarded: .coder/.claude materialized into ($wt)"
|
||
|
|
}
|
||
|
|
|
||
|
|
def "main commit" [
|
||
|
|
message: string # commit message
|
||
|
|
--worktree: string = "" # override (else env / walk-up)
|
||
|
|
...paths: string # tracked dirs (default: .coder .claude)
|
||
|
|
] {
|
||
|
|
let paths = (if ($paths | is-empty) { [".coder" ".claude"] } else { $paths })
|
||
|
|
let wt = (resolve-wt $worktree)
|
||
|
|
let pre = (git-prefix $wt)
|
||
|
|
# -f bypasses the work-tree .gitignore that hides these dirs from the public repo.
|
||
|
|
^git ...($pre ++ ["add" "-f"] ++ $paths)
|
||
|
|
^git ...($pre ++ ["commit" "-m" $message])
|
||
|
|
}
|
||
|
|
|
||
|
|
def "main run" [
|
||
|
|
git_command: string # quoted git args, e.g. "push -u forgejo-priv HEAD"
|
||
|
|
--worktree: string = "" # override (else env / walk-up)
|
||
|
|
] {
|
||
|
|
let wt = (resolve-wt $worktree)
|
||
|
|
let parts = ($git_command | split row " " | where ($it | is-not-empty))
|
||
|
|
^git ...((git-prefix $wt) ++ $parts)
|
||
|
|
}
|
||
|
|
|
||
|
|
def "main status" [
|
||
|
|
--worktree: string = "" # override (else env / walk-up)
|
||
|
|
] {
|
||
|
|
let wt = (resolve-wt $worktree)
|
||
|
|
^git ...((git-prefix $wt) ++ ["status" "--short" "--branch"])
|
||
|
|
}
|