Skip to content

Commit 5af68d5

Browse files
committed
[CI] Scope PR builds to changed tasks only if applicable
Run build for one student task only in 'pull_request' scope. All tests are being run on push event
1 parent 3d7fb4c commit 5af68d5

6 files changed

Lines changed: 170 additions & 0 deletions

File tree

.github/workflows/mac.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ name: macOS
22

33
on:
44
workflow_call:
5+
inputs:
6+
ppc_tasks:
7+
description: 'Tasks to build'
8+
required: false
9+
type: string
10+
default: all
511

612
permissions:
713
contents: read
@@ -37,6 +43,7 @@ jobs:
3743
-DCMAKE_C_FLAGS="-I$(brew --prefix)/opt/libomp/include"
3844
-DCMAKE_CXX_FLAGS="-I$(brew --prefix)/opt/libomp/include"
3945
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=install
46+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
4047
- name: Build project
4148
run: |
4249
cmake --build build --parallel -- --quiet

.github/workflows/main.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,58 @@ concurrency:
1616
!startsWith(github.ref, 'refs/heads/gh-readonly-queue') }}
1717
1818
jobs:
19+
ci-scope:
20+
runs-on: ubuntu-24.04
21+
outputs:
22+
ppc_tasks: ${{ steps.detect.outputs.ppc_tasks }}
23+
task_scoped: ${{ steps.detect.outputs.task_scoped }}
24+
steps:
25+
- uses: actions/checkout@v6
26+
if: ${{ github.event_name == 'pull_request' }}
27+
with:
28+
fetch-depth: 0
29+
- name: Detect CI task scope
30+
id: detect
31+
env:
32+
EVENT_NAME: ${{ github.event_name }}
33+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
34+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
35+
run: |
36+
if [ "$EVENT_NAME" != "pull_request" ]; then
37+
echo "ppc_tasks=all" >> "$GITHUB_OUTPUT"
38+
echo "task_scoped=false" >> "$GITHUB_OUTPUT"
39+
echo "PPC_TASKS=all"
40+
exit 0
41+
fi
42+
43+
python3 scripts/detect_ci_task_scope.py \
44+
--base-sha "$BASE_SHA" \
45+
--head-sha "$HEAD_SHA" \
46+
--github-output "$GITHUB_OUTPUT"
1947
pre-commit:
2048
uses: ./.github/workflows/pre-commit.yml
2149
ubuntu:
2250
needs:
51+
- ci-scope
2352
- pre-commit
2453
uses: ./.github/workflows/ubuntu.yml
2554
with:
2655
is_nightly: ${{ github.event_name == 'schedule' }}
56+
ppc_tasks: ${{ needs.ci-scope.outputs.ppc_tasks || 'all' }}
2757
mac:
2858
needs:
59+
- ci-scope
2960
- pre-commit
3061
uses: ./.github/workflows/mac.yml
62+
with:
63+
ppc_tasks: ${{ needs.ci-scope.outputs.ppc_tasks || 'all' }}
3164
windows:
3265
needs:
66+
- ci-scope
3367
- pre-commit
3468
uses: ./.github/workflows/windows.yml
69+
with:
70+
ppc_tasks: ${{ needs.ci-scope.outputs.ppc_tasks || 'all' }}
3571
perf:
3672
needs:
3773
- ubuntu

.github/workflows/static-analysis-pr.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,28 @@ permissions:
2424
packages: read
2525

2626
jobs:
27+
ci-scope:
28+
runs-on: ubuntu-24.04
29+
outputs:
30+
ppc_tasks: ${{ steps.detect.outputs.ppc_tasks }}
31+
task_scoped: ${{ steps.detect.outputs.task_scoped }}
32+
steps:
33+
- uses: actions/checkout@v6
34+
with:
35+
fetch-depth: 0
36+
- name: Detect CI task scope
37+
id: detect
38+
env:
39+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
40+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
41+
run: |
42+
python3 scripts/detect_ci_task_scope.py \
43+
--base-sha "$BASE_SHA" \
44+
--head-sha "$HEAD_SHA" \
45+
--github-output "$GITHUB_OUTPUT"
2746
clang-tidy:
47+
needs:
48+
- ci-scope
2849
runs-on: ubuntu-24.04
2950
container:
3051
image: ghcr.io/learning-process/ppc-ubuntu:1.2
@@ -48,6 +69,7 @@ jobs:
4869
run: >
4970
cmake -S . -B build -G Ninja
5071
-D CMAKE_BUILD_TYPE=RELEASE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON
72+
-D PPC_TASKS="${{ needs.ci-scope.outputs.ppc_tasks || 'all' }}"
5173
env:
5274
CC: clang-22
5375
CXX: clang++-22
@@ -73,6 +95,7 @@ jobs:
7395
exit 1
7496
clang-tidy-for-gcc-build:
7597
needs:
98+
- ci-scope
7699
- clang-tidy
77100
runs-on: ubuntu-24.04
78101
container:
@@ -97,6 +120,7 @@ jobs:
97120
run: >
98121
cmake -S . -B build -G Ninja
99122
-D CMAKE_BUILD_TYPE=RELEASE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON
123+
-D PPC_TASKS="${{ needs.ci-scope.outputs.ppc_tasks || 'all' }}"
100124
env:
101125
CC: gcc-14
102126
CXX: g++-14

.github/workflows/ubuntu.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ on:
88
required: false
99
type: boolean
1010
default: false
11+
ppc_tasks:
12+
description: 'Tasks to build'
13+
required: false
14+
type: string
15+
default: all
1116

1217
permissions:
1318
contents: read
@@ -39,6 +44,7 @@ jobs:
3944
run: >
4045
cmake -S . -B build -G Ninja
4146
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=install
47+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
4248
env:
4349
CC: gcc-14
4450
CXX: g++-14
@@ -148,6 +154,7 @@ jobs:
148154
run: >
149155
cmake -S . -B build -G Ninja
150156
-D CMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX=install
157+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
151158
env:
152159
CC: clang-22
153160
CXX: clang++-22
@@ -253,6 +260,7 @@ jobs:
253260
-D CMAKE_BUILD_TYPE=RelWithDebInfo
254261
-D ENABLE_ADDRESS_SANITIZER=ON -D ENABLE_UB_SANITIZER=ON -D ENABLE_LEAK_SANITIZER=ON
255262
-D CMAKE_INSTALL_PREFIX=install
263+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
256264
env:
257265
CC: clang-22
258266
CXX: clang++-22
@@ -368,6 +376,7 @@ jobs:
368376
-D CMAKE_BUILD_TYPE=RELEASE
369377
-D CMAKE_VERBOSE_MAKEFILE=ON -D USE_COVERAGE=ON
370378
-DCMAKE_INSTALL_PREFIX=install
379+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
371380
- name: Build project
372381
run: |
373382
cmake --build build --parallel -- --quiet

.github/workflows/windows.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ name: Windows
22

33
on:
44
workflow_call:
5+
inputs:
6+
ppc_tasks:
7+
description: 'Tasks to build'
8+
required: false
9+
type: string
10+
default: all
511

612
permissions:
713
contents: read
@@ -26,6 +32,7 @@ jobs:
2632
cmake -S . -B build -G Ninja -D CMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl
2733
-D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache
2834
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=install
35+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
2936
- name: Build project
3037
shell: bash
3138
run: |
@@ -108,6 +115,7 @@ jobs:
108115
-D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache
109116
-D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=install
110117
-D CMAKE_PREFIX_PATH="C:/Program Files/LLVM"
118+
-D PPC_TASKS="${{ inputs.ppc_tasks }}"
111119
env:
112120
CC: clang-cl
113121
CXX: clang-cl

scripts/detect_ci_task_scope.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import subprocess
5+
from pathlib import Path, PurePosixPath
6+
7+
FULL_SUITE = "all"
8+
9+
10+
def _changed_files(base_sha: str, head_sha: str) -> list[str]:
11+
result = subprocess.run(
12+
["git", "diff", "--name-only", "--no-renames", f"{base_sha}...{head_sha}"],
13+
check=True,
14+
stdout=subprocess.PIPE,
15+
text=True,
16+
)
17+
return [line for line in result.stdout.splitlines() if line]
18+
19+
20+
def _task_from_path(path: str, tasks_root: Path) -> str | None:
21+
parts = PurePosixPath(path).parts
22+
if len(parts) < 3 or parts[0] != "tasks" or parts[1] == "common":
23+
return None
24+
25+
task_id = parts[1]
26+
if not (tasks_root / task_id).is_dir():
27+
return None
28+
29+
return task_id
30+
31+
32+
def detect_scope(changed_files: list[str], tasks_root: Path) -> tuple[str, bool]:
33+
task_ids = set()
34+
35+
for changed_file in changed_files:
36+
task_id = _task_from_path(changed_file, tasks_root)
37+
if task_id is None:
38+
return FULL_SUITE, False
39+
task_ids.add(task_id)
40+
41+
if not task_ids:
42+
return FULL_SUITE, False
43+
44+
return ";".join(sorted(task_ids)), True
45+
46+
47+
def _write_github_output(github_output: Path, scope: str, task_scoped: bool) -> None:
48+
with github_output.open("a", encoding="utf-8") as output:
49+
output.write(f"ppc_tasks={scope}\n")
50+
output.write(f"task_scoped={str(task_scoped).lower()}\n")
51+
52+
53+
def _parse_args() -> argparse.Namespace:
54+
parser = argparse.ArgumentParser(
55+
description="Detect the PPC_TASKS value for CI from changed PR paths.",
56+
)
57+
parser.add_argument(
58+
"--base-sha", required=True, help="Base commit for the PR diff."
59+
)
60+
parser.add_argument(
61+
"--head-sha", required=True, help="Head commit for the PR diff."
62+
)
63+
parser.add_argument(
64+
"--tasks-root", default="tasks", type=Path, help="Path to the tasks directory."
65+
)
66+
parser.add_argument(
67+
"--github-output",
68+
type=Path,
69+
help="Optional GITHUB_OUTPUT file to append workflow outputs.",
70+
)
71+
return parser.parse_args()
72+
73+
74+
def main() -> None:
75+
args = _parse_args()
76+
scope, task_scoped = detect_scope(
77+
_changed_files(args.base_sha, args.head_sha), args.tasks_root
78+
)
79+
80+
print(f"PPC_TASKS={scope}")
81+
if args.github_output is not None:
82+
_write_github_output(args.github_output, scope, task_scoped)
83+
84+
85+
if __name__ == "__main__":
86+
main()

0 commit comments

Comments
 (0)