diff --git a/.github/scripts/publish/determine-workflow-path.sh b/.github/scripts/publish/determine-workflow-path.sh deleted file mode 100644 index a80af08c2..000000000 --- a/.github/scripts/publish/determine-workflow-path.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -set -e - -# Arguments -EVENT_NAME="$1" -INPUT_VERSION_BUMP="$2" -INPUT_NPM_TAG="$3" - -# Default values -WORKFLOW_PATH="unknown" -SHOULD_BUILD="false" -VERSION_BUMP="patch" -NPM_TAG="" - -if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then - echo "🔵 Manual workflow trigger detected" - WORKFLOW_PATH="manual" - SHOULD_BUILD="true" - VERSION_BUMP="$INPUT_VERSION_BUMP" - NPM_TAG="$INPUT_NPM_TAG" - -elif [[ "$EVENT_NAME" == "push" ]]; then - COMMIT_MSG=$(git log -1 --pretty=%B) - if [[ "$COMMIT_MSG" == *"#minor"* || "$COMMIT_MSG" == *"#major"* ]]; then - echo "🟢 Version bump commit detected!" - WORKFLOW_PATH="version-bump" - SHOULD_BUILD="true" - - if [[ "$COMMIT_MSG" == *"#minor"* ]]; then - echo "Minor version bump" - VERSION_BUMP="minor" - elif [[ "$COMMIT_MSG" == *"#major"* ]]; then - echo "Major version bump" - VERSION_BUMP="major" - fi - - elif git diff --name-only HEAD^ HEAD | grep -v "\.test\." | grep -q -E "(^\.browserslistrc$|^babel\.config\.|^src/)"; then - echo "🟠 Relevant file changes detected" - WORKFLOW_PATH="file-changes" - SHOULD_BUILD="true" - - else - echo "⚪ No publishing-relevant changes detected" - WORKFLOW_PATH="skip" - fi -fi - -echo "workflow-path=$WORKFLOW_PATH" >> $GITHUB_OUTPUT -echo "should-build=$SHOULD_BUILD" >> $GITHUB_OUTPUT -echo "version-bump=$VERSION_BUMP" >> $GITHUB_OUTPUT -echo "npm-tag=$NPM_TAG" >> $GITHUB_OUTPUT - -echo "===========================================" -echo "Workflow Path: $WORKFLOW_PATH" -echo "Should Build: $SHOULD_BUILD" -echo "Version Bump: $VERSION_BUMP" -echo "NPM Tag: ${NPM_TAG:-}" -echo "===========================================" diff --git a/.github/scripts/publish/publish-to-npm.sh b/.github/scripts/publish/publish-to-npm.sh deleted file mode 100644 index 3b0e7e938..000000000 --- a/.github/scripts/publish/publish-to-npm.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -set -e - -BRANCH_NAME="$1" -WORKFLOW_PATH="$2" -NPM_TAG="$3" -DRY_RUN="$4" - -NEW_VERSION=$(npm pkg get version | tr -d \") -PUBLISH_ARGS="--access public" - -USING_DEFAULT_TAG=false -if [[ "$WORKFLOW_PATH" == "manual" && "$NPM_TAG" == "beta" ]]; then - USING_DEFAULT_TAG=true -fi - -if [[ -n "$NPM_TAG" && "$USING_DEFAULT_TAG" == "false" ]]; then - DIST_TAG="$NPM_TAG" - PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG" - echo "Publishing v$NEW_VERSION with custom tag '$DIST_TAG'" -elif [[ "$BRANCH_NAME" == "main" ]]; then - DIST_TAG="beta" - PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG" - echo "Publishing v$NEW_VERSION from main -> using 'beta' tag" -elif [[ "$BRANCH_NAME" =~ release/v([0-9]+) ]]; then - MAJOR_VERSION="${BASH_REMATCH[1]}" - DIST_TAG="${MAJOR_VERSION}x" - PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG" - echo "Publishing v$NEW_VERSION from $BRANCH_NAME -> using tag '$DIST_TAG'" -else - if [[ "$WORKFLOW_PATH" == "manual" && "$USING_DEFAULT_TAG" == "true" ]]; then - DIST_TAG="beta" - PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG" - echo "⚠️ WARNING: Publishing from non-standard branch '$BRANCH_NAME' with default 'beta' tag" - else - echo "Branch $BRANCH_NAME doesn't match expected patterns, using default publishing" - fi -fi - -if [[ "$DRY_RUN" == "true" ]]; then - PUBLISH_ARGS="$PUBLISH_ARGS --dry-run" - echo "DRY RUN MODE - No actual publishing will occur" -fi - -# Temporary guards for testing -# PUBLISH_ARGS="$PUBLISH_ARGS --dry-run" -# echo "⚠️ TEST MODE: Force using --dry-run flag. Remove before merging to main! ⚠️" - -npm publish $PUBLISH_ARGS diff --git a/.github/scripts/publish/version-bump-details.sh b/.github/scripts/publish/version-bump-details.sh deleted file mode 100644 index bcf468d19..000000000 --- a/.github/scripts/publish/version-bump-details.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -e - -BRANCH_NAME="$1" -VERSION_TYPE="$2" - -echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV - -if [[ "$BRANCH_NAME" =~ release/v([0-9]+) ]]; then - MAJOR_VERSION="${BASH_REMATCH[1]}" - - if [[ "$VERSION_TYPE" == "major" ]]; then - echo "::error::⛔ MAJOR VERSION BUMP NOT ALLOWED ON RELEASE BRANCH" - echo "::error::Branch release/v${MAJOR_VERSION} is locked to major version ${MAJOR_VERSION}." - echo "::error::To publish a new major version, create a new branch named release/v$((MAJOR_VERSION+1))." - exit 1 - fi - - CURRENT_VERSION=$(npm pkg get version | tr -d \") - CURRENT_MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1) - - if [[ "$CURRENT_MAJOR" != "$MAJOR_VERSION" ]]; then - echo "::error::🚫 Major version mismatch: package.json version $CURRENT_VERSION does not match release/v${MAJOR_VERSION} branch." - echo "::error::Please update the package.json manually to match major version ${MAJOR_VERSION}, or rename the branch if you intended a different major." - exit 1 - fi -fi - -echo "VERSION_TYPE=$VERSION_TYPE" >> $GITHUB_ENV diff --git a/.github/workflows/check-pull-request.yml b/.github/workflows/check-pull-request.yml index ceb69602a..0f03ebc6c 100644 --- a/.github/workflows/check-pull-request.yml +++ b/.github/workflows/check-pull-request.yml @@ -10,6 +10,13 @@ on: workflow_dispatch: + workflow_call: + inputs: + skip-sonar: + description: 'Skip SonarCloud scan' + type: boolean + default: false + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} @@ -193,7 +200,7 @@ jobs: analysis: name: Analysis - if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request' + if: inputs.skip-sonar != true && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request') runs-on: ubuntu-24.04 needs: [build, tasks] diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ad7091ded..e07381ca4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,202 +1,48 @@ -name: Publish Engine Plugin - +name: Publish to NPM on: - workflow_dispatch: - inputs: - version_bump: - description: 'Type of version bump to perform' - required: true - default: 'patch' - type: choice - options: - - patch - - minor - - major - npm_tag: - description: 'Custom npm tag (only needed for non-standard branches; release branches will use their major version tag automatically)' - required: false - default: 'beta' - type: string - dry_run: - description: 'Dry run (no actual publishing)' - required: false - default: true - type: boolean - - push: - branches: - - main - - 'release/v[0-9]*' - -concurrency: - group: publish-engine-plugin-${{ github.ref }} - -permissions: - contents: write - packages: write - + release: + types: + - published jobs: - determine-path: - name: Determine Workflow Path - runs-on: ubuntu-24.04 - outputs: - workflow-path: ${{ steps.check-path.outputs.workflow-path }} - version-bump: ${{ steps.check-path.outputs.version-bump }} - npm-tag: ${{ steps.check-path.outputs.npm-tag }} - - steps: - - name: Check out code - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Determine workflow path - id: check-path - run: bash .github/scripts/publish/determine-workflow-path.sh "${{ github.event_name }}" "${{ github.event.inputs.version_bump }}" "${{ github.event.inputs.npm_tag }}" - - build: - name: Build - needs: [determine-path] - if: needs.determine-path.outputs.workflow-path != 'skip' - runs-on: ubuntu-24.04 + ci: + uses: ./.github/workflows/check-pull-request.yml + secrets: inherit + with: + skip-sonar: true + publish: + needs: ci + runs-on: ubuntu-latest steps: - - name: Check out code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: fetch-depth: 0 + ref: ${{ github.event.release.tag_name }} - - name: Cache dependencies - uses: actions/cache@v4 - id: npm-install-cache + - uses: actions/setup-node@v5 with: - enableCrossOsArchive: true - key: npm-install-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - path: node_modules + node-version-file: .nvmrc + registry-url: 'https://registry.npmjs.org' + cache: 'npm' - - name: Cache build + - name: Cache webpack and build artifacts uses: actions/cache@v4 with: - enableCrossOsArchive: true - key: npm-build-${{ runner.os }}-${{ github.sha }} path: | - .server - .public - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - cache: 'npm' - node-version-file: .nvmrc + node_modules/.cache/webpack + node_modules/.cache/terser-webpack-plugin + key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('webpack.*.mjs', 'babel.config.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}- + ${{ runner.os }}-build- - name: Install dependencies - if: steps.npm-install-cache.outputs.cache-hit != 'true' run: npm ci - - name: Run build + - name: Build run: npm run build - publish: - name: Publish - needs: [determine-path, build] - if: needs.determine-path.outputs.workflow-path != 'skip' - runs-on: ubuntu-24.04 - environment: production - - steps: - - name: Check out code - uses: actions/checkout@v4 - with: - fetch-depth: 2 - ref: ${{ github.ref }} - - - name: Display workflow path - run: | - echo "⭐ Executing ${{ needs.determine-path.outputs.workflow-path }} workflow path" - echo "Version bump type: ${{ needs.determine-path.outputs.version-bump }}" - echo "NPM Tag: ${{ needs.determine-path.outputs.npm-tag || '' }}" - - - name: Restore dependencies - uses: actions/cache/restore@v4 - with: - enableCrossOsArchive: true - key: npm-install-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - path: node_modules - - - name: Restore build artifacts - uses: actions/cache/restore@v4 - with: - enableCrossOsArchive: true - key: npm-build-${{ runner.os }}-${{ github.sha }} - path: | - .server - .public - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - registry-url: https://registry.npmjs.org - scope: '@defra' - - - name: Determine version bump details - id: version-details - run: bash .github/scripts/publish/version-bump-details.sh "${{ github.ref_name }}" "${{ needs.determine-path.outputs.version-bump }}" - - - name: Update package version - run: | - echo "Bumping version: $VERSION_TYPE" - npm version $VERSION_TYPE --git-tag-version false --save - - - name: Commit and push updates - run: | - git config user.name github-actions - git config user.email github-actions@github.com - NEW_VERSION=$(npm pkg get version | tr -d \") - git commit -am "v$NEW_VERSION [skip ci]" && git push - - - name: Publish to npm with appropriate dist-tag - run: bash .github/scripts/publish/publish-to-npm.sh "${{ github.ref_name }}" "${{ needs.determine-path.outputs.workflow-path }}" "${{ needs.determine-path.outputs.npm-tag }}" "${{ github.event.inputs.dry_run }}" + - name: Publish + run: ./scripts/publish-package.sh "${{ github.event.release.tag_name }}" "${{ github.event.release.prerelease }}" env: - NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - - generate-docs: - name: Generate and Publish Documentation - needs: [publish] - if: needs.determine-path.outputs.workflow-path != 'skip' && (startsWith(github.ref, 'refs/heads/release/v') || github.ref == 'refs/heads/main') - runs-on: ubuntu-24.04 - permissions: - contents: read - pages: write - id-token: write - - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Restore dependencies - uses: actions/cache/restore@v4 - with: - enableCrossOsArchive: true - key: npm-install-${{ runner.os }}-${{ hashFiles('package-lock.json') }} - path: node_modules - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Generate and process documentation - run: bash .github/scripts/docs/generate-and-publish-docs.sh "${{ github.ref_name }}" "$(npm pkg get version | tr -d \")" - - - name: Setup Pages - uses: actions/configure-pages@v5 - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: './docs-site' - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/scripts/publish-package.sh b/scripts/publish-package.sh new file mode 100755 index 000000000..ab2708741 --- /dev/null +++ b/scripts/publish-package.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +set -e + +VERSION_PATTERN="^v([0-9]{1,}.[0-9]{1,}.[0-9]{1,})(-[0-9A-Za-z-].*)?$" +PRE_RELEASE_PATTERN="(-[0-9A-Za-z-].*)$" + +validate_arguments() { + if [ -z "$TAG_NAME" ]; then + echo "ERROR: TAG_NAME is required" + echo "Usage: $0 [dry_run]" + exit 1 + fi +} + +validate_version_format() { + if ! [[ "$TAG_NAME" =~ $VERSION_PATTERN ]]; then + echo "ERROR: FAILED TO MATCH VERSION_PATTERN" + echo "Tag name must be in format: v1.2.3 or v1.2.3-beta.1" + exit 1 + fi +} + +get_published_version() { + local major_version=$1 + local published=$(npm view "${PACKAGE_NAME}@^${major_version}.0.0" version --json 2>/dev/null | jq -r '.[-1]' 2>/dev/null || echo "") + + if [ -z "$published" ] || [ "$published" = "null" ]; then + echo "0.0.0" + else + echo "$published" + fi +} + +validate_version_bump() { + local new_version="${TAG_NAME#v}" + local major_version=$(echo "$new_version" | cut -d. -f1) + + echo "Package: ${PACKAGE_NAME}" + echo "Checking version constraints for v${major_version}.x line..." + + local published_version=$(get_published_version "$major_version") + + echo "Latest v${major_version}.x published: $published_version" + echo "New version to publish: $new_version" + + if npx --yes semver "$new_version" -r "<=$published_version" >/dev/null 2>&1; then + echo "ERROR: Version $new_version is not greater than published version $published_version in the v${major_version}.x line" + exit 1 + fi + + echo "✓ Version check passed" +} + +determine_release_tag() { + if [[ "$IS_PRE_RELEASE" == "true" ]] || [[ "$TAG_NAME" =~ $PRE_RELEASE_PATTERN ]]; then + echo "pre-release" + else + echo "latest" + fi +} + +publish_package() { + local release_tag=$1 + local new_version="${TAG_NAME#v}" + + echo "Publishing ${PACKAGE_NAME}@${new_version} to npm with tag: ${release_tag}" + + if [ "$DRY_RUN" = "true" ]; then + echo "[DRY RUN] Would run: npm version --no-git-tag-version ${TAG_NAME}" + echo "[DRY RUN] Would run: npm publish --access public --tag=${release_tag}" + else + npm version --no-git-tag-version "${TAG_NAME}" + npm publish --access public --tag="${release_tag}" + fi + + echo "✓ Successfully published ${PACKAGE_NAME}@${new_version}" +} + +main() { + PACKAGE_NAME=$(npm pkg get name | tr -d '"') + TAG_NAME="${1:-}" + IS_PRE_RELEASE="${2:-false}" + DRY_RUN="${3:-false}" + + validate_arguments + validate_version_format + validate_version_bump + + RELEASE_TAG=$(determine_release_tag) + publish_package "$RELEASE_TAG" +} + +main "$@"