-
Notifications
You must be signed in to change notification settings - Fork 770
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This workflow is meant to replace the GitHub action workflow defined in https://github.com/golang/vscode-go/blob/master/.github/workflows/release.yml The GH workflow was a collection of command line tools invocation and some bash scripting to parse the version tag and adjust the workflow depending on whether it's for rc or not. I figured it's easier to work with go, so ported the logic to build/release.go. TODO: convert most part of build/all.bash and the nightly release workflow with go code. release.go uses `gh` (GitHub CLI tool) to post the release note and the artifacts (vsix) to GitHub. Dockerfile is updated to include `gh`. Reference: https://stackoverflow.com/a/69477930 The workflow needs to access GitHub personal access token. To access the release API, write permission for Content & Workflow is necessary. https://docs.github.com/en/rest/authentication/permissions-required-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28#repository-permissions-for-contents Tested it by triggering the GCB workflow against a forked repo. For b/216321767 Change-Id: I1eec6b4b896fe1f1565bbf9e160f0368cade36c7 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/553115 Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Suzy Mueller <suzmue@golang.org> Commit-Queue: Hyang-Ah Hana Kim <hyangah@gmail.com> TryBot-Result: kokoro <noreply+kokoro@google.com>
- Loading branch information
Showing
4 changed files
with
283 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
// Copyright 2023 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// This script is used to build and publish VS Code Go extension. | ||
// The script should be run from the root of the repository where package.json is located. | ||
// | ||
// The script requires the following environment variables to be set: | ||
// | ||
// TAG_NAME: the name of the tag to be released. | ||
// COMMIT_SHA: the commit SHA to be released (optional. if not set, it will be retrieved from git) | ||
// VSCE_PAT: the Personal Access Token for the VS Code Marketplace. | ||
// GITHUB_TOKEN: the GitHub token for the Go repository. | ||
// | ||
// This script requires the following tools to be installed: | ||
// | ||
// jq, npx, gh, git | ||
// | ||
// Usage: | ||
// | ||
// // package the extension (based on TAG_NAME). | ||
// go run build/release.go package | ||
// // publish the extension. | ||
// go run build/release.go publish | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
func main() { | ||
if len(os.Args) != 2 { | ||
fatalf("usage: %s [package|publish]", os.Args[0]) | ||
} | ||
cmd := os.Args[1] | ||
|
||
checkWD() | ||
requireTools("jq", "npx", "gh", "git") | ||
requireEnvVars("TAG_NAME") | ||
|
||
tagName, version, isRC := releaseVersionInfo() | ||
vsix := fmt.Sprintf("go-%s.vsix", version) | ||
|
||
switch cmd { | ||
case "package": | ||
buildPackage(version, vsix) | ||
case "publish": | ||
requireEnvVars("VSCE_PAT", "GITHUB_TOKEN") | ||
publish(tagName, vsix, isRC) | ||
default: | ||
fatalf("usage: %s [package|publish]", os.Args[0]) | ||
} | ||
} | ||
|
||
func fatalf(format string, args ...any) { | ||
fmt.Fprintf(os.Stderr, format, args...) | ||
fmt.Fprintf(os.Stderr, "\n") | ||
os.Exit(1) | ||
} | ||
|
||
func requireTools(tools ...string) { | ||
for _, tool := range tools { | ||
if _, err := exec.LookPath(tool); err != nil { | ||
fatalf("required tool %q not found", tool) | ||
} | ||
} | ||
} | ||
|
||
func requireEnvVars(vars ...string) { | ||
for _, v := range vars { | ||
if os.Getenv(v) == "" { | ||
fatalf("required environment variable %q not set", v) | ||
} | ||
} | ||
} | ||
|
||
// checkWD checks if the working directory is the root of the repository where package.json is located. | ||
func checkWD() { | ||
wd, err := os.Getwd() | ||
if err != nil { | ||
fatalf("failed to get working directory") | ||
} | ||
// check if package.json is in the working directory | ||
if _, err := os.Stat("package.json"); os.IsNotExist(err) { | ||
fatalf("package.json not found in working directory %q", wd) | ||
} | ||
} | ||
|
||
// releaseVersionInfo computes the version and label information for this release. | ||
// It requires the TAG_NAME environment variable to be set and the tag matches the version info embedded in package.json. | ||
func releaseVersionInfo() (tagName, version string, isPrerelease bool) { | ||
tagName = os.Getenv("TAG_NAME") | ||
if tagName == "" { | ||
fatalf("TAG_NAME environment variable is not set") | ||
} | ||
// versionTag should be of the form vMajor.Minor.Patch[-rc.N]. | ||
// e.g. v1.1.0-rc.1, v1.1.0 | ||
// The MajorMinorPatch part should match the version in package.json. | ||
// The optional `-rc.N` part is captured as the `Label` group | ||
// and the validity is checked below. | ||
versionTagRE := regexp.MustCompile(`^v(?P<MajorMinorPatch>\d+\.\d+\.\d+)(?P<Label>\S*)$`) | ||
m := versionTagRE.FindStringSubmatch(tagName) | ||
if m == nil { | ||
fatalf("TAG_NAME environment variable %q is not a valid version", tagName) | ||
} | ||
mmp := m[versionTagRE.SubexpIndex("MajorMinorPatch")] | ||
label := m[versionTagRE.SubexpIndex("Label")] | ||
if label != "" { | ||
if !strings.HasPrefix(label, "-rc.") { | ||
fatalf("TAG_NAME environment variable %q is not a valid release candidate version", tagName) | ||
} | ||
isPrerelease = true | ||
} | ||
|
||
cmd := exec.Command("jq", "-r", ".version", "package.json") | ||
cmd.Stderr = os.Stderr | ||
versionInPackageJSON, err := cmd.Output() | ||
if err != nil { | ||
fatalf("failed to read package.json version") | ||
} | ||
if got := string(bytes.TrimSpace(versionInPackageJSON)); got != mmp { | ||
fatalf("package.json version %q does not match TAG_NAME %q", got, tagName) | ||
} | ||
|
||
return tagName, mmp + label, isPrerelease | ||
} | ||
|
||
// buildPackage builds the extension of the given version, using npx vsce package. | ||
func buildPackage(version, output string) { | ||
cmd := exec.Command("npx", "vsce", "package", | ||
"-o", output, | ||
"--baseContentUrl", "https://github.com/golang/vscode-go", | ||
"--baseImagesUrl", "https://github.com/golang/vscode-go", | ||
"--no-update-package-json", | ||
"--no-git-tag-version", | ||
version) | ||
|
||
cmd.Stderr = os.Stderr | ||
if err := cmd.Run(); err != nil { | ||
fatalf("failed to build package") | ||
} | ||
|
||
cmd = exec.Command("git", "add", output) | ||
cmd.Stderr = os.Stderr | ||
if err := cmd.Run(); err != nil { | ||
fatalf("failed to build package") | ||
} | ||
} | ||
|
||
// publish publishes the extension to the VS Code Marketplace and GitHub, using npx vsce and gh release create. | ||
func publish(tagName, packageFile string, isPrerelease bool) { | ||
// check if the package file exists. | ||
if _, err := os.Stat(packageFile); os.IsNotExist(err) { | ||
fatalf("package file %q does not exist. Did you run 'go run build/release.go package'?", packageFile) | ||
} | ||
|
||
// publish release to GitHub. This will create a draft release - manually publish it after reviewing the draft. | ||
// TODO(hyangah): populate the changelog (the first section of CHANGELOG.md) and pass it using --notes-file instead of --generate-notes. | ||
ghArgs := []string{"release", "create", "--generate-notes", "--target", commitSHA(), "--title", "Release " + tagName, "--draft"} | ||
fmt.Printf("%s\n", strings.Join(ghArgs, " ")) | ||
if isPrerelease { | ||
ghArgs = append(ghArgs, "--prerelease") | ||
} | ||
ghArgs = append(ghArgs, "-R", "github.com/golang/vscode-go") | ||
ghArgs = append(ghArgs, tagName, packageFile) | ||
cmd := exec.Command("gh", ghArgs...) | ||
cmd.Stderr = os.Stderr | ||
if err := cmd.Run(); err != nil { | ||
fatalf("failed to publish release") | ||
} | ||
|
||
if isPrerelease { | ||
return // TODO: release with the -pre-release flag if isPrerelease is set. | ||
} | ||
|
||
/* TODO(hyangah): uncomment this to finalize the release workflow migration. | ||
npxVsceArgs := []string{"vsce", "publish", "-i", packageFile} | ||
cmd2 := exec.Command("npx", npxVsceArgs...) | ||
cmd2.Stderr = os.Stderr | ||
if err := cmd2.Run(); err != nil { | ||
fatalf("failed to publish release") | ||
} | ||
*/ | ||
} | ||
|
||
// commitSHA returns COMMIT_SHA environment variable, or the commit SHA of the current branch. | ||
func commitSHA() string { | ||
if commitSHA := os.Getenv("COMMIT_SHA"); commitSHA != "" { | ||
return commitSHA | ||
} | ||
|
||
cmd := exec.Command("git", "rev-parse", "HEAD") | ||
cmd.Stderr = os.Stderr | ||
commitSHA, err := cmd.Output() | ||
if err != nil { | ||
fatalf("failed to get commit SHA") | ||
} | ||
return strings.TrimSpace(string(commitSHA)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# This workflow will be triggered when a new release tag (of the form vX.Y.Z) is | ||
# pushed to the vscode-go repo.) | ||
# For local testing, run: | ||
# gcloud builds submit --config release.yaml --no-source --substitutions=_TAG_NAME="v0.40.1-rc.1",_GITHUB_TOKEN="alias1",_VSCE_TOKEN="alias2" | ||
# | ||
# WARNING: this will publish the extension. | ||
# | ||
# This will check out the vscode-go repo on the specified tag, build the extension, | ||
# and publish it to the VS Code Marketplace and the GitHub Releases page if not published already. | ||
steps: | ||
# TODO: check build/test status | ||
# TODO: configure failure notification https://cloud.google.com/build/docs/configuring-notifications/notifiers | ||
- name: gcr.io/cloud-builders/git | ||
args: | ||
- clone | ||
- '--branch=$TAG_NAME' | ||
- '--single-branch' | ||
- '--depth=1' | ||
- 'https://go.googlesource.com/vscode-go' | ||
- vscode-go | ||
id: clone vscode-go repo | ||
- name: gcr.io/cloud-builders/docker | ||
args: | ||
- '-R' | ||
- '1000:1000' | ||
- /workspace | ||
- /builder/home | ||
dir: / | ||
id: adjust file permissions | ||
entrypoint: chown | ||
- name: us-docker.pkg.dev/$PROJECT_ID/vscode-go-docker-repo/ci-image | ||
args: | ||
- ci | ||
dir: vscode-go | ||
id: install npm dependencies | ||
entrypoint: npm | ||
- name: us-docker.pkg.dev/$PROJECT_ID/vscode-go-docker-repo/ci-image | ||
args: | ||
- -c | ||
- | | ||
go run build/release.go package && | ||
go run build/release.go publish | ||
dir: vscode-go | ||
id: package and publish the extension | ||
entrypoint: bash | ||
env: | ||
- 'TAG_NAME=$TAG_NAME' | ||
- 'COMMIT_SHA=$COMMIT_SHA' | ||
secretEnv: | ||
- VSCE_PAT | ||
- GITHUB_TOKEN | ||
timeout: 1800s | ||
options: | ||
substitutionOption: ALLOW_LOOSE | ||
artifacts: | ||
objects: | ||
location: 'gs://$PROJECT_ID/releases/$TAG_NAME' | ||
paths: | ||
- vscode-go/*.vsix | ||
availableSecrets: | ||
secretManager: | ||
- versionName: projects/$PROJECT_ID/secrets/$_VSCE_TOKEN/versions/latest | ||
env: VSCE_PAT | ||
- versionName: projects/$PROJECT_ID/secrets/$_GITHUB_TOKEN/versions/latest | ||
env: GITHUB_TOKEN |