This commit is contained in:
2026-03-26 09:47:13 +08:00
commit dce2088374
7 changed files with 500 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
name: 'Auto Deploy Abort'
description: 'Aborts a pending deployment on the app server'
inputs:
ssh_key:
description: 'SSH Private Key'
required: true
host:
description: 'Remote App Host'
required: true
port:
description: 'Remote SSH Port'
required: true
user:
description: 'Remote User'
required: true
app_key:
description: 'Application Key'
required: true
auto_deploy_path:
description: 'Path to auto-deploy repo on remote'
required: true
auto_deploy_branch:
description: 'Branch to checkout in auto-deploy repo'
required: false
default: 'main'
runs:
using: 'composite'
steps:
- name: SSH Deploy Abort
shell: bash
env:
SSH_KEY: ${{ inputs.ssh_key }}
DEPLOY_HOST: ${{ inputs.host }}
DEPLOY_PORT: ${{ inputs.port }}
DEPLOY_USER: ${{ inputs.user }}
APP_KEY: ${{ inputs.app_key }}
AUTO_DEPLOY_PATH: ${{ inputs.auto_deploy_path }}
AUTO_DEPLOY_BRANCH: ${{ inputs.auto_deploy_branch }}
run: |
set -eu
SSH_TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$SSH_TMP_DIR"' EXIT
echo "$SSH_KEY" > "$SSH_TMP_DIR/id_rsa"
chmod 600 "$SSH_TMP_DIR/id_rsa"
ssh-keyscan -p "$DEPLOY_PORT" "$DEPLOY_HOST" > "$SSH_TMP_DIR/known_hosts"
SSH_OPTS=(
-i "$SSH_TMP_DIR/id_rsa"
-o UserKnownHostsFile="$SSH_TMP_DIR/known_hosts"
-o StrictHostKeyChecking=yes
-p "$DEPLOY_PORT"
)
ssh "${SSH_OPTS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"APP_KEY=$(printf '%q' "$APP_KEY") AUTO_DEPLOY_PATH=$(printf '%q' "$AUTO_DEPLOY_PATH") AUTO_DEPLOY_BRANCH=$(printf '%q' "$AUTO_DEPLOY_BRANCH") bash -se -c 'cd \"\$AUTO_DEPLOY_PATH\"; git checkout \"\$AUTO_DEPLOY_BRANCH\"; git pull --ff-only; git submodule sync --recursive; git submodule update --init --recursive; echo \"[remote] auto-deploy repo updated\"; ./common/deploy.sh abort-pending \"\$APP_KEY\"; echo \"[remote] \$APP_KEY pending deployment aborted\"'"
+57
View File
@@ -0,0 +1,57 @@
name: 'Auto Deploy Finalize'
description: 'Finalizes the deployment on the app server'
inputs:
ssh_key:
description: 'SSH Private Key'
required: true
host:
description: 'Remote App Host'
required: true
port:
description: 'Remote SSH Port'
required: true
user:
description: 'Remote User'
required: true
app_key:
description: 'Application Key'
required: true
auto_deploy_path:
description: 'Path to auto-deploy repo on remote'
required: true
auto_deploy_branch:
description: 'Branch to checkout in auto-deploy repo'
required: false
default: 'main'
runs:
using: 'composite'
steps:
- name: SSH Deploy Finalize
shell: bash
env:
SSH_KEY: ${{ inputs.ssh_key }}
DEPLOY_HOST: ${{ inputs.host }}
DEPLOY_PORT: ${{ inputs.port }}
DEPLOY_USER: ${{ inputs.user }}
APP_KEY: ${{ inputs.app_key }}
AUTO_DEPLOY_PATH: ${{ inputs.auto_deploy_path }}
AUTO_DEPLOY_BRANCH: ${{ inputs.auto_deploy_branch }}
run: |
set -eu
SSH_TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$SSH_TMP_DIR"' EXIT
echo "$SSH_KEY" > "$SSH_TMP_DIR/id_rsa"
chmod 600 "$SSH_TMP_DIR/id_rsa"
ssh-keyscan -p "$DEPLOY_PORT" "$DEPLOY_HOST" > "$SSH_TMP_DIR/known_hosts"
SSH_OPTS=(
-i "$SSH_TMP_DIR/id_rsa"
-o UserKnownHostsFile="$SSH_TMP_DIR/known_hosts"
-o StrictHostKeyChecking=yes
-p "$DEPLOY_PORT"
)
ssh "${SSH_OPTS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"APP_KEY=$(printf '%q' "$APP_KEY") AUTO_DEPLOY_PATH=$(printf '%q' "$AUTO_DEPLOY_PATH") AUTO_DEPLOY_BRANCH=$(printf '%q' "$AUTO_DEPLOY_BRANCH") bash -se -c 'cd \"\$AUTO_DEPLOY_PATH\"; git checkout \"\$AUTO_DEPLOY_BRANCH\"; git pull --ff-only; git submodule sync --recursive; git submodule update --init --recursive; echo \"[remote] auto-deploy repo updated\"; ./common/deploy.sh finalize \"\$APP_KEY\"; echo \"[remote] \$APP_KEY deployment finalized\"'"
+69
View File
@@ -0,0 +1,69 @@
name: 'Auto Deploy Stage'
description: 'Stages a new deployment on the app server'
inputs:
ssh_key:
description: 'SSH Private Key'
required: true
host:
description: 'Remote App Host'
required: true
port:
description: 'Remote SSH Port'
required: true
user:
description: 'Remote User'
required: true
harbor_user:
description: 'Harbor Username'
required: true
harbor_secret:
description: 'Harbor Secret'
required: true
image_tag:
description: 'Image Tag to deploy'
required: true
app_key:
description: 'Application Key'
required: true
auto_deploy_path:
description: 'Path to auto-deploy repo on remote'
required: true
auto_deploy_branch:
description: 'Branch to checkout in auto-deploy repo'
required: false
default: 'main'
runs:
using: 'composite'
steps:
- name: SSH Deploy Stage
shell: bash
env:
SSH_KEY: ${{ inputs.ssh_key }}
DEPLOY_HOST: ${{ inputs.host }}
DEPLOY_PORT: ${{ inputs.port }}
DEPLOY_USER: ${{ inputs.user }}
HARBOR_USER: ${{ inputs.harbor_user }}
HARBOR_SECRET: ${{ inputs.harbor_secret }}
IMAGE_TAG: ${{ inputs.image_tag }}
APP_KEY: ${{ inputs.app_key }}
AUTO_DEPLOY_PATH: ${{ inputs.auto_deploy_path }}
AUTO_DEPLOY_BRANCH: ${{ inputs.auto_deploy_branch }}
run: |
set -eu
SSH_TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$SSH_TMP_DIR"' EXIT
echo "$SSH_KEY" > "$SSH_TMP_DIR/id_rsa"
chmod 600 "$SSH_TMP_DIR/id_rsa"
ssh-keyscan -p "$DEPLOY_PORT" "$DEPLOY_HOST" > "$SSH_TMP_DIR/known_hosts"
SSH_OPTS=(
-i "$SSH_TMP_DIR/id_rsa"
-o UserKnownHostsFile="$SSH_TMP_DIR/known_hosts"
-o StrictHostKeyChecking=yes
-p "$DEPLOY_PORT"
)
ssh "${SSH_OPTS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"APP_KEY=$(printf '%q' "$APP_KEY") IMAGE_TAG=$(printf '%q' "$IMAGE_TAG") HARBOR_USER=$(printf '%q' "$HARBOR_USER") HARBOR_SECRET=$(printf '%q' "$HARBOR_SECRET") AUTO_DEPLOY_PATH=$(printf '%q' "$AUTO_DEPLOY_PATH") AUTO_DEPLOY_BRANCH=$(printf '%q' "$AUTO_DEPLOY_BRANCH") bash -se -c 'printf \"%s\" \"\$HARBOR_SECRET\" | sudo /usr/bin/docker login harbor.hclife.co -u \"\$HARBOR_USER\" --password-stdin; echo \"[remote] Harbor login succeeded\"; cd \"\$AUTO_DEPLOY_PATH\"; git checkout \"\$AUTO_DEPLOY_BRANCH\"; git pull --ff-only; git submodule sync --recursive; git submodule update --init --recursive; echo \"[remote] auto-deploy repo updated\"; ./common/deploy.sh deploy \"\$APP_KEY\" \"\$IMAGE_TAG\"; echo \"[remote] \$APP_KEY deploy staged with IMAGE_TAG=\$IMAGE_TAG\"; echo \"[remote] current deployment status:\"; ./common/deploy.sh status \"\$APP_KEY\" --format env'"
+75
View File
@@ -0,0 +1,75 @@
name: 'Auto Deploy Status'
description: 'Reads current deployment status from app server'
inputs:
ssh_key:
description: 'SSH Private Key'
required: true
host:
description: 'Remote App Host'
required: true
port:
description: 'Remote SSH Port'
required: true
user:
description: 'Remote User'
required: true
app_key:
description: 'Application Key'
required: true
auto_deploy_path:
description: 'Path to auto-deploy repo on remote'
required: true
outputs:
app_upstream_host:
description: 'Upstream Host or IP'
value: ${{ steps.get_status.outputs.app_upstream_host }}
pending_port:
description: 'Pending Port'
value: ${{ steps.get_status.outputs.pending_port }}
pending_color:
description: 'Pending Color'
value: ${{ steps.get_status.outputs.pending_color }}
runs:
using: 'composite'
steps:
- name: SSH Deploy Status
id: get_status
shell: bash
env:
SSH_KEY: ${{ inputs.ssh_key }}
DEPLOY_HOST: ${{ inputs.host }}
DEPLOY_PORT: ${{ inputs.port }}
DEPLOY_USER: ${{ inputs.user }}
APP_KEY: ${{ inputs.app_key }}
AUTO_DEPLOY_PATH: ${{ inputs.auto_deploy_path }}
run: |
set -eu
SSH_TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$SSH_TMP_DIR"' EXIT
echo "$SSH_KEY" > "$SSH_TMP_DIR/id_rsa"
chmod 600 "$SSH_TMP_DIR/id_rsa"
ssh-keyscan -p "$DEPLOY_PORT" "$DEPLOY_HOST" > "$SSH_TMP_DIR/known_hosts"
SSH_OPTS=(
-i "$SSH_TMP_DIR/id_rsa"
-o UserKnownHostsFile="$SSH_TMP_DIR/known_hosts"
-o StrictHostKeyChecking=yes
-p "$DEPLOY_PORT"
)
STATUS_OUTPUT="$(ssh "${SSH_OPTS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" "cd $(printf '%q' "$AUTO_DEPLOY_PATH") && ./common/deploy.sh status $(printf '%q' "$APP_KEY") --format env")"
printf '%s\n' "$STATUS_OUTPUT"
APP_UPSTREAM_HOST="$(printf '%s\n' "$STATUS_OUTPUT" | sed -n 's/^APP_UPSTREAM_HOST=//p')"
PENDING_PORT="$(printf '%s\n' "$STATUS_OUTPUT" | sed -n 's/^PENDING_PORT=//p')"
PENDING_COLOR="$(printf '%s\n' "$STATUS_OUTPUT" | sed -n 's/^PENDING_COLOR=//p')"
test -n "$APP_UPSTREAM_HOST"
test -n "$PENDING_PORT"
test -n "$PENDING_COLOR"
echo "app_upstream_host=$APP_UPSTREAM_HOST" >> "$GITHUB_OUTPUT"
echo "pending_port=$PENDING_PORT" >> "$GITHUB_OUTPUT"
echo "pending_color=$PENDING_COLOR" >> "$GITHUB_OUTPUT"
+69
View File
@@ -0,0 +1,69 @@
name: 'Nginx Switch'
description: 'Switches the Nginx upstream to the newly staged color'
inputs:
ssh_key:
description: 'SSH Private Key'
required: true
host:
description: 'Remote Nginx Host'
required: true
port:
description: 'Remote SSH Port'
required: true
user:
description: 'Remote User'
required: true
app_key:
description: 'Application Key'
required: true
app_upstream_host:
description: 'App Server Host or IP'
required: true
pending_port:
description: 'Pending Port'
required: true
pending_color:
description: 'Pending Color'
required: true
auto_deploy_path:
description: 'Path to auto-deploy repo on remote'
required: true
auto_deploy_branch:
description: 'Branch to checkout in auto-deploy repo'
required: false
default: 'main'
runs:
using: 'composite'
steps:
- name: SSH Nginx Switch
shell: bash
env:
SSH_KEY: ${{ inputs.ssh_key }}
DEPLOY_HOST: ${{ inputs.host }}
DEPLOY_PORT: ${{ inputs.port }}
DEPLOY_USER: ${{ inputs.user }}
APP_KEY: ${{ inputs.app_key }}
APP_UPSTREAM_HOST: ${{ inputs.app_upstream_host }}
PENDING_PORT: ${{ inputs.pending_port }}
PENDING_COLOR: ${{ inputs.pending_color }}
AUTO_DEPLOY_PATH: ${{ inputs.auto_deploy_path }}
AUTO_DEPLOY_BRANCH: ${{ inputs.auto_deploy_branch }}
run: |
set -eu
SSH_TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$SSH_TMP_DIR"' EXIT
echo "$SSH_KEY" > "$SSH_TMP_DIR/id_rsa"
chmod 600 "$SSH_TMP_DIR/id_rsa"
ssh-keyscan -p "$DEPLOY_PORT" "$DEPLOY_HOST" > "$SSH_TMP_DIR/known_hosts"
SSH_OPTS=(
-i "$SSH_TMP_DIR/id_rsa"
-o UserKnownHostsFile="$SSH_TMP_DIR/known_hosts"
-o StrictHostKeyChecking=yes
-p "$DEPLOY_PORT"
)
ssh "${SSH_OPTS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"APP_KEY=$(printf '%q' "$APP_KEY") APP_UPSTREAM_HOST=$(printf '%q' "$APP_UPSTREAM_HOST") PENDING_PORT=$(printf '%q' "$PENDING_PORT") PENDING_COLOR=$(printf '%q' "$PENDING_COLOR") AUTO_DEPLOY_PATH=$(printf '%q' "$AUTO_DEPLOY_PATH") AUTO_DEPLOY_BRANCH=$(printf '%q' "$AUTO_DEPLOY_BRANCH") bash -se -c 'cd \"\$AUTO_DEPLOY_PATH\"; git checkout \"\$AUTO_DEPLOY_BRANCH\"; git pull --ff-only; git submodule sync --recursive; git submodule update --init --recursive; echo \"[remote] auto-deploy repo updated on nginx server\"; ./common/nginx/manage-nginx.sh switch \"\$APP_KEY\" \"\$APP_UPSTREAM_HOST\" \"\$PENDING_PORT\"; echo \"[remote] nginx switched \$APP_KEY to \$APP_UPSTREAM_HOST:\$PENDING_PORT (\$PENDING_COLOR)\"'"
+84
View File
@@ -0,0 +1,84 @@
name: 'Checkout and Config Injection'
description: 'Clones the project via SSH and injects configs from a separate repo'
inputs:
ssh_key:
description: 'SSH Private Key'
required: true
config_repo:
description: 'Configuration Repository Path (e.g., dev-ops/configs.git)'
required: false
default: ''
config_repo_branch:
description: 'Configuration Repository Branch'
required: false
default: 'main'
app_key:
description: 'Application Key in the config repo'
required: true
gitea_host:
description: 'Gitea Hostname'
required: false
default: 'gitea.hclife.co'
gitea_port:
description: 'Gitea SSH Port'
required: false
default: '2222'
runs:
using: 'composite'
steps:
- name: Checkout and Config
shell: bash
env:
SSH_KEY: ${{ inputs.ssh_key }}
CONFIG_REPO: ${{ inputs.config_repo }}
CONFIG_REPO_BRANCH: ${{ inputs.config_repo_branch }}
APP_KEY: ${{ inputs.app_key }}
GITEA_HOST: ${{ inputs.gitea_host }}
GITEA_PORT: ${{ inputs.gitea_port }}
run: |
set -eu
SSH_TMP_DIR=$(mktemp -d)
trap 'rm -rf "$SSH_TMP_DIR"' EXIT
echo "$SSH_KEY" > "$SSH_TMP_DIR/id_rsa"
chmod 600 "$SSH_TMP_DIR/id_rsa"
ssh-keyscan -p "$GITEA_PORT" "$GITEA_HOST" > "$SSH_TMP_DIR/known_hosts"
export GIT_SSH_COMMAND="ssh -i $SSH_TMP_DIR/id_rsa -o UserKnownHostsFile=$SSH_TMP_DIR/known_hosts -o StrictHostKeyChecking=yes"
echo "Initializing project repository..."
git init
git config --global --add safe.directory "$GITHUB_WORKSPACE"
if git remote get-url origin >/dev/null 2>&1; then
git remote set-url origin "ssh://git@$GITEA_HOST:$GITEA_PORT/${{ github.repository }}.git"
else
git remote add origin "ssh://git@$GITEA_HOST:$GITEA_PORT/${{ github.repository }}.git"
fi
git fetch --depth 1 origin "${{ github.sha }}"
git checkout FETCH_HEAD
if [ -n "$CONFIG_REPO" ]; then
echo "Fetching optional config repository..."
echo "Config repo: $CONFIG_REPO"
echo "Config repo branch: $CONFIG_REPO_BRANCH"
echo "Expected config app key: $APP_KEY"
git clone -b "$CONFIG_REPO_BRANCH" "ssh://git@$GITEA_HOST:$GITEA_PORT/$CONFIG_REPO" configs
echo "Config repo top-level entries:"
find configs -maxdepth 2 -mindepth 1 | sort
CONFIG_SOURCE_DIR="configs/${APP_KEY}"
if [ -d "$CONFIG_SOURCE_DIR" ]; then
echo "Applying config tree from '$CONFIG_SOURCE_DIR'..."
cp -Rv "$CONFIG_SOURCE_DIR"/. .
rm -rf configs
else
echo "Error: '$CONFIG_SOURCE_DIR' not found in config repository"
echo "Available directories under configs/:"
find configs -maxdepth 3 -type d | sort
exit 1
fi
else
echo "No config repository configured; skipping config injection."
fi
+89
View File
@@ -0,0 +1,89 @@
name: 'Docker Build and Push'
description: 'Log into Harbor, build and push image'
inputs:
harbor_user:
description: 'Harbor Username'
required: true
harbor_secret:
description: 'Harbor Secret'
required: true
image_repo:
description: 'Harbor Image Repository (without tag)'
required: true
dockerfile:
description: 'Path to Dockerfile'
required: false
default: './Dockerfile'
build_context:
description: 'Docker Build Context'
required: false
default: '.'
docker_build_args:
description: 'Docker Build Args as multiple lines or single line'
required: false
default: ''
harbor_host:
description: 'Harbor Host'
required: false
default: 'harbor.hclife.co'
outputs:
image_tag:
description: 'Generated Image Tag (UTC timestamp)'
value: ${{ steps.build_push.outputs.image_tag }}
runs:
using: 'composite'
steps:
- name: Build and Push
id: build_push
shell: bash
env:
DOCKER_BUILDKIT: 1
HARBOR_USER: ${{ inputs.harbor_user }}
HARBOR_SECRET: ${{ inputs.harbor_secret }}
COMMIT_SHA: ${{ github.sha }}
IMAGE_REPO: ${{ inputs.image_repo }}
DOCKERFILE: ${{ inputs.dockerfile }}
BUILD_CONTEXT: ${{ inputs.build_context }}
DOCKER_BUILD_ARGS: ${{ inputs.docker_build_args }}
HARBOR_HOST: ${{ inputs.harbor_host }}
run: |
set -eu
IMAGE_TAG="$(date -u +%Y%m%d-%H%M%S)"
echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
test -n "$IMAGE_REPO"
test -n "$DOCKERFILE"
test -n "$BUILD_CONTEXT"
test -f "$DOCKERFILE"
test -d "$BUILD_CONTEXT"
echo "Logging into $HARBOR_HOST..."
echo "$HARBOR_SECRET" | docker login "$HARBOR_HOST" -u "$HARBOR_USER" --password-stdin
BUILD_ARG_FLAGS=()
if [ -n "$DOCKER_BUILD_ARGS" ]; then
while IFS= read -r build_arg_line; do
[ -n "$build_arg_line" ] || continue
BUILD_ARG_FLAGS+=("--build-arg" "$build_arg_line")
done <<< "$DOCKER_BUILD_ARGS"
fi
echo "Building Image..."
if [ ${#BUILD_ARG_FLAGS[@]} -gt 0 ]; then
docker build "${BUILD_ARG_FLAGS[@]}" \
-t "$IMAGE_REPO:$IMAGE_TAG" \
-t "$IMAGE_REPO:$COMMIT_SHA" \
-t "$IMAGE_REPO:latest" \
-f "$DOCKERFILE" "$BUILD_CONTEXT"
else
docker build \
-t "$IMAGE_REPO:$IMAGE_TAG" \
-t "$IMAGE_REPO:$COMMIT_SHA" \
-t "$IMAGE_REPO:latest" \
-f "$DOCKERFILE" "$BUILD_CONTEXT"
fi
echo "Pushing Image..."
docker push "$IMAGE_REPO:$IMAGE_TAG"
docker push "$IMAGE_REPO:$COMMIT_SHA"
docker push "$IMAGE_REPO:latest"