Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 33 additions & 22 deletions .github/actions/security-container-scan/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ runs:
- name: Precheck local image exists
id: precheck
shell: bash
env:
INPUT_IMAGE: ${{ inputs.image }}
run: |
set +e
IMAGE="${{ inputs.image }}"
docker image inspect "${IMAGE}" >/dev/null 2>&1
docker image inspect "${INPUT_IMAGE}" >/dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo "image_exists=true" >> "$GITHUB_OUTPUT"
Expand All @@ -96,13 +97,14 @@ runs:
id: grype
if: ${{ steps.precheck.outputs.image_exists == 'true' }}
shell: bash
env:
IMAGE: ${{ inputs.image }}
REPORT_JSON: ${{ inputs.report-json }}
REPORT_SARIF: ${{ inputs.report-sarif }}
FAIL_ON: ${{ inputs.fail-on }}
GRYPE_IMAGE: ${{ inputs.grype-image }}
run: |
set +e
IMAGE="${{ inputs.image }}"
REPORT_JSON="${{ inputs.report-json }}"
REPORT_SARIF="${{ inputs.report-sarif }}"
FAIL_ON="${{ inputs.fail-on }}"
GRYPE_IMAGE="${{ inputs.grype-image }}"

echo "Pulling Grype scanner image..."
docker pull "${GRYPE_IMAGE}" >/dev/null 2>&1
Expand Down Expand Up @@ -147,58 +149,67 @@ runs:
- name: Write scan summary
if: ${{ inputs.write-summary == 'true' }}
shell: bash
env:
INPUT_IMAGE: ${{ inputs.image }}
INPUT_ARTIFACT_NAME: ${{ inputs.artifact-name }}
INPUT_REPORT_JSON: ${{ inputs.report-json }}
run: |
set -euo pipefail
echo "### 🔍 Container Scan (SBOM + Grype)" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- Image: \`${{ inputs.image }}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Reports artifact: \`${{ inputs.artifact-name }}\` (sarif + json)" >> "$GITHUB_STEP_SUMMARY"
echo "- Image: \`${INPUT_IMAGE}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Reports artifact: \`${INPUT_ARTIFACT_NAME}\` (sarif + json)" >> "$GITHUB_STEP_SUMMARY"

if [ -f "${{ inputs.report-json }}" ]; then
python3 "$GITHUB_ACTION_PATH/grype_summary.py" --json "${{ inputs.report-json }}" --max-top 10 >> "$GITHUB_STEP_SUMMARY" || true
if [ -f "${INPUT_REPORT_JSON}" ]; then
python3 "$GITHUB_ACTION_PATH/grype_summary.py" --json "${INPUT_REPORT_JSON}" --max-top 10 >> "$GITHUB_STEP_SUMMARY" || true
fi

- name: Finalize status/outputs
id: final
if: always()
shell: bash
env:
IMAGE_EXISTS: ${{ steps.precheck.outputs.image_exists }}
SBOM_OUTCOME: ${{ steps.sbom.outcome }}
GRYPE_STATUS: ${{ steps.grype.outputs.status }}
GRYPE_EXIT: ${{ steps.grype.outputs.exit_code }}
INPUT_GENERATE_SBOM: ${{ inputs.generate-sbom }}
INPUT_FAIL_ON: ${{ inputs.fail-on }}
INPUT_REPORT_JSON: ${{ inputs.report-json }}
INPUT_REPORT_SARIF: ${{ inputs.report-sarif }}
INPUT_FAIL_BUILD: ${{ inputs.fail-build }}
run: |
set -euo pipefail

IMAGE_EXISTS="${{ steps.precheck.outputs.image_exists }}"
SBOM_OUTCOME="${{ steps.sbom.outcome }}"
GRYPE_STATUS="${{ steps.grype.outputs.status }}"
GRYPE_EXIT="${{ steps.grype.outputs.exit_code }}"

status="ok"
detail="ok"

if [ "${IMAGE_EXISTS}" != "true" ]; then
status="image_not_found"
detail="local docker image not found"
elif [ "${{ inputs.generate-sbom }}" = "true" ] && [ "${SBOM_OUTCOME}" != "success" ]; then
elif [ "${INPUT_GENERATE_SBOM}" = "true" ] && [ "${SBOM_OUTCOME}" != "success" ]; then
status="sbom_failed"
detail="sbom-action failed"
elif [ -z "${GRYPE_STATUS}" ]; then
status="grype_unknown"
detail="grype step did not produce status"
elif [ "${GRYPE_STATUS}" = "ok" ]; then
status="ok"
detail="no vulnerabilities at/above fail-on=${{ inputs.fail-on }}"
detail="no vulnerabilities at/above fail-on=${INPUT_FAIL_ON}"
elif [ "${GRYPE_STATUS}" = "high_or_error" ]; then
status="high_or_error"
detail="grype exit_code=${GRYPE_EXIT} (fail-on=${{ inputs.fail-on }})"
detail="grype exit_code=${GRYPE_EXIT} (fail-on=${INPUT_FAIL_ON})"
else
status="${GRYPE_STATUS}"
detail="grype exit_code=${GRYPE_EXIT}"
fi

echo "status=${status}" >> "$GITHUB_OUTPUT"
echo "detail=${detail}" >> "$GITHUB_OUTPUT"
echo "report_json=${{ inputs.report-json }}" >> "$GITHUB_OUTPUT"
echo "report_sarif=${{ inputs.report-sarif }}" >> "$GITHUB_OUTPUT"
echo "report_json=${INPUT_REPORT_JSON}" >> "$GITHUB_OUTPUT"
echo "report_sarif=${INPUT_REPORT_SARIF}" >> "$GITHUB_OUTPUT"

if [ "${{ inputs.fail-build }}" = "true" ] && [ "${status}" != "ok" ]; then
if [ "${INPUT_FAIL_BUILD}" = "true" ] && [ "${status}" != "ok" ]; then
echo "Failing build due to status=${status}: ${detail}" 1>&2
exit 1
fi
48 changes: 25 additions & 23 deletions .github/actions/slack-notify/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,23 @@ runs:
- name: Prepare Slack Payload
id: prepare-payload
shell: bash
env:
INPUT_CHANNEL_ID: ${{ inputs.channel-id }}
INPUT_PAYLOAD: ${{ inputs.payload }}
INPUT_MESSAGE: ${{ inputs.message }}
run: |
CHANNEL_ID="${{ inputs.channel-id }}"

# If custom payload is provided, merge channel into it
if [ -n '${{ inputs.payload }}' ]; then
if [ -n "${INPUT_PAYLOAD}" ]; then
# Parse the JSON and add/update channel field
PAYLOAD=$(cat <<'PAYLOAD_EOF' | jq -c ". + {channel: \"$CHANNEL_ID\"}"
${{ inputs.payload }}
PAYLOAD_EOF
)
PAYLOAD=$(echo "${INPUT_PAYLOAD}" | jq -c ". + {channel: \"${INPUT_CHANNEL_ID}\"}")
echo "payload<<EOF" >> $GITHUB_OUTPUT
echo "$PAYLOAD" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
elif [ -n '${{ inputs.message }}' ]; then
elif [ -n "${INPUT_MESSAGE}" ]; then
# Simple message mode - construct basic payload
MESSAGE=$(cat <<'MESSAGE_EOF'
${{ inputs.message }}
MESSAGE_EOF
)
PAYLOAD=$(jq -n \
--arg channel "$CHANNEL_ID" \
--arg text "$MESSAGE" \
--arg channel "${INPUT_CHANNEL_ID}" \
--arg text "${INPUT_MESSAGE}" \
'{channel: $channel, text: $text}')
echo "payload<<EOF" >> $GITHUB_OUTPUT
echo "$PAYLOAD" >> $GITHUB_OUTPUT
Expand All @@ -100,32 +95,39 @@ runs:
- name: Display Notification Info
if: steps.slack.outcome == 'success'
shell: bash
env:
SLACK_TS: ${{ steps.slack.outputs.ts }}
SLACK_THREAD_TS: ${{ steps.slack.outputs.thread_ts }}
INPUT_CHANNEL_ID: ${{ inputs.channel-id }}
INPUT_ERRORS: ${{ inputs.errors }}
run: |
if [ -n "${{ steps.slack.outputs.ts }}" ]; then
if [ -n "${SLACK_TS}" ]; then
echo "✅ Slack notification sent successfully!"
echo "📬 Channel ID: ${{ inputs.channel-id }}"
echo "⏰ Timestamp: ${{ steps.slack.outputs.ts }}"
if [ -n "${{ steps.slack.outputs.thread_ts }}" ]; then
echo "🧵 Thread Timestamp: ${{ steps.slack.outputs.thread_ts }}"
echo "📬 Channel ID: ${INPUT_CHANNEL_ID}"
echo "⏰ Timestamp: ${SLACK_TS}"
if [ -n "${SLACK_THREAD_TS}" ]; then
echo "🧵 Thread Timestamp: ${SLACK_THREAD_TS}"
fi
else
echo "⚠️ Slack API call completed but returned no timestamp!"
echo "This usually means the bot doesn't have permission to post to the channel."
echo ""
echo "Common solutions:"
echo " 1. Invite the bot to channel ${{ inputs.channel-id }}: /invite @YourBotName"
echo " 1. Invite the bot to channel ${INPUT_CHANNEL_ID}: /invite @YourBotName"
echo " 2. Add 'chat:write.public' scope to your bot (for public channels)"
echo " 3. Verify the channel ID is correct: ${{ inputs.channel-id }}"
if [ "${{ inputs.errors }}" == "true" ]; then
echo " 3. Verify the channel ID is correct: ${INPUT_CHANNEL_ID}"
if [ "${INPUT_ERRORS}" == "true" ]; then
exit 1
fi
fi

- name: Display Error Info
if: steps.slack.outcome == 'failure'
shell: bash
env:
INPUT_ERRORS: ${{ inputs.errors }}
run: |
echo "❌ Slack notification failed!"
if [ "${{ inputs.errors }}" == "false" ]; then
if [ "${INPUT_ERRORS}" == "false" ]; then
echo "⚠️ Continuing workflow despite failure (errors: false)"
fi
17 changes: 13 additions & 4 deletions .github/workflows/build-cds-containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ env:
IMAGE_NAMESPACE: nvidia
IMAGE_PREFIX: dsx-cds- # Prefix to identify CDS container images

permissions:
contents: read
packages: write # Required to push to GHCR

jobs:
# Job 1: Read version from VERSION.md
get-version:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.extract-version.outputs.version }}

Expand All @@ -41,6 +39,9 @@ jobs:
# Job 2: Build and push all container images
build-and-push-images:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # Required to push to GHCR
needs: get-version
strategy:
fail-fast: false
Expand Down Expand Up @@ -122,6 +123,9 @@ jobs:
# Job 3: Test using the built go-dev image
test-go-dev-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
needs: [get-version, build-and-push-images]
# Only run tests when images are pushed (main only)
if: github.ref == 'refs/heads/main'
Expand Down Expand Up @@ -161,6 +165,9 @@ jobs:
# Job 4: Test using tools container
test-tools-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
needs: [get-version, build-and-push-images]
# Only run tests when images are pushed (main only)
if: github.ref == 'refs/heads/main'
Expand Down Expand Up @@ -196,6 +203,8 @@ jobs:
# Job 5: Summary
summary:
runs-on: ubuntu-latest
permissions:
contents: read
needs: [get-version, build-and-push-images, test-go-dev-image, test-tools-image]
if: always()

Expand Down
14 changes: 10 additions & 4 deletions .github/workflows/promote-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,24 @@ jobs:
- name: Prepare refs
id: refs
shell: bash
env:
INPUT_DIGEST: ${{ inputs.digest }}
INPUT_SOURCE: ${{ inputs.source }}
INPUT_SOURCE_TAG: ${{ inputs.source_tag }}
INPUT_DESTINATION: ${{ inputs.destination }}
INPUT_DESTINATION_TAG: ${{ inputs.destination_tag }}
run: |
set -euo pipefail

# Assemble source reference
if [[ -n "${{ inputs.digest }}" ]]; then
source_ref="${{ inputs.source }}@${{ inputs.digest }}"
if [[ -n "${INPUT_DIGEST}" ]]; then
source_ref="${INPUT_SOURCE}@${INPUT_DIGEST}"
else
source_ref="${{ inputs.source }}:${{ inputs.source_tag }}"
source_ref="${INPUT_SOURCE}:${INPUT_SOURCE_TAG}"
fi

# Assemble destination reference
destination_ref="${{ inputs.destination }}:${{ inputs.destination_tag }}"
destination_ref="${INPUT_DESTINATION}:${INPUT_DESTINATION_TAG}"

echo "source_ref=${source_ref}" >> "$GITHUB_OUTPUT"
echo "destination_ref=${destination_ref}" >> "$GITHUB_OUTPUT"
Expand Down
Loading