feat: migrate secrets to Infisical
This commit is contained in:
@@ -7,7 +7,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: shd-build-production
|
||||
group: shd-build-host-maintenance
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Validate application
|
||||
run: npm run check
|
||||
run: npm run check && npm test
|
||||
|
||||
- name: Build Node application
|
||||
run: npm run build:app
|
||||
@@ -33,30 +33,63 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install environment tooling
|
||||
run: npm ci
|
||||
|
||||
- name: Prepare production configuration
|
||||
env:
|
||||
DOTENV_PRIVATE_KEY_PRODUCTION: ${{ secrets.DOTENV_PRIVATE_KEY_PRODUCTION }}
|
||||
SCHEDULER_TOKEN_PRODUCTION: ${{ secrets.SCHEDULER_TOKEN_PRODUCTION }}
|
||||
INFISICAL_APP_CLIENT_ID:
|
||||
${{ vars.INFISICAL_PRODUCTION_APP_CLIENT_ID }}
|
||||
INFISICAL_APP_CLIENT_SECRET:
|
||||
${{ secrets.INFISICAL_PRODUCTION_APP_CLIENT_SECRET }}
|
||||
INFISICAL_SCHEDULER_CLIENT_ID:
|
||||
${{ vars.INFISICAL_PRODUCTION_SCHEDULER_CLIENT_ID }}
|
||||
INFISICAL_SCHEDULER_CLIENT_SECRET:
|
||||
${{ secrets.INFISICAL_PRODUCTION_SCHEDULER_CLIENT_SECRET }}
|
||||
CONFIGURED_DEPLOY_PATH: ${{ vars.PRODUCTION_DEPLOY_PATH }}
|
||||
CONFIGURED_PRODUCTION_PORT: ${{ vars.PRODUCTION_PORT }}
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
BUILD_NUMBER: ${{ gitea.run_number }}
|
||||
run: |
|
||||
set -eu
|
||||
: "${DOTENV_PRIVATE_KEY_PRODUCTION:?DOTENV_PRIVATE_KEY_PRODUCTION is required}"
|
||||
: "${SCHEDULER_TOKEN_PRODUCTION:?SCHEDULER_TOKEN_PRODUCTION is required}"
|
||||
umask 077
|
||||
: "${INFISICAL_APP_CLIENT_ID:?INFISICAL_PRODUCTION_APP_CLIENT_ID is required}"
|
||||
: "${INFISICAL_APP_CLIENT_SECRET:?INFISICAL_PRODUCTION_APP_CLIENT_SECRET is required}"
|
||||
: "${INFISICAL_SCHEDULER_CLIENT_ID:?INFISICAL_PRODUCTION_SCHEDULER_CLIENT_ID is required}"
|
||||
: "${INFISICAL_SCHEDULER_CLIENT_SECRET:?INFISICAL_PRODUCTION_SCHEDULER_CLIENT_SECRET is required}"
|
||||
deployment_path="${CONFIGURED_DEPLOY_PATH:-/opt/shd-build-production}"
|
||||
production_port="${CONFIGURED_PRODUCTION_PORT:-3102}"
|
||||
case "$deployment_path" in
|
||||
/opt/shd-build-*) ;;
|
||||
*) echo "PRODUCTION_DEPLOY_PATH must be beneath /opt/shd-build-." >&2; exit 1 ;;
|
||||
esac
|
||||
case "$production_port" in
|
||||
''|*[!0-9]*) echo "PRODUCTION_PORT must be an integer." >&2; exit 1 ;;
|
||||
esac
|
||||
if [ "$production_port" -lt 1 ] || [ "$production_port" -gt 65535 ]; then
|
||||
echo "PRODUCTION_PORT must be from 1 through 65535." >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$deployment_path"
|
||||
cp deploy/compose.production.yml deploy/deploy-production.sh deploy/bootstrap-production.sh deploy/sync-production-isac-to-staging.sh "$deployment_path/"
|
||||
DOTENV_PRIVATE_KEY_PRODUCTION="$DOTENV_PRIVATE_KEY_PRODUCTION" npx dotenvx decrypt -f .env.production --stdout > "$deployment_path/.env.production.runtime"
|
||||
printf '\nSCHEDULER_TOKEN=%s\n' "$SCHEDULER_TOKEN_PRODUCTION" >> "$deployment_path/.env.production.runtime"
|
||||
printf 'SHD_IMAGE=shd-build-production:%s\nSHD_PRODUCTION_PORT=%s\nSHD_BUILD_NUMBER=%s\n' "$COMMIT_SHA" "$production_port" "$BUILD_NUMBER" > "$deployment_path/.env.deploy"
|
||||
chmod 600 "$deployment_path/.env.production.runtime" "$deployment_path/.env.deploy"
|
||||
chmod 755 "$deployment_path/deploy-production.sh" "$deployment_path/bootstrap-production.sh" "$deployment_path/sync-production-isac-to-staging.sh"
|
||||
release_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}"
|
||||
candidate_path="$deployment_path/candidate-$release_id"
|
||||
rm -rf "$candidate_path"
|
||||
mkdir -p "$candidate_path"
|
||||
cp deploy/compose.production.yml deploy/deploy-production.sh deploy/bootstrap-production.sh deploy/sync-production-isac-to-staging.sh "$candidate_path/"
|
||||
write_credential() {
|
||||
target="$1"
|
||||
value="$2"
|
||||
temporary_file=$(mktemp "${target%/*}/.infisical.XXXXXX")
|
||||
printf '%s' "$value" > "$temporary_file"
|
||||
chmod 600 "$temporary_file"
|
||||
mv "$temporary_file" "$target"
|
||||
}
|
||||
write_credential "$candidate_path/.infisical.app.client-id" "$INFISICAL_APP_CLIENT_ID"
|
||||
write_credential "$candidate_path/.infisical.app.client-secret" "$INFISICAL_APP_CLIENT_SECRET"
|
||||
write_credential "$candidate_path/.infisical.scheduler.client-id" "$INFISICAL_SCHEDULER_CLIENT_ID"
|
||||
write_credential "$candidate_path/.infisical.scheduler.client-secret" "$INFISICAL_SCHEDULER_CLIENT_SECRET"
|
||||
temporary_deploy_environment=$(mktemp "$candidate_path/.env.deploy.XXXXXX")
|
||||
printf 'SHD_IMAGE=shd-build-production:%s-%s\nSHD_PRODUCTION_PORT=%s\nSHD_BUILD_NUMBER=%s\nSHD_COMMIT_SHA=%s\n' "$COMMIT_SHA" "$release_id" "$production_port" "$BUILD_NUMBER" "$COMMIT_SHA" > "$temporary_deploy_environment"
|
||||
chmod 600 "$temporary_deploy_environment"
|
||||
mv "$temporary_deploy_environment" "$candidate_path/.env.deploy"
|
||||
chmod 755 "$candidate_path/deploy-production.sh" "$candidate_path/bootstrap-production.sh" "$candidate_path/sync-production-isac-to-staging.sh"
|
||||
|
||||
- name: Deploy production
|
||||
env:
|
||||
@@ -64,8 +97,12 @@ jobs:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
deployment_path="${CONFIGURED_DEPLOY_PATH:-/opt/shd-build-production}"
|
||||
"$deployment_path/deploy-production.sh" "$deployment_path" "$GITHUB_WORKSPACE" "shd-build-production:$COMMIT_SHA"
|
||||
release_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}"
|
||||
candidate_path="$deployment_path/candidate-$release_id"
|
||||
"$candidate_path/deploy-production.sh" "$deployment_path" "$GITHUB_WORKSPACE" "shd-build-production:$COMMIT_SHA-$release_id" "$candidate_path"
|
||||
|
||||
- name: Remove unused application images
|
||||
if: success()
|
||||
run: docker image prune --force --filter "label=org.opencontainers.image.title=shd-build"
|
||||
run:
|
||||
docker image prune --force --filter
|
||||
"label=org.opencontainers.image.title=shd-build"
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: shd-build-staging
|
||||
group: shd-build-host-maintenance
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Validate application
|
||||
run: npm run check
|
||||
run: npm run check && npm test
|
||||
|
||||
- name: Build Node application
|
||||
run: npm run build:app
|
||||
@@ -33,26 +33,62 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install environment tooling
|
||||
run: npm ci
|
||||
|
||||
- name: Prepare deployment configuration
|
||||
env:
|
||||
DOTENV_PRIVATE_KEY_STAGING: ${{ secrets.DOTENV_PRIVATE_KEY_STAGING }}
|
||||
INFISICAL_APP_CLIENT_ID: ${{ vars.INFISICAL_STAGING_APP_CLIENT_ID }}
|
||||
INFISICAL_APP_CLIENT_SECRET:
|
||||
${{ secrets.INFISICAL_STAGING_APP_CLIENT_SECRET }}
|
||||
INFISICAL_SCHEDULER_CLIENT_ID:
|
||||
${{ vars.INFISICAL_STAGING_SCHEDULER_CLIENT_ID }}
|
||||
INFISICAL_SCHEDULER_CLIENT_SECRET:
|
||||
${{ secrets.INFISICAL_STAGING_SCHEDULER_CLIENT_SECRET }}
|
||||
CONFIGURED_DEPLOY_PATH: ${{ vars.STAGE_DEPLOY_PATH }}
|
||||
CONFIGURED_STAGE_PORT: ${{ vars.STAGE_PORT }}
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
BUILD_NUMBER: ${{ gitea.run_number }}
|
||||
run: |
|
||||
set -eu
|
||||
umask 077
|
||||
: "${INFISICAL_APP_CLIENT_ID:?INFISICAL_STAGING_APP_CLIENT_ID is required}"
|
||||
: "${INFISICAL_APP_CLIENT_SECRET:?INFISICAL_STAGING_APP_CLIENT_SECRET is required}"
|
||||
: "${INFISICAL_SCHEDULER_CLIENT_ID:?INFISICAL_STAGING_SCHEDULER_CLIENT_ID is required}"
|
||||
: "${INFISICAL_SCHEDULER_CLIENT_SECRET:?INFISICAL_STAGING_SCHEDULER_CLIENT_SECRET is required}"
|
||||
deployment_path="${CONFIGURED_DEPLOY_PATH:-/opt/shd-build-stage}"
|
||||
stage_port="${CONFIGURED_STAGE_PORT:-5569}"
|
||||
case "$deployment_path" in
|
||||
/opt/shd-build-*) ;;
|
||||
*) echo "STAGE_DEPLOY_PATH must be beneath /opt/shd-build-." >&2; exit 1 ;;
|
||||
esac
|
||||
case "$stage_port" in
|
||||
''|*[!0-9]*) echo "STAGE_PORT must be an integer." >&2; exit 1 ;;
|
||||
esac
|
||||
if [ "$stage_port" -lt 1 ] || [ "$stage_port" -gt 65535 ]; then
|
||||
echo "STAGE_PORT must be from 1 through 65535." >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$deployment_path"
|
||||
cp deploy/compose.staging.yml deploy/deploy-staging.sh deploy/bootstrap-staging.sh "$deployment_path/"
|
||||
DOTENV_PRIVATE_KEY_STAGING="$DOTENV_PRIVATE_KEY_STAGING" npx dotenvx decrypt -f .env.staging --stdout > "$deployment_path/.env.staging.runtime"
|
||||
printf 'SHD_IMAGE=shd-build-stage:%s\nSHD_STAGE_PORT=%s\nSHD_BUILD_NUMBER=%s\n' "$COMMIT_SHA" "$stage_port" "$BUILD_NUMBER" > "$deployment_path/.env.deploy"
|
||||
chmod 600 "$deployment_path/.env.staging.runtime" "$deployment_path/.env.deploy"
|
||||
chmod 755 "$deployment_path/deploy-staging.sh" "$deployment_path/bootstrap-staging.sh"
|
||||
release_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}"
|
||||
candidate_path="$deployment_path/candidate-$release_id"
|
||||
rm -rf "$candidate_path"
|
||||
mkdir -p "$candidate_path"
|
||||
cp deploy/compose.staging.yml deploy/deploy-staging.sh deploy/bootstrap-staging.sh "$candidate_path/"
|
||||
write_credential() {
|
||||
target="$1"
|
||||
value="$2"
|
||||
temporary_file=$(mktemp "${target%/*}/.infisical.XXXXXX")
|
||||
printf '%s' "$value" > "$temporary_file"
|
||||
chmod 600 "$temporary_file"
|
||||
mv "$temporary_file" "$target"
|
||||
}
|
||||
write_credential "$candidate_path/.infisical.app.client-id" "$INFISICAL_APP_CLIENT_ID"
|
||||
write_credential "$candidate_path/.infisical.app.client-secret" "$INFISICAL_APP_CLIENT_SECRET"
|
||||
write_credential "$candidate_path/.infisical.scheduler.client-id" "$INFISICAL_SCHEDULER_CLIENT_ID"
|
||||
write_credential "$candidate_path/.infisical.scheduler.client-secret" "$INFISICAL_SCHEDULER_CLIENT_SECRET"
|
||||
temporary_deploy_environment=$(mktemp "$candidate_path/.env.deploy.XXXXXX")
|
||||
printf 'SHD_IMAGE=shd-build-stage:%s-%s\nSHD_STAGE_PORT=%s\nSHD_BUILD_NUMBER=%s\nSHD_COMMIT_SHA=%s\n' "$COMMIT_SHA" "$release_id" "$stage_port" "$BUILD_NUMBER" "$COMMIT_SHA" > "$temporary_deploy_environment"
|
||||
chmod 600 "$temporary_deploy_environment"
|
||||
mv "$temporary_deploy_environment" "$candidate_path/.env.deploy"
|
||||
chmod 755 "$candidate_path/deploy-staging.sh" "$candidate_path/bootstrap-staging.sh"
|
||||
|
||||
- name: Deploy staging
|
||||
env:
|
||||
@@ -60,8 +96,12 @@ jobs:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
deployment_path="${CONFIGURED_DEPLOY_PATH:-/opt/shd-build-stage}"
|
||||
"$deployment_path/deploy-staging.sh" "$deployment_path" "$GITHUB_WORKSPACE" "shd-build-stage:$COMMIT_SHA"
|
||||
release_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}"
|
||||
candidate_path="$deployment_path/candidate-$release_id"
|
||||
"$candidate_path/deploy-staging.sh" "$deployment_path" "$GITHUB_WORKSPACE" "shd-build-stage:$COMMIT_SHA-$release_id" "$candidate_path"
|
||||
|
||||
- name: Remove unused application images
|
||||
if: success()
|
||||
run: docker image prune --force --filter "label=org.opencontainers.image.title=shd-build"
|
||||
run:
|
||||
docker image prune --force --filter
|
||||
"label=org.opencontainers.image.title=shd-build"
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: shd-build-staging
|
||||
group: shd-build-host-maintenance
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
@@ -25,15 +25,32 @@ jobs:
|
||||
compose() {
|
||||
docker compose --env-file "$deploy_environment" -f "$compose_file" "$@"
|
||||
}
|
||||
restart_services() {
|
||||
compose up -d --no-build app scheduler
|
||||
}
|
||||
trap restart_services EXIT
|
||||
snapshot_name="isac-stage-${GITHUB_RUN_ID}.json"
|
||||
volume_snapshot="/app/data/exports/$snapshot_name"
|
||||
remove_volume_snapshot=false
|
||||
cleanup() {
|
||||
exit_status=$?
|
||||
trap - EXIT
|
||||
if [ "$remove_volume_snapshot" = true ]; then
|
||||
compose run --rm --no-deps app rm -f "$volume_snapshot" || true
|
||||
fi
|
||||
if ! compose up -d --no-build app scheduler; then
|
||||
if [ "$exit_status" -eq 0 ]; then exit_status=1; fi
|
||||
fi
|
||||
exit "$exit_status"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
compose stop scheduler app
|
||||
compose run --rm --no-deps app node scripts/isac-data-export.mjs "/app/data/exports/$snapshot_name"
|
||||
compose run --rm --no-deps -v "$GITHUB_WORKSPACE:/export" app \
|
||||
cp "/app/data/exports/$snapshot_name" /export/isac-stage.json
|
||||
app_container="$(compose ps --all --quiet app)"
|
||||
if [ -z "$app_container" ]; then
|
||||
echo "The stopped staging app container could not be found." >&2
|
||||
exit 1
|
||||
fi
|
||||
remove_volume_snapshot=true
|
||||
compose run --rm --no-deps app node scripts/isac-data-export.mjs "$volume_snapshot"
|
||||
docker cp "$app_container:$volume_snapshot" "$GITHUB_WORKSPACE/isac-stage.json"
|
||||
compose run --rm --no-deps app rm -f "$volume_snapshot"
|
||||
remove_volume_snapshot=false
|
||||
|
||||
- name: Publish ISAC snapshot
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: shd-build-production
|
||||
group: shd-build-host-maintenance
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
@@ -25,15 +25,32 @@ jobs:
|
||||
compose() {
|
||||
docker compose --env-file "$deploy_environment" -f "$compose_file" "$@"
|
||||
}
|
||||
restart_services() {
|
||||
compose up -d --no-build app scheduler
|
||||
}
|
||||
trap restart_services EXIT
|
||||
snapshot_name="isac-production-${GITHUB_RUN_ID}.json"
|
||||
volume_snapshot="/app/data/exports/$snapshot_name"
|
||||
remove_volume_snapshot=false
|
||||
cleanup() {
|
||||
exit_status=$?
|
||||
trap - EXIT
|
||||
if [ "$remove_volume_snapshot" = true ]; then
|
||||
compose run --rm --no-deps app rm -f "$volume_snapshot" || true
|
||||
fi
|
||||
if ! compose up -d --no-build app scheduler; then
|
||||
if [ "$exit_status" -eq 0 ]; then exit_status=1; fi
|
||||
fi
|
||||
exit "$exit_status"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
compose stop scheduler app
|
||||
compose run --rm --no-deps app node scripts/isac-data-export.mjs "/app/data/exports/$snapshot_name"
|
||||
compose run --rm --no-deps -v "$GITHUB_WORKSPACE:/export" app \
|
||||
cp "/app/data/exports/$snapshot_name" /export/isac-production.json
|
||||
app_container="$(compose ps --all --quiet app)"
|
||||
if [ -z "$app_container" ]; then
|
||||
echo "The stopped production app container could not be found." >&2
|
||||
exit 1
|
||||
fi
|
||||
remove_volume_snapshot=true
|
||||
compose run --rm --no-deps app node scripts/isac-data-export.mjs "$volume_snapshot"
|
||||
docker cp "$app_container:$volume_snapshot" "$GITHUB_WORKSPACE/isac-production.json"
|
||||
compose run --rm --no-deps app rm -f "$volume_snapshot"
|
||||
remove_volume_snapshot=false
|
||||
|
||||
- name: Publish production ISAC snapshot
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: shd-build-production-to-staging
|
||||
group: shd-build-host-maintenance
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
|
||||
Reference in New Issue
Block a user