Skip to content

Commit fbd1308

Browse files
committed
Merge branch 'main' of github.com:IBM/mcp-context-forge
2 parents ab46ce1 + 558ddcd commit fbd1308

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed

.github/workflows/docker-image.yml

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# ===============================================================
2+
# 📦 Secure Docker Build & Scan Workflow
3+
# ===============================================================
4+
#
5+
# This workflow:
6+
# • Builds and tags the container image (`latest` + timestamp)
7+
# • Re-uses a BuildKit layer cache for faster rebuilds
8+
# • Lints the Dockerfile with **Hadolint** (CLI) → SARIF
9+
# • Lints the finished image with **Dockle** (CLI) → SARIF
10+
# • Generates an SPDX SBOM with **Syft**
11+
# • Scans the image for CRITICAL/HIGH CVEs with **Trivy**
12+
# • Uploads Hadolint, Dockle and Trivy results as SARIF files
13+
# • Pushes the image to **GitHub Container Registry (GHCR)**
14+
# • Signs & attests the image with **Cosign (key-less OIDC)**
15+
#
16+
# Triggers:
17+
# • Every push / PR to `main`
18+
# • Weekly scheduled run (Tue 18:17 UTC) to catch new CVEs
19+
# ---------------------------------------------------------------
20+
21+
name: Secure Docker Build
22+
23+
on:
24+
push:
25+
branches: [ "main" ]
26+
pull_request:
27+
branches: [ "main" ]
28+
schedule:
29+
- cron: '17 18 * * 2' # Tuesday @ 18:17 UTC
30+
31+
# -----------------------------------------------------------------
32+
# Minimal permissions – keep the principle of least privilege
33+
# -----------------------------------------------------------------
34+
permissions:
35+
contents: read
36+
packages: write # push to ghcr.io via GITHUB_TOKEN
37+
security-events: write # upload SARIF to “Code scanning”
38+
actions: read # needed by upload-sarif in private repos
39+
id-token: write # required for OIDC token generation
40+
41+
jobs:
42+
build-scan-sign:
43+
runs-on: ubuntu-latest
44+
45+
env:
46+
CACHE_DIR: /tmp/.buildx-cache # BuildKit layer cache dir
47+
48+
steps:
49+
# -------------------------------------------------------------
50+
# 0️⃣ Checkout source
51+
# -------------------------------------------------------------
52+
- name: ⬇️ Checkout code
53+
uses: actions/checkout@v4
54+
55+
# -------------------------------------------------------------
56+
# 0️⃣.5️⃣ Derive lower-case IMAGE_NAME for Docker tag
57+
# -------------------------------------------------------------
58+
- name: 🏷️ Set IMAGE_NAME (lower-case repo path)
59+
run: |
60+
IMAGE="ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')"
61+
echo "IMAGE_NAME=$IMAGE" >> "$GITHUB_ENV"
62+
echo "Will build & push: $IMAGE_NAME"
63+
64+
# -------------------------------------------------------------
65+
# 1️⃣ Dockerfile lint (Hadolint CLI → SARIF)
66+
# -------------------------------------------------------------
67+
- name: 🔍 Dockerfile lint (Hadolint)
68+
id: hadolint
69+
continue-on-error: true
70+
run: |
71+
curl -sSL https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 -o /usr/local/bin/hadolint
72+
chmod +x /usr/local/bin/hadolint
73+
hadolint -f sarif Containerfile.lite > hadolint-results.sarif
74+
echo "HADOLINT_EXIT=$?" >> "$GITHUB_ENV"
75+
exit 0
76+
- name: ☁️ Upload Hadolint SARIF
77+
if: always()
78+
uses: github/codeql-action/upload-sarif@v3
79+
with:
80+
sarif_file: hadolint-results.sarif
81+
82+
# -------------------------------------------------------------
83+
# 2️⃣ Set up Buildx & restore cache
84+
# -------------------------------------------------------------
85+
- name: 🛠️ Set up Docker Buildx
86+
uses: docker/setup-buildx-action@v3
87+
88+
- name: 🔄 Restore BuildKit layer cache
89+
uses: actions/cache@v4
90+
with:
91+
path: ${{ env.CACHE_DIR }}
92+
key: ${{ runner.os }}-buildx-${{ github.sha }}
93+
restore-keys: ${{ runner.os }}-buildx-
94+
95+
# -------------------------------------------------------------
96+
# 3️⃣ Build & tag image (timestamp + latest)
97+
# -------------------------------------------------------------
98+
- name: 🏗️ Build Docker image
99+
run: |
100+
TAG=$(date +%s)
101+
echo "TAG=$TAG" >> "$GITHUB_ENV"
102+
docker buildx build \
103+
--file Containerfile.lite \
104+
--tag $IMAGE_NAME:$TAG \
105+
--tag $IMAGE_NAME:latest \
106+
--cache-from type=local,src=${{ env.CACHE_DIR }} \
107+
--cache-to type=local,dest=${{ env.CACHE_DIR }},mode=max \
108+
--load \
109+
. # build context is mandatory
110+
111+
# -------------------------------------------------------------
112+
# 4️⃣ Image lint (Dockle CLI → SARIF)
113+
# -------------------------------------------------------------
114+
- name: 🔍 Image lint (Dockle)
115+
id: dockle
116+
continue-on-error: true
117+
env:
118+
DOCKLE_VERSION: 0.4.15
119+
run: |
120+
curl -sSL "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.tar.gz" \
121+
| tar -xz -C /usr/local/bin dockle
122+
dockle --exit-code 1 --format sarif \
123+
--output dockle-results.sarif \
124+
$IMAGE_NAME:latest
125+
echo "DOCKLE_EXIT=$?" >> "$GITHUB_ENV"
126+
exit 0
127+
- name: ☁️ Upload Dockle SARIF
128+
if: always()
129+
uses: github/codeql-action/upload-sarif@v3
130+
with:
131+
sarif_file: dockle-results.sarif
132+
133+
# -------------------------------------------------------------
134+
# 5️⃣ Generate SPDX SBOM with Syft
135+
# -------------------------------------------------------------
136+
- name: 📄 Generate SBOM (Syft)
137+
uses: anchore/sbom-action@v0
138+
with:
139+
image: ${{ env.IMAGE_NAME }}:latest
140+
output-file: sbom.spdx.json
141+
142+
# -------------------------------------------------------------
143+
# 6️⃣ Trivy CVE scan → SARIF
144+
# -------------------------------------------------------------
145+
- name: 🛡️ Trivy vulnerability scan
146+
id: trivy
147+
continue-on-error: true
148+
uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe
149+
with:
150+
image-ref: ${{ env.IMAGE_NAME }}:latest
151+
format: sarif
152+
output: trivy-results.sarif
153+
severity: CRITICAL,HIGH
154+
exit-code: 1
155+
- name: ☁️ Upload Trivy SARIF
156+
if: always()
157+
uses: github/codeql-action/upload-sarif@v3
158+
with:
159+
sarif_file: trivy-results.sarif
160+
161+
# -------------------------------------------------------------
162+
# 7️⃣ Push both tags to GHCR
163+
# -------------------------------------------------------------
164+
- name: 🔑 Log in to GHCR
165+
uses: docker/login-action@v3
166+
with:
167+
registry: ghcr.io
168+
username: ${{ github.actor }}
169+
password: ${{ secrets.GITHUB_TOKEN }}
170+
171+
- name: 🚀 Push image to GHCR
172+
run: |
173+
docker push $IMAGE_NAME:${{ env.TAG }}
174+
docker push $IMAGE_NAME:latest
175+
176+
# -------------------------------------------------------------
177+
# 8️⃣ Key-less Cosign sign + attest (latest **and** timestamp)
178+
# -------------------------------------------------------------
179+
- name: 📥 Install Cosign
180+
uses: sigstore/cosign-installer@v3 # provides the matching CLI
181+
182+
- name: 🔏 Sign & attest images (latest + timestamp)
183+
env:
184+
COSIGN_EXPERIMENTAL: "1"
185+
run: |
186+
for REF in $IMAGE_NAME:latest $IMAGE_NAME:${{ env.TAG }}; do
187+
echo "🔑 Signing $REF"
188+
cosign sign --yes "$REF"
189+
190+
echo "📝 Attesting SBOM for $REF"
191+
cosign attest --yes \
192+
--predicate sbom.spdx.json \
193+
--type spdxjson \
194+
"$REF"
195+
done
196+
197+
# -------------------------------------------------------------
198+
# 9️⃣ Single gate – fail job on any scanner error
199+
# -------------------------------------------------------------
200+
- name: ⛔ Enforce lint & vuln gates
201+
if: |
202+
env.HADOLINT_EXIT != '0' ||
203+
env.DOCKLE_EXIT != '0' ||
204+
steps.trivy.outcome == 'failure'
205+
run: |
206+
echo "Hadolint exit: $HADOLINT_EXIT"
207+
echo "Dockle exit: $DOCKLE_EXIT"
208+
echo "Trivy status: ${{ steps.trivy.outcome }}"
209+
exit 1

.github/workflows/python-package.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Build-only workflow — runs `make dist` to create sdist & wheel, no lint/tests
2+
# Docs: https://docs.github.com/en/actions | PyPA build: https://pypi.org/project/build
3+
4+
name: Build Python Package
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
jobs:
13+
build-package:
14+
runs-on: ubuntu-latest
15+
16+
# Build the package under multiple Python versions to catch ABI issues early
17+
strategy:
18+
matrix:
19+
python-version: [ "3.10", "3.11", "3.12" ] # Extend as needed
20+
21+
steps:
22+
# 1️⃣ Check out repository so Makefile & sources are available
23+
- name: Checkout code
24+
uses: actions/checkout@v4 # Standard checkout step
25+
26+
# 2️⃣ Set up the requested Python version from the runner tool-cache
27+
- name: Set up Python ${{ matrix.python-version }}
28+
uses: actions/setup-python@v5 # Recommended approach for consistent PATH
29+
with:
30+
python-version: ${{ matrix.python-version }}
31+
32+
# 3️⃣ Install build front-end; Keep pip current
33+
- name: Install build tool
34+
run: |
35+
python -m pip install --upgrade pip
36+
python -m pip install build # PyPA-endorsed PEP 517 builder
37+
38+
# 4️⃣ Invoke the Makefile ‘dist’ target (creates ./dist/*.whl & *.tar.gz)
39+
- name: Build distributions
40+
run: make dist # Uses the Makefile’s `dist` rule
41+
42+
# 5️⃣ Upload built artifacts so they can be downloaded from the run page
43+
- name: Upload distributions
44+
uses: actions/upload-artifact@v4
45+
with:
46+
name: python-package-${{ matrix.python-version }}
47+
path: dist/*

0 commit comments

Comments
 (0)