Validate downloads before installing workspace files
This commit is contained in:
@@ -5,8 +5,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
||||
@@ -58,23 +60,23 @@ func hydratePreviousSessionArtifacts(
|
||||
return nil, fmt.Errorf("create previous-session path directory for %q: %w", record.LocalPath, err)
|
||||
}
|
||||
|
||||
if err := fileops.DownloadAndInstall(record.LocalPath, fileops.WorkspaceFileMode, func(destination io.Writer) error {
|
||||
temporaryPath, err := writePreviousTemp(record.LocalPath, func(destination io.Writer) error {
|
||||
if len(record.VerifiedContent) > 0 {
|
||||
_, err := io.Copy(destination, bytes.NewReader(record.VerifiedContent))
|
||||
return err
|
||||
}
|
||||
return storage.DownloadTo(ctx, env.ObjectStore, record.RemoteKey, destination)
|
||||
}); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("write previous-session object %q: %w", record.RemoteKey, err)
|
||||
}
|
||||
defer func() { _ = os.Remove(temporaryPath) }()
|
||||
checksum, err := validatePreviousTemp(record, temporaryPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if record.Kind == preparePreviousInputKindArtifact {
|
||||
if err := requireNonEmptyFile(record.LocalPath, "previous-session artifact "+record.RequirementName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
checksum, err := env.ArtifactStore.Checksum(record.LocalPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("checksum previous-session object %q: %w", record.RemoteKey, err)
|
||||
if err := fileops.InstallSiblingTempFile(temporaryPath, record.LocalPath, fileops.WorkspaceFileMode); err != nil {
|
||||
return nil, fmt.Errorf("install previous-session object %q: %w", record.RemoteKey, err)
|
||||
}
|
||||
|
||||
result.Inputs = append(result.Inputs, manifest.InputRecord{
|
||||
@@ -100,3 +102,55 @@ func hydratePreviousSessionArtifacts(
|
||||
})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func writePreviousTemp(destination string, write func(io.Writer) error) (string, error) {
|
||||
if strings.TrimSpace(destination) == "" {
|
||||
return "", fmt.Errorf("previous-session destination path is required")
|
||||
}
|
||||
if write == nil {
|
||||
return "", fmt.Errorf("previous-session temporary writer is required")
|
||||
}
|
||||
directory := filepath.Dir(destination)
|
||||
if err := fileops.EnsureWorkspaceDirectory(directory); err != nil {
|
||||
return "", fmt.Errorf("create previous-session path directory: %w", err)
|
||||
}
|
||||
temporary, err := os.CreateTemp(directory, "."+filepath.Base(destination)+".tmp-*")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create sibling temporary file: %w", err)
|
||||
}
|
||||
temporaryPath := temporary.Name()
|
||||
if err := write(temporary); err != nil {
|
||||
_ = temporary.Close()
|
||||
_ = os.Remove(temporaryPath)
|
||||
return "", err
|
||||
}
|
||||
if err := temporary.Close(); err != nil {
|
||||
_ = os.Remove(temporaryPath)
|
||||
return "", fmt.Errorf("close sibling temporary file: %w", err)
|
||||
}
|
||||
return temporaryPath, nil
|
||||
}
|
||||
|
||||
func validatePreviousTemp(record previouscache.Record, temporaryPath string) (string, error) {
|
||||
info, err := os.Stat(temporaryPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("inspect previous-session object %q: %w", record.RemoteKey, err)
|
||||
}
|
||||
hasDeclaredSize := record.Size > 0 || strings.TrimSpace(record.SHA256) != "" || strings.TrimSpace(record.Generation) != ""
|
||||
if hasDeclaredSize && info.Size() != record.Size {
|
||||
return "", fmt.Errorf("previous-session object %q size mismatch: got %d, want %d", record.RemoteKey, info.Size(), record.Size)
|
||||
}
|
||||
if record.Kind == preparePreviousInputKindArtifact {
|
||||
if err := requireNonEmptyFile(temporaryPath, "previous-session artifact "+record.RequirementName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
checksum, err := artifacts.SHA256File(temporaryPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("checksum previous-session object %q: %w", record.RemoteKey, err)
|
||||
}
|
||||
if expected := strings.TrimSpace(record.SHA256); expected != "" && !strings.EqualFold(checksum, expected) {
|
||||
return "", fmt.Errorf("previous-session object %q checksum mismatch", record.RemoteKey)
|
||||
}
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user