Refactor GitHub Actions pipeline
This commit is contained in:
parent
fdbef2ab29
commit
79fd46678b
47
.github/scripts/normalize-private-key.py
vendored
Normal file
47
.github/scripts/normalize-private-key.py
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def strip_outer_quotes(value: str) -> str:
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
||||
return value[1:-1].strip()
|
||||
return value
|
||||
|
||||
|
||||
def raw_payload() -> str:
|
||||
return strip_outer_quotes(os.environ["SINGLE_NODE_VPS_SSH_PRIVATE_KEY"].replace("\r", "").strip())
|
||||
|
||||
|
||||
def normalize() -> str:
|
||||
raw = raw_payload()
|
||||
candidates = [raw]
|
||||
|
||||
if "\\n" in raw:
|
||||
candidates.append(strip_outer_quotes(raw.replace("\\n", "\n").strip()))
|
||||
|
||||
try:
|
||||
decoded = base64.b64decode(raw, validate=True).decode("utf-8").replace("\r", "").strip()
|
||||
except Exception:
|
||||
decoded = ""
|
||||
|
||||
if decoded:
|
||||
candidates.append(strip_outer_quotes(decoded))
|
||||
|
||||
for candidate in candidates:
|
||||
if "BEGIN " in candidate and "PRIVATE KEY" in candidate:
|
||||
return candidate.rstrip("\n") + "\n"
|
||||
|
||||
return raw.rstrip("\n") + "\n"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 2 or sys.argv[1] != "normalize":
|
||||
raise SystemExit("usage: normalize-private-key.py normalize")
|
||||
|
||||
sys.stdout.write(normalize())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
184
.github/workflows/build-base-images.yml
vendored
184
.github/workflows/build-base-images.yml
vendored
@ -1,184 +0,0 @@
|
||||
name: Build Base Images
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
registry:
|
||||
description: "Target registry"
|
||||
type: string
|
||||
required: true
|
||||
|
||||
org:
|
||||
description: "Target organization"
|
||||
type: string
|
||||
required: true
|
||||
|
||||
push_images:
|
||||
description: "Push images instead of building locally"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
dockerhub_namespace:
|
||||
description: "Docker Hub namespace (user/org)"
|
||||
type: string
|
||||
default: "cloudneutral"
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
registry:
|
||||
description: "Target registry"
|
||||
type: string
|
||||
default: "ghcr.io"
|
||||
org:
|
||||
description: "Target organization"
|
||||
type: string
|
||||
default: "cloud-neutral-toolkit"
|
||||
push_images:
|
||||
description: "Push images instead of building locally"
|
||||
type: boolean
|
||||
default: true
|
||||
dockerhub_namespace:
|
||||
description: "Docker Hub namespace (user/org)"
|
||||
type: string
|
||||
default: "cloudneutral"
|
||||
|
||||
push:
|
||||
paths:
|
||||
- "deploy/base-images/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
env:
|
||||
REGISTRY: ${{ inputs.registry || github.event.inputs.registry || 'ghcr.io' }}
|
||||
ORG: ${{ inputs.org || github.event.inputs.org || 'cloud-neutral-toolkit' }}
|
||||
|
||||
# Push control
|
||||
PUSH_IMAGES: ${{ github.event_name == 'push'
|
||||
|| (github.event_name == 'workflow_call' && inputs.push_images)
|
||||
|| (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true') }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
image:
|
||||
- { name: openresty-geoip, file: deploy/base-images/openresty-geoip.Dockerfile }
|
||||
- { name: postgres-runtime, file: deploy/base-images/postgres-runtime-wth-extensions.Dockerfile }
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate Auto Tags
|
||||
id: meta
|
||||
uses: ./.github/actions/auto-tag
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.image.name }}
|
||||
|
||||
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
id: build
|
||||
with:
|
||||
context: .
|
||||
file: ${{ matrix.image.file }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ (github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch') && inputs.push_images || github.event_name == 'push' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Push to Docker Hub (optional)
|
||||
# -------------------------------------------------------------
|
||||
- name: Login to Docker Hub
|
||||
if: env.PUSH_IMAGES == 'true'
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Re-tag & Push service image to Docker Hub
|
||||
# -------------------------------------------------------------
|
||||
- name: Re-tag & Push Image (Docker Hub)
|
||||
if: env.PUSH_IMAGES == 'true'
|
||||
env:
|
||||
TARGET_NS: ${{ inputs.dockerhub_namespace || github.event.inputs.dockerhub_namespace || 'cloudneutral' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
SERVICE="${{ matrix.image.name }}"
|
||||
ORIGIN_IMG="${{ env.REGISTRY }}/${{ env.ORG }}/${SERVICE}@${{ steps.build.outputs.digest }}"
|
||||
TARGET_REPO="docker.io/${TARGET_NS}/${SERVICE}"
|
||||
|
||||
TAG="latest"
|
||||
docker pull "$ORIGIN_IMG"
|
||||
docker tag "$ORIGIN_IMG" "$TARGET_REPO:$TAG"
|
||||
docker push "$TARGET_REPO:$TAG"
|
||||
|
||||
Security-service:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
image:
|
||||
- { name: openresty-geoip, file: deploy/base-images/openresty-geoip.Dockerfile }
|
||||
- { name: postgres-runtime, file: deploy/base-images/postgres-runtime-wth-extensions.Dockerfile }
|
||||
|
||||
steps:
|
||||
# -------------------------------------------------------------
|
||||
# Checkout source
|
||||
# -------------------------------------------------------------
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Resolve short sha tag
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "sha_short=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
|
||||
- uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
|
||||
with:
|
||||
image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.image.name }}:${{ steps.vars.outputs.sha_short }}
|
||||
output-file: sbom.spdx.json
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sbom-${{ matrix.image.name }}
|
||||
path: sbom.spdx.json
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Trivy Vulnerability Scan
|
||||
# -------------------------------------------------------------
|
||||
- uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
|
||||
with:
|
||||
image-ref: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.image.name }}:${{ steps.vars.outputs.sha_short }}
|
||||
severity: HIGH,CRITICAL
|
||||
exit-code: '1'
|
||||
|
||||
- uses: sigstore/cosign-installer@2e2f661cd4be3a4b891a882064e49d0fed4b7b88 # v3.9.0
|
||||
with:
|
||||
cosign-release: 'v2.4.1'
|
||||
|
||||
- name: Sign Image
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
run: |
|
||||
COSIGN_IMAGE=${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.image.name }}:${{ steps.vars.outputs.sha_short }}
|
||||
cosign sign --yes "$COSIGN_IMAGE"
|
||||
75
.github/workflows/build-push-ghcr-image.yml
vendored
75
.github/workflows/build-push-ghcr-image.yml
vendored
@ -1,75 +0,0 @@
|
||||
name: Build And Push GHCR Image
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: Optional image tag. Defaults to the current commit SHA.
|
||||
required: false
|
||||
type: string
|
||||
push_latest:
|
||||
description: Also publish the `latest` tag.
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: Optional image tag. Defaults to the current commit SHA.
|
||||
required: false
|
||||
type: string
|
||||
push_latest:
|
||||
description: Also publish the `latest` tag.
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: build-push-ghcr-image-accounts-${{ github.ref_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_REPO_OWNER: ${{ vars.IMAGE_REPO_OWNER || github.repository_owner }}
|
||||
IMAGE_NAME: accounts
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set Up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Log In To GHCR
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ vars.GHCR_USERNAME || github.repository_owner }}
|
||||
password: ${{ secrets.GHCR_TOKEN || github.token }}
|
||||
|
||||
- name: Compute Image Tags
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_REPO_OWNER }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=raw,value=${{ inputs.image_tag }},enable=${{ inputs.image_tag != '' }}
|
||||
type=sha,format=short,enable=${{ inputs.image_tag == '' }}
|
||||
type=raw,value=latest,enable=${{ inputs.push_latest || github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: Build And Push Image
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
525
.github/workflows/pipeline.yml
vendored
Normal file
525
.github/workflows/pipeline.yml
vendored
Normal file
@ -0,0 +1,525 @@
|
||||
name: Pipeline
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_host:
|
||||
description: Ansible inventory host or alias.
|
||||
required: false
|
||||
default: "jp-xhttp-contabo.svc.plus"
|
||||
type: string
|
||||
run_apply:
|
||||
description: Apply deployment (false = dry-run).
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
image_tag:
|
||||
description: Optional service image tag. Defaults to the current commit SHA.
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
push_image:
|
||||
description: Push the service image to GHCR.
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
push_latest:
|
||||
description: Also publish the service image with the latest tag.
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
run_base_images:
|
||||
description: Also run the base image workflow when base image sources exist.
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
push_base_images:
|
||||
description: Push base images when the base image workflow runs.
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
base_image_registry:
|
||||
description: Target registry for base images.
|
||||
required: false
|
||||
default: "ghcr.io"
|
||||
type: string
|
||||
base_image_org:
|
||||
description: Target organization for base images.
|
||||
required: false
|
||||
default: "cloud-neutral-toolkit"
|
||||
type: string
|
||||
dockerhub_namespace:
|
||||
description: Docker Hub namespace for base images.
|
||||
required: false
|
||||
default: "cloudneutral"
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pipeline-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
DEFAULT_TARGET_HOST: jp-xhttp-contabo.svc.plus
|
||||
|
||||
jobs:
|
||||
prep:
|
||||
name: Prep
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
base_images_exists: ${{ steps.flags.outputs.base_images_exists }}
|
||||
run_base_images: ${{ steps.flags.outputs.run_base_images }}
|
||||
push_base_images: ${{ steps.flags.outputs.push_base_images }}
|
||||
base_image_registry: ${{ steps.flags.outputs.base_image_registry }}
|
||||
base_image_org: ${{ steps.flags.outputs.base_image_org }}
|
||||
dockerhub_namespace: ${{ steps.flags.outputs.dockerhub_namespace }}
|
||||
target_host: ${{ steps.flags.outputs.target_host }}
|
||||
run_apply: ${{ steps.flags.outputs.run_apply }}
|
||||
image_tag: ${{ steps.flags.outputs.image_tag }}
|
||||
push_image: ${{ steps.flags.outputs.push_image }}
|
||||
push_latest: ${{ steps.flags.outputs.push_latest }}
|
||||
steps:
|
||||
- name: Check Out Repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve Pipeline Flags
|
||||
id: flags
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
base_images_exists=false
|
||||
run_base_images=false
|
||||
push_base_images=false
|
||||
push_image=true
|
||||
push_latest=false
|
||||
image_tag=""
|
||||
base_image_registry="ghcr.io"
|
||||
base_image_org="${{ vars.IMAGE_REPO_OWNER || github.repository_owner }}"
|
||||
dockerhub_namespace="${{ vars.DOCKERHUB_NAMESPACE || 'cloudneutral' }}"
|
||||
target_host="${DEFAULT_TARGET_HOST}"
|
||||
run_apply=true
|
||||
|
||||
if [[ -d deploy/base-images ]] && find deploy/base-images -type f | grep -q .; then
|
||||
base_images_exists=true
|
||||
fi
|
||||
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
|
||||
target_host="${{ inputs.target_host }}"
|
||||
[[ "${{ inputs.run_apply }}" == "true" ]] && run_apply=true || run_apply=false
|
||||
image_tag="${{ inputs.image_tag }}"
|
||||
[[ "${{ inputs.push_image }}" == "true" ]] && push_image=true || push_image=false
|
||||
[[ "${{ inputs.push_latest }}" == "true" ]] && push_latest=true || push_latest=false
|
||||
[[ "${{ inputs.run_base_images }}" == "true" ]] && run_base_images=true || run_base_images=false
|
||||
[[ "${{ inputs.push_base_images }}" == "true" ]] && push_base_images=true || push_base_images=false
|
||||
base_image_registry="${{ inputs.base_image_registry }}"
|
||||
base_image_org="${{ inputs.base_image_org }}"
|
||||
dockerhub_namespace="${{ inputs.dockerhub_namespace }}"
|
||||
if [[ "${base_images_exists}" != "true" ]]; then
|
||||
run_base_images=false
|
||||
push_base_images=false
|
||||
fi
|
||||
else
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
|
||||
push_image=false
|
||||
fi
|
||||
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
|
||||
push_latest=true
|
||||
fi
|
||||
|
||||
if [[ "${base_images_exists}" == "true" ]]; then
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
|
||||
base_ref="${{ github.event.pull_request.base.sha }}"
|
||||
head_ref="${{ github.event.pull_request.head.sha }}"
|
||||
else
|
||||
base_ref="${{ github.event.before }}"
|
||||
head_ref="${{ github.sha }}"
|
||||
fi
|
||||
|
||||
if [[ -n "${base_ref}" && "${base_ref}" != "0000000000000000000000000000000000000000" ]]; then
|
||||
if git diff --name-only "${base_ref}" "${head_ref}" | grep -q '^deploy/base-images/'; then
|
||||
run_base_images=true
|
||||
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
|
||||
push_base_images=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
{
|
||||
echo "base_images_exists=${base_images_exists}"
|
||||
echo "run_base_images=${run_base_images}"
|
||||
echo "push_base_images=${push_base_images}"
|
||||
echo "base_image_registry=${base_image_registry}"
|
||||
echo "base_image_org=${base_image_org}"
|
||||
echo "dockerhub_namespace=${dockerhub_namespace}"
|
||||
echo "target_host=${target_host}"
|
||||
echo "run_apply=${run_apply}"
|
||||
echo "image_tag=${image_tag}"
|
||||
echo "push_image=${push_image}"
|
||||
echo "push_latest=${push_latest}"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
name: Build
|
||||
needs: prep
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BASE_REGISTRY: ${{ needs.prep.outputs.base_image_registry }}
|
||||
BASE_ORG: ${{ needs.prep.outputs.base_image_org }}
|
||||
PUSH_BASE_IMAGES: ${{ needs.prep.outputs.push_base_images }}
|
||||
DOCKERHUB_NAMESPACE: ${{ needs.prep.outputs.dockerhub_namespace }}
|
||||
SERVICE_REGISTRY: ghcr.io
|
||||
SERVICE_IMAGE_REPO_OWNER: ${{ vars.IMAGE_REPO_OWNER || github.repository_owner }}
|
||||
SERVICE_IMAGE_NAME: accounts
|
||||
outputs:
|
||||
artifact_name: ${{ steps.service_artifact.outputs.name }}
|
||||
service_image_repo: ${{ steps.service_image.outputs.repo }}
|
||||
base_images_pushed: ${{ needs.prep.outputs.push_base_images }}
|
||||
openresty_tag: ${{ steps.openresty_preferred.outputs.tag }}
|
||||
postgres_tag: ${{ steps.postgres_preferred.outputs.tag }}
|
||||
steps:
|
||||
- name: Check Out Repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set Up QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
|
||||
- name: Set Up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Log In To Base Image Registry
|
||||
if: needs.prep.outputs.run_base_images == 'true' && env.PUSH_BASE_IMAGES == 'true'
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ${{ env.BASE_REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate OpenResty Tags
|
||||
if: needs.prep.outputs.run_base_images == 'true'
|
||||
id: openresty_meta
|
||||
uses: ./.github/actions/auto-tag
|
||||
with:
|
||||
image: ${{ env.BASE_REGISTRY }}/${{ env.BASE_ORG }}/openresty-geoip
|
||||
|
||||
- name: Resolve OpenResty Preferred Tag
|
||||
if: needs.prep.outputs.run_base_images == 'true'
|
||||
id: openresty_preferred
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="$(bash .github/scripts/utils/preferred-tag.sh "${{ steps.openresty_meta.outputs.tags }}")"
|
||||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build OpenResty Base Image
|
||||
if: needs.prep.outputs.run_base_images == 'true'
|
||||
id: openresty_build
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: .
|
||||
file: deploy/base-images/openresty-geoip.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ env.PUSH_BASE_IMAGES == 'true' }}
|
||||
tags: ${{ steps.openresty_meta.outputs.tags }}
|
||||
labels: ${{ steps.openresty_meta.outputs.labels }}
|
||||
|
||||
- name: Generate Postgres Tags
|
||||
if: needs.prep.outputs.run_base_images == 'true'
|
||||
id: postgres_meta
|
||||
uses: ./.github/actions/auto-tag
|
||||
with:
|
||||
image: ${{ env.BASE_REGISTRY }}/${{ env.BASE_ORG }}/postgres-runtime
|
||||
|
||||
- name: Resolve Postgres Preferred Tag
|
||||
if: needs.prep.outputs.run_base_images == 'true'
|
||||
id: postgres_preferred
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="$(bash .github/scripts/utils/preferred-tag.sh "${{ steps.postgres_meta.outputs.tags }}")"
|
||||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build Postgres Base Image
|
||||
if: needs.prep.outputs.run_base_images == 'true'
|
||||
id: postgres_build
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: .
|
||||
file: deploy/base-images/postgres-runtime-wth-extensions.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ env.PUSH_BASE_IMAGES == 'true' }}
|
||||
tags: ${{ steps.postgres_meta.outputs.tags }}
|
||||
labels: ${{ steps.postgres_meta.outputs.labels }}
|
||||
|
||||
- name: Resolve Service Image Repository
|
||||
id: service_image
|
||||
run: echo "repo=${SERVICE_REGISTRY}/${SERVICE_IMAGE_REPO_OWNER}/${SERVICE_IMAGE_NAME}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Compute Service Image Tags
|
||||
id: service_meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ${{ steps.service_image.outputs.repo }}
|
||||
tags: |
|
||||
type=raw,value=${{ needs.prep.outputs.image_tag }},enable=${{ needs.prep.outputs.image_tag != '' }}
|
||||
type=sha,format=short,enable=${{ needs.prep.outputs.image_tag == '' }}
|
||||
type=raw,value=latest,enable=${{ needs.prep.outputs.push_latest == 'true' || github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: Build Service Image Artifact
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: false
|
||||
tags: ${{ steps.service_meta.outputs.tags }}
|
||||
labels: ${{ steps.service_meta.outputs.labels }}
|
||||
outputs: type=docker,dest=${{ runner.temp }}/accounts-image.tar
|
||||
|
||||
- name: Prepare Service Artifact Bundle
|
||||
id: service_artifact
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bundle_dir="${RUNNER_TEMP}/service-image-artifact"
|
||||
mkdir -p "${bundle_dir}"
|
||||
cp "${RUNNER_TEMP}/accounts-image.tar" "${bundle_dir}/accounts-image.tar"
|
||||
printf '%s\n' "${{ steps.service_meta.outputs.tags }}" > "${bundle_dir}/tags.txt"
|
||||
echo "name=accounts-image-artifact" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload Service Artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: ${{ steps.service_artifact.outputs.name }}
|
||||
path: ${{ runner.temp }}/service-image-artifact
|
||||
|
||||
deploy:
|
||||
name: Deploy
|
||||
needs:
|
||||
- prep
|
||||
- build
|
||||
if: ${{ needs.prep.outputs.push_image == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image: ${{ needs.build.outputs.service_image_repo }}
|
||||
preferred_tag: ${{ steps.push.outputs.preferred_tag }}
|
||||
run_apply: ${{ needs.prep.outputs.run_apply }}
|
||||
pushed: "true"
|
||||
steps:
|
||||
- name: Check Out Repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Check Out Playbooks Repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
repository: x-evor/playbooks
|
||||
token: ${{ secrets.WORKSPACE_REPO_TOKEN || github.token }}
|
||||
path: playbooks
|
||||
|
||||
- name: Download Service Artifact
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: ${{ needs.build.outputs.artifact_name }}
|
||||
path: ${{ runner.temp }}/service-image-artifact
|
||||
|
||||
- name: Log In To GHCR
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ vars.GHCR_USERNAME || github.repository_owner }}
|
||||
password: ${{ secrets.GHCR_TOKEN || github.token }}
|
||||
|
||||
- name: Load And Push Service Image
|
||||
id: push
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
docker load -i "${RUNNER_TEMP}/service-image-artifact/accounts-image.tar"
|
||||
|
||||
preferred_tag="$(bash .github/scripts/utils/preferred-tag.sh "$(cat "${RUNNER_TEMP}/service-image-artifact/tags.txt")")"
|
||||
while IFS= read -r tag; do
|
||||
[[ -z "${tag}" ]] && continue
|
||||
docker push "${tag}"
|
||||
done < "${RUNNER_TEMP}/service-image-artifact/tags.txt"
|
||||
|
||||
echo "preferred_tag=${preferred_tag}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Resolve Deploy Image Tag
|
||||
id: deploy_image_tag
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="${{ steps.push.outputs.preferred_tag }}"
|
||||
echo "value=${tag##*:}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set Up Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.0.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install Ansible Runtime
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install "ansible-core==2.18.3"
|
||||
|
||||
- name: Prepare Runner SSH Access
|
||||
env:
|
||||
SINGLE_NODE_VPS_SSH_PRIVATE_KEY: ${{ secrets.SINGLE_NODE_VPS_SSH_PRIVATE_KEY }}
|
||||
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
|
||||
run: |
|
||||
bash ./scripts/github-actions/prepare-ssh.sh \
|
||||
"${{ needs.prep.outputs.target_host }}" \
|
||||
"${SSH_KNOWN_HOSTS}"
|
||||
|
||||
- name: Run Accounts Deploy Playbook
|
||||
env:
|
||||
ACCOUNTS_IMAGE_REPO: ${{ needs.build.outputs.service_image_repo }}
|
||||
ACCOUNTS_IMAGE_TAG: ${{ steps.deploy_image_tag.outputs.value }}
|
||||
ACCOUNTS_PULL_IMAGE: "true"
|
||||
run: |
|
||||
bash ./scripts/github-actions/deploy-accounts.sh \
|
||||
"${{ needs.prep.outputs.target_host }}" \
|
||||
"${{ needs.prep.outputs.run_apply }}" \
|
||||
"${GITHUB_WORKSPACE}/playbooks"
|
||||
|
||||
- name: Log In To Docker Hub
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push Base Images To Docker Hub
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
env:
|
||||
BASE_REGISTRY: ${{ needs.prep.outputs.base_image_registry }}
|
||||
BASE_ORG: ${{ needs.prep.outputs.base_image_org }}
|
||||
DOCKERHUB_NAMESPACE: ${{ needs.prep.outputs.dockerhub_namespace }}
|
||||
OPENRESTY_TAG: ${{ needs.build.outputs.openresty_tag }}
|
||||
POSTGRES_TAG: ${{ needs.build.outputs.postgres_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
docker pull "${OPENRESTY_TAG}"
|
||||
docker tag "${OPENRESTY_TAG}" "docker.io/${DOCKERHUB_NAMESPACE}/openresty-geoip:latest"
|
||||
docker push "docker.io/${DOCKERHUB_NAMESPACE}/openresty-geoip:latest"
|
||||
|
||||
docker pull "${POSTGRES_TAG}"
|
||||
docker tag "${POSTGRES_TAG}" "docker.io/${DOCKERHUB_NAMESPACE}/postgres-runtime:latest"
|
||||
docker push "docker.io/${DOCKERHUB_NAMESPACE}/postgres-runtime:latest"
|
||||
|
||||
validate:
|
||||
name: Validate
|
||||
needs:
|
||||
- prep
|
||||
- build
|
||||
- deploy
|
||||
if: ${{ always() && needs.deploy.result == 'success' && needs.deploy.outputs.pushed == 'true' && needs.deploy.outputs.run_apply == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Out Repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Validate Deployed Endpoint
|
||||
run: bash ./scripts/github-actions/validate-deploy.sh https://accounts.svc.plus
|
||||
|
||||
- name: Generate Service SBOM
|
||||
uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
|
||||
with:
|
||||
image: ${{ needs.deploy.outputs.preferred_tag }}
|
||||
output-file: sbom-accounts.spdx.json
|
||||
|
||||
- name: Upload Service SBOM
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sbom-accounts
|
||||
path: sbom-accounts.spdx.json
|
||||
|
||||
- name: Scan Service Image
|
||||
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
|
||||
with:
|
||||
image-ref: ${{ needs.deploy.outputs.preferred_tag }}
|
||||
severity: HIGH,CRITICAL
|
||||
exit-code: '1'
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@2e2f661cd4be3a4b891a882064e49d0fed4b7b88 # v3.9.0
|
||||
with:
|
||||
cosign-release: 'v2.4.1'
|
||||
|
||||
- name: Sign Service Image
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
run: |
|
||||
cosign sign --yes "${{ needs.deploy.outputs.preferred_tag }}"
|
||||
|
||||
- name: Generate OpenResty SBOM
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
|
||||
with:
|
||||
image: ${{ needs.build.outputs.openresty_tag }}
|
||||
output-file: sbom-openresty-geoip.spdx.json
|
||||
|
||||
- name: Upload OpenResty SBOM
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sbom-openresty-geoip
|
||||
path: sbom-openresty-geoip.spdx.json
|
||||
|
||||
- name: Scan OpenResty Image
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
|
||||
with:
|
||||
image-ref: ${{ needs.build.outputs.openresty_tag }}
|
||||
severity: HIGH,CRITICAL
|
||||
exit-code: '1'
|
||||
|
||||
- name: Sign OpenResty Image
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
run: |
|
||||
cosign sign --yes "${{ needs.build.outputs.openresty_tag }}"
|
||||
|
||||
- name: Generate Postgres SBOM
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
|
||||
with:
|
||||
image: ${{ needs.build.outputs.postgres_tag }}
|
||||
output-file: sbom-postgres-runtime.spdx.json
|
||||
|
||||
- name: Upload Postgres SBOM
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sbom-postgres-runtime
|
||||
path: sbom-postgres-runtime.spdx.json
|
||||
|
||||
- name: Scan Postgres Image
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
|
||||
with:
|
||||
image-ref: ${{ needs.build.outputs.postgres_tag }}
|
||||
severity: HIGH,CRITICAL
|
||||
exit-code: '1'
|
||||
|
||||
- name: Sign Postgres Image
|
||||
if: needs.prep.outputs.run_base_images == 'true' && needs.build.outputs.base_images_pushed == 'true'
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
run: |
|
||||
cosign sign --yes "${{ needs.build.outputs.postgres_tag }}"
|
||||
26
scripts/github-actions/deploy-accounts.sh
Normal file
26
scripts/github-actions/deploy-accounts.sh
Normal file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_HOST="${1:?target host is required}"
|
||||
RUN_APPLY="${2:?run_apply flag is required}"
|
||||
PLAYBOOK_DIR="${3:?playbook dir is required}"
|
||||
|
||||
test -n "${ACCOUNTS_IMAGE_REPO:-}"
|
||||
test -n "${ACCOUNTS_IMAGE_TAG:-}"
|
||||
|
||||
cd "${PLAYBOOK_DIR}"
|
||||
|
||||
args=(
|
||||
ansible-playbook
|
||||
-i inventory.ini
|
||||
deploy_accounts_svc_plus.yml
|
||||
-l "${TARGET_HOST}"
|
||||
)
|
||||
|
||||
if [[ "${RUN_APPLY}" != "true" ]]; then
|
||||
args+=(-C)
|
||||
fi
|
||||
|
||||
ANSIBLE_CONFIG="${PWD}/ansible.cfg" \
|
||||
ACCOUNTS_PULL_IMAGE="${ACCOUNTS_PULL_IMAGE:-true}" \
|
||||
"${args[@]}"
|
||||
23
scripts/github-actions/prepare-ssh.sh
Normal file
23
scripts/github-actions/prepare-ssh.sh
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_HOST="${1:?target host is required}"
|
||||
SSH_KNOWN_HOSTS_PAYLOAD="${2:-}"
|
||||
|
||||
test -n "${SINGLE_NODE_VPS_SSH_PRIVATE_KEY:-}"
|
||||
|
||||
mkdir -p "${HOME}/.ssh"
|
||||
chmod 700 "${HOME}/.ssh"
|
||||
|
||||
python3 .github/scripts/normalize-private-key.py normalize > "${HOME}/.ssh/id_rsa"
|
||||
chmod 600 "${HOME}/.ssh/id_rsa"
|
||||
ssh-keygen -y -f "${HOME}/.ssh/id_rsa" >/dev/null
|
||||
|
||||
touch "${HOME}/.ssh/known_hosts"
|
||||
chmod 600 "${HOME}/.ssh/known_hosts"
|
||||
|
||||
if [[ -n "${SSH_KNOWN_HOSTS_PAYLOAD}" ]]; then
|
||||
printf '%s\n' "${SSH_KNOWN_HOSTS_PAYLOAD}" >> "${HOME}/.ssh/known_hosts"
|
||||
fi
|
||||
|
||||
ssh-keyscan -H "${TARGET_HOST}" >> "${HOME}/.ssh/known_hosts" 2>/dev/null || true
|
||||
12
scripts/github-actions/validate-deploy.sh
Normal file
12
scripts/github-actions/validate-deploy.sh
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${1:-https://accounts.svc.plus}"
|
||||
|
||||
curl \
|
||||
--silent \
|
||||
--show-error \
|
||||
--fail \
|
||||
--location \
|
||||
--max-time 20 \
|
||||
"${BASE_URL}/healthz" | grep -q '"status":"ok"'
|
||||
Loading…
Reference in New Issue
Block a user