Skip to content

Commit

Permalink
Add stabilize binary
Browse files Browse the repository at this point in the history
  • Loading branch information
msuozzo committed Oct 25, 2024
1 parent f98b326 commit ada844e
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 2 deletions.
168 changes: 168 additions & 0 deletions cmd/stabilize/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package main

import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"slices"
"strings"

"github.com/google/oss-rebuild/pkg/archive"
"github.com/pkg/errors"
)

var (
infile = flag.String("infile", "", "Input path to the file to be stabilized.")
outfile = flag.String("outfile", "", "Output path to which the stabilized file will be written.")
enablePasses = flag.String("enable-passes", "all", "Enable the comma-separated set of stabilizers or 'all'. -help for full list of options")
disablePasses = flag.String("disable-passes", "none", "Disable only the comma-separated set of stabilizers or 'none'. -help for full list of options")
)

func getName(san any) string {
switch san.(type) {
case archive.TarArchiveStabilizer:
return san.(archive.TarArchiveStabilizer).Name
case archive.TarEntryStabilizer:
return san.(archive.TarEntryStabilizer).Name
case archive.ZipArchiveStabilizer:
return san.(archive.ZipArchiveStabilizer).Name
case archive.ZipEntryStabilizer:
return san.(archive.ZipEntryStabilizer).Name
default:
log.Fatalf("unknown sanitizer type: %T", san)
return "" // unreachable
}
}

func filetype(path string) archive.Format {
ext := filepath.Ext(path)
switch ext {
case ".tar":
return archive.TarFormat
case ".tgz", ".crate", ".gz", ".Z":
return archive.TarGzFormat
case ".zip", ".whl", ".egg", ".jar":
return archive.ZipFormat
default:
return archive.RawFormat
}
}

type StabilizerRegistry struct {
sanitizers []any
byName map[string]any
}

func NewStabilizerRegistry(sans ...any) StabilizerRegistry {
reg := StabilizerRegistry{sanitizers: sans}
reg.byName = make(map[string]any)
for _, san := range reg.sanitizers {
reg.byName[getName(san)] = san
}
return reg
}

func (reg StabilizerRegistry) Get(name string) (any, bool) {
val, ok := reg.byName[name]
return val, ok
}

func (reg StabilizerRegistry) GetAll() []any {
return reg.sanitizers[:]
}

// determinePasses returns the passes specified with the given pass specs.
//
// - Preserves the order specified in enableSpec. Order of "all" is impl-defined.
// - Disable has precedence over enable.
// - Duplicates are retained and respected.
func determinePasses(sanitizers StabilizerRegistry, enableSpec, disableSpec string) ([]any, error) {
var toRun []any
enabled := make(map[string]bool)
if *enablePasses == "all" {
for _, pass := range sanitizers.GetAll() {
toRun = append(toRun, pass)
enabled[getName(pass)] = true
}
} else {
for _, name := range strings.Split(enableSpec, ",") {
cleanName := strings.TrimSpace(name)
if san, ok := sanitizers.Get(cleanName); !ok {
return nil, fmt.Errorf("unknown pass name: %s", cleanName)
} else {
toRun = append(toRun, san)
enabled[cleanName] = true
}
}
}
if *disablePasses != "none" {
if *disablePasses == "all" {
clear(enabled)
} else {
for _, name := range strings.Split(disableSpec, ",") {
cleanName := strings.TrimSpace(name)
if _, ok := sanitizers.Get(cleanName); !ok {
return nil, fmt.Errorf("unknown pass name: %s", cleanName)
}
if _, ok := enabled[cleanName]; ok {
delete(enabled, cleanName)
}
}
}
// Apply deletions from "enabled".
toRun = slices.DeleteFunc(toRun, func(san any) bool {
_, ok := enabled[getName(san)]
return !ok
})
}
return toRun, nil
}

func run() error {
sanitizers := NewStabilizerRegistry(archive.AllStabilizers...)

// Update usage to include available passes.
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nAvailable stabilizers (in default order of application):\n")
for _, san := range archive.AllStabilizers {
fmt.Fprintf(os.Stderr, " - %s\n", getName(san))
}
}

flag.Parse()

if *infile == "" || *outfile == "" {
flag.Usage()
return errors.New("both -infile and -outfile are required")
}
toRun, err := determinePasses(sanitizers, *enablePasses, *disablePasses)
if err != nil {
flag.Usage()
return err
}

in, err := os.Open(*infile)
if err != nil {
return errors.Wrap(err, "opening input file")
}
defer in.Close()

out, err := os.Create(*outfile)
if err != nil {
return errors.Wrap(err, "creating output file")
}
defer out.Close()

err = archive.StabilizeWithOpts(out, in, filetype(*infile), archive.StabilizeOpts{Stabilizers: toRun})
return errors.Wrap(err, "canonicalizing file")
}

func main() {
if err := run(); err != nil {
log.Fatalf("Error: %v", err)
}
}
13 changes: 11 additions & 2 deletions pkg/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ import (

var AllStabilizers = append(AllZipStabilizers, AllTarStabilizers...)

// Stabilize selects and applies the stabilization routine for the given archive format.
// Stabilize selects and applies the default stabilization routine for the given archive format.
func Stabilize(dst io.Writer, src io.Reader, f Format) error {
opts := StabilizeOpts{Stabilizers: AllStabilizers}
return StabilizeWithOpts(dst, src, f, StabilizeOpts{Stabilizers: AllStabilizers})
}

// StabilizeWithOpts selects and applies the provided stabilization routine for the given archive format.
func StabilizeWithOpts(dst io.Writer, src io.Reader, f Format, opts StabilizeOpts) error {
switch f {
case ZipFormat:
srcReader, size, err := toZipCompatibleReader(src)
Expand All @@ -53,6 +57,11 @@ func Stabilize(dst io.Writer, src io.Reader, f Format) error {
gzw := gzip.NewWriter(dst)
defer gzw.Close()
err = StabilizeTar(tar.NewReader(gzr), tar.NewWriter(gzw), opts)
if err != nil {
return errors.Wrap(err, "stabilizing tar.gz")
}
case TarFormat:
err := StabilizeTar(tar.NewReader(src), tar.NewWriter(dst), opts)
if err != nil {
return errors.Wrap(err, "stabilizing tar")
}
Expand Down

0 comments on commit ada844e

Please sign in to comment.