1+ # =============================================================
2+ # ARCHIVAL MIRROR ONLY - DO NOT RUN FROM THE PRIVATE REPO
3+ # =============================================================
4+ # The live release workflow runs on the public repo at
5+ # https://github.com/OpenAnalystInc/cli/.github/workflows/release.yml
6+ # This copy is kept under version control here so the private
7+ # repo always has a synced record of the CI pipeline that ships
8+ # its source. To keep this from auto-triggering on tag pushes
9+ # and burning Actions minutes against the private repo, the
10+ # workflow is disabled at the GitHub Actions level
11+ # (`gh workflow disable release.yml -R OpenAnalystInc/openanalyst-cli`).
12+ # If you ever edit this file, mirror the change to the public
13+ # repo with the same diff so the two stay byte-identical.
14+ # =============================================================
15+
116name : Release
217
318# This workflow runs ON the public repo (OpenAnalystInc/cli) so it
@@ -6,13 +21,13 @@ name: Release
621# clones it into the runner's ephemeral filesystem with a PAT
722# (PRIVATE_REPO_TOKEN secret), builds, attaches binaries to a
823# release here, and publishes to npm. Source never persists in the
9- # public repo — the runner is destroyed after each job.
24+ # public repo — the runner is destroyed after each job.
1025#
1126# Trigger options:
12- # 1. `workflow_dispatch` from the Actions tab — supply the version
27+ # 1. `workflow_dispatch` from the Actions tab — supply the version
1328# manually. Use this for the first publish, or any time you
1429# want to rebuild a specific tag.
15- # 2. Push a `v*` tag to this public repo — re-runs the same flow
30+ # 2. Push a `v*` tag to this public repo — re-runs the same flow
1631# for that tag.
1732
1833on :
88103 runs-on : ${{ matrix.os }}
89104 steps :
90105 # Clone the private repo (the source of truth). The PAT lives
91- # in PRIVATE_REPO_TOKEN — set it once at repo-secret level on
106+ # in PRIVATE_REPO_TOKEN — set it once at repo-secret level on
92107 # this public repo.
93108 - uses : actions/checkout@v4
94109 with :
@@ -165,7 +180,7 @@ jobs:
165180 echo "$LEAKS"
166181 exit 1
167182 fi
168- echo "OK — only binary artifacts present."
183+ echo "OK — only binary artifacts present."
169184
170185 # Defense-in-depth: docs and Markdown must never reach the
171186 # public release. The Package step only copies binaries
@@ -177,7 +192,7 @@ jobs:
177192 echo "$DOC_LEAKS"
178193 exit 1
179194 fi
180- echo "OK — no docs in release artifacts."
195+ echo "OK — no docs in release artifacts."
181196
182197 # Belt-and-suspenders: scan every staged file for literal
183198 # API-key shapes. The CLI never embeds credentials, but if
@@ -194,7 +209,7 @@ jobs:
194209 echo "$SECRETS" | head -20
195210 exit 1
196211 fi
197- echo "OK — no API-key shapes in release artifacts."
212+ echo "OK — no API-key shapes in release artifacts."
198213
199214 - name : Create or update the release on this (public) repo
200215 env :
@@ -229,14 +244,27 @@ jobs:
229244 node-version : ${{ env.NODE_VERSION }}
230245 registry-url : https://registry.npmjs.org/
231246
247+ - uses : actions/download-artifact@v4
248+ with :
249+ path : artifacts
250+ merge-multiple : true
251+
232252 - name : Install dependencies
233253 working-directory : source
234- run : npm ci
254+ run : npm ci --omit=optional
235255
236256 - name : Build Ink TUI
237257 working-directory : source
238258 run : npm run build --prefix ink-tui
239259
260+ - name : Sync platform optional dependencies
261+ working-directory : source
262+ run : node scripts/sync-platform-optional-deps.js
263+
264+ - name : Stage platform npm packages
265+ working-directory : source
266+ run : node scripts/stage-npm-platform-packages.js --artifacts ../artifacts --out ../platform-npm
267+
240268 # Hard guard: parse `npm pack --dry-run --json` and fail the
241269 # workflow before `npm publish` if any file in the tarball
242270 # looks like source. The `.npmignore` already filters source,
@@ -255,14 +283,13 @@ jobs:
255283 const pack = JSON.parse(fs.readFileSync('/tmp/npm-pack.json', 'utf8'));
256284 const files = (pack[0]?.files ?? []).map(f => f.path);
257285 const forbidden = files.filter(f => {
258- // Allow-list: managed/ is the user-facing skill
259- // catalog (e.g. managed/skills/.../*.py helpers
260- // that ship as runnable skill artifacts, NOT CLI
261- // source). Never flag anything under managed/.
262- if (f.startsWith('managed/')) return false;
286+ // Public npm gets only the package-facing managed README.
287+ // Private hosted deployments can add managed skills/config
288+ // from the private repo or server filesystem.
289+ if (f.startsWith('managed/') && f !== 'managed/README.md') return true;
263290 // Only the minimal public README ships. CHANGELOG and
264291 // any internal docs stay private. Anything else markdown
265- // is treated as a leak — including any `*.internal.md`
292+ // is treated as a leak — including any `*.internal.md`
266293 // file regardless of name.
267294 const isAllowedMd = f === 'README.md';
268295 const isDocLeak = (f.endsWith('.md') && !isAllowedMd)
@@ -292,7 +319,7 @@ jobs:
292319 forbidden.forEach(f => console.error(' ' + f));
293320 process.exit(1);
294321 }
295- console.log('OK — no source files in tarball.');
322+ console.log('OK — no source files in tarball.');
296323 EOF
297324
298325 # Secret-scan: build the real tarball, untar to a temp dir,
@@ -315,7 +342,31 @@ jobs:
315342 exit 1
316343 fi
317344 rm -rf /tmp/pkg-scan "$PKG_TARBALL"
318- echo "OK — no API-key shapes in tarball."
345+ echo "OK — no API-key shapes in tarball."
346+
347+ - name : Publish platform npm packages
348+ shell : bash
349+ env :
350+ NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
351+ run : |
352+ set -euo pipefail
353+ shopt -s nullglob
354+ for tarball in platform-npm/*.tgz; do
355+ echo "Publishing $tarball"
356+ set +e
357+ output="$(npm publish "$tarball" --access public 2>&1)"
358+ status=$?
359+ set -e
360+ echo "$output"
361+ if [ "$status" -eq 0 ]; then
362+ continue
363+ fi
364+ if grep -qiE "previously published|cannot publish over|version already exists" <<< "$output"; then
365+ echo "Skipping already-published platform package: $tarball"
366+ continue
367+ fi
368+ exit "$status"
369+ done
319370
320371 - name : Publish to npm
321372 working-directory : source
0 commit comments