From b293b7bd5252c9f595d1b0343c690860e8ab1bed Mon Sep 17 00:00:00 2001 From: Burak Yigit Kaya Date: Fri, 22 May 2026 20:11:57 +0000 Subject: [PATCH] meta: add Craft-based release pipeline - Add .craft.yml with npm (OIDC) + GitHub Release targets - Add release.yml workflow (manual craft prepare trigger) - Add publish.yml workflow (issue-label-triggered craft publish) - Update build.yml: add release/** trigger, artifacts job, upgrade actions to v6/v7, remove stale env - Bump version to 0.5.0 (last published) so Craft can manage version bumps - Add AGENTS.md documenting project setup and release flow --- .craft.yml | 15 +++++++ .github/workflows/build.yml | 47 ++++++++++++++------ .github/workflows/publish.yml | 80 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 39 +++++++++++++++++ AGENTS.md | 42 ++++++++++++++++++ package-lock.json | 6 +-- package.json | 2 +- 7 files changed, 215 insertions(+), 16 deletions(-) create mode 100644 .craft.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 AGENTS.md diff --git a/.craft.yml b/.craft.yml new file mode 100644 index 0000000..98b09e6 --- /dev/null +++ b/.craft.yml @@ -0,0 +1,15 @@ +minVersion: '2.21.1' +changelog: + policy: auto +versioning: + policy: auto +artifactProvider: + name: github + config: + artifacts: + CI: npm-tarball +targets: + - name: npm + access: public + oidc: true + - name: github diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f661407..eaeea74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build & Test on: push: - branches: [main] + branches: [main, 'release/**'] pull_request: concurrency: @@ -13,23 +13,18 @@ defaults: run: shell: bash -env: - BUILD_CACHE_KEY: ${{ github.sha }} - CACHED_BUILD_PATHS: | - ${{ github.workspace }}/packages/*/dist - jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: package.json cache: npm @@ -41,7 +36,7 @@ jobs: run: npm run build - name: Store dist - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: dist if-no-files-found: error @@ -57,12 +52,12 @@ jobs: needs: build steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: package.json cache: npm @@ -71,7 +66,7 @@ jobs: run: npm install - name: Download dist - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: dist path: dist @@ -89,3 +84,31 @@ jobs: expected=$(cat asset.txt) actual=$(./dist-bin/sample-linux-x64) [ "$actual" = "$expected" ] + + artifacts: + name: Artifacts + if: startsWith(github.ref, 'refs/heads/release/') + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version-file: package.json + cache: npm + + - run: npm ci + + - uses: actions/download-artifact@v7 + with: + name: dist + path: dist + + - name: Pack tarball + run: npm pack + + - uses: actions/upload-artifact@v7 + with: + name: npm-tarball + path: '*.tgz' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..fbf3ba0 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,80 @@ +name: Publish +on: + issues: + types: [labeled] + +jobs: + publish: + if: github.event.label.name == 'accepted' && github.event.issue.state == 'open' + runs-on: ubuntu-latest + name: Publish release + environment: production + permissions: + contents: write + id-token: write + issues: write + timeout-minutes: 15 + steps: + - uses: actions/create-github-app-token@v3 + id: app-token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Parse publish request + id: inputs + env: + ISSUE_TITLE: ${{ github.event.issue.title }} + run: | + # Title format: "publish: owner/repo@VERSION" + VERSION=$(echo "$ISSUE_TITLE" | grep -oP '@\K[^\s]+$') + if [[ -z "$VERSION" ]]; then + echo "::error::Could not parse version from issue title: $ISSUE_TITLE" + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@v6 + with: + ref: release/${{ steps.inputs.outputs.version }} + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - name: Set git user + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Install Craft + run: | + CRAFT_URL=$(curl -fsSL https://api.github.com/repos/getsentry/craft/releases/latest \ + | jq -r '.assets[] | select(.name == "craft") | .browser_download_url') + sudo curl -fsSL -o /usr/local/bin/craft "$CRAFT_URL" + sudo chmod +x /usr/local/bin/craft + + - name: Publish + run: craft publish "${{ steps.inputs.outputs.version }}" --no-input --no-status-check + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Close issue on success + if: success() + env: + GH_TOKEN: ${{ github.token }} + run: | + gh issue close "${{ github.event.issue.number }}" \ + --comment "Published **${{ steps.inputs.outputs.version }}** successfully. + [Workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" + + - name: Comment on failure + if: failure() + env: + GH_TOKEN: ${{ github.token }} + run: | + gh issue comment "${{ github.event.issue.number }}" \ + --body "Publish failed. [View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" + gh issue edit "${{ github.event.issue.number }}" --remove-label accepted diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..776051b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: Release +on: + workflow_dispatch: + inputs: + version: + description: Version to release (or "auto") + required: false + force: + description: Force a release even when there are release-blockers + type: boolean + default: false + +jobs: + release: + runs-on: ubuntu-latest + name: Release a new version + environment: production + permissions: + contents: write + issues: write + steps: + - uses: actions/create-github-app-token@v3 + id: app-token + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - uses: actions/checkout@v6 + with: + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - uses: getsentry/craft@v2 + with: + version: ${{ inputs.version }} + force: ${{ inputs.force }} + publish_repo: self + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..25f8356 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,42 @@ +# Agents + +## Project Overview + +Fossilize is a CLI tool that creates Node.js Single Executable Applications (SEA) for multiple platforms. It bundles a Node.js app using esbuild, downloads platform-specific Node.js binaries, and injects the bundle using postject. + +## Tech Stack + +- **Language:** TypeScript (type-check only via `tsc --noEmit`, bundled by tsup) +- **Build:** tsup (ESM, code-splitting, minification) +- **Package manager:** npm (not pnpm/yarn) +- **Node version:** Pinned via Volta in package.json (`22.14.0`) +- **CLI framework:** Stricli + +## Release & Publishing + +This repo uses [getsentry/craft](https://github.com/getsentry/craft) for automated releases, following the `publish_repo: self` pattern (publish issues are created in this repo, not a separate publish repo). + +### Release flow +1. Maintainer triggers the **Release** workflow (`workflow_dispatch`) with version `auto` or explicit +2. Craft creates `release/X.Y.Z` branch, bumps `package.json`, generates `CHANGELOG.md`, opens publish issue +3. CI runs on the release branch — `build` + `test` + `artifacts` jobs (artifacts packs npm tarball) +4. Maintainer adds `accepted` label to the publish issue +5. **Publish** workflow fires, runs `craft publish` which publishes to npm (OIDC) and creates a GitHub Release +6. Issue auto-closes on success + +### Key files +- `.craft.yml` — Craft configuration (npm + github targets) +- `.github/workflows/build.yml` — CI: build, smoke test, artifact packing on release branches +- `.github/workflows/release.yml` — Manual trigger for `craft prepare` +- `.github/workflows/publish.yml` — Issue-label-triggered `craft publish` + +### Prerequisites for releasing +- GitHub App with `contents: write` and `issues: write` permissions + - Repo variable `APP_ID` and secret `APP_PRIVATE_KEY` +- GitHub environment `production` on the repo +- npm OIDC provenance linked for the `fossilize` package + +### npm publishing +- Uses OIDC authentication (no `NPM_TOKEN` secret needed) +- Package is published with `public` access +- The `artifacts` job in CI packs the tarball using `npm pack` after downloading the pre-built `dist/` artifact diff --git a/package-lock.json b/package-lock.json index b7f31cc..3340cbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fossilize", - "version": "0.6.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fossilize", - "version": "0.6.0", + "version": "0.5.0", "license": "MIT", "dependencies": { "@stricli/auto-complete": "^1.1.0", @@ -32,7 +32,7 @@ "typescript": "5.6.x" }, "engines": { - "node": ">=22" + "node": ">=18" } }, "node_modules/@esbuild/aix-ppc64": { diff --git a/package.json b/package.json index 13fd7d9..2345703 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "repository": "github:BYK/fossilize", "license": "MIT", "type": "module", - "version": "0.6.0", + "version": "0.5.0", "keywords": [ "node", "sea",