blob: c424021a380b39197b53e4496e15288df8e84884 [file] [log] [blame]
// Copyright 2024 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.
package task
import (
"errors"
"fmt"
"io"
"io/fs"
"path/filepath"
"strings"
"time"
wf "golang.org/x/build/internal/workflow"
"golang.org/x/sync/errgroup"
)
// VSCodeGoReleaseTask releases vscode go.
//
// 1. cross-compile github.com/golang/vscode-go/vscgo and store the artifacts in the scratchFS.
// 2. (TODO) sign the artifacts.
// 3. (TODO) tag the repository.
// 4. (TODO) trigger the GCB workflow that reads the signed artifacts, packages, and publishes them.
// 5. (TODO) announce (SNS)
type VSCodeGoReleaseTask struct {
CloudBuild CloudBuildClient
*ScratchFS
Revision string
}
func checkVersion(v string) error {
if len(v) < 2 || v[0] != 'v' {
return errors.New("release version must start with 'v'")
}
return nil
}
var vscgoVersionParam = wf.ParamDef[string]{
Name: "Extension version to release",
Example: "v0.0.0-rc.1", Check: checkVersion,
}
func (t *VSCodeGoReleaseTask) NewDefinition() *wf.Definition {
wd := wf.New()
version := wf.Param(wd, vscgoVersionParam)
unsignedArtifacts := wf.Task1(wd, "build vscgo", t.buildVSCGO, version)
wf.Output(wd, "build artifacts", unsignedArtifacts)
// TODO: sign
return wd
}
var vscgoPlatforms = []struct {
Platform string
Env []string
}{
{Platform: "win32-x64", Env: []string{"GOOS=windows", "GOARCH=amd64"}},
{Platform: "win32-arm64", Env: []string{"GOOS=windows", "GOARCH=arm64"}},
{Platform: "darwin-x64", Env: []string{"GOOS=darwin", "GOARCH=amd64"}},
{Platform: "darwin-arm64", Env: []string{"GOOS=darwin", "GOARCH=arm64"}},
{Platform: "linux-x64", Env: []string{"GOOS=linux", "GOARCH=amd64"}},
{Platform: "linux-arm64", Env: []string{"GOOS=linux", "GOARCH=arm64"}},
// { Platform: "linux-arm", Env: []string{"GOOS=linux", "GOARCH=arm"}},
// { Platform:"linux-armhf", Env: []string{"GOOS=linux", "GOARCH=arm64", "GOARM=7"}},
}
type goBuildArtifact struct {
Platform string
Filename string
}
func (t *VSCodeGoReleaseTask) buildVSCGO(ctx *wf.TaskContext, version string) ([]goBuildArtifact, error) {
// TODO: version stamping won't use the tagged version with go build.
// TODO: encode it in vscode-go's build script. Then,
// we can just "go run -C extension tools/release/release.go build-vscgo".
var b strings.Builder
fmt.Fprintf(&b, "git fetch && git switch %v\n", t.Revision)
fmt.Fprintf(&b, "export OUT=$(mktemp -d /tmp/vscgo-XXXXXXXX)\n")
fmt.Fprintf(&b, "export CGO_ENABLED=0\n")
for _, info := range vscgoPlatforms {
envs := strings.Join(info.Env, " ")
base := "vscgo"
if strings.HasPrefix(info.Platform, "win32") {
base = "vscgo.exe"
}
fmt.Fprintf(&b, "mkdir ${OUT}/%v\n", info.Platform)
fmt.Fprintf(&b, "%v go build -o ${OUT}/%v/%v github.com/golang/vscode-go/vscgo\n", envs, info.Platform, base)
}
fmt.Fprintf(&b, "mkdir out && mv ${OUT}/* out/\n")
script := b.String()
build, err := t.CloudBuild.RunScript(ctx, script, "vscode-go", []string{"out"})
if err != nil {
return nil, err
}
if _, err := AwaitCondition(ctx, 30*time.Second, func() (string, bool, error) {
return t.CloudBuild.Completed(ctx, build)
}); err != nil {
return nil, err
}
outfs, err := t.CloudBuild.ResultFS(ctx, build)
if err != nil {
return nil, err
}
var artifacts []goBuildArtifact
err = fs.WalkDir(outfs, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if name := d.Name(); name != "vscgo" && name != "vscgo.exe" {
return nil
}
platform := filepath.Base(filepath.Dir(path)) // platform name is the parent name.
artifacts = append(artifacts, goBuildArtifact{
Platform: platform,
Filename: path,
})
return nil
})
var eg errgroup.Group
for i := range artifacts {
idx := i
eg.Go(func() error {
platform, path := artifacts[idx].Platform, artifacts[idx].Filename
in, err := outfs.Open(path)
if err != nil {
return err
}
defer in.Close()
name, out, err := t.ScratchFS.OpenWrite(ctx, platform+"-"+filepath.Base(path))
if err != nil {
return err
}
if _, err := io.Copy(out, in); err != nil {
out.Close()
return err
}
if err := out.Close(); err != nil {
return err
}
// replace artifacts
artifacts[idx] = goBuildArtifact{
Platform: platform,
Filename: name,
}
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
return artifacts, nil
}