feat(platform): harden access scoping and delivery baseline
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
name: Deploy Production
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: Image tag to promote to production, for example sha-<commit>
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy To Production
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
timeout-minutes: 30
|
||||
concurrency:
|
||||
group: deploy-production
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- id: vars
|
||||
name: Resolve image refs
|
||||
run: |
|
||||
owner="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||
repo="$(basename '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')"
|
||||
image_tag="${{ inputs.image_tag }}"
|
||||
echo "app_image=ghcr.io/${owner}/${repo}-app:${image_tag}" >> "$GITHUB_OUTPUT"
|
||||
echo "migrator_image=ghcr.io/${owner}/${repo}-migrator:${image_tag}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Prepare SSH
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.PROD_SSH_KEY }}
|
||||
SSH_HOST: ${{ secrets.PROD_SSH_HOST }}
|
||||
SSH_PORT: ${{ secrets.PROD_SSH_PORT }}
|
||||
run: |
|
||||
install -m 700 -d ~/.ssh
|
||||
printf '%s\n' "${SSH_PRIVATE_KEY}" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -p "${SSH_PORT:-22}" -H "${SSH_HOST}" >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Bundle deploy assets
|
||||
run: tar czf deploy-bundle.tgz docker-compose.cicd.yml tooling/deploy
|
||||
|
||||
- name: Copy deploy assets to production
|
||||
env:
|
||||
SSH_PORT: ${{ secrets.PROD_SSH_PORT }}
|
||||
run: |
|
||||
ssh -p "${SSH_PORT:-22}" "${{ secrets.PROD_SSH_USER }}@${{ secrets.PROD_SSH_HOST }}" \
|
||||
"mkdir -p '${{ secrets.PROD_DEPLOY_PATH }}'"
|
||||
scp -P "${SSH_PORT:-22}" deploy-bundle.tgz \
|
||||
"${{ secrets.PROD_SSH_USER }}@${{ secrets.PROD_SSH_HOST }}:${{ secrets.PROD_DEPLOY_PATH }}/deploy-bundle.tgz"
|
||||
|
||||
- name: Run production deployment
|
||||
env:
|
||||
APP_IMAGE: ${{ steps.vars.outputs.app_image }}
|
||||
MIGRATOR_IMAGE: ${{ steps.vars.outputs.migrator_image }}
|
||||
APP_HOST_PORT: ${{ secrets.PROD_APP_HOST_PORT }}
|
||||
GHCR_USERNAME: ${{ secrets.PROD_GHCR_USERNAME }}
|
||||
GHCR_TOKEN: ${{ secrets.PROD_GHCR_TOKEN }}
|
||||
SSH_PORT: ${{ secrets.PROD_SSH_PORT }}
|
||||
run: |
|
||||
cat > deploy.env <<EOF
|
||||
APP_IMAGE=${APP_IMAGE}
|
||||
MIGRATOR_IMAGE=${MIGRATOR_IMAGE}
|
||||
APP_HOST_PORT=${APP_HOST_PORT}
|
||||
GHCR_USERNAME=${GHCR_USERNAME}
|
||||
GHCR_TOKEN=${GHCR_TOKEN}
|
||||
EOF
|
||||
scp -P "${SSH_PORT:-22}" deploy.env \
|
||||
"${{ secrets.PROD_SSH_USER }}@${{ secrets.PROD_SSH_HOST }}:${{ secrets.PROD_DEPLOY_PATH }}/deploy.env"
|
||||
ssh -p "${SSH_PORT:-22}" "${{ secrets.PROD_SSH_USER }}@${{ secrets.PROD_SSH_HOST }}" 'bash -se' <<'EOF'
|
||||
set -euo pipefail
|
||||
cd "${{ secrets.PROD_DEPLOY_PATH }}"
|
||||
tar xzf deploy-bundle.tgz
|
||||
rm -f deploy-bundle.tgz
|
||||
set -a
|
||||
. ./deploy.env
|
||||
set +a
|
||||
bash tooling/deploy/deploy-compose.sh production
|
||||
rm -f deploy.env
|
||||
EOF
|
||||
@@ -0,0 +1,86 @@
|
||||
name: Deploy Staging
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: Image tag to deploy, for example sha-<commit>
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy To Staging
|
||||
runs-on: ubuntu-latest
|
||||
environment: staging
|
||||
timeout-minutes: 30
|
||||
concurrency:
|
||||
group: deploy-staging
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- id: vars
|
||||
name: Resolve image refs
|
||||
run: |
|
||||
owner="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||
repo="$(basename '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')"
|
||||
image_tag="${{ inputs.image_tag }}"
|
||||
echo "app_image=ghcr.io/${owner}/${repo}-app:${image_tag}" >> "$GITHUB_OUTPUT"
|
||||
echo "migrator_image=ghcr.io/${owner}/${repo}-migrator:${image_tag}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Prepare SSH
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_KEY }}
|
||||
SSH_HOST: ${{ secrets.STAGING_SSH_HOST }}
|
||||
SSH_PORT: ${{ secrets.STAGING_SSH_PORT }}
|
||||
run: |
|
||||
install -m 700 -d ~/.ssh
|
||||
printf '%s\n' "${SSH_PRIVATE_KEY}" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -p "${SSH_PORT:-22}" -H "${SSH_HOST}" >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Bundle deploy assets
|
||||
run: tar czf deploy-bundle.tgz docker-compose.cicd.yml tooling/deploy
|
||||
|
||||
- name: Copy deploy assets to staging
|
||||
env:
|
||||
SSH_PORT: ${{ secrets.STAGING_SSH_PORT }}
|
||||
run: |
|
||||
ssh -p "${SSH_PORT:-22}" "${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SSH_HOST }}" \
|
||||
"mkdir -p '${{ secrets.STAGING_DEPLOY_PATH }}'"
|
||||
scp -P "${SSH_PORT:-22}" deploy-bundle.tgz \
|
||||
"${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SSH_HOST }}:${{ secrets.STAGING_DEPLOY_PATH }}/deploy-bundle.tgz"
|
||||
|
||||
- name: Run staging deployment
|
||||
env:
|
||||
APP_IMAGE: ${{ steps.vars.outputs.app_image }}
|
||||
MIGRATOR_IMAGE: ${{ steps.vars.outputs.migrator_image }}
|
||||
APP_HOST_PORT: ${{ secrets.STAGING_APP_HOST_PORT }}
|
||||
GHCR_USERNAME: ${{ secrets.STAGING_GHCR_USERNAME }}
|
||||
GHCR_TOKEN: ${{ secrets.STAGING_GHCR_TOKEN }}
|
||||
SSH_PORT: ${{ secrets.STAGING_SSH_PORT }}
|
||||
run: |
|
||||
cat > deploy.env <<EOF
|
||||
APP_IMAGE=${APP_IMAGE}
|
||||
MIGRATOR_IMAGE=${MIGRATOR_IMAGE}
|
||||
APP_HOST_PORT=${APP_HOST_PORT}
|
||||
GHCR_USERNAME=${GHCR_USERNAME}
|
||||
GHCR_TOKEN=${GHCR_TOKEN}
|
||||
EOF
|
||||
scp -P "${SSH_PORT:-22}" deploy.env \
|
||||
"${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SSH_HOST }}:${{ secrets.STAGING_DEPLOY_PATH }}/deploy.env"
|
||||
ssh -p "${SSH_PORT:-22}" "${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SSH_HOST }}" 'bash -se' <<'EOF'
|
||||
set -euo pipefail
|
||||
cd "${{ secrets.STAGING_DEPLOY_PATH }}"
|
||||
tar xzf deploy-bundle.tgz
|
||||
rm -f deploy-bundle.tgz
|
||||
set -a
|
||||
. ./deploy.env
|
||||
set +a
|
||||
bash tooling/deploy/deploy-compose.sh staging
|
||||
rm -f deploy.env
|
||||
EOF
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Nightly Security
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "17 2 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NODE_VERSION: "20"
|
||||
PNPM_VERSION: "9.14.2"
|
||||
|
||||
jobs:
|
||||
dependency-audit:
|
||||
name: Dependency Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run dependency audit
|
||||
run: pnpm audit --audit-level=high
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Release Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: Optional tag override, defaults to sha-<commit>
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
name: Build And Push Images
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- id: vars
|
||||
name: Compute image refs
|
||||
run: |
|
||||
owner="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
||||
repo="$(basename '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')"
|
||||
image_tag="${{ inputs.image_tag }}"
|
||||
if [ -z "${image_tag}" ]; then
|
||||
image_tag="sha-${GITHUB_SHA}"
|
||||
fi
|
||||
echo "app_image=ghcr.io/${owner}/${repo}-app:${image_tag}" >> "$GITHUB_OUTPUT"
|
||||
echo "migrator_image=ghcr.io/${owner}/${repo}-migrator:${image_tag}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build and push app image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.prod
|
||||
target: runner
|
||||
push: true
|
||||
tags: ${{ steps.vars.outputs.app_image }}
|
||||
cache-from: type=gha,scope=app-image
|
||||
cache-to: type=gha,mode=max,scope=app-image
|
||||
|
||||
- name: Build and push migrator image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.prod
|
||||
target: migrator
|
||||
push: true
|
||||
tags: ${{ steps.vars.outputs.migrator_image }}
|
||||
cache-from: type=gha,scope=migrator-image
|
||||
cache-to: type=gha,mode=max,scope=migrator-image
|
||||
Reference in New Issue
Block a user