Engineering Software Lab
Every change Amp made to Gruntwork Terratest, reviewed line by line — the correctness fix, the CVE dependency bumps, and the local performance optimizations, shown as real git diffs against upstream.
All diffs are measured against the original upstream origin/main (gruntwork-io/terratest). Two groups were submitted as focused PRs; the performance hoists were kept local to avoid flooding maintainers.
go.mod · go.sum — security-only version bumps
modules/random/random.go — correctness bug
4 files — regex compiled once + CI pin
| Group | Files | + / − | Status |
|---|---|---|---|
| PR #1860 | go.mod, go.sum | +27 / −27 | submitted |
| PR #1861 | modules/random/random.go | +2 / −9 | submitted |
| Local | packer, parser, docker, CI | +17 / −9 | not submitted |
UniqueID() namespaces real cloud resources. Collisions mean two parallel tests can target the same resource name — causing flaky failures or cross-test interference on live infrastructure.
@@ -4,12 +4,11 @@ package random import ( "bytes" "math/rand"- "time" ) // Random generates a random int between min and max, inclusive. func Random(min int, max int) int {- return newRand().Intn(max-min+1) + min+ return rand.Intn(max-min+1) + min } // RandomInt picks a random element in the slice of ints.@@ -33,9 +32,8 @@ const uniqueIDLength = 6 // Should be good for 62^6 = 56+ billion combinations func UniqueID() string { var out bytes.Buffer - generator := newRand() for i := 0; i < uniqueIDLength; i++ {- out.WriteByte(base62chars[generator.Intn(len(base62chars))])+ out.WriteByte(base62chars[rand.Intn(len(base62chars))]) } return out.String()@@ -49,8 +47,3 @@ func UniqueID() string { func UniqueId() string { return UniqueID() }--// newRand creates a new random number generator, seeding it with the current system time.-func newRand() *rand.Rand {- return rand.New(rand.NewSource(time.Now().UnixNano()))-} @@ -27,8 +27,8 @@ require ( github.com/tmccombs/hcl2json v0.6.4 github.com/urfave/cli v1.22.16 github.com/zclconf/go-cty v1.15.0- golang.org/x/crypto v0.49.0- golang.org/x/net v0.52.0 // indirect+ golang.org/x/crypto v0.52.0+ golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 google.golang.org/api v0.276.0 google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect@@ -96,7 +96,7 @@ require ( github.com/gonvenience/ytbx v1.4.4 github.com/hashicorp/go-getter/v2 v2.2.3 github.com/homeport/dyff v1.6.0- github.com/jackc/pgx/v5 v5.9.0+ github.com/jackc/pgx/v5 v5.9.2 github.com/lib/pq v1.10.9 github.com/microsoft/go-mssqldb v1.9.8 golang.org/x/sync v0.20.0@@ -205,7 +205,7 @@ require ( github.com/spf13/pflag v1.0.9 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/texttheater/golang-levenshtein v1.0.1 // indirect- github.com/ulikunitz/xz v0.5.10 // indirect+ github.com/ulikunitz/xz v0.5.15 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 // indirect github.com/x448/float16 v0.8.4 // indirect@@ -222,12 +222,12 @@ require ( go.opentelemetry.io/otel/trace v1.43.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect- golang.org/x/mod v0.33.0 // indirect- golang.org/x/sys v0.42.0 // indirect- golang.org/x/term v0.41.0 // indirect- golang.org/x/text v0.35.0 // indirect+ golang.org/x/mod v0.35.0 // indirect+ golang.org/x/sys v0.45.0 // indirect+ golang.org/x/term v0.43.0 // indirect+ golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect- golang.org/x/tools v0.42.0 // indirect+ golang.org/x/tools v0.44.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect func F(s string) string { re := regexp.MustCompile(`…`) return re.FindString(s)}Recompiles the pattern on every call — wasted CPU and allocations on the hot path.
var reF = regexp.MustCompile(`…`) func F(s string) string { return reF.FindString(s)}Compiled once at package load; the hot path only matches.
Applied to packer.go, parser.go and docker_compose.go.
@@ -24,6 +24,13 @@ import ( // ErrArtifactIDNotFound is returned when the Packer output does not contain an artifact ID. var ErrArtifactIDNotFound = errors.New("could not find artifact ID pattern in packer output") +// artifactIDRegexp and packerVersionRegexp are compiled once at package load instead of on every+// call to ExtractArtifactID / TrimPackerVersion.+var (+ artifactIDRegexp = regexp.MustCompile(`.+artifact,\d+?,id,(?:.+?:|)(.+)`)+ packerVersionRegexp = regexp.MustCompile(`(?:Packer v?|)(\d+\.\d+\.\d+)`)+)+ // BuildNameNotFoundError is returned when the specified build name is not found in the manifest file. type BuildNameNotFoundError struct { BuildName string@@ -239,8 +246,7 @@ const artifactIDMatchLen = 2 // 1456332887,amazon-ebs,artifact,0,id,us-east-1:ami-b481b3de // 1533742764,googlecompute,artifact,0,id,terratest-packer-example-2018-08-08t15-35-19z func ExtractArtifactID(packerLogOutput string) (string, error) {- re := regexp.MustCompile(`.+artifact,\d+?,id,(?:.+?:|)(.+)`)- matches := re.FindStringSubmatch(packerLogOutput)+ matches := artifactIDRegexp.FindStringSubmatch(packerLogOutput) if len(matches) == artifactIDMatchLen { return matches[1], nil@@ -363,8 +369,7 @@ func FormatPackerArgs(options *Options) []string { // TrimPackerVersion extracts the version number from packer version output. // From packer 1.10 the -version command output is prefixed with "Packer v". func TrimPackerVersion(versionCmdOutput string) string {- re := regexp.MustCompile(`(?:Packer v?|)(\d+\.\d+\.\d+)`)- matches := re.FindStringSubmatch(versionCmdOutput)+ matches := packerVersionRegexp.FindStringSubmatch(versionCmdOutput) if len(matches) > 1 { return matches[1] @@ -56,6 +56,7 @@ var ( regexStatus = regexp.MustCompile(`=== (RUN|PAUSE|CONT)\s+(.+)`) regexSummary = regexp.MustCompile(`(^FAIL$)|(^(ok|FAIL)\s+([^ ]+)\s+(?:(\d+\.\d+)s|\(cached\)|(\[\w+ failed]))(?:\s+coverage:\s+(\d+\.\d+)%\sof\sstatements(?:\sin\s.+)?)?$)`) regexPanic = regexp.MustCompile(`^panic:`)+ regexIndent = regexp.MustCompile(`^\s+`) ) // GetIndent takes a line and returns the indent string@@ -64,9 +65,7 @@ var ( // in: " --- FAIL: TestSnafu" // out: " " func GetIndent(data string) string {- re := regexp.MustCompile(`^\s+`)-- return re.FindString(data)+ return regexIndent.FindString(data) } // GetTestNameFromResultLine takes a go testing result line and extracts out the test name @@ -119,10 +119,14 @@ func runDockerComposeE(t testing.TestingT, ctx context.Context, stdout bool, opt return shell.RunCommandContextAndGetOutputE(t, ctx, cmd) } +// invalidDockerComposeProjectNameChars matches characters that are not allowed in a docker-compose+// project name. Compiled once at package load instead of on every call.+var invalidDockerComposeProjectNameChars = regexp.MustCompile(`[^a-zA-Z0-9 ]+`)+ // generateValidDockerComposeProjectName generates a valid project name for docker-compose. // Note: docker-compose command doesn't like lower case or special characters, other than -. func generateValidDockerComposeProjectName(str string) string { lowerStr := strings.ToLower(str) - return regexp.MustCompile(`[^a-zA-Z0-9 ]+`).ReplaceAllString(lowerStr, "-")+ return invalidDockerComposeProjectNameChars.ReplaceAllString(lowerStr, "-") } @@ -83,7 +83,7 @@ jobs: - uses: actions/checkout@v4 - name: Download all build artifacts- uses: actions/download-artifact@v4+ uses: actions/download-artifact@v4.1.3 with: path: cmd/bin/ pattern: binaries-* | File | Group | Added | Removed | Status |
|---|---|---|---|---|
| go.mod | PR #1860 | +9 | −9 | submitted |
| go.sum | PR #1860 | +18 | −18 | submitted |
| modules/random/random.go | PR #1861 | +2 | −9 | submitted |
| .github/workflows/build-and-release.yml | Local | +1 | −1 | not submitted |
| modules/docker/docker_compose.go | Local | +5 | −1 | not submitted |
| modules/logger/parser/parser.go | Local | +2 | −3 | not submitted |
| modules/packer/packer.go | Local | +9 | −4 | not submitted |
| Total | 7 files | +46 | −45 |