112 lines
3.4 KiB
Go
112 lines
3.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
|
)
|
|
|
|
const runResultSchemaVersion = "notarius.run-result.v1"
|
|
|
|
type runResult struct {
|
|
SchemaVersion string `json:"schema_version"`
|
|
RunID string `json:"run_id"`
|
|
PipelineID string `json:"pipeline_id"`
|
|
OutputDirectory string `json:"output_directory"`
|
|
IndexFile string `json:"index_file,omitempty"`
|
|
NormalizedOutputCount int `json:"normalized_output_count"`
|
|
RejectedOutputCount int `json:"rejected_output_count"`
|
|
WarningCount int `json:"warning_count"`
|
|
ValidationStatus string `json:"validation_status"`
|
|
DebugDirectory string `json:"debug_directory,omitempty"`
|
|
}
|
|
|
|
func newRunResult(resolved pipeline.ResolvedPipeline, output pipeline.RunOutput, outputDirectory, debugDirectory string) (runResult, error) {
|
|
if strings.TrimSpace(output.Manifest.RunID) == "" {
|
|
return runResult{}, fmt.Errorf("run result requires a run ID")
|
|
}
|
|
if strings.TrimSpace(resolved.ID) == "" {
|
|
return runResult{}, fmt.Errorf("run result requires a resolved pipeline ID")
|
|
}
|
|
if strings.TrimSpace(output.Manifest.PipelineID) == "" {
|
|
return runResult{}, fmt.Errorf("run result requires a manifest pipeline ID")
|
|
}
|
|
if output.Manifest.PipelineID != resolved.ID {
|
|
return runResult{}, fmt.Errorf("run result pipeline ID does not match resolved pipeline")
|
|
}
|
|
if strings.TrimSpace(output.Manifest.ValidationStatus) == "" {
|
|
return runResult{}, fmt.Errorf("run result requires a validation status")
|
|
}
|
|
if strings.TrimSpace(outputDirectory) == "" {
|
|
return runResult{}, fmt.Errorf("run result requires an output directory")
|
|
}
|
|
|
|
absOutputDirectory, err := filepath.Abs(outputDirectory)
|
|
if err != nil {
|
|
return runResult{}, fmt.Errorf("make output directory absolute: %w", err)
|
|
}
|
|
|
|
result := runResult{
|
|
SchemaVersion: runResultSchemaVersion,
|
|
RunID: output.Manifest.RunID,
|
|
PipelineID: output.Manifest.PipelineID,
|
|
OutputDirectory: absOutputDirectory,
|
|
NormalizedOutputCount: len(output.NormalizeOutputs),
|
|
RejectedOutputCount: len(output.Rejected),
|
|
WarningCount: len(output.Warnings),
|
|
ValidationStatus: output.Manifest.ValidationStatus,
|
|
}
|
|
|
|
if strings.TrimSpace(debugDirectory) != "" {
|
|
absDebugDirectory, err := filepath.Abs(debugDirectory)
|
|
if err != nil {
|
|
return runResult{}, fmt.Errorf("make debug directory absolute: %w", err)
|
|
}
|
|
result.DebugDirectory = absDebugDirectory
|
|
}
|
|
|
|
if resolved.Output.Module == pipeline.DefaultOutputModule {
|
|
indexCount := 0
|
|
for _, file := range output.OutputFiles {
|
|
if file.Name == "index.json" {
|
|
indexCount++
|
|
}
|
|
}
|
|
if indexCount != 1 {
|
|
return runResult{}, fmt.Errorf("production JSON output must contain exactly one index.json file")
|
|
}
|
|
result.IndexFile = "index.json"
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func encodeRunResult(result runResult) ([]byte, error) {
|
|
encoded, err := json.Marshal(result)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("encode run result: %w", err)
|
|
}
|
|
return append(encoded, '\n'), nil
|
|
}
|
|
|
|
func writeRunResult(writer io.Writer, content []byte) error {
|
|
for len(content) > 0 {
|
|
written, err := writer.Write(content)
|
|
if written < 0 || written > len(content) {
|
|
return io.ErrShortWrite
|
|
}
|
|
content = content[written:]
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if written == 0 {
|
|
return io.ErrShortWrite
|
|
}
|
|
}
|
|
return nil
|
|
}
|