release-action/main.go
2024-04-28 17:13:55 +08:00

229 lines
5.8 KiB
Go

package main
import (
"crypto/tls"
"fmt"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"code.gitea.io/sdk/gitea"
gha "github.com/sethvargo/go-githubactions"
)
func main() {
ctx, err := gha.Context()
if err != nil {
gha.Fatalf("failed to get context: %v", err)
}
if !strings.HasPrefix(ctx.Ref, "refs/tags/") {
gha.Fatalf("ref %s is not a tag", ctx.Ref)
}
files := gha.GetInput("files")
title := gha.GetInput("title")
body := gha.GetInput("body")
note := gha.GetInput("note")
apiKey := gha.GetInput("api_key")
preRelease, _ := strconv.ParseBool(gha.GetInput("pre_release"))
draft, _ := strconv.ParseBool(gha.GetInput("draft"))
if title == "" {
title = ctx.RefName
}
if apiKey == "" {
apiKey = os.Getenv("GITHUB_TOKEN")
}
insecure, _ := strconv.ParseBool(gha.GetInput("insecure"))
client := http.DefaultClient
if insecure {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client = &http.Client{Transport: tr}
}
c, err := gitea.NewClient(ctx.ServerURL, gitea.SetToken(apiKey), gitea.SetHTTPClient(client))
if err != nil {
gha.Fatalf("failed to create gitea client: %v", err)
}
owner := ctx.RepositoryOwner
repo := strings.Split(ctx.Repository, "/")[1]
// 获取上一个 tag 的 SHA
tags, _, err := c.ListRepoTags(owner, repo, gitea.ListRepoTagsOptions{
ListOptions: gitea.ListOptions{
Page: 1,
PageSize: 100,
},
})
if err != nil {
gha.Fatalf("failed to get tags: %v", err)
}
previewTagSHA := ""
if len(tags) > 1 {
previewTagSHA = tags[1].Commit.SHA
}
// 获取上一个 tag 至当前 tag 之间的所有 commit 信息,列出提交信息
commits, _, err := c.ListRepoCommits(owner, repo, gitea.ListCommitOptions{})
if err != nil {
gha.Fatalf("failed to get commits: %v", err)
}
commitCount := 1
commitHistory := ""
for _, commit := range commits {
if previewTagSHA == commit.SHA {
break
}
commitHistory += fmt.Sprintf("%d. %s\n", commitCount, commit.RepoCommit.Message)
commitCount++
}
if body == "" {
body = "\n## 内容\n" + commitHistory
}
body += "## 提示\n" + note
fmt.Printf("Creating release for %s/%s with tag %s\n", owner, repo, ctx.RefName)
fmt.Printf("Title: %s\n", title)
fmt.Printf("Note: %s\n", body)
rel, err := createOrGetRelease(ctx, c, owner, repo, gitea.CreateReleaseOption{
TagName: ctx.RefName,
IsDraft: draft,
IsPrerelease: preRelease,
Title: title,
Target: ctx.SHA,
Note: body,
})
if err != nil {
gha.Fatalf("failed to create release: %v", err)
}
matchedFiles, err := getFiles(ctx.Workspace, files)
if err != nil {
gha.Fatalf("failed to get files: %v", err)
}
if err := uploadFiles(ctx, c, owner, repo, rel.ID, matchedFiles); err != nil {
gha.Fatalf("Failed to upload files: %v", err)
}
gha.SetOutput("status", "success")
}
func getDirFiles(dir string) ([]string, error) {
d, err := os.Open(dir)
if err != nil {
return nil, err
}
defer d.Close()
info, err := d.Stat()
if err != nil {
return nil, err
}
if !info.IsDir() {
return []string{dir}, nil
}
list, err := d.Readdirnames(0)
if err != nil {
return nil, err
}
res := make([]string, 0, len(list))
for _, f := range list {
subs, err := getDirFiles(filepath.Join(dir, f))
if err != nil {
return nil, err
}
res = append(res, subs...)
}
return res, nil
}
func getFiles(parentDir, files string) ([]string, error) {
var fileList []string
lines := strings.Split(files, "\n")
for _, line := range lines {
line = strings.Trim(line, "'")
line = strings.TrimSpace(strings.Trim(line, `"`))
if line == "" {
continue
}
if filepath.IsAbs(line) {
return nil, fmt.Errorf("file path %s is absolute", line)
}
line = filepath.Join(parentDir, line)
matches, err := filepath.Glob(line)
if err != nil {
return nil, err
}
for _, match := range matches {
files, err := getDirFiles(match)
if err != nil {
return nil, err
}
fileList = append(fileList, files...)
}
}
return fileList, nil
}
func createOrGetRelease(ctx *gha.GitHubContext, c *gitea.Client, owner, repo string, opts gitea.CreateReleaseOption) (*gitea.Release, error) {
// Get the release by tag
release, resp, err := c.GetReleaseByTag(owner, repo, opts.TagName)
if err == nil {
return release, nil
}
errMessage := fmt.Errorf("failed to get release for tag: %s with error: %w", opts.TagName, err)
if resp.StatusCode != 404 {
return nil, errMessage
}
fmt.Printf("%s trying to create it\n", errMessage)
// Create the release
release, _, err = c.CreateRelease(owner, repo, opts)
if err != nil {
return nil, fmt.Errorf("failed to create release: %w and %s", err, errMessage)
}
return release, nil
}
func uploadFiles(ctx *gha.GitHubContext, c *gitea.Client, owner, repo string, releaseID int64, files []string) error {
attachments, _, err := c.ListReleaseAttachments(owner, repo, releaseID, gitea.ListReleaseAttachmentsOptions{})
if err != nil {
return fmt.Errorf("failed to fetch existing release attachments: %w", err)
}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return fmt.Errorf("failed to open release attachment %s: %w", file, err)
}
for _, attachment := range attachments {
if attachment.Name == filepath.Base(file) {
if _, err := c.DeleteReleaseAttachment(owner, repo, releaseID, attachment.ID); err != nil {
f.Close()
return fmt.Errorf("failed to delete release attachment %s: %w", file, err)
}
fmt.Printf("Successfully deleted old release attachment %s\n", attachment.Name)
}
}
if _, _, err = c.CreateReleaseAttachment(owner, repo, releaseID, f, filepath.Base(file)); err != nil {
f.Close()
return fmt.Errorf("failed to upload release attachment %s: %w", file, err)
}
f.Close()
fmt.Printf("Successfully uploaded release attachment %s\n", file)
}
return nil
}