Replace Notarius bundle promotion with rename
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -33,7 +34,7 @@ type extractStage struct{}
|
||||
|
||||
func (extractStage) Name() string { return "extract" }
|
||||
|
||||
func (extractStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*StageResult, error) {
|
||||
func (extractStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (result *StageResult, resultErr error) {
|
||||
if env == nil || env.Config == nil || env.Config.Pipeline == nil || env.Config.Session == nil {
|
||||
return nil, fmt.Errorf("extract: resolved stage environment config is required")
|
||||
}
|
||||
@@ -154,6 +155,15 @@ func (extractStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("extract: run notarius: %w", err)
|
||||
}
|
||||
bundleInstalled := false
|
||||
defer func() {
|
||||
if bundleInstalled {
|
||||
return
|
||||
}
|
||||
if cleanupErr := os.RemoveAll(outputRoot); cleanupErr != nil {
|
||||
resultErr = errors.Join(resultErr, fmt.Errorf("extract: remove failed Notarius output root: %w", cleanupErr))
|
||||
}
|
||||
}()
|
||||
if strings.TrimSpace(adapterResult.BundleRoot) == "" || strings.TrimSpace(adapterResult.Index.Path) == "" {
|
||||
return nil, fmt.Errorf("extract: notarius result is missing bundle or index path")
|
||||
}
|
||||
@@ -189,8 +199,24 @@ func (extractStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
return nil, fmt.Errorf("extract: validate staging index: %w", err)
|
||||
}
|
||||
|
||||
if err := fileops.PromoteDirectory(adapterResult.BundleRoot, durableBundle); err != nil {
|
||||
return nil, fmt.Errorf("extract: promote notarius bundle: %w", err)
|
||||
bundleRelative, err := pathsafe.SlashRelativeFromRoot(outputRoot, adapterResult.BundleRoot)
|
||||
if err != nil || bundleRelative == "." || bundleRelative == "" {
|
||||
return nil, fmt.Errorf("extract: Notarius bundle must be a proper descendant of the run output root")
|
||||
}
|
||||
if err := normalizeNotariusBundle(adapterResult.BundleRoot); err != nil {
|
||||
return nil, fmt.Errorf("extract: normalize Notarius bundle: %w", err)
|
||||
}
|
||||
if _, err := os.Lstat(durableBundle); err == nil {
|
||||
return nil, fmt.Errorf("extract: durable Notarius bundle destination already exists")
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("extract: inspect durable Notarius bundle destination: %w", err)
|
||||
}
|
||||
if err := os.Rename(adapterResult.BundleRoot, durableBundle); err != nil {
|
||||
return nil, fmt.Errorf("extract: install Notarius bundle: %w", err)
|
||||
}
|
||||
bundleInstalled = true
|
||||
if err := os.Remove(outputRoot); err != nil && !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("extract: remove empty Notarius output root: %w", err)
|
||||
}
|
||||
promotedIndexPath, err := pathsafe.JoinSlashRelativeUnderRoot(durableBundle, indexRelative)
|
||||
if err != nil {
|
||||
@@ -293,6 +319,28 @@ func (extractStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeNotariusBundle(root string) error {
|
||||
return filepath.WalkDir(root, func(path string, entry os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("bundle path %q is a symlink", path)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return os.Chmod(path, fileops.WorkspaceDirectoryMode)
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
return fmt.Errorf("bundle path %q is not regular", path)
|
||||
}
|
||||
return os.Chmod(path, fileops.WorkspaceFileMode)
|
||||
})
|
||||
}
|
||||
|
||||
type selectedNotariusLane struct {
|
||||
Key string
|
||||
Descriptor notarius.LaneDescriptor
|
||||
|
||||
@@ -310,8 +310,8 @@ func TestExtractStageProducesImmutableManifestReadyOutputs(t *testing.T) {
|
||||
t.Fatalf("promoted file %q missing: %v", relative, err)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(stagingBundle, "unknown", "private-debug.json")); err != nil {
|
||||
t.Fatalf("source bundle was not preserved: %v", err)
|
||||
if _, err := os.Stat(stagingBundle); !os.IsNotExist(err) {
|
||||
t.Fatalf("staging bundle still exists after rename: %v", err)
|
||||
}
|
||||
for _, output := range result.Outputs {
|
||||
if strings.Contains(output.AbsolutePath, "unconfigured") || strings.Contains(output.AbsolutePath, "private-debug") {
|
||||
@@ -507,11 +507,11 @@ func TestExtractStageAdapterAndPromotionFailuresReturnNoOutputs(t *testing.T) {
|
||||
t.Fatalf("MkdirAll(destination) error = %v", err)
|
||||
}
|
||||
result, err := (extractStage{}).Run(context.Background(), env, m)
|
||||
if err == nil || !strings.Contains(err.Error(), "promote") || result != nil {
|
||||
if err == nil || !strings.Contains(err.Error(), "destination already exists") || result != nil {
|
||||
t.Fatalf("Run() result = %#v error = %v", result, err)
|
||||
}
|
||||
if _, err := os.Stat(fake.Result.BundleRoot); err != nil {
|
||||
t.Fatalf("failed promotion removed staging bundle: %v", err)
|
||||
if _, err := os.Stat(fake.Result.BundleRoot); !os.IsNotExist(err) {
|
||||
t.Fatalf("failed installation staging bundle still exists: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user