internal/cuetest: consolidate testscript/cuetxtar helpers

Currently -update testing flags and the use of CUE_UPDATE to update the
golden files of testscript/cuetxtar scripts is littered throughout
various files/packages.

In this change we consolidate the use of -update flags to update golden
files into a single use of the CUE_UPDATE environment variable

We also defines a "long" build tag, along with an accompanying
testscript Condition function so that [long] can be used as a condition
in testscript scripts (used in a later CL)

Change-Id: If0898d4997010257d00ecf9b3bfdc1125796cbc4
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8601
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9db04ad..97462ae 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -57,6 +57,9 @@
         key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum')
           }}
         restore-keys: ${{ runner.os }}-${{ matrix.go-version }}-go-
+    - if: ${{ github.ref == 'refs/heads/master' }}
+      name: Set go build tags
+      run: go env -w GOFLAGS=-tags=long
     - if: matrix.go-version == '1.14.14' && matrix.os != 'windows-2019'
       name: Generate
       run: go generate ./...
diff --git a/cmd/cue/cmd/common_test.go b/cmd/cue/cmd/common_test.go
index d2c7184..8b122ba 100644
--- a/cmd/cue/cmd/common_test.go
+++ b/cmd/cue/cmd/common_test.go
@@ -15,7 +15,6 @@
 package cmd
 
 import (
-	"flag"
 	"os"
 	"testing"
 
@@ -24,8 +23,6 @@
 
 var _ = errors.Print
 
-var update = flag.Bool("update", os.Getenv("CUE_UPDATE") != "", "update the test files")
-
 func printConfig(t *testing.T) *errors.Config {
 	t.Helper()
 
diff --git a/cmd/cue/cmd/script_test.go b/cmd/cue/cmd/script_test.go
index 43012c9..45ae5d7 100644
--- a/cmd/cue/cmd/script_test.go
+++ b/cmd/cue/cmd/script_test.go
@@ -33,6 +33,7 @@
 
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/cuetest"
 )
 
 // TestLatest checks that the examples match the latest language standard,
@@ -80,7 +81,7 @@
 	}
 	p := testscript.Params{
 		Dir:           filepath.Join("testdata", "script"),
-		UpdateScripts: *update,
+		UpdateScripts: cuetest.UpdateGoldenFiles,
 		Setup: func(e *testscript.Env) error {
 			e.Vars = append(e.Vars,
 				"GOPROXY="+srv.URL,
@@ -88,6 +89,7 @@
 			)
 			return nil
 		},
+		Condition: cuetest.Condition,
 	}
 	if err := gotooltest.Setup(&p); err != nil {
 		t.Fatal(err)
diff --git a/cmd/cue/cmd/testdata/script/cmd_github.txt b/cmd/cue/cmd/testdata/script/cmd_github.txt
index 018f32f..3314ae3 100644
--- a/cmd/cue/cmd/testdata/script/cmd_github.txt
+++ b/cmd/cue/cmd/testdata/script/cmd_github.txt
@@ -232,6 +232,9 @@
         key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum')
           }}
         restore-keys: ${{ runner.os }}-${{ matrix.go-version }}-go-
+    - if: ${{ github.ref == 'refs/heads/master' }}
+      name: Set go build tags
+      run: go env -w GOFLAGS=-tags=long
     - if: matrix.go-version == '1.14.14' && matrix.os != 'windows-2019'
       name: Generate
       run: go generate ./...
@@ -1171,7 +1174,10 @@
 			needs:     "start"
 			strategy:  _#testStrategy
 			"runs-on": "${{ matrix.os }}"
-			steps: [_#writeCookiesFile, _#installGo, _#checkoutCode, _#cacheGoModules, _#goGenerate, _#goTest, _#goTestRace & {
+			steps: [_#writeCookiesFile, _#installGo, _#checkoutCode, _#cacheGoModules, _#setGoBuildTags & {
+				_#tags: "long"
+				if:     "${{ \(_#isMaster) }}"
+			}, _#goGenerate, _#goTest, _#goTestRace & {
 				if: "${{ \(_#isMaster) || \(_#isCLCITestBranch) && matrix.go-version == '\(_#latestStableGo)' && matrix.os == '\(_#linuxMachine)' }}"
 			}, _#goReleaseCheck, _#checkGitClean, _#pullThroughProxy, _#failCLBuild]
 		}
@@ -1401,6 +1407,11 @@
 		os: [_#linuxMachine, _#macosMachine, _#windowsMachine]
 	}
 }
+_#setGoBuildTags: _#step & {
+	_#tags: string
+	name:   "Set go build tags"
+	run:    "go env -w GOFLAGS=-tags=\(_#tags)"
+}
 _#installGo: _#step & {
 	name: "Install Go"
 	uses: "actions/setup-go@v2"
diff --git a/cue/format/format_test.go b/cue/format/format_test.go
index 47a0b6a..0c41f65 100644
--- a/cue/format/format_test.go
+++ b/cue/format/format_test.go
@@ -18,7 +18,6 @@
 
 import (
 	"bytes"
-	"flag"
 	"fmt"
 	"io/ioutil"
 	"path/filepath"
@@ -30,6 +29,7 @@
 	"cuelang.org/go/cue/parser"
 	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/cuetest"
 )
 
 var (
@@ -41,8 +41,6 @@
 	dataDir = "testdata"
 )
 
-var update = flag.Bool("update", false, "update golden files")
-
 type checkMode uint
 
 const (
@@ -136,7 +134,7 @@
 	}
 
 	// update golden files if necessary
-	if *update {
+	if cuetest.UpdateGoldenFiles {
 		if err := ioutil.WriteFile(golden, res, 0644); err != nil {
 			t.Error(err)
 		}
@@ -193,7 +191,7 @@
 	mode           checkMode
 }
 
-// Use go test -update to create/update the respective golden files.
+// Set CUE_UPDATE=1 to create/update the respective golden files.
 var data = []entry{
 	{"comments.input", "comments.golden", simplify},
 	{"simplify.input", "simplify.golden", simplify},
diff --git a/cue/testdata/eval/github.txtar b/cue/testdata/eval/github.txtar
index 1a4c38d..cf1d271 100644
--- a/cue/testdata/eval/github.txtar
+++ b/cue/testdata/eval/github.txtar
@@ -52,7 +52,10 @@
 			needs:     "start"
 			strategy:  _#testStrategy
 			"runs-on": "${{ matrix.os }}"
-			steps: [_#writeCookiesFile, _#installGo, _#checkoutCode, _#cacheGoModules, _#goGenerate, _#goTest, _#goTestRace & {
+			steps: [_#writeCookiesFile, _#installGo, _#checkoutCode, _#cacheGoModules, _#setGoBuildTags & {
+				_#tags: "long"
+				if:     "${{ \(_#isMaster) }}"
+			}, _#goGenerate, _#goTest, _#goTestRace & {
 				if: "${{ \(_#isMaster) || \(_#isCLCITestBranch) && matrix.go-version == '\(_#latestStableGo)' && matrix.os == '\(_#linuxMachine)' }}"
 			}, _#goReleaseCheck, _#checkGitClean, _#pullThroughProxy, _#failCLBuild]
 		}
@@ -282,6 +285,11 @@
 		os: [_#linuxMachine, _#macosMachine, _#windowsMachine]
 	}
 }
+_#setGoBuildTags: _#step & {
+	_#tags: string
+	name:   "Set go build tags"
+	run:    "go env -w GOFLAGS=-tags=\(_#tags)"
+}
 _#installGo: _#step & {
 	name: "Install Go"
 	uses: "actions/setup-go@v2"
@@ -1152,33 +1160,39 @@
                 }
               }
               4: (#struct){
+                _#tags(:ci): (string){ "long" }
+                name: (string){ "Set go build tags" }
+                run: (string){ "go env -w GOFLAGS=-tags=long" }
+                if: (string){ "${{ github.ref == 'refs/heads/master' }}" }
+              }
+              5: (#struct){
                 name: (string){ "Generate" }
                 run: (string){ "go generate ./..." }
                 if: (string){ "matrix.go-version == '1.14.14' && matrix.os != 'windows-2019'" }
               }
-              5: (#struct){
+              6: (#struct){
                 name: (string){ "Test" }
                 run: (string){ "go test ./..." }
               }
-              6: (#struct){
+              7: (#struct){
                 name: (string){ "Test with -race" }
                 run: (string){ "go test -race ./..." }
                 if: (string){ "${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/ci/') && matrix.go-version == '1.15.8' && matrix.os == 'ubuntu-18.04' }}" }
               }
-              7: (#struct){
+              8: (#struct){
                 name: (string){ "gorelease check" }
                 run: (string){ "go run golang.org/x/exp/cmd/gorelease" }
               }
-              8: (#struct){
+              9: (#struct){
                 name: (string){ "Check that git is clean post generate and tests" }
                 run: (string){ "test -z \"$(git status --porcelain)\" || (git status; git diff; false)" }
               }
-              9: (#struct){
+              10: (#struct){
                 name: (string){ "Pull this commit through the proxy on master" }
                 run: (string){ "v=$(git rev-parse HEAD)\ncd $(mktemp -d)\ngo mod init mod.com\nGOPROXY=https://proxy.golang.org go get -d cuelang.org/go/cmd/cue@$v" }
                 if: (string){ "${{ github.ref == 'refs/heads/master' }}" }
               }
-              10: (#struct){
+              11: (#struct){
                 if: (string){ "${{ startsWith(github.ref, 'refs/heads/ci/') && failure() }}" }
                 name: (string){ "Post any failures for this matrix entry" }
                 run: (string){ "curl -f -s -H \"Content-Type: application/json\" --request POST --data '{\"message\":\"Build failed for ${{ runner.os }}-${{ matrix.go-version }}; see ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} for more details\",\"labels\":{\"Code-Review\":-1}}' -b ~/.gitcookies https://cue-review.googlesource.com/a/changes/$(basename $(dirname $GITHUB_REF))/revisions/$(basename $GITHUB_REF)/review" }
@@ -1264,7 +1278,7 @@
             }
             res: (_|_){
               // [incomplete] invalid interpolation: cannot convert incomplete value "string" to JSON:
-              //     ./workflows.cue:122:9
+              //     ./workflows.cue:125:9
             }
           }
         }
@@ -1386,7 +1400,7 @@
           _#type(:ci): (string){ string }
           if: (_|_){
             // [incomplete] workflows.1.schema._#dispatchJob.if: invalid interpolation: non-concrete value string (type string):
-            //     ./workflows.cue:134:14
+            //     ./workflows.cue:137:14
           }
         }
         name: (string){ "Repository Dispatch" }
@@ -1770,33 +1784,39 @@
             }
           }
           4: (#struct){
+            _#tags(:ci): (string){ "long" }
+            name: (string){ "Set go build tags" }
+            run: (string){ "go env -w GOFLAGS=-tags=long" }
+            if: (string){ "${{ github.ref == 'refs/heads/master' }}" }
+          }
+          5: (#struct){
             name: (string){ "Generate" }
             run: (string){ "go generate ./..." }
             if: (string){ "matrix.go-version == '1.14.14' && matrix.os != 'windows-2019'" }
           }
-          5: (#struct){
+          6: (#struct){
             name: (string){ "Test" }
             run: (string){ "go test ./..." }
           }
-          6: (#struct){
+          7: (#struct){
             name: (string){ "Test with -race" }
             run: (string){ "go test -race ./..." }
             if: (string){ "${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/ci/') && matrix.go-version == '1.15.8' && matrix.os == 'ubuntu-18.04' }}" }
           }
-          7: (#struct){
+          8: (#struct){
             name: (string){ "gorelease check" }
             run: (string){ "go run golang.org/x/exp/cmd/gorelease" }
           }
-          8: (#struct){
+          9: (#struct){
             name: (string){ "Check that git is clean post generate and tests" }
             run: (string){ "test -z \"$(git status --porcelain)\" || (git status; git diff; false)" }
           }
-          9: (#struct){
+          10: (#struct){
             name: (string){ "Pull this commit through the proxy on master" }
             run: (string){ "v=$(git rev-parse HEAD)\ncd $(mktemp -d)\ngo mod init mod.com\nGOPROXY=https://proxy.golang.org go get -d cuelang.org/go/cmd/cue@$v" }
             if: (string){ "${{ github.ref == 'refs/heads/master' }}" }
           }
-          10: (#struct){
+          11: (#struct){
             if: (string){ "${{ startsWith(github.ref, 'refs/heads/ci/') && failure() }}" }
             name: (string){ "Post any failures for this matrix entry" }
             run: (string){ "curl -f -s -H \"Content-Type: application/json\" --request POST --data '{\"message\":\"Build failed for ${{ runner.os }}-${{ matrix.go-version }}; see ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} for more details\",\"labels\":{\"Code-Review\":-1}}' -b ~/.gitcookies https://cue-review.googlesource.com/a/changes/$(basename $(dirname $GITHUB_REF))/revisions/$(basename $GITHUB_REF)/review" }
@@ -1882,7 +1902,7 @@
         }
         res: (_|_){
           // [incomplete] invalid interpolation: cannot convert incomplete value "string" to JSON:
-          //     ./workflows.cue:122:9
+          //     ./workflows.cue:125:9
         }
       }
     }
@@ -2001,7 +2021,7 @@
       _#type(:ci): (string){ string }
       if: (_|_){
         // [incomplete] repository_dispatch._#dispatchJob.if: invalid interpolation: non-concrete value string (type string):
-        //     ./workflows.cue:134:14
+        //     ./workflows.cue:137:14
       }
     }
     name: (string){ "Repository Dispatch" }
@@ -2356,6 +2376,14 @@
       }
     }
   }
+  _#setGoBuildTags(:ci): (#struct){
+    _#tags(:ci): (string){ string }
+    name: (string){ "Set go build tags" }
+    run: (_|_){
+      // [incomplete] _#setGoBuildTags.run: invalid interpolation: non-concrete value string (type string):
+      //     ./workflows.cue:283:10
+    }
+  }
   _#installGo(:ci): (#struct){
     name: (string){ "Install Go" }
     uses: (string){ "actions/setup-go@v2" }
@@ -2407,7 +2435,7 @@
   _#cueckooCopybaraImage(:ci): (string){ "cueckoo/copybara:afc4ae03eed00b0c9d7415141cd1b5dfa583da7c" }
   _#copybaraCmd(:ci): (_|_){
     // [incomplete] _#copybaraCmd: invalid interpolation: non-concrete value string (type string):
-    //     ./workflows.cue:342:2
+    //     ./workflows.cue:350:2
     _#cmd(:ci): (string){ string }
   }
   _#copybaraSteps(:ci): (#list){
@@ -2421,7 +2449,7 @@
       name: (string){ string }
       run: (_|_){
         // [incomplete] _#copybaraSteps.1.run: invalid interpolation: non-concrete value string (type string):
-        //     ./workflows.cue:342:2
+        //     ./workflows.cue:350:2
         _#cmd(:ci): (string){ string }
       }
     }
@@ -2493,6 +2521,10 @@
           〈3;_#installGo〉,
           〈3;_#checkoutCode〉,
           〈3;_#cacheGoModules〉,
+          (〈3;_#setGoBuildTags〉 & {
+            _#tags: "long"
+            if: "${{ \(〈3;_#isMaster〉) }}"
+          }),
           〈3;_#goGenerate〉,
           〈3;_#goTest〉,
           (〈3;_#goTestRace〉 & {
@@ -2769,6 +2801,11 @@
       ]
     }
   }
+  _#setGoBuildTags: (〈0;_#step〉 & {
+    _#tags: string
+    name: "Set go build tags"
+    run: "go env -w GOFLAGS=-tags=\(〈0;_#tags〉)"
+  })
   _#installGo: (〈0;_#step〉 & {
     name: "Install Go"
     uses: "actions/setup-go@v2"
diff --git a/doc/tutorial/basics/script_test.go b/doc/tutorial/basics/script_test.go
index c91414e..02a385d 100644
--- a/doc/tutorial/basics/script_test.go
+++ b/doc/tutorial/basics/script_test.go
@@ -1,7 +1,6 @@
 package basics
 
 import (
-	"flag"
 	"os"
 	"path"
 	"path/filepath"
@@ -13,10 +12,9 @@
 
 	"cuelang.org/go/cmd/cue/cmd"
 	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/cuetest"
 )
 
-var update = flag.Bool("update", false, "update the test files")
-
 // TestLatest checks that the examples match the latest language standard,
 // even if still valid in backwards compatibility mode.
 func TestLatest(t *testing.T) {
@@ -54,7 +52,7 @@
 		}
 		testscript.Run(t, testscript.Params{
 			Dir:           path,
-			UpdateScripts: *update,
+			UpdateScripts: cuetest.UpdateGoldenFiles,
 		})
 		return nil
 	})
diff --git a/doc/tutorial/kubernetes/tut_test.go b/doc/tutorial/kubernetes/tut_test.go
index 2156e08..b32fdcd 100644
--- a/doc/tutorial/kubernetes/tut_test.go
+++ b/doc/tutorial/kubernetes/tut_test.go
@@ -38,7 +38,6 @@
 )
 
 var (
-	update  = flag.Bool("update", false, "update test data")
 	cleanup = flag.Bool("cleanup", true, "clean up generated files")
 )
 
@@ -78,7 +77,7 @@
 		// Stdin: strings.NewReader(input),
 	})
 
-	if *update {
+	if cuetest.UpdateGoldenFiles {
 		// The test environment won't work in all environments. We create
 		// a fake go.mod so that Go will find the module root. By default
 		// we won't set it.
@@ -164,7 +163,7 @@
 					break
 				}
 
-				if !*update && strings.HasPrefix(cmd, "cue get") {
+				if !cuetest.UpdateGoldenFiles && strings.HasPrefix(cmd, "cue get") {
 					// Don't fetch stuff in normal mode.
 					break
 				}
@@ -207,7 +206,7 @@
 		t.Fatal(err)
 	}
 
-	if *update {
+	if cuetest.UpdateGoldenFiles {
 		// Remove all old cue files.
 		err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
 			if isCUE(path) {
@@ -286,7 +285,7 @@
 
 			testfile := filepath.Join("testdata", dir+".out")
 
-			if *update {
+			if cuetest.UpdateGoldenFiles {
 				err := ioutil.WriteFile(testfile, got, 0644)
 				if err != nil {
 					t.Fatal(err)
diff --git a/encoding/gocode/generator_test.go b/encoding/gocode/generator_test.go
index d1c2740..5262fce 100644
--- a/encoding/gocode/generator_test.go
+++ b/encoding/gocode/generator_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"bytes"
-	"flag"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -28,11 +27,10 @@
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/load"
+	"cuelang.org/go/internal/cuetest"
 	_ "cuelang.org/go/pkg"
 )
 
-var update = flag.Bool("update", false, "update test files")
-
 func TestGenerate(t *testing.T) {
 	dirs, err := ioutil.ReadDir("testdata")
 	if err != nil {
@@ -67,7 +65,7 @@
 			}
 
 			goFile := filepath.Join("testdata", d.Name(), "cue_gen.go")
-			if *update {
+			if cuetest.UpdateGoldenFiles {
 				_ = ioutil.WriteFile(goFile, b, 0644)
 				return
 			}
diff --git a/encoding/jsonschema/decode_test.go b/encoding/jsonschema/decode_test.go
index 81a27f7..9912cd8 100644
--- a/encoding/jsonschema/decode_test.go
+++ b/encoding/jsonschema/decode_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"bytes"
-	"flag"
 	"io/ioutil"
 	"os"
 	"path"
@@ -35,15 +34,14 @@
 	"cuelang.org/go/cue/token"
 	"cuelang.org/go/encoding/json"
 	"cuelang.org/go/encoding/yaml"
+	"cuelang.org/go/internal/cuetest"
 	_ "cuelang.org/go/pkg"
 )
 
-var update = flag.Bool("update", os.Getenv("CUE_UPDATE") != "", "update the test files")
-
 // TestDecode reads the testdata/*.txtar files, converts the contained
 // JSON schema to CUE and compares it against the output.
 //
-// Use the --update flag to update test files with the corresponding output.
+// Set CUE_UPDATE=1 to update test files with the corresponding output.
 func TestDecode(t *testing.T) {
 	err := filepath.Walk("testdata", func(fullpath string, info os.FileInfo, err error) error {
 		_ = err
@@ -102,7 +100,7 @@
 
 				switch {
 				case !cmp.Equal(errout, got):
-					if *update {
+					if cuetest.UpdateGoldenFiles {
 						a.Files[errIndex].Data = got
 						updated = true
 						break
@@ -129,7 +127,7 @@
 
 				switch {
 				case !cmp.Equal(b, out):
-					if *update {
+					if cuetest.UpdateGoldenFiles {
 						updated = true
 						a.Files[outIndex].Data = b
 						break
diff --git a/encoding/openapi/decode_test.go b/encoding/openapi/decode_test.go
index 8a6d8a8..046ce75 100644
--- a/encoding/openapi/decode_test.go
+++ b/encoding/openapi/decode_test.go
@@ -33,12 +33,13 @@
 	"cuelang.org/go/encoding/json"
 	"cuelang.org/go/encoding/openapi"
 	"cuelang.org/go/encoding/yaml"
+	"cuelang.org/go/internal/cuetest"
 )
 
 // TestDecode reads the testdata/*.txtar files, converts the contained
 // JSON schema to CUE and compares it against the output.
 //
-// Use the --update flag to update test files with the corresponding output.
+// Set CUE_UPDATE=1 to update test files with the corresponding output.
 func TestDecode(t *testing.T) {
 	err := filepath.Walk("testdata/script", func(fullpath string, info os.FileInfo, err error) error {
 		_ = err
@@ -103,7 +104,7 @@
 				out = bytes.TrimSpace(out)
 
 				if !cmp.Equal(b, out) {
-					if *update {
+					if cuetest.UpdateGoldenFiles {
 						a.Files[outIndex].Data = b
 						b = txtar.Format(a)
 						err = ioutil.WriteFile(fullpath, b, 0644)
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index 0c04bfa..1575933 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -17,7 +17,6 @@
 import (
 	"bytes"
 	"encoding/json"
-	"flag"
 	"io/ioutil"
 	"path/filepath"
 	"strings"
@@ -30,10 +29,9 @@
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/load"
 	"cuelang.org/go/encoding/openapi"
+	"cuelang.org/go/internal/cuetest"
 )
 
-var update *bool = flag.Bool("update", false, "update the test output")
-
 func TestParseDefinitions(t *testing.T) {
 	info := *(*openapi.OrderedMap)(ast.NewStruct(
 		"title", ast.NewString("test"),
@@ -150,7 +148,7 @@
 			_ = json.Indent(out, b, "", "   ")
 
 			wantFile := filepath.Join("testdata", tc.out)
-			if *update {
+			if cuetest.UpdateGoldenFiles {
 				_ = ioutil.WriteFile(wantFile, out.Bytes(), 0644)
 				return
 			}
diff --git a/encoding/protobuf/protobuf_test.go b/encoding/protobuf/protobuf_test.go
index 144fd31..53213a2 100644
--- a/encoding/protobuf/protobuf_test.go
+++ b/encoding/protobuf/protobuf_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"bytes"
-	"flag"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -29,10 +28,9 @@
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/format"
+	"cuelang.org/go/internal/cuetest"
 )
 
-var update = flag.Bool("update", false, "update the test output")
-
 func TestExtractDefinitions(t *testing.T) {
 	testCases := []string{
 		"networking/v1alpha3/gateway.proto",
@@ -58,7 +56,7 @@
 			}
 
 			wantFile := filepath.Join("testdata", filepath.Base(file)+".out.cue")
-			if *update {
+			if cuetest.UpdateGoldenFiles {
 				_ = ioutil.WriteFile(wantFile, out.Bytes(), 0644)
 				return
 			}
@@ -98,7 +96,7 @@
 		t.Fatal(errors.Details(err, nil))
 	}
 
-	if *update {
+	if cuetest.UpdateGoldenFiles {
 		for _, f := range files {
 			b, err := format.Node(f)
 			if err != nil {
diff --git a/internal/ci/workflows.cue b/internal/ci/workflows.cue
index e068f45..37ac223 100644
--- a/internal/ci/workflows.cue
+++ b/internal/ci/workflows.cue
@@ -76,6 +76,10 @@
 				_#installGo,
 				_#checkoutCode,
 				_#cacheGoModules,
+				_#setGoBuildTags & {
+					_#tags: "long"
+					if:     "${{ \(_#isMaster) }}"
+				},
 				_#goGenerate,
 				_#goTest,
 				_#goTestRace & {
@@ -348,6 +352,14 @@
 	}
 }
 
+_#setGoBuildTags: _#step & {
+	_#tags: string
+	name:   "Set go build tags"
+	run:    """
+		go env -w GOFLAGS=-tags=\(_#tags)
+		"""
+}
+
 _#installGo: _#step & {
 	name: "Install Go"
 	uses: "actions/setup-go@v2"
diff --git a/internal/core/adt/eval_test.go b/internal/core/adt/eval_test.go
index ee351c1..e52677e 100644
--- a/internal/core/adt/eval_test.go
+++ b/internal/core/adt/eval_test.go
@@ -27,20 +27,20 @@
 	"cuelang.org/go/internal/core/debug"
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/validate"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 	_ "cuelang.org/go/pkg"
 )
 
 var (
-	update = flag.Bool("update", false, "update the test files")
-	todo   = flag.Bool("todo", false, "run tests marked with #todo-compile")
+	todo = flag.Bool("todo", false, "run tests marked with #todo-compile")
 )
 
 func TestEval(t *testing.T) {
 	test := cuetxtar.TxTarTest{
 		Root:   "../../../cue/testdata",
 		Name:   "eval",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 		Skip:   alwaysSkip,
 		ToDo:   needFix,
 	}
diff --git a/internal/core/compile/compile_test.go b/internal/core/compile/compile_test.go
index 4072ed9..e4ea14d 100644
--- a/internal/core/compile/compile_test.go
+++ b/internal/core/compile/compile_test.go
@@ -25,19 +25,19 @@
 	"cuelang.org/go/internal/core/compile"
 	"cuelang.org/go/internal/core/debug"
 	"cuelang.org/go/internal/core/runtime"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 )
 
 var (
-	update = flag.Bool("update", false, "update the test files")
-	todo   = flag.Bool("todo", false, "run tests marked with #todo-compile")
+	todo = flag.Bool("todo", false, "run tests marked with #todo-compile")
 )
 
 func TestCompile(t *testing.T) {
 	test := cuetxtar.TxTarTest{
 		Root:   "../../../cue/testdata/",
 		Name:   "compile",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 		Skip:   alwaysSkip,
 		ToDo:   needFix,
 	}
diff --git a/internal/core/dep/dep_test.go b/internal/core/dep/dep_test.go
index d4d6313..6fb476b 100644
--- a/internal/core/dep/dep_test.go
+++ b/internal/core/dep/dep_test.go
@@ -15,7 +15,6 @@
 package dep_test
 
 import (
-	"flag"
 	"fmt"
 	"strings"
 	"testing"
@@ -27,16 +26,15 @@
 	"cuelang.org/go/internal/core/dep"
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/runtime"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 )
 
-var update = flag.Bool("update", false, "update the test files")
-
 func TestVisit(t *testing.T) {
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "dependencies",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 	}
 
 	test.Run(t, func(t *cuetxtar.Test) {
diff --git a/internal/core/export/export_test.go b/internal/core/export/export_test.go
index 3c1dda4..73990b9 100644
--- a/internal/core/export/export_test.go
+++ b/internal/core/export/export_test.go
@@ -15,7 +15,6 @@
 package export_test
 
 import (
-	"flag"
 	"testing"
 
 	"cuelang.org/go/cue"
@@ -31,17 +30,16 @@
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/export"
 	"cuelang.org/go/internal/core/runtime"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 	"github.com/rogpeppe/go-internal/txtar"
 )
 
-var update = flag.Bool("update", false, "update the test files")
-
 func TestDefinition(t *testing.T) {
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "definition",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 	}
 
 	r := cue.NewRuntime()
diff --git a/internal/core/export/extract_test.go b/internal/core/export/extract_test.go
index a542975..df76632 100644
--- a/internal/core/export/extract_test.go
+++ b/internal/core/export/extract_test.go
@@ -23,6 +23,7 @@
 	"cuelang.org/go/internal/core/compile"
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/export"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 )
 
@@ -30,7 +31,7 @@
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "doc",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 	}
 
 	r := cue.NewRuntime()
diff --git a/internal/core/export/value_test.go b/internal/core/export/value_test.go
index df46544..7e7fc65 100644
--- a/internal/core/export/value_test.go
+++ b/internal/core/export/value_test.go
@@ -26,6 +26,7 @@
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/export"
 	"cuelang.org/go/internal/core/runtime"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 	"github.com/rogpeppe/go-internal/txtar"
 )
@@ -38,7 +39,7 @@
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "value",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 		Skip:   exclude,
 	}
 
diff --git a/internal/cuetest/cuetest.go b/internal/cuetest/cuetest.go
new file mode 100644
index 0000000..9d98116
--- /dev/null
+++ b/internal/cuetest/cuetest.go
@@ -0,0 +1,40 @@
+// Copyright 2021 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package testing is a helper package for test packages in the CUE project.
+// As such it should only be imported in _test.go files.
+package cuetest
+
+import (
+	"fmt"
+	"os"
+)
+
+// UpdateGoldenFiles determines whether testscript scripts should update txtar
+// archives in the event of cmp failures. It corresponds to
+// testscript.Params.UpdateGoldenFiles. See the docs for
+// testscript.Params.UpdateGoldenFiles for more details.
+var UpdateGoldenFiles = os.Getenv("CUE_UPDATE") != ""
+
+// Condition adds support for CUE-specific testscript conditions within
+// testscript scripts. The canonical case being [long] which evalutates to true
+// when the long build tag is specified, as is used to indicate that long tests
+// should be run.
+func Condition(cond string) (bool, error) {
+	switch cond {
+	case "long":
+		return Long, nil
+	}
+	return false, fmt.Errorf("unknown condition %v", cond)
+}
diff --git a/internal/cuetest/long.go b/internal/cuetest/long.go
new file mode 100644
index 0000000..01d6c74
--- /dev/null
+++ b/internal/cuetest/long.go
@@ -0,0 +1,21 @@
+// Copyright 2021 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build long
+
+package cuetest
+
+// LongTest is a build tag used to indicate that long tests should be run
+// Note this is not the equivalent of not supplying -short.
+const Long = true
diff --git a/internal/cuetest/nolong.go b/internal/cuetest/nolong.go
new file mode 100644
index 0000000..5be85a3
--- /dev/null
+++ b/internal/cuetest/nolong.go
@@ -0,0 +1,21 @@
+// Copyright 2021 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !long
+
+package cuetest
+
+// LongTest is a build tag used to indicate that long tests should be run.
+// Note this is not the equivalent of not supplying -short.
+const Long = false
diff --git a/internal/cuetxtar/txtar.go b/internal/cuetxtar/txtar.go
index ec2d126..759785c 100644
--- a/internal/cuetxtar/txtar.go
+++ b/internal/cuetxtar/txtar.go
@@ -31,12 +31,11 @@
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/load"
+	"cuelang.org/go/internal/cuetest"
 	"github.com/google/go-cmp/cmp"
 	"github.com/rogpeppe/go-internal/txtar"
 )
 
-var envUpdate = os.Getenv("CUE_UPDATE")
-
 // A TxTarTest represents a test run that process all CUE tests in the txtar
 // format rooted in a given directory.
 type TxTarTest struct {
@@ -310,7 +309,7 @@
 					continue
 				}
 
-				if x.Update || envUpdate != "" {
+				if x.Update || cuetest.UpdateGoldenFiles {
 					update = true
 					gold.Data = result
 					continue
diff --git a/internal/third_party/yaml/decode_test.go b/internal/third_party/yaml/decode_test.go
index 66fb314..d08104e 100644
--- a/internal/third_party/yaml/decode_test.go
+++ b/internal/third_party/yaml/decode_test.go
@@ -2,7 +2,6 @@
 
 import (
 	"errors"
-	"flag"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -12,11 +11,10 @@
 
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/format"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/third_party/yaml"
 )
 
-var update = flag.Bool("update", false, "update test data")
-
 var unmarshalIntTest = 123
 
 var unmarshalTests = []struct {
@@ -876,7 +874,7 @@
 				t.Fatal(err)
 			}
 			got := cueStr(expr)
-			if *update {
+			if cuetest.UpdateGoldenFiles {
 				ioutil.WriteFile(filename, []byte(got), 0644)
 				return
 			}
diff --git a/tools/fix/fixall_test.go b/tools/fix/fixall_test.go
index cd19f90..9004394 100644
--- a/tools/fix/fixall_test.go
+++ b/tools/fix/fixall_test.go
@@ -15,23 +15,21 @@
 package fix
 
 import (
-	"flag"
 	"fmt"
 	"testing"
 
 	"cuelang.org/go/cue/format"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 )
 
-var update = flag.Bool("update", false, "update the test files")
-
 func TestInstances(t *testing.T) {
 	t.Skip()
 
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "fixmod",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 	}
 
 	test.Run(t, func(t *cuetxtar.Test) {
diff --git a/tools/flow/flow_test.go b/tools/flow/flow_test.go
index 0711d97..c34d97c 100644
--- a/tools/flow/flow_test.go
+++ b/tools/flow/flow_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"context"
-	"flag"
 	"fmt"
 	"os"
 	"path"
@@ -27,19 +26,18 @@
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/format"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 	"cuelang.org/go/tools/flow"
 )
 
-var update = flag.Bool("update", false, "update the test files")
-
 // TestTasks tests the logic that determines which nodes are tasks and what are
 // their dependencies.
 func TestFlow(t *testing.T) {
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "run",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 	}
 
 	test.Run(t, func(t *cuetxtar.Test) {
diff --git a/tools/trim/trim_test.go b/tools/trim/trim_test.go
index 734e905..e3ea234 100644
--- a/tools/trim/trim_test.go
+++ b/tools/trim/trim_test.go
@@ -15,7 +15,6 @@
 package trim
 
 import (
-	"flag"
 	"testing"
 
 	"cuelang.org/go/cue"
@@ -23,12 +22,11 @@
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/cuetest"
 	"cuelang.org/go/internal/cuetxtar"
 	"github.com/rogpeppe/go-internal/txtar"
 )
 
-var update = flag.Bool("update", false, "update the test files")
-
 func TestFiles(t *testing.T) {
 	testCases := []struct {
 		name string
@@ -281,7 +279,7 @@
 	test := cuetxtar.TxTarTest{
 		Root:   "./testdata",
 		Name:   "trim",
-		Update: *update,
+		Update: cuetest.UpdateGoldenFiles,
 	}
 
 	test.Run(t, func(t *cuetxtar.Test) {