@caddis/cli 0.4.19 → 0.4.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/bundles/antigravity-plugin/plugin.json +1 -1
  2. package/bundles/antigravity-plugin/skills/_registry.md +4 -2
  3. package/bundles/antigravity-plugin/skills/data-lineage/SKILL.md +32 -0
  4. package/bundles/antigravity-plugin/skills/hld/SKILL.md +20 -8
  5. package/bundles/antigravity-plugin/skills/lld/SKILL.md +27 -12
  6. package/bundles/antigravity-plugin/skills/port-to-github/SKILL.md +231 -0
  7. package/bundles/antigravity-plugin/skills/port-to-github/references/ci-and-credentials.md +66 -0
  8. package/bundles/antigravity-plugin/skills/port-to-github/references/config.example.json +18 -0
  9. package/bundles/antigravity-plugin/skills/port-to-github/references/old-host.md +61 -0
  10. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Compare-PortRefs.ps1 +68 -0
  11. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Convert-PortWorkflow.ps1 +92 -0
  12. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Get-PortSource.ps1 +459 -0
  13. package/bundles/antigravity-plugin/skills/port-to-github/scripts/PortCommon.ps1 +130 -0
  14. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Publish-PortToGitHub.ps1 +156 -0
  15. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Switch-CheckoutOrigin.ps1 +182 -0
  16. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Test-PortReadiness.ps1 +218 -0
  17. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Test-PortWorkflowParses.ps1 +62 -0
  18. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  19. package/bundles/antigravity-plugin-extras/skills/_registry.md +3 -2
  20. package/bundles/antigravity-plugin-extras/skills/data-lineage/SKILL.md +158 -0
  21. package/bundles/antigravity-plugin-extras/skills/data-lineage/scripts/convert_doc_to_confluence.py +1430 -0
  22. package/bundles/antigravity-plugin-extras/skills/hld/SKILL.md +155 -35
  23. package/bundles/antigravity-plugin-extras/skills/hld/references/gold-standard-hld.md +419 -0
  24. package/bundles/antigravity-plugin-extras/skills/hld/scripts/convert_doc_to_confluence.py +900 -184
  25. package/bundles/antigravity-plugin-extras/skills/lld/SKILL.md +145 -41
  26. package/bundles/antigravity-plugin-extras/skills/lld/scripts/convert_doc_to_confluence.py +900 -184
  27. package/bundles/codex/.codex/skills/devops/port-to-github/SKILL.md +231 -0
  28. package/bundles/codex/.codex/skills/devops/port-to-github/references/ci-and-credentials.md +66 -0
  29. package/bundles/codex/.codex/skills/devops/port-to-github/references/config.example.json +18 -0
  30. package/bundles/codex/.codex/skills/devops/port-to-github/references/old-host.md +61 -0
  31. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Compare-PortRefs.ps1 +68 -0
  32. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Convert-PortWorkflow.ps1 +92 -0
  33. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Get-PortSource.ps1 +459 -0
  34. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/PortCommon.ps1 +130 -0
  35. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Publish-PortToGitHub.ps1 +156 -0
  36. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Switch-CheckoutOrigin.ps1 +182 -0
  37. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Test-PortReadiness.ps1 +218 -0
  38. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Test-PortWorkflowParses.ps1 +62 -0
  39. package/bundles/manifest.json +3 -3
  40. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "caddis",
3
- "version": "1.3.111",
3
+ "version": "1.3.112",
4
4
  "description": "caddis — agent-agnostic dev harness as an Antigravity (agy) plugin: TDD skills + commands, single-sourced with the Claude Code plugin."
5
5
  }
@@ -39,6 +39,7 @@
39
39
  | Ci Cd Pipeline | `ci-cd-pipeline/` | CI/CD pipeline design and implementation for GitHub Actions, Azure DevOps, and general pipeline architecture. Use when creating build pipelines, deployment workflows, quality gates, environment promotion strategies, or automating release processes. |
40
40
  | Gh Cli | `gh-cli/` | GitHub CLI operations — issues, PRs, releases, and repo management |
41
41
  | Git Commit | `git-commit/` | Create well-structured conventional commit messages following Conventional Commits standard. Use when committing changes, preparing PRs, or generating changelogs. |
42
+ | Port To Github | `port-to-github/` | Move (port) an app or repository to GitHub from ANY source - a Gitea/GitLab/any git server URL, a local folder, a network (UNC) share, or a folder on a remote Windows box reached over WinRM - with or without existing git history. Keeps every branch and tag, verifies by SHA, translates Gitea/Forgejo Actions CI, switches deploy checkouts to GitHub with automatic rollback, and retires the old host safely. Use when the user says "move this app to GitHub", "port X to GitHub", "migrate from Gitea", "put this folder on GitHub", "this app only exists on the server", "import the repo from the prod box", or "cut the deploy over to GitHub". |
42
43
  | Safe Cleanup | `safe-cleanup/` | Use BEFORE deleting any directory tree, clearing a scratchpad, removing git worktrees, or "tidying up" generated folders - especially on Windows, where a recursive delete that meets a junction or symlink destroys the link's TARGET. Also use when verifying that a Python venv or node_modules survived a cleanup, because pip check and file counts both report healthy on a half-deleted environment. |
43
44
  | Using Git Worktrees | `using-git-worktrees/` | Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification |
44
45
  | Windows Deployment | `windows-deployment/` | Deploy FastAPI + React/Vite apps to Windows Server with NSSM services, reverse proxy (IIS or nginx), and git-pull workflow. Use when deploying any web app to a Windows prod server, setting up NSSM services, configuring IIS or nginx reverse proxy, making code environment-aware for dev/prod, or troubleshooting prod deployment issues. |
@@ -49,9 +50,10 @@
49
50
  |-------|------|-------------|
50
51
  | Code Documentation | `code-documentation/` | Writing effective code documentation - API docs, README files, inline comments, and technical guides. Use for documenting codebases, APIs, or writing developer guides. |
51
52
  | Confluence Hla | `confluence-hla/` | Author enterprise High-Level Architecture (HLA) documents in Simplified Technical English and generate Confluence Storage Format XHTML with self-contained PlantUML diagrams, wrapped tables, and zero AI fingerprints. Use when creating high-level architecture designs, architecture documentation for Confluence, or converting markdown architecture to Confluence storage format. |
53
+ | Data Lineage | `data-lineage/` | Generate End-to-End Data Lineage and Telemetry Specification Markdown and Confluence XHTML for fleet observability. |
52
54
  | Explainer Doc | `explainer-doc/` | Write a document that hands off work to a team who did NOT do the investigation — an external vendor, another team, a contractor — and who must be able to act on it alone. Use when the user says "write a spec for the X team", "document this for someone outside the team", "explainer doc", "implementation spec for a vendor", "hand this off to Y", or is about to write a design doc whose primary reader did not live through the debugging/design process. NOT for internal specs where the reader already has full context (use technical-writing or writing-plans instead) — this is specifically for the "stranger must follow it alone" case. |
53
- | Hld | `hld/` | Generate High-Level Design (HLD) Markdown documentation and Confluence XHTML with Draw.io diagrams. |
54
- | Lld | `lld/` | Generate Lower-Level Design (LLD) Markdown documentation and Confluence XHTML with Draw.io diagrams. |
55
+ | Hld | `hld/` | Generate High-Level Design (HLD) Markdown documentation and Confluence XHTML with native PlantUML diagrams. |
56
+ | Lld | `lld/` | Generate Lower-Level Design (LLD) Markdown documentation and Confluence XHTML with native PlantUML diagrams. |
55
57
  | Tech Explainer | `tech-explainer/` | >- |
56
58
  | Technical Writing | `technical-writing/` | Technical documentation best practices for READMEs, API docs, architecture docs, runbooks, and developer guides. Use when writing or reviewing documentation, creating onboarding guides, or establishing documentation standards. |
57
59
  | Writing Plans | `writing-plans/` | Use when you have a spec or requirements for a multi-step task, before touching code |
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: data-lineage
3
+ description: Generate End-to-End Data Lineage and Telemetry Specification Markdown and Confluence XHTML for fleet observability.
4
+ ---
5
+
6
+ # /data-lineage Command
7
+
8
+ Run the End-to-End Data Lineage generation procedure:
9
+
10
+ 1. **Target Architecture & Telemetry Discovery:**
11
+ - Scan target application routes, pages, and controllers.
12
+ - Trace API handlers down to physical tables, views, and columns.
13
+ - Identify database server stored procedures, batch scripts, and scheduled SQL Agent jobs that populate each dataset.
14
+ - Cross-reference estate configuration catalogs to identify hostnames, IPs, ports, and synthetic health probe endpoints.
15
+
16
+ 2. **Author Canonical Markdown Document:**
17
+ - Create document at `docs/<app>/<app>-data-lineage.md`.
18
+ - Adhere strictly to the standard 5-section lineage structure:
19
+ 1. Physical Estate & Host Infrastructure (servers, IPs, ports, database instances, access methods).
20
+ 2. End-to-End Dependency & Impact Flowchart (vibrant Mermaid flowchart from feeds down to UI surfaces).
21
+ 3. Comprehensive 7-Column Lineage Matrix (`App`, `UI Page`, `API Route`, `Dataset`, `Producer`, `Database`, `Physical Host`).
22
+ 4. Stored Procedures & Producer Execution Catalog (schedules, triggers, parameters, source and target tables).
23
+ 5. Telemetry Ingestion Contract (machine-readable JSON target graph).
24
+ - Use ASD-STE100 Simplified Technical English: active voice, short sentences, zero em-dashes.
25
+
26
+ 3. **Convert to Confluence Storage Format XHTML:**
27
+ - Run the PlantUML-enabled converter:
28
+ ```powershell
29
+ python scripts/convert_doc_to_confluence.py docs/<app>/<app>-data-lineage.md --output docs/<app>/<app>-data-lineage.html --diagram-type plantuml
30
+ ```
31
+ - Verify that all diagrams convert to native `<ac:structured-macro ac:name="plantuml">` blocks with scale 1.2, natural splines, auto-wrapped edge labels, crimson red borders on impacted components, and embedded Creole legends.
32
+ - Verify that output validates as 100% well-formed XML.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hld
3
- description: Generate High-Level Design (HLD) Markdown documentation and Confluence XHTML with Draw.io diagrams.
3
+ description: Generate High-Level Design (HLD) Markdown documentation and Confluence XHTML with native PlantUML diagrams.
4
4
  ---
5
5
 
6
6
  # /hld Command
@@ -12,16 +12,28 @@ Run the High-Level Design (HLD) generation procedure:
12
12
  - Enforce HLD boundaries: exclude internal API routes, low-level stored procedures, and detailed ETL scripts.
13
13
  - Use high-level reporting summary tables and domain entities only.
14
14
 
15
- 2. **Author Canonical Markdown Document:**
15
+ 2. **Component & Interface Classification Interview (Grill-Me):**
16
+ - **MANDATORY GATE:** Present discovered systems (`SYS_xx`) and interfaces (`INT_xx`) to the user.
17
+ - Ask the user to confirm/clarify:
18
+ - Which systems/components are Existing fleet infrastructure (`Impacted = No`, Blue `#1D4ED8`) vs New / Impacted (`Impacted = Yes`, Red `#DC2626`)?
19
+ - Which interfaces are Existing Connectivity (Green `#16A34A`) vs New Connectivity (Red `#DC2626`)?
20
+ - Formulate recommendations from codebase discovery and seek user sign-off in a single interactive turn.
21
+
22
+ 3. **Author Canonical Markdown Document:**
16
23
  - Create document at `docs/<app>/<app>-hld.md`.
17
- - Adhere strictly to the standard 7-section HLD structure.
24
+ - Adhere strictly to the standard HLD structure.
25
+ - **Do not synthesize or fabricate "Target Architecture", "Target State Roadmap", or "Security Topology" sections/diagrams** when there are no notes mentioning future architecture and the user does not request them. Only include them when explicitly requested by the user or specified in existing project notes as future work.
26
+ - Place the core architecture diagram in Section 4 titled `### 4.1 Solution Design and Architecture` under `## 4. Solution Architecture Landscape`. Do not use `System Topology Architecture Diagram` or place it in Section 2.
27
+ - Represent domain data models in Section 5 (`### 5.2 Domain Entity Model`) using Mermaid `classDiagram` or `erDiagram` syntax, converted natively to PlantUML.
18
28
  - Ensure Mermaid diagrams depict automated schedulers as system participants, not human actors.
29
+ - Use colorful, professional Mermaid diagrams with explicit `classDef` styling palettes (no raw yellow boxes).
19
30
  - Use ASD-STE100 Simplified Technical English: active voice, short sentences, zero em-dashes.
20
31
 
21
- 3. **Convert to Confluence Storage Format XHTML:**
22
- - Run the Draw.io-enabled converter:
23
- ```bash
24
- python scripts/convert_doc_to_confluence.py docs/<app>/<app>-hld.md --output docs/<app>/<app>-hld.html
32
+ 4. **Convert to Confluence Storage Format XHTML:**
33
+ - Run the PlantUML-enabled converter:
34
+ ```powershell
35
+ python scripts/convert_doc_to_confluence.py docs/<app>/<app>-hld.md --output docs/<app>/<app>-hld.html --diagram-type plantuml
25
36
  ```
26
- - Verify that all diagrams are converted to native `<ac:structured-macro ac:name="drawio">` blocks.
37
+ - Verify that all diagrams are converted to native `<ac:structured-macro ac:name="plantuml">` blocks with scale 1.2, natural splines, auto-wrapped edge labels, crimson red borders on impacted components, and embedded Creole legends.
38
+ - Ensure multi-stage operational flows use horizontal side-by-side stages (`flowchart LR`) and architecture topologies follow clean 3-tier hierarchies (`flowchart TD`).
27
39
  - Verify that output validates as well-formed XML.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lld
3
- description: Generate Lower-Level Design (LLD) Markdown documentation and Confluence XHTML with Draw.io diagrams.
3
+ description: Generate Lower-Level Design (LLD) Markdown documentation and Confluence XHTML with native PlantUML diagrams.
4
4
  ---
5
5
 
6
6
  # /lld Command
@@ -11,20 +11,35 @@ Run the Lower-Level Design (LLD) generation procedure:
11
11
  - Scan physical hosts, service managers (NSSM, systemd), environment configurations, and ports.
12
12
  - Extract internal API routes, controllers, middleware, and dependency injection patterns.
13
13
  - Inspect database schemas, tables, views, primary keys, and index configurations.
14
- - Catalog stored procedures (`usp_*`), parameter lists, business logic flows, and return codes.
15
- - Map batch ingestion scripts, command-line arguments, and background refresher polling loops.
14
+ - Catalog stored procedures (`usp_*`), parameter lists, analytical logic flows, and discrepancy output structures.
15
+ - Map ETL scripts, daily/monthly cron schedules, record volumes, idempotency logic, and shared audit tables (e.g. `ETL_AUDIT_LOG`).
16
+ - Map background refresher loops, polling intervals, watermark sync, and caching tiers.
17
+ - Map UI screens to stable query IDs (`QRY-*`), API endpoints, repository methods, and source tables.
16
18
 
17
- 2. **Author Canonical Markdown Document:**
19
+ 2. **Component & Interface Classification Interview (Grill-Me):**
20
+ - **MANDATORY GATE:** Present discovered internal components (`SYS_Lxx`) and internal interfaces (`INT_Lxx`) to the user.
21
+ - Ask the user to confirm/clarify:
22
+ - Which internal components are New / Impacted (`Impacted = Yes`, Red `#DC2626`) vs Existing fleet databases/services (`Impacted = No`, Blue `#1D4ED8`)?
23
+ - Which interfaces are Existing Connectivity (Green `#16A34A`) vs New Connectivity (Red `#DC2626`)?
24
+ - Seek user sign-off in a single interactive turn before drafting diagrams.
25
+
26
+ 3. **Author Canonical Markdown Document:**
18
27
  - Create document at `docs/<app>/<app>-lld.md`.
19
- - Adhere strictly to the standard 7-section LLD structure.
20
- - Detail every stored procedure, table, ETL job, and endpoint contract.
21
- - Include detailed Mermaid sequence, flowchart, and component interaction diagrams.
28
+ - Adhere strictly to the standard LLD structure including the 5 mandatory technical sections:
29
+ 1. Operational Process Flow (multi-stage batch, validation, refresh, and serving pipeline)
30
+ 2. End-to-End Operational Sequence Diagram (technical interaction sequence)
31
+ 3. Detailed Solution Architecture Landscape (`SYS_Lxx` components, `INT_Lxx` interfaces, Applications and Interfaces tables)
32
+ 4. End-to-End Database and Data Processing Pipeline (staging, validation, mart rollup, API extraction)
33
+ 5. End-to-End Database Lineage Specification (complete domain -> source -> staging -> SP -> mart -> API -> UI matrix)
34
+ - Dissect every ETL pipeline with schedules, row volumes, and idempotency.
35
+ - Detail stored procedures catalog with parameters, join logic, and thresholds.
36
+ - Use colorful, professional Mermaid diagrams with explicit `classDef` styling palettes (no raw yellow boxes).
22
37
  - Use ASD-STE100 Simplified Technical English: active voice, short sentences, zero em-dashes.
23
38
 
24
- 3. **Convert to Confluence Storage Format XHTML:**
25
- - Run the Draw.io-enabled converter:
26
- ```bash
27
- python scripts/convert_doc_to_confluence.py docs/<app>/<app>-lld.md --output docs/<app>/<app>-lld.html
39
+ 4. **Convert to Confluence Storage Format XHTML:**
40
+ - Run the PlantUML-enabled converter:
41
+ ```powershell
42
+ python scripts/convert_doc_to_confluence.py docs/<app>/<app>-lld.md --output docs/<app>/<app>-lld.html --diagram-type plantuml
28
43
  ```
29
- - Verify that all diagrams are converted to native `<ac:structured-macro ac:name="drawio">` blocks.
44
+ - Verify that all diagrams are converted to native `<ac:structured-macro ac:name="plantuml">` blocks with scale 1.2, natural splines, auto-wrapped edge labels, crimson red borders on impacted components, and embedded Creole legends.
30
45
  - Verify that output validates as well-formed XML.
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: port-to-github
3
+ description: Move (port) an app or repository to GitHub from ANY source - a Gitea/GitLab/any git server URL, a local folder, a network (UNC) share, or a folder on a remote Windows box reached over WinRM - with or without existing git history. Keeps every branch and tag, verifies by SHA, translates Gitea/Forgejo Actions CI, switches deploy checkouts to GitHub with automatic rollback, and retires the old host safely. Use when the user says "move this app to GitHub", "port X to GitHub", "migrate from Gitea", "put this folder on GitHub", "this app only exists on the server", "import the repo from the prod box", or "cut the deploy over to GitHub".
4
+ ---
5
+
6
+ # port-to-github
7
+
8
+ Move one app to GitHub without losing history, leaking a secret, or breaking its deploy.
9
+
10
+ The work is in **six phases**. Each ends in a check that can FAIL. Do not start a phase until
11
+ the one before it passed. Scripts are in `scripts/` next to this file. They are PowerShell 7
12
+ (`pwsh`); the parts that run on a remote box are Windows PowerShell 5.1 compatible.
13
+
14
+ | Phase | Script | Changes anything outside this machine? |
15
+ |---|---|---|
16
+ | 1. Stage the source | `Get-PortSource.ps1` | No. The source is only read. |
17
+ | 2. Readiness | `Test-PortReadiness.ps1` | No. |
18
+ | 3. Publish | `Publish-PortToGitHub.ps1` | **Yes: creates a GitHub repo and pushes.** |
19
+ | 4. CI | `Convert-PortWorkflow.ps1`, `Test-PortWorkflowParses.ps1` | Yes: a feature branch and a PR. |
20
+ | 5. Cutover | `Switch-CheckoutOrigin.ps1` | **Yes: changes where a deploy host pulls from.** |
21
+ | 6. Retire the old host | manual, see `references/old-host.md` | **Yes, and can be destructive.** |
22
+
23
+ **Stop and ask the user before phases 3, 5 and 6.** Each one is outward-facing. Say exactly
24
+ what will be created or changed, on which host, and how to undo it.
25
+
26
+ Not every port needs every phase. A folder that never had CI or a deploy host ends after
27
+ phase 3. Say which phases apply and why, before you start.
28
+
29
+ ## Before you start
30
+
31
+ 1. **Read the project config**, if one exists: `.caddis/port-to-github.json` in the repo you are
32
+ working from (or `$env:PORT_TO_GITHUB_CONFIG`). It sets the GitHub `owner`, `visibility`,
33
+ staging folder, the name for the old remote, dependency hosts and their secrets, and
34
+ `projectNotes` - pointers to the project's own rules. **Read every file `projectNotes` names
35
+ before phase 4.** Project rules beat this skill. The shape is in `references/config.example.json`.
36
+ 2. `gh auth status` must show an account that can create repos in the owner. `git` must be able
37
+ to push to github.com (run `gh auth setup-git` once if not).
38
+ 3. **Find the authoritative copy.** If a folder is a clone of a server, port from the SERVER
39
+ URL, not the folder: the folder may lack branches the server has. Port from a folder only
40
+ when it is the only copy, or when it holds commits the server lacks. Phase 1 reports both
41
+ cases.
42
+
43
+ ## Phase 1 - stage the source
44
+
45
+ ```powershell
46
+ $s = '<path-to-this-skill>/scripts'
47
+ & $s/Get-PortSource.ps1 -Source <source> [-Name <repo-name>] [-IncludeUncommitted]
48
+ ```
49
+
50
+ | Source | Example `-Source` |
51
+ |---|---|
52
+ | any git server | `https://gitea.example/org/app.git`, `git@host:org/app.git` |
53
+ | local folder | `D:\work\app` |
54
+ | network share | `\\fileserver\projects\app` |
55
+ | remote box (WinRM) | `winrm://appserver01/D:/apps/app` (or `-ComputerName appserver01 -Source D:\apps\app`) |
56
+
57
+ What it does, for every kind: builds `<staging>/<Name>/mirror.git` holding ONLY branches and
58
+ tags, removes the remote so nothing can be pushed back to the source, and writes
59
+ `manifest.json`. A folder without history becomes one import commit; `node_modules`, virtual
60
+ envs and caches are skipped, and a `.gitignore` is written if there was none.
61
+
62
+ Read the output. These need a decision, not a shrug:
63
+
64
+ - **Uncommitted changes** in a source checkout are left out unless you pass
65
+ `-IncludeUncommitted`, which puts them on a separate branch `port/uncommitted-snapshot-*`.
66
+ The real branches stay exactly as committed. Ask the user which they want.
67
+ - **Remote-only branches**: branches the checkout only knows as `origin/x`. If the checkout is
68
+ the only copy, re-run with `-PromoteRemoteBranches origin`. If a server has them, port from it.
69
+ - **Likely secrets** in a plain folder or a snapshot stop the script (exit 2). Nothing has left
70
+ the machine. Show the user the list. Re-run with `-OnSecret Exclude` only after they agree
71
+ those files must not be published.
72
+ - **Dropped refs** (`refs/pull/*`, `refs/remotes/*`, notes) are expected. GitHub refuses
73
+ `refs/pull/*`, and pull-request discussion does not move with git anyway. If the old host's
74
+ PR and issue history matters, export it before phase 6.
75
+
76
+ ## Phase 2 - readiness
77
+
78
+ ```powershell
79
+ & $s/Test-PortReadiness.ps1 -Name <repo-name>
80
+ ```
81
+
82
+ **Blockers (exit 1) must be fixed before phase 3:** a file over 100 MB anywhere in history,
83
+ Git LFS content, a likely live credential in the current tree, an empty `${{ }}` in a file
84
+ under `.github/workflows`. A credential found in the tree must be **rotated**, not only
85
+ deleted: history keeps it. (An empty `${{ }}` in a `.gitea/` workflow is only a warning here:
86
+ GitHub does not read that folder, so it matters in phase 4.)
87
+
88
+ **Branches that already contain `.github/workflows` run them the moment they are pushed** -
89
+ deploy jobs included. Readiness lists those branches; publish refuses them unless you pass
90
+ `-AllowWorkflowRuns`, which you do only after reading every such workflow with the user.
91
+
92
+ **Warnings are decisions.** Go through each with the user: secret-type files in past history
93
+ (the new repo will expose them), CI credentials, dependency hosts, runner labels, files that
94
+ still name the old host. The report is in `readiness.json`.
95
+
96
+ ## Phase 3 - publish (ask first)
97
+
98
+ Tell the user: the repo name, owner, visibility, branch and tag counts. Then:
99
+
100
+ ```powershell
101
+ & $s/Publish-PortToGitHub.ps1 -Name <repo-name> [-Owner <org>] [-Visibility private]
102
+ ```
103
+
104
+ It first checks the stage still holds exactly the refs phase 1 recorded (anything else would be
105
+ pushed and then "verified" against itself), creates the repo EMPTY, pushes `refs/heads/*` and
106
+ `refs/tags/*` with an explicit refspec, sets the default branch, and compares every ref by SHA
107
+ (exit 1 on any difference). Do not run other git commands inside the stage; if you did,
108
+ rebuild it with `Get-PortSource.ps1 -Force`.
109
+
110
+ - **Never** `git push --mirror`: it tries to push hidden refs, fails AFTER pushing the rest (so
111
+ a populated repo reports failure), and deletes anything on GitHub the stage lacks.
112
+ - **An existing repo.** If one exists with history, the script refuses. Find out whether it
113
+ shares history with the source: for a few of its commit SHAs, `git -C mirror.git cat-file -e
114
+ <sha>^{commit}`. If it shares nothing (for example a snapshot someone uploaded with a fresh
115
+ "Initial commit"), pushing on top welds a fake ancestry in forever: save anything of value
116
+ from it, delete it, and re-run. Use `-AllowExisting` only when it is a genuine older copy; the
117
+ script then requires every GitHub branch to be an ancestor of the staged one.
118
+ - Verify with the SHA comparison, never by counting refs or by "the push said OK".
119
+
120
+ Stop here if the app has no CI and no deploy host. Otherwise continue.
121
+
122
+ ## Phase 4 - CI
123
+
124
+ Work in a normal clone of the NEW repo, on a feature branch.
125
+
126
+ ```powershell
127
+ git clone https://github.com/<owner>/<repo>.git; cd <repo>; git switch -c port/github-actions
128
+ & $s/Convert-PortWorkflow.ps1 -AddPermissions [-SourceRef <sha>]
129
+ ```
130
+
131
+ It copies `.gitea/workflows` (or `.forgejo/workflows`) to `.github/workflows`, rewrites the
132
+ `gitea.` context to `github.`, and prints every credential line. **Leave the scripts the
133
+ workflows call where they are** - GitHub only needs the workflow file under `.github/`.
134
+
135
+ Then do by hand, one line at a time - this is the whole job, and a blanket swap breaks it:
136
+
137
+ 1. **Classify every credential line.** It faces ORIGIN (checkout, fetch, reset, pushing a tag
138
+ back to this repo) or it names a DEPENDENCY host (a package registry, a library pinned from
139
+ another repo or server). There is no third category.
140
+ - ORIGIN -> `${{ github.token }}`, username `x-access-token`. This includes an
141
+ `actions/checkout` that pushes tags - it is easy to miss.
142
+ - DEPENDENCY -> a secret valid for THAT host. `github.token` reads only the repo whose
143
+ workflow is running, so a library in ANOTHER GitHub repo needs its own read token.
144
+ One job can hold two credential pairs that serve different hosts. Change only the pair
145
+ that faces origin.
146
+ 2. **One Authorization header per host.** `http.<url>.extraheader` values ACCUMULATE; two for the
147
+ same host is an HTTP 400. `actions/checkout` persists its own header, so a step that adds
148
+ another for github.com must unset the checkout's for that command and restore it after.
149
+ 3. **npm and GitHub dependencies:** npm fetches a GitHub dependency over SSH even when
150
+ `package.json` says `https`. Set `url.https://github.com/.insteadOf=ssh://git@github.com/`
151
+ and `GIT_TERMINAL_PROMPT=0` so a missing credential fails in seconds instead of hanging.
152
+ 4. **Runner labels** must exist on GitHub (`runs-on:`). Self-hosted runners must be registered
153
+ to the org or repo, not only to the old host.
154
+ 5. **Never write `${{ }}` in any text in a workflow**, including comments inside `run:` blocks.
155
+ 6. **If a workflow was frozen** on the old host (triggers reduced to `workflow_dispatch`), pass
156
+ `-SourceRef` with the last commit before the freeze. A workflow built from a frozen copy
157
+ fires on nothing, which looks like "still queued", not like a fault.
158
+
159
+ Details and the credential patterns: `references/ci-and-credentials.md`.
160
+
161
+ **Validate before anything reaches `main`.** Commit the workflow files BY NAME, push the branch,
162
+ then:
163
+
164
+ ```powershell
165
+ & $s/Test-PortWorkflowParses.ps1 -Owner <owner> -Repo <repo> -Branch port/github-actions
166
+ ```
167
+
168
+ An unparseable workflow produces a failed run with zero jobs even on a branch its triggers
169
+ ignore; a valid one produces no run (or a normal run). A broken workflow on `main` leaves the
170
+ app with no deploy path, so this check is not optional. Then open a PR and let the real jobs
171
+ run; merging follows the repo's normal rules.
172
+
173
+ ## Phase 5 - cutover (ask first)
174
+
175
+ Only for apps with a deploy host whose checkout pulls from the old origin. Order matters:
176
+
177
+ 1. `Switch-CheckoutOrigin.ps1 -Mode Inspect` on each host. Record what it prints.
178
+ 2. **Tell the user** the host, path, old and new URL, and the rollback command.
179
+ 3. Switch:
180
+ ```powershell
181
+ & $s/Switch-CheckoutOrigin.ps1 -Path D:\apps\app [-ComputerName appserver01] -Mode Switch `
182
+ -NewUrl https://github.com/<owner>/<repo>.git -VerifyWithGhToken
183
+ ```
184
+ It records the old URL first, keeps it as a second remote, fetches, and requires the
185
+ checkout's HEAD to be on a GitHub branch. On failure it restores the old origin itself.
186
+ `-VerifyWithGhToken` lends your gh token to that one fetch, in memory only, because a deploy
187
+ host normally has no GitHub credential of its own (CI supplies one per run).
188
+ 4. **Stop the old host from deploying** (disable its deploy job) only AFTER the first GitHub
189
+ deploy is green. Two schedulers on one checkout have no lock between them.
190
+ 5. **Verify the deploy by an artefact changing** - a built bundle's hash or file time - never by
191
+ an HTTP 200 or a version endpoint alone. Both pass when a new backend serves an old frontend.
192
+
193
+ Rollback, at any point before phase 6:
194
+
195
+ ```powershell
196
+ & $s/Switch-CheckoutOrigin.ps1 -Path D:\apps\app [-ComputerName appserver01] -Mode Rollback
197
+ ```
198
+
199
+ Also repoint developer checkouts the same way (`-Mode Switch` without `-VerifyWithGhToken`), so
200
+ `git push` goes to GitHub. Keep the old remote; it is the local rollback.
201
+
202
+ ## Phase 6 - retire the old host (ask first)
203
+
204
+ Read `references/old-host.md`. The short version:
205
+
206
+ - Prove nothing is lost first:
207
+ `& $s/Compare-PortRefs.ps1 -Left <old-url> -Right <github-url> -Mode LeftInRight` must exit 0.
208
+ Push any ref that exists only on the old host before you continue.
209
+ - Prefer a **read-only pull mirror** of GitHub on the old host (Actions off) over deleting it:
210
+ anything still pinned to the old URL keeps working. Never a push mirror from CI.
211
+ - Anything that still pins a dependency from the old host keeps the old host alive. Find those
212
+ pins before retiring it.
213
+
214
+ ## Report
215
+
216
+ End with a short table per app: source kind, branches and tags ported (SHA-verified yes/no),
217
+ warnings accepted and by whom, CI state, cutover state and rollback command, what is left.
218
+ Say which phases were skipped and why.
219
+
220
+ ## Rules that apply to every phase
221
+
222
+ - Stage files BY NAME. The only `git add -A` is inside the scripts, on fresh throwaway trees.
223
+ - A check that cannot fail is not a check. Every script here exits non-zero on the failure it
224
+ exists for; do not pipe it to `Out-Null` or read only its last line.
225
+ - Audit the default branch on the SERVER (`origin/main`), never a working tree that may sit on
226
+ an old feature branch.
227
+ - Code that runs on a remote box runs under Windows PowerShell 5.1. There, with
228
+ `$ErrorActionPreference='Stop'`, a native command's stderr is a terminating error even with
229
+ `2>$null`. Use `'Continue'` and judge native commands by `$LASTEXITCODE`.
230
+ - One variable at a time. When a whole batch fails the same way, change one thing and retry
231
+ one item before re-reading the request.
@@ -0,0 +1,66 @@
1
+ # CI and credentials after a port
2
+
3
+ Every item here broke a real deploy at least once.
4
+
5
+ ## The two kinds of credential
6
+
7
+ | Faces | Examples | On GitHub |
8
+ |---|---|---|
9
+ | ORIGIN - this repo | `actions/checkout`, a deploy script's `git fetch`/`reset`, pushing a version tag back | `${{ github.token }}`, username `x-access-token`. Needs `permissions: contents: write` to push. Nothing to store or rotate. |
10
+ | DEPENDENCY - another host or repo | a library pinned `git+https://host/org/lib.git@v1`, a private npm/PyPI registry, a Git server that stays behind | A secret that can read THAT host. `github.token` cannot read a different repo, even in the same org. |
11
+
12
+ Choose the credential **per host**, from the host named in the pin, never from where the app is
13
+ hosted. When a pin moves from the old server to GitHub, its credential must move with it in the
14
+ same commit; when an app moves but its pins do not, the old credential must stay.
15
+
16
+ A deploy script that builds its auth header from `git remote get-url origin` needs NO change
17
+ for the origin half: repointing origin (phase 5) is the whole cutover. It DOES need a change if
18
+ it also uses that origin-derived credential for dependency clones - the moment origin is
19
+ GitHub and the dependency is not, that pairing is wrong.
20
+
21
+ ## Injecting a credential for a child process (pip, npm)
22
+
23
+ `GIT_CONFIG_*` environment entries (git 2.31+) are inherited by every git a tool spawns:
24
+
25
+ ```yaml
26
+ env:
27
+ GIT_CONFIG_COUNT: '2'
28
+ GIT_CONFIG_KEY_0: http.https://github.com/.extraheader
29
+ GIT_CONFIG_VALUE_0: AUTHORIZATION: basic <base64 of x-access-token:TOKEN>
30
+ GIT_CONFIG_KEY_1: http.https://old-host.example/.extraheader
31
+ GIT_CONFIG_VALUE_1: AUTHORIZATION: basic <base64 of user:OLD_HOST_TOKEN>
32
+ ```
33
+
34
+ Add a new entry and increment the count. Never replace entry 0 when another host still needs
35
+ it. Build the base64 in a script step from `env:`; never put a token in a URL - it lands in
36
+ lockfiles and logs.
37
+
38
+ ## Traps
39
+
40
+ - **Header accumulation.** `http.<url>.extraheader` never overrides; git sends every matching
41
+ one. `actions/checkout` persists one in `.git/config`. A second for the same host -> HTTP 400
42
+ `Duplicate header: "Authorization"`. Unset the checkout's entry for the one command that
43
+ needs a different credential, then restore it; otherwise every later step loses its auth.
44
+ - **`git config --local` outside a repo fails.** A runner's temp working directory is not a
45
+ repository. Check `git rev-parse --is-inside-work-tree` first.
46
+ - **npm uses SSH for GitHub deps** despite `https` in `package.json` and the lockfile. Add
47
+ `url.https://github.com/.insteadOf ssh://git@github.com/`. Also make sure the lockfile's
48
+ `resolved` URLs say `git+https`, not `git+ssh`.
49
+ - **A cache hit is not proof of credentials.** A warm npm or pip cache skips the authenticated
50
+ fetch entirely. Prove a new credential with a fresh install (cleared cache, empty `HOME`, no
51
+ credential helper).
52
+ - **Windows PowerShell 5.1 runners.** With `$ErrorActionPreference='Stop'`, anything a native
53
+ exe writes to stderr becomes a terminating error, and `2>$null` does not prevent it. Use
54
+ `'Continue'` in the step, `2>&1 | Out-Null` to silence, and judge by `$LASTEXITCODE`.
55
+ - **A step whose last native command is EXPECTED to fail** (a negative test) must end with
56
+ `exit 0`, or the step is red after printing its pass.
57
+ - **Required checks.** A job skipped by a path filter at the TRIGGER never reports, so making it
58
+ a required check blocks every PR that does not touch those paths. A job skipped by `if:` counts
59
+ as passed. Renaming a job renames its check context and silently breaks a branch rule.
60
+ - **`concurrency: cancel-in-progress`** cancels a PR's running jobs when you push again. A
61
+ `cancelled` job or a PR with no runs is often this, not a fault. Re-check against the new
62
+ head SHA.
63
+ - **Deploy scripts may run from the deploy checkout**, not from the commit being deployed. If the
64
+ workflow syncs that checkout to the target commit before calling the script, a script change
65
+ takes effect on the first deploy; if it does not, it takes effect on the NEXT deploy. Check
66
+ which before predicting.
@@ -0,0 +1,18 @@
1
+ {
2
+ "_comment": "Copy to .caddis/port-to-github.json in the repo you run the port from. Every field is optional.",
3
+ "owner": "example-org",
4
+ "visibility": "private",
5
+ "stagingRoot": "%TEMP%\\port-to-github",
6
+ "oldRemoteName": "gitea",
7
+ "dependencyHosts": [
8
+ { "host": "github.com", "username": "x-access-token", "secret": "ORG_READ_TOKEN", "note": "libraries in other repos of the org" },
9
+ { "host": "git.example.internal", "username": "ci-bot", "secret": "OLD_HOST_READ_TOKEN", "note": "stays until nothing pins from it" }
10
+ ],
11
+ "deployHosts": [
12
+ { "computerName": "appserver01", "checkoutRoot": "D:\\apps" }
13
+ ],
14
+ "projectNotes": [
15
+ "AGENTS.md - the project's own rules; they override this skill",
16
+ "docs/runbooks/<your-migration-runbook>.md"
17
+ ]
18
+ }
@@ -0,0 +1,61 @@
1
+ # Retiring the old host
2
+
3
+ Only after: the app is on GitHub, SHA-verified, and its first GitHub deploy was green and
4
+ verified by an artefact changing.
5
+
6
+ ## 1. Prove nothing is lost
7
+
8
+ ```powershell
9
+ & <skill>/scripts/Compare-PortRefs.ps1 -Left <old-url> -Right https://github.com/<owner>/<repo>.git `
10
+ -Mode LeftInRight -LeftLabel old -RightLabel github
11
+ ```
12
+
13
+ Exit 0 means every branch and tag on the old host exists on GitHub with the same SHA. If not,
14
+ push the missing refs to GitHub first (from a fresh `git clone --mirror` of the old host, with
15
+ an explicit refspec for just those refs) and run it again. Release tags pushed by the old CI
16
+ after the port are the usual stragglers.
17
+
18
+ ## 2. Find what still depends on the old host
19
+
20
+ Search every consumer's default branch (not working trees) for the old host name: dependency
21
+ pins, registry config, CI steps, deploy scripts. Anything found keeps the old host alive, and
22
+ keeps a credential for it alive.
23
+
24
+ ## 3. Choose: read-only mirror, archive, or delete
25
+
26
+ | Option | When | Cost |
27
+ |---|---|---|
28
+ | **Read-only pull mirror of GitHub** (Actions OFF) | Something still pins from the old URL, or you want a live fallback | The old host keeps running; a mirror that stops pulling goes stale silently |
29
+ | Archive (read-only, no sync) | Nothing depends on it; you want the old PR/issue history browsable | A frozen snapshot that drifts from day one |
30
+ | Delete | Nothing depends on it and its history is exported | Irreversible |
31
+
32
+ **Never a push mirror driven by CI.** A job pushing into a host that then runs CI on what
33
+ arrives has caused a full outage: tests failed, deploy was skipped (not failed), the push guard
34
+ only checked `failure`, and it pushed a change disabling the old deploy - leaving no deploy path
35
+ anywhere. A pull mirror with Actions disabled cannot do that.
36
+
37
+ ## 4. Converting a Gitea repo into a pull mirror
38
+
39
+ An existing Gitea repo cannot be converted in place. It must be deleted and recreated as a
40
+ mirror under the same path, which **destroys its pull requests and issues** - export them first
41
+ (API: `/repos/{owner}/{repo}/pulls?state=all`, `/issues?state=all`, comments per item).
42
+
43
+ 1. Run step 1 again immediately before, and export PRs/issues.
44
+ 2. `DELETE /api/v1/repos/{owner}/{repo}`.
45
+ 3. **Verify the delete on BOTH halves: the API returns 404 AND the repository directory is gone
46
+ on disk.** Gitea can remove the database row and fail on disk (a locked pack file). The next
47
+ create then returns 500 while still making an EMPTY mirror row, on which `mirror-sync`
48
+ returns 400. The Gitea log names the locked file; nothing else does.
49
+ 4. `POST /api/v1/repos/migrate` with `mirror: true`, the GitHub clone URL, and a GitHub read
50
+ token (`auth_token`). Then disable Actions on it (`has_actions: false`).
51
+ 5. Verify by refs, not by existence: `Compare-PortRefs.ps1 -Left <github> -Right <gitea> -Mode Equal`.
52
+ 6. If something pins from the mirror, prove it: clone the exact pinned tag from the mirror.
53
+
54
+ ## 5. Watching a mirror
55
+
56
+ A pull mirror syncs on an interval (Gitea default 8h). A mirror behind GitHub is either LAGGING
57
+ (the GitHub commit is newer than the mirror's last pull - normal) or STALE (the commit was there
58
+ at the last pull and still did not arrive - the stored credential probably expired). Decide
59
+ which by comparing the GitHub commit time with the mirror's last-update time before you touch
60
+ any credential. `POST /repos/{owner}/{repo}/mirror-sync` forces a pull: an immediate match means
61
+ it was lag.
@@ -0,0 +1,68 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Compare branches and tags between two git locations by SHA. The only honest "did it copy" check.
4
+
5
+ .DESCRIPTION
6
+ -Left and -Right can each be a URL, a local path, a UNC path or a bare repository.
7
+ Uses git ls-remote, so nothing is fetched or changed.
8
+
9
+ Modes:
10
+ Equal every head and tag matches on both sides (after a push)
11
+ LeftInRight every Left ref exists in Right with the same SHA; Right may have more
12
+ (before destroying an old host: nothing on the old host may be lost)
13
+
14
+ Exit 0 when the mode holds, 1 when it does not, and the differing refs are printed.
15
+ #>
16
+ [CmdletBinding()]
17
+ param(
18
+ [Parameter(Mandatory)][string]$Left,
19
+ [Parameter(Mandatory)][string]$Right,
20
+ [ValidateSet('Equal', 'LeftInRight')][string]$Mode = 'Equal',
21
+ [string]$LeftLabel = 'left',
22
+ [string]$RightLabel = 'right',
23
+ [switch]$PassThru
24
+ )
25
+
26
+ $ErrorActionPreference = 'Stop'
27
+ . (Join-Path $PSScriptRoot 'PortCommon.ps1')
28
+
29
+ function Get-Refs([string]$Where) {
30
+ $lines = Invoke-PortGit @('ls-remote', '--heads', '--tags', $Where)
31
+ $map = @{}
32
+ foreach ($l in $lines) {
33
+ if (-not $l) { continue }
34
+ $sha, $ref = $l -split "`t", 2
35
+ # An annotated tag shows twice: the tag object and the peeled commit (^{}). Compare the
36
+ # tag object; the peeled line is derived from it.
37
+ if ($ref -like '*^{}') { continue }
38
+ $map[$ref] = $sha
39
+ }
40
+ return $map
41
+ }
42
+
43
+ $l = Get-Refs $Left
44
+ $r = Get-Refs $Right
45
+
46
+ $onlyLeft = @($l.Keys | Where-Object { -not $r.ContainsKey($_) } | Sort-Object)
47
+ $onlyRight = @($r.Keys | Where-Object { -not $l.ContainsKey($_) } | Sort-Object)
48
+ $differ = @($l.Keys | Where-Object { $r.ContainsKey($_) -and $r[$_] -ne $l[$_] } | Sort-Object)
49
+
50
+ $ok = ($onlyLeft.Count -eq 0 -and $differ.Count -eq 0)
51
+ if ($Mode -eq 'Equal') { $ok = $ok -and ($onlyRight.Count -eq 0) }
52
+
53
+ Write-Host ("{0}: {1} refs {2}: {3} refs only-{0}: {4} only-{2}: {5} differ: {6}" -f $LeftLabel, $l.Count, $RightLabel, $r.Count, $onlyLeft.Count, $onlyRight.Count, $differ.Count)
54
+ foreach ($x in $onlyLeft) { Write-Host " only in ${LeftLabel}: $x" }
55
+ if ($Mode -eq 'Equal') { foreach ($x in $onlyRight) { Write-Host " only in ${RightLabel}: $x" } }
56
+ foreach ($x in $differ) { Write-Host " differs: $x $($l[$x].Substring(0,12)) vs $($r[$x].Substring(0,12))" }
57
+
58
+ if ($l.Count -eq 0) {
59
+ Write-PortFail "$LeftLabel has no refs at all. An empty side cannot prove anything."
60
+ $ok = $false
61
+ }
62
+ if ($ok) { Write-PortOk "Refs agree ($Mode)." } else { Write-PortFail "Refs do NOT agree ($Mode)." }
63
+
64
+ if ($PassThru) {
65
+ [pscustomobject]@{ Ok = $ok; OnlyLeft = $onlyLeft; OnlyRight = $onlyRight; Differ = $differ; LeftCount = $l.Count; RightCount = $r.Count }
66
+ }
67
+ if (-not $ok) { exit 1 }
68
+ exit 0