docs(agents): add submit --wait, --check-only, and --json flags #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Publish Packages — publishes @recallnet/* packages to npmjs via trusted | |
| # publishing, mirrors them to GitHub Packages, and creates package-scoped | |
| # GitHub Releases. This workflow must be the direct publishing workflow | |
| # configured in npmjs, so it runs on pushes to main instead of chaining off | |
| # another workflow. | |
| name: Publish Packages | |
| on: | |
| push: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }} | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| # Trusted publishing binds npm packages to this exact workflow file. Keep | |
| # the job enabled for version-commit reruns so partial publish failures can | |
| # be resumed after Changesets has already consumed the pending files. | |
| if: github.repository == 'recallnet/codecontext' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| id-token: write | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: pnpm/action-setup@v2 | |
| with: | |
| version: 10 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: "pnpm" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Upgrade npm for trusted publishing | |
| run: npm install -g npm@latest | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Check for changesets | |
| id: changesets | |
| run: | | |
| output=$(pnpm changeset status 2>&1) && rc=0 || rc=$? | |
| if [ "$rc" -eq 0 ]; then | |
| echo "has_changesets=true" >> "$GITHUB_OUTPUT" | |
| echo "Found pending changesets" | |
| elif echo "$output" | grep -qi "no.*changesets"; then | |
| echo "has_changesets=false" >> "$GITHUB_OUTPUT" | |
| echo "No pending changesets" | |
| else | |
| echo "::error::changeset status failed unexpectedly:" | |
| echo "$output" | |
| exit 1 | |
| fi | |
| - name: Determine release mode | |
| id: release_mode | |
| env: | |
| HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} | |
| run: | | |
| if [ "${{ steps.changesets.outputs.has_changesets }}" = "true" ]; then | |
| echo "mode=fresh" >> "$GITHUB_OUTPUT" | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| echo "Release mode: fresh changeset publish" | |
| elif [ "$HEAD_COMMIT_MESSAGE" = "chore: version packages [skip ci]" ]; then | |
| echo "mode=recovery" >> "$GITHUB_OUTPUT" | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| echo "Release mode: recovery from version commit" | |
| else | |
| echo "mode=none" >> "$GITHUB_OUTPUT" | |
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | |
| echo "Release mode: nothing to publish" | |
| fi | |
| - name: Build packages | |
| if: steps.release_mode.outputs.should_publish == 'true' | |
| run: pnpm build | |
| - name: Version packages | |
| if: steps.release_mode.outputs.mode == 'fresh' | |
| run: pnpm changeset version | |
| - name: Prepare release manifest | |
| if: steps.release_mode.outputs.should_publish == 'true' | |
| id: releases | |
| env: | |
| RELEASE_MODE: ${{ steps.release_mode.outputs.mode }} | |
| run: | | |
| # @context decision: Publish and release steps run from an explicit | |
| # package/version manifest instead of live Changesets state so reruns | |
| # can resume after the version commit has already consumed the | |
| # changeset files. Without this, npmjs-success/GitHub-Packages-failure | |
| # leaves the mirror permanently skipped on retry. | |
| # | |
| # @context risk: New packages may not exist in the base revision. | |
| # Treat a missing previous package.json as "new package" rather than a | |
| # hard failure so first-release automation works. | |
| # | |
| # @context decision: GitHub Releases are package-scoped rather than | |
| # repo-scoped because a single publish run can ship multiple npm | |
| # packages. Collapsing that into one repo release would hide which | |
| # package/version pairs actually shipped. | |
| node <<'EOF' | |
| const fs = require('node:fs'); | |
| const path = require('node:path'); | |
| const { execFileSync } = require('node:child_process'); | |
| const mode = process.env.RELEASE_MODE; | |
| const outputPath = path.join(process.env.RUNNER_TEMP, 'published-releases.json'); | |
| const fileArgs = | |
| mode === 'fresh' | |
| ? ['diff', '--name-only', '--', 'packages/*/package.json'] | |
| : ['diff', '--name-only', 'HEAD^', 'HEAD', '--', 'packages/*/package.json']; | |
| const diff = execFileSync('git', fileArgs, { encoding: 'utf8' }) | |
| .trim() | |
| .split('\n') | |
| .filter(Boolean); | |
| const baseRef = mode === 'fresh' ? 'HEAD' : 'HEAD^'; | |
| const releases = []; | |
| for (const file of diff) { | |
| const after = JSON.parse(fs.readFileSync(file, 'utf8')); | |
| if (!after.name || after.private) { | |
| continue; | |
| } | |
| let before = null; | |
| try { | |
| before = JSON.parse(execFileSync('git', ['show', `${baseRef}:${file}`], { encoding: 'utf8' })); | |
| } catch (error) { | |
| if (error.status !== 128) { | |
| throw error; | |
| } | |
| } | |
| if (before?.version === after.version) { | |
| continue; | |
| } | |
| const shortName = after.name.replace(/^@[^/]+\//, ''); | |
| releases.push({ | |
| packageName: after.name, | |
| version: after.version, | |
| directory: path.dirname(file), | |
| tag: `${shortName}-v${after.version}`, | |
| npmUrl: `https://www.npmjs.com/package/${after.name}/v/${after.version}`, | |
| ghRegistry: 'https://npm.pkg.github.com', | |
| }); | |
| } | |
| fs.writeFileSync(outputPath, JSON.stringify(releases, null, 2) + '\n'); | |
| fs.appendFileSync( | |
| process.env.GITHUB_OUTPUT, | |
| `release_count=${releases.length}\nrelease_manifest=${outputPath}\n`, | |
| ); | |
| EOF | |
| - name: Commit and push version changes | |
| if: steps.release_mode.outputs.mode == 'fresh' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add -A | |
| git commit -m "chore: version packages [skip ci]" || echo "No changes to commit" | |
| git push | |
| - name: Publish packages to npmjs | |
| if: >- | |
| steps.release_mode.outputs.should_publish == 'true' && | |
| steps.releases.outputs.release_count != '0' | |
| env: | |
| NPM_CONFIG_PROVENANCE: "true" | |
| RELEASE_MANIFEST: ${{ steps.releases.outputs.release_manifest }} | |
| run: | | |
| node <<'EOF' | |
| const fs = require('node:fs'); | |
| const { execFileSync } = require('node:child_process'); | |
| const releases = JSON.parse(fs.readFileSync(process.env.RELEASE_MANIFEST, 'utf8')); | |
| for (const release of releases) { | |
| const spec = `${release.packageName}@${release.version}`; | |
| let published = false; | |
| try { | |
| const existing = execFileSync( | |
| 'npm', | |
| ['view', spec, 'version', '--registry=https://registry.npmjs.org'], | |
| { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }, | |
| ).trim(); | |
| published = existing === release.version; | |
| } catch (error) { | |
| if (error.status !== 1) { | |
| throw error; | |
| } | |
| } | |
| if (published) { | |
| console.log(`npmjs already has ${spec}; skipping publish`); | |
| continue; | |
| } | |
| execFileSync( | |
| 'npm', | |
| ['publish', '.', '--provenance', '--registry=https://registry.npmjs.org'], | |
| { cwd: release.directory, stdio: 'inherit' }, | |
| ); | |
| } | |
| EOF | |
| - name: Configure GitHub Packages auth | |
| if: >- | |
| steps.release_mode.outputs.should_publish == 'true' && | |
| steps.releases.outputs.release_count != '0' | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| registry-url: "https://npm.pkg.github.com" | |
| scope: "@recallnet" | |
| - name: Publish packages to GitHub Packages | |
| if: >- | |
| steps.release_mode.outputs.should_publish == 'true' && | |
| steps.releases.outputs.release_count != '0' | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NPM_CONFIG_PROVENANCE: "false" | |
| RELEASE_MANIFEST: ${{ steps.releases.outputs.release_manifest }} | |
| run: | | |
| node <<'EOF' | |
| const fs = require('node:fs'); | |
| const { execFileSync } = require('node:child_process'); | |
| const releases = JSON.parse(fs.readFileSync(process.env.RELEASE_MANIFEST, 'utf8')); | |
| for (const release of releases) { | |
| const spec = `${release.packageName}@${release.version}`; | |
| let published = false; | |
| try { | |
| const existing = execFileSync( | |
| 'npm', | |
| ['view', spec, 'version', '--registry=https://npm.pkg.github.com'], | |
| { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }, | |
| ).trim(); | |
| published = existing === release.version; | |
| } catch (error) { | |
| if (error.status !== 1) { | |
| throw error; | |
| } | |
| } | |
| if (published) { | |
| console.log(`GitHub Packages already has ${spec}; skipping publish`); | |
| continue; | |
| } | |
| execFileSync( | |
| 'npm', | |
| ['publish', '.', '--registry=https://npm.pkg.github.com'], | |
| { cwd: release.directory, stdio: 'inherit' }, | |
| ); | |
| } | |
| EOF | |
| - name: Create GitHub Releases | |
| if: >- | |
| steps.release_mode.outputs.should_publish == 'true' && | |
| steps.releases.outputs.release_count != '0' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| RELEASE_MANIFEST: ${{ steps.releases.outputs.release_manifest }} | |
| run: | | |
| release_target=$(git rev-parse HEAD) | |
| node <<'EOF' > "$RUNNER_TEMP/release-lines.tsv" | |
| const fs = require('node:fs'); | |
| const releases = JSON.parse(fs.readFileSync(process.env.RELEASE_MANIFEST, 'utf8')); | |
| for (const release of releases) { | |
| const body = [ | |
| `${release.packageName} ${release.version} was published to npmjs and mirrored to GitHub Packages from this workflow run.`, | |
| '', | |
| `npm: ${release.npmUrl}`, | |
| `github-packages-registry: ${release.ghRegistry}`, | |
| ].join('\n'); | |
| process.stdout.write([ | |
| release.tag, | |
| release.packageName, | |
| release.version, | |
| Buffer.from(body, 'utf8').toString('base64'), | |
| ].join('\t') + '\n'); | |
| } | |
| EOF | |
| while IFS=$'\t' read -r tag package_name version body_b64; do | |
| [ -n "$tag" ] || continue | |
| body=$(printf '%s' "$body_b64" | base64 --decode) | |
| if gh release view "$tag" >/dev/null 2>&1; then | |
| gh release edit "$tag" \ | |
| --title "$package_name@$version" \ | |
| --notes "$body" | |
| else | |
| gh release create "$tag" \ | |
| --target "$release_target" \ | |
| --title "$package_name@$version" \ | |
| --notes "$body" | |
| fi | |
| done < "$RUNNER_TEMP/release-lines.tsv" |