Open
Conversation
Extract workflowRunBackendId and workflowJobRunBackendId from the ACTIONS_RUNTIME_TOKEN JWT scp claim. The token is decoded without signature verification (consumed by the same process that received it). Includes ArtifactTransport, BackendIds, UploadArtifactResult, and ArtifactUploaderDeps type definitions in types.ts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CreateArtifact and FinalizeArtifact Twirp calls with protocol version 7. Both are wrapped in exponential-backoff retry for 429/5xx errors via withRetry from src/http/retry.ts. Injectable transport and sleep for testability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Single PUT upload to Azure Blob Storage signed URL with BlockBlob header, correct UTF-8 byte length, and retry for 5xx errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three-step upload flow: CreateArtifact → blob PUT → FinalizeArtifact. Includes GHES guard (github.com and *.ghe.com only), SHA-256 hash computation, MIME type detection, and barrel exports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wrap a GitHub-rendered HTML fragment in a complete HTML5 page that loads Primer CSS from unpkg CDN. The markdown-body class matches GitHub's rendered Markdown container for correct styling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tryUploadFullReport() renders full markdown to HTML via the GitHub API, wraps it in a Primer CSS page, uploads as an artifact, and returns the artifact URL. Never throws — all errors become ::warning:: annotations and the function returns undefined. All I/O dependencies are injected via parameters for testability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add hasUnresolvedFailures flag to the Report model, computed when any step fails without captured stdout/stderr. Add buildLogsNotice() to the compositor alongside buildTruncationNotice(), directing users to workflow run logs when error details are not in the report. Add INFO_ICON to status-icons for the logs notice blockquote. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wire artifact upload into the action entry point. When a report is truncated, the full markdown is rendered to HTML, uploaded as an artifact, and the truncation notice links to the artifact. Falls back to logs URL if upload is unavailable (GHES) or fails. Key changes: - Add operation and hasUnresolvedFailures to ReportFromStepsResult - Export buildLogsNotice from the public API - Refactor run() from positional params to RunDeps object for DI - Artifact name includes workspace and operation (e.g. cluster-plan) - Logs notice appended independently when steps fail without output - Add integration coverage exclusions for artifact/, html/, action/ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds an artifact-upload fallback path so that when the generated report exceeds GitHub comment limits, the action can still provide access to the full report by uploading an HTML artifact and linking it from the truncation notice. Also adds a separate “logs notice” for failure cases where step output wasn’t captured, directing users to workflow run logs for the missing error details.
Changes:
- Implement Actions Results Service (Twirp v7 + Azure Blob Storage) artifact upload pipeline and action-layer “never-throws” upload wrapper.
- Add HTML page wrapper for GitHub-rendered Markdown fragments and integrate upload attempt into truncation handling in
src/action/main.ts. - Track/report
hasUnresolvedFailuresand append a logs notice when failures have no captured stdout/stderr.
Reviewed changes
Copilot reviewed 24 out of 26 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.integration.config.ts | Excludes src/artifact/** and src/html/** from integration-only coverage with rationale comments. |
| src/model/status-icons.ts | Adds INFO_ICON for informational notices. |
| src/model/report.ts | Adds hasUnresolvedFailures flag to the Report model. |
| src/index.ts | Extends ReportFromStepsResult with operation and hasUnresolvedFailures; re-exports buildLogsNotice. |
| src/html/page.ts | New HTML page wrapper used for artifact rendering. |
| src/html/index.ts | Public barrel export for HTML utilities. |
| src/compositor/truncation.ts | Adds buildLogsNotice() alongside truncation notice helper. |
| src/builder/report-from-steps.ts | Computes report.hasUnresolvedFailures from step issues. |
| src/artifact/upload.ts | Artifact upload orchestrator (guard, hash, create → blob PUT → finalize). |
| src/artifact/types.ts | Shared artifact types and DI surface (transport/hash/sleep). |
| src/artifact/twirp.ts | Twirp RPC implementations with retry logic. |
| src/artifact/jwt.ts | Extracts backend IDs from ACTIONS_RUNTIME_TOKEN JWT scope. |
| src/artifact/index.ts | Artifact module barrel export. |
| src/artifact/blob-upload.ts | Single-PUT Azure blob upload with retry. |
| src/action/main.ts | Refactors run() deps to an object; attempts artifact upload on truncation; appends logs notice on unresolved failures. |
| src/action/artifact-upload.ts | “Never-throws” action-layer orchestrator: render Markdown via GitHub API → wrap HTML → upload artifact → return URL. |
| tests/unit/html/page.test.ts | Unit coverage for HTML wrapper behavior. |
| tests/unit/compositor/truncation.test.ts | Adds tests for logs notice rendering. |
| tests/unit/artifact/upload.test.ts | Unit coverage for upload orchestrator wiring (sequence, hash/size, MIME detection, GHES guard). |
| tests/unit/artifact/twirp.test.ts | Unit coverage for Twirp calls and retry behavior. |
| tests/unit/artifact/jwt.test.ts | Unit coverage for JWT scope parsing and failure modes. |
| tests/unit/artifact/blob-upload.test.ts | Unit coverage for blob PUT + retry policy. |
| tests/unit/action/main.test.ts | Updates for new run() signature and adds new tests around logs notice/artifact upload wiring. |
| tests/unit/action/artifact-upload.test.ts | Unit coverage for tryUploadFullReport() behavior and error handling. |
| dist/index.js | Re-bundled distribution output reflecting new/changed source modules. |
- Remove "self-contained" wording from HTML page builder JSDoc since the page loads Primer CSS from CDN (requires internet access) - Remove redundant test that claimed to test truncation wiring but did not actually force truncation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add an off-by-default `always-upload-report` input that unconditionally uploads the full report as an HTML artifact, even when the report is not truncated. When the report fits in the comment, a compact artifact link (📎) is appended. When truncated, the existing truncation notice still links to the artifact. Includes: - New ARTIFACT_ICON constant in status-icons - buildArtifactNotice() in compositor/truncation.ts - alwaysUploadReport boolean in ActionInputs - Wiring in main.ts: upload when flag is set OR truncated - Enabled in ci.yml to demonstrate on this PR - Documented in README inputs table, features, and size limits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…me_type - Add Logger interface (warning/error/info) in src/action/logger.ts with actionsLogger() for production and nullLogger() for tests - Thread Logger through RunDeps and TryUploadParams so tests never emit ::warning:: or ::error:: annotations into CI output - Add exit function DI in RunDeps (defaults to process.exit) - Fix CreateArtifact Twirp request: include mime_type field required by Actions Results Service v7 - Pass detected MIME type from upload orchestrator to createArtifact() - Rewrite main.test.ts: replace vi.spyOn(process/console) hacks with capturingLogger/nullLogger and throwingExit - Rewrite artifact-upload.test.ts: verify warning messages via capturing logger on error paths - Add ESLint no-restricted-syntax rule banning direct process.stderr, process.stdout, and console.* access in src/ (except logger.ts) - Add governance test (no-direct-io.test.ts) scanning source for forbidden I/O globals Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Actions Results Service returns snake_case JSON field names (signed_upload_url, artifact_id), not camelCase. This caused CreateArtifact to succeed but the response parsing to fail with 'missing signedUploadUrl'. Updated all response field access and test mock responses to use snake_case matching the real API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Actions Results Service expects protobuf JSON format:
- Request field names use proto (snake_case) names, matching the toolkit's
useProtoFieldName: true serialization option
- StringValue wrapper types serialize as plain strings, not {value: ...}
objects — fixes the FinalizeArtifact 'malformed' error caused by sending
hash as a nested object
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the Primer CSS CDN link (which requires data-color-mode attributes to activate) with fully embedded inline CSS and JavaScript. The artifact page is now self-contained with no external dependencies: - GitHub-like .markdown-body styles: headings, tables, code blocks, blockquotes, details/summary, ins/del diff styling, etc. - Copy-to-clipboard button on every <pre> code block (appears on hover, shows 'Copied!' feedback) - markdown-accessiblity-table custom element styled as display: block The render script (npm run render / npm run gallery) now imports the same shared CSS from src/html/ for consistent local preview. Also changes the artifact link text from 'Full report artifact' to 'View/Download Report'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Support both markdown-accessiblity-table (GitHub's actual misspelling) and markdown-accessibility-table (correct spelling) in CSS selector - Fix copy button text pollution: read from <code> child element instead of pre.textContent to avoid including the button label in copied text Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
✅
|
| Step | Outcome |
|---|---|
checkout |
success |
setup-node |
success |
install |
success |
ci |
success |
check-dist |
success |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User-facing changes
When a report is too large for a GitHub comment, the action now uploads the full
un-truncated report as an HTML artifact and links to it from the truncation
notice. Previously, truncated reports only linked to the workflow run logs.
The artifact is an HTML page styled with GitHub Primer CSS (loaded from CDN),
rendered via the GitHub
/markdownAPI for full GFM fidelity (task lists,emoji, syntax highlighting).
Additionally, when a step fails without any captured stdout/stderr output, a
new "logs notice" is appended directing users to the workflow run logs — this
appears independently of truncation and addresses the case where error details
are only visible in the raw logs.
always-upload-reportinputA new
always-upload-reportinput (default"false") allows opting in tounconditional artifact upload — even when the report is not truncated. When
enabled and the upload succeeds, a compact 📎 link to the artifact is appended
to the comment. This is useful for archival or sharing reports outside of GitHub.
This PR's CI workflow has this enabled to demonstrate the feature.
Artifact upload details
github.comand
*.ghe.comare supported). Falls back to linking the workflow run logs.::warning::and thetruncation notice falls back to the logs URL.
(e.g.,
cluster-plan.html,apply.html).scratch using
node:http/node:https(reusing the existing HTTP transport).Internal changes
New modules
src/artifact/jwt.tssrc/artifact/twirp.tssrc/artifact/blob-upload.tssrc/artifact/upload.tssrc/html/page.tssrc/action/artifact-upload.tsModified modules
src/action/main.ts—run()refactored from positional params toRunDepsobject for dependency injection. Upload triggered when truncated OR when
always-upload-reportis enabled.src/action/inputs.ts— NewalwaysUploadReportboolean parsed fromalways-upload-reportinput.src/index.ts—ReportFromStepsResultgainsoperationandhasUnresolvedFailuresfields; exportsbuildLogsNoticeandbuildArtifactNotice.src/model/report.ts—hasUnresolvedFailuresflag on Report.src/compositor/truncation.ts—buildLogsNotice(),buildArtifactNotice()alongside existing
buildTruncationNotice().src/model/status-icons.ts—INFO_ICON,ARTIFACT_ICON.Tests
93 new tests across 7 test files (all passing, coverage thresholds met).
Fix: CreateArtifact
mime_typefieldThe Actions Results Service v7 requires a
mime_typefield in theCreateArtifactTwirp request. The initial implementation omitted it,causing an HTTP 400 rejection. Now
mime_typeis derived from the filenameextension and passed through to the Twirp call.
Dependency-injected logging
All
::warning::and::error::annotation output is now routed througha
Loggerinterface (src/action/logger.ts). Tests injectnullLogger()or a capturing implementation, preventing annotations from leaking into the
CI test runner output (which GitHub Actions would interpret as real workflow
commands). This eliminates spurious warnings like
Artifact upload is not supported on github.mycompany.comthat appeared when tests exercised errorpaths.
New enforcement:
no-restricted-syntaxbansprocess.stderr,process.stdout, andconsole.*insrc/(exceptlogger.ts)no-direct-io.test.tsscans source files forforbidden I/O globals