Skip to content

feat(ci): migrate to gitleaks and optimize docs deployments #48

feat(ci): migrate to gitleaks and optimize docs deployments

feat(ci): migrate to gitleaks and optimize docs deployments #48

Workflow file for this run

name: "CI/CD"
on:
workflow_dispatch:
inputs:
job:
description: "Specific job to run (leave empty for all)"
type: string
required: false
nix_installer:
description: "Nix installer strategy"
type: choice
options:
- full
- quick
default: quick
required: false
debug_enabled:
description: "Run the workflow with tmate.io debugging enabled"
required: true
type: boolean
default: false
deploy_enabled:
description: "Deploy to Cloudflare Workers"
required: false
type: boolean
default: false
workflow_call:
pull_request:
types: [opened, labeled, reopened, synchronize]
paths-ignore:
- "*.md"
push:
branches:
- "main"
paths-ignore:
- "*.md"
defaults:
run:
shell: bash
permissions:
contents: read
deployments: write
actions: write
id-token: write
jobs:
# job 0: skip-check
# prevents duplicate workflow runs on same commit hash
skip-check:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # ratchet:fkirc/skip-duplicate-actions@v5
with:
skip_after_successful_duplicate: 'true'
do_not_skip: '["schedule"]'
concurrent_skipping: 'never'
cancel_others: 'false'
# job 1: secrets-scan
# scans repository for hardcoded secrets using gitleaks
# ALWAYS runs (ignores skip-check) - security critical
secrets-scan:
name: gitleaks
needs: skip-check
runs-on: ubuntu-latest
if: |
!cancelled() &&
(github.event_name != 'workflow_dispatch' ||
inputs.job == '' ||
inputs.job == 'secrets-scan')
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Nix
uses: ./.github/actions/setup-nix
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
with:
installer: ${{ inputs.nix_installer || 'quick' }}
system: x86_64-linux
enable-cachix: true
- name: Scan for secrets with gitleaks
run: nix develop -c just scan-secrets
# job 2: set-variables
# determines deployment settings and variables based on event type
# ALWAYS runs (ignores skip-check) - needed for job routing
set-variables:
needs: [skip-check, secrets-scan]
runs-on: ubuntu-latest
if: |
!cancelled() &&
(github.event_name != 'workflow_dispatch' ||
inputs.job == '' ||
inputs.job == 'set-variables')
outputs:
debug: ${{ steps.set-variables.outputs.debug }}
skip_ci: ${{ steps.set-variables.outputs.skip_ci }}
deploy_enabled: ${{ steps.set-variables.outputs.deploy_enabled }}
deploy_environment: ${{ steps.set-variables.outputs.deploy_environment }}
checkout_ref: ${{ steps.set-variables.outputs.checkout_ref }}
checkout_rev: ${{ steps.set-variables.outputs.checkout_rev }}
packages: ${{ steps.discover-packages.outputs.packages }}
steps:
- name: Set action variables
id: set-variables
run: |
DEBUG="false"
SKIP_CI="false"
DEPLOY_ENABLED="false"
DEPLOY_ENVIRONMENT="preview"
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
DEBUG="${{ inputs.debug_enabled }}"
DEPLOY_ENABLED="${{ inputs.deploy_enabled }}"
fi
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
if ${{ contains(github.event.pull_request.labels.*.name, 'skip-ci') }}; then
SKIP_CI="true"
fi
if ${{ contains(github.event.pull_request.labels.*.name, 'actions-debug') }}; then
DEBUG="true"
fi
if ${{ contains(github.event.pull_request.labels.*.name, 'docs-preview') }}; then
DEPLOY_ENABLED="true"
DEPLOY_ENVIRONMENT="preview"
fi
CHECKOUT_REF="${{ github.event.pull_request.head.ref }}"
CHECKOUT_REV="${{ github.event.pull_request.head.sha }}"
else
CHECKOUT_REF="${{ github.ref_name }}"
CHECKOUT_REV="${{ github.sha }}"
fi
# Enable deployment on push to main (production)
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then
DEPLOY_ENABLED="true"
DEPLOY_ENVIRONMENT="production"
fi
echo "DEBUG=$DEBUG"
echo "SKIP_CI=$SKIP_CI"
echo "DEPLOY_ENABLED=$DEPLOY_ENABLED"
echo "DEPLOY_ENVIRONMENT=$DEPLOY_ENVIRONMENT"
echo "CHECKOUT_REF=$CHECKOUT_REF"
echo "CHECKOUT_REV=$CHECKOUT_REV"
echo "DEBUG=$DEBUG" >> $GITHUB_OUTPUT
echo "SKIP_CI=$SKIP_CI" >> $GITHUB_OUTPUT
echo "DEPLOY_ENABLED=$DEPLOY_ENABLED" >> $GITHUB_OUTPUT
echo "DEPLOY_ENVIRONMENT=$DEPLOY_ENVIRONMENT" >> $GITHUB_OUTPUT
echo "CHECKOUT_REF=$CHECKOUT_REF" >> $GITHUB_OUTPUT
echo "CHECKOUT_REV=$CHECKOUT_REV" >> $GITHUB_OUTPUT
- name: Checkout for package discovery
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
with:
sparse-checkout: |
packages
justfile
sparse-checkout-cone-mode: false
- name: Discover packages
id: discover-packages
run: |
# Install just for package discovery
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
PACKAGES=$(just list-packages-json)
echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
echo "Discovered packages: $PACKAGES"
nix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
needs: [skip-check, set-variables]
if: |
!cancelled() &&
needs.skip-check.outputs.should_skip != 'true' &&
needs.set-variables.outputs.skip_ci != 'true' &&
(github.event_name != 'workflow_dispatch' ||
inputs.job == '' ||
inputs.job == 'nix')
concurrency:
group: nix-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
- name: Setup Nix
uses: ./.github/actions/setup-nix
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
with:
installer: ${{ inputs.nix_installer || 'quick' }}
system: x86_64-linux
enable-cachix: true
cachix-name: ${{ vars.CACHIX_CACHE_NAME }}
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Setup tmate debug session
uses: mxschmitt/action-tmate@e5c7151931ca95bad1c6f4190c730ecf8c7dde48 # ratchet:mxschmitt/action-tmate@v3
if: ${{ needs.set-variables.outputs.debug == 'true' }}
- name: Install omnix
run: nix --accept-flake-config profile install "github:juspay/omnix"
- name: Summarize flake
run: om show .
- name: Run flake CI and push to cachix
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
run: |
nix develop -c sops exec-env vars/shared.yaml '
om ci run | tee /dev/stderr | cachix push "$CACHIX_CACHE_NAME"
'
test:
needs: [skip-check, set-variables]
if: |
!cancelled() &&
needs.skip-check.outputs.should_skip != 'true' &&
needs.set-variables.outputs.skip_ci != 'true' &&
(github.event_name != 'workflow_dispatch' ||
inputs.job == '' ||
inputs.job == 'test')
strategy:
matrix:
package: ${{ fromJson(needs.set-variables.outputs.packages) }}
uses: ./.github/workflows/package-test.yaml
with:
package-name: ${{ matrix.package.name }}
package-path: ${{ matrix.package.path }}
debug-enabled: ${{ needs.set-variables.outputs.debug }}
nix-installer: ${{ inputs.nix_installer || 'quick' }}
secrets:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
# job 3: preview-release-version
# Preview semantic-release version for each package (PR only, fast feedback)
preview-release-version:
needs: [set-variables]
if: |
!cancelled() &&
github.event_name == 'pull_request'
strategy:
fail-fast: false
matrix:
package: ${{ fromJson(needs.set-variables.outputs.packages) }}
runs-on: ubuntu-latest
# semantic-release verifyAuth requires push permission even in dry-run mode
# https://github.com/semantic-release/semantic-release/blob/v25.0.1/index.js#L87-L98
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4
with:
ref: ${{ github.head_ref }} # Checkout actual PR branch, not merge commit
fetch-depth: 0 # Full history needed for semantic-release analysis
fetch-tags: true # Explicitly fetch all tags for version detection
- name: Fetch target branch for preview
run: |
git fetch origin
git branch -f main origin/main
- name: Configure git identity for temporary commits
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Setup Nix
uses: ./.github/actions/setup-nix
env:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
with:
installer: quick
system: x86_64-linux
enable-cachix: true
- name: Install dependencies
run: nix develop -c bun install
- name: Setup tmate debug session
if: needs.set-variables.outputs.debug == 'true'
uses: mxschmitt/action-tmate@e5c7151931ca95bad1c6f4190c730ecf8c7dde48 # ratchet:mxschmitt/action-tmate@v3
- name: Preview version for ${{ matrix.package.name }}
run: |
echo "::group::Preview semantic-release version"
nix develop -c just preview-version main ${{ matrix.package.path }}
echo "::endgroup::"
# job 4: preview-docs-deploy
# Deploy docs to preview environment (PR only, fast feedback)
preview-docs-deploy:
needs: [set-variables]
if: |
!cancelled() &&
github.event_name == 'pull_request'
permissions:
contents: read
deployments: write
uses: ./.github/workflows/deploy-docs.yaml
with:
branch: ${{ github.head_ref }}
environment: preview
debug_enabled: ${{ needs.set-variables.outputs.debug }}
secrets: inherit
# job 5: production-release-packages
# Release packages to production on main/beta branches
# IGNORES skip-check but REQUIRES test+nix success/skipped (safe for fast-forward merge)
production-release-packages:
needs: [set-variables, test, nix]
if: |
github.repository_owner == 'sciexp' &&
(github.event_name == 'push' || github.event_name == 'workflow_dispatch') &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') &&
needs.set-variables.outputs.skip_ci != 'true'
strategy:
fail-fast: false
matrix:
package: ${{ fromJson(needs.set-variables.outputs.packages) }}
permissions:
contents: write
id-token: write
uses: ./.github/workflows/package-release.yaml
with:
package-path: ${{ matrix.package.path }}
package-name: ${{ matrix.package.name }}
release-dry-run: false
debug-enabled: ${{ needs.set-variables.outputs.debug == 'true' }}
checkout-ref: ${{ needs.set-variables.outputs.checkout_ref }}
secrets:
SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
# job 6: production-docs-deploy
# Documentation deployment to production (conditional)
# IGNORES skip-check - depends on production-release-packages to ensure packages released first
production-docs-deploy:
needs: [set-variables, test, production-release-packages]
if: |
!cancelled() &&
needs.set-variables.outputs.skip_ci != 'true' &&
(github.event_name == 'push' || github.event_name == 'workflow_dispatch') &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') &&
needs.set-variables.outputs.deploy_enabled == 'true' &&
(github.event_name != 'workflow_dispatch' ||
inputs.job == '' ||
inputs.job == 'deploy')
uses: ./.github/workflows/deploy-docs.yaml
with:
debug_enabled: ${{ needs.set-variables.outputs.debug }}
branch: ${{ needs.set-variables.outputs.checkout_ref }}
environment: ${{ needs.set-variables.outputs.deploy_environment }}
secrets: inherit