## Summary
Fixes two issues:
### Issue #8: Distinguish 404 from other errors in GetAllFilesInPath
- Added `APIError` type with `StatusCode` field to carry HTTP status codes from Gitea API responses
- Added `IsNotFound` helper function for ergonomic 404 checks
- `GetAllFilesInPath` now only falls back to single-file fetch when the error is a 404 (path is a file, not a directory)
- All other errors (auth failures, 500s, rate limits) are properly propagated instead of being masked
### Issue #10: Make release asset uploads idempotent
- Release workflow now checks for existing assets with the same name before uploading
- If an asset already exists, it is deleted first, then re-uploaded
- This makes workflow re-runs safe — no more failures from duplicate asset names
### Tests
- `TestGetAllFilesInPath_404FallsBackToFile` — verifies 404 triggers file fallback
- `TestGetAllFilesInPath_500Propagates` — verifies 500 is propagated (no fallback)
- `TestGetAllFilesInPath_403Propagates` — verifies 403 is propagated (no fallback)
- `TestIsNotFound` — table test for the helper function
Closes #8
Closes #10
- Add APIError type with StatusCode field so callers can inspect HTTP
status codes from Gitea API responses
- Add IsNotFound helper for ergonomic 404 checks
- GetAllFilesInPath now only falls back to single-file fetch on 404;
all other errors (auth failures, server errors, rate limits) propagate
- Release workflow asset uploads are now idempotent: existing assets
with the same name are deleted before re-upload on workflow re-runs
Closes#8Closes#10
gpt-review-bot
approved these changes 2026-05-02 16:51:35 +00:00
This change robustly addresses 404 handling and release asset idempotency, introducing clear error typing and strong test coverage. Implementation is idiomatic and aligns with Go and repo conventions.
Recommendation
APPROVE — The update meaningfully improves error distinction in Gitea API handling by adding an idiomatic APIError type with status code propagation, and leverages this to prevent fallback-to-file for non-404 errors in GetAllFilesInPath. The new IsNotFound helper provides a clear ergonomic API for checking 404s, and the workflow improvement ensures release asset uploads are idempotent. Unit test coverage is comprehensive and table-driven as per project guidelines. All changes are well-designed, readable, and conform to standard Go error handling and style. No issues found—merge is recommended.
Review by gpt
# Gpt Review
## Summary
This change robustly addresses 404 handling and release asset idempotency, introducing clear error typing and strong test coverage. Implementation is idiomatic and aligns with Go and repo conventions.
## Recommendation
**APPROVE** — The update meaningfully improves error distinction in Gitea API handling by adding an idiomatic APIError type with status code propagation, and leverages this to prevent fallback-to-file for non-404 errors in GetAllFilesInPath. The new IsNotFound helper provides a clear ergonomic API for checking 404s, and the workflow improvement ensures release asset uploads are idempotent. Unit test coverage is comprehensive and table-driven as per project guidelines. All changes are well-designed, readable, and conform to standard Go error handling and style. No issues found—merge is recommended.
---
*Review by gpt*
<!-- review-bot:gpt -->
Changes introduce structured HTTP error handling (APIError) and a precise 404 check for safe fallback logic, plus idempotent asset uploads in the release workflow. CI has passed and no exploitable security issues are evident in the diff.
Recommendation
APPROVE — The introduction of APIError and IsNotFound improves error semantics without expanding the attack surface, and the release workflow’s pre-delete step makes uploads idempotent without exposing secrets or enabling injection. Inputs to API calls continue to be safely escaped, timeouts are in place, and no sensitive data is logged beyond server responses as before. Consider hardening the release asset lookup to robustly handle unusual filenames and HTTP failures, but this is not security-critical. Overall, the changes are safe to merge.
Review by security
# Security Review
## Summary
Changes introduce structured HTTP error handling (APIError) and a precise 404 check for safe fallback logic, plus idempotent asset uploads in the release workflow. CI has passed and no exploitable security issues are evident in the diff.
## Recommendation
**APPROVE** — The introduction of APIError and IsNotFound improves error semantics without expanding the attack surface, and the release workflow’s pre-delete step makes uploads idempotent without exposing secrets or enabling injection. Inputs to API calls continue to be safely escaped, timeouts are in place, and no sensitive data is logged beyond server responses as before. Consider hardening the release asset lookup to robustly handle unusual filenames and HTTP failures, but this is not security-critical. Overall, the changes are safe to merge.
---
*Review by security*
<!-- review-bot:security -->
The changes cleanly introduce a structured APIError with an IsNotFound helper and correctly adjust GetAllFilesInPath to only fall back on 404s, improving error handling. The release workflow gains idempotent asset uploads. Tests comprehensively cover the new behavior, and CI passed.
Findings
#
Severity
File
Line
Finding
1
[MINOR]
.gitea/workflows/release.yml
86
The asset upload now uses jq to URI-encode filenames. This adds an extra dependency that may not be present in all runners. Since python3 is already used earlier in the job, consider using Python's urllib.parse.quote to avoid relying on jq.
2
[NIT]
.gitea/workflows/release.yml
82
On deleting existing assets, curl is invoked with -sSf and will fail the job on non-2xx responses (e.g., transient 404/race). If you prefer the workflow to proceed in such cases, consider handling 404s gracefully or ignoring DELETE failures when the asset disappears between list and delete.
Recommendation
APPROVE — Overall the implementation is solid and adheres to idiomatic Go error handling patterns. The new APIError type and IsNotFound helper allow precise control flow in GetAllFilesInPath, and the fix to wrap the correct error on file fallback is correct. Tests validate the new behavior for 404/403/500 conditions and IsNotFound. The release workflow improvements make uploads idempotent; consider replacing the jq usage with Python’s urllib.parse.quote to avoid an extra dependency and optionally make DELETE failures non-fatal if desired. With CI passing and no major issues found, this is good to merge.
Review by sonnet
# Sonnet Review
## Summary
The changes cleanly introduce a structured APIError with an IsNotFound helper and correctly adjust GetAllFilesInPath to only fall back on 404s, improving error handling. The release workflow gains idempotent asset uploads. Tests comprehensively cover the new behavior, and CI passed.
## Findings
| # | Severity | File | Line | Finding |
|---|----------|------|------|--------|
| 1 | [MINOR] | `.gitea/workflows/release.yml` | 86 | The asset upload now uses jq to URI-encode filenames. This adds an extra dependency that may not be present in all runners. Since python3 is already used earlier in the job, consider using Python's urllib.parse.quote to avoid relying on jq. |
| 2 | [NIT] | `.gitea/workflows/release.yml` | 82 | On deleting existing assets, curl is invoked with -sSf and will fail the job on non-2xx responses (e.g., transient 404/race). If you prefer the workflow to proceed in such cases, consider handling 404s gracefully or ignoring DELETE failures when the asset disappears between list and delete. |
## Recommendation
**APPROVE** — Overall the implementation is solid and adheres to idiomatic Go error handling patterns. The new APIError type and IsNotFound helper allow precise control flow in GetAllFilesInPath, and the fix to wrap the correct error on file fallback is correct. Tests validate the new behavior for 404/403/500 conditions and IsNotFound. The release workflow improvements make uploads idempotent; consider replacing the jq usage with Python’s urllib.parse.quote to avoid an extra dependency and optionally make DELETE failures non-fatal if desired. With CI passing and no major issues found, this is good to merge.
---
*Review by sonnet*
<!-- review-bot:sonnet -->
[NIT] The inline Python filter compares asset names using a shell-expanded ${filename} inside single quotes. If a filename ever contained a single quote, this would break the command. While current artifact names are safe, consider more robust quoting or using a tool like jq.
**[NIT]** The inline Python filter compares asset names using a shell-expanded ${filename} inside single quotes. If a filename ever contained a single quote, this would break the command. While current artifact names are safe, consider more robust quoting or using a tool like jq.
[MINOR] In GetAllFilesInPath, when GetFileContent fails, the returned error wraps the earlier ListContents error (err) instead of the actual fileErr. This loses the specific cause of the file fetch failure. Prefer wrapping fileErr for better diagnostics.
**[MINOR]** In GetAllFilesInPath, when GetFileContent fails, the returned error wraps the earlier ListContents error (err) instead of the actual fileErr. This loses the specific cause of the file fetch failure. Prefer wrapping fileErr for better diagnostics.
Security-focused changes look sound: API errors now preserve status codes to prevent masking non-404 failures, and the release workflow’s asset upload is made idempotent without exposing secrets. CI passed.
Findings
#
Severity
File
Line
Finding
1
[MINOR]
.gitea/workflows/release.yml
78
The asset name is interpolated directly into the URL query string without URL-encoding (…/assets?name=${filename}). While current filenames are controlled and simple, not URL-encoding can cause unexpected behavior or edge-case injection via special characters. Consider URL-encoding the filename when building the query parameter.
2
[NIT]
gitea/client.go
20
APIError.Error includes the entire response body in error strings. If upstream returns verbose bodies, this could leak detailed server messages into logs. Not a direct vulnerability here, but consider truncating or sanitizing bodies in errors if upstream may include sensitive details.
Recommendation
APPROVE — The changes correctly distinguish 404s using a typed APIError and prevent masking other HTTP failures, improving security posture around error handling. The idempotent release upload logic avoids duplicate asset errors without exposing the token in logs. To harden further, URL-encode the asset filename when constructing the upload URL in the release workflow, and consider truncating response bodies in APIError messages if there’s any risk of verbose or sensitive content being logged. Otherwise, the PR is safe to merge.
Review by security
# Security Review
## Summary
Security-focused changes look sound: API errors now preserve status codes to prevent masking non-404 failures, and the release workflow’s asset upload is made idempotent without exposing secrets. CI passed.
## Findings
| # | Severity | File | Line | Finding |
|---|----------|------|------|--------|
| 1 | [MINOR] | `.gitea/workflows/release.yml` | 78 | The asset name is interpolated directly into the URL query string without URL-encoding (…/assets?name=${filename}). While current filenames are controlled and simple, not URL-encoding can cause unexpected behavior or edge-case injection via special characters. Consider URL-encoding the filename when building the query parameter. |
| 2 | [NIT] | `gitea/client.go` | 20 | APIError.Error includes the entire response body in error strings. If upstream returns verbose bodies, this could leak detailed server messages into logs. Not a direct vulnerability here, but consider truncating or sanitizing bodies in errors if upstream may include sensitive details. |
## Recommendation
**APPROVE** — The changes correctly distinguish 404s using a typed APIError and prevent masking other HTTP failures, improving security posture around error handling. The idempotent release upload logic avoids duplicate asset errors without exposing the token in logs. To harden further, URL-encode the asset filename when constructing the upload URL in the release workflow, and consider truncating response bodies in APIError messages if there’s any risk of verbose or sensitive content being logged. Otherwise, the PR is safe to merge.
---
*Review by security*
<!-- review-bot:security -->
[MINOR] The asset name is interpolated directly into the URL query string without URL-encoding (…/assets?name=${filename}). While current filenames are controlled and simple, not URL-encoding can cause unexpected behavior or edge-case injection via special characters. Consider URL-encoding the filename when building the query parameter.
**[MINOR]** The asset name is interpolated directly into the URL query string without URL-encoding (…/assets?name=${filename}). While current filenames are controlled and simple, not URL-encoding can cause unexpected behavior or edge-case injection via special characters. Consider URL-encoding the filename when building the query parameter.
[NIT] APIError.Error includes the entire response body in error strings. If upstream returns verbose bodies, this could leak detailed server messages into logs. Not a direct vulnerability here, but consider truncating or sanitizing bodies in errors if upstream may include sensitive details.
**[NIT]** APIError.Error includes the entire response body in error strings. If upstream returns verbose bodies, this could leak detailed server messages into logs. Not a direct vulnerability here, but consider truncating or sanitizing bodies in errors if upstream may include sensitive details.
- URL-encode filename in release upload query param (MINOR)
- Truncate APIError.Body to 200 chars in Error() to avoid leaking
verbose server responses into logs (NIT)
Changes introduce a typed API error with status codes and improve idempotency of release asset uploads. No security vulnerabilities are apparent; the updates generally improve error handling and robustness.
Recommendation
APPROVE — CI passed and the modifications are security-neutral or improving: APIError enables precise 404 handling without masking other errors, and the release workflow now safely URL-encodes asset names and deletes any existing asset before upload. No exploitable issues were identified. Consider continuing to limit sensitive information in error messages and ensure the release environment has required tools (jq, python3) installed, but these are non-security concerns.
Review by security
# Security Review
## Summary
Changes introduce a typed API error with status codes and improve idempotency of release asset uploads. No security vulnerabilities are apparent; the updates generally improve error handling and robustness.
## Recommendation
**APPROVE** — CI passed and the modifications are security-neutral or improving: APIError enables precise 404 handling without masking other errors, and the release workflow now safely URL-encodes asset names and deletes any existing asset before upload. No exploitable issues were identified. Consider continuing to limit sensitive information in error messages and ensure the release environment has required tools (jq, python3) installed, but these are non-security concerns.
---
*Review by security*
<!-- review-bot:security -->
rodin
merged commit d640eb6e71 into main2026-05-02 17:07:23 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Fixes two issues:
Issue #8: Distinguish 404 from other errors in GetAllFilesInPath
APIErrortype withStatusCodefield to carry HTTP status codes from Gitea API responsesIsNotFoundhelper function for ergonomic 404 checksGetAllFilesInPathnow only falls back to single-file fetch when the error is a 404 (path is a file, not a directory)Issue #10: Make release asset uploads idempotent
Tests
TestGetAllFilesInPath_404FallsBackToFile— verifies 404 triggers file fallbackTestGetAllFilesInPath_500Propagates— verifies 500 is propagated (no fallback)TestGetAllFilesInPath_403Propagates— verifies 403 is propagated (no fallback)TestIsNotFound— table test for the helper functionCloses #8
Closes #10
Gpt Review
Summary
This change robustly addresses 404 handling and release asset idempotency, introducing clear error typing and strong test coverage. Implementation is idiomatic and aligns with Go and repo conventions.
Recommendation
APPROVE — The update meaningfully improves error distinction in Gitea API handling by adding an idiomatic APIError type with status code propagation, and leverages this to prevent fallback-to-file for non-404 errors in GetAllFilesInPath. The new IsNotFound helper provides a clear ergonomic API for checking 404s, and the workflow improvement ensures release asset uploads are idempotent. Unit test coverage is comprehensive and table-driven as per project guidelines. All changes are well-designed, readable, and conform to standard Go error handling and style. No issues found—merge is recommended.
Review by gpt
Security Review
Summary
Changes introduce structured HTTP error handling (APIError) and a precise 404 check for safe fallback logic, plus idempotent asset uploads in the release workflow. CI has passed and no exploitable security issues are evident in the diff.
Recommendation
APPROVE — The introduction of APIError and IsNotFound improves error semantics without expanding the attack surface, and the release workflow’s pre-delete step makes uploads idempotent without exposing secrets or enabling injection. Inputs to API calls continue to be safely escaped, timeouts are in place, and no sensitive data is logged beyond server responses as before. Consider hardening the release asset lookup to robustly handle unusual filenames and HTTP failures, but this is not security-critical. Overall, the changes are safe to merge.
Review by security
Sonnet Review
Summary
The changes cleanly introduce a structured APIError with an IsNotFound helper and correctly adjust GetAllFilesInPath to only fall back on 404s, improving error handling. The release workflow gains idempotent asset uploads. Tests comprehensively cover the new behavior, and CI passed.
Findings
.gitea/workflows/release.yml.gitea/workflows/release.ymlRecommendation
APPROVE — Overall the implementation is solid and adheres to idiomatic Go error handling patterns. The new APIError type and IsNotFound helper allow precise control flow in GetAllFilesInPath, and the fix to wrap the correct error on file fallback is correct. Tests validate the new behavior for 404/403/500 conditions and IsNotFound. The release workflow improvements make uploads idempotent; consider replacing the jq usage with Python’s urllib.parse.quote to avoid an extra dependency and optionally make DELETE failures non-fatal if desired. With CI passing and no major issues found, this is good to merge.
Review by sonnet
@@ -74,2 +74,4 @@filename=$(basename "$file")echo "Uploading ${filename}..."# Check if asset already exists and delete it[NIT] The inline Python filter compares asset names using a shell-expanded ${filename} inside single quotes. If a filename ever contained a single quote, this would break the command. While current artifact names are safe, consider more robust quoting or using a tool like jq.
@@ -258,3 +281,4 @@// 404 means the path might be a file — try fetching directlycontent, fileErr := c.GetFileContent(ctx, owner, repo, path)if fileErr != nil {return nil, fmt.Errorf("path %q is neither a file nor directory: %w", path, err)[MINOR] In GetAllFilesInPath, when GetFileContent fails, the returned error wraps the earlier ListContents error (err) instead of the actual fileErr. This loses the specific cause of the file fetch failure. Prefer wrapping fileErr for better diagnostics.
Security Review
Summary
Security-focused changes look sound: API errors now preserve status codes to prevent masking non-404 failures, and the release workflow’s asset upload is made idempotent without exposing secrets. CI passed.
Findings
.gitea/workflows/release.ymlgitea/client.goRecommendation
APPROVE — The changes correctly distinguish 404s using a typed APIError and prevent masking other HTTP failures, improving security posture around error handling. The idempotent release upload logic avoids duplicate asset errors without exposing the token in logs. To harden further, URL-encode the asset filename when constructing the upload URL in the release workflow, and consider truncating response bodies in APIError messages if there’s any risk of verbose or sensitive content being logged. Otherwise, the PR is safe to merge.
Review by security
@@ -75,1 +75,4 @@echo "Uploading ${filename}..."# Check if asset already exists and delete itEXISTING_ID=$(export ASSET_NAME="${filename}"; curl -sS \[MINOR] The asset name is interpolated directly into the URL query string without URL-encoding (…/assets?name=${filename}). While current filenames are controlled and simple, not URL-encoding can cause unexpected behavior or edge-case injection via special characters. Consider URL-encoding the filename when building the query parameter.
@@ -16,6 +17,24 @@ import ("time")// APIError represents an HTTP error response from the Gitea API.[NIT] APIError.Error includes the entire response body in error strings. If upstream returns verbose bodies, this could leak detailed server messages into logs. Not a direct vulnerability here, but consider truncating or sanitizing bodies in errors if upstream may include sensitive details.
Security Review
Summary
Changes introduce a typed API error with status codes and improve idempotency of release asset uploads. No security vulnerabilities are apparent; the updates generally improve error handling and robustness.
Recommendation
APPROVE — CI passed and the modifications are security-neutral or improving: APIError enables precise 404 handling without masking other errors, and the release workflow now safely URL-encodes asset names and deletes any existing asset before upload. No exploitable issues were identified. Consider continuing to limit sensitive information in error messages and ensure the release environment has required tools (jq, python3) installed, but these are non-security concerns.
Review by security