fix(ci): use Gitea file API to push CHANGELOG.md #40
No reviewers
Labels
No Label
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: sarman/tftsr-devops_investigation#40
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/changelog-push"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The changelog job was failing at
Commit CHANGELOG.md to masterwith a non-fast-forward rejection. This is a race condition:git push origin HEAD:masterfails whenever master advances between the jobs fetch and its push.Fix
Replace
git commit + git pushwithPUT /repos/.../contents/CHANGELOG.mdvia the Gitea file API. This atomically updates the file on master regardless of where local HEAD is — no fast-forward required.The step now:
branch: masterTest plan
🤖 Automated PR Review:\n\n### 1) Summary
The PR replaces a Git CLI-based workflow (staging, committing, and pushing
CHANGELOG.mdviagit add/commit/push) with a direct call to the Gitea REST API to update the file. This avoids local Git state management and potential merge conflicts, and aligns with CI best practices for declarative file updates.However, the implementation contains critical bugs, security vulnerabilities, and best-practice violations that must be addressed before merging.
2) Bugs / Errors
❌ Missing closing quote and backslash in
curlcommand→ Missing
)— the command substitution is unclosed. This will cause a syntax error or hang the job.✅ Fix:
(Also: the API returns JSON — you need to extract
.sha, not use raw output.)❌ No fallback for new files (SHA missing)
If
CHANGELOG.mddoesn’t exist yet (e.g., first release),curlreturns404, andCURRENT_SHAbecomes empty or invalid. The subsequentPUTwith"sha": ""will fail.✅ Fix: Handle the
404case:Then conditionally include
"sha"in the JSON payload only if the file exists.❌ No error handling for API calls
The
curlcommands use-sf(silent fail), but if the API returns a non-2xx (e.g.,401,403,422,409), the script continues, potentially leaving the repo in an inconsistent state.✅ Fix: Check HTTP status explicitly:
❌ Hardcoded internal IP (
172.0.0.29) and port (3000)This assumes the Gitea instance is always reachable at that exact address/port — fragile and non-portable across environments (e.g., production, different runners, cloud).
✅ Fix: Use environment variables or a configurable base URL:
Then:
3) Security Issues
⚠️ Exposing
RELEASE_TOKENin logsThe token is passed via
env:, but thecurlcommand and JSON payload are printed in logs ifset -xis enabled (or if the workflow runner logs the full script). Worse, ifcurlfails, the full request (including token inAuthorizationheader) may leak.✅ Fix:
-H "Authorization: token $RELEASE_TOKEN"explicitly and ensure no debug logging.GITHUB_TOKENif it has write permissions (Gitea supports GitHub-style tokens), but verify scope.⚠️ No input sanitization for
TAGTAG=$(git describe --tags --abbrev=0)could return malicious strings (e.g.,v1.0.0"; rm -rf /). Used in commit message and API request — potential injection.✅ Fix: Validate
TAGagainst a safe regex:⚠️ No rate limiting or conflict retry
Concurrent CI runs could cause
409 Conflict(ifCURRENT_SHAis stale). The current script fails on conflict.✅ Fix: Implement retry logic with exponential backoff for
409(or useHEADto get latest SHA before commit).4) Best Practices
✅ Good: Avoiding
git pushin CIUsing the API is more reliable than
git push(avoids SSH key setup, merge conflicts, andgit configrequirements).❌ Missing:
Content-Type: application/jsonin GET requestThe
curlfor fetching SHA doesn’t specifyAccept: application/json, though Gitea defaults to JSON. Still, be explicit.✅ Fix:
❌ No handling of
base64encoding edge casesbase64 -w 0is correct, but ensureCHANGELOG.mdis UTF-8 and not binary. Gitea expects base64-encoded content.✅ Fix: Add encoding check:
❌ Hardcoded
masterbranchAssumes
masterexists (notmain). Modern repos often usemain.✅ Fix: Use a configurable branch (e.g.,
GITEA_DEFAULT_BRANCH):Then:
❌ No cleanup of temp files
CHANGELOG.mdis read directly — safe if it’s in the repo, but ensure no sensitive data leaks.✅ Good:
set -euPrevents silent failures.
🔧 Suggested Complete Fix (Key Snippet)
Final Recommendation
Do not merge as-is. Fix all critical bugs (especially the unclosed command substitution and missing SHA extraction) and security issues (token exposure, input validation) before merging. Consider adding tests (e.g., mock API responses) and documenting assumptions (e.g.,
GITHUB_REPOSITORYcompatibility).\n\n---\nthis is an automated review from Ollama🤖 Automated PR Review:\n\n### Code Review:
.gitea/workflows/auto-tag.yml— Using Gitea File API to PushCHANGELOG.md1. Summary
This change replaces a
git add/commit/pushworkflow (which requires local Git state and remote branch tracking) with a direct Gitea REST API call to atomically updateCHANGELOG.mdon themasterbranch. This is a solid improvement for CI reliability, avoids merge conflicts, and removes dependency on Git credentials/SSH keys in the runner.However, the implementation introduces critical security and correctness issues related to:
2. Bugs & Correctness Issues
2.1 Hardcoded API endpoint (
http://172.0.0.29:3000)${GITEA_SERVER_URL:-http://localhost:3000}or derive from environment (e.g.,GITHUB_SERVER_URLis not Gitea-compatible, but Gitea CI setsGITEA_SERVER_URL). If this workflow runs inside the same Docker network (e.g., Gitea self-hosted), document the requirement and use a config variable with fallback.2.2 Missing Authorization Header
curlcalls lack-H "Authorization: token $RELEASE_TOKEN". Without this, the API request will return404(for private repos) or401, causing silent failure.2.3 Unsafe JSON generation with
jq+ untrusted$TAG❌ High: Although
$TAGis validated withgrep, the validation is insufficient and the JSON construction is still vulnerable:jq --argdoes escape strings safely for JSON, so the JSON itself is safe.BUT: the real issue is that the tag validation regex
^v[0-9]+\.[0-9]+\.[0-9]+$is too permissive:v1.02.03) → may cause issues downstream (e.g., semantic versioning tools reject this).0 ≤ major ≤ 2^31-1, etc. — but more critically, it doesn’t reject empty or malformed tags early enough.However, the bigger correctness bug is:
jq -r '.sha // empty'returns"null"if.shais missing, but// emptyconverts it to empty string — good.jq -rmay outputnullwithout quotes if no match →CURRENT_SHA="null"→PAYLOADincludes"sha": "null"(a string"null", not JSONnull) → Gitea API will reject it.✅ Fix:
2.4
base64 -w 0may not be portable-w 0is GNUbase64-specific. On macOS/BSD, use-b 0(orbase64 -w 0may fail silently or wrap).base64 -w0only if GNUbase64is detected, or use portable alternative:2.5 Temp file not cleaned up
RESP_FILE=$(mktemp)but neverrm "$RESP_FILE". In CI, this leaks temp files (though minor, it’s bad practice).3. Security Issues
3.1 Shell injection via
$TAGinecho(low risk but avoidable)❌ Low-Medium:
echo "$TAG"is safe if$TAGis validated, but the validation regex is insufficient:v1.2.3is OK, butv1.2.3-rc1is rejected (false positive for pre-release tags).v1.2.3; rm -rf /would be rejected by the regex — but only if the regex is applied before shell expansion. Since it’secho "$TAG" | grep, andechodoesn’t interpret shell metacharacters, it’s safe — but the real risk is downstream if$TAGis used in other contexts (e.g.,git tag "$TAG"later?).However, the current code only uses
$TAGin:echo "ERROR: ... $TAG"→ safe (quoted)jq--arg msg→ safe (jq --argescapes)So no injection here, but the regex is too strict for real-world SemVer.
✅ Fix: Use stricter validation and support pre-release tags:
3.2 No validation of
CHANGELOG.mdcontent❌ High:
base64 -w 0 CHANGELOG.mduploads any content — including malicious payloads (e.g., ifCHANGELOG.mdis tampered with by a previous step or attacker).✅ Fix: Validate content before upload:
< 1MB)</script>,javascript:, etc.) — though less critical for Markdown.Example:
3.3
curlwithout--failcurl -sdoes not fail on HTTP error codes (e.g., 4xx/5xx). You check$HTTP_CODE, butcurl -smay still output error HTML to stdout (which gets piped tojq→jqfails with parse error).-f(fail silently on HTTP errors) or keepHTTP_CODEcheck (as done), but ensurecurldoesn’t output to stdout/stderr in success case (already done with-s).4. Best Practices
4.1 Use environment variables for config
APIandRELEASE_TOKENto environment variables (already done forRELEASE_TOKEN, butAPIis hardcoded).4.2 Idempotency & atomicity
PUT /contents/{path}is atomic and handles create/update in one call — much better thangit push.4.3 Logging & debugging
HTTP_CODEand response body on error is helpful.TAG(sanitized) for traceability:4.4 Error handling with
set -euo pipefailset -euo pipefailis correctly used.4.5
jqversion compatibilityjq -n --arg ... 'if ... then ... else ... end'is fine, but ensurejq >= 1.6(older versions may have issues with// emptyin conditionals). Consider usingjq --argjsonfor booleans, but here it’s not needed.✅ Recommended Fixes Summary
${GITEA_API_URL:-http://gitea:3000/api/v1/repos/$GITHUB_REPOSITORY}or derive from CI env-H "Authorization: token $RELEASE_TOKEN"CURRENT_SHA="null"CURRENT_SHA="${CURRENT_SHA:-}"; [ "$CURRENT_SHA" = "null" ] && CURRENT_SHA=""base64portabilitytr -d '\n'afterbase64trap 'rm -f "$RESP_FILE"' EXITCHANGELOG.mdvalidation-rc1,+build, etc. in tag regexFinal Verdict
Do not merge as-is. Fix the auth header and tag validation first — those are blocking issues.
Let me know if you'd like a revised diff with all fixes applied!\n\n---\nthis is an automated review from Ollama