ohif-viewer/.circleci/config.yml
Bill Wallace 256b8347e7
fix(docs): remove stray tool-call tags breaking the MDX build (#6081)
This is a fix to pnpm deployment which needs testing as the final part of origin/master release
No functional changes

* fix(docs): remove stray tool-call tags breaking the MDX build

platform/docs/docs/migration-guide/3p12-to-3p13/build-tooling.md ended with
two orphan closing tags (leftover tool-call serialization artifacts):

  </content>
  </invoke>

Docusaurus compiles Markdown as MDX (JSX-aware), so the orphan closing tag
failed the docs build:

  MDX compilation failed ... Unexpected closing slash in tag, expected an
  open tag first (build-tooling.md line 402)

This was the remaining blocker for build-and-deploy-docs once the
--no-frozen-lockfile change let the install step succeed. A scan of the docs
tree found no other such artifacts. Verified locally: docusaurus build now
generates static files with no MDX errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update lockfile and avoid freshness check on every command

* fix(release): keep workspace:* in the repo, concretize only at publish

The release flow rewrote internal @ohif/* dependency specifiers to the concrete
version and committed them, so pnpm-lock.yaml (which records workspace links)
drifted from the manifests on every version bump. The resulting
ERR_PNPM_OUTDATED_LOCKFILE broke every frozen install: Netlify (viewer-dev),
the docs deploy, pnpm's pre-run deps check, and post-merge installs.

Keep workspace:* everywhere in the committed repo and move the concrete-version
substitution to publish time only:

- publish-version.mjs: bump each package's own `version` field only; stop
  rewriting @ohif/* dependency/peerDependency specifiers.
- publish-package.mjs: publish with `pnpm publish --no-git-checks` instead of
  `npm publish`. pnpm rewrites workspace:* to the exact version in the published
  tarball; npm would publish the literal "workspace:*", which npm/yarn consumers
  cannot resolve.
- One-time: revert the 25 workspace manifests' @ohif/* specifiers to workspace:*
  (version fields untouched) and regenerate pnpm-lock.yaml to match.

Because internal deps are workspace:* (links, not versions in the lockfile),
version bumps no longer change pnpm-lock.yaml, so it stays in sync and frozen
installs keep working.

Verified: `pnpm install --frozen-lockfile` passes, and `pnpm pack` of @ohif/core
emits a tarball whose @ohif/ui dependency is the exact version (3.13.0-beta.92),
not workspace:*.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(ci): correct build-docs install comment for workspace:* release flow

publish-version.mjs no longer rewrites @ohif/* deps to concrete versions, so
the old comment was stale. Internal deps stay workspace:* and the lockfile
stays consistent; pnpm publish concretizes only the published tarball.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci(docs): use --frozen-lockfile now that the lockfile no longer drifts

With internal deps as workspace:* the lockfile stays in sync across version
bumps, so the docs deploy can install frozen -- failing fast on genuine
lockfile drift instead of silently reconciling. The --no-frozen-lockfile
workaround is no longer needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: use --frozen-lockfile in CI install steps now that the lockfile is stable

Internal @ohif/* deps are workspace:* so pnpm-lock.yaml no longer drifts; the
UNIT_TESTS/BUILD/NPM_PUBLISH installs can run frozen and fail fast on genuine
drift. Kept --no-frozen-lockfile only where it is still required: the Dockerfile
(platform/docs is excluded from the build context) and the playwright CS3D-version
step (mutates @cornerstonejs versions before installing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: stability of seg load mpr test

* Better drag fix for crosshairs

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 11:59:04 -04:00

581 lines
22 KiB
YAML

version: 2.1
# ci: re-trigger pipeline
orbs:
codecov: codecov/codecov@1.0.5
cypress: cypress-io/cypress@3.4.2
defaults: &defaults
docker:
- image: cimg/node:24.0.0
environment:
TERM: xterm
QUICK_BUILD: true
working_directory: ~/repo
commands:
install_pnpm:
steps:
- run:
name: Install pnpm
command: |
# The cimg/node global modules dir (/usr/local/lib/node_modules) is
# root-owned, so a plain `npm install -g` fails with EACCES. Install
# with sudo so the global pnpm binary lands in the shared prefix.
sudo npm install -g pnpm@11.5.2
echo 'export PATH="$(pnpm store path)/../.bin:$PATH"' >> $BASH_ENV
source $BASH_ENV
jobs:
UNIT_TESTS:
<<: *defaults
resource_class: large
steps:
- install_pnpm
- run: node --version
- checkout
- run:
name: Install Dependencies
command: pnpm install --frozen-lockfile
# RUN TESTS
- run:
name: 'JavaScript Test Suite'
command: pnpm run test:unit:ci
# platform/app
- run:
name: 'VIEWER: Combine report output'
command: |
viewerCov="/home/circleci/repo/platform/app/coverage"
touch "${viewerCov}/reports"
cat "${viewerCov}/clover.xml" >> "${viewerCov}/reports"
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
cat "${viewerCov}/lcov.info" >>"${viewerCov}/reports"
echo "\<<\<<\<< EOF" >> "${viewerCov}/reports"
- codecov/upload:
file: '/home/circleci/repo/platform/app/coverage/reports'
flags: 'viewer'
# PLATFORM/CORE
- run:
name: 'CORE: Combine report output'
command: |
coreCov="/home/circleci/repo/platform/core/coverage"
touch "${coreCov}/reports"
cat "${coreCov}/clover.xml" >> "${coreCov}/reports"
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
cat "${coreCov}/lcov.info" >> "${coreCov}/reports"
echo "\<<\<<\<< EOF" >> "${coreCov}/reports"
- codecov/upload:
file: '/home/circleci/repo/platform/core/coverage/reports'
flags: 'core'
BUILD:
<<: *defaults
resource_class: large
steps:
# Checkout code and ALL Git Tags
- checkout
- install_pnpm
- run:
name: Install Dependencies
command: pnpm install --frozen-lockfile
# Build & Test
- run:
name: 'Perform the versioning before build'
command: node ./version.mjs
- run:
name: 'Build the OHIF Viewer'
command: pnpm run build
no_output_timeout: 45m
- run:
name: 'Upload SourceMaps, Send Deploy Notification'
command: |
# export FILE_1=$(find ./build/static/js -type f -name "2.*.js" -exec basename {} \;)
# export FILE_MAIN=$(find ./build/static/js -type f -name "main.*.js" -exec basename {} \;)
# export FILE_RUNTIME_MAIN=$(find ./build/static/js -type f -name "runtime~main.*.js" -exec basename {} \;)
# curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_1.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_1
# curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_MAIN
# curl https://api.rollbar.com/api/1/sourcemap -F source_map=@build/static/js/$FILE_RUNTIME_MAIN.map -F access_token=$ROLLBAR_TOKEN -F version=$CIRCLE_SHA1 -F minified_url=https://$GOOGLE_STORAGE_BUCKET/static/js/$FILE_RUNTIME_MAIN
curl --request POST https://api.rollbar.com/api/1/deploy/ -F access_token=$ROLLBAR_TOKEN -F environment=$GOOGLE_STORAGE_BUCKET -F revision=$CIRCLE_SHA1 -F local_username=CircleCI
# Persist :+1:
- persist_to_workspace:
root: ~/repo
paths:
- platform/app/dist
- Dockerfile
- version.txt
- commit.txt
- version.json
BUILD_PACKAGES_QUICK:
<<: *defaults
resource_class: large
steps:
- install_pnpm
# Checkout code and ALL Git Tags
- checkout
- attach_workspace:
at: ~/repo
# SECURITY AUDIT - only when pnpm-lock.yaml has changed
- run:
name: 'Security Audit - High Risk Vulnerabilities'
command: |
git fetch origin master 2>/dev/null || true
BASE_REF=$(git merge-base HEAD origin/master 2>/dev/null)
if [[ -z "$BASE_REF" ]]; then
echo "Could not determine base ref (e.g. shallow clone or no origin/master), skipping security audit."
exit 0
fi
CHANGED_FILES=$(git diff --name-only origin/master...HEAD 2>/dev/null || echo "")
if ! echo "$CHANGED_FILES" | grep -qx 'pnpm-lock.yaml'; then
echo "pnpm-lock.yaml unchanged - skipping security audit."
exit 0
fi
echo "pnpm-lock.yaml changed - running pnpm audit for security vulnerabilities..."
echo "Checking for HIGH-RISK vulnerabilities..."
if pnpm audit --audit-level high; then
echo "No high-risk vulnerabilities found"
echo "Security audit passed!"
else
echo ""
echo "HIGH-RISK VULNERABILITIES DETECTED!"
echo "======================================"
echo ""
echo "To fix these issues:"
echo " 1. Run: pnpm audit"
echo " 2. Review the vulnerability details"
echo " 3. Update affected packages to secure versions"
echo " 4. Test your changes"
echo " 5. Re-run: pnpm audit --audit-level high"
echo ""
echo "Full audit report:"
pnpm audit || true
echo ""
echo "This build cannot proceed until high-risk vulnerabilities are resolved."
exit 1
fi
- run:
name: Install Dependencies
command: pnpm install
- run:
name: Avoid hosts unknown for github
command: |
rm -rf ~/.ssh
mkdir ~/.ssh/
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
git config --global user.name "ohif-bot"
- run:
name: Authenticate with NPM registry
# Write the auth token to npm's per-user config (~/.npmrc), not the
# repo root. publish-package.mjs chdir's into each package dir to run
# `npm publish`, and npm only reads the project .npmrc from that dir
# plus the user .npmrc from $HOME -- it never sees ~/repo/.npmrc, so a
# root-level token yields ENEEDAUTH. Using ~/.npmrc also avoids
# clobbering the committed workspace-config .npmrc (node-linker=hoisted).
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
- run:
name: build half of the packages (to avoid out of memory in circleci)
command: |
pnpm run build:package-all
- run:
name: build the other half of the packages
command: |
pnpm run build:package-all-1
NPM_PUBLISH:
<<: *defaults
resource_class: large
steps:
- install_pnpm
# Checkout code and ALL Git Tags
- checkout
- attach_workspace:
at: ~/repo
- run:
name: Install Dependencies
command: pnpm install --frozen-lockfile
- run:
name: Avoid hosts unknown for github
command: |
rm -rf ~/.ssh
mkdir ~/.ssh/
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
git config --global user.name "ohif-bot"
- run:
name: Authenticate with NPM registry
# Write the auth token to npm's per-user config (~/.npmrc), not the
# repo root. publish-package.mjs chdir's into each package dir to run
# `npm publish`, and npm only reads the project .npmrc from that dir
# plus the user .npmrc from $HOME -- it never sees ~/repo/.npmrc, so a
# root-level token yields ENEEDAUTH. Using ~/.npmrc also avoids
# clobbering the committed workspace-config .npmrc (node-linker=hoisted).
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
- run:
name: build half of the packages (to avoid out of memory in circleci)
command: |
pnpm run build:package-all
- run:
name: build the other half of the packages
command: |
pnpm run build:package-all-1
- run:
name: increase min time out
command: |
npm config set fetch-retry-mintimeout 20000
- run:
name: increase max time out
command: |
npm config set fetch-retry-maxtimeout 120000
- run:
name: publish package versions
command: |
node ./publish-version.mjs
- run:
name: Re-assert the NPM auth token before publishing
# Write the auth token to npm's per-user config (~/.npmrc), not the
# repo root. publish-package.mjs chdir's into each package dir to run
# `npm publish`, and npm only reads the project .npmrc from that dir
# plus the user .npmrc from $HOME -- it never sees ~/repo/.npmrc, so a
# root-level token yields ENEEDAUTH. Using ~/.npmrc also avoids
# clobbering the committed workspace-config .npmrc (node-linker=hoisted).
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
- run:
name: publish package dist
command: |
node ./publish-package.mjs
- persist_to_workspace:
root: ~/repo
paths:
- .
DOCKER_RELEASE_PUBLISH:
<<: *defaults
resource_class: large
steps:
- attach_workspace:
at: ~/repo
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image for amd64
command: |
# This file will exist if a new version was published by
# our command in the previous job.
if [[ ! -e version.txt ]]; then
exit 0
else
# Restore the committed .npmrc (pnpm workspace config such as
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
# auth token, but the Dockerfile COPYs .npmrc for the in-image
# pnpm install, so it needs the config file present and token-free.
git checkout -- .npmrc
# Set our version number using vars
export IMAGE_VERSION=$(cat version.txt)
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
echo $IMAGE_VERSION
echo $IMAGE_VERSION_FULL
# Build our amd64 image, auth, and push
docker build --platform linux/amd64 --tag ohif/app:$IMAGE_VERSION_FULL-amd64 --tag ohif/app:latest-amd64 .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push ohif/app:$IMAGE_VERSION_FULL-amd64
docker push ohif/app:latest-amd64
fi
- persist_to_workspace:
root: ~/repo
paths:
- .
DOCKER_RELEASE_PUBLISH_ARM:
<<: *defaults
resource_class: arm.large
steps:
- attach_workspace:
at: ~/repo
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image for arm64 (Release)
command: |
# This file will exist if a new version was published by
# our command in the previous job.
if [[ ! -e version.txt ]]; then
exit 0
else
# Restore the committed .npmrc (pnpm workspace config such as
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
# auth token, but the Dockerfile COPYs .npmrc for the in-image
# pnpm install, so it needs the config file present and token-free.
git checkout -- .npmrc
# Set our version number using vars
export IMAGE_VERSION=$(cat version.txt)
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
echo $IMAGE_VERSION
echo $IMAGE_VERSION_FULL
# Build our arm64 image, auth, and push
docker build --platform linux/arm64 --tag ohif/app:$IMAGE_VERSION_FULL-arm64 --tag ohif/app:latest-arm64 .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push ohif/app:$IMAGE_VERSION_FULL-arm64
docker push ohif/app:latest-arm64
fi
- persist_to_workspace:
root: ~/repo
paths:
- .
DOCKER_BETA_PUBLISH:
<<: *defaults
resource_class: large
steps:
- attach_workspace:
at: ~/repo
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image for amd64 (Beta)
command: |
echo $(ls -l)
# This file will exist if a new version was published by
# our command in the previous job.
if [[ ! -e version.txt ]]; then
echo "don't have version txt"
exit 0
else
echo "Building and pushing Docker image from the master branch (beta releases)"
# Restore the committed .npmrc (pnpm workspace config such as
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
# auth token, but the Dockerfile COPYs .npmrc for the in-image
# pnpm install, so it needs the config file present and token-free.
git checkout -- .npmrc
# Set our version number using vars
export IMAGE_VERSION=$(cat version.txt)
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
echo $IMAGE_VERSION
echo $IMAGE_VERSION_FULL
# Build our amd64 image, auth, and push
docker build --platform linux/amd64 --tag ohif/app:$IMAGE_VERSION_FULL-amd64 --tag ohif/app:latest-beta-amd64 .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push ohif/app:$IMAGE_VERSION_FULL-amd64
docker push ohif/app:latest-beta-amd64
fi
DOCKER_BETA_PUBLISH_ARM:
<<: *defaults
resource_class: arm.large
steps:
- attach_workspace:
at: ~/repo
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image for arm64 (Beta)
command: |
echo $(ls -l)
# This file will exist if a new version was published by
# our command in the previous job.
if [[ ! -e version.txt ]]; then
echo "don't have version txt"
exit 0
else
echo "Building and pushing ARM64 Docker image from the master branch (beta releases)"
# Restore the committed .npmrc (pnpm workspace config such as
# node-linker=hoisted). NPM_PUBLISH overwrote it with a publish
# auth token, but the Dockerfile COPYs .npmrc for the in-image
# pnpm install, so it needs the config file present and token-free.
git checkout -- .npmrc
# Set our version number using vars
export IMAGE_VERSION=$(cat version.txt)
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
echo $IMAGE_VERSION
echo $IMAGE_VERSION_FULL
# Build our arm64 image, auth, and push
docker build --platform linux/arm64 --tag ohif/app:$IMAGE_VERSION_FULL-arm64 --tag ohif/app:latest-beta-arm64 .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push ohif/app:$IMAGE_VERSION_FULL-arm64
docker push ohif/app:latest-beta-arm64
fi
CYPRESS:
<<: *defaults
resource_class: large
parallelism: 8
steps:
- install_pnpm
- run:
name: Install System Dependencies
command: |
# CircleCI's base image registers third-party apt sources (git-lfs via
# packagecloud, git-core PPA via launchpad) that periodically hang
# `apt-get update`. The Cypress libs below all come from the Ubuntu
# archive, so drop those sources and bound the update with timeouts +
# retries so a slow mirror can't stall the job indefinitely.
sudo rm -f /etc/apt/sources.list.d/*git* || true
sudo apt-get update \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=20 \
-o Acquire::https::Timeout=20
sudo apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6
- run:
name: Start Xvfb
command: Xvfb :99 -screen 0 1920x1080x24 &
background: true
- run:
name: Export Display Variable
command: export DISPLAY=:99
- cypress/install:
install-command: pnpm install
- cypress/run-tests:
# CI runs headless under Xvfb with no GPU, so Electron uses software
# WebGL. Newer Chromium deprecated that implicit fallback (canvas
# rendering degrades and races with element clicks). ELECTRON_EXTRA_LAUNCH_ARGS
# is the reliable way to pass the opt-in flag to Cypress's Electron browser.
cypress-command: |
npx wait-on@latest http://localhost:3000 && cd platform/app && ELECTRON_EXTRA_LAUNCH_ARGS="--enable-unsafe-swiftshader" npx cypress run --record --parallel
start-command: pnpm run test:data && pnpm run test:e2e:serve
DOCKER_MULTIARCH_MANIFEST:
<<: *defaults
resource_class: large
steps:
- attach_workspace:
at: ~/repo
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Create and push multi-architecture manifest (Release)
command: |
# This file will exist if a new version was published by
# our command in the previous job.
if [[ ! -e version.txt ]]; then
exit 0
else
echo "Building and pushing multi-architecture manifest from the master branch (release releases)"
rm -f ./.npmrc
# Set our version number using vars
export IMAGE_VERSION=$(cat version.txt)
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
echo $IMAGE_VERSION
echo $IMAGE_VERSION_FULL
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
# Create and push manifest for specific version
docker manifest create ohif/app:$IMAGE_VERSION_FULL \
--amend ohif/app:$IMAGE_VERSION_FULL-amd64 \
--amend ohif/app:$IMAGE_VERSION_FULL-arm64
docker manifest push ohif/app:$IMAGE_VERSION_FULL
# Create and push manifest for "latest" tag
docker manifest create ohif/app:latest \
--amend ohif/app:latest-amd64 \
--amend ohif/app:latest-arm64
docker manifest push ohif/app:latest
fi
DOCKER_BETA_MULTIARCH_MANIFEST:
<<: *defaults
resource_class: large
steps:
- attach_workspace:
at: ~/repo
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Create and push multi-architecture manifest (Beta)
command: |
echo $(ls -l)
# This file will exist if a new version was published by
# our command in the previous job.
if [[ ! -e version.txt ]]; then
exit 0
else
echo "Building and pushing multi-architecture manifest from the master branch (beta releases)"
rm -f ./.npmrc
# Set our version number using vars
export IMAGE_VERSION=$(cat version.txt)
export IMAGE_VERSION_FULL=v$IMAGE_VERSION
echo $IMAGE_VERSION
echo $IMAGE_VERSION_FULL
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
# Create and push manifest for specific beta version
docker manifest create ohif/app:$IMAGE_VERSION_FULL \
--amend ohif/app:$IMAGE_VERSION_FULL-amd64 \
--amend ohif/app:$IMAGE_VERSION_FULL-arm64
docker manifest push ohif/app:$IMAGE_VERSION_FULL
# Create and push manifest for "latest-beta" tag
docker manifest create ohif/app:latest-beta \
--amend ohif/app:latest-beta-amd64 \
--amend ohif/app:latest-beta-arm64
docker manifest push ohif/app:latest-beta
fi
workflows:
PR_CHECKS:
jobs:
- BUILD_PACKAGES_QUICK:
filters:
branches:
ignore: master
- UNIT_TESTS
- CYPRESS:
name: 'Cypress Tests'
context: cypress
# viewer-dev.ohif.org
DEPLOY_MASTER:
jobs:
- BUILD:
filters:
branches:
only: master
# - HOLD_FOR_APPROVAL:
# type: approval
# requires:
# - BUILD
- NPM_PUBLISH:
requires:
# - HOLD_FOR_APPROVAL
- BUILD
- DOCKER_BETA_PUBLISH:
requires:
- NPM_PUBLISH
- DOCKER_BETA_PUBLISH_ARM:
requires:
- DOCKER_BETA_PUBLISH
- DOCKER_BETA_MULTIARCH_MANIFEST:
requires:
- DOCKER_BETA_PUBLISH_ARM
# viewer.ohif.org
DEPLOY_RELEASE:
jobs:
- BUILD:
filters:
branches:
only: /^release\/.*/
- HOLD_FOR_APPROVAL:
type: approval
requires:
- BUILD
- NPM_PUBLISH:
requires:
- HOLD_FOR_APPROVAL
- DOCKER_RELEASE_PUBLISH:
requires:
- NPM_PUBLISH
- DOCKER_RELEASE_PUBLISH_ARM:
requires:
- DOCKER_RELEASE_PUBLISH
- DOCKER_MULTIARCH_MANIFEST:
requires:
- DOCKER_RELEASE_PUBLISH_ARM