Skip to content

fix: update remaining docker images and fix hadolint errors #1

fix: update remaining docker images and fix hadolint errors

fix: update remaining docker images and fix hadolint errors #1

Workflow file for this run

# Copyright (c) 2026 SnowdreamTech. All rights reserved.
# Licensed under the MIT License. See LICENSE file in the project root for full license information.
---
# CD (Continuous Deployment)
# Purpose: Unified release pipeline covering Project Verification and Automated Release Orchestration.
# Trigger: Every push to the default branch (main).
# Design:
# - Sequential execution: Verify -> Release Please.
# - Atomic: Release only happens if verification passes.
# - Secure: Eliminates workflow_run risks by using internal job dependencies.
name: "πŸš€ Continuous Delivery"
"on":
push:
branches:
- "main"
- "dev"
workflow_dispatch:
permissions:
contents: read
env:
UNIRTM_LOCKED: 1
# Opt into Node.js 24 now ahead of GitHub's June 16, 2026 forced migration.
# See: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "1"
jobs:
# 1. Project Verification Stage (Verify)
verify:
name: "🏁 Pre-flight Integrity Check (${{ matrix.os }})"
runs-on: ${{ matrix.os }}
environment: development
concurrency:
group: verify-${{ github.workflow }}-${{ matrix.os }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
permissions:
contents: read
statuses: write
security-events: write
timeout-minutes: 50
env:
PYTHONUTF8: 1
STEP_SECURITY_DISABLE: ${{ matrix.os != 'ubuntu-latest' }}
steps:
- name: "πŸ”’ Harden Runner"
if: matrix.os == 'ubuntu-latest'
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
api.github.com:443
raw.githubusercontent.com:443
objects.githubusercontent.com:443
pkg-containers.githubusercontent.com:443
avatars.githubusercontent.com:443
github.com:443
packages.microsoft.com:443
archive.ubuntu.com:80
archive.ubuntu.com:443
security.ubuntu.com:80
security.ubuntu.com:443
ports.ubuntu.com:80
ports.ubuntu.com:443
keyserver.ubuntu.com:80
keyserver.ubuntu.com:443
changelogs.ubuntu.com:80
changelogs.ubuntu.com:443
deb.debian.org:80
deb.debian.org:443
security.debian.org:80
security.debian.org:443
snapshot.debian.org:80
snapshot.debian.org:443
dl.rockylinux.org:443
mirrors.rockylinux.org:443
mirror.centos.org:443
vault.centos.org:443
isv-data.centos.org:443
mirrorlist.centos.org:80
mirrorlist.centos.org:443
cdn.redhat.com:443
cdn-ubi.redhat.com:443
access.redhat.com:443
sso.redhat.com:443
dl-cdn.alpinelinux.org:443
registry.npmjs.org:443
registry.yarnpkg.com:443
pypi.org:443
files.pythonhosted.org:443
proxy.golang.org:443
sum.golang.org:443
index.crates.io:443
static.rust-lang.org:443
packagist.org:443
repo.maven.apache.org:443
golang.org:443
pkg.go.dev:443
dl.google.com:443
rubygems.org:443
registry.terraform.io:443
formulae.brew.sh:443
repo.yarnpkg.com:443
ghcr.io:443
production.cloudflare.docker.com:80
production.cloudflare.docker.com:443
registry-1.docker.io:443
auth.docker.io:443
docker.io:443
quay.io:443
cdn.quay.io:443
docker-images-prod.s3.us-west-2.amazonaws.com:443
docker-images-prod.s3.us-east-1.amazonaws.com:443
docker-images-prod.s3.amazonaws.com:443
s3.amazonaws.com:443
s3.us-west-2.amazonaws.com:443
s3.us-east-1.amazonaws.com:443
osv-vulnerabilities.storage.googleapis.com:443
api.osv.dev:443
get.trivy.dev:443
aquasecurity.github.io:443
tuf-repo-cdn.sigstore.dev:443
oauth2.sigstore.dev:443
rekor.sigstore.dev:443
fulcio.sigstore.dev:443
api.sigstore.dev:443
- name: "πŸ“‚ Checkout Repository Code"
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
fetch-depth: 0
- name: "⚑ Setup UniRTM"
uses: snowdreamtech/setup-unirtm@cacfb4d739ee46d4a4b2528b02b508df2f560706 # v0.4.0
with:
unirtm-version: "0.25.0"
install: true
trust: true
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_SECRET || secrets.GITHUB_TOKEN }}
- name: "πŸ“ Validate Commit Convention (Commitlint)"
shell: sh
run: |
unirtm exec -- commitlint --last --verbose
- name: "πŸ§ͺ Execute Full Quality & Safety Verification"
shell: sh
run: |
unirtm run verify
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYTHONUTF8: 1
TRIVY_CACHE_DIR: .trivycache
UNIRTM_YES: 1
# - name: "πŸ§ͺ Execute Shell Tests (Bats)"
# if: matrix.os != 'windows-latest'
# shell: sh
# run: |
# unirtm run test:shell
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# UNIRTM_YES: 1
#
# - name: "πŸ§ͺ Execute PowerShell Tests (Pester)"
# if: matrix.os == 'windows-latest'
# shell: pwsh
# run: |
# unirtm run test:powershell
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# UNIRTM_YES: 1
- name: "⚑ Cache Documentation Links (Lychee)"
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: .lycheecache
key: ${{ runner.os }}-lychee-${{ hashFiles('**/*.md') }}
restore-keys: |
${{ runner.os }}-lychee-
- name: "πŸ”— Verify Documentation Links (Lychee)"
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
if: ${{ always() && matrix.os == 'ubuntu-latest' }}
continue-on-error: true # Don't block CD on link check failures (network issues, rate limits, etc.)
with:
args: --config lychee.toml '**/*.md'
fail: true
env:
# Pass GitHub token to avoid rate limiting and access private repos
GITHUB_TOKEN: ${{ secrets.WORKFLOW_SECRET || secrets.GITHUB_TOKEN }}
- name: "πŸ•΅οΈ Detect Vulnerabilities (Trivy FS)"
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
if: ${{ always() && matrix.os == 'ubuntu-latest' }}
env:
TRIVY_DB_REPOSITORY: "public.ecr.aws/aquasecurity/trivy-db"
TRIVY_CHECKS_BUNDLE_REPOSITORY: "public.ecr.aws/aquasecurity/trivy-checks"
with:
scan-type: "fs"
ignore-unfixed: true
format: "sarif"
output: "trivy-results.sarif"
severity: "CRITICAL,HIGH,MEDIUM,LOW"
exit-code: "1"
skip-dirs: "unirtm_data,cmd/unirtm_data"
- name: "πŸ“€ Upload Security Audit (SARIF)"
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
if: always() && matrix.os == 'ubuntu-latest'
with:
sarif_file: "trivy-results.sarif"
# 2. Release Orchestration Stage (Release Please)
release-please:
name: "πŸš€ Release Orchestration"
needs: [verify]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: internal
concurrency:
group: release-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
statuses: write
security-events: write
timeout-minutes: 50
steps:
- name: "πŸ—οΈ Checkout Repository"
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: "πŸ“ Orchestrate Release Lifecycle (Release Please)"
id: release
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
token: ${{ secrets.WORKFLOW_SECRET || secrets.GITHUB_TOKEN }}
config-file: .release-please-config.json
manifest-file: .release-please-manifest.json
target-branch: ${{ github.ref_name }}
skip-github-release: true
- name: "πŸ—οΈ Checkout Release PR Branch"
if: ${{ steps.release.outputs.pr }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ fromJson(steps.release.outputs.pr).headBranchName }}
persist-credentials: false
- name: "πŸ“¦ Setup Node Environment (Syncing)"
if: ${{ steps.release.outputs.pr && hashFiles('package.json') != '' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24"
cache: ""
- name: "πŸ”— Synchronize Manifests & Lockfiles"
if: ${{ steps.release.outputs.pr && hashFiles('package.json') != '' }}
shell: sh
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if [ -f "pnpm-lock.yaml" ]; then
pnpm install --no-frozen-lockfile
elif [ -f "yarn.lock" ]; then
yarn install --no-immutable
elif [ -f "package-lock.json" ]; then
npm install --package-lock-only
fi
git add .
git commit --signoff -m "chore(release): synchronize lockfiles for version bump" || echo "No changes to sync"
git push "https://${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_SECRET || secrets.GITHUB_TOKEN }}
- name: "🧹 Deduplicate CHANGELOGs"
if: ${{ steps.release.outputs.pr }}
shell: sh
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
find . -name "CHANGELOG.md" -type f | while read -r file; do
awk '
/^## \[/ {
match($0, /\[([^]]+)\]/)
ver = substr($0, RSTART+1, RLENGTH-2)
if (seen[ver] == 1) {
skip = 1
} else {
seen[ver] = 1
skip = 0
}
}
{
if (!skip) {
print $0
}
}
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
done
git add .
git commit --signoff -m "chore(release): deduplicate CHANGELOG headers" || echo "No changes to sync"
git push "https://${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD
env:
GITHUB_TOKEN: ${{ secrets.WORKFLOW_SECRET || secrets.GITHUB_TOKEN }}
- name: "πŸš€ Synchronize GitHub Releases"
if: "${{ startsWith(github.event.head_commit.message, 'chore(release):') || startsWith(github.event.head_commit.message, 'chore: release') }}"
shell: sh
run: |
for path in $(awk -F'"' '/":/ {print $2}' .release-please-manifest.json); do
if [ "$path" = "." ]; then
file="CHANGELOG.md"
tag_prefix="v"
else
file="${path}/CHANGELOG.md"
comp=$(awk -F'"' -v p="$path" '
$0 ~ "\"" p "\": \\{" { in_block = 1 }
in_block && $0 ~ /"component":/ {
for(i=1; i<=NF; i++) {
if($i=="component") { print $(i+2); exit }
}
}
in_block && /^ *\}/ { in_block = 0 }
' .release-please-config.json)
if [ -z "$comp" ] || [ "$comp" = "null" ]; then
comp=$(basename "$path")
fi
tag_prefix="${comp}-v"
fi
if [ ! -f "$file" ]; then continue; fi
ver=$(awk '/^## \[/ { match($0, /\[([^]]+)\]/); print substr($0, RSTART+1, RLENGTH-2); exit }' "$file")
if [ -n "$ver" ]; then
tag="${tag_prefix}${ver}"
awk '
/^## \[/ {
count++
if (count == 2) exit
}
{ if (count == 1) print $0 }
' "$file" > "log.tmp.md"
echo "Force updating Git tag $tag to current commit..."
git tag -f "$tag"
git push --force "https://${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/$tag" || true
echo "Synchronizing GitHub Release for $tag..."
gh release create "$tag" --notes-file "log.tmp.md" 2>/dev/null || \
gh release edit "$tag" --notes-file "log.tmp.md" 2>/dev/null || \
echo "Release sync skipped or failed for $tag"
rm -f "log.tmp.md"
fi
done
# Cleanup: skip-github-release prevents release-please from updating PR labels.
# We must manually clear "autorelease: pending" on merged PRs to avoid blocking future releases.
echo "Cleaning up pending release labels..."
merged_prs=$(gh api -X GET search/issues -f q="repo:$GITHUB_REPOSITORY is:pr is:merged label:\"autorelease: pending\"" --jq '.items[].number' 2>/dev/null) || merged_prs=""
for pr in $merged_prs; do
# Validate that $pr is a number to avoid invalid CLI queries
case "$pr" in
''|*[!0-9]*) continue ;;
esac
echo "Marking PR #$pr as tagged..."
# Use REST API to avoid GraphQL scope errors (read:org) from 'gh pr edit'
gh api -X POST "repos/$GITHUB_REPOSITORY/issues/$pr/labels" -f "labels[]=autorelease: tagged" >/dev/null 2>&1 || true
gh api -X DELETE "repos/$GITHUB_REPOSITORY/issues/$pr/labels/autorelease:%20pending" >/dev/null 2>&1 || true
done
env:
GH_TOKEN: ${{ secrets.WORKFLOW_SECRET || secrets.GITHUB_TOKEN }}