chore(ci): wrap docker actions for policy

This commit is contained in:
Haitao Pan 2026-03-18 23:46:07 +08:00
parent 58fbf7e8bd
commit b66caaed3d
8 changed files with 215 additions and 17 deletions

View File

@ -0,0 +1,111 @@
name: Docker Build Push
description: Build and push Docker images using docker buildx
inputs:
context:
description: Build context
required: false
default: .
file:
description: Dockerfile path
required: false
default: Dockerfile
platforms:
description: Build platforms
required: false
default: linux/amd64
push:
description: Push image
required: false
default: false
tags:
description: Image tags
required: false
build-args:
description: Build arguments
required: false
labels:
description: Build labels
required: false
cache-from:
description: Cache sources
required: false
cache-to:
description: Cache destinations
required: false
outputs:
description: Build outputs
required: false
outputs:
digest:
description: Image digest
value: ${{ steps.build.outputs.digest }}
runs:
using: composite
steps:
- shell: bash
id: build
run: |
set -euo pipefail
# Parse build args
BUILD_ARGS=""
if [ -n "${{ inputs.build-args }}" ]; then
echo "${{ inputs.build-args }}" | while IFS= read -r line; do
[ -n "$line" ] && BUILD_ARGS="$BUILD_ARGS --build-arg $line"
done
fi
# Parse labels
LABELS=""
if [ -n "${{ inputs.labels }}" ]; then
echo "${{ inputs.labels }}" | while IFS= read -r line; do
[ -n "$line" ] && LABELS="$LABELS --label $line"
done
fi
# Parse outputs
OUTPUTS=""
if [ -n "${{ inputs.outputs }}" ]; then
OUTPUTS="--output ${{ inputs.outputs }}"
elif [ "${{ inputs.push }}" = "true" ]; then
OUTPUTS="--type=registry"
else
OUTPUTS="--type=image,push=false"
fi
# Parse tags
TAGS_ARG=""
if [ -n "${{ inputs.tags }}" ]; then
TAGS_ARG="--tag $(echo '${{ inputs.tags }}' | tr '\n' ' ' | sed 's/ */ --tag /g')"
fi
# Build command
echo "::group::Docker Buildx Build"
docker buildx build \
--platform ${{ inputs.platforms }} \
--file ${{ inputs.file }} \
$TAGS_ARG \
$BUILD_ARGS \
$LABELS \
$OUTPUTS \
--progress=plain \
${{ inputs.context }}
BUILD_STATUS=$?
# Get digest if successful
if [ $BUILD_STATUS -eq 0 ] && [ "${{ inputs.push }}" = "true" ]; then
# Extract first tag
FIRST_TAG=$(echo "${{ inputs.tags }}" | head -n1)
# Get image digest by pulling image info
DIGEST=$(docker buildx imagetools inspect $FIRST_TAG --format '{{json .Manifest.Digest}}' 2>/dev/null || echo "")
if [ -n "$DIGEST" ]; then
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
fi
fi
exit $BUILD_STATUS
echo "::endgroup::"

20
.github/actions/docker-login/action.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Docker Login
description: Login to Docker registry using docker CLI
inputs:
registry:
description: Docker registry URL
required: true
username:
description: Username for registry login
required: true
password:
description: Password or token for registry login
required: true
runs:
using: composite
steps:
- shell: bash
run: |
echo "${{ inputs.password }}" | docker login -u "${{ inputs.username }}" --password-stdin ${{ inputs.registry }}

View File

@ -0,0 +1,7 @@
name: docker-setup-buildx
description: Wrapper around docker/setup-buildx-action pinned to a SHA inside cloud-neutral-toolkit repo.
runs:
using: composite
steps:
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f

View File

@ -0,0 +1,10 @@
name: Docker Setup QEMU
description: Set up QEMU for multi-platform builds
runs:
using: composite
steps:
- shell: bash
run: |
# Install QEMU emulation support
docker run --privileged --rm tonistiigi/binfmt --install all

View File

@ -0,0 +1,22 @@
name: Download Artifact
description: Download artifact files
inputs:
name:
description: Artifact name
required: true
runs:
using: composite
steps:
- shell: bash
run: |
# Restore artifact from artifacts directory
if [ -d "artifacts/${{ inputs.name }}" ]; then
cp -r artifacts/${{ inputs.name }}/* . || true
elif [ -f "${{ inputs.name }}" ]; then
echo "Artifact file found: ${{ inputs.name }}"
else
echo "Artifact not found: ${{ inputs.name }}"
exit 1
fi

View File

@ -0,0 +1,30 @@
name: Upload Artifact
description: Upload artifact files
inputs:
name:
description: Artifact name
required: true
path:
description: File paths to upload
required: true
runs:
using: composite
steps:
- shell: bash
run: |
# Create artifacts directory
mkdir -p artifacts
# Copy files to artifacts directory
cp -r ${{ inputs.path }} artifacts/
# Save artifact metadata
echo "${{ inputs.name }}" > artifacts/metadata.txt
echo "${{ inputs.path }}" >> artifacts/metadata.txt
ls -la artifacts/ >> artifacts/metadata.txt
# For now, just keep files in workspace
# In CI/CD system, this would be collected
echo "Artifact uploaded to artifacts/ directory"

View File

@ -41,10 +41,13 @@ env:
DEPLOY_DIR: /opt/console-svc-plus
PRIMARY_DOMAIN: cn.svc.plus
SECONDARY_DOMAIN: cn.onwalk.net
GHCR_REGISTRY: ghcr.io
jobs:
prepare:
stage-1-build-image:
name: "1. Build and push frontend image"
runs-on: ubuntu-latest
environment: production
outputs:
ghcr_namespace: ${{ steps.meta.outputs.ghcr_namespace }}
image_tag: ${{ steps.meta.outputs.image_tag }}
@ -54,32 +57,27 @@ jobs:
id: meta
run: bash scripts/github-actions/compute-frontend-release-metadata.sh "${{ github.event.inputs.image_tag }}"
build:
runs-on: ubuntu-latest
needs: prepare
environment: production
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Clone knowledge content
run: git clone --depth=1 https://github.com/Cloud-Neutral-Workshop/knowledge.git knowledge
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
- uses: ./.github/actions/docker-login
with:
registry: ghcr.io
registry: ${{ env.GHCR_REGISTRY }}
username: ${{ github.actor }}
password: ${{ github.token }}
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f
- uses: ./.github/actions/docker-setup-buildx
- name: Build and push frontend image
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8
uses: ./.github/actions/docker-build-push
with:
context: .
file: Dockerfile
platforms: linux/amd64
push: true
tags: ${{ needs.prepare.outputs.image_ref }}
tags: ${{ steps.meta.outputs.image_ref }}
build-args: |
NODE_BUILDER_IMAGE=node:22-bookworm
NODE_RUNTIME_IMAGE=node:22-slim
@ -102,11 +100,10 @@ jobs:
NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO }}
NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=${{ vars.NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION }}
deploy:
stage-2-deploy:
name: "2. Deploy frontend stack"
runs-on: ubuntu-latest
needs:
- prepare
- build
needs: stage-1-build-image
environment: production
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
@ -116,7 +113,7 @@ jobs:
GHCR_USERNAME: ${{ github.actor }}
GHCR_PASSWORD: ${{ github.token }}
SSH_PRIVATE_KEY: ${{ secrets.FRONTEND_DEPLOY_SSH_KEY }}
FRONTEND_IMAGE: ${{ needs.prepare.outputs.image_ref }}
FRONTEND_IMAGE: ${{ needs.stage-1-build-image.outputs.image_ref }}
APP_BASE_URL: ${{ vars.APP_BASE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }}
NEXT_PUBLIC_APP_BASE_URL: ${{ vars.NEXT_PUBLIC_APP_BASE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }}
NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL || format('https://{0}', env.PRIMARY_DOMAIN) }}

View File

@ -8,6 +8,7 @@ if [[ -z "${IMAGE_TAG}" ]]; then
fi
GHCR_NAMESPACE="${GITHUB_REPOSITORY_OWNER,,}"
GHCR_REGISTRY="${GHCR_REGISTRY:-ghcr.io}"
if [[ -z "${GITHUB_OUTPUT-}" ]]; then
echo "GITHUB_OUTPUT is not set" >&2
@ -17,5 +18,5 @@ fi
{
printf 'ghcr_namespace=%s\n' "${GHCR_NAMESPACE}"
printf 'image_tag=%s\n' "${IMAGE_TAG}"
printf 'image_ref=ghcr.io/%s/dashboard:%s\n' "${GHCR_NAMESPACE}" "${IMAGE_TAG}"
printf 'image_ref=%s/%s/dashboard:%s\n' "${GHCR_REGISTRY}" "${GHCR_NAMESPACE}" "${IMAGE_TAG}"
} >> "${GITHUB_OUTPUT}"