Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 73 additions & 16 deletions .github/workflows/export-secrets.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,96 @@
name: Export & Encrypt Secrets
# .github/workflows/export-secrets.yml
name: Export & Encrypt Secrets for Actor

on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment prefix (e.g. dev, staging, prod)'
required: true
default: 'dev'

permissions:
contents: read # for checkout
actions: read # to list/download runs & artifacts

jobs:
build:
export:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

steps:
- name: Checkout code
- name: Checkout
uses: actions/checkout@v3

- name: Export all repo secrets to env
- name: Export environment-prefixed secrets
uses: oNaiPs/secrets-to-env-action@v1
with:
# Pass in all your repo’s secrets as JSON:
secrets: ${{ toJSON(secrets) }}
exclude: ENV_PASSPHRASE
# Skip the GITHUB_TOKEN (and any other you inject via env vars)
exclude: GITHUB_TOKEN, GH_TOKEN

# 2) now grab the runner-injected ENV_-prefixed ones
- name: Export DEV-environment secrets
run: |
env | grep '^ENV_' >> secrets.env

- name: Fetch GPG keys for ${{ github.actor }}
run: |
echo "🔎 Fetching public GPG keys for ${{ github.actor }}…"
gh api /users/${{ github.actor }}/gpg_keys \
--jq '[.[] | .raw_key]' > actor_keys.json

- name: Dump selected secrets to .env
- name: Ensure actor has ≥1 GPG key
run: |
# adjust pattern to match your secret names
env | grep -E '^(DB_|API_|OTHER_)' > secrets.env
COUNT=$(jq 'length' actor_keys.json)
if [ "$COUNT" -eq 0 ]; then
echo "❌ No public GPG keys found for ${{ github.actor }}."
echo " Please upload a key at https://github.com/settings/keys"
exit 1
fi

- name: Encrypt .env symmetrically
- name: Import & encrypt for actor
run: |
gpg --quiet --batch \
--yes \
--pinentry-mode loopback \
--passphrase "${{ secrets.ENV_PASSPHRASE }}" \
--cipher-algo AES256 \
--symmetric \
# 1) Import every public key block at once
echo "🔑 Importing all public keys for ${{ github.actor }}…"
jq -r '.[]' actor_keys.json \
| gpg --batch --import

# 2) Gather fingerprints of all imported keys
mapfile -t FPS < <(
gpg --with-colons --list-keys \
| awk -F: '/^fpr:/ {print $10}'
)

# 3) Sanity check
if [[ ${#FPS[@]} -eq 0 ]]; then
echo "❌ No GPG fingerprints found after import. Aborting." >&2
exit 1
fi

# 4) Build recipient args
RECIP_ARGS=()
for fp in "${FPS[@]}"; do
RECIP_ARGS+=(--recipient "$fp")
done

# 5) Encrypt
echo "🔒 Encrypting secrets.env for ${{ github.actor }} (keys: ${FPS[*]})…"
gpg --yes --batch --quiet \
--trust-model always \
--encrypt \
"${RECIP_ARGS[@]}" \
--output secrets.env.gpg \
secrets.env

- name: Upload encrypted .env

- name: Upload encrypted-secrets
uses: actions/upload-artifact@v4
with:
name: encrypted-secrets
path: secrets.env.gpg
retention-days: 1
overwrite: true
37 changes: 29 additions & 8 deletions bin/sync-env.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail

# 1. Find the most recent export run
WORKFLOW="Export & Encrypt Secrets"
RUN_ID=$(gh run list --workflow="export-secrets.yml" --limit 1 --json databaseId --jq '.[0].databaseId')
# 1) Prompt for the passphrase if ENV_PASSPHRASE isn’t already set
if [[ -z "${ENV_PASSPHRASE-}" ]]; then
read -rsp "Enter shared ENV_PASSPHRASE: " PASSPHRASE
echo
else
PASSPHRASE="$ENV_PASSPHRASE"
fi

# 2. Download the artifact
gh run download "$RUN_ID" --name encrypted-secrets --dir .
# 2) Identify the latest run of our export workflow
WORKFLOW_FILE="export-secrets.yml"
RUN_ID=$(gh run list --workflow="$WORKFLOW_FILE" --limit 1 --json databaseId --jq '.[0].databaseId')

# 3. Decrypt to .env
if [[ -z "$RUN_ID" ]]; then
echo "❌ No workflow run found for $WORKFLOW_FILE"
exit 1
fi

echo "📥 Downloading artifact from run ID $RUN_ID..."

# 3) Prepare a clean download directory
ARTIFACT_DIR="encrypted-secrets"
rm -rf "$ARTIFACT_DIR"
mkdir -p "$ARTIFACT_DIR"

# 4) Download the encrypted .env.gpg
gh run download "$RUN_ID" --name encrypted-secrets --dir "$ARTIFACT_DIR"

# 5) Decrypt into .env
echo "🔓 Decrypting into .env..."
gpg --quiet --batch \
--yes \
--pinentry-mode loopback \
--passphrase "${ENV_PASSPHRASE:-}" \
--passphrase "$PASSPHRASE" \
--output .env \
--decrypt secrets.env.gpg
--decrypt "$ARTIFACT_DIR/secrets.env.gpg"

echo "✅ .env synced and ready"