Skip to content

Commit db76b19

Browse files
Add release packaging script and align workflow naming
- Create scripts/bash/package_release.sh to produce gateway-<hash>.zip artifacts for VM deployment - Add stage-3-build workflow with smoke test and build provenance attestation - Rename workflows to staged convention (stage-1-commit, stage-2-test) - Replace pip install uv with astral-sh/setup-uv across all workflows
1 parent 00e7dd1 commit db76b19

4 files changed

Lines changed: 196 additions & 5 deletions

File tree

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
name: Lint
1+
name: "Stage 1: Commit"
22

33
on:
44
pull_request:
55
push:
66
branches:
77
- main
8+
- DTOSS-12157-create-application-package-script
89

910
jobs:
1011
ruff:
@@ -21,7 +22,7 @@ jobs:
2122
python-version-file: .tool-versions
2223

2324
- name: Install uv
24-
run: pip install uv
25+
uses: astral-sh/setup-uv@v6
2526

2627
- name: Install dependencies
2728
run: uv sync --all-extras
@@ -46,7 +47,7 @@ jobs:
4647
python-version-file: .tool-versions
4748

4849
- name: Install uv
49-
run: pip install uv
50+
uses: astral-sh/setup-uv@v6
5051

5152
- name: Install dependencies
5253
run: uv sync --all-extras
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
name: Test
1+
name: "Stage 2: Test"
22

33
on:
44
pull_request:
55
push:
66
branches:
77
- main
8+
- DTOSS-12157-create-application-package-script
89

910
jobs:
1011
test:
@@ -21,7 +22,7 @@ jobs:
2122
python-version-file: .tool-versions
2223

2324
- name: Install uv
24-
run: pip install uv
25+
uses: astral-sh/setup-uv@v6
2526

2627
- name: Install dependencies
2728
run: uv sync --all-extras
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: "Stage 3: Build"
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- DTOSS-12157-create-application-package-script
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: build-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
build:
16+
name: Build release package
17+
runs-on: ubuntu-latest
18+
19+
permissions:
20+
contents: read
21+
id-token: write
22+
attestations: write
23+
24+
outputs:
25+
version: ${{ steps.version.outputs.short_hash }}
26+
artifact_name: ${{ steps.version.outputs.artifact_name }}
27+
28+
steps:
29+
- name: Checkout code
30+
uses: actions/checkout@v6
31+
32+
- name: Determine version
33+
id: version
34+
run: |
35+
SHORT_HASH="$(git rev-parse --short HEAD)"
36+
echo "short_hash=${SHORT_HASH}" >> "$GITHUB_OUTPUT"
37+
echo "artifact_name=gateway-${SHORT_HASH}.zip" >> "$GITHUB_OUTPUT"
38+
39+
- name: Build package
40+
run: ./scripts/bash/package_release.sh "${{ runner.temp }}/dist"
41+
42+
- name: Set up Python
43+
uses: actions/setup-python@v6
44+
with:
45+
python-version-file: .tool-versions
46+
47+
- name: Install uv
48+
uses: astral-sh/setup-uv@v6
49+
50+
- name: Smoke test
51+
run: |
52+
WORK_DIR="${{ runner.temp }}/smoke-test"
53+
mkdir -p "$WORK_DIR"
54+
unzip "${{ runner.temp }}/dist/${{ steps.version.outputs.artifact_name }}" -d "$WORK_DIR"
55+
cd "$WORK_DIR"
56+
uv sync --frozen --no-dev
57+
PYTHONPATH=src uv run python -c "
58+
import pacs_main
59+
import mwl_main
60+
import upload_main
61+
import relay_listener
62+
print('All service modules imported successfully')
63+
"
64+
65+
- name: Upload artifact
66+
uses: actions/upload-artifact@v4
67+
with:
68+
name: gateway-package
69+
path: |
70+
${{ runner.temp }}/dist/gateway-*.zip
71+
${{ runner.temp }}/dist/gateway-*.zip.sha256
72+
retention-days: 90
73+
74+
- name: Attest build provenance
75+
uses: actions/attest-build-provenance@v2
76+
with:
77+
subject-path: ${{ runner.temp }}/dist/${{ steps.version.outputs.artifact_name }}

scripts/bash/package_release.sh

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Package the Manage Breast Screening Gateway for VM deployment.
4+
#
5+
# Produces a zip archive named gateway-<git-short-hash>.zip containing
6+
# everything needed to run `uv sync --frozen` and launch the services
7+
# on a target VM without Docker.
8+
#
9+
# A SHA256 checksum file is generated alongside the archive for integrity
10+
# verification. For cryptographic signing, use GitHub Artifact Attestations
11+
# in the CI workflow (actions/attest-build-provenance).
12+
13+
set -euo pipefail
14+
15+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
16+
OUTPUT_DIR="${1:-$REPO_ROOT/dist}"
17+
18+
# ── Prerequisites ──────────────────────────────────────────────────────────────
19+
20+
if ! command -v git &>/dev/null; then
21+
echo "Error: git is not installed." >&2
22+
exit 1
23+
fi
24+
25+
if ! command -v zip &>/dev/null; then
26+
echo "Error: zip is not installed." >&2
27+
exit 1
28+
fi
29+
30+
if ! git -C "$REPO_ROOT" rev-parse --git-dir &>/dev/null; then
31+
echo "Error: $REPO_ROOT is not a git repository." >&2
32+
exit 1
33+
fi
34+
35+
# ── Dirty tree warning ────────────────────────────────────────────────────────
36+
37+
if [[ -n "$(git -C "$REPO_ROOT" status --porcelain)" ]]; then
38+
echo "Warning: working tree has uncommitted changes." >&2
39+
echo " The git hash will not reflect the actual contents." >&2
40+
echo ""
41+
fi
42+
43+
# ── Version from Git ──────────────────────────────────────────────────────────
44+
45+
SHORT_HASH="$(git -C "$REPO_ROOT" rev-parse --short HEAD)"
46+
ARTIFACT_NAME="gateway-${SHORT_HASH}.zip"
47+
48+
echo "========================================"
49+
echo "Packaging Gateway Release"
50+
echo "========================================"
51+
echo "Commit: ${SHORT_HASH}"
52+
echo "Output: ${OUTPUT_DIR}/${ARTIFACT_NAME}"
53+
echo ""
54+
55+
# ── Validate required files ───────────────────────────────────────────────────
56+
57+
REQUIRED_FILES=("src" "pyproject.toml" "uv.lock" "README.md")
58+
59+
for item in "${REQUIRED_FILES[@]}"; do
60+
if [[ ! -e "${REPO_ROOT}/${item}" ]]; then
61+
echo "Error: required path '${item}' not found in repository root." >&2
62+
exit 1
63+
fi
64+
done
65+
66+
# ── Build the archive ────────────────────────────────────────────────────────
67+
68+
mkdir -p "$OUTPUT_DIR"
69+
ARTIFACT_PATH="${OUTPUT_DIR}/${ARTIFACT_NAME}"
70+
71+
# Remove existing artifact if present
72+
rm -f "$ARTIFACT_PATH"
73+
74+
cd "$REPO_ROOT"
75+
76+
zip -r "$ARTIFACT_PATH" \
77+
src/ \
78+
pyproject.toml \
79+
uv.lock \
80+
README.md \
81+
-x "**/__pycache__/*" "**/*.pyc"
82+
83+
# ── Verify archive integrity ─────────────────────────────────────────────────
84+
85+
if ! unzip -t "$ARTIFACT_PATH" > /dev/null 2>&1; then
86+
echo "Error: archive integrity check failed." >&2
87+
exit 1
88+
fi
89+
90+
# ── Generate SHA256 checksum ──────────────────────────────────────────────────
91+
92+
CHECKSUM_PATH="${ARTIFACT_PATH}.sha256"
93+
(cd "$OUTPUT_DIR" && shasum -a 256 "$ARTIFACT_NAME" > "${ARTIFACT_NAME}.sha256")
94+
95+
# ── Summary ───────────────────────────────────────────────────────────────────
96+
97+
ARTIFACT_SIZE="$(du -h "$ARTIFACT_PATH" | cut -f1)"
98+
99+
echo ""
100+
echo "========================================"
101+
echo "Package Complete"
102+
echo "========================================"
103+
echo "Artifact: ${ARTIFACT_PATH}"
104+
echo "Checksum: ${CHECKSUM_PATH}"
105+
echo "Size: ${ARTIFACT_SIZE}"
106+
echo "SHA256: $(cut -d' ' -f1 "$CHECKSUM_PATH")"
107+
echo ""
108+
echo "Contents:"
109+
zipinfo -1 "$ARTIFACT_PATH" | head -20
110+
TOTAL_FILES="$(zipinfo -t "$ARTIFACT_PATH" 2>&1 | grep -oE '[0-9]+ files')"
111+
echo "..."
112+
echo "Total: ${TOTAL_FILES}"

0 commit comments

Comments
 (0)