v4.2.0-rc1 #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Notes Diff | |
| on: | |
| release: | |
| types: | |
| - published | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to (re)generate notes for" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| actions: read | |
| security-events: read | |
| jobs: | |
| generate-diff: | |
| if: ${{ !github.event.release.prerelease && !github.event.release.draft }} | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| CURRENT_TAG: ${{ github.event.release.tag_name || inputs.tag }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Check for CVEs in standard k3s/k0s images | |
| id: cve-check | |
| run: | | |
| tag="${CURRENT_TAG}" | |
| # Find workflow runs for this tag from both release workflows | |
| workflow_runs=$(gh api "repos/${{ github.repository }}/actions/runs?event=push&per_page=100" \ | |
| --jq "[.workflow_runs[] | select(.head_branch == \"$tag\" and (.name | test(\"Release (AMD64|ARM)\")))]") | |
| if [ -z "$workflow_runs" ] || [ "$workflow_runs" = "[]" ]; then | |
| echo "No matching workflow runs found for tag $tag" | |
| echo "has_cve=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| cve_warning="" | |
| has_k3s_cve=false | |
| has_k0s_cve=false | |
| # Check each workflow run for failed or successful jobs with CVE indicators | |
| for run_id in $(echo "$workflow_runs" | jq -r '.[].id'); do | |
| jobs=$(gh api "repos/${{ github.repository }}/actions/runs/${run_id}/jobs?per_page=100" --jq '.jobs') | |
| # Check for standard k3s jobs with security issues (jobs that have grype/trivy and are standard) | |
| k3s_jobs=$(echo "$jobs" | jq -r '[.[] | select(.name | test("standard.*k3s"; "i"))]') | |
| k0s_jobs=$(echo "$jobs" | jq -r '[.[] | select(.name | test("standard.*k0s"; "i"))]') | |
| # For report-only mode, we need to check the job logs or annotations for CVE findings | |
| # Since we changed to report-only, jobs will succeed but may have CVE warnings in annotations | |
| for job_id in $(echo "$k3s_jobs" | jq -r '.[].id // empty'); do | |
| annotations=$(gh api "repos/${{ github.repository }}/check-runs/${job_id}/annotations" 2>/dev/null || echo "[]") | |
| if echo "$annotations" | jq -e '.[] | select(.annotation_level == "warning" and (.message | test("CVE|vulnerability|critical|high"; "i")))' > /dev/null 2>&1; then | |
| has_k3s_cve=true | |
| fi | |
| done | |
| for job_id in $(echo "$k0s_jobs" | jq -r '.[].id // empty'); do | |
| annotations=$(gh api "repos/${{ github.repository }}/check-runs/${job_id}/annotations" 2>/dev/null || echo "[]") | |
| if echo "$annotations" | jq -e '.[] | select(.annotation_level == "warning" and (.message | test("CVE|vulnerability|critical|high"; "i")))' > /dev/null 2>&1; then | |
| has_k0s_cve=true | |
| fi | |
| done | |
| done | |
| # Also check code scanning alerts for this ref | |
| alerts=$(gh api "repos/${{ github.repository }}/code-scanning/alerts?ref=refs/tags/${tag}&per_page=100" 2>/dev/null || echo "[]") | |
| if [ "$alerts" != "[]" ] && [ -n "$alerts" ]; then | |
| # Check for k3s related CVEs | |
| if echo "$alerts" | jq -e '.[] | select(.rule.description | test("k3s"; "i"))' > /dev/null 2>&1; then | |
| has_k3s_cve=true | |
| fi | |
| # Check for k0s related CVEs | |
| if echo "$alerts" | jq -e '.[] | select(.rule.description | test("k0s"; "i"))' > /dev/null 2>&1; then | |
| has_k0s_cve=true | |
| fi | |
| fi | |
| # Build warning message | |
| if [ "$has_k3s_cve" = "true" ] || [ "$has_k0s_cve" = "true" ]; then | |
| cve_warning="> [!WARNING]\n> **Security Notice:** The standard images in this release contain known CVEs from upstream components" | |
| if [ "$has_k3s_cve" = "true" ] && [ "$has_k0s_cve" = "true" ]; then | |
| cve_warning="${cve_warning} (k3s and k0s)." | |
| elif [ "$has_k3s_cve" = "true" ]; then | |
| cve_warning="${cve_warning} (k3s)." | |
| else | |
| cve_warning="${cve_warning} (k0s)." | |
| fi | |
| cve_warning="${cve_warning} These vulnerabilities originate from the Kubernetes distribution binaries and are outside our control. Please review the security scan results before deploying to production.\n" | |
| echo "has_cve=true" >> "$GITHUB_OUTPUT" | |
| # Use a delimiter to handle multiline output | |
| { | |
| echo "warning<<EOF" | |
| echo -e "$cve_warning" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_cve=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Prepend CVE warning to release notes | |
| if: ${{ steps.cve-check.outputs.has_cve == 'true' }} | |
| run: | | |
| warning_header="> [!WARNING]" | |
| gh release view "$CURRENT_TAG" --repo "${{ github.repository }}" --json body --jq '.body // ""' > current_body.md | |
| # Check if warning already exists | |
| if grep -Fq "$warning_header" current_body.md; then | |
| echo "CVE warning already present in release body. Skipping prepend." | |
| exit 0 | |
| fi | |
| { | |
| echo '${{ steps.cve-check.outputs.warning }}' | |
| cat current_body.md | |
| } > updated_body.md | |
| gh release edit "$CURRENT_TAG" --repo "${{ github.repository }}" --notes-file updated_body.md | |
| echo "CVE warning prepended to release notes." | |
| - name: Resolve previous final release tag | |
| id: prev | |
| run: | | |
| # NOTE: per_page=100 reliably times out (HTTP 504) on this repo's releases | |
| # endpoint and returns an HTML error page instead of JSON. Releases are | |
| # returned newest-first, so a smaller page is enough to find the relevant | |
| # releases. We also guard against a non-JSON response so a transient API | |
| # hiccup skips diff generation instead of failing the release. | |
| releases_json=$(gh api "repos/${{ github.repository }}/releases?per_page=50") || { | |
| echo "Could not fetch releases (API error). Skipping diff generation." | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| } | |
| if ! echo "$releases_json" | jq -e 'type == "array"' > /dev/null 2>&1; then | |
| echo "Unexpected releases response (not a JSON array). Skipping diff generation." | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Resolve the final release that immediately precedes CURRENT_TAG in the | |
| # newest-first list. This works for both the release:published trigger | |
| # (where CURRENT_TAG is the newest release) and manual workflow_dispatch | |
| # runs that target an older tag. If CURRENT_TAG is not among the recent | |
| # final releases (e.g. too old for this page), we skip rather than diff | |
| # against the wrong base. | |
| previous_tag=$(echo "$releases_json" | jq -r --arg current "$CURRENT_TAG" '[.[] | select(.draft == false and .prerelease == false) | .tag_name] as $finals | ($finals | index($current)) as $i | (if $i == null then empty else $finals[$i + 1] end) // empty') | |
| if [ -z "$previous_tag" ] || [ "$previous_tag" = "null" ]; then | |
| echo "No previous final release found for $CURRENT_TAG. Skipping diff generation." | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "Previous final release: $previous_tag" | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| echo "tag=$previous_tag" >> "$GITHUB_OUTPUT" | |
| - name: Generate release diff markdown | |
| if: ${{ steps.prev.outputs.skip == 'false' }} | |
| run: | | |
| output_file="RELEASE_DIFF_${CURRENT_TAG}.md" | |
| ./scripts/kairos-diff.sh "${{ steps.prev.outputs.tag }}" "$CURRENT_TAG" --output "$output_file" | |
| echo "output_file=$output_file" >> "$GITHUB_ENV" | |
| - name: Append diff to release description | |
| if: ${{ steps.prev.outputs.skip == 'false' }} | |
| id: append | |
| run: | | |
| header="# Changes since previous version (${{ steps.prev.outputs.tag }})" | |
| gh release view "$CURRENT_TAG" --repo "${{ github.repository }}" --json body --jq '.body // ""' > current_body.md | |
| if grep -Fq "$header" current_body.md; then | |
| echo "Header already present in release body. Skipping append." | |
| echo "appended=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| { | |
| cat current_body.md | |
| echo | |
| echo "$header" | |
| echo | |
| cat "${output_file}" | |
| } > combined_body.md | |
| gh release edit "$CURRENT_TAG" --repo "${{ github.repository }}" --notes-file combined_body.md | |
| echo "appended=true" >> "$GITHUB_OUTPUT" | |
| - name: Write workflow summary | |
| if: ${{ steps.prev.outputs.skip == 'false' }} | |
| run: | | |
| { | |
| echo "## Release diff generated" | |
| echo | |
| echo "- Current release: \`${CURRENT_TAG}\`" | |
| echo "- Previous final release: \`${{ steps.prev.outputs.tag }}\`" | |
| if [ "${{ steps.append.outputs.appended }}" = "true" ]; then | |
| echo "- Release description updated: \`yes\`" | |
| else | |
| echo "- Release description updated: \`no (already contained section header)\`" | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" |