Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/actions/artifacts/action.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
name: 'Frontend Artifacts Handler Action'
description: 'Github Action to handle frontend artifacts'
outputs:
tag:
description: 'The pushed version tag, set only once the version is published and tagged (release flow only)'
value: ${{ steps.handler.outputs.tag }}
previous_tag:
description: 'The previous tag of the same release line, empty for X.Y.1 (release flow only)'
value: ${{ steps.handler.outputs.previous_tag }}
is_latest:
description: 'true if the pushed tag is the highest version/* tag (release flow only)'
value: ${{ steps.handler.outputs.is_latest }}
runs:
using: 'composite'
steps:
- run: printenv
shell: bash
- run: cd "$GITHUB_ACTION_PATH/../../.." && pwd && npm ci
shell: bash
- run: npx ts-node "$GITHUB_ACTION_PATH/../../../tools/scripts/artifacts/main.ts"
- id: handler
run: npx ts-node "$GITHUB_ACTION_PATH/../../../tools/scripts/artifacts/main.ts"
shell: bash
5 changes: 5 additions & 0 deletions .github/workflow-templates/fe/fe-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ permissions:
actions: read
checks: write

# a new push to the branch cancels the run of the previous one
concurrency:
group: fe-main-${{ github.ref }}
cancel-in-progress: true

env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}
Expand Down
5 changes: 5 additions & 0 deletions .github/workflow-templates/fe/fe-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ permissions:
actions: read
checks: write

# a new push to the PR cancels the run of the previous one
concurrency:
group: fe-pr-${{ github.event.pull_request.number }}
cancel-in-progress: true

env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}
Expand Down
61 changes: 20 additions & 41 deletions .github/workflow-templates/fe/fe-release.yml
Original file line number Diff line number Diff line change
@@ -1,53 +1,32 @@
# Replaces the old two-file release (this file as tagger + fe-tag-pushed.yml / on_tag_pushed.yaml / tag_pushed.yml).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consumers with an old tag-push caller publish twice, and manual tags still release, with only this comment as protection. Please add a migration check, mark the old workflows as deprecated, and link PFM-ISSUE-34282. 🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, all three:

  • guard now sparse-checks out .github/workflows and fails while any workflow still calls workflows/fe-release.yml@ or workflows/fe-tag.yml@
  • fe-tag.yml and workflows/fe-release.yml print a ::warning:: deprecation notice on every run (behavior unchanged)
  • PFM-ISSUE-34282 is linked in the template comment and the README

With publish before tag, a leftover tag-push caller can no longer publish twice (it would fail with E403), but the guard now blocks the half-migrated state up front.

# Delete BOTH old caller files in the same commit that adds this one: the release flow still pushes
# the tag with the PAT, so a tag-push workflow left in the tree would publish that tag again (and
# release manually pushed tags). The release flow's guard job fails while an old caller exists.
# PFM-ISSUE-34282 (pushing the tag with GITHUB_TOKEN) removes the tag-push trigger for good.
name: 'Frontend Release CI'
on:
push:
branches:
- 'release/*'
workflow_dispatch:
inputs:
force:
description: 'Tag and publish (release/*) or publish a snapshot of all projects (default branch). Unticked: build and test only, nothing is tagged or published.'
type: boolean
default: false

permissions:
contents: write
pull-requests: write
actions: read
checks: write

env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}
# one release run per branch at a time; a running release is never cancelled
concurrency:
group: fe-release-${{ github.ref }}
cancel-in-progress: false

jobs:
install-deps:
uses: collaborationFactory/github-actions/.github/workflows/fe-install-deps.yml@master
release-flow:
uses: collaborationFactory/github-actions/.github/workflows/fe-release-flow.yml@master
with:
GHA_REF: ''
secrets:
DOT_NPMRC: ${{ secrets.DOT_NPMRC }}

e2e-tests:
needs: install-deps
uses: collaborationFactory/github-actions/.github/workflows/fe-e2e.yml@master
with:
GHA_REF: ''
GHA_BASE: ${{ github.event.before }}
secrets:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

build:
needs: install-deps
uses: collaborationFactory/github-actions/.github/workflows/fe-build.yml@master
with:
GHA_REF: ''
GHA_BASE: ${{ github.event.before }}

tag:
needs: [build, e2e-tests]
uses: collaborationFactory/github-actions/.github/workflows/fe-tag.yml@master
with:
GHA_REF: ''
GHA_BASE: ${{ github.event.before }}
secrets:
JFROG_BASE64_TOKEN: ${{ secrets.JFROG_BASE64_TOKEN }}
JFROG_URL: ${{ secrets.JFROG_URL }}
JFROG_USER: ${{ secrets.JFROG_USER }}
GIT_USER_TOKEN: ${{ secrets.GIT_USER_TOKEN }}
GIT_USER: ${{ secrets.GIT_USER }}
GIT_USER_EMAIL: ${{ secrets.GIT_USER_EMAIL }}
FORCE: ${{ inputs.force == true }}
# RUN_E2E: false # only for repos that do not run e2e before a release
secrets: inherit
24 changes: 0 additions & 24 deletions .github/workflow-templates/fe/fe-tag-pushed.yml

This file was deleted.

3 changes: 0 additions & 3 deletions .github/workflows/fe-install-deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ on:
jobs:
install-deps:
runs-on: ubuntu-latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Without concurrency, old fe-pr and fe-main runs are no longer cancelled; was that meant? A caller-specific group would protect only release runs:

      group: fe-install-deps-${{ github.workflow }}-${{ github.ref }}

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intended to keep fe-install-deps.yml free of concurrency: the reusable workflow shouldn't decide that for its callers. Concurrency is now set at run level in the caller templates: fe-pr.yml (fe-pr-<PR number>, cancelling), fe-main.yml (fe-main-<ref>, cancelling), fe-release.yml (non-cancelling). The README has numbered migration steps; adding the concurrency block to the PR and main callers is step 3.

concurrency:
group: fe-install-deps-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
Expand Down
212 changes: 212 additions & 0 deletions .github/workflows/fe-release-flow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
name: Frontend Release Flow Workflow

on:
workflow_call:
inputs:
FORCE:
description: 'release/*: release even if no project is affected. Default branch: publish a snapshot of all projects. false: build and test only.'
type: boolean
required: false
default: false
RUN_E2E:
type: boolean
required: false
default: true
secrets:
JFROG_BASE64_TOKEN:
required: true
JFROG_URL:
required: true
JFROG_USER:
required: true
DOT_NPMRC:
required: true
GIT_USER_TOKEN:
required: true
GIT_USER:
required: true
GIT_USER_EMAIL:
required: true
PERCY_TOKEN:
required: false

env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}

jobs:
guard:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check dispatch ref
env:
EVENT_NAME: ${{ github.event_name }}
REF: ${{ github.ref }}
DEFAULT_REF: refs/heads/${{ github.event.repository.default_branch }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$REF" != "$DEFAULT_REF" ] && [[ "$REF" != refs/heads/release/* ]]; then
echo "::error::The release flow can only be dispatched on the default branch or a release/* branch, not on $REF"
exit 1
fi

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
sparse-checkout: .github/workflows
persist-credentials: false

# an old tag-push caller would publish manually pushed tags and fail on the tags of this flow
- name: Check for old release callers
run: |
old=$(grep -lE 'github-actions/\.github/workflows/(fe-release|fe-tag)\.yml@' .github/workflows/*.y*ml 2> /dev/null || true)
if [ -n "$old" ]; then
echo "::error::Old release callers found: $(echo $old). Delete them in the same commit that adds the fe-release-flow caller (see 'FE Release Flow' in the github-actions README)."
exit 1
fi

install-deps:
needs: guard
permissions:
contents: read
uses: collaborationFactory/github-actions/.github/workflows/fe-install-deps.yml@master
with:
GHA_REF: ''
secrets:
DOT_NPMRC: ${{ secrets.DOT_NPMRC }}

build:
needs: install-deps
permissions:
contents: read
uses: collaborationFactory/github-actions/.github/workflows/fe-build.yml@master
with:
GHA_REF: ''
GHA_BASE: ${{ github.event.before }}

e2e:
needs: install-deps
if: inputs.RUN_E2E
permissions:
contents: read
uses: collaborationFactory/github-actions/.github/workflows/fe-e2e.yml@master
with:
GHA_REF: ''
GHA_BASE: ${{ github.event.before }}
secrets:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

release:
needs: [build, e2e]
# a skipped e2e (RUN_E2E: false) must not skip the release; a non-forced dispatch is a dry run
if: >-
!cancelled()
&& needs.build.result == 'success'
&& (needs.e2e.result == 'success' || needs.e2e.result == 'skipped')
&& (github.event_name == 'push' || inputs.FORCE)
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
tag: ${{ steps.release.outputs.tag }}
previous_tag: ${{ steps.release.outputs.previous_tag }}
is_latest: ${{ steps.release.outputs.is_latest }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ''
fetch-depth: 0
token: ${{ secrets.GIT_USER_TOKEN }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24.20.0

- name: Cache Node Modules
id: npm-cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}

- name: Use .npmrc
uses: collaborationFactory/github-actions/.github/actions/use-npmrc@master
with:
dot-npmrc: ${{ secrets.DOT_NPMRC }}

- name: Install Modules
if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci

- name: Configure git user
env:
GIT_USER: ${{ secrets.GIT_USER }}
GIT_USER_EMAIL: ${{ secrets.GIT_USER_EMAIL }}
run: |
git config user.name "$GIT_USER"
git config user.email "$GIT_USER_EMAIL"

- name: Publish to Jfrog NPM Registry and Tag
id: release
uses: collaborationFactory/github-actions/.github/actions/artifacts@master
env:
JFROG_BASE64_TOKEN: ${{ secrets.JFROG_BASE64_TOKEN }}
JFROG_URL: ${{ secrets.JFROG_URL }}
JFROG_USER: ${{ secrets.JFROG_USER }}
RELEASE_FLOW: true
FORCE: ${{ inputs.FORCE }}

# the release step writes the version before publishing; once the tag is pushed
# the release is complete and must not be unpublished
- name: Check for an incomplete release
id: incomplete
if: failure() || cancelled()
run: |
file="$RUNNER_TEMP/fe-release-version"
if [ ! -f "$file" ]; then
echo "Publishing did not start, nothing to clean up"
exit 0
fi
tag=$(cat "$file")
if git ls-remote --exit-code --tags origin "refs/tags/$tag" > /dev/null; then
echo "$tag is already pushed, the release is complete: nothing to clean up"
exit 0
fi
echo "$tag was not pushed: unpublishing its artifacts"
echo "tag=$tag" >> "$GITHUB_OUTPUT"

- name: Unpublish the incomplete release
if: (failure() || cancelled()) && steps.incomplete.outputs.tag != ''
uses: collaborationFactory/github-actions/.github/actions/artifacts@master
env:
JFROG_BASE64_TOKEN: ${{ secrets.JFROG_BASE64_TOKEN }}
JFROG_URL: ${{ secrets.JFROG_URL }}
JFROG_USER: ${{ secrets.JFROG_USER }}
ONLY_DELETE_ARTIFACTS: true
TAG: ${{ steps.incomplete.outputs.tag }}

github-release:
needs: release
# the tag is set only once the version is published and tagged
if: needs.release.outputs.tag != ''
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
TAG: ${{ needs.release.outputs.tag }}
PREVIOUS_TAG: ${{ needs.release.outputs.previous_tag }}
IS_LATEST: ${{ needs.release.outputs.is_latest }}
run: |
if gh release view "$TAG" > /dev/null 2>&1; then
echo "A GitHub release for $TAG already exists, nothing to do"
exit 0
fi
args=(--verify-tag --generate-notes "--latest=$IS_LATEST")
if [ -n "$PREVIOUS_TAG" ]; then
args+=(--notes-start-tag "$PREVIOUS_TAG")
fi
gh release create "$TAG" "${args[@]}"
3 changes: 3 additions & 0 deletions .github/workflows/fe-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
release-version:
runs-on: ubuntu-latest
steps:
- name: Deprecation notice
run: echo "::warning::fe-release.yml (tag push) is deprecated. Migrate to fe-release-flow.yml, see 'FE Release Flow' in the github-actions README."

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ''
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/fe-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
tag:
runs-on: ubuntu-latest
steps:
- name: Deprecation notice
run: echo "::warning::fe-tag.yml is deprecated. Migrate to fe-release-flow.yml, see 'FE Release Flow' in the github-actions README."

- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.GHA_REF }}
Expand Down
Loading