github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/release/release.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package main 5 6 import ( 7 "fmt" 8 "log" 9 "os" 10 "runtime" 11 "strings" 12 13 gh "github.com/keybase/client/go/release/github" 14 "github.com/keybase/client/go/release/update" 15 "github.com/keybase/client/go/release/version" 16 "github.com/keybase/client/go/release/winbuild" 17 "gopkg.in/alecthomas/kingpin.v2" 18 ) 19 20 func githubToken(required bool) string { 21 token := os.Getenv("GITHUB_TOKEN") 22 if token == "" && required { 23 log.Fatal("No GITHUB_TOKEN set") 24 } 25 return token 26 } 27 28 func keybaseToken(required bool) string { 29 token := os.Getenv("KEYBASE_TOKEN") 30 if token == "" && required { 31 log.Fatal("No KEYBASE_TOKEN set") 32 } 33 return token 34 } 35 36 func tag(version string) string { 37 return fmt.Sprintf("v%s", version) 38 } 39 40 var ( 41 app = kingpin.New("release", "Release tool for build and release scripts") 42 latestVersionCmd = app.Command("latest-version", "Get latest version of a Github repo") 43 latestVersionUser = latestVersionCmd.Flag("user", "Github user").Required().String() 44 latestVersionRepo = latestVersionCmd.Flag("repo", "Repository name").Required().String() 45 46 platformCmd = app.Command("platform", "Get the OS platform name") 47 48 urlCmd = app.Command("url", "Get the github release URL for a repo") 49 urlUser = urlCmd.Flag("user", "Github user").Required().String() 50 urlRepo = urlCmd.Flag("repo", "Repository name").Required().String() 51 urlVersion = urlCmd.Flag("version", "Version").Required().String() 52 53 createCmd = app.Command("create", "Create a Github release") 54 createRepo = createCmd.Flag("repo", "Repository name").Required().String() 55 createVersion = createCmd.Flag("version", "Version").Required().String() 56 57 uploadCmd = app.Command("upload", "Upload a file to a Github release") 58 uploadRepo = uploadCmd.Flag("repo", "Repository name").Required().String() 59 uploadVersion = uploadCmd.Flag("version", "Version").Required().String() 60 uploadSrc = uploadCmd.Flag("src", "Source file").Required().ExistingFile() 61 uploadDest = uploadCmd.Flag("dest", "Destination file").String() 62 63 downloadCmd = app.Command("download", "Download a file from a Github release") 64 downloadRepo = downloadCmd.Flag("repo", "Repository name").Required().String() 65 downloadVersion = downloadCmd.Flag("version", "Version").Required().String() 66 downloadSrc = downloadCmd.Flag("src", "Source file").Required().ExistingFile() 67 68 updateJSONCmd = app.Command("update-json", "Generate update.json file for updater") 69 updateJSONVersion = updateJSONCmd.Flag("version", "Version").Required().String() 70 updateJSONSrc = updateJSONCmd.Flag("src", "Source file").ExistingFile() 71 updateJSONURI = updateJSONCmd.Flag("uri", "URI for location of files").URL() 72 updateJSONSignature = updateJSONCmd.Flag("signature", "Signature file").ExistingFile() 73 updateJSONDescription = updateJSONCmd.Flag("description", "Description file").ExistingFile() 74 updateJSONProps = updateJSONCmd.Flag("prop", "Properties to include").Strings() 75 76 indexHTMLCmd = app.Command("index-html", "Generate index.html for s3 bucket") 77 indexHTMLBucketName = indexHTMLCmd.Flag("bucket-name", "Bucket name to index").Required().String() 78 indexHTMLPrefixes = indexHTMLCmd.Flag("prefixes", "Prefixes to include (comma-separated)").Required().String() 79 indexHTMLSuffix = indexHTMLCmd.Flag("suffix", "Suffix of files").String() 80 indexHTMLDest = indexHTMLCmd.Flag("dest", "Write to file").String() 81 indexHTMLUpload = indexHTMLCmd.Flag("upload", "Upload to S3").String() 82 83 parseVersionCmd = app.Command("version-parse", "Parse a sematic version string") 84 parseVersionString = parseVersionCmd.Arg("version", "Semantic version to parse").Required().String() 85 86 promoteReleasesCmd = app.Command("promote-releases", "Promote releases") 87 promoteReleasesBucketName = promoteReleasesCmd.Flag("bucket-name", "Bucket name to use").Required().String() 88 promoteReleasesPlatform = promoteReleasesCmd.Flag("platform", "Platform (darwin, linux, windows)").Required().String() 89 90 promoteAReleaseCmd = app.Command("promote-a-release", "Promote a specific release") 91 releaseToPromote = promoteAReleaseCmd.Flag("release", "Specific release to promote to public").Required().String() 92 promoteAReleaseBucketName = promoteAReleaseCmd.Flag("bucket-name", "Bucket name to use").Required().String() 93 promoteAReleasePlatform = promoteAReleaseCmd.Flag("platform", "Platform (darwin, linux, windows)").Required().String() 94 promoteAReleaseDryRun = promoteAReleaseCmd.Flag("dry-run", "Announce what would be done without doing it").Bool() 95 96 brokenReleaseCmd = app.Command("broken-release", "Mark a release as broken") 97 brokenReleaseName = brokenReleaseCmd.Flag("release", "Release to mark as broken").Required().String() 98 brokenReleaseBucketName = brokenReleaseCmd.Flag("bucket-name", "Bucket name to use").Required().String() 99 brokenReleasePlatformName = brokenReleaseCmd.Flag("platform", "Platform (darwin, linux, windows)").Required().String() 100 101 promoteTestReleasesCmd = app.Command("promote-test-releases", "Promote test releases") 102 promoteTestReleasesBucketName = promoteTestReleasesCmd.Flag("bucket-name", "Bucket name to use").Required().String() 103 promoteTestReleasesPlatform = promoteTestReleasesCmd.Flag("platform", "Platform (darwin, linux, windows)").Required().String() 104 promoteTestReleasesRelease = promoteTestReleasesCmd.Flag("release", "Specific release to promote to test").String() 105 106 updatesReportCmd = app.Command("updates-report", "Summary of updates/releases") 107 updatesReportBucketName = updatesReportCmd.Flag("bucket-name", "Bucket name to use").Required().String() 108 109 saveLogCmd = app.Command("save-log", "Save log") 110 saveLogBucketName = saveLogCmd.Flag("bucket-name", "Bucket name to use").Required().String() 111 saveLogPath = saveLogCmd.Flag("path", "File to save").Required().String() 112 saveLogNoErr = saveLogCmd.Flag("noerr", "No error status on failure").Bool() 113 saveLogMaxSize = saveLogCmd.Flag("maxsize", "Max size, (default 102400)").Default("102400").Int64() 114 115 latestCommitCmd = app.Command("latest-commit", "Latests commit we can use to safely build from") 116 latestCommitRepo = latestCommitCmd.Flag("repo", "Repository name").Required().String() 117 latestCommitContexts = latestCommitCmd.Flag("context", "Context to check for success").Required().Strings() 118 119 waitForCICmd = app.Command("wait-ci", "Waits on a the latest commit being successful in CI") 120 waitForCIRepo = waitForCICmd.Flag("repo", "Repository name").Required().String() 121 waitForCICommit = waitForCICmd.Flag("commit", "Commit").Required().String() 122 waitForCIContexts = waitForCICmd.Flag("context", "Context to check for success").Required().Strings() 123 waitForCIDelay = waitForCICmd.Flag("delay", "Delay between checks").Default("1m").Duration() 124 waitForCITimeout = waitForCICmd.Flag("timeout", "Delay between checks").Default("1h").Duration() 125 126 announceBuildCmd = app.Command("announce-build", "Inform the API server of the existence of a new build") 127 announceBuildA = announceBuildCmd.Flag("build-a", "The first of the two IDs comprising the new build").Required().String() 128 announceBuildB = announceBuildCmd.Flag("build-b", "The second of the two IDs comprising the new build").Required().String() 129 announceBuildPlatform = announceBuildCmd.Flag("platform", "Platform (darwin, linux, windows)").Required().String() 130 131 setBuildInTestingCmd = app.Command("set-build-in-testing", "Enroll or unenroll a build in smoketesting") 132 setBuildInTestingA = setBuildInTestingCmd.Flag("build-a", "The first build's ID").Required().String() 133 setBuildInTestingPlatform = setBuildInTestingCmd.Flag("platform", "Platform (darwin, linux, windows)").Required().String() 134 setBuildInTestingEnable = setBuildInTestingCmd.Flag("enable", "Enroll the build in smoketesting (boolish string)").Required().String() 135 setBuildInTestingMaxTesters = setBuildInTestingCmd.Flag("max-testers", "Max number of testers for this build").Required().Int() 136 137 ciStatusesCmd = app.Command("ci-statuses", "List statuses for CI") 138 ciStatusesRepo = ciStatusesCmd.Flag("repo", "Repository name").Required().String() 139 ciStatusesCommit = ciStatusesCmd.Flag("commit", "Commit").Required().String() 140 141 getWinBuildNumberCmd = app.Command("winbuildnumber", "Atomically retrieve and increment build number for given version") 142 getWinBuildNumberVersion = getWinBuildNumberCmd.Flag("version", "Major version, e.g. 1.0.30").Required().String() 143 getWinBuildNumberBotID = getWinBuildNumberCmd.Flag("botid", "bot ID").Default("1").String() 144 getWinBuildNumberPlatform = getWinBuildNumberCmd.Flag("platform", "platform").Default("1").String() 145 ) 146 147 func main() { 148 switch kingpin.MustParse(app.Parse(os.Args[1:])) { 149 case latestVersionCmd.FullCommand(): 150 tag, err := gh.LatestTag(*latestVersionUser, *latestVersionRepo, githubToken(false)) 151 if err != nil { 152 log.Fatal(err) 153 } 154 if strings.HasPrefix(tag.Name, "v") { 155 version := tag.Name[1:] 156 fmt.Printf("%s", version) 157 } 158 case platformCmd.FullCommand(): 159 fmt.Printf("%s", runtime.GOOS) 160 161 case urlCmd.FullCommand(): 162 release, err := gh.ReleaseOfTag(*urlUser, *urlRepo, tag(*urlVersion), githubToken(false)) 163 if _, ok := err.(*gh.ErrNotFound); ok { 164 // No release 165 } else if err != nil { 166 log.Fatal(err) 167 } else { 168 fmt.Printf("%s", release.URL) 169 } 170 case createCmd.FullCommand(): 171 err := gh.CreateRelease(githubToken(true), *createRepo, tag(*createVersion), tag(*createVersion)) 172 if err != nil { 173 log.Fatal(err) 174 } 175 case uploadCmd.FullCommand(): 176 if *uploadDest == "" { 177 uploadDest = uploadSrc 178 } 179 log.Printf("Uploading %s as %s (%s)", *uploadSrc, *uploadDest, tag(*uploadVersion)) 180 err := gh.Upload(githubToken(true), *uploadRepo, tag(*uploadVersion), *uploadDest, *uploadSrc) 181 if err != nil { 182 log.Fatal(err) 183 } 184 case downloadCmd.FullCommand(): 185 defaultSrc := fmt.Sprintf("keybase-%s-%s.tgz", *downloadVersion, runtime.GOOS) 186 if *downloadSrc == "" { 187 downloadSrc = &defaultSrc 188 } 189 log.Printf("Downloading %s (%s)", *downloadSrc, tag(*downloadVersion)) 190 err := gh.DownloadAsset(githubToken(false), *downloadRepo, tag(*downloadVersion), *downloadSrc) 191 if err != nil { 192 log.Fatal(err) 193 } 194 case updateJSONCmd.FullCommand(): 195 out, err := update.EncodeJSON(*updateJSONVersion, tag(*updateJSONVersion), *updateJSONDescription, *updateJSONProps, *updateJSONSrc, *updateJSONURI, *updateJSONSignature) 196 if err != nil { 197 log.Fatal(err) 198 } 199 fmt.Fprintf(os.Stdout, "%s\n", out) 200 case indexHTMLCmd.FullCommand(): 201 err := update.WriteHTML(*indexHTMLBucketName, *indexHTMLPrefixes, *indexHTMLSuffix, *indexHTMLDest, *indexHTMLUpload) 202 if err != nil { 203 log.Fatal(err) 204 } 205 case parseVersionCmd.FullCommand(): 206 versionFull, versionShort, date, commit, err := version.Parse(*parseVersionString) 207 if err != nil { 208 log.Fatal(err) 209 } 210 log.Printf("%s\n", versionFull) 211 log.Printf("%s\n", versionShort) 212 log.Printf("%s\n", date) 213 log.Printf("%s\n", commit) 214 case promoteReleasesCmd.FullCommand(): 215 const dryRun bool = false 216 release, err := update.PromoteReleases(*promoteReleasesBucketName, *promoteReleasesPlatform) 217 if err != nil { 218 log.Fatal(err) 219 } 220 err = update.CopyLatest(*promoteReleasesBucketName, *promoteReleasesPlatform, dryRun) 221 if err != nil { 222 log.Fatal(err) 223 } 224 if release == nil { 225 log.Print("Not notifying API server of release") 226 } else { 227 releaseTime, err := update.KBWebPromote(keybaseToken(true), release.Version, *promoteReleasesPlatform, dryRun) 228 if err != nil { 229 log.Fatal(err) 230 } 231 log.Printf("Release time set to %v for build %v", releaseTime, release.Version) 232 } 233 case promoteAReleaseCmd.FullCommand(): 234 release, err := update.PromoteARelease(*releaseToPromote, *promoteAReleaseBucketName, *promoteAReleasePlatform, *promoteAReleaseDryRun) 235 if err != nil { 236 log.Fatal(err) 237 } 238 err = update.CopyLatest(*promoteAReleaseBucketName, *promoteAReleasePlatform, *promoteAReleaseDryRun) 239 if err != nil { 240 log.Fatal(err) 241 } 242 if release == nil { 243 log.Fatal("No release found") 244 } else { 245 _, err := update.KBWebPromote(keybaseToken(!*promoteAReleaseDryRun), release.Version, *promoteAReleasePlatform, *promoteAReleaseDryRun) 246 if err != nil { 247 log.Fatal(err) 248 } 249 } 250 case promoteTestReleasesCmd.FullCommand(): 251 err := update.PromoteTestReleases(*promoteTestReleasesBucketName, *promoteTestReleasesPlatform, *promoteTestReleasesRelease) 252 if err != nil { 253 log.Fatal(err) 254 } 255 case updatesReportCmd.FullCommand(): 256 err := update.Report(*updatesReportBucketName, os.Stdout) 257 if err != nil { 258 log.Fatal(err) 259 } 260 case brokenReleaseCmd.FullCommand(): 261 _, err := update.ReleaseBroken(*brokenReleaseName, *brokenReleaseBucketName, *brokenReleasePlatformName) 262 if err != nil { 263 log.Fatal(err) 264 } 265 case saveLogCmd.FullCommand(): 266 267 url, err := update.SaveLog(*saveLogBucketName, *saveLogPath, *saveLogMaxSize) 268 if err != nil { 269 if *saveLogNoErr { 270 log.Printf("%s", err) 271 return 272 } 273 log.Fatal(err) 274 } 275 fmt.Fprintf(os.Stdout, "%s\n", url) 276 case latestCommitCmd.FullCommand(): 277 commit, err := gh.LatestCommit(githubToken(true), *latestCommitRepo, *latestCommitContexts) 278 if err != nil { 279 log.Fatal(err) 280 } 281 fmt.Printf("%s", commit.SHA) 282 case waitForCICmd.FullCommand(): 283 err := gh.WaitForCI(githubToken(true), *waitForCIRepo, *waitForCICommit, *waitForCIContexts, *waitForCIDelay, *waitForCITimeout) 284 if err != nil { 285 log.Fatal(err) 286 } 287 case announceBuildCmd.FullCommand(): 288 err := update.AnnounceBuild(keybaseToken(true), *announceBuildA, *announceBuildB, *announceBuildPlatform) 289 if err != nil { 290 log.Fatal(err) 291 } 292 case setBuildInTestingCmd.FullCommand(): 293 err := update.SetBuildInTesting(keybaseToken(true), *setBuildInTestingA, *setBuildInTestingPlatform, *setBuildInTestingEnable, *setBuildInTestingMaxTesters) 294 if err != nil { 295 log.Fatal(err) 296 } 297 case ciStatusesCmd.FullCommand(): 298 err := gh.CIStatuses(githubToken(true), *ciStatusesRepo, *ciStatusesCommit) 299 if err != nil { 300 log.Fatal(err) 301 } 302 case getWinBuildNumberCmd.FullCommand(): 303 err := winbuild.GetNextBuildNumber(keybaseToken(true), *getWinBuildNumberVersion, *getWinBuildNumberBotID, *getWinBuildNumberPlatform) 304 if err != nil { 305 log.Fatal(err) 306 } 307 } 308 309 }