in container_images/registry-image-forked/commands/out.go [48:191]
func (o *Out) Execute() error {
setupLogging(o.stderr)
var req resource.OutRequest
decoder := json.NewDecoder(o.stdin)
decoder.DisallowUnknownFields()
err := decoder.Decode(&req)
if err != nil {
return fmt.Errorf("invalid payload: %s", err)
}
if req.Source.Debug {
logrus.SetLevel(logrus.DebugLevel)
}
if len(o.args) < 2 {
return fmt.Errorf("destination path not specified")
}
src := o.args[1]
if req.Source.AwsAccessKeyID != "" && req.Source.AwsSecretAccessKey != "" && req.Source.AwsRegion != "" {
if !req.Source.AuthenticateToECR() {
return fmt.Errorf("cannot authenticate with ECR")
}
}
tagsToPush := []name.Tag{}
repo, err := req.Source.NewRepository()
if err != nil {
return fmt.Errorf("could not resolve repository: %w", err)
}
if req.Source.Tag != "" {
tagsToPush = append(tagsToPush, repo.Tag(req.Source.Tag.String()))
}
if req.Params.Version != "" {
ver, err := semver.NewVersion(req.Params.Version)
if err != nil {
if err == semver.ErrInvalidSemVer {
return fmt.Errorf("invalid semantic version: %q", req.Params.Version)
}
return fmt.Errorf("failed to parse version: %w", err)
}
// vito: subtle gotcha here - if someone passes the version as v1.2.3, the
// 'v' will be stripped, as *semver.Version parses it but does not preserve
// it in .String().
//
// we could call .Original(), of course, but it seems common practice to
// *not* have the v prefix in Docker image tags, so it might be better to
// just enforce it until someone complains enough; it seems more likely to
// be an accident than a legacy practice that must be preserved.
//
// if that's the person reading this: sorry! PR welcome! (maybe we should
// add tag_prefix:?)
tag := ver.String()
if req.Source.Variant != "" {
tag += "-" + req.Source.Variant
}
tagsToPush = append(tagsToPush, repo.Tag(tag))
if req.Params.BumpAliases && ver.Prerelease() == "" {
aliasTags, err := aliasesToBump(req, repo, ver)
if err != nil {
return fmt.Errorf("determine aliases: %w", err)
}
tagsToPush = append(tagsToPush, aliasTags...)
}
}
additionalTags, err := req.Params.ParseAdditionalTags(src)
if err != nil {
return fmt.Errorf("could not parse additional tags: %w", err)
}
for _, tagName := range additionalTags {
tag, err := name.NewTag(fmt.Sprintf("%s:%s", req.Source.Repository, tagName))
if err != nil {
return fmt.Errorf("could not resolve repository/tag reference: %w", err)
}
tagsToPush = append(tagsToPush, tag)
}
if len(tagsToPush) == 0 {
return fmt.Errorf("no tag specified - need either 'version:' in params or 'tag:' in source")
}
imagePath := filepath.Join(src, req.Params.Image)
matches, err := filepath.Glob(imagePath)
if err != nil {
return fmt.Errorf("failed to glob path '%s': %w", req.Params.Image, err)
}
if len(matches) == 0 {
return fmt.Errorf("no files match glob '%s'", req.Params.Image)
}
if len(matches) > 1 {
return fmt.Errorf("too many files match glob '%s': %v", req.Params.Image, matches)
}
img, err := tarball.ImageFromPath(matches[0], nil)
if err != nil {
return fmt.Errorf("could not load image from path '%s': %w", req.Params.Image, err)
}
digest, err := img.Digest()
if err != nil {
return fmt.Errorf("failed to get image digest: %w", err)
}
err = resource.RetryOnRateLimit(func() error {
return put(req, img, tagsToPush)
})
if err != nil {
return fmt.Errorf("pushing image failed: %w", err)
}
pushedTags := []string{}
for _, tag := range tagsToPush {
pushedTags = append(pushedTags, tag.TagStr())
}
err = json.NewEncoder(os.Stdout).Encode(resource.OutResponse{
Version: resource.Version{
Tag: tagsToPush[0].TagStr(),
Digest: digest.String(),
},
Metadata: append(req.Source.Metadata(), resource.MetadataField{
Name: "tags",
Value: strings.Join(pushedTags, " "),
}),
})
if err != nil {
return fmt.Errorf("could not marshal JSON: %s", err)
}
return nil
}