2424 type : string
2525 default : actions
2626 stale-days :
27- description : Mark branches/issues stale after this many days
27+ description : Mark branches/pull requests/ issues stale after this many days
2828 type : number
2929 default : 60
30- issue-title :
31- description : Issue title for weekly report
32- type : string
33- default : (Weekly) Repository health report
34- issue-labels :
35- description : Comma-separated labels for weekly report issue
36- type : string
37- default : automation,dependencies
3830 secrets :
3931 DOCKER_TOKEN :
4032 required : false
4133
4234permissions :
43- contents : read
44- issues : write
45- pull-requests : read
35+ contents : write
36+ pull-requests : write
4637 packages : write
38+ issues : read
4739
4840jobs :
4941 dependency-check :
@@ -60,11 +52,11 @@ jobs:
6052 uses : arduino/setup-task@v2.0.0
6153 with :
6254 version : ${{ inputs.task-version }}
55+ repo-token : ${{ secrets.GITHUB_TOKEN }}
6356
6457 - name : Prepare report workspace
6558 run : |
66- mkdir -p .tmp
67- : > .tmp/findings.md
59+ REPORT_FILE="${RUNNER_TEMP}/weekly-health-report.md"
6860 {
6961 echo "## Weekly Health Report"
7062 echo ""
7365 echo "- Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
7466 echo "- Generated: $(date -u +'%Y-%m-%dT%H:%M:%SZ')"
7567 echo ""
76- echo "### Findings"
77- } > .tmp/report.md
68+ echo "### Important findings"
69+ } > "$REPORT_FILE"
70+ echo "REPORT_FILE=$REPORT_FILE" >> "$GITHUB_ENV"
71+ echo "HAS_FINDINGS=false" >> "$GITHUB_ENV"
7872
7973 - name : Run lint
8074 id : lint
8478
8579 - name : Record lint findings
8680 if : inputs.enable-lint && steps.lint.outcome != 'success'
87- run : echo "- [lint] \`task lint\` failed" >> .tmp/findings.md
88-
89- - name : Validate baseline
90- id : baseline
91- continue-on-error : true
9281 run : |
93- set -eu
94- [ -f Taskfile.yml ] || { echo "Taskfile.yml missing"; exit 1; }
95- [ -f .github/workflows/auto-create-pull-request.yml ] || { echo "Missing workflow: auto-create-pull-request.yml"; exit 1; }
96- [ -f .github/workflows/cron-check-dependencies.yml ] || { echo "Missing workflow: cron-check-dependencies.yml"; exit 1; }
97- [ -f .github/workflows/manual-update-version.yml ] || { echo "Missing workflow: manual-update-version.yml"; exit 1; }
98- if grep -R -n -E "uses:[[:space:]]+.+@master" .github/workflows >/dev/null 2>&1; then
99- echo "Found @master workflow references in caller workflows"
100- exit 1
101- fi
102-
103- - name : Record baseline findings
104- if : steps.baseline.outcome != 'success'
105- run : echo "- [baseline] Required files are missing or caller workflows still reference \`@master\`" >> .tmp/findings.md
82+ echo "- lint failed: \`task lint\`" >> "$REPORT_FILE"
83+ echo "HAS_FINDINGS=true" >> "$GITHUB_ENV"
10684
10785 - name : Run dependency checks
10886 id : deps
@@ -124,7 +102,9 @@ jobs:
124102
125103 - name : Record dependency findings
126104 if : steps.deps.outcome != 'success'
127- run : echo "- [dependencies] Dependency check reported updates or failed" >> .tmp/findings.md
105+ run : |
106+ echo "- dependency checks reported updates or failed" >> "$REPORT_FILE"
107+ echo "HAS_FINDINGS=true" >> "$GITHUB_ENV"
128108
129109 - name : Install Docker Buildx
130110 if : inputs.profile == 'actions' || inputs.profile == 'dockerized'
@@ -149,11 +129,60 @@ jobs:
149129 task docker:push
150130 task docker:push:inspect
151131
132+ - name : Detect container structure test configs
133+ id : cst-configs
134+ if : inputs.profile == 'actions' || inputs.profile == 'dockerized'
135+ continue-on-error : true
136+ run : |
137+ shopt -s nullglob
138+ files=(tests/docker/*.yml tests/docker/*.yaml)
139+ if [ "${#files[@]}" -eq 0 ]; then
140+ echo "has_tests=false" >> "$GITHUB_OUTPUT"
141+ exit 0
142+ fi
143+ printf '%s\n' "${files[@]}" | sort > "$RUNNER_TEMP/cst-configs.txt"
144+ {
145+ echo "has_tests=true"
146+ echo "config<<EOF"
147+ cat "$RUNNER_TEMP/cst-configs.txt"
148+ echo "EOF"
149+ } >> "$GITHUB_OUTPUT"
150+
151+ - name : Resolve CST image
152+ id : cst-image
153+ if : (inputs.profile == 'actions' || inputs.profile == 'dockerized') && steps.cst-configs.outputs.has_tests == 'true'
154+ continue-on-error : true
155+ run : |
156+ if task --list | grep -q "docker:image:test:ref"; then
157+ IMAGE_REF="$(task docker:image:test:ref)"
158+ else
159+ VERSION="$(task version:get)"
160+ IMAGE_REF="devopsinfra/${{ github.event.repository.name }}:${VERSION}-test"
161+ fi
162+ echo "image=$IMAGE_REF" >> "$GITHUB_OUTPUT"
163+
164+ - name : Run container structure tests
165+ id : cst
166+ if : (inputs.profile == 'actions' || inputs.profile == 'dockerized') && steps.cst-configs.outputs.has_tests == 'true'
167+ continue-on-error : true
168+ uses : devops-infra/action-container-structure-test@v1
169+ with :
170+ image : ${{ steps.cst-image.outputs.image }}
171+ config : ${{ steps.cst-configs.outputs.config }}
172+
152173 - name : Record docker findings
153174 if : (inputs.profile == 'actions' || inputs.profile == 'dockerized') && steps.docker.outcome != 'success'
154- run : echo "- [docker] Docker validation failed (build/push/inspect)" >> .tmp/findings.md
175+ run : |
176+ echo "- docker build/push/inspect failed" >> "$REPORT_FILE"
177+ echo "HAS_FINDINGS=true" >> "$GITHUB_ENV"
178+
179+ - name : Record container structure test findings
180+ if : (inputs.profile == 'actions' || inputs.profile == 'dockerized') && steps.cst-configs.outputs.has_tests == 'true' && steps.cst.outcome != 'success'
181+ run : |
182+ echo "- container structure tests failed for tests/docker configs" >> "$REPORT_FILE"
183+ echo "HAS_FINDINGS=true" >> "$GITHUB_ENV"
155184
156- - name : Detect stale branches and issues
185+ - name : Detect stale branches, pull requests, and issues
157186 id : stale
158187 uses : actions/github-script@v9
159188 with :
@@ -170,6 +199,12 @@ jobs:
170199 const commitDate = new Date(commit.data.commit.committer.date).getTime()
171200 if (now - commitDate > staleMs) staleBranches.push(branch.name)
172201 }
202+ const stalePullRequests = []
203+ const pullRequests = await github.paginate(github.rest.pulls.list, { owner: context.repo.owner, repo: context.repo.repo, state: 'open', per_page: 100 })
204+ for (const pullRequest of pullRequests) {
205+ const updated = new Date(pullRequest.updated_at).getTime()
206+ if (now - updated > staleMs) stalePullRequests.push(`#${pullRequest.number}`)
207+ }
173208 const staleIssues = []
174209 const issues = await github.paginate(github.rest.issues.listForRepo, { owner: context.repo.owner, repo: context.repo.repo, state: 'open', per_page: 100 })
175210 for (const issue of issues) {
@@ -178,57 +213,67 @@ jobs:
178213 if (now - updated > staleMs) staleIssues.push(`#${issue.number}`)
179214 }
180215 core.setOutput('stale_branch_count', String(staleBranches.length))
216+ core.setOutput('stale_pr_count', String(stalePullRequests.length))
181217 core.setOutput('stale_issue_count', String(staleIssues.length))
182218 core.setOutput('stale_branches', staleBranches.slice(0, 20).join(', '))
219+ core.setOutput('stale_prs', stalePullRequests.slice(0, 20).join(', '))
183220 core.setOutput('stale_issues', staleIssues.slice(0, 20).join(', '))
184221
185222 - name : Record stale findings
186- if : steps.stale.outputs.stale_branch_count != '0' || steps.stale.outputs.stale_issue_count != '0'
223+ if : steps.stale.outputs.stale_branch_count != '0' || steps.stale.outputs.stale_pr_count != '0' || steps.stale.outputs. stale_issue_count != '0'
187224 run : |
188225 if [ "${{ steps.stale.outputs.stale_branch_count }}" != "0" ]; then
189- echo "- [stale-branches] Found ${{ steps.stale.outputs.stale_branch_count }} stale branches: ${{ steps.stale.outputs.stale_branches }}" >> .tmp/findings.md
226+ echo "- stale branches (${{ steps.stale.outputs.stale_branch_count }}): ${{ steps.stale.outputs.stale_branches }}" >> "$REPORT_FILE"
227+ fi
228+ if [ "${{ steps.stale.outputs.stale_pr_count }}" != "0" ]; then
229+ echo "- stale pull requests (${{ steps.stale.outputs.stale_pr_count }}): ${{ steps.stale.outputs.stale_prs }}" >> "$REPORT_FILE"
190230 fi
191231 if [ "${{ steps.stale.outputs.stale_issue_count }}" != "0" ]; then
192- echo "- [ stale- issues] Found ${{ steps.stale.outputs.stale_issue_count }} stale issues : ${{ steps.stale.outputs.stale_issues }}" >> .tmp/findings.md
232+ echo "- stale issues ( ${{ steps.stale.outputs.stale_issue_count }}) : ${{ steps.stale.outputs.stale_issues }}" >> "$REPORT_FILE"
193233 fi
234+ echo "HAS_FINDINGS=true" >> "$GITHUB_ENV"
194235
195236 - name : Finalize report
196237 id : report
197238 run : |
198- if [ -s .tmp/findings.md ]; then
199- cat .tmp/findings.md >> .tmp/report.md
200- echo "status=issues" >> "$GITHUB_OUTPUT"
239+ if [ "$HAS_FINDINGS" != "true" ]; then
240+ echo "- no important updates or breaking changes detected" >> "$REPORT_FILE"
241+ fi
242+
243+ - name : Detect repository changes
244+ id : changes
245+ run : |
246+ if [ -n "$(git status --porcelain)" ]; then
247+ echo "has_changes=true" >> "$GITHUB_OUTPUT"
201248 else
202- echo "- No findings. Repository matches current baseline." >> .tmp/report.md
203- echo "status=clean" >> "$GITHUB_OUTPUT"
249+ echo "has_changes=false" >> "$GITHUB_OUTPUT"
204250 fi
205251
206- - name : Create or update weekly issue
207- uses : actions/github-script@v9
208- env :
209- ISSUE_TITLE : ${{ inputs.issue-title }}
210- ISSUE_LABELS : ${{ inputs.issue-labels }}
211- REPORT_STATUS : ${{ steps.report.outputs.status }}
252+ - name : Commit and push changes
253+ id : commit
254+ if : steps.changes.outputs.has_changes == 'true'
255+ run : |
256+ git config user.name "github-actions[bot]"
257+ git config user.email "github-actions[bot]@users.noreply.github.com"
258+ BRANCH_NAME="chore/weekly-health-${GITHUB_RUN_ID}"
259+ git checkout -B "$BRANCH_NAME"
260+ git add -A
261+ git commit -m "chore: weekly dependency and health updates" -m "$(cat "$REPORT_FILE")"
262+ git push --set-upstream origin "$BRANCH_NAME"
263+ {
264+ echo "branch_name=$BRANCH_NAME"
265+ echo "report_body<<EOF"
266+ cat "$REPORT_FILE"
267+ echo "EOF"
268+ } >> "$GITHUB_OUTPUT"
269+
270+ - name : Create pull request
271+ if : steps.changes.outputs.has_changes == 'true'
272+ uses : devops-infra/action-pull-request@v1
212273 with :
213- script : |
214- const fs = require('fs')
215- const title = process.env.ISSUE_TITLE
216- const labels = process.env.ISSUE_LABELS.split(',').map(v => v.trim()).filter(Boolean)
217- const status = process.env.REPORT_STATUS
218- const body = fs.readFileSync('.tmp/report.md', 'utf8')
219- const repoLabels = await github.paginate(github.rest.issues.listLabelsForRepo, { owner: context.repo.owner, repo: context.repo.repo, per_page: 100 })
220- const repoLabelSet = new Set(repoLabels.map(l => l.name))
221- const safeLabels = labels.filter(l => repoLabelSet.has(l))
222- const openIssues = await github.paginate(github.rest.issues.listForRepo, { owner: context.repo.owner, repo: context.repo.repo, state: 'open', per_page: 100 })
223- const existing = openIssues.find(i => i.title === title)
224- if (status === 'clean') {
225- if (existing) {
226- await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: existing.number, body, state: 'closed', state_reason: 'completed' })
227- }
228- return
229- }
230- if (existing) {
231- await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: existing.number, body })
232- } else {
233- await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title, body, labels: safeLabels })
234- }
274+ github_token : ${{ github.token }}
275+ source_branch : ${{ steps.commit.outputs.branch_name }}
276+ target_branch : ${{ github.event.repository.default_branch }}
277+ title : " chore: weekly dependency and health updates"
278+ body : ${{ steps.commit.outputs.report_body }}
279+ assignee : ${{ github.actor }}
0 commit comments