2727 TAG_TEST : " test"
2828 TAG_PROD : " prod"
2929 DEPLOYMENT_NAMESPACE : " 3cd915-dev"
30+ WORKFLOW_NAME : " ${{ github.workflow }}"
3031
3132on :
3233 workflow_dispatch :
@@ -86,118 +87,177 @@ jobs:
8687
8788 detect-dependency-changes :
8889 runs-on : ubuntu-22.04
90+ needs : ci-cd-start-notification
8991 outputs :
90- deps_changed : ${{ steps.changed .outputs.any_changed }}
92+ deps_changed : ${{ steps.detect .outputs.deps_changed }}
9193 steps :
9294 - uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
93- - name : Check dependency-related file changes
94- id : changed
95- uses : tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
9695 with :
97- files : |
98- **/package.json
99- **/package-lock.json
100- **/*.csproj
101- **/*.props
96+ fetch-depth : 0
97+
98+ - name : Detect dependency changes since last successful run
99+ id : detect
100+ env :
101+ GH_TOKEN : ${{ github.token }}
102+ run : |
103+ echo "WORKFLOW_NAME: $WORKFLOW_NAME"
104+ RUNS=$(gh run list \
105+ --workflow "${{ env.WORKFLOW_NAME }}" \
106+ --status success \
107+ --limit 1 \
108+ --json headSha)
109+
110+ LAST_SHA=$(echo "$RUNS" | jq -r '.[0].headSha // empty')
111+
112+ if [ -z "$LAST_SHA" ]; then
113+ echo "No previous successful run found — using HEAD~1"
114+ LAST_SHA=$(git rev-parse HEAD~1)
115+ fi
116+
117+ echo "Last successful run SHA: $LAST_SHA"
118+ echo "Current SHA: $(git rev-parse HEAD)"
119+
120+ tracked_files=$(git ls-files \
121+ '**/package.json' \
122+ '**/package-lock.json' \
123+ '**/*.csproj' \
124+ '**/*.props')
125+
126+ deps_changed=false
127+
128+ extract_node_deps() {
129+ jq '{
130+ dependencies: (.dependencies // {}),
131+ devDependencies: (.devDependencies // {})
132+ }' -S
133+ }
134+
135+ extract_itemgroups() {
136+ sed -n '/<ItemGroup>/,/<\/ItemGroup>/p' | grep '<PackageReference' | sed 's/^ *//'
137+ }
138+
139+ for file in $tracked_files; do
140+ echo "Checking $file"
141+
142+ old_raw=$(git show "$LAST_SHA:$file" 2>/dev/null || true)
143+ new_raw=$(git show "HEAD:$file" 2>/dev/null || true)
144+
145+ if [[ -z "$old_raw" || -z "$new_raw" ]]; then
146+ echo "Skipping $file (missing in one revision)"
147+ continue
148+ fi
149+
150+ ############################################
151+ # package.json and package-lock.json
152+ ############################################
153+ if [[ "$file" == *package.json || "$file" == *package-lock.json ]]; then
154+ old=$(echo "$old_raw" | extract_node_deps)
155+ new=$(echo "$new_raw" | extract_node_deps)
156+ ############################################
157+ # .csproj / .props
158+ ############################################
159+ elif [[ "$file" == *.csproj || "$file" == *.props ]]; then
160+ old=$(echo "$old_raw" | extract_itemgroups)
161+ new=$(echo "$new_raw" | extract_itemgroups)
162+ fi
163+
164+ ############################################
165+ # Show diff
166+ ############################################
167+ diff_output=$(diff -u <(echo "$old") <(echo "$new") || true)
168+
169+ if [[ -n "$diff_output" ]]; then
170+ deps_changed=true
171+ echo ""
172+ echo "=============================="
173+ echo "Semantic diff for $file"
174+ echo "=============================="
175+ echo "$diff_output"
176+ fi
177+ done
178+ echo "deps_changed=$deps_changed" >> "$GITHUB_OUTPUT"
102179
103180 scan-images :
104181 name : Scan container images for vulnerabilities
105182 needs : [build-frontend, build-api, detect-dependency-changes]
106183 if : needs.detect-dependency-changes.outputs.deps_changed == 'true'
107184 runs-on : ubuntu-22.04
185+
186+ strategy :
187+ fail-fast : false
188+ matrix :
189+ service :
190+ - name : frontend
191+ image : pims-app
192+ - name : backend
193+ image : pims-api
194+ - name : proxy
195+ image : pims-proxy
196+ - name : scheduler
197+ image : pims-scheduler
198+
108199 steps :
109200 - name : Checkout Source Code
110201 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
202+
111203 - name : Login to OpenShift
112204 uses : redhat-actions/oc-login@5eb45e848b168b6bf6b8fe7f1561003c12e3c99d # v1.3
113205 with :
114206 openshift_server_url : ${{ env.OPENSHIFT_SERVER }}
115207 openshift_token : ${{ env.OPENSHIFT_TOKEN }}
116208 insecure_skip_tls_verify : true
117209 namespace : ${{ env.OPENSHIFT_TOOLS_NAMESPACE }}
210+
118211 - name : Docker login to OpenShift registry
119- run : echo ${{ env.OPENSHIFT_TOKEN }} | docker login ${{ env.OPENSHIFT_REGISTRY }} -u unused --password-stdin
120- - name : Pre-deploy scan frontend (block on CRITICAL/HIGH)
121- env :
122- IMAGE : ${{ env.OPENSHIFT_REGISTRY }}/3cd915-tools/pims-app:latest-dev
123- run : |
124- docker pull "$IMAGE";
125- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --exit-code 1 --scanners vuln,secret,misconfig --format table --severity CRITICAL,HIGH "$IMAGE" | tee frontend_predeploy_scan.txt;
126- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/workspace" aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --scanners vuln,secret,misconfig --format sarif --severity CRITICAL,HIGH "$IMAGE" -o /workspace/frontend_predeploy_scan.sarif
127- - name : Upload frontend SARIF to Security tab
128- if : always()
129- uses : github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.9
130- with :
131- sarif_file : frontend_predeploy_scan.sarif
132- category : dev-frontend-predeploy
133- ref : refs/heads/dev
134- sha : ${{ github.sha }}
135- - name : Pre-deploy scan api (block on CRITICAL/HIGH)
136- env :
137- IMAGE : ${{ env.OPENSHIFT_REGISTRY }}/3cd915-tools/pims-api:latest-dev
138- run : |
139- docker pull "$IMAGE";
140- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --exit-code 1 --scanners vuln,secret,misconfig --format table --severity CRITICAL,HIGH "$IMAGE" | tee backend_predeploy_scan.txt;
141- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/workspace" aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --scanners vuln,secret,misconfig --format sarif --severity CRITICAL,HIGH "$IMAGE" -o /workspace/backend_predeploy_scan.sarif
142- - name : Upload api SARIF to Security tab
143- if : always()
144- uses : github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.9
145- with :
146- sarif_file : backend_predeploy_scan.sarif
147- category : dev-api-predeploy
148- ref : refs/heads/dev
149- sha : ${{ github.sha }}
150- - name : Pre-deploy scan proxy (block on CRITICAL/HIGH)
151- env :
152- IMAGE : ${{ env.OPENSHIFT_REGISTRY }}/3cd915-tools/pims-proxy:latest-dev
153- run : |
154- docker pull "$IMAGE";
155- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --exit-code 1 --scanners vuln,secret,misconfig --format table --severity CRITICAL,HIGH "$IMAGE" | tee proxy_predeploy_scan.txt;
156- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/workspace" aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --scanners vuln,secret,misconfig --format sarif --severity CRITICAL,HIGH "$IMAGE" -o /workspace/proxy_predeploy_scan.sarif
157- - name : Upload proxy SARIF to Security tab
158- if : always()
159- uses : github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.9
160- with :
161- sarif_file : proxy_predeploy_scan.sarif
162- category : dev-proxy-predeploy
163- ref : refs/heads/dev
164- sha : ${{ github.sha }}
165- - name : Pre-deploy scan scheduler (block on CRITICAL/HIGH)
212+ run : echo "${{ env.OPENSHIFT_TOKEN }}" | docker login ${{ env.OPENSHIFT_REGISTRY }} -u unused --password-stdin
213+
214+ - name : Scan container image
166215 env :
167- IMAGE : ${{ env.OPENSHIFT_REGISTRY }}/3cd915-tools/pims-scheduler :latest-dev
216+ IMAGE : ${{ env.OPENSHIFT_REGISTRY }}/3cd915-tools/${{ matrix.service.image }} :latest-dev
168217 run : |
169- docker pull "$IMAGE";
170- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --exit-code 1 --scanners vuln,secret,misconfig --format table --severity CRITICAL,HIGH "$IMAGE" | tee scheduler_predeploy_scan.txt;
171- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:/workspace" aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 image --scanners vuln,secret,misconfig --format sarif --severity CRITICAL,HIGH "$IMAGE" -o /workspace/scheduler_predeploy_scan.sarif
172- - name : Upload scheduler SARIF to Security tab
218+ docker pull "$IMAGE"
219+
220+ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
221+ aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 \
222+ image --exit-code 1 \
223+ --scanners vuln,secret,misconfig \
224+ --format table \
225+ --severity CRITICAL,HIGH \
226+ "$IMAGE" | tee ${{ matrix.service.name }}_predeploy_scan.txt
227+
228+ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
229+ -v "$PWD:/workspace" \
230+ aquasec/trivy@sha256:b7dc41ff0c3224dea024ee21bb9f6920a8af2fb343bba7139140d8fd0df1bac3 \
231+ image \
232+ --scanners vuln,secret,misconfig \
233+ --format sarif \
234+ --severity CRITICAL,HIGH \
235+ "$IMAGE" \
236+ -o /workspace/${{ matrix.service.name }}_predeploy_scan.sarif
237+
238+ - name : Upload SARIF to Security tab
173239 if : always()
174240 uses : github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.9
175241 with :
176- sarif_file : scheduler_predeploy_scan .sarif
177- category : dev-scheduler -predeploy
242+ sarif_file : ${{ matrix.service.name }}_predeploy_scan .sarif
243+ category : dev-${{ matrix.service.name }} -predeploy
178244 ref : refs/heads/dev
179245 sha : ${{ github.sha }}
246+
180247 - name : Upload SARIF files as artifacts
181248 if : always()
182249 uses : actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
183250 with :
184- name : dev-predeploy-sarif-reports
185- path : |
186- frontend_predeploy_scan.sarif
187- backend_predeploy_scan.sarif
188- proxy_predeploy_scan.sarif
189- scheduler_predeploy_scan.sarif
251+ name : dev-predeploy-sarif-${{ matrix.service.name }}
252+ path : ${{ matrix.service.name }}_predeploy_scan.sarif
190253 retention-days : 14
254+
191255 - name : Upload scan reports
192256 if : failure()
193257 uses : actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
194258 with :
195- name : dev-predeploy-scan-reports
196- path : |
197- frontend_predeploy_scan.txt
198- backend_predeploy_scan.txt
199- proxy_predeploy_scan.txt
200- scheduler_predeploy_scan.txt
259+ name : dev-predeploy-scan-${{ matrix.service.name }}
260+ path : ${{ matrix.service.name }}_predeploy_scan.txt
201261 retention-days : 14
202262
203263 deploy :
@@ -331,4 +391,4 @@ jobs:
331391 ms-teams-webhook-uri : ${{ env.MS_TEAMS_WEBHOOK_BUILD_CHANNEL }}
332392 notification-summary : PIMS CI-CD GitHub Action COMPLETED in DEV environment with status ${{ steps.check.outputs.status }}
333393 notification-color : 17a2b8
334- timezone : America/Los_Angeles
394+ timezone : America/Los_Angeles
0 commit comments