Skip to content

Commit 47a22a7

Browse files
authored
ENH: Add GitHub actions (#1)
* CNF: add base workflows * CNF: use previous image * CNF: added tar based image build * CNF: pinned debian, changed pull request workflow * CNF: fixed env var referencing * CNF: updated os for runners * CNF: update upload version * CNF: added dev multistage * CNF: updated action versions * CNF: try new config * CNF: target dev stage * CNF: added coverage * CNF: only include source files for coverage * CNF: added source to the correct place * CNF: add PR comment and update coverage report toml settings * CNF: call with uv * CNF: enable caching * CNF: optimize docker cache and remove uid change * CNF: removed upload to artifact storage * CNF: add sticky comment to avoid spam
1 parent 7139257 commit 47a22a7

3 files changed

Lines changed: 127 additions & 67 deletions

File tree

.github/workflows/main.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: CI - Run Unit Tests in Docker with Coverage
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Docker Buildx
18+
uses: docker/setup-buildx-action@v3
19+
20+
- name: Build Docker image with Cache
21+
uses: docker/build-push-action@v5
22+
with:
23+
context: .
24+
file: ./Dockerfile # Assuming Dockerfile is at the root
25+
# Build the 'dev' target stage
26+
target: dev
27+
# Tag the image locally so subsequent steps can use it
28+
tags: my-test-image:latest
29+
# IMPORTANT: Load the image into the local Docker daemon
30+
load: true
31+
# Enable layer caching using GitHub Actions cache
32+
cache-from: type=gha
33+
cache-to: type=gha,mode=max
34+
35+
- name: Run tests with dFBA extra
36+
run: |
37+
# Runs coverage collection via 'uv run' using --with pytest-cov.
38+
# Uses --parallel-mode for coverage data.
39+
# Generates uniquely named JUnit report.
40+
docker run --rm \
41+
-v "$(pwd):/app" \
42+
-w /app \
43+
my-test-image:latest \
44+
uv run --with dfba --with pytest --with pytest-cov coverage run --parallel-mode -m pytest ./tests/extras --junitxml=test-report-dfba.xml
45+
46+
- name: Run tests without dFBA extra
47+
run: |
48+
# Runs coverage collection via 'uv run' using --with pytest-cov.
49+
# Uses --parallel-mode for coverage data.
50+
# Generates uniquely named JUnit report.
51+
docker run --rm \
52+
-v "$(pwd):/app" \
53+
-w /app \
54+
my-test-image:latest \
55+
uv run --with pytest --with pytest-cov coverage run --parallel-mode -m pytest ./tests/core --junitxml=test-report-core.xml
56+
57+
# --- The step below only runs if BOTH test steps above succeeded ---
58+
59+
- name: Combine and Report Coverage
60+
if: success()
61+
run: |
62+
# Runs combine, report, and html generation in a single container
63+
# Saves the console summary to /app/coverage_summary.txt within the container
64+
# which maps to coverage_summary.txt on the runner host via the volume mount.
65+
docker run --rm \
66+
-v "$(pwd):/app" \
67+
-w /app \
68+
my-test-image:latest \
69+
sh -c '\
70+
echo "Combining coverage data..." && \
71+
uv run --with pytest --with pytest-cov coverage combine && \
72+
echo "Generating coverage summary..." && \
73+
uv run --with pytest --with pytest-cov coverage report -m > /app/coverage_summary.txt && \
74+
echo "Generating HTML coverage report..." && \
75+
uv run --with pytest --with pytest-cov coverage html -d htmlcov \
76+
'
77+
# Also echo the summary to the main job log for easy viewing there
78+
echo "--- Coverage Summary ---"
79+
cat coverage_summary.txt
80+
echo "------------------------"
81+
82+
- name: Format Coverage Comment Body
83+
# Prepare the content for the comment in a file
84+
# Only run on successful PR builds
85+
if: success() && github.event_name == 'pull_request'
86+
run: |
87+
echo '### :test_tube: Coverage Report' > coverage_comment.md
88+
echo '' >> coverage_comment.md
89+
echo '```text' >> coverage_comment.md
90+
cat coverage_summary.txt >> coverage_comment.md
91+
echo '' >> coverage_comment.md # Ensure newline before closing fence
92+
echo '```' >> coverage_comment.md
93+
# Optional: Add a hidden HTML comment marker for extra safety/identification
94+
# echo "<!-- coverage-report-marker -->" >> coverage_comment.md
95+
96+
- name: Post or Update Coverage Comment
97+
# Use the sticky comment action
98+
# Only run on successful PR builds
99+
if: success() && github.event_name == 'pull_request'
100+
uses: marocchino/sticky-pull-request-comment@v2
101+
with:
102+
# Tell the action to read the comment body from the file we just created
103+
path: coverage_comment.md

Dockerfile

Lines changed: 4 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,8 @@
1-
FROM debian:bookworm-slim as builder
2-
3-
ARG SUNDIALS_VERSION="5.0.0"
4-
5-
ENV SUNDIALS="/opt/sundials"
6-
7-
WORKDIR /
8-
9-
# install debian essentials for building dependencies
10-
RUN apt-get update && apt-get install -y --no-install-recommends \
11-
gcc \
12-
cmake \
13-
build-essential \
14-
curl \
15-
gnupg2 \
16-
ca-certificates \
17-
&& update-ca-certificates \
18-
&& rm -rf /var/lib/apt/lists/*
19-
20-
RUN curl -L -O "https://github.com/LLNL/sundials/releases/download/v${SUNDIALS_VERSION}/sundials-${SUNDIALS_VERSION}.tar.gz" && \
21-
tar -xzf sundials-${SUNDIALS_VERSION}.tar.gz && \
22-
mkdir ${SUNDIALS} && \
23-
mkdir ${SUNDIALS}/install && \
24-
mkdir ${SUNDIALS}/build && \
25-
cd ${SUNDIALS}/build && \
26-
cmake -DCMAKE_INSTALL_PREFIX=${SUNDIALS}/install \
27-
-DEXAMPLES_INSTALL_PATH=${SUNDIALS}/install/examples \
28-
/sundials-${SUNDIALS_VERSION} && \
29-
make && \
30-
make install
31-
32-
# SUNDIALS container image
33-
FROM python:3.10-slim-bookworm as dev
34-
35-
ARG GLPK_VERSION="4.65"
36-
ENV SUNDIALS="/opt/sundials"
37-
38-
COPY --from=builder ${SUNDIALS}/install ${SUNDIALS}
39-
40-
RUN apt-get update && apt-get install -y --no-install-recommends \
41-
curl \
42-
pybind11-dev \
43-
build-essential \
44-
gcc \
45-
gnupg2 \
46-
cmake \
47-
gfortran \
48-
libgmp-dev \
49-
python-dev-is-python3 \
50-
libglpk-dev && \
51-
rm -rf /var/lib/apt/lists/*
52-
53-
# GLPK
54-
# Download the specified distribution, verify it and install
55-
RUN curl -L -O "ftp://ftp.gnu.org/gnu/glpk/glpk-${GLPK_VERSION}.tar.gz" && \
56-
curl -L -O "ftp://ftp.gnu.org/gnu/glpk/glpk-${GLPK_VERSION}.tar.gz.sig" && \
57-
gpg --keyserver keyserver.ubuntu.com --recv-keys 5981E818 && \
58-
gpg --verify "glpk-${GLPK_VERSION}.tar.gz.sig" && \
59-
tar -xzf "glpk-${GLPK_VERSION}.tar.gz" && \
60-
cd "glpk-${GLPK_VERSION}" && \
61-
./configure --with-gmp && \
62-
make && \
63-
make install && \
64-
cd .. && \
65-
rm -rf "glpk-${GLPK_VERSION}"*
66-
1+
ARG SUNDIALS_VERSION=5.1.0
2+
ARG GLPK_VERSION=4.65
3+
ARG EXTRAS="dfba"
674

5+
FROM ghcr.io/amyris/sundials-glpk-pybind:sundials-${SUNDIALS_VERSION}-glpk-${GLPK_VERSION} as dev
686
COPY --from=ghcr.io/astral-sh/uv:0.6.11 /uv /uvx /bin/
697

708
ENV UV_COMPILE_BYTECODE=1
@@ -74,7 +12,6 @@ WORKDIR /app
7412

7513
COPY pyproject.toml ./
7614

77-
ARG EXTRAS="dfba"
7815
RUN EXTRA_NAMES=$(echo $EXTRAS | sed 's/,/ --extra /g' | sed 's/^/--extra /')
7916
RUN echo ${EXTRA_NAMES}
8017

pyproject.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,23 @@ requires = [
7979
]
8080
build-backend = "setuptools.build_meta"
8181

82+
[tool.coverage.run]
83+
source = ["src"] # List format often used here
84+
parallel = true
85+
branch = true
86+
# omit = ["src/__init__.py", "src/config/*"] # Optional
87+
88+
[tool.coverage.report]
89+
show_missing = true
90+
# fail_under = 90 # Optional
91+
# exclude_lines = [ # Optional
92+
# "pragma: no cover",
93+
# "raise NotImplementedError",
94+
# "if __name__ == .__main__.:",
95+
# ]
96+
97+
[tool.coverage.html]
98+
directory = "htmlcov"
99+
100+
[tool.coverage.xml]
101+
# output = "coverage.xml" # Optional

0 commit comments

Comments
 (0)