Skip to content

Commit b0c0106

Browse files
committed
chore(ci): Build modified backends on PR
Signed-off-by: Richard Palethorpe <[email protected]>
1 parent 0a1e27c commit b0c0106

File tree

3 files changed

+131
-5
lines changed

3 files changed

+131
-5
lines changed

.github/workflows/backend_build.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ on:
5555
type: string
5656
secrets:
5757
dockerUsername:
58-
required: true
58+
required: false
5959
dockerPassword:
60-
required: true
60+
required: false
6161
quayUsername:
62-
required: true
62+
required: false
6363
quayPassword:
64-
required: true
64+
required: false
6565

6666
jobs:
6767
backend-build:
@@ -238,4 +238,4 @@ jobs:
238238

239239
- name: job summary
240240
run: |
241-
echo "Built image: ${{ steps.meta.outputs.labels }}" >> $GITHUB_STEP_SUMMARY
241+
echo "Built image: ${{ steps.meta.outputs.labels }}" >> $GITHUB_STEP_SUMMARY

.github/workflows/backend_pr.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: 'build backend container images (PR-filtered)'
2+
3+
on:
4+
pull_request:
5+
6+
concurrency:
7+
group: ci-backends-pr-${{ github.head_ref || github.ref }}-${{ github.repository }}
8+
cancel-in-progress: true
9+
10+
jobs:
11+
generate-matrix:
12+
runs-on: ubuntu-latest
13+
outputs:
14+
matrix: ${{ steps.set-matrix.outputs.matrix }}
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Bun
20+
uses: oven-sh/setup-bun@v2
21+
22+
- name: Install dependencies
23+
run: |
24+
bun add js-yaml
25+
bun add @octokit/core
26+
27+
# filters the matrix in backend.yml
28+
- name: Filter matrix for changed backends
29+
id: set-matrix
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
GITHUB_EVENT_PATH: ${{ github.event_path }}
33+
run: bun run scripts/changed-backends.js
34+
35+
backend-jobs:
36+
needs: generate-matrix
37+
uses: ./.github/workflows/backend_build.yml
38+
with:
39+
tag-latest: ${{ matrix.tag-latest }}
40+
tag-suffix: ${{ matrix.tag-suffix }}
41+
build-type: ${{ matrix.build-type }}
42+
cuda-major-version: ${{ matrix.cuda-major-version }}
43+
cuda-minor-version: ${{ matrix.cuda-minor-version }}
44+
platforms: ${{ matrix.platforms }}
45+
runs-on: ${{ matrix.runs-on }}
46+
base-image: ${{ matrix.base-image }}
47+
backend: ${{ matrix.backend }}
48+
dockerfile: ${{ matrix.dockerfile }}
49+
skip-drivers: ${{ matrix.skip-drivers }}
50+
context: ${{ matrix.context }}
51+
strategy:
52+
fail-fast: false
53+
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}

scripts/changed-backends.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import fs from "fs";
2+
import yaml from "js-yaml";
3+
import { Octokit } from "@octokit/core";
4+
5+
// Load backend.yml and parse matrix.include
6+
const backendYml = yaml.load(fs.readFileSync(".github/workflows/backend.yml", "utf8"));
7+
const jobs = backendYml.jobs;
8+
const backendJob = jobs["backend-jobs"];
9+
const strategy = backendJob.strategy;
10+
const matrix = strategy.matrix;
11+
const includes = matrix.include;
12+
13+
// Set up Octokit for PR changed files
14+
const token = process.env.GITHUB_TOKEN;
15+
const octokit = new Octokit({ auth: token });
16+
17+
const eventPath = process.env.GITHUB_EVENT_PATH;
18+
const event = JSON.parse(fs.readFileSync(eventPath, "utf8"));
19+
20+
let prNumber, repo, owner;
21+
if (event.pull_request) {
22+
prNumber = event.pull_request.number;
23+
repo = event.repository.name;
24+
owner = event.repository.owner.login;
25+
} else {
26+
throw new Error("This workflow must be triggered by a pull_request event.");
27+
}
28+
29+
async function getChangedFiles() {
30+
let files = [];
31+
let page = 1;
32+
while (true) {
33+
const res = await octokit.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/files', {
34+
owner,
35+
repo,
36+
pull_number: prNumber,
37+
per_page: 100,
38+
page
39+
});
40+
files = files.concat(res.data.map(f => f.filename));
41+
if (res.data.length < 100) break;
42+
page++;
43+
}
44+
return files;
45+
}
46+
47+
// Infer backend path
48+
function inferBackendPath(item) {
49+
if (item.dockerfile === "Dockerfile.python") {
50+
return `backend/python/${item.backend}`;
51+
}
52+
if (item.dockerfile === "Dockerfile.golang") {
53+
return `backend/go/${item.backend}`;
54+
}
55+
if (item.dockerfile === "Dockerfile.llama-cpp") {
56+
return `backend/cpp/llama-cpp`;
57+
}
58+
return null;
59+
}
60+
61+
(async () => {
62+
const changedFiles = await getChangedFiles();
63+
64+
const filtered = includes.filter(item => {
65+
const backendPath = inferBackendPath(item);
66+
if (!backendPath) return false;
67+
return changedFiles.some(file => file.startsWith(backendPath));
68+
});
69+
70+
// If no backends changed, fall back to an empty matrix to avoid workflow failure
71+
const outputMatrix = filtered.length > 0 ? { include: filtered } : { include: [] };
72+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `matrix=${JSON.stringify(outputMatrix)}\n`);
73+
})();

0 commit comments

Comments
 (0)