-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
182 lines (150 loc) · 5.5 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package main
import (
"flag"
"log/slog"
"os"
"path/filepath"
"time"
)
var argVersion = flag.Bool("version", false, "print version and exit")
var assetDir = flag.String("asset-dir", "build", "directory to search for assets to upload")
var tagName = flag.String("tag-name", "", "release tag to reference or create")
var targetCommitish = flag.String("target-commitish", "", "when creating a tag, the commit to reference")
var githubOwner = flag.String("github-owner", "", "github owner")
var githubRepo = flag.String("github-repo", "", "github repo")
var releaseName = flag.String("release-name", "", "release name, defaults to tag name")
var releaseBody = flag.String("release-body", "no results", "release body contents (markdown supported)")
var releaseDraft = flag.Bool("release-draft", true, "set draft flag on release")
var releasePrerelease = flag.Bool("release-prerelease", false, "set prerelease flag on release")
var generateReleaseNotes = flag.Bool("generate-release-notes", false, "tell GitHub to generate release notes")
func usage() {
println(`Usage: [go tool] go-github-release [options]
Simple GitHub tool to cut a release and upload assets.
NOTE: An env:GITHUB_TOKEN is required to authenticate with GitHub and its
recommended to use a personal access token scoped to repository.
GitHub releases are fairly simple, its a ccollection of assets with name and
message body that reference a commit, which can be a newly created tag as a
commitish (--target-commitish) or existing tag in which case the target
commitish is ignored. Assets are uploaded to this release based on mime-type
which is inferred from the file extension.
If you are unsure of what workflow to use, the author recommends creating a
tag ahead of release and using that tag as the --tag-name. This way you can
seperate the release process from the tag creation process and ensure your
tag is referencing the proper moment in history. The automatic tag creation
feature is a GitHub conveinence and not related to git.
ex: to draft a new release for an existing tag v1:
[go tool] go-github-release \
--github-owner myorg \
--github-repo myrepo \
--tag-name v1 \
--asset-dir build
ex: to draft a new release and new tag v1 pointing to abc123:
[go tool] go-github-release \
--github-owner myorg \
--github-repo myrepo \
--tag-name v1 \
--target-commitish abc123 \
--asset-dir dist
Tips:
- drafts that create a tag, won't do so until the release is published
- if --github-repo is not set, the current directory is used
- if --release-name is not set, the tag name is used
- if --target-commitish is not set, main is used
- a release body supports markdown
- you can safely edit the release body while the assets are uploading
- find the latest tag by name: git describe --tags --abbrev=0
- find a tag's reference commit: git rev-list -n 1 tags/v0.0.1
Options:
`)
flag.PrintDefaults()
}
func main() {
flag.Usage = usage
flag.Parse()
// print some debug information and exit
if *argVersion {
version()
return
}
// we wrap the run inside the main function so we can capture a status
// code and cleanly let defer functions within run, uh run
status := run()
os.Exit(status)
}
func run() int {
// clock the whole enchalada
start := time.Now()
defer func() {
slog.Info("operation complete", "duration", time.Since(start))
}()
// get GITHUB_TOKEN from environment
token := os.Getenv("GITHUB_TOKEN")
if token == "" {
slog.Error("missing GITHUB_TOKEN")
return TokenNotFound
}
if *githubOwner == "" {
slog.Error("--github-owner is required")
return OwnerNotFound
}
if *githubRepo == "" {
pwd, err := os.Getwd()
if err == nil {
*githubRepo = filepath.Base(pwd)
slog.Warn("--github-repo not set, using working dir", "repo", *githubRepo)
} else {
slog.Error("--github-repo is required")
return RepoNotFound
}
}
if *tagName == "" {
slog.Error("--tag-name is required")
return TagNameRequired
}
// ensure the asset directory exists
if _, err := os.Stat(*assetDir); os.IsNotExist(err) {
slog.Error("asset directory not found", "dir", *assetDir)
return AssetDirNotFound
}
// set release name to tag name if not set
if *releaseName == "" {
*releaseName = *tagName
slog.Warn("--release-name not set, using tag name", "name", *releaseName)
}
// create a release request populated with some defaults
r := CreateReleaseRequest{
TagName: *tagName,
TargetCommitish: *targetCommitish,
Name: *releaseName,
Body: *releaseBody,
Draft: *releaseDraft,
Prerelease: *releasePrerelease,
GenerateReleaseNotes: *generateReleaseNotes,
}
// create a release on GitHub and get the response struct back
resp, err := r.CreateRelease(token, *githubOwner, *githubRepo)
if err != nil {
slog.Error("release issue", "err", err)
return ErrorCreateRequest
}
slog.Info("release created", "id", resp.ID, "url", resp.HTMLURL)
// get a list of assets in the directory
assets, err := filepath.Glob(filepath.Join(*assetDir, "*"))
if err != nil {
slog.Error("glob issue", "err", err)
return ErrorBadPattern
}
slog.Info("assets", "count", len(assets))
// upload each file to the release
for _, asset := range assets {
uploadStart := time.Now()
err := uploadAsset(token, resp.UploadURL, asset)
if err != nil {
slog.Error("upload issue", "err", err)
return AssetUploadError
}
slog.Info("asset uploaded", "asset", asset, "duration", time.Since(uploadStart))
}
slog.Info("assets uploaded", "count", len(assets))
return NoError
}