diff --git a/.changeset/README.md b/.changeset/README.md index e7dcf28..f10c98b 100644 --- a/.changeset/README.md +++ b/.changeset/README.md @@ -1,7 +1,7 @@ # Changesets This folder is managed by `@changesets/cli`. Use it to record release notes, -version `@anarchitecture/madrigal`, and publish the package to npm. +version `@anarchitecture/madrigal`, and prepare an explicit release. Common workflow: @@ -10,3 +10,7 @@ pnpm changeset pnpm version-packages pnpm release ``` + +Merges to `main` create version PRs only. Publish the release tarball by pushing +an `anarchitecture-madrigal@` tag or by running the GitHub Actions +tarball workflow manually with the version from `package.json`. diff --git a/.github/workflows/release-tarball.yml b/.github/workflows/release-tarball.yml new file mode 100644 index 0000000..42833ad --- /dev/null +++ b/.github/workflows/release-tarball.yml @@ -0,0 +1,102 @@ +name: Publish tarball to GitHub Release + +# Explicit distribution channel while main-branch publishing is disabled. Packs +# @anarchitecture/madrigal and attaches the .tgz to a GitHub Release, so +# consumers can install it directly: +# +# npm install https://github.com/block/madrigal/releases/download//.tgz +# +# Triggered by pushing a tag of the form `anarchitecture-madrigal@` or +# by manual workflow_dispatch. + +on: + push: + tags: + - "anarchitecture-madrigal@*" + workflow_dispatch: + inputs: + version: + description: "Version to release (must match package.json)" + required: true + +permissions: + contents: write + +concurrency: + group: tarball-release-${{ github.ref }} + cancel-in-progress: false + +jobs: + tarball: + name: Pack and release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # No setup-node and no pnpm/action-setup -- both would trip zizmor's + # cache-poisoning rule because any GitHub-official setup action is + # treated as cache-capable regardless of whether caching is enabled. + # ubuntu-latest ships Node 20 + corepack, which satisfies our + # engines.node (>=18). Corepack reads `packageManager` from root + # package.json to install the exact pinned pnpm version with no + # cross-branch cache store. + - run: node --version && corepack enable + + - run: pnpm install --frozen-lockfile --registry=https://registry.npmjs.org + + - run: npm ci --prefix src/dev/ui --no-audit --no-fund --registry=https://registry.npmjs.org --replace-registry-host=always + + - name: Build + run: pnpm build + + - name: Validate tag + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_VERSION: ${{ inputs.version }} + REF_NAME: ${{ github.ref_name }} + run: | + PACKAGE_VERSION="$(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version)")" + if [ "$EVENT_NAME" = "push" ]; then + EXPECTED_TAG="anarchitecture-madrigal@$PACKAGE_VERSION" + if [ "$REF_NAME" != "$EXPECTED_TAG" ]; then + echo "Tag must be $EXPECTED_TAG for package.json version $PACKAGE_VERSION" >&2 + exit 1 + fi + elif [ "$INPUT_VERSION" != "$PACKAGE_VERSION" ]; then + echo "Input version must match package.json version $PACKAGE_VERSION" >&2 + exit 1 + fi + + - name: Pack + run: | + mkdir -p dist-tarball + pnpm pack --pack-destination "$GITHUB_WORKSPACE/dist-tarball" + ls -la dist-tarball + + # Resolve the release tag. Inputs from workflow_dispatch are attacker- + # controlled (anyone with Actions write can trigger). Pass them in via + # `env:` and reference as shell variables so they can't be interpolated + # as shell syntax -- that's what the semgrep shell-injection rule wants. + - name: Resolve tag + id: tag + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_VERSION: ${{ inputs.version }} + run: | + if [ "$EVENT_NAME" = "push" ]; then + TAG="$GITHUB_REF_NAME" + else + TAG="anarchitecture-madrigal@$INPUT_VERSION" + fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.tag.outputs.tag }} + run: | + gh release create "$TAG" \ + --title "$TAG" \ + --generate-notes \ + dist-tarball/*.tgz diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8e2add..16ff8d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release +name: Version Packages on: push: @@ -12,11 +12,10 @@ concurrency: permissions: contents: write pull-requests: write - id-token: write jobs: - release: - name: Version or publish + version: + name: Create Release PR runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -30,22 +29,14 @@ jobs: with: node-version: '22' cache: pnpm - registry-url: https://registry.npmjs.org - run: pnpm install --frozen-lockfile --registry=https://registry.npmjs.org - - run: npm ci --prefix src/dev/ui --no-audit --no-fund --registry=https://registry.npmjs.org --replace-registry-host=always - - - run: pnpm build - - - name: Create Release PR or publish + - name: Create Release PR uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf # v1.7.0 with: - publish: npm publish --access public --provenance version: pnpm changeset version commit: "chore: version packages" title: "chore: version packages" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.MADRIGAL_NPM_PUBLISH_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.MADRIGAL_NPM_PUBLISH_TOKEN }} diff --git a/README.md b/README.md index 35bb4aa..853e284 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,7 @@ ln -sf "$(pwd)/skills/{skill-name}/SKILL.md" ~/.claude/skills/{skill-name}/SKILL ## Release Workflow Madrigal uses [Changesets](https://github.com/changesets/changesets) for -versioning and npm publishing. +versioning. For a release-bearing change, run: @@ -325,10 +325,21 @@ For a release-bearing change, run: pnpm changeset ``` -After the change lands on `main`, the `Release` GitHub Actions workflow creates -a version PR when changesets are pending. When that version PR lands, the same -workflow builds Madrigal and publishes it to npm with provenance. The workflow -expects the npm automation token in `MADRIGAL_NPM_PUBLISH_TOKEN`. +After the change lands on `main`, the `Version Packages` GitHub Actions +workflow creates a version PR when changesets are pending. Merging to `main` +does not publish Madrigal. + +To publish a GitHub Release tarball, merge the version PR and push a matching +tag: + +```bash +VERSION=$(node -e "console.log(JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version)") +git tag "anarchitecture-madrigal@$VERSION" +git push origin "anarchitecture-madrigal@$VERSION" +``` + +The `Publish tarball to GitHub Release` workflow can also be run manually with +the package version from `package.json`. Manual release commands are also available: diff --git a/package.json b/package.json index 670d019..9174f7d 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "clean": "rm -rf dist", "changeset": "changeset", "version-packages": "changeset version", - "release": "pnpm build && changeset publish", + "release": "pnpm build && pnpm pack", "prepublishOnly": "pnpm build", "prepare": "lefthook install" },