diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75277ba3f97..c2d2c3a5e66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,6 +114,7 @@ jobs: migrate: permissions: contents: read + id-token: write name: migrate needs: [checks] # Explicit need results instead of the implicit success(): a skipped job @@ -133,6 +134,8 @@ jobs: STAGING_DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }} MIGRATION_DATABASE_URL: ${{ secrets.MIGRATION_DATABASE_URL }} STAGING_MIGRATION_DATABASE_URL: ${{ secrets.STAGING_MIGRATION_DATABASE_URL }} + TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }} + TS_AUDIENCE: ${{ secrets.TS_AUDIENCE }} # Same ordering for dev (schema push before the dev image lands in ECR) migrate-dev: @@ -146,6 +149,8 @@ jobs: environment: dev secrets: DEV_DATABASE_URL: ${{ secrets.DEV_DATABASE_URL }} + TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }} + TS_AUDIENCE: ${{ secrets.TS_AUDIENCE }} # Dev: build immutable images alongside the schema push and Trigger upload. image-dev: @@ -1166,6 +1171,9 @@ jobs: # Process docs embeddings (only when docs change, after images are promoted) docs: + permissions: + contents: read + id-token: write name: docs needs: [promote, docs-changes] # Explicit results: see migrate's comment. @@ -1178,6 +1186,8 @@ jobs: secrets: DATABASE_URL: ${{ secrets.DATABASE_URL }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }} + TS_AUDIENCE: ${{ secrets.TS_AUDIENCE }} # Create GitHub Release (only for version commits on main, after all builds complete) release: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 99ec8cf1ce8..761ef30962c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,6 +7,10 @@ on: required: false OPENAI_API_KEY: required: false + TS_OAUTH_CLIENT_ID: + required: false + TS_AUDIENCE: + required: false workflow_dispatch: inputs: publish: @@ -40,6 +44,10 @@ jobs: concurrency: group: docs-search-index-production cancel-in-progress: false + permissions: + contents: read + # Tailscale workload identity federation exchanges this job's OIDC token. + id-token: write steps: - name: Record verified deployment and backup @@ -68,6 +76,16 @@ jobs: provider: ${{ vars.CI_PROVIDER }} node-version: '24' + # Database IP restrictions admit the tailnet's app connector; see migrate.yml. + - name: Join the tailnet + if: vars.CI_DB_TAILNET == 'true' + uses: tailscale/github-action@d1b6cd204f8dceda5b3eaad7f1f767be390056cd # v4.2.0 + with: + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} + audience: ${{ secrets.TS_AUDIENCE }} + tags: tag:ci + args: --accept-routes + - name: Process docs embeddings working-directory: ./apps/docs env: diff --git a/.github/workflows/migrate.yml b/.github/workflows/migrate.yml index 4975ca60334..bb446f79cdb 100644 --- a/.github/workflows/migrate.yml +++ b/.github/workflows/migrate.yml @@ -18,6 +18,10 @@ on: required: false STAGING_MIGRATION_DATABASE_URL: required: false + TS_OAUTH_CLIENT_ID: + required: false + TS_AUDIENCE: + required: false workflow_dispatch: inputs: environment: @@ -46,6 +50,10 @@ jobs: concurrency: group: migrate-${{ inputs.environment }} cancel-in-progress: false + permissions: + contents: read + # Tailscale workload identity federation exchanges this job's OIDC token. + id-token: write steps: # An environment only migrates from its own branch, so a manual dispatch @@ -73,6 +81,18 @@ jobs: exit 1 fi + # Database IP restrictions admit the tailnet's app connector, never a + # runner's address. Linux nodes only use app connector routes with + # --accept-routes. + - name: Join the tailnet + if: vars.CI_DB_TAILNET == 'true' + uses: tailscale/github-action@d1b6cd204f8dceda5b3eaad7f1f767be390056cd # v4.2.0 + with: + oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} + audience: ${{ secrets.TS_AUDIENCE }} + tags: tag:ci + args: --accept-routes + - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 with: