Prepare Sandwich Hime v1 release candidate source
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
package releaseartifact
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const nativeReceiptMaximumAge = 30 * 24 * time.Hour
|
||||
|
||||
var runnerVersionPattern = regexp.MustCompile(`^v(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)$`)
|
||||
|
||||
type nativeLane struct {
|
||||
directory string
|
||||
goos string
|
||||
goarch string
|
||||
goVersion string
|
||||
runner string
|
||||
}
|
||||
|
||||
var requiredNativeLanes = []nativeLane{
|
||||
{directory: "darwin-arm64-go1.26.7", goos: "darwin", goarch: "arm64", goVersion: "go1.26.7", runner: "macbook-air-himesan-darwin-arm64"},
|
||||
{directory: "darwin-arm64-go1.27.0", goos: "darwin", goarch: "arm64", goVersion: "go1.27.0", runner: "macbook-air-himesan-darwin-arm64"},
|
||||
{directory: "linux-amd64-go1.26.7", goos: "linux", goarch: "amd64", goVersion: "go1.26.7", runner: "cliff-himesan-linux-amd64"},
|
||||
{directory: "linux-amd64-go1.27.0", goos: "linux", goarch: "amd64", goVersion: "go1.27.0", runner: "cliff-himesan-linux-amd64"},
|
||||
}
|
||||
|
||||
var requiredNativeGates = []string{
|
||||
"build", "consumer", "contracts", "fuzz", "generation", "package",
|
||||
"public-snapshot", "race", "test", "vet", "vulnerability",
|
||||
}
|
||||
|
||||
// NativeReceiptExpectation identifies the exact source verified by all native
|
||||
// runner lanes.
|
||||
type NativeReceiptExpectation struct {
|
||||
Repository string
|
||||
Commit string
|
||||
Tree string
|
||||
}
|
||||
|
||||
// NativeReceiptSummary is safe to include in human release evidence.
|
||||
type NativeReceiptSummary struct {
|
||||
SchemaVersion string `json:"schema_version"`
|
||||
Repository string `json:"repository"`
|
||||
Commit string `json:"commit"`
|
||||
Tree string `json:"tree"`
|
||||
GeneratedDigest string `json:"generated_output_sha256"`
|
||||
Lanes []NativeLaneSummary `json:"lanes"`
|
||||
}
|
||||
|
||||
// NativeLaneSummary records one verified native receipt without local paths.
|
||||
type NativeLaneSummary struct {
|
||||
Platform string `json:"platform"`
|
||||
GoVersion string `json:"go_version"`
|
||||
RunnerVersion string `json:"runner_version"`
|
||||
ArtifactSHA256 string `json:"unsigned_artifact_sha256"`
|
||||
ReceiptSHA256 string `json:"receipt_sha256"`
|
||||
CompletedAt string `json:"completed_at"`
|
||||
}
|
||||
|
||||
// VerifyNativeReceiptSet requires all four maintained target/toolchain lanes,
|
||||
// validates their checksum sidecars, and proves identical generated output.
|
||||
func VerifyNativeReceiptSet(directory string, expected NativeReceiptExpectation) (NativeReceiptSummary, error) {
|
||||
if strings.TrimSpace(expected.Repository) == "" || strings.ContainsAny(expected.Repository, "\r\n\x00") {
|
||||
return NativeReceiptSummary{}, errors.New("native receipt repository is required and must be one line")
|
||||
}
|
||||
if !gitObjectPattern.MatchString(expected.Commit) || !gitObjectPattern.MatchString(expected.Tree) {
|
||||
return NativeReceiptSummary{}, errors.New("native receipt commit and tree must be full lowercase Git object identities")
|
||||
}
|
||||
if err := validateNativeDirectory(directory); err != nil {
|
||||
return NativeReceiptSummary{}, err
|
||||
}
|
||||
|
||||
summary := NativeReceiptSummary{
|
||||
SchemaVersion: "himesan.native-receipt-set.v1",
|
||||
Repository: expected.Repository, Commit: expected.Commit, Tree: expected.Tree,
|
||||
}
|
||||
for _, lane := range requiredNativeLanes {
|
||||
receipt, receiptDigest, err := readNativeLane(filepath.Join(directory, lane.directory))
|
||||
if err != nil {
|
||||
return NativeReceiptSummary{}, fmt.Errorf("verify native lane %s: %w", lane.directory, err)
|
||||
}
|
||||
if receipt.SchemaVersion != receiptSchema || receipt.Repository != expected.Repository ||
|
||||
receipt.Commit != expected.Commit || receipt.Tree != expected.Tree ||
|
||||
receipt.GOOS != lane.goos || receipt.GOARCH != lane.goarch ||
|
||||
receipt.GoVersion != lane.goVersion || receipt.RunnerName != lane.runner {
|
||||
return NativeReceiptSummary{}, fmt.Errorf("native lane %s does not match its source, platform, toolchain, or runner", lane.directory)
|
||||
}
|
||||
if !runnerVersionPattern.MatchString(receipt.RunnerVersion) || !digestPattern.MatchString(receipt.UnsignedArtifactSHA) {
|
||||
return NativeReceiptSummary{}, fmt.Errorf("native lane %s has an invalid runner or artifact identity", lane.directory)
|
||||
}
|
||||
completedAt, err := time.Parse(time.RFC3339, receipt.CompletedAt)
|
||||
if err != nil || completedAt.After(time.Now().UTC().Add(5*time.Minute)) || time.Since(completedAt) > nativeReceiptMaximumAge {
|
||||
return NativeReceiptSummary{}, fmt.Errorf("native lane %s is not fresh, completed, RFC3339 evidence", lane.directory)
|
||||
}
|
||||
if err := requireNativeGates(receipt.SuccessfulGates); err != nil {
|
||||
return NativeReceiptSummary{}, fmt.Errorf("native lane %s: %w", lane.directory, err)
|
||||
}
|
||||
if summary.GeneratedDigest == "" {
|
||||
summary.GeneratedDigest = receipt.GeneratedDigest
|
||||
} else if summary.GeneratedDigest != receipt.GeneratedDigest {
|
||||
return NativeReceiptSummary{}, errors.New("native lanes did not produce identical generated output")
|
||||
}
|
||||
summary.Lanes = append(summary.Lanes, NativeLaneSummary{
|
||||
Platform: lane.goos + "/" + lane.goarch, GoVersion: lane.goVersion,
|
||||
RunnerVersion: receipt.RunnerVersion, ArtifactSHA256: receipt.UnsignedArtifactSHA,
|
||||
ReceiptSHA256: receiptDigest, CompletedAt: receipt.CompletedAt,
|
||||
})
|
||||
}
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func validateNativeDirectory(directory string) error {
|
||||
info, err := os.Lstat(directory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inspect native evidence directory: %w", err)
|
||||
}
|
||||
if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
||||
return errors.New("native evidence must be a real directory")
|
||||
}
|
||||
entries, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read native evidence directory: %w", err)
|
||||
}
|
||||
expected := make([]string, 0, len(requiredNativeLanes))
|
||||
for _, lane := range requiredNativeLanes {
|
||||
expected = append(expected, lane.directory)
|
||||
}
|
||||
observed := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
observed = append(observed, entry.Name())
|
||||
}
|
||||
sort.Strings(expected)
|
||||
sort.Strings(observed)
|
||||
if !equalStrings(expected, observed) {
|
||||
return fmt.Errorf("native evidence directories = %v, want exactly %v", observed, expected)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readNativeLane(directory string) (Receipt, string, error) {
|
||||
info, err := os.Lstat(directory)
|
||||
if err != nil {
|
||||
return Receipt{}, "", err
|
||||
}
|
||||
if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
||||
return Receipt{}, "", errors.New("lane must be a real directory")
|
||||
}
|
||||
entries, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
return Receipt{}, "", err
|
||||
}
|
||||
if len(entries) != 2 || entries[0].Name() != "TEND-CI-VERIFICATION.json" || entries[1].Name() != "TEND-CI-VERIFICATION.json.sha256" {
|
||||
return Receipt{}, "", errors.New("lane must contain only its receipt and checksum sidecar")
|
||||
}
|
||||
receiptPath := filepath.Join(directory, "TEND-CI-VERIFICATION.json")
|
||||
contents, err := readRegularFile(receiptPath, maximumEvidenceSize)
|
||||
if err != nil || len(contents) == 0 {
|
||||
return Receipt{}, "", errors.New("receipt must be a non-empty bounded regular file")
|
||||
}
|
||||
receiptDigest := digest(contents)
|
||||
sidecar, err := readRegularFile(receiptPath+".sha256", 512)
|
||||
if err != nil {
|
||||
return Receipt{}, "", fmt.Errorf("read receipt checksum: %w", err)
|
||||
}
|
||||
expectedSidecar := receiptDigest + " TEND-CI-VERIFICATION.json\n"
|
||||
if string(sidecar) != expectedSidecar {
|
||||
return Receipt{}, "", errors.New("receipt checksum sidecar does not match")
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(contents))
|
||||
decoder.DisallowUnknownFields()
|
||||
var receipt Receipt
|
||||
if err := decoder.Decode(&receipt); err != nil {
|
||||
return Receipt{}, "", fmt.Errorf("decode receipt: %w", err)
|
||||
}
|
||||
if err := requireJSONEOF(decoder); err != nil {
|
||||
return Receipt{}, "", fmt.Errorf("decode receipt: %w", err)
|
||||
}
|
||||
if err := validateReceipt(receipt); err != nil {
|
||||
return Receipt{}, "", err
|
||||
}
|
||||
return receipt, receiptDigest, nil
|
||||
}
|
||||
|
||||
func requireNativeGates(gates []string) error {
|
||||
observed := make(map[string]bool, len(gates))
|
||||
for _, gate := range gates {
|
||||
if observed[gate] {
|
||||
return fmt.Errorf("successful gate %q is duplicated", gate)
|
||||
}
|
||||
observed[gate] = true
|
||||
}
|
||||
for _, gate := range requiredNativeGates {
|
||||
if !observed[gate] {
|
||||
return fmt.Errorf("required successful gate %q is missing", gate)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func equalStrings(left, right []string) bool {
|
||||
if len(left) != len(right) {
|
||||
return false
|
||||
}
|
||||
for index := range left {
|
||||
if left[index] != right[index] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user