Report JSON output failures from recovery commands
This commit is contained in:
@@ -102,14 +102,17 @@ func runRecovery(ctx context.Context, invocation cli.Invocation, stdout, stderr
|
||||
for _, item := range listed.Diagnostics {
|
||||
diagnostics = append(diagnostics, diagnostic(item))
|
||||
}
|
||||
writeJSON(stdout, attemptsDocument{
|
||||
if writeErr := writeJSON(stdout, attemptsDocument{
|
||||
SchemaVersion: 1,
|
||||
Command: "publication-attempts",
|
||||
RecordDir: git.DisplayDiagnostic(listed.RecordDir),
|
||||
Attempts: attempts,
|
||||
Diagnostics: diagnostics,
|
||||
Diagnostic: diagnostic(err),
|
||||
})
|
||||
}); writeErr != nil {
|
||||
fmt.Fprintf(stderr, "write recovery output: %v\n", git.DisplayError(writeErr))
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(stdout, "publication records in %s\n", git.DisplayDiagnostic(listed.RecordDir))
|
||||
for _, item := range listed.Attempts {
|
||||
@@ -149,7 +152,10 @@ func runReconcile(ctx context.Context, invocation cli.Invocation, stdout, stderr
|
||||
dto.ObservedCommit = result.ObservedCommit
|
||||
}
|
||||
dto.Diagnostic = diagnostic(err)
|
||||
writeJSON(stdout, dto)
|
||||
if writeErr := writeJSON(stdout, dto); writeErr != nil {
|
||||
fmt.Fprintf(stderr, "write recovery output: %v\n", git.DisplayError(writeErr))
|
||||
return 1
|
||||
}
|
||||
} else if err == nil {
|
||||
fmt.Fprintf(stdout, "reconciled %s module=%s tag=%s state=%s\n", git.DisplayDiagnostic(invocation.RecordPath), git.DisplayDiagnostic(evidence.ModulePath), git.DisplayDiagnostic(evidence.Tag), result.State)
|
||||
if result.State == releasekit.PublicationConflict {
|
||||
@@ -243,7 +249,7 @@ func humanDiagnostic(err error) string {
|
||||
return git.DisplayError(err).Error()
|
||||
}
|
||||
|
||||
func writeJSON(writer io.Writer, value any) {
|
||||
func writeJSON(writer io.Writer, value any) error {
|
||||
encoder := json.NewEncoder(writer)
|
||||
_ = encoder.Encode(value)
|
||||
return encoder.Encode(value)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -14,6 +15,41 @@ import (
|
||||
"go.maximumdirect.net/releasekit/internal/publish"
|
||||
)
|
||||
|
||||
type failingRecoveryWriter struct{ err error }
|
||||
|
||||
func (writer failingRecoveryWriter) Write([]byte) (int, error) { return 0, writer.err }
|
||||
|
||||
func TestRecoveryCommandsReportJSONWriteFailure(t *testing.T) {
|
||||
repo := newCLIRepository(t)
|
||||
cliGitRun(t, repo, "tag", "-a", "v1.2.3", "-m", "Release")
|
||||
store := t.TempDir()
|
||||
path, err := publish.SaveEvidence(t.Context(), repo, store, publish.Evidence{
|
||||
RepositoryDir: repo, Endpoint: repo, Tag: "v1.2.3",
|
||||
CandidateCommit: cliGitOutput(t, repo, "rev-parse", "HEAD"),
|
||||
TagObject: cliGitOutput(t, repo, "rev-parse", "v1.2.3"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
failure := errors.New("output unavailable: https://user:audit-secret@example.invalid/output")
|
||||
for _, args := range [][]string{{"reconcile", "--record", path, "--json"}, {"publication-attempts", "--record-dir", store, "--json"}} {
|
||||
var normal, stderr bytes.Buffer
|
||||
if status := run(t.Context(), args, &normal, &stderr); status != 0 {
|
||||
t.Fatalf("normal output status=%d: %s", status, &stderr)
|
||||
}
|
||||
stderr.Reset()
|
||||
if status := run(t.Context(), args, failingRecoveryWriter{failure}, &stderr); status != 1 {
|
||||
t.Fatalf("failed output status=%d: %s", status, &stderr)
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "output unavailable") || strings.Contains(stderr.String(), "audit-secret") {
|
||||
t.Fatalf("output diagnostic: %s", &stderr)
|
||||
}
|
||||
if args[0] == "reconcile" && !strings.Contains(normal.String(), `"state":"published"`) {
|
||||
t.Fatalf("observation changed: %s", &normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecoveryDisplaysRedactCredentialPathsAndMetadata(t *testing.T) {
|
||||
secret := "audit-secret"
|
||||
raw := "https://audit-user:" + secret + "@example.invalid/attempt.json"
|
||||
|
||||
@@ -188,6 +188,11 @@ using current supported transport settings for its recorded remote name.
|
||||
| Discovery completes without record diagnostics, including an existing empty store | 0 |
|
||||
| Discovery is incomplete or fails, including cancellation | 1 |
|
||||
| Discovery argument or usage error | 2 |
|
||||
| Either command cannot write its JSON output | 1 |
|
||||
|
||||
A JSON write failure reports a local output diagnostic on standard error. This
|
||||
status describes reporting failure and leaves the observed publication state
|
||||
unchanged.
|
||||
|
||||
The CLI JSON schema and safe evidence fields are documented in the
|
||||
[publication record format](integrations/publication-records.md). Recovery
|
||||
|
||||
@@ -210,6 +210,11 @@ endpoint-only case. No new credential-storage policy is needed.
|
||||
|
||||
## R06: Propagate structured-output write failures — P2
|
||||
|
||||
**Completed:** Both recovery commands now report JSON encoder failures on
|
||||
standard error and return status 1. Failing-writer regressions passed alongside
|
||||
ordinary recovery status tests; observed publication state remains unchanged.
|
||||
The CLI reference records the reporting-failure status.
|
||||
|
||||
**Relevant code:** `cmd/releasekit/recovery.go:242` and its two `writeJSON`
|
||||
call sites.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user