@@ -3,8 +3,12 @@ name: Helm Render Diff
33# Informational: renders every test-values scenario on both the PR base and head,
44# diffs the resulting Kubernetes manifests, and posts a sticky PR comment so
55# reviewers can see exactly what a change does to the rendered output.
6- # Runs on GitHub-hosted runners with only public actions (public repo, no secrets
7- # beyond the automatic GITHUB_TOKEN). Never fails the PR.
6+ #
7+ # This ports the diff/report logic from comet-ml/gha-tools helm/helm-diff, but
8+ # runs self-contained on GitHub-hosted runners with public actions — that private
9+ # action runs on the self-hosted `helm` runner and can't be used from a public
10+ # repo (fork PRs can't pull a private action, and self-hosted runners must not run
11+ # fork code). Never fails the PR (the hard gate is lint-render.yaml).
812
913on :
1014 pull_request :
1418permissions :
1519 contents : read
1620 pull-requests : write
21+ issues : write
1722
1823env :
1924 KUBE_VERSION : " 1.29.0"
25+ CHART_PATH : charts/s3proxy
26+ # Match gha-tools helm-diff defaults so output is consistent with our other charts.
27+ RELEASE_NAME : release
28+ NAMESPACE : default
2029
2130jobs :
2231 render-diff :
@@ -26,13 +35,13 @@ jobs:
2635 uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
2736 with :
2837 ref : ${{ github.event.pull_request.head.sha }}
29- path : head
38+ path : pr-repo
3039
3140 - name : Checkout base
3241 uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
3342 with :
3443 ref : ${{ github.event.pull_request.base.sha }}
35- path : base
44+ path : base-repo
3645
3746 - name : Set up Helm
3847 uses : azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
@@ -42,74 +51,138 @@ jobs:
4251 - name : Render and diff
4352 run : |
4453 set -uo pipefail
54+
55+ # render <repo-dir> <values-relpath> <out-file>
56+ # Renders the chart if both the chart dir and the values file exist on
57+ # that side; otherwise writes an empty baseline (mirrors gha-tools, so a
58+ # values file that only exists on the PR shows up as fully added).
4559 render() {
46- # render <repo-dir> <values-relpath> <out-file>
4760 local dir="$1" vals="$2" out="$3"
48- if [ -f "${dir}/${vals}" ] && [ -d "${dir}/charts/s3proxy " ]
61+ if [ -f "${dir}/${vals}" ] && [ -d "${dir}/${CHART_PATH} " ]
4962 then
50- helm template s3proxy "${dir}/charts/s3proxy" \
51- --values "${dir}/${vals}" \
52- --kube-version "${KUBE_VERSION}" > "$out" 2>"${out}.err" || {
53- echo "render error for ${dir}/${vals}:"
54- cat "${out}.err"
55- : > "$out"
56- }
63+ if ! helm template "${RELEASE_NAME}" "${dir}/${CHART_PATH}" \
64+ --namespace "${NAMESPACE}" \
65+ --values "${dir}/${vals}" \
66+ --kube-version "${KUBE_VERSION}" > "$out" 2>"${out}.err"
67+ then
68+ echo "⚠️ render error for ${dir}/${vals}:"
69+ cat "${out}.err"
70+ : > "$out"
71+ fi
5772 else
5873 : > "$out"
5974 fi
6075 }
6176
6277 summary=diff-summary.md
63- echo "## 📊 Helm render diff (base vs PR)" > "$summary"
64- echo "" >> "$summary"
65- echo "_Rendered with Kubernetes ${KUBE_VERSION}. Informational only — this check never fails the PR._" >> "$summary"
78+ {
79+ echo "<!-- helm-render-diff -->"
80+ echo "## 📊 Helm Render Diff (base vs PR)"
81+ echo ""
82+ echo "Chart \`${CHART_PATH}\` rendered with Kubernetes \`${KUBE_VERSION}\`. Informational only — this check never fails the PR."
83+ } > "$summary"
84+
85+ # Union of scenario files across base and head, so a scenario removed in
86+ # the PR (present on base, absent on head) still shows up as a deletion.
87+ names="$(
88+ { ls -1 pr-repo/test-values/*.yaml base-repo/test-values/*.yaml 2>/dev/null || true; } \
89+ | xargs -r -n1 basename | sort -u
90+ )"
6691
67- changed =0
68- for f in head/test-values/*.yaml
92+ any_changes =0
93+ while IFS= read -r name
6994 do
70- name="$(basename "$f")"
71- render head "test-values/${name}" "/tmp/head-${name}"
72- render base "test-values/${name}" "/tmp/base-${name}"
73- if ! diff -u "/tmp/base-${name}" "/tmp/head-${name}" > "/tmp/diff-${name}"
95+ [ -n "$name" ] || continue
96+ render pr-repo "test-values/${name}" "/tmp/after-${name}"
97+ render base-repo "test-values/${name}" "/tmp/before-${name}"
98+
99+ diff -u "/tmp/before-${name}" "/tmp/after-${name}" > "/tmp/diff-${name}" || true
100+
101+ if [ -s "/tmp/diff-${name}" ]
74102 then
75- changed=1
103+ any_changes=1
104+ # Additions/deletions, excluding the +++/--- file headers (as gha-tools does).
105+ additions=$(grep '^+' "/tmp/diff-${name}" | grep -vc '^+++' || true)
106+ deletions=$(grep '^-' "/tmp/diff-${name}" | grep -vc '^---' || true)
76107 {
77108 echo ""
78- echo "<details><summary>🔄 <code>${name}</code></summary>"
109+ echo "### \`test-values/${name}\`"
110+ echo ""
111+ echo "- **Status**: 🔄 Changes Detected"
112+ echo "- **Changes**: +${additions} -${deletions}"
113+ echo ""
114+ echo "<details><summary>🔍 Click to view complete diff</summary>"
79115 echo ""
80116 echo '```diff'
81117 cat "/tmp/diff-${name}"
82118 echo '```'
83119 echo ""
84120 echo "</details>"
85121 } >> "$summary"
122+ else
123+ {
124+ echo ""
125+ echo "### \`test-values/${name}\`"
126+ echo ""
127+ echo "- **Status**: ✅ No Changes"
128+ } >> "$summary"
86129 fi
87- done
130+ done <<< "$names"
88131
89- if [ "$changed " -eq 0 ]
132+ if [ "$any_changes " -eq 0 ]
90133 then
91134 echo "" >> "$summary"
92- echo "_No rendered manifest changes across the test-values scenarios ._" >> "$summary"
135+ echo "_No rendered manifest changes across any scenario ._" >> "$summary"
93136 fi
94137
95- # Always publish the full diff to the job summary.
138+ # Always publish the full report to the job summary (no size limit there) .
96139 cat "$summary" >> "$GITHUB_STEP_SUMMARY"
97140
98141 # Cap the PR comment to stay well under GitHub's 65536-char limit.
99142 if [ "$(wc -c < "$summary")" -gt 60000 ]
100143 then
101144 head -c 60000 "$summary" > diff-comment.md
102- {
103- echo ""
104- echo "…diff truncated — see the full render diff in the workflow job summary."
105- } >> diff-comment.md
145+ printf '\n\n…diff truncated — see the full render diff in the workflow job summary.\n' >> diff-comment.md
106146 else
107147 cp "$summary" diff-comment.md
108148 fi
109149
110- - name : Post sticky comment
150+ # Match the repo's preview-readme.yaml convention: minimize (collapse) any
151+ # previous render-diff comments as OUTDATED, then post a fresh one. Uses
152+ # first-party actions/github-script (no third-party comment action).
153+ # continue-on-error: fork PRs get a read-only token and cannot comment; the
154+ # full diff is still available in the job summary.
155+ - name : Minimize outdated diff comments and post the latest
111156 continue-on-error : true
112- uses : marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
157+ uses : actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
113158 with :
114- header : helm-render-diff
115- path : diff-comment.md
159+ script : |
160+ const fs = require('fs');
161+ const marker = '<!-- helm-render-diff -->';
162+ const body = fs.readFileSync('diff-comment.md', 'utf8');
163+
164+ const comments = await github.paginate(github.rest.issues.listComments, {
165+ owner: context.repo.owner,
166+ repo: context.repo.repo,
167+ issue_number: context.issue.number,
168+ });
169+
170+ for (const comment of comments) {
171+ if (comment.user.type === 'Bot' && comment.body.includes(marker)) {
172+ await github.graphql(
173+ `mutation($id: ID!) {
174+ minimizeComment(input: { subjectId: $id, classifier: OUTDATED }) {
175+ clientMutationId
176+ }
177+ }`,
178+ { id: comment.node_id },
179+ );
180+ }
181+ }
182+
183+ await github.rest.issues.createComment({
184+ owner: context.repo.owner,
185+ repo: context.repo.repo,
186+ issue_number: context.issue.number,
187+ body,
188+ });
0 commit comments