diff --git a/.golangci.yml b/.golangci.yml index 34fe368..deed852 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -75,6 +75,9 @@ issues: - source: "`json:" linters: - lll + - source: "`yaml:" + linters: + - lll run: skip-dirs: diff --git a/build-emacs-for-macos b/build-emacs-for-macos index 73d7468..59a8fb7 100755 --- a/build-emacs-for-macos +++ b/build-emacs-for-macos @@ -53,7 +53,7 @@ class OSVersion end def to_s - @to_s ||= "#{major}.#{minor}" + @to_s ||= major >= 11 ? major.to_s : "#{major}.#{minor}" end def major diff --git a/pkg/cask/release_info.go b/pkg/cask/release_info.go index 35d3ed4..e060613 100644 --- a/pkg/cask/release_info.go +++ b/pkg/cask/release_info.go @@ -11,9 +11,11 @@ type ReleaseInfo struct { Assets map[string]*ReleaseAsset } -func (s *ReleaseInfo) Asset(nameMatch string) *ReleaseAsset { - if a, ok := s.Assets[nameMatch]; ok { - return a +func (s *ReleaseInfo) Asset(needles ...string) *ReleaseAsset { + if len(needles) == 1 { + if a, ok := s.Assets[needles[0]]; ok { + return a + } } // Dirty and inefficient way to ensure assets are searched in a predictable @@ -27,16 +29,20 @@ func (s *ReleaseInfo) Asset(nameMatch string) *ReleaseAsset { }) for _, a := range assets { - if strings.Contains(a.Filename, nameMatch) { - return a + for _, needle := range needles { + if !strings.Contains(a.Filename, needle) { + continue + } } + + return a } return nil } -func (s *ReleaseInfo) DownloadURL(nameMatch string) string { - a := s.Asset(nameMatch) +func (s *ReleaseInfo) DownloadURL(needles ...string) string { + a := s.Asset(needles...) if a == nil { return "" } @@ -44,8 +50,8 @@ func (s *ReleaseInfo) DownloadURL(nameMatch string) string { return a.DownloadURL } -func (s *ReleaseInfo) SHA256(nameMatch string) string { - a := s.Asset(nameMatch) +func (s *ReleaseInfo) SHA256(needles ...string) string { + a := s.Asset(needles...) if a == nil { return "" } diff --git a/pkg/cli/cask.go b/pkg/cli/cask.go index dc16b3c..fd47541 100644 --- a/pkg/cli/cask.go +++ b/pkg/cli/cask.go @@ -112,7 +112,7 @@ func caskUpdateCmd() *cli2.Command { func caskUpdateAction( c *cli2.Context, - opts *Options, + _ *Options, cOpts *caskOptions, ) error { updateOpts := &cask.UpdateOptions{ diff --git a/pkg/cli/notarize.go b/pkg/cli/notarize.go index 1abc44f..794f90d 100644 --- a/pkg/cli/notarize.go +++ b/pkg/cli/notarize.go @@ -52,7 +52,7 @@ func notarizeCmd() *cli2.Command { } } -func notarizeAction(c *cli2.Context, opts *Options) error { +func notarizeAction(c *cli2.Context, _ *Options) error { options := ¬arize.Options{ File: c.Args().Get(0), BundleID: c.String("bundle-id"), diff --git a/pkg/cli/plan.go b/pkg/cli/plan.go index 32df157..cfae4c2 100644 --- a/pkg/cli/plan.go +++ b/pkg/cli/plan.go @@ -1,6 +1,7 @@ package cli import ( + "fmt" "os" "path/filepath" @@ -37,6 +38,12 @@ func planCmd() *cli2.Command { Name: "sha", Usage: "override commit SHA of specified git branch/tag", }, + &cli2.StringFlag{ + Name: "format", + Aliases: []string{"f"}, + Usage: "output format of build plan (yaml or json)", + Value: "yaml", + }, &cli2.StringFlag{ Name: "output", Usage: "output filename to write plan to instead of printing " + @@ -102,7 +109,18 @@ func planAction(c *cli2.Context, opts *Options) error { return err } - planYAML, err := p.YAML() + format := c.String("format") + var plan string + switch format { + case "yaml", "yml": + format = "yaml" + plan, err = p.YAML() + case "json": + format = "json" + plan, err = p.JSON() + default: + err = fmt.Errorf("--format must be yaml or json") + } if err != nil { return err } @@ -111,7 +129,7 @@ func planAction(c *cli2.Context, opts *Options) error { out = os.Stdout if f := c.String("output"); f != "" { logger.Info("writing plan", "file", f) - logger.Debug("content", "yaml", planYAML) + logger.Debug("content", format, plan) out, err = os.Create(f) if err != nil { return err @@ -119,7 +137,7 @@ func planAction(c *cli2.Context, opts *Options) error { defer out.Close() } - _, err = out.WriteString(planYAML) + _, err = out.WriteString(plan) if err != nil { return err } diff --git a/pkg/cli/release.go b/pkg/cli/release.go index f73c6b5..4de46d8 100644 --- a/pkg/cli/release.go +++ b/pkg/cli/release.go @@ -43,7 +43,7 @@ func releaseCmd() *cli2.Command { Usage: "owner/name of GitHub repo to check for release, " + "ignored if a plan is provided", EnvVars: []string{"GITHUB_REPOSITORY"}, - Value: "jimeh/emacs-builds", + Value: "", }, &cli2.StringFlag{ Name: "name", @@ -108,7 +108,7 @@ func releaseCheckCmd() *cli2.Command { func releaseCheckAction( c *cli2.Context, - opts *Options, + _ *Options, rOpts *releaseOptions, ) error { rlsOpts := &release.CheckOptions{ @@ -153,6 +153,12 @@ func releasePublishCmd() *cli2.Command { "specified", Value: "", }, + &cli2.BoolFlag{ + Name: "asset-size-check", + Usage: "Do not replace existing asset files if local and " + + "remote file sizes match.", + Value: false, + }, }, Action: releaseActionWrapper(releasePublishAction), } @@ -160,16 +166,17 @@ func releasePublishCmd() *cli2.Command { func releasePublishAction( c *cli2.Context, - opts *Options, + _ *Options, rOpts *releaseOptions, ) error { rlsOpts := &release.PublishOptions{ - Repository: rOpts.Repository, - CommitRef: c.String("release-sha"), - ReleaseName: rOpts.Name, - ReleaseTitle: c.String("title"), - AssetFiles: c.Args().Slice(), - GithubToken: rOpts.GithubToken, + Repository: rOpts.Repository, + CommitRef: c.String("release-sha"), + ReleaseName: rOpts.Name, + ReleaseTitle: c.String("title"), + AssetFiles: c.Args().Slice(), + AssetSizeCheck: c.Bool("asset-size-check"), + GithubToken: rOpts.GithubToken, } rlsType := c.String("type") @@ -184,7 +191,13 @@ func releasePublishAction( return fmt.Errorf("invalid --type \"%s\"", rlsType) } + if c.Args().Len() > 0 { + rlsOpts.AssetFiles = c.Args().Slice() + } + if rOpts.Plan != nil { + rlsOpts.Source = rOpts.Plan.Source + if rOpts.Plan.Release != nil { rlsOpts.ReleaseName = rOpts.Plan.Release.Name rlsOpts.ReleaseTitle = rOpts.Plan.Release.Title @@ -196,7 +209,8 @@ func releasePublishAction( } } - if rOpts.Plan.Output != nil { + // Set asset files based on plan if no file arguments were given. + if len(rlsOpts.AssetFiles) == 0 && rOpts.Plan.Output != nil { rlsOpts.AssetFiles = []string{ filepath.Join( rOpts.Plan.Output.Directory, @@ -235,7 +249,7 @@ func releaseBulkCmd() *cli2.Command { func releaseBulkAction( c *cli2.Context, - opts *Options, + _ *Options, rOpts *releaseOptions, ) error { bulkOpts := &release.BulkOptions{ diff --git a/pkg/commit/commit.go b/pkg/commit/commit.go index f7d4b79..673386c 100644 --- a/pkg/commit/commit.go +++ b/pkg/commit/commit.go @@ -8,11 +8,11 @@ import ( ) type Commit struct { - SHA string `yaml:"sha"` - Date *time.Time `yaml:"date"` - Author string `yaml:"author"` - Committer string `yaml:"committer"` - Message string `yaml:"message"` + SHA string `yaml:"sha" json:"sha"` + Date *time.Time `yaml:"date" json:"date"` + Author string `yaml:"author" json:"author"` + Committer string `yaml:"committer" json:"committer"` + Message string `yaml:"message" json:"message"` } func New(rc *github.RepositoryCommit) *Commit { diff --git a/pkg/osinfo/osinfo.go b/pkg/osinfo/osinfo.go index e8bca96..31d35b8 100644 --- a/pkg/osinfo/osinfo.go +++ b/pkg/osinfo/osinfo.go @@ -2,13 +2,14 @@ package osinfo import ( "os/exec" + "strconv" "strings" ) type OSInfo struct { - Name string `yaml:"name"` - Version string `yaml:"version"` - Arch string `yaml:"arch"` + Name string `yaml:"name" json:"name"` + Version string `yaml:"version" json:"version"` + Arch string `yaml:"arch" json:"arch"` } func New() (*OSInfo, error) { @@ -29,8 +30,17 @@ func New() (*OSInfo, error) { }, nil } -func (s *OSInfo) MajorMinor() string { +// DistinctVersion returns macOS version down to a distinct "major" +// version. For macOS 10.x, this will include the first two numeric parts of the +// version (10.15), while for 11.x and later, the first numeric part is enough +// (11). +func (s *OSInfo) DistinctVersion() string { parts := strings.Split(s.Version, ".") + + if n, _ := strconv.Atoi(parts[0]); n >= 11 { + return parts[0] + } + max := len(parts) if max > 2 { max = 2 diff --git a/pkg/plan/create.go b/pkg/plan/create.go index fbef7eb..d9abe05 100644 --- a/pkg/plan/create.go +++ b/pkg/plan/create.go @@ -13,6 +13,7 @@ import ( "github.com/jimeh/build-emacs-for-macos/pkg/osinfo" "github.com/jimeh/build-emacs-for-macos/pkg/release" "github.com/jimeh/build-emacs-for-macos/pkg/repository" + "github.com/jimeh/build-emacs-for-macos/pkg/source" ) var nonAlphaNum = regexp.MustCompile(`[^\w_-]+`) @@ -76,7 +77,7 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { buildName := fmt.Sprintf( "Emacs.%s.%s.%s", version, - sanitizeString(osInfo.Name+"-"+osInfo.MajorMinor()), + sanitizeString(osInfo.Name+"-"+osInfo.DistinctVersion()), sanitizeString(osInfo.Arch), ) diskImage := buildName + ".dmg" @@ -85,11 +86,11 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { Build: &Build{ Name: buildName, }, - Source: &Source{ + Source: &source.Source{ Ref: opts.Ref, Repository: repo, Commit: commitInfo, - Tarball: &Tarball{ + Tarball: &source.Tarball{ URL: repo.TarballURL(commitInfo.SHA), }, }, diff --git a/pkg/plan/plan.go b/pkg/plan/plan.go index e33181a..7d119e2 100644 --- a/pkg/plan/plan.go +++ b/pkg/plan/plan.go @@ -2,21 +2,21 @@ package plan import ( "bytes" + "encoding/json" "io" "os" - "github.com/jimeh/build-emacs-for-macos/pkg/commit" "github.com/jimeh/build-emacs-for-macos/pkg/osinfo" - "github.com/jimeh/build-emacs-for-macos/pkg/repository" + "github.com/jimeh/build-emacs-for-macos/pkg/source" "gopkg.in/yaml.v3" ) type Plan struct { - Build *Build `yaml:"build,omitempty"` - Source *Source `yaml:"source,omitempty"` - OS *osinfo.OSInfo `yaml:"os,omitempty"` - Release *Release `yaml:"release,omitempty"` - Output *Output `yaml:"output,omitempty"` + Build *Build `yaml:"build,omitempty" json:"build,omitempty"` + Source *source.Source `yaml:"source,omitempty" json:"source,omitempty"` + OS *osinfo.OSInfo `yaml:"os,omitempty" json:"os,omitempty"` + Release *Release `yaml:"release,omitempty" json:"release,omitempty"` + Output *Output `yaml:"output,omitempty" json:"output,omitempty"` } // Load attempts to loads a plan YAML from given filename. @@ -54,29 +54,37 @@ func (s *Plan) YAML() (string, error) { return buf.String(), nil } +// WriteJSON writes plan in JSON format to given io.Writer. +func (s *Plan) WriteJSON(w io.Writer) error { + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + + return enc.Encode(s) +} + +// JSON returns plan in JSON format. +func (s *Plan) JSON() (string, error) { + var buf bytes.Buffer + err := s.WriteJSON(&buf) + if err != nil { + return "", err + } + + return buf.String(), nil +} + type Build struct { - Name string `yaml:"name,omitempty"` -} - -type Source struct { - Ref string `yaml:"ref,omitempty"` - Repository *repository.Repository `yaml:"repository,omitempty"` - Commit *commit.Commit `yaml:"commit,omitempty"` - Tarball *Tarball `yaml:"tarball,omitempty"` -} - -type Tarball struct { - URL string `yaml:"url,omitempty"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` } type Release struct { - Name string `yaml:"name"` - Title string `yaml:"title,omitempty"` - Draft bool `yaml:"draft,omitempty"` - Prerelease bool `yaml:"prerelease,omitempty"` + Name string `yaml:"name" json:"name"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Draft bool `yaml:"draft,omitempty" json:"draft,omitempty"` + Prerelease bool `yaml:"prerelease,omitempty" json:"prerelease,omitempty"` } type Output struct { - Directory string `yaml:"directory,omitempty"` - DiskImage string `yaml:"disk_image,omitempty"` + Directory string `yaml:"directory,omitempty" json:"directory,omitempty"` + DiskImage string `yaml:"disk_image,omitempty" json:"disk_image,omitempty"` } diff --git a/pkg/release/publish.go b/pkg/release/publish.go index d96f675..81ae6fd 100644 --- a/pkg/release/publish.go +++ b/pkg/release/publish.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/jimeh/build-emacs-for-macos/pkg/gh" "github.com/jimeh/build-emacs-for-macos/pkg/repository" + "github.com/jimeh/build-emacs-for-macos/pkg/source" ) type releaseType int @@ -40,15 +41,26 @@ type PublishOptions struct { // draft) ReleaseType releaseType + // Source contains the source used to build the asset files. When set a + // release body/description text will be generated based on source commit + // details. + Source *source.Source + // AssetFiles is a list of files which must all exist in the release for // the check to pass. AssetFiles []string + // AssetSizeCheck causes a file size check for any existing asset files on a + // release which have the same filename as a asset we want to upload. If the + // size of the local and remote files are the same, the existing asset file + // is left in place. When this is false, given asset files will always be + // uploaded, replacing any asset files with the same filename. + AssetSizeCheck bool + // GitHubToken is the OAuth token used to talk to the GitHub API. GithubToken string } -//nolint:funlen,gocyclo // Publish creates and publishes a GitHub release. func Publish(ctx context.Context, opts *PublishOptions) error { logger := hclog.FromContext(ctx).Named("release") @@ -68,6 +80,16 @@ func Publish(ctx context.Context, opts *PublishOptions) error { prerelease := opts.ReleaseType == Prerelease draft := opts.ReleaseType == Draft + body := "" + if opts.Source != nil { + body, err = releaseBody(opts) + if err != nil { + return err + } + logger.Debug("rendered release body", "content", body) + } + + created := false logger.Info("checking release", "tag", tagName) release, resp, err := gh.Repositories.GetReleaseByTag( ctx, opts.Repository.Owner(), opts.Repository.Name(), tagName, @@ -77,6 +99,7 @@ func Publish(ctx context.Context, opts *PublishOptions) error { return err } + created = true logger.Info("creating release", "tag", tagName, "name", name) release, _, err = gh.Repositories.CreateRelease( @@ -87,6 +110,7 @@ func Publish(ctx context.Context, opts *PublishOptions) error { TargetCommitish: &opts.CommitRef, Prerelease: boolPtr(false), Draft: boolPtr(true), + Body: &body, }, ) if err != nil { @@ -94,62 +118,9 @@ func Publish(ctx context.Context, opts *PublishOptions) error { } } - for _, fileName := range files { - fileIO, err2 := os.Open(fileName) - if err2 != nil { - return err2 - } - defer fileIO.Close() - - fileInfo, err2 := fileIO.Stat() - if err2 != nil { - return err2 - } - - fileBaseName := filepath.Base(fileName) - assetExists := false - - for _, a := range release.Assets { - if a.GetName() != fileBaseName { - continue - } - - if a.GetSize() == int(fileInfo.Size()) { - logger.Info("asset exists with correct size", - "file", fileBaseName, - "local_size", byteCountIEC(fileInfo.Size()), - "remote_size", byteCountIEC(int64(a.GetSize())), - ) - assetExists = true - } else { - _, err = gh.Repositories.DeleteReleaseAsset( - ctx, opts.Repository.Owner(), opts.Repository.Name(), - a.GetID(), - ) - if err != nil { - return err - } - logger.Info( - "deleted asset with wrong size", "file", fileBaseName, - ) - } - } - - if !assetExists { - logger.Info("uploading asset", - "file", fileBaseName, - "size", byteCountIEC(fileInfo.Size()), - ) - _, _, err2 = gh.Repositories.UploadReleaseAsset( - ctx, opts.Repository.Owner(), opts.Repository.Name(), - release.GetID(), - &github.UploadOptions{Name: fileBaseName}, - fileIO, - ) - if err2 != nil { - return err2 - } - } + err = uploadReleaseAssets(ctx, gh, release, files, opts) + if err != nil { + return err } changed := false @@ -158,6 +129,11 @@ func Publish(ctx context.Context, opts *PublishOptions) error { changed = true } + if body != "" && release.GetBody() != body { + release.Body = &body + changed = true + } + if release.GetDraft() != draft { release.Draft = &draft changed = true @@ -169,6 +145,7 @@ func Publish(ctx context.Context, opts *PublishOptions) error { } if changed { + logger.Info("updating release attributes", "url", release.GetHTMLURL()) release, _, err = gh.Repositories.EditRelease( ctx, opts.Repository.Owner(), opts.Repository.Name(), release.GetID(), release, @@ -178,13 +155,89 @@ func Publish(ctx context.Context, opts *PublishOptions) error { } } - logger.Info("release created", "url", release.GetHTMLURL()) + if created { + logger.Info("release created", "url", release.GetHTMLURL()) + } else { + logger.Info("release updated", "url", release.GetHTMLURL()) + } + + return nil +} + +func uploadReleaseAssets( + ctx context.Context, + gh *github.Client, + release *github.RepositoryRelease, + fileNames []string, + opts *PublishOptions, +) error { + logger := hclog.FromContext(ctx).Named("release") + + for _, fileName := range fileNames { + logger.Debug("processing asset", "file", filepath.Base(fileName)) + + fileIO, err := os.Open(fileName) + if err != nil { + return err + } + defer fileIO.Close() + + fileInfo, err := fileIO.Stat() + if err != nil { + return err + } + + fileBaseName := filepath.Base(fileName) + assetExists := false + + for _, a := range release.Assets { + if a.GetName() != fileBaseName { + continue + } + + if opts.AssetSizeCheck && a.GetSize() == int(fileInfo.Size()) { + logger.Info("asset exists with correct size", + "file", fileBaseName, + "local_size", byteCountIEC(fileInfo.Size()), + "remote_size", byteCountIEC(int64(a.GetSize())), + ) + assetExists = true + } else { + logger.Info( + "deleting existing asset", "file", fileBaseName, + ) + _, err = gh.Repositories.DeleteReleaseAsset( + ctx, opts.Repository.Owner(), opts.Repository.Name(), + a.GetID(), + ) + if err != nil { + return err + } + } + } + + if !assetExists { + logger.Info("uploading asset", + "file", fileBaseName, + "size", byteCountIEC(fileInfo.Size()), + ) + _, _, err = gh.Repositories.UploadReleaseAsset( + ctx, opts.Repository.Owner(), opts.Repository.Name(), + release.GetID(), + &github.UploadOptions{Name: fileBaseName}, + fileIO, + ) + if err != nil { + return err + } + } + } return nil } func publishFileList(files []string) ([]string, error) { - var output []string + results := map[string]struct{}{} for _, file := range files { var err error file, err = filepath.Abs(file) @@ -200,11 +253,10 @@ func publishFileList(files []string) ([]string, error) { return nil, fmt.Errorf("\"%s\" is not a file", file) } - output = append(output, file) + results[file] = struct{}{} sumFile := file + ".sha256" _, err = os.Stat(sumFile) - fmt.Printf("err: %+v\n", err) if err != nil { if os.IsNotExist(err) { continue @@ -212,7 +264,12 @@ func publishFileList(files []string) ([]string, error) { return nil, err } - output = append(output, sumFile) + results[sumFile] = struct{}{} + } + + var output []string + for f := range results { + output = append(output, f) } return output, nil diff --git a/pkg/release/release_body.go b/pkg/release/release_body.go new file mode 100644 index 0000000..b014075 --- /dev/null +++ b/pkg/release/release_body.go @@ -0,0 +1,80 @@ +package release + +import ( + "bytes" + "os" + "strings" + "text/template" +) + +var tplFuncs = template.FuncMap{ + "indent": func(n int, s string) string { + pad := strings.Repeat(" ", n) + + return pad + strings.ReplaceAll(s, "\n", "\n"+pad) + }, +} + +var bodyTpl = template.Must(template.New("body").Funcs(tplFuncs).Parse(` +{{- $t := "` + "`" + `" -}} +### Build Details + +{{ with .SourceURL -}} +- Source: {{ . }} +{{- end }} +{{- if and .CommitSHA .CommitURL }} +- Commit: [{{ $t }}{{ .CommitSHA }}{{ $t }}] + {{- if .CommitURL }}({{ .CommitURL }}){{ end }} +{{- else if and .CommitSHA }} +- Commit: {{ $t }}{{ .CommitSHA }}{{ $t }} +{{- end }} +{{- with .TarballURL }} +- Tarball: {{ . }} +{{- end }} +{{- with .BuildLogURL }} +- Build Log: {{ . }} (available for 90 days) +{{- end }}`, +)) + +type bodyData struct { + SourceURL string + CommitSHA string + CommitURL string + BuildLogURL string + TarballURL string +} + +func releaseBody(opts *PublishOptions) (string, error) { + src := opts.Source + + if src.Repository == nil || src.Commit == nil { + return "", nil + } + + data := &bodyData{ + SourceURL: src.Repository.TreeURL(src.Ref), + CommitSHA: src.Commit.SHA, + CommitURL: src.Repository.CommitURL(src.Commit.SHA), + TarballURL: src.Repository.TarballURL(src.Commit.SHA), + } + + // If available, use the exact value from the build plan. + if src.Tarball != nil { + data.TarballURL = src.Tarball.URL + } + + // If running within GitHub Actions, provide link to build log. + if opts.Repository != nil { + if id := os.Getenv("GITHUB_RUN_ID"); id != "" { + data.BuildLogURL = opts.Repository.ActionRunURL(id) + } + } + + var buf bytes.Buffer + err := bodyTpl.Execute(&buf, data) + if err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go index 98d5eeb..501fa28 100644 --- a/pkg/repository/repository.go +++ b/pkg/repository/repository.go @@ -22,8 +22,8 @@ const GitHub Type = "github" // Repository represents basic information about a repository with helper // methods to get various pieces of information from it. type Repository struct { - Type Type `yaml:"type,omitempty"` - Source string `yaml:"source,omitempty"` + Type Type `yaml:"type,omitempty" json:"type,omitempty"` + Source string `yaml:"source,omitempty" json:"source,omitempty"` } func NewGitHub(ownerAndName string) (*Repository, error) { @@ -89,3 +89,42 @@ func (s *Repository) TarballURL(ref string) string { return "" } } + +func (s *Repository) CommitURL(ref string) string { + if ref == "" { + return "" + } + + switch s.Type { + case GitHub: + return GitHubBaseURL + s.Source + "/commit/" + ref + default: + return "" + } +} + +func (s *Repository) TreeURL(ref string) string { + if ref == "" { + return "" + } + + switch s.Type { + case GitHub: + return GitHubBaseURL + s.Source + "/tree/" + ref + default: + return "" + } +} + +func (s *Repository) ActionRunURL(runID string) string { + if runID == "" { + return "" + } + + switch s.Type { + case GitHub: + return GitHubBaseURL + s.Source + "/actions/runs/" + runID + default: + return "" + } +} diff --git a/pkg/source/source.go b/pkg/source/source.go new file mode 100644 index 0000000..b315168 --- /dev/null +++ b/pkg/source/source.go @@ -0,0 +1,17 @@ +package source + +import ( + "github.com/jimeh/build-emacs-for-macos/pkg/commit" + "github.com/jimeh/build-emacs-for-macos/pkg/repository" +) + +type Source struct { + Ref string `yaml:"ref,omitempty" json:"ref,omitempty"` + Repository *repository.Repository `yaml:"repository,omitempty" json:"repository,omitempty"` + Commit *commit.Commit `yaml:"commit,omitempty" json:"commit,omitempty"` + Tarball *Tarball `yaml:"tarball,omitempty" json:"tarball,omitempty"` +} + +type Tarball struct { + URL string `yaml:"url,omitempty" json:"url,omitempty"` +}