src/goToolsInformation: use the commit hash to pin dlv-dap version

We cannot rely on pseudo-version reliably yet.

Fixes golang/vscode-go#1682

Change-Id: Ibf36627d9f5755f513876751bbab6ce62d135e56
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/341529
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
diff --git a/src/goDebugFactory.ts b/src/goDebugFactory.ts
index d822039..241cee6 100644
--- a/src/goDebugFactory.ts
+++ b/src/goDebugFactory.ts
@@ -8,7 +8,6 @@
 import stream = require('stream');
 import vscode = require('vscode');
 import { OutputEvent, TerminatedEvent } from 'vscode-debugadapter';
-import { killProcessTree } from './utils/processUtils';
 import getPort = require('get-port');
 import path = require('path');
 import * as fs from 'fs';
diff --git a/src/goToolsInformation.ts b/src/goToolsInformation.ts
index 04afe9f..1785726 100644
--- a/src/goToolsInformation.ts
+++ b/src/goToolsInformation.ts
@@ -223,7 +223,7 @@
 		replacedByGopls: false,
 		isImportant: true,
 		description: 'Go debugger & debug adapter (Delve DAP)',
-		defaultVersion: 'v1.7.1-0.20210804080032-f95340ae1bf9', // pinned version
+		defaultVersion: 'f95340ae1bf9', // pinned version
 		minimumGoVersion: semver.coerce('1.12'), // dlv requires 1.12+ for build
 		latestVersion: semver.parse('v1.7.1-0.20210804080032-f95340ae1bf9'),
 		latestVersionTimestamp: moment('2021-08-04', 'YYYY-MM-DD')
diff --git a/test/integration/install.test.ts b/test/integration/install.test.ts
index 00eaef3..ed8f0f6 100644
--- a/test/integration/install.test.ts
+++ b/test/integration/install.test.ts
@@ -213,14 +213,12 @@
 		fs.writeFileSync(path.join(dir, 'list'), `${versions.join('\n')}\n`);
 
 		versions.map((version) => {
-			if (version === 'master') {
-				// for dlv-dap that retrieves the version from master
+			if (!version.match(/^v\d+\.\d+\.\d+/)) {
+				// for dlv-dap that retrieves the version from a revision (commit hash)
 				const resolvedVersion = tool.latestVersion?.toString() || '1.0.0';
+				const infoPath = path.join(dir, `${version}.info`);
 				version = `v${resolvedVersion}`;
-				fs.writeFileSync(
-					path.join(dir, 'master.info'),
-					`{ "Version": "${version}", "Time": "2020-04-07T14:45:07Z" } `
-				);
+				fs.writeFileSync(infoPath, `{ "Version": "${version}", "Time": "2020-04-07T14:45:07Z" } `);
 			}
 
 			// Write the go.mod file.
diff --git a/tools/generate.go b/tools/generate.go
index a3f64f7..e3169d1 100644
--- a/tools/generate.go
+++ b/tools/generate.go
@@ -15,6 +15,7 @@
 import (
 	"bytes"
 	"encoding/json"
+	"errors"
 	"flag"
 	"fmt"
 	"io"
@@ -23,6 +24,7 @@
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"sort"
 	"strings"
 
@@ -235,6 +237,12 @@
 	if err != nil {
 		log.Fatal(err)
 	}
+	// Due to https://github.com/golang/vscode-go/issues/1682, we cannot use
+	// pseudo-version as the pinned version reliably.
+	dlvRevOrStable := dlvVersion.Version
+	if rev, err := pseudoVersionRev(dlvVersion.Version); err == nil { // pseudo-version
+		dlvRevOrStable = rev
+	}
 
 	// Check for the latest gopls version.
 	versions, err := listAllModuleVersions("golang.org/x/tools/gopls")
@@ -269,7 +277,7 @@
 	}
 
 	// TODO(suzmue): change input to json and avoid magic string printing.
-	toolsString := fmt.Sprintf(string(data), goplsVersion.Version, goplsVersion.Time[:len("YYYY-MM-DD")], goplsVersionPre.Version, goplsVersionPre.Time[:len("YYYY-MM-DD")], dlvVersion.Version, dlvVersion.Version, dlvVersion.Time[:len("YYYY-MM-DD")])
+	toolsString := fmt.Sprintf(string(data), goplsVersion.Version, goplsVersion.Time[:len("YYYY-MM-DD")], goplsVersionPre.Version, goplsVersionPre.Time[:len("YYYY-MM-DD")], dlvRevOrStable, dlvVersion.Version, dlvVersion.Time[:len("YYYY-MM-DD")])
 
 	// Write tools section.
 	b.WriteString(toolsString)
@@ -685,3 +693,19 @@
 	}
 	return b.String()
 }
+
+// pseudoVersionRev extracts the revision info if the given version is pseudo version.
+// We wanted to use golang.org/x/mod/module.PseudoVersionRev, but couldn't due to
+// an error in the CI. This is a workaround.
+//
+// It assumes the version string came from the proxy, so a valid, canonical version
+// string. Thus, the check for pseudoversion is not as robust as golang.org/x/mod/module
+// offers.
+func pseudoVersionRev(ver string) (rev string, _ error) {
+	var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`)
+	if strings.Count(ver, "-") < 2 || !pseudoVersionRE.MatchString(ver) {
+		return "", errors.New("not a pseudo version")
+	}
+	j := strings.LastIndex(ver, "-")
+	return ver[j+1:], nil
+}