Skip to content

docs: add readme badges #27

docs: add readme badges

docs: add readme badges #27

name: Build and Push Docker Images
on:
push:
branches: [main]
paths:
- "apps/**"
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
apps: ${{ steps.set-matrix.outputs.apps }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: set-matrix
env:
HEAD_SHA: ${{ github.sha }}
BASE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
if [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ] || [ -z "$BASE_SHA" ]; then
BASE_SHA="HEAD^"
fi
if ! git rev-parse --verify "$BASE_SHA" >/dev/null 2>&1; then
echo "::error::Invalid BASE_SHA: $BASE_SHA"
exit 1
fi
DIFF=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- apps/ \
| cut -d/ -f2 \
| sort -u)
if [ -z "$DIFF" ]; then
echo "apps=[]" >> "$GITHUB_OUTPUT"
else
SAFE_APPS=""
while IFS= read -r app; do
if echo "$app" | grep -qE '^[a-zA-Z0-9_-]+$'; then
SAFE_APPS="${SAFE_APPS}${app}"$'\n'
else
echo "::warning::Skipping app with unsafe name: $app"
fi
done <<< "$DIFF"
if [ -z "$SAFE_APPS" ]; then
echo "apps=[]" >> "$GITHUB_OUTPUT"
else
APPS_JSON=$(echo "$SAFE_APPS" | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "apps=$APPS_JSON" >> "$GITHUB_OUTPUT"
fi
fi
build-and-push:
needs: detect-changes
if: ${{ needs.detect-changes.outputs.apps != '[]' && needs.detect-changes.outputs.apps != '' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
app: ${{ fromJSON(needs.detect-changes.outputs.apps) }}
steps:
- uses: actions/checkout@v4
- uses: nhedger/setup-sops@v2
- name: Decrypt SOPS Secrets and Setup OCI
id: setup_oci
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
run: |
set -euo pipefail
TMPKEY=$(mktemp)
trap 'rm -f "$TMPKEY"' EXIT
echo "$SOPS_AGE_KEY" > "$TMPKEY"
export SOPS_AGE_KEY_FILE="$TMPKEY"
SECRETS_JSON=$(sops -d --output-type json secrets/prod/secrets.yaml)
OCI_USER=$(echo "$SECRETS_JSON" | jq -r '.TF_VAR_user_ocid')
OCI_TENANCY=$(echo "$SECRETS_JSON" | jq -r '.TF_VAR_tenancy_ocid')
OCI_FINGERPRINT=$(echo "$SECRETS_JSON" | jq -r '.TF_VAR_fingerprint')
OCI_REGION=$(echo "$SECRETS_JSON" | jq -r '.TF_VAR_region')
OCI_COMPARTMENT=$(echo "$SECRETS_JSON" | jq -r '.TF_VAR_compartment_ocid')
OCI_PRIVATE_KEY=$(echo "$SECRETS_JSON" | jq -r '.TF_VAR_private_key_content')
echo "::add-mask::$OCI_USER"
echo "::add-mask::$OCI_TENANCY"
echo "::add-mask::$OCI_FINGERPRINT"
echo "::add-mask::$OCI_COMPARTMENT"
while IFS= read -r line; do
[ -n "$line" ] && echo "::add-mask::$line"
done <<< "$OCI_PRIVATE_KEY"
echo "OCI_CLI_USER=$OCI_USER" >> "$GITHUB_ENV"
echo "OCI_CLI_TENANCY=$OCI_TENANCY" >> "$GITHUB_ENV"
echo "OCI_CLI_FINGERPRINT=$OCI_FINGERPRINT" >> "$GITHUB_ENV"
echo "OCI_CLI_REGION=$OCI_REGION" >> "$GITHUB_ENV"
echo "OCI_COMPARTMENT_ID=$OCI_COMPARTMENT" >> "$GITHUB_ENV"
{
echo "OCI_CLI_KEY_CONTENT<<EOF_OCI_KEY"
echo "$OCI_PRIVATE_KEY"
echo "EOF_OCI_KEY"
} >> "$GITHUB_ENV"
- name: Setup OCI Config
id: setup_oci_config
run: |
set -euo pipefail
# Create OCI config directory
mkdir -p ~/.oci
chmod 700 ~/.oci
# Write private key to file
echo "$OCI_CLI_KEY_CONTENT" > ~/.oci/key.pem
chmod 600 ~/.oci/key.pem
# Create OCI config file
cat > ~/.oci/config <<EOF
[DEFAULT]
user=$OCI_CLI_USER
fingerprint=$OCI_CLI_FINGERPRINT
tenancy=$OCI_CLI_TENANCY
region=$OCI_CLI_REGION
key_file=~/.oci/key.pem
EOF
chmod 600 ~/.oci/config
echo "✓ OCI CLI configured"
- name: Install OCI CLI
run: |
set -euo pipefail
python3 -m pip install --user oci-cli
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: List Vault Secrets
id: list_secrets
run: |
set -euo pipefail
SECRETS_JSON=$(oci vault secret list \
--config-file ~/.oci/config \
--compartment-id "$OCI_COMPARTMENT_ID" \
--all \
--lifecycle-state ACTIVE \
--query 'data[].{"name": "secret-name", "id": id}' \
--output json)
{
echo "secrets<<EOF_SECRETS_JSON"
echo "$SECRETS_JSON"
echo "EOF_SECRETS_JSON"
} >> "$GITHUB_OUTPUT"
echo "✓ Found $(echo "$SECRETS_JSON" | jq '. | length') secrets"
- name: Resolve Vault Secrets
id: vault_secrets
run: |
set -euo pipefail
VAULT_SECRETS='${{ steps.list_secrets.outputs.secrets }}'
SECRET_FILE="$RUNNER_TEMP/vault-secrets-${{ github.run_id }}.env"
> "$SECRET_FILE"
chmod 600 "$SECRET_FILE"
if [ -n "$VAULT_SECRETS" ] && [ "$VAULT_SECRETS" != "[]" ]; then
while IFS= read -r secret; do
NAME=$(echo "$secret" | jq -r '.name')
ID=$(echo "$secret" | jq -r '.id')
if ! echo "$NAME" | grep -qE '^[a-zA-Z0-9_-]+$'; then
echo "::warning::Skipping secret with unsafe name: $NAME"
continue
fi
echo "Fetching secret: $NAME"
BUNDLE=$(oci secrets secret-bundle get \
--config-file ~/.oci/config \
--secret-id "$ID" \
--stage CURRENT 2>&1) || {
echo "::warning::Could not fetch secret: $NAME" >&2
continue
}
VAL=$(echo "$BUNDLE" | jq -r '.data."secret-bundle-content".content' 2>/dev/null | base64 -d 2>/dev/null) || {
echo "::warning::Could not decode secret: $NAME" >&2
continue
}
if [ -n "$VAL" ]; then
echo "::add-mask::$VAL"
while IFS= read -r line; do
[ -n "$line" ] && echo "::add-mask::$line"
done <<< "$VAL"
ARG_NAME=$(echo "$NAME" | tr '-' '_' | tr '[:lower:]' '[:upper:]')
# Write to file
echo "${ARG_NAME}=${VAL}" >> "$SECRET_FILE"
echo "✓ Added secret: $ARG_NAME"
fi
done < <(echo "$VAULT_SECRETS" | jq -c '.[]')
else
echo "::warning::No secrets found or empty list"
fi
echo "secret_file=$SECRET_FILE" >> "$GITHUB_OUTPUT"
echo "::notice::Secrets written to secure file ($(wc -l < "$SECRET_FILE") lines)"
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v6
with:
context: apps/${{ matrix.app }}
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.app }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.app }}:${{ github.sha }}
secret-files: |
secrets=${{ steps.vault_secrets.outputs.secret_file }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Cleanup Secrets File
if: always()
run: |
SECRET_FILE="${{ steps.vault_secrets.outputs.secret_file }}"
if [ -f "$SECRET_FILE" ]; then
shred -vfz -n 3 "$SECRET_FILE" 2>/dev/null || rm -f "$SECRET_FILE"
fi