Skip to content

Commit 0e942f4

Browse files
committed
Run path tracer compare from Jenkins
1 parent 3c4d199 commit 0e942f4

3 files changed

Lines changed: 214 additions & 9 deletions

File tree

.github/scripts/ci_common.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def add_args(parser, names, required=True, default=None):
9999
parser.add_argument(f"--{name}", required=required, default=default)
100100

101101

102-
def multipart(fields, file_field, file_path):
102+
def multipart_files(fields, files):
103103
boundary = "----devsh-ci-" + os.urandom(12).hex()
104104
parts = []
105105
for name, value in fields.items():
@@ -108,17 +108,22 @@ def multipart(fields, file_field, file_path):
108108
str(value).encode(),
109109
b"\r\n",
110110
]
111-
path = Path(file_path)
112-
parts += [
113-
f"--{boundary}\r\nContent-Disposition: form-data; name=\"{file_field}\"; filename=\"{path.name}\"\r\n".encode(),
114-
b"Content-Type: application/zip\r\n\r\n",
115-
path.read_bytes(),
116-
b"\r\n",
117-
f"--{boundary}--\r\n".encode(),
118-
]
111+
for file_field, file_path in files:
112+
path = Path(file_path)
113+
parts += [
114+
f"--{boundary}\r\nContent-Disposition: form-data; name=\"{file_field}\"; filename=\"{path.name}\"\r\n".encode(),
115+
b"Content-Type: application/zip\r\n\r\n",
116+
path.read_bytes(),
117+
b"\r\n",
118+
]
119+
parts.append(f"--{boundary}--\r\n".encode())
119120
return f"multipart/form-data; boundary={boundary}", b"".join(parts)
120121

121122

123+
def multipart(fields, file_field, file_path):
124+
return multipart_files(fields, [(file_field, file_path)])
125+
126+
122127
def extract_single_tar(artifact_dir, pattern):
123128
artifact_dir = Path(artifact_dir)
124129
matches = list(artifact_dir.glob(pattern))
@@ -284,6 +289,14 @@ def start_file_job(base, headers, job, fields, file_field, file_path):
284289
return wait_queue(base, headers, response_headers["Location"], job)
285290

286291

292+
def start_files_job(base, headers, job, fields, files):
293+
content_type, body = multipart_files(fields, files)
294+
_, response_headers, _ = jpost(base, headers, f"/{job_path(job)}/buildWithParameters", body, content_type)
295+
if not response_headers.get("Location"):
296+
raise CiError(f"Jenkins did not return a queue location for {job}.")
297+
return wait_queue(base, headers, response_headers["Location"], job)
298+
299+
287300
def stop_build(base, headers, job, number):
288301
try:
289302
jpost(base, headers, f"/{job_path(job)}/{number}/stop")

.github/scripts/path_tracer_jenkins.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
print_console_tail,
2424
require_sha,
2525
start_file_job,
26+
start_files_job,
2627
stop_build,
2728
wait_build,
2829
wait_workflow_job_artifact,
@@ -121,6 +122,10 @@ def set_status(args, suite, state, target, description):
121122
commit_status(REPO, args.sha, args.github_token, f"jenkins/path-tracer-{suite}", state, target, description)
122123

123124

125+
def set_compare_status(args, suite, state, target, description):
126+
commit_status(REPO, args.sha, args.github_token, f"jenkins/path-tracer-compare-{suite}", state, target, description)
127+
128+
124129
def start_suite(args, headers, suite):
125130
job = f"ci/ditt/real/ex40-{suite}"
126131
set_status(args, suite, "pending", f"https://github.com/{REPO}/actions/runs/{args.source_run_id}", f"Waiting for Jenkins {suite} path tracer run.")
@@ -157,6 +162,45 @@ def start_suite(args, headers, suite):
157162
return job, number, result, build_url
158163

159164

165+
def start_compare_suite(args, headers, suite):
166+
job = f"ci/ditt/compare/o1experimental-vs-o3-{suite}"
167+
set_compare_status(args, suite, "pending", f"https://github.com/{REPO}/actions/runs/{args.source_run_id}", f"Waiting for Jenkins {suite} O1experimental vs O3 comparison.")
168+
cancel_superseded(args.jenkins_url, headers, job, {"SOURCE_REPOSITORY": REPO, "SOURCE_BRANCH": BRANCH}, {
169+
"SOURCE_RUN_ID": args.source_run_id,
170+
"SOURCE_RUN_ATTEMPT": args.source_run_attempt,
171+
})
172+
fields = {
173+
"PUBLISH": args.publish,
174+
"SOURCE_REPOSITORY": REPO,
175+
"SOURCE_BRANCH": BRANCH,
176+
"SOURCE_SHA": args.sha,
177+
"SOURCE_RUN_ID": args.source_run_id,
178+
"SOURCE_RUN_ATTEMPT": args.source_run_attempt,
179+
"SOURCE_WORKFLOW": args.source_workflow,
180+
}
181+
files = [
182+
("EX40_RELEASE_PACKAGE_FILE", args.release_package_path),
183+
("EX40_O1_PACKAGE_FILE", args.o1_package_path),
184+
]
185+
number = start_files_job(args.jenkins_url, headers, job, fields, files)
186+
build_url = f"{args.jenkins_url.rstrip('/')}/{job_path(job)}/{number}/"
187+
set_compare_status(args, suite, "pending", build_url, f"Jenkins {suite} O1experimental vs O3 build #{number} is running.")
188+
print(f"Started Jenkins {job} #{number}: {build_url}")
189+
try:
190+
result = wait_build(args.jenkins_url, headers, job, number, int(args.jenkins_timeout_minutes) * 60)
191+
except CiError:
192+
stop_build(args.jenkins_url, headers, job, number)
193+
print_console_tail(args.jenkins_url, headers, job, number)
194+
set_compare_status(args, suite, "failure", build_url, f"Jenkins {suite} comparison did not complete.")
195+
raise
196+
if result not in {"SUCCESS", "UNSTABLE"}:
197+
print_console_tail(args.jenkins_url, headers, job, number)
198+
set_compare_status(args, suite, "failure", build_url, f"Jenkins {suite} comparison finished with {result}.")
199+
raise CiError(f"Jenkins {job} #{number} finished with {result}.")
200+
set_compare_status(args, suite, "success", build_url, f"Jenkins {suite} O1experimental vs O3 {result.lower()}.")
201+
return job, number, result, build_url
202+
203+
160204
def trigger(args):
161205
if args.repository != REPO or choice(args.branch, {BRANCH}, "branch") != BRANCH:
162206
raise CiError("Invalid source repository or branch.")
@@ -179,6 +223,28 @@ def trigger(args):
179223
print(f"{result[0]} #{result[1]} {result[2]} {result[3]}")
180224

181225

226+
def trigger_compare(args):
227+
if args.repository != REPO or choice(args.branch, {BRANCH}, "branch") != BRANCH:
228+
raise CiError("Invalid source repository or branch.")
229+
args.sha = require_sha(args.sha)
230+
args.scene_set = choice(args.scene_set, SCENES, "scene set")
231+
args.publish = bool_text(args.publish)
232+
if not args.jenkins_url.startswith("https://") or not args.jenkins_user or not args.jenkins_token:
233+
raise CiError("Invalid Jenkins connection settings.")
234+
if not args.source_run_id.isdigit() or not args.source_run_attempt.isdigit():
235+
raise CiError("Invalid source run metadata.")
236+
if not args.jenkins_timeout_minutes.isdigit() or not 10 <= int(args.jenkins_timeout_minutes) <= 720:
237+
raise CiError("Invalid Jenkins timeout.")
238+
for path in [Path(args.release_package_path), Path(args.o1_package_path)]:
239+
if not path.is_file() or path.stat().st_size > MAX_PACKAGE_BYTES:
240+
raise CiError("Invalid compare package path or size.")
241+
headers = basic_headers(args.jenkins_user, args.jenkins_token)
242+
add_jenkins_crumb(args.jenkins_url, headers)
243+
suites = ["public", "private"] if args.scene_set == "both" else [args.scene_set]
244+
for result in [start_compare_suite(args, headers, suite) for suite in suites]:
245+
print(f"{result[0]} #{result[1]} {result[2]} {result[3]}")
246+
247+
182248
def parser():
183249
parser = argparse.ArgumentParser()
184250
sub = parser.add_subparsers(dest="cmd", required=True)
@@ -196,6 +262,10 @@ def parser():
196262
add_args(trigger_parser, ["jenkins-url", "jenkins-user", "jenkins-token", "github-token", "package-path", "repository", "branch", "sha", "source-run-id", "source-run-attempt", "source-workflow", "scene-set", "publish"])
197263
trigger_parser.add_argument("--jenkins-timeout-minutes", default="300")
198264
trigger_parser.set_defaults(func=trigger)
265+
compare_parser = sub.add_parser("trigger-compare")
266+
add_args(compare_parser, ["jenkins-url", "jenkins-user", "jenkins-token", "github-token", "release-package-path", "o1-package-path", "repository", "branch", "sha", "source-run-id", "source-run-attempt", "source-workflow", "scene-set", "publish"])
267+
compare_parser.add_argument("--jenkins-timeout-minutes", default="420")
268+
compare_parser.set_defaults(func=trigger_compare)
199269
return parser
200270

201271

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Run Path Tracer Compare Jenkins
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
run_id:
7+
description: "Build workflow run id to use as the EX40 package source"
8+
required: true
9+
scene_set:
10+
description: "Path tracer scene set to compare in Jenkins"
11+
required: true
12+
default: "both"
13+
type: choice
14+
options:
15+
- both
16+
- public
17+
- private
18+
publish:
19+
description: "Publish the generated comparison report to store.devsh.eu"
20+
required: true
21+
default: true
22+
type: boolean
23+
24+
permissions:
25+
contents: read
26+
actions: read
27+
statuses: write
28+
29+
concurrency:
30+
group: path-tracer-compare-jenkins-ptCLI
31+
cancel-in-progress: true
32+
33+
jobs:
34+
trigger-jenkins:
35+
name: Trigger Path Tracer Compare Jenkins
36+
runs-on: ubuntu-latest
37+
timeout-minutes: 420
38+
39+
steps:
40+
- name: Checkout
41+
uses: actions/checkout@v6
42+
with:
43+
fetch-depth: 1
44+
sparse-checkout: |
45+
.github/scripts
46+
47+
- name: Resolve source build
48+
id: source
49+
env:
50+
GH_TOKEN: ${{ github.token }}
51+
run: |
52+
python3 .github/scripts/path_tracer_jenkins.py resolve \
53+
--github-token "$GH_TOKEN" \
54+
--repository "${{ github.repository }}" \
55+
--event-name "${{ github.event_name }}" \
56+
--branch "${{ github.ref_name }}" \
57+
--sha "${{ github.sha }}" \
58+
--run-id "${{ inputs.run_id }}" \
59+
--build-config Release \
60+
--scene-set "${{ inputs.scene_set || 'both' }}" \
61+
--publish "${{ inputs.publish }}"
62+
63+
- name: Download Release install artifact
64+
uses: actions/download-artifact@v8
65+
with:
66+
run-id: ${{ steps.source.outputs.run_id }}
67+
pattern: run-windows-*-msvc-Release-install
68+
path: artifact-release
69+
merge-multiple: true
70+
github-token: ${{ secrets.READ_PAT || github.token }}
71+
repository: Devsh-Graphics-Programming/Nabla
72+
73+
- name: Download RelWithDebInfo install artifact
74+
uses: actions/download-artifact@v8
75+
with:
76+
run-id: ${{ steps.source.outputs.run_id }}
77+
pattern: run-windows-*-msvc-RelWithDebInfo-install
78+
path: artifact-o1experimental
79+
merge-multiple: true
80+
github-token: ${{ secrets.READ_PAT || github.token }}
81+
repository: Devsh-Graphics-Programming/Nabla
82+
83+
- name: Prepare Release package
84+
id: release_package
85+
run: |
86+
python3 .github/scripts/path_tracer_jenkins.py package \
87+
--artifact-dir artifact-release \
88+
--build-config Release \
89+
--package-root ex40-release-package \
90+
--zip-path ex40-release-package.zip
91+
92+
- name: Prepare O1experimental package
93+
id: o1_package
94+
run: |
95+
python3 .github/scripts/path_tracer_jenkins.py package \
96+
--artifact-dir artifact-o1experimental \
97+
--build-config RelWithDebInfo \
98+
--package-root ex40-o1experimental-package \
99+
--zip-path ex40-o1experimental-package.zip
100+
101+
- name: Trigger Jenkins
102+
env:
103+
GITHUB_TOKEN: ${{ github.token }}
104+
JENKINS_URL: https://jenkins.devsh.eu
105+
JENKINS_USER: ${{ secrets.JENKINS_DITT_USER }}
106+
JENKINS_TOKEN: ${{ secrets.JENKINS_DITT_TOKEN }}
107+
run: |
108+
python3 .github/scripts/path_tracer_jenkins.py trigger-compare \
109+
--jenkins-url "$JENKINS_URL" \
110+
--jenkins-user "$JENKINS_USER" \
111+
--jenkins-token "$JENKINS_TOKEN" \
112+
--github-token "$GITHUB_TOKEN" \
113+
--release-package-path "${{ steps.release_package.outputs.zip_path }}" \
114+
--o1-package-path "${{ steps.o1_package.outputs.zip_path }}" \
115+
--repository "${{ github.repository }}" \
116+
--branch "${{ steps.source.outputs.branch }}" \
117+
--sha "${{ steps.source.outputs.sha }}" \
118+
--source-run-id "${{ steps.source.outputs.run_id }}" \
119+
--source-run-attempt "${{ steps.source.outputs.run_attempt }}" \
120+
--source-workflow "${{ steps.source.outputs.workflow }}" \
121+
--scene-set "${{ steps.source.outputs.scene_set }}" \
122+
--publish "${{ steps.source.outputs.publish }}"

0 commit comments

Comments
 (0)