2 Commits
v1 .. v2
Author SHA1 Message Date
zcode_mulm 1be2f0f072 templates: boolean inputs are not passed reliably through workflow_call with: — switch toggles to string form (mount-docker-socket / verify-sha) 2026-08-27 12:51:10 +08:00
zcode_mulm 7d8d9d8a2a 模板 v2:script 模式与通用件
- oci:build-command 脚本模式(install+build 同容器;全套挂载
  sock/docker-config/buildx/CA/cache;commit-env-name 注入;extra-env;
  digest summary 复用 image-matrix;image-matrix 改可选)
- 两模板:cache-mode fixed|per-run(per-run = mktemp 子目录 + always()
  清理,满足 from-zero 工具缓存契约)、mount-docker-socket、commit-env-name
- 呼应 P1-9:中性变量注入由模板承担(EST_GIT_COMMIT 等)
2026-08-27 12:47:39 +08:00
3 changed files with 187 additions and 11 deletions
+4 -4
View File
@@ -16,9 +16,9 @@ on:
default: 1
verify-sha:
required: false
type: boolean
default: true
description: "Fail unless the checked-out HEAD equals the triggering commit"
type: string
default: "yes"
description: "Empty disables; otherwise fail unless HEAD equals the triggering commit (boolean inputs are NOT reliable through workflow_call with:)"
jobs:
checkout:
@@ -32,7 +32,7 @@ jobs:
fetch-depth: ${{ inputs.fetch-depth }}
- name: verify HEAD equals triggering commit
if: inputs.verify-sha
if: inputs.verify-sha != ''
run: |
set -eu
test "$(git rev-parse HEAD)" = "${GITHUB_SHA}"
+55 -5
View File
@@ -44,7 +44,27 @@ on:
required: false
type: string
default: /data/cache/ci/quality
description: "Persistent host-side cache dir (pnpm store, tools, browsers)"
description: "Host-side cache dir (pnpm store, tools, browsers)"
cache-mode:
required: false
type: string
default: fixed
description: "fixed: one shared persistent dir. per-run: fresh mktemp child per run + always() cleanup (for scripts with from-zero guards on commit-keyed tool caches)"
mount-docker-socket:
required: false
type: string
default: ""
description: "Non-empty (e.g. 'yes') mounts /var/run/docker.sock into the toolchain containers (boolean inputs are NOT passed reliably through workflow_call with:)"
commit-env-name:
required: false
type: string
default: ""
description: "Env var name that receives the triggering commit SHA inside the containers (e.g. EST_GIT_COMMIT); empty disables"
extra-env:
required: false
type: string
default: ""
description: "Multiline KEY=VALUE extra env for the toolchain containers (values must not contain spaces; caller-side expressions are NOT evaluated)"
secrets:
REGISTRY_PASSWORD:
required: false
@@ -80,21 +100,51 @@ jobs:
- name: prepare cache dir (dirs 0777, files untouched)
run: |
set -eu
mkdir -p "${{ inputs.cache-dir }}"
chmod 0777 "${{ inputs.cache-dir }}"
find "${{ inputs.cache-dir }}" -mindepth 1 -maxdepth 1 -type d -exec chmod 0777 {} + 2>/dev/null || true
cache_root="${{ inputs.cache-dir }}"
mkdir -p "$cache_root"
chmod 0777 "$cache_root"
if [ "${{ inputs.cache-mode }}" = "per-run" ]; then
cache_dir="$(mktemp -d "$cache_root/run-XXXXXXXX")"
else
cache_dir="$cache_root"
fi
chmod 0777 "$cache_dir"
find "$cache_dir" -mindepth 1 -maxdepth 1 -type d -exec chmod 0777 {} + 2>/dev/null || true
printf 'TOOL_CACHE_DIR=%s\n' "$cache_dir" >>"$GITHUB_ENV"
# install and quality run in ONE container: toolchains installed by the
# install command (global pnpm, local bins) must still be on PATH for the
# quality command — two `docker run --rm` invocations would not share
# anything but the workspace and /cache mounts.
- name: install and quality (containerized toolchain)
env:
COMMIT_ENV_NAME: ${{ inputs.commit-env-name }}
run: |
set -eu
mount_args=""
if [ -n "${{ inputs.mount-docker-socket }}" ]; then
mount_args="-v /var/run/docker.sock:/var/run/docker.sock"
fi
env_args=""
if [ -n "${COMMIT_ENV_NAME}" ]; then
env_args="${env_args} -e ${COMMIT_ENV_NAME}=${GITHUB_SHA}"
fi
while IFS= read -r line; do
[ -n "$line" ] && env_args="${env_args} -e $line"
done <<EOF
${{ inputs.extra-env }}
EOF
# shellcheck disable=SC2086
docker run --rm --add-host npm.cache.est:172.17.0.1 \
$mount_args \
-v "$PWD:${{ inputs.workspace }}" -w "${{ inputs.workspace }}" \
-v "${{ inputs.cache-dir }}:/cache" -e TMPDIR=/cache \
-v "${TOOL_CACHE_DIR}:/cache" -e TMPDIR=/cache \
-v /usr/local/share/ca-certificates/est-bundle.crt:/ca/est-bundle.crt:ro \
-e SSL_CERT_FILE=/ca/est-bundle.crt \
-e NODE_EXTRA_CA_CERTS=/ca/est-bundle.crt \
$env_args \
"${{ inputs.node-image }}" sh -euxc '${{ inputs.install-command }} && ${{ inputs.quality-command }}'
- name: cleanup per-run cache dir
if: always() && inputs.cache-mode == 'per-run'
run: rm -rf -- "${TOOL_CACHE_DIR:-}"
@@ -31,9 +31,10 @@ on:
type: string
description: "Digest-pinned helper image with node + docker CLI + verifier tooling"
image-matrix:
required: true
required: false
type: string
description: "Multiline matrix: service|dockerfile|target per line"
default: ""
description: "Multiline matrix: service|dockerfile|target per line (required for the built-in loop; script mode uses it only for the digest summary)"
platform:
required: false
type: string
@@ -62,6 +63,41 @@ on:
required: false
type: string
default: git.moneywood.site
build-command:
required: false
type: string
default: ""
description: "Script mode: run this command inside the helper container (full mounts: sock/docker-config/buildx/CA/cache + envs) instead of the built-in matrix loop"
install-command:
required: false
type: string
default: ""
description: "Script mode only: runs in the SAME container before build-command (toolchains installed here must survive to the build phase)"
cache-dir:
required: false
type: string
default: /data/cache/ci/build
description: "Script mode: host-side cache dir mounted at /cache"
cache-mode:
required: false
type: string
default: fixed
description: "fixed | per-run (per-run = fresh mktemp child + always() cleanup; for from-zero tool caches)"
commit-env-name:
required: false
type: string
default: ""
description: "Script mode: env var receiving the commit SHA inside the container (e.g. EST_GIT_COMMIT)"
extra-env:
required: false
type: string
default: ""
description: "Script mode: multiline KEY=VALUE extra env (no spaces in values; expressions NOT evaluated)"
workspace:
required: false
type: string
default: /workspace
description: "Script mode: workspace mount point inside the container"
secrets:
REGISTRY_PASSWORD:
required: true
@@ -92,7 +128,60 @@ jobs:
printf '%s' "${REGISTRY_PASSWORD}" \
| docker login "${{ inputs.registry-host }}" -u "${{ github.actor }}" --password-stdin
- name: prepare script-mode cache dir
if: inputs.build-command != ''
run: |
set -eu
cache_root="${{ inputs.cache-dir }}"
mkdir -p "$cache_root"
chmod 0777 "$cache_root"
if [ "${{ inputs.cache-mode }}" = "per-run" ]; then
cache_dir="$(mktemp -d "$cache_root/run-XXXXXXXX")"
else
cache_dir="$cache_root"
fi
chmod 0777 "$cache_dir"
find "$cache_dir" -mindepth 1 -maxdepth 1 -type d -exec chmod 0777 {} + 2>/dev/null || true
printf 'TOOL_CACHE_DIR=%s\n' "$cache_dir" >>"$GITHUB_ENV"
- name: run build script (script mode)
if: inputs.build-command != ''
env:
REGISTRY_AUTH_USER: ${{ github.actor }}
REGISTRY_AUTH_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
COMMIT_ENV_NAME: ${{ inputs.commit-env-name }}
run: |
set -eu
mount_args="-v /var/run/docker.sock:/var/run/docker.sock"
if [ -f "$HOME/.docker/config.json" ]; then
mount_args="$mount_args -v $HOME/.docker/config.json:/root/.docker/config.json:ro"
fi
if [ -f /usr/libexec/docker/cli-plugins/docker-buildx ]; then
mount_args="$mount_args -v /usr/libexec/docker/cli-plugins/docker-buildx:/root/.docker/cli-plugins/docker-buildx:ro"
fi
env_args=""
if [ -n "${COMMIT_ENV_NAME}" ]; then
env_args="${env_args} -e ${COMMIT_ENV_NAME}=${GITHUB_SHA}"
fi
env_args="${env_args} -e REGISTRY_AUTH_USER -e REGISTRY_AUTH_PASSWORD"
while IFS= read -r line; do
[ -n "$line" ] && env_args="${env_args} -e $line"
done <<EOF
${{ inputs.extra-env }}
EOF
# shellcheck disable=SC2086
docker run --rm --add-host npm.cache.est:172.17.0.1 \
$mount_args \
-v "$PWD:${{ inputs.workspace }}" -w "${{ inputs.workspace }}" \
-v "${TOOL_CACHE_DIR}:/cache" -e TMPDIR=/cache \
-v /usr/local/share/ca-certificates/est-bundle.crt:/ca/est-bundle.crt:ro \
-e SSL_CERT_FILE=/ca/est-bundle.crt \
-e NODE_EXTRA_CA_CERTS=/ca/est-bundle.crt \
$env_args \
"${{ inputs.node-image }}" sh -euxc '${{ inputs.install-command }} && ${{ inputs.build-command }}'
- name: build push and verify image matrix
if: inputs.build-command == ''
env:
REGISTRY_AUTH_USER: ${{ github.actor }}
REGISTRY_AUTH_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
@@ -102,6 +191,7 @@ jobs:
VERIFY_COMMAND: ${{ inputs.verify-command }}
CACHE_REGISTRY_REF: ${{ inputs.cache-registry-ref }}
REVISION_ARG_NAME: ${{ inputs.revision-arg-name }}
IMAGE_MATRIX: ${{ inputs.image-matrix }}
run: |
set -eu
IMAGE_TAG="git-${GITHUB_SHA}"
@@ -137,6 +227,10 @@ jobs:
done <<EOF
${{ inputs.build-args }}
EOF
if [ -z "${IMAGE_MATRIX:-}" ]; then
echo "image-matrix is required for the built-in loop (or use build-command script mode)" >&2
exit 64
fi
if [ -n "${REVISION_ARG_NAME}" ]; then
build_args="${build_args} --build-arg ${REVISION_ARG_NAME}=${GITHUB_SHA}"
fi
@@ -185,3 +279,35 @@ jobs:
echo '```'
} >>"${GITHUB_STEP_SUMMARY:-/dev/stdout}"
cat "$digest_file"
- name: digest summary (script mode)
if: inputs.build-command != '' && inputs.image-matrix != ''
env:
IMAGE_ROOT: ${{ inputs.image-root }}
run: |
set -eu
image_tag="git-${GITHUB_SHA}"
digest_file=".tmp-ci/digests-script.txt"
mkdir -p .tmp-ci
: >"$digest_file"
while IFS='|' read -r service dockerfile target; do
[ -n "${service:-}" ] || continue
index_digest=$(docker buildx imagetools inspect "${IMAGE_ROOT}/${service}:${image_tag}" | awk '/^Digest:/{print $2}')
printf '%s|%s|%s\n' "$service" "$image_tag" "$index_digest" >>"$digest_file"
done <<'IMAGES'
${{ inputs.image-matrix }}
IMAGES
{
echo "## V4 fresh image digests"
echo
echo "revision: ${GITHUB_SHA}"
echo
echo '```'
cat "$digest_file"
echo '```'
} >>"${GITHUB_STEP_SUMMARY:-/dev/stdout}"
cat "$digest_file"
- name: cleanup per-run cache dir
if: always() && inputs.build-command != '' && inputs.cache-mode == 'per-run'
run: rm -rf -- "${TOOL_CACHE_DIR:-}"