Skip to content

Commit f869d99

Browse files
committed
Establishing and validating Jenkins based CI for the repository
1 parent 1bfd690 commit f869d99

File tree

11 files changed

+324
-39
lines changed

11 files changed

+324
-39
lines changed

.github/workflows/CI.yml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Jenkins CI Regression
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
trigger-jenkins:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Trigger Jenkins Job
13+
id: trigger
14+
run: |
15+
echo "🚀 Triggering Jenkins job..."
16+
# Send trigger and extract the queue URL from the Location header
17+
QUEUE_URL=$(curl -s -X POST -D - \
18+
-u "${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }}" \
19+
"${{ secrets.JENKINS_URL }}/job/${{ secrets.JENKINS_JOB }}/build?token=${{ secrets.JENKINS_TRIGGER }}" \
20+
| grep -i Location | awk '{print $2}' | tr -d '\r\n')
21+
22+
echo "Queue URL: $QUEUE_URL"
23+
echo "queue_url=${QUEUE_URL}" >> $GITHUB_OUTPUT
24+
25+
- name: Wait for Jenkins to Start (Get Build Number)
26+
id: wait_for_start
27+
run: |
28+
echo "⏳ Waiting for Jenkins to assign build number..."
29+
ATTEMPTS=0
30+
BUILD_NUMBER=""
31+
QUEUE_URL="${{ steps.trigger.outputs.queue_url }}api/json"
32+
33+
until [ -n "$BUILD_NUMBER" ] && [ "$BUILD_NUMBER" != "null" ]; do
34+
sleep 10
35+
ATTEMPTS=$((ATTEMPTS + 1))
36+
BUILD_NUMBER=$(curl -s -u "${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }}" "$QUEUE_URL" | jq -r '.executable.number')
37+
echo "[$ATTEMPTS] Build number: $BUILD_NUMBER"
38+
if [ $ATTEMPTS -gt 30 ]; then
39+
echo "❌ Timed out waiting for Jenkins to start."
40+
exit 1
41+
fi
42+
done
43+
44+
BUILD_URL="${{ secrets.JENKINS_URL }}/job/${{ secrets.JENKINS_JOB }}/${BUILD_NUMBER}/"
45+
echo "✅ Jenkins build started: #$BUILD_NUMBER"
46+
echo "🔗 Jenkins console: ${BUILD_URL}console"
47+
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
48+
echo "build_url=$BUILD_URL" >> $GITHUB_OUTPUT
49+
50+
- name: Wait for Jenkins Build to Finish
51+
id: wait_for_finish
52+
run: |
53+
echo "⏳ Polling Jenkins for build completion..."
54+
ATTEMPTS=0
55+
BUILD_STATUS="PENDING"
56+
BUILD_NUMBER="${{ steps.wait_for_start.outputs.build_number }}"
57+
BUILD_URL="${{ steps.wait_for_start.outputs.build_url }}"
58+
59+
until [ "$BUILD_STATUS" = "SUCCESS" ] || [ "$BUILD_STATUS" = "FAILURE" ]; do
60+
sleep 30
61+
ATTEMPTS=$((ATTEMPTS + 1))
62+
BUILD_STATUS=$(curl -s -u "${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }}" \
63+
"${BUILD_URL}api/json" | jq -r '.result')
64+
echo "[$ATTEMPTS] Jenkins build status: $BUILD_STATUS"
65+
if [ $ATTEMPTS -gt 60 ]; then
66+
echo "❌ Timed out after 30 minutes waiting for Jenkins build #$BUILD_NUMBER"
67+
exit 1
68+
fi
69+
done
70+
71+
echo "🏁 Final Jenkins status for build #$BUILD_NUMBER: $BUILD_STATUS"
72+
echo "status=$BUILD_STATUS" >> $GITHUB_OUTPUT
73+
74+
- name: Mark Failed if Jenkins Failed
75+
if: steps.wait_for_finish.outputs.status == 'FAILURE'
76+
run: |
77+
echo "❌ Jenkins build failed."
78+
exit 1
79+
80+
- name: Jenkins Build Successful
81+
if: steps.wait_for_finish.outputs.status == 'SUCCESS'
82+
run: |
83+
echo "✅ Jenkins Regression Passed Successfully"
84+
echo "🔗 Build link: ${{ steps.wait_for_start.outputs.build_url }}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
run/*
2+
rdf.test/*

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
url = https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts.git
44
[submodule "tools/global_place/DG-RePlAce-AutoDMP"]
55
path = tools/global_place/DG-RePlAce-AutoDMP
6-
url = git@github.com:ABKGroup/DG-RePlAce-AutoDMP.git
6+
url = https://github.com/ABKGroup/DG-RePlAce-AutoDMP.git

Jenkinsfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
pipeline {
2+
agent any
3+
4+
stages {
5+
stage('Workspace Cleanup') {
6+
steps {
7+
cleanWs()
8+
}
9+
}
10+
11+
stage('Checkout') {
12+
steps {
13+
checkout scm
14+
}
15+
}
16+
17+
stage('Run Regression (Docker)') {
18+
steps {
19+
script {
20+
docker.image('rdf-openroad-ci').inside('--user root:root --rm') {
21+
withEnv([
22+
'RDF_INSTALL_ROOT=/opt/Robust-Design-Flow',
23+
'PYTHONUNBUFFERED=1',
24+
'XDG_RUNTIME_DIR=/tmp/runtime-root'
25+
]) {
26+
// sh 'git config --global --add safe.directory "$(pwd)"'
27+
// sh 'git submodule update --init --recursive'
28+
sh 'python3 tests/run_regression.py'
29+
}
30+
}
31+
}
32+
}
33+
}
34+
}
35+
36+
post {
37+
always {
38+
junit allowEmptyResults: true, testResults: 'rdf.test/logs/*.xml'
39+
archiveArtifacts artifacts: 'rdf.test/**', allowEmptyArchive: true
40+
}
41+
}
42+
}

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@ We currently support an academic point tool-based configurable flow that is wrap
1818
#### Build RDF
1919
RDF depends on [OpenROAD-flow-scripts](https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts). Please refer build instructions from ORFS repository and [documentation](https://openroad-flow-scripts.readthedocs.io/en/latest/index2.html). Once ORFS is built, follow the instructions below to run RDF.
2020

21+
#### Containerised Setup (Recommended)
22+
23+
Build the provided Docker image from the repository root:
24+
25+
```bash
26+
docker build -t rdf-openroad-ci -f docker/Dockerfile .
27+
```
28+
29+
Run the regression inside the container (the current repository is mounted at
30+
`/workspace`):
31+
32+
```bash
33+
docker run --rm -it \
34+
-v "$PWD":/workspace \
35+
rdf-openroad-ci \
36+
bash -lc "python3 tests/run_regression.py"
37+
```
38+
39+
The same image can be used by Jenkins (see `Jenkinsfile`) to keep the CI tool
40+
chain reproducible. When running against a prebuilt installation (e.g.
41+
`/opt/Robust-Design-Flow`), set `RDF_INSTALL_ROOT` to that path; otherwise the
42+
scripts default to the current working copy.
43+
2144
#### Academic Point Tool-Based Configurable Flow
2245

2346
The academic point-tool-based flow is a conventional RDF flow that uses a flow configuration file in YAML format. An example RDF flow configuration file is shown below ([Example](./scripts/sample_run.yml)).

docker/Dockerfile

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
FROM ubuntu:24.04
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
ENV SUDO_USER=root
5+
6+
RUN apt-get update && apt-get install -y \
7+
build-essential \
8+
cmake \
9+
ninja-build \
10+
git \
11+
sudo \
12+
curl \
13+
python3 \
14+
python3-pip \
15+
tcl-dev \
16+
swig \
17+
flex \
18+
bison \
19+
libboost-all-dev \
20+
libgflags-dev \
21+
libeigen3-dev \
22+
libspdlog-dev \
23+
libyaml-cpp-dev \
24+
&& rm -rf /var/lib/apt/lists/*
25+
26+
WORKDIR /opt
27+
28+
RUN git clone --recursive https://github.com/ieee-ceda-datc/Robust-Design-Flow.git
29+
30+
RUN git -C Robust-Design-Flow submodule sync --recursive \
31+
&& git -C Robust-Design-Flow submodule update --init --recursive
32+
33+
WORKDIR /opt/Robust-Design-Flow/tools/OpenROAD-flow-scripts
34+
35+
RUN ./setup.sh
36+
RUN ./build_openroad.sh --local
37+
38+
39+
40+
ENV PATH="/opt/Robust-Design-Flow/tools/OpenROAD-flow-scripts/tools/install/OpenROAD/bin:\
41+
/opt/Robust-Design-Flow/tools/OpenROAD-flow-scripts/tools/install/yosys/bin:${PATH}"
42+
43+
ENV LD_LIBRARY_PATH="/opt/Robust-Design-Flow/tools/OpenROAD-flow-scripts/tools/install/OpenROAD/lib:\
44+
/opt/Robust-Design-Flow/tools/OpenROAD-flow-scripts/dependencies/lib"
45+
46+
47+
ENV XDG_RUNTIME_DIR=/tmp/runtime-root
48+
49+
RUN python3 -m pip install --no-cache-dir --break-system-packages \
50+
unittest-xml-reporting \
51+
pyyaml
52+
53+
WORKDIR /workspace
54+

scripts/robust_design_flow.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def create_def_verilog(self, stage, base_cmd):
146146

147147
def create_odb(self, stage, base_cmd):
148148
create_odb_cmd = base_cmd + " compile_odb " + \
149-
f" RUN_SCRIPT={self.src_dir/'create_odb.tcl'} " +\
149+
f" RUN_SCRIPT={self.scripts_dir/'create_odb.tcl'} " +\
150150
f" RDF_ODB_FILE={stage} " +\
151151
f" RDF_DEF_FILE={stage}.def "
152152
return [create_odb_cmd]
@@ -217,10 +217,33 @@ def process_inputs(self):
217217
job_id.mkdir()
218218
self.workdir = job_id
219219

220-
self.src_dir = Path(__file__).resolve().parent
221-
self.orfs_dir = self.src_dir/"../tools/OpenROAD-flow-scripts"
222-
self.orfs_flow = self.orfs_dir/"flow"
223-
self.rdf_make = self.src_dir/"Makefile"
220+
workspace_root = Path(__file__).resolve().parent.parent
221+
222+
install_root_env = os.environ.get("RDF_INSTALL_ROOT")
223+
if install_root_env:
224+
candidate = Path(install_root_env).expanduser().resolve()
225+
if not candidate.exists():
226+
self.logger.warning(
227+
"RDF_INSTALL_ROOT=%s not found. Falling back to workspace copy.",
228+
install_root_env,
229+
)
230+
candidate = workspace_root
231+
else:
232+
candidate = workspace_root
233+
234+
self.install_root = candidate
235+
self.workspace_scripts = workspace_root / "scripts"
236+
self.install_scripts = self.install_root / "scripts"
237+
238+
preferred_scripts = self.workspace_scripts if self.workspace_scripts.exists() else self.install_scripts
239+
self.scripts_dir = preferred_scripts
240+
241+
self.orfs_dir = self.install_root / "tools/OpenROAD-flow-scripts"
242+
self.orfs_flow = self.orfs_dir / "flow"
243+
if (self.install_scripts / "Makefile").exists():
244+
self.rdf_make = self.install_scripts / "Makefile"
245+
else:
246+
self.rdf_make = self.workspace_scripts / "Makefile"
224247
# self.orfs_make = self.orfs_flow/"Makefile"
225248
self.orfs_platform = args.platform
226249
self.orfs_design = args.design

tests/README.md

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,61 @@
11
# RDF Test Harness
22

3-
This folder contains an end-to-end regression for the Robust Design Flow (RDF).
4-
The test runs the full OpenROAD flow on the `gcd` design for the Nangate45
5-
platform and compares the resulting report artifacts against the goldens in
3+
This directory exercises the Robust Design Flow (RDF) end-to-end on the `gcd`
4+
design (Nangate45) and compares the reported metrics against the goldens in
65
`tests/golden/`.
76

8-
## Prerequisites
7+
## Recommended: Run in Docker
98

10-
- OpenROAD-flow-scripts and its toolchain must be built (see the repository
11-
`README.md` for build instructions).
12-
- `make`, `python3`, and all runtime dependencies required by ORFS must be on
13-
your `PATH`.
9+
Build the container image (from the repository root):
1410

15-
## Running the Regression
11+
```bash
12+
docker build -t rdf-openroad-ci -f docker/Dockerfile .
13+
```
1614

17-
From the repository root:
15+
Run the regression inside the image, mounting your working copy at
16+
`/workspace`:
1817

1918
```bash
20-
python3 -m unittest discover -s tests
19+
docker run --rm -it \
20+
-v "$PWD":/workspace \
21+
rdf-openroad-ci \
22+
bash -lc "python3 tests/run_regression.py"
2123
```
2224

23-
This takes about a minute and streams the full flow output to the terminal so
24-
you can watch each stage execute. Temporary results live in `rdf.test/` while
25-
the test runs and are cleaned up automatically afterward.
25+
This matches the Jenkins CI environment and prints the full flow log.
2626

2727
## Updating Golden Artifacts
2828

29-
If the flow metrics legitimately change (e.g., due to tool updates):
29+
When tool updates legitimately change the reported metrics:
3030

31-
1. Run the flow manually to generate fresh artifacts:
31+
1. Regenerate the reports in Docker:
3232
```bash
33-
python3 scripts/robust_design_flow.py -t -r -y -v \
34-
-c scripts/sample_run.yml -d gcd -n nangate45
33+
docker run --rm -it \
34+
-v "$PWD":/workspace \
35+
rdf-openroad-ci \
36+
bash -lc "python3 scripts/robust_design_flow.py -t -r -y -v \\
37+
-c scripts/sample_run.yml -d gcd -n nangate45"
3538
```
36-
2. Copy the new report files into the golden directory:
39+
2. Copy the updated files back onto the host:
3740
```bash
3841
cp rdf.test/logs/nangate45/gcd/base/6_report.log tests/golden/6_report.log.ok
3942
cp rdf.test/logs/nangate45/gcd/base/6_report.json tests/golden/6_report.json.ok
4043
```
41-
3. Remove `rdf.test/` when done: `rm -rf rdf.test`.
42-
4. Re-run the regression to confirm parity.
43-
44-
## Troubleshooting
45-
46-
- If the test fails because `make` or OpenROAD is not found, verify that the
47-
ORFS environment setup scripts have been sourced.
48-
- To inspect a failing run, comment out the `tearDown` cleanup in
49-
`tests/test_robust_design_flow.py` so the `rdf.test/` directory is preserved
50-
for manual inspection.
44+
3. Clean up the temporary workspace:
45+
```bash
46+
rm -rf rdf.test
47+
```
48+
4. Re-run `python3 tests/run_regression.py` (or the Docker command above) to
49+
confirm parity.
50+
51+
## Tips
52+
53+
- The test fixture removes `rdf.test/` after each run. Comment out
54+
`tearDown()` in `tests/test_robust_design_flow.py` if you need to inspect the
55+
intermediate flow outputs while debugging.
56+
- To use a prebuilt installation located elsewhere (for example
57+
`/opt/Robust-Design-Flow` on Jenkins), set `RDF_INSTALL_ROOT` before running
58+
the regression. When unset, the scripts default to the current working copy.
59+
- If you choose to run natively (outside Docker), ensure OpenROAD-flow-scripts
60+
is built, the toolchain is on `PATH`, and Python has `unittest-xml-reporting`
61+
and `PyYAML` installed.

tests/golden/6_report.log.ok

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ Features included (+) or not (-): +GPU +GUI +Python
33
This program is licensed under the BSD-3 license. See the LICENSE file for details.
44
Components of this program may be licensed under more restrictive licenses which must be honored.
55
[INFO ORD-0030] Using 72 thread(s).
6-
read_liberty /rdf_projects/vachhabr/datc/Robust-Design-Flow/scripts//../tools/OpenROAD-flow-scripts/flow/platforms/nangate45/lib/NangateOpenCellLibrary_typical.lib
6+
read_liberty <REPO>/scripts//../tools/OpenROAD-flow-scripts/flow/platforms/nangate45/lib/NangateOpenCellLibrary_typical.lib
77
read_db rdf.test/results/nangate45/gcd/base/6_1_fill.odb
88
Deleted 0 routing obstructions
99
[INFO RCX-0431] Defined process_corner X with ext_model_index 0
1010
[INFO RCX-0029] Defined extraction corner X
11-
[INFO RCX-0435] Reading extraction model file /rdf_projects/vachhabr/datc/Robust-Design-Flow/scripts//../tools/OpenROAD-flow-scripts/flow/platforms/nangate45/rcx_patterns.rules ...
11+
[INFO RCX-0435] Reading extraction model file <REPO>/scripts//../tools/OpenROAD-flow-scripts/flow/platforms/nangate45/rcx_patterns.rules ...
1212
[INFO RCX-0436] RC segment generation gcd (max_merge_res 50.0) ...
1313
[INFO RCX-0040] Final 1679 rc segments
1414
[INFO RCX-0439] Coupling Cap extraction gcd ...

0 commit comments

Comments
 (0)