@mokoconsulting/mcp-mokogitea-api 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +94 -0
- package/.gitmessage +9 -0
- package/.mokogitea/ISSUE_TEMPLATE/adr.md +110 -0
- package/.mokogitea/ISSUE_TEMPLATE/bug_report.md +48 -0
- package/.mokogitea/ISSUE_TEMPLATE/config.yml +18 -0
- package/.mokogitea/ISSUE_TEMPLATE/documentation.md +52 -0
- package/.mokogitea/ISSUE_TEMPLATE/enterprise_support.md +85 -0
- package/.mokogitea/ISSUE_TEMPLATE/feature_request.md +51 -0
- package/.mokogitea/ISSUE_TEMPLATE/firewall-request.md +190 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_api_integration.md +48 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_connection_issue.md +67 -0
- package/.mokogitea/ISSUE_TEMPLATE/mcp_tool_request.md +49 -0
- package/.mokogitea/ISSUE_TEMPLATE/question.md +82 -0
- package/.mokogitea/ISSUE_TEMPLATE/rfc.md +126 -0
- package/.mokogitea/ISSUE_TEMPLATE/security.md +51 -0
- package/.mokogitea/ISSUE_TEMPLATE/version.md +24 -0
- package/.mokogitea/auto-assign.yml +76 -0
- package/.mokogitea/auto-dev-issue.yml +207 -0
- package/.mokogitea/auto-release.yml +337 -0
- package/.mokogitea/branch-protection.yml +251 -0
- package/.mokogitea/changelog-validation.yml +101 -0
- package/.mokogitea/codeql-analysis.yml +115 -0
- package/.mokogitea/copilot-agent.yml +44 -0
- package/.mokogitea/deploy-demo.yml +734 -0
- package/.mokogitea/deploy-dev.yml +700 -0
- package/.mokogitea/enterprise-firewall-setup.yml +758 -0
- package/.mokogitea/manifest.xml +25 -0
- package/.mokogitea/mcp-auto-release.yml +278 -0
- package/.mokogitea/mcp-build-test.yml +65 -0
- package/.mokogitea/mcp-sdk-check.yml +109 -0
- package/.mokogitea/mcp-tool-inventory.yml +61 -0
- package/.mokogitea/pr-branch-check.yml +90 -0
- package/.mokogitea/repository-cleanup.yml +525 -0
- package/.mokogitea/standards-compliance.yml +2614 -0
- package/.mokogitea/sync-version-on-merge.yml +133 -0
- package/.mokogitea/workflows/auto-assign.yml +76 -0
- package/.mokogitea/workflows/auto-bump.yml +66 -0
- package/.mokogitea/workflows/auto-dev-issue.yml +207 -0
- package/.mokogitea/workflows/auto-release.yml +341 -0
- package/.mokogitea/workflows/branch-cleanup.yml +48 -0
- package/.mokogitea/workflows/cascade-dev.yml +10 -0
- package/.mokogitea/workflows/changelog-validation.yml +101 -0
- package/.mokogitea/workflows/ci-generic.yml +204 -0
- package/.mokogitea/workflows/cleanup.yml +87 -0
- package/.mokogitea/workflows/codeql-analysis.yml +115 -0
- package/.mokogitea/workflows/copilot-agent.yml +44 -0
- package/.mokogitea/workflows/deploy-manual.yml +126 -0
- package/.mokogitea/workflows/enterprise-firewall-setup.yml +758 -0
- package/.mokogitea/workflows/gitleaks.yml +96 -0
- package/.mokogitea/workflows/issue-branch.yml +73 -0
- package/.mokogitea/workflows/mcp-auto-release.yml +280 -0
- package/.mokogitea/workflows/mcp-build-test.yml +65 -0
- package/.mokogitea/workflows/mcp-sdk-check.yml +109 -0
- package/.mokogitea/workflows/mcp-tool-inventory.yml +61 -0
- package/.mokogitea/workflows/notify.yml +70 -0
- package/.mokogitea/workflows/npm-publish.yml +51 -0
- package/.mokogitea/workflows/pr-check.yml +508 -0
- package/.mokogitea/workflows/pre-release.yml +11 -0
- package/.mokogitea/workflows/repo-health.yml +711 -0
- package/.mokogitea/workflows/repository-cleanup.yml +525 -0
- package/.mokogitea/workflows/security-audit.yml +82 -0
- package/.mokogitea/workflows/standards-compliance.yml +2614 -0
- package/.mokogitea/workflows/sync-version-on-merge.yml +130 -0
- package/.mokogitea/workflows/update-server.yml +312 -0
- package/CHANGELOG.md +145 -0
- package/CLAUDE.md +43 -0
- package/CONTRIBUTING.md +161 -0
- package/README.md +286 -0
- package/SECURITY.md +91 -0
- package/automation/ci-issue-reporter.sh +237 -0
- package/config.example.json +13 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +104 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +48 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1119 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.js +16 -0
- package/package.json +34 -0
- package/scripts/setup.mjs +40 -0
- package/src/client.ts +120 -0
- package/src/config.ts +58 -0
- package/src/index.ts +1712 -0
- package/src/types.ts +37 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
#
|
|
3
|
+
# This file is part of a Moko Consulting project.
|
|
4
|
+
#
|
|
5
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
6
|
+
#
|
|
7
|
+
# FILE INFORMATION
|
|
8
|
+
# DEFGROUP: GitHub.Workflow
|
|
9
|
+
# INGROUP: MokoStandards.Maintenance
|
|
10
|
+
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
|
11
|
+
# PATH: /templates/workflows/shared/repository-cleanup.yml.template
|
|
12
|
+
# VERSION: 04.06.00
|
|
13
|
+
# BRIEF: Recurring repository maintenance — labels, branches, workflows, logs, doc indexes
|
|
14
|
+
# NOTE: Synced via bulk-repo-sync to .mokogitea/workflows/repository-cleanup.yml in all governed repos.
|
|
15
|
+
# Runs on the 1st and 15th of each month at 6:00 AM UTC, and on manual dispatch.
|
|
16
|
+
|
|
17
|
+
name: "Universal: Repository Cleanup"
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
schedule:
|
|
21
|
+
- cron: '0 6 1,15 * *'
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
inputs:
|
|
24
|
+
reset_labels:
|
|
25
|
+
description: 'Delete ALL existing labels and recreate the standard set'
|
|
26
|
+
type: boolean
|
|
27
|
+
default: false
|
|
28
|
+
clean_branches:
|
|
29
|
+
description: 'Delete old chore/sync-mokostandards-* branches'
|
|
30
|
+
type: boolean
|
|
31
|
+
default: true
|
|
32
|
+
clean_workflows:
|
|
33
|
+
description: 'Delete orphaned workflow runs (cancelled, stale)'
|
|
34
|
+
type: boolean
|
|
35
|
+
default: true
|
|
36
|
+
clean_logs:
|
|
37
|
+
description: 'Delete workflow run logs older than 30 days'
|
|
38
|
+
type: boolean
|
|
39
|
+
default: true
|
|
40
|
+
fix_templates:
|
|
41
|
+
description: 'Strip copyright comment blocks from issue templates'
|
|
42
|
+
type: boolean
|
|
43
|
+
default: true
|
|
44
|
+
rebuild_indexes:
|
|
45
|
+
description: 'Rebuild docs/ index files'
|
|
46
|
+
type: boolean
|
|
47
|
+
default: true
|
|
48
|
+
delete_closed_issues:
|
|
49
|
+
description: 'Delete issues that have been closed for more than 30 days'
|
|
50
|
+
type: boolean
|
|
51
|
+
default: false
|
|
52
|
+
|
|
53
|
+
env:
|
|
54
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
55
|
+
|
|
56
|
+
permissions:
|
|
57
|
+
contents: write
|
|
58
|
+
issues: write
|
|
59
|
+
actions: write
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
cleanup:
|
|
63
|
+
name: Repository Maintenance
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
|
|
66
|
+
steps:
|
|
67
|
+
- name: Checkout repository
|
|
68
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
69
|
+
with:
|
|
70
|
+
token: ${{ secrets.GH_TOKEN || github.token }}
|
|
71
|
+
fetch-depth: 0
|
|
72
|
+
|
|
73
|
+
- name: Check actor permission
|
|
74
|
+
env:
|
|
75
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
|
76
|
+
run: |
|
|
77
|
+
ACTOR="${{ github.actor }}"
|
|
78
|
+
# Schedule triggers use github-actions[bot]
|
|
79
|
+
if [ "${{ github.event_name }}" = "schedule" ]; then
|
|
80
|
+
echo "✅ Scheduled run — authorized"
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
AUTHORIZED_USERS="jmiller github-actions[bot]"
|
|
84
|
+
for user in $AUTHORIZED_USERS; do
|
|
85
|
+
if [ "$ACTOR" = "$user" ]; then
|
|
86
|
+
echo "✅ ${ACTOR} authorized"
|
|
87
|
+
exit 0
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
PERMISSION=$(gh api "repos/${{ github.repository }}/collaborators/${ACTOR}/permission" \
|
|
91
|
+
--jq '.permission' 2>/dev/null)
|
|
92
|
+
case "$PERMISSION" in
|
|
93
|
+
admin|maintain) echo "✅ ${ACTOR} has ${PERMISSION}" ;;
|
|
94
|
+
*) echo "❌ Admin or maintain required"; exit 1 ;;
|
|
95
|
+
esac
|
|
96
|
+
|
|
97
|
+
# ── Determine which tasks to run ─────────────────────────────────────
|
|
98
|
+
# On schedule: run all tasks with safe defaults (labels NOT reset)
|
|
99
|
+
# On dispatch: use input toggles
|
|
100
|
+
- name: Set task flags
|
|
101
|
+
id: tasks
|
|
102
|
+
run: |
|
|
103
|
+
if [ "${{ github.event_name }}" = "schedule" ]; then
|
|
104
|
+
echo "reset_labels=false" >> $GITHUB_OUTPUT
|
|
105
|
+
echo "clean_branches=true" >> $GITHUB_OUTPUT
|
|
106
|
+
echo "clean_workflows=true" >> $GITHUB_OUTPUT
|
|
107
|
+
echo "clean_logs=true" >> $GITHUB_OUTPUT
|
|
108
|
+
echo "fix_templates=true" >> $GITHUB_OUTPUT
|
|
109
|
+
echo "rebuild_indexes=true" >> $GITHUB_OUTPUT
|
|
110
|
+
echo "delete_closed_issues=false" >> $GITHUB_OUTPUT
|
|
111
|
+
else
|
|
112
|
+
echo "reset_labels=${{ inputs.reset_labels }}" >> $GITHUB_OUTPUT
|
|
113
|
+
echo "clean_branches=${{ inputs.clean_branches }}" >> $GITHUB_OUTPUT
|
|
114
|
+
echo "clean_workflows=${{ inputs.clean_workflows }}" >> $GITHUB_OUTPUT
|
|
115
|
+
echo "clean_logs=${{ inputs.clean_logs }}" >> $GITHUB_OUTPUT
|
|
116
|
+
echo "fix_templates=${{ inputs.fix_templates }}" >> $GITHUB_OUTPUT
|
|
117
|
+
echo "rebuild_indexes=${{ inputs.rebuild_indexes }}" >> $GITHUB_OUTPUT
|
|
118
|
+
echo "delete_closed_issues=${{ inputs.delete_closed_issues }}" >> $GITHUB_OUTPUT
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# ── DELETE RETIRED WORKFLOWS (always runs) ────────────────────────────
|
|
122
|
+
- name: Delete retired workflow files
|
|
123
|
+
run: |
|
|
124
|
+
echo "## 🗑️ Retired Workflow Cleanup" >> $GITHUB_STEP_SUMMARY
|
|
125
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
126
|
+
|
|
127
|
+
RETIRED=(
|
|
128
|
+
".github/workflows/build.yml"
|
|
129
|
+
".github/workflows/code-quality.yml"
|
|
130
|
+
".github/workflows/release-cycle.yml"
|
|
131
|
+
".github/workflows/release-pipeline.yml"
|
|
132
|
+
".github/workflows/branch-cleanup.yml"
|
|
133
|
+
".github/workflows/auto-update-changelog.yml"
|
|
134
|
+
".github/workflows/enterprise-issue-manager.yml"
|
|
135
|
+
".github/workflows/flush-actions-cache.yml"
|
|
136
|
+
".github/workflows/mokostandards-script-runner.yml"
|
|
137
|
+
".github/workflows/unified-ci.yml"
|
|
138
|
+
".github/workflows/unified-platform-testing.yml"
|
|
139
|
+
".github/workflows/reusable-build.yml"
|
|
140
|
+
".github/workflows/reusable-ci-validation.yml"
|
|
141
|
+
".github/workflows/reusable-deploy.yml"
|
|
142
|
+
".github/workflows/reusable-php-quality.yml"
|
|
143
|
+
".github/workflows/reusable-platform-testing.yml"
|
|
144
|
+
".github/workflows/reusable-project-detector.yml"
|
|
145
|
+
".github/workflows/reusable-release.yml"
|
|
146
|
+
".github/workflows/reusable-script-executor.yml"
|
|
147
|
+
".github/workflows/rebuild-docs-indexes.yml"
|
|
148
|
+
".github/workflows/setup-project-v2.yml"
|
|
149
|
+
".github/workflows/sync-docs-to-project.yml"
|
|
150
|
+
".github/workflows/release.yml"
|
|
151
|
+
".github/workflows/sync-changelogs.yml"
|
|
152
|
+
".github/workflows/version_branch.yml"
|
|
153
|
+
"update.json"
|
|
154
|
+
".github/workflows/auto-version-branch.yml"
|
|
155
|
+
".github/workflows/publish-to-mokodolibarr.yml"
|
|
156
|
+
".github/workflows/ci.yml"
|
|
157
|
+
".github/workflows/deploy-rs.yml"
|
|
158
|
+
"sftp-config.json"
|
|
159
|
+
"sftp-config.json.template"
|
|
160
|
+
"scripts/sftp-config"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
DELETED=0
|
|
164
|
+
for wf in "${RETIRED[@]}"; do
|
|
165
|
+
if [ -f "$wf" ]; then
|
|
166
|
+
git rm "$wf" 2>/dev/null || rm -f "$wf"
|
|
167
|
+
echo " Deleted: \`$(basename $wf)\`" >> $GITHUB_STEP_SUMMARY
|
|
168
|
+
DELETED=$((DELETED+1))
|
|
169
|
+
fi
|
|
170
|
+
done
|
|
171
|
+
|
|
172
|
+
if [ "$DELETED" -gt 0 ]; then
|
|
173
|
+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
174
|
+
git config --local user.name "github-actions[bot]"
|
|
175
|
+
git add -A
|
|
176
|
+
git commit -m "chore: delete ${DELETED} retired workflow file(s) [skip ci]" \
|
|
177
|
+
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
|
178
|
+
git push
|
|
179
|
+
echo "✅ ${DELETED} retired workflow(s) deleted" >> $GITHUB_STEP_SUMMARY
|
|
180
|
+
else
|
|
181
|
+
echo "✅ No retired workflows found" >> $GITHUB_STEP_SUMMARY
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
# ── LABEL RESET ──────────────────────────────────────────────────────
|
|
185
|
+
- name: Reset labels to standard set
|
|
186
|
+
if: steps.tasks.outputs.reset_labels == 'true'
|
|
187
|
+
env:
|
|
188
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
|
189
|
+
run: |
|
|
190
|
+
REPO="${{ github.repository }}"
|
|
191
|
+
echo "## 🏷️ Label Reset" >> $GITHUB_STEP_SUMMARY
|
|
192
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
193
|
+
|
|
194
|
+
gh api "repos/${REPO}/labels?per_page=100" --paginate --jq '.[].name' | while read -r label; do
|
|
195
|
+
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$label', safe=''))")
|
|
196
|
+
gh api -X DELETE "repos/${REPO}/labels/${ENCODED}" --silent 2>/dev/null || true
|
|
197
|
+
done
|
|
198
|
+
|
|
199
|
+
while IFS='|' read -r name color description; do
|
|
200
|
+
[ -z "$name" ] && continue
|
|
201
|
+
gh api "repos/${REPO}/labels" \
|
|
202
|
+
-f name="$name" -f color="$color" -f description="$description" \
|
|
203
|
+
--silent 2>/dev/null || true
|
|
204
|
+
done << 'LABELS'
|
|
205
|
+
joomla|7F52FF|Joomla extension or component
|
|
206
|
+
dolibarr|FF6B6B|Dolibarr module or extension
|
|
207
|
+
generic|808080|Generic project or library
|
|
208
|
+
php|4F5D95|PHP code changes
|
|
209
|
+
javascript|F7DF1E|JavaScript code changes
|
|
210
|
+
typescript|3178C6|TypeScript code changes
|
|
211
|
+
python|3776AB|Python code changes
|
|
212
|
+
css|1572B6|CSS/styling changes
|
|
213
|
+
html|E34F26|HTML template changes
|
|
214
|
+
documentation|0075CA|Documentation changes
|
|
215
|
+
ci-cd|000000|CI/CD pipeline changes
|
|
216
|
+
docker|2496ED|Docker configuration changes
|
|
217
|
+
tests|00FF00|Test suite changes
|
|
218
|
+
security|FF0000|Security-related changes
|
|
219
|
+
dependencies|0366D6|Dependency updates
|
|
220
|
+
config|F9D0C4|Configuration file changes
|
|
221
|
+
build|FFA500|Build system changes
|
|
222
|
+
automation|8B4513|Automated processes or scripts
|
|
223
|
+
mokostandards|B60205|MokoStandards compliance
|
|
224
|
+
needs-review|FBCA04|Awaiting code review
|
|
225
|
+
work-in-progress|D93F0B|Work in progress, not ready for merge
|
|
226
|
+
breaking-change|D73A4A|Breaking API or functionality change
|
|
227
|
+
priority: critical|B60205|Critical priority, must be addressed immediately
|
|
228
|
+
priority: high|D93F0B|High priority
|
|
229
|
+
priority: medium|FBCA04|Medium priority
|
|
230
|
+
priority: low|0E8A16|Low priority
|
|
231
|
+
type: bug|D73A4A|Something isn't working
|
|
232
|
+
type: feature|A2EEEF|New feature or request
|
|
233
|
+
type: enhancement|84B6EB|Enhancement to existing feature
|
|
234
|
+
type: refactor|F9D0C4|Code refactoring
|
|
235
|
+
type: chore|FEF2C0|Maintenance tasks
|
|
236
|
+
type: version|0E8A16|Version-related change
|
|
237
|
+
status: pending|FBCA04|Pending action or decision
|
|
238
|
+
status: in-progress|0E8A16|Currently being worked on
|
|
239
|
+
status: blocked|B60205|Blocked by another issue or dependency
|
|
240
|
+
status: on-hold|D4C5F9|Temporarily on hold
|
|
241
|
+
status: wontfix|FFFFFF|This will not be worked on
|
|
242
|
+
size/xs|C5DEF5|Extra small change (1-10 lines)
|
|
243
|
+
size/s|6FD1E2|Small change (11-30 lines)
|
|
244
|
+
size/m|F9DD72|Medium change (31-100 lines)
|
|
245
|
+
size/l|FFA07A|Large change (101-300 lines)
|
|
246
|
+
size/xl|FF6B6B|Extra large change (301-1000 lines)
|
|
247
|
+
size/xxl|B60205|Extremely large change (1000+ lines)
|
|
248
|
+
health: excellent|0E8A16|Health score 90-100
|
|
249
|
+
health: good|FBCA04|Health score 70-89
|
|
250
|
+
health: fair|FFA500|Health score 50-69
|
|
251
|
+
health: poor|FF6B6B|Health score below 50
|
|
252
|
+
standards-update|B60205|MokoStandards sync update
|
|
253
|
+
standards-drift|FBCA04|Repository drifted from MokoStandards
|
|
254
|
+
sync-report|0075CA|Bulk sync run report
|
|
255
|
+
sync-failure|D73A4A|Bulk sync failure requiring attention
|
|
256
|
+
push-failure|D73A4A|File push failure requiring attention
|
|
257
|
+
health-check|0E8A16|Repository health check results
|
|
258
|
+
version-drift|FFA500|Version mismatch detected
|
|
259
|
+
deploy-failure|CC0000|Automated deploy failure tracking
|
|
260
|
+
template-validation-failure|D73A4A|Template workflow validation failure
|
|
261
|
+
version|0E8A16|Version bump or release
|
|
262
|
+
LABELS
|
|
263
|
+
|
|
264
|
+
echo "✅ Standard labels created" >> $GITHUB_STEP_SUMMARY
|
|
265
|
+
|
|
266
|
+
# ── BRANCH CLEANUP ───────────────────────────────────────────────────
|
|
267
|
+
- name: Delete old sync branches
|
|
268
|
+
if: steps.tasks.outputs.clean_branches == 'true'
|
|
269
|
+
env:
|
|
270
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
|
271
|
+
run: |
|
|
272
|
+
REPO="${{ github.repository }}"
|
|
273
|
+
CURRENT="chore/sync-mokostandards-v04.05"
|
|
274
|
+
echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY
|
|
275
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
276
|
+
|
|
277
|
+
FOUND=false
|
|
278
|
+
gh api "repos/${REPO}/branches?per_page=100" --jq '.[].name' | \
|
|
279
|
+
grep "^chore/sync-mokostandards" | \
|
|
280
|
+
grep -v "^${CURRENT}$" | while read -r branch; do
|
|
281
|
+
gh pr list --repo "$REPO" --head "$branch" --state open --json number --jq '.[].number' 2>/dev/null | while read -r pr; do
|
|
282
|
+
gh pr close "$pr" --repo "$REPO" --comment "Superseded by \`${CURRENT}\`" 2>/dev/null || true
|
|
283
|
+
echo " Closed PR #${pr}" >> $GITHUB_STEP_SUMMARY
|
|
284
|
+
done
|
|
285
|
+
gh api -X DELETE "repos/${REPO}/git/refs/heads/${branch}" --silent 2>/dev/null || true
|
|
286
|
+
echo " Deleted: \`${branch}\`" >> $GITHUB_STEP_SUMMARY
|
|
287
|
+
FOUND=true
|
|
288
|
+
done
|
|
289
|
+
|
|
290
|
+
if [ "$FOUND" != "true" ]; then
|
|
291
|
+
echo "✅ No old sync branches found" >> $GITHUB_STEP_SUMMARY
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
# ── WORKFLOW RUN CLEANUP ─────────────────────────────────────────────
|
|
295
|
+
- name: Clean up workflow runs
|
|
296
|
+
if: steps.tasks.outputs.clean_workflows == 'true'
|
|
297
|
+
env:
|
|
298
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
|
299
|
+
run: |
|
|
300
|
+
REPO="${{ github.repository }}"
|
|
301
|
+
echo "## 🔄 Workflow Run Cleanup" >> $GITHUB_STEP_SUMMARY
|
|
302
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
303
|
+
|
|
304
|
+
DELETED=0
|
|
305
|
+
# Delete cancelled and stale workflow runs
|
|
306
|
+
for status in cancelled stale; do
|
|
307
|
+
gh api "repos/${REPO}/actions/runs?status=${status}&per_page=100" \
|
|
308
|
+
--jq '.workflow_runs[].id' 2>/dev/null | while read -r run_id; do
|
|
309
|
+
gh api -X DELETE "repos/${REPO}/actions/runs/${run_id}" --silent 2>/dev/null || true
|
|
310
|
+
DELETED=$((DELETED+1))
|
|
311
|
+
done
|
|
312
|
+
done
|
|
313
|
+
|
|
314
|
+
echo "✅ Cleaned cancelled/stale workflow runs" >> $GITHUB_STEP_SUMMARY
|
|
315
|
+
|
|
316
|
+
# ── LOG CLEANUP ──────────────────────────────────────────────────────
|
|
317
|
+
- name: Delete old workflow run logs
|
|
318
|
+
if: steps.tasks.outputs.clean_logs == 'true'
|
|
319
|
+
env:
|
|
320
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
|
321
|
+
run: |
|
|
322
|
+
REPO="${{ github.repository }}"
|
|
323
|
+
CUTOFF=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
|
324
|
+
echo "## 📋 Log Cleanup" >> $GITHUB_STEP_SUMMARY
|
|
325
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
326
|
+
echo "Deleting logs older than: ${CUTOFF}" >> $GITHUB_STEP_SUMMARY
|
|
327
|
+
|
|
328
|
+
DELETED=0
|
|
329
|
+
gh api "repos/${REPO}/actions/runs?created=<${CUTOFF}&per_page=100" \
|
|
330
|
+
--jq '.workflow_runs[].id' 2>/dev/null | while read -r run_id; do
|
|
331
|
+
gh api -X DELETE "repos/${REPO}/actions/runs/${run_id}/logs" --silent 2>/dev/null || true
|
|
332
|
+
DELETED=$((DELETED+1))
|
|
333
|
+
done
|
|
334
|
+
|
|
335
|
+
echo "✅ Cleaned old workflow run logs" >> $GITHUB_STEP_SUMMARY
|
|
336
|
+
|
|
337
|
+
# ── ISSUE TEMPLATE FIX ──────────────────────────────────────────────
|
|
338
|
+
- name: Strip copyright headers from issue templates
|
|
339
|
+
if: steps.tasks.outputs.fix_templates == 'true'
|
|
340
|
+
run: |
|
|
341
|
+
echo "## 📋 Issue Template Cleanup" >> $GITHUB_STEP_SUMMARY
|
|
342
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
343
|
+
|
|
344
|
+
FIXED=0
|
|
345
|
+
for f in .github/ISSUE_TEMPLATE/*.md; do
|
|
346
|
+
[ -f "$f" ] || continue
|
|
347
|
+
if grep -q '^<!--$' "$f"; then
|
|
348
|
+
sed -i '/^<!--$/,/^-->$/d' "$f"
|
|
349
|
+
echo " Cleaned: \`$(basename $f)\`" >> $GITHUB_STEP_SUMMARY
|
|
350
|
+
FIXED=$((FIXED+1))
|
|
351
|
+
fi
|
|
352
|
+
done
|
|
353
|
+
|
|
354
|
+
if [ "$FIXED" -gt 0 ]; then
|
|
355
|
+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
356
|
+
git config --local user.name "github-actions[bot]"
|
|
357
|
+
git add .github/ISSUE_TEMPLATE/
|
|
358
|
+
git commit -m "fix: strip copyright comment blocks from issue templates [skip ci]" \
|
|
359
|
+
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
|
360
|
+
git push
|
|
361
|
+
echo "✅ ${FIXED} template(s) cleaned and committed" >> $GITHUB_STEP_SUMMARY
|
|
362
|
+
else
|
|
363
|
+
echo "✅ No templates need cleaning" >> $GITHUB_STEP_SUMMARY
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
# ── REBUILD DOC INDEXES ─────────────────────────────────────────────
|
|
367
|
+
- name: Rebuild docs/ index files
|
|
368
|
+
if: steps.tasks.outputs.rebuild_indexes == 'true'
|
|
369
|
+
run: |
|
|
370
|
+
echo "## 📚 Documentation Index Rebuild" >> $GITHUB_STEP_SUMMARY
|
|
371
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
372
|
+
|
|
373
|
+
if [ ! -d "docs" ]; then
|
|
374
|
+
echo "⏭️ No docs/ directory — skipping" >> $GITHUB_STEP_SUMMARY
|
|
375
|
+
exit 0
|
|
376
|
+
fi
|
|
377
|
+
|
|
378
|
+
UPDATED=0
|
|
379
|
+
# Generate index.md for each docs/ subdirectory
|
|
380
|
+
find docs -type d | while read -r dir; do
|
|
381
|
+
INDEX="${dir}/index.md"
|
|
382
|
+
FILES=$(find "$dir" -maxdepth 1 -name "*.md" ! -name "index.md" -printf "- [%f](./%f)\n" 2>/dev/null | sort)
|
|
383
|
+
if [ -z "$FILES" ]; then
|
|
384
|
+
continue
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
cat > "$INDEX" << INDEXEOF
|
|
388
|
+
# $(basename "$dir")
|
|
389
|
+
|
|
390
|
+
## Documents
|
|
391
|
+
|
|
392
|
+
${FILES}
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
*Auto-generated by repository-cleanup workflow*
|
|
396
|
+
INDEXEOF
|
|
397
|
+
# Dedent
|
|
398
|
+
sed -i 's/^ //' "$INDEX"
|
|
399
|
+
UPDATED=$((UPDATED+1))
|
|
400
|
+
done
|
|
401
|
+
|
|
402
|
+
if [ "$UPDATED" -gt 0 ]; then
|
|
403
|
+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
404
|
+
git config --local user.name "github-actions[bot]"
|
|
405
|
+
git add docs/
|
|
406
|
+
if ! git diff --cached --quiet; then
|
|
407
|
+
git commit -m "docs: rebuild documentation indexes [skip ci]" \
|
|
408
|
+
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
|
409
|
+
git push
|
|
410
|
+
echo "✅ ${UPDATED} index file(s) rebuilt and committed" >> $GITHUB_STEP_SUMMARY
|
|
411
|
+
else
|
|
412
|
+
echo "✅ All indexes already up to date" >> $GITHUB_STEP_SUMMARY
|
|
413
|
+
fi
|
|
414
|
+
else
|
|
415
|
+
echo "✅ No indexes to rebuild" >> $GITHUB_STEP_SUMMARY
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
# ── VERSION DRIFT DETECTION ──────────────────────────────────────────
|
|
419
|
+
- name: Check for version drift
|
|
420
|
+
run: |
|
|
421
|
+
echo "## 📦 Version Drift Check" >> $GITHUB_STEP_SUMMARY
|
|
422
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
423
|
+
|
|
424
|
+
if [ ! -f "README.md" ]; then
|
|
425
|
+
echo "⏭️ No README.md — skipping" >> $GITHUB_STEP_SUMMARY
|
|
426
|
+
exit 0
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
README_VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md 2>/dev/null | head -1)
|
|
430
|
+
if [ -z "$README_VERSION" ]; then
|
|
431
|
+
echo "⚠️ No VERSION found in README.md FILE INFORMATION block" >> $GITHUB_STEP_SUMMARY
|
|
432
|
+
exit 0
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
echo "**README version:** \`${README_VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
|
436
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
437
|
+
|
|
438
|
+
DRIFT=0
|
|
439
|
+
CHECKED=0
|
|
440
|
+
|
|
441
|
+
# Check all files with FILE INFORMATION blocks
|
|
442
|
+
while IFS= read -r -d '' file; do
|
|
443
|
+
FILE_VERSION=$(grep -oP '^\s*\*?\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' "$file" 2>/dev/null | head -1)
|
|
444
|
+
[ -z "$FILE_VERSION" ] && continue
|
|
445
|
+
CHECKED=$((CHECKED+1))
|
|
446
|
+
if [ "$FILE_VERSION" != "$README_VERSION" ]; then
|
|
447
|
+
echo " ⚠️ \`${file}\`: \`${FILE_VERSION}\` (expected \`${README_VERSION}\`)" >> $GITHUB_STEP_SUMMARY
|
|
448
|
+
DRIFT=$((DRIFT+1))
|
|
449
|
+
fi
|
|
450
|
+
done < <(find . -maxdepth 4 -type f \( -name "*.php" -o -name "*.md" -o -name "*.yml" \) ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -print0 2>/dev/null)
|
|
451
|
+
|
|
452
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
453
|
+
if [ "$DRIFT" -gt 0 ]; then
|
|
454
|
+
echo "⚠️ **${DRIFT}** file(s) out of ${CHECKED} have version drift" >> $GITHUB_STEP_SUMMARY
|
|
455
|
+
echo "Run \`sync-version-on-merge\` workflow or update manually" >> $GITHUB_STEP_SUMMARY
|
|
456
|
+
else
|
|
457
|
+
echo "✅ All ${CHECKED} file(s) match README version \`${README_VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
|
458
|
+
fi
|
|
459
|
+
|
|
460
|
+
# ── PROTECT CUSTOM WORKFLOWS ────────────────────────────────────────
|
|
461
|
+
- name: Ensure custom workflow directory exists
|
|
462
|
+
run: |
|
|
463
|
+
echo "## 🔧 Custom Workflows" >> $GITHUB_STEP_SUMMARY
|
|
464
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
465
|
+
|
|
466
|
+
if [ ! -d ".github/workflows/custom" ]; then
|
|
467
|
+
mkdir -p .github/workflows/custom
|
|
468
|
+
cat > .github/workflows/custom/README.md << 'CWEOF'
|
|
469
|
+
# Custom Workflows
|
|
470
|
+
|
|
471
|
+
Place repo-specific workflows here. Files in this directory are:
|
|
472
|
+
- **Never overwritten** by MokoStandards bulk sync
|
|
473
|
+
- **Never deleted** by the repository-cleanup workflow
|
|
474
|
+
- Safe for custom CI, notifications, or repo-specific automation
|
|
475
|
+
|
|
476
|
+
Synced workflows live in `.github/workflows/` (parent directory).
|
|
477
|
+
CWEOF
|
|
478
|
+
sed -i 's/^ //' .github/workflows/custom/README.md
|
|
479
|
+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
480
|
+
git config --local user.name "github-actions[bot]"
|
|
481
|
+
git add .github/workflows/custom/
|
|
482
|
+
if ! git diff --cached --quiet; then
|
|
483
|
+
git commit -m "chore: create .github/workflows/custom/ for repo-specific workflows [skip ci]" \
|
|
484
|
+
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
|
485
|
+
git push
|
|
486
|
+
echo "✅ Created \`.github/workflows/custom/\` directory" >> $GITHUB_STEP_SUMMARY
|
|
487
|
+
fi
|
|
488
|
+
else
|
|
489
|
+
CUSTOM_COUNT=$(find .github/workflows/custom -name "*.yml" -o -name "*.yaml" 2>/dev/null | wc -l)
|
|
490
|
+
echo "✅ Custom workflow directory exists (${CUSTOM_COUNT} workflow(s))" >> $GITHUB_STEP_SUMMARY
|
|
491
|
+
fi
|
|
492
|
+
|
|
493
|
+
# ── DELETE CLOSED ISSUES ──────────────────────────────────────────────
|
|
494
|
+
- name: Delete old closed issues
|
|
495
|
+
if: steps.tasks.outputs.delete_closed_issues == 'true'
|
|
496
|
+
env:
|
|
497
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
|
498
|
+
run: |
|
|
499
|
+
REPO="${{ github.repository }}"
|
|
500
|
+
CUTOFF=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
|
501
|
+
echo "## 🗑️ Closed Issue Cleanup" >> $GITHUB_STEP_SUMMARY
|
|
502
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
503
|
+
echo "Deleting issues closed before: ${CUTOFF}" >> $GITHUB_STEP_SUMMARY
|
|
504
|
+
|
|
505
|
+
DELETED=0
|
|
506
|
+
gh api "repos/${REPO}/issues?state=closed&since=1970-01-01T00:00:00Z&per_page=100&sort=updated&direction=asc" \
|
|
507
|
+
--jq ".[] | select(.closed_at < \"${CUTOFF}\") | .number" 2>/dev/null | while read -r num; do
|
|
508
|
+
# Lock and close with "not_planned" to mark as cleaned up
|
|
509
|
+
gh api "repos/${REPO}/issues/${num}/lock" -X PUT -f lock_reason="resolved" --silent 2>/dev/null || true
|
|
510
|
+
echo " Locked issue #${num}" >> $GITHUB_STEP_SUMMARY
|
|
511
|
+
DELETED=$((DELETED+1))
|
|
512
|
+
done
|
|
513
|
+
|
|
514
|
+
if [ "$DELETED" -eq 0 ] 2>/dev/null; then
|
|
515
|
+
echo "✅ No old closed issues found" >> $GITHUB_STEP_SUMMARY
|
|
516
|
+
else
|
|
517
|
+
echo "✅ Locked ${DELETED} old closed issue(s)" >> $GITHUB_STEP_SUMMARY
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
- name: Summary
|
|
521
|
+
if: always()
|
|
522
|
+
run: |
|
|
523
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
524
|
+
echo "---" >> $GITHUB_STEP_SUMMARY
|
|
525
|
+
echo "*Run by @${{ github.actor }} — trigger: ${{ github.event_name }}*" >> $GITHUB_STEP_SUMMARY
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# FILE INFORMATION
|
|
6
|
+
# DEFGROUP: Gitea.Workflow
|
|
7
|
+
# INGROUP: MokoStandards.Security
|
|
8
|
+
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
|
9
|
+
# PATH: /.gitea/workflows/security-audit.yml
|
|
10
|
+
# VERSION: 01.00.00
|
|
11
|
+
# BRIEF: Dependency vulnerability scanning for composer and npm packages
|
|
12
|
+
|
|
13
|
+
name: "Universal: Security Audit"
|
|
14
|
+
|
|
15
|
+
on:
|
|
16
|
+
schedule:
|
|
17
|
+
- cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC
|
|
18
|
+
pull_request:
|
|
19
|
+
branches:
|
|
20
|
+
- main
|
|
21
|
+
paths:
|
|
22
|
+
- 'composer.json'
|
|
23
|
+
- 'composer.lock'
|
|
24
|
+
- 'package.json'
|
|
25
|
+
- 'package-lock.json'
|
|
26
|
+
workflow_dispatch:
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
|
|
31
|
+
env:
|
|
32
|
+
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
|
|
33
|
+
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
|
|
34
|
+
|
|
35
|
+
jobs:
|
|
36
|
+
audit:
|
|
37
|
+
name: Dependency Audit
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- name: Composer audit
|
|
45
|
+
if: hashFiles('composer.lock') != ''
|
|
46
|
+
run: |
|
|
47
|
+
echo "=== Composer Security Audit ==="
|
|
48
|
+
if ! command -v composer &> /dev/null; then
|
|
49
|
+
sudo apt-get update -qq
|
|
50
|
+
sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1
|
|
51
|
+
fi
|
|
52
|
+
composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt
|
|
53
|
+
RESULT=$?
|
|
54
|
+
if [ $RESULT -ne 0 ]; then
|
|
55
|
+
echo "::warning::Composer vulnerabilities found"
|
|
56
|
+
echo "composer_vulnerable=true" >> "$GITHUB_ENV"
|
|
57
|
+
else
|
|
58
|
+
echo "No known vulnerabilities in composer dependencies"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: NPM audit
|
|
62
|
+
if: hashFiles('package-lock.json') != ''
|
|
63
|
+
run: |
|
|
64
|
+
echo "=== NPM Security Audit ==="
|
|
65
|
+
npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true
|
|
66
|
+
if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then
|
|
67
|
+
echo "No known vulnerabilities in npm dependencies"
|
|
68
|
+
else
|
|
69
|
+
echo "::warning::NPM vulnerabilities found"
|
|
70
|
+
echo "npm_vulnerable=true" >> "$GITHUB_ENV"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
- name: Notify on vulnerabilities
|
|
74
|
+
if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true'
|
|
75
|
+
run: |
|
|
76
|
+
REPO="${{ github.event.repository.name }}"
|
|
77
|
+
curl -sS \
|
|
78
|
+
-H "Title: ${REPO} has vulnerable dependencies" \
|
|
79
|
+
-H "Tags: lock,warning" \
|
|
80
|
+
-H "Priority: high" \
|
|
81
|
+
-d "Security audit found vulnerabilities. Review dependency updates." \
|
|
82
|
+
"${NTFY_URL}/${NTFY_TOPIC}" || true
|