Skip to content

Commit b111316

Browse files
authored
Split internal vs fork builds (#9729)
Internal builds (from within the repo) will go through a straight `pull_request` flow, which triggers faster and allows for testing non-dbt changes like updates to uv.lock, etc. This flow has access to secrets, so cannot be ran from a fork. If a fork tries to edit the workflow file to run it, it will fail to get secrets and just not run 🤷 The downside here is that internal PRs show "duplicate" statuses for PRs: - The `dbt-ci/...` ones for status checking (mandatory checks). In the case of a fork PR, they are generated and posted from outside the PR flow, so we need to keep them this way - The `dbt CI internal / ...` ones are automatically created from the PR flow here, and post their status back to `dbt-ci/...` but cannot be hidden
1 parent 62c8e9e commit b111316

3 files changed

Lines changed: 80 additions & 22 deletions

File tree

.github/workflows/dbt_full_run.yml

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,85 @@
1-
name: dbt CI full run
1+
name: dbt CI internal
22

3-
# Manually-triggered CI run across all dbt projects, without filtering on changes.
4-
# Useful to validate changes to shared tools (scripts, uv.lock, workflows) that the PR flow can't
5-
# exercise pre-merge.
3+
# CI for PRs from duneanalytics/spellbook branches. Runs the code of the PR branch directly
4+
# (including workflows, scripts and uv.lock), and needs secrets.
5+
# PRs from forks are handled by the two-part flow instead (dbt_pr_trigger.yml + dbt_pr_run.yml)
66
#
7-
# This runs the code of the selected branch directly, so it will only work on branches from the
8-
# duneanalytics/spellbook repo. It will not work from forks.
7+
# Both flows report the same dbt-ci/<project> commit statuses for every project on every PR
8+
# ("skipped" when unaffected), so branch protection can require those regardless of where the
9+
# PR comes from and without deadlocking.
910

1011
on:
11-
workflow_dispatch:
12+
pull_request:
1213

1314
concurrency:
1415
group: ${{ github.workflow }}-${{ github.ref }}
1516
cancel-in-progress: true
1617

1718
jobs:
19+
plan:
20+
# Internal PRs only: fork PRs don't get secrets here and go through the
21+
# "Receive dbt PR" flow instead
22+
if: github.event.pull_request.head.repo.full_name == github.repository
23+
runs-on: ubuntu-latest
24+
permissions:
25+
contents: read
26+
pull-requests: read
27+
statuses: write
28+
outputs:
29+
projects: ${{ steps.projects.outputs.projects }}
30+
steps:
31+
- uses: actions/checkout@v6
32+
33+
- name: Determine modified dbt projects
34+
id: projects
35+
env:
36+
GH_TOKEN: ${{ github.token }}
37+
run: |
38+
gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \
39+
--paginate --jq '.[].filename' > changed_files.txt
40+
41+
projects=$(ls -d dbt_subprojects/*/ | cut -d/ -f2)
42+
modified=""
43+
# Changes to shared code or CI machinery affect every project
44+
if grep -qE '^(dbt_macros/shared/|sources/|scripts/|\.github/workflows/|uv\.lock|pyproject\.toml)' changed_files.txt; then
45+
modified="$projects"
46+
else
47+
for p in $projects; do
48+
if grep -q "^dbt_subprojects/$p/" changed_files.txt; then
49+
modified="$modified $p"
50+
fi
51+
done
52+
fi
53+
echo "projects=$(echo "$modified" | tr ' ' '\n' | jq -Rnc '[inputs | select(length > 0)]')" >> "$GITHUB_OUTPUT"
54+
55+
- name: Report initial statuses to PR
56+
env:
57+
GH_TOKEN: ${{ github.token }}
58+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
59+
PROJECTS: ${{ steps.projects.outputs.projects }}
60+
run: |
61+
# Pending for modified projects; success for the rest so that
62+
# per-project required checks don't block PRs that never run them.
63+
for p in $(ls -d dbt_subprojects/*/ | cut -d/ -f2); do
64+
if echo "$PROJECTS" | jq -e --arg p "$p" 'index($p)' > /dev/null; then
65+
state=pending; description="dbt CI queued"
66+
else
67+
state=success; description="Skipped - no changes"
68+
fi
69+
gh api "repos/${{ github.repository }}/statuses/$HEAD_SHA" \
70+
-f "state=$state" \
71+
-f "context=dbt-ci/$p" \
72+
-f "description=$description" \
73+
-f "target_url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
74+
done
75+
1876
dbt-run:
77+
needs: plan
78+
if: needs.plan.outputs.projects != '[]'
1979
strategy:
2080
fail-fast: false
2181
matrix:
22-
project:
23-
- daily_spellbook
24-
- dex
25-
- hourly_spellbook
26-
- solana
27-
- tokens
82+
project: ${{ fromJSON(needs.plan.outputs.projects) }}
2883
uses: ./.github/workflows/dbt_run.yml
2984
secrets: inherit
3085
permissions:
@@ -33,3 +88,5 @@ jobs:
3388
statuses: write
3489
with:
3590
project: ${{ matrix.project }}
91+
pr_number: ${{ github.event.pull_request.number }}
92+
head_sha: ${{ github.event.pull_request.head.sha }}

.github/workflows/dbt_pr_run.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: dbt CI
1+
name: dbt CI from fork
22

33
# Second half of the fork-safe CI pipeline. This workflow has access to secrets.
44
# It downloads the artefacts generated in "Receive dbt PR", and runs dbt CI from these.
@@ -18,9 +18,12 @@ concurrency:
1818

1919
jobs:
2020
plan:
21+
# head_repository check: this flow is for fork PRs only; in-repo PRs are
22+
# built directly by dbt_full_run.yml
2123
if: >
2224
github.event.workflow_run.event == 'pull_request' &&
23-
github.event.workflow_run.conclusion == 'success'
25+
github.event.workflow_run.conclusion == 'success' &&
26+
github.event.workflow_run.head_repository.full_name != github.repository
2427
runs-on: ubuntu-latest
2528
permissions:
2629
contents: read

.github/workflows/dbt_pr_trigger.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
name: Receive dbt PR
22

33
# First half of the fork-safe CI pipeline. This workflow runs in the unprivileged pull_request
4-
# context and packages the dbt code + metadata as artefacts. The privileged "dbt CI" workflow
5-
# then picks them up via workflow_run and runs CI if needed.
4+
# context and packages the dbt code + metadata as artefacts. The privileged "dbt CI from fork"
5+
# workflow then picks them up via workflow_run and runs CI if needed.
66

77
on:
88
pull_request:
9-
paths:
10-
- dbt_subprojects/**
11-
- sources/**
12-
- dbt_macros/shared/**
13-
- .github/workflows/**
149

1510
permissions:
1611
contents: read
1712
pull-requests: read
1813

1914
jobs:
2015
build:
16+
# Only run if this is a PR from a fork
17+
# PRs from duneanalytics/spellbook run directly via dbt_full_run.yml instead
18+
if: github.event.pull_request.head.repo.full_name != github.repository
2119
runs-on: ubuntu-latest
2220

2321
steps:

0 commit comments

Comments
 (0)