Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
32ead5f
PD-13324 speed up CI and move formatting/i18n gates to the PR
cryptalith Sep 5, 2026
3cc8e31
PD-13324 add characterization tests for the postbuild layout
cryptalith Sep 5, 2026
47e02e4
PD-13324 build the app with the esbuild application builder
cryptalith Sep 5, 2026
73b457a
PD-13324 address review findings and make formatting a self-applying …
cryptalith Sep 5, 2026
fcea6c9
PD-13324 stop serializing the WAR build behind lint, assert formatted…
cryptalith Sep 5, 2026
d0d8d02
PD-13324 make the pull request pipeline work for forks and Dependabot
cryptalith Sep 5, 2026
75cc05a
PD-13324 cache node_modules and install through one composite action
cryptalith Sep 5, 2026
c3ac923
PD-13324 pass only the secrets each reusable workflow reads
cryptalith Sep 7, 2026
2116139
PD-13324 stop passing secrets to the release dry run
cryptalith Sep 7, 2026
63020ba
Merge branch 'main' into lmendoa/PD-13324
cryptalith Sep 21, 2026
91950eb
Merge branch 'main' into lmendoa/PD-13324
cryptalith Oct 7, 2026
630f977
Merge branch 'main' into lmendoa/PD-13324
cryptalith Oct 7, 2026
839a7b5
PD-13324 skip git hooks in the format job
cryptalith Oct 7, 2026
7bb38a7
PD-13324 format job never stacks a second fix commit
cryptalith Oct 7, 2026
abbfaf2
PD-13324 separate Angular cache keys for build and test jobs
cryptalith Oct 7, 2026
5775798
PD-13324 run unit tests on ubuntu-latest like every other job
cryptalith Oct 7, 2026
6040e35
PD-13324 document resolving XLF modify/delete conflicts after merge
cryptalith Oct 7, 2026
5cd4c04
PD-13324 rewrite postbuild bundle references in one pass per file
cryptalith Oct 7, 2026
78acee3
Merge branch 'main' into lmendoa/PD-13324
cryptalith Oct 7, 2026
46407fe
PD-13324 yarn-install comment no longer claims two runner images
cryptalith Oct 7, 2026
65f88b3
PD-13324 return the app build to the webpack builder
cryptalith Oct 7, 2026
2e9464f
PD-13324 point the harness and verify at webpack output
cryptalith Oct 7, 2026
e52b5b3
PD-13324 seed the Angular build cache from main
cryptalith Oct 7, 2026
44cc28d
PD-13324 release build no longer fails on formatting
cryptalith Oct 7, 2026
7cae63a
PD-13324 format job checks formatting instead of pushing fixes
cryptalith Oct 7, 2026
6dce0f5
PD-13324 pin buffer to the version main already ships
cryptalith Oct 7, 2026
a584237
PD-13324 pull requests restore the build cache without saving it
cryptalith Oct 7, 2026
26a2dcc
PD-13324 GTM spec never loads the real gtm.js
cryptalith Oct 8, 2026
ce863b1
PD-13324 format the GTM spec
cryptalith Oct 8, 2026
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
76 changes: 76 additions & 0 deletions .github/actions/yarn-install/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: 'Install dependencies'
description: >
Set up the Node version from .nvmrc and get node_modules in place, restoring
it from cache when nothing that shapes the tree has changed.

outputs:
os:
description: >-
The runner's OS release as used in this action's cache keys, e.g.
ubuntu24.04, so callers can key their own caches the same way.
value: ${{ steps.os.outputs.id }}

runs:
using: composite
steps:
# The runner's OS release, not just `runner.os`. Every release reports
# "Linux", so keying on runner.os alone would let a tree installed on one
# image be restored on another, e.g. when ubuntu-latest moves to a new
# release, or if a job pins a different one again. All jobs use
# ubuntu-latest so they share one entry. The prebuilt native binaries we pull
# (rollup, lmdb, lightningcss, esbuild) target an old glibc and would
# survive a mismatch today, but nothing guarantees the next dependency will.
- id: os
shell: bash
run: |
if [ -r /etc/os-release ]; then
. /etc/os-release
echo "id=${ID}${VERSION_ID}" >> "$GITHUB_OUTPUT"
else
echo "id=${RUNNER_OS}" >> "$GITHUB_OUTPUT"
fi

# First, because whether this hits decides if restoring the ~480 MB yarn
# download cache below is worth doing at all.
- id: node_modules
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: node_modules
# .nvmrc is in the key because the tree records the Node ABI it was
# built for, and yarn.lock because it is what the tree must match.
#
# No restore-keys on purpose. A tree from a different lockfile is not a
# useful starting point: yarn has to reconcile it, and an optional
# dependency that is no longer selected can survive the reconcile.
# Exact match or a clean install; there is no useful middle.
key: ${{ steps.os.outputs.id }}-${{ runner.arch }}-node-modules-${{ hashFiles('.nvmrc', 'yarn.lock') }}

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
# Deliberately no `cache: yarn`; the two steps below do it conditionally.

# The yarn download cache only speeds up an install that actually downloads.
# It is ~480 MB in this repo, so restoring it on a run whose node_modules
# already hit costs more than the 2s install it would be accelerating.
- id: yarn_cache_dir
if: steps.node_modules.outputs.cache-hit != 'true'
shell: bash
run: echo "path=$(yarn cache dir)" >> "$GITHUB_OUTPUT"

- if: steps.node_modules.outputs.cache-hit != 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.yarn_cache_dir.outputs.path }}
key: ${{ steps.os.outputs.id }}-${{ runner.arch }}-yarn-${{ hashFiles('yarn.lock') }}
# Here restore-keys IS right: a partial download cache is still a
# head start, and yarn verifies every package against the lockfile.
restore-keys: |
${{ steps.os.outputs.id }}-${{ runner.arch }}-yarn-

# Always, hit or miss. On a hit yarn checks node_modules/.yarn-integrity
# against the lockfile and exits in about two seconds with "Already
# up-to-date", and the `prepare` script still runs. The cache is an
# accelerator; yarn stays the source of truth.
- shell: bash
run: yarn --frozen-lockfile
58 changes: 58 additions & 0 deletions .github/workflows/bld_mvn.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ on:
required: false
default: "default"
type: string
deploy:
description: 'Whether to upload the WAR to the artifact repository. Set false for a dry run.'
required: false
default: true
type: boolean

workflow_dispatch:
inputs:
Expand All @@ -48,6 +53,11 @@ on:
required: false
default: "default"
type: string
deploy:
description: 'Whether to upload the WAR to the artifact repository. Set false for a dry run.'
required: false
default: true
type: boolean

jobs:
bld_mvn:
Expand All @@ -72,6 +82,46 @@ jobs:
# NOTE: history does not include tags!
fetch-depth: 100

# Build the Angular app here rather than letting frontend-maven-plugin do
# it: this reuses the runner's Node and the cached node_modules instead of
# downloading Node and installing from cold inside maven. The maven build
# below skips the plugin's node/yarn goals and just packages ./dist.
- id: deps
uses: ./.github/actions/yarn-install

# No formatting check here: formatting is a pull request gate (the
# `format` job), and a release build must not fail on whitespace.

# The Angular build cache, same lineage as bld_yarn's (the comment there
# explains the key and when it refreshes). Restoring main's newest entry
# speeds up this release build; saving below is what gives every new pull
# request a warm first run.
- name: restore the Angular build cache
id: ng_cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .angular/cache
key: ng-build-${{ steps.deps.outputs.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'angular.json', '.nvmrc') }}-${{ github.sha }}
restore-keys: |
ng-build-${{ steps.deps.outputs.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'angular.json', '.nvmrc') }}-

- name: build the Angular app (prebuild + postbuild included)
run: yarn build

# The WAR is a verbatim copy of ./dist, so validate it before packaging.
- run: yarn build:manifest:check
- run: yarn build:verify

# Saved only after the build and both layout checks passed: steps run
# under `if: success()` unless told otherwise, so a failed build never
# saves. Skipped on an exact hit, i.e. a re-run of the same commit.
- name: save the Angular build cache
if: steps.ng_cache.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .angular/cache
key: ng-build-${{ steps.deps.outputs.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'angular.json', '.nvmrc') }}-${{ github.sha }}

- name: find next version
id: version
uses: ORCID/version-bump-action@main
Expand Down Expand Up @@ -117,10 +167,15 @@ jobs:

- name: build our project
run: |
# skip.installyarn skips the install-node-and-yarn goal and skip.yarn
# skips both `yarn` goals of frontend-maven-plugin: ./dist was already
# built by the `yarn build` step above. pom.xml is left untouched so
# local `mvn` builds still run the full frontend chain.
mvn -T 1C --batch-mode \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \
--file "pom.xml" \
--activate-profiles prod -Dnodejs.workingDirectory=. \
-Dskip.installyarn=true -Dskip.yarn=true \
package -Dmaven.test.skip

echo "------------------------------------------------------"
Expand All @@ -129,7 +184,10 @@ jobs:
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'

# Skipped by the ci_only dry run: it proves the WAR still builds without
# publishing an artifact nobody asked for.
- name: deploy same artifact for all environments
if: ${{ inputs.deploy }}
run: |
WAR_FILE="target/orcid-web-frontend-prod.war"
REPO_URL="${ARTIFACT_URL}${ARTIFACT_REPO_PATH}"
Expand Down
56 changes: 31 additions & 25 deletions .github/workflows/bld_test_rel_tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ on:
type: boolean
required: false
default: false
deploy:
description: 'Whether to upload the WAR and push the i18n source. Set false for a dry run.'
type: boolean
required: false
default: true

workflow_dispatch:
inputs:
Expand Down Expand Up @@ -62,44 +67,43 @@ on:
type: boolean
required: false
default: false
deploy:
description: 'Whether to upload the WAR and push the i18n source. Set false for a dry run.'
type: boolean
required: false
default: true

# cancel running job if another commit comes in
concurrency:
group: main-${{ github.ref }}-1
cancel-in-progress: true

jobs:
format_i18n:
uses: ./.github/workflows/format_i18n.yml
secrets: inherit # pass all secrets for pushing

format_prettier:
uses: ./.github/workflows/format_prettier.yml
secrets: inherit # pass all secrets for pushing
# Extract messages.xlf from the code and push it to Transifex. Off the
# critical path: nothing downstream depends on it.
# Formatting and unit tests are PR gates (pr.yml), not main gates: branch
# protection requires an up-to-date, green branch, so main re-tests a tree
# that has already passed.
push_i18n_source:
if: ${{ github.ref == 'refs/heads/main' && inputs.deploy }}
uses: ./.github/workflows/push_i18n_source.yml
secrets:
TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}

##############################################################################

lint:
uses: ./.github/workflows/lint.yml
needs:
- format_i18n
- format_prettier

test_yarn:
uses: ./.github/workflows/test_yarn.yml
needs:
- format_i18n
- format_prettier

##############################################################################
# uses maven to build via yarn into a war file
# No `needs: lint`. lint is a required check on the pull request (pr.yml
# produces `lint / pre-commit`), so main is re-running something the merge
# already gated on. Running it in parallel takes ~40s off the critical path;
# rel_tag still waits for it, so nothing is tagged or released on a lint failure.
bld_mvn:
uses: ./.github/workflows/bld_mvn.yml
secrets: inherit # pass all secrets for uploading assets
needs:
- lint
- format_i18n
- format_prettier
permissions:
checks: write
contents: read
Expand All @@ -109,15 +113,12 @@ jobs:
version_tag: ${{ inputs.version_tag }}
bump: ${{ inputs.bump }}
ref: ${{ inputs.ref }}
deploy: ${{ inputs.deploy }}

bld_docker:
if: ${{ inputs.run_docker }}
uses: ./.github/workflows/bld_docker.yml
secrets: inherit # pass all secrets for uploading assets
needs:
- lint
- format_i18n
- format_prettier
permissions:
checks: write
contents: read
Expand All @@ -130,10 +131,15 @@ jobs:

##############################################################################

# Gated on lint as well as the WAR: the build can run in parallel, but nothing
# gets tagged or released while lint is red. lint (~30s) always finishes long
# before bld_mvn (~4min), so this adds no latency.
rel_tag:
uses: ./.github/workflows/rel_tag.yml
if: ${{ inputs.rel_tag }}
needs: bld_mvn
needs:
- bld_mvn
- lint
with:
version_tag: ${{ inputs.version_tag }}
bump: ${{ inputs.bump }}
Expand Down
47 changes: 41 additions & 6 deletions .github/workflows/bld_yarn.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
name: bld_yarn

# The job only builds; it calls no GitHub API.
permissions:
checks: write
contents: read
issues: read

on:
workflow_call:
Expand All @@ -20,9 +19,45 @@ jobs:
egress-policy: audit

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- id: deps
uses: ./.github/actions/yarn-install

# Angular's persistent build cache (webpack's module and minifier caches
# and babel's, ~70 MB compressed), restored from the newest entry bld_mvn
# saved on main. Restore only. Angular gives webpack timestamp snapshots
# for app modules, so a fresh checkout recompiles every app module
# whichever entry is restored, and what carries over (node_modules, babel
# and minifier results) is the same in main's entry as in one this pull
# request could save, apart from the few files it changes itself. Saving
# would add a ~70 MB upload to this job, the slowest on the pull request.
# test_yarn keeps a separate `ng-test-` lineage: the two jobs run at once,
# and one shared key made them race and restore each other's cache.
#
# When it refreshes: the key hashes yarn.lock, angular.json and .nvmrc, so
# a dependency, Angular configuration or Node change starts a new lineage
# instead of reusing entries built by another toolchain, and every
# successful build on main saves a newer entry. A pull request that
# changes one of those files finds no entry yet and builds cold. Within a
# lineage webpack checks every cached module against its source before
# reusing it, so cold and warm builds emit the same content-hashed
# bundles. Translation inlining is not cached at all.
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
node-version: 'v20.x'
cache: 'yarn'
- run: yarn
path: .angular/cache
key: ng-build-${{ steps.deps.outputs.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'angular.json', '.nvmrc') }}-${{ github.sha }}
restore-keys: |
ng-build-${{ steps.deps.outputs.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'angular.json', '.nvmrc') }}-

- run: yarn build

# Fails when the real build changes the shape of the deployed tree (a
# locale that stopped being emitted, a bundle that lost its locale
# suffix, a file that moved out of share-assets). Lazy-chunk count
# changes are reported but do not fail; see the helper for why.
- run: yarn build:manifest:check

# Resolves every asset URL in the real 21-locale tree through the
# production nginx rules, and every lazy chunk in each locale's webpack
# runtime map to a file with that locale's suffix. Catches what the
# fixture cannot.
- run: yarn build:verify
Loading
Loading