@boringstudio_org/gitea-mcp 1.7.1 → 1.9.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/.gitea/workflows/release.yaml +41 -24
- package/.gitea/workflows/security.yaml +611 -0
- package/.pre-commit-config.yaml +22 -0
- package/CHANGELOG.md +72 -22
- package/README.md +11 -2
- package/RELEASE.md +48 -0
- package/index.js +10 -3
- package/package.json +13 -8
- package/renovate.json +31 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name: Release
|
|
1
|
+
name: Auto Release
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
@@ -8,42 +8,59 @@ on:
|
|
|
8
8
|
jobs:
|
|
9
9
|
release:
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
|
-
if: "!contains(github.event.head_commit.message, 'chore(release)')"
|
|
12
11
|
steps:
|
|
13
|
-
-
|
|
14
|
-
uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/checkout@v4
|
|
15
13
|
with:
|
|
16
14
|
fetch-depth: 0
|
|
17
|
-
token: ${{ secrets.RELEASE_TOKEN }}
|
|
18
15
|
|
|
19
|
-
- name:
|
|
16
|
+
- name: Check if release is needed
|
|
17
|
+
id: check
|
|
20
18
|
run: |
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
20
|
+
TAG="v$VERSION"
|
|
21
|
+
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
|
22
|
+
echo "Tag $TAG already exists. Skipping release."
|
|
23
|
+
echo "release=false" >> $GITHUB_OUTPUT
|
|
24
|
+
else
|
|
25
|
+
echo "New version $VERSION detected. Proceeding with release."
|
|
26
|
+
echo "release=true" >> $GITHUB_OUTPUT
|
|
27
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
28
|
+
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
29
|
+
fi
|
|
23
30
|
|
|
24
31
|
- name: Setup Node.js
|
|
32
|
+
if: steps.check.outputs.release == 'true'
|
|
25
33
|
uses: actions/setup-node@v4
|
|
26
34
|
with:
|
|
27
|
-
node-version: '24'
|
|
35
|
+
node-version: '24.14.0'
|
|
28
36
|
registry-url: 'https://registry.npmjs.org'
|
|
29
37
|
|
|
30
|
-
- name:
|
|
38
|
+
- name: Install & Publish
|
|
39
|
+
if: steps.check.outputs.release == 'true'
|
|
31
40
|
run: |
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
npm ci
|
|
42
|
+
npm publish
|
|
43
|
+
env:
|
|
44
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
34
45
|
|
|
35
|
-
- name:
|
|
36
|
-
|
|
46
|
+
- name: Create & Push Tag
|
|
47
|
+
if: steps.check.outputs.release == 'true'
|
|
48
|
+
env:
|
|
49
|
+
TAG: ${{ steps.check.outputs.tag }}
|
|
50
|
+
TOKEN: ${{ secrets.RENOVATE_TOKEN }}
|
|
51
|
+
CF_ID: ${{ secrets.CF_CLIENT_ID }}
|
|
52
|
+
CF_SECRET: ${{ secrets.CF_CLIENT_SECRET }}
|
|
53
|
+
run: |
|
|
54
|
+
git config user.name "Gitea Actions"
|
|
55
|
+
git config user.email "actions@boringstudio.by"
|
|
37
56
|
|
|
38
|
-
|
|
39
|
-
|
|
57
|
+
# Configure Cloudflare Access Headers
|
|
58
|
+
git config --global http.extraHeader "CF-Access-Client-Id: $CF_ID"
|
|
59
|
+
git config --global --add http.extraHeader "CF-Access-Client-Secret: $CF_SECRET"
|
|
40
60
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
env:
|
|
44
|
-
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
61
|
+
# Configure Auth
|
|
62
|
+
git remote set-url origin https://$TOKEN@git.boringstudio.by/BoringStudio/mcp-gitea.git
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
64
|
+
# Create and Push Tag
|
|
65
|
+
git tag $TAG
|
|
66
|
+
git push origin $TAG
|
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
name: "🔒 Mandatory Security Check"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
pull_request:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
# 1. Check Changes (Smart Skip)
|
|
15
|
+
changes:
|
|
16
|
+
runs-on: "ubuntu-latest"
|
|
17
|
+
outputs:
|
|
18
|
+
js: ${{ steps.diff.outputs.js }}
|
|
19
|
+
php: ${{ steps.diff.outputs.php }}
|
|
20
|
+
docker: ${{ steps.diff.outputs.docker }}
|
|
21
|
+
shell: ${{ steps.diff.outputs.shell }}
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout code
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
28
|
+
|
|
29
|
+
- name: Check modified files
|
|
30
|
+
id: diff
|
|
31
|
+
run: |
|
|
32
|
+
if [ "${{ github.event_name }}" == "pull_request" ]; then
|
|
33
|
+
BASE=${{ github.event.pull_request.base.sha }}
|
|
34
|
+
HEAD=${{ github.event.pull_request.head.sha }}
|
|
35
|
+
else
|
|
36
|
+
BASE=${{ github.event.before }}
|
|
37
|
+
HEAD=${{ github.sha }}
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Handle first commit or force push
|
|
41
|
+
if [ -z "$BASE" ] || [ "$BASE" == "0000000000000000000000000000000000000000" ]; then
|
|
42
|
+
BASE=$(git rev-parse HEAD~1 2>/dev/null || echo $HEAD)
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo "Checking diff between $BASE and $HEAD"
|
|
46
|
+
DIFF=$(git diff --name-only $BASE $HEAD)
|
|
47
|
+
echo "$DIFF"
|
|
48
|
+
|
|
49
|
+
# JS/TS
|
|
50
|
+
if echo "$DIFF" | grep -qE "\.(js|ts|tsx|jsx|json)$"; then
|
|
51
|
+
echo "js=true" >> $GITHUB_OUTPUT
|
|
52
|
+
else
|
|
53
|
+
echo "js=false" >> $GITHUB_OUTPUT
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# PHP
|
|
57
|
+
if echo "$DIFF" | grep -qE "\.(php|json|lock)$"; then
|
|
58
|
+
echo "php=true" >> $GITHUB_OUTPUT
|
|
59
|
+
else
|
|
60
|
+
echo "php=false" >> $GITHUB_OUTPUT
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Docker
|
|
64
|
+
if echo "$DIFF" | grep -qE "Dockerfile"; then
|
|
65
|
+
echo "docker=true" >> $GITHUB_OUTPUT
|
|
66
|
+
else
|
|
67
|
+
echo "docker=false" >> $GITHUB_OUTPUT
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# Shell
|
|
71
|
+
if echo "$DIFF" | grep -qE "\.sh$"; then
|
|
72
|
+
echo "shell=true" >> $GITHUB_OUTPUT
|
|
73
|
+
else
|
|
74
|
+
echo "shell=false" >> $GITHUB_OUTPUT
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# 2. Gitleaks: Secrets Detection (Gatekeeper)
|
|
78
|
+
gitleaks:
|
|
79
|
+
runs-on: "ubuntu-latest"
|
|
80
|
+
outputs:
|
|
81
|
+
log: ${{ steps.scan.outputs.log }}
|
|
82
|
+
steps:
|
|
83
|
+
- name: Checkout code
|
|
84
|
+
uses: actions/checkout@v4
|
|
85
|
+
with:
|
|
86
|
+
fetch-depth: 0
|
|
87
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
88
|
+
|
|
89
|
+
- name: 🧹 Gitleaks (Run & Report)
|
|
90
|
+
id: scan
|
|
91
|
+
run: |
|
|
92
|
+
C_NAME="gitleaks-${{ github.run_id }}"
|
|
93
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
94
|
+
|
|
95
|
+
# Use custom config if exists, otherwise fallback to default
|
|
96
|
+
CONFIG_PATH=".gitleaks.toml"
|
|
97
|
+
CONFIG_FLAG=""
|
|
98
|
+
if [ -f "$CONFIG_PATH" ]; then
|
|
99
|
+
CONFIG_FLAG="--config=$CONFIG_PATH"
|
|
100
|
+
echo "Using custom gitleaks config: $CONFIG_PATH"
|
|
101
|
+
else
|
|
102
|
+
echo "Custom gitleaks config not found, using default rules."
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
docker create --name $C_NAME -w /code zricethezav/gitleaks:v8.30.0 detect --source=. $CONFIG_FLAG --verbose --redact --no-banner
|
|
106
|
+
docker cp . $C_NAME:/code
|
|
107
|
+
docker start -a $C_NAME > gitleaks.log 2>&1 || EXIT=$?
|
|
108
|
+
sed -i 's/\x1b\[[0-9;]*m//g' gitleaks.log
|
|
109
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
110
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
111
|
+
cat gitleaks.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
112
|
+
echo '' >> $GITHUB_OUTPUT
|
|
113
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
114
|
+
fi
|
|
115
|
+
docker rm -f $C_NAME
|
|
116
|
+
exit ${EXIT:-0}
|
|
117
|
+
|
|
118
|
+
# --- Sequential Jobs ---
|
|
119
|
+
|
|
120
|
+
# 3. Semgrep: SAST
|
|
121
|
+
semgrep:
|
|
122
|
+
runs-on: "ubuntu-latest"
|
|
123
|
+
needs: [gitleaks, changes]
|
|
124
|
+
outputs:
|
|
125
|
+
log: ${{ steps.scan.outputs.log }}
|
|
126
|
+
steps:
|
|
127
|
+
- name: Checkout code
|
|
128
|
+
uses: actions/checkout@v4
|
|
129
|
+
with:
|
|
130
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
131
|
+
|
|
132
|
+
- name: Checkout Custom Rules
|
|
133
|
+
uses: actions/checkout@v4
|
|
134
|
+
with:
|
|
135
|
+
repository: BoringStudio/.gitea
|
|
136
|
+
path: .gitea-config
|
|
137
|
+
sparse-checkout: .gitea/semgrep-rules
|
|
138
|
+
token: ${{ secrets.RENOVATE_TOKEN }}
|
|
139
|
+
|
|
140
|
+
- name: 🔬 Semgrep (Run & Report)
|
|
141
|
+
id: scan
|
|
142
|
+
run: |
|
|
143
|
+
set +e
|
|
144
|
+
C_NAME="semgrep-${{ github.run_id }}"
|
|
145
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
146
|
+
# Create container
|
|
147
|
+
docker create --name $C_NAME -w /src returntocorp/semgrep:1.151.0 semgrep scan --config=p/default --config=p/typescript --config=p/nodejsscan --config=p/php --config=p/docker --config=p/owasp-top-ten --config=.gitea-config/.gitea/semgrep-rules --exclude='node_modules/' --exclude='vendor/' --exclude='.strapi/' --exclude='dist/' --exclude='build/' --exclude='wp-admin/' --exclude='wp-includes/' --exclude='wordpress/wp-content/plugins/' --exclude='wordpress/wp-content/themes/' --exclude='tests/' --exclude='*_test.go' --error --verbose
|
|
148
|
+
|
|
149
|
+
# Copy files into container explicitly
|
|
150
|
+
docker cp . $C_NAME:/src/
|
|
151
|
+
if [ -d ".gitea-config" ]; then
|
|
152
|
+
docker cp .gitea-config $C_NAME:/src/
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Run and capture output
|
|
156
|
+
docker start -a $C_NAME > semgrep.log 2>&1
|
|
157
|
+
EXIT=$?
|
|
158
|
+
sed -i 's/\x1b\[[0-9;]*m//g' semgrep.log
|
|
159
|
+
|
|
160
|
+
if [ "$EXIT" -ne 0 ]; then
|
|
161
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
162
|
+
cat semgrep.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
163
|
+
echo '' >> $GITHUB_OUTPUT
|
|
164
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
165
|
+
fi
|
|
166
|
+
docker rm -f $C_NAME
|
|
167
|
+
exit $EXIT
|
|
168
|
+
|
|
169
|
+
# 4. Hadolint: Dockerfile Linting
|
|
170
|
+
hadolint:
|
|
171
|
+
runs-on: "ubuntu-latest"
|
|
172
|
+
needs: [semgrep, changes]
|
|
173
|
+
if: needs.changes.outputs.docker == 'true'
|
|
174
|
+
outputs:
|
|
175
|
+
log: ${{ steps.scan.outputs.log }}
|
|
176
|
+
steps:
|
|
177
|
+
- name: Checkout code
|
|
178
|
+
uses: actions/checkout@v4
|
|
179
|
+
with:
|
|
180
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
181
|
+
|
|
182
|
+
- name: 🐳 Hadolint (Run & Report)
|
|
183
|
+
id: scan
|
|
184
|
+
run: |
|
|
185
|
+
C_NAME="hadolint-${{ github.run_id }}"
|
|
186
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
187
|
+
docker create --name $C_NAME -w /code hadolint/hadolint:latest-debian /bin/bash -c "{ find . -name 'Dockerfile*' -not -path '*/.*' -print0 | xargs -0 -r hadolint; }"
|
|
188
|
+
docker cp . $C_NAME:/code
|
|
189
|
+
docker start -a $C_NAME > hadolint.log 2>&1 || EXIT=$?
|
|
190
|
+
sed -i 's/\x1b\[[0-9;]*m//g' hadolint.log
|
|
191
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
192
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
193
|
+
cat hadolint.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
194
|
+
echo '' >> $GITHUB_OUTPUT
|
|
195
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
196
|
+
fi
|
|
197
|
+
docker rm -f $C_NAME
|
|
198
|
+
exit ${EXIT:-0}
|
|
199
|
+
|
|
200
|
+
# 5. ShellCheck: Bash Script Linting
|
|
201
|
+
shellcheck:
|
|
202
|
+
runs-on: "ubuntu-latest"
|
|
203
|
+
needs: [hadolint, changes]
|
|
204
|
+
if: needs.changes.outputs.shell == 'true'
|
|
205
|
+
outputs:
|
|
206
|
+
log: ${{ steps.scan.outputs.log }}
|
|
207
|
+
steps:
|
|
208
|
+
- name: Checkout code
|
|
209
|
+
uses: actions/checkout@v4
|
|
210
|
+
with:
|
|
211
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
212
|
+
|
|
213
|
+
- name: 🐚 ShellCheck (Run & Report)
|
|
214
|
+
id: scan
|
|
215
|
+
run: |
|
|
216
|
+
C_NAME="shellcheck-${{ github.run_id }}"
|
|
217
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
218
|
+
docker create --name $C_NAME -w /code koalaman/shellcheck-alpine:v0.11.0 /bin/sh -c "{ find . -name '*.sh' -not -path '*/.*' -print0 | xargs -0 -r shellcheck --color=always; }"
|
|
219
|
+
docker cp . $C_NAME:/code
|
|
220
|
+
docker start -a $C_NAME > shellcheck.log 2>&1 || EXIT=$?
|
|
221
|
+
sed -i 's/\x1b\[[0-9;]*m//g' shellcheck.log
|
|
222
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
223
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
224
|
+
cat shellcheck.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
225
|
+
echo '' >> $GITHUB_OUTPUT
|
|
226
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
227
|
+
fi
|
|
228
|
+
docker rm -f $C_NAME
|
|
229
|
+
exit ${EXIT:-0}
|
|
230
|
+
|
|
231
|
+
# 6. Checkov: IaC Security
|
|
232
|
+
checkov-scan:
|
|
233
|
+
runs-on: "ubuntu-latest"
|
|
234
|
+
needs: [shellcheck]
|
|
235
|
+
outputs:
|
|
236
|
+
log: ${{ steps.scan.outputs.log }}
|
|
237
|
+
steps:
|
|
238
|
+
- name: Checkout code
|
|
239
|
+
uses: actions/checkout@v4
|
|
240
|
+
with:
|
|
241
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
242
|
+
|
|
243
|
+
- name: 🏗️ Checkov (Run & Report)
|
|
244
|
+
id: scan
|
|
245
|
+
run: |
|
|
246
|
+
C_NAME="checkov-${{ github.run_id }}"
|
|
247
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
248
|
+
docker create --name $C_NAME -w /code bridgecrew/checkov:3.2.502 /bin/bash -c "{ checkov -d . --quiet --compact --skip-check CKV_DOCKER_2,CKV_DOCKER_3; }"
|
|
249
|
+
docker cp . $C_NAME:/code
|
|
250
|
+
docker start -a $C_NAME > checkov.log 2>&1 || EXIT=$?
|
|
251
|
+
sed -i 's/\x1b\[[0-9;]*m//g' checkov.log
|
|
252
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
253
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
254
|
+
cat checkov.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
255
|
+
echo '' >> $GITHUB_OUTPUT
|
|
256
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
257
|
+
fi
|
|
258
|
+
docker rm -f $C_NAME
|
|
259
|
+
exit ${EXIT:-0}
|
|
260
|
+
|
|
261
|
+
# 7. Trivy: Dependencies & Misconfig
|
|
262
|
+
trivy:
|
|
263
|
+
runs-on: "ubuntu-latest"
|
|
264
|
+
needs: [checkov-scan]
|
|
265
|
+
outputs:
|
|
266
|
+
log: ${{ steps.scan.outputs.log }}
|
|
267
|
+
steps:
|
|
268
|
+
- name: Checkout code
|
|
269
|
+
uses: actions/checkout@v4
|
|
270
|
+
with:
|
|
271
|
+
fetch-depth: 0
|
|
272
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
273
|
+
|
|
274
|
+
- name: Checkout Custom Rules
|
|
275
|
+
uses: actions/checkout@v4
|
|
276
|
+
with:
|
|
277
|
+
repository: BoringStudio/.gitea
|
|
278
|
+
path: .gitea-config
|
|
279
|
+
sparse-checkout: .gitea/trivy.yaml
|
|
280
|
+
token: ${{ secrets.RENOVATE_TOKEN }}
|
|
281
|
+
|
|
282
|
+
- name: Cache Trivy DB
|
|
283
|
+
uses: actions/cache@v4
|
|
284
|
+
with:
|
|
285
|
+
path: .trivy-cache
|
|
286
|
+
key: trivy-db-${{ github.run_id }}
|
|
287
|
+
restore-keys: |
|
|
288
|
+
trivy-db-
|
|
289
|
+
|
|
290
|
+
- name: 🛡️ Trivy (Run & Report)
|
|
291
|
+
id: scan
|
|
292
|
+
run: |
|
|
293
|
+
C_NAME="trivy-${{ github.run_id }}"
|
|
294
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
295
|
+
docker create --name $C_NAME -w /code aquasec/trivy:0.69.1 fs --config .gitea-config/.gitea/trivy.yaml --cache-dir /root/.cache --scanners vuln,misconfig,license --severity CRITICAL,HIGH --skip-dirs .git --exit-code 1 --no-progress .
|
|
296
|
+
docker cp . $C_NAME:/code
|
|
297
|
+
docker start -a $C_NAME > trivy.log 2>&1 || EXIT=$?
|
|
298
|
+
|
|
299
|
+
# Clean log from ANSI colors and limit size
|
|
300
|
+
sed -i 's/\x1b\[[0-9;]*m//g' trivy.log
|
|
301
|
+
|
|
302
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
303
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
304
|
+
cat trivy.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
305
|
+
echo '' >> $GITHUB_OUTPUT
|
|
306
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
307
|
+
fi
|
|
308
|
+
docker rm -f $C_NAME
|
|
309
|
+
exit ${EXIT:-0}
|
|
310
|
+
|
|
311
|
+
# 7.1 Composer Audit: PHP Security
|
|
312
|
+
composer-audit:
|
|
313
|
+
runs-on: "ubuntu-latest"
|
|
314
|
+
needs: [trivy, changes]
|
|
315
|
+
if: needs.changes.outputs.php == 'true'
|
|
316
|
+
outputs:
|
|
317
|
+
log: ${{ steps.scan.outputs.log }}
|
|
318
|
+
steps:
|
|
319
|
+
- name: Checkout code
|
|
320
|
+
uses: actions/checkout@v4
|
|
321
|
+
with:
|
|
322
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
323
|
+
|
|
324
|
+
- name: 🐘 Composer Audit (Run & Report)
|
|
325
|
+
id: scan
|
|
326
|
+
run: |
|
|
327
|
+
if [ -f composer.lock ]; then
|
|
328
|
+
C_NAME="composer-audit-${{ github.run_id }}"
|
|
329
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
330
|
+
docker create --name $C_NAME -w /code composer:2 composer audit
|
|
331
|
+
docker cp . $C_NAME:/code
|
|
332
|
+
docker start -a $C_NAME > composer.log 2>&1 || EXIT=$?
|
|
333
|
+
sed -i 's/\x1b\[[0-9;]*m//g' composer.log
|
|
334
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
335
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
336
|
+
cat composer.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
337
|
+
echo '' >> $GITHUB_OUTPUT
|
|
338
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
339
|
+
fi
|
|
340
|
+
docker rm -f $C_NAME
|
|
341
|
+
exit ${EXIT:-0}
|
|
342
|
+
else
|
|
343
|
+
echo "No composer.lock found, skipping audit."
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# 8. Lint JS/TS (ESLint)
|
|
347
|
+
lint-js:
|
|
348
|
+
runs-on: "ubuntu-latest"
|
|
349
|
+
needs: [composer-audit, changes]
|
|
350
|
+
if: needs.changes.outputs.js == 'true'
|
|
351
|
+
outputs:
|
|
352
|
+
log: ${{ steps.lint.outputs.log }}
|
|
353
|
+
steps:
|
|
354
|
+
- uses: actions/checkout@v4
|
|
355
|
+
with:
|
|
356
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
357
|
+
|
|
358
|
+
- name: Lint JS/TS
|
|
359
|
+
id: lint
|
|
360
|
+
run: |
|
|
361
|
+
if [ -f package.json ]; then
|
|
362
|
+
C_NAME="js-lint-${{ github.run_id }}"
|
|
363
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
364
|
+
# Create container
|
|
365
|
+
docker create --name $C_NAME -w /app node:22-alpine /bin/sh -c "if [ -f package-lock.json ]; then npm ci; else npm install; fi && if npm run | grep -q 'lint'; then npm run lint; else echo 'No lint script found'; fi"
|
|
366
|
+
|
|
367
|
+
# Copy code
|
|
368
|
+
docker cp . $C_NAME:/app
|
|
369
|
+
|
|
370
|
+
# Run
|
|
371
|
+
docker start -a $C_NAME > lint-js.log 2>&1 || EXIT=$?
|
|
372
|
+
sed -i 's/\x1b\[[0-9;]*m//g' lint-js.log
|
|
373
|
+
|
|
374
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
375
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
376
|
+
cat lint-js.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
377
|
+
echo '' >> $GITHUB_OUTPUT
|
|
378
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
379
|
+
fi
|
|
380
|
+
docker rm -f $C_NAME
|
|
381
|
+
exit ${EXIT:-0}
|
|
382
|
+
else
|
|
383
|
+
echo "No package.json, skipping"
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
# 9. Lint PHP (PHP-CS-Fixer)
|
|
387
|
+
lint-php:
|
|
388
|
+
runs-on: "ubuntu-latest"
|
|
389
|
+
needs: [lint-js, changes]
|
|
390
|
+
if: needs.changes.outputs.php == 'true'
|
|
391
|
+
outputs:
|
|
392
|
+
log: ${{ steps.lint.outputs.log }}
|
|
393
|
+
steps:
|
|
394
|
+
- uses: actions/checkout@v4
|
|
395
|
+
with:
|
|
396
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
397
|
+
|
|
398
|
+
- name: Lint PHP
|
|
399
|
+
id: lint
|
|
400
|
+
run: |
|
|
401
|
+
if [ -f composer.json ]; then
|
|
402
|
+
C_NAME="php-lint-${{ github.run_id }}"
|
|
403
|
+
docker rm -f $C_NAME 2>/dev/null || true
|
|
404
|
+
# Create container
|
|
405
|
+
docker create --name $C_NAME -w /app composer:2 /bin/sh -c "composer install --no-progress --prefer-dist && if [ -f vendor/bin/php-cs-fixer ]; then vendor/bin/php-cs-fixer fix --dry-run --diff; else echo 'No php-cs-fixer found'; fi"
|
|
406
|
+
|
|
407
|
+
# Copy code
|
|
408
|
+
docker cp . $C_NAME:/app
|
|
409
|
+
|
|
410
|
+
# Run
|
|
411
|
+
docker start -a $C_NAME > lint-php.log 2>&1 || EXIT=$?
|
|
412
|
+
sed -i 's/\x1b\[[0-9;]*m//g' lint-php.log
|
|
413
|
+
|
|
414
|
+
if [ -n "$EXIT" ] && [ "$EXIT" -ne 0 ]; then
|
|
415
|
+
echo 'log<<EOF' >> $GITHUB_OUTPUT
|
|
416
|
+
cat lint-php.log | tail -c 10000 >> $GITHUB_OUTPUT
|
|
417
|
+
echo '' >> $GITHUB_OUTPUT
|
|
418
|
+
echo 'EOF' >> $GITHUB_OUTPUT
|
|
419
|
+
fi
|
|
420
|
+
docker rm -f $C_NAME
|
|
421
|
+
exit ${EXIT:-0}
|
|
422
|
+
else
|
|
423
|
+
echo "No composer.json, skipping"
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
# --- Notification & Summary ---
|
|
427
|
+
notify:
|
|
428
|
+
runs-on: "ubuntu-latest"
|
|
429
|
+
if: always() && !cancelled()
|
|
430
|
+
needs: [gitleaks, semgrep, hadolint, shellcheck, checkov-scan, trivy, composer-audit, lint-js, lint-php]
|
|
431
|
+
steps:
|
|
432
|
+
- name: Generate Summary Table
|
|
433
|
+
env:
|
|
434
|
+
LOG_GITLEAKS: ${{ needs.gitleaks.outputs.log }}
|
|
435
|
+
LOG_SEMGREP: ${{ needs.semgrep.outputs.log }}
|
|
436
|
+
LOG_HADOLINT: ${{ needs.hadolint.outputs.log }}
|
|
437
|
+
LOG_SHELLCHECK: ${{ needs.shellcheck.outputs.log }}
|
|
438
|
+
LOG_CHECKOV: ${{ needs.checkov-scan.outputs.log }}
|
|
439
|
+
LOG_TRIVY: ${{ needs.trivy.outputs.log }}
|
|
440
|
+
LOG_COMPOSER: ${{ needs.composer-audit.outputs.log }}
|
|
441
|
+
LOG_LINT_JS: ${{ needs.lint-js.outputs.log }}
|
|
442
|
+
LOG_LINT_PHP: ${{ needs.lint-php.outputs.log }}
|
|
443
|
+
run: |
|
|
444
|
+
SUMMARY_FILE="/tmp/security_summary.md"
|
|
445
|
+
DETAILS_FILE="/tmp/security_details.md"
|
|
446
|
+
|
|
447
|
+
echo "# 🔒 Security & Quality Report" > $SUMMARY_FILE
|
|
448
|
+
echo "" >> $SUMMARY_FILE
|
|
449
|
+
echo "| Check | Status |" >> $SUMMARY_FILE
|
|
450
|
+
echo "| :--- | :--- |" >> $SUMMARY_FILE
|
|
451
|
+
|
|
452
|
+
# Initialize details file
|
|
453
|
+
echo "" > $DETAILS_FILE
|
|
454
|
+
echo "### 🔍 Failure Details" >> $DETAILS_FILE
|
|
455
|
+
echo "" >> $DETAILS_FILE
|
|
456
|
+
|
|
457
|
+
clean_log() {
|
|
458
|
+
local log="$1"
|
|
459
|
+
log=$(echo "$log" | sed 's/\x1b\[[0-9;]*m//g')
|
|
460
|
+
echo "$log"
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
print_row() {
|
|
464
|
+
local job=$1
|
|
465
|
+
local status=$2
|
|
466
|
+
local log_var=$3
|
|
467
|
+
local log_content="${!log_var}"
|
|
468
|
+
local log=$(clean_log "$log_content")
|
|
469
|
+
local icon="❓"
|
|
470
|
+
|
|
471
|
+
if [[ "$status" == "success" ]]; then icon="✅ Success"; fi
|
|
472
|
+
if [[ "$status" == "failure" ]]; then
|
|
473
|
+
icon="❌ Failure"
|
|
474
|
+
echo "<details><summary><strong>$job</strong> Output</summary>" >> $DETAILS_FILE
|
|
475
|
+
echo "" >> $DETAILS_FILE
|
|
476
|
+
echo "\`\`\`" >> $DETAILS_FILE
|
|
477
|
+
echo "$log" >> $DETAILS_FILE
|
|
478
|
+
echo "\`\`\`" >> $DETAILS_FILE
|
|
479
|
+
echo "" >> $DETAILS_FILE
|
|
480
|
+
echo "</details>" >> $DETAILS_FILE
|
|
481
|
+
echo "" >> $DETAILS_FILE
|
|
482
|
+
fi
|
|
483
|
+
if [[ "$status" == "skipped" ]]; then icon="⏭️ Skipped"; fi
|
|
484
|
+
|
|
485
|
+
echo "| **$job** | $icon |" >> $SUMMARY_FILE
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
print_row "Gitleaks" "${{ needs.gitleaks.result }}" "LOG_GITLEAKS"
|
|
489
|
+
print_row "Semgrep" "${{ needs.semgrep.result }}" "LOG_SEMGREP"
|
|
490
|
+
print_row "Hadolint" "${{ needs.hadolint.result }}" "LOG_HADOLINT"
|
|
491
|
+
print_row "ShellCheck" "${{ needs.shellcheck.result }}" "LOG_SHELLCHECK"
|
|
492
|
+
print_row "Checkov" "${{ needs.checkov-scan.result }}" "LOG_CHECKOV"
|
|
493
|
+
print_row "Trivy" "${{ needs.trivy.result }}" "LOG_TRIVY"
|
|
494
|
+
print_row "Composer" "${{ needs.composer-audit.result }}" "LOG_COMPOSER"
|
|
495
|
+
print_row "Lint JS" "${{ needs.lint-js.result }}" "LOG_LINT_JS"
|
|
496
|
+
print_row "Lint PHP" "${{ needs.lint-php.result }}" "LOG_LINT_PHP"
|
|
497
|
+
|
|
498
|
+
cat $DETAILS_FILE >> $SUMMARY_FILE
|
|
499
|
+
cat $SUMMARY_FILE >> $GITHUB_STEP_SUMMARY
|
|
500
|
+
|
|
501
|
+
- name: Post Comment to PR
|
|
502
|
+
if: always() && github.event_name == 'pull_request'
|
|
503
|
+
env:
|
|
504
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
505
|
+
API_URL: ${{ github.api_url }}
|
|
506
|
+
REPO: ${{ github.repository }}
|
|
507
|
+
PR_NUMBER: ${{ github.event.number }}
|
|
508
|
+
S_GITLEAKS: ${{ needs.gitleaks.result }}
|
|
509
|
+
S_SEMGREP: ${{ needs.semgrep.result }}
|
|
510
|
+
S_HADOLINT: ${{ needs.hadolint.result }}
|
|
511
|
+
S_SHELLCHECK: ${{ needs.shellcheck.result }}
|
|
512
|
+
S_CHECKOV: ${{ needs.checkov-scan.result }}
|
|
513
|
+
S_TRIVY: ${{ needs.trivy.result }}
|
|
514
|
+
S_COMPOSER: ${{ needs.composer-audit.result }}
|
|
515
|
+
S_LINT_JS: ${{ needs.lint-js.result }}
|
|
516
|
+
S_LINT_PHP: ${{ needs.lint-php.result }}
|
|
517
|
+
run: |
|
|
518
|
+
if [ -z "$PR_NUMBER" ]; then exit 0; fi
|
|
519
|
+
|
|
520
|
+
if [[ "$S_GITLEAKS" == "failure" ]] || [[ "$S_SEMGREP" == "failure" ]] || [[ "$S_HADOLINT" == "failure" ]] || [[ "$S_SHELLCHECK" == "failure" ]] || [[ "$S_CHECKOV" == "failure" ]] || [[ "$S_TRIVY" == "failure" ]] || [[ "$S_COMPOSER" == "failure" ]] || [[ "$S_LINT_JS" == "failure" ]] || [[ "$S_LINT_PHP" == "failure" ]]; then
|
|
521
|
+
TITLE="### 🛡️ Checks Failed ❌"
|
|
522
|
+
else
|
|
523
|
+
TITLE="### 🛡️ Checks Passed ✅"
|
|
524
|
+
fi
|
|
525
|
+
|
|
526
|
+
echo "$TITLE" > /tmp/comment_body.md
|
|
527
|
+
echo "" >> /tmp/comment_body.md
|
|
528
|
+
if [ -f "/tmp/security_summary.md" ]; then
|
|
529
|
+
cat /tmp/security_summary.md >> /tmp/comment_body.md
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
python3 -c 'import json, sys; print(json.dumps({"body": sys.stdin.read()}))' < /tmp/comment_body.md > /tmp/comment.json
|
|
533
|
+
|
|
534
|
+
curl -s -X POST "$API_URL/repos/$REPO/issues/$PR_NUMBER/comments" \
|
|
535
|
+
-H "Authorization: token $GITHUB_TOKEN" \
|
|
536
|
+
-H "Content-Type: application/json" \
|
|
537
|
+
-d @/tmp/comment.json
|
|
538
|
+
|
|
539
|
+
- name: Send Telegram Notification
|
|
540
|
+
if: always()
|
|
541
|
+
env:
|
|
542
|
+
TG_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
|
|
543
|
+
TG_TO: ${{ secrets.TELEGRAM_TO }}
|
|
544
|
+
TG_THREAD_ID: ${{ secrets.TELEGRAM_THREAD_ID }}
|
|
545
|
+
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
|
546
|
+
REPO: ${{ github.repository }}
|
|
547
|
+
BRANCH: ${{ github.ref_name }}
|
|
548
|
+
ACTOR: ${{ github.actor }}
|
|
549
|
+
SERVER_URL: https://git.boringstudio.by
|
|
550
|
+
RUN_ID: ${{ github.run_id }}
|
|
551
|
+
SHA: ${{ github.sha }}
|
|
552
|
+
# Statuses
|
|
553
|
+
S_GITLEAKS: ${{ needs.gitleaks.result }}
|
|
554
|
+
S_SEMGREP: ${{ needs.semgrep.result }}
|
|
555
|
+
S_HADOLINT: ${{ needs.hadolint.result }}
|
|
556
|
+
S_SHELLCHECK: ${{ needs.shellcheck.result }}
|
|
557
|
+
S_CHECKOV: ${{ needs.checkov-scan.result }}
|
|
558
|
+
S_TRIVY: ${{ needs.trivy.result }}
|
|
559
|
+
S_COMPOSER: ${{ needs.composer-audit.result }}
|
|
560
|
+
S_LINT_JS: ${{ needs.lint-js.result }}
|
|
561
|
+
S_LINT_PHP: ${{ needs.lint-php.result }}
|
|
562
|
+
run: |
|
|
563
|
+
# Determine Overall Status
|
|
564
|
+
if [[ "$S_GITLEAKS" == "failure" ]] || [[ "$S_SEMGREP" == "failure" ]] || [[ "$S_HADOLINT" == "failure" ]] || [[ "$S_SHELLCHECK" == "failure" ]] || [[ "$S_CHECKOV" == "failure" ]] || [[ "$S_TRIVY" == "failure" ]] || [[ "$S_COMPOSER" == "failure" ]] || [[ "$S_LINT_JS" == "failure" ]] || [[ "$S_LINT_PHP" == "failure" ]]; then
|
|
565
|
+
STATUS="failure"
|
|
566
|
+
ICON="❌"
|
|
567
|
+
else
|
|
568
|
+
STATUS="success"
|
|
569
|
+
ICON="✅"
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
# Skip Telegram on Successful PRs
|
|
573
|
+
if [[ "$STATUS" == "success" ]] && [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
|
574
|
+
echo "✅ PR Success. Skipping Telegram notification."
|
|
575
|
+
exit 0
|
|
576
|
+
fi
|
|
577
|
+
|
|
578
|
+
COMMIT_MSG_CLEAN=$(printf "%s" "$COMMIT_MESSAGE" | head -n 1 | sed 's/"/\\"/g')
|
|
579
|
+
|
|
580
|
+
# Build Detail List
|
|
581
|
+
DETAILS=""
|
|
582
|
+
add_status() {
|
|
583
|
+
local name=$1
|
|
584
|
+
local res=$2
|
|
585
|
+
local i="✅"
|
|
586
|
+
if [[ "$res" == "failure" ]]; then i="❌"; fi
|
|
587
|
+
if [[ "$res" == "skipped" ]]; then i="⏭️"; fi
|
|
588
|
+
DETAILS="${DETAILS}${i} ${name}\n"
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
add_status "Gitleaks" "$S_GITLEAKS"
|
|
592
|
+
add_status "Semgrep" "$S_SEMGREP"
|
|
593
|
+
add_status "Hadolint" "$S_HADOLINT"
|
|
594
|
+
add_status "ShellCheck" "$S_SHELLCHECK"
|
|
595
|
+
add_status "Checkov" "$S_CHECKOV"
|
|
596
|
+
add_status "Trivy" "$S_TRIVY"
|
|
597
|
+
add_status "Composer" "$S_COMPOSER"
|
|
598
|
+
add_status "Lint JS" "$S_LINT_JS"
|
|
599
|
+
add_status "Lint PHP" "$S_LINT_PHP"
|
|
600
|
+
|
|
601
|
+
# Escape newlines for JSON
|
|
602
|
+
DETAILS_JSON=$(echo -e "$DETAILS" | sed ':a;N;$!ba;s/\n/\\n/g')
|
|
603
|
+
|
|
604
|
+
curl -s -X POST "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
|
|
605
|
+
-H 'Content-Type: application/json' \
|
|
606
|
+
-d "{
|
|
607
|
+
\"chat_id\": \"$TG_TO\",
|
|
608
|
+
\"message_thread_id\": \"$TG_THREAD_ID\",
|
|
609
|
+
\"parse_mode\": \"HTML\",
|
|
610
|
+
\"text\": \"🔒 <b>Security & Quality: $ICON</b>\n\n📂 <b>Repo:</b> <a href=\\\"$SERVER_URL/$REPO\\\">$REPO</a>\n🌿 <b>Branch:</b> <code>$BRANCH</code>\n👤 <b>User:</b> $ACTOR\n📝 <b>Commit:</b> <i>$COMMIT_MSG_CLEAN</i>\n\n<b>Details:</b>\n$DETAILS_JSON\n\n🔗 <a href=\\\"$SERVER_URL/$REPO/commit/$SHA\\\">View Commit</a> | <a href=\\\"$SERVER_URL/$REPO/actions\\\">View Actions</a>\"
|
|
611
|
+
}"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# See https://pre-commit.com for more information
|
|
2
|
+
# See https://pre-commit.com/hooks.html for more hooks
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
+
rev: v4.5.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
- id: check-merge-conflict
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
14
|
+
rev: v8.18.2
|
|
15
|
+
hooks:
|
|
16
|
+
- id: gitleaks
|
|
17
|
+
|
|
18
|
+
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
|
19
|
+
rev: 2.7.3
|
|
20
|
+
hooks:
|
|
21
|
+
- id: editorconfig-checker
|
|
22
|
+
alias: ec
|
package/CHANGELOG.md
CHANGED
|
@@ -2,77 +2,127 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [1.9.0](https://github.com/boringstudio-org/mcp-gitea/compare/v1.8.0...v1.9.0) (2026-03-13)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add husky and gitleaks, ignore .idea ([b706acc](https://github.com/boringstudio-org/mcp-gitea/commit/b706accd83998bdce9082040abcff54b72f3fb56))
|
|
11
|
+
* add project files (v1.4.0) ([01ae6c1](https://github.com/boringstudio-org/mcp-gitea/commit/01ae6c10b4f5ec17d606db69da0d9d9cd0ea82ec))
|
|
12
|
+
* **ci:** pass secrets explicitly in security workflow ([0d9604f](https://github.com/boringstudio-org/mcp-gitea/commit/0d9604f0b4cebc1180e0b971abb898424521b928))
|
|
13
|
+
* enhance security and optimization ([72d3a8a](https://github.com/boringstudio-org/mcp-gitea/commit/72d3a8ac8b3c590025e43f00578a167906ee2e0f))
|
|
14
|
+
* update readme with security details ([650ffde](https://github.com/boringstudio-org/mcp-gitea/commit/650ffde4e25c0b7b472b7a9b20c5bd4da695e0e2))
|
|
15
|
+
* update security, dependencies and CI workflow (v1.8.0) ([#34](https://github.com/boringstudio-org/mcp-gitea/issues/34)) ([faf4dba](https://github.com/boringstudio-org/mcp-gitea/commit/faf4dba5d0373830d3776017b77467cad6941672))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **ci:** add .versionrc to skip ci on release commits ([4a8856b](https://github.com/boringstudio-org/mcp-gitea/commit/4a8856bc93215f98aa7eb0637a449601b1c96bd9))
|
|
21
|
+
* **ci:** checkout main branch before release ([d85bd5c](https://github.com/boringstudio-org/mcp-gitea/commit/d85bd5c57982d25d4cc2145f41ae21c75e91f97a))
|
|
22
|
+
* **ci:** use admin email for release workflow ([9fb8e1d](https://github.com/boringstudio-org/mcp-gitea/commit/9fb8e1d14a2ca30bb4e81d6587c1aed6f4541d03))
|
|
23
|
+
* **ci:** use correct bot email for release workflow ([c4899e4](https://github.com/boringstudio-org/mcp-gitea/commit/c4899e46e32e621273ebdde9f783d3ff29f9a251))
|
|
24
|
+
* **deps:** pin dependencies ([641be6b](https://github.com/boringstudio-org/mcp-gitea/commit/641be6b051289d3f6023a6fa2adfd58247052c9a))
|
|
25
|
+
* **deps:** resolve merge conflict, update sdk to 1.26.0 and axios to 1.13.5 ([bf5dd0a](https://github.com/boringstudio-org/mcp-gitea/commit/bf5dd0afdc01fa2e71e1856286ccd2ae52b56080))
|
|
26
|
+
* **deps:** update @modelcontextprotocol/sdk to 1.26.0 to fix CVE-2026-25536 ([57480cb](https://github.com/boringstudio-org/mcp-gitea/commit/57480cbfbb355b71caa0d00cc634ecc6f655fb30))
|
|
27
|
+
* **deps:** update dependency @modelcontextprotocol/sdk to v1.27.0 ([a743a42](https://github.com/boringstudio-org/mcp-gitea/commit/a743a42fc2eee5d5180bb8189d26c01c6cffdf17))
|
|
28
|
+
* **deps:** update dependency @modelcontextprotocol/sdk to v1.27.1 ([f8a6d52](https://github.com/boringstudio-org/mcp-gitea/commit/f8a6d5205662370bb9fec1bfb17da3573fb11164))
|
|
29
|
+
* **deps:** update dependency axios to v1.13.5 ([3236b60](https://github.com/boringstudio-org/mcp-gitea/commit/3236b607f1521ec2b5e367e183eb2f5ed2afa4cf))
|
|
30
|
+
* **deps:** update dependency axios to v1.13.6 ([02d21a7](https://github.com/boringstudio-org/mcp-gitea/commit/02d21a7d1b791fe35b6fbe7feb87805274882528))
|
|
31
|
+
* **deps:** update dependency dotenv to v17.3.1 ([d6b40ae](https://github.com/boringstudio-org/mcp-gitea/commit/d6b40ae2c342bdb96505da6ac3814f728538b7e9))
|
|
32
|
+
* resolve merge conflicts in CHANGELOG.md ([01db96f](https://github.com/boringstudio-org/mcp-gitea/commit/01db96f4fb1fb8c6d2baa4e05dbdc921d9644658))
|
|
33
|
+
* resolve semgrep security findings ([5615cfa](https://github.com/boringstudio-org/mcp-gitea/commit/5615cfa9db3eb65cc95bc34338891f5dead679cc))
|
|
34
|
+
* resolve semgrep security findings ([05b45a7](https://github.com/boringstudio-org/mcp-gitea/commit/05b45a72da017c122a8e08e2c38ce56c2654dbab))
|
|
35
|
+
* **security:** override @isaacs/brace-expansion to v5.0.1 to resolve CVE-2026-25547 ([aa220c1](https://github.com/boringstudio-org/mcp-gitea/commit/aa220c14ad9e2d31a6da25677d0d996347305f32))
|
|
36
|
+
|
|
37
|
+
## [1.8.0](https://github.com/boringstudio-org/mcp-gitea/compare/v1.7.0...v1.8.0) (2026-01-17)
|
|
38
|
+
|
|
5
39
|
### [1.7.1](https://git.boringstudio.by/BoringStudio/mcp-gitea/compare/v1.7.0...v1.7.1) (2026-01-15)
|
|
6
40
|
|
|
7
41
|
## [1.7.0](https://git.boringstudio.by/BoringStudio/mcp-gitea/compare/v1.6.1...v1.7.0) (2026-01-15)
|
|
8
42
|
|
|
43
|
+
### Features
|
|
44
|
+
|
|
45
|
+
* add project files (v1.4.0) ([01ae6c1](https://github.com/boringstudio-org/mcp-gitea/commit/01ae6c10b4f5ec17d606db69da0d9d9cd0ea82ec))
|
|
46
|
+
* enhance security and optimization ([72d3a8a](https://github.com/boringstudio-org/mcp-gitea/commit/72d3a8ac8b3c590025e43f00578a167906ee2e0f))
|
|
47
|
+
* update readme with security details ([650ffde](https://github.com/boringstudio-org/mcp-gitea/commit/650ffde4e25c0b7b472b7a9b20c5bd4da695e0e2))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
* **ci:** add .versionrc to skip ci on release commits ([4a8856b](https://github.com/boringstudio-org/mcp-gitea/commit/4a8856bc93215f98aa7eb0637a449601b1c96bd9))
|
|
53
|
+
* **ci:** checkout main branch before release ([d85bd5c](https://github.com/boringstudio-org/mcp-gitea/commit/d85bd5c57982d25d4cc2145f41ae21c75e91f97a))
|
|
54
|
+
* **ci:** use admin email for release workflow ([9fb8e1d](https://github.com/boringstudio-org/mcp-gitea/commit/9fb8e1d14a2ca30bb4e81d6587c1aed6f4541d03))
|
|
55
|
+
* **ci:** use correct bot email for release workflow ([c4899e4](https://github.com/boringstudio-org/mcp-gitea/commit/c4899e46e32e621273ebdde9f783d3ff29f9a251))
|
|
56
|
+
|
|
57
|
+
## [1.7.0](https://github.com/boringstudio-org/mcp-gitea/compare/v1.6.1...v1.7.0) (2026-01-15)
|
|
58
|
+
|
|
9
59
|
|
|
10
60
|
### Features
|
|
11
61
|
|
|
12
|
-
* update readme with security details ([#7](https://
|
|
62
|
+
* update readme with security details ([#7](https://github.com/boringstudio-org/mcp-gitea/issues/7)) ([9c719bb](https://github.com/boringstudio-org/mcp-gitea/commit/9c719bbf6f4e46bcba98f3656f899c9e11fd1e5e))
|
|
13
63
|
|
|
14
|
-
### [1.6.1](https://
|
|
64
|
+
### [1.6.1](https://github.com/boringstudio-org/mcp-gitea/compare/v1.6.0...v1.6.1) (2026-01-15)
|
|
15
65
|
|
|
16
|
-
## [1.6.0](https://
|
|
66
|
+
## [1.6.0](https://github.com/boringstudio-org/mcp-gitea/compare/v1.4.1...v1.6.0) (2026-01-15)
|
|
17
67
|
|
|
18
68
|
|
|
19
69
|
### Features
|
|
20
70
|
|
|
21
|
-
* enhance security and optimization ([#5](https://
|
|
71
|
+
* enhance security and optimization ([#5](https://github.com/boringstudio-org/mcp-gitea/issues/5)) ([f9f7055](https://github.com/boringstudio-org/mcp-gitea/commit/f9f70556240482149ac245357f23f1160e873e5c))
|
|
22
72
|
|
|
23
73
|
### 1.4.1 (2026-01-14)
|
|
24
74
|
|
|
25
|
-
### [1.3.6](https://
|
|
75
|
+
### [1.3.6](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.3.5...v1.3.6) (2026-01-14)
|
|
26
76
|
|
|
27
|
-
### [1.3.5](https://
|
|
77
|
+
### [1.3.5](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.3.4...v1.3.5) (2026-01-14)
|
|
28
78
|
|
|
29
79
|
|
|
30
80
|
### Bug Fixes
|
|
31
81
|
|
|
32
|
-
* Update start script to use global package and load env ([#20](https://
|
|
82
|
+
* Update start script to use global package and load env ([#20](https://github.com/boringstudio-org/mcp-gitea-proxy/issues/20)) ([c4598fc](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/c4598fce2bbfcd9acc48599fe0eddde013ecedb5))
|
|
33
83
|
|
|
34
|
-
### [1.3.4](https://
|
|
84
|
+
### [1.3.4](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.3.3...v1.3.4) (2026-01-14)
|
|
35
85
|
|
|
36
86
|
|
|
37
87
|
### Features
|
|
38
88
|
|
|
39
|
-
* Native Node.js Google Search implementation ([#19](https://
|
|
89
|
+
* Native Node.js Google Search implementation ([#19](https://github.com/boringstudio-org/mcp-gitea-proxy/issues/19)) ([555f44a](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/555f44a9bd9017d2cd01351efe19b5b2c766b442))
|
|
40
90
|
|
|
41
|
-
### [1.3.3](https://
|
|
91
|
+
### [1.3.3](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.3.2...v1.3.3) (2026-01-14)
|
|
42
92
|
|
|
43
93
|
|
|
44
94
|
### Bug Fixes
|
|
45
95
|
|
|
46
|
-
* Silence npx output, map Google ID, add start script and update docs ([#18](https://
|
|
96
|
+
* Silence npx output, map Google ID, add start script and update docs ([#18](https://github.com/boringstudio-org/mcp-gitea-proxy/issues/18)) ([7c36bdc](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/7c36bdcedd5c29281a1562a7a801c3d0ac6d3256))
|
|
47
97
|
|
|
48
|
-
### [1.3.2](https://
|
|
98
|
+
### [1.3.2](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.3.1...v1.3.2) (2026-01-14)
|
|
49
99
|
|
|
50
100
|
|
|
51
101
|
### Bug Fixes
|
|
52
102
|
|
|
53
|
-
* Add ~/.local/bin to PATH for uvx discovery ([#16](https://
|
|
103
|
+
* Add ~/.local/bin to PATH for uvx discovery ([#16](https://github.com/boringstudio-org/mcp-gitea-proxy/issues/16)) ([b2a25d5](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/b2a25d5fa5ec6d5d9fface87d48eb431ff9fd175))
|
|
54
104
|
|
|
55
|
-
### [1.3.1](https://
|
|
105
|
+
### [1.3.1](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.3.0...v1.3.1) (2026-01-14)
|
|
56
106
|
|
|
57
107
|
|
|
58
108
|
### Bug Fixes
|
|
59
109
|
|
|
60
|
-
* Remove hardcoded env from npm config and update docs ([9842c48](https://
|
|
110
|
+
* Remove hardcoded env from npm config and update docs ([9842c48](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/9842c48e07e91a0d5b79d547782aa0aa9b6d9390))
|
|
61
111
|
|
|
62
|
-
## [1.3.0](https://
|
|
112
|
+
## [1.3.0](https://github.com/boringstudio-org/mcp-gitea-proxy/compare/v1.2.0...v1.3.0) (2026-01-13)
|
|
63
113
|
|
|
64
114
|
## 1.2.0 (2026-01-13)
|
|
65
115
|
|
|
66
116
|
|
|
67
117
|
### Features
|
|
68
118
|
|
|
69
|
-
* add create_pull_request and list_branches tools (Closes [#3](https://
|
|
70
|
-
* add list_labels and update_issue with labels support (Closes [#2](https://
|
|
71
|
-
* add memory schema docs and update project structure ([841b0d6](https://
|
|
72
|
-
* add secure shell execution and full Gitea integration ([d22d48c](https://
|
|
73
|
-
* upgrade to v1.4.0 with Resources and Prompts ([1510e9e](https://
|
|
119
|
+
* add create_pull_request and list_branches tools (Closes [#3](https://github.com/boringstudio-org/mcp-gitea-proxy/issues/3)) ([465d4ed](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/465d4ed1738cf7e54d2b67b7a8c5f2df70194cfe))
|
|
120
|
+
* add list_labels and update_issue with labels support (Closes [#2](https://github.com/boringstudio-org/mcp-gitea-proxy/issues/2)) ([9006385](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/9006385ce82cd5adc5f7af9bdbf5e28c89789816))
|
|
121
|
+
* add memory schema docs and update project structure ([841b0d6](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/841b0d6075c9e9b501a7d76a7cfa10a1f228a2bb))
|
|
122
|
+
* add secure shell execution and full Gitea integration ([d22d48c](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/d22d48cb99de046b560313995b08d1c67955fb41))
|
|
123
|
+
* upgrade to v1.4.0 with Resources and Prompts ([1510e9e](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/1510e9e7e1b3d682c011a7fdc56ae2b26435c086))
|
|
74
124
|
|
|
75
125
|
|
|
76
126
|
### Bug Fixes
|
|
77
127
|
|
|
78
|
-
* Add fallback for uvx path ([dba4e21](https://
|
|
128
|
+
* Add fallback for uvx path ([dba4e21](https://github.com/boringstudio-org/mcp-gitea-proxy/commit/dba4e213790c91a8d031c37cac9a0eaf8724664a))
|
package/README.md
CHANGED
|
@@ -29,9 +29,14 @@ npm install @boringstudio_org/gitea-mcp
|
|
|
29
29
|
```bash
|
|
30
30
|
# Configure environment variables
|
|
31
31
|
export GITEA_TOKEN=your_token
|
|
32
|
-
# Optional: Defaults to https://git.
|
|
32
|
+
# Optional: Defaults to https://git.your-instance.com/api/v1
|
|
33
33
|
export GITEA_API_URL=https://git.your-instance.com/api/v1
|
|
34
34
|
|
|
35
|
+
# Cloudflare Access (Optional)
|
|
36
|
+
# If your Gitea instance is behind Cloudflare Zero Trust
|
|
37
|
+
export CF_ID=your_cf_client_id
|
|
38
|
+
export CF_SECRET=your_cf_client_secret
|
|
39
|
+
|
|
35
40
|
# Run
|
|
36
41
|
npx @boringstudio_org/gitea-mcp
|
|
37
42
|
```
|
|
@@ -42,7 +47,11 @@ npx @boringstudio_org/gitea-mcp
|
|
|
42
47
|
npm install
|
|
43
48
|
|
|
44
49
|
# Configure .env
|
|
45
|
-
echo "
|
|
50
|
+
echo "GITEA_TOKEN=..." > .env
|
|
51
|
+
echo "GITEA_API_URL=..." >> .env
|
|
52
|
+
# Optional: Cloudflare Access
|
|
53
|
+
echo "CF_ID=..." >> .env
|
|
54
|
+
echo "CF_SECRET=..." >> .env
|
|
46
55
|
|
|
47
56
|
# Run
|
|
48
57
|
node index.js
|
package/RELEASE.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 🚀 Release Process
|
|
2
|
+
|
|
3
|
+
Releases for `@boringstudio_org/gitea-mcp` are automated via Gitea Actions.
|
|
4
|
+
|
|
5
|
+
## 🛠 Prerequisites
|
|
6
|
+
|
|
7
|
+
* **NPM Token**: `NPM_TOKEN` must be configured in repository secrets for publishing.
|
|
8
|
+
* **Git Token**: `GITEA_TOKEN` or `RENOVATE_TOKEN` with write permissions for automated tagging.
|
|
9
|
+
|
|
10
|
+
## 📋 Steps to Release
|
|
11
|
+
|
|
12
|
+
### 1. Prepare Release (on `dev` branch)
|
|
13
|
+
Run the release script locally to bump the version and generate the changelog.
|
|
14
|
+
**Important**: Use `--no-tag` to let the CI handle the official tagging on the `main` branch.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# For a bug fix (patch): 1.8.0 -> 1.8.1
|
|
18
|
+
npm run release -- --release-as patch --no-tag
|
|
19
|
+
|
|
20
|
+
# For a new feature (minor): 1.8.0 -> 1.9.0
|
|
21
|
+
npm run release -- --release-as minor --no-tag
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This command will:
|
|
25
|
+
* Update `version` in `package.json`.
|
|
26
|
+
* Append new changes to `CHANGELOG.md`.
|
|
27
|
+
* Create a "chore(release): X.X.X" commit.
|
|
28
|
+
|
|
29
|
+
### 2. Push to Development
|
|
30
|
+
```bash
|
|
31
|
+
git push origin dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 3. Merge to Main
|
|
35
|
+
1. Open a **Pull Request** from `dev` to `main` in Gitea.
|
|
36
|
+
2. Complete the code review.
|
|
37
|
+
3. Merge the PR.
|
|
38
|
+
|
|
39
|
+
### 4. Automation (CI)
|
|
40
|
+
Once the release commit is merged into `main`, the Gitea Action will:
|
|
41
|
+
* Detect the version bump.
|
|
42
|
+
* **Publish** the package to the NPM registry.
|
|
43
|
+
* **Create & Push** the official git tag (e.g., `v1.9.0`) to the repository.
|
|
44
|
+
|
|
45
|
+
## 🔍 Troubleshooting
|
|
46
|
+
|
|
47
|
+
* **Release not triggered?** Ensure the `chore(release)` commit is present on the `main` branch and the version is unique.
|
|
48
|
+
* **NPM Publish failed?** Verify the `NPM_TOKEN` has not expired and has "Automation" permissions.
|
package/index.js
CHANGED
|
@@ -7,6 +7,10 @@ import dotenv from "dotenv";
|
|
|
7
7
|
import path from "path";
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { spawn } from "child_process";
|
|
10
|
+
import { createRequire } from "module";
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const pkg = require("./package.json");
|
|
10
14
|
|
|
11
15
|
// Load .env from the current working directory
|
|
12
16
|
dotenv.config({ path: path.join(process.cwd(), ".env"), quiet: true });
|
|
@@ -14,11 +18,11 @@ dotenv.config({ path: path.join(process.cwd(), ".env"), quiet: true });
|
|
|
14
18
|
export async function runGiteaServer() {
|
|
15
19
|
const server = new McpServer({
|
|
16
20
|
name: "gitea-proxy-agent",
|
|
17
|
-
version:
|
|
21
|
+
version: pkg.version,
|
|
18
22
|
});
|
|
19
23
|
|
|
20
24
|
const GITEA_TOKEN = process.env.GITEA_TOKEN;
|
|
21
|
-
const BASE_URL = process.env.GITEA_API_URL
|
|
25
|
+
const BASE_URL = process.env.GITEA_API_URL;
|
|
22
26
|
|
|
23
27
|
if (!GITEA_TOKEN) {
|
|
24
28
|
console.error("❌ Error: GITEA_TOKEN not found.");
|
|
@@ -48,7 +52,7 @@ export async function runGiteaServer() {
|
|
|
48
52
|
const config = { method, url: endpoint };
|
|
49
53
|
if (data) config.data = data;
|
|
50
54
|
if (params) config.params = params;
|
|
51
|
-
|
|
55
|
+
|
|
52
56
|
const response = await api(config);
|
|
53
57
|
return response.data;
|
|
54
58
|
} catch (error) {
|
|
@@ -136,9 +140,11 @@ export async function runGiteaServer() {
|
|
|
136
140
|
// --- TOOLS ---
|
|
137
141
|
|
|
138
142
|
// Security: Allowed paths for run_safe_shell
|
|
143
|
+
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
|
|
139
144
|
const ALLOWED_PATHS = (process.env.MCP_ALLOWED_PATHS || process.cwd()).split(',').map(p => path.resolve(p.trim()));
|
|
140
145
|
|
|
141
146
|
function isPathAllowed(targetPath) {
|
|
147
|
+
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
|
|
142
148
|
const resolved = path.resolve(targetPath);
|
|
143
149
|
return ALLOWED_PATHS.some(allowed => resolved.startsWith(allowed));
|
|
144
150
|
}
|
|
@@ -182,6 +188,7 @@ export async function runGiteaServer() {
|
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
return new Promise((resolve) => {
|
|
191
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
185
192
|
const child = spawn(command, args, {
|
|
186
193
|
cwd: cwd,
|
|
187
194
|
env: process.env,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boringstudio_org/gitea-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "A Gitea MCP Server for interacting with repositories, issues, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,18 +9,23 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node index.js",
|
|
12
|
-
"release": "standard-version"
|
|
12
|
+
"release": "standard-version",
|
|
13
|
+
"test": "echo \"No tests yet\" && exit 0",
|
|
14
|
+
"scan:secrets": "docker run --rm -v \"$(pwd):/path\" zricethezav/gitleaks:latest detect --source=\"/path\" -v"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
|
-
"@modelcontextprotocol/sdk": "
|
|
16
|
-
"axios": "
|
|
17
|
-
"dotenv": "
|
|
18
|
-
"zod": "
|
|
17
|
+
"@modelcontextprotocol/sdk": "1.27.1",
|
|
18
|
+
"axios": "1.13.6",
|
|
19
|
+
"dotenv": "17.3.1",
|
|
20
|
+
"zod": "3.25.76"
|
|
21
|
+
},
|
|
22
|
+
"overrides": {
|
|
23
|
+
"@isaacs/brace-expansion": "5.0.1"
|
|
19
24
|
},
|
|
20
25
|
"license": "MIT",
|
|
21
26
|
"repository": {
|
|
22
27
|
"type": "git",
|
|
23
|
-
"url": "https://
|
|
28
|
+
"url": "https://github.com/boringstudio-org/mcp-gitea.git"
|
|
24
29
|
},
|
|
25
30
|
"keywords": [
|
|
26
31
|
"mcp",
|
|
@@ -33,6 +38,6 @@
|
|
|
33
38
|
"access": "public"
|
|
34
39
|
},
|
|
35
40
|
"devDependencies": {
|
|
36
|
-
"standard-version": "
|
|
41
|
+
"standard-version": "9.5.0"
|
|
37
42
|
}
|
|
38
43
|
}
|
package/renovate.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"config:recommended",
|
|
4
|
+
":disableMajorUpdates"
|
|
5
|
+
],
|
|
6
|
+
"prConcurrentLimit": 2,
|
|
7
|
+
"baseBranchPatterns": [
|
|
8
|
+
"dev"
|
|
9
|
+
],
|
|
10
|
+
"useBaseBranchConfig": "merge",
|
|
11
|
+
"labels": [
|
|
12
|
+
"renovate"
|
|
13
|
+
],
|
|
14
|
+
"rebaseWhen": "conflicted",
|
|
15
|
+
"rangeStrategy": "pin",
|
|
16
|
+
"packageRules": [
|
|
17
|
+
{
|
|
18
|
+
"matchDatasources": [
|
|
19
|
+
"docker",
|
|
20
|
+
"github-tags"
|
|
21
|
+
],
|
|
22
|
+
"matchUpdateTypes": [
|
|
23
|
+
"minor",
|
|
24
|
+
"patch",
|
|
25
|
+
"pin",
|
|
26
|
+
"digest"
|
|
27
|
+
],
|
|
28
|
+
"groupName": "CI tools updates"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|