448 lines
17 KiB
YAML
448 lines
17 KiB
YAML
name: Pipeline
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
push:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
inputs:
|
|
target_host:
|
|
description: Ansible host or alias
|
|
required: false
|
|
default: "jp-xhttp-contabo.svc.plus"
|
|
type: string
|
|
run_apply:
|
|
description: Apply deployment
|
|
required: true
|
|
default: true
|
|
type: boolean
|
|
internal_service_token:
|
|
description: Optional ACP auth token
|
|
required: false
|
|
default: ""
|
|
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
|
|
env:
|
|
IMAGE_REPO_OWNER: ${{ vars.IMAGE_REPO_OWNER || github.repository_owner }}
|
|
DEFAULT_TARGET_HOST: ${{ env.DEFAULT_TARGET_HOST }}
|
|
DOCKERHUB_NAMESPACE: ${{ vars.DOCKERHUB_NAMESPACE || 'cloudneutral' }}
|
|
INPUT_TARGET_HOST: ${{ inputs.target_host }}
|
|
INPUT_RUN_APPLY: ${{ inputs.run_apply }}
|
|
INPUT_IMAGE_TAG: ${{ inputs.image_tag }}
|
|
INPUT_PUSH_IMAGE: ${{ inputs.push_image }}
|
|
INPUT_PUSH_LATEST: ${{ inputs.push_latest }}
|
|
INPUT_RUN_BASE_IMAGES: ${{ inputs.run_base_images }}
|
|
INPUT_PUSH_BASE_IMAGES: ${{ inputs.push_base_images }}
|
|
INPUT_BASE_IMAGE_REGISTRY: ${{ inputs.base_image_registry }}
|
|
INPUT_BASE_IMAGE_ORG: ${{ inputs.base_image_org }}
|
|
INPUT_DOCKERHUB_NAMESPACE: ${{ inputs.dockerhub_namespace }}
|
|
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
GITHUB_BEFORE: ${{ github.event.before }}
|
|
GITHUB_SHA: ${{ github.sha }}
|
|
run: bash ./scripts/github-actions/resolve-pipeline-flags.sh >> "$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
|
|
# Pull latest playbooks HEAD from the default branch.
|
|
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
|
|
working-directory: ${{ github.workspace }}/playbooks
|
|
env:
|
|
ACCOUNTS_IMAGE_REPO: ${{ needs.build.outputs.service_image_repo }}
|
|
ACCOUNTS_IMAGE_TAG: ${{ steps.deploy_image_tag.outputs.value }}
|
|
ACCOUNTS_PULL_IMAGE: "true"
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
args=(
|
|
ansible-playbook
|
|
-i inventory.ini
|
|
deploy_accounts_svc_plus.yml
|
|
-l "${{ needs.prep.outputs.target_host }}"
|
|
)
|
|
|
|
if [[ "${{ needs.prep.outputs.run_apply }}" != "true" ]]; then
|
|
args+=(-C)
|
|
fi
|
|
|
|
ANSIBLE_CONFIG="${PWD}/ansible.cfg" \
|
|
ACCOUNTS_PULL_IMAGE="${ACCOUNTS_PULL_IMAGE:-true}" \
|
|
"${args[@]}"
|
|
|
|
- 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 }}"
|