fix(azure): fetch build-log content as text/plain to avoid ISecuredObject 500 (#192)#194
Conversation
…ject 500 (#192) The single build-log content endpoint (GET .../build/builds/{id}/logs/{logId}?api-version=7.1) returns a bare List<String>. AzureClient.get hardcoded Accept: application/json, so Azure DevOps refused to serialize the response and returned 500 "Cannot return type System.Collections.Generic.List`1[System.String] ... doesn't implement ISecuredObject" on every log-content fetch for the conda-forge org. A plain curl worked because its default Accept (*/*) yields text/plain. - AzureClient.get gains an `accept` parameter (default application/json) that sets the Accept header; the log-list path is unchanged. - azure_get_build_logs requests the single-log content with accept="text/plain"; the existing text/plain response branch handles it. - azure_get_failing_jobs: same text/plain fix on its inline log fetch, plus fix a NoneType crash — `record.get("log", {}).get("id")` raised 'NoneType' object has no attribute 'get' when a timeline record's "log" key was present but null; now `(record.get("log") or {}).get("id")`. Adds 4 tests: Accept header override + default on AzureClient.get, the text/plain request for single-log content, and a regression test that azure_get_failing_jobs does not raise when a failed record has log=None. Closes #192 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Claude finished @MementoRC's task —— View job PR Review
SummaryThis is a clean, targeted fix. The root cause analysis is accurate, the changes are minimal and well-scoped, and the test coverage is solid. A few observations below. Code Quality
The design is good. Adding One minor observation: the
if include_logs and (record.get("log") or {}).get("id"):This is the correct idiomatic fix. The comment on lines 298-300 explaining the Duplicated log-parsing logic ( The content-type branching block that parses JSON vs. plain text log responses is now duplicated between Potential Bugs / Edge Cases
Since the single-log endpoint now always requests Consider updating the comment or simplifying to just the
log_text = await response.text()
log_lines = log_text.split("\n")If Test CoverageThe 4 new tests are well-written and cover the right scenarios:
The regression test at Missing: There's no test verifying that SecurityNo concerns. No credentials are exposed or logged. The Minor Nits
Overall: This is a solid, production-worthy fix. The two minor bugs (stale comments implying a JSON path that's now unreachable, and |
Summary
Fixes #192.
azure_get_build_logsreturned a 500ISecuredObjecterror on every single-log content fetch for the conda-forge org.Root cause
The single build-log content endpoint (
GET .../build/builds/{id}/logs/{logId}?api-version=7.1) returns a bareList<String>.AzureClient.gethardcodedAccept: application/json, so Azure DevOps refused to serialize it and returned:A plain
curlworked because its defaultAccept: */*yieldstext/plain. The log-list path (nolog_id) was unaffected — it returns a proper JSON object.Changes
AzureClient.getgains anacceptparameter (defaultapplication/json) that sets the Accept header; the list path is unchanged.azure_get_build_logsrequests single-log content withaccept="text/plain"; the existing text/plain response branch handles it.azure_get_failing_jobs: sametext/plainfix on its inline log fetch, plus a NoneType crash fix —record.get("log", {}).get("id")raised'NoneType' object has no attribute 'get'when a timeline record'slogkey was present butnull(the secondary symptom reported in azure_get_build_logs: 500 ISecuredObject error on every log-content fetch (conda-forge org) #192); now(record.get("log") or {}).get("id").Tests
Adds 4 tests (Accept-header override + default on
AzureClient.get, thetext/plainrequest for single-log content, and a regression test thatazure_get_failing_jobsdoes not raise when a failed record haslog=None). Full azure unit suite: 31 passed.Closes #192
🤖 Generated with Claude Code