Publish workflow: issue_comment #451
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: code-npm-publish-release-and-snapshot | |
| run-name: "Publish workflow: ${{ github.event_name }}" | |
| on: | |
| issue_comment: | |
| types: [created] | |
| pull_request: | |
| types: [closed] | |
| branches: ["main", "main-*"] | |
| paths: ["code/**", ".github/workflows/code**"] | |
| workflow_dispatch: | |
| inputs: | |
| BASELINE: | |
| description: "Baseline branch" | |
| required: true | |
| default: "main" | |
| RELEASE_TYPE: | |
| description: "Release type to use" | |
| required: true | |
| default: "release-type/minor" | |
| type: choice | |
| options: | |
| - "release-type/hotfix" | |
| - "release-type/multi-hotfix" | |
| - "release-type/major" | |
| - "release-type/minor" | |
| - "release-type/patch" | |
| jobs: | |
| publish-snapshot: | |
| name: Publish Snapshot | |
| concurrency: code-publish-snapshot-${{ github.ref }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| if: > | |
| github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request != null && | |
| github.event.comment.body == '/publish-snapshot' | |
| runs-on: ubuntu-24.04 | |
| env: | |
| ASDF_BRANCH_VERSION: 0.18.0 | |
| steps: | |
| - name: Validate admin permissions | |
| id: permission-check | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| with: | |
| script: | | |
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| username: context.actor | |
| }); | |
| const permission = data.permission; | |
| core.info(`User permission level: ${permission}`); | |
| if (permission !== 'admin') { | |
| core.setFailed(`User @${context.actor} is not a repository admin.`); | |
| } | |
| - name: Get input parameters | |
| run: | | |
| RELEASE_LABELS="${{ join(github.event.issue.labels.*.name, ', ') }}" | |
| if [[ $RELEASE_LABELS != *release-type/* ]]; then | |
| RELEASE_LABELS="$RELEASE_LABELS, release-type/minor" | |
| fi | |
| echo "RELEASE_LABELS=$RELEASE_LABELS" >> "$GITHUB_ENV" | |
| - name: Checkout PR branch | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: refs/pull/${{ github.event.issue.number }}/head | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: NPM / Setup Dependencies Cache | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.npm | |
| key: ${{ runner.os }}-node-${{ hashFiles('code/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-node- | |
| - name: Restore asdf Cache | |
| id: asdf-cache | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| continue-on-error: true | |
| with: | |
| path: ~/.asdf | |
| key: ${{ runner.os }}-asdf-${{ env.ASDF_BRANCH_VERSION }}-${{ hashFiles('code/.tool-versions') }} | |
| - name: Save tool-versions content | |
| run: | | |
| { | |
| echo "TOOL_VERSIONS<<EOF" | |
| cat code/.tool-versions | |
| echo "EOF" | |
| } >> "$GITHUB_ENV" | |
| - name: Setup asdf environment | |
| uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 # v4.0.0 | |
| if: steps.asdf-cache.outputs.cache-hit != 'true' | |
| with: | |
| tool_versions: ${{ env.TOOL_VERSIONS }} | |
| asdf_version: ${{ env.ASDF_BRANCH_VERSION }} | |
| - name: Save asdf Cache | |
| if: steps.asdf-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.asdf | |
| key: ${{ runner.os }}-asdf-${{ env.ASDF_BRANCH_VERSION }}-${{ hashFiles('code/.tool-versions') }} | |
| - name: Restore asdf shims to PATH | |
| if: steps.asdf-cache.outputs.cache-hit == 'true' | |
| run: | | |
| echo "$HOME/.asdf/bin" >> "$GITHUB_PATH" | |
| echo "$HOME/.asdf/shims" >> "$GITHUB_PATH" | |
| cp code/.tool-versions "$HOME/.tool-versions" | |
| - name: NPM / Set npmrc registry | |
| env: | |
| NPM_REGISTRY: "https://registry.npmjs.org/" | |
| run: | | |
| echo "registry=${NPM_REGISTRY}" >> /home/runner/.npmrc | |
| echo "@inditextech:registry=${NPM_REGISTRY}" >> /home/runner/.npmrc | |
| - name: NPM / Create Cache Folders | |
| run: | | |
| mkdir -p "$HOME"/.npm | |
| - name: Setup patch release type version | |
| if: contains(env.RELEASE_LABELS, 'release-type/hotfix') | |
| || contains(env.RELEASE_LABELS, 'release-type/multi-hotfix') | |
| || contains(env.RELEASE_LABELS, 'release-type/patch') | |
| run: echo "RELEASE_VERSION=patch" >> "$GITHUB_ENV" | |
| - name: Setup minor release type version | |
| if: contains(env.RELEASE_LABELS, 'release-type/minor') | |
| run: echo "RELEASE_VERSION=minor" >> "$GITHUB_ENV" | |
| - name: Setup minor release type version when no label set and TBD | |
| if: ${{ !contains(env.RELEASE_LABELS, 'release-type') && vars.DEVELOPMENT_FLOW == 'trunk-based-development' }} | |
| run: echo "RELEASE_VERSION=minor" >> "$GITHUB_ENV" | |
| - name: Setup major release type version | |
| if: contains(env.RELEASE_LABELS, 'release-type/major') | |
| run: echo "RELEASE_VERSION=major" >> "$GITHUB_ENV" | |
| - name: Update CHANGELOG.md | |
| id: update-changelog | |
| uses: release-flow/keep-a-changelog-action@74931dec7ecdbfc8e38ac9ae7e8dd84c08db2f32 # v3.0.0 | |
| with: | |
| command: bump | |
| version: ${{ env.RELEASE_VERSION }} | |
| changelog: code/CHANGELOG.md | |
| fail-on-empty-release-notes: false | |
| keep-unreleased-section: true | |
| tag-prefix: "" | |
| - name: Define snapshot version | |
| working-directory: code | |
| env: | |
| RELEASE_VERSION: ${{ steps.update-changelog.outputs.version }} | |
| run: | | |
| CLEAN_VERSION=$(echo "$RELEASE_VERSION" | sed -E 's/-.*$//') | |
| PRE_RELEASE_VERSION="${CLEAN_VERSION}-SNAPSHOT.${{ github.run_number }}.${{ github.run_attempt }}" | |
| echo "SNAPSHOT_VERSION=$PRE_RELEASE_VERSION" >> "$GITHUB_ENV" | |
| - name: NPM / Prepare snapshot | |
| working-directory: code | |
| env: | |
| RELEASE_VERSION: ${{ env.SNAPSHOT_VERSION }} | |
| run: | | |
| npm run version:release | |
| npm run release:prepare | |
| - name: NPM / Ensure minimum npm version for trusted publishing | |
| run: | | |
| CURRENT_NPM=$(npm --version) | |
| REQUIRED_NPM="11.5.1" | |
| if printf '%s\n%s\n' "$REQUIRED_NPM" "$CURRENT_NPM" | sort -V -C; then | |
| echo "npm $CURRENT_NPM already meets the minimum requirement ($REQUIRED_NPM). Skipping upgrade." | |
| else | |
| echo "npm $CURRENT_NPM is below the minimum requirement ($REQUIRED_NPM). Upgrading…" | |
| npm install -g npm@^11.5.1 --registry="https://registry.npmjs.org/" | |
| asdf reshim nodejs | |
| fi | |
| echo "node path: $(command -v node)" | |
| echo "npm path: $(command -v npm)" | |
| node --version | |
| npm --version | |
| - name: NPM / Verify packages exist in registry | |
| id: verify-packages | |
| working-directory: code | |
| run: | | |
| MISSING=() | |
| for pkg_dir in packages/*/; do | |
| PKG_NAME=$(node -p "require('./$pkg_dir/package.json').name") | |
| ENCODED_PKG_NAME=$(node -p "encodeURIComponent(process.argv[1])" "$PKG_NAME") | |
| RESPONSE_FILE=$(mktemp) | |
| if ! HTTP_STATUS=$(curl -sS -o "$RESPONSE_FILE" -w "%{http_code}" \ | |
| --connect-timeout 10 \ | |
| --max-time 30 \ | |
| --retry 3 \ | |
| --retry-delay 2 \ | |
| --retry-all-errors \ | |
| "https://registry.npmjs.org/${ENCODED_PKG_NAME}"); then | |
| echo "::error::Failed to verify $PKG_NAME due to npm registry connectivity error" | |
| rm -f "$RESPONSE_FILE" | |
| exit 1 | |
| fi | |
| case "$HTTP_STATUS" in | |
| 200) | |
| PARSE_STATUS=0 | |
| node -e "try { const d=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); process.exit(Object.keys(d.versions||{}).length > 0 ? 0 : 1); } catch(e) { console.error('JSON parse error:', e.message); process.exit(2); }" "$RESPONSE_FILE" || PARSE_STATUS=$? | |
| case "$PARSE_STATUS" in | |
| 0) | |
| echo "✓ $PKG_NAME exists in registry with active versions" | |
| ;; | |
| 1) | |
| echo "✗ $PKG_NAME has no active versions (unpublished) — treating as new" | |
| MISSING+=("$PKG_NAME") | |
| ;; | |
| *) | |
| echo "::error::Failed to verify $PKG_NAME — npm registry returned invalid JSON (HTTP 200)" | |
| rm -f "$RESPONSE_FILE" | |
| exit 1 | |
| ;; | |
| esac | |
| ;; | |
| 404) | |
| echo "✗ $PKG_NAME NOT found in registry" | |
| MISSING+=("$PKG_NAME") | |
| ;; | |
| 429|5??) | |
| echo "::error::Failed to verify $PKG_NAME due to transient npm registry error (HTTP $HTTP_STATUS)" | |
| exit 1 | |
| ;; | |
| *) | |
| echo "::error::Failed to verify $PKG_NAME due to unexpected npm registry response (HTTP $HTTP_STATUS)" | |
| exit 1 | |
| ;; | |
| esac | |
| rm -f "$RESPONSE_FILE" | |
| done | |
| if [[ ${#MISSING[@]} -gt 0 ]]; then | |
| echo "MISSING_PACKAGES=${MISSING[*]}" >> "$GITHUB_ENV" | |
| echo "HAS_NEW_PACKAGES=true" >> "$GITHUB_ENV" | |
| echo "::warning::New packages detected: ${MISSING[*]} — will use NPM_TOKEN instead of OIDC" | |
| else | |
| echo "HAS_NEW_PACKAGES=false" >> "$GITHUB_ENV" | |
| echo "All packages verified in registry — OIDC publish is safe." | |
| fi | |
| - name: NPM / Publish Snapshot (OIDC + provenance) | |
| if: env.HAS_NEW_PACKAGES == 'false' | |
| env: | |
| NPM_CONFIG_PROVENANCE: "true" | |
| working-directory: code | |
| run: | | |
| npm run publish:snapshot | |
| - name: NPM / Publish Snapshot (NPM_TOKEN for new packages) | |
| if: env.HAS_NEW_PACKAGES == 'true' | |
| env: | |
| NPM_AUTH: ${{ secrets.NPM_TOKEN }} | |
| working-directory: code | |
| run: | | |
| if [ -z "${NPM_AUTH:-}" ]; then | |
| echo "::error::NPM_TOKEN secret is required for publishing new packages but is not set" | |
| exit 1 | |
| fi | |
| TEMP_NPMRC="$(mktemp)" | |
| trap 'rm -f "$TEMP_NPMRC"' EXIT | |
| cp ~/.npmrc "$TEMP_NPMRC" | |
| chmod 600 "$TEMP_NPMRC" | |
| echo "//registry.npmjs.org/:_authToken=${NPM_AUTH}" >> "$TEMP_NPMRC" | |
| export NPM_CONFIG_USERCONFIG="$TEMP_NPMRC" | |
| npm run publish:snapshot | |
| - name: Result / Comment on PR | |
| if: always() | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| with: | |
| script: | | |
| const status = '${{ job.status }}'; | |
| const authMethod = '${{ env.HAS_NEW_PACKAGES }}' === 'true' | |
| ? 'NPM_TOKEN (new packages detected)' | |
| : 'OIDC trusted publishing (provenance)'; | |
| const body = status === 'success' | |
| ? [ | |
| "### :rocket: NPM snapshot published", | |
| "", | |
| `- Version: \`${process.env.SNAPSHOT_VERSION}\``, | |
| "- Tag: `next`", | |
| `- Auth: ${authMethod}` | |
| ] | |
| : [ | |
| "### :x: NPM snapshot publication failed", | |
| "", | |
| "Please check the workflow logs for details." | |
| ] | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: body.join("\n") | |
| }); | |
| release: | |
| name: Release | |
| concurrency: code-release-${{ github.ref }} | |
| permissions: | |
| id-token: write | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| if: github.event_name == 'workflow_dispatch' | |
| || (github.event_name == 'pull_request' && github.event.pull_request.merged == true | |
| && !contains(join(github.event.pull_request.labels.*.name, ', '), 'skip-release') | |
| && (contains(join(github.event.pull_request.labels.*.name, ', '), 'release-type') | |
| || vars.DEVELOPMENT_FLOW == 'trunk-based-development' )) | |
| runs-on: ubuntu-24.04 | |
| env: | |
| ASDF_BRANCH_VERSION: 0.18.0 | |
| steps: | |
| - name: Validate admin permissions | |
| id: permission-check | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 | |
| with: | |
| script: | | |
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| username: context.actor | |
| }); | |
| const permission = data.permission; | |
| core.info(`User permission level: ${permission}`); | |
| if (permission !== 'admin') { | |
| core.setFailed(`User @${context.actor} is not a repository admin.`); | |
| } | |
| - name: Get input parameters | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| RELEASE_LABELS="${{ github.event.inputs.RELEASE_TYPE }}" | |
| else | |
| RELEASE_LABELS="${{ join(github.event.pull_request.labels.*.name, ', ') }}" | |
| if [[ $RELEASE_LABELS != *release-type/* ]]; then | |
| RELEASE_LABELS="$RELEASE_LABELS, release-type/minor" | |
| fi | |
| fi | |
| echo "RELEASE_LABELS=$RELEASE_LABELS" >> "$GITHUB_ENV" | |
| BASELINE_BRANCH=${{ github.event.inputs.BASELINE || github.ref }} | |
| echo "BASELINE_BRANCH=${BASELINE_BRANCH#refs/heads/}" >> "$GITHUB_ENV" | |
| - uses: actions/create-github-app-token@v3 | |
| id: app-token | |
| with: | |
| client-id: ${{ vars.APP_CLIENT_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - name: Checkout merge commit | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| ref: ${{ env.BASELINE_BRANCH }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: NPM / Setup Dependencies Cache | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.npm | |
| key: ${{ runner.os }}-node-${{ hashFiles('code/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-node- | |
| - name: Restore asdf Cache | |
| id: asdf-cache | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| continue-on-error: true | |
| with: | |
| path: ~/.asdf | |
| key: ${{ runner.os }}-asdf-${{ env.ASDF_BRANCH_VERSION }}-${{ hashFiles('code/.tool-versions') }} | |
| - name: Save tool-versions content | |
| run: | | |
| { | |
| echo "TOOL_VERSIONS<<EOF" | |
| cat code/.tool-versions | |
| echo "EOF" | |
| } >> "$GITHUB_ENV" | |
| - name: Setup asdf environment | |
| uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 # v4.0.0 | |
| if: steps.asdf-cache.outputs.cache-hit != 'true' | |
| with: | |
| tool_versions: ${{ env.TOOL_VERSIONS }} | |
| asdf_version: ${{ env.ASDF_BRANCH_VERSION }} | |
| - name: Save asdf Cache | |
| if: steps.asdf-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: ~/.asdf | |
| key: ${{ runner.os }}-asdf-${{ env.ASDF_BRANCH_VERSION }}-${{ hashFiles('code/.tool-versions') }} | |
| - name: Restore asdf shims to PATH | |
| if: steps.asdf-cache.outputs.cache-hit == 'true' | |
| run: | | |
| echo "$HOME/.asdf/bin" >> "$GITHUB_PATH" | |
| echo "$HOME/.asdf/shims" >> "$GITHUB_PATH" | |
| cp code/.tool-versions "$HOME/.tool-versions" | |
| - name: NPM / Set npmrc registry | |
| env: | |
| NPM_REGISTRY: "https://registry.npmjs.org/" | |
| run: | | |
| echo "registry=${NPM_REGISTRY}" >> /home/runner/.npmrc | |
| echo "@inditextech:registry=${NPM_REGISTRY}" >> /home/runner/.npmrc | |
| - name: NPM / Create Cache Folders | |
| run: | | |
| mkdir -p "$HOME"/.npm | |
| - name: Setup patch release type version | |
| if: contains(env.RELEASE_LABELS, 'release-type/hotfix') | |
| || contains(env.RELEASE_LABELS, 'release-type/multi-hotfix') | |
| || contains(env.RELEASE_LABELS, 'release-type/patch') | |
| run: echo "RELEASE_VERSION=patch" >> "$GITHUB_ENV" | |
| - name: Setup minor release type version | |
| if: contains(env.RELEASE_LABELS, 'release-type/minor') | |
| run: echo "RELEASE_VERSION=minor" >> "$GITHUB_ENV" | |
| - name: Setup minor release type version when no label set and TBD | |
| if: ${{ !contains(env.RELEASE_LABELS, 'release-type') && vars.DEVELOPMENT_FLOW == 'trunk-based-development' }} | |
| run: echo "RELEASE_VERSION=minor" >> "$GITHUB_ENV" | |
| - name: Setup major release type version | |
| if: contains(env.RELEASE_LABELS, 'release-type/major') | |
| run: echo "RELEASE_VERSION=major" >> "$GITHUB_ENV" | |
| - name: Prepare committer information | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| GPG_PRIVATE_KEY: ${{ secrets.CI_GPG_SECRET_KEY }} | |
| GPG_PASSPHRASE: ${{ secrets.CI_GPG_SECRET_KEY_PASSWORD }} | |
| run: | | |
| git config --global credential.helper store | |
| cat <<EOT >> ~/.git-credentials | |
| https://ci-user:$GITHUB_TOKEN@github.com | |
| EOT | |
| # GPG: non-interactive signing setup | |
| mkdir -p ~/.gnupg && chmod 700 ~/.gnupg | |
| printf 'allow-loopback-pinentry\nallow-preset-passphrase\n' > ~/.gnupg/gpg-agent.conf | |
| printf 'use-agent\npinentry-mode loopback\n' > ~/.gnupg/gpg.conf | |
| gpgconf --kill gpg-agent || true | |
| echo "$GPG_PRIVATE_KEY" | gpg --batch --import | |
| KEY_DATA=$(gpg --list-secret-keys --with-colons) | |
| echo "$KEY_DATA" | awk -F: '/^grp:/ {print $10}' | while read -r GRIP; do | |
| /usr/lib/gnupg/gpg-preset-passphrase --preset "$GRIP" <<< "$GPG_PASSPHRASE" | |
| done | |
| # Git: identity and signing | |
| FPR=$(echo "$KEY_DATA" | awk -F: '/^fpr:/ {print $10; exit}') | |
| git config user.name "srvcosoitxtech" | |
| git config user.email "oso@inditex.com" | |
| git config user.signingkey "$FPR" | |
| git config commit.gpgsign true | |
| git config tag.gpgsign true | |
| - name: Update CHANGELOG.md | |
| id: update-changelog | |
| uses: release-flow/keep-a-changelog-action@74931dec7ecdbfc8e38ac9ae7e8dd84c08db2f32 # v3.0.0 | |
| with: | |
| command: bump | |
| version: ${{ env.RELEASE_VERSION }} | |
| changelog: code/CHANGELOG.md | |
| fail-on-empty-release-notes: false | |
| keep-unreleased-section: true | |
| tag-prefix: "" | |
| - name: NPM / Prepare release | |
| working-directory: code | |
| env: | |
| RELEASE_VERSION: ${{ steps.update-changelog.outputs.version }} | |
| run: | | |
| npm run version:release | |
| npm run release:prepare | |
| - name: NPM / Ensure minimum npm version for trusted publishing | |
| run: | | |
| CURRENT_NPM=$(npm --version) | |
| REQUIRED_NPM="11.5.1" | |
| if printf '%s\n%s\n' "$REQUIRED_NPM" "$CURRENT_NPM" | sort -V -C; then | |
| echo "npm $CURRENT_NPM already meets the minimum requirement ($REQUIRED_NPM). Skipping upgrade." | |
| else | |
| echo "npm $CURRENT_NPM is below the minimum requirement ($REQUIRED_NPM). Upgrading…" | |
| npm install -g npm@^11.5.1 --registry="https://registry.npmjs.org/" | |
| asdf reshim nodejs | |
| fi | |
| echo "node path: $(command -v node)" | |
| echo "npm path: $(command -v npm)" | |
| node --version | |
| npm --version | |
| - name: NPM / Verify packages exist in registry | |
| id: verify-packages | |
| working-directory: code | |
| run: | | |
| MISSING=() | |
| for pkg_dir in packages/*/; do | |
| PKG_NAME=$(node -p "require('./$pkg_dir/package.json').name") | |
| ENCODED_PKG_NAME=$(node -p "encodeURIComponent(process.argv[1])" "$PKG_NAME") | |
| RESPONSE_FILE=$(mktemp) | |
| if ! HTTP_STATUS=$(curl -sS -o "$RESPONSE_FILE" -w "%{http_code}" \ | |
| --connect-timeout 10 \ | |
| --max-time 30 \ | |
| --retry 3 \ | |
| --retry-delay 2 \ | |
| --retry-all-errors \ | |
| "https://registry.npmjs.org/${ENCODED_PKG_NAME}"); then | |
| echo "::error::Failed to verify $PKG_NAME due to npm registry connectivity error" | |
| rm -f "$RESPONSE_FILE" | |
| exit 1 | |
| fi | |
| case "$HTTP_STATUS" in | |
| 200) | |
| PARSE_STATUS=0 | |
| node -e "try { const d=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); process.exit(Object.keys(d.versions||{}).length > 0 ? 0 : 1); } catch(e) { console.error('JSON parse error:', e.message); process.exit(2); }" "$RESPONSE_FILE" || PARSE_STATUS=$? | |
| case "$PARSE_STATUS" in | |
| 0) | |
| echo "✓ $PKG_NAME exists in registry with active versions" | |
| ;; | |
| 1) | |
| echo "✗ $PKG_NAME has no active versions (unpublished) — treating as new" | |
| MISSING+=("$PKG_NAME") | |
| ;; | |
| *) | |
| echo "::error::Failed to verify $PKG_NAME — npm registry returned invalid JSON (HTTP 200)" | |
| rm -f "$RESPONSE_FILE" | |
| exit 1 | |
| ;; | |
| esac | |
| ;; | |
| 404) | |
| echo "✗ $PKG_NAME NOT found in registry" | |
| MISSING+=("$PKG_NAME") | |
| ;; | |
| 429|5??) | |
| echo "::error::Failed to verify $PKG_NAME due to transient npm registry error (HTTP $HTTP_STATUS)" | |
| exit 1 | |
| ;; | |
| *) | |
| echo "::error::Failed to verify $PKG_NAME due to unexpected npm registry response (HTTP $HTTP_STATUS)" | |
| exit 1 | |
| ;; | |
| esac | |
| rm -f "$RESPONSE_FILE" | |
| done | |
| if [[ ${#MISSING[@]} -gt 0 ]]; then | |
| echo "MISSING_PACKAGES=${MISSING[*]}" >> "$GITHUB_ENV" | |
| echo "HAS_NEW_PACKAGES=true" >> "$GITHUB_ENV" | |
| echo "::warning::New packages detected: ${MISSING[*]} — will use NPM_TOKEN instead of OIDC" | |
| else | |
| echo "HAS_NEW_PACKAGES=false" >> "$GITHUB_ENV" | |
| echo "All packages verified in registry — OIDC publish is safe." | |
| fi | |
| - name: NPM / Release Perform (OIDC + provenance) | |
| if: env.HAS_NEW_PACKAGES == 'false' | |
| env: | |
| NPM_CONFIG_PROVENANCE: "true" | |
| working-directory: code | |
| run: | | |
| set -o pipefail | |
| npm run release:perform 2>&1 | tee /tmp/release-perform.log | |
| - name: NPM / Release Perform (NPM_TOKEN for new packages) | |
| if: env.HAS_NEW_PACKAGES == 'true' | |
| env: | |
| NPM_AUTH: ${{ secrets.NPM_TOKEN }} | |
| working-directory: code | |
| run: | | |
| if [ -z "${NPM_AUTH:-}" ]; then | |
| echo "::error::NPM_TOKEN secret is required for publishing new packages but is not set" | |
| exit 1 | |
| fi | |
| TEMP_NPMRC="$(mktemp)" | |
| trap 'rm -f "$TEMP_NPMRC"' EXIT | |
| cp ~/.npmrc "$TEMP_NPMRC" | |
| chmod 600 "$TEMP_NPMRC" | |
| echo "//registry.npmjs.org/:_authToken=${NPM_AUTH}" >> "$TEMP_NPMRC" | |
| export NPM_CONFIG_USERCONFIG="$TEMP_NPMRC" | |
| set -o pipefail | |
| npm run release:perform 2>&1 | tee /tmp/release-perform.log | |
| - name: Release / Commit and Tag | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| working-directory: code | |
| run: | | |
| git commit -S -i CHANGELOG.md -m "chore: Update CHANGELOG with ${{ steps.update-changelog.outputs.version }} version Signed-off-by: srvcosoitxtech <oso@inditex.com>" --no-verify | |
| git commit -S -i package.json package-lock.json packages/*/package.json -m "[npm-scripts] Release ${{ steps.update-changelog.outputs.version }} Signed-off-by: srvcosoitxtech <oso@inditex.com>" --no-verify | |
| git tag -a -m "Release ${{ steps.update-changelog.outputs.version }}" ${{ steps.update-changelog.outputs.version }} | |
| git push --atomic --no-verify origin HEAD ${{ steps.update-changelog.outputs.version }} | |
| - name: NPM / Set next development version | |
| working-directory: code | |
| run: | | |
| npm run version:development | |
| - name: Next Development Iteration / Commit Changes | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| working-directory: code | |
| run: | | |
| git commit -S -i package.json package-lock.json packages/*/package.json -m "[npm-scripts] prepare for next development iteration Signed-off-by: srvcosoitxtech <oso@inditex.com>" --no-verify | |
| git push --no-verify origin HEAD | |
| - name: Next Development Iteration / Create Sync Branch PR into Develop | |
| id: sync-branch-pr | |
| continue-on-error: true | |
| if: ${{ vars.DEVELOPMENT_FLOW != 'trunk-based-development' }} | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: | | |
| DEVELOP=${BASELINE_BRANCH/main/develop} | |
| echo "DEVELOP=$DEVELOP" >> "$GITHUB_OUTPUT" | |
| # Avoid creating sync PR if the corresponding development branch does not exist | |
| if [[ -z $(git ls-remote --heads origin "$DEVELOP") ]]; then | |
| echo "The '$DEVELOP' branch does not exist in remote. Skipping the creation of sync PR" | |
| else | |
| git checkout -b "automated/sync-release-${{ steps.update-changelog.outputs.version }}-to-$DEVELOP" | |
| git push --no-verify -u origin HEAD | |
| gh pr create --base "$DEVELOP" \ | |
| --title "Sync release ${{ steps.update-changelog.outputs.version }} to $DEVELOP" \ | |
| --body "**Automated Pull Request**" | |
| fi | |
| - name: GitHub Release / Create | |
| uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 | |
| id: github-release | |
| continue-on-error: true | |
| with: | |
| name: ${{ steps.update-changelog.outputs.version }} | |
| tag: ${{ steps.update-changelog.outputs.version }} | |
| token: ${{ steps.app-token.outputs.token }} | |
| body: | | |
| Check out the [changelog](code/CHANGELOG.md) for version ${{ steps.update-changelog.outputs.version }} | |
| - name: Comment in PR / Sync PR creation failed | |
| if: ${{ vars.DEVELOPMENT_FLOW != 'trunk-based-development' }} | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: | | |
| DEVELOP=${BASELINE_BRANCH/main/develop} | |
| git remote set-url origin "https://x-access-token:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY" | |
| # shellcheck disable=SC2140 | |
| if ! git ls-remote --exit-code --heads origin automated/sync-release-"${{ steps.update-changelog.outputs.version }}"-to-"$DEVELOP"; then | |
| gh pr comment "${{ github.event.number }}" --body "An error occurred creating the \`sync\` branch that synchronizes the \`$BASELINE_BRANCH\` and \`$DEVELOP\` branches. | |
| Please create a branch from \`$BASELINE_BRANCH\` (e.g. \`internal/sync-$BASELINE_BRANCH-with-$DEVELOP\`) and then create a pull request against \`$DEVELOP\` to finish the release process." | |
| fi | |
| - name: Comment in PR / Release creation failed | |
| if: ${{ steps.github-release.outcome == 'failure' }} | |
| env: | |
| GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: > | |
| gh pr comment ${{ github.event.number }} --body "An error occurred creating the GitHub Release. | |
| Don't panic! Your artifacts were successfully uploaded to the InditexTech npmjs registry and the new release tag was created. | |
| You can manually complete the release by creating it in the [releases](https://github.com/${{ github.repository }}/releases) | |
| page." |