package main import ( "context" "fmt" "os" "os/signal" "path/filepath" "regexp" "strconv" "strings" "sync" "git.geekeey.de/actions/sdk" ) func main() { action := &ReleaseAction{Action: sdk.New()} ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() if err := action.run(ctx); err != nil { action.Errorf("%s", err) os.Exit(1) } } type ReleaseAction struct { *sdk.Action repository string version string draft bool prerelease bool label string notes string artifacts []string } func (action *ReleaseAction) setup() error { var err error action.repository = action.GetInput("repository") if ok, err := regexp.MatchString(`^(\w+)/(\w+)$`, action.repository); !ok || err != nil { return fmt.Errorf("input 'repository': is empty or does not match /") } action.version = action.GetInput("version") if len(action.version) == 0 { return fmt.Errorf("input 'version': is empty") } pattern := func(name string) (bool, error) { input := action.GetInput(name) if len(input) == 0 { return false, fmt.Errorf("input %q: is empty", name) } value, err := strconv.ParseBool(input) if err != nil { expr, err := regexp.Compile(input) if err != nil { return false, fmt.Errorf("input %q: is not a bool or a valid regex", name) } value = expr.MatchString(action.version) } return value, nil } // draft might be a bool constant or a regex which determins if it is // a draft by matching against the input version action.draft, err = pattern("draft") if err != nil { return fmt.Errorf("input 'draft': is not a bool or a valid regex") } // prerelease might be a bool constant or a regex which determins if it is // a prelease by matching against the input version action.prerelease, err = pattern("prerelease") if err != nil { return fmt.Errorf("input 'prerelease': is not a bool or a valid regex") } action.label = action.GetInput("label") action.notes = action.GetInput("notes") // attachements can be a multline string, treat each line as a pattern patterns := strings.Split(strings.TrimSuffix(action.GetInput("attachments"), "\n"), "\n") action.artifacts = []string{} for _, pattern := range patterns { if len(pattern) == 0 { continue } matches, err := filepath.Glob(pattern) if err != nil { return fmt.Errorf("input 'attachments': pattern '%s' was malformatted: %s", pattern, err) } action.artifacts = append(action.artifacts, matches...) } return nil } func (action *ReleaseAction) run(ctx context.Context) error { if err := action.setup(); err != nil { return err } github := NewGitHub(action.Client()) // only get the commit info when we are missing a name or a text if len(action.label) == 0 || len(action.notes) == 0 { action.Debugf("Getting git tag information from repository") tag, err := github.GetGitTagInfo(action.repository, action.version, ctx) if err != nil { return err } if len(action.label) == 0 { action.label = tag.Name } if len(action.notes) == 0 { action.notes = tag.Message } } action.Debugf("Creating release %s", action.version) release, err := github.CreateRelease(&CreateReleaseRequest{ Repository: action.repository, TagName: action.version, Name: action.label, Body: action.notes, Draft: action.draft, PreRelease: action.prerelease, }, ctx) if err != nil { return fmt.Errorf("cannot create release for %s: %s", action.version, err) } action.Noticef("Created release %s: %s", release.Name, release.HtmlURL) wg := sync.WaitGroup{} for _, artifact := range action.artifacts { wg.Add(1) go func() { defer wg.Done() f, err := os.Open(artifact) if err != nil { action.Warningf("Unable to open file %s", artifact) return } a, err := github.CreateReleaseArtifact(&CreateReleaseArtifactRequest{ Repository: action.repository, Id: release.Id, Name: f.Name(), // unused? Attachment: f, }, ctx) if err != nil { action.Warningf("Unable to create artifact for %s", artifact) return } action.Debugf("Uploaded artifact %s (%d)", a.Name, a.Size) }() } wg.Wait() return nil }