diff --git a/.github/workflows/publish-package-release.yml b/.github/workflows/publish-package-release.yml new file mode 100644 index 00000000..6fad1ac2 --- /dev/null +++ b/.github/workflows/publish-package-release.yml @@ -0,0 +1,110 @@ +name: Package Release + +on: + push: + branches: + - main + +env: + GH_PACKAGES_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }} + +permissions: + # Enable the use of OIDC for trusted publishing and npm provenance + id-token: write + # Enable the use of GitHub Packages registry + packages: write + # Enable `semantic-release` to publish a GitHub release and push commits + contents: write + # Enable `semantic-release` to post comments on issues + issues: write + # Enable `semantic-release` to post comments on pull requests + pull-requests: write + +# The release workflow involves many crucial steps that once triggered shouldn't be cancelled until +# finished, otherwise we might end up in an inconsistent state (e.g., published to GitHub Packages +# but not npm), so new workflow runs are queued until the previous one has completely finished. +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + release-and-publish: + name: Release & Publish + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Generate release bot token + id: release-bot + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.DOIST_RELEASE_BOT_ID }} + private-key: ${{ secrets.DOIST_RELEASE_BOT_PRIVATE_KEY }} + permission-contents: write + permission-issues: write + permission-pull-requests: write + + - name: Checkout repository + uses: actions/checkout@v6 + with: + token: ${{ steps.release-bot.outputs.token }} + fetch-depth: 0 + + - name: Prepare Node.js environment + uses: actions/setup-node@v6 + with: + cache: npm + node-version-file: .node-version + + - name: Cache project 'node_modules' directory + id: node-modules-cache + uses: actions/cache@v5 + with: + key: node-modules-cache-${{ hashFiles('**/package-lock.json', '**/.node-version', 'patches/**') }} + path: node_modules/ + + - name: Install project npm dependencies + if: ${{ steps.node-modules-cache.outputs.cache-hit != 'true' }} + run: | + npm ci + + - name: Build package + run: | + npm run build + + # The Node.js environment is prepared based on the `.npmrc` file in the repo, which + # configures Doist scoped packages to use the public npm registry with OIDC + # authentication for the initial `semantic-release` publish, after which we remove the + # Doist registry configuration, and prepare the Node.js environment for the GitHub + # Packages registry, providing a predictable release workflow for both registries. + - name: Publish package to public npm registry + id: semantic-release + run: | + npx semantic-release + env: + GITHUB_TOKEN: ${{ steps.release-bot.outputs.token }} + GIT_AUTHOR_EMAIL: doistbot@users.noreply.github.com + GIT_AUTHOR_NAME: Doist Bot + GIT_COMMITTER_EMAIL: doistbot@users.noreply.github.com + GIT_COMMITTER_NAME: Doist Bot + + - name: Remove Doist registry configuration from `.npmrc` + if: ${{ steps.semantic-release.outputs.package-published == 'true' }} + run: | + npm config delete @doist:registry --location=project + + - name: Prepare Node.js environment for GitHub Packages registry + if: ${{ steps.semantic-release.outputs.package-published == 'true' }} + uses: actions/setup-node@v6 + with: + cache: npm + node-version-file: .node-version + registry-url: https://npm.pkg.github.com/ + scope: '@doist' + + - name: Publish package to private GitHub Packages registry + if: ${{ steps.semantic-release.outputs.package-published == 'true' }} + run: | + npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index eae9e946..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: Release - -on: - push: - branches: - - main - -permissions: - # Enable `semantic-release` to publish a GitHub release and push commits - contents: write - # Enable `semantic-release` to post comments on issues - issues: write - # Enable `semantic-release` to post comments on pull requests - pull-requests: write - # Enable the use of OIDC for trusted publishing and npm provenance - id-token: write - # Enable the use of GitHub Packages registry - packages: write - -# Release involves crucial steps that shouldn't be cancelled mid-run, -# so new workflow runs are queued until the previous one finishes. -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: false - -jobs: - release: - name: Release - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - name: Generate bot token - id: generate_token - uses: actions/create-github-app-token@v3 - with: - app-id: ${{ secrets.DOIST_RELEASE_BOT_ID }} - private-key: ${{ secrets.DOIST_RELEASE_BOT_PRIVATE_KEY }} - permission-contents: write - permission-issues: write - permission-pull-requests: write - - - name: Get bot user ID - id: bot_user - run: | - user_id=$(gh api "/users/${{ steps.generate_token.outputs.app-slug }}[bot]" --jq .id) - if [ -z "$user_id" ]; then - echo "Failed to get bot user ID" >&2 - exit 1 - fi - echo "id=$user_id" >> "$GITHUB_OUTPUT" - env: - GH_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ steps.generate_token.outputs.token }} - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .node-version - cache: npm - scope: '@doist' - registry-url: 'https://registry.npmjs.org/' - - - name: Install dependencies - run: npm ci - - - name: Capture previous tag - id: previous_tag - run: echo "tag=$(git describe --tags --abbrev=0 2>/dev/null || true)" >> "$GITHUB_OUTPUT" - - - name: Release - run: npx semantic-release - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - GIT_AUTHOR_NAME: ${{ steps.generate_token.outputs.app-slug }}[bot] - GIT_AUTHOR_EMAIL: ${{ steps.bot_user.outputs.id }}+${{ steps.generate_token.outputs.app-slug }}[bot]@users.noreply.github.com - GIT_COMMITTER_NAME: ${{ steps.generate_token.outputs.app-slug }}[bot] - GIT_COMMITTER_EMAIL: ${{ steps.bot_user.outputs.id }}+${{ steps.generate_token.outputs.app-slug }}[bot]@users.noreply.github.com - - - name: Check if release was created - id: check_release - env: - PREVIOUS_TAG: ${{ steps.previous_tag.outputs.tag }} - run: | - git fetch --force --tags origin - new_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)" - if [ -z "${new_tag}" ] || [ "${new_tag}" = "${PREVIOUS_TAG}" ]; then - echo "released=false" >> "$GITHUB_OUTPUT" - else - echo "released=true" >> "$GITHUB_OUTPUT" - fi - - - name: Remove Doist registry configuration from .npmrc - if: ${{ steps.check_release.outputs.released == 'true' }} - run: npm config delete @doist:registry --location=project - - - name: Prepare Node.js environment for GitHub Packages registry - if: ${{ steps.check_release.outputs.released == 'true' }} - uses: actions/setup-node@v4 - with: - node-version-file: .node-version - registry-url: https://npm.pkg.github.com/ - scope: '@doist' - - - name: Publish package to GitHub Packages registry - if: ${{ steps.check_release.outputs.released == 'true' }} - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 15659a1b..c10799d5 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,7 @@ "**/*.css" ], "publishConfig": { - "access": "public", - "provenance": true + "access": "public" }, "files": [ "CHANGELOG.md", @@ -55,8 +54,7 @@ "lint": "eslint --format codeframe --cache --ext js,jsx,ts,tsx ./", "storybook": "start-storybook -p 6006", "prettify": "prettier --write \"./**/*.{js,jsx,ts,tsx,json,css,scss,less,md,mdx}\"", - "plop": "plop", - "prepublishOnly": "npm run build && npm test" + "plop": "plop" }, "peerDependencies": { "@ariakit/react": "~0.4.19", diff --git a/release.config.js b/release.config.js index f249c239..4923d450 100644 --- a/release.config.js +++ b/release.config.js @@ -17,5 +17,14 @@ export default { }, ], '@semantic-release/github', + [ + '@semantic-release/exec', + { + verifyConditionsCmd: + 'if [ -n "$GITHUB_OUTPUT" ]; then echo "package-published=false" >> "$GITHUB_OUTPUT"; fi', + successCmd: + 'if [ -n "$GITHUB_OUTPUT" ]; then echo "package-published=true" >> "$GITHUB_OUTPUT"; fi', + }, + ], ], }