...

Source file src/edge-infra.dev/pkg/f8n/warehouse/lift/cmd/internal/packer.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/lift/cmd/internal

     1  package internal
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"edge-infra.dev/pkg/f8n/warehouse/lift"
    10  	"edge-infra.dev/pkg/f8n/warehouse/lift/pack"
    11  	"edge-infra.dev/pkg/f8n/warehouse/lift/pack/filters"
    12  	"edge-infra.dev/pkg/f8n/warehouse/lift/pack/filters/transformers/bazel"
    13  	"edge-infra.dev/pkg/f8n/warehouse/oci/validate"
    14  	"edge-infra.dev/pkg/f8n/warehouse/pallet"
    15  	"edge-infra.dev/pkg/lib/build"
    16  	"edge-infra.dev/pkg/lib/build/git"
    17  	"edge-infra.dev/pkg/lib/cli/rags"
    18  	"edge-infra.dev/pkg/lib/cli/sink"
    19  )
    20  
    21  type Packer struct {
    22  	Tags []string
    23  	tag  string
    24  
    25  	// required by pkg/lib/build.Version
    26  	rc       bool
    27  	version  string
    28  	source   string
    29  	revision string
    30  	created  int64
    31  
    32  	// filters
    33  	bazel bazel.Filter
    34  
    35  	// embedded instance of package builder, computed after flags are parsed
    36  	*pack.Packer
    37  
    38  	Config *lift.Config
    39  }
    40  
    41  func NewPacker(cfg lift.Config) *Packer {
    42  	return &Packer{Config: &cfg}
    43  }
    44  
    45  func (p *Packer) RegisterFlags(fs *rags.RagSet) {
    46  	fs.StringVar(&p.tag, "tag", "latest", "comma separated list of tags to apply to built pallets")
    47  
    48  	// BuildInfo flags
    49  	fs.StringVar(&p.version, "version", "", "valid semver version to apply to pallets")
    50  	fs.StringVar(&p.source, "source", "", "HTTPS url path to relevant source code")
    51  	fs.StringVar(&p.revision, "revision", "", "the full commit hash for relevant source code")
    52  	fs.Int64Var(&p.created, "created", 0, "time the revision was authored, in seconds since the Unix epoch (https://reproducible-builds.org/docs/source-date-epoch/)")
    53  	fs.BoolVar(&p.rc, "rc", true, "is the build a release candidate")
    54  
    55  	// bazel container pushing integration flags
    56  	p.bazel.RegisterFlags(fs)
    57  }
    58  
    59  func (p *Packer) BeforeRun(ctx context.Context, r sink.Run) (context.Context, sink.Run, error) {
    60  	buildInfo, err := p.parseBuildInfo()
    61  	if err != nil {
    62  		return ctx, r, err
    63  	}
    64  
    65  	p.Tags = strings.Split(p.tag, ",")
    66  
    67  	p.Packer, err = pack.New(pack.Context{
    68  		Config:  p.Config,
    69  		Filters: []filters.Filter{&p.bazel},
    70  	}, buildInfo)
    71  
    72  	return ctx, r, err
    73  }
    74  
    75  func (p *Packer) parseBuildInfo() (pallet.BuildInfo, error) {
    76  	var (
    77  		err error
    78  		v   build.Version
    79  	)
    80  
    81  	// Infer BuildInfo from git repo if any component is empty
    82  	g := git.New()
    83  
    84  	commit := p.revision
    85  	if commit == "" {
    86  		commit, err = g.Commit()
    87  		if err != nil {
    88  			return pallet.BuildInfo{}, err
    89  		}
    90  	}
    91  
    92  	source := p.source
    93  	if source == "" {
    94  		remote, err := g.Remote()
    95  		if err != nil {
    96  			return pallet.BuildInfo{}, err
    97  		}
    98  
    99  		// handle ssh url
   100  		sshPrefix := "git@github.com:"
   101  		if strings.HasPrefix(remote, sshPrefix) {
   102  			remote = strings.TrimPrefix(remote, sshPrefix)
   103  			remote = fmt.Sprintf("https://github.com/%s", remote)
   104  		}
   105  		source = fmt.Sprintf("%s/tree/%s", remote, commit)
   106  	}
   107  
   108  	var date time.Time
   109  	created := p.created
   110  	if created <= 0 {
   111  		date, err = g.Timestamp(commit)
   112  		if err != nil {
   113  			return pallet.BuildInfo{}, err
   114  		}
   115  		created = date.Unix()
   116  	} else {
   117  		date = time.Unix(created, 0).UTC()
   118  	}
   119  
   120  	v = build.Version{
   121  		Commit:           commit,
   122  		ReleaseCandidate: p.rc,
   123  		Timestamp:        created,
   124  	}
   125  
   126  	version := p.version
   127  	if version == "" {
   128  		path, err := g.Path()
   129  		if err != nil {
   130  			return pallet.BuildInfo{}, err
   131  		}
   132  		semver, err := build.LoadVersion(path)
   133  		if err != nil {
   134  			return pallet.BuildInfo{}, err
   135  		}
   136  		version = semver
   137  	}
   138  	v.SemVer = version
   139  
   140  	if err := v.Validate(); err != nil {
   141  		return pallet.BuildInfo{}, err
   142  	}
   143  
   144  	buildInfo := pallet.BuildInfo{
   145  		Source:   source,
   146  		Version:  v.String(),
   147  		Created:  date.Format(time.RFC3339),
   148  		Revision: commit,
   149  	}
   150  
   151  	if err = validate.BuildInfo(buildInfo); err != nil {
   152  		return pallet.BuildInfo{}, fmt.Errorf("invalid build info: %w", err)
   153  	}
   154  
   155  	return buildInfo, nil
   156  }
   157  

View as plain text