diff --git a/.github/prerelease-config.json b/.github/prerelease-config.json new file mode 100644 index 000000000..092279b68 --- /dev/null +++ b/.github/prerelease-config.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "always-update": true, + "changelog-type": "default", + "commit-search-depth": 50, + "draft": true, + "force-tag-creation": true, + "include-commit-authors": true, + "include-component-in-tag": false, + "include-v-in-release-name": true, + "include-v-in-tag": true, + "initial-version": "2.9.0", + "prerelease": true, + "prerelease-type": "rc.1", + "release-type": "node", + "versioning": "prerelease", + "packages": { + ".": { + "changelog-sections": [ + { "type": "feat", "section": ":sparkles: Features" }, + { "type": "feature", "section": ":sparkles: Features" }, + { "type": "fix", "section": ":bug: Bug Fixes" }, + { "type": "patch", "section": ":bug: Bug Fixes" }, + { "type": "perf", "section": ":zap: Performance Improvements" }, + { "type": "refactor", "section": ":wrench: Code Refactoring" }, + { "type": "docs", "section": ":books: Documentation" }, + { "type": "style", "section": ":art: Styles" }, + { "type": "test", "section": ":test_tube: Tests" }, + { "type": "build", "section": ":building_construction: Build System and dependencies" }, + { "type": "ci", "section": ":building_construction: Build System and dependencies" }, + { "type": "chore", "section": ":broom: Chores", "hidden": false }, + { "type": "revert", "section": ":rewind: Reverts" } + ] + } + } +} diff --git a/.github/prerelease-manifest.json b/.github/prerelease-manifest.json new file mode 100644 index 000000000..ffcd4415b --- /dev/null +++ b/.github/prerelease-manifest.json @@ -0,0 +1 @@ +{ } diff --git a/.github/release-config.json b/.github/release-config.json new file mode 100644 index 000000000..2811a1c5a --- /dev/null +++ b/.github/release-config.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "always-update": true, + "changelog-type": "default", + "commit-search-depth": 50, + "draft": true, + "force-tag-creation": true, + "include-commit-authors": true, + "include-component-in-tag": false, + "include-v-in-release-name": true, + "include-v-in-tag": true, + "initial-version": "2.9.0", + "prerelease": false, + "prerelease-type": "rc.1", + "release-type": "node", + "versioning": "default", + "packages": { + ".": { + "changelog-sections": [ + { "type": "feat", "section": ":sparkles: Features" }, + { "type": "feature", "section": ":sparkles: Features" }, + { "type": "fix", "section": ":bug: Bug Fixes" }, + { "type": "patch", "section": ":bug: Bug Fixes" }, + { "type": "perf", "section": ":zap: Performance Improvements" }, + { "type": "refactor", "section": ":wrench: Code Refactoring" }, + { "type": "docs", "section": ":books: Documentation" }, + { "type": "style", "section": ":art: Styles" }, + { "type": "test", "section": ":test_tube: Tests" }, + { "type": "build", "section": ":building_construction: Build System and dependencies" }, + { "type": "ci", "section": ":building_construction: Build System and dependencies" }, + { "type": "chore", "section": ":broom: Chores", "hidden": false }, + { "type": "revert", "section": ":rewind: Reverts" } + ], + "extra-files": [ + { + "type": "json", + "path": ".github/prerelease-manifest.json", + "jsonpath": "$[\".\"]" + } + ] + } + } +} diff --git a/.github/release-manifest.json b/.github/release-manifest.json new file mode 100644 index 000000000..ffcd4415b --- /dev/null +++ b/.github/release-manifest.json @@ -0,0 +1 @@ +{ } diff --git a/.github/release.yml b/.github/release.yml deleted file mode 100644 index 1d8bb9d81..000000000 --- a/.github/release.yml +++ /dev/null @@ -1,11 +0,0 @@ -changelog: - categories: - - title: 🏕 Features - labels: - - "*" - exclude: - labels: - - dependencies - - title: 👒 Dependencies - labels: - - dependencies diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml deleted file mode 100644 index d34907085..000000000 --- a/.github/workflows/create-release.yml +++ /dev/null @@ -1,35 +0,0 @@ -# This workflow runs upon creation of a release in this project. -# This workflow will build the project for production, create a downloadable artifact and uploads this to the tagged release. - -name: Create artifact and add to release - -on: - release: - types: - - published - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: write - - env: - TAG_NAME: ${{ github.event.release.tag_name }} - - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Use Node.js LTS - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: 'lts/*' - cache: 'npm' - - run: npm ci - - run: npm run build-artifact - - name: Create zip file - run: pushd packages/pxweb2/dist && zip -r $OLDPWD/pxweb-${{ env.TAG_NAME }}.zip ./ - - name: Upload release asset - env: - GH_TOKEN: ${{ github.token }} - run: | - gh release upload ${{ env.TAG_NAME }} pxweb-${{ env.TAG_NAME }}.zip diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml deleted file mode 100644 index 091eb342e..000000000 --- a/.github/workflows/docker-publish.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Docker build and publish - -on: - release: - types: - - published - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build and push Docker image - id: build-and-push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 000000000..4604c27d9 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,160 @@ +name: Create release + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + prerelease: + description: Create a prerelease instead of a release + type: boolean + required: false + default: false + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write + outputs: + release_created: ${{ steps.release.outputs.release_created || steps.prerelease.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name || steps.prerelease.outputs.tag_name }} + is_prerelease: ${{ steps.detect-prerelease.outputs.is_prerelease }} + + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Detect prerelease from version in package.json + id: detect-prerelease + run: | + VERSION=$(sed -nE 's/^[[:space:]]*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' package.json | head -n 1) + IS_PRERELEASE=false + if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then + IS_PRERELEASE=true + fi + echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT" + + - name: Create prerelease + if: (github.event_name == 'workflow_dispatch' && inputs.prerelease == true) || steps.detect-prerelease.outputs.is_prerelease == 'true' + uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 + id: prerelease + with: + config-file: .github/prerelease-config.json + manifest-file: .github/prerelease-manifest.json + + - name: Create release + if: (github.event_name == 'workflow_dispatch' && inputs.prerelease == false) || steps.detect-prerelease.outputs.is_prerelease == 'false' + uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 + id: release + with: + config-file: .github/release-config.json + manifest-file: .github/release-manifest.json + + upload-asset: + needs: release + if: ${{ needs.release.outputs.release_created }} + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + attestations: write + + env: + TAG_NAME: ${{ needs.release.outputs.tag_name }} + + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 24.x + cache: 'npm' + + - run: npm ci + + - run: npm run build-artifact + + - name: Create zip file + run: pushd packages/pxweb2/dist && zip -r $OLDPWD/pxweb-${{ env.TAG_NAME }}.zip ./ + + - name: Upload release asset + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release upload ${{ env.TAG_NAME }} pxweb-${{ env.TAG_NAME }}.zip + + docker-publish: + needs: release + if: ${{ needs.release.outputs.release_created }} + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + TAG_NAME: ${{ needs.release.outputs.tag_name }} + + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}},value=${{ env.TAG_NAME }} + type=semver,pattern={{major}}.{{minor}},value=${{ env.TAG_NAME }} + type=semver,pattern={{major}},value=${{ env.TAG_NAME }} + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + publish-release: + needs: [release, upload-asset, docker-publish] + if: ${{ needs.release.outputs.release_created }} + runs-on: ubuntu-latest + permissions: + contents: write + + env: + TAG_NAME: ${{ needs.release.outputs.tag_name }} + + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release edit ${{ env.TAG_NAME }} --draft=false diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 7a9e259b4..4848f598b 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -29,7 +29,7 @@ An overview of the current technical debt in the project: - For security reasons, we want to wait at least 9 days before updating packages. - Use NPM '--before' or npm-check-updates '--cooldown' flags. - Note: There are challenges with using npm-check-updates and cooldown, read this issue: - https://github.com/raineorshine/npm-check-updates/issues/1556#issuecomment-3809050192 + - First update Storybook to the latest version that is outside the cooldown of 9 days: - Go to Storybook and find the correct version to update to OR look at which Storybook version is wanted in the command output of: @@ -138,3 +138,76 @@ For existing third-party component dependencies, we will utilize Dependabot to h We are committed to continuously updating our dependencies to ensure that we are using the latest versions. However, in some cases, we may encounter conflicts with NX plugins that prevent us from updating to the latest versions. In such situations, we carefully evaluate the impact and risks before deciding on the appropriate course of action. In the event of a security vulnerability being discovered in any of our dependencies, we will prioritize attending to it urgently. Our team will take immediate action to mitigate the risk and apply necessary patches or updates. + +## Releases and pre-releases + +We publish releases through the GitHub workflow [release-please.yml](.github/workflows/release-please.yml). +The workflow uses [release-please](https://github.com/googleapis/release-please) and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) to decide version bumps and generate release notes. + +Useful release-please links: + +1. [release-please repository](https://github.com/googleapis/release-please) +2. [release-please-action](https://github.com/googleapis/release-please-action) +3. [Action inputs and outputs](https://github.com/googleapis/release-please-action#action-inputs) +4. [Manifest configuration](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md) +5. [release-please config reference](https://github.com/googleapis/release-please/blob/main/docs/customizing.md) + +### Release PR flow (important) + +Release-please does not create a release immediately. +It first creates or updates a generated release PR. + +1. A generated PR is opened with the proposed version bump and changelog updates. +2. Maintainers must review and approve that PR. +3. The PR must be merged to main. +4. After merge, release-please creates the GitHub release and tag. + +Without approving and merging the generated release PR, there is no final release. + +### Linear commit history (important) + +Keep a linear history on main. +This makes release-please easier to reason about and keeps changelog generation predictable. + +1. Prefer squash-and-merge or rebase-and-merge for PRs. +2. Avoid unnecessary merge commits from outdated branches. +3. Keep commit messages clean, because they are used to generate release notes. + +### How it is triggered + +1. Push to main: runs automatically. +2. Manual run: use GitHub Actions and start the release-please workflow with workflow_dispatch. + - You can set the prerelease input to true for a manual pre-release run. + +### How stable vs pre-release is selected + +The workflow checks package.json version. + +1. If version is a semver pre-release (for example 2.9.10-rc or 2.9.10-beta.1), it uses the pre-release config. +2. If version is a normal semver release (for example 2.9.10), it uses the normal release config. +3. For manual runs, prerelease input can be used to force pre-release behavior. + +### What the workflow publishes + +When a release is created, the pipeline also: + +1. Builds and uploads a zip artifact named pxweb-{tag}.zip to the GitHub release. +2. Builds and publishes multi-arch Docker images to GHCR. +3. Publishes the GitHub release (removes draft state). + +### Commit message rules (important) + +release-please depends on [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). +Use commit messages like: + +1. feat: add x +2. fix: correct y + +If commits do not follow this style, release-please may not create the expected release PR or tag. + +### Maintainer checklist + +1. Ensure PR titles and commit messages are descriptive and follow conventional commits. +2. Confirm package.json version format is correct for the intended release type. +3. Maintain a linear history on main when merging PRs. +4. Verify workflow success in Actions before announcing the release.