cicd-release/api/github.go

312 lines
7.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/urfave/cli/v2"
"github.com/xuxiaowei-com-cn/cicd-release/constant"
"io"
"log"
"net/http"
"os"
"path"
"strings"
)
type GithubReleasesRequest struct {
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Body string `json:"body"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
GenerateReleaseNotes bool `json:"generate_release_notes"`
}
func Github(prerelease bool, context *cli.Context) error {
var releaseName = context.String(constant.ReleaseName)
var releaseBody = context.String(constant.ReleaseBody)
var tag = context.String(constant.Tag)
var draft = context.Bool(constant.Draft)
var autoCreateTag = context.Bool(constant.AutoCreateTag)
var artifacts = context.StringSlice(constant.Artifacts)
var githubRepository = context.String(constant.GithubRepository)
var githubUsername = context.String(constant.GithubUsername)
var githubToken = context.String(constant.GithubToken)
log.Printf("是否是预发布版本:%v", prerelease)
log.Printf("发布到 GitHub路径%s", githubRepository)
log.Printf("操作 GitHub 的用于名:%s", githubUsername)
// 检查发布
err := GithubGetReleases(githubRepository, githubToken)
if err != nil {
return err
}
// 自动创建标签
err = AutoCreateTag(tag, autoCreateTag)
if err != nil {
return err
}
// 检查标签
err = GithubGetTag(githubRepository, githubToken, tag)
if err != nil {
return err
}
// 用于判断是否处于 GitHub Actions 环境
githubSha := os.Getenv("GITHUB_SHA")
if githubSha != "" {
log.Printf("GITHUB_SHA%s", githubSha)
log.Printf("在 GitHub Actions 环境 中发布,无需推送 Git 标签(当前工作区的代码,已存在于 GitHub 仓库中)")
} else {
log.Printf("使用 Git 推送标签 开始")
// 推送标签
err = GitPushTag("https://github.com", githubRepository, githubUsername, githubToken, tag)
if err != nil {
return err
}
log.Printf("使用 Git 推送标签 结束")
}
// 发布
err = GithubReleases(prerelease, githubRepository, releaseName, releaseBody, tag, draft, artifacts, githubToken)
if err != nil {
return err
}
return nil
}
// GithubGetTag
// 检查标签
func GithubGetTag(githubRepository string, githubToken string, tag string) error {
url := fmt.Sprintf("https://api.github.com/repos/%s/tags/protection", githubRepository)
client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Println("Error creating request:", err)
return err
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", githubToken))
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
resp, err := client.Do(req)
if err != nil {
log.Println("Error sending request:", err)
return err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
log.Println("Error closing response body:", err)
}
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Println("Error reading response:", err)
return err
}
bodyStr := string(body)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
var tags []Tag
err := json.Unmarshal(body, &tags)
if err != nil {
log.Println("Error unmarshal Tag:", err)
return err
}
for _, t := range tags {
if tag == t.Mame {
sha, err := GitTagSha(tag)
if err != nil {
return nil
}
if t.Commit.Sha == sha {
return nil
} else {
return errors.New(fmt.Sprintf("本地标签 %s%s 和 远端 标签 %s%s 对应 SHA 不同,请检查!", tag, sha, tag, t.Commit.Sha))
}
}
}
} else {
return errors.New(fmt.Sprintf("检查 GitHub 标签异常(%d\n%s", resp.StatusCode, bodyStr))
}
return nil
}
// GithubGetReleases
// 检查发布
func GithubGetReleases(githubRepository string, githubToken string) error {
return nil
}
// GithubReleases
// 发布
func GithubReleases(prerelease bool, githubRepository string, releaseName string, releaseBody string, tag string,
draft bool, artifacts []string, githubToken string) error {
sha, err := GitTagSha(tag)
if err != nil {
return nil
}
data := GithubReleasesRequest{
TagName: tag,
TargetCommitish: sha,
Name: releaseName,
Body: releaseBody,
Draft: draft,
Prerelease: prerelease,
GenerateReleaseNotes: false,
}
url := fmt.Sprintf("https://api.github.com/repos/%s/releases", githubRepository)
jsonData, err := json.Marshal(data)
if err != nil {
log.Println("Error marshaling JSON:", err)
return err
}
client := &http.Client{}
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonData))
if err != nil {
log.Println("Error creating request:", err)
return err
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", githubToken))
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
resp, err := client.Do(req)
if err != nil {
log.Println("Error sending request:", err)
return err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
log.Println("Error closing response body:", err)
}
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Println("Error reading response:", err)
return err
}
bodyStr := string(body)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
log.Printf("发布结果:%s\n", bodyStr)
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
log.Println("Error unmarshal JSON:", err)
return err
}
// 从map中取出目标值
idFloat64, ok := data["id"].(float64)
if !ok {
log.Fatal("id value not found or not a float64")
}
id := int64(idFloat64)
err = GithubUploadReleaseAssets(githubRepository, artifacts, id, githubToken)
if err != nil {
return err
}
} else {
if strings.Contains(bodyStr, "already_exists") {
return errors.New(fmt.Sprintf("GitHub 已存在发布:\n%s", bodyStr))
} else {
return errors.New(fmt.Sprintf("发布 GitHub 异常:\n%s", bodyStr))
}
}
return nil
}
func GithubUploadReleaseAssets(githubRepository string, artifacts []string, id int64, githubToken string) error {
for _, artifact := range artifacts {
fileName := path.Base(artifact)
url := fmt.Sprintf("https://uploads.github.com/repos/%s/releases/%d/assets?name=%s", githubRepository, id, fileName)
file, err := os.Open(artifact)
if err != nil {
log.Printf("Failed to open file: %v", err)
return err
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
log.Printf("Failed to get file info: %v", err)
return err
}
req, err := http.NewRequest(http.MethodPost, url, file)
if err != nil {
log.Printf("Failed to create request: %v", err)
return err
}
req.ContentLength = fileInfo.Size() // 设置正确的Content-Length
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", githubToken))
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
req.Header.Set("Content-Type", "application/octet-stream")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed to send request: %v", err)
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Failed to read response body: %v", err)
return err
}
bodyStr := string(body)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
log.Printf("上传产物 %s 完成:\n%s\n", artifact, bodyStr)
} else {
return errors.New(fmt.Sprintf("上传产物 %s 异常:\n%s", artifact, bodyStr))
}
}
return nil
}