docs: correct @fortemi/graph dependency direction in architecture + A… #7
Workflow file for this run
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
| # Public npmjs.org publication for the GitHub mirror. | |
| # | |
| # Gitea remains the primary CI/internal-registry release path. This workflow is | |
| # the final public distribution leg: it runs only from signed v* release tags, | |
| # attaches npm provenance from GitHub Actions OIDC, and authenticates with the | |
| # NPMJS_TOKEN secret configured on the GitHub mirror. | |
| name: Publish to npmjs.org | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| tag_to_publish: | |
| description: 'Signed v* tag to publish' | |
| required: true | |
| type: string | |
| concurrency: | |
| group: npmjs-publish-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| id-token: write | |
| jobs: | |
| publish: | |
| name: Publish public npm packages with provenance | |
| runs-on: ubuntu-latest | |
| container: node:24@sha256:050bf2bbe33c1d6754e060bec89378a79ed831f04a7bb1a53fe45e997df7b3bb | |
| timeout-minutes: 20 | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 | |
| - name: Setup Node for npmjs.org | |
| uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 | |
| with: | |
| node-version: '24' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Resolve release tag | |
| id: release | |
| env: | |
| INPUT_TAG_TO_PUBLISH: ${{ github.event.inputs.tag_to_publish }} | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TAG="${INPUT_TAG_TO_PUBLISH}" | |
| else | |
| TAG="${GITHUB_REF#refs/tags/}" | |
| fi | |
| case "$TAG" in | |
| v*) ;; | |
| *) | |
| echo "Release tag must start with v: $TAG" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| echo "release_tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "GITHUB_REF=refs/tags/$TAG" >> "$GITHUB_ENV" | |
| - name: Fetch release tag object | |
| run: | | |
| set -euo pipefail | |
| git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| TAG="${{ steps.release.outputs.release_tag }}" | |
| git fetch origin "+refs/tags/${TAG}:refs/tags/${TAG}" --depth=1 | |
| git checkout --detach "refs/tags/${TAG}" | |
| - name: Verify signed release tag | |
| env: | |
| RELEASE_TAG: ${{ steps.release.outputs.release_tag }} | |
| run: bash tools/ci/verify-signed-tag.sh | |
| - name: Verify pnpm supply-chain gates | |
| run: | | |
| set -euo pipefail | |
| node - <<'CHECK' | |
| const fs = require('node:fs'); | |
| const text = fs.readFileSync('pnpm-workspace.yaml', 'utf8'); | |
| const age = text.match(/^minimumReleaseAge:\s*(\d+)\s*$/m); | |
| const exotic = text.match(/^blockExoticSubdeps:\s*(true|false)\s*$/m); | |
| if (!age || Number(age[1]) < 10080) { | |
| throw new Error('pnpm minimumReleaseAge must be at least 10080 minutes'); | |
| } | |
| if (!exotic || exotic[1] !== 'true') { | |
| throw new Error('pnpm blockExoticSubdeps must be true'); | |
| } | |
| CHECK | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Typecheck | |
| run: pnpm typecheck | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Build | |
| run: pnpm build | |
| - name: Verify package versions | |
| env: | |
| RELEASE_TAG: ${{ steps.release.outputs.release_tag }} | |
| run: | | |
| set -euo pipefail | |
| TAG_VERSION="${RELEASE_TAG#v}" | |
| export TAG_VERSION | |
| node - <<'CHECK' | |
| const fs = require('node:fs'); | |
| const expected = process.env.TAG_VERSION; | |
| for (const dir of ['packages/core', 'packages/graph', 'packages/react']) { | |
| const pkg = JSON.parse(fs.readFileSync(dir + '/package.json', 'utf8')); | |
| if (pkg.version !== expected) { | |
| throw new Error(pkg.name + ' version ' + pkg.version + ' does not match release tag ' + expected); | |
| } | |
| } | |
| CHECK | |
| - name: Pack and inspect artifacts | |
| env: | |
| RELEASE_TAG: ${{ steps.release.outputs.release_tag }} | |
| run: | | |
| set -euo pipefail | |
| TAG_VERSION="${RELEASE_TAG#v}" | |
| export TAG_VERSION | |
| PACK_DIR="/tmp/fortemi-publish-check" | |
| rm -rf "$PACK_DIR" | |
| mkdir -p "$PACK_DIR" | |
| (cd packages/core && pnpm pack --pack-destination "$PACK_DIR") | |
| (cd packages/graph && pnpm pack --pack-destination "$PACK_DIR") | |
| (cd packages/react && pnpm pack --pack-destination "$PACK_DIR") | |
| CORE_TGZ="$PACK_DIR/fortemi-core-${TAG_VERSION}.tgz" | |
| GRAPH_TGZ="$PACK_DIR/fortemi-graph-${TAG_VERSION}.tgz" | |
| REACT_TGZ="$PACK_DIR/fortemi-react-${TAG_VERSION}.tgz" | |
| test -s "$CORE_TGZ" | |
| test -s "$GRAPH_TGZ" | |
| test -s "$REACT_TGZ" | |
| tar -tzf "$CORE_TGZ" > "$PACK_DIR/core-files.txt" | |
| tar -tzf "$GRAPH_TGZ" > "$PACK_DIR/graph-files.txt" | |
| tar -tzf "$REACT_TGZ" > "$PACK_DIR/react-files.txt" | |
| if grep -E 'package/(\.aiwg|\.agents|\.codex|\.claude)/' "$PACK_DIR/core-files.txt" "$PACK_DIR/graph-files.txt" "$PACK_DIR/react-files.txt"; then | |
| echo "Packed artifact contains local AI/operator metadata" >&2 | |
| exit 1 | |
| fi | |
| tar -xOf "$REACT_TGZ" package/package.json > "$PACK_DIR/react-package.json" | |
| node - <<'CHECK' | |
| const fs = require('node:fs'); | |
| const pkg = JSON.parse(fs.readFileSync('/tmp/fortemi-publish-check/react-package.json', 'utf8')); | |
| const expected = process.env.TAG_VERSION; | |
| for (const name of ['@fortemi/core', '@fortemi/graph']) { | |
| const dep = pkg.dependencies && pkg.dependencies[name]; | |
| if (dep !== expected) { | |
| throw new Error('@fortemi/react artifact depends on ' + name + ' ' + dep + ', expected ' + expected); | |
| } | |
| } | |
| CHECK | |
| - name: Publish packages to npmjs.org | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }} | |
| RELEASE_TAG: ${{ steps.release.outputs.release_tag }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${NODE_AUTH_TOKEN:-}" ]; then | |
| echo "NPMJS_TOKEN is required on the GitHub mirror" >&2 | |
| exit 1 | |
| fi | |
| TAG_VERSION="${RELEASE_TAG#v}" | |
| NPM_TAG="latest" | |
| if [ "$TAG_VERSION" != "${TAG_VERSION%%-*}" ]; then | |
| NPM_TAG="next" | |
| if echo "$TAG_VERSION" | grep -qiE -- '-nightly\.'; then | |
| NPM_TAG="nightly" | |
| fi | |
| fi | |
| echo "Publishing npm dist-tag: $NPM_TAG" | |
| publish_package() { | |
| package_dir="$1" | |
| package_name="$(node -p "require('./${package_dir}/package.json').name")" | |
| package_version="$(node -p "require('./${package_dir}/package.json').version")" | |
| if npm view "${package_name}@${package_version}" version >/dev/null 2>&1; then | |
| echo "${package_name}@${package_version} already exists on npmjs.org; skipping publish" | |
| else | |
| echo "Publishing ${package_name}@${package_version} to npmjs.org" | |
| (cd "$package_dir" && pnpm publish --provenance --access public --no-git-checks --tag "$NPM_TAG") | |
| fi | |
| for attempt in 1 2 3 4 5; do | |
| published="$(npm view "${package_name}@${package_version}" version --prefer-online --no-update-notifier 2>/dev/null || echo "")" | |
| if [ "$published" = "$package_version" ]; then | |
| break | |
| fi | |
| echo "Attempt ${attempt}: ${package_name}@${package_version} not visible yet; retrying in 10s..." | |
| sleep 10 | |
| if [ "$attempt" = "5" ]; then | |
| echo "${package_name}@${package_version} did not become visible on npmjs.org" >&2 | |
| exit 1 | |
| fi | |
| done | |
| for attempt in 1 2 3 4 5; do | |
| if npm dist-tag add "${package_name}@${package_version}" "$NPM_TAG"; then | |
| break | |
| fi | |
| echo "Attempt ${attempt}: dist-tag promotion failed; retrying in 10s..." | |
| sleep 10 | |
| if [ "$attempt" = "5" ]; then | |
| echo "dist-tag promotion exhausted retries for ${package_name}@${package_version}" >&2 | |
| exit 1 | |
| fi | |
| done | |
| } | |
| publish_package packages/core | |
| publish_package packages/graph | |
| publish_package packages/react |