diff --git a/.github/filters.yaml b/.github/filters.yaml index 621876a56197..99016d348124 100644 --- a/.github/filters.yaml +++ b/.github/filters.yaml @@ -34,6 +34,36 @@ frontend: &frontend cli: &cli - 'tools/dotcms-cli/**' +# Server-side REST endpoints the dotCMS CLI consumes at runtime. The CLI's +# integration tests (tools/dotcms-cli, @QuarkusTest + testcontainers) exercise a +# live dotCMS, so a change to one of these endpoints can break the CLI even when +# tools/dotcms-cli/** is untouched. +# +# Keep in sync with the @Path REST-client interfaces in +# tools/dotcms-cli/api-data-model/src/main/java/com/dotcms/api/*API.java +cli_endpoints: &cli_endpoints + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/asset/**' # AssetAPI -> /v1/assets + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/folder/**' # FolderAPI -> /v1/folder + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/site/**' # SiteAPI -> /v1/site + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/contenttype/**' # ContentTypeAPI -> /v1/contenttype + - 'dotCMS/src/main/java/com/dotcms/rest/api/v2/languages/**' # LanguageAPI -> /v2/languages + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/workflow/**' # WorkflowAPI -> /v1/workflow + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/authentication/**' # AuthenticationAPI -> /v1/authentication + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/user/**' # UserAPI -> /v1/users + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/event/**' # AnalyticsAPI -> /v1/analytics/content/event + # Asset push/pull is the CLI's heaviest workflow; WebAssetHelper (in the asset + # package above) delegates into these asset-specific business APIs. + - 'dotCMS/src/main/java/com/dotmarketing/portlets/fileassets/business/**' # FileAssetAPI + - 'dotCMS/src/main/java/com/dotcms/browser/**' # BrowserAPI + - 'dotCMS/src/main/java/com/dotcms/rest/api/v1/temp/**' # TempFileAPI (CLI file uploads on the push path) + +# CLI test trigger: run the CLI integration tests when the CLI sources change OR +# when any server endpoint the CLI consumes changes. Distinct from `cli` (which +# gates the GraalVM native build) so endpoint-only changes don't force a rebuild. +cli_test: &cli_test + - *cli + - *cli_endpoints + sdk_libs: - 'core-web/libs/sdk/**' diff --git a/.github/workflows/cicd_1-pr.yml b/.github/workflows/cicd_1-pr.yml index ff6a18401831..0ad7d730bedb 100644 --- a/.github/workflows/cicd_1-pr.yml +++ b/.github/workflows/cicd_1-pr.yml @@ -82,7 +82,11 @@ jobs: postman: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }} karate: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }} frontend: ${{ fromJSON(needs.initialize.outputs.filters).frontend == 'true' }} - cli: ${{ fromJSON(needs.initialize.outputs.filters).cli == 'true' || fromJSON(needs.initialize.outputs.filters).backend == 'true' }} + # Run CLI tests only when the CLI sources or a REST endpoint the CLI consumes + # change (cli_test filter). A deep backend/service-layer change that alters CLI + # behavior without touching a watched endpoint is covered by the trunk CLI run + # (cicd_3-trunk.yml gates CLI tests on the broad `backend` filter post-merge). + cli: ${{ fromJSON(needs.initialize.outputs.filters).cli_test == 'true' }} e2e: ${{ fromJSON(needs.initialize.outputs.filters).build == 'true' }} secrets: DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }} diff --git a/.github/workflows/cicd_2-merge-queue.yml b/.github/workflows/cicd_2-merge-queue.yml index 0af9b08b50c1..1a60f7a3bd16 100644 --- a/.github/workflows/cicd_2-merge-queue.yml +++ b/.github/workflows/cicd_2-merge-queue.yml @@ -31,7 +31,11 @@ jobs: postman: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }} karate: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }} frontend: ${{ fromJSON(needs.initialize.outputs.filters).frontend == 'true' }} - cli: ${{ fromJSON(needs.initialize.outputs.filters).cli == 'true' || fromJSON(needs.initialize.outputs.filters).backend == 'true' }} + # Run CLI tests only when the CLI sources or a REST endpoint the CLI consumes + # change (cli_test filter). A deep backend/service-layer change that alters CLI + # behavior without touching a watched endpoint is covered by the trunk CLI run + # (cicd_3-trunk.yml gates CLI tests on the broad `backend` filter post-merge). + cli: ${{ fromJSON(needs.initialize.outputs.filters).cli_test == 'true' }} e2e: false secrets: DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }} diff --git a/.github/workflows/cicd_3-trunk.yml b/.github/workflows/cicd_3-trunk.yml index bcfe6cb7c0b9..d34e230eff93 100644 --- a/.github/workflows/cicd_3-trunk.yml +++ b/.github/workflows/cicd_3-trunk.yml @@ -95,6 +95,12 @@ jobs: java-version: ${{ github.event.inputs.java-version || '' }} maven-compiler-release: ${{ github.event.inputs.maven-compiler-release || '' }} artifact-suffix: ${{ github.event.inputs.artifact-suffix || '' }} + # Post-merge safety net for the CLI. The PR/merge-queue pipelines gate CLI + # tests on the narrow `cli_test` filter (CLI sources + the REST endpoints the + # CLI consumes), so a deeper business-layer change can slip through untested. + # On trunk we run the CLI tests for any backend change to catch that on main + # before it reaches a release. (Skipped on java-version overrides, like build-cli.) + cli: ${{ !github.event.inputs.java-version && fromJSON(needs.initialize.outputs.filters).backend == 'true' }} secrets: DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }} permissions: @@ -112,11 +118,17 @@ jobs: SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} # CLI Build job - builds CLI artifacts - # Skipped when java-version is overridden due to GraalVM/Quarkus compatibility requirements + # Skipped when java-version is overridden due to GraalVM/Quarkus compatibility requirements. + # Also skipped when the CLI sources are unchanged: the CLI is self-contained under + # tools/dotcms-cli/** (its only com.dotcms dependency, dotcms-api-data-model, lives in that + # same subtree), so the `cli` path filter is a correct change-key. This avoids the expensive + # 3-OS native matrix (incl. two macOS runners) on every trunk push when the CLI didn't change. + # When skipped, the deployment job's deploy-cli input is also disabled (see below) so it does + # not try to download CLI artifacts that were never produced. build-cli: name: CLI Build needs: [ initialize,test ] - if: always() && !failure() && !cancelled() && !inputs.java-version + if: always() && !failure() && !cancelled() && !inputs.java-version && fromJSON(needs.initialize.outputs.filters).cli == 'true' uses: ./.github/workflows/cicd_comp_cli-native-build-phase.yml with: buildNativeImage: true @@ -130,7 +142,10 @@ jobs: uses: ./.github/workflows/cicd_comp_deployment-phase.yml with: artifact-run-id: ${{ needs.initialize.outputs.artifact-run-id }} - deploy-cli: true + # Only deploy the CLI when it was actually (re)built this run. build-cli is skipped when the + # CLI sources are unchanged or java-version is overridden; in those cases there are no fresh + # cli-artifacts-* to deploy, so deploy-cli must be false to avoid a missing-artifact failure. + deploy-cli: ${{ !inputs.java-version && fromJSON(needs.initialize.outputs.filters).cli == 'true' }} publish-npm-sdk-libs: ${{ fromJSON(needs.initialize.outputs.filters).sdk_libs == 'true' && github.event_name != 'workflow_dispatch' }} environment: trunk # tag-identifier intentionally omitted: trunk uses only the environment name as its tag diff --git a/.github/workflows/cicd_5-lts.yml b/.github/workflows/cicd_5-lts.yml index e592bcf95a93..252ba51def50 100644 --- a/.github/workflows/cicd_5-lts.yml +++ b/.github/workflows/cicd_5-lts.yml @@ -90,7 +90,7 @@ jobs: postman: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }} karate: ${{ fromJSON(needs.initialize.outputs.filters).backend == 'true' }} frontend: ${{ fromJSON(needs.initialize.outputs.filters).frontend == 'true' }} - cli: ${{ fromJSON(needs.initialize.outputs.filters).cli == 'true' }} + cli: ${{ fromJSON(needs.initialize.outputs.filters).cli_test == 'true' }} e2e: false secrets: DOTCMS_LICENSE: ${{ secrets.DOTCMS_LICENSE }} diff --git a/.github/workflows/cicd_comp_initialize-phase.yml b/.github/workflows/cicd_comp_initialize-phase.yml index aae04e712f82..f0dd9d0949a6 100644 --- a/.github/workflows/cicd_comp_initialize-phase.yml +++ b/.github/workflows/cicd_comp_initialize-phase.yml @@ -163,9 +163,9 @@ jobs: # # test_filters: Subset of build_test_filters affected by CICD_SKIP_TESTS # ============================================================ - build_test_filters="frontend cli backend build jvm_unit_test" + build_test_filters="frontend cli cli_test backend build jvm_unit_test" info_filters="sdk_libs documentation cicd" - test_filters="frontend cli backend jvm_unit_test" + test_filters="frontend cli cli_test backend jvm_unit_test" declare -A results diff --git a/.github/workflows/cicd_release-cli.yml b/.github/workflows/cicd_release-cli.yml index be6f1fe66a6b..9667faf2f366 100644 --- a/.github/workflows/cicd_release-cli.yml +++ b/.github/workflows/cicd_release-cli.yml @@ -13,7 +13,6 @@ # - Performs pre-checks and version management # - Builds and packages the CLI tool # - Publishes the CLI as an NPM package -# - Sends Slack notifications upon successful release # - Cleans up temporary branches after release name: Release - CLI @@ -29,7 +28,7 @@ on: default: '1.0.0-SNAPSHOT' required: false dry-run: - description: 'Dry run' + description: 'Dry run - skip ALL publishing (npm, GitHub release, Artifactory) and JReleaser uploads' default: 'false' required: false @@ -66,6 +65,14 @@ jobs: RELEASE_VERSION: ${{ steps.version.outputs.RELEASE_VERSION }} HEAD: ${{ steps.version.outputs.HEAD }} AUXILIARY_BRANCH: ${{ steps.version.outputs.AUXILIARY_BRANCH }} + # 'true' -> CLI sources changed since the previous release: do a full native build. + # 'false' -> unchanged: skip the native build/JReleaser and republish the previously + # published binaries under the new version (npm: publish-npm-package; + # GitHub release + Artifactory: reuse-release). + CLI_CHANGED: ${{ steps.cli-changes.outputs.CLI_CHANGED }} + # Maven/JReleaser version of the previous release (tag without the leading 'v'), used by the + # reuse path to locate the prior release's artifacts. Empty when there is no previous tag. + PREV_RELEASE_VERSION: ${{ steps.cli-changes.outputs.PREV_RELEASE_VERSION }} steps: # Log GitHub context for debugging - name: 'Log GitHub context' @@ -87,8 +94,12 @@ jobs: echo "::endgroup::" # Checkout the repository + # fetch-depth: 0 brings full history + all tags so we can diff this release + # against the previous release tag to decide whether the CLI actually changed. - name: 'Checkout' uses: actions/checkout@v4 + with: + fetch-depth: 0 # Setup git configuration - name: 'Setup git config' @@ -96,6 +107,52 @@ jobs: git config user.name "${{ secrets.CI_MACHINE_USER }}" git config user.email "dotCMS-Machine-User@dotcms.com" + # Determine whether the CLI sources changed since the previous release. + # The CLI is self-contained under tools/dotcms-cli/** (its only com.dotcms + # dependency, dotcms-api-data-model, lives in that same subtree), so a path + # diff against the previous release tag is a correct change-key. + # + # Fail safe: any time we cannot confidently prove "unchanged" (manual dispatch, + # no previous tag, shallow history) we set CLI_CHANGED=true and do a full build. + # A false "unchanged" would silently ship stale binaries under a new version. + - name: 'Detect CLI source changes' + id: cli-changes + run: | + set -euo pipefail + CURRENT_TAG="${{ github.event.release.tag_name }}" + + if [[ -z "${CURRENT_TAG}" ]]; then + echo "::notice::No release tag in context (manual dispatch) - building CLI unconditionally" + echo "CLI_CHANGED=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + git fetch --tags --quiet || true + + # Previous non-LTS v* tag by creation order, excluding the current one. + PREV_TAG=$(git tag --sort=-version:refname --list 'v*' \ + | grep -viE 'lts' \ + | grep -vxF "${CURRENT_TAG}" \ + | head -n1 || true) + + if [[ -z "${PREV_TAG}" ]]; then + echo "::notice::No previous release tag found - building CLI" + echo "CLI_CHANGED=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Expose the previous release version (without leading 'v') for the reuse path. + echo "PREV_RELEASE_VERSION=${PREV_TAG#v}" >> "$GITHUB_OUTPUT" + + echo "::notice::Comparing tools/dotcms-cli between ${PREV_TAG} and ${CURRENT_TAG}" + if git diff --quiet "${PREV_TAG}" "${CURRENT_TAG}" -- tools/dotcms-cli/; then + echo "::notice::No CLI source changes since ${PREV_TAG} - will reuse previously published binaries" + echo "CLI_CHANGED=false" >> "$GITHUB_OUTPUT" + else + echo "::notice::CLI source changed since ${PREV_TAG} - full native build" + echo "CLI_CHANGED=true" >> "$GITHUB_OUTPUT" + fi + # Set release version and create auxiliary branch - name: 'Set release version' id: version @@ -123,10 +180,13 @@ jobs: echo "AUXILIARY_BRANCH=$AUXILIARY_BRANCH" >> "$GITHUB_OUTPUT" # Build the release + # Skipped when the CLI is unchanged: the only downstream consumer in that case is the + # publish-npm-package reuse path, which pulls the previously published binaries and needs + # neither the core .m2 nor freshly built artifacts. build: name: Release Build needs: [ initialize, precheck ] - if: needs.initialize.outputs.found_artifacts == 'false' + if: needs.initialize.outputs.found_artifacts == 'false' && needs.precheck.outputs.CLI_CHANGED == 'true' uses: ./.github/workflows/cicd_comp_build-phase.yml with: core-build: true @@ -138,11 +198,12 @@ jobs: contents: read packages: write - # Build CLI artifacts + # Build CLI artifacts (the expensive 3-OS GraalVM native matrix, incl. two macOS runners). + # Skipped when the CLI is unchanged since the previous release - we reuse the prior binaries. build-cli: name: Release CLI Build needs: [ initialize, precheck, build ] - if: always() && !failure() && !cancelled() + if: always() && !failure() && !cancelled() && needs.precheck.outputs.CLI_CHANGED == 'true' uses: ./.github/workflows/cicd_comp_cli-native-build-phase.yml with: buildNativeImage: true @@ -150,9 +211,11 @@ jobs: version: ${{ needs.precheck.outputs.RELEASE_VERSION }} branch: ${{ needs.precheck.outputs.AUXILIARY_BRANCH }} - # Perform the release + # Perform the release (JReleaser: native binaries -> GitHub release assets + Artifactory). + # Skipped when the CLI is unchanged; the previously published artifacts already cover this. release: needs: [ precheck, build-cli ] + if: always() && !failure() && !cancelled() && needs.precheck.outputs.CLI_CHANGED == 'true' runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} steps: - name: 'Check out repository' @@ -184,11 +247,107 @@ jobs: artifacts-from: ${{ env.ARTIFACT_RUN_ID }} version: ${{ needs.precheck.outputs.RELEASE_VERSION }} + # Reuse path (GitHub release + Artifactory parity). + # When the CLI is unchanged we skip the JReleaser `release` job above and instead republish the + # previous release's native artifacts under the new version, mirroring the two channels that + # JReleaser feeds (see tools/dotcms-cli/jreleaser.yml - keep this job in sync with it): + # 1. GitHub release -> tag `dotcms-cli-` with the native zips + runner jar attached + # 2. Artifactory -> libs-release-local/com/dotcms/dotcms-cli// + # The binaries are byte-identical to the prior release; only the version in the filename and + # coordinates advances. npm is handled separately in publish-npm-package. + # Note: JReleaser-generated checksums/signatures are NOT reproduced here - only the binary + # artifacts (.zip / .jar) are re-published. + reuse-release: + name: Reuse CLI Release + needs: [ precheck ] + if: needs.precheck.outputs.CLI_CHANGED == 'false' + runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} + permissions: + contents: write + env: + NEW_VERSION: ${{ needs.precheck.outputs.RELEASE_VERSION }} + PREV_VERSION: ${{ needs.precheck.outputs.PREV_RELEASE_VERSION }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DRY_RUN: ${{ github.event.inputs.dry-run || 'false' }} + steps: + - name: 'Checkout' + uses: actions/checkout@v4 + + - name: 'Download previous CLI release assets' + run: | + set -euo pipefail + mkdir -p prev new + echo "::notice::Reusing dotcms-cli-${PREV_VERSION} artifacts for new version ${NEW_VERSION}" + gh release download "dotcms-cli-${PREV_VERSION}" \ + --repo "${{ github.repository }}" \ + --dir prev + ls -l prev + + - name: 'Re-stamp artifacts to new version' + run: | + set -euo pipefail + shopt -s nullglob + artifacts=( prev/dotcms-cli-${PREV_VERSION}*.zip prev/dotcms-cli-${PREV_VERSION}*.jar ) + if [ ${#artifacts[@]} -eq 0 ]; then + echo "::error::No reusable .zip/.jar artifacts found in release dotcms-cli-${PREV_VERSION}" + exit 1 + fi + for f in "${artifacts[@]}"; do + base=$(basename "$f") + newname="${base/dotcms-cli-${PREV_VERSION}/dotcms-cli-${NEW_VERSION}}" + cp "$f" "new/${newname}" + done + echo "Re-stamped artifacts:" + ls -l new + + - name: 'Create GitHub release for new version' + run: | + set -euo pipefail + if [ "${DRY_RUN}" = "true" ]; then + echo "::notice::[dry-run] would create release dotcms-cli-${NEW_VERSION} with assets: $(cd new && echo *)" + exit 0 + fi + gh release create "dotcms-cli-${NEW_VERSION}" new/* \ + --repo "${{ github.repository }}" \ + --title "dotcms-cli - ${NEW_VERSION}" \ + --notes "Reused binaries from dotcms-cli-${PREV_VERSION} (CLI sources unchanged since the previous release)." + + - name: 'Upload artifacts to Artifactory' + env: + ARTIFACTORY_USER: ${{ secrets.EE_REPO_USERNAME }} + ARTIFACTORY_PASS: ${{ secrets.EE_REPO_PASSWORD }} + run: | + set -euo pipefail + BASE_URL="https://repo.dotcms.com/artifactory/libs-release-local/com/dotcms/dotcms-cli/${NEW_VERSION}" + for f in new/*; do + base=$(basename "$f") + if [ "${DRY_RUN}" = "true" ]; then + echo "::notice::[dry-run] would upload ${base} -> ${BASE_URL}/${base}" + continue + fi + echo "::notice::Uploading ${base} -> ${BASE_URL}/${base}" + curl -fsSL --user "${ARTIFACTORY_USER}:${ARTIFACTORY_PASS}" -T "$f" "${BASE_URL}/${base}" + done + # Publish NPM package + # Runs in both paths: the changed path consumes freshly built cli-artifacts-*, the unchanged + # path repackages the previously published binaries. Uses !failure() (not success()) so the + # intentionally-skipped build/build-cli/release jobs of the unchanged path don't block it, + # while still bailing if any job that DID run failed. publish-npm-package: name: "Publish NPM Package" - if: success() # Run only if explicitly indicated and successful - needs: [ precheck, build, build-cli, release ] + if: >- + ${{ + !cancelled() && + needs.precheck.result == 'success' && + (needs.reuse-release.result == 'success' || needs.reuse-release.result == 'skipped') && + (needs.release.result == 'success' || needs.release.result == 'skipped') + }} + # On the changed path (CLI_CHANGED=true): release runs and reuse-release is skipped. + # On the unchanged path (CLI_CHANGED=false): reuse-release runs and release is skipped. + # Requiring each to be 'success' or 'skipped' makes the mutual-exclusion explicit and + # ensures a failed release or reuse-release blocks the npm publish. + needs: [ precheck, build, build-cli, release, reuse-release ] runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} steps: - name: 'Checkout code' @@ -207,13 +366,15 @@ jobs: run: pip install jinja2-cli - name: 'Download all build artifacts' + if: needs.precheck.outputs.CLI_CHANGED == 'true' uses: actions/download-artifact@v4 with: path: ${{ github.workspace }}/artifacts pattern: cli-artifacts-* merge-multiple: true - # Determines the NPM package version and tag + # Determines the NPM package version and tag (runs in both paths - depends only on + # RELEASE_VERSION, not on build artifacts). # Distinguishes between snapshots and releases - name: 'Dynamic configuration of NPM package Version and Tag' env: @@ -302,6 +463,7 @@ jobs: # Adds the postinstall.js script # Generates the package.json file with Jinja2 - name: 'NPM Package setup' + if: needs.precheck.outputs.CLI_CHANGED == 'true' working-directory: ${{ github.workspace }}/tools/dotcms-cli/npm/ env: MVN_PACKAGE_VERSION: ${{ needs.precheck.outputs.RELEASE_VERSION }} @@ -326,34 +488,56 @@ jobs: echo "::endgroup::" - name: 'NPM Package tree' + if: needs.precheck.outputs.CLI_CHANGED == 'true' run: ls -R ${{ github.workspace }}/tools/dotcms-cli/npm/ - name: 'Publish to NPM registry' + if: needs.precheck.outputs.CLI_CHANGED == 'true' working-directory: ${{ github.workspace }}/tools/dotcms-cli/npm/ env: NPM_AUTH_TOKEN: ${{ secrets.NPM_ORG_TOKEN }} + DRY_RUN: ${{ github.event.inputs.dry-run || 'false' }} run: | echo "//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}" > ~/.npmrc - npm publish --access public --tag ${NPM_PACKAGE_VERSION_TAG} - - - name: Slack Notification - continue-on-error: true - uses: rtCamp/action-slack-notify@v2 + [ "${DRY_RUN}" = "true" ] && DRY_RUN_FLAG="--dry-run" || DRY_RUN_FLAG="" + npm publish --access public --tag ${NPM_PACKAGE_VERSION_TAG} ${DRY_RUN_FLAG} + + # Reuse path: CLI sources are unchanged since the previous release. Instead of the native + # rebuild, pull the currently published 'latest' tarball (which bundles the native binaries + # plus the postinstall wrapper), bump only its version field, and republish. The binaries + # stay byte-identical; only the version number advances to track the dotCMS release. + # We read the source version from npm ('latest') rather than reconstructing it from the git + # tag, sidestepping the leading-zero normalization applied to release versions above. + - name: 'Reuse previous CLI release and publish' + if: needs.precheck.outputs.CLI_CHANGED == 'false' + working-directory: ${{ runner.temp }} env: - SLACK_WEBHOOK: ${{ secrets.RELEASE_SLACK_WEBHOOK }} - SLACK_TITLE: "Important news!" - SLACK_MESSAGE: " This automated script is excited to announce the release of a new version of *dotCLI* `${{ needs.precheck.outputs.RELEASE_VERSION }}` :package: is available on the `NPM` registry!" - SLACK_USERNAME: dotBot - SLACK_MSG_AUTHOR: " " - MSG_MINIMAL: true - SLACK_FOOTER: "" - SLACK_ICON: https://avatars.slack-edge.com/temp/2021-12-08/2830145934625_e4e464d502865ff576e4.png + NPM_AUTH_TOKEN: ${{ secrets.NPM_ORG_TOKEN }} + DRY_RUN: ${{ github.event.inputs.dry-run || 'false' }} + run: | + set -euo pipefail + PACKAGE_FULL_NAME="${NPM_PACKAGE_SCOPE}/${NPM_PACKAGE_NAME}" + echo "//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}" > ~/.npmrc + + PREV_NPM_VERSION=$(npm view "${PACKAGE_FULL_NAME}" version) + echo "::notice::CLI unchanged - repackaging ${PACKAGE_FULL_NAME}@${PREV_NPM_VERSION} as ${NPM_PACKAGE_VERSION} (tag: ${NPM_PACKAGE_VERSION_TAG})" + + npm pack "${PACKAGE_FULL_NAME}@${PREV_NPM_VERSION}" + tar -xzf ./*.tgz # extracts to ./package + cd package + # Bump only the version; binaries and scripts remain byte-identical to the prior release. + npm version "${NPM_PACKAGE_VERSION}" --no-git-tag-version --allow-same-version + [ "${DRY_RUN}" = "true" ] && DRY_RUN_FLAG="--dry-run" || DRY_RUN_FLAG="" + npm publish --access public --tag "${NPM_PACKAGE_VERSION_TAG}" ${DRY_RUN_FLAG} # Clean up temporary branches clean-up: name: "Clean Up" - if: ${{ needs.precheck.outputs.AUXILIARY_BRANCH != '' }} - needs: [ precheck, build, build-cli, release, publish-npm-package ] + # always(): the temporary version-update branch must be deleted even when an + # upstream job fails (otherwise it leaks). AUXILIARY_BRANCH is only set when + # precheck succeeded, so this still no-ops when there is nothing to clean up. + if: ${{ always() && needs.precheck.outputs.AUXILIARY_BRANCH != '' }} + needs: [ precheck, build, build-cli, release, reuse-release, publish-npm-package ] runs-on: ubuntu-${{ vars.UBUNTU_RUNNER_VERSION || '24.04' }} steps: - name: Checkout Repository