github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/client/register_buildpack.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/url"
     7  	"runtime"
     8  	"strings"
     9  
    10  	"github.com/buildpacks/pack/internal/registry"
    11  	"github.com/buildpacks/pack/pkg/buildpack"
    12  	"github.com/buildpacks/pack/pkg/dist"
    13  	"github.com/buildpacks/pack/pkg/image"
    14  )
    15  
    16  // RegisterBuildpackOptions is a configuration struct that controls the
    17  // behavior of the RegisterBuildpack function.
    18  type RegisterBuildpackOptions struct {
    19  	ImageName string
    20  	Type      string
    21  	URL       string
    22  	Name      string
    23  }
    24  
    25  // RegisterBuildpack updates the Buildpack Registry with to include a new buildpack specified in
    26  // the opts argument
    27  func (c *Client) RegisterBuildpack(ctx context.Context, opts RegisterBuildpackOptions) error {
    28  	appImage, err := c.imageFetcher.Fetch(ctx, opts.ImageName, image.FetchOptions{Daemon: false, PullPolicy: image.PullAlways})
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	var buildpackInfo dist.ModuleInfo
    34  	if _, err := dist.GetLabel(appImage, buildpack.MetadataLabel, &buildpackInfo); err != nil {
    35  		return err
    36  	}
    37  
    38  	namespace, name, err := parseID(buildpackInfo.ID)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	id, err := appImage.Identifier()
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	buildpack := registry.Buildpack{
    49  		Namespace: namespace,
    50  		Name:      name,
    51  		Version:   buildpackInfo.Version,
    52  		Address:   id.String(),
    53  		Yanked:    false,
    54  	}
    55  
    56  	if opts.Type == "github" {
    57  		issueURL, err := registry.GetIssueURL(opts.URL)
    58  		if err != nil {
    59  			return err
    60  		}
    61  
    62  		issue, err := registry.CreateGithubIssue(buildpack)
    63  		if err != nil {
    64  			return err
    65  		}
    66  
    67  		params := url.Values{}
    68  		params.Add("title", issue.Title)
    69  		params.Add("body", issue.Body)
    70  		issueURL.RawQuery = params.Encode()
    71  
    72  		c.logger.Debugf("Open URL in browser: %s", issueURL)
    73  		cmd, err := registry.CreateBrowserCmd(issueURL.String(), runtime.GOOS)
    74  		if err != nil {
    75  			return err
    76  		}
    77  
    78  		return cmd.Start()
    79  	} else if opts.Type == "git" {
    80  		registryCache, err := getRegistry(c.logger, opts.Name)
    81  		if err != nil {
    82  			return err
    83  		}
    84  
    85  		username, err := parseUsernameFromURL(opts.URL)
    86  		if err != nil {
    87  			return err
    88  		}
    89  
    90  		if err := registry.GitCommit(buildpack, username, registryCache); err != nil {
    91  			return err
    92  		}
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  func parseUsernameFromURL(url string) (string, error) {
    99  	parts := strings.Split(url, "/")
   100  	if len(parts) < 3 {
   101  		return "", errors.New("invalid url: cannot parse username from url")
   102  	}
   103  	if parts[3] == "" {
   104  		return "", errors.New("invalid url: username is empty")
   105  	}
   106  
   107  	return parts[3], nil
   108  }
   109  
   110  func parseID(id string) (string, string, error) {
   111  	parts := strings.Split(id, "/")
   112  	if len(parts) < 2 {
   113  		return "", "", errors.New("invalid id: does not contain a namespace")
   114  	} else if len(parts) > 2 {
   115  		return "", "", errors.New("invalid id: contains unexpected characters")
   116  	}
   117  
   118  	return parts[0], parts[1], nil
   119  }