Skip to content

Commit 8f66f19

Browse files
authored
Merge pull request #6 from r2WillDev/feature/c2-sonarqube-sast
feat: add C2 SonarQube SAST pipeline
2 parents 76a3e91 + 4966cf7 commit 8f66f19

8 files changed

Lines changed: 981 additions & 0 deletions

File tree

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: C1 - Baseline Self-Hosted Pipeline
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- dev
8+
9+
10+
env:
11+
SCENARIO: C1-self-hosted
12+
RESULTS_FILE: analysis/raw/c1_self_hosted_run.csv
13+
IMAGE_NAME: tcc-devsecops-api:local
14+
CLUSTER_NAME: tcc-devsecops
15+
16+
jobs:
17+
c1-baseline-self-hosted:
18+
name: C1 baseline self-hosted
19+
runs-on: self-hosted
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v5
24+
25+
- name: Cleanup previous kind cluster container
26+
run: |
27+
docker rm -f "${CLUSTER_NAME}-control-plane" 2>/dev/null || true
28+
29+
- name: Prepare results CSV
30+
run: |
31+
mkdir -p analysis/raw
32+
echo "WORKFLOW_START_SECONDS=$(date +%s)" >> "$GITHUB_ENV"
33+
echo "WORKFLOW_START_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$GITHUB_ENV"
34+
echo "scenario,run_id,run_number,commit_sha,step_name,start_timestamp,end_timestamp,duration_seconds,status" > "$RESULTS_FILE"
35+
36+
- name: Setup Python
37+
uses: actions/setup-python@v6
38+
with:
39+
python-version: "3.12"
40+
41+
- name: Make measurement script executable
42+
run: chmod +x ci/scripts/measure_step.sh
43+
44+
- name: Install Python dependencies
45+
run: |
46+
./ci/scripts/measure_step.sh "install_dependencies" "$RESULTS_FILE" \
47+
python -m pip install -r app/requirements.txt
48+
49+
- name: Run unit tests
50+
run: |
51+
./ci/scripts/measure_step.sh "unit_tests" "$RESULTS_FILE" \
52+
python -m pytest app -v
53+
54+
- name: Build Docker image
55+
run: |
56+
./ci/scripts/measure_step.sh "docker_build" "$RESULTS_FILE" \
57+
docker build -t "$IMAGE_NAME" ./app
58+
59+
- name: Install kind
60+
run: |
61+
mkdir -p "$HOME/.local/bin"
62+
curl -Lo "$HOME/.local/bin/kind" https://kind.sigs.k8s.io/dl/v0.30.0/kind-linux-amd64
63+
chmod +x "$HOME/.local/bin/kind"
64+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
65+
export PATH="$HOME/.local/bin:$PATH"
66+
kind version
67+
68+
- name: Create local Kubernetes cluster
69+
run: |
70+
kind create cluster --name "$CLUSTER_NAME" --config infra/kind/kind-cluster.yaml
71+
kubectl cluster-info
72+
kubectl get nodes
73+
74+
- name: Deploy to Kubernetes
75+
run: |
76+
./ci/scripts/measure_step.sh "kubernetes_deploy" "$RESULTS_FILE" \
77+
bash -c 'kind load docker-image "$IMAGE_NAME" --name "$CLUSTER_NAME" && kubectl apply -f infra/k8s/namespace.yaml && kubectl apply -f infra/k8s/deployment.yaml && kubectl apply -f infra/k8s/service.yaml && kubectl rollout status deployment/tcc-api -n app --timeout=120s'
78+
79+
- name: Smoke test Kubernetes deployment
80+
run: |
81+
./ci/scripts/measure_step.sh "kubernetes_smoke_test" "$RESULTS_FILE" \
82+
bash -c 'kubectl port-forward svc/tcc-api 8000:80 -n app > /tmp/port-forward.log 2>&1 & PF_PID=$!; sleep 5; curl -fsS http://localhost:8000/health; kill $PF_PID'
83+
84+
- name: Append workflow total duration
85+
if: always()
86+
run: |
87+
WORKFLOW_END_TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
88+
WORKFLOW_END_SECONDS="$(date +%s)"
89+
WORKFLOW_DURATION_SECONDS=$((WORKFLOW_END_SECONDS - WORKFLOW_START_SECONDS))
90+
echo "${SCENARIO},${GITHUB_RUN_ID},${GITHUB_RUN_NUMBER},${GITHUB_SHA},workflow_total,${WORKFLOW_START_TIMESTAMP},${WORKFLOW_END_TIMESTAMP},${WORKFLOW_DURATION_SECONDS},success" >> "$RESULTS_FILE"
91+
92+
- name: Show collected metrics
93+
if: always()
94+
run: |
95+
echo "Collected C1 self-hosted metrics:"
96+
cat "$RESULTS_FILE"
97+
98+
- name: Write GitHub Actions summary
99+
if: always()
100+
run: |
101+
echo "## C1 - Baseline Self-Hosted Pipeline" >> "$GITHUB_STEP_SUMMARY"
102+
echo "" >> "$GITHUB_STEP_SUMMARY"
103+
echo "Scenario: $SCENARIO" >> "$GITHUB_STEP_SUMMARY"
104+
echo "" >> "$GITHUB_STEP_SUMMARY"
105+
echo "### Collected metrics" >> "$GITHUB_STEP_SUMMARY"
106+
echo "" >> "$GITHUB_STEP_SUMMARY"
107+
echo '```csv' >> "$GITHUB_STEP_SUMMARY"
108+
cat "$RESULTS_FILE" >> "$GITHUB_STEP_SUMMARY"
109+
echo '```' >> "$GITHUB_STEP_SUMMARY"
110+
111+
- name: Upload C1 self-hosted metrics artifact
112+
if: always()
113+
uses: actions/upload-artifact@v6
114+
with:
115+
name: c1-self-hosted-metrics
116+
path: analysis/raw/c1_self_hosted_run.csv

.github/workflows/c1-baseline.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ jobs:
2121
- name: Checkout repository
2222
uses: actions/checkout@v5
2323

24+
- name: Cleanup previous kind cluster container
25+
run: |
26+
docker rm -f "${CLUSTER_NAME}-control-plane" 2>/dev/null || true
27+
2428
- name: Prepare results CSV
2529
run: |
2630
mkdir -p analysis/raw

.github/workflows/c2-sast.yml

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
name: C2 - SAST SonarQube Pipeline
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- dev
8+
9+
env:
10+
SCENARIO: C2-sast
11+
RESULTS_FILE: analysis/raw/c2_sast_run.csv
12+
IMAGE_NAME: tcc-devsecops-api:local
13+
CLUSTER_NAME: tcc-devsecops
14+
SONAR_PROJECT_SETTINGS: ci/config/sonar-project.properties
15+
16+
jobs:
17+
c2-sast:
18+
name: C2 baseline plus SonarQube SAST
19+
runs-on: self-hosted
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v5
24+
25+
- name: Cleanup previous kind cluster container
26+
run: |
27+
docker rm -f "${CLUSTER_NAME}-control-plane" 2>/dev/null || true
28+
29+
- name: Prepare results CSV
30+
run: |
31+
mkdir -p analysis/raw
32+
echo "WORKFLOW_START_SECONDS=$(date +%s)" >> "$GITHUB_ENV"
33+
echo "WORKFLOW_START_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$GITHUB_ENV"
34+
echo "scenario,run_id,run_number,commit_sha,step_name,start_timestamp,end_timestamp,duration_seconds,status" > "$RESULTS_FILE"
35+
36+
- name: Setup Python
37+
uses: actions/setup-python@v6
38+
with:
39+
python-version: "3.12"
40+
41+
- name: Make measurement script executable
42+
run: chmod +x ci/scripts/measure_step.sh
43+
44+
- name: Install Python dependencies
45+
run: |
46+
./ci/scripts/measure_step.sh "install_dependencies" "$RESULTS_FILE" \
47+
python -m pip install -r app/requirements.txt
48+
49+
- name: Run unit tests
50+
run: |
51+
./ci/scripts/measure_step.sh "unit_tests" "$RESULTS_FILE" \
52+
python -m pytest app -v
53+
54+
- name: Run SonarQube SAST analysis
55+
env:
56+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
57+
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
58+
run: |
59+
SONARQUBE_NETWORK=$(docker inspect tcc-sonarqube --format '{{range $name, $_ := .NetworkSettings.Networks}}{{println $name}}{{end}}' | head -n 1)
60+
echo "Using SonarQube Docker network: $SONARQUBE_NETWORK"
61+
62+
./ci/scripts/measure_step.sh "sonarqube_sast" "$RESULTS_FILE" \
63+
docker run --rm \
64+
--network "$SONARQUBE_NETWORK" \
65+
-e SONAR_HOST_URL="$SONAR_HOST_URL" \
66+
-e SONAR_TOKEN="$SONAR_TOKEN" \
67+
-v "$GITHUB_WORKSPACE:/usr/src" \
68+
sonarsource/sonar-scanner-cli \
69+
-Dproject.settings="$SONAR_PROJECT_SETTINGS"
70+
71+
- name: Build Docker image
72+
run: |
73+
./ci/scripts/measure_step.sh "docker_build" "$RESULTS_FILE" \
74+
docker build -t "$IMAGE_NAME" ./app
75+
76+
- name: Install kind
77+
run: |
78+
mkdir -p "$HOME/.local/bin"
79+
curl -Lo "$HOME/.local/bin/kind" https://kind.sigs.k8s.io/dl/v0.30.0/kind-linux-amd64
80+
chmod +x "$HOME/.local/bin/kind"
81+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
82+
export PATH="$HOME/.local/bin:$PATH"
83+
kind version
84+
85+
- name: Create local Kubernetes cluster
86+
run: |
87+
kind create cluster --name "$CLUSTER_NAME" --config infra/kind/kind-cluster.yaml
88+
kubectl cluster-info
89+
kubectl get nodes
90+
91+
- name: Deploy to Kubernetes
92+
run: |
93+
./ci/scripts/measure_step.sh "kubernetes_deploy" "$RESULTS_FILE" \
94+
bash -c 'kind load docker-image "$IMAGE_NAME" --name "$CLUSTER_NAME" && kubectl apply -f infra/k8s/namespace.yaml && kubectl apply -f infra/k8s/deployment.yaml && kubectl apply -f infra/k8s/service.yaml && kubectl rollout status deployment/tcc-api -n app --timeout=120s'
95+
96+
- name: Smoke test Kubernetes deployment
97+
run: |
98+
./ci/scripts/measure_step.sh "kubernetes_smoke_test" "$RESULTS_FILE" \
99+
bash -c 'kubectl port-forward svc/tcc-api 8000:80 -n app > /tmp/port-forward.log 2>&1 & PF_PID=$!; sleep 5; curl -fsS http://localhost:8000/health; kill $PF_PID'
100+
101+
- name: Append workflow total duration
102+
if: always()
103+
run: |
104+
WORKFLOW_END_TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
105+
WORKFLOW_END_SECONDS="$(date +%s)"
106+
WORKFLOW_DURATION_SECONDS=$((WORKFLOW_END_SECONDS - WORKFLOW_START_SECONDS))
107+
echo "${SCENARIO},${GITHUB_RUN_ID},${GITHUB_RUN_NUMBER},${GITHUB_SHA},workflow_total,${WORKFLOW_START_TIMESTAMP},${WORKFLOW_END_TIMESTAMP},${WORKFLOW_DURATION_SECONDS},success" >> "$RESULTS_FILE"
108+
109+
- name: Show collected metrics
110+
if: always()
111+
run: |
112+
echo "Collected C2 SAST metrics:"
113+
cat "$RESULTS_FILE"
114+
115+
- name: Write GitHub Actions summary
116+
if: always()
117+
run: |
118+
echo "## C2 - SAST SonarQube Pipeline" >> "$GITHUB_STEP_SUMMARY"
119+
echo "" >> "$GITHUB_STEP_SUMMARY"
120+
echo "Scenario: $SCENARIO" >> "$GITHUB_STEP_SUMMARY"
121+
echo "" >> "$GITHUB_STEP_SUMMARY"
122+
echo "### Collected metrics" >> "$GITHUB_STEP_SUMMARY"
123+
echo "" >> "$GITHUB_STEP_SUMMARY"
124+
echo '```csv' >> "$GITHUB_STEP_SUMMARY"
125+
cat "$RESULTS_FILE" >> "$GITHUB_STEP_SUMMARY"
126+
echo '```' >> "$GITHUB_STEP_SUMMARY"
127+
128+
- name: Upload C2 metrics artifact
129+
if: always()
130+
uses: actions/upload-artifact@v6
131+
with:
132+
name: c2-sast-metrics
133+
path: analysis/raw/c2_sast_run.csv

analysis/raw/c1_self_hosted.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
scenario,run_id,run_number,commit_sha,step_name,start_timestamp,end_timestamp,duration_seconds,status

analysis/raw/c2_sast.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
scenario,run_id,run_number,commit_sha,step_name,start_timestamp,end_timestamp,duration_seconds,status

ci/config/sonar-project.properties

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
sonar.projectKey=tcc-devsecops-pipeline
2+
sonar.projectName=TCC DevSecOps Pipeline
3+
sonar.projectVersion=1.0
4+
5+
sonar.sources=app
6+
sonar.tests=app
7+
sonar.test.inclusions=app/test_*.py
8+
sonar.exclusions=app/test_*.py,**/__pycache__/**,**/.pytest_cache/**,**/.venv/**,**/venv/**,**/.mypy_cache/**,**/.ruff_cache/**
9+
10+
sonar.sourceEncoding=UTF-8
11+
sonar.python.version=3.12

0 commit comments

Comments
 (0)