From ade5184bbdd3e51df81384dcbff81c5c97d96a42 Mon Sep 17 00:00:00 2001 From: Stephen Zweibel Date: Mon, 28 Sep 2026 13:37:53 -0400 Subject: [PATCH] Install the shared CAIL packages from public npm cail-log 0.6.4, cail-identity 5.2.6 and cail-client 6.2.2 are now on the public npm registry, so neither CI nor the release host needs a GitHub Packages credential to install them. Map the @cuny-ai-lab scope to registry.npmjs.org in .npmrc, regenerate pnpm-lock.yaml against npm, and remove the token plumbing: the CI registry setup and NODE_AUTH_TOKEN, the packages: read permissions, and the release host's temporary .npmrc that fed the SSH-supplied token to pnpm. The host still uses that token to verify the release source, and now asserts the tracked .npmrc points at npm. NODE_AUTH_TOKEN is no longer a deployment credential, so the production-environment contract stops listing it. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 5 ----- .github/workflows/deploy.yml | 1 - .npmrc | 2 +- README.md | 2 +- pnpm-lock.yaml | 6 +++--- scripts/release/contract.mjs | 2 +- scripts/release/contract.test.mjs | 2 +- scripts/release/host.mjs | 13 +++++-------- 8 files changed, 12 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c64dec..1067c17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,6 @@ on: permissions: contents: read - packages: read concurrency: group: slide-maker-ci-${{ github.workflow }}-${{ github.ref }} @@ -35,13 +34,9 @@ jobs: with: node-version: '22.23.1' cache: pnpm - registry-url: https://npm.pkg.github.com - scope: "@cuny-ai-lab" - name: Install dependencies run: pnpm install --frozen-lockfile - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check frontend types run: pnpm --filter @slide-maker/web check diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0d4bea0..af29b3a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,6 @@ permissions: contents: read actions: read deployments: read - packages: read concurrency: group: slide-maker-production cancel-in-progress: false diff --git a/.npmrc b/.npmrc index 51f4345..f708d1d 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -@cuny-ai-lab:registry=https://npm.pkg.github.com +@cuny-ai-lab:registry=https://registry.npmjs.org diff --git a/README.md b/README.md index 8deb62d..df6acfa 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Open http://localhost:5173, log in with a seeded admin account, create a deck, a 1. **Install dependencies** - The pinned CAIL packages require authorized GitHub Packages read access. The checked-in `.npmrc` contains registry resolution only; supply credentials through your user configuration or CI environment. + The pinned CAIL packages install from public npm with no token. The checked-in `.npmrc` maps the `@cuny-ai-lab` scope to `registry.npmjs.org`, so a user configuration that still points it at GitHub Packages does not redirect the install. ```bash pnpm install diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4691403..7124e52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -305,15 +305,15 @@ packages: resolution: {integrity: sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==} '@cuny-ai-lab/cail-client@6.2.2': - resolution: {integrity: sha512-5Pcdu++6cHKjlBRVC/JtRPORzd6qE3LbR/0qnhbzTmLrZASUibFOQZ3Xbh7p1JQNMqF1H5DSkKXwhMGd324BWw==, tarball: https://npm.pkg.github.com/download/@cuny-ai-lab/cail-client/6.2.2/6efb4d2e477e255737f44899d98c606099c61ac1} + resolution: {integrity: sha512-4aBjULb7Bd7O8kBT7lrM5OGC7sTHkTp9Lc/5mMpK9f+QRSCkEK+eUCJjl/+ZPRdXoOk+XpyUdLZB8OtUvE5+xQ==} engines: {node: '>=20'} '@cuny-ai-lab/cail-identity@5.2.6': - resolution: {integrity: sha512-eaSX7lfSzZLqceTwiqkfz0I9EwFXHch3dNlpoHy+EzbHHRKwBz45mJGBpaK6znp7BAUJiJDffTw7t10JR+pslQ==, tarball: https://npm.pkg.github.com/download/@cuny-ai-lab/cail-identity/5.2.6/9744f7c35813cda8e6f07804a5d99fc1250e42f6} + resolution: {integrity: sha512-j4Klru5ivG6ZWoGQBvIIiI2v7EDwDPiDIOvzsOV73n7LrUj3deCXM4tzFTfhZntaYdonFh4lJ9xs933YwNRnNA==} engines: {node: '>=20'} '@cuny-ai-lab/cail-log@0.6.4': - resolution: {integrity: sha512-HnHbutsDhZyBD/SD8YFSITycujuX1Lhc1xHaELEemk2dYKPA5IF5fFlwyfkIqsZiCpaJdWo7H2QPQnD2uHXmYQ==, tarball: https://npm.pkg.github.com/download/@cuny-ai-lab/cail-log/0.6.4/7d30c2cf3e0b915eef23a2348cc5d3685cb208cc} + resolution: {integrity: sha512-j/YEmBC1hi/64rYTmHp8sawk5xJCQMCBU0F2ZllFIMbXMEG3mq0yF5SYo9sgqjn0MHy+ZQLdE2Svbz/oUbOTRA==} engines: {node: '>=20'} '@daybrush/utils@1.13.0': diff --git a/scripts/release/contract.mjs b/scripts/release/contract.mjs index 1167c0c..49e509f 100644 --- a/scripts/release/contract.mjs +++ b/scripts/release/contract.mjs @@ -20,7 +20,7 @@ export function validateConfiguration(env) { if (env.CAIL_IDENTITY_ISSUER !== 'https://tools.ailab.gc.cuny.edu/cail-sso' || env.CAIL_GATEWAY_URL !== 'https://tools.ailab.gc.cuny.edu' || env.PUBLIC_URL !== 'https://tools.ailab.gc.cuny.edu/slide-maker') throw new Error('Production identity, Gateway or public URL is not canonical.'); - for (const key of ['NODE_OPTIONS', 'NODE_PATH', 'NODE_AUTH_TOKEN', 'GITHUB_TOKEN', 'NPM_CONFIG_USERCONFIG', 'PM2_HOME']) { + for (const key of ['NODE_OPTIONS', 'NODE_PATH', 'GITHUB_TOKEN', 'NPM_CONFIG_USERCONFIG', 'PM2_HOME']) { if (env[key]) throw new Error(`Remove deployment-only or runtime-override setting ${key} from the application environment.`); } const database = env.DATABASE_URL?.startsWith('file:') ? env.DATABASE_URL.slice(5) : ''; diff --git a/scripts/release/contract.test.mjs b/scripts/release/contract.test.mjs index ac53d3e..ab92630 100644 --- a/scripts/release/contract.test.mjs +++ b/scripts/release/contract.test.mjs @@ -23,7 +23,7 @@ test('state paths must be explicit, separate and outside deployment storage', () test('application configuration cannot silently replace fleet endpoints or retain CI credentials', () => { for (const patch of [ { CAIL_GATEWAY_URL: 'https://tools.ailab.gc.cuny.edu/v1' }, { CAIL_IDENTITY_ISSUER: 'https://wrong.test' }, - { PUBLIC_URL: 'http://localhost:4173' }, { READINESS_TOKEN: '' }, { GITHUB_TOKEN: 'fixture' }, { NODE_AUTH_TOKEN: 'fixture' }, { NODE_OPTIONS: '--inspect=0.0.0.0' }, + { PUBLIC_URL: 'http://localhost:4173' }, { READINESS_TOKEN: '' }, { GITHUB_TOKEN: 'fixture' }, { NODE_OPTIONS: '--inspect=0.0.0.0' }, ]) assert.throws(() => validateConfiguration({ ...environment, ...patch })); }); test('rollback captures exactly one online process per component and preserves existing arguments and environment', () => { diff --git a/scripts/release/host.mjs b/scripts/release/host.mjs index fb6f0ee..7400869 100644 --- a/scripts/release/host.mjs +++ b/scripts/release/host.mjs @@ -1,5 +1,5 @@ import { execFileSync } from 'node:child_process'; -import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs'; +import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, symlinkSync, writeFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; import { parseEnv } from 'node:util'; import { pathToFileURL } from 'node:url'; @@ -46,13 +46,10 @@ export async function main() { if (command('pm2', ['--version']).trim() !== '7.0.4') throw new Error('Release host requires the tested PM2 CLI 7.0.4.'); command('lsof', ['-v']); await verifySource(); - // Install output stays private. The temporary credential is never written or - // included in PM2 environment; the only config file contains a placeholder. - if (readFileSync(join(root, '.npmrc'), 'utf8').trim() !== '@cuny-ai-lab:registry=https://npm.pkg.github.com') throw new Error('Unexpected tracked npm configuration.'); - const npmrc = join(root, '.release-npmrc'); - writeFileSync(npmrc, '@cuny-ai-lab:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n', { mode: 0o600, flag: 'wx' }); - try { command('pnpm', ['install', '--frozen-lockfile'], { timeout: 600000, env: { ...baseEnv, NODE_AUTH_TOKEN: sourceToken, NPM_CONFIG_USERCONFIG: npmrc } }); } - finally { rmSync(npmrc, { force: true }); } + // Install output stays private. The CAIL packages come from public npm, so + // the install needs no credential; the tracked .npmrc pins their scope there. + if (readFileSync(join(root, '.npmrc'), 'utf8').trim() !== '@cuny-ai-lab:registry=https://registry.npmjs.org') throw new Error('Unexpected tracked npm configuration.'); + command('pnpm', ['install', '--frozen-lockfile'], { timeout: 600000 }); command('pnpm', ['build'], { timeout: 600000, env: { ...baseEnv, NODE_ENV: 'production' } }); const apiDir = join(root, 'apps/api'); const node = (args, extraEnv = {}) => command(process.execPath, ['--import', 'tsx', ...args], { cwd: apiDir, env: { ...appEnv, ...extraEnv } });