golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/task/vscodego.go (about) 1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package task 6 7 import ( 8 "errors" 9 "fmt" 10 "io" 11 "io/fs" 12 "path/filepath" 13 "strings" 14 "time" 15 16 wf "golang.org/x/build/internal/workflow" 17 "golang.org/x/sync/errgroup" 18 ) 19 20 // VSCodeGoReleaseTask releases vscode go. 21 // 22 // 1. cross-compile github.com/golang/vscode-go/vscgo and store the artifacts in the scratchFS. 23 // 2. (TODO) sign the artifacts. 24 // 3. (TODO) tag the repository. 25 // 4. (TODO) trigger the GCB workflow that reads the signed artifacts, packages, and publishes them. 26 // 5. (TODO) announce (SNS) 27 type VSCodeGoReleaseTask struct { 28 CloudBuild CloudBuildClient 29 *ScratchFS 30 Revision string 31 } 32 33 func checkVersion(v string) error { 34 if len(v) < 2 || v[0] != 'v' { 35 return errors.New("release version must start with 'v'") 36 } 37 return nil 38 } 39 40 var vscgoVersionParam = wf.ParamDef[string]{ 41 Name: "Extension version to release", 42 Example: "v0.0.0-rc.1", Check: checkVersion, 43 } 44 45 func (t *VSCodeGoReleaseTask) NewDefinition() *wf.Definition { 46 wd := wf.New() 47 48 version := wf.Param(wd, vscgoVersionParam) 49 unsignedArtifacts := wf.Task1(wd, "build vscgo", t.buildVSCGO, version) 50 wf.Output(wd, "build artifacts", unsignedArtifacts) 51 52 // TODO: sign 53 return wd 54 } 55 56 var vscgoPlatforms = []struct { 57 Platform string 58 Env []string 59 }{ 60 {Platform: "win32-x64", Env: []string{"GOOS=windows", "GOARCH=amd64"}}, 61 {Platform: "win32-arm64", Env: []string{"GOOS=windows", "GOARCH=arm64"}}, 62 {Platform: "darwin-x64", Env: []string{"GOOS=darwin", "GOARCH=amd64"}}, 63 {Platform: "darwin-arm64", Env: []string{"GOOS=darwin", "GOARCH=arm64"}}, 64 {Platform: "linux-x64", Env: []string{"GOOS=linux", "GOARCH=amd64"}}, 65 {Platform: "linux-arm64", Env: []string{"GOOS=linux", "GOARCH=arm64"}}, 66 // { Platform: "linux-arm", Env: []string{"GOOS=linux", "GOARCH=arm"}}, 67 // { Platform:"linux-armhf", Env: []string{"GOOS=linux", "GOARCH=arm64", "GOARM=7"}}, 68 } 69 70 type goBuildArtifact struct { 71 Platform string 72 Filename string 73 } 74 75 func (t *VSCodeGoReleaseTask) buildVSCGO(ctx *wf.TaskContext, version string) ([]goBuildArtifact, error) { 76 // TODO: version stamping won't use the tagged version with go build. 77 78 // TODO: encode it in vscode-go's build script. Then, 79 // we can just "go run -C extension tools/release/release.go build-vscgo". 80 var b strings.Builder 81 fmt.Fprintf(&b, "git fetch && git switch %v\n", t.Revision) 82 fmt.Fprintf(&b, "export OUT=$(mktemp -d /tmp/vscgo-XXXXXXXX)\n") 83 fmt.Fprintf(&b, "export CGO_ENABLED=0\n") 84 for _, info := range vscgoPlatforms { 85 envs := strings.Join(info.Env, " ") 86 base := "vscgo" 87 if strings.HasPrefix(info.Platform, "win32") { 88 base = "vscgo.exe" 89 } 90 fmt.Fprintf(&b, "mkdir ${OUT}/%v\n", info.Platform) 91 fmt.Fprintf(&b, "%v go build -o ${OUT}/%v/%v github.com/golang/vscode-go/vscgo\n", envs, info.Platform, base) 92 } 93 fmt.Fprintf(&b, "mkdir out && mv ${OUT}/* out/\n") 94 script := b.String() 95 96 build, err := t.CloudBuild.RunScript(ctx, script, "vscode-go", []string{"out"}) 97 if err != nil { 98 return nil, err 99 } 100 if _, err := AwaitCondition(ctx, 30*time.Second, func() (string, bool, error) { 101 return t.CloudBuild.Completed(ctx, build) 102 }); err != nil { 103 return nil, err 104 } 105 outfs, err := t.CloudBuild.ResultFS(ctx, build) 106 if err != nil { 107 return nil, err 108 } 109 var artifacts []goBuildArtifact 110 err = fs.WalkDir(outfs, ".", func(path string, d fs.DirEntry, err error) error { 111 if err != nil { 112 return err 113 } 114 if d.IsDir() { 115 return nil 116 } 117 if name := d.Name(); name != "vscgo" && name != "vscgo.exe" { 118 return nil 119 } 120 platform := filepath.Base(filepath.Dir(path)) // platform name is the parent name. 121 artifacts = append(artifacts, goBuildArtifact{ 122 Platform: platform, 123 Filename: path, 124 }) 125 return nil 126 }) 127 128 var eg errgroup.Group 129 for i := range artifacts { 130 idx := i 131 eg.Go(func() error { 132 platform, path := artifacts[idx].Platform, artifacts[idx].Filename 133 134 in, err := outfs.Open(path) 135 if err != nil { 136 return err 137 } 138 defer in.Close() 139 name, out, err := t.ScratchFS.OpenWrite(ctx, platform+"-"+filepath.Base(path)) 140 if err != nil { 141 return err 142 } 143 if _, err := io.Copy(out, in); err != nil { 144 out.Close() 145 return err 146 } 147 if err := out.Close(); err != nil { 148 return err 149 } 150 // replace artifacts 151 artifacts[idx] = goBuildArtifact{ 152 Platform: platform, 153 Filename: name, 154 } 155 return nil 156 }) 157 } 158 if err := eg.Wait(); err != nil { 159 return nil, err 160 } 161 return artifacts, nil 162 }