From a92d61863c423ce331b8997b229f44d1ef8fc7ff Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 27 Aug 2024 11:56:09 -0400 Subject: [PATCH 001/440] Added nightly CVE scanner for the release version. --- .github/workflows/scan.yml | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/scan.yml diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml new file mode 100644 index 0000000000..569fc6cb69 --- /dev/null +++ b/.github/workflows/scan.yml @@ -0,0 +1,44 @@ +name: Scan + +on: + schedule: + - cron: 0 0 * * * + +jobs: + fetch-binaries: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install State Tool + uses: ActiveState/setup-state-tool@v1 + + - name: Copy State Tool binaries to workspace dir + shell: bash + run: | + exe=`which state` + dir=`dirname $exe` + cp -r $dir/* '${{ github.workspace }}' + + - name: Upload binaries + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.os }}-binaries + path: . + + scan: + needs: fetch-binaries + runs-on: ubuntu-latest + steps: + - name: Download binaries + uses: actions/download-artifact@v4 + + - name: Scan binaries + uses: aquasecurity/trivy-action@0.20.0 + with: + scan-type: rootfs + scan-ref: '.' + ignore-unfixed: true + format: table + exit-code: 1 From eda161c51bcbe1899c5d41f4964024ed9238d31d Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 27 Aug 2024 11:56:44 -0400 Subject: [PATCH 002/440] Temporarily enable CVE scanning per push for testing. --- .github/workflows/scan.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 569fc6cb69..56adc5168c 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -3,6 +3,7 @@ name: Scan on: schedule: - cron: 0 0 * * * + push: jobs: fetch-binaries: From 78790f9ce051f6959b016a6f016d46bc6e8ac780 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 27 Aug 2024 15:24:48 -0400 Subject: [PATCH 003/440] Revert "Temporarily enable CVE scanning per push for testing." This reverts commit eda161c51bcbe1899c5d41f4964024ed9238d31d. --- .github/workflows/scan.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 56adc5168c..569fc6cb69 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -3,7 +3,6 @@ name: Scan on: schedule: - cron: 0 0 * * * - push: jobs: fetch-binaries: From a541ffaae3044aa1adfffff981ea3a7248b03c81 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 27 Aug 2024 14:24:55 -0700 Subject: [PATCH 004/440] Update shell detection --- internal/subshell/subshell.go | 37 +++++++++++++++------------ internal/subshell/subshell_lin_mac.go | 5 ++++ internal/subshell/subshell_win.go | 5 ++++ ssTest/main.go | 30 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 ssTest/main.go diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index ac590f43b4..7992f1cde8 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -182,20 +182,17 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } }() - binary = os.Getenv("SHELL") - if binary == "" && runtime.GOOS == "windows" { - binary = detectShellWindows() + binary = configured + if binary == "" { + binary = detectShellParent() } if binary == "" { - binary = configured + binary = os.Getenv(SHELL_ENV_VAR) } + if binary == "" { - if runtime.GOOS == "windows" { - binary = "cmd.exe" - } else { - binary = "bash" - } + binary = OS_DEFULAT } path := resolveBinaryPath(binary) @@ -239,24 +236,30 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { return name, path } -func detectShellWindows() string { - // Windows does not provide a way of identifying which shell we are running in, so we have to look at the parent - // process. - +func detectShellParent() string { p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { - panic(err) + logging.Error("Failed to get parent process: %v", err) } - for p != nil { + for p != nil && p.Pid != 0 { name, err := p.Name() if err == nil { - if strings.Contains(name, "cmd.exe") || strings.Contains(name, "powershell.exe") { + if supportedShellName(name) { return name } } p, _ = p.Parent() } - return os.Getenv("ComSpec") + return "" +} + +func supportedShellName(name string) bool { + for _, subshell := range supportedShells { + if name == subshell.Shell() { + return true + } + } + return false } diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index bcf296d137..02e6178e6b 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -18,3 +18,8 @@ var supportedShells = []SubShell{ &fish.SubShell{}, &cmd.SubShell{}, } + +const ( + SHELL_ENV_VAR = "SHELL" + OS_DEFULAT = "bash" +) diff --git a/internal/subshell/subshell_win.go b/internal/subshell/subshell_win.go index 12df8f51e2..992ed7f5ce 100644 --- a/internal/subshell/subshell_win.go +++ b/internal/subshell/subshell_win.go @@ -8,3 +8,8 @@ import "github.com/ActiveState/cli/internal/subshell/cmd" var supportedShells = []SubShell{ &cmd.SubShell{}, } + +const ( + SHELL_ENV_VAR = "COMSPEC" + OS_DEFULAT = "cmd.exe" +) diff --git a/ssTest/main.go b/ssTest/main.go new file mode 100644 index 0000000000..ba3d364242 --- /dev/null +++ b/ssTest/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + + "github.com/ActiveState/cli/internal/config" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/subshell" +) + +func main() { + if err := run(); err != nil { + panic(err) + } +} + +func run() error { + fmt.Println("Setting up configuration") + cfg, err := config.New() + if err != nil { + return errs.Wrap(err, "Could not create configuration") + } + + fmt.Println("Detecting shell") + shell, path := subshell.DetectShell(cfg) + fmt.Println("Found shell:", shell) + fmt.Println("Path:", path) + + return nil +} From aa415e31b133a51f7cc61ddbac31932a27158d94 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 27 Aug 2024 17:28:53 -0400 Subject: [PATCH 005/440] Merge "PATH" with Window's "Path". This enables searching for exes in not just runtime PATH, but also Windows Path. --- pkg/runtime/internal/envdef/environment.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/runtime/internal/envdef/environment.go b/pkg/runtime/internal/envdef/environment.go index 0d379dcef5..ef51215c10 100644 --- a/pkg/runtime/internal/envdef/environment.go +++ b/pkg/runtime/internal/envdef/environment.go @@ -6,6 +6,7 @@ import ( "maps" "os" "path/filepath" + "runtime" "strings" "github.com/ActiveState/cli/internal/errs" @@ -340,7 +341,11 @@ func (ed *EnvironmentDefinition) GetEnvBasedOn(envLookup map[string]string) (map for _, ev := range ed.Env { pev := &ev if pev.Inherit { - osValue, hasOsValue := envLookup[pev.Name] + name := pev.Name + if runtime.GOOS == "windows" && name == "PATH" { + name = "Path" // env vars are case-insensitive on Windows, and this is what it uses + } + osValue, hasOsValue := envLookup[name] if hasOsValue { osEv := ev osEv.Values = []string{osValue} From 1807ceeec55cbf5153ef2a9e90cd0f6570562ec6 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 27 Aug 2024 14:41:00 -0700 Subject: [PATCH 006/440] Fix Windows detection --- internal/subshell/subshell.go | 9 --------- internal/subshell/subshell_lin_mac.go | 9 +++++++++ internal/subshell/subshell_win.go | 17 ++++++++++++++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 7992f1cde8..27cbeb2a94 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -254,12 +254,3 @@ func detectShellParent() string { return "" } - -func supportedShellName(name string) bool { - for _, subshell := range supportedShells { - if name == subshell.Shell() { - return true - } - } - return false -} diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index 02e6178e6b..b8bc036e12 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -23,3 +23,12 @@ const ( SHELL_ENV_VAR = "SHELL" OS_DEFULAT = "bash" ) + +func supportedShellName(name string) bool { + for _, subshell := range supportedShells { + if name == subshell.Shell() { + return true + } + } + return false +} \ No newline at end of file diff --git a/internal/subshell/subshell_win.go b/internal/subshell/subshell_win.go index 992ed7f5ce..7e976841b7 100644 --- a/internal/subshell/subshell_win.go +++ b/internal/subshell/subshell_win.go @@ -3,13 +3,28 @@ package subshell -import "github.com/ActiveState/cli/internal/subshell/cmd" +import ( + "fmt" + + "github.com/ActiveState/cli/internal/subshell/cmd" + "github.com/ActiveState/cli/internal/subshell/pwsh" +) var supportedShells = []SubShell{ &cmd.SubShell{}, + &pwsh.SubShell{}, } const ( SHELL_ENV_VAR = "COMSPEC" OS_DEFULAT = "cmd.exe" ) + +func supportedShellName(name string) bool { + for _, subshell := range supportedShells { + if name == fmt.Sprintf("%s.exe", subshell.Shell()) { + return true + } + } + return false +} From 958ba7fc65d22e90ca7182e685a4dcec1abef2d4 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 27 Aug 2024 14:55:23 -0700 Subject: [PATCH 007/440] Remove test code --- ssTest/main.go | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 ssTest/main.go diff --git a/ssTest/main.go b/ssTest/main.go deleted file mode 100644 index ba3d364242..0000000000 --- a/ssTest/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/ActiveState/cli/internal/config" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/subshell" -) - -func main() { - if err := run(); err != nil { - panic(err) - } -} - -func run() error { - fmt.Println("Setting up configuration") - cfg, err := config.New() - if err != nil { - return errs.Wrap(err, "Could not create configuration") - } - - fmt.Println("Detecting shell") - shell, path := subshell.DetectShell(cfg) - fmt.Println("Found shell:", shell) - fmt.Println("Path:", path) - - return nil -} From 2a9fbc2571da4a429894d99c11bade6b720d72c1 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 27 Aug 2024 15:16:30 -0700 Subject: [PATCH 008/440] Add newline --- internal/subshell/subshell_lin_mac.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index b8bc036e12..fe66d31c41 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -31,4 +31,4 @@ func supportedShellName(name string) bool { } } return false -} \ No newline at end of file +} From 14e274f868774d76842caa90232327bd94e1badd Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 27 Aug 2024 15:49:13 -0700 Subject: [PATCH 009/440] Debug test failures --- internal/subshell/subshell.go | 1 + internal/subshell/subshell_lin_mac.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 27cbeb2a94..f2815b3df5 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -245,6 +245,7 @@ func detectShellParent() string { for p != nil && p.Pid != 0 { name, err := p.Name() if err == nil { + logging.Debug("Searching for supported shell in parent process: %s", name) if supportedShellName(name) { return name } diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index fe66d31c41..b271543206 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -4,6 +4,7 @@ package subshell import ( + "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/cmd" "github.com/ActiveState/cli/internal/subshell/fish" @@ -26,6 +27,7 @@ const ( func supportedShellName(name string) bool { for _, subshell := range supportedShells { + logging.Debug("Shell name: %s", subshell.Shell()) if name == subshell.Shell() { return true } From 27932e13978acdb2f0766f6ba056e55e836f6257 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 27 Aug 2024 16:00:41 -0700 Subject: [PATCH 010/440] Debug --- internal/subshell/subshell.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index f2815b3df5..848a3d924d 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -183,6 +183,7 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { }() binary = configured + logging.Debug("Configured shell: %s", binary) if binary == "" { binary = detectShellParent() } @@ -237,6 +238,7 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } func detectShellParent() string { + logging.Debug("Detecting shell from parent process") p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { logging.Error("Failed to get parent process: %v", err) From e4f6e33f518bf51f68b28c226a58f8b78191010f Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 27 Aug 2024 16:16:30 -0700 Subject: [PATCH 011/440] Change order in shell detection --- internal/subshell/subshell.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 848a3d924d..b95e8713e8 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -182,10 +182,10 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } }() - binary = configured + binary = detectShellParent() logging.Debug("Configured shell: %s", binary) if binary == "" { - binary = detectShellParent() + binary = configured } if binary == "" { From 3f56d391388b64c0a4cf449c6c8f378e037a0737 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 28 Aug 2024 09:15:09 -0700 Subject: [PATCH 012/440] Add bash support on Windows --- internal/subshell/subshell_win.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/subshell/subshell_win.go b/internal/subshell/subshell_win.go index 7e976841b7..6ad787901f 100644 --- a/internal/subshell/subshell_win.go +++ b/internal/subshell/subshell_win.go @@ -6,6 +6,7 @@ package subshell import ( "fmt" + "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/cmd" "github.com/ActiveState/cli/internal/subshell/pwsh" ) @@ -13,6 +14,7 @@ import ( var supportedShells = []SubShell{ &cmd.SubShell{}, &pwsh.SubShell{}, + &bash.SubShell{}, } const ( From 84ba928c98eb7f0f4bede47c4a23b33e2bdb1aa7 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 28 Aug 2024 09:58:03 -0700 Subject: [PATCH 013/440] Remove debug logs --- internal/subshell/subshell.go | 2 -- internal/subshell/subshell_lin_mac.go | 2 -- 2 files changed, 4 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index b95e8713e8..6c491bce9e 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -238,7 +238,6 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } func detectShellParent() string { - logging.Debug("Detecting shell from parent process") p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { logging.Error("Failed to get parent process: %v", err) @@ -247,7 +246,6 @@ func detectShellParent() string { for p != nil && p.Pid != 0 { name, err := p.Name() if err == nil { - logging.Debug("Searching for supported shell in parent process: %s", name) if supportedShellName(name) { return name } diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index b271543206..fe66d31c41 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -4,7 +4,6 @@ package subshell import ( - "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/cmd" "github.com/ActiveState/cli/internal/subshell/fish" @@ -27,7 +26,6 @@ const ( func supportedShellName(name string) bool { for _, subshell := range supportedShells { - logging.Debug("Shell name: %s", subshell.Shell()) if name == subshell.Shell() { return true } From e1443fb27537c940f220a83c52617150c7702493 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:10:45 -0700 Subject: [PATCH 014/440] Update changeset structure to allow iterating through all changes in one loop --- internal/runbits/cves/cves.go | 10 ++-- .../runbits/dependencies/changesummary.go | 14 +++-- pkg/buildplan/artifact.go | 55 +++++++++++++++---- pkg/buildplan/buildplan.go | 27 ++++----- 4 files changed, 72 insertions(+), 34 deletions(-) diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index 4d7bb0d787..d6970c5514 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -47,8 +47,8 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } var ingredients []*request.Ingredient - for _, artifact := range changeset.Added { - for _, ing := range artifact.Ingredients { + for _, change := range changeset.Filter(buildplan.ArtifactAdded) { + for _, ing := range change.Artifact.Ingredients { ingredients = append(ingredients, &request.Ingredient{ Namespace: ing.Namespace, Name: ing.Name, @@ -57,12 +57,12 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } } - for _, change := range changeset.Updated { + for _, change := range changeset.Filter(buildplan.ArtifactUpdated) { if !change.VersionsChanged() { continue // For CVE reporting we only care about ingredient changes } - for _, ing := range change.To.Ingredients { + for _, ing := range change.Artifact.Ingredients { ingredients = append(ingredients, &request.Ingredient{ Namespace: ing.Namespace, Name: ing.Name, @@ -118,7 +118,7 @@ func (c *CveReport) shouldSkipReporting(changeset buildplan.ArtifactChangeset) b return true } - return len(changeset.Added) == 0 && len(changeset.Updated) == 0 + return len(changeset.Filter(buildplan.ArtifactAdded, buildplan.ArtifactUpdated)) == 0 } func (c *CveReport) shouldPromptForSecurity(vulnerabilities model.VulnerableIngredientsByLevels) bool { diff --git a/internal/runbits/dependencies/changesummary.go b/internal/runbits/dependencies/changesummary.go index 4d4c74c755..1e6a3f8630 100644 --- a/internal/runbits/dependencies/changesummary.go +++ b/internal/runbits/dependencies/changesummary.go @@ -28,7 +28,8 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, dependencies := buildplan.Ingredients{} directDependencies := buildplan.Ingredients{} changeset := newBuildPlan.DiffArtifacts(oldBuildPlan, false) - for _, a := range changeset.Added { + for _, change := range changeset.Filter(buildplan.ArtifactAdded) { + a := change.Artifact if _, exists := requested[a.ArtifactID]; !exists { continue } @@ -43,16 +44,17 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, } // Check for any direct dependencies added by a requested package update. - for _, u := range changeset.Updated { - if _, exists := requested[u.To.ArtifactID]; !exists { + for _, change := range changeset.Filter(buildplan.ArtifactUpdated) { + if _, exists := requested[change.Artifact.ArtifactID]; !exists { continue } - for _, dep := range u.To.RuntimeDependencies(false) { - for _, a := range changeset.Added { + for _, dep := range change.Artifact.RuntimeDependencies(false) { + for _, subchange := range changeset.Filter(buildplan.ArtifactAdded) { + a := subchange.Artifact if a.ArtifactID != dep.ArtifactID { continue } - v := fmt.Sprintf("%s@%s", u.To.Name(), u.To.Version()) // updated/requested package, not added package + v := fmt.Sprintf("%s@%s", change.Artifact.Name(), change.Artifact.Version()) // updated/requested package, not added package addedString = append(addedLocale, v) addedLocale = append(addedLocale, fmt.Sprintf("[ACTIONABLE]%s[/RESET]", v)) diff --git a/pkg/buildplan/artifact.go b/pkg/buildplan/artifact.go index 212f9da773..01123ddebd 100644 --- a/pkg/buildplan/artifact.go +++ b/pkg/buildplan/artifact.go @@ -133,25 +133,60 @@ func (a Artifacts) ToNameMap() ArtifactNameMap { return result } -type ArtifactChangeset struct { - Added []*Artifact - Removed []*Artifact - Updated []ArtifactUpdate +type ChangeType int + +const ( + ArtifactAdded ChangeType = iota + ArtifactRemoved + ArtifactUpdated +) + +func (c ChangeType) String() string { + switch c { + case ArtifactAdded: + return "added" + case ArtifactRemoved: + return "removed" + case ArtifactUpdated: + return "updated" + } + + return "unknown" +} + +type ArtifactChange struct { + ChangeType ChangeType + Artifact *Artifact + Old *Artifact // Old is only set when ChangeType=ArtifactUpdated } -type ArtifactUpdate struct { - From *Artifact - To *Artifact +type ArtifactChangeset []ArtifactChange + +func (a ArtifactChangeset) Filter(t ...ChangeType) ArtifactChangeset { + lookup := make(map[ChangeType]struct{}, len(t)) + for _, t := range t { + lookup[t] = struct{}{} + } + result := ArtifactChangeset{} + for _, ac := range a { + if _, ok := lookup[ac.ChangeType]; ok { + result = append(result, ac) + } + } + return result } -func (a ArtifactUpdate) VersionsChanged() bool { +func (a ArtifactChange) VersionsChanged() bool { + if a.Old == nil { + return false + } fromVersions := []string{} - for _, ing := range a.From.Ingredients { + for _, ing := range a.Old.Ingredients { fromVersions = append(fromVersions, ing.Version) } sort.Strings(fromVersions) toVersions := []string{} - for _, ing := range a.To.Ingredients { + for _, ing := range a.Artifact.Ingredients { toVersions = append(toVersions, ing.Version) } sort.Strings(toVersions) diff --git a/pkg/buildplan/buildplan.go b/pkg/buildplan/buildplan.go index e985b47b1a..70480f9443 100644 --- a/pkg/buildplan/buildplan.go +++ b/pkg/buildplan/buildplan.go @@ -99,38 +99,39 @@ func (b *BuildPlan) DiffArtifacts(oldBp *BuildPlan, requestedOnly bool) Artifact } } - var updated []ArtifactUpdate - var added []*Artifact + changeset := ArtifactChangeset{} for name, artf := range new { if artfOld, notNew := old[name]; notNew { // The artifact name exists in both the old and new recipe, maybe it was updated though if artfOld.ArtifactID == artf.ArtifactID { continue } - updated = append(updated, ArtifactUpdate{ - From: artfOld, - To: artf, + changeset = append(changeset, ArtifactChange{ + ChangeType: ArtifactUpdated, + Artifact: artf, + Old: artfOld, }) } else { // If it's not an update it is a new artifact - added = append(added, artf) + changeset = append(changeset, ArtifactChange{ + ChangeType: ArtifactAdded, + Artifact: artf, + }) } } - var removed []*Artifact for name, artf := range old { if _, noDiff := new[name]; noDiff { continue } - removed = append(removed, artf) + changeset = append(changeset, ArtifactChange{ + ChangeType: ArtifactRemoved, + Artifact: artf, + }) } - return ArtifactChangeset{ - Added: added, - Removed: removed, - Updated: updated, - } + return changeset } func (b *BuildPlan) Engine() types.BuildEngine { From 18373263511eb9e48e381b45e5a7d7f4a716e99a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:11:00 -0700 Subject: [PATCH 015/440] Expose org namespace prefix --- pkg/platform/model/vcs.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 8fd6626d0a..845b576a95 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -204,10 +204,12 @@ func NewNamespacePlatform() Namespace { return Namespace{NamespacePlatform, "platform"} } +const OrgNamespacePrefix = "private/" + func NewOrgNamespace(orgName string) Namespace { return Namespace{ nsType: NamespaceOrg, - value: fmt.Sprintf("private/%s", orgName), + value: OrgNamespacePrefix + orgName, } } From fa46b663f4b3424051d42f0fb9c142b6837416c5 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:11:12 -0700 Subject: [PATCH 016/440] Added buildscript.Clone() --- pkg/buildscript/buildscript.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 0ee5ca1037..159c15b1ff 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -37,3 +37,16 @@ func (b *BuildScript) Equals(other *BuildScript) (bool, error) { } return string(myBytes) == string(otherBytes), nil } + +func (b *BuildScript) Clone() (*BuildScript, error) { + m, err := b.Marshal() + if err != nil { + return nil, errs.Wrap(err, "unable to marshal this buildscript") + } + + u, err := Unmarshal(m) + if err != nil { + return nil, errs.Wrap(err, "unable to unmarshal buildscript") + } + return u, nil +} From e60d55e88456bed872ed80fef4869cafc91fbcb9 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:15:59 -0700 Subject: [PATCH 017/440] Added ability to ignore certain deps --- .../runbits/dependencies/changesummary.go | 2 +- pkg/buildplan/artifact.go | 37 +++++++++++-------- pkg/buildplan/artifact_test.go | 2 +- pkg/runtime/setup.go | 2 +- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/internal/runbits/dependencies/changesummary.go b/internal/runbits/dependencies/changesummary.go index 1e6a3f8630..a676b50c99 100644 --- a/internal/runbits/dependencies/changesummary.go +++ b/internal/runbits/dependencies/changesummary.go @@ -48,7 +48,7 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, if _, exists := requested[change.Artifact.ArtifactID]; !exists { continue } - for _, dep := range change.Artifact.RuntimeDependencies(false) { + for _, dep := range change.Artifact.RuntimeDependencies(false, nil) { for _, subchange := range changeset.Filter(buildplan.ArtifactAdded) { a := subchange.Artifact if a.ArtifactID != dep.ArtifactID { diff --git a/pkg/buildplan/artifact.go b/pkg/buildplan/artifact.go index 01123ddebd..fc4a0a6621 100644 --- a/pkg/buildplan/artifact.go +++ b/pkg/buildplan/artifact.go @@ -194,43 +194,46 @@ func (a ArtifactChange) VersionsChanged() bool { return !reflect.DeepEqual(fromVersions, toVersions) } -func (as Artifacts) RuntimeDependencies(recursive bool) Artifacts { - seen := make(map[strfmt.UUID]struct{}) +func (as Artifacts) RuntimeDependencies(recursive bool, ignore *map[strfmt.UUID]struct{}) Artifacts { dependencies := Artifacts{} for _, a := range as { - dependencies = append(dependencies, a.dependencies(recursive, seen, RuntimeRelation)...) + dependencies = append(dependencies, a.dependencies(recursive, ignore, RuntimeRelation)...) } return dependencies } -func (a *Artifact) RuntimeDependencies(recursive bool) Artifacts { - return a.dependencies(recursive, make(map[strfmt.UUID]struct{}), RuntimeRelation) +func (a *Artifact) RuntimeDependencies(recursive bool, ignore *map[strfmt.UUID]struct{}) Artifacts { + return a.dependencies(recursive, ignore, RuntimeRelation) } // Dependencies returns ALL dependencies that an artifact has, this covers runtime and build time dependencies. // It does not cover test dependencies as we have no use for them in the state tool. -func (as Artifacts) Dependencies(recursive bool) Artifacts { - seen := make(map[strfmt.UUID]struct{}) +func (as Artifacts) Dependencies(recursive bool, ignore *map[strfmt.UUID]struct{}) Artifacts { dependencies := Artifacts{} for _, a := range as { - dependencies = append(dependencies, a.dependencies(recursive, seen, RuntimeRelation, BuildtimeRelation)...) + dependencies = append(dependencies, a.dependencies(recursive, ignore, RuntimeRelation, BuildtimeRelation)...) } return dependencies } // Dependencies returns ALL dependencies that an artifact has, this covers runtime and build time dependencies. // It does not cover test dependencies as we have no use for them in the state tool. -func (a *Artifact) Dependencies(recursive bool) Artifacts { - return a.dependencies(recursive, make(map[strfmt.UUID]struct{}), RuntimeRelation, BuildtimeRelation) +func (a *Artifact) Dependencies(recursive bool, ignore *map[strfmt.UUID]struct{}) Artifacts { + return a.dependencies(recursive, ignore, RuntimeRelation, BuildtimeRelation) } -func (a *Artifact) dependencies(recursive bool, seen map[strfmt.UUID]struct{}, relations ...Relation) Artifacts { +func (a *Artifact) dependencies(recursive bool, maybeIgnore *map[strfmt.UUID]struct{}, relations ...Relation) Artifacts { + ignore := map[strfmt.UUID]struct{}{} + if maybeIgnore != nil { + ignore = *maybeIgnore + } + // Guard against recursion, this shouldn't really be possible but we don't know how the buildplan might evolve // so better safe than sorry. - if _, ok := seen[a.ArtifactID]; ok { + if _, ok := ignore[a.ArtifactID]; ok { return Artifacts{} } - seen[a.ArtifactID] = struct{}{} + ignore[a.ArtifactID] = struct{}{} dependencies := Artifacts{} for _, ac := range a.children { @@ -244,9 +247,11 @@ func (a *Artifact) dependencies(recursive bool, seen map[strfmt.UUID]struct{}, r continue } - dependencies = append(dependencies, ac.Artifact) - if recursive { - dependencies = append(dependencies, ac.Artifact.dependencies(recursive, seen, relations...)...) + if _, ok := ignore[ac.Artifact.ArtifactID]; !ok { + dependencies = append(dependencies, ac.Artifact) + if recursive { + dependencies = append(dependencies, ac.Artifact.dependencies(recursive, &ignore, relations...)...) + } } } return dependencies diff --git a/pkg/buildplan/artifact_test.go b/pkg/buildplan/artifact_test.go index a6493ee8da..fa26fff3e7 100644 --- a/pkg/buildplan/artifact_test.go +++ b/pkg/buildplan/artifact_test.go @@ -61,7 +61,7 @@ func TestArtifact_Dependencies(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := tt.artifact - deps := a.Dependencies(tt.recursive) + deps := a.Dependencies(tt.recursive, nil) got := make([]string, len(deps)) for i, dep := range deps { got[i] = dep.ArtifactID.String() diff --git a/pkg/runtime/setup.go b/pkg/runtime/setup.go index 33dbdca0a6..9ff5022e3c 100644 --- a/pkg/runtime/setup.go +++ b/pkg/runtime/setup.go @@ -143,7 +143,7 @@ func newSetup(path string, bp *buildplan.BuildPlan, env *envdef.Collection, depo // Now that we know which artifacts we'll need to download we can use this as our basis for calculating which artifacts // still need to be build. This encompasses the artifacts themselves, as well as any of their dependencies. And of // course we only want to filter artifacts that actually require a build, as the build may be cached server side. - artifactsToBuild := append(artifactsToDownload, artifactsToDownload.Dependencies(true)...).Filter(buildplan.FilterNotBuild()) + artifactsToBuild := append(artifactsToDownload, artifactsToDownload.Dependencies(true, nil)...).Filter(buildplan.FilterNotBuild()) artifactsToBuild = sliceutils.UniqueByProperty(artifactsToBuild, func(a *buildplan.Artifact) any { return a.ArtifactID }) // Check for cached build failures From d71eedc703a782a101b67d69ad779c2bac484a90 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:16:24 -0700 Subject: [PATCH 018/440] Added shortcuts for accessing revision and license info from artifact --- pkg/buildplan/artifact.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/buildplan/artifact.go b/pkg/buildplan/artifact.go index fc4a0a6621..b793c66f03 100644 --- a/pkg/buildplan/artifact.go +++ b/pkg/buildplan/artifact.go @@ -63,6 +63,26 @@ func (a *Artifact) Version() string { return "" } +// Revision returns the name of the ingredient for this artifact, if it only has exactly one ingredient associated. +// Otherwise it returns an empty version. +func (a *Artifact) Revision() int { + if len(a.Ingredients) == 1 { + return a.Ingredients[0].Revision + } + return -1 +} + +func (a *Artifact) Licenses() []string { + result := []string{} + if len(a.Ingredients) == 0 { + return result + } + for _, ing := range a.Ingredients { + result = append(result, ing.Licenses...) + } + return result +} + func (a *Artifact) NameAndVersion() string { version := a.Version() if version == "" { From fc2c65486b7de5ddbae6d8669ae94e04739003c4 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:16:47 -0700 Subject: [PATCH 019/440] Added slice comparison function that doesn't depend on ordering --- internal/sliceutils/sliceutils.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/internal/sliceutils/sliceutils.go b/internal/sliceutils/sliceutils.go index 511e5774a6..e31ff293d8 100644 --- a/internal/sliceutils/sliceutils.go +++ b/internal/sliceutils/sliceutils.go @@ -1,6 +1,8 @@ package sliceutils import ( + "cmp" + "github.com/ActiveState/cli/internal/errs" "github.com/go-openapi/strfmt" "golang.org/x/text/unicode/norm" @@ -112,3 +114,23 @@ func ToLookupMapByKey[T any, K string | int | strfmt.UUID](data []T, keyCb func( } return result } + +// EqualValues checks if two slices have equal values, regardless of ordering. This does not recurse into nested slices or structs. +func EqualValues[S ~[]E, E cmp.Ordered](a, b S) bool { + if len(a) != len(b) { + return false + } + + lookup := make(map[E]struct{}, len(a)) + for _, e := range a { + lookup[e] = struct{}{} + } + + for _, e := range b { + if _, ok := lookup[e]; !ok { + return false + } + } + + return true +} From 6d38a4d1f34b580c45ed9fa09223e73ab6138aa9 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:18:17 -0700 Subject: [PATCH 020/440] Added BOLD colorize tag --- internal/colorize/colorize.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/colorize/colorize.go b/internal/colorize/colorize.go index 024d39ffca..9fdd9b06c5 100644 --- a/internal/colorize/colorize.go +++ b/internal/colorize/colorize.go @@ -18,7 +18,7 @@ func init() { defer profile.Measure("colorize:init", time.Now()) var err error colorRx, err = regexp.Compile( - `\[(HEADING|NOTICE|SUCCESS|ERROR|WARNING|DISABLED|ACTIONABLE|CYAN|GREEN|RED|ORANGE|YELLOW|MAGENTA|/RESET)!?\]`, + `\[(HEADING|BOLD|NOTICE|SUCCESS|ERROR|WARNING|DISABLED|ACTIONABLE|CYAN|GREEN|RED|ORANGE|YELLOW|MAGENTA|/RESET)!?\]`, ) if err != nil { panic(fmt.Sprintf("Could not compile regex: %v", err)) @@ -27,6 +27,7 @@ func init() { type ColorTheme interface { Heading(writer io.Writer) + Bold(writer io.Writer) Notice(writer io.Writer) Success(writer io.Writer) Error(writer io.Writer) @@ -51,6 +52,11 @@ func (dct defaultColorTheme) Heading(writer io.Writer) { c.SetStyle(colorstyle.Bold, false) } +func (dct defaultColorTheme) Bold(writer io.Writer) { + c := colorstyle.New(writer) + c.SetStyle(colorstyle.Bold, false) +} + // Notice switches to bright foreground func (dct defaultColorTheme) Notice(writer io.Writer) { colorstyle.New(writer).SetStyle(colorstyle.Default, true) From 1514bde1f4840f2a6c596c4705eb8cfa74e704ad Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:18:50 -0700 Subject: [PATCH 021/440] Added output.Structured() --- internal/output/presets.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/internal/output/presets.go b/internal/output/presets.go index 3fa98e7693..6b26c2bc46 100644 --- a/internal/output/presets.go +++ b/internal/output/presets.go @@ -32,19 +32,32 @@ func (h Emphasize) MarshalStructured(f Format) interface{} { return Suppress } -type preparedOutput struct { - plain interface{} +type plainOutput struct { + plain interface{} +} + +type structuredOutput struct { structured interface{} } -func (o *preparedOutput) MarshalOutput(_ Format) interface{} { +type preparedOutput struct { + *plainOutput + *structuredOutput +} + +func (o *plainOutput) MarshalOutput(_ Format) interface{} { return o.plain } -func (o *preparedOutput) MarshalStructured(_ Format) interface{} { +func (o *structuredOutput) MarshalStructured(_ Format) interface{} { return o.structured } func Prepare(plain interface{}, structured interface{}) *preparedOutput { - return &preparedOutput{plain, structured} + return &preparedOutput{&plainOutput{plain}, &structuredOutput{structured}} +} + +func Structured(structured interface{}) *structuredOutput { + return &structuredOutput{structured} } + From efe42e92bc88afd348012d52c1fd2f80ed5f3de4 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 28 Aug 2024 11:23:53 -0700 Subject: [PATCH 022/440] Add back debug logging --- internal/subshell/subshell.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 6c491bce9e..b95e8713e8 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -238,6 +238,7 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } func detectShellParent() string { + logging.Debug("Detecting shell from parent process") p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { logging.Error("Failed to get parent process: %v", err) @@ -246,6 +247,7 @@ func detectShellParent() string { for p != nil && p.Pid != 0 { name, err := p.Name() if err == nil { + logging.Debug("Searching for supported shell in parent process: %s", name) if supportedShellName(name) { return name } From a6b056ee961d42b06447fa423aae4419c736ab19 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:24:44 -0700 Subject: [PATCH 023/440] Centralized tree symbols --- internal/output/presets.go | 3 +++ internal/runbits/dependencies/changesummary.go | 4 ++-- internal/runbits/dependencies/summary.go | 4 ++-- internal/runners/artifacts/artifacts.go | 8 ++++---- internal/runners/branch/tree.go | 7 ++++--- internal/runners/cve/cve.go | 4 ++-- internal/runners/manifest/requirements.go | 2 +- internal/runners/projects/projects.go | 6 +++--- internal/runners/show/show.go | 2 +- 9 files changed, 22 insertions(+), 18 deletions(-) diff --git a/internal/output/presets.go b/internal/output/presets.go index 6b26c2bc46..1033d44246 100644 --- a/internal/output/presets.go +++ b/internal/output/presets.go @@ -61,3 +61,6 @@ func Structured(structured interface{}) *structuredOutput { return &structuredOutput{structured} } +const TreeMid = "├─" +const TreeLink = "│" +const TreeEnd = "└─" diff --git a/internal/runbits/dependencies/changesummary.go b/internal/runbits/dependencies/changesummary.go index a676b50c99..1d7e7c3cd9 100644 --- a/internal/runbits/dependencies/changesummary.go +++ b/internal/runbits/dependencies/changesummary.go @@ -106,9 +106,9 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, // depending on whether or not it has subdependencies, and whether or not showUpdatedPackages is // `true`. for i, ingredient := range directDependencies { - prefix := "├─" + prefix := output.TreeMid if i == len(directDependencies)-1 { - prefix = "└─" + prefix = output.TreeEnd } // Retrieve runtime dependencies, and then filter out any dependencies that are common between all added ingredients. diff --git a/internal/runbits/dependencies/summary.go b/internal/runbits/dependencies/summary.go index 751f64dd28..61883d61f2 100644 --- a/internal/runbits/dependencies/summary.go +++ b/internal/runbits/dependencies/summary.go @@ -27,9 +27,9 @@ func OutputSummary(out output.Outputer, directDependencies buildplan.Artifacts) out.Notice(locale.Tl("setting_up_dependencies", " Setting up the following dependencies:")) for i, ingredient := range ingredients { - prefix := " ├─" + prefix := " " + output.TreeMid if i == len(ingredients)-1 { - prefix = " └─" + prefix = " " + output.TreeEnd } subdependencies := "" diff --git a/internal/runners/artifacts/artifacts.go b/internal/runners/artifacts/artifacts.go index 81ed37bbed..06fb06b513 100644 --- a/internal/runners/artifacts/artifacts.go +++ b/internal/runners/artifacts/artifacts.go @@ -203,8 +203,8 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error { switch { case len(artifact.Errors) > 0: b.out.Print(fmt.Sprintf(" • %s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed"))) - b.out.Print(fmt.Sprintf(" ├─ %s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": "))) - b.out.Print(fmt.Sprintf(" └─ %s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL)) + b.out.Print(fmt.Sprintf(" %s %s: [ERROR]%s[/RESET]", output.TreeMid, locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": "))) + b.out.Print(fmt.Sprintf(" %s %s: [ACTIONABLE]%s[/RESET]", output.TreeEnd, locale.T("artifact_status_failed_log"), artifact.LogURL)) continue case artifact.status == types.ArtifactSkipped: b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped"))) @@ -227,8 +227,8 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error { switch { case len(artifact.Errors) > 0: b.out.Print(fmt.Sprintf(" • %s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed"))) - b.out.Print(fmt.Sprintf(" ├─ %s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": "))) - b.out.Print(fmt.Sprintf(" └─ %s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL)) + b.out.Print(fmt.Sprintf(" %s %s: [ERROR]%s[/RESET]", output.TreeMid, locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": "))) + b.out.Print(fmt.Sprintf(" %s %s: [ACTIONABLE]%s[/RESET]", output.TreeEnd, locale.T("artifact_status_failed_log"), artifact.LogURL)) continue case artifact.status == types.ArtifactSkipped: b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped"))) diff --git a/internal/runners/branch/tree.go b/internal/runners/branch/tree.go index 7da0ab0c59..611401bccd 100644 --- a/internal/runners/branch/tree.go +++ b/internal/runners/branch/tree.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" "github.com/ActiveState/cli/pkg/platform/model" ) @@ -17,9 +18,9 @@ type branchNode struct { type tree map[branchNode]tree const ( - prefixLink string = "│" - prefixMid string = "├─" - prefixEnd string = "└─" + prefixLink string = output.TreeLink + prefixMid string = output.TreeMid + prefixEnd string = output.TreeEnd branchFormatting string = "[NOTICE]%s[/RESET]" localBranchFormatting string = "[ACTIONABLE]%s[/RESET] [DISABLED](Current)[/RESET]" diff --git a/internal/runners/cve/cve.go b/internal/runners/cve/cve.go index b9c74d9179..20f6e51116 100644 --- a/internal/runners/cve/cve.go +++ b/internal/runners/cve/cve.go @@ -194,9 +194,9 @@ func (rd *cveOutput) MarshalOutput(format output.Format) interface{} { }) for i, d := range ap.Details { - bar := "├─" + bar := output.TreeMid if i == len(ap.Details)-1 { - bar = "└─" + bar = output.TreeEnd } severity := d.Severity if severity == "CRITICAL" { diff --git a/internal/runners/manifest/requirements.go b/internal/runners/manifest/requirements.go index 3329cb1a72..e93b719bdc 100644 --- a/internal/runners/manifest/requirements.go +++ b/internal/runners/manifest/requirements.go @@ -70,7 +70,7 @@ func (o requirements) Print(out output.Outputer) { } if req.Namespace != "" { - requirementOutput.Namespace = locale.Tl("manifest_namespace", " └─ [DISABLED]namespace:[/RESET] [CYAN]{{.V0}}[/RESET]", req.Namespace) + requirementOutput.Namespace = locale.Tr("namespace_row", output.TreeEnd, req.Namespace) } requirementsOutput = append(requirementsOutput, requirementOutput) diff --git a/internal/runners/projects/projects.go b/internal/runners/projects/projects.go index 5f0c7ac521..91357549c3 100644 --- a/internal/runners/projects/projects.go +++ b/internal/runners/projects/projects.go @@ -62,16 +62,16 @@ func (o *projectsOutput) MarshalOutput(f output.Format) interface{} { } execDir := v.Executables[i] if execDir != "" { - checkouts = append(checkouts, locale.Tl("projects_local_checkout_exec", " ├─ Local Checkout → {{.V0}}", checkout)) + checkouts = append(checkouts, locale.Tl("projects_local_checkout_exec", " {{.V0}} Local Checkout → {{.V1}}", output.TreeMid, checkout)) if f == output.PlainFormatName { // Show executable path below checkout path for plain text output. - checkouts = append(checkouts, locale.Tl("projects_executables", " └─ Executables → {{.V0}}", execDir)) + checkouts = append(checkouts, locale.Tl("projects_executables", " {{.V0}} Executables → {{.V1}}", output.TreeEnd, execDir)) } else { // Show executables in a separate table. executables = append(executables, execDir) } } else { - checkouts = append(checkouts, locale.Tl("projects_local_checkout", " └─ Local Checkout → {{.V0}}", checkout)) + checkouts = append(checkouts, locale.Tl("projects_local_checkout", " {{.V0}} Local Checkout → {{.V1}}", output.TreeEnd, checkout)) } } r = append(r, projectOutputPlain{v.Name, v.Organization, strings.Join(checkouts, "\n"), strings.Join(executables, "\n")}) diff --git a/internal/runners/show/show.go b/internal/runners/show/show.go index 604f30c74a..2c5387d561 100644 --- a/internal/runners/show/show.go +++ b/internal/runners/show/show.go @@ -78,7 +78,7 @@ func formatScripts(scripts map[string]string) string { for k, v := range scripts { res = append(res, fmt.Sprintf("• %s", k)) if v != "" { - res = append(res, fmt.Sprintf(" └─ %s", v)) + res = append(res, fmt.Sprintf(" %s %s", output.TreeEnd, v)) } } return strings.Join(res, "\n") From c755cc52b75317fd096b1fd25b7caf301f25e850 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 11:24:56 -0700 Subject: [PATCH 024/440] Added `state upgrade` --- cmd/state/internal/cmdtree/cmdtree.go | 1 + cmd/state/internal/cmdtree/upgrade.go | 38 +++ internal/locale/locale.go | 9 + internal/locale/locales/en-us.yaml | 30 +++ internal/runners/upgrade/upgrade.go | 343 ++++++++++++++++++++++++++ 5 files changed, 421 insertions(+) create mode 100644 cmd/state/internal/cmdtree/upgrade.go create mode 100644 internal/runners/upgrade/upgrade.go diff --git a/cmd/state/internal/cmdtree/cmdtree.go b/cmd/state/internal/cmdtree/cmdtree.go index cfc4dd6b71..ce3991b304 100644 --- a/cmd/state/internal/cmdtree/cmdtree.go +++ b/cmd/state/internal/cmdtree/cmdtree.go @@ -214,6 +214,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { newEvalCommand(prime), newManifestCommmand(prime), artifactsCmd, + newUpgradeCommand(prime), ) return &CmdTree{ diff --git a/cmd/state/internal/cmdtree/upgrade.go b/cmd/state/internal/cmdtree/upgrade.go new file mode 100644 index 0000000000..05636d2cba --- /dev/null +++ b/cmd/state/internal/cmdtree/upgrade.go @@ -0,0 +1,38 @@ +package cmdtree + +import ( + "github.com/ActiveState/cli/internal/captain" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runners/upgrade" +) + +func newUpgradeCommand(prime *primer.Values) *captain.Command { + runner := upgrade.New(prime) + + params := upgrade.NewParams() + + cmd := captain.NewCommand( + "upgrade", + locale.Tl("upgrade_cmd_title", "Upgrading Project"), + locale.Tl("upgrade_cmd_description", "Upgrade dependencies of a project"), + prime, + []*captain.Flag{ + { + Name: "expand", + Description: locale.T("flag_state_upgrade_expand_description"), + Value: ¶ms.Expand, + }, + }, + []*captain.Argument{}, + func(_ *captain.Command, _ []string) error { + return runner.Run(params) + }, + ) + + cmd.SetGroup(PackagesGroup) + cmd.SetSupportsStructuredOutput() + cmd.SetUnstable(true) + + return cmd +} diff --git a/internal/locale/locale.go b/internal/locale/locale.go index b6f4565741..17db70f89f 100644 --- a/internal/locale/locale.go +++ b/internal/locale/locale.go @@ -138,6 +138,15 @@ func T(translationID string, args ...interface{}) string { return translateFunction(translationID, args...) } +// Ts aliases to T, but accepts a list of translationIDs to be translated +func Ts(translationIDs ...string) []string { + result := []string{} + for _, id := range translationIDs { + result = append(result, T(id)) + } + return result +} + // Tr is like T but it accepts string params that will be used as numbered params, eg. V0, V1, V2 etc func Tr(translationID string, values ...string) string { return T(translationID, parseInput(values...)) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index ac0f5deeab..2c9ec195bc 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1549,3 +1549,33 @@ warn_additional_requirements: [WARNING]WARNING:[/RESET] This project has additional build criteria that cannot be managed through the State Tool. Please edit your [ACTIONABLE]buildscript.as[/RESET] file manually to modify these: migrate_project_error: other: Could not migrate your project files. Please address any errors and try again. For full detail view your log file with '[ACTIONABLE]state export log -i 0[/RESET]'. +upgrade_solving: + other: • Searching for available upgrades for your requirements +upgrade_confirm: + other: Would you like to install these upgrades? +upgrade_no_changes: + other: No upgrades were found for your project. Note that requirements with fixed version numbers will never be upgraded. +upgrade_aborted: + other: Upgrade aborted +upgrade_success: + other: Upgrade completed +upgrade_field_same: + other: "[CYAN]{{.V0}}[/RESET]" +upgrade_field_change: + other: "[CYAN]{{.V0}}[/RESET] > [BOLD][ACTIONABLE]{{.V1}}[/RESET]" +name: + other: Name +version: + other: Version +license: + other: License +vulnerabilities: + other: Vulnerabilities (CVEs) +dependency_row: + other: " [DISABLED]{{.V0}}[/RESET] [CYAN]{{.V1}}[/RESET] [DISABLED]transitive dependencies touched[/RESET]" +dependency_detail_row: + other: " [DISABLED]{{.V0}} {{.V1}}[/RESET] {{.V2}}" +namespace_row: + other: " [DISABLED]{{.V0}} namespace:[/RESET] [CYAN]{{.V1}}[/RESET]" +flag_state_upgrade_expand_description: + other: Show individual transitive dependency changes rather than a summary diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go new file mode 100644 index 0000000000..55906ab017 --- /dev/null +++ b/internal/runners/upgrade/upgrade.go @@ -0,0 +1,343 @@ +package upgrade + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/internal/sliceutils" + "github.com/ActiveState/cli/internal/table" + "github.com/ActiveState/cli/pkg/buildplan" + "github.com/ActiveState/cli/pkg/localcommit" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/pkg/platform/model" + bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/go-openapi/strfmt" +) + +type primeable interface { + primer.Outputer + primer.Auther + primer.Projecter + primer.Prompter +} + +type Params struct { + Expand bool +} + +func NewParams() *Params { + return &Params{} +} + +type Upgrade struct { + prime primeable +} + +func New(p primeable) *Upgrade { + return &Upgrade{ + prime: p, + } +} + +var ErrNoChanges = errors.New("no changes") +var ErrAbort = errors.New("aborted") + +func rationalizeError(err *error) { + switch { + case err == nil: + return + case errors.Is(*err, ErrNoChanges): + *err = errs.WrapUserFacing(*err, locale.T("upgrade_no_changes"), errs.SetInput()) + case errors.Is(*err, ErrAbort): + *err = errs.WrapUserFacing(*err, locale.T("upgrade_aborted"), errs.SetInput()) + } +} + +func (u *Upgrade) Run(params *Params) (rerr error) { + defer rationalizeError(&rerr) + + // Validate project + proj := u.prime.Project() + if proj == nil { + return rationalize.ErrNoProject + } + if proj.IsHeadless() { + return rationalize.ErrHeadless + } + + out := u.prime.Output() + out.Notice(locale.Tr("operating_message", proj.NamespaceString(), proj.Dir())) + + // Collect buildplans for before/after upgrade + pg := output.StartSpinner(out, locale.T("upgrade_solving"), constants.TerminalAnimationInterval) + defer func() { + if pg != nil { + pg.Stop(locale.T("progress_fail")) + } + }() + + // Collect "before" buildplan + localCommitID, err := localcommit.Get(proj.Dir()) + if err != nil { + return errs.Wrap(err, "Failed to get local commit") + } + + bpm := bpModel.NewBuildPlannerModel(u.prime.Auth()) + localCommit, err := bpm.FetchCommit(localCommitID, proj.Owner(), proj.Name(), nil) + if err != nil { + return errs.Wrap(err, "Failed to fetch build result") + } + + // Collect "after" buildplan + bumpedBS, err := localCommit.BuildScript().Clone() + if err != nil { + return errs.Wrap(err, "Failed to clone build script") + } + ts, err := model.FetchLatestTimeStamp(u.prime.Auth()) + if err != nil { + return errs.Wrap(err, "Failed to fetch latest timestamp") + } + bumpedBS.SetAtTime(ts) + + if bumpedBS.AtTime().String() == localCommit.BuildScript().AtTime().String() { + panic(fmt.Sprintf("bumped buildscript is same as local commit, old: %s, new: %s", bumpedBS.AtTime(), localCommit.BuildScript().AtTime())) + } + + // Since our platform is commit based we need to create a commit for the "after" buildplan, even though we may not + // end up using it it the user doesn't confirm the upgrade. + bumpedCommit, err := bpm.StageCommit(bpModel.StageCommitParams{ + Owner: proj.Owner(), + Project: proj.Name(), + ParentCommit: localCommitID.String(), + Script: bumpedBS, + }) + if err != nil { + // The buildplanner itself can assert that there are no new changes, in which case we don't want to handle + // this as an error + var commitErr *response.CommitError + if errors.As(err, &commitErr) { + if commitErr.Type == types.NoChangeSinceLastCommitErrorType { + pg.Stop(locale.T("progress_success")) + pg = nil + return ErrNoChanges + } + } + return errs.Wrap(err, "Failed to stage bumped commit") + } + bumpedBP := bumpedCommit.BuildPlan() + + // All done collecting buildplans + pg.Stop(locale.T("progress_success")) + pg = nil + + changeset := bumpedBP.DiffArtifacts(localCommit.BuildPlan(), false) + if len(changeset.Filter(buildplan.ArtifactUpdated)) == 0 { + // In most cases we would've already reached this error due to the commit failing. But it is possible for + // the commit to be created (ie. there were changes), but without those changes being relevant to any artifacts + // that we care about. + return ErrNoChanges + } + + changes := u.calculateChanges(changeset, bumpedCommit) + if out.Type().IsStructured() { + out.Print(output.Structured(changes)) + } else { + if err := u.renderUserFacing(changes, params.Expand); err != nil { + return errs.Wrap(err, "Failed to render user facing upgrade") + } + } + + if err := localcommit.Set(u.prime.Project().Dir(), bumpedCommit.CommitID.String()); err != nil { + return errs.Wrap(err, "Failed to set local commit") + } + + out.Notice(locale.Tr("upgrade_success")) + + return nil +} + +type structuredChange struct { + Type string `json:"type"` + Name string `json:"name"` + Namespace string `json:"namespace,omitempty"` + OldVersion string `json:"old_version,omitempty"` + NewVersion string `json:"new_version"` + OldRevision int `json:"old_revision"` + NewRevision int `json:"new_revision"` + OldLicenses []string `json:"old_licenses,omitempty"` + NewLicenses []string `json:"new_licenses"` + TransitiveDeps []structuredChange `json:"transitive_dependencies,omitempty"` +} + +func (u *Upgrade) calculateChanges(changedArtifacts buildplan.ArtifactChangeset, bumpedCommit *bpModel.Commit) []structuredChange { + requested := bumpedCommit.BuildPlan().RequestedArtifacts().ToIDMap() + + relevantChanges := changedArtifacts.Filter(buildplan.ArtifactUpdated) + relevantRequestedArtifacts := buildplan.Artifacts{} + + // Calculate relevant artifacts ahead of time, as we'll need them to calculate transitive dependencies + // (we want to avoid recursing into the same artifact multiple times) + for _, change := range relevantChanges { + if _, ok := requested[change.Artifact.ArtifactID]; !ok { + continue + } + relevantRequestedArtifacts = append(relevantRequestedArtifacts, change.Artifact) + } + + changes := []structuredChange{} + for _, artifactUpdate := range changedArtifacts.Filter(buildplan.ArtifactUpdated) { + if _, ok := requested[artifactUpdate.Artifact.ArtifactID]; !ok { + continue + } + + change := structuredChange{ + Type: artifactUpdate.ChangeType.String(), + Name: artifactUpdate.Artifact.Name(), + OldVersion: artifactUpdate.Old.Version(), + NewVersion: artifactUpdate.Artifact.Version(), + OldRevision: artifactUpdate.Old.Revision(), + NewRevision: artifactUpdate.Artifact.Revision(), + OldLicenses: artifactUpdate.Old.Licenses(), + NewLicenses: artifactUpdate.Artifact.Licenses(), + } + + if len(artifactUpdate.Artifact.Ingredients) == 1 { + change.Namespace = artifactUpdate.Artifact.Ingredients[0].Namespace + } + + changedDeps := calculateChangedDeps(artifactUpdate.Artifact, relevantRequestedArtifacts, changedArtifacts) + if len(changedDeps) > 0 { + change.TransitiveDeps = make([]structuredChange, len(changedDeps)) + for n, changedDep := range changedDeps { + change.TransitiveDeps[n] = structuredChange{ + Type: changedDep.ChangeType.String(), + Name: changedDep.Artifact.Name(), + NewVersion: changedDep.Artifact.Version(), + NewRevision: changedDep.Artifact.Revision(), + NewLicenses: changedDep.Artifact.Licenses(), + } + if changedDep.Old != nil { + change.TransitiveDeps[n].OldVersion = changedDep.Old.Version() + change.TransitiveDeps[n].OldRevision = changedDep.Old.Revision() + change.TransitiveDeps[n].OldLicenses = changedDep.Old.Licenses() + } + } + } + + changes = append(changes, change) + } + + return changes +} + +func (u *Upgrade) renderUserFacing(changes []structuredChange, expand bool) error { + out := u.prime.Output() + + out.Notice("") // Empty line + + tbl := table.New(locale.Ts("name", "version", "license")) + tbl.HideDash = true + for _, change := range changes { + tbl.AddRow([]string{ + change.Name, + renderVersionChange(change), + renderLicenseChange(change), + }) + + needsDepRow := len(change.TransitiveDeps) > 0 + needsNamespaceRow := strings.HasPrefix(change.Namespace, model.OrgNamespacePrefix) + + if needsNamespaceRow { + treeSymbol := output.TreeEnd + if needsDepRow { + treeSymbol = output.TreeMid + } + tbl.AddRow([]string{locale.Tr("namespace_row", treeSymbol, change.Namespace)}) + } + + if needsDepRow { + if expand { + for n, changedDep := range change.TransitiveDeps { + treeSymbol := output.TreeEnd + if n != len(change.TransitiveDeps)-1 { + treeSymbol = output.TreeMid + } + tbl.AddRow([]string{locale.Tr("dependency_detail_row", treeSymbol, changedDep.Name, renderVersionChange(changedDep))}) + } + } else { + tbl.AddRow([]string{locale.Tr("dependency_row", output.TreeEnd, strconv.Itoa(len(change.TransitiveDeps)))}) + } + } + } + + out.Print(tbl.Render()) + + out.Notice(" ") // Empty line (prompts use Notice) + confirm, err := u.prime.Prompt().Confirm("", locale.Tr("upgrade_confirm"), ptr.To(true)) + if err != nil { + return errs.Wrap(err, "confirmation failed") + } + if !confirm { + return ErrAbort + } + + return nil +} + +func calculateChangedDeps(artifact *buildplan.Artifact, dontCount buildplan.Artifacts, changeset buildplan.ArtifactChangeset) buildplan.ArtifactChangeset { + ignore := map[strfmt.UUID]struct{}{} + for _, skip := range dontCount { + if skip.ArtifactID == artifact.ArtifactID { + continue // Don't ignore the current artifact, or we won't get dependencies for it + } + ignore[skip.ArtifactID] = struct{}{} + } + + result := buildplan.ArtifactChangeset{} + + deps := artifact.Dependencies(true, &ignore) + for _, change := range changeset.Filter(buildplan.ArtifactAdded, buildplan.ArtifactUpdated) { + for _, dep := range deps { + if dep.ArtifactID == change.Artifact.ArtifactID { + result = append(result, change) + } + } + } + + return sliceutils.Unique(result) +} + +func renderVersionChange(change structuredChange) string { + if change.OldVersion == "" { + return locale.Tr("upgrade_field_same", change.NewVersion) + } + old := change.OldVersion + new := change.NewVersion + if change.OldVersion == change.NewVersion { + old = fmt.Sprintf("%s (%d)", old, change.OldRevision) + new = fmt.Sprintf("%s (%d)", new, change.NewRevision) + } + if old == new { + return locale.Tr("upgrade_field_same", change.NewVersion) + } + return locale.Tr("upgrade_field_change", change.OldVersion, change.NewVersion) +} + +func renderLicenseChange(change structuredChange) string { + from := change.OldLicenses + to := change.NewLicenses + if sliceutils.EqualValues(from, to) { + return locale.Tr("upgrade_field_same", strings.Join(from, ", ")) + } + return locale.Tr("upgrade_field_change", strings.Join(from, ", "), strings.Join(to, ", ")) +} From 8b5f9cd3b61e9b3e89cb151aa29d64b6ac3d829f Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 28 Aug 2024 15:23:43 -0400 Subject: [PATCH 025/440] Revert "Merge "PATH" with Window's "Path"." This reverts commit aa415e31b133a51f7cc61ddbac31932a27158d94. --- pkg/runtime/internal/envdef/environment.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/runtime/internal/envdef/environment.go b/pkg/runtime/internal/envdef/environment.go index ef51215c10..0d379dcef5 100644 --- a/pkg/runtime/internal/envdef/environment.go +++ b/pkg/runtime/internal/envdef/environment.go @@ -6,7 +6,6 @@ import ( "maps" "os" "path/filepath" - "runtime" "strings" "github.com/ActiveState/cli/internal/errs" @@ -341,11 +340,7 @@ func (ed *EnvironmentDefinition) GetEnvBasedOn(envLookup map[string]string) (map for _, ev := range ed.Env { pev := &ev if pev.Inherit { - name := pev.Name - if runtime.GOOS == "windows" && name == "PATH" { - name = "Path" // env vars are case-insensitive on Windows, and this is what it uses - } - osValue, hasOsValue := envLookup[name] + osValue, hasOsValue := envLookup[pev.Name] if hasOsValue { osEv := ev osEv.Values = []string{osValue} From 10efe20d7d3884a4b7d1825790b3a4d57ed0e431 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 28 Aug 2024 15:30:11 -0400 Subject: [PATCH 026/440] `state exec` should also search "Path" for exes on Windows. Our runtime.json files use PATH, but Windows uses Path. Workaround this for now. --- internal/runners/exec/exec.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/internal/runners/exec/exec.go b/internal/runners/exec/exec.go index 6a307f232d..6ddca60d0a 100644 --- a/internal/runners/exec/exec.go +++ b/internal/runners/exec/exec.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strconv" "strings" @@ -31,7 +32,7 @@ import ( "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/project" "github.com/ActiveState/cli/pkg/projectfile" - "github.com/ActiveState/cli/pkg/runtime" + rt "github.com/ActiveState/cli/pkg/runtime" ) type Configurable interface { @@ -98,7 +99,7 @@ func (s *Exec) Run(params *Params, args ...string) (rerr error) { // If the path passed resolves to a runtime dir (ie. has a runtime marker) then the project is not used var proj *project.Project var err error - if params.Path != "" && runtime.IsRuntimeDir(params.Path) { + if params.Path != "" && rt.IsRuntimeDir(params.Path) { projectDir = projectFromRuntimeDir(s.cfg, params.Path) proj, err = project.FromPath(projectDir) if err != nil { @@ -145,14 +146,18 @@ func (s *Exec) Run(params *Params, args ...string) (rerr error) { if !fileutils.TargetExists(exeTarget) { // Report recursive execution of executor: The path for the executable should be different from the default bin dir - exesOnPath := osutils.FilterExesOnPATH(exeTarget, env["PATH"], func(exe string) bool { + filter := func(exe string) bool { v, err := executors.IsExecutor(exe) if err != nil { logging.Error("Could not find out if executable is an executor: %s", errs.JoinMessage(err)) return true // This usually means there's a permission issue, which means we likely don't own it } return !v - }) + } + exesOnPath := osutils.FilterExesOnPATH(exeTarget, env["PATH"], filter) + if runtime.GOOS == "windows" { + exesOnPath = append(exesOnPath, osutils.FilterExesOnPATH(exeTarget, env["Path"], filter)...) + } if len(exesOnPath) > 0 { exeTarget = exesOnPath[0] From 46e9a0048da4778e3347632be42a0e6d3fe1219d Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 28 Aug 2024 12:53:31 -0700 Subject: [PATCH 027/440] Set shell on Windows --- internal/testhelpers/e2e/session.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 030e279c23..f84b71007f 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -189,6 +189,12 @@ func new(t *testing.T, retainDirs, updatePath bool, extraEnv ...string) *Session require.NoError(session.T, err) } + if runtime.GOOS == "windows" { + if err := cfg.Set(subshell.ConfigKeyShell, "cmd.exe"); err != nil { + require.NoError(session.T, err) + } + } + return session } From dfac51e96a8a521d39618a5d4d7aaca5c7060172 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 14:33:05 -0700 Subject: [PATCH 028/440] Make time expansion reusable between commands --- internal/runbits/commits_runbit/time.go | 65 +++++++++++++++++++++++++ internal/runners/packages/info.go | 5 +- internal/runners/packages/install.go | 5 +- internal/runners/packages/search.go | 7 +-- internal/runners/packages/time.go | 51 ------------------- internal/runners/packages/uninstall.go | 5 +- 6 files changed, 78 insertions(+), 60 deletions(-) create mode 100644 internal/runbits/commits_runbit/time.go delete mode 100644 internal/runners/packages/time.go diff --git a/internal/runbits/commits_runbit/time.go b/internal/runbits/commits_runbit/time.go new file mode 100644 index 0000000000..478cfc544d --- /dev/null +++ b/internal/runbits/commits_runbit/time.go @@ -0,0 +1,65 @@ +package commits_runbit + +import ( + "time" + + "github.com/ActiveState/cli/internal/captain" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/pkg/localcommit" + "github.com/ActiveState/cli/pkg/platform/authentication" + "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/project" +) + +// ExpandTime returns a timestamp based on the given "--ts" value. +// If the timestamp was already defined, we just return it. +// If "now" was given, returns "now" according to the platform. +// Otherwise, returns the specified timestamp or nil (which falls back on the default Platform +// timestamp for a given operation) +func ExpandTime(ts *captain.TimeValue, auth *authentication.Auth) (time.Time, error) { + if ts.Time != nil { + return *ts.Time, nil + } + + if ts.Now() { + latest, err := model.FetchLatestRevisionTimeStamp(auth) + if err != nil { + return time.Time{}, errs.Wrap(err, "Unable to determine latest revision time") + } + return latest, nil + } + + latest, err := model.FetchLatestTimeStamp(auth) + if err != nil { + return time.Time{}, errs.Wrap(err, "Unable to fetch latest Platform timestamp") + } + + return latest, nil +} + +// ExpandTimeForProject is the same as ExpandTime except that it ensures the returned time is either the same or +// later than that of the most recent commit. +func ExpandTimeForProject(ts *captain.TimeValue, auth *authentication.Auth, proj *project.Project) (time.Time, error) { + timestamp, err := ExpandTime(ts, auth) + if err != nil { + return time.Time{}, errs.Wrap(err, "Unable to expand time") + } + + if proj != nil { + commitID, err := localcommit.Get(proj.Dir()) + if err != nil { + return time.Time{}, errs.Wrap(err, "Unable to get commit ID") + } + + atTime, err := model.FetchTimeStampForCommit(commitID, auth) + if err != nil { + return time.Time{}, errs.Wrap(err, "Unable to get commit time") + } + + if atTime.After(timestamp) { + return *atTime, nil + } + } + + return timestamp, nil +} diff --git a/internal/runners/packages/info.go b/internal/runners/packages/info.go index f81a4f63e1..fb559b8ecc 100644 --- a/internal/runners/packages/info.go +++ b/internal/runners/packages/info.go @@ -12,6 +12,7 @@ import ( "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/pkg/platform/api/inventory/inventory_models" "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/request" "github.com/ActiveState/cli/pkg/platform/authentication" @@ -70,12 +71,12 @@ func (i *Info) Run(params InfoRunParams, nstype model.NamespaceType) error { normalized = params.Package.Name } - ts, err := getTime(¶ms.Timestamp, i.auth, i.proj) + ts, err := commits_runbit.ExpandTimeForProject(¶ms.Timestamp, i.auth, i.proj) if err != nil { return errs.Wrap(err, "Unable to get timestamp from params") } - packages, err := model.SearchIngredientsStrict(ns.String(), normalized, false, false, ts, i.auth) // ideally case-sensitive would be true (PB-4371) + packages, err := model.SearchIngredientsStrict(ns.String(), normalized, false, false, &ts, i.auth) // ideally case-sensitive would be true (PB-4371) if err != nil { return locale.WrapError(err, "package_err_cannot_obtain_search_results") } diff --git a/internal/runners/packages/install.go b/internal/runners/packages/install.go index 16622378d3..30193f4cf8 100644 --- a/internal/runners/packages/install.go +++ b/internal/runners/packages/install.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/runtime/requirements" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/model" @@ -51,10 +52,10 @@ func (a *Install) Run(params InstallRunParams, nsType model.NamespaceType) (rerr reqs = append(reqs, req) } - ts, err := getTime(¶ms.Timestamp, a.prime.Auth(), a.prime.Project()) + ts, err := commits_runbit.ExpandTimeForProject(¶ms.Timestamp, a.prime.Auth(), a.prime.Project()) if err != nil { return errs.Wrap(err, "Unable to get timestamp from params") } - return requirements.NewRequirementOperation(a.prime).ExecuteRequirementOperation(ts, reqs...) + return requirements.NewRequirementOperation(a.prime).ExecuteRequirementOperation(&ts, reqs...) } diff --git a/internal/runners/packages/search.go b/internal/runners/packages/search.go index ce077506a2..ed6ad2e4f0 100644 --- a/internal/runners/packages/search.go +++ b/internal/runners/packages/search.go @@ -8,6 +8,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/request" "github.com/ActiveState/cli/pkg/platform/authentication" @@ -58,16 +59,16 @@ func (s *Search) Run(params SearchRunParams, nstype model.NamespaceType) error { ns = model.NewRawNamespace(params.Ingredient.Namespace) } - ts, err := getTime(¶ms.Timestamp, s.auth, s.proj) + ts, err := commits_runbit.ExpandTimeForProject(¶ms.Timestamp, s.auth, s.proj) if err != nil { return errs.Wrap(err, "Unable to get timestamp from params") } var packages []*model.IngredientAndVersion if params.ExactTerm { - packages, err = model.SearchIngredientsLatestStrict(ns.String(), params.Ingredient.Name, true, true, ts, s.auth) + packages, err = model.SearchIngredientsLatestStrict(ns.String(), params.Ingredient.Name, true, true, &ts, s.auth) } else { - packages, err = model.SearchIngredientsLatest(ns.String(), params.Ingredient.Name, true, ts, s.auth) + packages, err = model.SearchIngredientsLatest(ns.String(), params.Ingredient.Name, true, &ts, s.auth) } if err != nil { return locale.WrapError(err, "package_err_cannot_obtain_search_results") diff --git a/internal/runners/packages/time.go b/internal/runners/packages/time.go deleted file mode 100644 index 65923fda24..0000000000 --- a/internal/runners/packages/time.go +++ /dev/null @@ -1,51 +0,0 @@ -package packages - -import ( - "time" - - "github.com/ActiveState/cli/internal/captain" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/pkg/localcommit" - "github.com/ActiveState/cli/pkg/platform/authentication" - "github.com/ActiveState/cli/pkg/platform/model" - "github.com/ActiveState/cli/pkg/project" -) - -// getTime returns a timestamp based on the given "--ts" value. -// If "now" was given, returns "now" according to the platform. -// If no timestamp was given but the current project's commit time is after the latest inventory -// timestamp, returns that commit time. -// Otherwise, returns the specified timestamp or nil (which falls back on the default Platform -// timestamp for a given operation) -func getTime(ts *captain.TimeValue, auth *authentication.Auth, proj *project.Project) (*time.Time, error) { - if ts.Now() { - latest, err := model.FetchLatestRevisionTimeStamp(auth) - if err != nil { - return nil, errs.Wrap(err, "Unable to determine latest revision time") - } - return &latest, nil - } - - if ts.Time == nil && proj != nil { - latest, err := model.FetchLatestTimeStamp(auth) - if err != nil { - return nil, errs.Wrap(err, "Unable to fetch latest Platform timestamp") - } - - commitID, err := localcommit.Get(proj.Dir()) - if err != nil { - return nil, errs.Wrap(err, "Unable to get commit ID") - } - - atTime, err := model.FetchTimeStampForCommit(commitID, auth) - if err != nil { - return nil, errs.Wrap(err, "Unable to get commit time") - } - - if atTime.After(latest) { - return atTime, nil - } - } - - return ts.Time, nil -} diff --git a/internal/runners/packages/uninstall.go b/internal/runners/packages/uninstall.go index 3156c01d97..a9008261dd 100644 --- a/internal/runners/packages/uninstall.go +++ b/internal/runners/packages/uninstall.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/runtime/requirements" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" @@ -50,10 +51,10 @@ func (u *Uninstall) Run(params UninstallRunParams, nsType model.NamespaceType) ( reqs = append(reqs, req) } - ts, err := getTime(&captain.TimeValue{}, u.prime.Auth(), u.prime.Project()) + ts, err := commits_runbit.ExpandTimeForProject(&captain.TimeValue{}, u.prime.Auth(), u.prime.Project()) if err != nil { return errs.Wrap(err, "Unable to get timestamp from params") } - return requirements.NewRequirementOperation(u.prime).ExecuteRequirementOperation(ts, reqs...) + return requirements.NewRequirementOperation(u.prime).ExecuteRequirementOperation(&ts, reqs...) } From 14216bb9adfa678e798c2770160601ecb40d4ea6 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 14:33:49 -0700 Subject: [PATCH 029/440] Add `--ts` flag to `state upgrade` --- cmd/state/internal/cmdtree/upgrade.go | 5 +++++ internal/locale/locales/en-us.yaml | 2 ++ internal/runners/upgrade/upgrade.go | 7 +++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd/state/internal/cmdtree/upgrade.go b/cmd/state/internal/cmdtree/upgrade.go index 05636d2cba..18019f5f3b 100644 --- a/cmd/state/internal/cmdtree/upgrade.go +++ b/cmd/state/internal/cmdtree/upgrade.go @@ -18,6 +18,11 @@ func newUpgradeCommand(prime *primer.Values) *captain.Command { locale.Tl("upgrade_cmd_description", "Upgrade dependencies of a project"), prime, []*captain.Flag{ + { + Name: "ts", + Description: locale.T("flag_state_upgrade_ts_description"), + Value: ¶ms.Timestamp, + }, { Name: "expand", Description: locale.T("flag_state_upgrade_expand_description"), diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 2c9ec195bc..62aa856235 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1579,3 +1579,5 @@ namespace_row: other: " [DISABLED]{{.V0}} namespace:[/RESET] [CYAN]{{.V1}}[/RESET]" flag_state_upgrade_expand_description: other: Show individual transitive dependency changes rather than a summary +flag_state_upgrade_ts_description: + other: Manually specify the timestamp to 'upgrade' to. Can be either 'now' or RFC3339 formatted timestamp. diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index 55906ab017..a716823480 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -6,12 +6,14 @@ import ( "strconv" "strings" + "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/sliceutils" "github.com/ActiveState/cli/internal/table" @@ -32,7 +34,8 @@ type primeable interface { } type Params struct { - Expand bool + Timestamp captain.TimeValue + Expand bool } func NewParams() *Params { @@ -103,7 +106,7 @@ func (u *Upgrade) Run(params *Params) (rerr error) { if err != nil { return errs.Wrap(err, "Failed to clone build script") } - ts, err := model.FetchLatestTimeStamp(u.prime.Auth()) + ts, err := commits_runbit.ExpandTime(¶ms.Timestamp, u.prime.Auth()) if err != nil { return errs.Wrap(err, "Failed to fetch latest timestamp") } From b9b71b3792aaf3435a2df71d4366b03cbae24e83 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 14:34:02 -0700 Subject: [PATCH 030/440] Added integration test for `state upgrade` --- internal/testhelpers/tagsuite/tagsuite.go | 1 + test/integration/upgrade_int_test.go | 70 +++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 test/integration/upgrade_int_test.go diff --git a/internal/testhelpers/tagsuite/tagsuite.go b/internal/testhelpers/tagsuite/tagsuite.go index cbaa75737a..ce3a5d6968 100644 --- a/internal/testhelpers/tagsuite/tagsuite.go +++ b/internal/testhelpers/tagsuite/tagsuite.go @@ -80,6 +80,7 @@ const ( Show = "show" Switch = "switch" Uninstall = "uninstall" + Upgrade = "upgrade" Update = "update" Use = "use" ) diff --git a/test/integration/upgrade_int_test.go b/test/integration/upgrade_int_test.go new file mode 100644 index 0000000000..27b0eb66be --- /dev/null +++ b/test/integration/upgrade_int_test.go @@ -0,0 +1,70 @@ +package integration + +import ( + "testing" + + "github.com/ActiveState/cli/internal/testhelpers/e2e" + "github.com/ActiveState/cli/internal/testhelpers/suite" + "github.com/ActiveState/cli/internal/testhelpers/tagsuite" + "github.com/ActiveState/termtest" +) + +type UpgradeIntegrationTestSuite struct { + tagsuite.Suite +} + +func (suite *UpgradeIntegrationTestSuite) TestUpgrade() { + suite.OnlyRunForTags(tagsuite.Upgrade) + + ts := e2e.New(suite.T(), false) + defer ts.Close() + + ts.PrepareProject("ActiveState-CLI-Testing/python-upgradeable", "d0e56f96-b956-4b0c-9c20-938d3e843ab9") + + commitBefore := ts.CommitID() + + // Normally you wouldn't specify the timestamp except for special use-cases, but tests work better if they're + // reproducible, not to mention faster if they can hit the cache. + time := "2024-08-23T18:35:55.818Z" + + cp := ts.Spawn("upgrade", "--ts", time) + cp.Expect("transitive dependencies touched") + cp.Expect("install these upgrades?") + cp.SendLine("y") + cp.Expect("Upgrade completed") + cp.ExpectExitCode(0) + + // The ordering of these is not guaranteed, so we get a bit creative here + snapshot := cp.Snapshot() + suite.Contains(snapshot, "pytest") + suite.Contains(snapshot, "requests") + suite.Contains(snapshot, "7.2.2 > 8.3.2") // old pytest version + suite.Contains(snapshot, "2.28.2 > 2.32.3") // old requests version + + suite.NotEqual(commitBefore, ts.CommitID()) +} + +func (suite *UpgradeIntegrationTestSuite) TestUpgradeJSON() { + suite.OnlyRunForTags(tagsuite.Upgrade) + + ts := e2e.New(suite.T(), false) + defer ts.Close() + + ts.PrepareProject("ActiveState-CLI-Testing/python-upgradeable", "d0e56f96-b956-4b0c-9c20-938d3e843ab9") + + commitBefore := ts.CommitID() + + cp := ts.SpawnWithOpts( + e2e.OptArgs("upgrade", "--output=json"), + e2e.OptTermTest(termtest.OptRows(500)), // Ensure json fits inside snapshot + ) + cp.ExpectExitCode(0) + + AssertValidJSON(suite.T(), cp) + + suite.NotEqual(commitBefore, ts.CommitID()) +} + +func TestUpgradeIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(UpgradeIntegrationTestSuite)) +} From e70fae80ce01619091f8ad1d3778c869ecc065d4 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 28 Aug 2024 14:49:15 -0700 Subject: [PATCH 031/440] Fix assertion; this ID should never have shown up as a dependency --- pkg/buildplan/artifact_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/buildplan/artifact_test.go b/pkg/buildplan/artifact_test.go index fa26fff3e7..88ba3d707f 100644 --- a/pkg/buildplan/artifact_test.go +++ b/pkg/buildplan/artifact_test.go @@ -54,7 +54,6 @@ func TestArtifact_Dependencies(t *testing.T) { want: []string{ "00000000-0000-0000-0000-000000000002", "00000000-0000-0000-0000-000000000003", - "00000000-0000-0000-0000-000000000001", }, }, } From 66efe82484095a3ef9b0e8fd6a6a0067ef3b2a3b Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 29 Aug 2024 13:17:16 -0700 Subject: [PATCH 032/440] Remove debugging statement --- internal/runners/upgrade/upgrade.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index a716823480..9730b39361 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -112,10 +112,6 @@ func (u *Upgrade) Run(params *Params) (rerr error) { } bumpedBS.SetAtTime(ts) - if bumpedBS.AtTime().String() == localCommit.BuildScript().AtTime().String() { - panic(fmt.Sprintf("bumped buildscript is same as local commit, old: %s, new: %s", bumpedBS.AtTime(), localCommit.BuildScript().AtTime())) - } - // Since our platform is commit based we need to create a commit for the "after" buildplan, even though we may not // end up using it it the user doesn't confirm the upgrade. bumpedCommit, err := bpm.StageCommit(bpModel.StageCommitParams{ From b86f7a0a9a3e59c824e414eb704eea0ac5f33fb2 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 29 Aug 2024 13:26:16 -0700 Subject: [PATCH 033/440] Add unit test for sliceutils.EqualValues --- internal/sliceutils/sliceutils_test.go | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/internal/sliceutils/sliceutils_test.go b/internal/sliceutils/sliceutils_test.go index 4989aea6f7..dbd6b5d69c 100644 --- a/internal/sliceutils/sliceutils_test.go +++ b/internal/sliceutils/sliceutils_test.go @@ -323,3 +323,49 @@ func TestToLookupMapByKey(t *testing.T) { "Key3": v3, }, lookupMap) } + +func TestEqualValues(t *testing.T) { + tests := []struct { + name string + comparison func() bool + expected bool + }{ + { + name: "Equal int slices", + comparison: func() bool { + return EqualValues([]int{1, 2, 3}, []int{3, 2, 1}) + }, + expected: true, + }, + { + name: "Unequal int slices", + comparison: func() bool { + return EqualValues([]int{1, 2, 3}, []int{4, 5, 6}) + }, + expected: false, + }, + { + name: "Equal string slices", + comparison: func() bool { + return EqualValues([]string{"a", "b", "c"}, []string{"c", "b", "a"}) + }, + expected: true, + }, + { + name: "Unequal string slices", + comparison: func() bool { + return EqualValues([]string{"a", "b", "c"}, []string{"a", "b", "d"}) + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.comparison() + if result != tt.expected { + t.Errorf("%s = %v; want %v", tt.name, result, tt.expected) + } + }) + } +} From 9882ea8dcec7c0ba5d2ae83bfacdc5797293df47 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 29 Aug 2024 13:50:23 -0700 Subject: [PATCH 034/440] Run Windows CI tests inside subshell --- internal/testhelpers/e2e/session.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index f84b71007f..032d4c911b 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -261,6 +261,14 @@ func (s *Session) SpawnCmdWithOpts(exe string, optSetters ...SpawnOptSetter) *Sp termtest.OptNormalizedLineEnds(true), ) + if runtime.GOOS == "windows" && condition.OnCI() { + // Our Windows integration tests are run on bash. Due to the way the PTY library runs a new + // command we need to run the command inside a shell in order to setup the correct process + // tree. Without this integration tests run in bash will incorrectly identify the partent shell + // as bash, rather than the actual shell that is running the command. + spawnOpts.RunInsideShell = true + } + for _, optSet := range optSetters { optSet(&spawnOpts) } From d1a9f8ae46386e9c36714e6427a646b82ee49627 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 29 Aug 2024 15:31:03 -0700 Subject: [PATCH 035/440] Attempt to fix Windows tests --- .../test/integration/installer_int_test.go | 2 ++ internal/testhelpers/e2e/session.go | 22 ++++++++++++------- internal/testhelpers/e2e/spawn.go | 11 ++++++++++ test/integration/activate_int_test.go | 17 ++++++++------ test/integration/deploy_int_test.go | 1 + test/integration/install_scripts_int_test.go | 2 +- test/integration/shell_int_test.go | 2 +- 7 files changed, 40 insertions(+), 17 deletions(-) diff --git a/cmd/state-installer/test/integration/installer_int_test.go b/cmd/state-installer/test/integration/installer_int_test.go index 1f83eaa242..8b6b99c21b 100644 --- a/cmd/state-installer/test/integration/installer_int_test.go +++ b/cmd/state-installer/test/integration/installer_int_test.go @@ -44,6 +44,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { e2e.OptArgs(installationDir(ts), "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=false"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.OverwriteDefaultSystemPathEnvVarName, dir)), + e2e.OptMaybeRunInsideShell(), ) // Assert output @@ -176,6 +177,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallErrorTips() { e2e.OptArgs(installationDir(ts), "--activate", "ActiveState-CLI/Python3", "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=true"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.OverwriteDefaultSystemPathEnvVarName, dir)), + e2e.OptMaybeRunInsideShell(), ) cp.ExpectInput(e2e.RuntimeSourcingTimeoutOpt) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 032d4c911b..7db033dec0 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -234,6 +234,20 @@ func (s *Session) SpawnShellWithOpts(shell Shell, opts ...SpawnOptSetter) *Spawn return s.SpawnCmdWithOpts(string(shell), opts...) } +// SpawnShell spawns the state tool executable to be tested with arguments. +// This function differs from Spawn in that it runs the command in a shell on Windows CI. +// Our Windows integration tests are run on bash. Due to the way the PTY library runs a new +// command we need to run the command inside a shell in order to setup the correct process +// tree. Without this integration tests run in bash will incorrectly identify the partent shell +// as bash, rather than the actual shell that is running the command +func (s *Session) SpawnShell(args ...string) *SpawnedCmd { + opts := []SpawnOptSetter{OptArgs(args...)} + if runtime.GOOS == "windows" && condition.OnCI() { + opts = append(opts, OptRunInsideShell(true)) + } + return s.SpawnCmdWithOpts(s.Exe, opts...) +} + // SpawnCmdWithOpts executes an executable in a pseudo-terminal for integration tests // Arguments and other parameters can be specified by specifying SpawnOptSetter func (s *Session) SpawnCmdWithOpts(exe string, optSetters ...SpawnOptSetter) *SpawnedCmd { @@ -261,14 +275,6 @@ func (s *Session) SpawnCmdWithOpts(exe string, optSetters ...SpawnOptSetter) *Sp termtest.OptNormalizedLineEnds(true), ) - if runtime.GOOS == "windows" && condition.OnCI() { - // Our Windows integration tests are run on bash. Due to the way the PTY library runs a new - // command we need to run the command inside a shell in order to setup the correct process - // tree. Without this integration tests run in bash will incorrectly identify the partent shell - // as bash, rather than the actual shell that is running the command. - spawnOpts.RunInsideShell = true - } - for _, optSet := range optSetters { optSet(&spawnOpts) } diff --git a/internal/testhelpers/e2e/spawn.go b/internal/testhelpers/e2e/spawn.go index bf73ffcd22..bcc466ea9c 100644 --- a/internal/testhelpers/e2e/spawn.go +++ b/internal/testhelpers/e2e/spawn.go @@ -10,6 +10,7 @@ import ( "github.com/ActiveState/termtest" + "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/osutils" ) @@ -180,3 +181,13 @@ func OptRunInsideShell(v bool) SpawnOptSetter { opts.RunInsideShell = v } } + +func OptMaybeRunInsideShell() SpawnOptSetter { + if runtime.GOOS == "windows" && condition.OnCI() { + return func(opts *SpawnOpts) { + opts.RunInsideShell = true + } + } + + return func(opts *SpawnOpts) {} +} diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index d91ed46918..9f34c6daa3 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -54,7 +54,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateWithoutRuntime() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.Spawn("activate", "ActiveState-CLI/Empty") + cp := ts.SpawnShell("activate", "ActiveState-CLI/Empty") cp.Expect("Activated") cp.ExpectInput() @@ -134,7 +134,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateUsingCommitID() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.Spawn("activate", "ActiveState-CLI/Empty#6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", "--path", ts.Dirs.Work) + cp := ts.SpawnShell("activate", "ActiveState-CLI/Empty#6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", "--path", ts.Dirs.Work) cp.Expect("Activated") cp.ExpectInput() @@ -149,7 +149,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateNotOnPath() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.Spawn("activate", "activestate-cli/empty", "--path", ts.Dirs.Work) + cp := ts.SpawnShell("activate", "activestate-cli/empty", "--path", ts.Dirs.Work) cp.Expect("Activated") cp.ExpectInput() @@ -177,7 +177,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePythonByHostOnly() { defer close() projectName := "Python-LinuxWorks" - cp := ts.Spawn("activate", "cli-integration-tests/"+projectName, "--path="+ts.Dirs.Work) + cp := ts.SpawnShell("activate", "cli-integration-tests/"+projectName, "--path="+ts.Dirs.Work) if runtime.GOOS == "linux" { cp.Expect("Creating a Virtual Environment") @@ -221,6 +221,7 @@ func (suite *ActivateIntegrationTestSuite) activatePython(version string, extraE cp := ts.SpawnWithOpts( e2e.OptArgs("activate", namespace), e2e.OptAppendEnv(extraEnv...), + e2e.OptMaybeRunInsideShell(), ) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) @@ -294,7 +295,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_PythonPath() { namespace := "ActiveState-CLI/Python3" - cp := ts.Spawn("activate", namespace) + cp := ts.SpawnShell("activate", namespace) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) // ensure that shell is functional @@ -337,6 +338,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_SpaceInCacheDir() { cp := ts.SpawnWithOpts( e2e.OptArgs("activate", "ActiveState-CLI/Python3"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.CacheEnvVarName, cacheDir)), + e2e.OptMaybeRunInsideShell(), ) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) @@ -358,7 +360,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePerlCamel() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.Spawn("activate", "ActiveState-CLI/Perl") + cp := ts.SpawnShell("activate", "ActiveState-CLI/Perl") cp.Expect("Downloading", termtest.OptExpectTimeout(40*time.Second)) cp.Expect("Installing", termtest.OptExpectTimeout(140*time.Second)) @@ -402,6 +404,7 @@ version: %s c2 := ts.SpawnWithOpts( e2e.OptArgs("activate"), e2e.OptWD(filepath.Join(ts.Dirs.Work, "foo", "bar", "baz")), + e2e.OptMaybeRunInsideShell(), ) c2.Expect("Activated") @@ -474,7 +477,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_FromCache() { // Note: cannot use Empty project since we need artifacts to download and install. // Pick the langless project, which just has some small, non-language artifacts. - cp := ts.Spawn("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) + cp := ts.SpawnShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) suite.assertCompletedStatusBarReport(cp.Output()) diff --git a/test/integration/deploy_int_test.go b/test/integration/deploy_int_test.go index f51e686baa..28b2621daf 100644 --- a/test/integration/deploy_int_test.go +++ b/test/integration/deploy_int_test.go @@ -168,6 +168,7 @@ func (suite *DeployIntegrationTestSuite) TestDeployPython() { "cmd.exe", e2e.OptArgs("/k", filepath.Join(targetPath, "bin", "shell.bat")), e2e.OptAppendEnv("PATHEXT=.COM;.EXE;.BAT;.LNK", "SHELL="), + e2e.OptRunInsideShell(true), ) } else { cp = ts.SpawnCmdWithOpts( diff --git a/test/integration/install_scripts_int_test.go b/test/integration/install_scripts_int_test.go index 8846c44353..4f0b866d9b 100644 --- a/test/integration/install_scripts_int_test.go +++ b/test/integration/install_scripts_int_test.go @@ -104,7 +104,7 @@ func (suite *InstallScriptsIntegrationTestSuite) TestInstall() { } if runtime.GOOS == "windows" { cmd = "powershell.exe" - opts = append(opts, e2e.OptAppendEnv("SHELL=")) + opts = append(opts, e2e.OptAppendEnv("SHELL="), e2e.OptRunInsideShell(true)) } cp := ts.SpawnCmdWithOpts(cmd, opts...) cp.Expect("Preparing Installer for State Tool Package Manager") diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index 77183146e1..8faf38257f 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -466,7 +466,7 @@ events:`, lang, splat), 1) suite.Require().NoError(fileutils.WriteFile(asyFilename, []byte(contents))) // Verify that running a script as a command with an argument containing special characters works. - cp = ts.Spawn("shell") + cp = ts.SpawnShell("shell") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectInput() cp.SendLine(`args "<3"`) From 976c6fc016f24c98ee613e118e3ca568badb6454 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 29 Aug 2024 16:04:25 -0700 Subject: [PATCH 036/440] Address more tests --- cmd/state-installer/test/integration/installer_int_test.go | 1 + test/integration/activate_int_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/state-installer/test/integration/installer_int_test.go b/cmd/state-installer/test/integration/installer_int_test.go index 8b6b99c21b..b3231d0165 100644 --- a/cmd/state-installer/test/integration/installer_int_test.go +++ b/cmd/state-installer/test/integration/installer_int_test.go @@ -62,6 +62,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { e2e.OptArgs(installationDir(ts), "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=false"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.OverwriteDefaultSystemPathEnvVarName, dir)), + e2e.OptMaybeRunInsideShell(), ) cp.Expect("successfully installed") cp.ExpectInput() diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index 9f34c6daa3..e5a9843e04 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -485,7 +485,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_FromCache() { cp.ExpectExitCode(0) // next activation is cached - cp = ts.Spawn("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) + cp = ts.SpawnShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) cp.ExpectInput(e2e.RuntimeSourcingTimeoutOpt) cp.SendLine("exit") From 0f6e7d55ea3eb3c5a00bd9d698560cfdaea970f2 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 29 Aug 2024 16:27:31 -0700 Subject: [PATCH 037/440] Revert install scripts test change --- test/integration/install_scripts_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/install_scripts_int_test.go b/test/integration/install_scripts_int_test.go index 4f0b866d9b..8846c44353 100644 --- a/test/integration/install_scripts_int_test.go +++ b/test/integration/install_scripts_int_test.go @@ -104,7 +104,7 @@ func (suite *InstallScriptsIntegrationTestSuite) TestInstall() { } if runtime.GOOS == "windows" { cmd = "powershell.exe" - opts = append(opts, e2e.OptAppendEnv("SHELL="), e2e.OptRunInsideShell(true)) + opts = append(opts, e2e.OptAppendEnv("SHELL=")) } cp := ts.SpawnCmdWithOpts(cmd, opts...) cp.Expect("Preparing Installer for State Tool Package Manager") From 408e7ddae3247a425082b7c627d07572266c9db7 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Fri, 30 Aug 2024 09:46:37 -0700 Subject: [PATCH 038/440] Change powershell expect --- internal/testhelpers/e2e/spawn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testhelpers/e2e/spawn.go b/internal/testhelpers/e2e/spawn.go index bcc466ea9c..d36e97f369 100644 --- a/internal/testhelpers/e2e/spawn.go +++ b/internal/testhelpers/e2e/spawn.go @@ -75,7 +75,7 @@ func (s *SpawnedCmd) ExpectInput(opts ...termtest.SetExpectOpt) error { expect := `expect'input from posix shell` if cmdName != "bash" && shellName != "bash" && runtime.GOOS == "windows" { if strings.Contains(cmdName, "powershell") || strings.Contains(shellName, "powershell") { - send = "echo \"`\"" + send = "echo `" expect = `` } else { send = `echo ^` From 73fec747e75e77c117022bb4841e49d3ebc08f79 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Fri, 30 Aug 2024 10:46:07 -0700 Subject: [PATCH 039/440] Try another expect string --- internal/testhelpers/e2e/spawn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testhelpers/e2e/spawn.go b/internal/testhelpers/e2e/spawn.go index d36e97f369..1a04ae696b 100644 --- a/internal/testhelpers/e2e/spawn.go +++ b/internal/testhelpers/e2e/spawn.go @@ -75,7 +75,7 @@ func (s *SpawnedCmd) ExpectInput(opts ...termtest.SetExpectOpt) error { expect := `expect'input from posix shell` if cmdName != "bash" && shellName != "bash" && runtime.GOOS == "windows" { if strings.Contains(cmdName, "powershell") || strings.Contains(shellName, "powershell") { - send = "echo `" + send = "echo \"\"" expect = `` } else { send = `echo ^` From eb6d562d5fbba9b2803ed60c3f00be01aebc1371 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 3 Sep 2024 11:14:34 -0400 Subject: [PATCH 040/440] Add timeout for complex import test. The default timeout is too low for this test. --- test/integration/import_int_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/integration/import_int_test.go b/test/integration/import_int_test.go index b55fff639f..7fd0e0d9ea 100644 --- a/test/integration/import_int_test.go +++ b/test/integration/import_int_test.go @@ -6,6 +6,9 @@ import ( "path/filepath" "strings" "testing" + "time" + + "github.com/ActiveState/termtest" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/e2e" @@ -122,7 +125,7 @@ func (suite *ImportIntegrationTestSuite) TestImport() { cp.ExpectExitCode(0) cp = ts.Spawn("import", "requirements.txt") - cp.ExpectExitCode(0) + cp.ExpectExitCode(0, termtest.OptExpectTimeout(30*time.Second)) cp = ts.Spawn("packages") cp.Expect("coverage") From ae4a281c76788a9491e6fd681308849edae224a6 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 3 Sep 2024 12:02:35 -0400 Subject: [PATCH 041/440] Fixed macOS init integration test to infer language from a non-Camel project. --- test/integration/init_int_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/init_int_test.go b/test/integration/init_int_test.go index 61ff75ed21..cfbf5ae830 100644 --- a/test/integration/init_int_test.go +++ b/test/integration/init_int_test.go @@ -123,11 +123,11 @@ func (suite *InitIntegrationTestSuite) TestInit_InferLanguageFromUse() { cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") cp.ExpectExitCode(0) - cp = ts.Spawn("checkout", "ActiveState-CLI/Python3") + cp = ts.Spawn("checkout", "ActiveState-CLI/small-python") cp.Expect("Checked out project") cp.ExpectExitCode(0) - cp = ts.Spawn("use", "Python3") + cp = ts.Spawn("use", "small-python") cp.Expect("Switched to project", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) From 471f5097ac2988eccab36b90b95e14e711ebf768 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 3 Sep 2024 09:57:05 -0700 Subject: [PATCH 042/440] Remove maybe option --- .../test/integration/installer_int_test.go | 9 ++--- internal/testhelpers/e2e/session.go | 14 -------- internal/testhelpers/e2e/session_unix.go | 18 ++++++++++ internal/testhelpers/e2e/session_windows.go | 36 +++++++++++++++++++ internal/testhelpers/e2e/spawn.go | 11 ------ test/integration/activate_int_test.go | 25 ++++++------- test/integration/shell_int_test.go | 2 +- 7 files changed, 69 insertions(+), 46 deletions(-) diff --git a/cmd/state-installer/test/integration/installer_int_test.go b/cmd/state-installer/test/integration/installer_int_test.go index b3231d0165..82c9fb3a9a 100644 --- a/cmd/state-installer/test/integration/installer_int_test.go +++ b/cmd/state-installer/test/integration/installer_int_test.go @@ -39,12 +39,11 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { suite.NoError(err) // Run installer with source-path flag (ie. install from this local path) - cp := ts.SpawnCmdWithOpts( + cp := ts.SpawnCmdInsideShellWithOpts( suite.installerExe, e2e.OptArgs(installationDir(ts), "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=false"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.OverwriteDefaultSystemPathEnvVarName, dir)), - e2e.OptMaybeRunInsideShell(), ) // Assert output @@ -57,12 +56,11 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { cp.ExpectExitCode(0) // Ensure installing overtop doesn't result in errors - cp = ts.SpawnCmdWithOpts( + cp = ts.SpawnCmdInsideShellWithOpts( suite.installerExe, e2e.OptArgs(installationDir(ts), "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=false"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.OverwriteDefaultSystemPathEnvVarName, dir)), - e2e.OptMaybeRunInsideShell(), ) cp.Expect("successfully installed") cp.ExpectInput() @@ -173,12 +171,11 @@ func (suite *InstallerIntegrationTestSuite) TestInstallErrorTips() { dir, err := os.MkdirTemp("", "system*") suite.NoError(err) - cp := ts.SpawnCmdWithOpts( + cp := ts.SpawnCmdInsideShellWithOpts( suite.installerExe, e2e.OptArgs(installationDir(ts), "--activate", "ActiveState-CLI/Python3", "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=true"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.OverwriteDefaultSystemPathEnvVarName, dir)), - e2e.OptMaybeRunInsideShell(), ) cp.ExpectInput(e2e.RuntimeSourcingTimeoutOpt) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 7db033dec0..f84b71007f 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -234,20 +234,6 @@ func (s *Session) SpawnShellWithOpts(shell Shell, opts ...SpawnOptSetter) *Spawn return s.SpawnCmdWithOpts(string(shell), opts...) } -// SpawnShell spawns the state tool executable to be tested with arguments. -// This function differs from Spawn in that it runs the command in a shell on Windows CI. -// Our Windows integration tests are run on bash. Due to the way the PTY library runs a new -// command we need to run the command inside a shell in order to setup the correct process -// tree. Without this integration tests run in bash will incorrectly identify the partent shell -// as bash, rather than the actual shell that is running the command -func (s *Session) SpawnShell(args ...string) *SpawnedCmd { - opts := []SpawnOptSetter{OptArgs(args...)} - if runtime.GOOS == "windows" && condition.OnCI() { - opts = append(opts, OptRunInsideShell(true)) - } - return s.SpawnCmdWithOpts(s.Exe, opts...) -} - // SpawnCmdWithOpts executes an executable in a pseudo-terminal for integration tests // Arguments and other parameters can be specified by specifying SpawnOptSetter func (s *Session) SpawnCmdWithOpts(exe string, optSetters ...SpawnOptSetter) *SpawnedCmd { diff --git a/internal/testhelpers/e2e/session_unix.go b/internal/testhelpers/e2e/session_unix.go index 970a8fbec0..4e5493e68c 100644 --- a/internal/testhelpers/e2e/session_unix.go +++ b/internal/testhelpers/e2e/session_unix.go @@ -13,3 +13,21 @@ var ( RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) ) + +// SpawnInsideShell spawns the state tool executable to be tested with arguments. +// On Unix systems, this function is equivalent to Spawn. +func (s *Session) SpawnInsideShell(args ...string) *SpawnedCmd { + return s.SpawnCmd(s.Exe, args...) +} + +// SpawnCmdInsideShellWithOpts spawns the state tool executable to be tested with arguments and options. +// On Unix systems, this function is equivalent to SpawnCmdWithOpts. +func (s *Session) SpawnCmdInsideShellWithOpts(exe string, opts ...SpawnOptSetter) *SpawnedCmd { + return s.SpawnCmdWithOpts(exe, opts...) +} + +// SpawnInsideShell spawns the state tool executable to be tested with arguments. +// On Unix systems, this function is equivalent to SpawnWithOpts. +func (s *Session) SpawnInsideShellWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { + return s.SpawnCmdWithOpts(s.Exe, opts...) +} diff --git a/internal/testhelpers/e2e/session_windows.go b/internal/testhelpers/e2e/session_windows.go index 6c1830eadd..dd76f1e5d7 100644 --- a/internal/testhelpers/e2e/session_windows.go +++ b/internal/testhelpers/e2e/session_windows.go @@ -4,8 +4,10 @@ package e2e import ( + "runtime" "time" + "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/termtest" ) @@ -13,3 +15,37 @@ var ( RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) ) + +// SpawnInsideShell spawns the state tool executable to be tested with arguments. +// This function differs from Spawn in that it runs the command in a shell on Windows CI. +// Our Windows integration tests are run on bash. Due to the way the PTY library runs a new +// command we need to run the command inside a shell in order to setup the correct process +// tree. Without this integration tests run in bash will incorrectly identify the partent shell +// as bash, rather than the actual shell that is running the command +func (s *Session) SpawnInsideShell(args ...string) *SpawnedCmd { + opts := []SpawnOptSetter{OptArgs(args...)} + if runtime.GOOS == "windows" && condition.OnCI() { + opts = append(opts, OptRunInsideShell(true)) + } + return s.SpawnCmdWithOpts(s.Exe, opts...) +} + +// SpawnCmdInsideShellWithOpts spawns the executable to be tested with arguments and options. +// This function differs from SpawnCmdWithOpts in that it runs the command in a shell on Windows CI. +// See SpawnInsideShell for more information. +func (s *Session) SpawnCmdInsideShellWithOpts(exe string, opts ...SpawnOptSetter) *SpawnedCmd { + if runtime.GOOS == "windows" && condition.OnCI() { + opts = append(opts, OptRunInsideShell(true)) + } + return s.SpawnCmdWithOpts(exe, opts...) +} + +// SpawnInsideShell spawns the state tool executable to be tested with arguments. +// This function differs from SpawnWithOpts in that it runs the command in a shell on Windows CI. +// See SpawnInsideShell for more information. +func (s *Session) SpawnInsideShellWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { + if runtime.GOOS == "windows" && condition.OnCI() { + opts = append(opts, OptRunInsideShell(true)) + } + return s.SpawnCmdWithOpts(s.Exe, opts...) +} diff --git a/internal/testhelpers/e2e/spawn.go b/internal/testhelpers/e2e/spawn.go index 1a04ae696b..fff1cd25b8 100644 --- a/internal/testhelpers/e2e/spawn.go +++ b/internal/testhelpers/e2e/spawn.go @@ -10,7 +10,6 @@ import ( "github.com/ActiveState/termtest" - "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/osutils" ) @@ -181,13 +180,3 @@ func OptRunInsideShell(v bool) SpawnOptSetter { opts.RunInsideShell = v } } - -func OptMaybeRunInsideShell() SpawnOptSetter { - if runtime.GOOS == "windows" && condition.OnCI() { - return func(opts *SpawnOpts) { - opts.RunInsideShell = true - } - } - - return func(opts *SpawnOpts) {} -} diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index e5a9843e04..24057ad6b0 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -54,7 +54,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateWithoutRuntime() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnShell("activate", "ActiveState-CLI/Empty") + cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/Empty") cp.Expect("Activated") cp.ExpectInput() @@ -134,7 +134,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateUsingCommitID() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnShell("activate", "ActiveState-CLI/Empty#6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", "--path", ts.Dirs.Work) + cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/Empty#6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", "--path", ts.Dirs.Work) cp.Expect("Activated") cp.ExpectInput() @@ -149,7 +149,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateNotOnPath() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnShell("activate", "activestate-cli/empty", "--path", ts.Dirs.Work) + cp := ts.SpawnInsideShell("activate", "activestate-cli/empty", "--path", ts.Dirs.Work) cp.Expect("Activated") cp.ExpectInput() @@ -177,7 +177,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePythonByHostOnly() { defer close() projectName := "Python-LinuxWorks" - cp := ts.SpawnShell("activate", "cli-integration-tests/"+projectName, "--path="+ts.Dirs.Work) + cp := ts.SpawnInsideShell("activate", "cli-integration-tests/"+projectName, "--path="+ts.Dirs.Work) if runtime.GOOS == "linux" { cp.Expect("Creating a Virtual Environment") @@ -218,10 +218,9 @@ func (suite *ActivateIntegrationTestSuite) activatePython(version string, extraE namespace := "ActiveState-CLI/Python" + version - cp := ts.SpawnWithOpts( + cp := ts.SpawnInsideShellWithOpts( e2e.OptArgs("activate", namespace), e2e.OptAppendEnv(extraEnv...), - e2e.OptMaybeRunInsideShell(), ) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) @@ -295,7 +294,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_PythonPath() { namespace := "ActiveState-CLI/Python3" - cp := ts.SpawnShell("activate", namespace) + cp := ts.SpawnInsideShell("activate", namespace) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) // ensure that shell is functional @@ -335,10 +334,9 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_SpaceInCacheDir() { err := fileutils.MkdirUnlessExists(cacheDir) suite.Require().NoError(err) - cp := ts.SpawnWithOpts( + cp := ts.SpawnInsideShellWithOpts( e2e.OptArgs("activate", "ActiveState-CLI/Python3"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.CacheEnvVarName, cacheDir)), - e2e.OptMaybeRunInsideShell(), ) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) @@ -360,7 +358,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePerlCamel() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnShell("activate", "ActiveState-CLI/Perl") + cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/Perl") cp.Expect("Downloading", termtest.OptExpectTimeout(40*time.Second)) cp.Expect("Installing", termtest.OptExpectTimeout(140*time.Second)) @@ -401,10 +399,9 @@ version: %s ts.PrepareCommitIdFile("6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8") // Activate in the subdirectory - c2 := ts.SpawnWithOpts( + c2 := ts.SpawnInsideShellWithOpts( e2e.OptArgs("activate"), e2e.OptWD(filepath.Join(ts.Dirs.Work, "foo", "bar", "baz")), - e2e.OptMaybeRunInsideShell(), ) c2.Expect("Activated") @@ -477,7 +474,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_FromCache() { // Note: cannot use Empty project since we need artifacts to download and install. // Pick the langless project, which just has some small, non-language artifacts. - cp := ts.SpawnShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) + cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) suite.assertCompletedStatusBarReport(cp.Output()) @@ -485,7 +482,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_FromCache() { cp.ExpectExitCode(0) // next activation is cached - cp = ts.SpawnShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) + cp = ts.SpawnInsideShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) cp.ExpectInput(e2e.RuntimeSourcingTimeoutOpt) cp.SendLine("exit") diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index 8faf38257f..9fbe4365ea 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -466,7 +466,7 @@ events:`, lang, splat), 1) suite.Require().NoError(fileutils.WriteFile(asyFilename, []byte(contents))) // Verify that running a script as a command with an argument containing special characters works. - cp = ts.SpawnShell("shell") + cp = ts.SpawnInsideShell("shell") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectInput() cp.SendLine(`args "<3"`) From 8d8234f919c18a4a5de98808f5f6ad574916dce1 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 3 Sep 2024 11:52:03 -0700 Subject: [PATCH 043/440] Use env var --- .../test/integration/installer_int_test.go | 6 ++-- internal/constants/constants.go | 3 ++ internal/subshell/subshell.go | 11 ++++-- internal/testhelpers/e2e/env_windows.go | 8 ++++- internal/testhelpers/e2e/session_unix.go | 18 ---------- internal/testhelpers/e2e/session_windows.go | 36 ------------------- test/integration/activate_int_test.go | 22 ++++++------ test/integration/shell_int_test.go | 2 +- 8 files changed, 34 insertions(+), 72 deletions(-) diff --git a/cmd/state-installer/test/integration/installer_int_test.go b/cmd/state-installer/test/integration/installer_int_test.go index 82c9fb3a9a..1f83eaa242 100644 --- a/cmd/state-installer/test/integration/installer_int_test.go +++ b/cmd/state-installer/test/integration/installer_int_test.go @@ -39,7 +39,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { suite.NoError(err) // Run installer with source-path flag (ie. install from this local path) - cp := ts.SpawnCmdInsideShellWithOpts( + cp := ts.SpawnCmdWithOpts( suite.installerExe, e2e.OptArgs(installationDir(ts), "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=false"), @@ -56,7 +56,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { cp.ExpectExitCode(0) // Ensure installing overtop doesn't result in errors - cp = ts.SpawnCmdInsideShellWithOpts( + cp = ts.SpawnCmdWithOpts( suite.installerExe, e2e.OptArgs(installationDir(ts), "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=false"), @@ -171,7 +171,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallErrorTips() { dir, err := os.MkdirTemp("", "system*") suite.NoError(err) - cp := ts.SpawnCmdInsideShellWithOpts( + cp := ts.SpawnCmdWithOpts( suite.installerExe, e2e.OptArgs(installationDir(ts), "--activate", "ActiveState-CLI/Python3", "-n"), e2e.OptAppendEnv(constants.DisableUpdates+"=true"), diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 2433f9c5af..34718abd9c 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -490,3 +490,6 @@ const OverrideSandbox = "ACTIVESTATE_TEST_OVERRIDE_SANDBOX" // PlatformPrivateNamespace is the namespace for private packages. const PlatformPrivateNamespace = "private" + +// OverrideShellEnvVarName is the environment variable to set when overriding the shell for shell detection. +const OverrideShellEnvVarName = "ACTIVESTATE_CLI_SHELL_OVERRIDE" diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index b95e8713e8..67fb4d9111 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -8,6 +8,7 @@ import ( "runtime" "strings" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/shirou/gopsutil/v3/process" @@ -182,8 +183,14 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } }() - binary = detectShellParent() - logging.Debug("Configured shell: %s", binary) + if os.Getenv(constants.OverrideShellEnvVarName) != "" { + binary = os.Getenv(constants.OverrideShellEnvVarName) + } + + if binary == "" { + binary = detectShellParent() + } + if binary == "" { binary = configured } diff --git a/internal/testhelpers/e2e/env_windows.go b/internal/testhelpers/e2e/env_windows.go index 0c7b72aeec..c91d1efe39 100644 --- a/internal/testhelpers/e2e/env_windows.go +++ b/internal/testhelpers/e2e/env_windows.go @@ -17,7 +17,7 @@ const ( ) func platformSpecificEnv(dirs *Dirs) []string { - return []string{ + env := []string{ "SystemDrive=C:", "SystemRoot=C:\\Windows", "PROGRAMFILES=C:\\Program Files", @@ -39,6 +39,12 @@ func platformSpecificEnv(dirs *Dirs) []string { fmt.Sprintf("LOCALAPPDATA=%s", dirs.TempDir), fmt.Sprintf("%s=true", constants.DisableActivateEventsEnvVarName), } + + if condition.OnCI() { + env = append(env, fmt.Sprintf("%s=cmd.exe", constants.OverrideShellEnvVarName)) + } + + return env } func platformPath() string { diff --git a/internal/testhelpers/e2e/session_unix.go b/internal/testhelpers/e2e/session_unix.go index 4e5493e68c..970a8fbec0 100644 --- a/internal/testhelpers/e2e/session_unix.go +++ b/internal/testhelpers/e2e/session_unix.go @@ -13,21 +13,3 @@ var ( RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) ) - -// SpawnInsideShell spawns the state tool executable to be tested with arguments. -// On Unix systems, this function is equivalent to Spawn. -func (s *Session) SpawnInsideShell(args ...string) *SpawnedCmd { - return s.SpawnCmd(s.Exe, args...) -} - -// SpawnCmdInsideShellWithOpts spawns the state tool executable to be tested with arguments and options. -// On Unix systems, this function is equivalent to SpawnCmdWithOpts. -func (s *Session) SpawnCmdInsideShellWithOpts(exe string, opts ...SpawnOptSetter) *SpawnedCmd { - return s.SpawnCmdWithOpts(exe, opts...) -} - -// SpawnInsideShell spawns the state tool executable to be tested with arguments. -// On Unix systems, this function is equivalent to SpawnWithOpts. -func (s *Session) SpawnInsideShellWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { - return s.SpawnCmdWithOpts(s.Exe, opts...) -} diff --git a/internal/testhelpers/e2e/session_windows.go b/internal/testhelpers/e2e/session_windows.go index dd76f1e5d7..6c1830eadd 100644 --- a/internal/testhelpers/e2e/session_windows.go +++ b/internal/testhelpers/e2e/session_windows.go @@ -4,10 +4,8 @@ package e2e import ( - "runtime" "time" - "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/termtest" ) @@ -15,37 +13,3 @@ var ( RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) ) - -// SpawnInsideShell spawns the state tool executable to be tested with arguments. -// This function differs from Spawn in that it runs the command in a shell on Windows CI. -// Our Windows integration tests are run on bash. Due to the way the PTY library runs a new -// command we need to run the command inside a shell in order to setup the correct process -// tree. Without this integration tests run in bash will incorrectly identify the partent shell -// as bash, rather than the actual shell that is running the command -func (s *Session) SpawnInsideShell(args ...string) *SpawnedCmd { - opts := []SpawnOptSetter{OptArgs(args...)} - if runtime.GOOS == "windows" && condition.OnCI() { - opts = append(opts, OptRunInsideShell(true)) - } - return s.SpawnCmdWithOpts(s.Exe, opts...) -} - -// SpawnCmdInsideShellWithOpts spawns the executable to be tested with arguments and options. -// This function differs from SpawnCmdWithOpts in that it runs the command in a shell on Windows CI. -// See SpawnInsideShell for more information. -func (s *Session) SpawnCmdInsideShellWithOpts(exe string, opts ...SpawnOptSetter) *SpawnedCmd { - if runtime.GOOS == "windows" && condition.OnCI() { - opts = append(opts, OptRunInsideShell(true)) - } - return s.SpawnCmdWithOpts(exe, opts...) -} - -// SpawnInsideShell spawns the state tool executable to be tested with arguments. -// This function differs from SpawnWithOpts in that it runs the command in a shell on Windows CI. -// See SpawnInsideShell for more information. -func (s *Session) SpawnInsideShellWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { - if runtime.GOOS == "windows" && condition.OnCI() { - opts = append(opts, OptRunInsideShell(true)) - } - return s.SpawnCmdWithOpts(s.Exe, opts...) -} diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index 24057ad6b0..d91ed46918 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -54,7 +54,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateWithoutRuntime() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/Empty") + cp := ts.Spawn("activate", "ActiveState-CLI/Empty") cp.Expect("Activated") cp.ExpectInput() @@ -134,7 +134,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateUsingCommitID() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/Empty#6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", "--path", ts.Dirs.Work) + cp := ts.Spawn("activate", "ActiveState-CLI/Empty#6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", "--path", ts.Dirs.Work) cp.Expect("Activated") cp.ExpectInput() @@ -149,7 +149,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivateNotOnPath() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnInsideShell("activate", "activestate-cli/empty", "--path", ts.Dirs.Work) + cp := ts.Spawn("activate", "activestate-cli/empty", "--path", ts.Dirs.Work) cp.Expect("Activated") cp.ExpectInput() @@ -177,7 +177,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePythonByHostOnly() { defer close() projectName := "Python-LinuxWorks" - cp := ts.SpawnInsideShell("activate", "cli-integration-tests/"+projectName, "--path="+ts.Dirs.Work) + cp := ts.Spawn("activate", "cli-integration-tests/"+projectName, "--path="+ts.Dirs.Work) if runtime.GOOS == "linux" { cp.Expect("Creating a Virtual Environment") @@ -218,7 +218,7 @@ func (suite *ActivateIntegrationTestSuite) activatePython(version string, extraE namespace := "ActiveState-CLI/Python" + version - cp := ts.SpawnInsideShellWithOpts( + cp := ts.SpawnWithOpts( e2e.OptArgs("activate", namespace), e2e.OptAppendEnv(extraEnv...), ) @@ -294,7 +294,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_PythonPath() { namespace := "ActiveState-CLI/Python3" - cp := ts.SpawnInsideShell("activate", namespace) + cp := ts.Spawn("activate", namespace) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) // ensure that shell is functional @@ -334,7 +334,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_SpaceInCacheDir() { err := fileutils.MkdirUnlessExists(cacheDir) suite.Require().NoError(err) - cp := ts.SpawnInsideShellWithOpts( + cp := ts.SpawnWithOpts( e2e.OptArgs("activate", "ActiveState-CLI/Python3"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.CacheEnvVarName, cacheDir)), ) @@ -358,7 +358,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePerlCamel() { close := suite.addForegroundSvc(ts) defer close() - cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/Perl") + cp := ts.Spawn("activate", "ActiveState-CLI/Perl") cp.Expect("Downloading", termtest.OptExpectTimeout(40*time.Second)) cp.Expect("Installing", termtest.OptExpectTimeout(140*time.Second)) @@ -399,7 +399,7 @@ version: %s ts.PrepareCommitIdFile("6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8") // Activate in the subdirectory - c2 := ts.SpawnInsideShellWithOpts( + c2 := ts.SpawnWithOpts( e2e.OptArgs("activate"), e2e.OptWD(filepath.Join(ts.Dirs.Work, "foo", "bar", "baz")), ) @@ -474,7 +474,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_FromCache() { // Note: cannot use Empty project since we need artifacts to download and install. // Pick the langless project, which just has some small, non-language artifacts. - cp := ts.SpawnInsideShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) + cp := ts.Spawn("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) suite.assertCompletedStatusBarReport(cp.Output()) @@ -482,7 +482,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivate_FromCache() { cp.ExpectExitCode(0) // next activation is cached - cp = ts.SpawnInsideShell("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) + cp = ts.Spawn("activate", "ActiveState-CLI/langless", "--path", ts.Dirs.Work) cp.ExpectInput(e2e.RuntimeSourcingTimeoutOpt) cp.SendLine("exit") diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index 9fbe4365ea..77183146e1 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -466,7 +466,7 @@ events:`, lang, splat), 1) suite.Require().NoError(fileutils.WriteFile(asyFilename, []byte(contents))) // Verify that running a script as a command with an argument containing special characters works. - cp = ts.SpawnInsideShell("shell") + cp = ts.Spawn("shell") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectInput() cp.SendLine(`args "<3"`) From 77c970fc6f8400e35125f88ad0e9d3225af0960f Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 3 Sep 2024 13:44:00 -0700 Subject: [PATCH 044/440] Fix shells tests --- test/integration/shell_int_test.go | 6 +++++- test/integration/shells_int_test.go | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index 77183146e1..43fe711297 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -489,7 +489,11 @@ func (suite *ShellIntegrationTestSuite) TestWindowsShells() { hostname, err := os.Hostname() suite.Require().NoError(err) - cp := ts.SpawnCmd("cmd", "/C", "state", "shell") + cp := ts.SpawnCmdWithOpts( + "cmd", + e2e.OptArgs("/C", "state", "shell"), + e2e.OptAppendEnv(constants.OverrideShellEnvVarName+"="), + ) cp.ExpectInput() cp.SendLine("hostname") cp.Expect(hostname) // cmd.exe shows the actual hostname diff --git a/test/integration/shells_int_test.go b/test/integration/shells_int_test.go index f75211b204..82a7ec5c9f 100644 --- a/test/integration/shells_int_test.go +++ b/test/integration/shells_int_test.go @@ -6,6 +6,7 @@ import ( "runtime" "testing" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/fileutils" @@ -48,7 +49,10 @@ func (suite *ShellsIntegrationTestSuite) TestShells() { } // Run the checkout in a particular shell. - cp = ts.SpawnShellWithOpts(shell) + cp = ts.SpawnShellWithOpts( + shell, + e2e.OptAppendEnv(constants.OverrideShellEnvVarName+"="), + ) cp.SendLine(e2e.QuoteCommand(shell, ts.ExecutablePath(), "checkout", "ActiveState-CLI/small-python", string(shell))) cp.Expect("Checked out project") cp.SendLine("exit") @@ -58,7 +62,10 @@ func (suite *ShellsIntegrationTestSuite) TestShells() { // There are 2 or more instances checked out, so we should get a prompt in whichever shell we // use. - cp = ts.SpawnShellWithOpts(shell) + cp = ts.SpawnShellWithOpts( + shell, + e2e.OptAppendEnv(constants.OverrideShellEnvVarName+"="), + ) cp.SendLine(e2e.QuoteCommand(shell, ts.ExecutablePath(), "shell", "small-python")) cp.Expect("Multiple project paths") From b0886eaf76831d2e11f758f80fc1937733f87f66 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 3 Sep 2024 14:10:29 -0700 Subject: [PATCH 045/440] Shells test fix --- test/integration/shell_int_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index 43fe711297..a82050f157 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -500,7 +500,11 @@ func (suite *ShellIntegrationTestSuite) TestWindowsShells() { cp.SendLine("exit") cp.ExpectExitCode(0) - cp = ts.SpawnCmd("powershell", "-Command", "state", "shell") + cp = ts.SpawnCmdWithOpts( + "powershell", + e2e.OptArgs("-Command", "state", "shell"), + e2e.OptAppendEnv(constants.OverrideShellEnvVarName+"="), + ) cp.ExpectInput() cp.SendLine("$host.name") cp.Expect("ConsoleHost") // powershell always shows ConsoleHost, go figure From bc1ed84cca88c0cb626213c7490ff0789aacf4b4 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 3 Sep 2024 18:00:55 -0400 Subject: [PATCH 046/440] Show short CVE warning if not prompting. --- internal/locale/locales/en-us.yaml | 11 +++++ internal/runbits/cves/cves.go | 64 +++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 62aa856235..b14186a7e4 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1207,10 +1207,21 @@ warning_vulnerable_indirectonly: warning_vulnerable_directonly: other: | [ERROR]Warning: Dependency has {{.V0}} known vulnerabilities (CVEs)[/RESET] +cve_critical: + other: Critical +cve_high: + other: High +cve_medium: + other: Medium +cve_low: + other: Low more_info_vulnerabilities: other: For more information on these vulnerabilities run '[ACTIONABLE]state security open [/RESET]'. disable_prompting_vulnerabilities: other: To disable prompting for vulnerabilities run '[ACTIONABLE]state config set security.prompt.enabled false[/RESET]'. +warning_vulnerable_short: + other: | + [ERROR]Warning:[/RESET] Dependency has [ERROR]{{.V0}} known vulnerabilities (CVEs)[/RESET]. Severity: {{.V1}}. Run '[ACTIONABLE]state security[/RESET]' for more info. prompt_continue_pkg_operation: other: | Do you want to continue installing this dependency despite its vulnerabilities? diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index 53276b13de..e3cdf029e2 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -91,23 +91,26 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil pg = nil vulnerabilities := model.CombineVulnerabilities(ingredientVulnerabilities, names...) - c.summarizeCVEs(vulnerabilities) - if c.prime.Prompt() != nil && c.shouldPromptForSecurity(vulnerabilities) { - cont, err := c.promptForSecurity() - if err != nil { - return errs.Wrap(err, "Failed to prompt for security") - } + if c.prime.Prompt() == nil || !c.shouldPromptForSecurity(vulnerabilities) { + c.warnCVEs(vulnerabilities) + return nil + } - if !cont { - if !c.prime.Prompt().IsInteractive() { - return errs.AddTips( - locale.NewInputError("err_pkgop_security_prompt", "Operation aborted due to security prompt"), - locale.Tl("more_info_prompt", "To disable security prompting run: [ACTIONABLE]state config set security.prompt.enabled false[/RESET]"), - ) - } - return locale.NewInputError("err_pkgop_security_prompt", "Operation aborted due to security prompt") + c.summarizeCVEs(vulnerabilities) + cont, err := c.promptForSecurity() + if err != nil { + return errs.Wrap(err, "Failed to prompt for security") + } + + if !cont { + if !c.prime.Prompt().IsInteractive() { + return errs.AddTips( + locale.NewInputError("err_pkgop_security_prompt", "Operation aborted due to security prompt"), + locale.Tl("more_info_prompt", "To disable security prompting run: [ACTIONABLE]state config set security.prompt.enabled false[/RESET]"), + ) } + return locale.NewInputError("err_pkgop_security_prompt", "Operation aborted due to security prompt") } return nil @@ -153,6 +156,31 @@ func (c *CveReport) shouldPromptForSecurity(vulnerabilities model.VulnerableIngr return false } +func (c *CveReport) warnCVEs(vulnerabilities model.VulnerableIngredientsByLevels) { + if vulnerabilities.Count == 0 { + return + } + + c.prime.Output().Notice("") + + counts := []string{} + formatString := "%d [%s]%s[/RESET]" + if count := vulnerabilities.Critical.Count; count > 0 { + counts = append(counts, fmt.Sprintf(formatString, count, "RED", locale.T("cve_critical"))) + } + if count := vulnerabilities.High.Count; count > 0 { + counts = append(counts, fmt.Sprintf(formatString, count, "ORANGE", locale.T("cve_high"))) + } + if count := vulnerabilities.Medium.Count; count > 0 { + counts = append(counts, fmt.Sprintf(formatString, count, "YELLOW", locale.T("cve_medium"))) + } + if count := vulnerabilities.Low.Count; count > 0 { + counts = append(counts, fmt.Sprintf(formatString, count, "MAGENTA", locale.T("cve_low"))) + } + + c.prime.Output().Notice(" " + locale.Tr("warning_vulnerable_short", strconv.Itoa(vulnerabilities.Count), strings.Join(counts, ", "))) +} + func (c *CveReport) summarizeCVEs(vulnerabilities model.VulnerableIngredientsByLevels) { out := c.prime.Output() out.Print("") @@ -180,10 +208,10 @@ func (c *CveReport) summarizeCVEs(vulnerabilities model.VulnerableIngredientsByL } } - printVulnerabilities(vulnerabilities.Critical, locale.Tl("cve_critical", "Critical"), "RED") - printVulnerabilities(vulnerabilities.High, locale.Tl("cve_high", "High"), "ORANGE") - printVulnerabilities(vulnerabilities.Medium, locale.Tl("cve_medium", "Medium"), "YELLOW") - printVulnerabilities(vulnerabilities.Low, locale.Tl("cve_low", "Low"), "MAGENTA") + printVulnerabilities(vulnerabilities.Critical, locale.T("cve_critical"), "RED") + printVulnerabilities(vulnerabilities.High, locale.T("cve_high"), "ORANGE") + printVulnerabilities(vulnerabilities.Medium, locale.T("cve_medium"), "YELLOW") + printVulnerabilities(vulnerabilities.Low, locale.T("cve_low"), "MAGENTA") out.Print("") out.Print(" " + locale.T("more_info_vulnerabilities")) From 8af3168b4cf584066dfdf726e83ee26164a28f31 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 3 Sep 2024 15:06:44 -0700 Subject: [PATCH 047/440] Remove opt --- test/integration/deploy_int_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/deploy_int_test.go b/test/integration/deploy_int_test.go index 28b2621daf..f51e686baa 100644 --- a/test/integration/deploy_int_test.go +++ b/test/integration/deploy_int_test.go @@ -168,7 +168,6 @@ func (suite *DeployIntegrationTestSuite) TestDeployPython() { "cmd.exe", e2e.OptArgs("/k", filepath.Join(targetPath, "bin", "shell.bat")), e2e.OptAppendEnv("PATHEXT=.COM;.EXE;.BAT;.LNK", "SHELL="), - e2e.OptRunInsideShell(true), ) } else { cp = ts.SpawnCmdWithOpts( From e2df1a77ad085e98a0ba6746d253f7f62a62750d Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 3 Sep 2024 15:07:14 -0700 Subject: [PATCH 048/440] Remove logging calls --- internal/subshell/subshell.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 67fb4d9111..6f5e8d21c6 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -245,7 +245,6 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } func detectShellParent() string { - logging.Debug("Detecting shell from parent process") p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { logging.Error("Failed to get parent process: %v", err) @@ -254,7 +253,6 @@ func detectShellParent() string { for p != nil && p.Pid != 0 { name, err := p.Name() if err == nil { - logging.Debug("Searching for supported shell in parent process: %s", name) if supportedShellName(name) { return name } From fc8b956f3b0a30a3840800e5e982b6b469beae56 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 3 Sep 2024 18:34:17 -0400 Subject: [PATCH 049/440] Mark `state export log` as stable. --- cmd/state/internal/cmdtree/export.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/state/internal/cmdtree/export.go b/cmd/state/internal/cmdtree/export.go index 2d027e8788..6b20433dc9 100644 --- a/cmd/state/internal/cmdtree/export.go +++ b/cmd/state/internal/cmdtree/export.go @@ -197,7 +197,6 @@ func newExportLogCommand(prime *primer.Values) *captain.Command { }) cmd.SetSupportsStructuredOutput() - cmd.SetUnstable(true) return cmd } From e6eeb7bc2c48fa050a511dd77f4ff360608011ea Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 14:59:42 -0700 Subject: [PATCH 050/440] Fix env var name --- internal/subshell/subshell.go | 2 +- internal/subshell/subshell_lin_mac.go | 2 +- internal/subshell/subshell_win.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 6f5e8d21c6..48bb2f3bb3 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -200,7 +200,7 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } if binary == "" { - binary = OS_DEFULAT + binary = OS_DEFAULT } path := resolveBinaryPath(binary) diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index fe66d31c41..4a1dc705c3 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -21,7 +21,7 @@ var supportedShells = []SubShell{ const ( SHELL_ENV_VAR = "SHELL" - OS_DEFULAT = "bash" + OS_DEFAULT = "bash" ) func supportedShellName(name string) bool { diff --git a/internal/subshell/subshell_win.go b/internal/subshell/subshell_win.go index 6ad787901f..7775acbee5 100644 --- a/internal/subshell/subshell_win.go +++ b/internal/subshell/subshell_win.go @@ -19,7 +19,7 @@ var supportedShells = []SubShell{ const ( SHELL_ENV_VAR = "COMSPEC" - OS_DEFULAT = "cmd.exe" + OS_DEFAULT = "cmd.exe" ) func supportedShellName(name string) bool { From b6ddb92d259d9b6b8a1019037f52d520bfbab967 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 15:00:41 -0700 Subject: [PATCH 051/440] Make shell name comparison case-insensitive --- internal/subshell/subshell_lin_mac.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index 4a1dc705c3..2bb9f62520 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -4,6 +4,8 @@ package subshell import ( + "strings" + "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/cmd" "github.com/ActiveState/cli/internal/subshell/fish" @@ -26,7 +28,7 @@ const ( func supportedShellName(name string) bool { for _, subshell := range supportedShells { - if name == subshell.Shell() { + if strings.EqualFold(name, subshell.Shell()) { return true } } From 7907c120c3b6d3b0ed5f35768aaaf6a2d7d13514 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 15:04:52 -0700 Subject: [PATCH 052/440] Handle filepaths --- internal/subshell/subshell.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 48bb2f3bb3..361293e4b8 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -4,6 +4,7 @@ import ( "errors" "os" "os/exec" + "path" "path/filepath" "runtime" "strings" @@ -253,6 +254,9 @@ func detectShellParent() string { for p != nil && p.Pid != 0 { name, err := p.Name() if err == nil { + if strings.Contains(name, string(filepath.Separator)) { + name = path.Base(name) + } if supportedShellName(name) { return name } From 723b52091b423dc5aa3631342d1fdc20a171ea86 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 15:06:25 -0700 Subject: [PATCH 053/440] Update argument name Handle case insensitivitiy --- internal/subshell/subshell_lin_mac.go | 4 ++-- internal/subshell/subshell_win.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/subshell/subshell_lin_mac.go b/internal/subshell/subshell_lin_mac.go index 2bb9f62520..9a666e16cb 100644 --- a/internal/subshell/subshell_lin_mac.go +++ b/internal/subshell/subshell_lin_mac.go @@ -26,9 +26,9 @@ const ( OS_DEFAULT = "bash" ) -func supportedShellName(name string) bool { +func supportedShellName(filename string) bool { for _, subshell := range supportedShells { - if strings.EqualFold(name, subshell.Shell()) { + if strings.EqualFold(filename, subshell.Shell()) { return true } } diff --git a/internal/subshell/subshell_win.go b/internal/subshell/subshell_win.go index 7775acbee5..b0332a8008 100644 --- a/internal/subshell/subshell_win.go +++ b/internal/subshell/subshell_win.go @@ -5,6 +5,7 @@ package subshell import ( "fmt" + "strings" "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/cmd" @@ -22,9 +23,9 @@ const ( OS_DEFAULT = "cmd.exe" ) -func supportedShellName(name string) bool { +func supportedShellName(filename string) bool { for _, subshell := range supportedShells { - if name == fmt.Sprintf("%s.exe", subshell.Shell()) { + if strings.EqualFold(filename, fmt.Sprintf("%s.exe", subshell.Shell())) { return true } } From c97aadb210d9a2ac73ddb52eabd491d693d87a3d Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 15:09:36 -0700 Subject: [PATCH 054/440] Use configured first --- internal/subshell/subshell.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 361293e4b8..e167f87124 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -188,14 +188,11 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { binary = os.Getenv(constants.OverrideShellEnvVarName) } + binary = configured if binary == "" { binary = detectShellParent() } - if binary == "" { - binary = configured - } - if binary == "" { binary = os.Getenv(SHELL_ENV_VAR) } From 4cf5cdabf1ce0e89987bb1865a5471b8c7ee2931 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 15:25:15 -0700 Subject: [PATCH 055/440] Clear configured shell in test --- test/integration/shells_int_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/integration/shells_int_test.go b/test/integration/shells_int_test.go index 82a7ec5c9f..6e342afd65 100644 --- a/test/integration/shells_int_test.go +++ b/test/integration/shells_int_test.go @@ -6,8 +6,12 @@ import ( "runtime" "testing" + "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/rtutils/singlethread" + "github.com/ActiveState/cli/internal/subshell" "github.com/ActiveState/cli/internal/testhelpers/suite" + "github.com/stretchr/testify/require" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" @@ -48,6 +52,12 @@ func (suite *ShellsIntegrationTestSuite) TestShells() { suite.Require().NoError(err) } + // Clear configured shell. + cfg, err := config.NewCustom(ts.Dirs.Config, singlethread.New(), true) + suite.Require().NoError(err) + err = cfg.Set(subshell.ConfigKeyShell, "") + require.NoError(t, err) + // Run the checkout in a particular shell. cp = ts.SpawnShellWithOpts( shell, From f00d77c99d96270aedc494b9c04c94983d033fb0 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 15:58:20 -0700 Subject: [PATCH 056/440] Clear configured shell on Windows test --- test/integration/shell_int_test.go | 7 +++++++ test/integration/shells_int_test.go | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index a82050f157..b5d0114856 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -14,6 +14,7 @@ import ( "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/fileutils" + "github.com/ActiveState/cli/internal/rtutils/singlethread" "github.com/ActiveState/cli/internal/subshell" "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/sscommon" @@ -500,6 +501,12 @@ func (suite *ShellIntegrationTestSuite) TestWindowsShells() { cp.SendLine("exit") cp.ExpectExitCode(0) + // Clear configured shell. + cfg, err := config.NewCustom(ts.Dirs.Config, singlethread.New(), true) + suite.Require().NoError(err) + err = cfg.Set(subshell.ConfigKeyShell, "") + suite.Require().NoError(err) + cp = ts.SpawnCmdWithOpts( "powershell", e2e.OptArgs("-Command", "state", "shell"), diff --git a/test/integration/shells_int_test.go b/test/integration/shells_int_test.go index 6e342afd65..b2a2b3b045 100644 --- a/test/integration/shells_int_test.go +++ b/test/integration/shells_int_test.go @@ -11,7 +11,6 @@ import ( "github.com/ActiveState/cli/internal/rtutils/singlethread" "github.com/ActiveState/cli/internal/subshell" "github.com/ActiveState/cli/internal/testhelpers/suite" - "github.com/stretchr/testify/require" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" @@ -56,7 +55,7 @@ func (suite *ShellsIntegrationTestSuite) TestShells() { cfg, err := config.NewCustom(ts.Dirs.Config, singlethread.New(), true) suite.Require().NoError(err) err = cfg.Set(subshell.ConfigKeyShell, "") - require.NoError(t, err) + suite.Require().NoError(err) // Run the checkout in a particular shell. cp = ts.SpawnShellWithOpts( From e532c53b4a891283737aa696719ef9e3fe4faa89 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 4 Sep 2024 16:22:23 -0700 Subject: [PATCH 057/440] Revert config setting on Windows --- internal/testhelpers/e2e/session.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index f84b71007f..030e279c23 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -189,12 +189,6 @@ func new(t *testing.T, retainDirs, updatePath bool, extraEnv ...string) *Session require.NoError(session.T, err) } - if runtime.GOOS == "windows" { - if err := cfg.Set(subshell.ConfigKeyShell, "cmd.exe"); err != nil { - require.NoError(session.T, err) - } - } - return session } From df47edd559c010067cd36b44fe4cdb10d337b075 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 5 Sep 2024 09:48:13 -0700 Subject: [PATCH 058/440] Respect override --- internal/subshell/subshell.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index e167f87124..19dc6c4933 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -188,7 +188,10 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { binary = os.Getenv(constants.OverrideShellEnvVarName) } - binary = configured + if binary == "" { + binary = configured + } + if binary == "" { binary = detectShellParent() } From 4f23d47b80d56785e9bf7e3a88f90a81f5a317e1 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 5 Sep 2024 10:20:37 -0700 Subject: [PATCH 059/440] Test revert backtick change --- internal/testhelpers/e2e/spawn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testhelpers/e2e/spawn.go b/internal/testhelpers/e2e/spawn.go index fff1cd25b8..bf73ffcd22 100644 --- a/internal/testhelpers/e2e/spawn.go +++ b/internal/testhelpers/e2e/spawn.go @@ -74,7 +74,7 @@ func (s *SpawnedCmd) ExpectInput(opts ...termtest.SetExpectOpt) error { expect := `expect'input from posix shell` if cmdName != "bash" && shellName != "bash" && runtime.GOOS == "windows" { if strings.Contains(cmdName, "powershell") || strings.Contains(shellName, "powershell") { - send = "echo \"\"" + send = "echo \"`\"" expect = `` } else { send = `echo ^` From 7cb0820e3e59793bfc68297dfe2c2dcd40ea3a87 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 5 Sep 2024 11:02:22 -0700 Subject: [PATCH 060/440] Disable override for install scripts tests and clear config --- test/integration/install_scripts_int_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/integration/install_scripts_int_test.go b/test/integration/install_scripts_int_test.go index 8846c44353..87e8d5e424 100644 --- a/test/integration/install_scripts_int_test.go +++ b/test/integration/install_scripts_int_test.go @@ -7,6 +7,9 @@ import ( "runtime" "testing" + "github.com/ActiveState/cli/internal/config" + "github.com/ActiveState/cli/internal/rtutils/singlethread" + "github.com/ActiveState/cli/internal/subshell" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/stretchr/testify/require" "github.com/thoas/go-funk" @@ -104,7 +107,10 @@ func (suite *InstallScriptsIntegrationTestSuite) TestInstall() { } if runtime.GOOS == "windows" { cmd = "powershell.exe" - opts = append(opts, e2e.OptAppendEnv("SHELL=")) + opts = append(opts, + e2e.OptAppendEnv("SHELL="), + e2e.OptAppendEnv(constants.OverrideShellEnvVarName+"="), + ) } cp := ts.SpawnCmdWithOpts(cmd, opts...) cp.Expect("Preparing Installer for State Tool Package Manager") @@ -137,12 +143,19 @@ func (suite *InstallScriptsIntegrationTestSuite) TestInstall() { suite.assertAnalytics(ts) suite.DirExists(ts.Dirs.Config) + // Clear configured shell. + cfg, err := config.NewCustom(ts.Dirs.Config, singlethread.New(), true) + suite.Require().NoError(err) + err = cfg.Set(subshell.ConfigKeyShell, "") + suite.Require().NoError(err) + // Verify that can install overtop if runtime.GOOS != "windows" { cp = ts.SpawnCmdWithOpts("bash", e2e.OptArgs(argsPlain...)) } else { cp = ts.SpawnCmdWithOpts("powershell.exe", e2e.OptArgs(argsPlain...), e2e.OptAppendEnv("SHELL="), + e2e.OptAppendEnv(constants.OverrideShellEnvVarName+"="), ) } cp.Expect("successfully installed") From 396fe68200822569023cf29892072251b8090413 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 5 Sep 2024 16:07:45 -0700 Subject: [PATCH 061/440] Update internal/subshell/subshell.go Co-authored-by: Nathan Rijksen --- internal/subshell/subshell.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 19dc6c4933..4769ccda5d 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -248,7 +248,7 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { func detectShellParent() string { p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { - logging.Error("Failed to get parent process: %v", err) + logging.Error("Failed to get parent process: %s", errs.JoinMessage(err)) } for p != nil && p.Pid != 0 { From f57b755de82163ab1b387fe557a436d3dd7caa23 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 5 Sep 2024 16:10:17 -0700 Subject: [PATCH 062/440] Make configured a fallback --- internal/subshell/subshell.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index 4769ccda5d..a0a0746c9a 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -189,11 +189,11 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { } if binary == "" { - binary = configured + binary = detectShellParent() } if binary == "" { - binary = detectShellParent() + binary = configured } if binary == "" { From bc1e2eca0e129777db102da6f21c473354d6656b Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 5 Sep 2024 16:10:38 -0700 Subject: [PATCH 063/440] Make process error a multilog error --- internal/subshell/subshell.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/subshell/subshell.go b/internal/subshell/subshell.go index a0a0746c9a..8844de8e69 100644 --- a/internal/subshell/subshell.go +++ b/internal/subshell/subshell.go @@ -248,7 +248,7 @@ func DetectShell(cfg sscommon.Configurable) (string, string) { func detectShellParent() string { p, err := process.NewProcess(int32(os.Getppid())) if err != nil && !errors.As(err, ptr.To(&os.PathError{})) { - logging.Error("Failed to get parent process: %s", errs.JoinMessage(err)) + multilog.Error("Failed to get parent process: %s", errs.JoinMessage(err)) } for p != nil && p.Pid != 0 { From d859109b062b8bfb2e9dd530b6920adc339d7f2b Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 5 Sep 2024 13:39:07 -0700 Subject: [PATCH 064/440] Implemented package install runner that is self contained (not using requirement op runbit) --- cmd/state/internal/cmdtree/bundles.go | 7 +- cmd/state/internal/cmdtree/packages.go | 15 +- internal/locale/locales/en-us.yaml | 10 +- internal/runbits/cves/cves.go | 1 + internal/runbits/runtime/trigger/trigger.go | 1 + internal/runners/install/install.go | 383 ++++++++++++++++++++ internal/runners/install/rationalize.go | 99 +++++ internal/runners/packages/info.go | 2 +- internal/runners/packages/install.go | 61 ---- internal/runners/packages/search.go | 2 +- internal/runners/packages/uninstall.go | 2 +- pkg/buildscript/mutations.go | 18 +- pkg/platform/model/vcs.go | 14 +- test/integration/install_int_test.go | 4 +- 14 files changed, 529 insertions(+), 90 deletions(-) create mode 100644 internal/runners/install/install.go create mode 100644 internal/runners/install/rationalize.go delete mode 100644 internal/runners/packages/install.go diff --git a/cmd/state/internal/cmdtree/bundles.go b/cmd/state/internal/cmdtree/bundles.go index fcea913183..73e61d5175 100644 --- a/cmd/state/internal/cmdtree/bundles.go +++ b/cmd/state/internal/cmdtree/bundles.go @@ -4,6 +4,7 @@ import ( "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runners/install" "github.com/ActiveState/cli/internal/runners/packages" "github.com/ActiveState/cli/pkg/platform/model" ) @@ -43,9 +44,9 @@ func newBundlesCommand(prime *primer.Values) *captain.Command { } func newBundleInstallCommand(prime *primer.Values) *captain.Command { - runner := packages.NewInstall(prime) + runner := install.NewInstall(prime) - params := packages.InstallRunParams{} + params := install.InstallRunParams{} return captain.NewCommand( "install", @@ -62,7 +63,7 @@ func newBundleInstallCommand(prime *primer.Values) *captain.Command { }, }, func(_ *captain.Command, _ []string) error { - return runner.Run(params, model.NamespaceBundle) + return runner.Run(params) }, ).SetSupportsStructuredOutput() } diff --git a/cmd/state/internal/cmdtree/packages.go b/cmd/state/internal/cmdtree/packages.go index 39b66a1cb0..3c314c7311 100644 --- a/cmd/state/internal/cmdtree/packages.go +++ b/cmd/state/internal/cmdtree/packages.go @@ -4,6 +4,7 @@ import ( "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runners/install" "github.com/ActiveState/cli/internal/runners/packages" "github.com/ActiveState/cli/pkg/platform/model" ) @@ -49,9 +50,9 @@ func newPackagesCommand(prime *primer.Values) *captain.Command { } func newInstallCommand(prime *primer.Values) *captain.Command { - runner := packages.NewInstall(prime) + runner := install.NewInstall(prime) - params := packages.InstallRunParams{} + params := install.InstallRunParams{} var packagesRaw string cmd := captain.NewCommand( @@ -65,12 +66,6 @@ func newInstallCommand(prime *primer.Values) *captain.Command { Description: locale.T("package_flag_ts_description"), Value: ¶ms.Timestamp, }, - { - Name: "revision", - Shorthand: "r", - Description: locale.T("package_flag_rev_description"), - Value: ¶ms.Revision, - }, }, []*captain.Argument{ { @@ -83,10 +78,10 @@ func newInstallCommand(prime *primer.Values) *captain.Command { func(_ *captain.Command, args []string) error { for _, p := range args { if err := params.Packages.Set(p); err != nil { - return locale.WrapInputError(err, "err_install_packages_args", "Invalid package install arguments") + return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") } } - return runner.Run(params, model.NamespacePackage) + return runner.Run(params) }, ) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 62aa856235..4ddf73edc2 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -604,6 +604,8 @@ package_search_flag_exact-term_description: other: Ensures that search results match search term exactly package_import_flag_filename_description: other: The file to import +commit_message_added: + other: "Added: {{.V0}}" commit_message_added_package: other: "Added package: {{.V0}}@{{.V1}}" commit_message_removed_package: @@ -1155,13 +1157,13 @@ package_ingredient_alternatives_nosuggest: No results found for search term "[NOTICE]{{.V0}}[/RESET]". Run "[ACTIONABLE]state search {{.V0}}[/RESET]" to find alternatives. -package_ingredient_alternatives_nolang: +package_requirements_no_match: other: | - No results found for search term "[NOTICE]{{.V0}}[/RESET]". + No results found for following packages: {{.V0}}. - This may be because you have not installed a language for your project. Install a language by running "[ACTIONABLE]state languages install [/RESET]". + Run "[ACTIONABLE]state search {{.V0}}[/RESET]" to find alternatives. progress_search: - other: "• Searching for [ACTIONABLE]{{.V0}}[/RESET] in the ActiveState Catalog" + other: "• Searching for packages in the ActiveState Catalog" progress_cve_search: other: "• Checking for vulnerabilities (CVEs) on [ACTIONABLE]{{.V0}}[/RESET] and its dependencies" setup_runtime: diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index 53276b13de..d1965dee9c 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -196,6 +196,7 @@ func (c *CveReport) promptForSecurity() (bool, error) { if err != nil { return false, locale.WrapError(err, "err_pkgop_confirm", "Need a confirmation.") } + c.prime.Output().Notice("") // Empty line return confirm, nil } diff --git a/internal/runbits/runtime/trigger/trigger.go b/internal/runbits/runtime/trigger/trigger.go index b7189a552a..a64f2aa1a5 100644 --- a/internal/runbits/runtime/trigger/trigger.go +++ b/internal/runbits/runtime/trigger/trigger.go @@ -29,6 +29,7 @@ const ( TriggerShell Trigger = "shell" TriggerCheckout Trigger = "checkout" TriggerUse Trigger = "use" + TriggerInstall Trigger = "install" ) func NewExecTrigger(cmd string) Trigger { diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go new file mode 100644 index 0000000000..8b574b6099 --- /dev/null +++ b/internal/runners/install/install.go @@ -0,0 +1,383 @@ +package install + +import ( + "errors" + "fmt" + "strconv" + "strings" + "time" + + "github.com/ActiveState/cli/internal/captain" + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/rtutils/ptr" + buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" + "github.com/ActiveState/cli/internal/runbits/cves" + "github.com/ActiveState/cli/internal/runbits/dependencies" + "github.com/ActiveState/cli/internal/runbits/rationalize" + runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" + "github.com/ActiveState/cli/internal/runbits/runtime/trigger" + "github.com/ActiveState/cli/internal/sliceutils" + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/localcommit" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/pkg/platform/model" + bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/ActiveState/cli/pkg/runtime" + "github.com/go-openapi/strfmt" +) + +type primeable interface { + primer.Outputer + primer.Prompter + primer.Projecter + primer.Auther + primer.Configurer + primer.Analyticer + primer.SvcModeler +} + +// InstallRunParams tracks the info required for running Install. +type InstallRunParams struct { + Packages captain.PackagesValue + Timestamp captain.TimeValue +} + +type requirement struct { + input *captain.PackageValue + resolvedVersionReq []types.VersionRequirement + resolvedNamespace *model.Namespace + matchedIngredients []*model.IngredientAndVersion +} + +type requirements []*requirement + +func (r requirements) String() string { + result := []string{} + for _, req := range r { + if req.resolvedNamespace != nil { + result = append(result, fmt.Sprintf("%s/%s", req.resolvedNamespace.String(), req.input.Name)) + } else { + result = append(result, req.input.Name) + } + } + return strings.Join(result, ", ") +} + +// Install manages the installing execution context. +type Install struct { + prime primeable +} + +// NewInstall prepares an installation execution context for use. +func NewInstall(prime primeable) *Install { + return &Install{prime} +} + +// Run executes the install behavior. +func (i *Install) Run(params InstallRunParams) (rerr error) { + defer rationalizeError(i.prime.Auth(), &rerr) + + logging.Debug("ExecuteInstall") + + pj := i.prime.Project() + out := i.prime.Output() + bp := bpModel.NewBuildPlannerModel(i.prime.Auth()) + + // Verify input + if pj == nil { + return rationalize.ErrNoProject + } + if pj.IsHeadless() { + return rationalize.ErrHeadless + } + + out.Notice(locale.Tr("operating_message", pj.NamespaceString(), pj.Dir())) + + var pg *output.Spinner + defer func() { + if pg != nil { + // This is a bit awkward, but it would be even more awkward to manually address this for every error condition + pg.Stop(locale.T("progress_fail")) + } + }() + + // Start process of resolving requirements + var err error + var oldCommit *bpModel.Commit + var reqs requirements + var ts time.Time + { + pg = output.StartSpinner(out, locale.T("progress_search"), constants.TerminalAnimationInterval) + + // Resolve timestamp, commit and languages used for current project. + // This will be used to resolve the requirements. + ts, err = commits_runbit.ExpandTimeForProject(¶ms.Timestamp, i.prime.Auth(), i.prime.Project()) + if err != nil { + return errs.Wrap(err, "Unable to get timestamp from params") + } + + // Grab local commit info + localCommitID, err := localcommit.Get(i.prime.Project().Dir()) + if err != nil { + return errs.Wrap(err, "Unable to get local commit") + } + oldCommit, err = bp.FetchCommit(localCommitID, pj.Owner(), pj.Name(), nil) + if err != nil { + return errs.Wrap(err, "Failed to fetch old build result") + } + + // Get languages used in current project + languages, err := model.FetchLanguagesForCommit(localCommitID, i.prime.Auth()) + if err != nil { + logging.Debug("Could not get language from project: %v", err) + } + + // Resolve requirements + reqs, err = i.resolveRequirements(params.Packages, ts, languages) + if err != nil { + return errs.Wrap(err, "Unable to resolve requirements") + } + + // Done resolving requirements + pg.Stop(locale.T("progress_found")) + } + + // Start process of creating the commit, which also solves it + var newCommit *bpModel.Commit + { + pg = output.StartSpinner(out, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval) + + script, err := i.prepareBuildScript(oldCommit.BuildScript(), reqs, ts) + if err != nil { + return errs.Wrap(err, "Could not prepare build script") + } + + bsv, _ := script.Marshal() + logging.Debug("Buildscript: %s", string(bsv)) + + commitParams := bpModel.StageCommitParams{ + Owner: pj.Owner(), + Project: pj.Name(), + ParentCommit: string(oldCommit.CommitID), + Description: locale.Tr("commit_message_added", reqs.String()), + Script: script, + } + + // Solve runtime + newCommit, err = bp.StageCommit(commitParams) + if err != nil { + return errs.Wrap(err, "Could not stage commit") + } + + // Stop process of creating the commit + pg.Stop(locale.T("progress_success")) + pg = nil + } + + // Report changes and CVEs to user + { + dependencies.OutputChangeSummary(out, newCommit.BuildPlan(), oldCommit.BuildPlan()) + if err := cves.NewCveReport(i.prime).Report(newCommit.BuildPlan(), oldCommit.BuildPlan()); err != nil { + return errs.Wrap(err, "Could not report CVEs") + } + } + + // Start runtime sourcing UI + if !i.prime.Config().GetBool(constants.AsyncRuntimeConfig) { + // refresh or install runtime + _, err := runtime_runbit.Update(i.prime, trigger.TriggerInstall, + runtime_runbit.WithCommit(newCommit), + runtime_runbit.WithoutBuildscriptValidation(), + ) + if err != nil { + if !IsBuildError(err) { + // If the error is not a build error we still want to update the commit + if err2 := i.updateCommitID(newCommit.CommitID); err2 != nil { + return errs.Pack(err, locale.WrapError(err2, "err_package_update_commit_id")) + } + } + return errs.Wrap(err, "Failed to refresh runtime") + } + } + + // Update commit ID + if err := i.updateCommitID(newCommit.CommitID); err != nil { + return locale.WrapError(err, "err_package_update_commit_id") + } + + // All done + out.Notice(locale.T("operation_success_local")) + + return nil +} + +type errNoMatches struct { + error + requirements []*requirement +} + +// resolveRequirements will attempt to resolve the ingredient and namespace for each requested package +func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Time, languages []model.Language) (requirements, error) { + var disambiguate []*requirement + var failed []*requirement + reqs := []*requirement{} + for _, pkg := range packages { + req := &requirement{input: &pkg} + if pkg.Namespace != "" { + req.resolvedNamespace = ptr.To(model.NewNamespaceRaw(pkg.Namespace)) + } + + // Find ingredients that match the pkg query + ingredients, err := model.SearchIngredientsStrict(pkg.Namespace, pkg.Name, false, false, &ts, i.prime.Auth()) + if err != nil { + return nil, locale.WrapError(err, "err_pkgop_search_err", "Failed to check for ingredients.") + } + + // Resolve matched ingredients + if pkg.Namespace == "" { + // Filter out ingredients that don't target one of the supported languages + ingredients = sliceutils.Filter(ingredients, func(i *model.IngredientAndVersion) bool { + il := model.LanguageFromNamespace(*i.Ingredient.PrimaryNamespace) + for _, l := range languages { + if l.Name == il { + return true + } + } + return false + }) + } + req.matchedIngredients = ingredients + + // Validate that the ingredient is resolved, and prompt the user if multiple ingredients matched + if req.resolvedNamespace == nil { + len := len(ingredients) + switch { + case len == 1: + req.resolvedNamespace = ptr.To(model.ParseNamespace(*ingredients[0].Ingredient.PrimaryNamespace)) + case len > 1: + disambiguate = append(disambiguate, req) + case len == 0: + failed = append(failed, req) + } + } + + reqs = append(reqs, req) + } + + // Fail if not all requirements could be resolved + if len(failed) > 0 { + return nil, errNoMatches{error: errs.New("Failed to resolve requirements"), requirements: failed} + } + + // Disambiguate requirements that match multiple ingredients + if len(disambiguate) > 0 { + for _, req := range disambiguate { + ingredient, err := i.promptForMatchingIngredient(req) + if err != nil { + return nil, errs.Wrap(err, "Prompting for namespace failed") + } + req.matchedIngredients = []*model.IngredientAndVersion{ingredient} + req.resolvedNamespace = ptr.To(model.ParseNamespace(*ingredient.Ingredient.PrimaryNamespace)) + } + } + + // Now that we have the ingredient resolved we can also resolve the version requirement + for _, req := range reqs { + version := req.input.Version + if req.input.Version == "" { + continue + } + if _, err := strconv.Atoi(version); err == nil { + // If the version number provided is a straight up integer (no dots or dashes) then assume it's a wildcard + version = fmt.Sprintf("%d.x", version) + } + var err error + req.resolvedVersionReq, err = bpModel.VersionStringToRequirements(version) + if err != nil { + return nil, errs.Wrap(err, "Could not process version string into requirements") + } + } + + return reqs, nil +} + +func (i *Install) promptForMatchingIngredient(req *requirement) (*model.IngredientAndVersion, error) { + if len(req.matchedIngredients) <= 1 { + return nil, errs.New("promptForNamespace should never be called if there are no multiple ingredient matches") + } + + choices := []string{} + values := map[string]*model.IngredientAndVersion{} + for _, i := range req.matchedIngredients { + // Generate ingredient choices to present to the user + name := fmt.Sprintf("%s (%s)", *i.Ingredient.Name, i.Ingredient.PrimaryNamespace) + choices = append(choices, name) + values[name] = i + } + + // Prompt the user with the ingredient choices + choice, err := i.prime.Prompt().Select( + locale.Tl("prompt_pkgop_ingredient", "Multiple Matches"), + locale.Tl("prompt_pkgop_ingredient_msg", "Your query has multiple matches. Which one would you like to use?"), + choices, &choices[0], + ) + if err != nil { + return nil, errs.Wrap(err, "prompting failed") + } + + // Return the user selected ingredient + return values[choice], nil +} + +func (i *Install) prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) (*buildscript.BuildScript, error) { + script.SetAtTime(ts) + for _, req := range requirements { + requirement := types.Requirement{ + Namespace: req.resolvedNamespace.String(), + Name: req.input.Name, + VersionRequirement: req.resolvedVersionReq, + } + + err := script.AddRequirement(requirement) + if err != nil { + return nil, errs.Wrap(err, "Failed to update build expression with requirement") + } + } + + return script, nil +} + +func (i *Install) updateCommitID(commitID strfmt.UUID) error { + if err := localcommit.Set(i.prime.Project().Dir(), commitID.String()); err != nil { + return locale.WrapError(err, "err_package_update_commit_id") + } + + if i.prime.Config().GetBool(constants.OptinBuildscriptsConfig) { + bp := bpModel.NewBuildPlannerModel(i.prime.Auth()) + script, err := bp.GetBuildScript(commitID.String()) + if err != nil { + return errs.Wrap(err, "Could not get remote build expr and time") + } + + err = buildscript_runbit.Update(i.prime.Project(), script) + if err != nil { + return locale.WrapError(err, "err_update_build_script") + } + } + + return nil +} + +func IsBuildError(err error) bool { + var errBuild *runtime.BuildError + var errBuildPlanner *response.BuildPlannerError + + return errors.As(err, &errBuild) || errors.As(err, &errBuildPlanner) +} diff --git a/internal/runners/install/rationalize.go b/internal/runners/install/rationalize.go new file mode 100644 index 0000000000..ac69eed7bf --- /dev/null +++ b/internal/runners/install/rationalize.go @@ -0,0 +1,99 @@ +package install + +import ( + "errors" + "fmt" + "strings" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/multilog" + bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/pkg/platform/authentication" + "github.com/ActiveState/cli/pkg/platform/model" +) + +func rationalizeError(auth *authentication.Auth, rerr *error) { + var commitError *bpResp.CommitError + var noMatchErr errNoMatches + + switch { + case rerr == nil: + return + + // No matches found + case errors.As(*rerr, &noMatchErr): + names := []string{} + for _, r := range noMatchErr.requirements { + names = append(names, fmt.Sprintf(`"[[ACTIONABLE]%s[/RESET]"`, r.input.Name)) + } + if len(noMatchErr.requirements) > 1 { + *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_requirements_no_match", strings.Join(names, ", "))) + return + } + suggestions, err := getSuggestions(noMatchErr.requirements[0], auth) + if err != nil { + multilog.Error("Failed to retrieve suggestions: %v", err) + } + + if len(suggestions) == 0 { + *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives_nosuggest", strings.Join(names, ", "))) + return + } + + *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives", strings.Join(names, ", "))) + + // Error staging a commit during install. + case errors.As(*rerr, &commitError): + switch commitError.Type { + case types.NotFoundErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_not_found", "Could not make runtime changes because your project was not found."), + errs.SetInput(), + errs.SetTips(locale.T("tip_private_project_auth")), + ) + case types.ForbiddenErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_forbidden", "Could not make runtime changes because you do not have permission to do so."), + errs.SetInput(), + errs.SetTips(locale.T("tip_private_project_auth")), + ) + case types.HeadOnBranchMovedErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.T("err_buildplanner_head_on_branch_moved"), + errs.SetInput(), + ) + case types.NoChangeSinceLastCommitErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_exist", "The requested package(s) is already installed."), + errs.SetInput(), + ) + default: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_buildplanner_error", "Could not make runtime changes due to the following error: {{.V0}}", commitError.Message), + errs.SetInput(), + ) + } + + } +} + +func getSuggestions(req *requirement, auth *authentication.Auth) ([]string, error) { + results, err := model.SearchIngredients(req.input.Namespace, req.input.Name, false, nil, auth) + if err != nil { + return []string{}, locale.WrapError(err, "package_ingredient_err_search", "Failed to resolve ingredient named: {{.V0}}", req.input.Name) + } + + maxResults := 5 + if len(results) > maxResults { + results = results[:maxResults] + } + + suggestions := make([]string, 0, maxResults+1) + for _, result := range results { + suggestions = append(suggestions, fmt.Sprintf(" - %s", *result.Ingredient.Name)) + } + + return suggestions, nil +} diff --git a/internal/runners/packages/info.go b/internal/runners/packages/info.go index fb559b8ecc..2366cae3ff 100644 --- a/internal/runners/packages/info.go +++ b/internal/runners/packages/info.go @@ -52,7 +52,7 @@ func (i *Info) Run(params InfoRunParams, nstype model.NamespaceType) error { var ns *model.Namespace if params.Package.Namespace != "" { - ns = ptr.To(model.NewRawNamespace(params.Package.Namespace)) + ns = ptr.To(model.NewNamespaceRaw(params.Package.Namespace)) } else { nsTypeV = &nstype } diff --git a/internal/runners/packages/install.go b/internal/runners/packages/install.go deleted file mode 100644 index 30193f4cf8..0000000000 --- a/internal/runners/packages/install.go +++ /dev/null @@ -1,61 +0,0 @@ -package packages - -import ( - "github.com/ActiveState/cli/internal/captain" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/rtutils/ptr" - "github.com/ActiveState/cli/internal/runbits/commits_runbit" - "github.com/ActiveState/cli/internal/runbits/runtime/requirements" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - "github.com/ActiveState/cli/pkg/platform/model" -) - -// InstallRunParams tracks the info required for running Install. -type InstallRunParams struct { - Packages captain.PackagesValue - Timestamp captain.TimeValue - Revision captain.IntValue -} - -// Install manages the installing execution context. -type Install struct { - prime primeable -} - -// NewInstall prepares an installation execution context for use. -func NewInstall(prime primeable) *Install { - return &Install{prime} -} - -// Run executes the install behavior. -func (a *Install) Run(params InstallRunParams, nsType model.NamespaceType) (rerr error) { - defer rationalizeError(a.prime.Auth(), &rerr) - - logging.Debug("ExecuteInstall") - var reqs []*requirements.Requirement - for _, p := range params.Packages { - req := &requirements.Requirement{ - Name: p.Name, - Version: p.Version, - Operation: types.OperationAdded, - } - - if p.Namespace != "" { - req.Namespace = ptr.To(model.NewRawNamespace(p.Namespace)) - } else { - req.NamespaceType = &nsType - } - - req.Revision = params.Revision.Int - - reqs = append(reqs, req) - } - - ts, err := commits_runbit.ExpandTimeForProject(¶ms.Timestamp, a.prime.Auth(), a.prime.Project()) - if err != nil { - return errs.Wrap(err, "Unable to get timestamp from params") - } - - return requirements.NewRequirementOperation(a.prime).ExecuteRequirementOperation(&ts, reqs...) -} diff --git a/internal/runners/packages/search.go b/internal/runners/packages/search.go index ed6ad2e4f0..6af93400cf 100644 --- a/internal/runners/packages/search.go +++ b/internal/runners/packages/search.go @@ -56,7 +56,7 @@ func (s *Search) Run(params SearchRunParams, nstype model.NamespaceType) error { ns = model.NewNamespacePkgOrBundle(language, nstype) } else { - ns = model.NewRawNamespace(params.Ingredient.Namespace) + ns = model.NewNamespaceRaw(params.Ingredient.Namespace) } ts, err := commits_runbit.ExpandTimeForProject(¶ms.Timestamp, s.auth, s.proj) diff --git a/internal/runners/packages/uninstall.go b/internal/runners/packages/uninstall.go index a9008261dd..6deeccae73 100644 --- a/internal/runners/packages/uninstall.go +++ b/internal/runners/packages/uninstall.go @@ -43,7 +43,7 @@ func (u *Uninstall) Run(params UninstallRunParams, nsType model.NamespaceType) ( } if p.Namespace != "" { - req.Namespace = ptr.To(model.NewRawNamespace(p.Namespace)) + req.Namespace = ptr.To(model.NewNamespaceRaw(p.Namespace)) } else { req.NamespaceType = &nsType } diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 865c0d36e3..b7fd20282a 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -1,6 +1,8 @@ package buildscript import ( + "errors" + "github.com/go-openapi/strfmt" "github.com/ActiveState/cli/internal/errs" @@ -15,15 +17,15 @@ func (b *BuildScript) UpdateRequirement(operation types.Operation, requirement t var err error switch operation { case types.OperationAdded: - err = b.addRequirement(requirement) + err = b.AddRequirement(requirement) case types.OperationRemoved: - err = b.removeRequirement(requirement) + err = b.RemoveRequirement(requirement) case types.OperationUpdated: - err = b.removeRequirement(requirement) + err = b.RemoveRequirement(requirement) if err != nil { break } - err = b.addRequirement(requirement) + err = b.AddRequirement(requirement) default: return errs.New("Unsupported operation") } @@ -33,7 +35,11 @@ func (b *BuildScript) UpdateRequirement(operation types.Operation, requirement t return nil } -func (b *BuildScript) addRequirement(requirement types.Requirement) error { +func (b *BuildScript) AddRequirement(requirement types.Requirement) error { + if err := b.RemoveRequirement(requirement); err != nil && !errors.As(err, ptr.To(&RequirementNotFoundError{})) { + return errs.Wrap(err, "Could not remove requirement") + } + // Use object form for now, and then transform it into function form later. obj := []*Assignment{ {requirementNameKey, newString(requirement.Name)}, @@ -72,7 +78,7 @@ type RequirementNotFoundError struct { *locale.LocalizedError // for legacy non-user-facing error usages } -func (b *BuildScript) removeRequirement(requirement types.Requirement) error { +func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { requirementsNode, err := b.getRequirementsNode() if err != nil { return errs.Wrap(err, "Could not get requirements node") diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 845b576a95..2c4194f81f 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -169,6 +169,18 @@ func (n Namespace) String() string { return n.value } +func ParseNamespace(ns string) Namespace { + if ns == "" { + return Namespace{NamespaceBlank, ns} + } + for _, n := range []NamespaceType{NamespacePackage, NamespaceBundle, NamespaceLanguage, NamespacePlatform, NamespaceOrg} { + if NamespaceMatch(ns, n.Matchable()) { + return Namespace{n, ns} + } + } + return Namespace{nsType: NamespaceRaw, value: ns} +} + func NewNamespacePkgOrBundle(language string, nstype NamespaceType) Namespace { if nstype == NamespaceBundle { return NewNamespaceBundle(language) @@ -181,7 +193,7 @@ func NewNamespacePackage(language string) Namespace { return Namespace{NamespacePackage, fmt.Sprintf("language/%s", language)} } -func NewRawNamespace(value string) Namespace { +func NewNamespaceRaw(value string) Namespace { return Namespace{NamespaceRaw, value} } diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index fbe8630850..6865e4a67a 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -25,7 +25,7 @@ func (suite *InstallIntegrationTestSuite) TestInstall() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "trender") - cp.Expect("Package added") + cp.Expect("project has been updated") cp.ExpectExitCode(0) } @@ -103,7 +103,7 @@ func (suite *InstallIntegrationTestSuite) TestInstall_Resolved() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "requests") - cp.Expect("Package added") + cp.Expect("project has been updated") cp.ExpectExitCode(0) // Run `state packages` to verify a full package version was resolved. From 4cea33273772eee8f3aa24283dd708939047b72d Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 6 Sep 2024 12:43:15 -0400 Subject: [PATCH 065/440] Added marshaling and pre-unmarshaling hooks for buildscript <-> buildexpression. This allows for outside packages to hook into: - Unmarshaling from buildexpression to buildscript - Marshaling from buildscript to buildexpression --- pkg/buildscript/marshal_buildexpression.go | 19 ++++++++++++-- pkg/buildscript/unmarshal_buildexpression.go | 27 +++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index d9dbe61c99..1f55f87133 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -21,6 +21,21 @@ const ( requirementComparatorKey = "comparator" ) +type MarshalerFunc func([]*Value) ([]byte, error) + +var marshalers map[string]MarshalerFunc + +func init() { + marshalers = make(map[string]MarshalerFunc) + RegisterFunctionMarshaler("Req", marshalReq) // marshal into legacy object format for now +} + +// RegisterFunctionMarshaler registers a buildexpression marshaler for a buildscript function. +// Marshalers accept a buildscript Value, and marshals it to buildexpression JSON (e.g. an object). +func RegisterFunctionMarshaler(name string, marshalJSON MarshalerFunc) { + marshalers[name] = marshalJSON +} + // MarshalJSON returns this structure as a build expression in JSON format, suitable for sending to // the Platform. func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { @@ -91,8 +106,8 @@ func (v *Value) MarshalJSON() ([]byte, error) { } func (f *FuncCall) MarshalJSON() ([]byte, error) { - if f.Name == reqFuncName { - return marshalReq(f.Arguments) // marshal into legacy object format for now + if marshalJSON, exists := marshalers[f.Name]; exists { + return marshalJSON(f.Arguments) } m := make(map[string]interface{}) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 08e0c3b6e6..59479995b2 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -43,6 +43,22 @@ const ( inKey = "in" ) +type PreUnmarshalerFunc func(map[string]interface{}) (map[string]interface{}, error) + +var preUnmarshalers map[string]PreUnmarshalerFunc + +func init() { + preUnmarshalers = make(map[string]PreUnmarshalerFunc) +} + +// RegisterFunctionPreUnmarshaler registers a buildscript pre-unmarshaler for a buildexpression +// function. +// Pre-unmarshalers accept a JSON object of function arguments, transform those arguments as +// necessary, and return a JSON object for final unmarshaling to buildscript. +func RegisterFunctionPreUnmarshaler(name string, preUnmarshal PreUnmarshalerFunc) { + preUnmarshalers[name] = preUnmarshal +} + // UnmarshalBuildExpression returns a BuildScript constructed from the given build expression in // JSON format. // Build scripts and build expressions are almost identical, with the exception of the atTime field. @@ -251,12 +267,21 @@ func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, erro var name string var argsInterface interface{} for key, value := range m { - _, ok := value.(map[string]interface{}) + m, ok := value.(map[string]interface{}) if !ok { return nil, errs.New("Incorrect argument format") } name = key + + if preUnmarshal, exists := preUnmarshalers[name]; exists { + var err error + value, err = preUnmarshal(m) + if err != nil { + return nil, errs.Wrap(err, "Unable to pre-unmarshal function '%s'", name) + } + } + argsInterface = value } From a7ac5d8ba1b6de7f0653e7e0b6ae5bc123a3a08e Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Fri, 6 Sep 2024 10:21:00 -0700 Subject: [PATCH 066/440] Add back powershell change --- internal/testhelpers/e2e/spawn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testhelpers/e2e/spawn.go b/internal/testhelpers/e2e/spawn.go index bf73ffcd22..fff1cd25b8 100644 --- a/internal/testhelpers/e2e/spawn.go +++ b/internal/testhelpers/e2e/spawn.go @@ -74,7 +74,7 @@ func (s *SpawnedCmd) ExpectInput(opts ...termtest.SetExpectOpt) error { expect := `expect'input from posix shell` if cmdName != "bash" && shellName != "bash" && runtime.GOOS == "windows" { if strings.Contains(cmdName, "powershell") || strings.Contains(shellName, "powershell") { - send = "echo \"`\"" + send = "echo \"\"" expect = `` } else { send = `echo ^` From e5da35e712b39664c60b6b6cd3f54fe26c89ce22 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 6 Sep 2024 13:36:26 -0700 Subject: [PATCH 067/440] Implemented new requirement opts for `state platforms add` --- internal/locale/locales/en-us.yaml | 6 +- internal/runbits/commits_runbit/time.go | 4 +- internal/runbits/rationalizers/commit.go | 46 +++++++ internal/runbits/rationalizers/readme.md | 4 + internal/runbits/reqop_runbit/update.go | 147 +++++++++++++++++++++++ internal/runners/install/install.go | 106 ++-------------- internal/runners/install/rationalize.go | 36 +----- internal/runners/platforms/add.go | 97 ++++++++++++--- internal/runners/platforms/platforms.go | 22 ++-- internal/runners/platforms/remove.go | 6 +- pkg/buildscript/mutations.go | 8 +- pkg/platform/model/checkpoints.go | 9 +- pkg/platform/model/inventory.go | 27 ++++- pkg/platform/model/vcs.go | 4 +- test/integration/platforms_int_test.go | 23 ++++ 15 files changed, 365 insertions(+), 180 deletions(-) create mode 100644 internal/runbits/rationalizers/commit.go create mode 100644 internal/runbits/rationalizers/readme.md create mode 100644 internal/runbits/reqop_runbit/update.go diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 4ddf73edc2..64d81f4a71 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -941,7 +941,7 @@ err_arg_required: err_init_no_language: other: "A project has not been set using 'state use'. You must include the [NOTICE]language[/RESET] argument. For more help, run '[ACTIONABLE]state init --help[/RESET]'." platform_added: - other: "[NOTICE]{{.V0}}@{{.V1}}[/RESET] has been added." + other: "[NOTICE]{{.V0}}[/RESET] has been added." platform_removed: other: "[NOTICE]{{.V0}}@{{.V1}}[/RESET] has been removed." deploy_usable_path: @@ -1164,6 +1164,8 @@ package_requirements_no_match: Run "[ACTIONABLE]state search {{.V0}}[/RESET]" to find alternatives. progress_search: other: "• Searching for packages in the ActiveState Catalog" +progress_platform_search: + other: "• Searching for platform in the ActiveState Catalog" progress_cve_search: other: "• Checking for vulnerabilities (CVEs) on [ACTIONABLE]{{.V0}}[/RESET] and its dependencies" setup_runtime: @@ -1583,3 +1585,5 @@ flag_state_upgrade_expand_description: other: Show individual transitive dependency changes rather than a summary flag_state_upgrade_ts_description: other: Manually specify the timestamp to 'upgrade' to. Can be either 'now' or RFC3339 formatted timestamp. +platform_add_not_found: + other: Could not find a platform matching your criteria diff --git a/internal/runbits/commits_runbit/time.go b/internal/runbits/commits_runbit/time.go index 478cfc544d..ac9afb9717 100644 --- a/internal/runbits/commits_runbit/time.go +++ b/internal/runbits/commits_runbit/time.go @@ -17,11 +17,11 @@ import ( // Otherwise, returns the specified timestamp or nil (which falls back on the default Platform // timestamp for a given operation) func ExpandTime(ts *captain.TimeValue, auth *authentication.Auth) (time.Time, error) { - if ts.Time != nil { + if ts != nil && ts.Time != nil { return *ts.Time, nil } - if ts.Now() { + if ts != nil && ts.Now() { latest, err := model.FetchLatestRevisionTimeStamp(auth) if err != nil { return time.Time{}, errs.Wrap(err, "Unable to determine latest revision time") diff --git a/internal/runbits/rationalizers/commit.go b/internal/runbits/rationalizers/commit.go new file mode 100644 index 0000000000..dfa26018b0 --- /dev/null +++ b/internal/runbits/rationalizers/commit.go @@ -0,0 +1,46 @@ +package rationalizers + +import ( + "errors" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" +) + +func HandleCommitErrors(rerr *error) { + var commitError *bpResp.CommitError + if !errors.As(*rerr, &commitError) { + return + } + switch commitError.Type { + case types.NotFoundErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_not_found", "Could not make runtime changes because your project was not found."), + errs.SetInput(), + errs.SetTips(locale.T("tip_private_project_auth")), + ) + case types.ForbiddenErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_forbidden", "Could not make runtime changes because you do not have permission to do so."), + errs.SetInput(), + errs.SetTips(locale.T("tip_private_project_auth")), + ) + case types.HeadOnBranchMovedErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.T("err_buildplanner_head_on_branch_moved"), + errs.SetInput(), + ) + case types.NoChangeSinceLastCommitErrorType: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_exist", "The requested package is already installed."), + errs.SetInput(), + ) + default: + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_packages_buildplanner_error", "Could not make runtime changes due to the following error: {{.V0}}", commitError.Message), + errs.SetInput(), + ) + } +} diff --git a/internal/runbits/rationalizers/readme.md b/internal/runbits/rationalizers/readme.md new file mode 100644 index 0000000000..aed768812d --- /dev/null +++ b/internal/runbits/rationalizers/readme.md @@ -0,0 +1,4 @@ +The rationalizers package is intended to be used for common error rationalizers that are shared between multiple +runners. + +In MOST cases your rationalizer should be package specific, so don't default to using the rationalizers package. diff --git a/internal/runbits/reqop_runbit/update.go b/internal/runbits/reqop_runbit/update.go new file mode 100644 index 0000000000..447cb85c5b --- /dev/null +++ b/internal/runbits/reqop_runbit/update.go @@ -0,0 +1,147 @@ +package reqop_runbit + +import ( + "errors" + "fmt" + "strings" + + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runbits/buildscript" + "github.com/ActiveState/cli/internal/runbits/cves" + "github.com/ActiveState/cli/internal/runbits/dependencies" + "github.com/ActiveState/cli/internal/runbits/runtime" + "github.com/ActiveState/cli/internal/runbits/runtime/trigger" + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/localcommit" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/ActiveState/cli/pkg/runtime" + "github.com/go-openapi/strfmt" +) + +type primeable interface { + primer.Outputer + primer.Prompter + primer.Projecter + primer.Auther + primer.Configurer + primer.Analyticer + primer.SvcModeler +} + +type requirements []*Requirement + +func (r requirements) String() string { + result := []string{} + for _, req := range r { + result = append(result, fmt.Sprintf("%s/%s", req.Namespace, req.Name)) + } + return strings.Join(result, ", ") +} + +type Requirement struct { + Name string + Namespace model.Namespace + Version []types.VersionRequirement +} + +func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit *buildplanner.Commit, commitMsg string) error { + pj := prime.Project() + out := prime.Output() + cfg := prime.Config() + bp := buildplanner.NewBuildPlannerModel(prime.Auth()) + + var pg *output.Spinner + defer func() { + if pg != nil { + pg.Stop(locale.T("progress_fail")) + } + }() + pg = output.StartSpinner(out, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval) + + bsv, _ := script.Marshal() + logging.Debug("Buildscript: %s", string(bsv)) + + commitParams := buildplanner.StageCommitParams{ + Owner: pj.Owner(), + Project: pj.Name(), + ParentCommit: string(oldCommit.CommitID), + Description: commitMsg, + Script: script, + } + + // Solve runtime + newCommit, err := bp.StageCommit(commitParams) + if err != nil { + return errs.Wrap(err, "Could not stage commit") + } + + // Stop process of creating the commit + pg.Stop(locale.T("progress_success")) + pg = nil + + // Report changes and CVEs to user + dependencies.OutputChangeSummary(out, newCommit.BuildPlan(), oldCommit.BuildPlan()) + if err := cves.NewCveReport(prime).Report(newCommit.BuildPlan(), oldCommit.BuildPlan()); err != nil { + return errs.Wrap(err, "Could not report CVEs") + } + + // Start runtime sourcing UI + if !cfg.GetBool(constants.AsyncRuntimeConfig) { + // refresh or install runtime + _, err := runtime_runbit.Update(prime, trigger.TriggerInstall, + runtime_runbit.WithCommit(newCommit), + runtime_runbit.WithoutBuildscriptValidation(), + ) + if err != nil { + if !isBuildError(err) { + // If the error is not a build error we still want to update the commit + if err2 := updateCommitID(prime, newCommit.CommitID); err2 != nil { + return errs.Pack(err, locale.WrapError(err2, "err_package_update_commit_id")) + } + } + return errs.Wrap(err, "Failed to refresh runtime") + } + } + + // Update commit ID + if err := updateCommitID(prime, newCommit.CommitID); err != nil { + return locale.WrapError(err, "err_package_update_commit_id") + } + return nil +} + +func updateCommitID(prime primeable, commitID strfmt.UUID) error { + if err := localcommit.Set(prime.Project().Dir(), commitID.String()); err != nil { + return locale.WrapError(err, "err_package_update_commit_id") + } + + if prime.Config().GetBool(constants.OptinBuildscriptsConfig) { + bp := buildplanner.NewBuildPlannerModel(prime.Auth()) + script, err := bp.GetBuildScript(commitID.String()) + if err != nil { + return errs.Wrap(err, "Could not get remote build expr and time") + } + + err = buildscript_runbit.Update(prime.Project(), script) + if err != nil { + return locale.WrapError(err, "err_update_build_script") + } + } + + return nil +} + +func isBuildError(err error) bool { + var errBuild *runtime.BuildError + var errBuildPlanner *response.BuildPlannerError + + return errors.As(err, &errBuild) || errors.As(err, &errBuildPlanner) +} diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index 8b574b6099..d4f84a9560 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -1,7 +1,6 @@ package install import ( - "errors" "fmt" "strconv" "strings" @@ -15,22 +14,15 @@ import ( "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/rtutils/ptr" - buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/runbits/commits_runbit" - "github.com/ActiveState/cli/internal/runbits/cves" - "github.com/ActiveState/cli/internal/runbits/dependencies" "github.com/ActiveState/cli/internal/runbits/rationalize" - runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" - "github.com/ActiveState/cli/internal/runbits/runtime/trigger" + "github.com/ActiveState/cli/internal/runbits/reqop_runbit" "github.com/ActiveState/cli/internal/sliceutils" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/model" bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" - "github.com/ActiveState/cli/pkg/runtime" - "github.com/go-openapi/strfmt" ) type primeable interface { @@ -103,7 +95,6 @@ func (i *Install) Run(params InstallRunParams) (rerr error) { var pg *output.Spinner defer func() { if pg != nil { - // This is a bit awkward, but it would be even more awkward to manually address this for every error condition pg.Stop(locale.T("progress_fail")) } }() @@ -147,69 +138,18 @@ func (i *Install) Run(params InstallRunParams) (rerr error) { // Done resolving requirements pg.Stop(locale.T("progress_found")) - } - - // Start process of creating the commit, which also solves it - var newCommit *bpModel.Commit - { - pg = output.StartSpinner(out, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval) - - script, err := i.prepareBuildScript(oldCommit.BuildScript(), reqs, ts) - if err != nil { - return errs.Wrap(err, "Could not prepare build script") - } - - bsv, _ := script.Marshal() - logging.Debug("Buildscript: %s", string(bsv)) - - commitParams := bpModel.StageCommitParams{ - Owner: pj.Owner(), - Project: pj.Name(), - ParentCommit: string(oldCommit.CommitID), - Description: locale.Tr("commit_message_added", reqs.String()), - Script: script, - } - - // Solve runtime - newCommit, err = bp.StageCommit(commitParams) - if err != nil { - return errs.Wrap(err, "Could not stage commit") - } - - // Stop process of creating the commit - pg.Stop(locale.T("progress_success")) pg = nil } - // Report changes and CVEs to user - { - dependencies.OutputChangeSummary(out, newCommit.BuildPlan(), oldCommit.BuildPlan()) - if err := cves.NewCveReport(i.prime).Report(newCommit.BuildPlan(), oldCommit.BuildPlan()); err != nil { - return errs.Wrap(err, "Could not report CVEs") - } - } - - // Start runtime sourcing UI - if !i.prime.Config().GetBool(constants.AsyncRuntimeConfig) { - // refresh or install runtime - _, err := runtime_runbit.Update(i.prime, trigger.TriggerInstall, - runtime_runbit.WithCommit(newCommit), - runtime_runbit.WithoutBuildscriptValidation(), - ) - if err != nil { - if !IsBuildError(err) { - // If the error is not a build error we still want to update the commit - if err2 := i.updateCommitID(newCommit.CommitID); err2 != nil { - return errs.Pack(err, locale.WrapError(err2, "err_package_update_commit_id")) - } - } - return errs.Wrap(err, "Failed to refresh runtime") - } + // Prepare updated buildscript + script, err := prepareBuildScript(oldCommit.BuildScript(), reqs, ts) + if err != nil { + return errs.Wrap(err, "Could not prepare build script") } - // Update commit ID - if err := i.updateCommitID(newCommit.CommitID); err != nil { - return locale.WrapError(err, "err_package_update_commit_id") + // Update local checkout and source runtime changes + if err := reqop_runbit.UpdateAndReload(i.prime, script, oldCommit, locale.Tr("commit_message_added", reqs.String())); err != nil { + return errs.Wrap(err, "Failed to update local checkout") } // All done @@ -336,7 +276,7 @@ func (i *Install) promptForMatchingIngredient(req *requirement) (*model.Ingredie return values[choice], nil } -func (i *Install) prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) (*buildscript.BuildScript, error) { +func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) (*buildscript.BuildScript, error) { script.SetAtTime(ts) for _, req := range requirements { requirement := types.Requirement{ @@ -353,31 +293,3 @@ func (i *Install) prepareBuildScript(script *buildscript.BuildScript, requiremen return script, nil } - -func (i *Install) updateCommitID(commitID strfmt.UUID) error { - if err := localcommit.Set(i.prime.Project().Dir(), commitID.String()); err != nil { - return locale.WrapError(err, "err_package_update_commit_id") - } - - if i.prime.Config().GetBool(constants.OptinBuildscriptsConfig) { - bp := bpModel.NewBuildPlannerModel(i.prime.Auth()) - script, err := bp.GetBuildScript(commitID.String()) - if err != nil { - return errs.Wrap(err, "Could not get remote build expr and time") - } - - err = buildscript_runbit.Update(i.prime.Project(), script) - if err != nil { - return locale.WrapError(err, "err_update_build_script") - } - } - - return nil -} - -func IsBuildError(err error) bool { - var errBuild *runtime.BuildError - var errBuildPlanner *response.BuildPlannerError - - return errors.As(err, &errBuild) || errors.As(err, &errBuildPlanner) -} diff --git a/internal/runners/install/rationalize.go b/internal/runners/install/rationalize.go index ac69eed7bf..6d988539d7 100644 --- a/internal/runners/install/rationalize.go +++ b/internal/runners/install/rationalize.go @@ -8,14 +8,14 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/multilog" + "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/rationalizers" bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" ) func rationalizeError(auth *authentication.Auth, rerr *error) { - var commitError *bpResp.CommitError var noMatchErr errNoMatches switch { @@ -45,36 +45,8 @@ func rationalizeError(auth *authentication.Auth, rerr *error) { *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives", strings.Join(names, ", "))) // Error staging a commit during install. - case errors.As(*rerr, &commitError): - switch commitError.Type { - case types.NotFoundErrorType: - *rerr = errs.WrapUserFacing(*rerr, - locale.Tl("err_packages_not_found", "Could not make runtime changes because your project was not found."), - errs.SetInput(), - errs.SetTips(locale.T("tip_private_project_auth")), - ) - case types.ForbiddenErrorType: - *rerr = errs.WrapUserFacing(*rerr, - locale.Tl("err_packages_forbidden", "Could not make runtime changes because you do not have permission to do so."), - errs.SetInput(), - errs.SetTips(locale.T("tip_private_project_auth")), - ) - case types.HeadOnBranchMovedErrorType: - *rerr = errs.WrapUserFacing(*rerr, - locale.T("err_buildplanner_head_on_branch_moved"), - errs.SetInput(), - ) - case types.NoChangeSinceLastCommitErrorType: - *rerr = errs.WrapUserFacing(*rerr, - locale.Tl("err_packages_exist", "The requested package(s) is already installed."), - errs.SetInput(), - ) - default: - *rerr = errs.WrapUserFacing(*rerr, - locale.Tl("err_packages_buildplanner_error", "Could not make runtime changes due to the following error: {{.V0}}", commitError.Message), - errs.SetInput(), - ) - } + case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): + rationalizers.HandleCommitErrors(rerr) } } diff --git a/internal/runners/platforms/add.go b/internal/runners/platforms/add.go index 767c569f2a..dcbe238fe3 100644 --- a/internal/runners/platforms/add.go +++ b/internal/runners/platforms/add.go @@ -1,13 +1,23 @@ package platforms import ( + "errors" + + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/rationalize" - "github.com/ActiveState/cli/internal/runbits/runtime/requirements" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/internal/runbits/rationalizers" + "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/pkg/localcommit" + bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/model" + bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" ) // AddRunParams tracks the info required for running Add. @@ -38,7 +48,9 @@ func NewAdd(prime primeable) *Add { } // Run executes the add behavior. -func (a *Add) Run(ps AddRunParams) error { +func (a *Add) Run(ps AddRunParams) (rerr error) { + defer rationalizeAddPlatformError(&rerr) + logging.Debug("Execute platforms add") params, err := prepareParams(ps.Params) @@ -50,20 +62,75 @@ func (a *Add) Run(ps AddRunParams) error { return rationalize.ErrNoProject } - if err := requirements.NewRequirementOperation(a.prime).ExecuteRequirementOperation( - nil, - &requirements.Requirement{ - Name: params.name, - Version: params.version, - Operation: types.OperationAdded, - BitWidth: params.BitWidth, - NamespaceType: &model.NamespacePlatform, - }, - ); err != nil { - return locale.WrapError(err, "err_add_platform", "Could not add platform.") + pj := a.prime.Project() + out := a.prime.Output() + bp := bpModel.NewBuildPlannerModel(a.prime.Auth()) + + var pg *output.Spinner + defer func() { + if pg != nil { + pg.Stop(locale.T("progress_fail")) + } + }() + + pg = output.StartSpinner(out, locale.T("progress_platform_search"), constants.TerminalAnimationInterval) + + // Grab local commit info + localCommitID, err := localcommit.Get(pj.Dir()) + if err != nil { + return errs.Wrap(err, "Unable to get local commit") + } + oldCommit, err := bp.FetchCommit(localCommitID, pj.Owner(), pj.Name(), nil) + if err != nil { + return errs.Wrap(err, "Failed to fetch old build result") + } + + // Resolve platform + platform, err := model.FetchPlatformByDetails(params.Platform.Name(), params.Platform.Version(), params.BitWidth) + if err != nil { + return errs.Wrap(err, "Could not fetch platform") + } + + pg.Stop(locale.T("progress_found")) + pg = nil + + // Resolve timestamp, commit and languages used for current project. + ts, err := commits_runbit.ExpandTimeForProject(nil, a.prime.Auth(), pj) + if err != nil { + return errs.Wrap(err, "Unable to get timestamp from params") + } + + // Prepare updated buildscript + script := oldCommit.BuildScript() + script.SetAtTime(ts) + script.AddPlatform(*platform.PlatformID) + + // Update local checkout and source runtime changes + if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", *platform.DisplayName)); err != nil { + return errs.Wrap(err, "Failed to update local checkout") } - a.prime.Output().Notice(locale.Tr("platform_added", params.name, params.version)) + out.Notice(locale.Tr("platform_added", *platform.DisplayName)) return nil } + +func rationalizeAddPlatformError(rerr *error) { + switch { + case rerr == nil: + return + + // No matches found + case errors.Is(*rerr, model.ErrPlatformNotFound): + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("platform_add_not_found"), + errs.SetInput(), + ) + + // Error staging a commit during install. + case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): + rationalizers.HandleCommitErrors(rerr) + + } +} diff --git a/internal/runners/platforms/platforms.go b/internal/runners/platforms/platforms.go index 6d335eea64..5cd12af476 100644 --- a/internal/runners/platforms/platforms.go +++ b/internal/runners/platforms/platforms.go @@ -66,19 +66,19 @@ func makePlatformsFromModelPlatforms(platforms []*model.Platform) []*Platform { // Params represents the minimal defining details of a platform. type Params struct { - Platform PlatformVersion - BitWidth int - name string - version string + Platform PlatformVersion + BitWidth int + resolvedName string // Holds the provided platforn name, or defaults to curernt platform name if not provided + resolvedVersion string // Holds the provided platform version, or defaults to latest version if not provided } func prepareParams(ps Params) (Params, error) { - ps.name = ps.Platform.Name() - if ps.name == "" { - ps.name = sysinfo.OS().String() + ps.resolvedName = ps.Platform.Name() + if ps.resolvedName == "" { + ps.resolvedName = sysinfo.OS().String() } - ps.version = ps.Platform.Version() - if ps.version == "" { + ps.resolvedVersion = ps.Platform.Version() + if ps.resolvedVersion == "" { return prepareLatestVersion(ps) } @@ -99,8 +99,8 @@ func prepareLatestVersion(params Params) (Params, error) { if err != nil { return params, locale.WrapError(err, "err_fetch_platform", "Could not get platform details") } - params.name = *platform.Kernel.Name - params.version = *platform.KernelVersion.Version + params.resolvedName = *platform.Kernel.Name + params.resolvedVersion = *platform.KernelVersion.Version bitWidth, err := strconv.Atoi(*platform.CPUArchitecture.BitWidth) if err != nil { diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index 3690be8257..b8fe20c01f 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -43,8 +43,8 @@ func (r *Remove) Run(ps RemoveRunParams) error { if err := requirements.NewRequirementOperation(r.prime).ExecuteRequirementOperation( nil, &requirements.Requirement{ - Name: params.name, - Version: params.version, + Name: params.resolvedName, + Version: params.resolvedVersion, Operation: types.OperationRemoved, BitWidth: params.BitWidth, NamespaceType: &model.NamespacePlatform, @@ -53,7 +53,7 @@ func (r *Remove) Run(ps RemoveRunParams) error { return locale.WrapError(err, "err_remove_platform", "Could not remove platform.") } - r.prime.Output().Notice(locale.Tr("platform_removed", params.name, params.version)) + r.prime.Output().Notice(locale.Tr("platform_removed", params.resolvedName, params.resolvedVersion)) return nil } diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index b7fd20282a..913cb521a0 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -119,9 +119,9 @@ func (b *BuildScript) UpdatePlatform(operation types.Operation, platformID strfm var err error switch operation { case types.OperationAdded: - err = b.addPlatform(platformID) + err = b.AddPlatform(platformID) case types.OperationRemoved: - err = b.removePlatform(platformID) + err = b.RemovePlatform(platformID) default: return errs.New("Unsupported operation") } @@ -131,7 +131,7 @@ func (b *BuildScript) UpdatePlatform(operation types.Operation, platformID strfm return nil } -func (b *BuildScript) addPlatform(platformID strfmt.UUID) error { +func (b *BuildScript) AddPlatform(platformID strfmt.UUID) error { platformsNode, err := b.getPlatformsNode() if err != nil { return errs.Wrap(err, "Could not get platforms node") @@ -149,7 +149,7 @@ type PlatformNotFoundError struct { *locale.LocalizedError // for legacy non-user-facing error usages } -func (b *BuildScript) removePlatform(platformID strfmt.UUID) error { +func (b *BuildScript) RemovePlatform(platformID strfmt.UUID) error { platformsNode, err := b.getPlatformsNode() if err != nil { return errs.Wrap(err, "Could not get platforms node") diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index fe63fb8632..f7beccbc5d 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -191,12 +191,7 @@ func PlatformNameToPlatformID(name string) (string, error) { if name == "darwin" { name = "macos" } - id, err := hostPlatformToPlatformID(name) - return id, err -} - -func hostPlatformToPlatformID(os string) (string, error) { - switch strings.ToLower(os) { + switch strings.ToLower(name) { case strings.ToLower(sysinfo.Linux.String()): return constants.LinuxBit64UUID, nil case strings.ToLower(sysinfo.Mac.String()): @@ -204,7 +199,7 @@ func hostPlatformToPlatformID(os string) (string, error) { case strings.ToLower(sysinfo.Windows.String()): return constants.Win10Bit64UUID, nil default: - return "", locale.NewExternalError("err_unsupported_platform", "", os) + return "", ErrPlatformNotFound } } diff --git a/pkg/platform/model/inventory.go b/pkg/platform/model/inventory.go index 5e976b9c68..951869db4f 100644 --- a/pkg/platform/model/inventory.go +++ b/pkg/platform/model/inventory.go @@ -1,7 +1,7 @@ package model import ( - "fmt" + "errors" "regexp" "runtime" "sort" @@ -453,7 +453,18 @@ func FetchPlatformByUID(uid strfmt.UUID) (*Platform, error) { return nil, nil } -func FetchPlatformByDetails(name, version string, word int, auth *authentication.Auth) (*Platform, error) { +var ErrPlatformNotFound = errors.New("could not find platform matching provided criteria") + +func FetchPlatformByDetails(name, version string, bitwidth int) (*Platform, error) { + var platformID string + if version == "" && bitwidth == 0 { + var err error + platformID, err = PlatformNameToPlatformID(name) + if err != nil { + return nil, errs.Wrap(err, "platform id from name failed") + } + } + runtimePlatforms, err := FetchPlatforms() if err != nil { return nil, err @@ -462,6 +473,12 @@ func FetchPlatformByDetails(name, version string, word int, auth *authentication lower := strings.ToLower for _, rtPf := range runtimePlatforms { + if platformID != "" { + if rtPf.PlatformID.String() == platformID { + return rtPf, nil + } + continue + } if rtPf.Kernel == nil || rtPf.Kernel.Name == nil { continue } @@ -479,16 +496,14 @@ func FetchPlatformByDetails(name, version string, word int, auth *authentication if rtPf.CPUArchitecture == nil { continue } - if rtPf.CPUArchitecture.BitWidth == nil || *rtPf.CPUArchitecture.BitWidth != strconv.Itoa(word) { + if rtPf.CPUArchitecture.BitWidth == nil || *rtPf.CPUArchitecture.BitWidth != strconv.Itoa(bitwidth) { continue } return rtPf, nil } - details := fmt.Sprintf("%s %d %s", name, word, version) - - return nil, locale.NewExternalError("err_unsupported_platform", "", details) + return nil, ErrPlatformNotFound } func FetchLanguageForCommit(commitID strfmt.UUID, auth *authentication.Auth) (*Language, error) { diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 2c4194f81f..43a762286c 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -556,7 +556,7 @@ func UpdateProjectBranchCommitWithModel(pjm *mono_models.Project, branchName str // CommitInitial creates a root commit for a new branch func CommitInitial(hostPlatform string, langName, langVersion string, auth *authentication.Auth) (strfmt.UUID, error) { - platformID, err := hostPlatformToPlatformID(hostPlatform) + platformID, err := PlatformNameToPlatformID(hostPlatform) if err != nil { return "", err } @@ -665,7 +665,7 @@ func (cs indexedCommits) countBetween(first, last string) (int, error) { func ResolveRequirementNameAndVersion(name, version string, word int, namespace Namespace, auth *authentication.Auth) (string, string, error) { if namespace.Type() == NamespacePlatform { - platform, err := FetchPlatformByDetails(name, version, word, auth) + platform, err := FetchPlatformByDetails(name, version, word) if err != nil { return "", "", errs.Wrap(err, "Could not fetch platform") } diff --git a/test/integration/platforms_int_test.go b/test/integration/platforms_int_test.go index 780edc2f44..4968a5dc59 100644 --- a/test/integration/platforms_int_test.go +++ b/test/integration/platforms_int_test.go @@ -132,6 +132,29 @@ func (suite *PlatformsIntegrationTestSuite) TestPlatforms_addRemoveLatest() { } +func (suite *PlatformsIntegrationTestSuite) TestPlatforms_addNotFound() { + suite.OnlyRunForTags(tagsuite.Platforms) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + ts.PrepareEmptyProject() + + // OS name doesn't match + cp := ts.Spawn("platforms", "add", "bunnies") + cp.Expect("Could not find") + cp.ExpectExitCode(1) + + // OS version doesn't match + cp = ts.Spawn("platforms", "add", "windows@99.99.99") + cp.Expect("Could not find") + cp.ExpectExitCode(1) + + // bitwidth version doesn't match + cp = ts.Spawn("platforms", "add", "windows", "--bit-width=999") + cp.Expect("Could not find") + cp.ExpectExitCode(1) +} + func (suite *PlatformsIntegrationTestSuite) TestJSON() { suite.OnlyRunForTags(tagsuite.Platforms, tagsuite.JSON) ts := e2e.New(suite.T(), false) From 2dacc37d8f50289b09083fd669f8692eb9e71a5b Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 9 Sep 2024 10:20:48 -0700 Subject: [PATCH 068/440] Use new runner for `bundles install` --- cmd/state/internal/cmdtree/bundles.go | 11 ++++++++--- cmd/state/internal/cmdtree/packages.go | 4 ++-- internal/captain/values.go | 12 ++++++++---- internal/runners/install/install.go | 9 +++++---- internal/runners/upgrade/upgrade.go | 2 +- pkg/platform/model/vcs.go | 19 +++++++++++-------- test/integration/bundle_int_test.go | 14 ++++++++++++++ 7 files changed, 49 insertions(+), 22 deletions(-) diff --git a/cmd/state/internal/cmdtree/bundles.go b/cmd/state/internal/cmdtree/bundles.go index 73e61d5175..b1549568b8 100644 --- a/cmd/state/internal/cmdtree/bundles.go +++ b/cmd/state/internal/cmdtree/bundles.go @@ -44,8 +44,7 @@ func newBundlesCommand(prime *primer.Values) *captain.Command { } func newBundleInstallCommand(prime *primer.Values) *captain.Command { - runner := install.NewInstall(prime) - + runner := install.NewInstall(prime, model.NamespaceBundle) params := install.InstallRunParams{} return captain.NewCommand( @@ -62,7 +61,13 @@ func newBundleInstallCommand(prime *primer.Values) *captain.Command { Required: true, }, }, - func(_ *captain.Command, _ []string) error { + func(_ *captain.Command, args []string) error { + for _, p := range args { + _, err := params.Packages.Add(p) + if err != nil { + return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") + } + } return runner.Run(params) }, ).SetSupportsStructuredOutput() diff --git a/cmd/state/internal/cmdtree/packages.go b/cmd/state/internal/cmdtree/packages.go index 3c314c7311..d2e250f954 100644 --- a/cmd/state/internal/cmdtree/packages.go +++ b/cmd/state/internal/cmdtree/packages.go @@ -50,7 +50,7 @@ func newPackagesCommand(prime *primer.Values) *captain.Command { } func newInstallCommand(prime *primer.Values) *captain.Command { - runner := install.NewInstall(prime) + runner := install.NewInstall(prime, model.NamespacePackage) params := install.InstallRunParams{} @@ -77,7 +77,7 @@ func newInstallCommand(prime *primer.Values) *captain.Command { }, func(_ *captain.Command, args []string) error { for _, p := range args { - if err := params.Packages.Set(p); err != nil { + if _, err := params.Packages.Add(p); err != nil { return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") } } diff --git a/internal/captain/values.go b/internal/captain/values.go index d6f4c7faa3..a819291c3f 100644 --- a/internal/captain/values.go +++ b/internal/captain/values.go @@ -203,7 +203,7 @@ func (p *PackageValueNSRequired) Type() string { } // PackagesValue is used to represent multiple PackageValue, this is used when a flag can be passed multiple times. -type PackagesValue []PackageValue +type PackagesValue []*PackageValue var _ FlagMarshaler = &PackagesValue{} @@ -216,12 +216,16 @@ func (p *PackagesValue) String() string { } func (p *PackagesValue) Set(s string) error { + return nil // This is currently not natively supported by captain as it takes a full list of arguments +} + +func (p *PackagesValue) Add(s string) (*PackageValue, error) { pf := &PackageValue{} if err := pf.Set(s); err != nil { - return err + return nil, err } - *p = append(*p, *pf) - return nil + *p = append(*p, pf) + return pf, nil } func (p *PackagesValue) Type() string { diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index d4f84a9560..2c6fc7976e 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -64,12 +64,13 @@ func (r requirements) String() string { // Install manages the installing execution context. type Install struct { - prime primeable + prime primeable + nsType model.NamespaceType } // NewInstall prepares an installation execution context for use. -func NewInstall(prime primeable) *Install { - return &Install{prime} +func NewInstall(prime primeable, nsType model.NamespaceType) *Install { + return &Install{prime, nsType} } // Run executes the install behavior. @@ -169,7 +170,7 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti var failed []*requirement reqs := []*requirement{} for _, pkg := range packages { - req := &requirement{input: &pkg} + req := &requirement{input: pkg} if pkg.Namespace != "" { req.resolvedNamespace = ptr.To(model.NewNamespaceRaw(pkg.Namespace)) } diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index 9730b39361..9014cd12d0 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -254,7 +254,7 @@ func (u *Upgrade) renderUserFacing(changes []structuredChange, expand bool) erro }) needsDepRow := len(change.TransitiveDeps) > 0 - needsNamespaceRow := strings.HasPrefix(change.Namespace, model.OrgNamespacePrefix) + needsNamespaceRow := strings.HasPrefix(change.Namespace, model.NamespaceOrg.Prefix()) if needsNamespaceRow { treeSymbol := output.TreeEnd diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 43a762286c..7aef7f2fb4 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -134,7 +134,7 @@ var ( NamespaceBundle = NamespaceType{"bundle", "bundles", NamespaceBundlesMatch} NamespaceLanguage = NamespaceType{"language", "", NamespaceLanguageMatch} NamespacePlatform = NamespaceType{"platform", "", NamespacePlatformMatch} - NamespaceOrg = NamespaceType{"org", "org", NamespaceOrgMatch} + NamespaceOrg = NamespaceType{"org", "private/", NamespaceOrgMatch} NamespaceRaw = NamespaceType{"raw", "", ""} NamespaceBlank = NamespaceType{"", "", ""} ) @@ -216,21 +216,24 @@ func NewNamespacePlatform() Namespace { return Namespace{NamespacePlatform, "platform"} } -const OrgNamespacePrefix = "private/" - func NewOrgNamespace(orgName string) Namespace { return Namespace{ nsType: NamespaceOrg, - value: OrgNamespacePrefix + orgName, + value: NamespaceOrg.prefix + "/" + orgName, } } func LanguageFromNamespace(ns string) string { - values := strings.Split(ns, "/") - if len(values) != 2 { - return "" + matchables := []NamespaceMatchable{ + NamespacePackage.Matchable(), + NamespaceBundle.Matchable(), + } + for _, m := range matchables { + if NamespaceMatch(ns, m) { + return strings.Split(ns, "/")[1] + } } - return values[1] + return "" } // FilterSupportedIngredients filters a list of ingredients, returning only those that are currently supported (such that they can be built) by the Platform diff --git a/test/integration/bundle_int_test.go b/test/integration/bundle_int_test.go index c1d6907da7..6207c8f5de 100644 --- a/test/integration/bundle_int_test.go +++ b/test/integration/bundle_int_test.go @@ -36,6 +36,20 @@ func (suite *BundleIntegrationTestSuite) TestBundle_project_name_noData() { cp.ExpectExitCode(0) } +func (suite *BundleIntegrationTestSuite) TestBundle_install() { + suite.OnlyRunForTags(tagsuite.Bundle) + ts := e2e.New(suite.T(), false) + defer ts.Close() + ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") + + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("bundles", "install", "python-module-build-support") + cp.Expect("project has been updated") + cp.ExpectExitCode(0) +} + func (suite *BundleIntegrationTestSuite) TestBundle_searchSimple() { suite.OnlyRunForTags(tagsuite.Bundle) ts := e2e.New(suite.T(), false) From 60c7ee18787117eb90e560746a2d81d35ca849fc Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 9 Sep 2024 10:21:04 -0700 Subject: [PATCH 069/440] Fix suggestions --- internal/locale/locales/en-us.yaml | 8 ++--- internal/runners/install/install.go | 12 ++++--- internal/runners/install/rationalize.go | 43 +++++++++++++++++-------- test/integration/install_int_test.go | 17 ++++++++++ 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 64d81f4a71..1f50ebe6f0 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1147,16 +1147,16 @@ err_exec_recursion: To allow recursion, set [ACTIONABLE]{{.V1}}=true[/RESET] package_ingredient_alternatives: other: | - No results found for search term "[NOTICE]{{.V0}}[/RESET]". Did you mean: + No results found for search term {{.V0}}. Did you mean: {{.V1}} - Run "[ACTIONABLE]state search {{.V0}}[/RESET]" to see more suggestions. + Use "[ACTIONABLE]state search[/RESET]" to find more suggestions. package_ingredient_alternatives_nosuggest: other: | - No results found for search term "[NOTICE]{{.V0}}[/RESET]". + No results found for search term {{.V0}}. - Run "[ACTIONABLE]state search {{.V0}}[/RESET]" to find alternatives. + Use "[ACTIONABLE]state search[/RESET]" to find alternatives. package_requirements_no_match: other: | No results found for following packages: {{.V0}}. diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index 2c6fc7976e..74d2fe8cff 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -75,7 +75,7 @@ func NewInstall(prime primeable, nsType model.NamespaceType) *Install { // Run executes the install behavior. func (i *Install) Run(params InstallRunParams) (rerr error) { - defer rationalizeError(i.prime.Auth(), &rerr) + defer i.rationalizeError(&rerr) logging.Debug("ExecuteInstall") @@ -162,6 +162,7 @@ func (i *Install) Run(params InstallRunParams) (rerr error) { type errNoMatches struct { error requirements []*requirement + languages []model.Language } // resolveRequirements will attempt to resolve the ingredient and namespace for each requested package @@ -184,8 +185,11 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti // Resolve matched ingredients if pkg.Namespace == "" { // Filter out ingredients that don't target one of the supported languages - ingredients = sliceutils.Filter(ingredients, func(i *model.IngredientAndVersion) bool { - il := model.LanguageFromNamespace(*i.Ingredient.PrimaryNamespace) + ingredients = sliceutils.Filter(ingredients, func(iv *model.IngredientAndVersion) bool { + if !model.NamespaceMatch(*iv.Ingredient.PrimaryNamespace, i.nsType.Matchable()) { + return false + } + il := model.LanguageFromNamespace(*iv.Ingredient.PrimaryNamespace) for _, l := range languages { if l.Name == il { return true @@ -214,7 +218,7 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti // Fail if not all requirements could be resolved if len(failed) > 0 { - return nil, errNoMatches{error: errs.New("Failed to resolve requirements"), requirements: failed} + return nil, errNoMatches{error: errs.New("Failed to resolve requirements"), requirements: failed, languages: languages} } // Disambiguate requirements that match multiple ingredients diff --git a/internal/runners/install/rationalize.go b/internal/runners/install/rationalize.go index 6d988539d7..ecad652a7d 100644 --- a/internal/runners/install/rationalize.go +++ b/internal/runners/install/rationalize.go @@ -10,12 +10,12 @@ import ( "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalizers" + "github.com/ActiveState/cli/internal/sliceutils" bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" - "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" ) -func rationalizeError(auth *authentication.Auth, rerr *error) { +func (i *Install) rationalizeError(rerr *error) { var noMatchErr errNoMatches switch { @@ -26,13 +26,13 @@ func rationalizeError(auth *authentication.Auth, rerr *error) { case errors.As(*rerr, &noMatchErr): names := []string{} for _, r := range noMatchErr.requirements { - names = append(names, fmt.Sprintf(`"[[ACTIONABLE]%s[/RESET]"`, r.input.Name)) + names = append(names, fmt.Sprintf(`[ACTIONABLE]%s[/RESET]`, r.input.Name)) } if len(noMatchErr.requirements) > 1 { *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_requirements_no_match", strings.Join(names, ", "))) return } - suggestions, err := getSuggestions(noMatchErr.requirements[0], auth) + suggestions, err := i.getSuggestions(noMatchErr.requirements[0], noMatchErr.languages) if err != nil { multilog.Error("Failed to retrieve suggestions: %v", err) } @@ -42,7 +42,7 @@ func rationalizeError(auth *authentication.Auth, rerr *error) { return } - *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives", strings.Join(names, ", "))) + *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives", strings.Join(names, ", "), strings.Join(suggestions, "\n"))) // Error staging a commit during install. case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): @@ -51,20 +51,37 @@ func rationalizeError(auth *authentication.Auth, rerr *error) { } } -func getSuggestions(req *requirement, auth *authentication.Auth) ([]string, error) { - results, err := model.SearchIngredients(req.input.Namespace, req.input.Name, false, nil, auth) +func (i *Install) getSuggestions(req *requirement, languages []model.Language) ([]string, error) { + ingredients, err := model.SearchIngredients(req.input.Namespace, req.input.Name, false, nil, i.prime.Auth()) if err != nil { return []string{}, locale.WrapError(err, "package_ingredient_err_search", "Failed to resolve ingredient named: {{.V0}}", req.input.Name) } - maxResults := 5 - if len(results) > maxResults { - results = results[:maxResults] + // Filter out irrelevant ingredients + if req.input.Namespace == "" { + // Filter out ingredients that don't target one of the supported languages + ingredients = sliceutils.Filter(ingredients, func(iv *model.IngredientAndVersion) bool { + if !model.NamespaceMatch(*iv.Ingredient.PrimaryNamespace, i.nsType.Matchable()) { + return false + } + il := model.LanguageFromNamespace(*iv.Ingredient.PrimaryNamespace) + for _, l := range languages { + if l.Name == il { + return true + } + } + return false + }) + } + + suggestions := []string{} + for _, ing := range ingredients { + suggestions = append(suggestions, fmt.Sprintf(" - %s/%s", *ing.Ingredient.PrimaryNamespace, *ing.Ingredient.Name)) } - suggestions := make([]string, 0, maxResults+1) - for _, result := range results { - suggestions = append(suggestions, fmt.Sprintf(" - %s", *result.Ingredient.Name)) + maxResults := 5 + if len(suggestions) > maxResults { + suggestions = suggestions[:maxResults] } return suggestions, nil diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index 6865e4a67a..3adbf3939c 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -29,6 +29,23 @@ func (suite *InstallIntegrationTestSuite) TestInstall() { cp.ExpectExitCode(0) } +func (suite *InstallIntegrationTestSuite) TestInstallSuggest() { + suite.OnlyRunForTags(tagsuite.Install, tagsuite.Critical) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") + + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("install", "djang") + cp.Expect("No results found") + cp.Expect("Did you mean") + cp.Expect("language/python/djang") + cp.ExpectExitCode(1) +} + func (suite *InstallIntegrationTestSuite) TestInstall_InvalidCommit() { suite.OnlyRunForTags(tagsuite.Install) ts := e2e.New(suite.T(), false) From ac10a21ca438ed8ddd05a1b1907f8a0d8b183b9f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 9 Sep 2024 11:31:25 -0700 Subject: [PATCH 070/440] languages install now uses new runner --- cmd/state/internal/cmdtree/languages.go | 20 ++++++++++++++------ test/integration/languages_int_test.go | 6 ++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/cmd/state/internal/cmdtree/languages.go b/cmd/state/internal/cmdtree/languages.go index 5adafc39b5..dabf179d3f 100644 --- a/cmd/state/internal/cmdtree/languages.go +++ b/cmd/state/internal/cmdtree/languages.go @@ -4,7 +4,9 @@ import ( "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runners/install" "github.com/ActiveState/cli/internal/runners/languages" + "github.com/ActiveState/cli/pkg/platform/model" ) func newLanguagesCommand(prime *primer.Values) *captain.Command { @@ -24,9 +26,8 @@ func newLanguagesCommand(prime *primer.Values) *captain.Command { } func newLanguageInstallCommand(prime *primer.Values) *captain.Command { - runner := languages.NewUpdate(prime) - - params := languages.UpdateParams{} + runner := install.NewInstall(prime, model.NamespaceLanguage) + params := install.InstallRunParams{} return captain.NewCommand( "install", @@ -39,11 +40,18 @@ func newLanguageInstallCommand(prime *primer.Values) *captain.Command { Name: "language", Description: locale.T("arg_languages_install_description"), Required: true, - Value: ¶ms.Language, + Value: ¶ms.Packages, }, }, - func(ccmd *captain.Command, _ []string) error { - return runner.Run(¶ms) + func(ccmd *captain.Command, args []string) error { + for _, p := range args { + pkg, err := params.Packages.Add(p) + if err != nil { + return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") + } + pkg.Namespace = model.NamespaceLanguage.String() + } + return runner.Run(params) }, ).SetSupportsStructuredOutput() } diff --git a/test/integration/languages_int_test.go b/test/integration/languages_int_test.go index 4b05858ea3..d75f1b641d 100644 --- a/test/integration/languages_int_test.go +++ b/test/integration/languages_int_test.go @@ -51,10 +51,8 @@ func (suite *LanguagesIntegrationTestSuite) TestLanguages_install() { ts.PrepareProject("ActiveState-CLI/Languages", "1eb82b25-a564-42ee-a7d4-d51d2ea73cd5") - ts.LoginAsPersistentUser() - cp := ts.Spawn("languages") - cp.Expect("Name") + cp.Expect("Name", termtest.OptExpectTimeout(60*time.Second)) // Cached solves are often slow too cp.Expect("python") cp.ExpectExitCode(0) @@ -62,7 +60,7 @@ func (suite *LanguagesIntegrationTestSuite) TestLanguages_install() { cp.ExpectExitCode(0) cp = ts.Spawn("languages", "install", "python@3.9.16") - cp.Expect("Language updated: python@3.9.16") + cp.Expect("project has been updated") // This can take a little while cp.ExpectExitCode(0, termtest.OptExpectTimeout(60*time.Second)) From 589c236bce4f7f72373f626da5d4c158dbd1c04c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 9 Sep 2024 14:52:59 -0700 Subject: [PATCH 071/440] Implemented uninstall runner --- cmd/state/internal/cmdtree/bundles.go | 4 +- cmd/state/internal/cmdtree/languages.go | 4 +- cmd/state/internal/cmdtree/packages.go | 13 ++- internal/locale/locales/en-us.yaml | 6 + internal/runners/install/install.go | 20 ++-- internal/runners/uninstall/rationalize.go | 38 ++++++ internal/runners/uninstall/uninstall.go | 134 ++++++++++++++++++++++ pkg/buildscript/mutations.go | 28 +++-- test/integration/package_int_test.go | 4 +- 9 files changed, 220 insertions(+), 31 deletions(-) create mode 100644 internal/runners/uninstall/rationalize.go create mode 100644 internal/runners/uninstall/uninstall.go diff --git a/cmd/state/internal/cmdtree/bundles.go b/cmd/state/internal/cmdtree/bundles.go index b1549568b8..8351a2bc02 100644 --- a/cmd/state/internal/cmdtree/bundles.go +++ b/cmd/state/internal/cmdtree/bundles.go @@ -44,8 +44,8 @@ func newBundlesCommand(prime *primer.Values) *captain.Command { } func newBundleInstallCommand(prime *primer.Values) *captain.Command { - runner := install.NewInstall(prime, model.NamespaceBundle) - params := install.InstallRunParams{} + runner := install.New(prime, model.NamespaceBundle) + params := install.Params{} return captain.NewCommand( "install", diff --git a/cmd/state/internal/cmdtree/languages.go b/cmd/state/internal/cmdtree/languages.go index dabf179d3f..1c6ad1229e 100644 --- a/cmd/state/internal/cmdtree/languages.go +++ b/cmd/state/internal/cmdtree/languages.go @@ -26,8 +26,8 @@ func newLanguagesCommand(prime *primer.Values) *captain.Command { } func newLanguageInstallCommand(prime *primer.Values) *captain.Command { - runner := install.NewInstall(prime, model.NamespaceLanguage) - params := install.InstallRunParams{} + runner := install.New(prime, model.NamespaceLanguage) + params := install.Params{} return captain.NewCommand( "install", diff --git a/cmd/state/internal/cmdtree/packages.go b/cmd/state/internal/cmdtree/packages.go index d2e250f954..636dc02dc3 100644 --- a/cmd/state/internal/cmdtree/packages.go +++ b/cmd/state/internal/cmdtree/packages.go @@ -6,6 +6,7 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runners/install" "github.com/ActiveState/cli/internal/runners/packages" + "github.com/ActiveState/cli/internal/runners/uninstall" "github.com/ActiveState/cli/pkg/platform/model" ) @@ -50,9 +51,9 @@ func newPackagesCommand(prime *primer.Values) *captain.Command { } func newInstallCommand(prime *primer.Values) *captain.Command { - runner := install.NewInstall(prime, model.NamespacePackage) + runner := install.New(prime, model.NamespacePackage) - params := install.InstallRunParams{} + params := install.Params{} var packagesRaw string cmd := captain.NewCommand( @@ -93,9 +94,9 @@ func newInstallCommand(prime *primer.Values) *captain.Command { } func newUninstallCommand(prime *primer.Values) *captain.Command { - runner := packages.NewUninstall(prime) + runner := uninstall.New(prime, model.NamespacePackage) - params := packages.UninstallRunParams{} + params := uninstall.Params{} var packagesRaw string cmd := captain.NewCommand( @@ -114,11 +115,11 @@ func newUninstallCommand(prime *primer.Values) *captain.Command { }, func(_ *captain.Command, args []string) error { for _, p := range args { - if err := params.Packages.Set(p); err != nil { + if _, err := params.Packages.Add(p); err != nil { return locale.WrapInputError(err, "err_uninstall_packages_args", "Invalid package uninstall arguments") } } - return runner.Run(params, model.NamespacePackage) + return runner.Run(params) }, ) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 1f50ebe6f0..10154aa5a5 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -606,6 +606,8 @@ package_import_flag_filename_description: other: The file to import commit_message_added: other: "Added: {{.V0}}" +commit_message_removed: + other: "Removed: {{.V0}}" commit_message_added_package: other: "Added package: {{.V0}}@{{.V1}}" commit_message_removed_package: @@ -1587,3 +1589,7 @@ flag_state_upgrade_ts_description: other: Manually specify the timestamp to 'upgrade' to. Can be either 'now' or RFC3339 formatted timestamp. platform_add_not_found: other: Could not find a platform matching your criteria +err_uninstall_nomatch: + other: "The following package(s) could not be found in your project: {{.V0}}" +progress_requirements: + other: "• Updating requirements" diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index 74d2fe8cff..d0aaa90432 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -35,8 +35,8 @@ type primeable interface { primer.SvcModeler } -// InstallRunParams tracks the info required for running Install. -type InstallRunParams struct { +// Params tracks the info required for running Install. +type Params struct { Packages captain.PackagesValue Timestamp captain.TimeValue } @@ -68,13 +68,13 @@ type Install struct { nsType model.NamespaceType } -// NewInstall prepares an installation execution context for use. -func NewInstall(prime primeable, nsType model.NamespaceType) *Install { +// New prepares an installation execution context for use. +func New(prime primeable, nsType model.NamespaceType) *Install { return &Install{prime, nsType} } // Run executes the install behavior. -func (i *Install) Run(params InstallRunParams) (rerr error) { +func (i *Install) Run(params Params) (rerr error) { defer i.rationalizeError(&rerr) logging.Debug("ExecuteInstall") @@ -143,8 +143,8 @@ func (i *Install) Run(params InstallRunParams) (rerr error) { } // Prepare updated buildscript - script, err := prepareBuildScript(oldCommit.BuildScript(), reqs, ts) - if err != nil { + script := oldCommit.BuildScript() + if err := prepareBuildScript(script, reqs, ts); err != nil { return errs.Wrap(err, "Could not prepare build script") } @@ -281,7 +281,7 @@ func (i *Install) promptForMatchingIngredient(req *requirement) (*model.Ingredie return values[choice], nil } -func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) (*buildscript.BuildScript, error) { +func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) error { script.SetAtTime(ts) for _, req := range requirements { requirement := types.Requirement{ @@ -292,9 +292,9 @@ func prepareBuildScript(script *buildscript.BuildScript, requirements requiremen err := script.AddRequirement(requirement) if err != nil { - return nil, errs.Wrap(err, "Failed to update build expression with requirement") + return errs.Wrap(err, "Failed to update build expression with requirement") } } - return script, nil + return nil } diff --git a/internal/runners/uninstall/rationalize.go b/internal/runners/uninstall/rationalize.go new file mode 100644 index 0000000000..7efcdcdce7 --- /dev/null +++ b/internal/runners/uninstall/rationalize.go @@ -0,0 +1,38 @@ +package uninstall + +import ( + "errors" + "fmt" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/rationalizers" + bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" +) + +func (u *Uninstall) rationalizeError(rerr *error) { + var noMatchesErr *errNoMatches + + switch { + case rerr == nil: + return + + // Error staging a commit during uninstall. + case errors.As(*rerr, &noMatchesErr): + pkgs := []string{} + for _, pkg := range noMatchesErr.packages { + name := pkg.Name + if pkg.Namespace != "" { + name = fmt.Sprintf("%s/%s", pkg.Namespace, pkg.Name) + } + pkgs = append(pkgs, fmt.Sprintf("[ACTIONABLE]%s[/RESET]", name)) + } + *rerr = errs.WrapUserFacing(*rerr, locale.Tr("err_uninstall_nomatch", noMatchesErr.packages.String())) + + // Error staging a commit during install. + case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): + rationalizers.HandleCommitErrors(rerr) + + } +} diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go new file mode 100644 index 0000000000..9f4968b650 --- /dev/null +++ b/internal/runners/uninstall/uninstall.go @@ -0,0 +1,134 @@ +package uninstall + +import ( + "errors" + + "github.com/ActiveState/cli/internal/captain" + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/localcommit" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/pkg/platform/model" + bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" +) + +type primeable interface { + primer.Outputer + primer.Prompter + primer.Projecter + primer.Auther + primer.Configurer + primer.Analyticer + primer.SvcModeler +} + +// Params tracks the info required for running Uninstall. +type Params struct { + Packages captain.PackagesValue +} + +// Uninstall manages the installing execution context. +type Uninstall struct { + prime primeable + nsType model.NamespaceType +} + +// New prepares an installation execution context for use. +func New(prime primeable, nsType model.NamespaceType) *Uninstall { + return &Uninstall{prime, nsType} +} + +type errNoMatches struct { + error + packages captain.PackagesValue +} + +// Run executes the install behavior. +func (u *Uninstall) Run(params Params) (rerr error) { + defer u.rationalizeError(&rerr) + + logging.Debug("ExecuteUninstall") + + pj := u.prime.Project() + out := u.prime.Output() + bp := bpModel.NewBuildPlannerModel(u.prime.Auth()) + + // Verify input + if pj == nil { + return rationalize.ErrNoProject + } + if pj.IsHeadless() { + return rationalize.ErrHeadless + } + + out.Notice(locale.Tr("operating_message", pj.NamespaceString(), pj.Dir())) + + var pg *output.Spinner + defer func() { + if pg != nil { + pg.Stop(locale.T("progress_fail")) + } + }() + + // Start process of updating requirements + pg = output.StartSpinner(out, locale.T("progress_requirements"), constants.TerminalAnimationInterval) + + // Grab local commit info + localCommitID, err := localcommit.Get(u.prime.Project().Dir()) + if err != nil { + return errs.Wrap(err, "Unable to get local commit") + } + oldCommit, err := bp.FetchCommit(localCommitID, pj.Owner(), pj.Name(), nil) + if err != nil { + return errs.Wrap(err, "Failed to fetch old build result") + } + + // Update buildscript + script := oldCommit.BuildScript() + if err := prepareBuildScript(script, params.Packages); err != nil { + return errs.Wrap(err, "Could not prepare build script") + } + + // Done updating requirements + pg.Stop(locale.T("progress_success")) + pg = nil + + // Update local checkout and source runtime changes + if err := reqop_runbit.UpdateAndReload(u.prime, script, oldCommit, locale.Tr("commit_message_added", params.Packages.String())); err != nil { + return errs.Wrap(err, "Failed to update local checkout") + } + + // All done + out.Notice(locale.T("operation_success_local")) + + return nil +} + +func prepareBuildScript(script *buildscript.BuildScript, pkgs captain.PackagesValue) error { + // Remove requirements + var removeErrs error + notFound := captain.PackagesValue{} + for _, pkg := range pkgs { + if err := script.RemoveRequirement(types.Requirement{Name: pkg.Name, Namespace: pkg.Namespace}); err != nil { + if errors.As(err, ptr.To(&buildscript.RequirementNotFoundError{})) { + notFound = append(notFound, pkg) + removeErrs = errs.Pack(removeErrs, err) + } else { + return errs.Wrap(err, "Unable to remove requirement") + } + } + } + if len(notFound) > 0 { + return errs.Pack(&errNoMatches{error: errs.New("Could not find all requested packages"), packages: notFound}, removeErrs) + } + + return nil +} diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 913cb521a0..1edcb6d32c 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -78,34 +78,44 @@ type RequirementNotFoundError struct { *locale.LocalizedError // for legacy non-user-facing error usages } +// RemoveRequirement will remove any matching requirement. Note that it only operates on the Name and Namespace fields. +// It will not verify if revision or version match. func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { requirementsNode, err := b.getRequirementsNode() if err != nil { return errs.Wrap(err, "Could not get requirements node") } - var found bool + match := false for i, req := range *requirementsNode.List { if req.FuncCall == nil || req.FuncCall.Name != reqFuncName { continue } for _, arg := range req.FuncCall.Arguments { - if arg.Assignment.Key == requirementNameKey && strValue(arg.Assignment.Value) == requirement.Name { - list := *requirementsNode.List - list = append(list[:i], list[i+1:]...) - requirementsNode.List = &list - found = true - break + if arg.Assignment.Key == requirementNameKey { + match := strValue(arg.Assignment.Value) == requirement.Name + if !match || requirement.Namespace == "" { + break + } + } + if arg.Assignment.Key == requirementNamespaceKey { + match = strValue(arg.Assignment.Value) == requirement.Namespace + if !match { + break + } } } - if found { + if match { + list := *requirementsNode.List + list = append(list[:i], list[i+1:]...) + requirementsNode.List = &list break } } - if !found { + if !match { return &RequirementNotFoundError{ requirement.Name, locale.NewInputError("err_remove_requirement_not_found", "", requirement.Name), diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index d7243b1167..22ac663936 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -434,13 +434,13 @@ scripts: func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDoesNotExist() { suite.OnlyRunForTags(tagsuite.Package) - ts := e2e.New(suite.T(), false) + ts := e2e.New(suite.T(), true) defer ts.Close() suite.PrepareActiveStateYAML(ts) cp := ts.Spawn("uninstall", "doesNotExist") - cp.Expect("does not exist") + cp.Expect("could not be found") cp.ExpectExitCode(1) ts.IgnoreLogErrors() From 519d3049e104589bab28f62c2a084f71726b8f26 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 9 Sep 2024 15:28:49 -0700 Subject: [PATCH 072/440] Add hashGlobs query to state service --- cmd/state-svc/internal/resolver/resolver.go | 21 + .../internal/server/generated/generated.go | 1041 +- cmd/state-svc/schema/schema.graphqls | 1 + go.mod | 30 +- go.sum | 77 +- internal/graph/generated.go | 3 + internal/hash/file_hasher.go | 75 + internal/hash/file_hasher_test.go | 144 + .../99designs/gqlgen/complexity/complexity.go | 3 +- .../99designs/gqlgen/graphql/cache.go | 8 +- .../gqlgen/graphql/context_operation.go | 16 +- .../99designs/gqlgen/graphql/context_path.go | 1 - .../gqlgen/graphql/context_response.go | 8 + .../99designs/gqlgen/graphql/deferred.go | 26 + .../99designs/gqlgen/graphql/duration.go | 27 + .../gqlgen/graphql/executable_schema.go | 53 +- .../gqlgen/graphql/executable_schema_mock.go | 41 +- .../gqlgen/graphql/executor/executor.go | 5 +- .../99designs/gqlgen/graphql/fieldset.go | 27 +- .../99designs/gqlgen/graphql/handler.go | 16 +- .../gqlgen/graphql/handler/extension/apq.go | 5 +- .../graphql/handler/extension/complexity.go | 11 +- .../handler/extension/introspection.go | 3 +- .../gqlgen/graphql/handler/lru/lru.go | 7 +- .../gqlgen/graphql/handler/server.go | 3 +- .../gqlgen/graphql/handler/transport/error.go | 3 +- .../graphql/handler/transport/headers.go | 17 + .../{http_form.go => http_form_multipart.go} | 6 +- .../handler/transport/http_form_urlencoded.go | 119 + .../graphql/handler/transport/http_get.go | 13 +- .../graphql/handler/transport/http_graphql.go | 98 + .../graphql/handler/transport/http_post.go | 66 +- .../gqlgen/graphql/handler/transport/sse.go | 107 + .../gqlgen/graphql/handler/transport/util.go | 3 +- .../graphql/handler/transport/websocket.go | 100 +- .../transport/websocket_resolver_error.go | 69 + .../github.com/99designs/gqlgen/graphql/id.go | 29 + .../graphql/introspection/introspection.go | 6 +- .../gqlgen/graphql/introspection/schema.go | 4 - .../99designs/gqlgen/graphql/omittable.go | 58 + .../graphql/playground/altair_playground.go | 84 + .../playground/apollo_sandbox_playground.go | 186 + .../gqlgen/graphql/playground/playground.go | 38 +- .../99designs/gqlgen/graphql/response.go | 4 + .../99designs/gqlgen/graphql/stats.go | 4 +- .../99designs/gqlgen/graphql/string.go | 5 +- .../99designs/gqlgen/graphql/uint.go | 27 +- .../99designs/gqlgen/graphql/uuid.go | 25 + .../99designs/gqlgen/graphql/version.go | 2 +- vendor/github.com/cespare/xxhash/LICENSE.txt | 22 + vendor/github.com/cespare/xxhash/README.md | 50 + vendor/github.com/cespare/xxhash/rotate.go | 14 + vendor/github.com/cespare/xxhash/rotate19.go | 14 + vendor/github.com/cespare/xxhash/xxhash.go | 168 + .../github.com/cespare/xxhash/xxhash_amd64.go | 12 + .../github.com/cespare/xxhash/xxhash_amd64.s | 233 + .../github.com/cespare/xxhash/xxhash_other.go | 75 + .../github.com/cespare/xxhash/xxhash_safe.go | 10 + .../cespare/xxhash/xxhash_unsafe.go | 30 + vendor/github.com/google/uuid/.travis.yml | 9 - vendor/github.com/google/uuid/CHANGELOG.md | 41 + vendor/github.com/google/uuid/CONTRIBUTING.md | 16 + vendor/github.com/google/uuid/README.md | 10 +- vendor/github.com/google/uuid/hash.go | 6 + vendor/github.com/google/uuid/node_js.go | 2 +- vendor/github.com/google/uuid/time.go | 21 +- vendor/github.com/google/uuid/uuid.go | 89 +- vendor/github.com/google/uuid/version6.go | 56 + vendor/github.com/google/uuid/version7.go | 104 + .../github.com/hashicorp/golang-lru/README.md | 25 - vendor/github.com/hashicorp/golang-lru/arc.go | 257 - vendor/github.com/hashicorp/golang-lru/doc.go | 21 - vendor/github.com/hashicorp/golang-lru/lru.go | 150 - .../hashicorp/golang-lru/simplelru/lru.go | 177 - .../hashicorp/golang-lru/{ => v2}/.gitignore | 0 .../hashicorp/golang-lru/v2/.golangci.yml | 46 + .../hashicorp/golang-lru/{ => v2}/2q.go | 104 +- .../hashicorp/golang-lru/{ => v2}/LICENSE | 2 + .../hashicorp/golang-lru/v2/README.md | 79 + .../github.com/hashicorp/golang-lru/v2/doc.go | 24 + .../hashicorp/golang-lru/v2/internal/list.go | 142 + .../github.com/hashicorp/golang-lru/v2/lru.go | 250 + .../golang-lru/v2/simplelru/LICENSE_list | 29 + .../hashicorp/golang-lru/v2/simplelru/lru.go | 177 + .../{ => v2}/simplelru/lru_interface.go | 29 +- vendor/github.com/sosodev/duration/.gitignore | 1 + vendor/github.com/sosodev/duration/LICENSE | 21 + .../github.com/sosodev/duration/duration.go | 311 + vendor/github.com/sosodev/duration/readme.md | 54 + vendor/golang.org/x/crypto/LICENSE | 4 +- vendor/golang.org/x/crypto/acme/http.go | 21 +- .../golang.org/x/crypto/acme/version_go112.go | 27 - .../golang.org/x/crypto/argon2/blamka_amd64.s | 2972 ++- .../x/crypto/blake2b/blake2bAVX2_amd64.s | 5167 ++++- .../x/crypto/blake2b/blake2b_amd64.s | 1681 +- vendor/golang.org/x/crypto/blowfish/cipher.go | 2 +- vendor/golang.org/x/crypto/cast5/cast5.go | 2 +- .../x/crypto/curve25519/curve25519.go | 39 +- .../x/crypto/curve25519/curve25519_compat.go | 105 - .../x/crypto/curve25519/curve25519_go120.go | 46 - .../x/crypto/curve25519/internal/field/README | 7 - .../x/crypto/curve25519/internal/field/fe.go | 416 - .../curve25519/internal/field/fe_amd64.go | 15 - .../curve25519/internal/field/fe_amd64.s | 378 - .../internal/field/fe_amd64_noasm.go | 11 - .../curve25519/internal/field/fe_arm64.go | 15 - .../curve25519/internal/field/fe_arm64.s | 42 - .../internal/field/fe_arm64_noasm.go | 11 - .../curve25519/internal/field/fe_generic.go | 264 - .../curve25519/internal/field/sync.checkpoint | 1 - .../crypto/curve25519/internal/field/sync.sh | 19 - vendor/golang.org/x/crypto/hkdf/hkdf.go | 2 +- .../x/crypto/internal/poly1305/sum_amd64.s | 133 +- .../x/crypto/openpgp/armor/armor.go | 5 +- .../x/crypto/openpgp/elgamal/elgamal.go | 2 +- .../x/crypto/openpgp/errors/errors.go | 2 +- .../x/crypto/openpgp/packet/packet.go | 2 +- vendor/golang.org/x/crypto/openpgp/read.go | 2 +- vendor/golang.org/x/crypto/openpgp/s2k/s2k.go | 2 +- vendor/golang.org/x/crypto/sha3/doc.go | 2 +- vendor/golang.org/x/crypto/sha3/hashes.go | 42 +- .../x/crypto/sha3/hashes_generic.go | 27 - .../golang.org/x/crypto/sha3/hashes_noasm.go | 23 + .../golang.org/x/crypto/sha3/keccakf_amd64.s | 5787 ++++- vendor/golang.org/x/crypto/sha3/register.go | 18 - vendor/golang.org/x/crypto/sha3/sha3.go | 62 +- vendor/golang.org/x/crypto/sha3/sha3_s390x.go | 48 +- vendor/golang.org/x/crypto/sha3/shake.go | 16 +- .../golang.org/x/crypto/sha3/shake_generic.go | 19 - .../golang.org/x/crypto/sha3/shake_noasm.go | 15 + vendor/golang.org/x/crypto/sha3/xor.go | 45 +- .../golang.org/x/crypto/sha3/xor_generic.go | 28 - .../golang.org/x/crypto/sha3/xor_unaligned.go | 66 - .../golang.org/x/crypto/ssh/agent/client.go | 2 +- .../golang.org/x/crypto/ssh/agent/keyring.go | 9 + vendor/golang.org/x/crypto/ssh/client_auth.go | 4 + vendor/golang.org/x/crypto/ssh/doc.go | 2 +- vendor/golang.org/x/crypto/ssh/keys.go | 52 +- vendor/golang.org/x/crypto/ssh/server.go | 30 + vendor/golang.org/x/mod/LICENSE | 4 +- vendor/golang.org/x/net/LICENSE | 4 +- vendor/golang.org/x/net/http2/http2.go | 19 +- vendor/golang.org/x/net/http2/server.go | 94 +- vendor/golang.org/x/net/http2/testsync.go | 331 - vendor/golang.org/x/net/http2/timer.go | 20 + vendor/golang.org/x/net/http2/transport.go | 314 +- .../x/net/http2/writesched_priority.go | 4 +- vendor/golang.org/x/net/proxy/per_host.go | 8 +- vendor/golang.org/x/sync/LICENSE | 4 +- vendor/golang.org/x/sync/errgroup/errgroup.go | 3 + vendor/golang.org/x/sync/errgroup/go120.go | 1 - .../golang.org/x/sync/errgroup/pre_go120.go | 1 - vendor/golang.org/x/sys/LICENSE | 4 +- vendor/golang.org/x/sys/cpu/cpu.go | 21 + vendor/golang.org/x/sys/cpu/cpu_arm64.go | 12 + .../golang.org/x/sys/cpu/cpu_linux_arm64.go | 5 + .../golang.org/x/sys/cpu/cpu_linux_noinit.go | 2 +- .../golang.org/x/sys/cpu/cpu_linux_riscv64.go | 137 + vendor/golang.org/x/sys/cpu/cpu_riscv64.go | 11 +- vendor/golang.org/x/sys/unix/mkerrors.sh | 4 + vendor/golang.org/x/sys/unix/mremap.go | 5 + .../golang.org/x/sys/unix/syscall_darwin.go | 61 + vendor/golang.org/x/sys/unix/syscall_hurd.go | 1 + vendor/golang.org/x/sys/unix/syscall_linux.go | 1 + .../golang.org/x/sys/unix/syscall_openbsd.go | 1 + vendor/golang.org/x/sys/unix/syscall_unix.go | 9 + .../x/sys/unix/zerrors_darwin_amd64.go | 12 + .../x/sys/unix/zerrors_darwin_arm64.go | 12 + vendor/golang.org/x/sys/unix/zerrors_linux.go | 58 +- .../x/sys/unix/zerrors_linux_386.go | 3 + .../x/sys/unix/zerrors_linux_amd64.go | 3 + .../x/sys/unix/zerrors_linux_arm.go | 2 + .../x/sys/unix/zerrors_linux_arm64.go | 3 + .../x/sys/unix/zerrors_linux_loong64.go | 2 + .../x/sys/unix/zerrors_linux_mips.go | 2 + .../x/sys/unix/zerrors_linux_mips64.go | 2 + .../x/sys/unix/zerrors_linux_mips64le.go | 2 + .../x/sys/unix/zerrors_linux_mipsle.go | 2 + .../x/sys/unix/zerrors_linux_ppc.go | 2 + .../x/sys/unix/zerrors_linux_ppc64.go | 2 + .../x/sys/unix/zerrors_linux_ppc64le.go | 2 + .../x/sys/unix/zerrors_linux_riscv64.go | 2 + .../x/sys/unix/zerrors_linux_s390x.go | 2 + .../x/sys/unix/zerrors_linux_sparc64.go | 2 + .../x/sys/unix/zerrors_zos_s390x.go | 2 + .../x/sys/unix/zsyscall_darwin_amd64.go | 101 + .../x/sys/unix/zsyscall_darwin_amd64.s | 25 + .../x/sys/unix/zsyscall_darwin_arm64.go | 101 + .../x/sys/unix/zsyscall_darwin_arm64.s | 25 + .../golang.org/x/sys/unix/zsyscall_linux.go | 16 + .../x/sys/unix/zsyscall_openbsd_386.go | 24 + .../x/sys/unix/zsyscall_openbsd_386.s | 5 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 24 + .../x/sys/unix/zsyscall_openbsd_amd64.s | 5 + .../x/sys/unix/zsyscall_openbsd_arm.go | 24 + .../x/sys/unix/zsyscall_openbsd_arm.s | 5 + .../x/sys/unix/zsyscall_openbsd_arm64.go | 24 + .../x/sys/unix/zsyscall_openbsd_arm64.s | 5 + .../x/sys/unix/zsyscall_openbsd_mips64.go | 24 + .../x/sys/unix/zsyscall_openbsd_mips64.s | 5 + .../x/sys/unix/zsyscall_openbsd_ppc64.go | 24 + .../x/sys/unix/zsyscall_openbsd_ppc64.s | 6 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 24 + .../x/sys/unix/zsyscall_openbsd_riscv64.s | 5 + .../x/sys/unix/zsysnum_linux_386.go | 1 + .../x/sys/unix/zsysnum_linux_amd64.go | 1 + .../x/sys/unix/zsysnum_linux_arm.go | 1 + .../x/sys/unix/zsysnum_linux_arm64.go | 1 + .../x/sys/unix/zsysnum_linux_loong64.go | 1 + .../x/sys/unix/zsysnum_linux_mips.go | 1 + .../x/sys/unix/zsysnum_linux_mips64.go | 1 + .../x/sys/unix/zsysnum_linux_mips64le.go | 1 + .../x/sys/unix/zsysnum_linux_mipsle.go | 1 + .../x/sys/unix/zsysnum_linux_ppc.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 1 + .../x/sys/unix/zsysnum_linux_riscv64.go | 1 + .../x/sys/unix/zsysnum_linux_s390x.go | 1 + .../x/sys/unix/zsysnum_linux_sparc64.go | 1 + .../x/sys/unix/ztypes_darwin_amd64.go | 13 + .../x/sys/unix/ztypes_darwin_arm64.go | 13 + .../x/sys/unix/ztypes_freebsd_386.go | 1 + .../x/sys/unix/ztypes_freebsd_amd64.go | 1 + .../x/sys/unix/ztypes_freebsd_arm.go | 1 + .../x/sys/unix/ztypes_freebsd_arm64.go | 1 + .../x/sys/unix/ztypes_freebsd_riscv64.go | 1 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 49 +- .../x/sys/unix/ztypes_linux_riscv64.go | 33 + .../x/sys/windows/security_windows.go | 25 +- .../x/sys/windows/syscall_windows.go | 16 +- .../golang.org/x/sys/windows/types_windows.go | 72 +- .../x/sys/windows/zsyscall_windows.go | 89 + vendor/golang.org/x/term/LICENSE | 4 +- vendor/golang.org/x/term/term_windows.go | 1 + vendor/golang.org/x/text/LICENSE | 4 +- vendor/golang.org/x/tools/LICENSE | 4 +- .../x/tools/cmd/stringer/stringer.go | 5 +- .../x/tools/go/gcexportdata/gcexportdata.go | 2 +- .../tools/go/internal/packagesdriver/sizes.go | 48 - vendor/golang.org/x/tools/go/packages/doc.go | 54 +- .../x/tools/go/packages/external.go | 103 +- .../golang.org/x/tools/go/packages/golist.go | 238 +- .../x/tools/go/packages/golist_overlay.go | 492 - .../x/tools/go/packages/packages.go | 479 +- .../golang.org/x/tools/go/packages/visit.go | 9 + .../x/tools/go/types/objectpath/objectpath.go | 237 +- .../x/tools/internal/aliases/aliases.go | 38 + .../x/tools/internal/aliases/aliases_go121.go | 37 + .../x/tools/internal/aliases/aliases_go122.go | 98 + .../x/tools/internal/event/keys/util.go | 21 + .../x/tools/internal/event/tag/tag.go | 59 - .../x/tools/internal/gcimporter/gcimporter.go | 10 +- .../x/tools/internal/gcimporter/iexport.go | 307 +- .../x/tools/internal/gcimporter/iimport.go | 115 +- .../internal/gcimporter/support_go117.go | 16 - .../internal/gcimporter/support_go118.go | 3 - .../x/tools/internal/gcimporter/unified_no.go | 4 +- .../tools/internal/gcimporter/unified_yes.go | 4 +- .../x/tools/internal/gcimporter/ureader_no.go | 19 - .../tools/internal/gcimporter/ureader_yes.go | 36 +- .../x/tools/internal/gocommand/invoke.go | 153 +- .../x/tools/internal/gocommand/vendor.go | 54 + .../internal/packagesinternal/packages.go | 8 - .../x/tools/internal/pkgbits/decoder.go | 34 +- .../x/tools/internal/pkgbits/encoder.go | 43 +- .../x/tools/internal/pkgbits/frames_go1.go | 21 - .../x/tools/internal/pkgbits/frames_go17.go | 28 - .../x/tools/internal/pkgbits/support.go | 2 +- .../x/tools/internal/pkgbits/sync.go | 23 + .../internal/pkgbits/syncmarker_string.go | 7 +- .../x/tools/internal/pkgbits/version.go | 85 + .../x/tools/internal/stdlib/manifest.go | 17431 ++++++++++++++++ .../x/tools/internal/stdlib/stdlib.go | 97 + .../internal/tokeninternal/tokeninternal.go | 28 +- .../x/tools/internal/typeparams/common.go | 204 - .../x/tools/internal/typeparams/coretype.go | 122 - .../internal/typeparams/enabled_go117.go | 12 - .../internal/typeparams/enabled_go118.go | 15 - .../x/tools/internal/typeparams/normalize.go | 218 - .../x/tools/internal/typeparams/termlist.go | 163 - .../internal/typeparams/typeparams_go117.go | 197 - .../internal/typeparams/typeparams_go118.go | 151 - .../x/tools/internal/typeparams/typeterm.go | 169 - .../tools/internal/typesinternal/errorcode.go | 14 +- .../internal/typesinternal/objectpath.go | 24 - .../x/tools/internal/typesinternal/recv.go | 43 + .../x/tools/internal/typesinternal/toonew.go | 89 + .../x/tools/internal/typesinternal/types.go | 15 +- .../tools/internal/typesinternal/types_118.go | 19 - .../x/tools/internal/versions/constraint.go | 13 + .../internal/versions/constraint_go121.go | 14 + .../x/tools/internal/versions/features.go | 43 + .../x/tools/internal/versions/gover.go | 172 + .../x/tools/internal/versions/toolchain.go | 14 + .../internal/versions/toolchain_go119.go | 14 + .../internal/versions/toolchain_go120.go | 14 + .../internal/versions/toolchain_go121.go | 14 + .../x/tools/internal/versions/types.go | 19 + .../x/tools/internal/versions/types_go121.go | 30 + .../x/tools/internal/versions/types_go122.go | 41 + .../x/tools/internal/versions/versions.go | 57 + vendor/modules.txt | 54 +- 302 files changed, 39895 insertions(+), 7818 deletions(-) create mode 100644 internal/hash/file_hasher.go create mode 100644 internal/hash/file_hasher_test.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/deferred.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/duration.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/handler/transport/headers.go rename vendor/github.com/99designs/gqlgen/graphql/handler/transport/{http_form.go => http_form_multipart.go} (96%) create mode 100644 vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/handler/transport/sse.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_resolver_error.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/omittable.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go create mode 100644 vendor/github.com/99designs/gqlgen/graphql/uuid.go create mode 100644 vendor/github.com/cespare/xxhash/LICENSE.txt create mode 100644 vendor/github.com/cespare/xxhash/README.md create mode 100644 vendor/github.com/cespare/xxhash/rotate.go create mode 100644 vendor/github.com/cespare/xxhash/rotate19.go create mode 100644 vendor/github.com/cespare/xxhash/xxhash.go create mode 100644 vendor/github.com/cespare/xxhash/xxhash_amd64.go create mode 100644 vendor/github.com/cespare/xxhash/xxhash_amd64.s create mode 100644 vendor/github.com/cespare/xxhash/xxhash_other.go create mode 100644 vendor/github.com/cespare/xxhash/xxhash_safe.go create mode 100644 vendor/github.com/cespare/xxhash/xxhash_unsafe.go delete mode 100644 vendor/github.com/google/uuid/.travis.yml create mode 100644 vendor/github.com/google/uuid/CHANGELOG.md create mode 100644 vendor/github.com/google/uuid/version6.go create mode 100644 vendor/github.com/google/uuid/version7.go delete mode 100644 vendor/github.com/hashicorp/golang-lru/README.md delete mode 100644 vendor/github.com/hashicorp/golang-lru/arc.go delete mode 100644 vendor/github.com/hashicorp/golang-lru/doc.go delete mode 100644 vendor/github.com/hashicorp/golang-lru/lru.go delete mode 100644 vendor/github.com/hashicorp/golang-lru/simplelru/lru.go rename vendor/github.com/hashicorp/golang-lru/{ => v2}/.gitignore (100%) create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/.golangci.yml rename vendor/github.com/hashicorp/golang-lru/{ => v2}/2q.go (64%) rename vendor/github.com/hashicorp/golang-lru/{ => v2}/LICENSE (99%) create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/README.md create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/doc.go create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/internal/list.go create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/lru.go create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/simplelru/LICENSE_list create mode 100644 vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru.go rename vendor/github.com/hashicorp/golang-lru/{ => v2}/simplelru/lru_interface.go (57%) create mode 100644 vendor/github.com/sosodev/duration/.gitignore create mode 100644 vendor/github.com/sosodev/duration/LICENSE create mode 100644 vendor/github.com/sosodev/duration/duration.go create mode 100644 vendor/github.com/sosodev/duration/readme.md delete mode 100644 vendor/golang.org/x/crypto/acme/version_go112.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/curve25519_compat.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/curve25519_go120.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/README delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint delete mode 100644 vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh delete mode 100644 vendor/golang.org/x/crypto/sha3/hashes_generic.go create mode 100644 vendor/golang.org/x/crypto/sha3/hashes_noasm.go delete mode 100644 vendor/golang.org/x/crypto/sha3/register.go delete mode 100644 vendor/golang.org/x/crypto/sha3/shake_generic.go create mode 100644 vendor/golang.org/x/crypto/sha3/shake_noasm.go delete mode 100644 vendor/golang.org/x/crypto/sha3/xor_generic.go delete mode 100644 vendor/golang.org/x/crypto/sha3/xor_unaligned.go delete mode 100644 vendor/golang.org/x/net/http2/testsync.go create mode 100644 vendor/golang.org/x/net/http2/timer.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go delete mode 100644 vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go create mode 100644 vendor/golang.org/x/tools/internal/aliases/aliases.go create mode 100644 vendor/golang.org/x/tools/internal/aliases/aliases_go121.go create mode 100644 vendor/golang.org/x/tools/internal/aliases/aliases_go122.go create mode 100644 vendor/golang.org/x/tools/internal/event/keys/util.go delete mode 100644 vendor/golang.org/x/tools/internal/event/tag/tag.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/support_go117.go delete mode 100644 vendor/golang.org/x/tools/internal/gcimporter/ureader_no.go delete mode 100644 vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go delete mode 100644 vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go create mode 100644 vendor/golang.org/x/tools/internal/pkgbits/version.go create mode 100644 vendor/golang.org/x/tools/internal/stdlib/manifest.go create mode 100644 vendor/golang.org/x/tools/internal/stdlib/stdlib.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/common.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/coretype.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/normalize.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/termlist.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go delete mode 100644 vendor/golang.org/x/tools/internal/typeparams/typeterm.go delete mode 100644 vendor/golang.org/x/tools/internal/typesinternal/objectpath.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/recv.go create mode 100644 vendor/golang.org/x/tools/internal/typesinternal/toonew.go delete mode 100644 vendor/golang.org/x/tools/internal/typesinternal/types_118.go create mode 100644 vendor/golang.org/x/tools/internal/versions/constraint.go create mode 100644 vendor/golang.org/x/tools/internal/versions/constraint_go121.go create mode 100644 vendor/golang.org/x/tools/internal/versions/features.go create mode 100644 vendor/golang.org/x/tools/internal/versions/gover.go create mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain.go create mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain_go119.go create mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain_go120.go create mode 100644 vendor/golang.org/x/tools/internal/versions/toolchain_go121.go create mode 100644 vendor/golang.org/x/tools/internal/versions/types.go create mode 100644 vendor/golang.org/x/tools/internal/versions/types_go121.go create mode 100644 vendor/golang.org/x/tools/internal/versions/types_go122.go create mode 100644 vendor/golang.org/x/tools/internal/versions/versions.go diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index ef4fb27061..cc784ed121 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "os" + "path/filepath" "runtime/debug" "sort" "strconv" @@ -20,6 +21,7 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/graph" + "github.com/ActiveState/cli/internal/hash" "github.com/ActiveState/cli/internal/logging" configMediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/multilog" @@ -36,6 +38,7 @@ type Resolver struct { updatePoller *poller.Poller authPoller *poller.Poller projectIDCache *projectcache.ID + fileHasher *hash.FileHasher an *sync.Client anForClient *sync.Client // Use separate client for events sent through service so we don't contaminate one with the other rtwatch *rtwatcher.Watcher @@ -81,6 +84,7 @@ func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Res pollUpdate, pollAuth, projectcache.NewID(), + hash.NewFileHasher(), an, anForClient, rtwatcher.New(cfg, anForClient), @@ -263,6 +267,8 @@ func (r *Resolver) GetProcessesInUse(ctx context.Context, execDir string) ([]*gr } func (r *Resolver) GetJwt(ctx context.Context) (*graph.Jwt, error) { + defer func() { handlePanics(recover(), debug.Stack()) }() + if err := r.auth.MaybeRenew(); err != nil { return nil, errs.Wrap(err, "Could not renew auth token") } @@ -296,6 +302,21 @@ func (r *Resolver) GetJwt(ctx context.Context) (*graph.Jwt, error) { return jwt, nil } +func (r *Resolver) HashGlobs(ctx context.Context, globs []string) (string, error) { + defer func() { handlePanics(recover(), debug.Stack()) }() + + var files []string + for _, glob := range globs { + matches, err := filepath.Glob(glob) + if err != nil { + return "", errs.Wrap(err, "Could not match glob: %s", glob) + } + files = append(files, matches...) + } + + return r.fileHasher.HashFiles(files) +} + func handlePanics(recovered interface{}, stack []byte) { if recovered != nil { multilog.Error("Panic: %v", recovered) diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index 9d0589b013..551c6c0d4a 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -9,6 +9,7 @@ import ( "fmt" "strconv" "sync" + "sync/atomic" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -22,6 +23,7 @@ import ( // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { return &executableSchema{ + schema: cfg.Schema, resolvers: cfg.Resolvers, directives: cfg.Directives, complexity: cfg.Complexity, @@ -29,6 +31,7 @@ func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { } type Config struct { + Schema *ast.Schema Resolvers ResolverRoot Directives DirectiveRoot Complexity ComplexityRoot @@ -95,6 +98,7 @@ type ComplexityRoot struct { FetchLogTail func(childComplexity int) int GetJwt func(childComplexity int) int GetProcessesInUse func(childComplexity int, execDir string) int + HashGlobs func(childComplexity int, globs []string) int Projects func(childComplexity int) int ReportRuntimeUsage func(childComplexity int, pid int, exec string, source string, dimensionsJSON string) int Version func(childComplexity int) int @@ -135,20 +139,25 @@ type QueryResolver interface { FetchLogTail(ctx context.Context) (string, error) GetProcessesInUse(ctx context.Context, execDir string) ([]*graph.ProcessInfo, error) GetJwt(ctx context.Context) (*graph.Jwt, error) + HashGlobs(ctx context.Context, globs []string) (string, error) } type executableSchema struct { + schema *ast.Schema resolvers ResolverRoot directives DirectiveRoot complexity ComplexityRoot } func (e *executableSchema) Schema() *ast.Schema { + if e.schema != nil { + return e.schema + } return parsedSchema } func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { - ec := executionContext{nil, e} + ec := executionContext{nil, e, 0, 0, nil} _ = ec switch typeName + "." + field { @@ -373,6 +382,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.GetProcessesInUse(childComplexity, args["execDir"].(string)), true + case "Query.hashGlobs": + if e.complexity.Query.HashGlobs == nil { + break + } + + args, err := ec.field_Query_hashGlobs_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.HashGlobs(childComplexity, args["globs"].([]string)), true + case "Query.projects": if e.complexity.Query.Projects == nil { break @@ -482,25 +503,40 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e} + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap() first := true switch rc.Operation.Operation { case ast.Query: return func(ctx context.Context) *graphql.Response { - if !first { - return nil + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, rc.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { + result := <-ec.deferredResults + atomic.AddInt32(&ec.pendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } } - first = false - ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data := ec._Query(ctx, rc.Operation.SelectionSet) var buf bytes.Buffer data.MarshalGQL(&buf) - - return &graphql.Response{ - Data: buf.Bytes(), + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext } + + return &response } default: @@ -511,20 +547,42 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { type executionContext struct { *graphql.OperationContext *executableSchema + deferred int32 + pendingDeferred int32 + deferredResults chan graphql.DeferredResult +} + +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() } func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") } - return introspection.WrapSchema(parsedSchema), nil + return introspection.WrapSchema(ec.Schema()), nil } func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { if ec.DisableIntrospection { return nil, errors.New("introspection disabled") } - return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil + return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil } var sources = []*ast.Source{ @@ -618,6 +676,7 @@ type Query { fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT + hashGlobs(globs: [String!]!): String! } type ConfigChangedResponse { @@ -780,6 +839,21 @@ func (ec *executionContext) field_Query_getProcessesInUse_args(ctx context.Conte return args, nil } +func (ec *executionContext) field_Query_hashGlobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 []string + if tmp, ok := rawArgs["globs"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("globs")) + arg0, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + if err != nil { + return nil, err + } + } + args["globs"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_reportRuntimeUsage_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -891,7 +965,7 @@ func (ec *executionContext) _AnalyticsEventResponse_sent(ctx context.Context, fi return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AnalyticsEventResponse_sent(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AnalyticsEventResponse_sent(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AnalyticsEventResponse", Field: field, @@ -935,7 +1009,7 @@ func (ec *executionContext) _AvailableUpdate_version(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AvailableUpdate_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AvailableUpdate_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AvailableUpdate", Field: field, @@ -979,7 +1053,7 @@ func (ec *executionContext) _AvailableUpdate_channel(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AvailableUpdate_channel(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AvailableUpdate_channel(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AvailableUpdate", Field: field, @@ -1023,7 +1097,7 @@ func (ec *executionContext) _AvailableUpdate_path(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AvailableUpdate_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AvailableUpdate_path(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AvailableUpdate", Field: field, @@ -1067,7 +1141,7 @@ func (ec *executionContext) _AvailableUpdate_platform(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AvailableUpdate_platform(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AvailableUpdate_platform(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AvailableUpdate", Field: field, @@ -1111,7 +1185,7 @@ func (ec *executionContext) _AvailableUpdate_sha256(ctx context.Context, field g return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_AvailableUpdate_sha256(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_AvailableUpdate_sha256(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "AvailableUpdate", Field: field, @@ -1155,7 +1229,7 @@ func (ec *executionContext) _ConfigChangedResponse_received(ctx context.Context, return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ConfigChangedResponse_received(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ConfigChangedResponse_received(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ConfigChangedResponse", Field: field, @@ -1199,7 +1273,7 @@ func (ec *executionContext) _JWT_token(ctx context.Context, field graphql.Collec return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_JWT_token(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JWT_token(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "JWT", Field: field, @@ -1243,7 +1317,7 @@ func (ec *executionContext) _JWT_user(ctx context.Context, field graphql.Collect return ec.marshalNUser2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐUser(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_JWT_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_JWT_user(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "JWT", Field: field, @@ -1297,7 +1371,7 @@ func (ec *executionContext) _MessageInfo_id(ctx context.Context, field graphql.C return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MessageInfo_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MessageInfo", Field: field, @@ -1341,7 +1415,7 @@ func (ec *executionContext) _MessageInfo_message(ctx context.Context, field grap return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MessageInfo_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MessageInfo", Field: field, @@ -1385,7 +1459,7 @@ func (ec *executionContext) _MessageInfo_condition(ctx context.Context, field gr return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_condition(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MessageInfo_condition(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MessageInfo", Field: field, @@ -1429,7 +1503,7 @@ func (ec *executionContext) _MessageInfo_repeat(ctx context.Context, field graph return ec.marshalNMessageRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageRepeatType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_repeat(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MessageInfo_repeat(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MessageInfo", Field: field, @@ -1473,7 +1547,7 @@ func (ec *executionContext) _MessageInfo_interrupt(ctx context.Context, field gr return ec.marshalNMessageInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInterruptType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_interrupt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MessageInfo_interrupt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MessageInfo", Field: field, @@ -1517,7 +1591,7 @@ func (ec *executionContext) _MessageInfo_placement(ctx context.Context, field gr return ec.marshalNMessagePlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessagePlacementType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_placement(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MessageInfo_placement(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MessageInfo", Field: field, @@ -1561,7 +1635,7 @@ func (ec *executionContext) _Organization_URLname(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Organization_URLname(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Organization_URLname(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Organization", Field: field, @@ -1605,7 +1679,7 @@ func (ec *executionContext) _Organization_role(ctx context.Context, field graphq return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Organization_role(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Organization_role(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Organization", Field: field, @@ -1649,7 +1723,7 @@ func (ec *executionContext) _ProcessInfo_exe(ctx context.Context, field graphql. return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ProcessInfo_exe(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ProcessInfo_exe(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ProcessInfo", Field: field, @@ -1693,7 +1767,7 @@ func (ec *executionContext) _ProcessInfo_pid(ctx context.Context, field graphql. return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ProcessInfo_pid(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ProcessInfo_pid(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ProcessInfo", Field: field, @@ -1737,7 +1811,7 @@ func (ec *executionContext) _Project_namespace(ctx context.Context, field graphq return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Project_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Project_namespace(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Project", Field: field, @@ -1781,7 +1855,7 @@ func (ec *executionContext) _Project_locations(ctx context.Context, field graphq return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Project_locations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Project_locations(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Project", Field: field, @@ -1812,6 +1886,7 @@ func (ec *executionContext) _Query_version(ctx context.Context, field graphql.Co }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -1821,7 +1896,7 @@ func (ec *executionContext) _Query_version(ctx context.Context, field graphql.Co return ec.marshalOVersion2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐVersion(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -1856,6 +1931,7 @@ func (ec *executionContext) _Query_availableUpdate(ctx context.Context, field gr }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -1896,7 +1972,7 @@ func (ec *executionContext) fieldContext_Query_availableUpdate(ctx context.Conte ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_availableUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -1919,6 +1995,7 @@ func (ec *executionContext) _Query_projects(ctx context.Context, field graphql.C }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { if !graphql.HasFieldError(ctx, fc) { @@ -1931,7 +2008,7 @@ func (ec *executionContext) _Query_projects(ctx context.Context, field graphql.C return ec.marshalNProject2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐProject(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_projects(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_projects(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -1968,6 +2045,7 @@ func (ec *executionContext) _Query_analyticsEvent(ctx context.Context, field gra }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -2000,7 +2078,7 @@ func (ec *executionContext) fieldContext_Query_analyticsEvent(ctx context.Contex ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_analyticsEvent_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2023,6 +2101,7 @@ func (ec *executionContext) _Query_reportRuntimeUsage(ctx context.Context, field }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -2055,7 +2134,7 @@ func (ec *executionContext) fieldContext_Query_reportRuntimeUsage(ctx context.Co ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_reportRuntimeUsage_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2078,6 +2157,7 @@ func (ec *executionContext) _Query_checkMessages(ctx context.Context, field grap }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { if !graphql.HasFieldError(ctx, fc) { @@ -2123,7 +2203,7 @@ func (ec *executionContext) fieldContext_Query_checkMessages(ctx context.Context ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_checkMessages_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2146,6 +2226,7 @@ func (ec *executionContext) _Query_configChanged(ctx context.Context, field grap }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -2178,7 +2259,7 @@ func (ec *executionContext) fieldContext_Query_configChanged(ctx context.Context ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_configChanged_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2201,6 +2282,7 @@ func (ec *executionContext) _Query_fetchLogTail(ctx context.Context, field graph }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { if !graphql.HasFieldError(ctx, fc) { @@ -2213,7 +2295,7 @@ func (ec *executionContext) _Query_fetchLogTail(ctx context.Context, field graph return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_fetchLogTail(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_fetchLogTail(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -2244,6 +2326,7 @@ func (ec *executionContext) _Query_getProcessesInUse(ctx context.Context, field }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { if !graphql.HasFieldError(ctx, fc) { @@ -2281,7 +2364,7 @@ func (ec *executionContext) fieldContext_Query_getProcessesInUse(ctx context.Con ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query_getProcessesInUse_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2304,6 +2387,7 @@ func (ec *executionContext) _Query_getJWT(ctx context.Context, field graphql.Col }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -2313,7 +2397,7 @@ func (ec *executionContext) _Query_getJWT(ctx context.Context, field graphql.Col return ec.marshalOJWT2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐJwt(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_getJWT(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_getJWT(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -2332,6 +2416,61 @@ func (ec *executionContext) fieldContext_Query_getJWT(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Query_hashGlobs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_hashGlobs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().HashGlobs(rctx, fc.Args["globs"].([]string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_hashGlobs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_hashGlobs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -2350,6 +2489,7 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -2400,7 +2540,7 @@ func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -2423,6 +2563,7 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C }) if err != nil { ec.Error(ctx, err) + return graphql.Null } if resTmp == nil { return graphql.Null @@ -2432,7 +2573,7 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query___schema(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -2490,7 +2631,7 @@ func (ec *executionContext) _ReportRuntimeUsageResponse_received(ctx context.Con return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ReportRuntimeUsageResponse_received(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ReportRuntimeUsageResponse_received(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ReportRuntimeUsageResponse", Field: field, @@ -2534,7 +2675,7 @@ func (ec *executionContext) _StateVersion_license(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_StateVersion_license(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_StateVersion_license(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "StateVersion", Field: field, @@ -2578,7 +2719,7 @@ func (ec *executionContext) _StateVersion_version(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_StateVersion_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_StateVersion_version(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "StateVersion", Field: field, @@ -2622,7 +2763,7 @@ func (ec *executionContext) _StateVersion_channel(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_StateVersion_channel(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_StateVersion_channel(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "StateVersion", Field: field, @@ -2666,7 +2807,7 @@ func (ec *executionContext) _StateVersion_revision(ctx context.Context, field gr return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_StateVersion_revision(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_StateVersion_revision(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "StateVersion", Field: field, @@ -2710,7 +2851,7 @@ func (ec *executionContext) _StateVersion_date(ctx context.Context, field graphq return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_StateVersion_date(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_StateVersion_date(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "StateVersion", Field: field, @@ -2754,7 +2895,7 @@ func (ec *executionContext) _User_userID(ctx context.Context, field graphql.Coll return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_User_userID(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_User_userID(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "User", Field: field, @@ -2798,7 +2939,7 @@ func (ec *executionContext) _User_username(ctx context.Context, field graphql.Co return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_User_username(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_User_username(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "User", Field: field, @@ -2842,7 +2983,7 @@ func (ec *executionContext) _User_email(ctx context.Context, field graphql.Colle return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_User_email(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_User_email(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "User", Field: field, @@ -2886,7 +3027,7 @@ func (ec *executionContext) _User_organizations(ctx context.Context, field graph return ec.marshalNOrganization2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐOrganizationᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_User_organizations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_User_organizations(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "User", Field: field, @@ -2936,7 +3077,7 @@ func (ec *executionContext) _Version_state(ctx context.Context, field graphql.Co return ec.marshalNStateVersion2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐStateVersion(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Version_state(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Version_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Version", Field: field, @@ -2992,7 +3133,7 @@ func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Directive_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Directive", Field: field, @@ -3033,7 +3174,7 @@ func (ec *executionContext) ___Directive_description(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Directive_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Directive", Field: field, @@ -3077,7 +3218,7 @@ func (ec *executionContext) ___Directive_locations(ctx context.Context, field gr return ec.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_locations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Directive_locations(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Directive", Field: field, @@ -3121,7 +3262,7 @@ func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Directive_args(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Directive", Field: field, @@ -3175,7 +3316,7 @@ func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Directive_isRepeatable(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Directive", Field: field, @@ -3219,7 +3360,7 @@ func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___EnumValue_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__EnumValue", Field: field, @@ -3260,7 +3401,7 @@ func (ec *executionContext) ___EnumValue_description(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___EnumValue_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__EnumValue", Field: field, @@ -3304,7 +3445,7 @@ func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___EnumValue_isDeprecated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__EnumValue", Field: field, @@ -3345,7 +3486,7 @@ func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___EnumValue_deprecationReason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__EnumValue", Field: field, @@ -3389,7 +3530,7 @@ func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.Col return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -3430,7 +3571,7 @@ func (ec *executionContext) ___Field_description(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -3474,7 +3615,7 @@ func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.Col return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_args(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -3528,7 +3669,7 @@ func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.Col return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -3594,7 +3735,7 @@ func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field gra return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_isDeprecated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -3635,7 +3776,7 @@ func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Field_deprecationReason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Field", Field: field, @@ -3679,7 +3820,7 @@ func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphq return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___InputValue_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__InputValue", Field: field, @@ -3720,7 +3861,7 @@ func (ec *executionContext) ___InputValue_description(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___InputValue_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__InputValue", Field: field, @@ -3764,7 +3905,7 @@ func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphq return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___InputValue_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__InputValue", Field: field, @@ -3827,7 +3968,7 @@ func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___InputValue_defaultValue(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__InputValue", Field: field, @@ -3868,7 +4009,7 @@ func (ec *executionContext) ___Schema_description(ctx context.Context, field gra return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Schema_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Schema", Field: field, @@ -3912,7 +4053,7 @@ func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.C return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_types(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Schema_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Schema", Field: field, @@ -3978,7 +4119,7 @@ func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graph return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_queryType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Schema_queryType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Schema", Field: field, @@ -4041,7 +4182,7 @@ func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field gr return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_mutationType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Schema_mutationType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Schema", Field: field, @@ -4104,7 +4245,7 @@ func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, fiel return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Schema_subscriptionType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Schema", Field: field, @@ -4170,7 +4311,7 @@ func (ec *executionContext) ___Schema_directives(ctx context.Context, field grap return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_directives(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Schema_directives(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Schema", Field: field, @@ -4226,7 +4367,7 @@ func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.Coll return ec.marshalN__TypeKind2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4267,7 +4408,7 @@ func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.Coll return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4308,7 +4449,7 @@ func (ec *executionContext) ___Type_description(ctx context.Context, field graph return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4382,7 +4523,7 @@ func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, fiel ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -4415,7 +4556,7 @@ func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphq return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_interfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_interfaces(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4478,7 +4619,7 @@ func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field gra return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_possibleTypes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_possibleTypes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4570,7 +4711,7 @@ func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, ctx = graphql.WithFieldContext(ctx, fc) if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) - return + return fc, err } return fc, nil } @@ -4603,7 +4744,7 @@ func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graph return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_inputFields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_inputFields(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4654,7 +4795,7 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_ofType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4717,7 +4858,7 @@ func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "__Type", Field: field, @@ -4746,27 +4887,38 @@ var analyticsEventResponseImplementors = []string{"AnalyticsEventResponse"} func (ec *executionContext) _AnalyticsEventResponse(ctx context.Context, sel ast.SelectionSet, obj *graph.AnalyticsEventResponse) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, analyticsEventResponseImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("AnalyticsEventResponse") case "sent": - out.Values[i] = ec._AnalyticsEventResponse_sent(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -4774,55 +4926,58 @@ var availableUpdateImplementors = []string{"AvailableUpdate"} func (ec *executionContext) _AvailableUpdate(ctx context.Context, sel ast.SelectionSet, obj *graph.AvailableUpdate) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, availableUpdateImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("AvailableUpdate") case "version": - out.Values[i] = ec._AvailableUpdate_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "channel": - out.Values[i] = ec._AvailableUpdate_channel(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "path": - out.Values[i] = ec._AvailableUpdate_path(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "platform": - out.Values[i] = ec._AvailableUpdate_platform(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "sha256": - out.Values[i] = ec._AvailableUpdate_sha256(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -4830,27 +4985,38 @@ var configChangedResponseImplementors = []string{"ConfigChangedResponse"} func (ec *executionContext) _ConfigChangedResponse(ctx context.Context, sel ast.SelectionSet, obj *graph.ConfigChangedResponse) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, configChangedResponseImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("ConfigChangedResponse") case "received": - out.Values[i] = ec._ConfigChangedResponse_received(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -4858,34 +5024,43 @@ var jWTImplementors = []string{"JWT"} func (ec *executionContext) _JWT(ctx context.Context, sel ast.SelectionSet, obj *graph.Jwt) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, jWTImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("JWT") case "token": - out.Values[i] = ec._JWT_token(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "user": - out.Values[i] = ec._JWT_user(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -4893,62 +5068,63 @@ var messageInfoImplementors = []string{"MessageInfo"} func (ec *executionContext) _MessageInfo(ctx context.Context, sel ast.SelectionSet, obj *graph.MessageInfo) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, messageInfoImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("MessageInfo") case "id": - out.Values[i] = ec._MessageInfo_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "message": - out.Values[i] = ec._MessageInfo_message(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "condition": - out.Values[i] = ec._MessageInfo_condition(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "repeat": - out.Values[i] = ec._MessageInfo_repeat(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "interrupt": - out.Values[i] = ec._MessageInfo_interrupt(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "placement": - out.Values[i] = ec._MessageInfo_placement(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -4956,34 +5132,43 @@ var organizationImplementors = []string{"Organization"} func (ec *executionContext) _Organization(ctx context.Context, sel ast.SelectionSet, obj *graph.Organization) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, organizationImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Organization") case "URLname": - out.Values[i] = ec._Organization_URLname(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "role": - out.Values[i] = ec._Organization_role(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -4991,34 +5176,43 @@ var processInfoImplementors = []string{"ProcessInfo"} func (ec *executionContext) _ProcessInfo(ctx context.Context, sel ast.SelectionSet, obj *graph.ProcessInfo) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, processInfoImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("ProcessInfo") case "exe": - out.Values[i] = ec._ProcessInfo_exe(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "pid": - out.Values[i] = ec._ProcessInfo_pid(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5026,34 +5220,43 @@ var projectImplementors = []string{"Project"} func (ec *executionContext) _Project(ctx context.Context, sel ast.SelectionSet, obj *graph.Project) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, projectImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Project") case "namespace": - out.Values[i] = ec._Project_namespace(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "locations": - out.Values[i] = ec._Project_locations(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5066,6 +5269,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr }) out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ Object: field.Name, @@ -5078,7 +5282,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr case "version": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -5089,16 +5293,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "availableUpdate": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -5109,36 +5312,37 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "projects": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() res = ec._Query_projects(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "analyticsEvent": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -5149,16 +5353,15 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "reportRuntimeUsage": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -5169,36 +5372,37 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "checkMessages": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() res = ec._Query_checkMessages(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "configChanged": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -5209,56 +5413,59 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "fetchLogTail": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() res = ec._Query_fetchLogTail(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "getProcessesInUse": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() res = ec._Query_getProcessesInUse(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "getJWT": field := field - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -5269,29 +5476,61 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } - out.Concurrently(i, func() graphql.Marshaler { - return rrm(innerCtx) - }) - case "__type": + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "hashGlobs": + field := field + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_hashGlobs(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___type(ctx, field) }) - case "__schema": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Query___schema(ctx, field) }) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5299,27 +5538,38 @@ var reportRuntimeUsageResponseImplementors = []string{"ReportRuntimeUsageRespons func (ec *executionContext) _ReportRuntimeUsageResponse(ctx context.Context, sel ast.SelectionSet, obj *graph.ReportRuntimeUsageResponse) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, reportRuntimeUsageResponseImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("ReportRuntimeUsageResponse") case "received": - out.Values[i] = ec._ReportRuntimeUsageResponse_received(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5327,55 +5577,58 @@ var stateVersionImplementors = []string{"StateVersion"} func (ec *executionContext) _StateVersion(ctx context.Context, sel ast.SelectionSet, obj *graph.StateVersion) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, stateVersionImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("StateVersion") case "license": - out.Values[i] = ec._StateVersion_license(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "version": - out.Values[i] = ec._StateVersion_version(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "channel": - out.Values[i] = ec._StateVersion_channel(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "revision": - out.Values[i] = ec._StateVersion_revision(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "date": - out.Values[i] = ec._StateVersion_date(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5383,48 +5636,53 @@ var userImplementors = []string{"User"} func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj *graph.User) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, userImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("User") case "userID": - out.Values[i] = ec._User_userID(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "username": - out.Values[i] = ec._User_username(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "email": - out.Values[i] = ec._User_email(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "organizations": - out.Values[i] = ec._User_organizations(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5432,27 +5690,38 @@ var versionImplementors = []string{"Version"} func (ec *executionContext) _Version(ctx context.Context, sel ast.SelectionSet, obj *graph.Version) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, versionImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Version") case "state": - out.Values[i] = ec._Version_state(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5460,52 +5729,55 @@ var __DirectiveImplementors = []string{"__Directive"} func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Directive") case "name": - out.Values[i] = ec.___Directive_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Directive_description(ctx, field, obj) - case "locations": - out.Values[i] = ec.___Directive_locations(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "args": - out.Values[i] = ec.___Directive_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5513,42 +5785,47 @@ var __EnumValueImplementors = []string{"__EnumValue"} func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__EnumValue") case "name": - out.Values[i] = ec.___EnumValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___EnumValue_description(ctx, field, obj) - case "isDeprecated": - out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5556,56 +5833,57 @@ var __FieldImplementors = []string{"__Field"} func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Field") case "name": - out.Values[i] = ec.___Field_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___Field_description(ctx, field, obj) - case "args": - out.Values[i] = ec.___Field_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "type": - out.Values[i] = ec.___Field_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "isDeprecated": - out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "deprecationReason": - out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5613,42 +5891,47 @@ var __InputValueImplementors = []string{"__InputValue"} func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__InputValue") case "name": - out.Values[i] = ec.___InputValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "description": - out.Values[i] = ec.___InputValue_description(ctx, field, obj) - case "type": - out.Values[i] = ec.___InputValue_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "defaultValue": - out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5656,53 +5939,54 @@ var __SchemaImplementors = []string{"__Schema"} func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Schema") case "description": - out.Values[i] = ec.___Schema_description(ctx, field, obj) - case "types": - out.Values[i] = ec.___Schema_types(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "queryType": - out.Values[i] = ec.___Schema_queryType(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "mutationType": - out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) - case "subscriptionType": - out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) - case "directives": - out.Values[i] = ec.___Schema_directives(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } @@ -5710,63 +5994,56 @@ var __TypeImplementors = []string{"__Type"} func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + out := graphql.NewFieldSet(fields) - var invalids uint32 + deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("__Type") case "kind": - out.Values[i] = ec.___Type_kind(ctx, field, obj) - if out.Values[i] == graphql.Null { - invalids++ + out.Invalids++ } case "name": - out.Values[i] = ec.___Type_name(ctx, field, obj) - case "description": - out.Values[i] = ec.___Type_description(ctx, field, obj) - case "fields": - out.Values[i] = ec.___Type_fields(ctx, field, obj) - case "interfaces": - out.Values[i] = ec.___Type_interfaces(ctx, field, obj) - case "possibleTypes": - out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) - case "enumValues": - out.Values[i] = ec.___Type_enumValues(ctx, field, obj) - case "inputFields": - out.Values[i] = ec.___Type_inputFields(ctx, field, obj) - case "ofType": - out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) - default: panic("unknown field " + strconv.Quote(field.Name)) } } - out.Dispatch() - if invalids > 0 { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + return out } diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index 55b8d8e8d3..f91b34b3c4 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -88,6 +88,7 @@ type Query { fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT + hashGlobs(globs: [String!]!): String! } type ConfigChangedResponse { diff --git a/go.mod b/go.mod index adb9a64189..d8efcc9208 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,11 @@ module github.com/ActiveState/cli -go 1.22 +go 1.22.0 + +toolchain go1.22.4 require ( - github.com/99designs/gqlgen v0.17.19 + github.com/99designs/gqlgen v0.17.46 github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527 github.com/ActiveState/termtest v0.7.3-0.20240703202616-34f7899287a4 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 @@ -26,7 +28,7 @@ require ( github.com/go-openapi/validate v0.20.2 github.com/gofrs/flock v0.8.1 github.com/google/go-github/v45 v45.0.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-retryablehttp v0.7.7 @@ -55,11 +57,11 @@ require ( github.com/thoas/go-funk v0.8.0 github.com/vbauerster/mpb/v7 v7.1.5 github.com/vektah/gqlparser/v2 v2.5.16 - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/net v0.25.0 - golang.org/x/sys v0.20.0 - golang.org/x/term v0.20.0 - golang.org/x/text v0.15.0 + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 + golang.org/x/sys v0.25.0 + golang.org/x/term v0.24.0 + golang.org/x/text v0.18.0 gopkg.in/AlecAivazis/survey.v1 v1.8.8 gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 gopkg.in/yaml.v2 v2.4.0 @@ -84,12 +86,14 @@ require ( github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/kr/pty v1.1.8 // indirect @@ -104,11 +108,12 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/skeema/knownhosts v1.2.2 // indirect - golang.org/x/sync v0.3.0 // indirect + github.com/sosodev/duration v1.3.1 // indirect + golang.org/x/sync v0.8.0 // indirect ) require ( - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Netflix/go-expect v0.0.0-20201125194554-85d881c3777e // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect @@ -132,7 +137,6 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -169,9 +173,9 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver v1.5.3 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/time v0.1.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.25.0 // indirect gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 27789bcfcf..672d9e8bf3 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/99designs/gqlgen v0.17.19 h1:lO3PBSOv5Mw8RPt0Nwg7ovJ9tNfjGDEnU48AYqLzBVA= -github.com/99designs/gqlgen v0.17.19/go.mod h1:tXjo2/o1L/9A8S/4nLK7Arx/677H8hxlD/iSTRx0BtE= +github.com/99designs/gqlgen v0.17.46 h1:Dk/pSCMVp57z/vd6gwZ/wmqbPOL3i5iz4YQHTDfxyuw= +github.com/99designs/gqlgen v0.17.46/go.mod h1:qRtiAeVPgkBBSPzZtoZXRRl5WkNrUTpp1OeVt61TmGU= github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527 h1:lW+qgVXf/iAnSs8SgagWxh8d6nsbpmwyhmeg9/fp0Os= github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527/go.mod h1:/9SyzKLlJSuIa7WAsLUUhHqTK9+PtZD8cKF8G4SLpa0= github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 h1:UCx/ObpVRgC4fp2OlJM2iNdMMu+K87/aPxKrQ1WRLj4= @@ -27,9 +27,8 @@ github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliG github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= @@ -40,6 +39,8 @@ github.com/Netflix/go-expect v0.0.0-20201125194554-85d881c3777e/go.mod h1:68ORG0 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= +github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -66,6 +67,8 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/andygrunwald/go-jira v1.15.1 h1:6J9aYKb9sW8bxv3pBLYBrs0wdsFrmGI5IeTgWSKWKc8= github.com/andygrunwald/go-jira v1.15.1/go.mod h1:GIYN1sHOIsENWUZ7B4pDeT/nxEtrZpE8l0987O67ZR8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= @@ -96,6 +99,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= @@ -118,7 +122,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= @@ -317,7 +320,6 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -346,8 +348,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -380,8 +382,8 @@ github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -425,7 +427,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -454,7 +455,6 @@ github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITV github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -476,11 +476,9 @@ github.com/mash/go-tempfile-suffix v0.0.0-20150731093933-48f0f8a3a5ab h1:2lWb5W+ github.com/mash/go-tempfile-suffix v0.0.0-20150731093933-48f0f8a3a5ab/go.mod h1:oBJAJTenVXse2frk3J4anZ7funxla2ZdJHFQShkD43k= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -514,7 +512,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -596,11 +593,9 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/rollbar/rollbar-go v1.1.0 h1:3ysiHp3ep8W50ykgBMCKXJGaK2Jdivru7SW9EYfAo+M= github.com/rollbar/rollbar-go v1.1.0/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= @@ -624,6 +619,8 @@ github.com/skratchdot/open-golang v0.0.0-20190104022628-a2dfa6d0dab6/go.mod h1:s github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= @@ -647,7 +644,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -666,7 +662,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= @@ -674,7 +669,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/vbauerster/mpb/v7 v7.1.5 h1:vtUEUfQHmNeJETyF4AcRCOV6RC4wqFwNORy52UMXPbQ= github.com/vbauerster/mpb/v7 v7.1.5/go.mod h1:4M8+qAoQqV60WDNktBM5k05i1iTrXE7rjKOHEVkVlec= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs= github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8= github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -687,10 +681,8 @@ github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHM github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -731,8 +723,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -752,11 +744,10 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -785,15 +776,14 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -805,11 +795,10 @@ golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -847,7 +836,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -861,8 +849,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -870,8 +858,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -883,8 +871,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= @@ -916,11 +904,10 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -946,8 +933,6 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY= gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 8514664e0a..df667c86f3 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -53,6 +53,9 @@ type Project struct { Locations []string `json:"locations"` } +type Query struct { +} + type ReportRuntimeUsageResponse struct { Received bool `json:"received"` } diff --git a/internal/hash/file_hasher.go b/internal/hash/file_hasher.go new file mode 100644 index 0000000000..5b19622776 --- /dev/null +++ b/internal/hash/file_hasher.go @@ -0,0 +1,75 @@ +package hash + +import ( + "encoding/base64" + "fmt" + "io" + "os" + "sort" + "time" + + "github.com/ActiveState/cli/internal/errs" + "github.com/cespare/xxhash" + "github.com/patrickmn/go-cache" +) + +type fileCache interface { + Get(key string) (interface{}, bool) + Set(key string, value interface{}, expiration time.Duration) +} + +type FileHasher struct { + cache fileCache +} + +func NewFileHasher() *FileHasher { + return &FileHasher{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } +} + +func (f *FileHasher) HashFiles(files []string) (string, error) { + sort.Strings(files) + + hasher := xxhash.New() + for _, file := range files { + openFile, err := os.Open(file) + if err != nil { + return "", errs.Wrap(err, "Could not open file: %s", file) + } + + fileInfo, err := openFile.Stat() + if err != nil { + return "", errs.Wrap(err, "Could not stat file: %s", file) + } + + var hash string + fh, ok := f.cache.Get(cacheKey(file, fileInfo.ModTime().String())) + if ok { + hash, ok = fh.(string) + if !ok { + return "", errs.New("Could not convert cache value to string") + } + } else { + fileHasher := xxhash.New() + if _, err := io.Copy(fileHasher, openFile); err != nil { + return "", errs.Wrap(err, "Could not hash file: %s", file) + } + + if err := openFile.Close(); err != nil { + return "", errs.Wrap(err, "Could not close file: %s", file) + } + + hash = fmt.Sprintf("%x", fileHasher.Sum(nil)) + } + + f.cache.Set(cacheKey(file, fileInfo.ModTime().String()), hash, cache.NoExpiration) + fmt.Fprintf(hasher, "%x", hash) + } + + return base64.StdEncoding.EncodeToString(hasher.Sum(nil)), nil +} + +func cacheKey(file string, modTime string) string { + return fmt.Sprintf("%s-%s", file, modTime) +} diff --git a/internal/hash/file_hasher_test.go b/internal/hash/file_hasher_test.go new file mode 100644 index 0000000000..5917e610ab --- /dev/null +++ b/internal/hash/file_hasher_test.go @@ -0,0 +1,144 @@ +package hash + +import ( + "os" + "testing" + "time" + + "github.com/patrickmn/go-cache" + "github.com/stretchr/testify/assert" +) + +type testCache struct { + cache *cache.Cache + hits []string + misses []string +} + +func (tc *testCache) Get(key string) (interface{}, bool) { + val, ok := tc.cache.Get(key) + if ok { + tc.hits = append(tc.hits, key) + } else { + tc.misses = append(tc.misses, key) + } + + return val, ok +} + +func (tc *testCache) Set(key string, value interface{}, expiration time.Duration) { + tc.cache.Set(key, value, cache.DefaultExpiration) +} + +func TestFileHasher_HashFiles(t *testing.T) { + file1 := createTempFile(t, "file1") + file2 := createTempFile(t, "file2") + + hasher := NewFileHasher() + + hash1, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.Equal(t, hash1, hash2) +} + +func TestFileHasher_CacheHit(t *testing.T) { + file1 := createTempFile(t, "file1") + file2 := createTempFile(t, "file2") + + tc := &testCache{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } + + hasher := &FileHasher{ + cache: tc, + } + + hash1, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.Equal(t, hash1, hash2) + assert.Len(t, tc.hits, 2) + assert.Len(t, tc.misses, 2) +} + +func TestFileHasher_CacheMiss(t *testing.T) { + file1 := createTempFile(t, "file1") + file2 := createTempFile(t, "file2") + + tc := &testCache{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } + + hasher := &FileHasher{ + cache: tc, + } + + hash1, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + if err := os.Chtimes(file1, time.Now(), time.Now()); err != nil { + t.Fatal(err) + } + + file, err := os.Open(file1) + assert.NoError(t, err) + err = file.Sync() + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.Equal(t, hash1, hash2) + assert.Len(t, tc.hits, 1) + assert.Len(t, tc.misses, 3) +} + +func TestFileHasher_ContentAgnostic(t *testing.T) { + // Files have same content but different names and modification times + file1 := createTempFile(t, "file1") + + // Ensure mod times are different + time.Sleep(1 * time.Millisecond) + file2 := createTempFile(t, "file1") + + tc := &testCache{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } + + hasher := &FileHasher{ + cache: tc, + } + + hash1, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.Equal(t, hash1, hash2) + assert.Len(t, tc.hits, 2) + assert.Len(t, tc.misses, 2) +} + +func createTempFile(t *testing.T, content string) string { + tmpfile, err := os.CreateTemp("", "testfile") + if err != nil { + t.Fatal(err) + } + + if _, err := tmpfile.Write([]byte(content)); err != nil { + t.Fatal(err) + } + if err := tmpfile.Close(); err != nil { + t.Fatal(err) + } + + return tmpfile.Name() +} diff --git a/vendor/github.com/99designs/gqlgen/complexity/complexity.go b/vendor/github.com/99designs/gqlgen/complexity/complexity.go index e3ecf7612d..aa0f86432e 100644 --- a/vendor/github.com/99designs/gqlgen/complexity/complexity.go +++ b/vendor/github.com/99designs/gqlgen/complexity/complexity.go @@ -1,8 +1,9 @@ package complexity import ( - "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/ast" + + "github.com/99designs/gqlgen/graphql" ) func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]interface{}) int { diff --git a/vendor/github.com/99designs/gqlgen/graphql/cache.go b/vendor/github.com/99designs/gqlgen/graphql/cache.go index fe86ca3502..e552ce6722 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/cache.go +++ b/vendor/github.com/99designs/gqlgen/graphql/cache.go @@ -15,15 +15,15 @@ type Cache interface { type MapCache map[string]interface{} // Get looks up a key's value from the cache. -func (m MapCache) Get(ctx context.Context, key string) (value interface{}, ok bool) { +func (m MapCache) Get(_ context.Context, key string) (value interface{}, ok bool) { v, ok := m[key] return v, ok } // Add adds a value to the cache. -func (m MapCache) Add(ctx context.Context, key string, value interface{}) { m[key] = value } +func (m MapCache) Add(_ context.Context, key string, value interface{}) { m[key] = value } type NoCache struct{} -func (n NoCache) Get(ctx context.Context, key string) (value interface{}, ok bool) { return nil, false } -func (n NoCache) Add(ctx context.Context, key string, value interface{}) {} +func (n NoCache) Get(_ context.Context, _ string) (value interface{}, ok bool) { return nil, false } +func (n NoCache) Add(_ context.Context, _ string, _ interface{}) {} diff --git a/vendor/github.com/99designs/gqlgen/graphql/context_operation.go b/vendor/github.com/99designs/gqlgen/graphql/context_operation.go index 0518ecc6ba..3e6a221b0b 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context_operation.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context_operation.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" ) // Deprecated: Please update all references to OperationContext instead @@ -72,8 +73,8 @@ func WithOperationContext(ctx context.Context, rc *OperationContext) context.Con // // Some errors can happen outside of an operation, eg json unmarshal errors. func HasOperationContext(ctx context.Context) bool { - _, ok := ctx.Value(operationCtx).(*OperationContext) - return ok + val, ok := ctx.Value(operationCtx).(*OperationContext) + return ok && val != nil } // This is just a convenient wrapper method for CollectFields @@ -106,9 +107,16 @@ func (c *OperationContext) Errorf(ctx context.Context, format string, args ...in AddErrorf(ctx, format, args...) } -// Error sends an error to the client, passing it through the formatter. -// Deprecated: use graphql.AddError(ctx, err) instead +// Error add error or multiple errors (if underlaying type is gqlerror.List) into the stack. +// Then it will be sends to the client, passing it through the formatter. func (c *OperationContext) Error(ctx context.Context, err error) { + if errList, ok := err.(gqlerror.List); ok { + for _, e := range errList { + AddError(ctx, e) + } + return + } + AddError(ctx, err) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/context_path.go b/vendor/github.com/99designs/gqlgen/graphql/context_path.go index a46ed83ddc..bdb53e7a74 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context_path.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context_path.go @@ -34,7 +34,6 @@ func (fic *PathContext) Path() ast.Path { if fic.ParentField != nil { fieldPath := fic.ParentField.Path() return append(fieldPath, path...) - } return path diff --git a/vendor/github.com/99designs/gqlgen/graphql/context_response.go b/vendor/github.com/99designs/gqlgen/graphql/context_response.go index c128fdb49c..6d223c8a94 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context_response.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context_response.go @@ -36,6 +36,14 @@ func WithResponseContext(ctx context.Context, presenterFunc ErrorPresenterFunc, }) } +func WithFreshResponseContext(ctx context.Context) context.Context { + e := getResponseContext(ctx) + return context.WithValue(ctx, resultCtx, &responseContext{ + errorPresenter: e.errorPresenter, + recover: e.recover, + }) +} + // AddErrorf writes a formatted error to the client, first passing it through the error presenter. func AddErrorf(ctx context.Context, format string, args ...interface{}) { AddError(ctx, fmt.Errorf(format, args...)) diff --git a/vendor/github.com/99designs/gqlgen/graphql/deferred.go b/vendor/github.com/99designs/gqlgen/graphql/deferred.go new file mode 100644 index 0000000000..1aeb48c180 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/deferred.go @@ -0,0 +1,26 @@ +package graphql + +import ( + "context" + + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" +) + +type Deferrable struct { + Label string +} + +type DeferredGroup struct { + Path ast.Path + Label string + FieldSet *FieldSet + Context context.Context +} + +type DeferredResult struct { + Path ast.Path + Label string + Result Marshaler + Errors gqlerror.List +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/duration.go b/vendor/github.com/99designs/gqlgen/graphql/duration.go new file mode 100644 index 0000000000..3eb392db87 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/duration.go @@ -0,0 +1,27 @@ +package graphql + +import ( + "fmt" + "time" + + dur "github.com/sosodev/duration" +) + +// UnmarshalDuration returns the duration from a string in ISO8601 format +func UnmarshalDuration(v interface{}) (time.Duration, error) { + input, ok := v.(string) + if !ok { + return 0, fmt.Errorf("input must be a string") + } + + d2, err := dur.Parse(input) + if err != nil { + return 0, err + } + return d2.ToTimeDuration(), nil +} + +// MarshalDuration returns the duration on ISO8601 format +func MarshalDuration(d time.Duration) Marshaler { + return MarshalString(dur.Format(d)) +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go b/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go index 541b65fbee..58f942a104 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go @@ -45,9 +45,19 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies if len(satisfies) > 0 && !instanceOf(sel.TypeCondition, satisfies) { continue } + + shouldDefer, label := deferrable(sel.Directives, reqCtx.Variables) + for _, childField := range collectFields(reqCtx, sel.SelectionSet, satisfies, visited) { - f := getOrCreateAndAppendField(&groupedFields, childField.Name, childField.Alias, childField.ObjectDefinition, func() CollectedField { return childField }) + f := getOrCreateAndAppendField( + &groupedFields, childField.Name, childField.Alias, childField.ObjectDefinition, + func() CollectedField { return childField }) f.Selections = append(f.Selections, childField.Selections...) + if shouldDefer { + f.Deferrable = &Deferrable{ + Label: label, + } + } } case *ast.FragmentSpread: @@ -70,9 +80,16 @@ func collectFields(reqCtx *OperationContext, selSet ast.SelectionSet, satisfies continue } + shouldDefer, label := deferrable(sel.Directives, reqCtx.Variables) + for _, childField := range collectFields(reqCtx, fragment.SelectionSet, satisfies, visited) { - f := getOrCreateAndAppendField(&groupedFields, childField.Name, childField.Alias, childField.ObjectDefinition, func() CollectedField { return childField }) + f := getOrCreateAndAppendField(&groupedFields, + childField.Name, childField.Alias, childField.ObjectDefinition, + func() CollectedField { return childField }) f.Selections = append(f.Selections, childField.Selections...) + if shouldDefer { + f.Deferrable = &Deferrable{Label: label} + } } default: @@ -87,6 +104,7 @@ type CollectedField struct { *ast.Field Selections ast.SelectionSet + Deferrable *Deferrable } func instanceOf(val string, satisfies []string) bool { @@ -118,6 +136,11 @@ func getOrCreateAndAppendField(c *[]CollectedField, name string, alias string, o return &(*c)[i] } } + for _, ifc := range cf.ObjectDefinition.Interfaces { + if ifc == objectDefinition.Name { + return &(*c)[i] + } + } } } @@ -145,6 +168,32 @@ func shouldIncludeNode(directives ast.DirectiveList, variables map[string]interf return !skip && include } +func deferrable(directives ast.DirectiveList, variables map[string]interface{}) (shouldDefer bool, label string) { + d := directives.ForName("defer") + if d == nil { + return false, "" + } + + shouldDefer = true + + for _, arg := range d.Arguments { + switch arg.Name { + case "if": + if value, err := arg.Value.Value(variables); err == nil { + shouldDefer, _ = value.(bool) + } + case "label": + if value, err := arg.Value.Value(variables); err == nil { + label, _ = value.(string) + } + default: + panic(fmt.Sprintf("defer: argument '%s' not supported", arg.Name)) + } + } + + return shouldDefer, label +} + func resolveIfArgument(d *ast.Directive, variables map[string]interface{}) bool { arg := d.Arguments.ForName("if") if arg == nil { diff --git a/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go b/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go index 5d7433162f..5e71cb8304 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go @@ -15,25 +15,25 @@ var _ ExecutableSchema = &ExecutableSchemaMock{} // ExecutableSchemaMock is a mock implementation of ExecutableSchema. // -// func TestSomethingThatUsesExecutableSchema(t *testing.T) { +// func TestSomethingThatUsesExecutableSchema(t *testing.T) { // -// // make and configure a mocked ExecutableSchema -// mockedExecutableSchema := &ExecutableSchemaMock{ -// ComplexityFunc: func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) { -// panic("mock out the Complexity method") -// }, -// ExecFunc: func(ctx context.Context) ResponseHandler { -// panic("mock out the Exec method") -// }, -// SchemaFunc: func() *ast.Schema { -// panic("mock out the Schema method") -// }, -// } +// // make and configure a mocked ExecutableSchema +// mockedExecutableSchema := &ExecutableSchemaMock{ +// ComplexityFunc: func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) { +// panic("mock out the Complexity method") +// }, +// ExecFunc: func(ctx context.Context) ResponseHandler { +// panic("mock out the Exec method") +// }, +// SchemaFunc: func() *ast.Schema { +// panic("mock out the Schema method") +// }, +// } // -// // use mockedExecutableSchema in code that requires ExecutableSchema -// // and then make assertions. +// // use mockedExecutableSchema in code that requires ExecutableSchema +// // and then make assertions. // -// } +// } type ExecutableSchemaMock struct { // ComplexityFunc mocks the Complexity method. ComplexityFunc func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) @@ -95,7 +95,8 @@ func (mock *ExecutableSchemaMock) Complexity(typeName string, fieldName string, // ComplexityCalls gets all the calls that were made to Complexity. // Check the length with: -// len(mockedExecutableSchema.ComplexityCalls()) +// +// len(mockedExecutableSchema.ComplexityCalls()) func (mock *ExecutableSchemaMock) ComplexityCalls() []struct { TypeName string FieldName string @@ -132,7 +133,8 @@ func (mock *ExecutableSchemaMock) Exec(ctx context.Context) ResponseHandler { // ExecCalls gets all the calls that were made to Exec. // Check the length with: -// len(mockedExecutableSchema.ExecCalls()) +// +// len(mockedExecutableSchema.ExecCalls()) func (mock *ExecutableSchemaMock) ExecCalls() []struct { Ctx context.Context } { @@ -160,7 +162,8 @@ func (mock *ExecutableSchemaMock) Schema() *ast.Schema { // SchemaCalls gets all the calls that were made to Schema. // Check the length with: -// len(mockedExecutableSchema.SchemaCalls()) +// +// len(mockedExecutableSchema.SchemaCalls()) func (mock *ExecutableSchemaMock) SchemaCalls() []struct { } { var calls []struct { diff --git a/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go b/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go index c46a007b99..ef0603eaa0 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go @@ -3,12 +3,13 @@ package executor import ( "context" - "github.com/99designs/gqlgen/graphql" - "github.com/99designs/gqlgen/graphql/errcode" "github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/parser" "github.com/vektah/gqlparser/v2/validator" + + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/errcode" ) // Executor executes graphql queries against a schema. diff --git a/vendor/github.com/99designs/gqlgen/graphql/fieldset.go b/vendor/github.com/99designs/gqlgen/graphql/fieldset.go index 351e266fdb..2c9e3dc932 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/fieldset.go +++ b/vendor/github.com/99designs/gqlgen/graphql/fieldset.go @@ -1,19 +1,21 @@ package graphql import ( + "context" "io" "sync" ) type FieldSet struct { - fields []CollectedField - Values []Marshaler - delayed []delayedResult + fields []CollectedField + Values []Marshaler + Invalids uint32 + delayed []delayedResult } type delayedResult struct { i int - f func() Marshaler + f func(context.Context) Marshaler } func NewFieldSet(fields []CollectedField) *FieldSet { @@ -23,15 +25,20 @@ func NewFieldSet(fields []CollectedField) *FieldSet { } } -func (m *FieldSet) Concurrently(i int, f func() Marshaler) { +func (m *FieldSet) AddField(field CollectedField) { + m.fields = append(m.fields, field) + m.Values = append(m.Values, nil) +} + +func (m *FieldSet) Concurrently(i int, f func(context.Context) Marshaler) { m.delayed = append(m.delayed, delayedResult{i: i, f: f}) } -func (m *FieldSet) Dispatch() { +func (m *FieldSet) Dispatch(ctx context.Context) { if len(m.delayed) == 1 { // only one concurrent task, no need to spawn a goroutine or deal create waitgroups d := m.delayed[0] - m.Values[d.i] = d.f() + m.Values[d.i] = d.f(ctx) } else if len(m.delayed) > 1 { // more than one concurrent task, use the main goroutine to do one, only spawn goroutines for the others @@ -39,12 +46,12 @@ func (m *FieldSet) Dispatch() { for _, d := range m.delayed[1:] { wg.Add(1) go func(d delayedResult) { - m.Values[d.i] = d.f() - wg.Done() + defer wg.Done() + m.Values[d.i] = d.f(ctx) }(d) } - m.Values[m.delayed[0].i] = m.delayed[0].f() + m.Values[m.delayed[0].i] = m.delayed[0].f(ctx) wg.Wait() } } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler.go b/vendor/github.com/99designs/gqlgen/graphql/handler.go index b76381f43e..cd358740c8 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler.go @@ -42,14 +42,14 @@ type ( // Its important to understand the lifecycle of a graphql request and the terminology we use in gqlgen // before working with these // - // +--- REQUEST POST /graphql --------------------------------------------+ - // | +- OPERATION query OpName { viewer { name } } -----------------------+ | - // | | RESPONSE { "data": { "viewer": { "name": "bob" } } } | | - // | +- OPERATION subscription OpName2 { chat { message } } --------------+ | - // | | RESPONSE { "data": { "chat": { "message": "hello" } } } | | - // | | RESPONSE { "data": { "chat": { "message": "byee" } } } | | - // | +--------------------------------------------------------------------+ | - // +------------------------------------------------------------------------+ + // +--- REQUEST POST /graphql --------------------------------------------+ + // | +- OPERATION query OpName { viewer { name } } -----------------------+ | + // | | RESPONSE { "data": { "viewer": { "name": "bob" } } } | | + // | +- OPERATION subscription OpName2 { chat { message } } --------------+ | + // | | RESPONSE { "data": { "chat": { "message": "hello" } } } | | + // | | RESPONSE { "data": { "chat": { "message": "byee" } } } | | + // | +--------------------------------------------------------------------+ | + // +------------------------------------------------------------------------+ HandlerExtension interface { // ExtensionName should be a CamelCase string version of the extension which may be shown in stats and logging. ExtensionName() string diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go index 866276eed9..465c2ada61 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go @@ -6,12 +6,11 @@ import ( "encoding/hex" "fmt" - "github.com/99designs/gqlgen/graphql/errcode" - + "github.com/mitchellh/mapstructure" "github.com/vektah/gqlparser/v2/gqlerror" "github.com/99designs/gqlgen/graphql" - "github.com/mitchellh/mapstructure" + "github.com/99designs/gqlgen/graphql/errcode" ) const ( diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go index 2d853802bf..a5b6a60409 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go @@ -4,10 +4,11 @@ import ( "context" "fmt" + "github.com/vektah/gqlparser/v2/gqlerror" + "github.com/99designs/gqlgen/complexity" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/errcode" - "github.com/vektah/gqlparser/v2/gqlerror" ) const errComplexityLimit = "COMPLEXITY_LIMIT_EXCEEDED" @@ -59,17 +60,17 @@ func (c *ComplexityLimit) Validate(schema graphql.ExecutableSchema) error { func (c ComplexityLimit) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error { op := rc.Doc.Operations.ForName(rc.OperationName) - complexity := complexity.Calculate(c.es, op, rc.Variables) + complexityCalcs := complexity.Calculate(c.es, op, rc.Variables) limit := c.Func(ctx, rc) rc.Stats.SetExtension(complexityExtension, &ComplexityStats{ - Complexity: complexity, + Complexity: complexityCalcs, ComplexityLimit: limit, }) - if complexity > limit { - err := gqlerror.Errorf("operation has complexity %d, which exceeds the limit of %d", complexity, limit) + if complexityCalcs > limit { + err := gqlerror.Errorf("operation has complexity %d, which exceeds the limit of %d", complexityCalcs, limit) errcode.Set(err, errComplexityLimit) return err } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/introspection.go b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/introspection.go index acc5db2fbc..8e3912651d 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/introspection.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/introspection.go @@ -3,8 +3,9 @@ package extension import ( "context" - "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" ) // EnableIntrospection enables clients to reflect all of the types available on the graph. diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go b/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go index e2b1561acb..6ae8a38e64 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go @@ -3,18 +3,19 @@ package lru import ( "context" + lru "github.com/hashicorp/golang-lru/v2" + "github.com/99designs/gqlgen/graphql" - lru "github.com/hashicorp/golang-lru" ) type LRU struct { - lru *lru.Cache + lru *lru.Cache[string, any] } var _ graphql.Cache = &LRU{} func New(size int) *LRU { - cache, err := lru.New(size) + cache, err := lru.New[string, any](size) if err != nil { // An error is only returned for non-positive cache size // and we already checked for that. diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/server.go b/vendor/github.com/99designs/gqlgen/graphql/handler/server.go index b6524d8da1..fd365ccbbf 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/server.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/server.go @@ -7,12 +7,13 @@ import ( "net/http" "time" + "github.com/vektah/gqlparser/v2/gqlerror" + "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/executor" "github.com/99designs/gqlgen/graphql/handler/extension" "github.com/99designs/gqlgen/graphql/handler/lru" "github.com/99designs/gqlgen/graphql/handler/transport" - "github.com/vektah/gqlparser/v2/gqlerror" ) type ( diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go index b1aeaf144d..18f09f5567 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go @@ -5,8 +5,9 @@ import ( "fmt" "net/http" - "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" ) // SendError sends a best effort error to a raw response writer. It assumes the client can understand the standard diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/headers.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/headers.go new file mode 100644 index 0000000000..bc4e572444 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/headers.go @@ -0,0 +1,17 @@ +package transport + +import "net/http" + +func writeHeaders(w http.ResponseWriter, headers map[string][]string) { + if len(headers) == 0 { + headers = map[string][]string{ + "Content-Type": {"application/json"}, + } + } + + for key, values := range headers { + for _, value := range values { + w.Header().Add(key, value) + } + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_multipart.go similarity index 96% rename from vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form.go rename to vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_multipart.go index 3d3477b9ba..b9eb5f8f43 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_multipart.go @@ -20,6 +20,10 @@ type MultipartForm struct { // as multipart/form-data in memory, with the remainder stored on disk in // temporary files. MaxMemory int64 + + // Map of all headers that are added to graphql response. If not + // set, only one header: Content-Type: application/json will be set. + ResponseHeaders map[string][]string } var _ graphql.Transport = MultipartForm{} @@ -52,7 +56,7 @@ func (f MultipartForm) maxMemory() int64 { } func (f MultipartForm) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { - w.Header().Set("Content-Type", "application/json") + writeHeaders(w, f.ResponseHeaders) start := graphql.Now() diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go new file mode 100644 index 0000000000..73317e4bea --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go @@ -0,0 +1,119 @@ +package transport + +import ( + "io" + "mime" + "net/http" + "net/url" + "strings" + + "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" +) + +// FORM implements the application/x-www-form-urlencoded side of the default HTTP transport +type UrlEncodedForm struct { + // Map of all headers that are added to graphql response. If not + // set, only one header: Content-Type: application/json will be set. + ResponseHeaders map[string][]string +} + +var _ graphql.Transport = UrlEncodedForm{} + +func (h UrlEncodedForm) Supports(r *http.Request) bool { + if r.Header.Get("Upgrade") != "" { + return false + } + + mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return false + } + + return r.Method == "POST" && mediaType == "application/x-www-form-urlencoded" +} + +func (h UrlEncodedForm) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { + ctx := r.Context() + writeHeaders(w, h.ResponseHeaders) + params := &graphql.RawParams{} + start := graphql.Now() + params.Headers = r.Header + params.ReadTime = graphql.TraceTiming{ + Start: start, + End: graphql.Now(), + } + + bodyString, err := getRequestBody(r) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + gqlErr := gqlerror.Errorf("could not get form body: %+v", err) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + writeJson(w, resp) + return + } + + params, err = h.parseBody(bodyString) + if err != nil { + w.WriteHeader(http.StatusUnprocessableEntity) + gqlErr := gqlerror.Errorf("could not cleanup body: %+v", err) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + writeJson(w, resp) + return + } + + rc, OpErr := exec.CreateOperationContext(ctx, params) + if OpErr != nil { + w.WriteHeader(statusFor(OpErr)) + resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) + writeJson(w, resp) + return + } + + var responses graphql.ResponseHandler + responses, ctx = exec.DispatchOperation(ctx, rc) + writeJson(w, responses(ctx)) +} + +func (h UrlEncodedForm) parseBody(bodyString string) (*graphql.RawParams, error) { + switch { + case strings.Contains(bodyString, "\"query\":"): + // body is json + return h.parseJson(bodyString) + case strings.HasPrefix(bodyString, "query=%7B"): + // body is urlencoded + return h.parseEncoded(bodyString) + default: + // body is plain text + params := &graphql.RawParams{} + params.Query = strings.TrimPrefix(bodyString, "query=") + + return params, nil + } +} + +func (h UrlEncodedForm) parseEncoded(bodyString string) (*graphql.RawParams, error) { + params := &graphql.RawParams{} + + query, err := url.QueryUnescape(bodyString) + if err != nil { + return nil, err + } + + params.Query = strings.TrimPrefix(query, "query=") + + return params, nil +} + +func (h UrlEncodedForm) parseJson(bodyString string) (*graphql.RawParams, error) { + params := &graphql.RawParams{} + bodyReader := io.NopCloser(strings.NewReader(bodyString)) + + err := jsonDecode(bodyReader, ¶ms) + if err != nil { + return nil, err + } + + return params, nil +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go index 8114ba66a3..9a47bfbef8 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go @@ -7,15 +7,20 @@ import ( "net/url" "strings" - "github.com/99designs/gqlgen/graphql" - "github.com/99designs/gqlgen/graphql/errcode" "github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/errcode" ) // GET implements the GET side of the default HTTP transport // defined in https://github.com/APIs-guru/graphql-over-http#get -type GET struct{} +type GET struct { + // Map of all headers that are added to graphql response. If not + // set, only one header: Content-Type: application/json will be set. + ResponseHeaders map[string][]string +} var _ graphql.Transport = GET{} @@ -34,7 +39,7 @@ func (h GET) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecut writeJsonError(w, err.Error()) return } - w.Header().Set("Content-Type", "application/json") + writeHeaders(w, h.ResponseHeaders) raw := &graphql.RawParams{ Query: query.Get("query"), diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go new file mode 100644 index 0000000000..b54a27d043 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go @@ -0,0 +1,98 @@ +package transport + +import ( + "mime" + "net/http" + "net/url" + "strings" + + "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" +) + +// GRAPHQL implements the application/graphql side of the HTTP transport +// see: https://graphql.org/learn/serving-over-http/#post-request +// If the "application/graphql" Content-Type header is present, treat +// the HTTP POST body contents as the GraphQL query string. +type GRAPHQL struct { + // Map of all headers that are added to graphql response. If not + // set, only one header: Content-Type: application/json will be set. + ResponseHeaders map[string][]string +} + +var _ graphql.Transport = GRAPHQL{} + +func (h GRAPHQL) Supports(r *http.Request) bool { + if r.Header.Get("Upgrade") != "" { + return false + } + + mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return false + } + + return r.Method == "POST" && mediaType == "application/graphql" +} + +func (h GRAPHQL) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { + ctx := r.Context() + writeHeaders(w, h.ResponseHeaders) + params := &graphql.RawParams{} + start := graphql.Now() + params.Headers = r.Header + params.ReadTime = graphql.TraceTiming{ + Start: start, + End: graphql.Now(), + } + + bodyString, err := getRequestBody(r) + if err != nil { + gqlErr := gqlerror.Errorf("could not get request body: %+v", err) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + writeJson(w, resp) + return + } + + params.Query, err = cleanupBody(bodyString) + if err != nil { + w.WriteHeader(http.StatusUnprocessableEntity) + gqlErr := gqlerror.Errorf("could not cleanup body: %+v", err) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + writeJson(w, resp) + return + } + + rc, OpErr := exec.CreateOperationContext(ctx, params) + if OpErr != nil { + w.WriteHeader(statusFor(OpErr)) + resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) + writeJson(w, resp) + return + } + + var responses graphql.ResponseHandler + responses, ctx = exec.DispatchOperation(ctx, rc) + writeJson(w, responses(ctx)) +} + +// Makes sure we strip "query=" keyword from body and +// that body is not url escaped +func cleanupBody(body string) (out string, err error) { + // Some clients send 'query=' at the start of body payload. Let's remove + // it to get GQL query only. + body = strings.TrimPrefix(body, "query=") + + // Body payload can be url encoded or not. We check if %7B - "{" character + // is where query starts. If it is, query is url encoded. + if strings.HasPrefix(body, "%7B") { + body, err = url.QueryUnescape(body) + + if err != nil { + return body, err + } + } + + return body, err +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go index 99c3157185..a37010ab74 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go @@ -1,15 +1,24 @@ package transport import ( + "fmt" + "io" "mime" "net/http" + "strings" + + "github.com/vektah/gqlparser/v2/gqlerror" "github.com/99designs/gqlgen/graphql" ) // POST implements the POST side of the default HTTP transport // defined in https://github.com/APIs-guru/graphql-over-http#post -type POST struct{} +type POST struct { + // Map of all headers that are added to graphql response. If not + // set, only one header: Content-Type: application/json will be set. + ResponseHeaders map[string][]string +} var _ graphql.Transport = POST{} @@ -26,31 +35,58 @@ func (h POST) Supports(r *http.Request) bool { return r.Method == "POST" && mediaType == "application/json" } -func (h POST) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { - w.Header().Set("Content-Type", "application/json") - - var params *graphql.RawParams - start := graphql.Now() - if err := jsonDecode(r.Body, ¶ms); err != nil { - w.WriteHeader(http.StatusBadRequest) - writeJsonErrorf(w, "json body could not be decoded: "+err.Error()) - return +func getRequestBody(r *http.Request) (string, error) { + if r == nil || r.Body == nil { + return "", nil + } + body, err := io.ReadAll(r.Body) + if err != nil { + return "", fmt.Errorf("unable to get Request Body %w", err) } + return string(body), nil +} +func (h POST) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { + ctx := r.Context() + writeHeaders(w, h.ResponseHeaders) + params := &graphql.RawParams{} + start := graphql.Now() params.Headers = r.Header - params.ReadTime = graphql.TraceTiming{ Start: start, End: graphql.Now(), } - rc, err := exec.CreateOperationContext(r.Context(), params) + bodyString, err := getRequestBody(r) if err != nil { - w.WriteHeader(statusFor(err)) - resp := exec.DispatchError(graphql.WithOperationContext(r.Context(), rc), err) + gqlErr := gqlerror.Errorf("could not get json request body: %+v", err) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + writeJson(w, resp) + return + } + + bodyReader := io.NopCloser(strings.NewReader(bodyString)) + if err = jsonDecode(bodyReader, ¶ms); err != nil { + w.WriteHeader(http.StatusBadRequest) + gqlErr := gqlerror.Errorf( + "json request body could not be decoded: %+v body:%s", + err, + bodyString, + ) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) writeJson(w, resp) return } - responses, ctx := exec.DispatchOperation(r.Context(), rc) + + rc, OpErr := exec.CreateOperationContext(ctx, params) + if OpErr != nil { + w.WriteHeader(statusFor(OpErr)) + resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) + writeJson(w, resp) + return + } + + var responses graphql.ResponseHandler + responses, ctx = exec.DispatchOperation(ctx, rc) writeJson(w, responses(ctx)) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/sse.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/sse.go new file mode 100644 index 0000000000..1d59fdffe5 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/sse.go @@ -0,0 +1,107 @@ +package transport + +import ( + "encoding/json" + "fmt" + "io" + "log" + "mime" + "net/http" + "strings" + + "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" +) + +type SSE struct{} + +var _ graphql.Transport = SSE{} + +func (t SSE) Supports(r *http.Request) bool { + if !strings.Contains(r.Header.Get("Accept"), "text/event-stream") { + return false + } + mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return false + } + return r.Method == http.MethodPost && mediaType == "application/json" +} + +func (t SSE) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { + ctx := r.Context() + flusher, ok := w.(http.Flusher) + if !ok { + SendErrorf(w, http.StatusInternalServerError, "streaming unsupported") + return + } + defer flusher.Flush() + + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + w.Header().Set("Content-Type", "application/json") + + params := &graphql.RawParams{} + start := graphql.Now() + params.Headers = r.Header + params.ReadTime = graphql.TraceTiming{ + Start: start, + End: graphql.Now(), + } + + bodyString, err := getRequestBody(r) + if err != nil { + gqlErr := gqlerror.Errorf("could not get json request body: %+v", err) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + log.Printf("could not get json request body: %+v", err.Error()) + writeJson(w, resp) + return + } + + bodyReader := io.NopCloser(strings.NewReader(bodyString)) + if err = jsonDecode(bodyReader, ¶ms); err != nil { + w.WriteHeader(http.StatusBadRequest) + gqlErr := gqlerror.Errorf( + "json request body could not be decoded: %+v body:%s", + err, + bodyString, + ) + resp := exec.DispatchError(ctx, gqlerror.List{gqlErr}) + log.Printf("decoding error: %+v body:%s", err.Error(), bodyString) + writeJson(w, resp) + return + } + + rc, opErr := exec.CreateOperationContext(ctx, params) + ctx = graphql.WithOperationContext(ctx, rc) + + w.Header().Set("Content-Type", "text/event-stream") + fmt.Fprint(w, ":\n\n") + flusher.Flush() + + if opErr != nil { + resp := exec.DispatchError(ctx, opErr) + writeJsonWithSSE(w, resp) + } else { + responses, ctx := exec.DispatchOperation(ctx, rc) + for { + response := responses(ctx) + if response == nil { + break + } + writeJsonWithSSE(w, response) + flusher.Flush() + } + } + + fmt.Fprint(w, "event: complete\n\n") +} + +func writeJsonWithSSE(w io.Writer, response *graphql.Response) { + b, err := json.Marshal(response) + if err != nil { + panic(err) + } + fmt.Fprintf(w, "event: next\ndata: %s\n\n", b) +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go index ce845c1964..19b7521c08 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go @@ -5,8 +5,9 @@ import ( "fmt" "io" - "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" ) func writeJson(w io.Writer, response *graphql.Response) { diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go index 51b1104ccc..e1334b9290 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go @@ -12,10 +12,11 @@ import ( "sync" "time" - "github.com/99designs/gqlgen/graphql" - "github.com/99designs/gqlgen/graphql/errcode" "github.com/gorilla/websocket" "github.com/vektah/gqlparser/v2/gqlerror" + + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/errcode" ) type ( @@ -24,8 +25,19 @@ type ( InitFunc WebsocketInitFunc InitTimeout time.Duration ErrorFunc WebsocketErrorFunc + CloseFunc WebsocketCloseFunc KeepAlivePingInterval time.Duration + PongOnlyInterval time.Duration PingPongInterval time.Duration + /* If PingPongInterval has a non-0 duration, then when the server sends a ping + * it sets a ReadDeadline of PingPongInterval*2 and if the client doesn't respond + * with pong before that deadline is reached then the connection will die with a + * 1006 error code. + * + * MissingPongOk if true, tells the server to not use a ReadDeadline such that a + * missing/slow pong response from the client doesn't kill the connection. + */ + MissingPongOk bool didInjectSubprotocols bool } @@ -37,14 +49,20 @@ type ( active map[string]context.CancelFunc mu sync.Mutex keepAliveTicker *time.Ticker + pongOnlyTicker *time.Ticker pingPongTicker *time.Ticker + receivedPong bool exec graphql.GraphExecutor + closed bool initPayload InitPayload } - WebsocketInitFunc func(ctx context.Context, initPayload InitPayload) (context.Context, error) + WebsocketInitFunc func(ctx context.Context, initPayload InitPayload) (context.Context, *InitPayload, error) WebsocketErrorFunc func(ctx context.Context, err error) + + // Callback called when websocket is closed. + WebsocketCloseFunc func(ctx context.Context, closeCode int) ) var errReadTimeout = errors.New("read timeout") @@ -175,8 +193,10 @@ func (c *wsConnection) init() bool { } } + var initAckPayload *InitPayload = nil if c.InitFunc != nil { - ctx, err := c.InitFunc(c.ctx, c.initPayload) + var ctx context.Context + ctx, initAckPayload, err = c.InitFunc(c.ctx, c.initPayload) if err != nil { c.sendConnectionError(err.Error()) c.close(websocket.CloseNormalClosure, "terminated") @@ -185,7 +205,15 @@ func (c *wsConnection) init() bool { c.ctx = ctx } - c.write(&message{t: connectionAckMessageType}) + if initAckPayload != nil { + initJsonAckPayload, err := json.Marshal(*initAckPayload) + if err != nil { + panic(err) + } + c.write(&message{t: connectionAckMessageType, payload: initJsonAckPayload}) + } else { + c.write(&message{t: connectionAckMessageType}) + } c.write(&message{t: keepAliveMessageType}) case connectionCloseMessageType: c.close(websocket.CloseNormalClosure, "terminated") @@ -224,16 +252,28 @@ func (c *wsConnection) run() { go c.keepAlive(ctx) } + // If we're running in graphql-transport-ws mode, create a timer that will trigger a + // just a pong message every interval + if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PongOnlyInterval != 0 { + c.mu.Lock() + c.pongOnlyTicker = time.NewTicker(c.PongOnlyInterval) + c.mu.Unlock() + + go c.keepAlivePongOnly(ctx) + } + // If we're running in graphql-transport-ws mode, create a timer that will - // trigger a ping message every interval + // trigger a ping message every interval and expect a pong! if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PingPongInterval != 0 { c.mu.Lock() c.pingPongTicker = time.NewTicker(c.PingPongInterval) c.mu.Unlock() - // Note: when the connection is closed by this deadline, the client - // will receive an "invalid close code" - c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) + if !c.MissingPongOk { + // Note: when the connection is closed by this deadline, the client + // will receive an "invalid close code" + c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) + } go c.ping(ctx) } @@ -268,7 +308,11 @@ func (c *wsConnection) run() { case pingMessageType: c.write(&message{t: pongMessageType, payload: m.payload}) case pongMessageType: - c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) + c.mu.Lock() + c.receivedPong = true + c.mu.Unlock() + // Clear ReadTimeout -- 0 time val clears. + c.conn.SetReadDeadline(time.Time{}) default: c.sendConnectionError("unexpected message %s", m.t) c.close(websocket.CloseProtocolError, "unexpected message") @@ -277,6 +321,18 @@ func (c *wsConnection) run() { } } +func (c *wsConnection) keepAlivePongOnly(ctx context.Context) { + for { + select { + case <-ctx.Done(): + c.pongOnlyTicker.Stop() + return + case <-c.pongOnlyTicker.C: + c.write(&message{t: pongMessageType, payload: json.RawMessage{}}) + } + } +} + func (c *wsConnection) keepAlive(ctx context.Context) { for { select { @@ -297,6 +353,14 @@ func (c *wsConnection) ping(ctx context.Context) { return case <-c.pingPongTicker.C: c.write(&message{t: pingMessageType, payload: json.RawMessage{}}) + // The initial deadline for this method is set in run() + // if we have not yet received a pong, don't reset the deadline. + c.mu.Lock() + if !c.MissingPongOk && c.receivedPong { + c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) + } + c.receivedPong = false + c.mu.Unlock() } } } @@ -350,6 +414,7 @@ func (c *wsConnection) subscribe(start time.Time, msg *message) { c.mu.Unlock() go func() { + ctx = withSubscriptionErrorContext(ctx) defer func() { if r := recover(); r != nil { err := rc.Recover(ctx, r) @@ -362,7 +427,11 @@ func (c *wsConnection) subscribe(start time.Time, msg *message) { } c.sendError(msg.id, gqlerr) } - c.complete(msg.id) + if errs := getSubscriptionError(ctx); len(errs) != 0 { + c.sendError(msg.id, errs...) + } else { + c.complete(msg.id) + } c.mu.Lock() delete(c.active, msg.id) c.mu.Unlock() @@ -422,10 +491,19 @@ func (c *wsConnection) sendConnectionError(format string, args ...interface{}) { func (c *wsConnection) close(closeCode int, message string) { c.mu.Lock() + if c.closed { + c.mu.Unlock() + return + } _ = c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, message)) for _, closer := range c.active { closer() } + c.closed = true c.mu.Unlock() _ = c.conn.Close() + + if c.CloseFunc != nil { + c.CloseFunc(c.ctx, closeCode) + } } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_resolver_error.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_resolver_error.go new file mode 100644 index 0000000000..cb8e3ed1a3 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_resolver_error.go @@ -0,0 +1,69 @@ +package transport + +import ( + "context" + + "github.com/vektah/gqlparser/v2/gqlerror" +) + +// A private key for context that only this package can access. This is important +// to prevent collisions between different context uses +var wsSubscriptionErrorCtxKey = &wsSubscriptionErrorContextKey{"subscription-error"} + +type wsSubscriptionErrorContextKey struct { + name string +} + +type subscriptionError struct { + errs []*gqlerror.Error +} + +// AddSubscriptionError is used to let websocket return an error message after subscription resolver returns a channel. +// for example: +// +// func (r *subscriptionResolver) Method(ctx context.Context) (<-chan *model.Message, error) { +// ch := make(chan *model.Message) +// go func() { +// defer func() { +// close(ch) +// } +// // some kind of block processing (e.g.: gRPC client streaming) +// stream, err := gRPCClientStreamRequest(ctx) +// if err != nil { +// transport.AddSubscriptionError(ctx, err) +// return // must return and close channel so websocket can send error back +// } +// for { +// m, err := stream.Recv() +// if err == io.EOF { +// return +// } +// if err != nil { +// transport.AddSubscriptionError(ctx, err) +// return // must return and close channel so websocket can send error back +// } +// ch <- m +// } +// }() +// +// return ch, nil +// } +// +// see https://github.com/99designs/gqlgen/pull/2506 for more details +func AddSubscriptionError(ctx context.Context, err *gqlerror.Error) { + subscriptionErrStruct := getSubscriptionErrorStruct(ctx) + subscriptionErrStruct.errs = append(subscriptionErrStruct.errs, err) +} + +func withSubscriptionErrorContext(ctx context.Context) context.Context { + return context.WithValue(ctx, wsSubscriptionErrorCtxKey, &subscriptionError{}) +} + +func getSubscriptionErrorStruct(ctx context.Context) *subscriptionError { + v, _ := ctx.Value(wsSubscriptionErrorCtxKey).(*subscriptionError) + return v +} + +func getSubscriptionError(ctx context.Context) []*gqlerror.Error { + return getSubscriptionErrorStruct(ctx).errs +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/id.go b/vendor/github.com/99designs/gqlgen/graphql/id.go index b24605d1d8..0583995f78 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/id.go +++ b/vendor/github.com/99designs/gqlgen/graphql/id.go @@ -56,3 +56,32 @@ func UnmarshalIntID(v interface{}) (int, error) { return 0, fmt.Errorf("%T is not an int", v) } } + +func MarshalUintID(i uint) Marshaler { + return WriterFunc(func(w io.Writer) { + writeQuotedString(w, strconv.FormatUint(uint64(i), 10)) + }) +} + +func UnmarshalUintID(v interface{}) (uint, error) { + switch v := v.(type) { + case string: + result, err := strconv.ParseUint(v, 10, 64) + return uint(result), err + case int: + return uint(v), nil + case int64: + return uint(v), nil + case int32: + return uint(v), nil + case uint32: + return uint(v), nil + case uint64: + return uint(v), nil + case json.Number: + result, err := strconv.ParseUint(string(v), 10, 64) + return uint(result), err + default: + return 0, fmt.Errorf("%T is not an uint", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go index 8482d62a86..30c865cc0d 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go @@ -74,13 +74,15 @@ func (f *Field) IsDeprecated() bool { } func (f *Field) DeprecationReason() *string { - if f.deprecation == nil { + if f.deprecation == nil || !f.IsDeprecated() { return nil } reason := f.deprecation.Arguments.ForName("reason") + if reason == nil { - return nil + defaultReason := "No longer supported" + return &defaultReason } return &reason.Value.Raw diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go index b7b0ad94e0..897b1a098f 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go @@ -2,7 +2,6 @@ package introspection import ( "sort" - "strings" "github.com/vektah/gqlparser/v2/ast" ) @@ -22,9 +21,6 @@ func (s *Schema) Types() []Type { typeIndex := map[string]Type{} typeNames := make([]string, 0, len(s.schema.Types)) for _, typ := range s.schema.Types { - if strings.HasPrefix(typ.Name, "__") { - continue - } typeNames = append(typeNames, typ.Name) typeIndex[typ.Name] = *WrapTypeFromDef(s.schema, typ) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/omittable.go b/vendor/github.com/99designs/gqlgen/graphql/omittable.go new file mode 100644 index 0000000000..89716400b6 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/omittable.go @@ -0,0 +1,58 @@ +package graphql + +import "encoding/json" + +// Omittable is a wrapper around a value that also stores whether it is set +// or not. +type Omittable[T any] struct { + value T + set bool +} + +var ( + _ json.Marshaler = Omittable[struct{}]{} + _ json.Unmarshaler = (*Omittable[struct{}])(nil) +) + +func OmittableOf[T any](value T) Omittable[T] { + return Omittable[T]{ + value: value, + set: true, + } +} + +func (o Omittable[T]) Value() T { + if !o.set { + var zero T + return zero + } + return o.value +} + +func (o Omittable[T]) ValueOK() (T, bool) { + if !o.set { + var zero T + return zero, false + } + return o.value, true +} + +func (o Omittable[T]) IsSet() bool { + return o.set +} + +func (o Omittable[T]) MarshalJSON() ([]byte, error) { + if !o.set { + return []byte("null"), nil + } + return json.Marshal(o.value) +} + +func (o *Omittable[T]) UnmarshalJSON(bytes []byte) error { + err := json.Unmarshal(bytes, &o.value) + if err != nil { + return err + } + o.set = true + return nil +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go b/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go new file mode 100644 index 0000000000..6928828cdf --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go @@ -0,0 +1,84 @@ +package playground + +import ( + "html/template" + "net/http" +) + +var altairPage = template.Must(template.New("altair").Parse(` + + + + + {{.title}} + + + + + + + + + +
+
+
+ Altair +
+
+ + + +
+
+
+
+ + + + + + + + +`)) + +// AltairHandler responsible for setting up the altair playground +func AltairHandler(title, endpoint string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + err := altairPage.Execute(w, map[string]interface{}{ + "title": title, + "endpoint": endpoint, + "endpointIsAbsolute": endpointHasScheme(endpoint), + "subscriptionEndpoint": getSubscriptionEndpoint(endpoint), + "version": "5.0.5", + "cssSRI": "sha256-kZ35e5mdMYN5ALEbnsrA2CLn85Oe4hBodfsih9BqNxs=", + "mainSRI": "sha256-nWdVTcGTlBDV1L04UQnqod+AJedzBCnKHv6Ct65liHE=", + "polyfillsSRI": "sha256-1aVEg2sROcCQ/RxU3AlcPaRZhZdIWA92q2M+mdd/R4c=", + "runtimeSRI": "sha256-cK2XhXqQr0WS1Z5eKNdac0rJxTD6miC3ubd+aEVMQDk=", + }) + if err != nil { + panic(err) + } + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go b/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go new file mode 100644 index 0000000000..750420b480 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go @@ -0,0 +1,186 @@ +package playground + +import ( + "encoding/json" + "fmt" + "html/template" + "net/http" +) + +// NOTE: New version available at https://embeddable-sandbox.cdn.apollographql.com/ --> +var apolloSandboxPage = template.Must(template.New("ApolloSandbox").Parse(` + + + + + {{.title}} + + + + + + +
+ + + +`)) + +// ApolloSandboxHandler responsible for setting up the apollo sandbox playground +func ApolloSandboxHandler(title, endpoint string, opts ...ApolloSandboxOption) http.HandlerFunc { + options := &apolloSandboxOptions{ + HideCookieToggle: true, + InitialState: apolloSandboxInitialState{ + IncludeCookies: true, + PollForSchemaUpdates: false, + }, + } + + for _, opt := range opts { + opt(options) + } + + optionsBytes, err := json.Marshal(options) + if err != nil { + panic(fmt.Errorf("failed to marshal apollo sandbox options: %w", err)) + } + + return func(w http.ResponseWriter, r *http.Request) { + err := apolloSandboxPage.Execute(w, map[string]interface{}{ + "title": title, + "endpoint": endpoint, + "endpointIsAbsolute": endpointHasScheme(endpoint), + "mainSRI": "sha256-pYhw/8TGkZxk960PMMpDtjhw9YtKXUzGv6XQQaMJSh8=", + "options": string(optionsBytes), + }) + if err != nil { + panic(err) + } + } +} + +// See https://www.apollographql.com/docs/graphos/explorer/sandbox/#options --> +type apolloSandboxOptions struct { + HideCookieToggle bool `json:"hideCookieToggle"` + EndpointIsEditable bool `json:"endpointIsEditable"` + InitialState apolloSandboxInitialState `json:"initialState,omitempty"` +} + +type apolloSandboxInitialState struct { + IncludeCookies bool `json:"includeCookies"` + Document string `json:"document,omitempty"` + Variables map[string]any `json:"variables,omitempty"` + Headers map[string]any `json:"headers,omitempty"` + CollectionId string `json:"collectionId,omitempty"` + OperationId string `json:"operationId,omitempty"` + PollForSchemaUpdates bool `json:"pollForSchemaUpdates"` + SharedHeaders map[string]any `json:"sharedHeaders,omitempty"` +} + +type ApolloSandboxOption func(options *apolloSandboxOptions) + +// WithApolloSandboxHideCookieToggle By default, the embedded Sandbox does not show the Include cookies toggle in its connection settings. +// +// Set hideCookieToggle to false to enable users of your embedded Sandbox instance to toggle the Include cookies setting. +func WithApolloSandboxHideCookieToggle(hideCookieToggle bool) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.HideCookieToggle = hideCookieToggle + } +} + +// WithApolloSandboxEndpointIsEditable By default, the embedded Sandbox has a URL input box that is editable by users. +// +// Set endpointIsEditable to false to prevent users of your embedded Sandbox instance from changing the endpoint URL. +func WithApolloSandboxEndpointIsEditable(endpointIsEditable bool) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.EndpointIsEditable = endpointIsEditable + } +} + +// WithApolloSandboxInitialStateIncludeCookies Set this value to true if you want the Sandbox to pass { credentials: 'include' } for its requests by default. +// +// If you set hideCookieToggle to false, users can override this default setting with the Include cookies toggle. (By default, the embedded Sandbox does not show the Include cookies toggle in its connection settings.) +// +// If you also pass the handleRequest option, this option is ignored. +// +// Read more about the fetch API and credentials here https://developer.mozilla.org/en-US/docs/Web/API/fetch#credentials +func WithApolloSandboxInitialStateIncludeCookies(includeCookies bool) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.IncludeCookies = includeCookies + } +} + +// WithApolloSandboxInitialStateDocument Document operation to populate in the Sandbox's editor on load. +// +// If you omit this, the Sandbox initially loads an example query based on your schema. +func WithApolloSandboxInitialStateDocument(document string) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.Document = document + } +} + +// WithApolloSandboxInitialStateVariables Variables containing initial variable values to populate in the Sandbox on load. +// +// If provided, these variables should apply to the initial query you provide for document. +func WithApolloSandboxInitialStateVariables(variables map[string]any) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.Variables = variables + } +} + +// WithApolloSandboxInitialStateHeaders Headers containing initial variable values to populate in the Sandbox on load. +// +// If provided, these variables should apply to the initial query you provide for document. +func WithApolloSandboxInitialStateHeaders(headers map[string]any) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.Headers = headers + } +} + +// WithApolloSandboxInitialStateCollectionIdAndOperationId The ID of a collection, paired with an operation ID to populate in the Sandbox on load. +// +// You can find these values from a registered graph in Studio by clicking the ... menu next to an operation in the Explorer of that graph and selecting View operation details. +func WithApolloSandboxInitialStateCollectionIdAndOperationId(collectionId, operationId string) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.CollectionId = collectionId + options.InitialState.OperationId = operationId + } +} + +// WithApolloSandboxInitialStatePollForSchemaUpdates If true, the embedded Sandbox periodically polls your initialEndpoint for schema updates. +// +// The default value is false. +func WithApolloSandboxInitialStatePollForSchemaUpdates(pollForSchemaUpdates bool) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.PollForSchemaUpdates = pollForSchemaUpdates + } +} + +// WithApolloSandboxInitialStateSharedHeaders Headers that are applied by default to every operation executed by the embedded Sandbox. +// +// Users can disable the application of these headers, but they can't modify their values. +// +// The embedded Sandbox always includes these headers in its introspection queries to your initialEndpoint. +func WithApolloSandboxInitialStateSharedHeaders(sharedHeaders map[string]any) ApolloSandboxOption { + return func(options *apolloSandboxOptions) { + options.InitialState.SharedHeaders = sharedHeaders + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go b/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go index 08e5e4a8d7..05ad02332f 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go +++ b/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go @@ -24,12 +24,12 @@ var page = template.Must(template.New("graphiql").Parse(` } @@ -58,13 +58,24 @@ var page = template.Must(template.New("graphiql").Parse(` const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:'; const subscriptionUrl = wsProto + '//' + location.host + {{.endpoint}}; {{- end}} +{{- if .fetcherHeaders}} + const fetcherHeaders = {{.fetcherHeaders}}; +{{- else}} + const fetcherHeaders = undefined; +{{- end}} +{{- if .uiHeaders}} + const uiHeaders = {{.uiHeaders}}; +{{- else}} + const uiHeaders = undefined; +{{- end}} - const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl }); + const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl, headers: fetcherHeaders }); ReactDOM.render( React.createElement(GraphiQL, { fetcher: fetcher, isHeadersEditorEnabled: true, - shouldPersistHeaders: true + shouldPersistHeaders: true, + headers: JSON.stringify(uiHeaders, null, 2) }), document.getElementById('graphiql'), ); @@ -75,18 +86,27 @@ var page = template.Must(template.New("graphiql").Parse(` // Handler responsible for setting up the playground func Handler(title string, endpoint string) http.HandlerFunc { + return HandlerWithHeaders(title, endpoint, nil, nil) +} + +// HandlerWithHeaders sets up the playground. +// fetcherHeaders are used by the playground's fetcher instance and will not be visible in the UI. +// uiHeaders are default headers that will show up in the UI headers editor. +func HandlerWithHeaders(title string, endpoint string, fetcherHeaders map[string]string, uiHeaders map[string]string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/html; charset=UTF-8") err := page.Execute(w, map[string]interface{}{ "title": title, "endpoint": endpoint, + "fetcherHeaders": fetcherHeaders, + "uiHeaders": uiHeaders, "endpointIsAbsolute": endpointHasScheme(endpoint), "subscriptionEndpoint": getSubscriptionEndpoint(endpoint), - "version": "2.0.7", - "cssSRI": "sha256-gQryfbGYeYFxnJYnfPStPYFt0+uv8RP8Dm++eh00G9c=", - "jsSRI": "sha256-qQ6pw7LwTLC+GfzN+cJsYXfVWRKH9O5o7+5H96gTJhQ=", - "reactSRI": "sha256-Ipu/TQ50iCCVZBUsZyNJfxrDk0E2yhaEIz0vqI+kFG8=", - "reactDOMSRI": "sha256-nbMykgB6tsOFJ7OdVmPpdqMFVk4ZsqWocT6issAPUF0=", + "version": "3.0.6", + "cssSRI": "sha256-wTzfn13a+pLMB5rMeysPPR1hO7x0SwSeQI+cnw7VdbE=", + "jsSRI": "sha256-eNxH+Ah7Z9up9aJYTQycgyNuy953zYZwE9Rqf5rH+r4=", + "reactSRI": "sha256-S0lp+k7zWUMk2ixteM6HZvu8L9Eh//OVrt+ZfbCpmgY=", + "reactDOMSRI": "sha256-IXWO0ITNDjfnNXIu5POVfqlgYoop36bDzhodR6LW5Pc=", }) if err != nil { panic(err) diff --git a/vendor/github.com/99designs/gqlgen/graphql/response.go b/vendor/github.com/99designs/gqlgen/graphql/response.go index 0d36049a33..a82f27e27c 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/response.go +++ b/vendor/github.com/99designs/gqlgen/graphql/response.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" + "github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/gqlerror" ) @@ -14,6 +15,9 @@ import ( type Response struct { Errors gqlerror.List `json:"errors,omitempty"` Data json.RawMessage `json:"data"` + Label string `json:"label,omitempty"` + Path ast.Path `json:"path,omitempty"` + HasNext *bool `json:"hasNext,omitempty"` Extensions map[string]interface{} `json:"extensions,omitempty"` } diff --git a/vendor/github.com/99designs/gqlgen/graphql/stats.go b/vendor/github.com/99designs/gqlgen/graphql/stats.go index a52e143ebe..31b9e6055b 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/stats.go +++ b/vendor/github.com/99designs/gqlgen/graphql/stats.go @@ -12,7 +12,7 @@ type Stats struct { Parsing TraceTiming Validation TraceTiming - // Stats collected by handler extensions. Dont use directly, the extension should provide a type safe way to + // Stats collected by handler extensions. Don't use directly, the extension should provide a type safe way to // access this. extension map[string]interface{} } @@ -26,7 +26,7 @@ var ctxTraceStart key = "trace_start" // StartOperationTrace captures the current time and stores it in context. This will eventually be added to request // context but we want to grab it as soon as possible. For transports that can only handle a single graphql query -// per http requests you dont need to call this at all, the server will do it for you. For transports that handle +// per http requests you don't need to call this at all, the server will do it for you. For transports that handle // multiple (eg batching, subscriptions) this should be called before decoding each request. func StartOperationTrace(ctx context.Context) context.Context { return context.WithValue(ctx, ctxTraceStart, Now()) diff --git a/vendor/github.com/99designs/gqlgen/graphql/string.go b/vendor/github.com/99designs/gqlgen/graphql/string.go index 742e50cc3b..4da4706478 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/string.go +++ b/vendor/github.com/99designs/gqlgen/graphql/string.go @@ -1,6 +1,7 @@ package graphql import ( + "encoding/json" "fmt" "io" "strconv" @@ -55,7 +56,9 @@ func UnmarshalString(v interface{}) (string, error) { case int64: return strconv.FormatInt(v, 10), nil case float64: - return fmt.Sprintf("%f", v), nil + return strconv.FormatFloat(v, 'f', -1, 64), nil + case json.Number: + return string(v), nil case bool: if v { return "true", nil diff --git a/vendor/github.com/99designs/gqlgen/graphql/uint.go b/vendor/github.com/99designs/gqlgen/graphql/uint.go index 9349c2f4d2..8730d90042 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/uint.go +++ b/vendor/github.com/99designs/gqlgen/graphql/uint.go @@ -2,6 +2,7 @@ package graphql import ( "encoding/json" + "errors" "fmt" "io" "strconv" @@ -19,8 +20,16 @@ func UnmarshalUint(v interface{}) (uint, error) { u64, err := strconv.ParseUint(v, 10, 64) return uint(u64), err case int: + if v < 0 { + return 0, errors.New("cannot convert negative numbers to uint") + } + return uint(v), nil case int64: + if v < 0 { + return 0, errors.New("cannot convert negative numbers to uint") + } + return uint(v), nil case json.Number: u64, err := strconv.ParseUint(string(v), 10, 64) @@ -41,8 +50,16 @@ func UnmarshalUint64(v interface{}) (uint64, error) { case string: return strconv.ParseUint(v, 10, 64) case int: + if v < 0 { + return 0, errors.New("cannot convert negative numbers to uint64") + } + return uint64(v), nil case int64: + if v < 0 { + return 0, errors.New("cannot convert negative numbers to uint64") + } + return uint64(v), nil case json.Number: return strconv.ParseUint(string(v), 10, 64) @@ -60,14 +77,22 @@ func MarshalUint32(i uint32) Marshaler { func UnmarshalUint32(v interface{}) (uint32, error) { switch v := v.(type) { case string: - iv, err := strconv.ParseInt(v, 10, 32) + iv, err := strconv.ParseUint(v, 10, 32) if err != nil { return 0, err } return uint32(iv), nil case int: + if v < 0 { + return 0, errors.New("cannot convert negative numbers to uint32") + } + return uint32(v), nil case int64: + if v < 0 { + return 0, errors.New("cannot convert negative numbers to uint32") + } + return uint32(v), nil case json.Number: iv, err := strconv.ParseUint(string(v), 10, 32) diff --git a/vendor/github.com/99designs/gqlgen/graphql/uuid.go b/vendor/github.com/99designs/gqlgen/graphql/uuid.go new file mode 100644 index 0000000000..e9f22dccdb --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/uuid.go @@ -0,0 +1,25 @@ +package graphql + +import ( + "fmt" + + "github.com/google/uuid" +) + +func MarshalUUID(id uuid.UUID) Marshaler { + if id == uuid.Nil { + return Null + } + return MarshalString(id.String()) +} + +func UnmarshalUUID(v any) (uuid.UUID, error) { + switch v := v.(type) { + case string: + return uuid.Parse(v) + case []byte: + return uuid.ParseBytes(v) + default: + return uuid.Nil, fmt.Errorf("%T is not a uuid", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/version.go b/vendor/github.com/99designs/gqlgen/graphql/version.go index a03d70aca1..9e108750e5 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/version.go +++ b/vendor/github.com/99designs/gqlgen/graphql/version.go @@ -1,3 +1,3 @@ package graphql -const Version = "v0.17.19" +const Version = "v0.17.46" diff --git a/vendor/github.com/cespare/xxhash/LICENSE.txt b/vendor/github.com/cespare/xxhash/LICENSE.txt new file mode 100644 index 0000000000..24b53065f4 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2016 Caleb Spare + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cespare/xxhash/README.md b/vendor/github.com/cespare/xxhash/README.md new file mode 100644 index 0000000000..0982fd25e5 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/README.md @@ -0,0 +1,50 @@ +# xxhash + +[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash) + +xxhash is a Go implementation of the 64-bit +[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a +high-quality hashing algorithm that is much faster than anything in the Go +standard library. + +The API is very small, taking its cue from the other hashing packages in the +standard library: + + $ go doc github.com/cespare/xxhash ! + package xxhash // import "github.com/cespare/xxhash" + + Package xxhash implements the 64-bit variant of xxHash (XXH64) as described + at http://cyan4973.github.io/xxHash/. + + func New() hash.Hash64 + func Sum64(b []byte) uint64 + func Sum64String(s string) uint64 + +This implementation provides a fast pure-Go implementation and an even faster +assembly implementation for amd64. + +## Benchmarks + +Here are some quick benchmarks comparing the pure-Go and assembly +implementations of Sum64 against another popular Go XXH64 implementation, +[github.com/OneOfOne/xxhash](https://github.com/OneOfOne/xxhash): + +| input size | OneOfOne | cespare (purego) | cespare | +| --- | --- | --- | --- | +| 5 B | 416 MB/s | 720 MB/s | 872 MB/s | +| 100 B | 3980 MB/s | 5013 MB/s | 5252 MB/s | +| 4 KB | 12727 MB/s | 12999 MB/s | 13026 MB/s | +| 10 MB | 9879 MB/s | 10775 MB/s | 10913 MB/s | + +These numbers were generated with: + +``` +$ go test -benchtime 10s -bench '/OneOfOne,' +$ go test -tags purego -benchtime 10s -bench '/xxhash,' +$ go test -benchtime 10s -bench '/xxhash,' +``` + +## Projects using this package + +- [InfluxDB](https://github.com/influxdata/influxdb) +- [Prometheus](https://github.com/prometheus/prometheus) diff --git a/vendor/github.com/cespare/xxhash/rotate.go b/vendor/github.com/cespare/xxhash/rotate.go new file mode 100644 index 0000000000..f3eac5ebc0 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/rotate.go @@ -0,0 +1,14 @@ +// +build !go1.9 + +package xxhash + +// TODO(caleb): After Go 1.10 comes out, remove this fallback code. + +func rol1(x uint64) uint64 { return (x << 1) | (x >> (64 - 1)) } +func rol7(x uint64) uint64 { return (x << 7) | (x >> (64 - 7)) } +func rol11(x uint64) uint64 { return (x << 11) | (x >> (64 - 11)) } +func rol12(x uint64) uint64 { return (x << 12) | (x >> (64 - 12)) } +func rol18(x uint64) uint64 { return (x << 18) | (x >> (64 - 18)) } +func rol23(x uint64) uint64 { return (x << 23) | (x >> (64 - 23)) } +func rol27(x uint64) uint64 { return (x << 27) | (x >> (64 - 27)) } +func rol31(x uint64) uint64 { return (x << 31) | (x >> (64 - 31)) } diff --git a/vendor/github.com/cespare/xxhash/rotate19.go b/vendor/github.com/cespare/xxhash/rotate19.go new file mode 100644 index 0000000000..b99612bab8 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/rotate19.go @@ -0,0 +1,14 @@ +// +build go1.9 + +package xxhash + +import "math/bits" + +func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } +func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } +func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } +func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } +func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } +func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } +func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } +func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/cespare/xxhash/xxhash.go b/vendor/github.com/cespare/xxhash/xxhash.go new file mode 100644 index 0000000000..f896bd28f0 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/xxhash.go @@ -0,0 +1,168 @@ +// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described +// at http://cyan4973.github.io/xxHash/. +package xxhash + +import ( + "encoding/binary" + "hash" +) + +const ( + prime1 uint64 = 11400714785074694791 + prime2 uint64 = 14029467366897019727 + prime3 uint64 = 1609587929392839161 + prime4 uint64 = 9650029242287828579 + prime5 uint64 = 2870177450012600261 +) + +// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where +// possible in the Go code is worth a small (but measurable) performance boost +// by avoiding some MOVQs. Vars are needed for the asm and also are useful for +// convenience in the Go code in a few places where we need to intentionally +// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the +// result overflows a uint64). +var ( + prime1v = prime1 + prime2v = prime2 + prime3v = prime3 + prime4v = prime4 + prime5v = prime5 +) + +type xxh struct { + v1 uint64 + v2 uint64 + v3 uint64 + v4 uint64 + total int + mem [32]byte + n int // how much of mem is used +} + +// New creates a new hash.Hash64 that implements the 64-bit xxHash algorithm. +func New() hash.Hash64 { + var x xxh + x.Reset() + return &x +} + +func (x *xxh) Reset() { + x.n = 0 + x.total = 0 + x.v1 = prime1v + prime2 + x.v2 = prime2 + x.v3 = 0 + x.v4 = -prime1v +} + +func (x *xxh) Size() int { return 8 } +func (x *xxh) BlockSize() int { return 32 } + +// Write adds more data to x. It always returns len(b), nil. +func (x *xxh) Write(b []byte) (n int, err error) { + n = len(b) + x.total += len(b) + + if x.n+len(b) < 32 { + // This new data doesn't even fill the current block. + copy(x.mem[x.n:], b) + x.n += len(b) + return + } + + if x.n > 0 { + // Finish off the partial block. + copy(x.mem[x.n:], b) + x.v1 = round(x.v1, u64(x.mem[0:8])) + x.v2 = round(x.v2, u64(x.mem[8:16])) + x.v3 = round(x.v3, u64(x.mem[16:24])) + x.v4 = round(x.v4, u64(x.mem[24:32])) + b = b[32-x.n:] + x.n = 0 + } + + if len(b) >= 32 { + // One or more full blocks left. + b = writeBlocks(x, b) + } + + // Store any remaining partial block. + copy(x.mem[:], b) + x.n = len(b) + + return +} + +func (x *xxh) Sum(b []byte) []byte { + s := x.Sum64() + return append( + b, + byte(s>>56), + byte(s>>48), + byte(s>>40), + byte(s>>32), + byte(s>>24), + byte(s>>16), + byte(s>>8), + byte(s), + ) +} + +func (x *xxh) Sum64() uint64 { + var h uint64 + + if x.total >= 32 { + v1, v2, v3, v4 := x.v1, x.v2, x.v3, x.v4 + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = x.v3 + prime5 + } + + h += uint64(x.total) + + i, end := 0, x.n + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(x.mem[i:i+8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(x.mem[i:i+4])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for i < end { + h ^= uint64(x.mem[i]) * prime5 + h = rol11(h) * prime1 + i++ + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } +func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func round(acc, input uint64) uint64 { + acc += input * prime2 + acc = rol31(acc) + acc *= prime1 + return acc +} + +func mergeRound(acc, val uint64) uint64 { + val = round(0, val) + acc ^= val + acc = acc*prime1 + prime4 + return acc +} diff --git a/vendor/github.com/cespare/xxhash/xxhash_amd64.go b/vendor/github.com/cespare/xxhash/xxhash_amd64.go new file mode 100644 index 0000000000..d617652680 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/xxhash_amd64.go @@ -0,0 +1,12 @@ +// +build !appengine +// +build gc +// +build !purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +// +//go:noescape +func Sum64(b []byte) uint64 + +func writeBlocks(x *xxh, b []byte) []byte diff --git a/vendor/github.com/cespare/xxhash/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/xxhash_amd64.s new file mode 100644 index 0000000000..757f2011f0 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/xxhash_amd64.s @@ -0,0 +1,233 @@ +// +build !appengine +// +build gc +// +build !purego + +#include "textflag.h" + +// Register allocation: +// AX h +// CX pointer to advance through b +// DX n +// BX loop end +// R8 v1, k1 +// R9 v2 +// R10 v3 +// R11 v4 +// R12 tmp +// R13 prime1v +// R14 prime2v +// R15 prime4v + +// round reads from and advances the buffer pointer in CX. +// It assumes that R13 has prime1v and R14 has prime2v. +#define round(r) \ + MOVQ (CX), R12 \ + ADDQ $8, CX \ + IMULQ R14, R12 \ + ADDQ R12, r \ + ROLQ $31, r \ + IMULQ R13, r + +// mergeRound applies a merge round on the two registers acc and val. +// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v. +#define mergeRound(acc, val) \ + IMULQ R14, val \ + ROLQ $31, val \ + IMULQ R13, val \ + XORQ val, acc \ + IMULQ R13, acc \ + ADDQ R15, acc + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT, $0-32 + // Load fixed primes. + MOVQ ·prime1v(SB), R13 + MOVQ ·prime2v(SB), R14 + MOVQ ·prime4v(SB), R15 + + // Load slice. + MOVQ b_base+0(FP), CX + MOVQ b_len+8(FP), DX + LEAQ (CX)(DX*1), BX + + // The first loop limit will be len(b)-32. + SUBQ $32, BX + + // Check whether we have at least one block. + CMPQ DX, $32 + JLT noBlocks + + // Set up initial state (v1, v2, v3, v4). + MOVQ R13, R8 + ADDQ R14, R8 + MOVQ R14, R9 + XORQ R10, R10 + XORQ R11, R11 + SUBQ R13, R11 + + // Loop until CX > BX. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ CX, BX + JLE blockLoop + + MOVQ R8, AX + ROLQ $1, AX + MOVQ R9, R12 + ROLQ $7, R12 + ADDQ R12, AX + MOVQ R10, R12 + ROLQ $12, R12 + ADDQ R12, AX + MOVQ R11, R12 + ROLQ $18, R12 + ADDQ R12, AX + + mergeRound(AX, R8) + mergeRound(AX, R9) + mergeRound(AX, R10) + mergeRound(AX, R11) + + JMP afterBlocks + +noBlocks: + MOVQ ·prime5v(SB), AX + +afterBlocks: + ADDQ DX, AX + + // Right now BX has len(b)-32, and we want to loop until CX > len(b)-8. + ADDQ $24, BX + + CMPQ CX, BX + JG fourByte + +wordLoop: + // Calculate k1. + MOVQ (CX), R8 + ADDQ $8, CX + IMULQ R14, R8 + ROLQ $31, R8 + IMULQ R13, R8 + + XORQ R8, AX + ROLQ $27, AX + IMULQ R13, AX + ADDQ R15, AX + + CMPQ CX, BX + JLE wordLoop + +fourByte: + ADDQ $4, BX + CMPQ CX, BX + JG singles + + MOVL (CX), R8 + ADDQ $4, CX + IMULQ R13, R8 + XORQ R8, AX + + ROLQ $23, AX + IMULQ R14, AX + ADDQ ·prime3v(SB), AX + +singles: + ADDQ $4, BX + CMPQ CX, BX + JGE finalize + +singlesLoop: + MOVBQZX (CX), R12 + ADDQ $1, CX + IMULQ ·prime5v(SB), R12 + XORQ R12, AX + + ROLQ $11, AX + IMULQ R13, AX + + CMPQ CX, BX + JL singlesLoop + +finalize: + MOVQ AX, R12 + SHRQ $33, R12 + XORQ R12, AX + IMULQ R14, AX + MOVQ AX, R12 + SHRQ $29, R12 + XORQ R12, AX + IMULQ ·prime3v(SB), AX + MOVQ AX, R12 + SHRQ $32, R12 + XORQ R12, AX + + MOVQ AX, ret+24(FP) + RET + +// writeBlocks uses the same registers as above except that it uses AX to store +// the x pointer. + +// func writeBlocks(x *xxh, b []byte) []byte +TEXT ·writeBlocks(SB), NOSPLIT, $0-56 + // Load fixed primes needed for round. + MOVQ ·prime1v(SB), R13 + MOVQ ·prime2v(SB), R14 + + // Load slice. + MOVQ b_base+8(FP), CX + MOVQ CX, ret_base+32(FP) // initialize return base pointer; see NOTE below + MOVQ b_len+16(FP), DX + LEAQ (CX)(DX*1), BX + SUBQ $32, BX + + // Load vN from x. + MOVQ x+0(FP), AX + MOVQ 0(AX), R8 // v1 + MOVQ 8(AX), R9 // v2 + MOVQ 16(AX), R10 // v3 + MOVQ 24(AX), R11 // v4 + + // We don't need to check the loop condition here; this function is + // always called with at least one block of data to process. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ CX, BX + JLE blockLoop + + // Copy vN back to x. + MOVQ R8, 0(AX) + MOVQ R9, 8(AX) + MOVQ R10, 16(AX) + MOVQ R11, 24(AX) + + // Construct return slice. + // NOTE: It's important that we don't construct a slice that has a base + // pointer off the end of the original slice, as in Go 1.7+ this will + // cause runtime crashes. (See discussion in, for example, + // https://github.com/golang/go/issues/16772.) + // Therefore, we calculate the length/cap first, and if they're zero, we + // keep the old base. This is what the compiler does as well if you + // write code like + // b = b[len(b):] + + // New length is 32 - (CX - BX) -> BX+32 - CX. + ADDQ $32, BX + SUBQ CX, BX + JZ afterSetBase + + MOVQ CX, ret_base+32(FP) + +afterSetBase: + MOVQ BX, ret_len+40(FP) + MOVQ BX, ret_cap+48(FP) // set cap == len + + RET diff --git a/vendor/github.com/cespare/xxhash/xxhash_other.go b/vendor/github.com/cespare/xxhash/xxhash_other.go new file mode 100644 index 0000000000..c68d13f89e --- /dev/null +++ b/vendor/github.com/cespare/xxhash/xxhash_other.go @@ -0,0 +1,75 @@ +// +build !amd64 appengine !gc purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 { + // A simpler version would be + // x := New() + // x.Write(b) + // return x.Sum64() + // but this is faster, particularly for small inputs. + + n := len(b) + var h uint64 + + if n >= 32 { + v1 := prime1v + prime2 + v2 := prime2 + v3 := uint64(0) + v4 := -prime1v + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = prime5 + } + + h += uint64(n) + + i, end := 0, len(b) + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(b[i:i+8:len(b)])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(b[i:i+4:len(b)])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for ; i < end; i++ { + h ^= uint64(b[i]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +func writeBlocks(x *xxh, b []byte) []byte { + v1, v2, v3, v4 := x.v1, x.v2, x.v3, x.v4 + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + x.v1, x.v2, x.v3, x.v4 = v1, v2, v3, v4 + return b +} diff --git a/vendor/github.com/cespare/xxhash/xxhash_safe.go b/vendor/github.com/cespare/xxhash/xxhash_safe.go new file mode 100644 index 0000000000..dfa15ab7e2 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/xxhash_safe.go @@ -0,0 +1,10 @@ +// +build appengine + +// This file contains the safe implementations of otherwise unsafe-using code. + +package xxhash + +// Sum64String computes the 64-bit xxHash digest of s. +func Sum64String(s string) uint64 { + return Sum64([]byte(s)) +} diff --git a/vendor/github.com/cespare/xxhash/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/xxhash_unsafe.go new file mode 100644 index 0000000000..d2b64e8bb0 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/xxhash_unsafe.go @@ -0,0 +1,30 @@ +// +build !appengine + +// This file encapsulates usage of unsafe. +// xxhash_safe.go contains the safe implementations. + +package xxhash + +import ( + "reflect" + "unsafe" +) + +// Sum64String computes the 64-bit xxHash digest of s. +// It may be faster than Sum64([]byte(s)) by avoiding a copy. +// +// TODO(caleb): Consider removing this if an optimization is ever added to make +// it unnecessary: https://golang.org/issue/2205. +// +// TODO(caleb): We still have a function call; we could instead write Go/asm +// copies of Sum64 for strings to squeeze out a bit more speed. +func Sum64String(s string) uint64 { + // See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ + // for some discussion about this unsafe conversion. + var b []byte + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data + bh.Len = len(s) + bh.Cap = len(s) + return Sum64(b) +} diff --git a/vendor/github.com/google/uuid/.travis.yml b/vendor/github.com/google/uuid/.travis.yml deleted file mode 100644 index d8156a60ba..0000000000 --- a/vendor/github.com/google/uuid/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go - -go: - - 1.4.3 - - 1.5.3 - - tip - -script: - - go test -v ./... diff --git a/vendor/github.com/google/uuid/CHANGELOG.md b/vendor/github.com/google/uuid/CHANGELOG.md new file mode 100644 index 0000000000..7ec5ac7ea9 --- /dev/null +++ b/vendor/github.com/google/uuid/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16) + + +### Features + +* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3)) + + +### Bug Fixes + +* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06)) +* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6)) + +## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12) + + +### Features + +* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29)) + +## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26) + + +### Features + +* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4)) + +### Fixes + +* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior) + +## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) + + +### Bug Fixes + +* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) + +## Changelog diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md index 04fdf09f13..a502fdc515 100644 --- a/vendor/github.com/google/uuid/CONTRIBUTING.md +++ b/vendor/github.com/google/uuid/CONTRIBUTING.md @@ -2,6 +2,22 @@ We definitely welcome patches and contribution to this project! +### Tips + +Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). + +Always try to include a test case! If it is not possible or not necessary, +please explain why in the pull request description. + +### Releasing + +Commits that would precipitate a SemVer change, as described in the Conventional +Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) +to create a release candidate pull request. Once submitted, `release-please` +will create a release. + +For tips on how to work with `release-please`, see its documentation. + ### Legal requirements In order to protect both you and ourselves, you will need to sign the diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md index f765a46f91..3e9a61889d 100644 --- a/vendor/github.com/google/uuid/README.md +++ b/vendor/github.com/google/uuid/README.md @@ -1,6 +1,6 @@ -# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) +# uuid The uuid package generates and inspects UUIDs based on -[RFC 4122](http://tools.ietf.org/html/rfc4122) +[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) and DCE 1.1: Authentication and Security Services. This package is based on the github.com/pborman/uuid package (previously named @@ -9,10 +9,12 @@ a UUID is a 16 byte array rather than a byte slice. One loss due to this change is the ability to represent an invalid UUID (vs a NIL UUID). ###### Install -`go get github.com/google/uuid` +```sh +go get github.com/google/uuid +``` ###### Documentation -[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) +[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here: diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go index b404f4bec2..dc60082d3b 100644 --- a/vendor/github.com/google/uuid/hash.go +++ b/vendor/github.com/google/uuid/hash.go @@ -17,6 +17,12 @@ var ( NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) Nil UUID // empty UUID, all zeros + + // The Max UUID is special form of UUID that is specified to have all 128 bits set to 1. + Max = UUID{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + } ) // NewHash returns a new UUID derived from the hash of space concatenated with diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go index 24b78edc90..b2a0bc8711 100644 --- a/vendor/github.com/google/uuid/node_js.go +++ b/vendor/github.com/google/uuid/node_js.go @@ -7,6 +7,6 @@ package uuid // getHardwareInterface returns nil values for the JS version of the code. -// This remvoves the "net" dependency, because it is not used in the browser. +// This removes the "net" dependency, because it is not used in the browser. // Using the "net" library inflates the size of the transpiled JS code by 673k bytes. func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go index e6ef06cdc8..c351129279 100644 --- a/vendor/github.com/google/uuid/time.go +++ b/vendor/github.com/google/uuid/time.go @@ -108,12 +108,23 @@ func setClockSequence(seq int) { } // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in -// uuid. The time is only defined for version 1 and 2 UUIDs. +// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs. func (uuid UUID) Time() Time { - time := int64(binary.BigEndian.Uint32(uuid[0:4])) - time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 - time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 - return Time(time) + var t Time + switch uuid.Version() { + case 6: + time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110 + t = Time(time) + case 7: + time := binary.BigEndian.Uint64(uuid[:8]) + t = Time((time>>16)*10000 + g1582ns100) + default: // forward compatible + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + t = Time(time) + } + return t } // ClockSequence returns the clock sequence encoded in uuid. diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index a57207aeb6..5232b48678 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -56,11 +56,15 @@ func IsInvalidLengthError(err error) bool { return ok } -// Parse decodes s into a UUID or returns an error. Both the standard UUID -// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the -// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex -// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. +// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both +// the standard UUID forms defined in RFC 4122 +// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition, +// Parse accepts non-standard strings such as the raw hex encoding +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings, +// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are +// examined in the latter case. Parse should not be used to validate strings as +// it parses non-standard encodings as indicated above. func Parse(s string) (UUID, error) { var uuid UUID switch len(s) { @@ -69,7 +73,7 @@ func Parse(s string) (UUID, error) { // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: - if strings.ToLower(s[:9]) != "urn:uuid:" { + if !strings.EqualFold(s[:9], "urn:uuid:") { return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] @@ -101,7 +105,8 @@ func Parse(s string) (UUID, error) { 9, 11, 14, 16, 19, 21, - 24, 26, 28, 30, 32, 34} { + 24, 26, 28, 30, 32, 34, + } { v, ok := xtob(s[x], s[x+1]) if !ok { return uuid, errors.New("invalid UUID format") @@ -117,7 +122,7 @@ func ParseBytes(b []byte) (UUID, error) { switch len(b) { case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) } b = b[9:] @@ -145,7 +150,8 @@ func ParseBytes(b []byte) (UUID, error) { 9, 11, 14, 16, 19, 21, - 24, 26, 28, 30, 32, 34} { + 24, 26, 28, 30, 32, 34, + } { v, ok := xtob(b[x], b[x+1]) if !ok { return uuid, errors.New("invalid UUID format") @@ -180,6 +186,59 @@ func Must(uuid UUID, err error) UUID { return uuid } +// Validate returns an error if s is not a properly formatted UUID in one of the following formats: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} +// It returns an error if the format is invalid, otherwise nil. +func Validate(s string) error { + switch len(s) { + // Standard UUID format + case 36: + + // UUID with "urn:uuid:" prefix + case 36 + 9: + if !strings.EqualFold(s[:9], "urn:uuid:") { + return fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // UUID enclosed in braces + case 36 + 2: + if s[0] != '{' || s[len(s)-1] != '}' { + return fmt.Errorf("invalid bracketed UUID format") + } + s = s[1 : len(s)-1] + + // UUID without hyphens + case 32: + for i := 0; i < len(s); i += 2 { + _, ok := xtob(s[i], s[i+1]) + if !ok { + return errors.New("invalid UUID format") + } + } + + default: + return invalidLengthError{len(s)} + } + + // Check for standard UUID format + if len(s) == 36 { + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return errors.New("invalid UUID format") + } + for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { + if _, ok := xtob(s[x], s[x+1]); !ok { + return errors.New("invalid UUID format") + } + } + } + + return nil +} + // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // , or "" if uuid is invalid. func (uuid UUID) String() string { @@ -292,3 +351,15 @@ func DisableRandPool() { poolMu.Lock() poolPos = randPoolSize } + +// UUIDs is a slice of UUID types. +type UUIDs []UUID + +// Strings returns a string slice containing the string form of each UUID in uuids. +func (uuids UUIDs) Strings() []string { + var uuidStrs = make([]string, len(uuids)) + for i, uuid := range uuids { + uuidStrs[i] = uuid.String() + } + return uuidStrs +} diff --git a/vendor/github.com/google/uuid/version6.go b/vendor/github.com/google/uuid/version6.go new file mode 100644 index 0000000000..339a959a7a --- /dev/null +++ b/vendor/github.com/google/uuid/version6.go @@ -0,0 +1,56 @@ +// Copyright 2023 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "encoding/binary" + +// UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality. +// It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs. +// Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead. +// +// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#uuidv6 +// +// NewV6 returns a Version 6 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewV6 set NodeID is random bits automatically . If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewV6 returns Nil and an error. +func NewV6() (UUID, error) { + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | time_high | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | time_mid | time_low_and_version | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |clk_seq_hi_res | clk_seq_low | node (0-1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | node (2-5) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + binary.BigEndian.PutUint64(uuid[0:], uint64(now)) + binary.BigEndian.PutUint16(uuid[8:], seq) + + uuid[6] = 0x60 | (uuid[6] & 0x0F) + uuid[8] = 0x80 | (uuid[8] & 0x3F) + + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + copy(uuid[10:], nodeID[:]) + nodeMu.Unlock() + + return uuid, nil +} diff --git a/vendor/github.com/google/uuid/version7.go b/vendor/github.com/google/uuid/version7.go new file mode 100644 index 0000000000..3167b643d4 --- /dev/null +++ b/vendor/github.com/google/uuid/version7.go @@ -0,0 +1,104 @@ +// Copyright 2023 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// UUID version 7 features a time-ordered value field derived from the widely +// implemented and well known Unix Epoch timestamp source, +// the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded. +// As well as improved entropy characteristics over versions 1 or 6. +// +// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7 +// +// Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible. +// +// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch). +// Uses the randomness pool if it was enabled with EnableRandPool. +// On error, NewV7 returns Nil and an error +func NewV7() (UUID, error) { + uuid, err := NewRandom() + if err != nil { + return uuid, err + } + makeV7(uuid[:]) + return uuid, nil +} + +// NewV7FromReader returns a Version 7 UUID based on the current time(Unix Epoch). +// it use NewRandomFromReader fill random bits. +// On error, NewV7FromReader returns Nil and an error. +func NewV7FromReader(r io.Reader) (UUID, error) { + uuid, err := NewRandomFromReader(r) + if err != nil { + return uuid, err + } + + makeV7(uuid[:]) + return uuid, nil +} + +// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6]) +// uuid[8] already has the right version number (Variant is 10) +// see function NewV7 and NewV7FromReader +func makeV7(uuid []byte) { + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | unix_ts_ms | ver | rand_a (12 bit seq) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |var| rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | rand_b | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + _ = uuid[15] // bounds check + + t, s := getV7Time() + + uuid[0] = byte(t >> 40) + uuid[1] = byte(t >> 32) + uuid[2] = byte(t >> 24) + uuid[3] = byte(t >> 16) + uuid[4] = byte(t >> 8) + uuid[5] = byte(t) + + uuid[6] = 0x70 | (0x0F & byte(s>>8)) + uuid[7] = byte(s) +} + +// lastV7time is the last time we returned stored as: +// +// 52 bits of time in milliseconds since epoch +// 12 bits of (fractional nanoseconds) >> 8 +var lastV7time int64 + +const nanoPerMilli = 1000000 + +// getV7Time returns the time in milliseconds and nanoseconds / 256. +// The returned (milli << 12 + seq) is guarenteed to be greater than +// (milli << 12 + seq) returned by any previous call to getV7Time. +func getV7Time() (milli, seq int64) { + timeMu.Lock() + defer timeMu.Unlock() + + nano := timeNow().UnixNano() + milli = nano / nanoPerMilli + // Sequence number is between 0 and 3906 (nanoPerMilli>>8) + seq = (nano - milli*nanoPerMilli) >> 8 + now := milli<<12 + seq + if now <= lastV7time { + now = lastV7time + 1 + milli = now >> 12 + seq = now & 0xfff + } + lastV7time = now + return milli, seq +} diff --git a/vendor/github.com/hashicorp/golang-lru/README.md b/vendor/github.com/hashicorp/golang-lru/README.md deleted file mode 100644 index 33e58cfaf9..0000000000 --- a/vendor/github.com/hashicorp/golang-lru/README.md +++ /dev/null @@ -1,25 +0,0 @@ -golang-lru -========== - -This provides the `lru` package which implements a fixed-size -thread safe LRU cache. It is based on the cache in Groupcache. - -Documentation -============= - -Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru) - -Example -======= - -Using the LRU is very simple: - -```go -l, _ := New(128) -for i := 0; i < 256; i++ { - l.Add(i, nil) -} -if l.Len() != 128 { - panic(fmt.Sprintf("bad len: %v", l.Len())) -} -``` diff --git a/vendor/github.com/hashicorp/golang-lru/arc.go b/vendor/github.com/hashicorp/golang-lru/arc.go deleted file mode 100644 index 555225a218..0000000000 --- a/vendor/github.com/hashicorp/golang-lru/arc.go +++ /dev/null @@ -1,257 +0,0 @@ -package lru - -import ( - "sync" - - "github.com/hashicorp/golang-lru/simplelru" -) - -// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC). -// ARC is an enhancement over the standard LRU cache in that tracks both -// frequency and recency of use. This avoids a burst in access to new -// entries from evicting the frequently used older entries. It adds some -// additional tracking overhead to a standard LRU cache, computationally -// it is roughly 2x the cost, and the extra memory overhead is linear -// with the size of the cache. ARC has been patented by IBM, but is -// similar to the TwoQueueCache (2Q) which requires setting parameters. -type ARCCache struct { - size int // Size is the total capacity of the cache - p int // P is the dynamic preference towards T1 or T2 - - t1 simplelru.LRUCache // T1 is the LRU for recently accessed items - b1 simplelru.LRUCache // B1 is the LRU for evictions from t1 - - t2 simplelru.LRUCache // T2 is the LRU for frequently accessed items - b2 simplelru.LRUCache // B2 is the LRU for evictions from t2 - - lock sync.RWMutex -} - -// NewARC creates an ARC of the given size -func NewARC(size int) (*ARCCache, error) { - // Create the sub LRUs - b1, err := simplelru.NewLRU(size, nil) - if err != nil { - return nil, err - } - b2, err := simplelru.NewLRU(size, nil) - if err != nil { - return nil, err - } - t1, err := simplelru.NewLRU(size, nil) - if err != nil { - return nil, err - } - t2, err := simplelru.NewLRU(size, nil) - if err != nil { - return nil, err - } - - // Initialize the ARC - c := &ARCCache{ - size: size, - p: 0, - t1: t1, - b1: b1, - t2: t2, - b2: b2, - } - return c, nil -} - -// Get looks up a key's value from the cache. -func (c *ARCCache) Get(key interface{}) (value interface{}, ok bool) { - c.lock.Lock() - defer c.lock.Unlock() - - // If the value is contained in T1 (recent), then - // promote it to T2 (frequent) - if val, ok := c.t1.Peek(key); ok { - c.t1.Remove(key) - c.t2.Add(key, val) - return val, ok - } - - // Check if the value is contained in T2 (frequent) - if val, ok := c.t2.Get(key); ok { - return val, ok - } - - // No hit - return nil, false -} - -// Add adds a value to the cache. -func (c *ARCCache) Add(key, value interface{}) { - c.lock.Lock() - defer c.lock.Unlock() - - // Check if the value is contained in T1 (recent), and potentially - // promote it to frequent T2 - if c.t1.Contains(key) { - c.t1.Remove(key) - c.t2.Add(key, value) - return - } - - // Check if the value is already in T2 (frequent) and update it - if c.t2.Contains(key) { - c.t2.Add(key, value) - return - } - - // Check if this value was recently evicted as part of the - // recently used list - if c.b1.Contains(key) { - // T1 set is too small, increase P appropriately - delta := 1 - b1Len := c.b1.Len() - b2Len := c.b2.Len() - if b2Len > b1Len { - delta = b2Len / b1Len - } - if c.p+delta >= c.size { - c.p = c.size - } else { - c.p += delta - } - - // Potentially need to make room in the cache - if c.t1.Len()+c.t2.Len() >= c.size { - c.replace(false) - } - - // Remove from B1 - c.b1.Remove(key) - - // Add the key to the frequently used list - c.t2.Add(key, value) - return - } - - // Check if this value was recently evicted as part of the - // frequently used list - if c.b2.Contains(key) { - // T2 set is too small, decrease P appropriately - delta := 1 - b1Len := c.b1.Len() - b2Len := c.b2.Len() - if b1Len > b2Len { - delta = b1Len / b2Len - } - if delta >= c.p { - c.p = 0 - } else { - c.p -= delta - } - - // Potentially need to make room in the cache - if c.t1.Len()+c.t2.Len() >= c.size { - c.replace(true) - } - - // Remove from B2 - c.b2.Remove(key) - - // Add the key to the frequently used list - c.t2.Add(key, value) - return - } - - // Potentially need to make room in the cache - if c.t1.Len()+c.t2.Len() >= c.size { - c.replace(false) - } - - // Keep the size of the ghost buffers trim - if c.b1.Len() > c.size-c.p { - c.b1.RemoveOldest() - } - if c.b2.Len() > c.p { - c.b2.RemoveOldest() - } - - // Add to the recently seen list - c.t1.Add(key, value) - return -} - -// replace is used to adaptively evict from either T1 or T2 -// based on the current learned value of P -func (c *ARCCache) replace(b2ContainsKey bool) { - t1Len := c.t1.Len() - if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) { - k, _, ok := c.t1.RemoveOldest() - if ok { - c.b1.Add(k, nil) - } - } else { - k, _, ok := c.t2.RemoveOldest() - if ok { - c.b2.Add(k, nil) - } - } -} - -// Len returns the number of cached entries -func (c *ARCCache) Len() int { - c.lock.RLock() - defer c.lock.RUnlock() - return c.t1.Len() + c.t2.Len() -} - -// Keys returns all the cached keys -func (c *ARCCache) Keys() []interface{} { - c.lock.RLock() - defer c.lock.RUnlock() - k1 := c.t1.Keys() - k2 := c.t2.Keys() - return append(k1, k2...) -} - -// Remove is used to purge a key from the cache -func (c *ARCCache) Remove(key interface{}) { - c.lock.Lock() - defer c.lock.Unlock() - if c.t1.Remove(key) { - return - } - if c.t2.Remove(key) { - return - } - if c.b1.Remove(key) { - return - } - if c.b2.Remove(key) { - return - } -} - -// Purge is used to clear the cache -func (c *ARCCache) Purge() { - c.lock.Lock() - defer c.lock.Unlock() - c.t1.Purge() - c.t2.Purge() - c.b1.Purge() - c.b2.Purge() -} - -// Contains is used to check if the cache contains a key -// without updating recency or frequency. -func (c *ARCCache) Contains(key interface{}) bool { - c.lock.RLock() - defer c.lock.RUnlock() - return c.t1.Contains(key) || c.t2.Contains(key) -} - -// Peek is used to inspect the cache value of a key -// without updating recency or frequency. -func (c *ARCCache) Peek(key interface{}) (value interface{}, ok bool) { - c.lock.RLock() - defer c.lock.RUnlock() - if val, ok := c.t1.Peek(key); ok { - return val, ok - } - return c.t2.Peek(key) -} diff --git a/vendor/github.com/hashicorp/golang-lru/doc.go b/vendor/github.com/hashicorp/golang-lru/doc.go deleted file mode 100644 index 2547df979d..0000000000 --- a/vendor/github.com/hashicorp/golang-lru/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -// Package lru provides three different LRU caches of varying sophistication. -// -// Cache is a simple LRU cache. It is based on the -// LRU implementation in groupcache: -// https://github.com/golang/groupcache/tree/master/lru -// -// TwoQueueCache tracks frequently used and recently used entries separately. -// This avoids a burst of accesses from taking out frequently used entries, -// at the cost of about 2x computational overhead and some extra bookkeeping. -// -// ARCCache is an adaptive replacement cache. It tracks recent evictions as -// well as recent usage in both the frequent and recent caches. Its -// computational overhead is comparable to TwoQueueCache, but the memory -// overhead is linear with the size of the cache. -// -// ARC has been patented by IBM, so do not use it if that is problematic for -// your program. -// -// All caches in this package take locks while operating, and are therefore -// thread-safe for consumers. -package lru diff --git a/vendor/github.com/hashicorp/golang-lru/lru.go b/vendor/github.com/hashicorp/golang-lru/lru.go deleted file mode 100644 index 4e5e9d8fd0..0000000000 --- a/vendor/github.com/hashicorp/golang-lru/lru.go +++ /dev/null @@ -1,150 +0,0 @@ -package lru - -import ( - "sync" - - "github.com/hashicorp/golang-lru/simplelru" -) - -// Cache is a thread-safe fixed size LRU cache. -type Cache struct { - lru simplelru.LRUCache - lock sync.RWMutex -} - -// New creates an LRU of the given size. -func New(size int) (*Cache, error) { - return NewWithEvict(size, nil) -} - -// NewWithEvict constructs a fixed size cache with the given eviction -// callback. -func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { - lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted)) - if err != nil { - return nil, err - } - c := &Cache{ - lru: lru, - } - return c, nil -} - -// Purge is used to completely clear the cache. -func (c *Cache) Purge() { - c.lock.Lock() - c.lru.Purge() - c.lock.Unlock() -} - -// Add adds a value to the cache. Returns true if an eviction occurred. -func (c *Cache) Add(key, value interface{}) (evicted bool) { - c.lock.Lock() - evicted = c.lru.Add(key, value) - c.lock.Unlock() - return evicted -} - -// Get looks up a key's value from the cache. -func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { - c.lock.Lock() - value, ok = c.lru.Get(key) - c.lock.Unlock() - return value, ok -} - -// Contains checks if a key is in the cache, without updating the -// recent-ness or deleting it for being stale. -func (c *Cache) Contains(key interface{}) bool { - c.lock.RLock() - containKey := c.lru.Contains(key) - c.lock.RUnlock() - return containKey -} - -// Peek returns the key value (or undefined if not found) without updating -// the "recently used"-ness of the key. -func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) { - c.lock.RLock() - value, ok = c.lru.Peek(key) - c.lock.RUnlock() - return value, ok -} - -// ContainsOrAdd checks if a key is in the cache without updating the -// recent-ness or deleting it for being stale, and if not, adds the value. -// Returns whether found and whether an eviction occurred. -func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) { - c.lock.Lock() - defer c.lock.Unlock() - - if c.lru.Contains(key) { - return true, false - } - evicted = c.lru.Add(key, value) - return false, evicted -} - -// PeekOrAdd checks if a key is in the cache without updating the -// recent-ness or deleting it for being stale, and if not, adds the value. -// Returns whether found and whether an eviction occurred. -func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) { - c.lock.Lock() - defer c.lock.Unlock() - - previous, ok = c.lru.Peek(key) - if ok { - return previous, true, false - } - - evicted = c.lru.Add(key, value) - return nil, false, evicted -} - -// Remove removes the provided key from the cache. -func (c *Cache) Remove(key interface{}) (present bool) { - c.lock.Lock() - present = c.lru.Remove(key) - c.lock.Unlock() - return -} - -// Resize changes the cache size. -func (c *Cache) Resize(size int) (evicted int) { - c.lock.Lock() - evicted = c.lru.Resize(size) - c.lock.Unlock() - return evicted -} - -// RemoveOldest removes the oldest item from the cache. -func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) { - c.lock.Lock() - key, value, ok = c.lru.RemoveOldest() - c.lock.Unlock() - return -} - -// GetOldest returns the oldest entry -func (c *Cache) GetOldest() (key interface{}, value interface{}, ok bool) { - c.lock.Lock() - key, value, ok = c.lru.GetOldest() - c.lock.Unlock() - return -} - -// Keys returns a slice of the keys in the cache, from oldest to newest. -func (c *Cache) Keys() []interface{} { - c.lock.RLock() - keys := c.lru.Keys() - c.lock.RUnlock() - return keys -} - -// Len returns the number of items in the cache. -func (c *Cache) Len() int { - c.lock.RLock() - length := c.lru.Len() - c.lock.RUnlock() - return length -} diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go deleted file mode 100644 index a86c8539e0..0000000000 --- a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go +++ /dev/null @@ -1,177 +0,0 @@ -package simplelru - -import ( - "container/list" - "errors" -) - -// EvictCallback is used to get a callback when a cache entry is evicted -type EvictCallback func(key interface{}, value interface{}) - -// LRU implements a non-thread safe fixed size LRU cache -type LRU struct { - size int - evictList *list.List - items map[interface{}]*list.Element - onEvict EvictCallback -} - -// entry is used to hold a value in the evictList -type entry struct { - key interface{} - value interface{} -} - -// NewLRU constructs an LRU of the given size -func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { - if size <= 0 { - return nil, errors.New("Must provide a positive size") - } - c := &LRU{ - size: size, - evictList: list.New(), - items: make(map[interface{}]*list.Element), - onEvict: onEvict, - } - return c, nil -} - -// Purge is used to completely clear the cache. -func (c *LRU) Purge() { - for k, v := range c.items { - if c.onEvict != nil { - c.onEvict(k, v.Value.(*entry).value) - } - delete(c.items, k) - } - c.evictList.Init() -} - -// Add adds a value to the cache. Returns true if an eviction occurred. -func (c *LRU) Add(key, value interface{}) (evicted bool) { - // Check for existing item - if ent, ok := c.items[key]; ok { - c.evictList.MoveToFront(ent) - ent.Value.(*entry).value = value - return false - } - - // Add new item - ent := &entry{key, value} - entry := c.evictList.PushFront(ent) - c.items[key] = entry - - evict := c.evictList.Len() > c.size - // Verify size not exceeded - if evict { - c.removeOldest() - } - return evict -} - -// Get looks up a key's value from the cache. -func (c *LRU) Get(key interface{}) (value interface{}, ok bool) { - if ent, ok := c.items[key]; ok { - c.evictList.MoveToFront(ent) - if ent.Value.(*entry) == nil { - return nil, false - } - return ent.Value.(*entry).value, true - } - return -} - -// Contains checks if a key is in the cache, without updating the recent-ness -// or deleting it for being stale. -func (c *LRU) Contains(key interface{}) (ok bool) { - _, ok = c.items[key] - return ok -} - -// Peek returns the key value (or undefined if not found) without updating -// the "recently used"-ness of the key. -func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) { - var ent *list.Element - if ent, ok = c.items[key]; ok { - return ent.Value.(*entry).value, true - } - return nil, ok -} - -// Remove removes the provided key from the cache, returning if the -// key was contained. -func (c *LRU) Remove(key interface{}) (present bool) { - if ent, ok := c.items[key]; ok { - c.removeElement(ent) - return true - } - return false -} - -// RemoveOldest removes the oldest item from the cache. -func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) { - ent := c.evictList.Back() - if ent != nil { - c.removeElement(ent) - kv := ent.Value.(*entry) - return kv.key, kv.value, true - } - return nil, nil, false -} - -// GetOldest returns the oldest entry -func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) { - ent := c.evictList.Back() - if ent != nil { - kv := ent.Value.(*entry) - return kv.key, kv.value, true - } - return nil, nil, false -} - -// Keys returns a slice of the keys in the cache, from oldest to newest. -func (c *LRU) Keys() []interface{} { - keys := make([]interface{}, len(c.items)) - i := 0 - for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { - keys[i] = ent.Value.(*entry).key - i++ - } - return keys -} - -// Len returns the number of items in the cache. -func (c *LRU) Len() int { - return c.evictList.Len() -} - -// Resize changes the cache size. -func (c *LRU) Resize(size int) (evicted int) { - diff := c.Len() - size - if diff < 0 { - diff = 0 - } - for i := 0; i < diff; i++ { - c.removeOldest() - } - c.size = size - return diff -} - -// removeOldest removes the oldest item from the cache. -func (c *LRU) removeOldest() { - ent := c.evictList.Back() - if ent != nil { - c.removeElement(ent) - } -} - -// removeElement is used to remove a given list element from the cache -func (c *LRU) removeElement(e *list.Element) { - c.evictList.Remove(e) - kv := e.Value.(*entry) - delete(c.items, kv.key) - if c.onEvict != nil { - c.onEvict(kv.key, kv.value) - } -} diff --git a/vendor/github.com/hashicorp/golang-lru/.gitignore b/vendor/github.com/hashicorp/golang-lru/v2/.gitignore similarity index 100% rename from vendor/github.com/hashicorp/golang-lru/.gitignore rename to vendor/github.com/hashicorp/golang-lru/v2/.gitignore diff --git a/vendor/github.com/hashicorp/golang-lru/v2/.golangci.yml b/vendor/github.com/hashicorp/golang-lru/v2/.golangci.yml new file mode 100644 index 0000000000..7e7b8a9627 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/.golangci.yml @@ -0,0 +1,46 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +linters: + fast: false + disable-all: true + enable: + - revive + - megacheck + - govet + - unconvert + - gas + - gocyclo + - dupl + - misspell + - unparam + - unused + - typecheck + - ineffassign + # - stylecheck + - exportloopref + - gocritic + - nakedret + - gosimple + - prealloc + +# golangci-lint configuration file +linters-settings: + revive: + ignore-generated-header: true + severity: warning + rules: + - name: package-comments + severity: warning + disabled: true + - name: exported + severity: warning + disabled: false + arguments: ["checkPrivateReceivers", "disableStutteringCheck"] + +issues: + exclude-use-default: false + exclude-rules: + - path: _test\.go + linters: + - dupl diff --git a/vendor/github.com/hashicorp/golang-lru/2q.go b/vendor/github.com/hashicorp/golang-lru/v2/2q.go similarity index 64% rename from vendor/github.com/hashicorp/golang-lru/2q.go rename to vendor/github.com/hashicorp/golang-lru/v2/2q.go index e474cd0758..8c95252b6f 100644 --- a/vendor/github.com/hashicorp/golang-lru/2q.go +++ b/vendor/github.com/hashicorp/golang-lru/v2/2q.go @@ -1,10 +1,13 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package lru import ( - "fmt" + "errors" "sync" - "github.com/hashicorp/golang-lru/simplelru" + "github.com/hashicorp/golang-lru/v2/simplelru" ) const ( @@ -26,33 +29,35 @@ const ( // computationally about 2x the cost, and adds some metadata over // head. The ARCCache is similar, but does not require setting any // parameters. -type TwoQueueCache struct { - size int - recentSize int +type TwoQueueCache[K comparable, V any] struct { + size int + recentSize int + recentRatio float64 + ghostRatio float64 - recent simplelru.LRUCache - frequent simplelru.LRUCache - recentEvict simplelru.LRUCache + recent simplelru.LRUCache[K, V] + frequent simplelru.LRUCache[K, V] + recentEvict simplelru.LRUCache[K, struct{}] lock sync.RWMutex } // New2Q creates a new TwoQueueCache using the default // values for the parameters. -func New2Q(size int) (*TwoQueueCache, error) { - return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries) +func New2Q[K comparable, V any](size int) (*TwoQueueCache[K, V], error) { + return New2QParams[K, V](size, Default2QRecentRatio, Default2QGhostEntries) } // New2QParams creates a new TwoQueueCache using the provided // parameter values. -func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) { +func New2QParams[K comparable, V any](size int, recentRatio, ghostRatio float64) (*TwoQueueCache[K, V], error) { if size <= 0 { - return nil, fmt.Errorf("invalid size") + return nil, errors.New("invalid size") } if recentRatio < 0.0 || recentRatio > 1.0 { - return nil, fmt.Errorf("invalid recent ratio") + return nil, errors.New("invalid recent ratio") } if ghostRatio < 0.0 || ghostRatio > 1.0 { - return nil, fmt.Errorf("invalid ghost ratio") + return nil, errors.New("invalid ghost ratio") } // Determine the sub-sizes @@ -60,23 +65,25 @@ func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCa evictSize := int(float64(size) * ghostRatio) // Allocate the LRUs - recent, err := simplelru.NewLRU(size, nil) + recent, err := simplelru.NewLRU[K, V](size, nil) if err != nil { return nil, err } - frequent, err := simplelru.NewLRU(size, nil) + frequent, err := simplelru.NewLRU[K, V](size, nil) if err != nil { return nil, err } - recentEvict, err := simplelru.NewLRU(evictSize, nil) + recentEvict, err := simplelru.NewLRU[K, struct{}](evictSize, nil) if err != nil { return nil, err } // Initialize the cache - c := &TwoQueueCache{ + c := &TwoQueueCache[K, V]{ size: size, recentSize: recentSize, + recentRatio: recentRatio, + ghostRatio: ghostRatio, recent: recent, frequent: frequent, recentEvict: recentEvict, @@ -85,7 +92,7 @@ func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCa } // Get looks up a key's value from the cache. -func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) { +func (c *TwoQueueCache[K, V]) Get(key K) (value V, ok bool) { c.lock.Lock() defer c.lock.Unlock() @@ -103,11 +110,11 @@ func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) { } // No hit - return nil, false + return } // Add adds a value to the cache. -func (c *TwoQueueCache) Add(key, value interface{}) { +func (c *TwoQueueCache[K, V]) Add(key K, value V) { c.lock.Lock() defer c.lock.Unlock() @@ -138,11 +145,10 @@ func (c *TwoQueueCache) Add(key, value interface{}) { // Add to the recently seen list c.ensureSpace(false) c.recent.Add(key, value) - return } // ensureSpace is used to ensure we have space in the cache -func (c *TwoQueueCache) ensureSpace(recentEvict bool) { +func (c *TwoQueueCache[K, V]) ensureSpace(recentEvict bool) { // If we have space, nothing to do recentLen := c.recent.Len() freqLen := c.frequent.Len() @@ -154,7 +160,7 @@ func (c *TwoQueueCache) ensureSpace(recentEvict bool) { // the target, evict from there if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) { k, _, _ := c.recent.RemoveOldest() - c.recentEvict.Add(k, nil) + c.recentEvict.Add(k, struct{}{}) return } @@ -163,15 +169,43 @@ func (c *TwoQueueCache) ensureSpace(recentEvict bool) { } // Len returns the number of items in the cache. -func (c *TwoQueueCache) Len() int { +func (c *TwoQueueCache[K, V]) Len() int { c.lock.RLock() defer c.lock.RUnlock() return c.recent.Len() + c.frequent.Len() } +// Resize changes the cache size. +func (c *TwoQueueCache[K, V]) Resize(size int) (evicted int) { + c.lock.Lock() + defer c.lock.Unlock() + + // Recalculate the sub-sizes + recentSize := int(float64(size) * c.recentRatio) + evictSize := int(float64(size) * c.ghostRatio) + c.size = size + c.recentSize = recentSize + + // ensureSpace + diff := c.recent.Len() + c.frequent.Len() - size + if diff < 0 { + diff = 0 + } + for i := 0; i < diff; i++ { + c.ensureSpace(true) + } + + // Reallocate the LRUs + c.recent.Resize(size) + c.frequent.Resize(size) + c.recentEvict.Resize(evictSize) + + return diff +} + // Keys returns a slice of the keys in the cache. // The frequently used keys are first in the returned slice. -func (c *TwoQueueCache) Keys() []interface{} { +func (c *TwoQueueCache[K, V]) Keys() []K { c.lock.RLock() defer c.lock.RUnlock() k1 := c.frequent.Keys() @@ -179,8 +213,18 @@ func (c *TwoQueueCache) Keys() []interface{} { return append(k1, k2...) } +// Values returns a slice of the values in the cache. +// The frequently used values are first in the returned slice. +func (c *TwoQueueCache[K, V]) Values() []V { + c.lock.RLock() + defer c.lock.RUnlock() + v1 := c.frequent.Values() + v2 := c.recent.Values() + return append(v1, v2...) +} + // Remove removes the provided key from the cache. -func (c *TwoQueueCache) Remove(key interface{}) { +func (c *TwoQueueCache[K, V]) Remove(key K) { c.lock.Lock() defer c.lock.Unlock() if c.frequent.Remove(key) { @@ -195,7 +239,7 @@ func (c *TwoQueueCache) Remove(key interface{}) { } // Purge is used to completely clear the cache. -func (c *TwoQueueCache) Purge() { +func (c *TwoQueueCache[K, V]) Purge() { c.lock.Lock() defer c.lock.Unlock() c.recent.Purge() @@ -205,7 +249,7 @@ func (c *TwoQueueCache) Purge() { // Contains is used to check if the cache contains a key // without updating recency or frequency. -func (c *TwoQueueCache) Contains(key interface{}) bool { +func (c *TwoQueueCache[K, V]) Contains(key K) bool { c.lock.RLock() defer c.lock.RUnlock() return c.frequent.Contains(key) || c.recent.Contains(key) @@ -213,7 +257,7 @@ func (c *TwoQueueCache) Contains(key interface{}) bool { // Peek is used to inspect the cache value of a key // without updating recency or frequency. -func (c *TwoQueueCache) Peek(key interface{}) (value interface{}, ok bool) { +func (c *TwoQueueCache[K, V]) Peek(key K) (value V, ok bool) { c.lock.RLock() defer c.lock.RUnlock() if val, ok := c.frequent.Peek(key); ok { diff --git a/vendor/github.com/hashicorp/golang-lru/LICENSE b/vendor/github.com/hashicorp/golang-lru/v2/LICENSE similarity index 99% rename from vendor/github.com/hashicorp/golang-lru/LICENSE rename to vendor/github.com/hashicorp/golang-lru/v2/LICENSE index be2cc4dfb6..0e5d580e0e 100644 --- a/vendor/github.com/hashicorp/golang-lru/LICENSE +++ b/vendor/github.com/hashicorp/golang-lru/v2/LICENSE @@ -1,3 +1,5 @@ +Copyright (c) 2014 HashiCorp, Inc. + Mozilla Public License, version 2.0 1. Definitions diff --git a/vendor/github.com/hashicorp/golang-lru/v2/README.md b/vendor/github.com/hashicorp/golang-lru/v2/README.md new file mode 100644 index 0000000000..a942eb5397 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/README.md @@ -0,0 +1,79 @@ +golang-lru +========== + +This provides the `lru` package which implements a fixed-size +thread safe LRU cache. It is based on the cache in Groupcache. + +Documentation +============= + +Full docs are available on [Go Packages](https://pkg.go.dev/github.com/hashicorp/golang-lru/v2) + +LRU cache example +================= + +```go +package main + +import ( + "fmt" + "github.com/hashicorp/golang-lru/v2" +) + +func main() { + l, _ := lru.New[int, any](128) + for i := 0; i < 256; i++ { + l.Add(i, nil) + } + if l.Len() != 128 { + panic(fmt.Sprintf("bad len: %v", l.Len())) + } +} +``` + +Expirable LRU cache example +=========================== + +```go +package main + +import ( + "fmt" + "time" + + "github.com/hashicorp/golang-lru/v2/expirable" +) + +func main() { + // make cache with 10ms TTL and 5 max keys + cache := expirable.NewLRU[string, string](5, nil, time.Millisecond*10) + + + // set value under key1. + cache.Add("key1", "val1") + + // get value under key1 + r, ok := cache.Get("key1") + + // check for OK value + if ok { + fmt.Printf("value before expiration is found: %v, value: %q\n", ok, r) + } + + // wait for cache to expire + time.Sleep(time.Millisecond * 12) + + // get value under key1 after key expiration + r, ok = cache.Get("key1") + fmt.Printf("value after expiration is found: %v, value: %q\n", ok, r) + + // set value under key2, would evict old entry because it is already expired. + cache.Add("key2", "val2") + + fmt.Printf("Cache len: %d\n", cache.Len()) + // Output: + // value before expiration is found: true, value: "val1" + // value after expiration is found: false, value: "" + // Cache len: 1 +} +``` diff --git a/vendor/github.com/hashicorp/golang-lru/v2/doc.go b/vendor/github.com/hashicorp/golang-lru/v2/doc.go new file mode 100644 index 0000000000..24107ee0ed --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/doc.go @@ -0,0 +1,24 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package lru provides three different LRU caches of varying sophistication. +// +// Cache is a simple LRU cache. It is based on the LRU implementation in +// groupcache: https://github.com/golang/groupcache/tree/master/lru +// +// TwoQueueCache tracks frequently used and recently used entries separately. +// This avoids a burst of accesses from taking out frequently used entries, at +// the cost of about 2x computational overhead and some extra bookkeeping. +// +// ARCCache is an adaptive replacement cache. It tracks recent evictions as well +// as recent usage in both the frequent and recent caches. Its computational +// overhead is comparable to TwoQueueCache, but the memory overhead is linear +// with the size of the cache. +// +// ARC has been patented by IBM, so do not use it if that is problematic for +// your program. For this reason, it is in a separate go module contained within +// this repository. +// +// All caches in this package take locks while operating, and are therefore +// thread-safe for consumers. +package lru diff --git a/vendor/github.com/hashicorp/golang-lru/v2/internal/list.go b/vendor/github.com/hashicorp/golang-lru/v2/internal/list.go new file mode 100644 index 0000000000..5cd74a0343 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/internal/list.go @@ -0,0 +1,142 @@ +// Copyright 2009 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_list file. + +package internal + +import "time" + +// Entry is an LRU Entry +type Entry[K comparable, V any] struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *Entry[K, V] + + // The list to which this element belongs. + list *LruList[K, V] + + // The LRU Key of this element. + Key K + + // The Value stored with this element. + Value V + + // The time this element would be cleaned up, optional + ExpiresAt time.Time + + // The expiry bucket item was put in, optional + ExpireBucket uint8 +} + +// PrevEntry returns the previous list element or nil. +func (e *Entry[K, V]) PrevEntry() *Entry[K, V] { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// LruList represents a doubly linked list. +// The zero Value for LruList is an empty list ready to use. +type LruList[K comparable, V any] struct { + root Entry[K, V] // sentinel list element, only &root, root.prev, and root.next are used + len int // current list Length excluding (this) sentinel element +} + +// Init initializes or clears list l. +func (l *LruList[K, V]) Init() *LruList[K, V] { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// NewList returns an initialized list. +func NewList[K comparable, V any]() *LruList[K, V] { return new(LruList[K, V]).Init() } + +// Length returns the number of elements of list l. +// The complexity is O(1). +func (l *LruList[K, V]) Length() int { return l.len } + +// Back returns the last element of list l or nil if the list is empty. +func (l *LruList[K, V]) Back() *Entry[K, V] { + if l.len == 0 { + return nil + } + return l.root.prev +} + +// lazyInit lazily initializes a zero List Value. +func (l *LruList[K, V]) lazyInit() { + if l.root.next == nil { + l.Init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *LruList[K, V]) insert(e, at *Entry[K, V]) *Entry[K, V] { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Entry{Value: v, ExpiresAt: ExpiresAt}, at). +func (l *LruList[K, V]) insertValue(k K, v V, expiresAt time.Time, at *Entry[K, V]) *Entry[K, V] { + return l.insert(&Entry[K, V]{Value: v, Key: k, ExpiresAt: expiresAt}, at) +} + +// Remove removes e from its list, decrements l.len +func (l *LruList[K, V]) Remove(e *Entry[K, V]) V { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- + + return e.Value +} + +// move moves e to next to at. +func (l *LruList[K, V]) move(e, at *Entry[K, V]) { + if e == at { + return + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e +} + +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *LruList[K, V]) PushFront(k K, v V) *Entry[K, V] { + l.lazyInit() + return l.insertValue(k, v, time.Time{}, &l.root) +} + +// PushFrontExpirable inserts a new expirable element e with Value v at the front of list l and returns e. +func (l *LruList[K, V]) PushFrontExpirable(k K, v V, expiresAt time.Time) *Entry[K, V] { + l.lazyInit() + return l.insertValue(k, v, expiresAt, &l.root) +} + +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *LruList[K, V]) MoveToFront(e *Entry[K, V]) { + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} diff --git a/vendor/github.com/hashicorp/golang-lru/v2/lru.go b/vendor/github.com/hashicorp/golang-lru/v2/lru.go new file mode 100644 index 0000000000..a2655f1f31 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/lru.go @@ -0,0 +1,250 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lru + +import ( + "sync" + + "github.com/hashicorp/golang-lru/v2/simplelru" +) + +const ( + // DefaultEvictedBufferSize defines the default buffer size to store evicted key/val + DefaultEvictedBufferSize = 16 +) + +// Cache is a thread-safe fixed size LRU cache. +type Cache[K comparable, V any] struct { + lru *simplelru.LRU[K, V] + evictedKeys []K + evictedVals []V + onEvictedCB func(k K, v V) + lock sync.RWMutex +} + +// New creates an LRU of the given size. +func New[K comparable, V any](size int) (*Cache[K, V], error) { + return NewWithEvict[K, V](size, nil) +} + +// NewWithEvict constructs a fixed size cache with the given eviction +// callback. +func NewWithEvict[K comparable, V any](size int, onEvicted func(key K, value V)) (c *Cache[K, V], err error) { + // create a cache with default settings + c = &Cache[K, V]{ + onEvictedCB: onEvicted, + } + if onEvicted != nil { + c.initEvictBuffers() + onEvicted = c.onEvicted + } + c.lru, err = simplelru.NewLRU(size, onEvicted) + return +} + +func (c *Cache[K, V]) initEvictBuffers() { + c.evictedKeys = make([]K, 0, DefaultEvictedBufferSize) + c.evictedVals = make([]V, 0, DefaultEvictedBufferSize) +} + +// onEvicted save evicted key/val and sent in externally registered callback +// outside of critical section +func (c *Cache[K, V]) onEvicted(k K, v V) { + c.evictedKeys = append(c.evictedKeys, k) + c.evictedVals = append(c.evictedVals, v) +} + +// Purge is used to completely clear the cache. +func (c *Cache[K, V]) Purge() { + var ks []K + var vs []V + c.lock.Lock() + c.lru.Purge() + if c.onEvictedCB != nil && len(c.evictedKeys) > 0 { + ks, vs = c.evictedKeys, c.evictedVals + c.initEvictBuffers() + } + c.lock.Unlock() + // invoke callback outside of critical section + if c.onEvictedCB != nil { + for i := 0; i < len(ks); i++ { + c.onEvictedCB(ks[i], vs[i]) + } + } +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +func (c *Cache[K, V]) Add(key K, value V) (evicted bool) { + var k K + var v V + c.lock.Lock() + evicted = c.lru.Add(key, value) + if c.onEvictedCB != nil && evicted { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && evicted { + c.onEvictedCB(k, v) + } + return +} + +// Get looks up a key's value from the cache. +func (c *Cache[K, V]) Get(key K) (value V, ok bool) { + c.lock.Lock() + value, ok = c.lru.Get(key) + c.lock.Unlock() + return value, ok +} + +// Contains checks if a key is in the cache, without updating the +// recent-ness or deleting it for being stale. +func (c *Cache[K, V]) Contains(key K) bool { + c.lock.RLock() + containKey := c.lru.Contains(key) + c.lock.RUnlock() + return containKey +} + +// Peek returns the key value (or undefined if not found) without updating +// the "recently used"-ness of the key. +func (c *Cache[K, V]) Peek(key K) (value V, ok bool) { + c.lock.RLock() + value, ok = c.lru.Peek(key) + c.lock.RUnlock() + return value, ok +} + +// ContainsOrAdd checks if a key is in the cache without updating the +// recent-ness or deleting it for being stale, and if not, adds the value. +// Returns whether found and whether an eviction occurred. +func (c *Cache[K, V]) ContainsOrAdd(key K, value V) (ok, evicted bool) { + var k K + var v V + c.lock.Lock() + if c.lru.Contains(key) { + c.lock.Unlock() + return true, false + } + evicted = c.lru.Add(key, value) + if c.onEvictedCB != nil && evicted { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && evicted { + c.onEvictedCB(k, v) + } + return false, evicted +} + +// PeekOrAdd checks if a key is in the cache without updating the +// recent-ness or deleting it for being stale, and if not, adds the value. +// Returns whether found and whether an eviction occurred. +func (c *Cache[K, V]) PeekOrAdd(key K, value V) (previous V, ok, evicted bool) { + var k K + var v V + c.lock.Lock() + previous, ok = c.lru.Peek(key) + if ok { + c.lock.Unlock() + return previous, true, false + } + evicted = c.lru.Add(key, value) + if c.onEvictedCB != nil && evicted { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && evicted { + c.onEvictedCB(k, v) + } + return +} + +// Remove removes the provided key from the cache. +func (c *Cache[K, V]) Remove(key K) (present bool) { + var k K + var v V + c.lock.Lock() + present = c.lru.Remove(key) + if c.onEvictedCB != nil && present { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && present { + c.onEvictedCB(k, v) + } + return +} + +// Resize changes the cache size. +func (c *Cache[K, V]) Resize(size int) (evicted int) { + var ks []K + var vs []V + c.lock.Lock() + evicted = c.lru.Resize(size) + if c.onEvictedCB != nil && evicted > 0 { + ks, vs = c.evictedKeys, c.evictedVals + c.initEvictBuffers() + } + c.lock.Unlock() + if c.onEvictedCB != nil && evicted > 0 { + for i := 0; i < len(ks); i++ { + c.onEvictedCB(ks[i], vs[i]) + } + } + return evicted +} + +// RemoveOldest removes the oldest item from the cache. +func (c *Cache[K, V]) RemoveOldest() (key K, value V, ok bool) { + var k K + var v V + c.lock.Lock() + key, value, ok = c.lru.RemoveOldest() + if c.onEvictedCB != nil && ok { + k, v = c.evictedKeys[0], c.evictedVals[0] + c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0] + } + c.lock.Unlock() + if c.onEvictedCB != nil && ok { + c.onEvictedCB(k, v) + } + return +} + +// GetOldest returns the oldest entry +func (c *Cache[K, V]) GetOldest() (key K, value V, ok bool) { + c.lock.RLock() + key, value, ok = c.lru.GetOldest() + c.lock.RUnlock() + return +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (c *Cache[K, V]) Keys() []K { + c.lock.RLock() + keys := c.lru.Keys() + c.lock.RUnlock() + return keys +} + +// Values returns a slice of the values in the cache, from oldest to newest. +func (c *Cache[K, V]) Values() []V { + c.lock.RLock() + values := c.lru.Values() + c.lock.RUnlock() + return values +} + +// Len returns the number of items in the cache. +func (c *Cache[K, V]) Len() int { + c.lock.RLock() + length := c.lru.Len() + c.lock.RUnlock() + return length +} diff --git a/vendor/github.com/hashicorp/golang-lru/v2/simplelru/LICENSE_list b/vendor/github.com/hashicorp/golang-lru/v2/simplelru/LICENSE_list new file mode 100644 index 0000000000..c4764e6b2f --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/simplelru/LICENSE_list @@ -0,0 +1,29 @@ +This license applies to simplelru/list.go + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru.go b/vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru.go new file mode 100644 index 0000000000..f69792388c --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru.go @@ -0,0 +1,177 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package simplelru + +import ( + "errors" + + "github.com/hashicorp/golang-lru/v2/internal" +) + +// EvictCallback is used to get a callback when a cache entry is evicted +type EvictCallback[K comparable, V any] func(key K, value V) + +// LRU implements a non-thread safe fixed size LRU cache +type LRU[K comparable, V any] struct { + size int + evictList *internal.LruList[K, V] + items map[K]*internal.Entry[K, V] + onEvict EvictCallback[K, V] +} + +// NewLRU constructs an LRU of the given size +func NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V]) (*LRU[K, V], error) { + if size <= 0 { + return nil, errors.New("must provide a positive size") + } + + c := &LRU[K, V]{ + size: size, + evictList: internal.NewList[K, V](), + items: make(map[K]*internal.Entry[K, V]), + onEvict: onEvict, + } + return c, nil +} + +// Purge is used to completely clear the cache. +func (c *LRU[K, V]) Purge() { + for k, v := range c.items { + if c.onEvict != nil { + c.onEvict(k, v.Value) + } + delete(c.items, k) + } + c.evictList.Init() +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +func (c *LRU[K, V]) Add(key K, value V) (evicted bool) { + // Check for existing item + if ent, ok := c.items[key]; ok { + c.evictList.MoveToFront(ent) + ent.Value = value + return false + } + + // Add new item + ent := c.evictList.PushFront(key, value) + c.items[key] = ent + + evict := c.evictList.Length() > c.size + // Verify size not exceeded + if evict { + c.removeOldest() + } + return evict +} + +// Get looks up a key's value from the cache. +func (c *LRU[K, V]) Get(key K) (value V, ok bool) { + if ent, ok := c.items[key]; ok { + c.evictList.MoveToFront(ent) + return ent.Value, true + } + return +} + +// Contains checks if a key is in the cache, without updating the recent-ness +// or deleting it for being stale. +func (c *LRU[K, V]) Contains(key K) (ok bool) { + _, ok = c.items[key] + return ok +} + +// Peek returns the key value (or undefined if not found) without updating +// the "recently used"-ness of the key. +func (c *LRU[K, V]) Peek(key K) (value V, ok bool) { + var ent *internal.Entry[K, V] + if ent, ok = c.items[key]; ok { + return ent.Value, true + } + return +} + +// Remove removes the provided key from the cache, returning if the +// key was contained. +func (c *LRU[K, V]) Remove(key K) (present bool) { + if ent, ok := c.items[key]; ok { + c.removeElement(ent) + return true + } + return false +} + +// RemoveOldest removes the oldest item from the cache. +func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) { + if ent := c.evictList.Back(); ent != nil { + c.removeElement(ent) + return ent.Key, ent.Value, true + } + return +} + +// GetOldest returns the oldest entry +func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) { + if ent := c.evictList.Back(); ent != nil { + return ent.Key, ent.Value, true + } + return +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (c *LRU[K, V]) Keys() []K { + keys := make([]K, c.evictList.Length()) + i := 0 + for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() { + keys[i] = ent.Key + i++ + } + return keys +} + +// Values returns a slice of the values in the cache, from oldest to newest. +func (c *LRU[K, V]) Values() []V { + values := make([]V, len(c.items)) + i := 0 + for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() { + values[i] = ent.Value + i++ + } + return values +} + +// Len returns the number of items in the cache. +func (c *LRU[K, V]) Len() int { + return c.evictList.Length() +} + +// Resize changes the cache size. +func (c *LRU[K, V]) Resize(size int) (evicted int) { + diff := c.Len() - size + if diff < 0 { + diff = 0 + } + for i := 0; i < diff; i++ { + c.removeOldest() + } + c.size = size + return diff +} + +// removeOldest removes the oldest item from the cache. +func (c *LRU[K, V]) removeOldest() { + if ent := c.evictList.Back(); ent != nil { + c.removeElement(ent) + } +} + +// removeElement is used to remove a given list element from the cache +func (c *LRU[K, V]) removeElement(e *internal.Entry[K, V]) { + c.evictList.Remove(e) + delete(c.items, e.Key) + if c.onEvict != nil { + c.onEvict(e.Key, e.Value) + } +} diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go b/vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru_interface.go similarity index 57% rename from vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go rename to vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru_interface.go index 92d70934d6..043b8bcc3f 100644 --- a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go +++ b/vendor/github.com/hashicorp/golang-lru/v2/simplelru/lru_interface.go @@ -1,32 +1,39 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package simplelru provides simple LRU implementation based on build-in container/list. package simplelru // LRUCache is the interface for simple LRU cache. -type LRUCache interface { +type LRUCache[K comparable, V any] interface { // Adds a value to the cache, returns true if an eviction occurred and // updates the "recently used"-ness of the key. - Add(key, value interface{}) bool + Add(key K, value V) bool // Returns key's value from the cache and // updates the "recently used"-ness of the key. #value, isFound - Get(key interface{}) (value interface{}, ok bool) + Get(key K) (value V, ok bool) // Checks if a key exists in cache without updating the recent-ness. - Contains(key interface{}) (ok bool) + Contains(key K) (ok bool) // Returns key's value without updating the "recently used"-ness of the key. - Peek(key interface{}) (value interface{}, ok bool) + Peek(key K) (value V, ok bool) // Removes a key from the cache. - Remove(key interface{}) bool + Remove(key K) bool // Removes the oldest entry from cache. - RemoveOldest() (interface{}, interface{}, bool) + RemoveOldest() (K, V, bool) // Returns the oldest entry from the cache. #key, value, isFound - GetOldest() (interface{}, interface{}, bool) + GetOldest() (K, V, bool) // Returns a slice of the keys in the cache, from oldest to newest. - Keys() []interface{} + Keys() []K + + // Values returns a slice of the values in the cache, from oldest to newest. + Values() []V // Returns the number of items in the cache. Len() int @@ -34,6 +41,6 @@ type LRUCache interface { // Clears all cache entries. Purge() - // Resizes cache, returning number evicted - Resize(int) int + // Resizes cache, returning number evicted + Resize(int) int } diff --git a/vendor/github.com/sosodev/duration/.gitignore b/vendor/github.com/sosodev/duration/.gitignore new file mode 100644 index 0000000000..485dee64bc --- /dev/null +++ b/vendor/github.com/sosodev/duration/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/github.com/sosodev/duration/LICENSE b/vendor/github.com/sosodev/duration/LICENSE new file mode 100644 index 0000000000..0e660e6f48 --- /dev/null +++ b/vendor/github.com/sosodev/duration/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Kyle McGough + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/sosodev/duration/duration.go b/vendor/github.com/sosodev/duration/duration.go new file mode 100644 index 0000000000..9a0f7a4bba --- /dev/null +++ b/vendor/github.com/sosodev/duration/duration.go @@ -0,0 +1,311 @@ +package duration + +import ( + "encoding/json" + "errors" + "fmt" + "math" + "strconv" + "strings" + "time" + "unicode" +) + +// Duration holds all the smaller units that make up the duration +type Duration struct { + Years float64 + Months float64 + Weeks float64 + Days float64 + Hours float64 + Minutes float64 + Seconds float64 + Negative bool +} + +const ( + parsingPeriod = iota + parsingTime + + hoursPerDay = 24 + hoursPerWeek = hoursPerDay * 7 + hoursPerMonth = hoursPerYear / 12 + hoursPerYear = hoursPerDay * 365 + + nsPerSecond = 1000000000 + nsPerMinute = nsPerSecond * 60 + nsPerHour = nsPerMinute * 60 + nsPerDay = nsPerHour * hoursPerDay + nsPerWeek = nsPerHour * hoursPerWeek + nsPerMonth = nsPerHour * hoursPerMonth + nsPerYear = nsPerHour * hoursPerYear +) + +var ( + // ErrUnexpectedInput is returned when an input in the duration string does not match expectations + ErrUnexpectedInput = errors.New("unexpected input") +) + +// Parse attempts to parse the given duration string into a *Duration, +// if parsing fails an error is returned instead. +func Parse(d string) (*Duration, error) { + state := parsingPeriod + duration := &Duration{} + num := "" + var err error + + switch { + case strings.HasPrefix(d, "P"): // standard duration + case strings.HasPrefix(d, "-P"): // negative duration + duration.Negative = true + d = strings.TrimPrefix(d, "-") // remove the negative sign + default: + return nil, ErrUnexpectedInput + } + + for _, char := range d { + switch char { + case 'P': + if state != parsingPeriod { + return nil, ErrUnexpectedInput + } + case 'T': + state = parsingTime + case 'Y': + if state != parsingPeriod { + return nil, ErrUnexpectedInput + } + + duration.Years, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + case 'M': + if state == parsingPeriod { + duration.Months, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + } else if state == parsingTime { + duration.Minutes, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + } + case 'W': + if state != parsingPeriod { + return nil, ErrUnexpectedInput + } + + duration.Weeks, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + case 'D': + if state != parsingPeriod { + return nil, ErrUnexpectedInput + } + + duration.Days, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + case 'H': + if state != parsingTime { + return nil, ErrUnexpectedInput + } + + duration.Hours, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + case 'S': + if state != parsingTime { + return nil, ErrUnexpectedInput + } + + duration.Seconds, err = strconv.ParseFloat(num, 64) + if err != nil { + return nil, err + } + num = "" + default: + if unicode.IsNumber(char) || char == '.' { + num += string(char) + continue + } + + return nil, ErrUnexpectedInput + } + } + + return duration, nil +} + +// FromTimeDuration converts the given time.Duration into duration.Duration. +// Note that for *Duration's with period values of a month or year that the duration becomes a bit fuzzy +// since obviously those things vary month to month and year to year. +func FromTimeDuration(d time.Duration) *Duration { + duration := &Duration{} + if d == 0 { + return duration + } + + if d < 0 { + d = -d + duration.Negative = true + } + + if d.Hours() >= hoursPerYear { + duration.Years = math.Floor(d.Hours() / hoursPerYear) + d -= time.Duration(duration.Years) * nsPerYear + } + if d.Hours() >= hoursPerMonth { + duration.Months = math.Floor(d.Hours() / hoursPerMonth) + d -= time.Duration(duration.Months) * nsPerMonth + } + if d.Hours() >= hoursPerWeek { + duration.Weeks = math.Floor(d.Hours() / hoursPerWeek) + d -= time.Duration(duration.Weeks) * nsPerWeek + } + if d.Hours() >= hoursPerDay { + duration.Days = math.Floor(d.Hours() / hoursPerDay) + d -= time.Duration(duration.Days) * nsPerDay + } + if d.Hours() >= 1 { + duration.Hours = math.Floor(d.Hours()) + d -= time.Duration(duration.Hours) * nsPerHour + } + if d.Minutes() >= 1 { + duration.Minutes = math.Floor(d.Minutes()) + d -= time.Duration(duration.Minutes) * nsPerMinute + } + duration.Seconds = d.Seconds() + + return duration +} + +// Format formats the given time.Duration into an ISO 8601 duration string (e.g., P1DT6H5M), +// negative durations are prefixed with a minus sign, for a zero duration "PT0S" is returned. +// Note that for *Duration's with period values of a month or year that the duration becomes a bit fuzzy +// since obviously those things vary month to month and year to year. +func Format(d time.Duration) string { + return FromTimeDuration(d).String() +} + +// ToTimeDuration converts the *Duration to the standard library's time.Duration. +// Note that for *Duration's with period values of a month or year that the duration becomes a bit fuzzy +// since obviously those things vary month to month and year to year. +func (duration *Duration) ToTimeDuration() time.Duration { + var timeDuration time.Duration + + // zero checks are here to avoid unnecessary math operations, on a duration such as `PT5M` + if duration.Years != 0 { + timeDuration += time.Duration(math.Round(duration.Years * nsPerYear)) + } + if duration.Months != 0 { + timeDuration += time.Duration(math.Round(duration.Months * nsPerMonth)) + } + if duration.Weeks != 0 { + timeDuration += time.Duration(math.Round(duration.Weeks * nsPerWeek)) + } + if duration.Days != 0 { + timeDuration += time.Duration(math.Round(duration.Days * nsPerDay)) + } + if duration.Hours != 0 { + timeDuration += time.Duration(math.Round(duration.Hours * nsPerHour)) + } + if duration.Minutes != 0 { + timeDuration += time.Duration(math.Round(duration.Minutes * nsPerMinute)) + } + if duration.Seconds != 0 { + timeDuration += time.Duration(math.Round(duration.Seconds * nsPerSecond)) + } + if duration.Negative { + timeDuration = -timeDuration + } + + return timeDuration +} + +// String returns the ISO8601 duration string for the *Duration +func (duration *Duration) String() string { + d := "P" + hasTime := false + + appendD := func(designator string, value float64, isTime bool) { + if !hasTime && isTime { + d += "T" + hasTime = true + } + + d += strconv.FormatFloat(value, 'f', -1, 64) + designator + } + + if duration.Years != 0 { + appendD("Y", duration.Years, false) + } + + if duration.Months != 0 { + appendD("M", duration.Months, false) + } + + if duration.Weeks != 0 { + appendD("W", duration.Weeks, false) + } + + if duration.Days != 0 { + appendD("D", duration.Days, false) + } + + if duration.Hours != 0 { + appendD("H", duration.Hours, true) + } + + if duration.Minutes != 0 { + appendD("M", duration.Minutes, true) + } + + if duration.Seconds != 0 { + appendD("S", duration.Seconds, true) + } + + // if the duration is zero, return "PT0S" + if d == "P" { + d += "T0S" + } + + if duration.Negative { + return "-" + d + } + + return d +} + +// MarshalJSON satisfies the Marshaler interface by return a valid JSON string representation of the duration +func (duration Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(duration.String()) +} + +// UnmarshalJSON satisfies the Unmarshaler interface by return a valid JSON string representation of the duration +func (duration *Duration) UnmarshalJSON(source []byte) error { + durationString := "" + err := json.Unmarshal(source, &durationString) + if err != nil { + return err + } + + parsed, err := Parse(durationString) + if err != nil { + return fmt.Errorf("failed to parse duration: %w", err) + } + + *duration = *parsed + return nil +} diff --git a/vendor/github.com/sosodev/duration/readme.md b/vendor/github.com/sosodev/duration/readme.md new file mode 100644 index 0000000000..a1d9f37d10 --- /dev/null +++ b/vendor/github.com/sosodev/duration/readme.md @@ -0,0 +1,54 @@ +# duration + +[![Go Reference](https://pkg.go.dev/badge/github.com/sosodev/duration.svg)](https://pkg.go.dev/github.com/sosodev/duration) + +It's a Go module for parsing [ISO 8601 durations](https://en.wikipedia.org/wiki/ISO_8601#Durations) and converting them to the often much more useful `time.Duration`. + +## why? + +ISO 8601 is a pretty common standard and sometimes these durations show up in the wild. + +## installation + +`go get github.com/sosodev/duration` + +## [usage](https://go.dev/play/p/Nz5akjy1c6W) + +```go +package main + +import ( + "fmt" + "time" + "github.com/sosodev/duration" +) + +func main() { + d, err := duration.Parse("P3Y6M4DT12H30M5.5S") + if err != nil { + panic(err) + } + + fmt.Println(d.Years) // 3 + fmt.Println(d.Months) // 6 + fmt.Println(d.Days) // 4 + fmt.Println(d.Hours) // 12 + fmt.Println(d.Minutes) // 30 + fmt.Println(d.Seconds) // 5.5 + + d, err = duration.Parse("PT33.3S") + if err != nil { + panic(err) + } + + fmt.Println(d.ToTimeDuration() == time.Second*33+time.Millisecond*300) // true +} +``` + +## correctness + +This module aims to implement the ISO 8601 duration specification correctly. It properly supports fractional units and has unit tests +that assert the correctness of it's parsing and conversion to a `time.Duration`. + +With that said durations with months or years specified will be converted to `time.Duration` with a little fuzziness. Since I +couldn't find a standard value, and they obviously vary, for those I used `2.628e+15` nanoseconds for a month and `3.154e+16` nanoseconds for a year. diff --git a/vendor/golang.org/x/crypto/LICENSE b/vendor/golang.org/x/crypto/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/crypto/LICENSE +++ b/vendor/golang.org/x/crypto/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/crypto/acme/http.go b/vendor/golang.org/x/crypto/acme/http.go index 58836e5d30..d92ff232fe 100644 --- a/vendor/golang.org/x/crypto/acme/http.go +++ b/vendor/golang.org/x/crypto/acme/http.go @@ -15,6 +15,7 @@ import ( "io" "math/big" "net/http" + "runtime/debug" "strconv" "strings" "time" @@ -271,9 +272,27 @@ func (c *Client) httpClient() *http.Client { } // packageVersion is the version of the module that contains this package, for -// sending as part of the User-Agent header. It's set in version_go112.go. +// sending as part of the User-Agent header. var packageVersion string +func init() { + // Set packageVersion if the binary was built in modules mode and x/crypto + // was not replaced with a different module. + info, ok := debug.ReadBuildInfo() + if !ok { + return + } + for _, m := range info.Deps { + if m.Path != "golang.org/x/crypto" { + continue + } + if m.Replace == nil { + packageVersion = m.Version + } + break + } +} + // userAgent returns the User-Agent header value. It includes the package name, // the module version (if available), and the c.UserAgent value (if set). func (c *Client) userAgent() string { diff --git a/vendor/golang.org/x/crypto/acme/version_go112.go b/vendor/golang.org/x/crypto/acme/version_go112.go deleted file mode 100644 index cc5fab604b..0000000000 --- a/vendor/golang.org/x/crypto/acme/version_go112.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 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. - -//go:build go1.12 - -package acme - -import "runtime/debug" - -func init() { - // Set packageVersion if the binary was built in modules mode and x/crypto - // was not replaced with a different module. - info, ok := debug.ReadBuildInfo() - if !ok { - return - } - for _, m := range info.Deps { - if m.Path != "golang.org/x/crypto" { - continue - } - if m.Replace == nil { - packageVersion = m.Version - } - break - } -} diff --git a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s index 6713accac0..c3895478ed 100644 --- a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s +++ b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s @@ -1,243 +1,2791 @@ -// Copyright 2017 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. +// Code generated by command: go run blamka_amd64.go -out ../blamka_amd64.s -pkg argon2. DO NOT EDIT. //go:build amd64 && gc && !purego #include "textflag.h" -DATA ·c40<>+0x00(SB)/8, $0x0201000706050403 -DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b -GLOBL ·c40<>(SB), (NOPTR+RODATA), $16 - -DATA ·c48<>+0x00(SB)/8, $0x0100070605040302 -DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a -GLOBL ·c48<>(SB), (NOPTR+RODATA), $16 - -#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \ - MOVO v4, t1; \ - MOVO v5, v4; \ - MOVO t1, v5; \ - MOVO v6, t1; \ - PUNPCKLQDQ v6, t2; \ - PUNPCKHQDQ v7, v6; \ - PUNPCKHQDQ t2, v6; \ - PUNPCKLQDQ v7, t2; \ - MOVO t1, v7; \ - MOVO v2, t1; \ - PUNPCKHQDQ t2, v7; \ - PUNPCKLQDQ v3, t2; \ - PUNPCKHQDQ t2, v2; \ - PUNPCKLQDQ t1, t2; \ - PUNPCKHQDQ t2, v3 - -#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \ - MOVO v4, t1; \ - MOVO v5, v4; \ - MOVO t1, v5; \ - MOVO v2, t1; \ - PUNPCKLQDQ v2, t2; \ - PUNPCKHQDQ v3, v2; \ - PUNPCKHQDQ t2, v2; \ - PUNPCKLQDQ v3, t2; \ - MOVO t1, v3; \ - MOVO v6, t1; \ - PUNPCKHQDQ t2, v3; \ - PUNPCKLQDQ v7, t2; \ - PUNPCKHQDQ t2, v6; \ - PUNPCKLQDQ t1, t2; \ - PUNPCKHQDQ t2, v7 - -#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, c40, c48) \ - MOVO v0, t0; \ - PMULULQ v2, t0; \ - PADDQ v2, v0; \ - PADDQ t0, v0; \ - PADDQ t0, v0; \ - PXOR v0, v6; \ - PSHUFD $0xB1, v6, v6; \ - MOVO v4, t0; \ - PMULULQ v6, t0; \ - PADDQ v6, v4; \ - PADDQ t0, v4; \ - PADDQ t0, v4; \ - PXOR v4, v2; \ - PSHUFB c40, v2; \ - MOVO v0, t0; \ - PMULULQ v2, t0; \ - PADDQ v2, v0; \ - PADDQ t0, v0; \ - PADDQ t0, v0; \ - PXOR v0, v6; \ - PSHUFB c48, v6; \ - MOVO v4, t0; \ - PMULULQ v6, t0; \ - PADDQ v6, v4; \ - PADDQ t0, v4; \ - PADDQ t0, v4; \ - PXOR v4, v2; \ - MOVO v2, t0; \ - PADDQ v2, t0; \ - PSRLQ $63, v2; \ - PXOR t0, v2; \ - MOVO v1, t0; \ - PMULULQ v3, t0; \ - PADDQ v3, v1; \ - PADDQ t0, v1; \ - PADDQ t0, v1; \ - PXOR v1, v7; \ - PSHUFD $0xB1, v7, v7; \ - MOVO v5, t0; \ - PMULULQ v7, t0; \ - PADDQ v7, v5; \ - PADDQ t0, v5; \ - PADDQ t0, v5; \ - PXOR v5, v3; \ - PSHUFB c40, v3; \ - MOVO v1, t0; \ - PMULULQ v3, t0; \ - PADDQ v3, v1; \ - PADDQ t0, v1; \ - PADDQ t0, v1; \ - PXOR v1, v7; \ - PSHUFB c48, v7; \ - MOVO v5, t0; \ - PMULULQ v7, t0; \ - PADDQ v7, v5; \ - PADDQ t0, v5; \ - PADDQ t0, v5; \ - PXOR v5, v3; \ - MOVO v3, t0; \ - PADDQ v3, t0; \ - PSRLQ $63, v3; \ - PXOR t0, v3 - -#define LOAD_MSG_0(block, off) \ - MOVOU 8*(off+0)(block), X0; \ - MOVOU 8*(off+2)(block), X1; \ - MOVOU 8*(off+4)(block), X2; \ - MOVOU 8*(off+6)(block), X3; \ - MOVOU 8*(off+8)(block), X4; \ - MOVOU 8*(off+10)(block), X5; \ - MOVOU 8*(off+12)(block), X6; \ - MOVOU 8*(off+14)(block), X7 - -#define STORE_MSG_0(block, off) \ - MOVOU X0, 8*(off+0)(block); \ - MOVOU X1, 8*(off+2)(block); \ - MOVOU X2, 8*(off+4)(block); \ - MOVOU X3, 8*(off+6)(block); \ - MOVOU X4, 8*(off+8)(block); \ - MOVOU X5, 8*(off+10)(block); \ - MOVOU X6, 8*(off+12)(block); \ - MOVOU X7, 8*(off+14)(block) - -#define LOAD_MSG_1(block, off) \ - MOVOU 8*off+0*8(block), X0; \ - MOVOU 8*off+16*8(block), X1; \ - MOVOU 8*off+32*8(block), X2; \ - MOVOU 8*off+48*8(block), X3; \ - MOVOU 8*off+64*8(block), X4; \ - MOVOU 8*off+80*8(block), X5; \ - MOVOU 8*off+96*8(block), X6; \ - MOVOU 8*off+112*8(block), X7 - -#define STORE_MSG_1(block, off) \ - MOVOU X0, 8*off+0*8(block); \ - MOVOU X1, 8*off+16*8(block); \ - MOVOU X2, 8*off+32*8(block); \ - MOVOU X3, 8*off+48*8(block); \ - MOVOU X4, 8*off+64*8(block); \ - MOVOU X5, 8*off+80*8(block); \ - MOVOU X6, 8*off+96*8(block); \ - MOVOU X7, 8*off+112*8(block) - -#define BLAMKA_ROUND_0(block, off, t0, t1, c40, c48) \ - LOAD_MSG_0(block, off); \ - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \ - SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \ - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \ - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \ - STORE_MSG_0(block, off) - -#define BLAMKA_ROUND_1(block, off, t0, t1, c40, c48) \ - LOAD_MSG_1(block, off); \ - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \ - SHUFFLE(X2, X3, X4, X5, X6, X7, t0, t1); \ - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, t0, c40, c48); \ - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, t0, t1); \ - STORE_MSG_1(block, off) - // func blamkaSSE4(b *block) -TEXT ·blamkaSSE4(SB), 4, $0-8 - MOVQ b+0(FP), AX - - MOVOU ·c40<>(SB), X10 - MOVOU ·c48<>(SB), X11 +// Requires: SSE2, SSSE3 +TEXT ·blamkaSSE4(SB), NOSPLIT, $0-8 + MOVQ b+0(FP), AX + MOVOU ·c40<>+0(SB), X10 + MOVOU ·c48<>+0(SB), X11 + MOVOU (AX), X0 + MOVOU 16(AX), X1 + MOVOU 32(AX), X2 + MOVOU 48(AX), X3 + MOVOU 64(AX), X4 + MOVOU 80(AX), X5 + MOVOU 96(AX), X6 + MOVOU 112(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, (AX) + MOVOU X1, 16(AX) + MOVOU X2, 32(AX) + MOVOU X3, 48(AX) + MOVOU X4, 64(AX) + MOVOU X5, 80(AX) + MOVOU X6, 96(AX) + MOVOU X7, 112(AX) + MOVOU 128(AX), X0 + MOVOU 144(AX), X1 + MOVOU 160(AX), X2 + MOVOU 176(AX), X3 + MOVOU 192(AX), X4 + MOVOU 208(AX), X5 + MOVOU 224(AX), X6 + MOVOU 240(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 128(AX) + MOVOU X1, 144(AX) + MOVOU X2, 160(AX) + MOVOU X3, 176(AX) + MOVOU X4, 192(AX) + MOVOU X5, 208(AX) + MOVOU X6, 224(AX) + MOVOU X7, 240(AX) + MOVOU 256(AX), X0 + MOVOU 272(AX), X1 + MOVOU 288(AX), X2 + MOVOU 304(AX), X3 + MOVOU 320(AX), X4 + MOVOU 336(AX), X5 + MOVOU 352(AX), X6 + MOVOU 368(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 256(AX) + MOVOU X1, 272(AX) + MOVOU X2, 288(AX) + MOVOU X3, 304(AX) + MOVOU X4, 320(AX) + MOVOU X5, 336(AX) + MOVOU X6, 352(AX) + MOVOU X7, 368(AX) + MOVOU 384(AX), X0 + MOVOU 400(AX), X1 + MOVOU 416(AX), X2 + MOVOU 432(AX), X3 + MOVOU 448(AX), X4 + MOVOU 464(AX), X5 + MOVOU 480(AX), X6 + MOVOU 496(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 384(AX) + MOVOU X1, 400(AX) + MOVOU X2, 416(AX) + MOVOU X3, 432(AX) + MOVOU X4, 448(AX) + MOVOU X5, 464(AX) + MOVOU X6, 480(AX) + MOVOU X7, 496(AX) + MOVOU 512(AX), X0 + MOVOU 528(AX), X1 + MOVOU 544(AX), X2 + MOVOU 560(AX), X3 + MOVOU 576(AX), X4 + MOVOU 592(AX), X5 + MOVOU 608(AX), X6 + MOVOU 624(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 512(AX) + MOVOU X1, 528(AX) + MOVOU X2, 544(AX) + MOVOU X3, 560(AX) + MOVOU X4, 576(AX) + MOVOU X5, 592(AX) + MOVOU X6, 608(AX) + MOVOU X7, 624(AX) + MOVOU 640(AX), X0 + MOVOU 656(AX), X1 + MOVOU 672(AX), X2 + MOVOU 688(AX), X3 + MOVOU 704(AX), X4 + MOVOU 720(AX), X5 + MOVOU 736(AX), X6 + MOVOU 752(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 640(AX) + MOVOU X1, 656(AX) + MOVOU X2, 672(AX) + MOVOU X3, 688(AX) + MOVOU X4, 704(AX) + MOVOU X5, 720(AX) + MOVOU X6, 736(AX) + MOVOU X7, 752(AX) + MOVOU 768(AX), X0 + MOVOU 784(AX), X1 + MOVOU 800(AX), X2 + MOVOU 816(AX), X3 + MOVOU 832(AX), X4 + MOVOU 848(AX), X5 + MOVOU 864(AX), X6 + MOVOU 880(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 768(AX) + MOVOU X1, 784(AX) + MOVOU X2, 800(AX) + MOVOU X3, 816(AX) + MOVOU X4, 832(AX) + MOVOU X5, 848(AX) + MOVOU X6, 864(AX) + MOVOU X7, 880(AX) + MOVOU 896(AX), X0 + MOVOU 912(AX), X1 + MOVOU 928(AX), X2 + MOVOU 944(AX), X3 + MOVOU 960(AX), X4 + MOVOU 976(AX), X5 + MOVOU 992(AX), X6 + MOVOU 1008(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 896(AX) + MOVOU X1, 912(AX) + MOVOU X2, 928(AX) + MOVOU X3, 944(AX) + MOVOU X4, 960(AX) + MOVOU X5, 976(AX) + MOVOU X6, 992(AX) + MOVOU X7, 1008(AX) + MOVOU (AX), X0 + MOVOU 128(AX), X1 + MOVOU 256(AX), X2 + MOVOU 384(AX), X3 + MOVOU 512(AX), X4 + MOVOU 640(AX), X5 + MOVOU 768(AX), X6 + MOVOU 896(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, (AX) + MOVOU X1, 128(AX) + MOVOU X2, 256(AX) + MOVOU X3, 384(AX) + MOVOU X4, 512(AX) + MOVOU X5, 640(AX) + MOVOU X6, 768(AX) + MOVOU X7, 896(AX) + MOVOU 16(AX), X0 + MOVOU 144(AX), X1 + MOVOU 272(AX), X2 + MOVOU 400(AX), X3 + MOVOU 528(AX), X4 + MOVOU 656(AX), X5 + MOVOU 784(AX), X6 + MOVOU 912(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 16(AX) + MOVOU X1, 144(AX) + MOVOU X2, 272(AX) + MOVOU X3, 400(AX) + MOVOU X4, 528(AX) + MOVOU X5, 656(AX) + MOVOU X6, 784(AX) + MOVOU X7, 912(AX) + MOVOU 32(AX), X0 + MOVOU 160(AX), X1 + MOVOU 288(AX), X2 + MOVOU 416(AX), X3 + MOVOU 544(AX), X4 + MOVOU 672(AX), X5 + MOVOU 800(AX), X6 + MOVOU 928(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 32(AX) + MOVOU X1, 160(AX) + MOVOU X2, 288(AX) + MOVOU X3, 416(AX) + MOVOU X4, 544(AX) + MOVOU X5, 672(AX) + MOVOU X6, 800(AX) + MOVOU X7, 928(AX) + MOVOU 48(AX), X0 + MOVOU 176(AX), X1 + MOVOU 304(AX), X2 + MOVOU 432(AX), X3 + MOVOU 560(AX), X4 + MOVOU 688(AX), X5 + MOVOU 816(AX), X6 + MOVOU 944(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 48(AX) + MOVOU X1, 176(AX) + MOVOU X2, 304(AX) + MOVOU X3, 432(AX) + MOVOU X4, 560(AX) + MOVOU X5, 688(AX) + MOVOU X6, 816(AX) + MOVOU X7, 944(AX) + MOVOU 64(AX), X0 + MOVOU 192(AX), X1 + MOVOU 320(AX), X2 + MOVOU 448(AX), X3 + MOVOU 576(AX), X4 + MOVOU 704(AX), X5 + MOVOU 832(AX), X6 + MOVOU 960(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 64(AX) + MOVOU X1, 192(AX) + MOVOU X2, 320(AX) + MOVOU X3, 448(AX) + MOVOU X4, 576(AX) + MOVOU X5, 704(AX) + MOVOU X6, 832(AX) + MOVOU X7, 960(AX) + MOVOU 80(AX), X0 + MOVOU 208(AX), X1 + MOVOU 336(AX), X2 + MOVOU 464(AX), X3 + MOVOU 592(AX), X4 + MOVOU 720(AX), X5 + MOVOU 848(AX), X6 + MOVOU 976(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 80(AX) + MOVOU X1, 208(AX) + MOVOU X2, 336(AX) + MOVOU X3, 464(AX) + MOVOU X4, 592(AX) + MOVOU X5, 720(AX) + MOVOU X6, 848(AX) + MOVOU X7, 976(AX) + MOVOU 96(AX), X0 + MOVOU 224(AX), X1 + MOVOU 352(AX), X2 + MOVOU 480(AX), X3 + MOVOU 608(AX), X4 + MOVOU 736(AX), X5 + MOVOU 864(AX), X6 + MOVOU 992(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 96(AX) + MOVOU X1, 224(AX) + MOVOU X2, 352(AX) + MOVOU X3, 480(AX) + MOVOU X4, 608(AX) + MOVOU X5, 736(AX) + MOVOU X6, 864(AX) + MOVOU X7, 992(AX) + MOVOU 112(AX), X0 + MOVOU 240(AX), X1 + MOVOU 368(AX), X2 + MOVOU 496(AX), X3 + MOVOU 624(AX), X4 + MOVOU 752(AX), X5 + MOVOU 880(AX), X6 + MOVOU 1008(AX), X7 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFD $0xb1, X6, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + PSHUFB X10, X2 + MOVO X0, X8 + PMULULQ X2, X8 + PADDQ X2, X0 + PADDQ X8, X0 + PADDQ X8, X0 + PXOR X0, X6 + PSHUFB X11, X6 + MOVO X4, X8 + PMULULQ X6, X8 + PADDQ X6, X4 + PADDQ X8, X4 + PADDQ X8, X4 + PXOR X4, X2 + MOVO X2, X8 + PADDQ X2, X8 + PSRLQ $0x3f, X2 + PXOR X8, X2 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFD $0xb1, X7, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + PSHUFB X10, X3 + MOVO X1, X8 + PMULULQ X3, X8 + PADDQ X3, X1 + PADDQ X8, X1 + PADDQ X8, X1 + PXOR X1, X7 + PSHUFB X11, X7 + MOVO X5, X8 + PMULULQ X7, X8 + PADDQ X7, X5 + PADDQ X8, X5 + PADDQ X8, X5 + PXOR X5, X3 + MOVO X3, X8 + PADDQ X3, X8 + PSRLQ $0x3f, X3 + PXOR X8, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU X0, 112(AX) + MOVOU X1, 240(AX) + MOVOU X2, 368(AX) + MOVOU X3, 496(AX) + MOVOU X4, 624(AX) + MOVOU X5, 752(AX) + MOVOU X6, 880(AX) + MOVOU X7, 1008(AX) + RET - BLAMKA_ROUND_0(AX, 0, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 16, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 32, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 48, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 64, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 80, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 96, X8, X9, X10, X11) - BLAMKA_ROUND_0(AX, 112, X8, X9, X10, X11) +DATA ·c40<>+0(SB)/8, $0x0201000706050403 +DATA ·c40<>+8(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·c40<>(SB), RODATA|NOPTR, $16 - BLAMKA_ROUND_1(AX, 0, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 2, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 4, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 6, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 8, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 10, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 12, X8, X9, X10, X11) - BLAMKA_ROUND_1(AX, 14, X8, X9, X10, X11) - RET +DATA ·c48<>+0(SB)/8, $0x0100070605040302 +DATA ·c48<>+8(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·c48<>(SB), RODATA|NOPTR, $16 -// func mixBlocksSSE2(out, a, b, c *block) -TEXT ·mixBlocksSSE2(SB), 4, $0-32 +// func mixBlocksSSE2(out *block, a *block, b *block, c *block) +// Requires: SSE2 +TEXT ·mixBlocksSSE2(SB), NOSPLIT, $0-32 MOVQ out+0(FP), DX MOVQ a+8(FP), AX MOVQ b+16(FP), BX MOVQ c+24(FP), CX - MOVQ $128, DI + MOVQ $0x00000080, DI loop: - MOVOU 0(AX), X0 - MOVOU 0(BX), X1 - MOVOU 0(CX), X2 + MOVOU (AX), X0 + MOVOU (BX), X1 + MOVOU (CX), X2 PXOR X1, X0 PXOR X2, X0 - MOVOU X0, 0(DX) - ADDQ $16, AX - ADDQ $16, BX - ADDQ $16, CX - ADDQ $16, DX - SUBQ $2, DI + MOVOU X0, (DX) + ADDQ $0x10, AX + ADDQ $0x10, BX + ADDQ $0x10, CX + ADDQ $0x10, DX + SUBQ $0x02, DI JA loop RET -// func xorBlocksSSE2(out, a, b, c *block) -TEXT ·xorBlocksSSE2(SB), 4, $0-32 +// func xorBlocksSSE2(out *block, a *block, b *block, c *block) +// Requires: SSE2 +TEXT ·xorBlocksSSE2(SB), NOSPLIT, $0-32 MOVQ out+0(FP), DX MOVQ a+8(FP), AX MOVQ b+16(FP), BX MOVQ c+24(FP), CX - MOVQ $128, DI + MOVQ $0x00000080, DI loop: - MOVOU 0(AX), X0 - MOVOU 0(BX), X1 - MOVOU 0(CX), X2 - MOVOU 0(DX), X3 + MOVOU (AX), X0 + MOVOU (BX), X1 + MOVOU (CX), X2 + MOVOU (DX), X3 PXOR X1, X0 PXOR X2, X0 PXOR X3, X0 - MOVOU X0, 0(DX) - ADDQ $16, AX - ADDQ $16, BX - ADDQ $16, CX - ADDQ $16, DX - SUBQ $2, DI + MOVOU X0, (DX) + ADDQ $0x10, AX + ADDQ $0x10, BX + ADDQ $0x10, CX + ADDQ $0x10, DX + SUBQ $0x02, DI JA loop RET diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s index 9ae8206c20..f75162e039 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s @@ -1,722 +1,4517 @@ -// Copyright 2016 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. +// Code generated by command: go run blake2bAVX2_amd64_asm.go -out ../../blake2bAVX2_amd64.s -pkg blake2b. DO NOT EDIT. //go:build amd64 && gc && !purego #include "textflag.h" -DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 -DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b -DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b -DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1 -GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32 - -DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1 -DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f -DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b -DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179 -GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32 - -DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403 -DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b -DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403 -DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b -GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32 - -DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302 -DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a -DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302 -DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a -GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32 - -DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 -DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b -GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16 - -DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b -DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1 -GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16 - -DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1 -DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f -GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16 - -DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b -DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179 -GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16 - -DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403 -DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b -GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16 - -DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302 -DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a -GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16 - -#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39 -#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93 -#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e -#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93 -#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39 - -#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \ - VPADDQ m0, Y0, Y0; \ - VPADDQ Y1, Y0, Y0; \ - VPXOR Y0, Y3, Y3; \ - VPSHUFD $-79, Y3, Y3; \ - VPADDQ Y3, Y2, Y2; \ - VPXOR Y2, Y1, Y1; \ - VPSHUFB c40, Y1, Y1; \ - VPADDQ m1, Y0, Y0; \ - VPADDQ Y1, Y0, Y0; \ - VPXOR Y0, Y3, Y3; \ - VPSHUFB c48, Y3, Y3; \ - VPADDQ Y3, Y2, Y2; \ - VPXOR Y2, Y1, Y1; \ - VPADDQ Y1, Y1, t; \ - VPSRLQ $63, Y1, Y1; \ - VPXOR t, Y1, Y1; \ - VPERMQ_0x39_Y1_Y1; \ - VPERMQ_0x4E_Y2_Y2; \ - VPERMQ_0x93_Y3_Y3; \ - VPADDQ m2, Y0, Y0; \ - VPADDQ Y1, Y0, Y0; \ - VPXOR Y0, Y3, Y3; \ - VPSHUFD $-79, Y3, Y3; \ - VPADDQ Y3, Y2, Y2; \ - VPXOR Y2, Y1, Y1; \ - VPSHUFB c40, Y1, Y1; \ - VPADDQ m3, Y0, Y0; \ - VPADDQ Y1, Y0, Y0; \ - VPXOR Y0, Y3, Y3; \ - VPSHUFB c48, Y3, Y3; \ - VPADDQ Y3, Y2, Y2; \ - VPXOR Y2, Y1, Y1; \ - VPADDQ Y1, Y1, t; \ - VPSRLQ $63, Y1, Y1; \ - VPXOR t, Y1, Y1; \ - VPERMQ_0x39_Y3_Y3; \ - VPERMQ_0x4E_Y2_Y2; \ - VPERMQ_0x93_Y1_Y1 - -#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E -#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26 -#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E -#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36 -#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E - -#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n -#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n -#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n -#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n -#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n - -#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01 -#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01 -#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01 -#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01 -#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01 - -#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01 -#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01 -#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01 -#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01 -#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01 - -#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8 -#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01 - -// load msg: Y12 = (i0, i1, i2, i3) -// i0, i1, i2, i3 must not be 0 -#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \ - VMOVQ_SI_X12(i0*8); \ - VMOVQ_SI_X11(i2*8); \ - VPINSRQ_1_SI_X12(i1*8); \ - VPINSRQ_1_SI_X11(i3*8); \ - VINSERTI128 $1, X11, Y12, Y12 - -// load msg: Y13 = (i0, i1, i2, i3) -// i0, i1, i2, i3 must not be 0 -#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \ - VMOVQ_SI_X13(i0*8); \ - VMOVQ_SI_X11(i2*8); \ - VPINSRQ_1_SI_X13(i1*8); \ - VPINSRQ_1_SI_X11(i3*8); \ - VINSERTI128 $1, X11, Y13, Y13 - -// load msg: Y14 = (i0, i1, i2, i3) -// i0, i1, i2, i3 must not be 0 -#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \ - VMOVQ_SI_X14(i0*8); \ - VMOVQ_SI_X11(i2*8); \ - VPINSRQ_1_SI_X14(i1*8); \ - VPINSRQ_1_SI_X11(i3*8); \ - VINSERTI128 $1, X11, Y14, Y14 - -// load msg: Y15 = (i0, i1, i2, i3) -// i0, i1, i2, i3 must not be 0 -#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \ - VMOVQ_SI_X15(i0*8); \ - VMOVQ_SI_X11(i2*8); \ - VPINSRQ_1_SI_X15(i1*8); \ - VPINSRQ_1_SI_X11(i3*8); \ - VINSERTI128 $1, X11, Y15, Y15 - -#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \ - VMOVQ_SI_X12_0; \ - VMOVQ_SI_X11(4*8); \ - VPINSRQ_1_SI_X12(2*8); \ - VPINSRQ_1_SI_X11(6*8); \ - VINSERTI128 $1, X11, Y12, Y12; \ - LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \ - LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \ - LOAD_MSG_AVX2_Y15(9, 11, 13, 15) - -#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \ - LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \ - LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \ - VMOVQ_SI_X11(11*8); \ - VPSHUFD $0x4E, 0*8(SI), X14; \ - VPINSRQ_1_SI_X11(5*8); \ - VINSERTI128 $1, X11, Y14, Y14; \ - LOAD_MSG_AVX2_Y15(12, 2, 7, 3) - -#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \ - VMOVQ_SI_X11(5*8); \ - VMOVDQU 11*8(SI), X12; \ - VPINSRQ_1_SI_X11(15*8); \ - VINSERTI128 $1, X11, Y12, Y12; \ - VMOVQ_SI_X13(8*8); \ - VMOVQ_SI_X11(2*8); \ - VPINSRQ_1_SI_X13_0; \ - VPINSRQ_1_SI_X11(13*8); \ - VINSERTI128 $1, X11, Y13, Y13; \ - LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \ - LOAD_MSG_AVX2_Y15(14, 6, 1, 4) - -#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \ - LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \ - LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \ - LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \ - VMOVQ_SI_X15(6*8); \ - VMOVQ_SI_X11_0; \ - VPINSRQ_1_SI_X15(10*8); \ - VPINSRQ_1_SI_X11(8*8); \ - VINSERTI128 $1, X11, Y15, Y15 - -#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \ - LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \ - VMOVQ_SI_X13_0; \ - VMOVQ_SI_X11(4*8); \ - VPINSRQ_1_SI_X13(7*8); \ - VPINSRQ_1_SI_X11(15*8); \ - VINSERTI128 $1, X11, Y13, Y13; \ - LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \ - LOAD_MSG_AVX2_Y15(1, 12, 8, 13) - -#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \ - VMOVQ_SI_X12(2*8); \ - VMOVQ_SI_X11_0; \ - VPINSRQ_1_SI_X12(6*8); \ - VPINSRQ_1_SI_X11(8*8); \ - VINSERTI128 $1, X11, Y12, Y12; \ - LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \ - LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \ - LOAD_MSG_AVX2_Y15(13, 5, 14, 9) - -#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \ - LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \ - LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \ - VMOVQ_SI_X14_0; \ - VPSHUFD $0x4E, 8*8(SI), X11; \ - VPINSRQ_1_SI_X14(6*8); \ - VINSERTI128 $1, X11, Y14, Y14; \ - LOAD_MSG_AVX2_Y15(7, 3, 2, 11) - -#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \ - LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \ - LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \ - LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \ - VMOVQ_SI_X15_0; \ - VMOVQ_SI_X11(6*8); \ - VPINSRQ_1_SI_X15(4*8); \ - VPINSRQ_1_SI_X11(10*8); \ - VINSERTI128 $1, X11, Y15, Y15 - -#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \ - VMOVQ_SI_X12(6*8); \ - VMOVQ_SI_X11(11*8); \ - VPINSRQ_1_SI_X12(14*8); \ - VPINSRQ_1_SI_X11_0; \ - VINSERTI128 $1, X11, Y12, Y12; \ - LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \ - VMOVQ_SI_X11(1*8); \ - VMOVDQU 12*8(SI), X14; \ - VPINSRQ_1_SI_X11(10*8); \ - VINSERTI128 $1, X11, Y14, Y14; \ - VMOVQ_SI_X15(2*8); \ - VMOVDQU 4*8(SI), X11; \ - VPINSRQ_1_SI_X15(7*8); \ - VINSERTI128 $1, X11, Y15, Y15 - -#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \ - LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \ - VMOVQ_SI_X13(2*8); \ - VPSHUFD $0x4E, 5*8(SI), X11; \ - VPINSRQ_1_SI_X13(4*8); \ - VINSERTI128 $1, X11, Y13, Y13; \ - LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \ - VMOVQ_SI_X15(11*8); \ - VMOVQ_SI_X11(12*8); \ - VPINSRQ_1_SI_X15(14*8); \ - VPINSRQ_1_SI_X11_0; \ - VINSERTI128 $1, X11, Y15, Y15 - // func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) -TEXT ·hashBlocksAVX2(SB), 4, $320-48 // frame size = 288 + 32 byte alignment - MOVQ h+0(FP), AX - MOVQ c+8(FP), BX - MOVQ flag+16(FP), CX - MOVQ blocks_base+24(FP), SI - MOVQ blocks_len+32(FP), DI - - MOVQ SP, DX - ADDQ $31, DX - ANDQ $~31, DX - - MOVQ CX, 16(DX) - XORQ CX, CX - MOVQ CX, 24(DX) - - VMOVDQU ·AVX2_c40<>(SB), Y4 - VMOVDQU ·AVX2_c48<>(SB), Y5 - - VMOVDQU 0(AX), Y8 +// Requires: AVX, AVX2 +TEXT ·hashBlocksAVX2(SB), NOSPLIT, $320-48 + MOVQ h+0(FP), AX + MOVQ c+8(FP), BX + MOVQ flag+16(FP), CX + MOVQ blocks_base+24(FP), SI + MOVQ blocks_len+32(FP), DI + MOVQ SP, DX + ADDQ $+31, DX + ANDQ $-32, DX + MOVQ CX, 16(DX) + XORQ CX, CX + MOVQ CX, 24(DX) + VMOVDQU ·AVX2_c40<>+0(SB), Y4 + VMOVDQU ·AVX2_c48<>+0(SB), Y5 + VMOVDQU (AX), Y8 VMOVDQU 32(AX), Y9 - VMOVDQU ·AVX2_iv0<>(SB), Y6 - VMOVDQU ·AVX2_iv1<>(SB), Y7 - - MOVQ 0(BX), R8 - MOVQ 8(BX), R9 - MOVQ R9, 8(DX) + VMOVDQU ·AVX2_iv0<>+0(SB), Y6 + VMOVDQU ·AVX2_iv1<>+0(SB), Y7 + MOVQ (BX), R8 + MOVQ 8(BX), R9 + MOVQ R9, 8(DX) loop: - ADDQ $128, R8 - MOVQ R8, 0(DX) - CMPQ R8, $128 + ADDQ $0x80, R8 + MOVQ R8, (DX) + CMPQ R8, $0x80 JGE noinc INCQ R9 MOVQ R9, 8(DX) noinc: - VMOVDQA Y8, Y0 - VMOVDQA Y9, Y1 - VMOVDQA Y6, Y2 - VPXOR 0(DX), Y7, Y3 - - LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() - VMOVDQA Y12, 32(DX) - VMOVDQA Y13, 64(DX) - VMOVDQA Y14, 96(DX) - VMOVDQA Y15, 128(DX) - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() - VMOVDQA Y12, 160(DX) - VMOVDQA Y13, 192(DX) - VMOVDQA Y14, 224(DX) - VMOVDQA Y15, 256(DX) - - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() - ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) - - ROUND_AVX2(32(DX), 64(DX), 96(DX), 128(DX), Y10, Y4, Y5) - ROUND_AVX2(160(DX), 192(DX), 224(DX), 256(DX), Y10, Y4, Y5) - - VPXOR Y0, Y8, Y8 - VPXOR Y1, Y9, Y9 - VPXOR Y2, Y8, Y8 - VPXOR Y3, Y9, Y9 - - LEAQ 128(SI), SI - SUBQ $128, DI - JNE loop - - MOVQ R8, 0(BX) - MOVQ R9, 8(BX) - - VMOVDQU Y8, 0(AX) - VMOVDQU Y9, 32(AX) + VMOVDQA Y8, Y0 + VMOVDQA Y9, Y1 + VMOVDQA Y6, Y2 + VPXOR (DX), Y7, Y3 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x26 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x20 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x10 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x30 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x08 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x28 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x38 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x40 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x60 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x70 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x68 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x58 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x78 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VMOVDQA Y12, 32(DX) + VMOVDQA Y13, 64(DX) + VMOVDQA Y14, 96(DX) + VMOVDQA Y15, 128(DX) + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x48 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x20 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x68 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x50 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x78 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x40 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x30 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x58 + VPSHUFD $0x4e, (SI), X14 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x28 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x38 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x10 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x18 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VMOVDQA Y12, 160(DX) + VMOVDQA Y13, 192(DX) + VMOVDQA Y14, 224(DX) + VMOVDQA Y15, 256(DX) + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x28 + VMOVDQU 88(SI), X12 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x78 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x40 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x10 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x2e + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x68 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x50 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x38 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x48 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x08 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x30 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x20 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x38 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x68 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x58 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x60 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x08 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x70 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x10 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x20 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x78 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x30 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x1e + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x40 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x10 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x50 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x2e + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x20 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x38 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x78 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x30 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x58 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x18 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x08 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x40 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x60 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x68 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x10 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x1e + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x30 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x40 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x58 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x18 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x20 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x78 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x38 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x08 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x68 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x70 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x48 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x70 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x08 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x20 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x28 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x68 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x78 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x50 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x36 + VPSHUFD $0x4e, 64(SI), X11 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x30 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x38 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x10 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x58 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x68 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x60 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x38 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x18 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x58 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x08 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x48 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x28 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x40 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x78 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x10 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x3e + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x30 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x20 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x50 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x30 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x58 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x1e + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x78 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x18 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x48 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x40 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x08 + VMOVDQU 96(SI), X14 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x50 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x10 + VMOVDQU 32(SI), X11 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x38 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x50 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x38 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x40 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x08 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y12, Y12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x10 + VPSHUFD $0x4e, 40(SI), X11 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x20 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y13, Y13 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x78 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x18 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x48 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x5e + BYTE $0x68 + BYTE $0x01 + VINSERTI128 $0x01, X11, Y14, Y14 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x58 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x5e + BYTE $0x60 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0xa1 + BYTE $0x22 + BYTE $0x1e + BYTE $0x01 + VINSERTI128 $0x01, X11, Y15, Y15 + VPADDQ Y12, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y13, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ Y14, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ Y15, Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + VPADDQ 32(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ 64(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ 96(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ 128(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + VPADDQ 160(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ 192(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x93 + VPADDQ 224(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFD $-79, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPSHUFB Y4, Y1, Y1 + VPADDQ 256(DX), Y0, Y0 + VPADDQ Y1, Y0, Y0 + VPXOR Y0, Y3, Y3 + VPSHUFB Y5, Y3, Y3 + VPADDQ Y3, Y2, Y2 + VPXOR Y2, Y1, Y1 + VPADDQ Y1, Y1, Y10 + VPSRLQ $0x3f, Y1, Y1 + VPXOR Y10, Y1, Y1 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xdb + BYTE $0x39 + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xd2 + BYTE $0x4e + BYTE $0xc4 + BYTE $0xe3 + BYTE $0xfd + BYTE $0x00 + BYTE $0xc9 + BYTE $0x93 + VPXOR Y0, Y8, Y8 + VPXOR Y1, Y9, Y9 + VPXOR Y2, Y8, Y8 + VPXOR Y3, Y9, Y9 + LEAQ 128(SI), SI + SUBQ $0x80, DI + JNE loop + MOVQ R8, (BX) + MOVQ R9, 8(BX) + VMOVDQU Y8, (AX) + VMOVDQU Y9, 32(AX) VZEROUPPER - RET -#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA -#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB -#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF -#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD -#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE - -#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7 -#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF -#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7 -#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF -#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7 -#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7 -#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF -#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF - -#define SHUFFLE_AVX() \ - VMOVDQA X6, X13; \ - VMOVDQA X2, X14; \ - VMOVDQA X4, X6; \ - VPUNPCKLQDQ_X13_X13_X15; \ - VMOVDQA X5, X4; \ - VMOVDQA X6, X5; \ - VPUNPCKHQDQ_X15_X7_X6; \ - VPUNPCKLQDQ_X7_X7_X15; \ - VPUNPCKHQDQ_X15_X13_X7; \ - VPUNPCKLQDQ_X3_X3_X15; \ - VPUNPCKHQDQ_X15_X2_X2; \ - VPUNPCKLQDQ_X14_X14_X15; \ - VPUNPCKHQDQ_X15_X3_X3; \ - -#define SHUFFLE_AVX_INV() \ - VMOVDQA X2, X13; \ - VMOVDQA X4, X14; \ - VPUNPCKLQDQ_X2_X2_X15; \ - VMOVDQA X5, X4; \ - VPUNPCKHQDQ_X15_X3_X2; \ - VMOVDQA X14, X5; \ - VPUNPCKLQDQ_X3_X3_X15; \ - VMOVDQA X6, X14; \ - VPUNPCKHQDQ_X15_X13_X3; \ - VPUNPCKLQDQ_X7_X7_X15; \ - VPUNPCKHQDQ_X15_X6_X6; \ - VPUNPCKLQDQ_X14_X14_X15; \ - VPUNPCKHQDQ_X15_X7_X7; \ - -#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ - VPADDQ m0, v0, v0; \ - VPADDQ v2, v0, v0; \ - VPADDQ m1, v1, v1; \ - VPADDQ v3, v1, v1; \ - VPXOR v0, v6, v6; \ - VPXOR v1, v7, v7; \ - VPSHUFD $-79, v6, v6; \ - VPSHUFD $-79, v7, v7; \ - VPADDQ v6, v4, v4; \ - VPADDQ v7, v5, v5; \ - VPXOR v4, v2, v2; \ - VPXOR v5, v3, v3; \ - VPSHUFB c40, v2, v2; \ - VPSHUFB c40, v3, v3; \ - VPADDQ m2, v0, v0; \ - VPADDQ v2, v0, v0; \ - VPADDQ m3, v1, v1; \ - VPADDQ v3, v1, v1; \ - VPXOR v0, v6, v6; \ - VPXOR v1, v7, v7; \ - VPSHUFB c48, v6, v6; \ - VPSHUFB c48, v7, v7; \ - VPADDQ v6, v4, v4; \ - VPADDQ v7, v5, v5; \ - VPXOR v4, v2, v2; \ - VPXOR v5, v3, v3; \ - VPADDQ v2, v2, t0; \ - VPSRLQ $63, v2, v2; \ - VPXOR t0, v2, v2; \ - VPADDQ v3, v3, t0; \ - VPSRLQ $63, v3, v3; \ - VPXOR t0, v3, v3 - -// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7) -// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0 -#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \ - VMOVQ_SI_X12(i0*8); \ - VMOVQ_SI_X13(i2*8); \ - VMOVQ_SI_X14(i4*8); \ - VMOVQ_SI_X15(i6*8); \ - VPINSRQ_1_SI_X12(i1*8); \ - VPINSRQ_1_SI_X13(i3*8); \ - VPINSRQ_1_SI_X14(i5*8); \ - VPINSRQ_1_SI_X15(i7*8) - -// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7) -#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \ - VMOVQ_SI_X12_0; \ - VMOVQ_SI_X13(4*8); \ - VMOVQ_SI_X14(1*8); \ - VMOVQ_SI_X15(5*8); \ - VPINSRQ_1_SI_X12(2*8); \ - VPINSRQ_1_SI_X13(6*8); \ - VPINSRQ_1_SI_X14(3*8); \ - VPINSRQ_1_SI_X15(7*8) - -// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3) -#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \ - VPSHUFD $0x4E, 0*8(SI), X12; \ - VMOVQ_SI_X13(11*8); \ - VMOVQ_SI_X14(12*8); \ - VMOVQ_SI_X15(7*8); \ - VPINSRQ_1_SI_X13(5*8); \ - VPINSRQ_1_SI_X14(2*8); \ - VPINSRQ_1_SI_X15(3*8) - -// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13) -#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \ - VMOVDQU 11*8(SI), X12; \ - VMOVQ_SI_X13(5*8); \ - VMOVQ_SI_X14(8*8); \ - VMOVQ_SI_X15(2*8); \ - VPINSRQ_1_SI_X13(15*8); \ - VPINSRQ_1_SI_X14_0; \ - VPINSRQ_1_SI_X15(13*8) - -// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8) -#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \ - VMOVQ_SI_X12(2*8); \ - VMOVQ_SI_X13(4*8); \ - VMOVQ_SI_X14(6*8); \ - VMOVQ_SI_X15_0; \ - VPINSRQ_1_SI_X12(5*8); \ - VPINSRQ_1_SI_X13(15*8); \ - VPINSRQ_1_SI_X14(10*8); \ - VPINSRQ_1_SI_X15(8*8) +DATA ·AVX2_c40<>+0(SB)/8, $0x0201000706050403 +DATA ·AVX2_c40<>+8(SB)/8, $0x0a09080f0e0d0c0b +DATA ·AVX2_c40<>+16(SB)/8, $0x0201000706050403 +DATA ·AVX2_c40<>+24(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·AVX2_c40<>(SB), RODATA|NOPTR, $32 -// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15) -#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \ - VMOVQ_SI_X12(9*8); \ - VMOVQ_SI_X13(2*8); \ - VMOVQ_SI_X14_0; \ - VMOVQ_SI_X15(4*8); \ - VPINSRQ_1_SI_X12(5*8); \ - VPINSRQ_1_SI_X13(10*8); \ - VPINSRQ_1_SI_X14(7*8); \ - VPINSRQ_1_SI_X15(15*8) +DATA ·AVX2_c48<>+0(SB)/8, $0x0100070605040302 +DATA ·AVX2_c48<>+8(SB)/8, $0x09080f0e0d0c0b0a +DATA ·AVX2_c48<>+16(SB)/8, $0x0100070605040302 +DATA ·AVX2_c48<>+24(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·AVX2_c48<>(SB), RODATA|NOPTR, $32 -// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3) -#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \ - VMOVQ_SI_X12(2*8); \ - VMOVQ_SI_X13_0; \ - VMOVQ_SI_X14(12*8); \ - VMOVQ_SI_X15(11*8); \ - VPINSRQ_1_SI_X12(6*8); \ - VPINSRQ_1_SI_X13(8*8); \ - VPINSRQ_1_SI_X14(10*8); \ - VPINSRQ_1_SI_X15(3*8) +DATA ·AVX2_iv0<>+0(SB)/8, $0x6a09e667f3bcc908 +DATA ·AVX2_iv0<>+8(SB)/8, $0xbb67ae8584caa73b +DATA ·AVX2_iv0<>+16(SB)/8, $0x3c6ef372fe94f82b +DATA ·AVX2_iv0<>+24(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·AVX2_iv0<>(SB), RODATA|NOPTR, $32 -// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11) -#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \ - MOVQ 0*8(SI), X12; \ - VPSHUFD $0x4E, 8*8(SI), X13; \ - MOVQ 7*8(SI), X14; \ - MOVQ 2*8(SI), X15; \ - VPINSRQ_1_SI_X12(6*8); \ - VPINSRQ_1_SI_X14(3*8); \ - VPINSRQ_1_SI_X15(11*8) - -// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8) -#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \ - MOVQ 6*8(SI), X12; \ - MOVQ 11*8(SI), X13; \ - MOVQ 15*8(SI), X14; \ - MOVQ 3*8(SI), X15; \ - VPINSRQ_1_SI_X12(14*8); \ - VPINSRQ_1_SI_X13_0; \ - VPINSRQ_1_SI_X14(9*8); \ - VPINSRQ_1_SI_X15(8*8) - -// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10) -#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \ - MOVQ 5*8(SI), X12; \ - MOVQ 8*8(SI), X13; \ - MOVQ 0*8(SI), X14; \ - MOVQ 6*8(SI), X15; \ - VPINSRQ_1_SI_X12(15*8); \ - VPINSRQ_1_SI_X13(2*8); \ - VPINSRQ_1_SI_X14(4*8); \ - VPINSRQ_1_SI_X15(10*8) - -// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5) -#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \ - VMOVDQU 12*8(SI), X12; \ - MOVQ 1*8(SI), X13; \ - MOVQ 2*8(SI), X14; \ - VPINSRQ_1_SI_X13(10*8); \ - VPINSRQ_1_SI_X14(7*8); \ - VMOVDQU 4*8(SI), X15 - -// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0) -#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \ - MOVQ 15*8(SI), X12; \ - MOVQ 3*8(SI), X13; \ - MOVQ 11*8(SI), X14; \ - MOVQ 12*8(SI), X15; \ - VPINSRQ_1_SI_X12(9*8); \ - VPINSRQ_1_SI_X13(13*8); \ - VPINSRQ_1_SI_X14(14*8); \ - VPINSRQ_1_SI_X15_0 +DATA ·AVX2_iv1<>+0(SB)/8, $0x510e527fade682d1 +DATA ·AVX2_iv1<>+8(SB)/8, $0x9b05688c2b3e6c1f +DATA ·AVX2_iv1<>+16(SB)/8, $0x1f83d9abfb41bd6b +DATA ·AVX2_iv1<>+24(SB)/8, $0x5be0cd19137e2179 +GLOBL ·AVX2_iv1<>(SB), RODATA|NOPTR, $32 // func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) -TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment - MOVQ h+0(FP), AX - MOVQ c+8(FP), BX - MOVQ flag+16(FP), CX - MOVQ blocks_base+24(FP), SI - MOVQ blocks_len+32(FP), DI - - MOVQ SP, R10 - ADDQ $15, R10 - ANDQ $~15, R10 - - VMOVDQU ·AVX_c40<>(SB), X0 - VMOVDQU ·AVX_c48<>(SB), X1 +// Requires: AVX, SSE2 +TEXT ·hashBlocksAVX(SB), NOSPLIT, $288-48 + MOVQ h+0(FP), AX + MOVQ c+8(FP), BX + MOVQ flag+16(FP), CX + MOVQ blocks_base+24(FP), SI + MOVQ blocks_len+32(FP), DI + MOVQ SP, R10 + ADDQ $0x0f, R10 + ANDQ $-16, R10 + VMOVDQU ·AVX_c40<>+0(SB), X0 + VMOVDQU ·AVX_c48<>+0(SB), X1 VMOVDQA X0, X8 VMOVDQA X1, X9 - - VMOVDQU ·AVX_iv3<>(SB), X0 - VMOVDQA X0, 0(R10) - XORQ CX, 0(R10) // 0(R10) = ·AVX_iv3 ^ (CX || 0) - - VMOVDQU 0(AX), X10 + VMOVDQU ·AVX_iv3<>+0(SB), X0 + VMOVDQA X0, (R10) + XORQ CX, (R10) + VMOVDQU (AX), X10 VMOVDQU 16(AX), X11 VMOVDQU 32(AX), X2 VMOVDQU 48(AX), X3 - - MOVQ 0(BX), R8 - MOVQ 8(BX), R9 + MOVQ (BX), R8 + MOVQ 8(BX), R9 loop: - ADDQ $128, R8 - CMPQ R8, $128 + ADDQ $0x80, R8 + CMPQ R8, $0x80 JGE noinc INCQ R9 noinc: - VMOVQ_R8_X15 - VPINSRQ_1_R9_X15 - + BYTE $0xc4 + BYTE $0x41 + BYTE $0xf9 + BYTE $0x6e + BYTE $0xf8 + BYTE $0xc4 + BYTE $0x43 + BYTE $0x81 + BYTE $0x22 + BYTE $0xf9 + BYTE $0x01 VMOVDQA X10, X0 VMOVDQA X11, X1 - VMOVDQU ·AVX_iv0<>(SB), X4 - VMOVDQU ·AVX_iv1<>(SB), X5 - VMOVDQU ·AVX_iv2<>(SB), X6 - + VMOVDQU ·AVX_iv0<>+0(SB), X4 + VMOVDQU ·AVX_iv1<>+0(SB), X5 + VMOVDQU ·AVX_iv2<>+0(SB), X6 VPXOR X15, X6, X6 - VMOVDQA 0(R10), X7 - - LOAD_MSG_AVX_0_2_4_6_1_3_5_7() + VMOVDQA (R10), X7 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x26 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x20 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x08 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x28 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x10 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x30 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x38 + BYTE $0x01 VMOVDQA X12, 16(R10) VMOVDQA X13, 32(R10) VMOVDQA X14, 48(R10) VMOVDQA X15, 64(R10) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15) + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x40 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x68 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x58 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x78 + BYTE $0x01 VMOVDQA X12, 80(R10) VMOVDQA X13, 96(R10) VMOVDQA X14, 112(R10) VMOVDQA X15, 128(R10) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6) + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x50 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x78 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x20 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x68 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x40 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x30 + BYTE $0x01 VMOVDQA X12, 144(R10) VMOVDQA X13, 160(R10) VMOVDQA X14, 176(R10) VMOVDQA X15, 192(R10) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX_1_0_11_5_12_2_7_3() + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + VPSHUFD $0x4e, (SI), X12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x58 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x38 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x10 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x18 + BYTE $0x01 VMOVDQA X12, 208(R10) VMOVDQA X13, 224(R10) VMOVDQA X14, 240(R10) VMOVDQA X15, 256(R10) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX_11_12_5_15_8_0_2_13() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX_2_5_4_15_6_10_0_8() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX_9_5_2_10_0_7_4_15() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX_2_6_0_8_12_10_11_3() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX_0_6_9_8_7_3_2_11() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX_5_15_8_2_0_4_6_10() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX_6_14_11_0_15_9_3_8() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX_12_13_1_10_2_7_4_5() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5) - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX() - LOAD_MSG_AVX_15_9_3_13_11_14_12_0() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) - SHUFFLE_AVX_INV() - - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(R10), 32(R10), 48(R10), 64(R10), X15, X8, X9) - SHUFFLE_AVX() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(R10), 96(R10), 112(R10), 128(R10), X15, X8, X9) - SHUFFLE_AVX_INV() - - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(R10), 160(R10), 176(R10), 192(R10), X15, X8, X9) - SHUFFLE_AVX() - HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(R10), 224(R10), 240(R10), 256(R10), X15, X8, X9) - SHUFFLE_AVX_INV() - + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + VMOVDQU 88(SI), X12 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x28 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x40 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x10 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x78 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x36 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x68 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x50 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x38 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x08 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x48 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x30 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x20 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x38 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x68 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x60 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x58 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x08 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x70 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x10 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x20 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x30 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x3e + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x78 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x40 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x48 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x10 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x36 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x20 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x38 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x78 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x30 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x08 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x40 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x58 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x60 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x68 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x10 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x2e + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x58 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x30 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x40 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x18 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x20 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x78 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x68 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x70 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x38 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x08 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x28 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x48 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x70 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x28 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x68 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x08 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x20 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x78 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x50 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + MOVQ (SI), X12 + VPSHUFD $0x4e, 64(SI), X13 + MOVQ 56(SI), X14 + MOVQ 16(SI), X15 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x30 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x58 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x68 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x60 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x58 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x08 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x38 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x18 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x48 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + MOVQ 40(SI), X12 + MOVQ 64(SI), X13 + MOVQ (SI), X14 + MOVQ 48(SI), X15 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x78 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x10 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x20 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x50 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + MOVQ 48(SI), X12 + MOVQ 88(SI), X13 + MOVQ 120(SI), X14 + MOVQ 24(SI), X15 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x2e + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x48 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x40 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + VMOVDQU 96(SI), X12 + MOVQ 8(SI), X13 + MOVQ 16(SI), X14 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x50 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x38 + BYTE $0x01 + VMOVDQU 32(SI), X15 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x66 + BYTE $0x50 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x6e + BYTE $0x38 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x76 + BYTE $0x10 + BYTE $0xc5 + BYTE $0x7a + BYTE $0x7e + BYTE $0x7e + BYTE $0x30 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x40 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x08 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x20 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x7e + BYTE $0x28 + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + MOVQ 120(SI), X12 + MOVQ 24(SI), X13 + MOVQ 88(SI), X14 + MOVQ 96(SI), X15 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x99 + BYTE $0x22 + BYTE $0x66 + BYTE $0x48 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x91 + BYTE $0x22 + BYTE $0x6e + BYTE $0x68 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x89 + BYTE $0x22 + BYTE $0x76 + BYTE $0x70 + BYTE $0x01 + BYTE $0xc4 + BYTE $0x63 + BYTE $0x81 + BYTE $0x22 + BYTE $0x3e + BYTE $0x01 + VPADDQ X12, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X13, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ X14, X0, X0 + VPADDQ X2, X0, X0 + VPADDQ X15, X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + VPADDQ 16(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 32(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ 48(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 64(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + VPADDQ 80(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 96(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ 112(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 128(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff + VPADDQ 144(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 160(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ 176(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 192(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X6, X13 + VMOVDQA X2, X14 + VMOVDQA X4, X6 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x11 + BYTE $0x6c + BYTE $0xfd + VMOVDQA X5, X4 + VMOVDQA X6, X5 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xff + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x69 + BYTE $0x6d + BYTE $0xd7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xdf + VPADDQ 208(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 224(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFD $-79, X6, X6 + VPSHUFD $-79, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPSHUFB X8, X2, X2 + VPSHUFB X8, X3, X3 + VPADDQ 240(R10), X0, X0 + VPADDQ X2, X0, X0 + VPADDQ 256(R10), X1, X1 + VPADDQ X3, X1, X1 + VPXOR X0, X6, X6 + VPXOR X1, X7, X7 + VPSHUFB X9, X6, X6 + VPSHUFB X9, X7, X7 + VPADDQ X6, X4, X4 + VPADDQ X7, X5, X5 + VPXOR X4, X2, X2 + VPXOR X5, X3, X3 + VPADDQ X2, X2, X15 + VPSRLQ $0x3f, X2, X2 + VPXOR X15, X2, X2 + VPADDQ X3, X3, X15 + VPSRLQ $0x3f, X3, X3 + VPXOR X15, X3, X3 + VMOVDQA X2, X13 + VMOVDQA X4, X14 + BYTE $0xc5 + BYTE $0x69 + BYTE $0x6c + BYTE $0xfa + VMOVDQA X5, X4 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x61 + BYTE $0x6d + BYTE $0xd7 + VMOVDQA X14, X5 + BYTE $0xc5 + BYTE $0x61 + BYTE $0x6c + BYTE $0xfb + VMOVDQA X6, X14 + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x11 + BYTE $0x6d + BYTE $0xdf + BYTE $0xc5 + BYTE $0x41 + BYTE $0x6c + BYTE $0xff + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x49 + BYTE $0x6d + BYTE $0xf7 + BYTE $0xc4 + BYTE $0x41 + BYTE $0x09 + BYTE $0x6c + BYTE $0xfe + BYTE $0xc4 + BYTE $0xc1 + BYTE $0x41 + BYTE $0x6d + BYTE $0xff VMOVDQU 32(AX), X14 VMOVDQU 48(AX), X15 VPXOR X0, X10, X10 @@ -729,16 +4524,36 @@ noinc: VPXOR X7, X15, X3 VMOVDQU X2, 32(AX) VMOVDQU X3, 48(AX) + LEAQ 128(SI), SI + SUBQ $0x80, DI + JNE loop + VMOVDQU X10, (AX) + VMOVDQU X11, 16(AX) + MOVQ R8, (BX) + MOVQ R9, 8(BX) + VZEROUPPER + RET - LEAQ 128(SI), SI - SUBQ $128, DI - JNE loop +DATA ·AVX_c40<>+0(SB)/8, $0x0201000706050403 +DATA ·AVX_c40<>+8(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·AVX_c40<>(SB), RODATA|NOPTR, $16 - VMOVDQU X10, 0(AX) - VMOVDQU X11, 16(AX) +DATA ·AVX_c48<>+0(SB)/8, $0x0100070605040302 +DATA ·AVX_c48<>+8(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·AVX_c48<>(SB), RODATA|NOPTR, $16 - MOVQ R8, 0(BX) - MOVQ R9, 8(BX) - VZEROUPPER +DATA ·AVX_iv3<>+0(SB)/8, $0x1f83d9abfb41bd6b +DATA ·AVX_iv3<>+8(SB)/8, $0x5be0cd19137e2179 +GLOBL ·AVX_iv3<>(SB), RODATA|NOPTR, $16 - RET +DATA ·AVX_iv0<>+0(SB)/8, $0x6a09e667f3bcc908 +DATA ·AVX_iv0<>+8(SB)/8, $0xbb67ae8584caa73b +GLOBL ·AVX_iv0<>(SB), RODATA|NOPTR, $16 + +DATA ·AVX_iv1<>+0(SB)/8, $0x3c6ef372fe94f82b +DATA ·AVX_iv1<>+8(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·AVX_iv1<>(SB), RODATA|NOPTR, $16 + +DATA ·AVX_iv2<>+0(SB)/8, $0x510e527fade682d1 +DATA ·AVX_iv2<>+8(SB)/8, $0x9b05688c2b3e6c1f +GLOBL ·AVX_iv2<>(SB), RODATA|NOPTR, $16 diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s index adfac00c15..9a0ce21244 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s @@ -1,278 +1,1441 @@ -// Copyright 2016 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. +// Code generated by command: go run blake2b_amd64_asm.go -out ../../blake2b_amd64.s -pkg blake2b. DO NOT EDIT. //go:build amd64 && gc && !purego #include "textflag.h" -DATA ·iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 -DATA ·iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b -GLOBL ·iv0<>(SB), (NOPTR+RODATA), $16 - -DATA ·iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b -DATA ·iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1 -GLOBL ·iv1<>(SB), (NOPTR+RODATA), $16 - -DATA ·iv2<>+0x00(SB)/8, $0x510e527fade682d1 -DATA ·iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f -GLOBL ·iv2<>(SB), (NOPTR+RODATA), $16 - -DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b -DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179 -GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16 - -DATA ·c40<>+0x00(SB)/8, $0x0201000706050403 -DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b -GLOBL ·c40<>(SB), (NOPTR+RODATA), $16 - -DATA ·c48<>+0x00(SB)/8, $0x0100070605040302 -DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a -GLOBL ·c48<>(SB), (NOPTR+RODATA), $16 - -#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \ - MOVO v4, t1; \ - MOVO v5, v4; \ - MOVO t1, v5; \ - MOVO v6, t1; \ - PUNPCKLQDQ v6, t2; \ - PUNPCKHQDQ v7, v6; \ - PUNPCKHQDQ t2, v6; \ - PUNPCKLQDQ v7, t2; \ - MOVO t1, v7; \ - MOVO v2, t1; \ - PUNPCKHQDQ t2, v7; \ - PUNPCKLQDQ v3, t2; \ - PUNPCKHQDQ t2, v2; \ - PUNPCKLQDQ t1, t2; \ - PUNPCKHQDQ t2, v3 - -#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \ - MOVO v4, t1; \ - MOVO v5, v4; \ - MOVO t1, v5; \ - MOVO v2, t1; \ - PUNPCKLQDQ v2, t2; \ - PUNPCKHQDQ v3, v2; \ - PUNPCKHQDQ t2, v2; \ - PUNPCKLQDQ v3, t2; \ - MOVO t1, v3; \ - MOVO v6, t1; \ - PUNPCKHQDQ t2, v3; \ - PUNPCKLQDQ v7, t2; \ - PUNPCKHQDQ t2, v6; \ - PUNPCKLQDQ t1, t2; \ - PUNPCKHQDQ t2, v7 - -#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ - PADDQ m0, v0; \ - PADDQ m1, v1; \ - PADDQ v2, v0; \ - PADDQ v3, v1; \ - PXOR v0, v6; \ - PXOR v1, v7; \ - PSHUFD $0xB1, v6, v6; \ - PSHUFD $0xB1, v7, v7; \ - PADDQ v6, v4; \ - PADDQ v7, v5; \ - PXOR v4, v2; \ - PXOR v5, v3; \ - PSHUFB c40, v2; \ - PSHUFB c40, v3; \ - PADDQ m2, v0; \ - PADDQ m3, v1; \ - PADDQ v2, v0; \ - PADDQ v3, v1; \ - PXOR v0, v6; \ - PXOR v1, v7; \ - PSHUFB c48, v6; \ - PSHUFB c48, v7; \ - PADDQ v6, v4; \ - PADDQ v7, v5; \ - PXOR v4, v2; \ - PXOR v5, v3; \ - MOVOU v2, t0; \ - PADDQ v2, t0; \ - PSRLQ $63, v2; \ - PXOR t0, v2; \ - MOVOU v3, t0; \ - PADDQ v3, t0; \ - PSRLQ $63, v3; \ - PXOR t0, v3 - -#define LOAD_MSG(m0, m1, m2, m3, src, i0, i1, i2, i3, i4, i5, i6, i7) \ - MOVQ i0*8(src), m0; \ - PINSRQ $1, i1*8(src), m0; \ - MOVQ i2*8(src), m1; \ - PINSRQ $1, i3*8(src), m1; \ - MOVQ i4*8(src), m2; \ - PINSRQ $1, i5*8(src), m2; \ - MOVQ i6*8(src), m3; \ - PINSRQ $1, i7*8(src), m3 - // func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) -TEXT ·hashBlocksSSE4(SB), 4, $288-48 // frame size = 272 + 16 byte alignment - MOVQ h+0(FP), AX - MOVQ c+8(FP), BX - MOVQ flag+16(FP), CX - MOVQ blocks_base+24(FP), SI - MOVQ blocks_len+32(FP), DI - - MOVQ SP, R10 - ADDQ $15, R10 - ANDQ $~15, R10 - - MOVOU ·iv3<>(SB), X0 - MOVO X0, 0(R10) - XORQ CX, 0(R10) // 0(R10) = ·iv3 ^ (CX || 0) - - MOVOU ·c40<>(SB), X13 - MOVOU ·c48<>(SB), X14 - - MOVOU 0(AX), X12 +// Requires: SSE2, SSE4.1, SSSE3 +TEXT ·hashBlocksSSE4(SB), NOSPLIT, $288-48 + MOVQ h+0(FP), AX + MOVQ c+8(FP), BX + MOVQ flag+16(FP), CX + MOVQ blocks_base+24(FP), SI + MOVQ blocks_len+32(FP), DI + MOVQ SP, R10 + ADDQ $0x0f, R10 + ANDQ $-16, R10 + MOVOU ·iv3<>+0(SB), X0 + MOVO X0, (R10) + XORQ CX, (R10) + MOVOU ·c40<>+0(SB), X13 + MOVOU ·c48<>+0(SB), X14 + MOVOU (AX), X12 MOVOU 16(AX), X15 - - MOVQ 0(BX), R8 - MOVQ 8(BX), R9 + MOVQ (BX), R8 + MOVQ 8(BX), R9 loop: - ADDQ $128, R8 - CMPQ R8, $128 + ADDQ $0x80, R8 + CMPQ R8, $0x80 JGE noinc INCQ R9 noinc: - MOVQ R8, X8 - PINSRQ $1, R9, X8 - - MOVO X12, X0 - MOVO X15, X1 - MOVOU 32(AX), X2 - MOVOU 48(AX), X3 - MOVOU ·iv0<>(SB), X4 - MOVOU ·iv1<>(SB), X5 - MOVOU ·iv2<>(SB), X6 - - PXOR X8, X6 - MOVO 0(R10), X7 - - LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7) - MOVO X8, 16(R10) - MOVO X9, 32(R10) - MOVO X10, 48(R10) - MOVO X11, 64(R10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15) - MOVO X8, 80(R10) - MOVO X9, 96(R10) - MOVO X10, 112(R10) - MOVO X11, 128(R10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6) - MOVO X8, 144(R10) - MOVO X9, 160(R10) - MOVO X10, 176(R10) - MOVO X11, 192(R10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3) - MOVO X8, 208(R10) - MOVO X9, 224(R10) - MOVO X10, 240(R10) - MOVO X11, 256(R10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) - - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 16(R10), 32(R10), 48(R10), 64(R10), X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 80(R10), 96(R10), 112(R10), 128(R10), X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + MOVQ R8, X8 + PINSRQ $0x01, R9, X8 + MOVO X12, X0 + MOVO X15, X1 + MOVOU 32(AX), X2 + MOVOU 48(AX), X3 + MOVOU ·iv0<>+0(SB), X4 + MOVOU ·iv1<>+0(SB), X5 + MOVOU ·iv2<>+0(SB), X6 + PXOR X8, X6 + MOVO (R10), X7 + MOVQ (SI), X8 + PINSRQ $0x01, 16(SI), X8 + MOVQ 32(SI), X9 + PINSRQ $0x01, 48(SI), X9 + MOVQ 8(SI), X10 + PINSRQ $0x01, 24(SI), X10 + MOVQ 40(SI), X11 + PINSRQ $0x01, 56(SI), X11 + MOVO X8, 16(R10) + MOVO X9, 32(R10) + MOVO X10, 48(R10) + MOVO X11, 64(R10) + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 64(SI), X8 + PINSRQ $0x01, 80(SI), X8 + MOVQ 96(SI), X9 + PINSRQ $0x01, 112(SI), X9 + MOVQ 72(SI), X10 + PINSRQ $0x01, 88(SI), X10 + MOVQ 104(SI), X11 + PINSRQ $0x01, 120(SI), X11 + MOVO X8, 80(R10) + MOVO X9, 96(R10) + MOVO X10, 112(R10) + MOVO X11, 128(R10) + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 112(SI), X8 + PINSRQ $0x01, 32(SI), X8 + MOVQ 72(SI), X9 + PINSRQ $0x01, 104(SI), X9 + MOVQ 80(SI), X10 + PINSRQ $0x01, 64(SI), X10 + MOVQ 120(SI), X11 + PINSRQ $0x01, 48(SI), X11 + MOVO X8, 144(R10) + MOVO X9, 160(R10) + MOVO X10, 176(R10) + MOVO X11, 192(R10) + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 8(SI), X8 + PINSRQ $0x01, (SI), X8 + MOVQ 88(SI), X9 + PINSRQ $0x01, 40(SI), X9 + MOVQ 96(SI), X10 + PINSRQ $0x01, 16(SI), X10 + MOVQ 56(SI), X11 + PINSRQ $0x01, 24(SI), X11 + MOVO X8, 208(R10) + MOVO X9, 224(R10) + MOVO X10, 240(R10) + MOVO X11, 256(R10) + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 88(SI), X8 + PINSRQ $0x01, 96(SI), X8 + MOVQ 40(SI), X9 + PINSRQ $0x01, 120(SI), X9 + MOVQ 64(SI), X10 + PINSRQ $0x01, (SI), X10 + MOVQ 16(SI), X11 + PINSRQ $0x01, 104(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 80(SI), X8 + PINSRQ $0x01, 24(SI), X8 + MOVQ 56(SI), X9 + PINSRQ $0x01, 72(SI), X9 + MOVQ 112(SI), X10 + PINSRQ $0x01, 48(SI), X10 + MOVQ 8(SI), X11 + PINSRQ $0x01, 32(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 56(SI), X8 + PINSRQ $0x01, 24(SI), X8 + MOVQ 104(SI), X9 + PINSRQ $0x01, 88(SI), X9 + MOVQ 72(SI), X10 + PINSRQ $0x01, 8(SI), X10 + MOVQ 96(SI), X11 + PINSRQ $0x01, 112(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 16(SI), X8 + PINSRQ $0x01, 40(SI), X8 + MOVQ 32(SI), X9 + PINSRQ $0x01, 120(SI), X9 + MOVQ 48(SI), X10 + PINSRQ $0x01, 80(SI), X10 + MOVQ (SI), X11 + PINSRQ $0x01, 64(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 72(SI), X8 + PINSRQ $0x01, 40(SI), X8 + MOVQ 16(SI), X9 + PINSRQ $0x01, 80(SI), X9 + MOVQ (SI), X10 + PINSRQ $0x01, 56(SI), X10 + MOVQ 32(SI), X11 + PINSRQ $0x01, 120(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 112(SI), X8 + PINSRQ $0x01, 88(SI), X8 + MOVQ 48(SI), X9 + PINSRQ $0x01, 24(SI), X9 + MOVQ 8(SI), X10 + PINSRQ $0x01, 96(SI), X10 + MOVQ 64(SI), X11 + PINSRQ $0x01, 104(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 16(SI), X8 + PINSRQ $0x01, 48(SI), X8 + MOVQ (SI), X9 + PINSRQ $0x01, 64(SI), X9 + MOVQ 96(SI), X10 + PINSRQ $0x01, 80(SI), X10 + MOVQ 88(SI), X11 + PINSRQ $0x01, 24(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 32(SI), X8 + PINSRQ $0x01, 56(SI), X8 + MOVQ 120(SI), X9 + PINSRQ $0x01, 8(SI), X9 + MOVQ 104(SI), X10 + PINSRQ $0x01, 40(SI), X10 + MOVQ 112(SI), X11 + PINSRQ $0x01, 72(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 96(SI), X8 + PINSRQ $0x01, 8(SI), X8 + MOVQ 112(SI), X9 + PINSRQ $0x01, 32(SI), X9 + MOVQ 40(SI), X10 + PINSRQ $0x01, 120(SI), X10 + MOVQ 104(SI), X11 + PINSRQ $0x01, 80(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ (SI), X8 + PINSRQ $0x01, 48(SI), X8 + MOVQ 72(SI), X9 + PINSRQ $0x01, 64(SI), X9 + MOVQ 56(SI), X10 + PINSRQ $0x01, 24(SI), X10 + MOVQ 16(SI), X11 + PINSRQ $0x01, 88(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 104(SI), X8 + PINSRQ $0x01, 56(SI), X8 + MOVQ 96(SI), X9 + PINSRQ $0x01, 24(SI), X9 + MOVQ 88(SI), X10 + PINSRQ $0x01, 112(SI), X10 + MOVQ 8(SI), X11 + PINSRQ $0x01, 72(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 40(SI), X8 + PINSRQ $0x01, 120(SI), X8 + MOVQ 64(SI), X9 + PINSRQ $0x01, 16(SI), X9 + MOVQ (SI), X10 + PINSRQ $0x01, 32(SI), X10 + MOVQ 48(SI), X11 + PINSRQ $0x01, 80(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 48(SI), X8 + PINSRQ $0x01, 112(SI), X8 + MOVQ 88(SI), X9 + PINSRQ $0x01, (SI), X9 + MOVQ 120(SI), X10 + PINSRQ $0x01, 72(SI), X10 + MOVQ 24(SI), X11 + PINSRQ $0x01, 64(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 96(SI), X8 + PINSRQ $0x01, 104(SI), X8 + MOVQ 8(SI), X9 + PINSRQ $0x01, 80(SI), X9 + MOVQ 16(SI), X10 + PINSRQ $0x01, 56(SI), X10 + MOVQ 32(SI), X11 + PINSRQ $0x01, 40(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVQ 80(SI), X8 + PINSRQ $0x01, 64(SI), X8 + MOVQ 56(SI), X9 + PINSRQ $0x01, 8(SI), X9 + MOVQ 16(SI), X10 + PINSRQ $0x01, 32(SI), X10 + MOVQ 48(SI), X11 + PINSRQ $0x01, 40(SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + MOVQ 120(SI), X8 + PINSRQ $0x01, 72(SI), X8 + MOVQ 24(SI), X9 + PINSRQ $0x01, 104(SI), X9 + MOVQ 88(SI), X10 + PINSRQ $0x01, 112(SI), X10 + MOVQ 96(SI), X11 + PINSRQ $0x01, (SI), X11 + PADDQ X8, X0 + PADDQ X9, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ X10, X0 + PADDQ X11, X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + PADDQ 16(R10), X0 + PADDQ 32(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ 48(R10), X0 + PADDQ 64(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + PADDQ 80(R10), X0 + PADDQ 96(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ 112(R10), X0 + PADDQ 128(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + PADDQ 144(R10), X0 + PADDQ 160(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ 176(R10), X0 + PADDQ 192(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X6, X8 + PUNPCKLQDQ X6, X9 + PUNPCKHQDQ X7, X6 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X7, X9 + MOVO X8, X7 + MOVO X2, X8 + PUNPCKHQDQ X9, X7 + PUNPCKLQDQ X3, X9 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X3 + PADDQ 208(R10), X0 + PADDQ 224(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFD $0xb1, X6, X6 + PSHUFD $0xb1, X7, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + PSHUFB X13, X2 + PSHUFB X13, X3 + PADDQ 240(R10), X0 + PADDQ 256(R10), X1 + PADDQ X2, X0 + PADDQ X3, X1 + PXOR X0, X6 + PXOR X1, X7 + PSHUFB X14, X6 + PSHUFB X14, X7 + PADDQ X6, X4 + PADDQ X7, X5 + PXOR X4, X2 + PXOR X5, X3 + MOVOU X2, X11 + PADDQ X2, X11 + PSRLQ $0x3f, X2 + PXOR X11, X2 + MOVOU X3, X11 + PADDQ X3, X11 + PSRLQ $0x3f, X3 + PXOR X11, X3 + MOVO X4, X8 + MOVO X5, X4 + MOVO X8, X5 + MOVO X2, X8 + PUNPCKLQDQ X2, X9 + PUNPCKHQDQ X3, X2 + PUNPCKHQDQ X9, X2 + PUNPCKLQDQ X3, X9 + MOVO X8, X3 + MOVO X6, X8 + PUNPCKHQDQ X9, X3 + PUNPCKLQDQ X7, X9 + PUNPCKHQDQ X9, X6 + PUNPCKLQDQ X8, X9 + PUNPCKHQDQ X9, X7 + MOVOU 32(AX), X10 + MOVOU 48(AX), X11 + PXOR X0, X12 + PXOR X1, X15 + PXOR X2, X10 + PXOR X3, X11 + PXOR X4, X12 + PXOR X5, X15 + PXOR X6, X10 + PXOR X7, X11 + MOVOU X10, 32(AX) + MOVOU X11, 48(AX) + LEAQ 128(SI), SI + SUBQ $0x80, DI + JNE loop + MOVOU X12, (AX) + MOVOU X15, 16(AX) + MOVQ R8, (BX) + MOVQ R9, 8(BX) + RET - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 144(R10), 160(R10), 176(R10), 192(R10), X11, X13, X14) - SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) - HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 208(R10), 224(R10), 240(R10), 256(R10), X11, X13, X14) - SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) +DATA ·iv3<>+0(SB)/8, $0x1f83d9abfb41bd6b +DATA ·iv3<>+8(SB)/8, $0x5be0cd19137e2179 +GLOBL ·iv3<>(SB), RODATA|NOPTR, $16 - MOVOU 32(AX), X10 - MOVOU 48(AX), X11 - PXOR X0, X12 - PXOR X1, X15 - PXOR X2, X10 - PXOR X3, X11 - PXOR X4, X12 - PXOR X5, X15 - PXOR X6, X10 - PXOR X7, X11 - MOVOU X10, 32(AX) - MOVOU X11, 48(AX) +DATA ·c40<>+0(SB)/8, $0x0201000706050403 +DATA ·c40<>+8(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·c40<>(SB), RODATA|NOPTR, $16 - LEAQ 128(SI), SI - SUBQ $128, DI - JNE loop +DATA ·c48<>+0(SB)/8, $0x0100070605040302 +DATA ·c48<>+8(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·c48<>(SB), RODATA|NOPTR, $16 - MOVOU X12, 0(AX) - MOVOU X15, 16(AX) +DATA ·iv0<>+0(SB)/8, $0x6a09e667f3bcc908 +DATA ·iv0<>+8(SB)/8, $0xbb67ae8584caa73b +GLOBL ·iv0<>(SB), RODATA|NOPTR, $16 - MOVQ R8, 0(BX) - MOVQ R9, 8(BX) +DATA ·iv1<>+0(SB)/8, $0x3c6ef372fe94f82b +DATA ·iv1<>+8(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·iv1<>(SB), RODATA|NOPTR, $16 - RET +DATA ·iv2<>+0(SB)/8, $0x510e527fade682d1 +DATA ·iv2<>+8(SB)/8, $0x9b05688c2b3e6c1f +GLOBL ·iv2<>(SB), RODATA|NOPTR, $16 diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go index 213bf204af..0898956807 100644 --- a/vendor/golang.org/x/crypto/blowfish/cipher.go +++ b/vendor/golang.org/x/crypto/blowfish/cipher.go @@ -11,7 +11,7 @@ // Deprecated: any new system should use AES (from crypto/aes, if necessary in // an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from // golang.org/x/crypto/chacha20poly1305). -package blowfish // import "golang.org/x/crypto/blowfish" +package blowfish // The code is a port of Bruce Schneier's C implementation. // See https://www.schneier.com/blowfish.html. diff --git a/vendor/golang.org/x/crypto/cast5/cast5.go b/vendor/golang.org/x/crypto/cast5/cast5.go index 425e8eecb0..016e90215c 100644 --- a/vendor/golang.org/x/crypto/cast5/cast5.go +++ b/vendor/golang.org/x/crypto/cast5/cast5.go @@ -11,7 +11,7 @@ // Deprecated: any new system should use AES (from crypto/aes, if necessary in // an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from // golang.org/x/crypto/chacha20poly1305). -package cast5 // import "golang.org/x/crypto/cast5" +package cast5 import ( "errors" diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go index 00f963ea20..21ca3b2ee4 100644 --- a/vendor/golang.org/x/crypto/curve25519/curve25519.go +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -6,9 +6,11 @@ // performs scalar multiplication on the elliptic curve known as Curve25519. // See RFC 7748. // -// Starting in Go 1.20, this package is a wrapper for the X25519 implementation +// This package is a wrapper for the X25519 implementation // in the crypto/ecdh package. -package curve25519 // import "golang.org/x/crypto/curve25519" +package curve25519 + +import "crypto/ecdh" // ScalarMult sets dst to the product scalar * point. // @@ -16,7 +18,13 @@ package curve25519 // import "golang.org/x/crypto/curve25519" // zeroes, irrespective of the scalar. Instead, use the X25519 function, which // will return an error. func ScalarMult(dst, scalar, point *[32]byte) { - scalarMult(dst, scalar, point) + if _, err := x25519(dst, scalar[:], point[:]); err != nil { + // The only error condition for x25519 when the inputs are 32 bytes long + // is if the output would have been the all-zero value. + for i := range dst { + dst[i] = 0 + } + } } // ScalarBaseMult sets dst to the product scalar * base where base is the @@ -25,7 +33,12 @@ func ScalarMult(dst, scalar, point *[32]byte) { // It is recommended to use the X25519 function with Basepoint instead, as // copying into fixed size arrays can lead to unexpected bugs. func ScalarBaseMult(dst, scalar *[32]byte) { - scalarBaseMult(dst, scalar) + curve := ecdh.X25519() + priv, err := curve.NewPrivateKey(scalar[:]) + if err != nil { + panic("curve25519: internal error: scalarBaseMult was not 32 bytes") + } + copy(dst[:], priv.PublicKey().Bytes()) } const ( @@ -57,3 +70,21 @@ func X25519(scalar, point []byte) ([]byte, error) { var dst [32]byte return x25519(&dst, scalar, point) } + +func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { + curve := ecdh.X25519() + pub, err := curve.NewPublicKey(point) + if err != nil { + return nil, err + } + priv, err := curve.NewPrivateKey(scalar) + if err != nil { + return nil, err + } + out, err := priv.ECDH(pub) + if err != nil { + return nil, err + } + copy(dst[:], out) + return dst[:], nil +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_compat.go b/vendor/golang.org/x/crypto/curve25519/curve25519_compat.go deleted file mode 100644 index ba647e8d77..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_compat.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 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. - -//go:build !go1.20 - -package curve25519 - -import ( - "crypto/subtle" - "errors" - "strconv" - - "golang.org/x/crypto/curve25519/internal/field" -) - -func scalarMult(dst, scalar, point *[32]byte) { - var e [32]byte - - copy(e[:], scalar[:]) - e[0] &= 248 - e[31] &= 127 - e[31] |= 64 - - var x1, x2, z2, x3, z3, tmp0, tmp1 field.Element - x1.SetBytes(point[:]) - x2.One() - x3.Set(&x1) - z3.One() - - swap := 0 - for pos := 254; pos >= 0; pos-- { - b := e[pos/8] >> uint(pos&7) - b &= 1 - swap ^= int(b) - x2.Swap(&x3, swap) - z2.Swap(&z3, swap) - swap = int(b) - - tmp0.Subtract(&x3, &z3) - tmp1.Subtract(&x2, &z2) - x2.Add(&x2, &z2) - z2.Add(&x3, &z3) - z3.Multiply(&tmp0, &x2) - z2.Multiply(&z2, &tmp1) - tmp0.Square(&tmp1) - tmp1.Square(&x2) - x3.Add(&z3, &z2) - z2.Subtract(&z3, &z2) - x2.Multiply(&tmp1, &tmp0) - tmp1.Subtract(&tmp1, &tmp0) - z2.Square(&z2) - - z3.Mult32(&tmp1, 121666) - x3.Square(&x3) - tmp0.Add(&tmp0, &z3) - z3.Multiply(&x1, &z2) - z2.Multiply(&tmp1, &tmp0) - } - - x2.Swap(&x3, swap) - z2.Swap(&z3, swap) - - z2.Invert(&z2) - x2.Multiply(&x2, &z2) - copy(dst[:], x2.Bytes()) -} - -func scalarBaseMult(dst, scalar *[32]byte) { - checkBasepoint() - scalarMult(dst, scalar, &basePoint) -} - -func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { - var in [32]byte - if l := len(scalar); l != 32 { - return nil, errors.New("bad scalar length: " + strconv.Itoa(l) + ", expected 32") - } - if l := len(point); l != 32 { - return nil, errors.New("bad point length: " + strconv.Itoa(l) + ", expected 32") - } - copy(in[:], scalar) - if &point[0] == &Basepoint[0] { - scalarBaseMult(dst, &in) - } else { - var base, zero [32]byte - copy(base[:], point) - scalarMult(dst, &in, &base) - if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 { - return nil, errors.New("bad input point: low order point") - } - } - return dst[:], nil -} - -func checkBasepoint() { - if subtle.ConstantTimeCompare(Basepoint, []byte{ - 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }) != 1 { - panic("curve25519: global Basepoint value was modified") - } -} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_go120.go b/vendor/golang.org/x/crypto/curve25519/curve25519_go120.go deleted file mode 100644 index 627df49727..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/curve25519_go120.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.20 - -package curve25519 - -import "crypto/ecdh" - -func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { - curve := ecdh.X25519() - pub, err := curve.NewPublicKey(point) - if err != nil { - return nil, err - } - priv, err := curve.NewPrivateKey(scalar) - if err != nil { - return nil, err - } - out, err := priv.ECDH(pub) - if err != nil { - return nil, err - } - copy(dst[:], out) - return dst[:], nil -} - -func scalarMult(dst, scalar, point *[32]byte) { - if _, err := x25519(dst, scalar[:], point[:]); err != nil { - // The only error condition for x25519 when the inputs are 32 bytes long - // is if the output would have been the all-zero value. - for i := range dst { - dst[i] = 0 - } - } -} - -func scalarBaseMult(dst, scalar *[32]byte) { - curve := ecdh.X25519() - priv, err := curve.NewPrivateKey(scalar[:]) - if err != nil { - panic("curve25519: internal error: scalarBaseMult was not 32 bytes") - } - copy(dst[:], priv.PublicKey().Bytes()) -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/README b/vendor/golang.org/x/crypto/curve25519/internal/field/README deleted file mode 100644 index e25bca7dc8..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/README +++ /dev/null @@ -1,7 +0,0 @@ -This package is kept in sync with crypto/ed25519/internal/edwards25519/field in -the standard library. - -If there are any changes in the standard library that need to be synced to this -package, run sync.sh. It will not overwrite any local changes made since the -previous sync, so it's ok to land changes in this package first, and then sync -to the standard library later. diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go deleted file mode 100644 index ca841ad99e..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe.go +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright (c) 2017 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 field implements fast arithmetic modulo 2^255-19. -package field - -import ( - "crypto/subtle" - "encoding/binary" - "math/bits" -) - -// Element represents an element of the field GF(2^255-19). Note that this -// is not a cryptographically secure group, and should only be used to interact -// with edwards25519.Point coordinates. -// -// This type works similarly to math/big.Int, and all arguments and receivers -// are allowed to alias. -// -// The zero value is a valid zero element. -type Element struct { - // An element t represents the integer - // t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204 - // - // Between operations, all limbs are expected to be lower than 2^52. - l0 uint64 - l1 uint64 - l2 uint64 - l3 uint64 - l4 uint64 -} - -const maskLow51Bits uint64 = (1 << 51) - 1 - -var feZero = &Element{0, 0, 0, 0, 0} - -// Zero sets v = 0, and returns v. -func (v *Element) Zero() *Element { - *v = *feZero - return v -} - -var feOne = &Element{1, 0, 0, 0, 0} - -// One sets v = 1, and returns v. -func (v *Element) One() *Element { - *v = *feOne - return v -} - -// reduce reduces v modulo 2^255 - 19 and returns it. -func (v *Element) reduce() *Element { - v.carryPropagate() - - // After the light reduction we now have a field element representation - // v < 2^255 + 2^13 * 19, but need v < 2^255 - 19. - - // If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1, - // generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise. - c := (v.l0 + 19) >> 51 - c = (v.l1 + c) >> 51 - c = (v.l2 + c) >> 51 - c = (v.l3 + c) >> 51 - c = (v.l4 + c) >> 51 - - // If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's - // effectively applying the reduction identity to the carry. - v.l0 += 19 * c - - v.l1 += v.l0 >> 51 - v.l0 = v.l0 & maskLow51Bits - v.l2 += v.l1 >> 51 - v.l1 = v.l1 & maskLow51Bits - v.l3 += v.l2 >> 51 - v.l2 = v.l2 & maskLow51Bits - v.l4 += v.l3 >> 51 - v.l3 = v.l3 & maskLow51Bits - // no additional carry - v.l4 = v.l4 & maskLow51Bits - - return v -} - -// Add sets v = a + b, and returns v. -func (v *Element) Add(a, b *Element) *Element { - v.l0 = a.l0 + b.l0 - v.l1 = a.l1 + b.l1 - v.l2 = a.l2 + b.l2 - v.l3 = a.l3 + b.l3 - v.l4 = a.l4 + b.l4 - // Using the generic implementation here is actually faster than the - // assembly. Probably because the body of this function is so simple that - // the compiler can figure out better optimizations by inlining the carry - // propagation. TODO - return v.carryPropagateGeneric() -} - -// Subtract sets v = a - b, and returns v. -func (v *Element) Subtract(a, b *Element) *Element { - // We first add 2 * p, to guarantee the subtraction won't underflow, and - // then subtract b (which can be up to 2^255 + 2^13 * 19). - v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0 - v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1 - v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2 - v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3 - v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4 - return v.carryPropagate() -} - -// Negate sets v = -a, and returns v. -func (v *Element) Negate(a *Element) *Element { - return v.Subtract(feZero, a) -} - -// Invert sets v = 1/z mod p, and returns v. -// -// If z == 0, Invert returns v = 0. -func (v *Element) Invert(z *Element) *Element { - // Inversion is implemented as exponentiation with exponent p − 2. It uses the - // same sequence of 255 squarings and 11 multiplications as [Curve25519]. - var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element - - z2.Square(z) // 2 - t.Square(&z2) // 4 - t.Square(&t) // 8 - z9.Multiply(&t, z) // 9 - z11.Multiply(&z9, &z2) // 11 - t.Square(&z11) // 22 - z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0 - - t.Square(&z2_5_0) // 2^6 - 2^1 - for i := 0; i < 4; i++ { - t.Square(&t) // 2^10 - 2^5 - } - z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0 - - t.Square(&z2_10_0) // 2^11 - 2^1 - for i := 0; i < 9; i++ { - t.Square(&t) // 2^20 - 2^10 - } - z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0 - - t.Square(&z2_20_0) // 2^21 - 2^1 - for i := 0; i < 19; i++ { - t.Square(&t) // 2^40 - 2^20 - } - t.Multiply(&t, &z2_20_0) // 2^40 - 2^0 - - t.Square(&t) // 2^41 - 2^1 - for i := 0; i < 9; i++ { - t.Square(&t) // 2^50 - 2^10 - } - z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0 - - t.Square(&z2_50_0) // 2^51 - 2^1 - for i := 0; i < 49; i++ { - t.Square(&t) // 2^100 - 2^50 - } - z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0 - - t.Square(&z2_100_0) // 2^101 - 2^1 - for i := 0; i < 99; i++ { - t.Square(&t) // 2^200 - 2^100 - } - t.Multiply(&t, &z2_100_0) // 2^200 - 2^0 - - t.Square(&t) // 2^201 - 2^1 - for i := 0; i < 49; i++ { - t.Square(&t) // 2^250 - 2^50 - } - t.Multiply(&t, &z2_50_0) // 2^250 - 2^0 - - t.Square(&t) // 2^251 - 2^1 - t.Square(&t) // 2^252 - 2^2 - t.Square(&t) // 2^253 - 2^3 - t.Square(&t) // 2^254 - 2^4 - t.Square(&t) // 2^255 - 2^5 - - return v.Multiply(&t, &z11) // 2^255 - 21 -} - -// Set sets v = a, and returns v. -func (v *Element) Set(a *Element) *Element { - *v = *a - return v -} - -// SetBytes sets v to x, which must be a 32-byte little-endian encoding. -// -// Consistent with RFC 7748, the most significant bit (the high bit of the -// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1) -// are accepted. Note that this is laxer than specified by RFC 8032. -func (v *Element) SetBytes(x []byte) *Element { - if len(x) != 32 { - panic("edwards25519: invalid field element input size") - } - - // Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51). - v.l0 = binary.LittleEndian.Uint64(x[0:8]) - v.l0 &= maskLow51Bits - // Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51). - v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3 - v.l1 &= maskLow51Bits - // Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51). - v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6 - v.l2 &= maskLow51Bits - // Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51). - v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1 - v.l3 &= maskLow51Bits - // Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51). - // Note: not bytes 25:33, shift 4, to avoid overread. - v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12 - v.l4 &= maskLow51Bits - - return v -} - -// Bytes returns the canonical 32-byte little-endian encoding of v. -func (v *Element) Bytes() []byte { - // This function is outlined to make the allocations inline in the caller - // rather than happen on the heap. - var out [32]byte - return v.bytes(&out) -} - -func (v *Element) bytes(out *[32]byte) []byte { - t := *v - t.reduce() - - var buf [8]byte - for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} { - bitsOffset := i * 51 - binary.LittleEndian.PutUint64(buf[:], l<= len(out) { - break - } - out[off] |= bb - } - } - - return out[:] -} - -// Equal returns 1 if v and u are equal, and 0 otherwise. -func (v *Element) Equal(u *Element) int { - sa, sv := u.Bytes(), v.Bytes() - return subtle.ConstantTimeCompare(sa, sv) -} - -// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise. -func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) } - -// Select sets v to a if cond == 1, and to b if cond == 0. -func (v *Element) Select(a, b *Element, cond int) *Element { - m := mask64Bits(cond) - v.l0 = (m & a.l0) | (^m & b.l0) - v.l1 = (m & a.l1) | (^m & b.l1) - v.l2 = (m & a.l2) | (^m & b.l2) - v.l3 = (m & a.l3) | (^m & b.l3) - v.l4 = (m & a.l4) | (^m & b.l4) - return v -} - -// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v. -func (v *Element) Swap(u *Element, cond int) { - m := mask64Bits(cond) - t := m & (v.l0 ^ u.l0) - v.l0 ^= t - u.l0 ^= t - t = m & (v.l1 ^ u.l1) - v.l1 ^= t - u.l1 ^= t - t = m & (v.l2 ^ u.l2) - v.l2 ^= t - u.l2 ^= t - t = m & (v.l3 ^ u.l3) - v.l3 ^= t - u.l3 ^= t - t = m & (v.l4 ^ u.l4) - v.l4 ^= t - u.l4 ^= t -} - -// IsNegative returns 1 if v is negative, and 0 otherwise. -func (v *Element) IsNegative() int { - return int(v.Bytes()[0] & 1) -} - -// Absolute sets v to |u|, and returns v. -func (v *Element) Absolute(u *Element) *Element { - return v.Select(new(Element).Negate(u), u, u.IsNegative()) -} - -// Multiply sets v = x * y, and returns v. -func (v *Element) Multiply(x, y *Element) *Element { - feMul(v, x, y) - return v -} - -// Square sets v = x * x, and returns v. -func (v *Element) Square(x *Element) *Element { - feSquare(v, x) - return v -} - -// Mult32 sets v = x * y, and returns v. -func (v *Element) Mult32(x *Element, y uint32) *Element { - x0lo, x0hi := mul51(x.l0, y) - x1lo, x1hi := mul51(x.l1, y) - x2lo, x2hi := mul51(x.l2, y) - x3lo, x3hi := mul51(x.l3, y) - x4lo, x4hi := mul51(x.l4, y) - v.l0 = x0lo + 19*x4hi // carried over per the reduction identity - v.l1 = x1lo + x0hi - v.l2 = x2lo + x1hi - v.l3 = x3lo + x2hi - v.l4 = x4lo + x3hi - // The hi portions are going to be only 32 bits, plus any previous excess, - // so we can skip the carry propagation. - return v -} - -// mul51 returns lo + hi * 2⁵¹ = a * b. -func mul51(a uint64, b uint32) (lo uint64, hi uint64) { - mh, ml := bits.Mul64(a, uint64(b)) - lo = ml & maskLow51Bits - hi = (mh << 13) | (ml >> 51) - return -} - -// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3. -func (v *Element) Pow22523(x *Element) *Element { - var t0, t1, t2 Element - - t0.Square(x) // x^2 - t1.Square(&t0) // x^4 - t1.Square(&t1) // x^8 - t1.Multiply(x, &t1) // x^9 - t0.Multiply(&t0, &t1) // x^11 - t0.Square(&t0) // x^22 - t0.Multiply(&t1, &t0) // x^31 - t1.Square(&t0) // x^62 - for i := 1; i < 5; i++ { // x^992 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1 - t1.Square(&t0) // 2^11 - 2 - for i := 1; i < 10; i++ { // 2^20 - 2^10 - t1.Square(&t1) - } - t1.Multiply(&t1, &t0) // 2^20 - 1 - t2.Square(&t1) // 2^21 - 2 - for i := 1; i < 20; i++ { // 2^40 - 2^20 - t2.Square(&t2) - } - t1.Multiply(&t2, &t1) // 2^40 - 1 - t1.Square(&t1) // 2^41 - 2 - for i := 1; i < 10; i++ { // 2^50 - 2^10 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // 2^50 - 1 - t1.Square(&t0) // 2^51 - 2 - for i := 1; i < 50; i++ { // 2^100 - 2^50 - t1.Square(&t1) - } - t1.Multiply(&t1, &t0) // 2^100 - 1 - t2.Square(&t1) // 2^101 - 2 - for i := 1; i < 100; i++ { // 2^200 - 2^100 - t2.Square(&t2) - } - t1.Multiply(&t2, &t1) // 2^200 - 1 - t1.Square(&t1) // 2^201 - 2 - for i := 1; i < 50; i++ { // 2^250 - 2^50 - t1.Square(&t1) - } - t0.Multiply(&t1, &t0) // 2^250 - 1 - t0.Square(&t0) // 2^251 - 2 - t0.Square(&t0) // 2^252 - 4 - return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3) -} - -// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion. -var sqrtM1 = &Element{1718705420411056, 234908883556509, - 2233514472574048, 2117202627021982, 765476049583133} - -// SqrtRatio sets r to the non-negative square root of the ratio of u and v. -// -// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio -// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00, -// and returns r and 0. -func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) { - var a, b Element - - // r = (u * v3) * (u * v7)^((p-5)/8) - v2 := a.Square(v) - uv3 := b.Multiply(u, b.Multiply(v2, v)) - uv7 := a.Multiply(uv3, a.Square(v2)) - r.Multiply(uv3, r.Pow22523(uv7)) - - check := a.Multiply(v, a.Square(r)) // check = v * r^2 - - uNeg := b.Negate(u) - correctSignSqrt := check.Equal(u) - flippedSignSqrt := check.Equal(uNeg) - flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1)) - - rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r - // r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r) - r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI) - - r.Absolute(r) // Choose the nonnegative square root. - return r, correctSignSqrt | flippedSignSqrt -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go deleted file mode 100644 index 70c541692c..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go +++ /dev/null @@ -1,15 +0,0 @@ -// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. - -//go:build amd64 && gc && !purego - -package field - -// feMul sets out = a * b. It works like feMulGeneric. -// -//go:noescape -func feMul(out *Element, a *Element, b *Element) - -// feSquare sets out = a * a. It works like feSquareGeneric. -// -//go:noescape -func feSquare(out *Element, a *Element) diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s deleted file mode 100644 index 60817acc41..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s +++ /dev/null @@ -1,378 +0,0 @@ -// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. - -//go:build amd64 && gc && !purego - -#include "textflag.h" - -// func feMul(out *Element, a *Element, b *Element) -TEXT ·feMul(SB), NOSPLIT, $0-24 - MOVQ a+8(FP), CX - MOVQ b+16(FP), BX - - // r0 = a0×b0 - MOVQ (CX), AX - MULQ (BX) - MOVQ AX, DI - MOVQ DX, SI - - // r0 += 19×a1×b4 - MOVQ 8(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r0 += 19×a2×b3 - MOVQ 16(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r0 += 19×a3×b2 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 16(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r0 += 19×a4×b1 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 8(BX) - ADDQ AX, DI - ADCQ DX, SI - - // r1 = a0×b1 - MOVQ (CX), AX - MULQ 8(BX) - MOVQ AX, R9 - MOVQ DX, R8 - - // r1 += a1×b0 - MOVQ 8(CX), AX - MULQ (BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r1 += 19×a2×b4 - MOVQ 16(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r1 += 19×a3×b3 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r1 += 19×a4×b2 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 16(BX) - ADDQ AX, R9 - ADCQ DX, R8 - - // r2 = a0×b2 - MOVQ (CX), AX - MULQ 16(BX) - MOVQ AX, R11 - MOVQ DX, R10 - - // r2 += a1×b1 - MOVQ 8(CX), AX - MULQ 8(BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r2 += a2×b0 - MOVQ 16(CX), AX - MULQ (BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r2 += 19×a3×b4 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r2 += 19×a4×b3 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(BX) - ADDQ AX, R11 - ADCQ DX, R10 - - // r3 = a0×b3 - MOVQ (CX), AX - MULQ 24(BX) - MOVQ AX, R13 - MOVQ DX, R12 - - // r3 += a1×b2 - MOVQ 8(CX), AX - MULQ 16(BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r3 += a2×b1 - MOVQ 16(CX), AX - MULQ 8(BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r3 += a3×b0 - MOVQ 24(CX), AX - MULQ (BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r3 += 19×a4×b4 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(BX) - ADDQ AX, R13 - ADCQ DX, R12 - - // r4 = a0×b4 - MOVQ (CX), AX - MULQ 32(BX) - MOVQ AX, R15 - MOVQ DX, R14 - - // r4 += a1×b3 - MOVQ 8(CX), AX - MULQ 24(BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // r4 += a2×b2 - MOVQ 16(CX), AX - MULQ 16(BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // r4 += a3×b1 - MOVQ 24(CX), AX - MULQ 8(BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // r4 += a4×b0 - MOVQ 32(CX), AX - MULQ (BX) - ADDQ AX, R15 - ADCQ DX, R14 - - // First reduction chain - MOVQ $0x0007ffffffffffff, AX - SHLQ $0x0d, DI, SI - SHLQ $0x0d, R9, R8 - SHLQ $0x0d, R11, R10 - SHLQ $0x0d, R13, R12 - SHLQ $0x0d, R15, R14 - ANDQ AX, DI - IMUL3Q $0x13, R14, R14 - ADDQ R14, DI - ANDQ AX, R9 - ADDQ SI, R9 - ANDQ AX, R11 - ADDQ R8, R11 - ANDQ AX, R13 - ADDQ R10, R13 - ANDQ AX, R15 - ADDQ R12, R15 - - // Second reduction chain (carryPropagate) - MOVQ DI, SI - SHRQ $0x33, SI - MOVQ R9, R8 - SHRQ $0x33, R8 - MOVQ R11, R10 - SHRQ $0x33, R10 - MOVQ R13, R12 - SHRQ $0x33, R12 - MOVQ R15, R14 - SHRQ $0x33, R14 - ANDQ AX, DI - IMUL3Q $0x13, R14, R14 - ADDQ R14, DI - ANDQ AX, R9 - ADDQ SI, R9 - ANDQ AX, R11 - ADDQ R8, R11 - ANDQ AX, R13 - ADDQ R10, R13 - ANDQ AX, R15 - ADDQ R12, R15 - - // Store output - MOVQ out+0(FP), AX - MOVQ DI, (AX) - MOVQ R9, 8(AX) - MOVQ R11, 16(AX) - MOVQ R13, 24(AX) - MOVQ R15, 32(AX) - RET - -// func feSquare(out *Element, a *Element) -TEXT ·feSquare(SB), NOSPLIT, $0-16 - MOVQ a+8(FP), CX - - // r0 = l0×l0 - MOVQ (CX), AX - MULQ (CX) - MOVQ AX, SI - MOVQ DX, BX - - // r0 += 38×l1×l4 - MOVQ 8(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 32(CX) - ADDQ AX, SI - ADCQ DX, BX - - // r0 += 38×l2×l3 - MOVQ 16(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 24(CX) - ADDQ AX, SI - ADCQ DX, BX - - // r1 = 2×l0×l1 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 8(CX) - MOVQ AX, R8 - MOVQ DX, DI - - // r1 += 38×l2×l4 - MOVQ 16(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 32(CX) - ADDQ AX, R8 - ADCQ DX, DI - - // r1 += 19×l3×l3 - MOVQ 24(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 24(CX) - ADDQ AX, R8 - ADCQ DX, DI - - // r2 = 2×l0×l2 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 16(CX) - MOVQ AX, R10 - MOVQ DX, R9 - - // r2 += l1×l1 - MOVQ 8(CX), AX - MULQ 8(CX) - ADDQ AX, R10 - ADCQ DX, R9 - - // r2 += 38×l3×l4 - MOVQ 24(CX), AX - IMUL3Q $0x26, AX, AX - MULQ 32(CX) - ADDQ AX, R10 - ADCQ DX, R9 - - // r3 = 2×l0×l3 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 24(CX) - MOVQ AX, R12 - MOVQ DX, R11 - - // r3 += 2×l1×l2 - MOVQ 8(CX), AX - IMUL3Q $0x02, AX, AX - MULQ 16(CX) - ADDQ AX, R12 - ADCQ DX, R11 - - // r3 += 19×l4×l4 - MOVQ 32(CX), AX - IMUL3Q $0x13, AX, AX - MULQ 32(CX) - ADDQ AX, R12 - ADCQ DX, R11 - - // r4 = 2×l0×l4 - MOVQ (CX), AX - SHLQ $0x01, AX - MULQ 32(CX) - MOVQ AX, R14 - MOVQ DX, R13 - - // r4 += 2×l1×l3 - MOVQ 8(CX), AX - IMUL3Q $0x02, AX, AX - MULQ 24(CX) - ADDQ AX, R14 - ADCQ DX, R13 - - // r4 += l2×l2 - MOVQ 16(CX), AX - MULQ 16(CX) - ADDQ AX, R14 - ADCQ DX, R13 - - // First reduction chain - MOVQ $0x0007ffffffffffff, AX - SHLQ $0x0d, SI, BX - SHLQ $0x0d, R8, DI - SHLQ $0x0d, R10, R9 - SHLQ $0x0d, R12, R11 - SHLQ $0x0d, R14, R13 - ANDQ AX, SI - IMUL3Q $0x13, R13, R13 - ADDQ R13, SI - ANDQ AX, R8 - ADDQ BX, R8 - ANDQ AX, R10 - ADDQ DI, R10 - ANDQ AX, R12 - ADDQ R9, R12 - ANDQ AX, R14 - ADDQ R11, R14 - - // Second reduction chain (carryPropagate) - MOVQ SI, BX - SHRQ $0x33, BX - MOVQ R8, DI - SHRQ $0x33, DI - MOVQ R10, R9 - SHRQ $0x33, R9 - MOVQ R12, R11 - SHRQ $0x33, R11 - MOVQ R14, R13 - SHRQ $0x33, R13 - ANDQ AX, SI - IMUL3Q $0x13, R13, R13 - ADDQ R13, SI - ANDQ AX, R8 - ADDQ BX, R8 - ANDQ AX, R10 - ADDQ DI, R10 - ANDQ AX, R12 - ADDQ R9, R12 - ANDQ AX, R14 - ADDQ R11, R14 - - // Store output - MOVQ out+0(FP), AX - MOVQ SI, (AX) - MOVQ R8, 8(AX) - MOVQ R10, 16(AX) - MOVQ R12, 24(AX) - MOVQ R14, 32(AX) - RET diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go deleted file mode 100644 index 9da280d1d8..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2019 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. - -//go:build !amd64 || !gc || purego - -package field - -func feMul(v, x, y *Element) { feMulGeneric(v, x, y) } - -func feSquare(v, x *Element) { feSquareGeneric(v, x) } diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go deleted file mode 100644 index 075fe9b925..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2020 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. - -//go:build arm64 && gc && !purego - -package field - -//go:noescape -func carryPropagate(v *Element) - -func (v *Element) carryPropagate() *Element { - carryPropagate(v) - return v -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s deleted file mode 100644 index 3126a43419..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2020 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. - -//go:build arm64 && gc && !purego - -#include "textflag.h" - -// carryPropagate works exactly like carryPropagateGeneric and uses the -// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but -// avoids loading R0-R4 twice and uses LDP and STP. -// -// See https://golang.org/issues/43145 for the main compiler issue. -// -// func carryPropagate(v *Element) -TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8 - MOVD v+0(FP), R20 - - LDP 0(R20), (R0, R1) - LDP 16(R20), (R2, R3) - MOVD 32(R20), R4 - - AND $0x7ffffffffffff, R0, R10 - AND $0x7ffffffffffff, R1, R11 - AND $0x7ffffffffffff, R2, R12 - AND $0x7ffffffffffff, R3, R13 - AND $0x7ffffffffffff, R4, R14 - - ADD R0>>51, R11, R11 - ADD R1>>51, R12, R12 - ADD R2>>51, R13, R13 - ADD R3>>51, R14, R14 - // R4>>51 * 19 + R10 -> R10 - LSR $51, R4, R21 - MOVD $19, R22 - MADD R22, R10, R21, R10 - - STP (R10, R11), 0(R20) - STP (R12, R13), 16(R20) - MOVD R14, 32(R20) - - RET diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go deleted file mode 100644 index fc029ac12d..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2021 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. - -//go:build !arm64 || !gc || purego - -package field - -func (v *Element) carryPropagate() *Element { - return v.carryPropagateGeneric() -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go deleted file mode 100644 index 2671217da5..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_generic.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2017 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 field - -import "math/bits" - -// uint128 holds a 128-bit number as two 64-bit limbs, for use with the -// bits.Mul64 and bits.Add64 intrinsics. -type uint128 struct { - lo, hi uint64 -} - -// mul64 returns a * b. -func mul64(a, b uint64) uint128 { - hi, lo := bits.Mul64(a, b) - return uint128{lo, hi} -} - -// addMul64 returns v + a * b. -func addMul64(v uint128, a, b uint64) uint128 { - hi, lo := bits.Mul64(a, b) - lo, c := bits.Add64(lo, v.lo, 0) - hi, _ = bits.Add64(hi, v.hi, c) - return uint128{lo, hi} -} - -// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits. -func shiftRightBy51(a uint128) uint64 { - return (a.hi << (64 - 51)) | (a.lo >> 51) -} - -func feMulGeneric(v, a, b *Element) { - a0 := a.l0 - a1 := a.l1 - a2 := a.l2 - a3 := a.l3 - a4 := a.l4 - - b0 := b.l0 - b1 := b.l1 - b2 := b.l2 - b3 := b.l3 - b4 := b.l4 - - // Limb multiplication works like pen-and-paper columnar multiplication, but - // with 51-bit limbs instead of digits. - // - // a4 a3 a2 a1 a0 x - // b4 b3 b2 b1 b0 = - // ------------------------ - // a4b0 a3b0 a2b0 a1b0 a0b0 + - // a4b1 a3b1 a2b1 a1b1 a0b1 + - // a4b2 a3b2 a2b2 a1b2 a0b2 + - // a4b3 a3b3 a2b3 a1b3 a0b3 + - // a4b4 a3b4 a2b4 a1b4 a0b4 = - // ---------------------------------------------- - // r8 r7 r6 r5 r4 r3 r2 r1 r0 - // - // We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to - // reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5, - // r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc. - // - // Reduction can be carried out simultaneously to multiplication. For - // example, we do not compute r5: whenever the result of a multiplication - // belongs to r5, like a1b4, we multiply it by 19 and add the result to r0. - // - // a4b0 a3b0 a2b0 a1b0 a0b0 + - // a3b1 a2b1 a1b1 a0b1 19×a4b1 + - // a2b2 a1b2 a0b2 19×a4b2 19×a3b2 + - // a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 + - // a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 = - // -------------------------------------- - // r4 r3 r2 r1 r0 - // - // Finally we add up the columns into wide, overlapping limbs. - - a1_19 := a1 * 19 - a2_19 := a2 * 19 - a3_19 := a3 * 19 - a4_19 := a4 * 19 - - // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) - r0 := mul64(a0, b0) - r0 = addMul64(r0, a1_19, b4) - r0 = addMul64(r0, a2_19, b3) - r0 = addMul64(r0, a3_19, b2) - r0 = addMul64(r0, a4_19, b1) - - // r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2) - r1 := mul64(a0, b1) - r1 = addMul64(r1, a1, b0) - r1 = addMul64(r1, a2_19, b4) - r1 = addMul64(r1, a3_19, b3) - r1 = addMul64(r1, a4_19, b2) - - // r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3) - r2 := mul64(a0, b2) - r2 = addMul64(r2, a1, b1) - r2 = addMul64(r2, a2, b0) - r2 = addMul64(r2, a3_19, b4) - r2 = addMul64(r2, a4_19, b3) - - // r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4 - r3 := mul64(a0, b3) - r3 = addMul64(r3, a1, b2) - r3 = addMul64(r3, a2, b1) - r3 = addMul64(r3, a3, b0) - r3 = addMul64(r3, a4_19, b4) - - // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 - r4 := mul64(a0, b4) - r4 = addMul64(r4, a1, b3) - r4 = addMul64(r4, a2, b2) - r4 = addMul64(r4, a3, b1) - r4 = addMul64(r4, a4, b0) - - // After the multiplication, we need to reduce (carry) the five coefficients - // to obtain a result with limbs that are at most slightly larger than 2⁵¹, - // to respect the Element invariant. - // - // Overall, the reduction works the same as carryPropagate, except with - // wider inputs: we take the carry for each coefficient by shifting it right - // by 51, and add it to the limb above it. The top carry is multiplied by 19 - // according to the reduction identity and added to the lowest limb. - // - // The largest coefficient (r0) will be at most 111 bits, which guarantees - // that all carries are at most 111 - 51 = 60 bits, which fits in a uint64. - // - // r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1) - // r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²) - // r0 < (1 + 19 × 4) × 2⁵² × 2⁵² - // r0 < 2⁷ × 2⁵² × 2⁵² - // r0 < 2¹¹¹ - // - // Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most - // 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and - // allows us to easily apply the reduction identity. - // - // r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0 - // r4 < 5 × 2⁵² × 2⁵² - // r4 < 2¹⁰⁷ - // - - c0 := shiftRightBy51(r0) - c1 := shiftRightBy51(r1) - c2 := shiftRightBy51(r2) - c3 := shiftRightBy51(r3) - c4 := shiftRightBy51(r4) - - rr0 := r0.lo&maskLow51Bits + c4*19 - rr1 := r1.lo&maskLow51Bits + c0 - rr2 := r2.lo&maskLow51Bits + c1 - rr3 := r3.lo&maskLow51Bits + c2 - rr4 := r4.lo&maskLow51Bits + c3 - - // Now all coefficients fit into 64-bit registers but are still too large to - // be passed around as a Element. We therefore do one last carry chain, - // where the carries will be small enough to fit in the wiggle room above 2⁵¹. - *v = Element{rr0, rr1, rr2, rr3, rr4} - v.carryPropagate() -} - -func feSquareGeneric(v, a *Element) { - l0 := a.l0 - l1 := a.l1 - l2 := a.l2 - l3 := a.l3 - l4 := a.l4 - - // Squaring works precisely like multiplication above, but thanks to its - // symmetry we get to group a few terms together. - // - // l4 l3 l2 l1 l0 x - // l4 l3 l2 l1 l0 = - // ------------------------ - // l4l0 l3l0 l2l0 l1l0 l0l0 + - // l4l1 l3l1 l2l1 l1l1 l0l1 + - // l4l2 l3l2 l2l2 l1l2 l0l2 + - // l4l3 l3l3 l2l3 l1l3 l0l3 + - // l4l4 l3l4 l2l4 l1l4 l0l4 = - // ---------------------------------------------- - // r8 r7 r6 r5 r4 r3 r2 r1 r0 - // - // l4l0 l3l0 l2l0 l1l0 l0l0 + - // l3l1 l2l1 l1l1 l0l1 19×l4l1 + - // l2l2 l1l2 l0l2 19×l4l2 19×l3l2 + - // l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 + - // l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 = - // -------------------------------------- - // r4 r3 r2 r1 r0 - // - // With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with - // only three Mul64 and four Add64, instead of five and eight. - - l0_2 := l0 * 2 - l1_2 := l1 * 2 - - l1_38 := l1 * 38 - l2_38 := l2 * 38 - l3_38 := l3 * 38 - - l3_19 := l3 * 19 - l4_19 := l4 * 19 - - // r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3) - r0 := mul64(l0, l0) - r0 = addMul64(r0, l1_38, l4) - r0 = addMul64(r0, l2_38, l3) - - // r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3 - r1 := mul64(l0_2, l1) - r1 = addMul64(r1, l2_38, l4) - r1 = addMul64(r1, l3_19, l3) - - // r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4 - r2 := mul64(l0_2, l2) - r2 = addMul64(r2, l1, l1) - r2 = addMul64(r2, l3_38, l4) - - // r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4 - r3 := mul64(l0_2, l3) - r3 = addMul64(r3, l1_2, l2) - r3 = addMul64(r3, l4_19, l4) - - // r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2 - r4 := mul64(l0_2, l4) - r4 = addMul64(r4, l1_2, l3) - r4 = addMul64(r4, l2, l2) - - c0 := shiftRightBy51(r0) - c1 := shiftRightBy51(r1) - c2 := shiftRightBy51(r2) - c3 := shiftRightBy51(r3) - c4 := shiftRightBy51(r4) - - rr0 := r0.lo&maskLow51Bits + c4*19 - rr1 := r1.lo&maskLow51Bits + c0 - rr2 := r2.lo&maskLow51Bits + c1 - rr3 := r3.lo&maskLow51Bits + c2 - rr4 := r4.lo&maskLow51Bits + c3 - - *v = Element{rr0, rr1, rr2, rr3, rr4} - v.carryPropagate() -} - -// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction -// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. TODO inline -func (v *Element) carryPropagateGeneric() *Element { - c0 := v.l0 >> 51 - c1 := v.l1 >> 51 - c2 := v.l2 >> 51 - c3 := v.l3 >> 51 - c4 := v.l4 >> 51 - - v.l0 = v.l0&maskLow51Bits + c4*19 - v.l1 = v.l1&maskLow51Bits + c0 - v.l2 = v.l2&maskLow51Bits + c1 - v.l3 = v.l3&maskLow51Bits + c2 - v.l4 = v.l4&maskLow51Bits + c3 - - return v -} diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint deleted file mode 100644 index e3685f95ca..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.checkpoint +++ /dev/null @@ -1 +0,0 @@ -b0c49ae9f59d233526f8934262c5bbbe14d4358d diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh b/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh deleted file mode 100644 index 1ba22a8b4c..0000000000 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/sync.sh +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/bash -set -euo pipefail - -cd "$(git rev-parse --show-toplevel)" - -STD_PATH=src/crypto/ed25519/internal/edwards25519/field -LOCAL_PATH=curve25519/internal/field -LAST_SYNC_REF=$(cat $LOCAL_PATH/sync.checkpoint) - -git fetch https://go.googlesource.com/go master - -if git diff --quiet $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH; then - echo "No changes." -else - NEW_REF=$(git rev-parse FETCH_HEAD | tee $LOCAL_PATH/sync.checkpoint) - echo "Applying changes from $LAST_SYNC_REF to $NEW_REF..." - git diff $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH | \ - git apply -3 --directory=$LOCAL_PATH -fi diff --git a/vendor/golang.org/x/crypto/hkdf/hkdf.go b/vendor/golang.org/x/crypto/hkdf/hkdf.go index f4ded5fee2..3bee66294e 100644 --- a/vendor/golang.org/x/crypto/hkdf/hkdf.go +++ b/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -8,7 +8,7 @@ // HKDF is a cryptographic key derivation function (KDF) with the goal of // expanding limited input keying material into one or more cryptographically // strong secret keys. -package hkdf // import "golang.org/x/crypto/hkdf" +package hkdf import ( "crypto/hmac" diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s index e0d3c64756..133757384b 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s @@ -1,108 +1,93 @@ -// Copyright 2012 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. +// Code generated by command: go run sum_amd64_asm.go -out ../sum_amd64.s -pkg poly1305. DO NOT EDIT. //go:build gc && !purego -#include "textflag.h" - -#define POLY1305_ADD(msg, h0, h1, h2) \ - ADDQ 0(msg), h0; \ - ADCQ 8(msg), h1; \ - ADCQ $1, h2; \ - LEAQ 16(msg), msg - -#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \ - MOVQ r0, AX; \ - MULQ h0; \ - MOVQ AX, t0; \ - MOVQ DX, t1; \ - MOVQ r0, AX; \ - MULQ h1; \ - ADDQ AX, t1; \ - ADCQ $0, DX; \ - MOVQ r0, t2; \ - IMULQ h2, t2; \ - ADDQ DX, t2; \ - \ - MOVQ r1, AX; \ - MULQ h0; \ - ADDQ AX, t1; \ - ADCQ $0, DX; \ - MOVQ DX, h0; \ - MOVQ r1, t3; \ - IMULQ h2, t3; \ - MOVQ r1, AX; \ - MULQ h1; \ - ADDQ AX, t2; \ - ADCQ DX, t3; \ - ADDQ h0, t2; \ - ADCQ $0, t3; \ - \ - MOVQ t0, h0; \ - MOVQ t1, h1; \ - MOVQ t2, h2; \ - ANDQ $3, h2; \ - MOVQ t2, t0; \ - ANDQ $0xFFFFFFFFFFFFFFFC, t0; \ - ADDQ t0, h0; \ - ADCQ t3, h1; \ - ADCQ $0, h2; \ - SHRQ $2, t3, t2; \ - SHRQ $2, t3; \ - ADDQ t2, h0; \ - ADCQ t3, h1; \ - ADCQ $0, h2 - -// func update(state *[7]uint64, msg []byte) +// func update(state *macState, msg []byte) TEXT ·update(SB), $0-32 MOVQ state+0(FP), DI MOVQ msg_base+8(FP), SI MOVQ msg_len+16(FP), R15 - - MOVQ 0(DI), R8 // h0 - MOVQ 8(DI), R9 // h1 - MOVQ 16(DI), R10 // h2 - MOVQ 24(DI), R11 // r0 - MOVQ 32(DI), R12 // r1 - - CMPQ R15, $16 + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + CMPQ R15, $0x10 JB bytes_between_0_and_15 loop: - POLY1305_ADD(SI, R8, R9, R10) + ADDQ (SI), R8 + ADCQ 8(SI), R9 + ADCQ $0x01, R10 + LEAQ 16(SI), SI multiply: - POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14) - SUBQ $16, R15 - CMPQ R15, $16 - JAE loop + MOVQ R11, AX + MULQ R8 + MOVQ AX, BX + MOVQ DX, CX + MOVQ R11, AX + MULQ R9 + ADDQ AX, CX + ADCQ $0x00, DX + MOVQ R11, R13 + IMULQ R10, R13 + ADDQ DX, R13 + MOVQ R12, AX + MULQ R8 + ADDQ AX, CX + ADCQ $0x00, DX + MOVQ DX, R8 + MOVQ R12, R14 + IMULQ R10, R14 + MOVQ R12, AX + MULQ R9 + ADDQ AX, R13 + ADCQ DX, R14 + ADDQ R8, R13 + ADCQ $0x00, R14 + MOVQ BX, R8 + MOVQ CX, R9 + MOVQ R13, R10 + ANDQ $0x03, R10 + MOVQ R13, BX + ANDQ $-4, BX + ADDQ BX, R8 + ADCQ R14, R9 + ADCQ $0x00, R10 + SHRQ $0x02, R14, R13 + SHRQ $0x02, R14 + ADDQ R13, R8 + ADCQ R14, R9 + ADCQ $0x00, R10 + SUBQ $0x10, R15 + CMPQ R15, $0x10 + JAE loop bytes_between_0_and_15: TESTQ R15, R15 JZ done - MOVQ $1, BX + MOVQ $0x00000001, BX XORQ CX, CX XORQ R13, R13 ADDQ R15, SI flush_buffer: - SHLQ $8, BX, CX - SHLQ $8, BX + SHLQ $0x08, BX, CX + SHLQ $0x08, BX MOVB -1(SI), R13 XORQ R13, BX DECQ SI DECQ R15 JNZ flush_buffer - ADDQ BX, R8 ADCQ CX, R9 - ADCQ $0, R10 - MOVQ $16, R15 + ADCQ $0x00, R10 + MOVQ $0x00000010, R15 JMP multiply done: - MOVQ R8, 0(DI) + MOVQ R8, (DI) MOVQ R9, 8(DI) MOVQ R10, 16(DI) RET diff --git a/vendor/golang.org/x/crypto/openpgp/armor/armor.go b/vendor/golang.org/x/crypto/openpgp/armor/armor.go index 8907183ec0..e664d127cb 100644 --- a/vendor/golang.org/x/crypto/openpgp/armor/armor.go +++ b/vendor/golang.org/x/crypto/openpgp/armor/armor.go @@ -10,14 +10,15 @@ // for their specific task. If you are required to interoperate with OpenPGP // systems and need a maintained package, consider a community fork. // See https://golang.org/issue/44226. -package armor // import "golang.org/x/crypto/openpgp/armor" +package armor import ( "bufio" "bytes" "encoding/base64" - "golang.org/x/crypto/openpgp/errors" "io" + + "golang.org/x/crypto/openpgp/errors" ) // A Block represents an OpenPGP armored structure. diff --git a/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go index 743b35a120..f922bdbcaa 100644 --- a/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go +++ b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go @@ -16,7 +16,7 @@ // https://golang.org/issue/44226), and ElGamal in the OpenPGP ecosystem has // compatibility and security issues (see https://eprint.iacr.org/2021/923). // Moreover, this package doesn't protect against side-channel attacks. -package elgamal // import "golang.org/x/crypto/openpgp/elgamal" +package elgamal import ( "crypto/rand" diff --git a/vendor/golang.org/x/crypto/openpgp/errors/errors.go b/vendor/golang.org/x/crypto/openpgp/errors/errors.go index 1d7a0ea05a..a328749471 100644 --- a/vendor/golang.org/x/crypto/openpgp/errors/errors.go +++ b/vendor/golang.org/x/crypto/openpgp/errors/errors.go @@ -9,7 +9,7 @@ // for their specific task. If you are required to interoperate with OpenPGP // systems and need a maintained package, consider a community fork. // See https://golang.org/issue/44226. -package errors // import "golang.org/x/crypto/openpgp/errors" +package errors import ( "strconv" diff --git a/vendor/golang.org/x/crypto/openpgp/packet/packet.go b/vendor/golang.org/x/crypto/openpgp/packet/packet.go index 0a19794a8e..a84a1a214e 100644 --- a/vendor/golang.org/x/crypto/openpgp/packet/packet.go +++ b/vendor/golang.org/x/crypto/openpgp/packet/packet.go @@ -10,7 +10,7 @@ // for their specific task. If you are required to interoperate with OpenPGP // systems and need a maintained package, consider a community fork. // See https://golang.org/issue/44226. -package packet // import "golang.org/x/crypto/openpgp/packet" +package packet import ( "bufio" diff --git a/vendor/golang.org/x/crypto/openpgp/read.go b/vendor/golang.org/x/crypto/openpgp/read.go index 48a8931468..cff3db9196 100644 --- a/vendor/golang.org/x/crypto/openpgp/read.go +++ b/vendor/golang.org/x/crypto/openpgp/read.go @@ -9,7 +9,7 @@ // for their specific task. If you are required to interoperate with OpenPGP // systems and need a maintained package, consider a community fork. // See https://golang.org/issue/44226. -package openpgp // import "golang.org/x/crypto/openpgp" +package openpgp import ( "crypto" diff --git a/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go b/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go index f53244a1c7..fa1a919079 100644 --- a/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go +++ b/vendor/golang.org/x/crypto/openpgp/s2k/s2k.go @@ -10,7 +10,7 @@ // for their specific task. If you are required to interoperate with OpenPGP // systems and need a maintained package, consider a community fork. // See https://golang.org/issue/44226. -package s2k // import "golang.org/x/crypto/openpgp/s2k" +package s2k import ( "crypto" diff --git a/vendor/golang.org/x/crypto/sha3/doc.go b/vendor/golang.org/x/crypto/sha3/doc.go index decd8cf9bf..7e02309070 100644 --- a/vendor/golang.org/x/crypto/sha3/doc.go +++ b/vendor/golang.org/x/crypto/sha3/doc.go @@ -59,4 +59,4 @@ // They produce output of the same length, with the same security strengths // against all attacks. This means, in particular, that SHA3-256 only has // 128-bit collision resistance, because its output length is 32 bytes. -package sha3 // import "golang.org/x/crypto/sha3" +package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/hashes.go b/vendor/golang.org/x/crypto/sha3/hashes.go index 0d8043fd2a..c544b29e5f 100644 --- a/vendor/golang.org/x/crypto/sha3/hashes.go +++ b/vendor/golang.org/x/crypto/sha3/hashes.go @@ -9,6 +9,7 @@ package sha3 // bytes. import ( + "crypto" "hash" ) @@ -16,39 +17,50 @@ import ( // Its generic security strength is 224 bits against preimage attacks, // and 112 bits against collision attacks. func New224() hash.Hash { - if h := new224Asm(); h != nil { - return h - } - return &state{rate: 144, outputLen: 28, dsbyte: 0x06} + return new224() } // New256 creates a new SHA3-256 hash. // Its generic security strength is 256 bits against preimage attacks, // and 128 bits against collision attacks. func New256() hash.Hash { - if h := new256Asm(); h != nil { - return h - } - return &state{rate: 136, outputLen: 32, dsbyte: 0x06} + return new256() } // New384 creates a new SHA3-384 hash. // Its generic security strength is 384 bits against preimage attacks, // and 192 bits against collision attacks. func New384() hash.Hash { - if h := new384Asm(); h != nil { - return h - } - return &state{rate: 104, outputLen: 48, dsbyte: 0x06} + return new384() } // New512 creates a new SHA3-512 hash. // Its generic security strength is 512 bits against preimage attacks, // and 256 bits against collision attacks. func New512() hash.Hash { - if h := new512Asm(); h != nil { - return h - } + return new512() +} + +func init() { + crypto.RegisterHash(crypto.SHA3_224, New224) + crypto.RegisterHash(crypto.SHA3_256, New256) + crypto.RegisterHash(crypto.SHA3_384, New384) + crypto.RegisterHash(crypto.SHA3_512, New512) +} + +func new224Generic() *state { + return &state{rate: 144, outputLen: 28, dsbyte: 0x06} +} + +func new256Generic() *state { + return &state{rate: 136, outputLen: 32, dsbyte: 0x06} +} + +func new384Generic() *state { + return &state{rate: 104, outputLen: 48, dsbyte: 0x06} +} + +func new512Generic() *state { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} } diff --git a/vendor/golang.org/x/crypto/sha3/hashes_generic.go b/vendor/golang.org/x/crypto/sha3/hashes_generic.go deleted file mode 100644 index fe8c84793c..0000000000 --- a/vendor/golang.org/x/crypto/sha3/hashes_generic.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 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. - -//go:build !gc || purego || !s390x - -package sha3 - -import ( - "hash" -) - -// new224Asm returns an assembly implementation of SHA3-224 if available, -// otherwise it returns nil. -func new224Asm() hash.Hash { return nil } - -// new256Asm returns an assembly implementation of SHA3-256 if available, -// otherwise it returns nil. -func new256Asm() hash.Hash { return nil } - -// new384Asm returns an assembly implementation of SHA3-384 if available, -// otherwise it returns nil. -func new384Asm() hash.Hash { return nil } - -// new512Asm returns an assembly implementation of SHA3-512 if available, -// otherwise it returns nil. -func new512Asm() hash.Hash { return nil } diff --git a/vendor/golang.org/x/crypto/sha3/hashes_noasm.go b/vendor/golang.org/x/crypto/sha3/hashes_noasm.go new file mode 100644 index 0000000000..9d85fb6214 --- /dev/null +++ b/vendor/golang.org/x/crypto/sha3/hashes_noasm.go @@ -0,0 +1,23 @@ +// Copyright 2023 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. + +//go:build !gc || purego || !s390x + +package sha3 + +func new224() *state { + return new224Generic() +} + +func new256() *state { + return new256Generic() +} + +func new384() *state { + return new384Generic() +} + +func new512() *state { + return new512Generic() +} diff --git a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s index 1f53938861..99e2f16e97 100644 --- a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s +++ b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s @@ -1,390 +1,5419 @@ -// Copyright 2015 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. +// Code generated by command: go run keccakf_amd64_asm.go -out ../keccakf_amd64.s -pkg sha3. DO NOT EDIT. //go:build amd64 && !purego && gc -// This code was translated into a form compatible with 6a from the public -// domain sources at https://github.com/gvanas/KeccakCodePackage - -// Offsets in state -#define _ba (0*8) -#define _be (1*8) -#define _bi (2*8) -#define _bo (3*8) -#define _bu (4*8) -#define _ga (5*8) -#define _ge (6*8) -#define _gi (7*8) -#define _go (8*8) -#define _gu (9*8) -#define _ka (10*8) -#define _ke (11*8) -#define _ki (12*8) -#define _ko (13*8) -#define _ku (14*8) -#define _ma (15*8) -#define _me (16*8) -#define _mi (17*8) -#define _mo (18*8) -#define _mu (19*8) -#define _sa (20*8) -#define _se (21*8) -#define _si (22*8) -#define _so (23*8) -#define _su (24*8) - -// Temporary registers -#define rT1 AX - -// Round vars -#define rpState DI -#define rpStack SP - -#define rDa BX -#define rDe CX -#define rDi DX -#define rDo R8 -#define rDu R9 - -#define rBa R10 -#define rBe R11 -#define rBi R12 -#define rBo R13 -#define rBu R14 - -#define rCa SI -#define rCe BP -#define rCi rBi -#define rCo rBo -#define rCu R15 - -#define MOVQ_RBI_RCE MOVQ rBi, rCe -#define XORQ_RT1_RCA XORQ rT1, rCa -#define XORQ_RT1_RCE XORQ rT1, rCe -#define XORQ_RBA_RCU XORQ rBa, rCu -#define XORQ_RBE_RCU XORQ rBe, rCu -#define XORQ_RDU_RCU XORQ rDu, rCu -#define XORQ_RDA_RCA XORQ rDa, rCa -#define XORQ_RDE_RCE XORQ rDe, rCe - -#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \ - /* Prepare round */ \ - MOVQ rCe, rDa; \ - ROLQ $1, rDa; \ - \ - MOVQ _bi(iState), rCi; \ - XORQ _gi(iState), rDi; \ - XORQ rCu, rDa; \ - XORQ _ki(iState), rCi; \ - XORQ _mi(iState), rDi; \ - XORQ rDi, rCi; \ - \ - MOVQ rCi, rDe; \ - ROLQ $1, rDe; \ - \ - MOVQ _bo(iState), rCo; \ - XORQ _go(iState), rDo; \ - XORQ rCa, rDe; \ - XORQ _ko(iState), rCo; \ - XORQ _mo(iState), rDo; \ - XORQ rDo, rCo; \ - \ - MOVQ rCo, rDi; \ - ROLQ $1, rDi; \ - \ - MOVQ rCu, rDo; \ - XORQ rCe, rDi; \ - ROLQ $1, rDo; \ - \ - MOVQ rCa, rDu; \ - XORQ rCi, rDo; \ - ROLQ $1, rDu; \ - \ - /* Result b */ \ - MOVQ _ba(iState), rBa; \ - MOVQ _ge(iState), rBe; \ - XORQ rCo, rDu; \ - MOVQ _ki(iState), rBi; \ - MOVQ _mo(iState), rBo; \ - MOVQ _su(iState), rBu; \ - XORQ rDe, rBe; \ - ROLQ $44, rBe; \ - XORQ rDi, rBi; \ - XORQ rDa, rBa; \ - ROLQ $43, rBi; \ - \ - MOVQ rBe, rCa; \ - MOVQ rc, rT1; \ - ORQ rBi, rCa; \ - XORQ rBa, rT1; \ - XORQ rT1, rCa; \ - MOVQ rCa, _ba(oState); \ - \ - XORQ rDu, rBu; \ - ROLQ $14, rBu; \ - MOVQ rBa, rCu; \ - ANDQ rBe, rCu; \ - XORQ rBu, rCu; \ - MOVQ rCu, _bu(oState); \ - \ - XORQ rDo, rBo; \ - ROLQ $21, rBo; \ - MOVQ rBo, rT1; \ - ANDQ rBu, rT1; \ - XORQ rBi, rT1; \ - MOVQ rT1, _bi(oState); \ - \ - NOTQ rBi; \ - ORQ rBa, rBu; \ - ORQ rBo, rBi; \ - XORQ rBo, rBu; \ - XORQ rBe, rBi; \ - MOVQ rBu, _bo(oState); \ - MOVQ rBi, _be(oState); \ - B_RBI_RCE; \ - \ - /* Result g */ \ - MOVQ _gu(iState), rBe; \ - XORQ rDu, rBe; \ - MOVQ _ka(iState), rBi; \ - ROLQ $20, rBe; \ - XORQ rDa, rBi; \ - ROLQ $3, rBi; \ - MOVQ _bo(iState), rBa; \ - MOVQ rBe, rT1; \ - ORQ rBi, rT1; \ - XORQ rDo, rBa; \ - MOVQ _me(iState), rBo; \ - MOVQ _si(iState), rBu; \ - ROLQ $28, rBa; \ - XORQ rBa, rT1; \ - MOVQ rT1, _ga(oState); \ - G_RT1_RCA; \ - \ - XORQ rDe, rBo; \ - ROLQ $45, rBo; \ - MOVQ rBi, rT1; \ - ANDQ rBo, rT1; \ - XORQ rBe, rT1; \ - MOVQ rT1, _ge(oState); \ - G_RT1_RCE; \ - \ - XORQ rDi, rBu; \ - ROLQ $61, rBu; \ - MOVQ rBu, rT1; \ - ORQ rBa, rT1; \ - XORQ rBo, rT1; \ - MOVQ rT1, _go(oState); \ - \ - ANDQ rBe, rBa; \ - XORQ rBu, rBa; \ - MOVQ rBa, _gu(oState); \ - NOTQ rBu; \ - G_RBA_RCU; \ - \ - ORQ rBu, rBo; \ - XORQ rBi, rBo; \ - MOVQ rBo, _gi(oState); \ - \ - /* Result k */ \ - MOVQ _be(iState), rBa; \ - MOVQ _gi(iState), rBe; \ - MOVQ _ko(iState), rBi; \ - MOVQ _mu(iState), rBo; \ - MOVQ _sa(iState), rBu; \ - XORQ rDi, rBe; \ - ROLQ $6, rBe; \ - XORQ rDo, rBi; \ - ROLQ $25, rBi; \ - MOVQ rBe, rT1; \ - ORQ rBi, rT1; \ - XORQ rDe, rBa; \ - ROLQ $1, rBa; \ - XORQ rBa, rT1; \ - MOVQ rT1, _ka(oState); \ - K_RT1_RCA; \ - \ - XORQ rDu, rBo; \ - ROLQ $8, rBo; \ - MOVQ rBi, rT1; \ - ANDQ rBo, rT1; \ - XORQ rBe, rT1; \ - MOVQ rT1, _ke(oState); \ - K_RT1_RCE; \ - \ - XORQ rDa, rBu; \ - ROLQ $18, rBu; \ - NOTQ rBo; \ - MOVQ rBo, rT1; \ - ANDQ rBu, rT1; \ - XORQ rBi, rT1; \ - MOVQ rT1, _ki(oState); \ - \ - MOVQ rBu, rT1; \ - ORQ rBa, rT1; \ - XORQ rBo, rT1; \ - MOVQ rT1, _ko(oState); \ - \ - ANDQ rBe, rBa; \ - XORQ rBu, rBa; \ - MOVQ rBa, _ku(oState); \ - K_RBA_RCU; \ - \ - /* Result m */ \ - MOVQ _ga(iState), rBe; \ - XORQ rDa, rBe; \ - MOVQ _ke(iState), rBi; \ - ROLQ $36, rBe; \ - XORQ rDe, rBi; \ - MOVQ _bu(iState), rBa; \ - ROLQ $10, rBi; \ - MOVQ rBe, rT1; \ - MOVQ _mi(iState), rBo; \ - ANDQ rBi, rT1; \ - XORQ rDu, rBa; \ - MOVQ _so(iState), rBu; \ - ROLQ $27, rBa; \ - XORQ rBa, rT1; \ - MOVQ rT1, _ma(oState); \ - M_RT1_RCA; \ - \ - XORQ rDi, rBo; \ - ROLQ $15, rBo; \ - MOVQ rBi, rT1; \ - ORQ rBo, rT1; \ - XORQ rBe, rT1; \ - MOVQ rT1, _me(oState); \ - M_RT1_RCE; \ - \ - XORQ rDo, rBu; \ - ROLQ $56, rBu; \ - NOTQ rBo; \ - MOVQ rBo, rT1; \ - ORQ rBu, rT1; \ - XORQ rBi, rT1; \ - MOVQ rT1, _mi(oState); \ - \ - ORQ rBa, rBe; \ - XORQ rBu, rBe; \ - MOVQ rBe, _mu(oState); \ - \ - ANDQ rBa, rBu; \ - XORQ rBo, rBu; \ - MOVQ rBu, _mo(oState); \ - M_RBE_RCU; \ - \ - /* Result s */ \ - MOVQ _bi(iState), rBa; \ - MOVQ _go(iState), rBe; \ - MOVQ _ku(iState), rBi; \ - XORQ rDi, rBa; \ - MOVQ _ma(iState), rBo; \ - ROLQ $62, rBa; \ - XORQ rDo, rBe; \ - MOVQ _se(iState), rBu; \ - ROLQ $55, rBe; \ - \ - XORQ rDu, rBi; \ - MOVQ rBa, rDu; \ - XORQ rDe, rBu; \ - ROLQ $2, rBu; \ - ANDQ rBe, rDu; \ - XORQ rBu, rDu; \ - MOVQ rDu, _su(oState); \ - \ - ROLQ $39, rBi; \ - S_RDU_RCU; \ - NOTQ rBe; \ - XORQ rDa, rBo; \ - MOVQ rBe, rDa; \ - ANDQ rBi, rDa; \ - XORQ rBa, rDa; \ - MOVQ rDa, _sa(oState); \ - S_RDA_RCA; \ - \ - ROLQ $41, rBo; \ - MOVQ rBi, rDe; \ - ORQ rBo, rDe; \ - XORQ rBe, rDe; \ - MOVQ rDe, _se(oState); \ - S_RDE_RCE; \ - \ - MOVQ rBo, rDi; \ - MOVQ rBu, rDo; \ - ANDQ rBu, rDi; \ - ORQ rBa, rDo; \ - XORQ rBi, rDi; \ - XORQ rBo, rDo; \ - MOVQ rDi, _si(oState); \ - MOVQ rDo, _so(oState) \ - // func keccakF1600(a *[25]uint64) -TEXT ·keccakF1600(SB), 0, $200-8 - MOVQ a+0(FP), rpState +TEXT ·keccakF1600(SB), $200-8 + MOVQ a+0(FP), DI // Convert the user state into an internal state - NOTQ _be(rpState) - NOTQ _bi(rpState) - NOTQ _go(rpState) - NOTQ _ki(rpState) - NOTQ _mi(rpState) - NOTQ _sa(rpState) + NOTQ 8(DI) + NOTQ 16(DI) + NOTQ 64(DI) + NOTQ 96(DI) + NOTQ 136(DI) + NOTQ 160(DI) // Execute the KeccakF permutation - MOVQ _ba(rpState), rCa - MOVQ _be(rpState), rCe - MOVQ _bu(rpState), rCu - - XORQ _ga(rpState), rCa - XORQ _ge(rpState), rCe - XORQ _gu(rpState), rCu - - XORQ _ka(rpState), rCa - XORQ _ke(rpState), rCe - XORQ _ku(rpState), rCu - - XORQ _ma(rpState), rCa - XORQ _me(rpState), rCe - XORQ _mu(rpState), rCu - - XORQ _sa(rpState), rCa - XORQ _se(rpState), rCe - MOVQ _si(rpState), rDi - MOVQ _so(rpState), rDo - XORQ _su(rpState), rCu - - mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) - mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP) + MOVQ (DI), SI + MOVQ 8(DI), BP + MOVQ 32(DI), R15 + XORQ 40(DI), SI + XORQ 48(DI), BP + XORQ 72(DI), R15 + XORQ 80(DI), SI + XORQ 88(DI), BP + XORQ 112(DI), R15 + XORQ 120(DI), SI + XORQ 128(DI), BP + XORQ 152(DI), R15 + XORQ 160(DI), SI + XORQ 168(DI), BP + MOVQ 176(DI), DX + MOVQ 184(DI), R8 + XORQ 192(DI), R15 - // Revert the internal state to the user state - NOTQ _be(rpState) - NOTQ _bi(rpState) - NOTQ _go(rpState) - NOTQ _ki(rpState) - NOTQ _mi(rpState) - NOTQ _sa(rpState) + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x0000000000000001, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x0000000000008082, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x800000000000808a, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000080008000, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x000000000000808b, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x0000000080000001, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000080008081, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000000008009, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x000000000000008a, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x0000000000000088, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x0000000080008009, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x000000008000000a, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x000000008000808b, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x800000000000008b, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000000008089, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000000008003, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000000008002, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000000000080, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x000000000000800a, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x800000008000000a, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000080008081, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000000008080, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + MOVQ R12, BP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + XORQ R10, R15 + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + XORQ R11, R15 + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(DI), R12 + XORQ 56(DI), DX + XORQ R15, BX + XORQ 96(DI), R12 + XORQ 136(DI), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(DI), R13 + XORQ 64(DI), R8 + XORQ SI, CX + XORQ 104(DI), R13 + XORQ 144(DI), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (DI), R10 + MOVQ 48(DI), R11 + XORQ R13, R9 + MOVQ 96(DI), R12 + MOVQ 144(DI), R13 + MOVQ 192(DI), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x0000000080000001, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (SP) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(SP) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(SP) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(SP) + MOVQ R12, 8(SP) + MOVQ R12, BP + + // Result g + MOVQ 72(DI), R11 + XORQ R9, R11 + MOVQ 80(DI), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(DI), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(DI), R13 + MOVQ 176(DI), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(SP) + XORQ AX, SI + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(SP) + XORQ AX, BP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(SP) + NOTQ R14 + XORQ R10, R15 + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(SP) + + // Result k + MOVQ 8(DI), R10 + MOVQ 56(DI), R11 + MOVQ 104(DI), R12 + MOVQ 152(DI), R13 + MOVQ 160(DI), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(SP) + XORQ AX, SI + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(SP) + XORQ AX, BP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(SP) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(SP) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(SP) + XORQ R10, R15 + + // Result m + MOVQ 40(DI), R11 + XORQ BX, R11 + MOVQ 88(DI), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(DI), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(DI), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(DI), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(SP) + XORQ AX, SI + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(SP) + XORQ AX, BP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(SP) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(SP) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(SP) + XORQ R11, R15 + + // Result s + MOVQ 16(DI), R10 + MOVQ 64(DI), R11 + MOVQ 112(DI), R12 + XORQ DX, R10 + MOVQ 120(DI), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(DI), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(SP) + ROLQ $0x27, R12 + XORQ R9, R15 + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(SP) + XORQ BX, SI + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(SP) + XORQ CX, BP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(SP) + MOVQ R8, 184(SP) + + // Prepare round + MOVQ BP, BX + ROLQ $0x01, BX + MOVQ 16(SP), R12 + XORQ 56(SP), DX + XORQ R15, BX + XORQ 96(SP), R12 + XORQ 136(SP), DX + XORQ DX, R12 + MOVQ R12, CX + ROLQ $0x01, CX + MOVQ 24(SP), R13 + XORQ 64(SP), R8 + XORQ SI, CX + XORQ 104(SP), R13 + XORQ 144(SP), R8 + XORQ R8, R13 + MOVQ R13, DX + ROLQ $0x01, DX + MOVQ R15, R8 + XORQ BP, DX + ROLQ $0x01, R8 + MOVQ SI, R9 + XORQ R12, R8 + ROLQ $0x01, R9 + + // Result b + MOVQ (SP), R10 + MOVQ 48(SP), R11 + XORQ R13, R9 + MOVQ 96(SP), R12 + MOVQ 144(SP), R13 + MOVQ 192(SP), R14 + XORQ CX, R11 + ROLQ $0x2c, R11 + XORQ DX, R12 + XORQ BX, R10 + ROLQ $0x2b, R12 + MOVQ R11, SI + MOVQ $0x8000000080008008, AX + ORQ R12, SI + XORQ R10, AX + XORQ AX, SI + MOVQ SI, (DI) + XORQ R9, R14 + ROLQ $0x0e, R14 + MOVQ R10, R15 + ANDQ R11, R15 + XORQ R14, R15 + MOVQ R15, 32(DI) + XORQ R8, R13 + ROLQ $0x15, R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 16(DI) + NOTQ R12 + ORQ R10, R14 + ORQ R13, R12 + XORQ R13, R14 + XORQ R11, R12 + MOVQ R14, 24(DI) + MOVQ R12, 8(DI) + NOP + + // Result g + MOVQ 72(SP), R11 + XORQ R9, R11 + MOVQ 80(SP), R12 + ROLQ $0x14, R11 + XORQ BX, R12 + ROLQ $0x03, R12 + MOVQ 24(SP), R10 + MOVQ R11, AX + ORQ R12, AX + XORQ R8, R10 + MOVQ 128(SP), R13 + MOVQ 176(SP), R14 + ROLQ $0x1c, R10 + XORQ R10, AX + MOVQ AX, 40(DI) + NOP + XORQ CX, R13 + ROLQ $0x2d, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 48(DI) + NOP + XORQ DX, R14 + ROLQ $0x3d, R14 + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 64(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 72(DI) + NOTQ R14 + NOP + ORQ R14, R13 + XORQ R12, R13 + MOVQ R13, 56(DI) + + // Result k + MOVQ 8(SP), R10 + MOVQ 56(SP), R11 + MOVQ 104(SP), R12 + MOVQ 152(SP), R13 + MOVQ 160(SP), R14 + XORQ DX, R11 + ROLQ $0x06, R11 + XORQ R8, R12 + ROLQ $0x19, R12 + MOVQ R11, AX + ORQ R12, AX + XORQ CX, R10 + ROLQ $0x01, R10 + XORQ R10, AX + MOVQ AX, 80(DI) + NOP + XORQ R9, R13 + ROLQ $0x08, R13 + MOVQ R12, AX + ANDQ R13, AX + XORQ R11, AX + MOVQ AX, 88(DI) + NOP + XORQ BX, R14 + ROLQ $0x12, R14 + NOTQ R13 + MOVQ R13, AX + ANDQ R14, AX + XORQ R12, AX + MOVQ AX, 96(DI) + MOVQ R14, AX + ORQ R10, AX + XORQ R13, AX + MOVQ AX, 104(DI) + ANDQ R11, R10 + XORQ R14, R10 + MOVQ R10, 112(DI) + NOP + + // Result m + MOVQ 40(SP), R11 + XORQ BX, R11 + MOVQ 88(SP), R12 + ROLQ $0x24, R11 + XORQ CX, R12 + MOVQ 32(SP), R10 + ROLQ $0x0a, R12 + MOVQ R11, AX + MOVQ 136(SP), R13 + ANDQ R12, AX + XORQ R9, R10 + MOVQ 184(SP), R14 + ROLQ $0x1b, R10 + XORQ R10, AX + MOVQ AX, 120(DI) + NOP + XORQ DX, R13 + ROLQ $0x0f, R13 + MOVQ R12, AX + ORQ R13, AX + XORQ R11, AX + MOVQ AX, 128(DI) + NOP + XORQ R8, R14 + ROLQ $0x38, R14 + NOTQ R13 + MOVQ R13, AX + ORQ R14, AX + XORQ R12, AX + MOVQ AX, 136(DI) + ORQ R10, R11 + XORQ R14, R11 + MOVQ R11, 152(DI) + ANDQ R10, R14 + XORQ R13, R14 + MOVQ R14, 144(DI) + NOP + + // Result s + MOVQ 16(SP), R10 + MOVQ 64(SP), R11 + MOVQ 112(SP), R12 + XORQ DX, R10 + MOVQ 120(SP), R13 + ROLQ $0x3e, R10 + XORQ R8, R11 + MOVQ 168(SP), R14 + ROLQ $0x37, R11 + XORQ R9, R12 + MOVQ R10, R9 + XORQ CX, R14 + ROLQ $0x02, R14 + ANDQ R11, R9 + XORQ R14, R9 + MOVQ R9, 192(DI) + ROLQ $0x27, R12 + NOP + NOTQ R11 + XORQ BX, R13 + MOVQ R11, BX + ANDQ R12, BX + XORQ R10, BX + MOVQ BX, 160(DI) + NOP + ROLQ $0x29, R13 + MOVQ R12, CX + ORQ R13, CX + XORQ R11, CX + MOVQ CX, 168(DI) + NOP + MOVQ R13, DX + MOVQ R14, R8 + ANDQ R14, DX + ORQ R10, R8 + XORQ R12, DX + XORQ R13, R8 + MOVQ DX, 176(DI) + MOVQ R8, 184(DI) + + // Revert the internal state to the user state + NOTQ 8(DI) + NOTQ 16(DI) + NOTQ 64(DI) + NOTQ 96(DI) + NOTQ 136(DI) + NOTQ 160(DI) RET diff --git a/vendor/golang.org/x/crypto/sha3/register.go b/vendor/golang.org/x/crypto/sha3/register.go deleted file mode 100644 index addfd5049b..0000000000 --- a/vendor/golang.org/x/crypto/sha3/register.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 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. - -//go:build go1.4 - -package sha3 - -import ( - "crypto" -) - -func init() { - crypto.RegisterHash(crypto.SHA3_224, New224) - crypto.RegisterHash(crypto.SHA3_256, New256) - crypto.RegisterHash(crypto.SHA3_384, New384) - crypto.RegisterHash(crypto.SHA3_512, New512) -} diff --git a/vendor/golang.org/x/crypto/sha3/sha3.go b/vendor/golang.org/x/crypto/sha3/sha3.go index 4884d172a4..afedde5abf 100644 --- a/vendor/golang.org/x/crypto/sha3/sha3.go +++ b/vendor/golang.org/x/crypto/sha3/sha3.go @@ -23,7 +23,6 @@ const ( type state struct { // Generic sponge components. a [25]uint64 // main state of the hash - buf []byte // points into storage rate int // the number of bytes of state to use // dsbyte contains the "domain separation" bits and the first bit of @@ -40,7 +39,8 @@ type state struct { // Extendable-Output Functions (May 2014)" dsbyte byte - storage storageBuf + i, n int // storage[i:n] is the buffer, i is only used while squeezing + storage [maxRate]byte // Specific to SHA-3 and SHAKE. outputLen int // the default output size in bytes @@ -54,24 +54,18 @@ func (d *state) BlockSize() int { return d.rate } func (d *state) Size() int { return d.outputLen } // Reset clears the internal state by zeroing the sponge state and -// the byte buffer, and setting Sponge.state to absorbing. +// the buffer indexes, and setting Sponge.state to absorbing. func (d *state) Reset() { // Zero the permutation's state. for i := range d.a { d.a[i] = 0 } d.state = spongeAbsorbing - d.buf = d.storage.asBytes()[:0] + d.i, d.n = 0, 0 } func (d *state) clone() *state { ret := *d - if ret.state == spongeAbsorbing { - ret.buf = ret.storage.asBytes()[:len(ret.buf)] - } else { - ret.buf = ret.storage.asBytes()[d.rate-cap(d.buf) : d.rate] - } - return &ret } @@ -82,43 +76,40 @@ func (d *state) permute() { case spongeAbsorbing: // If we're absorbing, we need to xor the input into the state // before applying the permutation. - xorIn(d, d.buf) - d.buf = d.storage.asBytes()[:0] + xorIn(d, d.storage[:d.rate]) + d.n = 0 keccakF1600(&d.a) case spongeSqueezing: // If we're squeezing, we need to apply the permutation before // copying more output. keccakF1600(&d.a) - d.buf = d.storage.asBytes()[:d.rate] - copyOut(d, d.buf) + d.i = 0 + copyOut(d, d.storage[:d.rate]) } } // pads appends the domain separation bits in dsbyte, applies // the multi-bitrate 10..1 padding rule, and permutes the state. -func (d *state) padAndPermute(dsbyte byte) { - if d.buf == nil { - d.buf = d.storage.asBytes()[:0] - } +func (d *state) padAndPermute() { // Pad with this instance's domain-separator bits. We know that there's // at least one byte of space in d.buf because, if it were full, // permute would have been called to empty it. dsbyte also contains the // first one bit for the padding. See the comment in the state struct. - d.buf = append(d.buf, dsbyte) - zerosStart := len(d.buf) - d.buf = d.storage.asBytes()[:d.rate] - for i := zerosStart; i < d.rate; i++ { - d.buf[i] = 0 + d.storage[d.n] = d.dsbyte + d.n++ + for d.n < d.rate { + d.storage[d.n] = 0 + d.n++ } // This adds the final one bit for the padding. Because of the way that // bits are numbered from the LSB upwards, the final bit is the MSB of // the last byte. - d.buf[d.rate-1] ^= 0x80 + d.storage[d.rate-1] ^= 0x80 // Apply the permutation d.permute() d.state = spongeSqueezing - d.buf = d.storage.asBytes()[:d.rate] - copyOut(d, d.buf) + d.n = d.rate + copyOut(d, d.storage[:d.rate]) } // Write absorbs more data into the hash's state. It panics if any @@ -127,28 +118,25 @@ func (d *state) Write(p []byte) (written int, err error) { if d.state != spongeAbsorbing { panic("sha3: Write after Read") } - if d.buf == nil { - d.buf = d.storage.asBytes()[:0] - } written = len(p) for len(p) > 0 { - if len(d.buf) == 0 && len(p) >= d.rate { + if d.n == 0 && len(p) >= d.rate { // The fast path; absorb a full "rate" bytes of input and apply the permutation. xorIn(d, p[:d.rate]) p = p[d.rate:] keccakF1600(&d.a) } else { // The slow path; buffer the input until we can fill the sponge, and then xor it in. - todo := d.rate - len(d.buf) + todo := d.rate - d.n if todo > len(p) { todo = len(p) } - d.buf = append(d.buf, p[:todo]...) + d.n += copy(d.storage[d.n:], p[:todo]) p = p[todo:] // If the sponge is full, apply the permutation. - if len(d.buf) == d.rate { + if d.n == d.rate { d.permute() } } @@ -161,19 +149,19 @@ func (d *state) Write(p []byte) (written int, err error) { func (d *state) Read(out []byte) (n int, err error) { // If we're still absorbing, pad and apply the permutation. if d.state == spongeAbsorbing { - d.padAndPermute(d.dsbyte) + d.padAndPermute() } n = len(out) // Now, do the squeezing. for len(out) > 0 { - n := copy(out, d.buf) - d.buf = d.buf[n:] + n := copy(out, d.storage[d.i:d.n]) + d.i += n out = out[n:] // Apply the permutation if we've squeezed the sponge dry. - if len(d.buf) == 0 { + if d.i == d.rate { d.permute() } } diff --git a/vendor/golang.org/x/crypto/sha3/sha3_s390x.go b/vendor/golang.org/x/crypto/sha3/sha3_s390x.go index b4fbbf8695..00d8034ae6 100644 --- a/vendor/golang.org/x/crypto/sha3/sha3_s390x.go +++ b/vendor/golang.org/x/crypto/sha3/sha3_s390x.go @@ -248,56 +248,56 @@ func (s *asmState) Clone() ShakeHash { return s.clone() } -// new224Asm returns an assembly implementation of SHA3-224 if available, -// otherwise it returns nil. -func new224Asm() hash.Hash { +// new224 returns an assembly implementation of SHA3-224 if available, +// otherwise it returns a generic implementation. +func new224() hash.Hash { if cpu.S390X.HasSHA3 { return newAsmState(sha3_224) } - return nil + return new224Generic() } -// new256Asm returns an assembly implementation of SHA3-256 if available, -// otherwise it returns nil. -func new256Asm() hash.Hash { +// new256 returns an assembly implementation of SHA3-256 if available, +// otherwise it returns a generic implementation. +func new256() hash.Hash { if cpu.S390X.HasSHA3 { return newAsmState(sha3_256) } - return nil + return new256Generic() } -// new384Asm returns an assembly implementation of SHA3-384 if available, -// otherwise it returns nil. -func new384Asm() hash.Hash { +// new384 returns an assembly implementation of SHA3-384 if available, +// otherwise it returns a generic implementation. +func new384() hash.Hash { if cpu.S390X.HasSHA3 { return newAsmState(sha3_384) } - return nil + return new384Generic() } -// new512Asm returns an assembly implementation of SHA3-512 if available, -// otherwise it returns nil. -func new512Asm() hash.Hash { +// new512 returns an assembly implementation of SHA3-512 if available, +// otherwise it returns a generic implementation. +func new512() hash.Hash { if cpu.S390X.HasSHA3 { return newAsmState(sha3_512) } - return nil + return new512Generic() } -// newShake128Asm returns an assembly implementation of SHAKE-128 if available, -// otherwise it returns nil. -func newShake128Asm() ShakeHash { +// newShake128 returns an assembly implementation of SHAKE-128 if available, +// otherwise it returns a generic implementation. +func newShake128() ShakeHash { if cpu.S390X.HasSHA3 { return newAsmState(shake_128) } - return nil + return newShake128Generic() } -// newShake256Asm returns an assembly implementation of SHAKE-256 if available, -// otherwise it returns nil. -func newShake256Asm() ShakeHash { +// newShake256 returns an assembly implementation of SHAKE-256 if available, +// otherwise it returns a generic implementation. +func newShake256() ShakeHash { if cpu.S390X.HasSHA3 { return newAsmState(shake_256) } - return nil + return newShake256Generic() } diff --git a/vendor/golang.org/x/crypto/sha3/shake.go b/vendor/golang.org/x/crypto/sha3/shake.go index bb69984027..1ea9275b8b 100644 --- a/vendor/golang.org/x/crypto/sha3/shake.go +++ b/vendor/golang.org/x/crypto/sha3/shake.go @@ -115,19 +115,21 @@ func (c *state) Clone() ShakeHash { // Its generic security strength is 128 bits against all attacks if at // least 32 bytes of its output are used. func NewShake128() ShakeHash { - if h := newShake128Asm(); h != nil { - return h - } - return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake} + return newShake128() } // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. // Its generic security strength is 256 bits against all attacks if // at least 64 bytes of its output are used. func NewShake256() ShakeHash { - if h := newShake256Asm(); h != nil { - return h - } + return newShake256() +} + +func newShake128Generic() *state { + return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake} +} + +func newShake256Generic() *state { return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake} } diff --git a/vendor/golang.org/x/crypto/sha3/shake_generic.go b/vendor/golang.org/x/crypto/sha3/shake_generic.go deleted file mode 100644 index 8d31cf5be2..0000000000 --- a/vendor/golang.org/x/crypto/sha3/shake_generic.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 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. - -//go:build !gc || purego || !s390x - -package sha3 - -// newShake128Asm returns an assembly implementation of SHAKE-128 if available, -// otherwise it returns nil. -func newShake128Asm() ShakeHash { - return nil -} - -// newShake256Asm returns an assembly implementation of SHAKE-256 if available, -// otherwise it returns nil. -func newShake256Asm() ShakeHash { - return nil -} diff --git a/vendor/golang.org/x/crypto/sha3/shake_noasm.go b/vendor/golang.org/x/crypto/sha3/shake_noasm.go new file mode 100644 index 0000000000..4276ba4ab2 --- /dev/null +++ b/vendor/golang.org/x/crypto/sha3/shake_noasm.go @@ -0,0 +1,15 @@ +// Copyright 2023 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. + +//go:build !gc || purego || !s390x + +package sha3 + +func newShake128() *state { + return newShake128Generic() +} + +func newShake256() *state { + return newShake256Generic() +} diff --git a/vendor/golang.org/x/crypto/sha3/xor.go b/vendor/golang.org/x/crypto/sha3/xor.go index 7337cca88e..6ada5c9574 100644 --- a/vendor/golang.org/x/crypto/sha3/xor.go +++ b/vendor/golang.org/x/crypto/sha3/xor.go @@ -2,22 +2,39 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!amd64 && !386 && !ppc64le) || purego - package sha3 -// A storageBuf is an aligned array of maxRate bytes. -type storageBuf [maxRate]byte - -func (b *storageBuf) asBytes() *[maxRate]byte { - return (*[maxRate]byte)(b) -} +import ( + "crypto/subtle" + "encoding/binary" + "unsafe" -var ( - xorIn = xorInGeneric - copyOut = copyOutGeneric - xorInUnaligned = xorInGeneric - copyOutUnaligned = copyOutGeneric + "golang.org/x/sys/cpu" ) -const xorImplementationUnaligned = "generic" +// xorIn xors the bytes in buf into the state. +func xorIn(d *state, buf []byte) { + if cpu.IsBigEndian { + for i := 0; len(buf) >= 8; i++ { + a := binary.LittleEndian.Uint64(buf) + d.a[i] ^= a + buf = buf[8:] + } + } else { + ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a)) + subtle.XORBytes(ab[:], ab[:], buf) + } +} + +// copyOut copies uint64s to a byte buffer. +func copyOut(d *state, b []byte) { + if cpu.IsBigEndian { + for i := 0; len(b) >= 8; i++ { + binary.LittleEndian.PutUint64(b, d.a[i]) + b = b[8:] + } + } else { + ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a)) + copy(b, ab[:]) + } +} diff --git a/vendor/golang.org/x/crypto/sha3/xor_generic.go b/vendor/golang.org/x/crypto/sha3/xor_generic.go deleted file mode 100644 index 8d94771127..0000000000 --- a/vendor/golang.org/x/crypto/sha3/xor_generic.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 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 sha3 - -import "encoding/binary" - -// xorInGeneric xors the bytes in buf into the state; it -// makes no non-portable assumptions about memory layout -// or alignment. -func xorInGeneric(d *state, buf []byte) { - n := len(buf) / 8 - - for i := 0; i < n; i++ { - a := binary.LittleEndian.Uint64(buf) - d.a[i] ^= a - buf = buf[8:] - } -} - -// copyOutGeneric copies uint64s to a byte buffer. -func copyOutGeneric(d *state, b []byte) { - for i := 0; len(b) >= 8; i++ { - binary.LittleEndian.PutUint64(b, d.a[i]) - b = b[8:] - } -} diff --git a/vendor/golang.org/x/crypto/sha3/xor_unaligned.go b/vendor/golang.org/x/crypto/sha3/xor_unaligned.go deleted file mode 100644 index 870e2d16e0..0000000000 --- a/vendor/golang.org/x/crypto/sha3/xor_unaligned.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 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. - -//go:build (amd64 || 386 || ppc64le) && !purego - -package sha3 - -import "unsafe" - -// A storageBuf is an aligned array of maxRate bytes. -type storageBuf [maxRate / 8]uint64 - -func (b *storageBuf) asBytes() *[maxRate]byte { - return (*[maxRate]byte)(unsafe.Pointer(b)) -} - -// xorInUnaligned uses unaligned reads and writes to update d.a to contain d.a -// XOR buf. -func xorInUnaligned(d *state, buf []byte) { - n := len(buf) - bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8] - if n >= 72 { - d.a[0] ^= bw[0] - d.a[1] ^= bw[1] - d.a[2] ^= bw[2] - d.a[3] ^= bw[3] - d.a[4] ^= bw[4] - d.a[5] ^= bw[5] - d.a[6] ^= bw[6] - d.a[7] ^= bw[7] - d.a[8] ^= bw[8] - } - if n >= 104 { - d.a[9] ^= bw[9] - d.a[10] ^= bw[10] - d.a[11] ^= bw[11] - d.a[12] ^= bw[12] - } - if n >= 136 { - d.a[13] ^= bw[13] - d.a[14] ^= bw[14] - d.a[15] ^= bw[15] - d.a[16] ^= bw[16] - } - if n >= 144 { - d.a[17] ^= bw[17] - } - if n >= 168 { - d.a[18] ^= bw[18] - d.a[19] ^= bw[19] - d.a[20] ^= bw[20] - } -} - -func copyOutUnaligned(d *state, buf []byte) { - ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) - copy(buf, ab[:]) -} - -var ( - xorIn = xorInUnaligned - copyOut = copyOutUnaligned -) - -const xorImplementationUnaligned = "unaligned" diff --git a/vendor/golang.org/x/crypto/ssh/agent/client.go b/vendor/golang.org/x/crypto/ssh/agent/client.go index fecba8eb38..106708d289 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/client.go +++ b/vendor/golang.org/x/crypto/ssh/agent/client.go @@ -10,7 +10,7 @@ // References: // // [PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00 -package agent // import "golang.org/x/crypto/ssh/agent" +package agent import ( "bytes" diff --git a/vendor/golang.org/x/crypto/ssh/agent/keyring.go b/vendor/golang.org/x/crypto/ssh/agent/keyring.go index 21bfa870fa..c1b4361087 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/keyring.go +++ b/vendor/golang.org/x/crypto/ssh/agent/keyring.go @@ -175,6 +175,15 @@ func (r *keyring) Add(key AddedKey) error { p.expire = &t } + // If we already have a Signer with the same public key, replace it with the + // new one. + for idx, k := range r.keys { + if bytes.Equal(k.signer.PublicKey().Marshal(), p.signer.PublicKey().Marshal()) { + r.keys[idx] = p + return nil + } + } + r.keys = append(r.keys, p) return nil diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index 9486c59862..b93961010d 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -71,6 +71,10 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { for auth := AuthMethod(new(noneAuth)); auth != nil; { ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions) if err != nil { + // On disconnect, return error immediately + if _, ok := err.(*disconnectMsg); ok { + return err + } // We return the error later if there is no other method left to // try. ok = authFailure diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go index edbe63340d..f5d352fe3a 100644 --- a/vendor/golang.org/x/crypto/ssh/doc.go +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -20,4 +20,4 @@ References: This package does not fall under the stability promise of the Go language itself, so its API may be changed when pressing needs arise. */ -package ssh // import "golang.org/x/crypto/ssh" +package ssh diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index df4ebdada5..98e6706d5d 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -488,7 +488,49 @@ func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { h := hash.New() h.Write(data) digest := h.Sum(nil) - return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob) + + // Signatures in PKCS1v15 must match the key's modulus in + // length. However with SSH, some signers provide RSA + // signatures which are missing the MSB 0's of the bignum + // represented. With ssh-rsa signatures, this is encouraged by + // the spec (even though e.g. OpenSSH will give the full + // length unconditionally). With rsa-sha2-* signatures, the + // verifier is allowed to support these, even though they are + // out of spec. See RFC 4253 Section 6.6 for ssh-rsa and RFC + // 8332 Section 3 for rsa-sha2-* details. + // + // In practice: + // * OpenSSH always allows "short" signatures: + // https://github.com/openssh/openssh-portable/blob/V_9_8_P1/ssh-rsa.c#L526 + // but always generates padded signatures: + // https://github.com/openssh/openssh-portable/blob/V_9_8_P1/ssh-rsa.c#L439 + // + // * PuTTY versions 0.81 and earlier will generate short + // signatures for all RSA signature variants. Note that + // PuTTY is embedded in other software, such as WinSCP and + // FileZilla. At the time of writing, a patch has been + // applied to PuTTY to generate padded signatures for + // rsa-sha2-*, but not yet released: + // https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=a5bcf3d384e1bf15a51a6923c3724cbbee022d8e + // + // * SSH.NET versions 2024.0.0 and earlier will generate short + // signatures for all RSA signature variants, fixed in 2024.1.0: + // https://github.com/sshnet/SSH.NET/releases/tag/2024.1.0 + // + // As a result, we pad these up to the key size by inserting + // leading 0's. + // + // Note that support for short signatures with rsa-sha2-* may + // be removed in the future due to such signatures not being + // allowed by the spec. + blob := sig.Blob + keySize := (*rsa.PublicKey)(r).Size() + if len(blob) < keySize { + padded := make([]byte, keySize) + copy(padded[keySize-len(blob):], blob) + blob = padded + } + return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, blob) } func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { @@ -904,6 +946,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error { return errors.New("ssh: signature did not verify") } +func (k *skECDSAPublicKey) CryptoPublicKey() crypto.PublicKey { + return &k.PublicKey +} + type skEd25519PublicKey struct { // application is a URL-like string, typically "ssh:" for SSH. // see openssh/PROTOCOL.u2f for details. @@ -1000,6 +1046,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error { return nil } +func (k *skEd25519PublicKey) CryptoPublicKey() crypto.PublicKey { + return k.PublicKey +} + // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, // *ecdsa.PrivateKey or any other crypto.Signer and returns a // corresponding Signer instance. ECDSA keys must use P-256, P-384 or diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index e2ae4f891b..3ca9e89e22 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -462,6 +462,24 @@ func (p *PartialSuccessError) Error() string { // It is returned in ServerAuthError.Errors from NewServerConn. var ErrNoAuth = errors.New("ssh: no auth passed yet") +// BannerError is an error that can be returned by authentication handlers in +// ServerConfig to send a banner message to the client. +type BannerError struct { + Err error + Message string +} + +func (b *BannerError) Unwrap() error { + return b.Err +} + +func (b *BannerError) Error() string { + if b.Err == nil { + return b.Message + } + return b.Err.Error() +} + func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { sessionID := s.transport.getSessionID() var cache pubKeyCache @@ -734,6 +752,18 @@ userAuthLoop: config.AuthLogCallback(s, userAuthReq.Method, authErr) } + var bannerErr *BannerError + if errors.As(authErr, &bannerErr) { + if bannerErr.Message != "" { + bannerMsg := &userAuthBannerMsg{ + Message: bannerErr.Message, + } + if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil { + return nil, err + } + } + } + if authErr == nil { break userAuthLoop } diff --git a/vendor/golang.org/x/mod/LICENSE b/vendor/golang.org/x/mod/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/mod/LICENSE +++ b/vendor/golang.org/x/mod/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/net/LICENSE +++ b/vendor/golang.org/x/net/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go index 6f2df28187..003e649f30 100644 --- a/vendor/golang.org/x/net/http2/http2.go +++ b/vendor/golang.org/x/net/http2/http2.go @@ -17,6 +17,7 @@ package http2 // import "golang.org/x/net/http2" import ( "bufio" + "context" "crypto/tls" "fmt" "io" @@ -26,6 +27,7 @@ import ( "strconv" "strings" "sync" + "time" "golang.org/x/net/http/httpguts" ) @@ -210,12 +212,6 @@ type stringWriter interface { WriteString(s string) (n int, err error) } -// A gate lets two goroutines coordinate their activities. -type gate chan struct{} - -func (g gate) Done() { g <- struct{}{} } -func (g gate) Wait() { <-g } - // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). type closeWaiter chan struct{} @@ -383,3 +379,14 @@ func validPseudoPath(v string) bool { // makes that struct also non-comparable, and generally doesn't add // any size (as long as it's first). type incomparable [0]func() + +// synctestGroupInterface is the methods of synctestGroup used by Server and Transport. +// It's defined as an interface here to let us keep synctestGroup entirely test-only +// and not a part of non-test builds. +type synctestGroupInterface interface { + Join() + Now() time.Time + NewTimer(d time.Duration) timer + AfterFunc(d time.Duration, f func()) timer + ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) +} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index c5d0810813..6c349f3ec6 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -154,6 +154,39 @@ type Server struct { // so that we don't embed a Mutex in this struct, which will make the // struct non-copyable, which might break some callers. state *serverInternalState + + // Synchronization group used for testing. + // Outside of tests, this is nil. + group synctestGroupInterface +} + +func (s *Server) markNewGoroutine() { + if s.group != nil { + s.group.Join() + } +} + +func (s *Server) now() time.Time { + if s.group != nil { + return s.group.Now() + } + return time.Now() +} + +// newTimer creates a new time.Timer, or a synthetic timer in tests. +func (s *Server) newTimer(d time.Duration) timer { + if s.group != nil { + return s.group.NewTimer(d) + } + return timeTimer{time.NewTimer(d)} +} + +// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. +func (s *Server) afterFunc(d time.Duration, f func()) timer { + if s.group != nil { + return s.group.AfterFunc(d, f) + } + return timeTimer{time.AfterFunc(d, f)} } func (s *Server) initialConnRecvWindowSize() int32 { @@ -400,6 +433,10 @@ func (o *ServeConnOpts) handler() http.Handler { // // The opts parameter is optional. If nil, default values are used. func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { + s.serveConn(c, opts, nil) +} + +func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverConn)) { baseCtx, cancel := serverConnBaseContext(c, opts) defer cancel() @@ -426,6 +463,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { pushEnabled: true, sawClientPreface: opts.SawClientPreface, } + if newf != nil { + newf(sc) + } s.state.registerConn(sc) defer s.state.unregisterConn(sc) @@ -599,8 +639,8 @@ type serverConn struct { inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop needToSendGoAway bool // we need to schedule a GOAWAY frame write goAwayCode ErrCode - shutdownTimer *time.Timer // nil until used - idleTimer *time.Timer // nil if unused + shutdownTimer timer // nil until used + idleTimer timer // nil if unused // Owned by the writeFrameAsync goroutine: headerWriteBuf bytes.Buffer @@ -649,12 +689,12 @@ type stream struct { flow outflow // limits writing from Handler to client inflow inflow // what the client is allowed to POST/etc to us state streamState - resetQueued bool // RST_STREAM queued for write; set by sc.resetStream - gotTrailerHeader bool // HEADER frame for trailers was seen - wroteHeaders bool // whether we wrote headers (not status 100) - readDeadline *time.Timer // nil if unused - writeDeadline *time.Timer // nil if unused - closeErr error // set before cw is closed + resetQueued bool // RST_STREAM queued for write; set by sc.resetStream + gotTrailerHeader bool // HEADER frame for trailers was seen + wroteHeaders bool // whether we wrote headers (not status 100) + readDeadline timer // nil if unused + writeDeadline timer // nil if unused + closeErr error // set before cw is closed trailer http.Header // accumulated trailers reqTrailer http.Header // handler's Request.Trailer @@ -811,8 +851,9 @@ type readFrameResult struct { // consumer is done with the frame. // It's run on its own goroutine. func (sc *serverConn) readFrames() { - gate := make(gate) - gateDone := gate.Done + sc.srv.markNewGoroutine() + gate := make(chan struct{}) + gateDone := func() { gate <- struct{}{} } for { f, err := sc.framer.ReadFrame() select { @@ -843,6 +884,7 @@ type frameWriteResult struct { // At most one goroutine can be running writeFrameAsync at a time per // serverConn. func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) { + sc.srv.markNewGoroutine() var err error if wd == nil { err = wr.write.writeFrame(sc) @@ -922,13 +964,13 @@ func (sc *serverConn) serve() { sc.setConnState(http.StateIdle) if sc.srv.IdleTimeout > 0 { - sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) + sc.idleTimer = sc.srv.afterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) defer sc.idleTimer.Stop() } go sc.readFrames() // closed by defer sc.conn.Close above - settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer) + settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer) defer settingsTimer.Stop() loopNum := 0 @@ -1057,10 +1099,10 @@ func (sc *serverConn) readPreface() error { errc <- nil } }() - timer := time.NewTimer(prefaceTimeout) // TODO: configurable on *Server? + timer := sc.srv.newTimer(prefaceTimeout) // TODO: configurable on *Server? defer timer.Stop() select { - case <-timer.C: + case <-timer.C(): return errPrefaceTimeout case err := <-errc: if err == nil { @@ -1425,7 +1467,7 @@ func (sc *serverConn) goAway(code ErrCode) { func (sc *serverConn) shutDownIn(d time.Duration) { sc.serveG.check() - sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer) + sc.shutdownTimer = sc.srv.afterFunc(d, sc.onShutdownTimer) } func (sc *serverConn) resetStream(se StreamError) { @@ -1639,7 +1681,7 @@ func (sc *serverConn) closeStream(st *stream, err error) { delete(sc.streams, st.id) if len(sc.streams) == 0 { sc.setConnState(http.StateIdle) - if sc.srv.IdleTimeout > 0 { + if sc.srv.IdleTimeout > 0 && sc.idleTimer != nil { sc.idleTimer.Reset(sc.srv.IdleTimeout) } if h1ServerKeepAlivesDisabled(sc.hs) { @@ -1661,6 +1703,7 @@ func (sc *serverConn) closeStream(st *stream, err error) { } } st.closeErr = err + st.cancelCtx() st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc sc.writeSched.CloseStream(st.id) } @@ -2021,7 +2064,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { // (in Go 1.8), though. That's a more sane option anyway. if sc.hs.ReadTimeout > 0 { sc.conn.SetReadDeadline(time.Time{}) - st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout) + st.readDeadline = sc.srv.afterFunc(sc.hs.ReadTimeout, st.onReadTimeout) } return sc.scheduleHandler(id, rw, req, handler) @@ -2119,7 +2162,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream st.flow.add(sc.initialStreamSendWindowSize) st.inflow.init(sc.srv.initialStreamRecvWindowSize()) if sc.hs.WriteTimeout > 0 { - st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) + st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } sc.streams[id] = st @@ -2343,6 +2386,7 @@ func (sc *serverConn) handlerDone() { // Run on its own goroutine. func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) { + sc.srv.markNewGoroutine() defer sc.sendServeMsg(handlerDoneMsg) didPanic := true defer func() { @@ -2639,7 +2683,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { var date string if _, ok := rws.snapHeader["Date"]; !ok { // TODO(bradfitz): be faster here, like net/http? measure. - date = time.Now().UTC().Format(http.TimeFormat) + date = rws.conn.srv.now().UTC().Format(http.TimeFormat) } for _, v := range rws.snapHeader["Trailer"] { @@ -2761,7 +2805,7 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() { func (w *responseWriter) SetReadDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(time.Now()) { + if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onReadTimeout() @@ -2777,9 +2821,9 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error { if deadline.IsZero() { st.readDeadline = nil } else if st.readDeadline == nil { - st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout) + st.readDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onReadTimeout) } else { - st.readDeadline.Reset(deadline.Sub(time.Now())) + st.readDeadline.Reset(deadline.Sub(sc.srv.now())) } }) return nil @@ -2787,7 +2831,7 @@ func (w *responseWriter) SetReadDeadline(deadline time.Time) error { func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { st := w.rws.stream - if !deadline.IsZero() && deadline.Before(time.Now()) { + if !deadline.IsZero() && deadline.Before(w.rws.conn.srv.now()) { // If we're setting a deadline in the past, reset the stream immediately // so writes after SetWriteDeadline returns will fail. st.onWriteTimeout() @@ -2803,9 +2847,9 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { if deadline.IsZero() { st.writeDeadline = nil } else if st.writeDeadline == nil { - st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout) + st.writeDeadline = sc.srv.afterFunc(deadline.Sub(sc.srv.now()), st.onWriteTimeout) } else { - st.writeDeadline.Reset(deadline.Sub(time.Now())) + st.writeDeadline.Reset(deadline.Sub(sc.srv.now())) } }) return nil diff --git a/vendor/golang.org/x/net/http2/testsync.go b/vendor/golang.org/x/net/http2/testsync.go deleted file mode 100644 index 61075bd16d..0000000000 --- a/vendor/golang.org/x/net/http2/testsync.go +++ /dev/null @@ -1,331 +0,0 @@ -// 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 http2 - -import ( - "context" - "sync" - "time" -) - -// testSyncHooks coordinates goroutines in tests. -// -// For example, a call to ClientConn.RoundTrip involves several goroutines, including: -// - the goroutine running RoundTrip; -// - the clientStream.doRequest goroutine, which writes the request; and -// - the clientStream.readLoop goroutine, which reads the response. -// -// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines -// are blocked waiting for some condition such as reading the Request.Body or waiting for -// flow control to become available. -// -// The testSyncHooks also manage timers and synthetic time in tests. -// This permits us to, for example, start a request and cause it to time out waiting for -// response headers without resorting to time.Sleep calls. -type testSyncHooks struct { - // active/inactive act as a mutex and condition variable. - // - // - neither chan contains a value: testSyncHooks is locked. - // - active contains a value: unlocked, and at least one goroutine is not blocked - // - inactive contains a value: unlocked, and all goroutines are blocked - active chan struct{} - inactive chan struct{} - - // goroutine counts - total int // total goroutines - condwait map[*sync.Cond]int // blocked in sync.Cond.Wait - blocked []*testBlockedGoroutine // otherwise blocked - - // fake time - now time.Time - timers []*fakeTimer - - // Transport testing: Report various events. - newclientconn func(*ClientConn) - newstream func(*clientStream) -} - -// testBlockedGoroutine is a blocked goroutine. -type testBlockedGoroutine struct { - f func() bool // blocked until f returns true - ch chan struct{} // closed when unblocked -} - -func newTestSyncHooks() *testSyncHooks { - h := &testSyncHooks{ - active: make(chan struct{}, 1), - inactive: make(chan struct{}, 1), - condwait: map[*sync.Cond]int{}, - } - h.inactive <- struct{}{} - h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) - return h -} - -// lock acquires the testSyncHooks mutex. -func (h *testSyncHooks) lock() { - select { - case <-h.active: - case <-h.inactive: - } -} - -// waitInactive waits for all goroutines to become inactive. -func (h *testSyncHooks) waitInactive() { - for { - <-h.inactive - if !h.unlock() { - break - } - } -} - -// unlock releases the testSyncHooks mutex. -// It reports whether any goroutines are active. -func (h *testSyncHooks) unlock() (active bool) { - // Look for a blocked goroutine which can be unblocked. - blocked := h.blocked[:0] - unblocked := false - for _, b := range h.blocked { - if !unblocked && b.f() { - unblocked = true - close(b.ch) - } else { - blocked = append(blocked, b) - } - } - h.blocked = blocked - - // Count goroutines blocked on condition variables. - condwait := 0 - for _, count := range h.condwait { - condwait += count - } - - if h.total > condwait+len(blocked) { - h.active <- struct{}{} - return true - } else { - h.inactive <- struct{}{} - return false - } -} - -// goRun starts a new goroutine. -func (h *testSyncHooks) goRun(f func()) { - h.lock() - h.total++ - h.unlock() - go func() { - defer func() { - h.lock() - h.total-- - h.unlock() - }() - f() - }() -} - -// blockUntil indicates that a goroutine is blocked waiting for some condition to become true. -// It waits until f returns true before proceeding. -// -// Example usage: -// -// h.blockUntil(func() bool { -// // Is the context done yet? -// select { -// case <-ctx.Done(): -// default: -// return false -// } -// return true -// }) -// // Wait for the context to become done. -// <-ctx.Done() -// -// The function f passed to blockUntil must be non-blocking and idempotent. -func (h *testSyncHooks) blockUntil(f func() bool) { - if f() { - return - } - ch := make(chan struct{}) - h.lock() - h.blocked = append(h.blocked, &testBlockedGoroutine{ - f: f, - ch: ch, - }) - h.unlock() - <-ch -} - -// broadcast is sync.Cond.Broadcast. -func (h *testSyncHooks) condBroadcast(cond *sync.Cond) { - h.lock() - delete(h.condwait, cond) - h.unlock() - cond.Broadcast() -} - -// broadcast is sync.Cond.Wait. -func (h *testSyncHooks) condWait(cond *sync.Cond) { - h.lock() - h.condwait[cond]++ - h.unlock() -} - -// newTimer creates a new fake timer. -func (h *testSyncHooks) newTimer(d time.Duration) timer { - h.lock() - defer h.unlock() - t := &fakeTimer{ - hooks: h, - when: h.now.Add(d), - c: make(chan time.Time), - } - h.timers = append(h.timers, t) - return t -} - -// afterFunc creates a new fake AfterFunc timer. -func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer { - h.lock() - defer h.unlock() - t := &fakeTimer{ - hooks: h, - when: h.now.Add(d), - f: f, - } - h.timers = append(h.timers, t) - return t -} - -func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { - ctx, cancel := context.WithCancel(ctx) - t := h.afterFunc(d, cancel) - return ctx, func() { - t.Stop() - cancel() - } -} - -func (h *testSyncHooks) timeUntilEvent() time.Duration { - h.lock() - defer h.unlock() - var next time.Time - for _, t := range h.timers { - if next.IsZero() || t.when.Before(next) { - next = t.when - } - } - if d := next.Sub(h.now); d > 0 { - return d - } - return 0 -} - -// advance advances time and causes synthetic timers to fire. -func (h *testSyncHooks) advance(d time.Duration) { - h.lock() - defer h.unlock() - h.now = h.now.Add(d) - timers := h.timers[:0] - for _, t := range h.timers { - t := t // remove after go.mod depends on go1.22 - t.mu.Lock() - switch { - case t.when.After(h.now): - timers = append(timers, t) - case t.when.IsZero(): - // stopped timer - default: - t.when = time.Time{} - if t.c != nil { - close(t.c) - } - if t.f != nil { - h.total++ - go func() { - defer func() { - h.lock() - h.total-- - h.unlock() - }() - t.f() - }() - } - } - t.mu.Unlock() - } - h.timers = timers -} - -// A timer wraps a time.Timer, or a synthetic equivalent in tests. -// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires. -type timer interface { - C() <-chan time.Time - Stop() bool - Reset(d time.Duration) bool -} - -// timeTimer implements timer using real time. -type timeTimer struct { - t *time.Timer - c chan time.Time -} - -// newTimeTimer creates a new timer using real time. -func newTimeTimer(d time.Duration) timer { - ch := make(chan time.Time) - t := time.AfterFunc(d, func() { - close(ch) - }) - return &timeTimer{t, ch} -} - -// newTimeAfterFunc creates an AfterFunc timer using real time. -func newTimeAfterFunc(d time.Duration, f func()) timer { - return &timeTimer{ - t: time.AfterFunc(d, f), - } -} - -func (t timeTimer) C() <-chan time.Time { return t.c } -func (t timeTimer) Stop() bool { return t.t.Stop() } -func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) } - -// fakeTimer implements timer using fake time. -type fakeTimer struct { - hooks *testSyncHooks - - mu sync.Mutex - when time.Time // when the timer will fire - c chan time.Time // closed when the timer fires; mutually exclusive with f - f func() // called when the timer fires; mutually exclusive with c -} - -func (t *fakeTimer) C() <-chan time.Time { return t.c } - -func (t *fakeTimer) Stop() bool { - t.mu.Lock() - defer t.mu.Unlock() - stopped := t.when.IsZero() - t.when = time.Time{} - return stopped -} - -func (t *fakeTimer) Reset(d time.Duration) bool { - if t.c != nil || t.f == nil { - panic("fakeTimer only supports Reset on AfterFunc timers") - } - t.mu.Lock() - defer t.mu.Unlock() - t.hooks.lock() - defer t.hooks.unlock() - active := !t.when.IsZero() - t.when = t.hooks.now.Add(d) - if !active { - t.hooks.timers = append(t.hooks.timers, t) - } - return active -} diff --git a/vendor/golang.org/x/net/http2/timer.go b/vendor/golang.org/x/net/http2/timer.go new file mode 100644 index 0000000000..0b1c17b812 --- /dev/null +++ b/vendor/golang.org/x/net/http2/timer.go @@ -0,0 +1,20 @@ +// 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 http2 + +import "time" + +// A timer is a time.Timer, as an interface which can be replaced in tests. +type timer = interface { + C() <-chan time.Time + Reset(d time.Duration) bool + Stop() bool +} + +// timeTimer adapts a time.Timer to the timer interface. +type timeTimer struct { + *time.Timer +} + +func (t timeTimer) C() <-chan time.Time { return t.Timer.C } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 2fa49490c9..61f511f97a 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -185,7 +185,45 @@ type Transport struct { connPoolOnce sync.Once connPoolOrDef ClientConnPool // non-nil version of ConnPool - syncHooks *testSyncHooks + *transportTestHooks +} + +// Hook points used for testing. +// Outside of tests, t.transportTestHooks is nil and these all have minimal implementations. +// Inside tests, see the testSyncHooks function docs. + +type transportTestHooks struct { + newclientconn func(*ClientConn) + group synctestGroupInterface +} + +func (t *Transport) markNewGoroutine() { + if t != nil && t.transportTestHooks != nil { + t.transportTestHooks.group.Join() + } +} + +// newTimer creates a new time.Timer, or a synthetic timer in tests. +func (t *Transport) newTimer(d time.Duration) timer { + if t.transportTestHooks != nil { + return t.transportTestHooks.group.NewTimer(d) + } + return timeTimer{time.NewTimer(d)} +} + +// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. +func (t *Transport) afterFunc(d time.Duration, f func()) timer { + if t.transportTestHooks != nil { + return t.transportTestHooks.group.AfterFunc(d, f) + } + return timeTimer{time.AfterFunc(d, f)} +} + +func (t *Transport) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { + if t.transportTestHooks != nil { + return t.transportTestHooks.group.ContextWithTimeout(ctx, d) + } + return context.WithTimeout(ctx, d) } func (t *Transport) maxHeaderListSize() uint32 { @@ -352,60 +390,6 @@ type ClientConn struct { werr error // first write error that has occurred hbuf bytes.Buffer // HPACK encoder writes into this henc *hpack.Encoder - - syncHooks *testSyncHooks // can be nil -} - -// Hook points used for testing. -// Outside of tests, cc.syncHooks is nil and these all have minimal implementations. -// Inside tests, see the testSyncHooks function docs. - -// goRun starts a new goroutine. -func (cc *ClientConn) goRun(f func()) { - if cc.syncHooks != nil { - cc.syncHooks.goRun(f) - return - } - go f() -} - -// condBroadcast is cc.cond.Broadcast. -func (cc *ClientConn) condBroadcast() { - if cc.syncHooks != nil { - cc.syncHooks.condBroadcast(cc.cond) - } - cc.cond.Broadcast() -} - -// condWait is cc.cond.Wait. -func (cc *ClientConn) condWait() { - if cc.syncHooks != nil { - cc.syncHooks.condWait(cc.cond) - } - cc.cond.Wait() -} - -// newTimer creates a new time.Timer, or a synthetic timer in tests. -func (cc *ClientConn) newTimer(d time.Duration) timer { - if cc.syncHooks != nil { - return cc.syncHooks.newTimer(d) - } - return newTimeTimer(d) -} - -// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests. -func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer { - if cc.syncHooks != nil { - return cc.syncHooks.afterFunc(d, f) - } - return newTimeAfterFunc(d, f) -} - -func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) { - if cc.syncHooks != nil { - return cc.syncHooks.contextWithTimeout(ctx, d) - } - return context.WithTimeout(ctx, d) } // clientStream is the state for a single HTTP/2 stream. One of these @@ -487,7 +471,7 @@ func (cs *clientStream) abortStreamLocked(err error) { // TODO(dneil): Clean up tests where cs.cc.cond is nil. if cs.cc.cond != nil { // Wake up writeRequestBody if it is waiting on flow control. - cs.cc.condBroadcast() + cs.cc.cond.Broadcast() } } @@ -497,7 +481,7 @@ func (cs *clientStream) abortRequestBodyWrite() { defer cc.mu.Unlock() if cs.reqBody != nil && cs.reqBodyClosed == nil { cs.closeReqBodyLocked() - cc.condBroadcast() + cc.cond.Broadcast() } } @@ -507,10 +491,11 @@ func (cs *clientStream) closeReqBodyLocked() { } cs.reqBodyClosed = make(chan struct{}) reqBodyClosed := cs.reqBodyClosed - cs.cc.goRun(func() { + go func() { + cs.cc.t.markNewGoroutine() cs.reqBody.Close() close(reqBodyClosed) - }) + }() } type stickyErrWriter struct { @@ -626,21 +611,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) d := time.Second * time.Duration(backoff) - var tm timer - if t.syncHooks != nil { - tm = t.syncHooks.newTimer(d) - t.syncHooks.blockUntil(func() bool { - select { - case <-tm.C(): - case <-req.Context().Done(): - default: - return false - } - return true - }) - } else { - tm = newTimeTimer(d) - } + tm := t.newTimer(d) select { case <-tm.C(): t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) @@ -725,8 +696,8 @@ func canRetryError(err error) bool { } func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { - if t.syncHooks != nil { - return t.newClientConn(nil, singleUse, t.syncHooks) + if t.transportTestHooks != nil { + return t.newClientConn(nil, singleUse) } host, _, err := net.SplitHostPort(addr) if err != nil { @@ -736,7 +707,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b if err != nil { return nil, err } - return t.newClientConn(tconn, singleUse, nil) + return t.newClientConn(tconn, singleUse) } func (t *Transport) newTLSConfig(host string) *tls.Config { @@ -802,10 +773,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 { } func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { - return t.newClientConn(c, t.disableKeepAlives(), nil) + return t.newClientConn(c, t.disableKeepAlives()) } -func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) { +func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { cc := &ClientConn{ t: t, tconn: c, @@ -820,16 +791,12 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo wantSettingsAck: true, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), - syncHooks: hooks, } - if hooks != nil { - hooks.newclientconn(cc) + if t.transportTestHooks != nil { + t.markNewGoroutine() + t.transportTestHooks.newclientconn(cc) c = cc.tconn } - if d := t.idleConnTimeout(); d != 0 { - cc.idleTimeout = d - cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout) - } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) } @@ -860,10 +827,6 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo cc.henc.SetMaxDynamicTableSizeLimit(t.maxEncoderHeaderTableSize()) cc.peerMaxHeaderTableSize = initialHeaderTableSize - if t.AllowHTTP { - cc.nextStreamID = 3 - } - if cs, ok := c.(connectionStater); ok { state := cs.ConnectionState() cc.tlsState = &state @@ -893,7 +856,13 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo return nil, cc.werr } - cc.goRun(cc.readLoop) + // Start the idle timer after the connection is fully initialized. + if d := t.idleConnTimeout(); d != 0 { + cc.idleTimeout = d + cc.idleTimer = t.afterFunc(d, cc.onIdleTimeout) + } + + go cc.readLoop() return cc, nil } @@ -901,7 +870,7 @@ func (cc *ClientConn) healthCheck() { pingTimeout := cc.t.pingTimeout() // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. - ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout) + ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout) defer cancel() cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) @@ -1144,7 +1113,8 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { // Wait for all in-flight streams to complete or connection to close done := make(chan struct{}) cancelled := false // guarded by cc.mu - cc.goRun(func() { + go func() { + cc.t.markNewGoroutine() cc.mu.Lock() defer cc.mu.Unlock() for { @@ -1156,9 +1126,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { if cancelled { break } - cc.condWait() + cc.cond.Wait() } - }) + }() shutdownEnterWaitStateHook() select { case <-done: @@ -1168,7 +1138,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { cc.mu.Lock() // Free the goroutine above cancelled = true - cc.condBroadcast() + cc.cond.Broadcast() cc.mu.Unlock() return ctx.Err() } @@ -1206,7 +1176,7 @@ func (cc *ClientConn) closeForError(err error) { for _, cs := range cc.streams { cs.abortStreamLocked(err) } - cc.condBroadcast() + cc.cond.Broadcast() cc.mu.Unlock() cc.closeConn() } @@ -1321,23 +1291,30 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) respHeaderRecv: make(chan struct{}), donec: make(chan struct{}), } - cc.goRun(func() { - cs.doRequest(req) - }) + + // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? + if !cc.t.disableCompression() && + req.Header.Get("Accept-Encoding") == "" && + req.Header.Get("Range") == "" && + !cs.isHead { + // Request gzip only, not deflate. Deflate is ambiguous and + // not as universally supported anyway. + // See: https://zlib.net/zlib_faq.html#faq39 + // + // Note that we don't request this for HEAD requests, + // due to a bug in nginx: + // http://trac.nginx.org/nginx/ticket/358 + // https://golang.org/issue/5522 + // + // We don't request gzip if the request is for a range, since + // auto-decoding a portion of a gzipped document will just fail + // anyway. See https://golang.org/issue/8923 + cs.requestedGzip = true + } + + go cs.doRequest(req, streamf) waitDone := func() error { - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-cs.donec: - case <-ctx.Done(): - case <-cs.reqCancel: - default: - return false - } - return true - }) - } select { case <-cs.donec: return nil @@ -1398,24 +1375,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) return err } - if streamf != nil { - streamf(cs) - } - for { - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-cs.respHeaderRecv: - case <-cs.abort: - case <-ctx.Done(): - case <-cs.reqCancel: - default: - return false - } - return true - }) - } select { case <-cs.respHeaderRecv: return handleResponseHeaders() @@ -1445,8 +1405,9 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) // doRequest runs for the duration of the request lifetime. // // It sends the request and performs post-request cleanup (closing Request.Body, etc.). -func (cs *clientStream) doRequest(req *http.Request) { - err := cs.writeRequest(req) +func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)) { + cs.cc.t.markNewGoroutine() + err := cs.writeRequest(req, streamf) cs.cleanupWriteRequest(err) } @@ -1457,7 +1418,7 @@ func (cs *clientStream) doRequest(req *http.Request) { // // It returns non-nil if the request ends otherwise. // If the returned error is StreamError, the error Code may be used in resetting the stream. -func (cs *clientStream) writeRequest(req *http.Request) (err error) { +func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStream)) (err error) { cc := cs.cc ctx := cs.ctx @@ -1471,21 +1432,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { if cc.reqHeaderMu == nil { panic("RoundTrip on uninitialized ClientConn") // for tests } - var newStreamHook func(*clientStream) - if cc.syncHooks != nil { - newStreamHook = cc.syncHooks.newstream - cc.syncHooks.blockUntil(func() bool { - select { - case cc.reqHeaderMu <- struct{}{}: - <-cc.reqHeaderMu - case <-cs.reqCancel: - case <-ctx.Done(): - default: - return false - } - return true - }) - } select { case cc.reqHeaderMu <- struct{}{}: case <-cs.reqCancel: @@ -1510,28 +1456,8 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { } cc.mu.Unlock() - if newStreamHook != nil { - newStreamHook(cs) - } - - // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? - if !cc.t.disableCompression() && - req.Header.Get("Accept-Encoding") == "" && - req.Header.Get("Range") == "" && - !cs.isHead { - // Request gzip only, not deflate. Deflate is ambiguous and - // not as universally supported anyway. - // See: https://zlib.net/zlib_faq.html#faq39 - // - // Note that we don't request this for HEAD requests, - // due to a bug in nginx: - // http://trac.nginx.org/nginx/ticket/358 - // https://golang.org/issue/5522 - // - // We don't request gzip if the request is for a range, since - // auto-decoding a portion of a gzipped document will just fail - // anyway. See https://golang.org/issue/8923 - cs.requestedGzip = true + if streamf != nil { + streamf(cs) } continueTimeout := cc.t.expectContinueTimeout() @@ -1594,7 +1520,7 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { var respHeaderTimer <-chan time.Time var respHeaderRecv chan struct{} if d := cc.responseHeaderTimeout(); d != 0 { - timer := cc.newTimer(d) + timer := cc.t.newTimer(d) defer timer.Stop() respHeaderTimer = timer.C() respHeaderRecv = cs.respHeaderRecv @@ -1603,21 +1529,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) { // or until the request is aborted (via context, error, or otherwise), // whichever comes first. for { - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-cs.peerClosed: - case <-respHeaderTimer: - case <-respHeaderRecv: - case <-cs.abort: - case <-ctx.Done(): - case <-cs.reqCancel: - default: - return false - } - return true - }) - } select { case <-cs.peerClosed: return nil @@ -1766,7 +1677,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { return nil } cc.pendingRequests++ - cc.condWait() + cc.cond.Wait() cc.pendingRequests-- select { case <-cs.abort: @@ -2028,7 +1939,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) cs.flow.take(take) return take, nil } - cc.condWait() + cc.cond.Wait() } } @@ -2311,7 +2222,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) { } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. - cc.condBroadcast() + cc.cond.Broadcast() closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 { @@ -2333,6 +2244,7 @@ type clientConnReadLoop struct { // readLoop runs in its own goroutine and reads and dispatches frames. func (cc *ClientConn) readLoop() { + cc.t.markNewGoroutine() rl := &clientConnReadLoop{cc: cc} defer rl.cleanup() cc.readerErr = rl.run() @@ -2399,7 +2311,7 @@ func (rl *clientConnReadLoop) cleanup() { cs.abortStreamLocked(err) } } - cc.condBroadcast() + cc.cond.Broadcast() cc.mu.Unlock() } @@ -2436,7 +2348,7 @@ func (rl *clientConnReadLoop) run() error { readIdleTimeout := cc.t.ReadIdleTimeout var t timer if readIdleTimeout != 0 { - t = cc.afterFunc(readIdleTimeout, cc.healthCheck) + t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck) } for { f, err := cc.fr.ReadFrame() @@ -3034,7 +2946,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { for _, cs := range cc.streams { cs.flow.add(delta) } - cc.condBroadcast() + cc.cond.Broadcast() cc.initialWindowSize = s.Val case SettingHeaderTableSize: @@ -3089,7 +3001,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { return ConnectionError(ErrCodeFlowControl) } - cc.condBroadcast() + cc.cond.Broadcast() return nil } @@ -3133,7 +3045,8 @@ func (cc *ClientConn) Ping(ctx context.Context) error { } var pingError error errc := make(chan struct{}) - cc.goRun(func() { + go func() { + cc.t.markNewGoroutine() cc.wmu.Lock() defer cc.wmu.Unlock() if pingError = cc.fr.WritePing(false, p); pingError != nil { @@ -3144,20 +3057,7 @@ func (cc *ClientConn) Ping(ctx context.Context) error { close(errc) return } - }) - if cc.syncHooks != nil { - cc.syncHooks.blockUntil(func() bool { - select { - case <-c: - case <-errc: - case <-ctx.Done(): - case <-cc.readerDone: - default: - return false - } - return true - }) - } + }() select { case <-c: return nil diff --git a/vendor/golang.org/x/net/http2/writesched_priority.go b/vendor/golang.org/x/net/http2/writesched_priority.go index 0a242c669e..f6783339d1 100644 --- a/vendor/golang.org/x/net/http2/writesched_priority.go +++ b/vendor/golang.org/x/net/http2/writesched_priority.go @@ -443,8 +443,8 @@ func (ws *priorityWriteScheduler) addClosedOrIdleNode(list *[]*priorityNode, max } func (ws *priorityWriteScheduler) removeNode(n *priorityNode) { - for k := n.kids; k != nil; k = k.next { - k.setParent(n.parent) + for n.kids != nil { + n.kids.setParent(n.parent) } n.setParent(nil) delete(ws.nodes, n.id) diff --git a/vendor/golang.org/x/net/proxy/per_host.go b/vendor/golang.org/x/net/proxy/per_host.go index 573fe79e86..d7d4b8b6e3 100644 --- a/vendor/golang.org/x/net/proxy/per_host.go +++ b/vendor/golang.org/x/net/proxy/per_host.go @@ -137,9 +137,7 @@ func (p *PerHost) AddNetwork(net *net.IPNet) { // AddZone specifies a DNS suffix that will use the bypass proxy. A zone of // "example.com" matches "example.com" and all of its subdomains. func (p *PerHost) AddZone(zone string) { - if strings.HasSuffix(zone, ".") { - zone = zone[:len(zone)-1] - } + zone = strings.TrimSuffix(zone, ".") if !strings.HasPrefix(zone, ".") { zone = "." + zone } @@ -148,8 +146,6 @@ func (p *PerHost) AddZone(zone string) { // AddHost specifies a host name that will use the bypass proxy. func (p *PerHost) AddHost(host string) { - if strings.HasSuffix(host, ".") { - host = host[:len(host)-1] - } + host = strings.TrimSuffix(host, ".") p.bypassHosts = append(p.bypassHosts, host) } diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/sync/LICENSE +++ b/vendor/golang.org/x/sync/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go index b18efb743f..948a3ee63d 100644 --- a/vendor/golang.org/x/sync/errgroup/errgroup.go +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -4,6 +4,9 @@ // Package errgroup provides synchronization, error propagation, and Context // cancelation for groups of goroutines working on subtasks of a common task. +// +// [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks +// returning errors. package errgroup import ( diff --git a/vendor/golang.org/x/sync/errgroup/go120.go b/vendor/golang.org/x/sync/errgroup/go120.go index 7d419d3760..f93c740b63 100644 --- a/vendor/golang.org/x/sync/errgroup/go120.go +++ b/vendor/golang.org/x/sync/errgroup/go120.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.20 -// +build go1.20 package errgroup diff --git a/vendor/golang.org/x/sync/errgroup/pre_go120.go b/vendor/golang.org/x/sync/errgroup/pre_go120.go index 1795c18ace..88ce33434e 100644 --- a/vendor/golang.org/x/sync/errgroup/pre_go120.go +++ b/vendor/golang.org/x/sync/errgroup/pre_go120.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.20 -// +build !go1.20 package errgroup diff --git a/vendor/golang.org/x/sys/LICENSE b/vendor/golang.org/x/sys/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/sys/LICENSE +++ b/vendor/golang.org/x/sys/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go index 8fa707aa4b..02609d5b21 100644 --- a/vendor/golang.org/x/sys/cpu/cpu.go +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -105,6 +105,8 @@ var ARM64 struct { HasSVE bool // Scalable Vector Extensions HasSVE2 bool // Scalable Vector Extensions 2 HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32 + HasDIT bool // Data Independent Timing support + HasI8MM bool // Advanced SIMD Int8 matrix multiplication instructions _ CacheLinePad } @@ -199,6 +201,25 @@ var S390X struct { _ CacheLinePad } +// RISCV64 contains the supported CPU features and performance characteristics for riscv64 +// platforms. The booleans in RISCV64, with the exception of HasFastMisaligned, indicate +// the presence of RISC-V extensions. +// +// It is safe to assume that all the RV64G extensions are supported and so they are omitted from +// this structure. As riscv64 Go programs require at least RV64G, the code that populates +// this structure cannot run successfully if some of the RV64G extensions are missing. +// The struct is padded to avoid false sharing. +var RISCV64 struct { + _ CacheLinePad + HasFastMisaligned bool // Fast misaligned accesses + HasC bool // Compressed instruction-set extension + HasV bool // Vector extension compatible with RVV 1.0 + HasZba bool // Address generation instructions extension + HasZbb bool // Basic bit-manipulation extension + HasZbs bool // Single-bit instructions extension + _ CacheLinePad +} + func init() { archInit() initOptions() diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_arm64.go index 0e27a21e1f..af2aa99f9f 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.go @@ -38,6 +38,8 @@ func initOptions() { {Name: "dcpop", Feature: &ARM64.HasDCPOP}, {Name: "asimddp", Feature: &ARM64.HasASIMDDP}, {Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM}, + {Name: "dit", Feature: &ARM64.HasDIT}, + {Name: "i8mm", Feature: &ARM64.HasI8MM}, } } @@ -145,6 +147,11 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { ARM64.HasLRCPC = true } + switch extractBits(isar1, 52, 55) { + case 1: + ARM64.HasI8MM = true + } + // ID_AA64PFR0_EL1 switch extractBits(pfr0, 16, 19) { case 0: @@ -168,6 +175,11 @@ func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) { parseARM64SVERegister(getzfr0()) } + + switch extractBits(pfr0, 48, 51) { + case 1: + ARM64.HasDIT = true + } } func parseARM64SVERegister(zfr0 uint64) { diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go index 3d386d0fc2..08f35ea177 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go @@ -35,8 +35,10 @@ const ( hwcap_SHA512 = 1 << 21 hwcap_SVE = 1 << 22 hwcap_ASIMDFHM = 1 << 23 + hwcap_DIT = 1 << 24 hwcap2_SVE2 = 1 << 1 + hwcap2_I8MM = 1 << 13 ) // linuxKernelCanEmulateCPUID reports whether we're running @@ -106,9 +108,12 @@ func doinit() { ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512) ARM64.HasSVE = isSet(hwCap, hwcap_SVE) ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM) + ARM64.HasDIT = isSet(hwCap, hwcap_DIT) + // HWCAP2 feature bits ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2) + ARM64.HasI8MM = isSet(hwCap2, hwcap2_I8MM) } func isSet(hwc uint, value uint) bool { diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go index cd63e73355..7d902b6847 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x +//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go new file mode 100644 index 0000000000..cb4a0c5728 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go @@ -0,0 +1,137 @@ +// 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 cpu + +import ( + "syscall" + "unsafe" +) + +// RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe +// syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available. +// +// A note on detection of the Vector extension using HWCAP. +// +// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. +// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe +// syscall is not available then neither is the Vector extension (which needs kernel support). +// The riscv_hwprobe syscall should then be all we need to detect the Vector extension. +// However, some RISC-V board manufacturers ship boards with an older kernel on top of which +// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe +// patches. These kernels advertise support for the Vector extension using HWCAP. Falling +// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not +// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. +// +// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by +// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board +// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified +// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use +// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector +// extension are binary incompatible. HWCAP can then not be used in isolation to populate the +// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. +// +// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector +// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype +// register. This check would allow us to safely detect version 1.0 of the Vector extension +// with HWCAP, if riscv_hwprobe were not available. However, the check cannot +// be added until the assembler supports the Vector instructions. +// +// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the +// extensions it advertises support for are explicitly versioned. It's also worth noting that +// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba. +// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority +// of RISC-V extensions. +// +// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information. + +// golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must +// reproduce the constants, types and functions needed to make the riscv_hwprobe syscall +// here. + +const ( + // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. + riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 + riscv_HWPROBE_IMA_C = 0x2 + riscv_HWPROBE_IMA_V = 0x4 + riscv_HWPROBE_EXT_ZBA = 0x8 + riscv_HWPROBE_EXT_ZBB = 0x10 + riscv_HWPROBE_EXT_ZBS = 0x20 + riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 + riscv_HWPROBE_MISALIGNED_FAST = 0x3 + riscv_HWPROBE_MISALIGNED_MASK = 0x7 +) + +const ( + // sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go. + sys_RISCV_HWPROBE = 258 +) + +// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. +type riscvHWProbePairs struct { + key int64 + value uint64 +} + +const ( + // CPU features + hwcap_RISCV_ISA_C = 1 << ('C' - 'A') +) + +func doinit() { + // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key + // field should be initialised with one of the key constants defined above, e.g., + // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. + // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. + + pairs := []riscvHWProbePairs{ + {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, + {riscv_HWPROBE_KEY_CPUPERF_0, 0}, + } + + // This call only indicates that extensions are supported if they are implemented on all cores. + if riscvHWProbe(pairs, 0) { + if pairs[0].key != -1 { + v := uint(pairs[0].value) + RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C) + RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) + RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA) + RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) + RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS) + } + if pairs[1].key != -1 { + v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK + RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST + } + } + + // Let's double check with HWCAP if the C extension does not appear to be supported. + // This may happen if we're running on a kernel older than 6.4. + + if !RISCV64.HasC { + RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C) + } +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} + +// riscvHWProbe is a simplified version of the generated wrapper function found in +// golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the +// cpuCount and cpus parameters which we do not need. We always want to pass 0 for +// these parameters here so the kernel only reports the extensions that are present +// on all cores. +func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool { + var _zero uintptr + var p0 unsafe.Pointer + if len(pairs) > 0 { + p0 = unsafe.Pointer(&pairs[0]) + } else { + p0 = unsafe.Pointer(&_zero) + } + + _, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0) + return e1 == 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go index 7f0c79c004..aca3199c91 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_riscv64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_riscv64.go @@ -8,4 +8,13 @@ package cpu const cacheLineSize = 64 -func initOptions() {} +func initOptions() { + options = []option{ + {Name: "fastmisaligned", Feature: &RISCV64.HasFastMisaligned}, + {Name: "c", Feature: &RISCV64.HasC}, + {Name: "v", Feature: &RISCV64.HasV}, + {Name: "zba", Feature: &RISCV64.HasZba}, + {Name: "zbb", Feature: &RISCV64.HasZbb}, + {Name: "zbs", Feature: &RISCV64.HasZbs}, + } +} diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index fdcaa974d2..e14b766a32 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -58,6 +58,7 @@ includes_Darwin=' #define _DARWIN_USE_64_BIT_INODE #define __APPLE_USE_RFC_3542 #include +#include #include #include #include @@ -263,6 +264,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -549,6 +551,8 @@ ccflags="$@" $2 !~ "NLA_TYPE_MASK" && $2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ && $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ || + $2 ~ /^SOCK_|SK_DIAG_|SKNLGRP_$/ || + $2 ~ /^(CONNECT|SAE)_/ || $2 ~ /^FIORDCHK$/ || $2 ~ /^SIOC/ || $2 ~ /^TIOC/ || diff --git a/vendor/golang.org/x/sys/unix/mremap.go b/vendor/golang.org/x/sys/unix/mremap.go index fd45fe529d..3a5e776f89 100644 --- a/vendor/golang.org/x/sys/unix/mremap.go +++ b/vendor/golang.org/x/sys/unix/mremap.go @@ -50,3 +50,8 @@ func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data [ func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) { return mapper.Mremap(oldData, newLength, flags) } + +func MremapPtr(oldAddr unsafe.Pointer, oldSize uintptr, newAddr unsafe.Pointer, newSize uintptr, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mremap(uintptr(oldAddr), oldSize, newSize, flags, uintptr(newAddr)) + return unsafe.Pointer(xaddr), err +} diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index 59542a897d..099867deed 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -402,6 +402,18 @@ func IoctlSetIfreqMTU(fd int, ifreq *IfreqMTU) error { return ioctlPtr(fd, SIOCSIFMTU, unsafe.Pointer(ifreq)) } +//sys renamexNp(from string, to string, flag uint32) (err error) + +func RenamexNp(from string, to string, flag uint32) (err error) { + return renamexNp(from, to, flag) +} + +//sys renameatxNp(fromfd int, from string, tofd int, to string, flag uint32) (err error) + +func RenameatxNp(fromfd int, from string, tofd int, to string, flag uint32) (err error) { + return renameatxNp(fromfd, from, tofd, to, flag) +} + //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS_SYSCTL func Uname(uname *Utsname) error { @@ -542,6 +554,55 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { } } +//sys pthread_chdir_np(path string) (err error) + +func PthreadChdir(path string) (err error) { + return pthread_chdir_np(path) +} + +//sys pthread_fchdir_np(fd int) (err error) + +func PthreadFchdir(fd int) (err error) { + return pthread_fchdir_np(fd) +} + +// Connectx calls connectx(2) to initiate a connection on a socket. +// +// srcIf, srcAddr, and dstAddr are filled into a [SaEndpoints] struct and passed as the endpoints argument. +// +// - srcIf is the optional source interface index. 0 means unspecified. +// - srcAddr is the optional source address. nil means unspecified. +// - dstAddr is the destination address. +// +// On success, Connectx returns the number of bytes enqueued for transmission. +func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocID, flags uint32, iov []Iovec, connid *SaeConnID) (n uintptr, err error) { + endpoints := SaEndpoints{ + Srcif: srcIf, + } + + if srcAddr != nil { + addrp, addrlen, err := srcAddr.sockaddr() + if err != nil { + return 0, err + } + endpoints.Srcaddr = (*RawSockaddr)(addrp) + endpoints.Srcaddrlen = uint32(addrlen) + } + + if dstAddr != nil { + addrp, addrlen, err := dstAddr.sockaddr() + if err != nil { + return 0, err + } + endpoints.Dstaddr = (*RawSockaddr)(addrp) + endpoints.Dstaddrlen = uint32(addrlen) + } + + err = connectx(fd, &endpoints, associd, flags, iov, &n, connid) + return +} + +//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_hurd.go b/vendor/golang.org/x/sys/unix/syscall_hurd.go index ba46651f8e..a6a2d2fc2b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_hurd.go +++ b/vendor/golang.org/x/sys/unix/syscall_hurd.go @@ -11,6 +11,7 @@ package unix int ioctl(int, unsigned long int, uintptr_t); */ import "C" +import "unsafe" func ioctl(fd int, req uint, arg uintptr) (err error) { r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(arg)) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 5682e2628a..3f1d3d4cb2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -2592,3 +2592,4 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) { } //sys Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error) +//sys Mseal(b []byte, flags uint) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index b25343c71a..b86ded549c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -293,6 +293,7 @@ func Uname(uname *Utsname) error { //sys Mkfifoat(dirfd int, path string, mode uint32) (err error) //sys Mknod(path string, mode uint32, dev int) (err error) //sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) +//sys Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_unix.go b/vendor/golang.org/x/sys/unix/syscall_unix.go index 77081de8c7..4e92e5aa40 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -154,6 +154,15 @@ func Munmap(b []byte) (err error) { return mapper.Munmap(b) } +func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mmap(uintptr(addr), length, prot, flags, fd, offset) + return unsafe.Pointer(xaddr), err +} + +func MunmapPtr(addr unsafe.Pointer, length uintptr) (err error) { + return mapper.munmap(uintptr(addr), length) +} + func Read(fd int, p []byte) (n int, err error) { n, err = read(fd, p) if raceenabled { diff --git a/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go index e40fa85245..d73c4652e6 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go @@ -237,6 +237,9 @@ const ( CLOCK_UPTIME_RAW_APPROX = 0x9 CLONE_NOFOLLOW = 0x1 CLONE_NOOWNERCOPY = 0x2 + CONNECT_DATA_AUTHENTICATED = 0x4 + CONNECT_DATA_IDEMPOTENT = 0x2 + CONNECT_RESUME_ON_READ_WRITE = 0x1 CR0 = 0x0 CR1 = 0x1000 CR2 = 0x2000 @@ -1169,6 +1172,11 @@ const ( PT_WRITE_D = 0x5 PT_WRITE_I = 0x4 PT_WRITE_U = 0x6 + RENAME_EXCL = 0x4 + RENAME_NOFOLLOW_ANY = 0x10 + RENAME_RESERVED1 = 0x8 + RENAME_SECLUDE = 0x1 + RENAME_SWAP = 0x2 RLIMIT_AS = 0x5 RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 @@ -1260,6 +1268,10 @@ const ( RTV_SSTHRESH = 0x20 RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 + SAE_ASSOCID_ALL = 0xffffffff + SAE_ASSOCID_ANY = 0x0 + SAE_CONNID_ALL = 0xffffffff + SAE_CONNID_ANY = 0x0 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 diff --git a/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go index bb02aa6c05..4a55a40058 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go @@ -237,6 +237,9 @@ const ( CLOCK_UPTIME_RAW_APPROX = 0x9 CLONE_NOFOLLOW = 0x1 CLONE_NOOWNERCOPY = 0x2 + CONNECT_DATA_AUTHENTICATED = 0x4 + CONNECT_DATA_IDEMPOTENT = 0x2 + CONNECT_RESUME_ON_READ_WRITE = 0x1 CR0 = 0x0 CR1 = 0x1000 CR2 = 0x2000 @@ -1169,6 +1172,11 @@ const ( PT_WRITE_D = 0x5 PT_WRITE_I = 0x4 PT_WRITE_U = 0x6 + RENAME_EXCL = 0x4 + RENAME_NOFOLLOW_ANY = 0x10 + RENAME_RESERVED1 = 0x8 + RENAME_SECLUDE = 0x1 + RENAME_SWAP = 0x2 RLIMIT_AS = 0x5 RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 @@ -1260,6 +1268,10 @@ const ( RTV_SSTHRESH = 0x20 RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 + SAE_ASSOCID_ALL = 0xffffffff + SAE_ASSOCID_ANY = 0x0 + SAE_CONNID_ALL = 0xffffffff + SAE_CONNID_ANY = 0x0 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 93a38a97d9..01a70b2463 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -457,6 +457,7 @@ const ( B600 = 0x8 B75 = 0x2 B9600 = 0xd + BCACHEFS_SUPER_MAGIC = 0xca451a4e BDEVFS_MAGIC = 0x62646576 BINDERFS_SUPER_MAGIC = 0x6c6f6f70 BINFMTFS_MAGIC = 0x42494e4d @@ -502,6 +503,7 @@ const ( BPF_IMM = 0x0 BPF_IND = 0x40 BPF_JA = 0x0 + BPF_JCOND = 0xe0 BPF_JEQ = 0x10 BPF_JGE = 0x30 BPF_JGT = 0x20 @@ -657,6 +659,9 @@ const ( CAN_NPROTO = 0x8 CAN_RAW = 0x1 CAN_RAW_FILTER_MAX = 0x200 + CAN_RAW_XL_VCID_RX_FILTER = 0x4 + CAN_RAW_XL_VCID_TX_PASS = 0x2 + CAN_RAW_XL_VCID_TX_SET = 0x1 CAN_RTR_FLAG = 0x40000000 CAN_SFF_ID_BITS = 0xb CAN_SFF_MASK = 0x7ff @@ -924,6 +929,7 @@ const ( EPOLL_CTL_ADD = 0x1 EPOLL_CTL_DEL = 0x2 EPOLL_CTL_MOD = 0x3 + EPOLL_IOC_TYPE = 0x8a EROFS_SUPER_MAGIC_V1 = 0xe0f5e1e2 ESP_V4_FLOW = 0xa ESP_V6_FLOW = 0xc @@ -937,9 +943,6 @@ const ( ETHTOOL_FEC_OFF = 0x4 ETHTOOL_FEC_RS = 0x8 ETHTOOL_FLAG_ALL = 0x7 - ETHTOOL_FLAG_COMPACT_BITSETS = 0x1 - ETHTOOL_FLAG_OMIT_REPLY = 0x2 - ETHTOOL_FLAG_STATS = 0x4 ETHTOOL_FLASHDEV = 0x33 ETHTOOL_FLASH_MAX_FILENAME = 0x80 ETHTOOL_FWVERS_LEN = 0x20 @@ -1339,6 +1342,7 @@ const ( F_OFD_SETLK = 0x25 F_OFD_SETLKW = 0x26 F_OK = 0x0 + F_SEAL_EXEC = 0x20 F_SEAL_FUTURE_WRITE = 0x10 F_SEAL_GROW = 0x4 F_SEAL_SEAL = 0x1 @@ -1627,6 +1631,7 @@ const ( IP_FREEBIND = 0xf IP_HDRINCL = 0x3 IP_IPSEC_POLICY = 0x10 + IP_LOCAL_PORT_RANGE = 0x33 IP_MAXPACKET = 0xffff IP_MAX_MEMBERSHIPS = 0x14 IP_MF = 0x2000 @@ -1653,6 +1658,7 @@ const ( IP_PMTUDISC_OMIT = 0x5 IP_PMTUDISC_PROBE = 0x3 IP_PMTUDISC_WANT = 0x1 + IP_PROTOCOL = 0x34 IP_RECVERR = 0xb IP_RECVERR_RFC4884 = 0x1a IP_RECVFRAGSIZE = 0x19 @@ -1698,6 +1704,7 @@ const ( KEXEC_ARCH_S390 = 0x160000 KEXEC_ARCH_SH = 0x2a0000 KEXEC_ARCH_X86_64 = 0x3e0000 + KEXEC_CRASH_HOTPLUG_SUPPORT = 0x8 KEXEC_FILE_DEBUG = 0x8 KEXEC_FILE_NO_INITRAMFS = 0x4 KEXEC_FILE_ON_CRASH = 0x2 @@ -1773,6 +1780,7 @@ const ( KEY_SPEC_USER_KEYRING = -0x4 KEY_SPEC_USER_SESSION_KEYRING = -0x5 LANDLOCK_ACCESS_FS_EXECUTE = 0x1 + LANDLOCK_ACCESS_FS_IOCTL_DEV = 0x8000 LANDLOCK_ACCESS_FS_MAKE_BLOCK = 0x800 LANDLOCK_ACCESS_FS_MAKE_CHAR = 0x40 LANDLOCK_ACCESS_FS_MAKE_DIR = 0x80 @@ -1854,6 +1862,19 @@ const ( MAP_FILE = 0x0 MAP_FIXED = 0x10 MAP_FIXED_NOREPLACE = 0x100000 + MAP_HUGE_16GB = 0x88000000 + MAP_HUGE_16KB = 0x38000000 + MAP_HUGE_16MB = 0x60000000 + MAP_HUGE_1GB = 0x78000000 + MAP_HUGE_1MB = 0x50000000 + MAP_HUGE_256MB = 0x70000000 + MAP_HUGE_2GB = 0x7c000000 + MAP_HUGE_2MB = 0x54000000 + MAP_HUGE_32MB = 0x64000000 + MAP_HUGE_512KB = 0x4c000000 + MAP_HUGE_512MB = 0x74000000 + MAP_HUGE_64KB = 0x40000000 + MAP_HUGE_8MB = 0x5c000000 MAP_HUGE_MASK = 0x3f MAP_HUGE_SHIFT = 0x1a MAP_PRIVATE = 0x2 @@ -2169,7 +2190,7 @@ const ( NFT_SECMARK_CTX_MAXLEN = 0x100 NFT_SET_MAXNAMELEN = 0x100 NFT_SOCKET_MAX = 0x3 - NFT_TABLE_F_MASK = 0x3 + NFT_TABLE_F_MASK = 0x7 NFT_TABLE_MAXNAMELEN = 0x100 NFT_TRACETYPE_MAX = 0x3 NFT_TUNNEL_F_MASK = 0x7 @@ -2403,6 +2424,7 @@ const ( PERF_RECORD_MISC_USER = 0x2 PERF_SAMPLE_BRANCH_PLM_ALL = 0x7 PERF_SAMPLE_WEIGHT_TYPE = 0x1004000 + PID_FS_MAGIC = 0x50494446 PIPEFS_MAGIC = 0x50495045 PPPIOCGNPMODE = 0xc008744c PPPIOCNEWUNIT = 0xc004743e @@ -2490,6 +2512,23 @@ const ( PR_PAC_GET_ENABLED_KEYS = 0x3d PR_PAC_RESET_KEYS = 0x36 PR_PAC_SET_ENABLED_KEYS = 0x3c + PR_PPC_DEXCR_CTRL_CLEAR = 0x4 + PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC = 0x10 + PR_PPC_DEXCR_CTRL_EDITABLE = 0x1 + PR_PPC_DEXCR_CTRL_MASK = 0x1f + PR_PPC_DEXCR_CTRL_SET = 0x2 + PR_PPC_DEXCR_CTRL_SET_ONEXEC = 0x8 + PR_PPC_DEXCR_IBRTPD = 0x1 + PR_PPC_DEXCR_NPHIE = 0x3 + PR_PPC_DEXCR_SBHE = 0x0 + PR_PPC_DEXCR_SRAPD = 0x2 + PR_PPC_GET_DEXCR = 0x48 + PR_PPC_SET_DEXCR = 0x49 + PR_RISCV_CTX_SW_FENCEI_OFF = 0x1 + PR_RISCV_CTX_SW_FENCEI_ON = 0x0 + PR_RISCV_SCOPE_PER_PROCESS = 0x0 + PR_RISCV_SCOPE_PER_THREAD = 0x1 + PR_RISCV_SET_ICACHE_FLUSH_CTX = 0x47 PR_RISCV_V_GET_CONTROL = 0x46 PR_RISCV_V_SET_CONTROL = 0x45 PR_RISCV_V_VSTATE_CTRL_CUR_MASK = 0x3 @@ -2896,8 +2935,9 @@ const ( RWF_APPEND = 0x10 RWF_DSYNC = 0x2 RWF_HIPRI = 0x1 + RWF_NOAPPEND = 0x20 RWF_NOWAIT = 0x8 - RWF_SUPPORTED = 0x1f + RWF_SUPPORTED = 0x3f RWF_SYNC = 0x4 RWF_WRITE_LIFE_NOT_SET = 0x0 SCHED_BATCH = 0x3 @@ -2918,7 +2958,9 @@ const ( SCHED_RESET_ON_FORK = 0x40000000 SCHED_RR = 0x2 SCM_CREDENTIALS = 0x2 + SCM_PIDFD = 0x4 SCM_RIGHTS = 0x1 + SCM_SECURITY = 0x3 SCM_TIMESTAMP = 0x1d SC_LOG_FLUSH = 0x100000 SECCOMP_ADDFD_FLAG_SEND = 0x2 @@ -3051,6 +3093,8 @@ const ( SIOCSMIIREG = 0x8949 SIOCSRARP = 0x8962 SIOCWANDEV = 0x894a + SK_DIAG_BPF_STORAGE_MAX = 0x3 + SK_DIAG_BPF_STORAGE_REQ_MAX = 0x1 SMACK_MAGIC = 0x43415d53 SMART_AUTOSAVE = 0xd2 SMART_AUTO_OFFLINE = 0xdb @@ -3071,6 +3115,8 @@ const ( SOCKFS_MAGIC = 0x534f434b SOCK_BUF_LOCK_MASK = 0x3 SOCK_DCCP = 0x6 + SOCK_DESTROY = 0x15 + SOCK_DIAG_BY_FAMILY = 0x14 SOCK_IOC_TYPE = 0x89 SOCK_PACKET = 0xa SOCK_RAW = 0x3 @@ -3177,6 +3223,7 @@ const ( STATX_MTIME = 0x40 STATX_NLINK = 0x4 STATX_SIZE = 0x200 + STATX_SUBVOL = 0x8000 STATX_TYPE = 0x1 STATX_UID = 0x8 STATX__RESERVED = 0x80000000 @@ -3260,6 +3307,7 @@ const ( TCP_MAX_WINSHIFT = 0xe TCP_MD5SIG = 0xe TCP_MD5SIG_EXT = 0x20 + TCP_MD5SIG_FLAG_IFINDEX = 0x2 TCP_MD5SIG_FLAG_PREFIX = 0x1 TCP_MD5SIG_MAXKEYLEN = 0x50 TCP_MSS = 0x200 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 42ff8c3c1b..684a5168da 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 @@ -118,6 +120,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 MAP_32BIT = 0x40 + MAP_ABOVE4G = 0x80 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 MAP_DENYWRITE = 0x800 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index dca436004f..61d74b592d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 @@ -118,6 +120,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 MAP_32BIT = 0x40 + MAP_ABOVE4G = 0x80 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 MAP_DENYWRITE = 0x800 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 5cca668ac3..a28c9e3e89 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index d8cae6d153..ab5d1fe8ea 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 ESR_MAGIC = 0x45535201 EXTPROC = 0x10000 @@ -87,6 +89,7 @@ const ( FICLONE = 0x40049409 FICLONERANGE = 0x4020940d FLUSHO = 0x1000 + FPMR_MAGIC = 0x46504d52 FPSIMD_MAGIC = 0x46508001 FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_GETFLAGS = 0x80086601 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index 28e39afdcb..c523090e7c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index cd66e92cb4..01e6ea7804 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x80 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index c1595eba78..7aa610b1e7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x80 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index ee9456b0da..92af771b44 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x80 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 8cfca81e1b..b27ef5e6f1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x80 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 60b0deb3af..237a2cefb3 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x20 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000000 FF1 = 0x4000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index f90aa7281b..4a5c555a36 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x20 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000000 FF1 = 0x4000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index ba9e015033..a02fb49a5f 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x20 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000000 FF1 = 0x4000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 07cdfd6e9f..e26a7c61b2 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 2f1dd214a7..c48f7c2103 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -78,6 +78,8 @@ const ( ECHOPRT = 0x400 EFD_CLOEXEC = 0x80000 EFD_NONBLOCK = 0x800 + EPIOCGPARAMS = 0x80088a02 + EPIOCSPARAMS = 0x40088a01 EPOLL_CLOEXEC = 0x80000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index f40519d901..ad4b9aace7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -82,6 +82,8 @@ const ( EFD_CLOEXEC = 0x400000 EFD_NONBLOCK = 0x4000 EMT_TAGOVF = 0x1 + EPIOCGPARAMS = 0x40088a02 + EPIOCSPARAMS = 0x80088a01 EPOLL_CLOEXEC = 0x400000 EXTPROC = 0x10000 FF1 = 0x8000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go index da08b2ab3d..1ec2b1407b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go @@ -581,6 +581,8 @@ const ( AT_EMPTY_PATH = 0x1000 AT_REMOVEDIR = 0x200 RENAME_NOREPLACE = 1 << 0 + ST_RDONLY = 1 + ST_NOSUID = 2 ) const ( diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index ccb02f240a..24b346e1a3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -740,6 +740,54 @@ func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func renamexNp(from string, to string, flag uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_renamex_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_renamex_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_renamex_np renamex_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func renameatxNp(fromfd int, from string, tofd int, to string, flag uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_renameatx_np_trampoline_addr, uintptr(fromfd), uintptr(unsafe.Pointer(_p0)), uintptr(tofd), uintptr(unsafe.Pointer(_p1)), uintptr(flag), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_renameatx_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_renameatx_np renameatx_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -760,6 +808,59 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pthread_chdir_np(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_chdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pthread_fchdir_np(fd int) (err error) { + _, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_fchdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) { + var _p0 unsafe.Pointer + if len(iov) > 0 { + _p0 = unsafe.Pointer(&iov[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := syscall_syscall9(libc_connectx_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(endpoints)), uintptr(associd), uintptr(flags), uintptr(_p0), uintptr(len(iov)), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(connid)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_connectx_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_connectx connectx "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index 8b8bb28402..ebd213100b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -223,11 +223,36 @@ TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_ioctl_trampoline_addr(SB)/8, $libc_ioctl_trampoline<>(SB) +TEXT libc_renamex_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_renamex_np(SB) +GLOBL ·libc_renamex_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_renamex_np_trampoline_addr(SB)/8, $libc_renamex_np_trampoline<>(SB) + +TEXT libc_renameatx_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_renameatx_np(SB) +GLOBL ·libc_renameatx_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_renameatx_np_trampoline_addr(SB)/8, $libc_renameatx_np_trampoline<>(SB) + TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sysctl(SB) GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_chdir_np(SB) +GLOBL ·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB) + +TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_fchdir_np(SB) +GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) + +TEXT libc_connectx_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_connectx(SB) +GLOBL ·libc_connectx_trampoline_addr(SB), RODATA, $8 +DATA ·libc_connectx_trampoline_addr(SB)/8, $libc_connectx_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 1b40b997b5..824b9c2d5e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -740,6 +740,54 @@ func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func renamexNp(from string, to string, flag uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_renamex_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_renamex_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_renamex_np renamex_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func renameatxNp(fromfd int, from string, tofd int, to string, flag uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_renameatx_np_trampoline_addr, uintptr(fromfd), uintptr(unsafe.Pointer(_p0)), uintptr(tofd), uintptr(unsafe.Pointer(_p1)), uintptr(flag), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_renameatx_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_renameatx_np renameatx_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { @@ -760,6 +808,59 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pthread_chdir_np(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_chdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pthread_fchdir_np(fd int) (err error) { + _, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_fchdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) { + var _p0 unsafe.Pointer + if len(iov) > 0 { + _p0 = unsafe.Pointer(&iov[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := syscall_syscall9(libc_connectx_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(endpoints)), uintptr(associd), uintptr(flags), uintptr(_p0), uintptr(len(iov)), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(connid)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_connectx_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_connectx connectx "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index 08362c1ab7..4f178a2293 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -223,11 +223,36 @@ TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_ioctl_trampoline_addr(SB)/8, $libc_ioctl_trampoline<>(SB) +TEXT libc_renamex_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_renamex_np(SB) +GLOBL ·libc_renamex_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_renamex_np_trampoline_addr(SB)/8, $libc_renamex_np_trampoline<>(SB) + +TEXT libc_renameatx_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_renameatx_np(SB) +GLOBL ·libc_renameatx_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_renameatx_np_trampoline_addr(SB)/8, $libc_renameatx_np_trampoline<>(SB) + TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sysctl(SB) GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_chdir_np(SB) +GLOBL ·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB) + +TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_fchdir_np(SB) +GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) + +TEXT libc_connectx_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_connectx(SB) +GLOBL ·libc_connectx_trampoline_addr(SB), RODATA, $8 +DATA ·libc_connectx_trampoline_addr(SB)/8, $libc_connectx_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 87d8612a1d..1bc1a5adb2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -2229,3 +2229,19 @@ func Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mseal(b []byte, flags uint) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MSEAL, uintptr(_p0), uintptr(len(b)), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 9dc42410b7..1851df14e8 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 41b5617316..0b43c69365 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -463,6 +463,11 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $4 DATA ·libc_mknodat_trampoline_addr(SB)/4, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $4 +DATA ·libc_mount_trampoline_addr(SB)/4, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) GLOBL ·libc_nanosleep_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 0d3a0751cd..e1ec0dbe4e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 4019a656f6..880c6d6e31 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -463,6 +463,11 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknodat_trampoline_addr(SB)/8, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) GLOBL ·libc_nanosleep_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index c39f7776db..7c8452a63e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index ac4af24f90..b8ef95b0fa 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -463,6 +463,11 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $4 DATA ·libc_mknodat_trampoline_addr(SB)/4, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $4 +DATA ·libc_mount_trampoline_addr(SB)/4, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) GLOBL ·libc_nanosleep_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index 57571d072f..2ffdf861f7 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index f77d532121..2af3b5c762 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -463,6 +463,11 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknodat_trampoline_addr(SB)/8, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) GLOBL ·libc_nanosleep_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index e62963e67e..1da08d5267 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index fae140b62c..b7a251353b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -463,6 +463,11 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknodat_trampoline_addr(SB)/8, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) GLOBL ·libc_nanosleep_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 00831354c8..6e85b0aac9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index 9d1e0ff06d..f15dadf055 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -555,6 +555,12 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknodat_trampoline_addr(SB)/8, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_mount(SB) + RET +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_nanosleep(SB) RET diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 79029ed584..28b487df25 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -1493,6 +1493,30 @@ var libc_mknodat_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := syscall_syscall(libc_nanosleep_trampoline_addr, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index da115f9a4b..1e7f321e43 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -463,6 +463,11 @@ TEXT libc_mknodat_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknodat_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknodat_trampoline_addr(SB)/8, $libc_mknodat_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_nanosleep_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_nanosleep(SB) GLOBL ·libc_nanosleep_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 53aef5dc58..524b0820cb 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -457,4 +457,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index 71d524763d..d3e38f681a 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -379,4 +379,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index c747706131..70b35bf3b0 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -421,4 +421,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index f96e214f6d..6c778c2327 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -324,4 +324,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index 28425346cf..37281cf51a 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -318,4 +318,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index d0953018da..7e567f1eff 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -441,4 +441,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 4459 SYS_LSM_SET_SELF_ATTR = 4460 SYS_LSM_LIST_MODULES = 4461 + SYS_MSEAL = 4462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 295c7f4b81..38ae55e5ef 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -371,4 +371,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 5459 SYS_LSM_SET_SELF_ATTR = 5460 SYS_LSM_LIST_MODULES = 5461 + SYS_MSEAL = 5462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index d1a9eaca7a..55e92e60a8 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -371,4 +371,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 5459 SYS_LSM_SET_SELF_ATTR = 5460 SYS_LSM_LIST_MODULES = 5461 + SYS_MSEAL = 5462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index bec157c39f..60658d6a02 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -441,4 +441,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 4459 SYS_LSM_SET_SELF_ATTR = 4460 SYS_LSM_LIST_MODULES = 4461 + SYS_MSEAL = 4462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index 7ee7bdc435..e203e8a7ed 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -448,4 +448,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index fad1f25b44..5944b97d54 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -420,4 +420,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index 7d3e16357d..c66d416dad 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -420,4 +420,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 0ed53ad9f7..9889f6a559 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -325,4 +325,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 2fba04ad50..01d86825bb 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -386,4 +386,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 621d00d741..7b703e77cd 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -399,4 +399,5 @@ const ( SYS_LSM_GET_SELF_ATTR = 459 SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 + SYS_MSEAL = 462 ) diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index 091d107f3a..d003c3d437 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -306,6 +306,19 @@ type XVSockPgen struct { type _Socklen uint32 +type SaeAssocID uint32 + +type SaeConnID uint32 + +type SaEndpoints struct { + Srcif uint32 + Srcaddr *RawSockaddr + Srcaddrlen uint32 + Dstaddr *RawSockaddr + Dstaddrlen uint32 + _ [4]byte +} + type Xucred struct { Version uint32 Uid uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index 28ff4ef74d..0d45a941aa 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -306,6 +306,19 @@ type XVSockPgen struct { type _Socklen uint32 +type SaeAssocID uint32 + +type SaeConnID uint32 + +type SaEndpoints struct { + Srcif uint32 + Srcaddr *RawSockaddr + Srcaddrlen uint32 + Dstaddr *RawSockaddr + Dstaddrlen uint32 + _ [4]byte +} + type Xucred struct { Version uint32 Uid uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 6cbd094a3a..51e13eb055 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -625,6 +625,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index 7c03b6ee77..d002d8ef3c 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -630,6 +630,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index 422107ee8b..3f863d898d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -616,6 +616,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index 505a12acfd..61c7293106 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -610,6 +610,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go index cc986c7900..b5d17414f0 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go @@ -612,6 +612,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 0036746ea1..9f2550dc31 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -110,7 +110,8 @@ type Statx_t struct { Mnt_id uint64 Dio_mem_align uint32 Dio_offset_align uint32 - _ [12]uint64 + Subvol uint64 + _ [11]uint64 } type Fsid struct { @@ -2485,7 +2486,7 @@ type XDPMmapOffsets struct { type XDPUmemReg struct { Addr uint64 Len uint64 - Chunk_size uint32 + Size uint32 Headroom uint32 Flags uint32 Tx_metadata_len uint32 @@ -3473,7 +3474,7 @@ const ( DEVLINK_PORT_FN_ATTR_STATE = 0x2 DEVLINK_PORT_FN_ATTR_OPSTATE = 0x3 DEVLINK_PORT_FN_ATTR_CAPS = 0x4 - DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x5 + DEVLINK_PORT_FUNCTION_ATTR_MAX = 0x6 ) type FsverityDigest struct { @@ -3806,6 +3807,9 @@ const ( ETHTOOL_MSG_PSE_GET_REPLY = 0x25 ETHTOOL_MSG_RSS_GET_REPLY = 0x26 ETHTOOL_MSG_KERNEL_MAX = 0x2b + ETHTOOL_FLAG_COMPACT_BITSETS = 0x1 + ETHTOOL_FLAG_OMIT_REPLY = 0x2 + ETHTOOL_FLAG_STATS = 0x4 ETHTOOL_A_HEADER_UNSPEC = 0x0 ETHTOOL_A_HEADER_DEV_INDEX = 0x1 ETHTOOL_A_HEADER_DEV_NAME = 0x2 @@ -3975,7 +3979,7 @@ const ( ETHTOOL_A_TSINFO_TX_TYPES = 0x3 ETHTOOL_A_TSINFO_RX_FILTERS = 0x4 ETHTOOL_A_TSINFO_PHC_INDEX = 0x5 - ETHTOOL_A_TSINFO_MAX = 0x5 + ETHTOOL_A_TSINFO_MAX = 0x6 ETHTOOL_A_CABLE_TEST_UNSPEC = 0x0 ETHTOOL_A_CABLE_TEST_HEADER = 0x1 ETHTOOL_A_CABLE_TEST_MAX = 0x1 @@ -4605,7 +4609,7 @@ const ( NL80211_ATTR_MAC_HINT = 0xc8 NL80211_ATTR_MAC_MASK = 0xd7 NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca - NL80211_ATTR_MAX = 0x149 + NL80211_ATTR_MAX = 0x14a NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 NL80211_ATTR_MAX_CSA_COUNTERS = 0xce NL80211_ATTR_MAX_MATCH_SETS = 0x85 @@ -5209,7 +5213,7 @@ const ( NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf - NL80211_FREQUENCY_ATTR_MAX = 0x1f + NL80211_FREQUENCY_ATTR_MAX = 0x20 NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc @@ -5703,7 +5707,7 @@ const ( NL80211_STA_FLAG_ASSOCIATED = 0x7 NL80211_STA_FLAG_AUTHENTICATED = 0x5 NL80211_STA_FLAG_AUTHORIZED = 0x1 - NL80211_STA_FLAG_MAX = 0x7 + NL80211_STA_FLAG_MAX = 0x8 NL80211_STA_FLAG_MAX_OLD_API = 0x6 NL80211_STA_FLAG_MFP = 0x4 NL80211_STA_FLAG_SHORT_PREAMBLE = 0x2 @@ -6001,3 +6005,34 @@ type CachestatRange struct { Off uint64 Len uint64 } + +const ( + SK_MEMINFO_RMEM_ALLOC = 0x0 + SK_MEMINFO_RCVBUF = 0x1 + SK_MEMINFO_WMEM_ALLOC = 0x2 + SK_MEMINFO_SNDBUF = 0x3 + SK_MEMINFO_FWD_ALLOC = 0x4 + SK_MEMINFO_WMEM_QUEUED = 0x5 + SK_MEMINFO_OPTMEM = 0x6 + SK_MEMINFO_BACKLOG = 0x7 + SK_MEMINFO_DROPS = 0x8 + SK_MEMINFO_VARS = 0x9 + SKNLGRP_NONE = 0x0 + SKNLGRP_INET_TCP_DESTROY = 0x1 + SKNLGRP_INET_UDP_DESTROY = 0x2 + SKNLGRP_INET6_TCP_DESTROY = 0x3 + SKNLGRP_INET6_UDP_DESTROY = 0x4 + SK_DIAG_BPF_STORAGE_REQ_NONE = 0x0 + SK_DIAG_BPF_STORAGE_REQ_MAP_FD = 0x1 + SK_DIAG_BPF_STORAGE_REP_NONE = 0x0 + SK_DIAG_BPF_STORAGE = 0x1 + SK_DIAG_BPF_STORAGE_NONE = 0x0 + SK_DIAG_BPF_STORAGE_PAD = 0x1 + SK_DIAG_BPF_STORAGE_MAP_ID = 0x2 + SK_DIAG_BPF_STORAGE_MAP_VALUE = 0x3 +) + +type SockDiagReq struct { + Family uint8 + Protocol uint8 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 15adc04142..ad05b51a60 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -727,6 +727,37 @@ const ( RISCV_HWPROBE_EXT_ZBA = 0x8 RISCV_HWPROBE_EXT_ZBB = 0x10 RISCV_HWPROBE_EXT_ZBS = 0x20 + RISCV_HWPROBE_EXT_ZICBOZ = 0x40 + RISCV_HWPROBE_EXT_ZBC = 0x80 + RISCV_HWPROBE_EXT_ZBKB = 0x100 + RISCV_HWPROBE_EXT_ZBKC = 0x200 + RISCV_HWPROBE_EXT_ZBKX = 0x400 + RISCV_HWPROBE_EXT_ZKND = 0x800 + RISCV_HWPROBE_EXT_ZKNE = 0x1000 + RISCV_HWPROBE_EXT_ZKNH = 0x2000 + RISCV_HWPROBE_EXT_ZKSED = 0x4000 + RISCV_HWPROBE_EXT_ZKSH = 0x8000 + RISCV_HWPROBE_EXT_ZKT = 0x10000 + RISCV_HWPROBE_EXT_ZVBB = 0x20000 + RISCV_HWPROBE_EXT_ZVBC = 0x40000 + RISCV_HWPROBE_EXT_ZVKB = 0x80000 + RISCV_HWPROBE_EXT_ZVKG = 0x100000 + RISCV_HWPROBE_EXT_ZVKNED = 0x200000 + RISCV_HWPROBE_EXT_ZVKNHA = 0x400000 + RISCV_HWPROBE_EXT_ZVKNHB = 0x800000 + RISCV_HWPROBE_EXT_ZVKSED = 0x1000000 + RISCV_HWPROBE_EXT_ZVKSH = 0x2000000 + RISCV_HWPROBE_EXT_ZVKT = 0x4000000 + RISCV_HWPROBE_EXT_ZFH = 0x8000000 + RISCV_HWPROBE_EXT_ZFHMIN = 0x10000000 + RISCV_HWPROBE_EXT_ZIHINTNTL = 0x20000000 + RISCV_HWPROBE_EXT_ZVFH = 0x40000000 + RISCV_HWPROBE_EXT_ZVFHMIN = 0x80000000 + RISCV_HWPROBE_EXT_ZFA = 0x100000000 + RISCV_HWPROBE_EXT_ZTSO = 0x200000000 + RISCV_HWPROBE_EXT_ZACAS = 0x400000000 + RISCV_HWPROBE_EXT_ZICOND = 0x800000000 + RISCV_HWPROBE_EXT_ZIHINTPAUSE = 0x1000000000 RISCV_HWPROBE_KEY_CPUPERF_0 = 0x5 RISCV_HWPROBE_MISALIGNED_UNKNOWN = 0x0 RISCV_HWPROBE_MISALIGNED_EMULATED = 0x1 @@ -734,4 +765,6 @@ const ( RISCV_HWPROBE_MISALIGNED_FAST = 0x3 RISCV_HWPROBE_MISALIGNED_UNSUPPORTED = 0x4 RISCV_HWPROBE_MISALIGNED_MASK = 0x7 + RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE = 0x6 + RISCV_HWPROBE_WHICH_CPUS = 0x1 ) diff --git a/vendor/golang.org/x/sys/windows/security_windows.go b/vendor/golang.org/x/sys/windows/security_windows.go index 26be94a8a7..b6e1ab76f8 100644 --- a/vendor/golang.org/x/sys/windows/security_windows.go +++ b/vendor/golang.org/x/sys/windows/security_windows.go @@ -68,6 +68,7 @@ type UserInfo10 struct { //sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo //sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation //sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree +//sys NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) = netapi32.NetUserEnum const ( // do not reorder @@ -893,7 +894,7 @@ type ACL struct { aclRevision byte sbz1 byte aclSize uint16 - aceCount uint16 + AceCount uint16 sbz2 uint16 } @@ -1086,6 +1087,27 @@ type EXPLICIT_ACCESS struct { Trustee TRUSTEE } +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header +type ACE_HEADER struct { + AceType uint8 + AceFlags uint8 + AceSize uint16 +} + +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace +type ACCESS_ALLOWED_ACE struct { + Header ACE_HEADER + Mask ACCESS_MASK + SidStart uint32 +} + +const ( + // Constants for AceType + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header + ACCESS_ALLOWED_ACE_TYPE = 0 + ACCESS_DENIED_ACE_TYPE = 1 +) + // This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions. type TrusteeValue uintptr @@ -1157,6 +1179,7 @@ type OBJECTS_AND_NAME struct { //sys makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD //sys setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW +//sys GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (err error) = advapi32.GetAce // Control returns the security descriptor control bits. func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) { diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 6525c62f3c..5cee9a3143 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -17,8 +17,10 @@ import ( "unsafe" ) -type Handle uintptr -type HWND uintptr +type ( + Handle uintptr + HWND uintptr +) const ( InvalidHandle = ^Handle(0) @@ -211,6 +213,10 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error) //sys ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) [failretval<=32] = shell32.ShellExecuteW //sys GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) = user32.GetWindowThreadProcessId +//sys LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) [failretval==0] = user32.LoadKeyboardLayoutW +//sys UnloadKeyboardLayout(hkl Handle) (err error) = user32.UnloadKeyboardLayout +//sys GetKeyboardLayout(tid uint32) (hkl Handle) = user32.GetKeyboardLayout +//sys ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32) = user32.ToUnicodeEx //sys GetShellWindow() (shellWindow HWND) = user32.GetShellWindow //sys MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW //sys ExitWindowsEx(flags uint32, reason uint32) (err error) = user32.ExitWindowsEx @@ -307,6 +313,10 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode //sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo //sys setConsoleCursorPosition(console Handle, position uint32) (err error) = kernel32.SetConsoleCursorPosition +//sys GetConsoleCP() (cp uint32, err error) = kernel32.GetConsoleCP +//sys GetConsoleOutputCP() (cp uint32, err error) = kernel32.GetConsoleOutputCP +//sys SetConsoleCP(cp uint32) (err error) = kernel32.SetConsoleCP +//sys SetConsoleOutputCP(cp uint32) (err error) = kernel32.SetConsoleOutputCP //sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW //sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW //sys resizePseudoConsole(pconsole Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole @@ -1368,9 +1378,11 @@ func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { func SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error) { return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4) } + func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (err error) { return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(mreq)), int32(unsafe.Sizeof(*mreq))) } + func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return syscall.EWINDOWS } diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index d8cb71db0a..7b97a154c9 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -1060,6 +1060,7 @@ const ( SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6 SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4 SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12 + SIO_UDP_NETRESET = IOC_IN | IOC_VENDOR | 15 // cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460 @@ -2003,7 +2004,21 @@ const ( MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20 ) -const GAA_FLAG_INCLUDE_PREFIX = 0x00000010 +// Flags for GetAdaptersAddresses, see +// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses. +const ( + GAA_FLAG_SKIP_UNICAST = 0x1 + GAA_FLAG_SKIP_ANYCAST = 0x2 + GAA_FLAG_SKIP_MULTICAST = 0x4 + GAA_FLAG_SKIP_DNS_SERVER = 0x8 + GAA_FLAG_INCLUDE_PREFIX = 0x10 + GAA_FLAG_SKIP_FRIENDLY_NAME = 0x20 + GAA_FLAG_INCLUDE_WINS_INFO = 0x40 + GAA_FLAG_INCLUDE_GATEWAYS = 0x80 + GAA_FLAG_INCLUDE_ALL_INTERFACES = 0x100 + GAA_FLAG_INCLUDE_ALL_COMPARTMENTS = 0x200 + GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER = 0x400 +) const ( IF_TYPE_OTHER = 1 @@ -2017,6 +2032,50 @@ const ( IF_TYPE_IEEE1394 = 144 ) +// Enum NL_PREFIX_ORIGIN for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin +const ( + IpPrefixOriginOther = 0 + IpPrefixOriginManual = 1 + IpPrefixOriginWellKnown = 2 + IpPrefixOriginDhcp = 3 + IpPrefixOriginRouterAdvertisement = 4 + IpPrefixOriginUnchanged = 1 << 4 +) + +// Enum NL_SUFFIX_ORIGIN for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_suffix_origin +const ( + NlsoOther = 0 + NlsoManual = 1 + NlsoWellKnown = 2 + NlsoDhcp = 3 + NlsoLinkLayerAddress = 4 + NlsoRandom = 5 + IpSuffixOriginOther = 0 + IpSuffixOriginManual = 1 + IpSuffixOriginWellKnown = 2 + IpSuffixOriginDhcp = 3 + IpSuffixOriginLinkLayerAddress = 4 + IpSuffixOriginRandom = 5 + IpSuffixOriginUnchanged = 1 << 4 +) + +// Enum NL_DAD_STATE for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_dad_state +const ( + NldsInvalid = 0 + NldsTentative = 1 + NldsDuplicate = 2 + NldsDeprecated = 3 + NldsPreferred = 4 + IpDadStateInvalid = 0 + IpDadStateTentative = 1 + IpDadStateDuplicate = 2 + IpDadStateDeprecated = 3 + IpDadStatePreferred = 4 +) + type SocketAddress struct { Sockaddr *syscall.RawSockaddrAny SockaddrLength int32 @@ -3404,3 +3463,14 @@ type DCB struct { EvtChar byte wReserved1 uint16 } + +// Keyboard Layout Flags. +// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadkeyboardlayoutw +const ( + KLF_ACTIVATE = 0x00000001 + KLF_SUBSTITUTE_OK = 0x00000002 + KLF_REORDER = 0x00000008 + KLF_REPLACELANG = 0x00000010 + KLF_NOTELLSHELL = 0x00000080 + KLF_SETFORPROCESS = 0x00000100 +) diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 5c6035ddfa..4c2e1bdc01 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -91,6 +91,7 @@ var ( procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") procEqualSid = modadvapi32.NewProc("EqualSid") procFreeSid = modadvapi32.NewProc("FreeSid") + procGetAce = modadvapi32.NewProc("GetAce") procGetLengthSid = modadvapi32.NewProc("GetLengthSid") procGetNamedSecurityInfoW = modadvapi32.NewProc("GetNamedSecurityInfoW") procGetSecurityDescriptorControl = modadvapi32.NewProc("GetSecurityDescriptorControl") @@ -246,7 +247,9 @@ var ( procGetCommandLineW = modkernel32.NewProc("GetCommandLineW") procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") procGetComputerNameW = modkernel32.NewProc("GetComputerNameW") + procGetConsoleCP = modkernel32.NewProc("GetConsoleCP") procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") + procGetConsoleOutputCP = modkernel32.NewProc("GetConsoleOutputCP") procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo") procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW") procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") @@ -346,8 +349,10 @@ var ( procSetCommMask = modkernel32.NewProc("SetCommMask") procSetCommState = modkernel32.NewProc("SetCommState") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procSetConsoleCP = modkernel32.NewProc("SetConsoleCP") procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition") procSetConsoleMode = modkernel32.NewProc("SetConsoleMode") + procSetConsoleOutputCP = modkernel32.NewProc("SetConsoleOutputCP") procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW") procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories") procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW") @@ -401,6 +406,7 @@ var ( procTransmitFile = modmswsock.NewProc("TransmitFile") procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree") procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation") + procNetUserEnum = modnetapi32.NewProc("NetUserEnum") procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo") procNtCreateFile = modntdll.NewProc("NtCreateFile") procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") @@ -476,12 +482,16 @@ var ( procGetDesktopWindow = moduser32.NewProc("GetDesktopWindow") procGetForegroundWindow = moduser32.NewProc("GetForegroundWindow") procGetGUIThreadInfo = moduser32.NewProc("GetGUIThreadInfo") + procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout") procGetShellWindow = moduser32.NewProc("GetShellWindow") procGetWindowThreadProcessId = moduser32.NewProc("GetWindowThreadProcessId") procIsWindow = moduser32.NewProc("IsWindow") procIsWindowUnicode = moduser32.NewProc("IsWindowUnicode") procIsWindowVisible = moduser32.NewProc("IsWindowVisible") + procLoadKeyboardLayoutW = moduser32.NewProc("LoadKeyboardLayoutW") procMessageBoxW = moduser32.NewProc("MessageBoxW") + procToUnicodeEx = moduser32.NewProc("ToUnicodeEx") + procUnloadKeyboardLayout = moduser32.NewProc("UnloadKeyboardLayout") procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock") procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock") procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") @@ -787,6 +797,14 @@ func FreeSid(sid *SID) (err error) { return } +func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (err error) { + r1, _, e1 := syscall.Syscall(procGetAce.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetLengthSid(sid *SID) (len uint32) { r0, _, _ := syscall.Syscall(procGetLengthSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) len = uint32(r0) @@ -2148,6 +2166,15 @@ func GetComputerName(buf *uint16, n *uint32) (err error) { return } +func GetConsoleCP() (cp uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0) + cp = uint32(r0) + if cp == 0 { + err = errnoErr(e1) + } + return +} + func GetConsoleMode(console Handle, mode *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0) if r1 == 0 { @@ -2156,6 +2183,15 @@ func GetConsoleMode(console Handle, mode *uint32) (err error) { return } +func GetConsoleOutputCP() (cp uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetConsoleOutputCP.Addr(), 0, 0, 0, 0) + cp = uint32(r0) + if cp == 0 { + err = errnoErr(e1) + } + return +} + func GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) { r1, _, e1 := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(info)), 0) if r1 == 0 { @@ -3024,6 +3060,14 @@ func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { return } +func SetConsoleCP(cp uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetConsoleCP.Addr(), 1, uintptr(cp), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func setConsoleCursorPosition(console Handle, position uint32) (err error) { r1, _, e1 := syscall.Syscall(procSetConsoleCursorPosition.Addr(), 2, uintptr(console), uintptr(position), 0) if r1 == 0 { @@ -3040,6 +3084,14 @@ func SetConsoleMode(console Handle, mode uint32) (err error) { return } +func SetConsoleOutputCP(cp uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetConsoleOutputCP.Addr(), 1, uintptr(cp), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SetCurrentDirectory(path *uint16) (err error) { r1, _, e1 := syscall.Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) if r1 == 0 { @@ -3486,6 +3538,14 @@ func NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (nete return } +func NetUserEnum(serverName *uint16, level uint32, filter uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32, resumeHandle *uint32) (neterr error) { + r0, _, _ := syscall.Syscall9(procNetUserEnum.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(filter), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), uintptr(unsafe.Pointer(resumeHandle)), 0) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + func NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) { r0, _, _ := syscall.Syscall6(procNetUserGetInfo.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(unsafe.Pointer(buf)), 0, 0) if r0 != 0 { @@ -4064,6 +4124,12 @@ func GetGUIThreadInfo(thread uint32, info *GUIThreadInfo) (err error) { return } +func GetKeyboardLayout(tid uint32) (hkl Handle) { + r0, _, _ := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(tid), 0, 0) + hkl = Handle(r0) + return +} + func GetShellWindow() (shellWindow HWND) { r0, _, _ := syscall.Syscall(procGetShellWindow.Addr(), 0, 0, 0, 0) shellWindow = HWND(r0) @@ -4097,6 +4163,15 @@ func IsWindowVisible(hwnd HWND) (isVisible bool) { return } +func LoadKeyboardLayout(name *uint16, flags uint32) (hkl Handle, err error) { + r0, _, e1 := syscall.Syscall(procLoadKeyboardLayoutW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(flags), 0) + hkl = Handle(r0) + if hkl == 0 { + err = errnoErr(e1) + } + return +} + func MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) { r0, _, e1 := syscall.Syscall6(procMessageBoxW.Addr(), 4, uintptr(hwnd), uintptr(unsafe.Pointer(text)), uintptr(unsafe.Pointer(caption)), uintptr(boxtype), 0, 0) ret = int32(r0) @@ -4106,6 +4181,20 @@ func MessageBox(hwnd HWND, text *uint16, caption *uint16, boxtype uint32) (ret i return } +func ToUnicodeEx(vkey uint32, scancode uint32, keystate *byte, pwszBuff *uint16, cchBuff int32, flags uint32, hkl Handle) (ret int32) { + r0, _, _ := syscall.Syscall9(procToUnicodeEx.Addr(), 7, uintptr(vkey), uintptr(scancode), uintptr(unsafe.Pointer(keystate)), uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(flags), uintptr(hkl), 0, 0) + ret = int32(r0) + return +} + +func UnloadKeyboardLayout(hkl Handle) (err error) { + r1, _, e1 := syscall.Syscall(procUnloadKeyboardLayout.Addr(), 1, uintptr(hkl), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) { var _p0 uint32 if inheritExisting { diff --git a/vendor/golang.org/x/term/LICENSE b/vendor/golang.org/x/term/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/term/LICENSE +++ b/vendor/golang.org/x/term/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/term/term_windows.go b/vendor/golang.org/x/term/term_windows.go index 465f560604..df6bf948e1 100644 --- a/vendor/golang.org/x/term/term_windows.go +++ b/vendor/golang.org/x/term/term_windows.go @@ -26,6 +26,7 @@ func makeRaw(fd int) (*State, error) { return nil, err } raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT) + raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil { return nil, err } diff --git a/vendor/golang.org/x/text/LICENSE b/vendor/golang.org/x/text/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/text/LICENSE +++ b/vendor/golang.org/x/text/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/tools/LICENSE b/vendor/golang.org/x/tools/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/vendor/golang.org/x/tools/LICENSE +++ b/vendor/golang.org/x/tools/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/tools/cmd/stringer/stringer.go b/vendor/golang.org/x/tools/cmd/stringer/stringer.go index 998d1a51bf..2b19c93e8e 100644 --- a/vendor/golang.org/x/tools/cmd/stringer/stringer.go +++ b/vendor/golang.org/x/tools/cmd/stringer/stringer.go @@ -188,6 +188,8 @@ type Generator struct { trimPrefix string lineComment bool + + logf func(format string, args ...interface{}) // test logging hook; nil when not testing } func (g *Generator) Printf(format string, args ...interface{}) { @@ -221,13 +223,14 @@ func (g *Generator) parsePackage(patterns []string, tags []string) { // in a separate pass? For later. Tests: false, BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, + Logf: g.logf, } pkgs, err := packages.Load(cfg, patterns...) if err != nil { log.Fatal(err) } if len(pkgs) != 1 { - log.Fatalf("error: %d packages found", len(pkgs)) + log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " ")) } g.addPackage(pkgs[0]) } diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go index 03543bd4bb..137cc8df1d 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -47,7 +47,7 @@ import ( func Find(importPath, srcDir string) (filename, path string) { cmd := exec.Command("go", "list", "-json", "-export", "--", importPath) cmd.Dir = srcDir - out, err := cmd.CombinedOutput() + out, err := cmd.Output() if err != nil { return "", "" } diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go deleted file mode 100644 index 0454cdd78e..0000000000 --- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 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 packagesdriver fetches type sizes for go/packages and go/analysis. -package packagesdriver - -import ( - "context" - "fmt" - "strings" - - "golang.org/x/tools/internal/gocommand" -) - -var debug = false - -func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) { - inv.Verb = "list" - inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} - stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) - var goarch, compiler string - if rawErr != nil { - if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { - // User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. - // TODO(matloob): Is this a problem in practice? - inv.Verb = "env" - inv.Args = []string{"GOARCH"} - envout, enverr := gocmdRunner.Run(ctx, inv) - if enverr != nil { - return "", "", enverr - } - goarch = strings.TrimSpace(envout.String()) - compiler = "gc" - } else { - return "", "", friendlyErr - } - } else { - fields := strings.Fields(stdout.String()) - if len(fields) < 2 { - return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>", - stdout.String(), stderr.String()) - } - goarch = fields[0] - compiler = fields[1] - } - return compiler, goarch, nil -} diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index da4ab89fe6..3531ac8f5f 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -5,12 +5,20 @@ /* Package packages loads Go packages for inspection and analysis. -The Load function takes as input a list of patterns and return a list of Package -structs describing individual packages matched by those patterns. -The LoadMode controls the amount of detail in the loaded packages. - -Load passes most patterns directly to the underlying build tool, -but all patterns with the prefix "query=", where query is a +The [Load] function takes as input a list of patterns and returns a +list of [Package] values describing individual packages matched by those +patterns. +A [Config] specifies configuration options, the most important of which is +the [LoadMode], which controls the amount of detail in the loaded packages. + +Load passes most patterns directly to the underlying build tool. +The default build tool is the go command. +Its supported patterns are described at +https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns. +Other build systems may be supported by providing a "driver"; +see [The driver protocol]. + +All patterns with the prefix "query=", where query is a non-empty string of letters from [a-z], are reserved and may be interpreted as query operators. @@ -35,7 +43,7 @@ The Package struct provides basic information about the package, including - Imports, a map from source import strings to the Packages they name; - Types, the type information for the package's exported symbols; - Syntax, the parsed syntax trees for the package's source code; and - - TypeInfo, the result of a complete type-check of the package syntax trees. + - TypesInfo, the result of a complete type-check of the package syntax trees. (See the documentation for type Package for the complete list of fields and more detailed descriptions.) @@ -64,9 +72,31 @@ reported about the loaded packages. See the documentation for type LoadMode for details. Most tools should pass their command-line arguments (after any flags) -uninterpreted to the loader, so that the loader can interpret them +uninterpreted to [Load], so that it can interpret them according to the conventions of the underlying build system. + See the Example function for typical usage. + +# The driver protocol + +[Load] may be used to load Go packages even in Go projects that use +alternative build systems, by installing an appropriate "driver" +program for the build system and specifying its location in the +GOPACKAGESDRIVER environment variable. +For example, +https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration +explains how to use the driver for Bazel. + +The driver program is responsible for interpreting patterns in its +preferred notation and reporting information about the packages that +those patterns identify. Drivers must also support the special "file=" +and "pattern=" patterns described above. + +The patterns are provided as positional command-line arguments. A +JSON-encoded [DriverRequest] message providing additional information +is written to the driver's standard input. The driver must write a +JSON-encoded [DriverResponse] message to its standard output. (This +message differs from the JSON schema produced by 'go list'.) */ package packages // import "golang.org/x/tools/go/packages" @@ -168,14 +198,6 @@ Instead, ssadump no longer requests the runtime package, but seeks it among the dependencies of the user-specified packages, and emits an error if it is not found. -Overlays: The Overlay field in the Config allows providing alternate contents -for Go source files, by providing a mapping from file path to contents. -go/packages will pull in new imports added in overlay files when go/packages -is run in LoadImports mode or greater. -Overlay support for the go list driver isn't complete yet: if the file doesn't -exist on disk, it will only be recognized in an overlay if it is a non-test file -and the package would be reported even without the overlay. - Questions & Tasks - Add GOARCH/GOOS? diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go index 7242a0a7d2..8f7afcb5df 100644 --- a/vendor/golang.org/x/tools/go/packages/external.go +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -2,48 +2,87 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file enables an external tool to intercept package requests. -// If the tool is present then its results are used in preference to -// the go list command. - package packages +// This file defines the protocol that enables an external "driver" +// tool to supply package metadata in place of 'go list'. + import ( "bytes" "encoding/json" "fmt" - exec "golang.org/x/sys/execabs" "os" + "os/exec" "strings" ) -// The Driver Protocol +// DriverRequest defines the schema of a request for package metadata +// from an external driver program. The JSON-encoded DriverRequest +// message is provided to the driver program's standard input. The +// query patterns are provided as command-line arguments. // -// The driver, given the inputs to a call to Load, returns metadata about the packages specified. -// This allows for different build systems to support go/packages by telling go/packages how the -// packages' source is organized. -// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in -// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package -// documentation in doc.go for the full description of the patterns that need to be supported. -// A driver receives as a JSON-serialized driverRequest struct in standard input and will -// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output. - -// driverRequest is used to provide the portion of Load's Config that is needed by a driver. -type driverRequest struct { +// See the package documentation for an overview. +type DriverRequest struct { Mode LoadMode `json:"mode"` + // Env specifies the environment the underlying build system should be run in. Env []string `json:"env"` + // BuildFlags are flags that should be passed to the underlying build system. BuildFlags []string `json:"build_flags"` + // Tests specifies whether the patterns should also return test packages. Tests bool `json:"tests"` - // Overlay maps file paths (relative to the driver's working directory) to the byte contents - // of overlay files. + + // Overlay maps file paths (relative to the driver's working directory) + // to the contents of overlay files (see Config.Overlay). Overlay map[string][]byte `json:"overlay"` } +// DriverResponse defines the schema of a response from an external +// driver program, providing the results of a query for package +// metadata. The driver program must write a JSON-encoded +// DriverResponse message to its standard output. +// +// See the package documentation for an overview. +type DriverResponse struct { + // NotHandled is returned if the request can't be handled by the current + // driver. If an external driver returns a response with NotHandled, the + // rest of the DriverResponse is ignored, and go/packages will fallback + // to the next driver. If go/packages is extended in the future to support + // lists of multiple drivers, go/packages will fall back to the next driver. + NotHandled bool + + // Compiler and Arch are the arguments pass of types.SizesFor + // to get a types.Sizes to use when type checking. + Compiler string + Arch string + + // Roots is the set of package IDs that make up the root packages. + // We have to encode this separately because when we encode a single package + // we cannot know if it is one of the roots as that requires knowledge of the + // graph it is part of. + Roots []string `json:",omitempty"` + + // Packages is the full set of packages in the graph. + // The packages are not connected into a graph. + // The Imports if populated will be stubs that only have their ID set. + // Imports will be connected and then type and syntax information added in a + // later pass (see refine). + Packages []*Package + + // GoVersion is the minor version number used by the driver + // (e.g. the go command on the PATH) when selecting .go files. + // Zero means unknown. + GoVersion int +} + +// driver is the type for functions that query the build system for the +// packages named by the patterns. +type driver func(cfg *Config, patterns ...string) (*DriverResponse, error) + // findExternalDriver returns the file path of a tool that supplies -// the build system package structure, or "" if not found." +// the build system package structure, or "" if not found. // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its // value, otherwise it searches for a binary named gopackagesdriver on the PATH. func findExternalDriver(cfg *Config) driver { @@ -64,8 +103,8 @@ func findExternalDriver(cfg *Config) driver { return nil } } - return func(cfg *Config, words ...string) (*driverResponse, error) { - req, err := json.Marshal(driverRequest{ + return func(cfg *Config, words ...string) (*DriverResponse, error) { + req, err := json.Marshal(DriverRequest{ Mode: cfg.Mode, Env: cfg.Env, BuildFlags: cfg.BuildFlags, @@ -80,7 +119,19 @@ func findExternalDriver(cfg *Config) driver { stderr := new(bytes.Buffer) cmd := exec.CommandContext(cfg.Context, tool, words...) cmd.Dir = cfg.Dir - cmd.Env = cfg.Env + // The cwd gets resolved to the real path. On Darwin, where + // /tmp is a symlink, this breaks anything that expects the + // working directory to keep the original path, including the + // go command when dealing with modules. + // + // os.Getwd stdlib has a special feature where if the + // cwd and the PWD are the same node then it trusts + // the PWD, so by setting it in the env for the child + // process we fix up all the paths returned by the go + // command. + // + // (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go) + cmd.Env = append(slicesClip(cfg.Env), "PWD="+cfg.Dir) cmd.Stdin = bytes.NewReader(req) cmd.Stdout = buf cmd.Stderr = stderr @@ -92,10 +143,14 @@ func findExternalDriver(cfg *Config) driver { fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr) } - var response driverResponse + var response DriverResponse if err := json.Unmarshal(buf.Bytes(), &response); err != nil { return nil, err } return &response, nil } } + +// slicesClip removes unused capacity from the slice, returning s[:len(s):len(s)]. +// TODO(adonovan): use go1.21 slices.Clip. +func slicesClip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] } diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index b5de9cf9f2..1a3a5b44f5 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -9,9 +9,9 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "log" "os" + "os/exec" "path" "path/filepath" "reflect" @@ -21,8 +21,6 @@ import ( "sync" "unicode" - exec "golang.org/x/sys/execabs" - "golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" ) @@ -36,23 +34,23 @@ type goTooOldError struct { error } -// responseDeduper wraps a driverResponse, deduplicating its contents. +// responseDeduper wraps a DriverResponse, deduplicating its contents. type responseDeduper struct { seenRoots map[string]bool seenPackages map[string]*Package - dr *driverResponse + dr *DriverResponse } func newDeduper() *responseDeduper { return &responseDeduper{ - dr: &driverResponse{}, + dr: &DriverResponse{}, seenRoots: map[string]bool{}, seenPackages: map[string]*Package{}, } } -// addAll fills in r with a driverResponse. -func (r *responseDeduper) addAll(dr *driverResponse) { +// addAll fills in r with a DriverResponse. +func (r *responseDeduper) addAll(dr *DriverResponse) { for _, pkg := range dr.Packages { r.addPackage(pkg) } @@ -129,7 +127,7 @@ func (state *golistState) mustGetEnv() map[string]string { // goListDriver uses the go list command to interpret the patterns and produce // the build system package structure. // See driver for more details. -func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { +func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error) { // Make sure that any asynchronous go commands are killed when we return. parentCtx := cfg.Context if parentCtx == nil { @@ -147,16 +145,18 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { } // Fill in response.Sizes asynchronously if necessary. - var sizeserr error - var sizeswg sync.WaitGroup if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { - sizeswg.Add(1) + errCh := make(chan error) go func() { - compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) - sizeserr = err + compiler, arch, err := getSizesForArgs(ctx, state.cfgInvocation(), cfg.gocmdRunner) response.dr.Compiler = compiler response.dr.Arch = arch - sizeswg.Done() + errCh <- err + }() + defer func() { + if sizesErr := <-errCh; sizesErr != nil { + err = sizesErr + } }() } @@ -209,87 +209,10 @@ extractQueries: } } - // Only use go/packages' overlay processing if we're using a Go version - // below 1.16. Otherwise, go list handles it. - if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 { - modifiedPkgs, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return nil, err - } - - var containsCandidates []string - if len(containFiles) > 0 { - containsCandidates = append(containsCandidates, modifiedPkgs...) - containsCandidates = append(containsCandidates, needPkgs...) - } - if err := state.addNeededOverlayPackages(response, needPkgs); err != nil { - return nil, err - } - // Check candidate packages for containFiles. - if len(containFiles) > 0 { - for _, id := range containsCandidates { - pkg, ok := response.seenPackages[id] - if !ok { - response.addPackage(&Package{ - ID: id, - Errors: []Error{{ - Kind: ListError, - Msg: fmt.Sprintf("package %s expected but not seen", id), - }}, - }) - continue - } - for _, f := range containFiles { - for _, g := range pkg.GoFiles { - if sameFile(f, g) { - response.addRoot(id) - } - } - } - } - } - // Add root for any package that matches a pattern. This applies only to - // packages that are modified by overlays, since they are not added as - // roots automatically. - for _, pattern := range restPatterns { - match := matchPattern(pattern) - for _, pkgID := range modifiedPkgs { - pkg, ok := response.seenPackages[pkgID] - if !ok { - continue - } - if match(pkg.PkgPath) { - response.addRoot(pkg.ID) - } - } - } - } - - sizeswg.Wait() - if sizeserr != nil { - return nil, sizeserr - } + // (We may yet return an error due to defer.) return response.dr, nil } -func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error { - if len(pkgs) == 0 { - return nil - } - dr, err := state.createDriverResponse(pkgs...) - if err != nil { - return err - } - for _, pkg := range dr.Packages { - response.addPackage(pkg) - } - _, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return err - } - return state.addNeededOverlayPackages(response, needPkgs) -} - func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. @@ -341,7 +264,7 @@ func (state *golistState) runContainsQueries(response *responseDeduper, queries // adhocPackage attempts to load or construct an ad-hoc package for a given // query, if the original call to the driver produced inadequate results. -func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) { +func (state *golistState) adhocPackage(pattern, query string) (*DriverResponse, error) { response, err := state.createDriverResponse(query) if err != nil { return nil, err @@ -432,7 +355,7 @@ func otherFiles(p *jsonPackage) [][]string { // createDriverResponse uses the "go list" command to expand the pattern // words and return a response for the specified packages. -func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) { +func (state *golistState) createDriverResponse(words ...string) (*DriverResponse, error) { // go list uses the following identifiers in ImportPath and Imports: // // "p" -- importable package or main (command) @@ -459,7 +382,7 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse pkgs := make(map[string]*Package) additionalErrors := make(map[string][]Error) // Decode the JSON and convert it to Package form. - response := &driverResponse{ + response := &DriverResponse{ GoVersion: goVersion, } for dec := json.NewDecoder(buf); dec.More(); { @@ -917,6 +840,7 @@ func (state *golistState) cfgInvocation() gocommand.Invocation { Env: cfg.Env, Logf: cfg.Logf, WorkingDir: cfg.Dir, + Overlay: cfg.goListOverlayFile, } } @@ -925,26 +849,6 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, cfg := state.cfg inv := state.cfgInvocation() - - // For Go versions 1.16 and above, `go list` accepts overlays directly via - // the -overlay flag. Set it, if it's available. - // - // The check for "list" is not necessarily required, but we should avoid - // getting the go version if possible. - if verb == "list" { - goVersion, err := state.getGoVersion() - if err != nil { - return nil, err - } - if goVersion >= 16 { - filename, cleanup, err := state.writeOverlays() - if err != nil { - return nil, err - } - defer cleanup() - inv.Overlay = filename - } - } inv.Verb = verb inv.Args = args gocmdRunner := cfg.gocmdRunner @@ -1091,67 +995,6 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, return stdout, nil } -// OverlayJSON is the format overlay files are expected to be in. -// The Replace map maps from overlaid paths to replacement paths: -// the Go command will forward all reads trying to open -// each overlaid path to its replacement path, or consider the overlaid -// path not to exist if the replacement path is empty. -// -// From golang/go#39958. -type OverlayJSON struct { - Replace map[string]string `json:"replace,omitempty"` -} - -// writeOverlays writes out files for go list's -overlay flag, as described -// above. -func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) { - // Do nothing if there are no overlays in the config. - if len(state.cfg.Overlay) == 0 { - return "", func() {}, nil - } - dir, err := ioutil.TempDir("", "gopackages-*") - if err != nil { - return "", nil, err - } - // The caller must clean up this directory, unless this function returns an - // error. - cleanup = func() { - os.RemoveAll(dir) - } - defer func() { - if err != nil { - cleanup() - } - }() - overlays := map[string]string{} - for k, v := range state.cfg.Overlay { - // Create a unique filename for the overlaid files, to avoid - // creating nested directories. - noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "") - f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator)) - if err != nil { - return "", func() {}, err - } - if _, err := f.Write(v); err != nil { - return "", func() {}, err - } - if err := f.Close(); err != nil { - return "", func() {}, err - } - overlays[k] = f.Name() - } - b, err := json.Marshal(OverlayJSON{Replace: overlays}) - if err != nil { - return "", func() {}, err - } - // Write out the overlay file that contains the filepath mappings. - filename = filepath.Join(dir, "overlay.json") - if err := ioutil.WriteFile(filename, b, 0665); err != nil { - return "", func() {}, err - } - return filename, cleanup, nil -} - func containsGoFile(s []string) bool { for _, f := range s { if strings.HasSuffix(f, ".go") { @@ -1180,3 +1023,44 @@ func cmdDebugStr(cmd *exec.Cmd) string { } return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " ")) } + +// getSizesForArgs queries 'go list' for the appropriate +// Compiler and GOARCH arguments to pass to [types.SizesFor]. +func getSizesForArgs(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) { + inv.Verb = "list" + inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} + stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) + var goarch, compiler string + if rawErr != nil { + rawErrMsg := rawErr.Error() + if strings.Contains(rawErrMsg, "cannot find main module") || + strings.Contains(rawErrMsg, "go.mod file not found") { + // User's running outside of a module. + // All bets are off. Get GOARCH and guess compiler is gc. + // TODO(matloob): Is this a problem in practice? + inv.Verb = "env" + inv.Args = []string{"GOARCH"} + envout, enverr := gocmdRunner.Run(ctx, inv) + if enverr != nil { + return "", "", enverr + } + goarch = strings.TrimSpace(envout.String()) + compiler = "gc" + } else if friendlyErr != nil { + return "", "", friendlyErr + } else { + // This should be unreachable, but be defensive + // in case RunRaw's error results are inconsistent. + return "", "", rawErr + } + } else { + fields := strings.Fields(stdout.String()) + if len(fields) < 2 { + return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>", + stdout.String(), stderr.String()) + } + goarch = fields[0] + compiler = fields[1] + } + return compiler, goarch, nil +} diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go index 9576b472f9..d823c474ad 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -6,314 +6,11 @@ package packages import ( "encoding/json" - "fmt" - "go/parser" - "go/token" - "os" "path/filepath" - "regexp" - "sort" - "strconv" - "strings" "golang.org/x/tools/internal/gocommand" ) -// processGolistOverlay provides rudimentary support for adding -// files that don't exist on disk to an overlay. The results can be -// sometimes incorrect. -// TODO(matloob): Handle unsupported cases, including the following: -// - determining the correct package to add given a new import path -func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { - havePkgs := make(map[string]string) // importPath -> non-test package ID - needPkgsSet := make(map[string]bool) - modifiedPkgsSet := make(map[string]bool) - - pkgOfDir := make(map[string][]*Package) - for _, pkg := range response.dr.Packages { - // This is an approximation of import path to id. This can be - // wrong for tests, vendored packages, and a number of other cases. - havePkgs[pkg.PkgPath] = pkg.ID - dir, err := commonDir(pkg.GoFiles) - if err != nil { - return nil, nil, err - } - if dir != "" { - pkgOfDir[dir] = append(pkgOfDir[dir], pkg) - } - } - - // If no new imports are added, it is safe to avoid loading any needPkgs. - // Otherwise, it's hard to tell which package is actually being loaded - // (due to vendoring) and whether any modified package will show up - // in the transitive set of dependencies (because new imports are added, - // potentially modifying the transitive set of dependencies). - var overlayAddsImports bool - - // If both a package and its test package are created by the overlay, we - // need the real package first. Process all non-test files before test - // files, and make the whole process deterministic while we're at it. - var overlayFiles []string - for opath := range state.cfg.Overlay { - overlayFiles = append(overlayFiles, opath) - } - sort.Slice(overlayFiles, func(i, j int) bool { - iTest := strings.HasSuffix(overlayFiles[i], "_test.go") - jTest := strings.HasSuffix(overlayFiles[j], "_test.go") - if iTest != jTest { - return !iTest // non-tests are before tests. - } - return overlayFiles[i] < overlayFiles[j] - }) - for _, opath := range overlayFiles { - contents := state.cfg.Overlay[opath] - base := filepath.Base(opath) - dir := filepath.Dir(opath) - var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant - var testVariantOf *Package // if opath is a test file, this is the package it is testing - var fileExists bool - isTestFile := strings.HasSuffix(opath, "_test.go") - pkgName, ok := extractPackageName(opath, contents) - if !ok { - // Don't bother adding a file that doesn't even have a parsable package statement - // to the overlay. - continue - } - // If all the overlay files belong to a different package, change the - // package name to that package. - maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir]) - nextPackage: - for _, p := range response.dr.Packages { - if pkgName != p.Name && p.ID != "command-line-arguments" { - continue - } - for _, f := range p.GoFiles { - if !sameFile(filepath.Dir(f), dir) { - continue - } - // Make sure to capture information on the package's test variant, if needed. - if isTestFile && !hasTestFiles(p) { - // TODO(matloob): Are there packages other than the 'production' variant - // of a package that this can match? This shouldn't match the test main package - // because the file is generated in another directory. - testVariantOf = p - continue nextPackage - } else if !isTestFile && hasTestFiles(p) { - // We're examining a test variant, but the overlaid file is - // a non-test file. Because the overlay implementation - // (currently) only adds a file to one package, skip this - // package, so that we can add the file to the production - // variant of the package. (https://golang.org/issue/36857 - // tracks handling overlays on both the production and test - // variant of a package). - continue nextPackage - } - if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { - // We have already seen the production version of the - // for which p is a test variant. - if hasTestFiles(p) { - testVariantOf = pkg - } - } - pkg = p - if filepath.Base(f) == base { - fileExists = true - } - } - } - // The overlay could have included an entirely new package or an - // ad-hoc package. An ad-hoc package is one that we have manually - // constructed from inadequate `go list` results for a file= query. - // It will have the ID command-line-arguments. - if pkg == nil || pkg.ID == "command-line-arguments" { - // Try to find the module or gopath dir the file is contained in. - // Then for modules, add the module opath to the beginning. - pkgPath, ok, err := state.getPkgPath(dir) - if err != nil { - return nil, nil, err - } - if !ok { - break - } - var forTest string // only set for x tests - isXTest := strings.HasSuffix(pkgName, "_test") - if isXTest { - forTest = pkgPath - pkgPath += "_test" - } - id := pkgPath - if isTestFile { - if isXTest { - id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest) - } else { - id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) - } - } - if pkg != nil { - // TODO(rstambler): We should change the package's path and ID - // here. The only issue is that this messes with the roots. - } else { - // Try to reclaim a package with the same ID, if it exists in the response. - for _, p := range response.dr.Packages { - if reclaimPackage(p, id, opath, contents) { - pkg = p - break - } - } - // Otherwise, create a new package. - if pkg == nil { - pkg = &Package{ - PkgPath: pkgPath, - ID: id, - Name: pkgName, - Imports: make(map[string]*Package), - } - response.addPackage(pkg) - havePkgs[pkg.PkgPath] = id - // Add the production package's sources for a test variant. - if isTestFile && !isXTest && testVariantOf != nil { - pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) - // Add the package under test and its imports to the test variant. - pkg.forTest = testVariantOf.PkgPath - for k, v := range testVariantOf.Imports { - pkg.Imports[k] = &Package{ID: v.ID} - } - } - if isXTest { - pkg.forTest = forTest - } - } - } - } - if !fileExists { - pkg.GoFiles = append(pkg.GoFiles, opath) - // TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior - // if the file will be ignored due to its build tags. - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath) - modifiedPkgsSet[pkg.ID] = true - } - imports, err := extractImports(opath, contents) - if err != nil { - // Let the parser or type checker report errors later. - continue - } - for _, imp := range imports { - // TODO(rstambler): If the package is an x test and the import has - // a test variant, make sure to replace it. - if _, found := pkg.Imports[imp]; found { - continue - } - overlayAddsImports = true - id, ok := havePkgs[imp] - if !ok { - var err error - id, err = state.resolveImport(dir, imp) - if err != nil { - return nil, nil, err - } - } - pkg.Imports[imp] = &Package{ID: id} - // Add dependencies to the non-test variant version of this package as well. - if testVariantOf != nil { - testVariantOf.Imports[imp] = &Package{ID: id} - } - } - } - - // toPkgPath guesses the package path given the id. - toPkgPath := func(sourceDir, id string) (string, error) { - if i := strings.IndexByte(id, ' '); i >= 0 { - return state.resolveImport(sourceDir, id[:i]) - } - return state.resolveImport(sourceDir, id) - } - - // Now that new packages have been created, do another pass to determine - // the new set of missing packages. - for _, pkg := range response.dr.Packages { - for _, imp := range pkg.Imports { - if len(pkg.GoFiles) == 0 { - return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath) - } - pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID) - if err != nil { - return nil, nil, err - } - if _, ok := havePkgs[pkgPath]; !ok { - needPkgsSet[pkgPath] = true - } - } - } - - if overlayAddsImports { - needPkgs = make([]string, 0, len(needPkgsSet)) - for pkg := range needPkgsSet { - needPkgs = append(needPkgs, pkg) - } - } - modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) - for pkg := range modifiedPkgsSet { - modifiedPkgs = append(modifiedPkgs, pkg) - } - return modifiedPkgs, needPkgs, err -} - -// resolveImport finds the ID of a package given its import path. -// In particular, it will find the right vendored copy when in GOPATH mode. -func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) { - env, err := state.getEnv() - if err != nil { - return "", err - } - if env["GOMOD"] != "" { - return importPath, nil - } - - searchDir := sourceDir - for { - vendorDir := filepath.Join(searchDir, "vendor") - exists, ok := state.vendorDirs[vendorDir] - if !ok { - info, err := os.Stat(vendorDir) - exists = err == nil && info.IsDir() - state.vendorDirs[vendorDir] = exists - } - - if exists { - vendoredPath := filepath.Join(vendorDir, importPath) - if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() { - // We should probably check for .go files here, but shame on anyone who fools us. - path, ok, err := state.getPkgPath(vendoredPath) - if err != nil { - return "", err - } - if ok { - return path, nil - } - } - } - - // We know we've hit the top of the filesystem when we Dir / and get /, - // or C:\ and get C:\, etc. - next := filepath.Dir(searchDir) - if next == searchDir { - break - } - searchDir = next - } - return importPath, nil -} - -func hasTestFiles(p *Package) bool { - for _, f := range p.GoFiles { - if strings.HasSuffix(f, "_test.go") { - return true - } - } - return false -} - // determineRootDirs returns a mapping from absolute directories that could // contain code to their corresponding import path prefixes. func (state *golistState) determineRootDirs() (map[string]string, error) { @@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { } return m, nil } - -func extractImports(filename string, contents []byte) ([]string, error) { - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset? - if err != nil { - return nil, err - } - var res []string - for _, imp := range f.Imports { - quotedPath := imp.Path.Value - path, err := strconv.Unquote(quotedPath) - if err != nil { - return nil, err - } - res = append(res, path) - } - return res, nil -} - -// reclaimPackage attempts to reuse a package that failed to load in an overlay. -// -// If the package has errors and has no Name, GoFiles, or Imports, -// then it's possible that it doesn't yet exist on disk. -func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - if pkg.ID != id { - return false - } - if len(pkg.Errors) != 1 { - return false - } - if pkg.Name != "" || pkg.ExportFile != "" { - return false - } - if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 { - return false - } - if len(pkg.Imports) > 0 { - return false - } - pkgName, ok := extractPackageName(filename, contents) - if !ok { - return false - } - pkg.Name = pkgName - pkg.Errors = nil - return true -} - -func extractPackageName(filename string, contents []byte) (string, bool) { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset? - if err != nil { - return "", false - } - return f.Name.Name, true -} - -// commonDir returns the directory that all files are in, "" if files is empty, -// or an error if they aren't in the same directory. -func commonDir(files []string) (string, error) { - seen := make(map[string]bool) - for _, f := range files { - seen[filepath.Dir(f)] = true - } - if len(seen) > 1 { - return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen) - } - for k := range seen { - // seen has only one element; return it. - return k, nil - } - return "", nil // no files -} - -// It is possible that the files in the disk directory dir have a different package -// name from newName, which is deduced from the overlays. If they all have a different -// package name, and they all have the same package name, then that name becomes -// the package name. -// It returns true if it changes the package name, false otherwise. -func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) { - names := make(map[string]int) - for _, p := range pkgsOfDir { - names[p.Name]++ - } - if len(names) != 1 { - // some files are in different packages - return - } - var oldName string - for k := range names { - oldName = k - } - if newName == oldName { - return - } - // We might have a case where all of the package names in the directory are - // the same, but the overlay file is for an x test, which belongs to its - // own package. If the x test does not yet exist on disk, we may not yet - // have its package name on disk, but we should not rename the packages. - // - // We use a heuristic to determine if this file belongs to an x test: - // The test file should have a package name whose package name has a _test - // suffix or looks like "newName_test". - maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test") - if isTestFile && maybeXTest { - return - } - for _, p := range pkgsOfDir { - p.Name = newName - } -} - -// This function is copy-pasted from -// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360. -// It should be deleted when we remove support for overlays from go/packages. -// -// NOTE: This does not handle any ./... or ./ style queries, as this function -// doesn't know the working directory. -// -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -// Unfortunately, there are two special cases. Quoting "go help packages": -// -// First, /... at the end of the pattern can match an empty string, -// so that net/... matches both net and packages in its subdirectories, like net/http. -// Second, any slash-separated pattern element containing a wildcard never -// participates in a match of the "vendor" element in the path of a vendored -// package, so that ./... does not match packages in subdirectories of -// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. -// Note, however, that a directory named vendor that itself contains code -// is not a vendored package: cmd/vendor would be a command named vendor, -// and the pattern cmd/... matches it. -func matchPattern(pattern string) func(name string) bool { - // Convert pattern to regular expression. - // The strategy for the trailing /... is to nest it in an explicit ? expression. - // The strategy for the vendor exclusion is to change the unmatchable - // vendor strings to a disallowed code point (vendorChar) and to use - // "(anything but that codepoint)*" as the implementation of the ... wildcard. - // This is a bit complicated but the obvious alternative, - // namely a hand-written search like in most shell glob matchers, - // is too easy to make accidentally exponential. - // Using package regexp guarantees linear-time matching. - - const vendorChar = "\x00" - - if strings.Contains(pattern, vendorChar) { - return func(name string) bool { return false } - } - - re := regexp.QuoteMeta(pattern) - re = replaceVendor(re, vendorChar) - switch { - case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): - re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` - case re == vendorChar+`/\.\.\.`: - re = `(/vendor|/` + vendorChar + `/\.\.\.)` - case strings.HasSuffix(re, `/\.\.\.`): - re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` - } - re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`) - - reg := regexp.MustCompile(`^` + re + `$`) - - return func(name string) bool { - if strings.Contains(name, vendorChar) { - return false - } - return reg.MatchString(replaceVendor(name, vendorChar)) - } -} - -// replaceVendor returns the result of replacing -// non-trailing vendor path elements in x with repl. -func replaceVendor(x, repl string) string { - if !strings.Contains(x, "vendor") { - return x - } - elem := strings.Split(x, "/") - for i := 0; i < len(elem)-1; i++ { - if elem[i] == "vendor" { - elem[i] = repl - } - } - return strings.Join(elem, "/") -} diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 124a6fe143..0b6bfaff80 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -9,6 +9,7 @@ package packages import ( "context" "encoding/json" + "errors" "fmt" "go/ast" "go/parser" @@ -16,7 +17,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -25,20 +25,31 @@ import ( "sync" "time" + "golang.org/x/sync/errgroup" + "golang.org/x/tools/go/gcexportdata" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" - "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/versions" ) // A LoadMode controls the amount of detail to return when loading. // The bits below can be combined to specify which fields should be // filled in the result packages. +// // The zero value is a special case, equivalent to combining // the NeedName, NeedFiles, and NeedCompiledGoFiles bits. +// // ID and Errors (if present) will always be filled. -// Load may return more information than requested. +// [Load] may return more information than requested. +// +// Unfortunately there are a number of open bugs related to +// interactions among the LoadMode bits: +// - https://github.com/golang/go/issues/56633 +// - https://github.com/golang/go/issues/56677 +// - https://github.com/golang/go/issues/58726 +// - https://github.com/golang/go/issues/63517 type LoadMode int const ( @@ -64,7 +75,7 @@ const ( // NeedTypes adds Types, Fset, and IllTyped. NeedTypes - // NeedSyntax adds Syntax. + // NeedSyntax adds Syntax and Fset. NeedSyntax // NeedTypesInfo adds TypesInfo. @@ -121,15 +132,21 @@ const ( // A Config specifies details about how packages should be loaded. // The zero value is a valid configuration. +// // Calls to Load do not modify this struct. +// +// TODO(adonovan): #67702: this is currently false: in fact, +// calls to [Load] do not modify the public fields of this struct, but +// may modify hidden fields, so concurrent calls to [Load] must not +// use the same Config. But perhaps we should reestablish the +// documented invariant. type Config struct { // Mode controls the level of information returned for each package. Mode LoadMode // Context specifies the context for the load operation. - // If the context is cancelled, the loader may stop early - // and return an ErrCancelled error. - // If Context is nil, the load cannot be cancelled. + // Cancelling the context may cause [Load] to abort and + // return an error. Context context.Context // Logf is the logger for the config. @@ -198,50 +215,23 @@ type Config struct { // setting Tests may have no effect. Tests bool - // Overlay provides a mapping of absolute file paths to file contents. - // If the file with the given path already exists, the parser will use the - // alternative file contents provided by the map. + // Overlay is a mapping from absolute file paths to file contents. // - // Overlays provide incomplete support for when a given file doesn't - // already exist on disk. See the package doc above for more details. + // For each map entry, [Load] uses the alternative file + // contents provided by the overlay mapping instead of reading + // from the file system. This mechanism can be used to enable + // editor-integrated tools to correctly analyze the contents + // of modified but unsaved buffers, for example. + // + // The overlay mapping is passed to the build system's driver + // (see "The driver protocol") so that it too can report + // consistent package metadata about unsaved files. However, + // drivers may vary in their level of support for overlays. Overlay map[string][]byte -} -// driver is the type for functions that query the build system for the -// packages named by the patterns. -type driver func(cfg *Config, patterns ...string) (*driverResponse, error) - -// driverResponse contains the results for a driver query. -type driverResponse struct { - // NotHandled is returned if the request can't be handled by the current - // driver. If an external driver returns a response with NotHandled, the - // rest of the driverResponse is ignored, and go/packages will fallback - // to the next driver. If go/packages is extended in the future to support - // lists of multiple drivers, go/packages will fall back to the next driver. - NotHandled bool - - // Compiler and Arch are the arguments pass of types.SizesFor - // to get a types.Sizes to use when type checking. - Compiler string - Arch string - - // Roots is the set of package IDs that make up the root packages. - // We have to encode this separately because when we encode a single package - // we cannot know if it is one of the roots as that requires knowledge of the - // graph it is part of. - Roots []string `json:",omitempty"` - - // Packages is the full set of packages in the graph. - // The packages are not connected into a graph. - // The Imports if populated will be stubs that only have their ID set. - // Imports will be connected and then type and syntax information added in a - // later pass (see refine). - Packages []*Package - - // GoVersion is the minor version number used by the driver - // (e.g. the go command on the PATH) when selecting .go files. - // Zero means unknown. - GoVersion int + // goListOverlayFile is the JSON file that encodes the Overlay + // mapping, used by 'go list -overlay=...' + goListOverlayFile string } // Load loads and returns the Go packages named by the given patterns. @@ -249,8 +239,22 @@ type driverResponse struct { // Config specifies loading options; // nil behaves the same as an empty Config. // -// Load returns an error if any of the patterns was invalid -// as defined by the underlying build system. +// The [Config.Mode] field is a set of bits that determine what kinds +// of information should be computed and returned. Modes that require +// more information tend to be slower. See [LoadMode] for details +// and important caveats. Its zero value is equivalent to +// NeedName | NeedFiles | NeedCompiledGoFiles. +// +// Each call to Load returns a new set of [Package] instances. +// The Packages and their Imports form a directed acyclic graph. +// +// If the [NeedTypes] mode flag was set, each call to Load uses a new +// [types.Importer], so [types.Object] and [types.Type] values from +// different calls to Load must not be mixed as they will have +// inconsistent notions of type identity. +// +// If any of the patterns was invalid as defined by the +// underlying build system, Load returns an error. // It may return an empty list of packages without an error, // for instance for an empty expansion of a valid wildcard. // Errors associated with a particular package are recorded in the @@ -259,34 +263,162 @@ type driverResponse struct { // proceeding with further analysis. The PrintErrors function is // provided for convenient display of all errors. func Load(cfg *Config, patterns ...string) ([]*Package, error) { - l := newLoader(cfg) - response, err := defaultDriver(&l.Config, patterns...) + ld := newLoader(cfg) + response, external, err := defaultDriver(&ld.Config, patterns...) if err != nil { return nil, err } - l.sizes = types.SizesFor(response.Compiler, response.Arch) - return l.refine(response) + + ld.sizes = types.SizesFor(response.Compiler, response.Arch) + if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 { + // Type size information is needed but unavailable. + if external { + // An external driver may fail to populate the Compiler/GOARCH fields, + // especially since they are relatively new (see #63700). + // Provide a sensible fallback in this case. + ld.sizes = types.SizesFor("gc", runtime.GOARCH) + if ld.sizes == nil { // gccgo-only arch + ld.sizes = types.SizesFor("gc", "amd64") + } + } else { + // Go list should never fail to deliver accurate size information. + // Reject the whole Load since the error is the same for every package. + return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q", + response.Compiler, response.Arch) + } + } + + return ld.refine(response) } // defaultDriver is a driver that implements go/packages' fallback behavior. // It will try to request to an external driver, if one exists. If there's // no external driver, or the driver returns a response with NotHandled set, // defaultDriver will fall back to the go list driver. -func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { - driver := findExternalDriver(cfg) - if driver == nil { - driver = goListDriver +// The boolean result indicates that an external driver handled the request. +func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, error) { + const ( + // windowsArgMax specifies the maximum command line length for + // the Windows' CreateProcess function. + windowsArgMax = 32767 + // maxEnvSize is a very rough estimation of the maximum environment + // size of a user. + maxEnvSize = 16384 + // safeArgMax specifies the maximum safe command line length to use + // by the underlying driver excl. the environment. We choose the Windows' + // ARG_MAX as the starting point because it's one of the lowest ARG_MAX + // constants out of the different supported platforms, + // e.g., https://www.in-ulm.de/~mascheck/various/argmax/#results. + safeArgMax = windowsArgMax - maxEnvSize + ) + chunks, err := splitIntoChunks(patterns, safeArgMax) + if err != nil { + return nil, false, err + } + + if driver := findExternalDriver(cfg); driver != nil { + response, err := callDriverOnChunks(driver, cfg, chunks) + if err != nil { + return nil, false, err + } else if !response.NotHandled { + return response, true, nil + } + // (fall through) } - response, err := driver(cfg, patterns...) + + // go list fallback + // + // Write overlays once, as there are many calls + // to 'go list' (one per chunk plus others too). + overlay, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay) + if err != nil { + return nil, false, err + } + defer cleanupOverlay() + cfg.goListOverlayFile = overlay + + response, err := callDriverOnChunks(goListDriver, cfg, chunks) if err != nil { - return response, err - } else if response.NotHandled { - return goListDriver(cfg, patterns...) + return nil, false, err } - return response, nil + return response, false, err +} + +// splitIntoChunks chunks the slice so that the total number of characters +// in a chunk is no longer than argMax. +func splitIntoChunks(patterns []string, argMax int) ([][]string, error) { + if argMax <= 0 { + return nil, errors.New("failed to split patterns into chunks, negative safe argMax value") + } + var chunks [][]string + charsInChunk := 0 + nextChunkStart := 0 + for i, v := range patterns { + vChars := len(v) + if vChars > argMax { + // a single pattern is longer than the maximum safe ARG_MAX, hardly should happen + return nil, errors.New("failed to split patterns into chunks, a pattern is too long") + } + charsInChunk += vChars + 1 // +1 is for a whitespace between patterns that has to be counted too + if charsInChunk > argMax { + chunks = append(chunks, patterns[nextChunkStart:i]) + nextChunkStart = i + charsInChunk = vChars + } + } + // add the last chunk + if nextChunkStart < len(patterns) { + chunks = append(chunks, patterns[nextChunkStart:]) + } + return chunks, nil +} + +func callDriverOnChunks(driver driver, cfg *Config, chunks [][]string) (*DriverResponse, error) { + if len(chunks) == 0 { + return driver(cfg) + } + responses := make([]*DriverResponse, len(chunks)) + errNotHandled := errors.New("driver returned NotHandled") + var g errgroup.Group + for i, chunk := range chunks { + i := i + chunk := chunk + g.Go(func() (err error) { + responses[i], err = driver(cfg, chunk...) + if responses[i] != nil && responses[i].NotHandled { + err = errNotHandled + } + return err + }) + } + if err := g.Wait(); err != nil { + if errors.Is(err, errNotHandled) { + return &DriverResponse{NotHandled: true}, nil + } + return nil, err + } + return mergeResponses(responses...), nil +} + +func mergeResponses(responses ...*DriverResponse) *DriverResponse { + if len(responses) == 0 { + return nil + } + response := newDeduper() + response.dr.NotHandled = false + response.dr.Compiler = responses[0].Compiler + response.dr.Arch = responses[0].Arch + response.dr.GoVersion = responses[0].GoVersion + for _, v := range responses { + response.addAll(v) + } + return response.dr } // A Package describes a loaded Go package. +// +// It also defines part of the JSON schema of [DriverResponse]. +// See the package documentation for an overview. type Package struct { // ID is a unique identifier for a package, // in a syntax provided by the underlying build system. @@ -345,19 +477,30 @@ type Package struct { // to corresponding loaded Packages. Imports map[string]*Package + // Module is the module information for the package if it exists. + // + // Note: it may be missing for std and cmd; see Go issue #65816. + Module *Module + + // -- The following fields are not part of the driver JSON schema. -- + // Types provides type information for the package. // The NeedTypes LoadMode bit sets this field for packages matching the // patterns; type information for dependencies may be missing or incomplete, // unless NeedDeps and NeedImports are also set. - Types *types.Package + // + // Each call to [Load] returns a consistent set of type + // symbols, as defined by the comment at [types.Identical]. + // Avoid mixing type information from two or more calls to [Load]. + Types *types.Package `json:"-"` // Fset provides position information for Types, TypesInfo, and Syntax. // It is set only when Types is set. - Fset *token.FileSet + Fset *token.FileSet `json:"-"` // IllTyped indicates whether the package or any dependency contains errors. // It is set only when Types is set. - IllTyped bool + IllTyped bool `json:"-"` // Syntax is the package's syntax trees, for the files listed in CompiledGoFiles. // @@ -367,26 +510,28 @@ type Package struct { // // Syntax is kept in the same order as CompiledGoFiles, with the caveat that nils are // removed. If parsing returned nil, Syntax may be shorter than CompiledGoFiles. - Syntax []*ast.File + Syntax []*ast.File `json:"-"` // TypesInfo provides type information about the package's syntax trees. // It is set only when Syntax is set. - TypesInfo *types.Info + TypesInfo *types.Info `json:"-"` // TypesSizes provides the effective size function for types in TypesInfo. - TypesSizes types.Sizes + TypesSizes types.Sizes `json:"-"` + + // -- internal -- // forTest is the package under test, if any. forTest string // depsErrors is the DepsErrors field from the go list response, if any. depsErrors []*packagesinternal.PackageError - - // module is the module information for the package if it exists. - Module *Module } // Module provides module information for a package. +// +// It also defines part of the JSON schema of [DriverResponse]. +// See the package documentation for an overview. type Module struct { Path string // module path Version string // module version @@ -412,12 +557,6 @@ func init() { packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError { return p.(*Package).depsErrors } - packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner { - return config.(*Config).gocmdRunner - } - packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) { - config.(*Config).gocmdRunner = runner - } packagesinternal.SetModFile = func(config interface{}, value string) { config.(*Config).modFile = value } @@ -525,6 +664,7 @@ func (p *Package) UnmarshalJSON(b []byte) error { OtherFiles: flat.OtherFiles, EmbedFiles: flat.EmbedFiles, EmbedPatterns: flat.EmbedPatterns, + IgnoredFiles: flat.IgnoredFiles, ExportFile: flat.ExportFile, } if len(flat.Imports) > 0 { @@ -554,7 +694,7 @@ type loaderPackage struct { type loader struct { pkgs map[string]*loaderPackage Config - sizes types.Sizes + sizes types.Sizes // non-nil if needed by mode parseCache map[string]*parseValue parseCacheMu sync.Mutex exportMu sync.Mutex // enforces mutual exclusion of exportdata operations @@ -634,7 +774,7 @@ func newLoader(cfg *Config) *loader { // refine connects the supplied packages into a graph and then adds type // and syntax information as requested by the LoadMode. -func (ld *loader) refine(response *driverResponse) ([]*Package, error) { +func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { roots := response.Roots rootMap := make(map[string]int, len(roots)) for i, root := range roots { @@ -679,39 +819,38 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } } - // Materialize the import graph. - - const ( - white = 0 // new - grey = 1 // in progress - black = 2 // complete - ) - - // visit traverses the import graph, depth-first, - // and materializes the graph as Packages.Imports. - // - // Valid imports are saved in the Packages.Import map. - // Invalid imports (cycles and missing nodes) are saved in the importErrors map. - // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. - // - // visit returns whether the package needs src or has a transitive - // dependency on a package that does. These are the only packages - // for which we load source code. - var stack []*loaderPackage - var visit func(lpkg *loaderPackage) bool - var srcPkgs []*loaderPackage - visit = func(lpkg *loaderPackage) bool { - switch lpkg.color { - case black: - return lpkg.needsrc - case grey: - panic("internal error: grey node") - } - lpkg.color = grey - stack = append(stack, lpkg) // push - stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports - // If NeedImports isn't set, the imports fields will all be zeroed out. - if ld.Mode&NeedImports != 0 { + if ld.Mode&NeedImports != 0 { + // Materialize the import graph. + + const ( + white = 0 // new + grey = 1 // in progress + black = 2 // complete + ) + + // visit traverses the import graph, depth-first, + // and materializes the graph as Packages.Imports. + // + // Valid imports are saved in the Packages.Import map. + // Invalid imports (cycles and missing nodes) are saved in the importErrors map. + // Thus, even in the presence of both kinds of errors, + // the Import graph remains a DAG. + // + // visit returns whether the package needs src or has a transitive + // dependency on a package that does. These are the only packages + // for which we load source code. + var stack []*loaderPackage + var visit func(lpkg *loaderPackage) bool + visit = func(lpkg *loaderPackage) bool { + switch lpkg.color { + case black: + return lpkg.needsrc + case grey: + panic("internal error: grey node") + } + lpkg.color = grey + stack = append(stack, lpkg) // push + stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports lpkg.Imports = make(map[string]*Package, len(stubs)) for importPath, ipkg := range stubs { var importErr error @@ -735,40 +874,39 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } lpkg.Imports[importPath] = imp.Package } - } - if lpkg.needsrc { - srcPkgs = append(srcPkgs, lpkg) - } - if ld.Mode&NeedTypesSizes != 0 { - lpkg.TypesSizes = ld.sizes - } - stack = stack[:len(stack)-1] // pop - lpkg.color = black - return lpkg.needsrc - } + // Complete type information is required for the + // immediate dependencies of each source package. + if lpkg.needsrc && ld.Mode&NeedTypes != 0 { + for _, ipkg := range lpkg.Imports { + ld.pkgs[ipkg.ID].needtypes = true + } + } - if ld.Mode&NeedImports == 0 { - // We do this to drop the stub import packages that we are not even going to try to resolve. - for _, lpkg := range initial { - lpkg.Imports = nil + // NeedTypeSizes causes TypeSizes to be set even + // on packages for which types aren't needed. + if ld.Mode&NeedTypesSizes != 0 { + lpkg.TypesSizes = ld.sizes + } + stack = stack[:len(stack)-1] // pop + lpkg.color = black + + return lpkg.needsrc } - } else { + // For each initial package, create its import DAG. for _, lpkg := range initial { visit(lpkg) } - } - if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { - for _, lpkg := range srcPkgs { - // Complete type information is required for the - // immediate dependencies of each source package. - for _, ipkg := range lpkg.Imports { - imp := ld.pkgs[ipkg.ID] - imp.needtypes = true - } + + } else { + // !NeedImports: drop the stub (ID-only) import packages + // that we are not even going to try to resolve. + for _, lpkg := range initial { + lpkg.Imports = nil } } + // Load type data and syntax if needed, starting at // the initial packages (roots of the import DAG). if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { @@ -783,6 +921,12 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { wg.Wait() } + // If the context is done, return its error and + // throw out [likely] incomplete packages. + if err := ld.Context.Err(); err != nil { + return nil, err + } + result := make([]*Package, len(initial)) for i, lpkg := range initial { result[i] = lpkg.Package @@ -816,12 +960,14 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } if ld.requestedMode&NeedTypes == 0 { ld.pkgs[i].Types = nil - ld.pkgs[i].Fset = nil ld.pkgs[i].IllTyped = false } if ld.requestedMode&NeedSyntax == 0 { ld.pkgs[i].Syntax = nil } + if ld.requestedMode&NeedTypes == 0 && ld.requestedMode&NeedSyntax == 0 { + ld.pkgs[i].Fset = nil + } if ld.requestedMode&NeedTypesInfo == 0 { ld.pkgs[i].TypesInfo = nil } @@ -878,6 +1024,14 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name) lpkg.Fset = ld.Fset + // Start shutting down if the context is done and do not load + // source or export data files. + // Packages that import this one will have ld.Context.Err() != nil. + // ld.Context.Err() will be returned later by refine. + if ld.Context.Err() != nil { + return + } + // Subtle: we populate all Types fields with an empty Package // before loading export data so that export data processing // never has to create a types.Package for an indirect dependency, @@ -997,15 +1151,23 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { return } + // Start shutting down if the context is done and do not type check. + // Packages that import this one will have ld.Context.Err() != nil. + // ld.Context.Err() will be returned later by refine. + if ld.Context.Err() != nil { + return + } + lpkg.TypesInfo = &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), + Instances: make(map[*ast.Ident]types.Instance), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), } - typeparams.InitInstanceInfo(lpkg.TypesInfo) + versions.InitFileVersions(lpkg.TypesInfo) lpkg.TypesSizes = ld.sizes importer := importerFunc(func(path string) (*types.Package, error) { @@ -1043,10 +1205,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, Error: appendError, - Sizes: ld.sizes, + Sizes: ld.sizes, // may be nil } if lpkg.Module != nil && lpkg.Module.GoVersion != "" { - typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion) + tc.GoVersion = "go" + lpkg.Module.GoVersion } if (ld.Mode & typecheckCgo) != 0 { if !typesinternal.SetUsesCgo(tc) { @@ -1057,10 +1219,24 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { return } } - types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) + typErr := types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) lpkg.importErrors = nil // no longer needed + // In go/types go1.21 and go1.22, Checker.Files failed fast with a + // a "too new" error, without calling tc.Error and without + // proceeding to type-check the package (#66525). + // We rely on the runtimeVersion error to give the suggested remedy. + if typErr != nil && len(lpkg.Errors) == 0 && len(lpkg.Syntax) > 0 { + if msg := typErr.Error(); strings.HasPrefix(msg, "package requires newer Go version") { + appendError(types.Error{ + Fset: ld.Fset, + Pos: lpkg.Syntax[0].Package, + Msg: msg, + }) + } + } + // If !Cgo, the type-checker uses FakeImportC mode, so // it doesn't invoke the importer for import "C", // nor report an error for the import, @@ -1082,6 +1258,12 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { } } + // If types.Checker.Files had an error that was unreported, + // make sure to report the unknown error so the package is illTyped. + if typErr != nil && len(lpkg.Errors) == 0 { + appendError(typErr) + } + // Record accumulated errors. illTyped := len(lpkg.Errors) > 0 if !illTyped { @@ -1127,7 +1309,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { var err error if src == nil { ioLimit <- true // wait - src, err = ioutil.ReadFile(filename) + src, err = os.ReadFile(filename) <-ioLimit // signal } if err != nil { @@ -1153,11 +1335,6 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { parsed := make([]*ast.File, n) errors := make([]error, n) for i, file := range filenames { - if ld.Config.Context.Err() != nil { - parsed[i] = nil - errors[i] = ld.Config.Context.Err() - continue - } wg.Add(1) go func(i int, filename string) { parsed[i], errors[i] = ld.parseFile(filename) @@ -1323,6 +1500,10 @@ func impliedLoadMode(loadMode LoadMode) LoadMode { // All these things require knowing the import graph. loadMode |= NeedImports } + if loadMode&NeedTypes != 0 { + // Types require the GoVersion from Module. + loadMode |= NeedModule + } return loadMode } diff --git a/vendor/golang.org/x/tools/go/packages/visit.go b/vendor/golang.org/x/tools/go/packages/visit.go index a1dcc40b72..df14ffd94d 100644 --- a/vendor/golang.org/x/tools/go/packages/visit.go +++ b/vendor/golang.org/x/tools/go/packages/visit.go @@ -49,11 +49,20 @@ func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { // PrintErrors returns the number of errors printed. func PrintErrors(pkgs []*Package) int { var n int + errModules := make(map[*Module]bool) Visit(pkgs, nil, func(pkg *Package) { for _, err := range pkg.Errors { fmt.Fprintln(os.Stderr, err) n++ } + + // Print pkg.Module.Error once if present. + mod := pkg.Module + if mod != nil && mod.Error != nil && !errModules[mod] { + errModules[mod] = true + fmt.Fprintln(os.Stderr, mod.Error.Err) + n++ + } }) return n } diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index fa5834baf7..9ada177758 100644 --- a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -26,15 +26,15 @@ package objectpath import ( "fmt" "go/types" - "sort" "strconv" "strings" - _ "unsafe" - "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typesinternal" ) +// TODO(adonovan): think about generic aliases. + // A Path is an opaque name that identifies a types.Object // relative to its package. Conceptually, the name consists of a // sequence of destructuring operations applied to the package scope @@ -51,7 +51,7 @@ type Path string // // PO package->object Package.Scope.Lookup // OT object->type Object.Type -// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU] +// TT type->type Type.{Elem,Key,{,{,Recv}Type}Params,Results,Underlying,Rhs} [EKPRUTrCa] // TO type->object Type.{At,Field,Method,Obj} [AFMO] // // All valid paths start with a package and end at an object @@ -63,8 +63,8 @@ type Path string // - The only PO operator is Package.Scope.Lookup, which requires an identifier. // - The only OT operator is Object.Type, // which we encode as '.' because dot cannot appear in an identifier. -// - The TT operators are encoded as [EKPRUTC]; -// one of these (TypeParam) requires an integer operand, +// - The TT operators are encoded as [EKPRUTrCa]; +// two of these ({,Recv}TypeParams) require an integer operand, // which is encoded as a string of decimal digits. // - The TO operators are encoded as [AFMO]; // three of these (At,Field,Method) require an integer operand, @@ -98,19 +98,21 @@ const ( opType = '.' // .Type() (Object) // type->type operators - opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) - opKey = 'K' // .Key() (Map) - opParams = 'P' // .Params() (Signature) - opResults = 'R' // .Results() (Signature) - opUnderlying = 'U' // .Underlying() (Named) - opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) - opConstraint = 'C' // .Constraint() (TypeParam) + opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) + opKey = 'K' // .Key() (Map) + opParams = 'P' // .Params() (Signature) + opResults = 'R' // .Results() (Signature) + opUnderlying = 'U' // .Underlying() (Named) + opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) + opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature) + opConstraint = 'C' // .Constraint() (TypeParam) + opRhs = 'a' // .Rhs() (Alias) // type->object operators - opAt = 'A' // .At(i) (Tuple) - opField = 'F' // .Field(i) (Struct) - opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) - opObj = 'O' // .Obj() (Named, TypeParam) + opAt = 'A' // .At(i) (Tuple) + opField = 'F' // .Field(i) (Struct) + opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) + opObj = 'O' // .Obj() (Named, TypeParam) ) // For is equivalent to new(Encoder).For(obj). @@ -123,20 +125,7 @@ func For(obj types.Object) (Path, error) { // An Encoder amortizes the cost of encoding the paths of multiple objects. // The zero value of an Encoder is ready to use. type Encoder struct { - scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects - namedMethodsMemo map[*types.Named][]*types.Func // memoization of namedMethods() - skipMethodSorting bool -} - -// Expose back doors so that gopls can avoid method sorting, which can dominate -// analysis on certain repositories. -// -// TODO(golang/go#61443): remove this. -func init() { - typesinternal.SkipEncoderMethodSorting = func(enc interface{}) { - enc.(*Encoder).skipMethodSorting = true - } - typesinternal.ObjectpathObject = object + scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects } // For returns the path to an object relative to its package, @@ -239,7 +228,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { // Reject obviously non-viable cases. switch obj := obj.(type) { case *types.TypeName: - if _, ok := obj.Type().(*typeparams.TypeParam); !ok { + if _, ok := aliases.Unalias(obj.Type()).(*types.TypeParam); !ok { // With the exception of type parameters, only package-level type names // have a path. return "", fmt.Errorf("no path for %v", obj) @@ -291,21 +280,26 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { path = append(path, opType) T := o.Type() + if alias, ok := T.(*aliases.Alias); ok { + if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam, nil); r != nil { + return Path(r), nil + } + if r := find(obj, aliases.Rhs(alias), append(path, opRhs), nil); r != nil { + return Path(r), nil + } - if tname.IsAlias() { - // type alias + } else if tname.IsAlias() { + // legacy alias if r := find(obj, T, path, nil); r != nil { return Path(r), nil } - } else { - if named, _ := T.(*types.Named); named != nil { - if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil { - // generic named type - return Path(r), nil - } - } + + } else if named, ok := T.(*types.Named); ok { // defined (named) type - if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil { + if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil { + return Path(r), nil + } + if r := find(obj, named.Underlying(), append(path, opUnderlying), nil); r != nil { return Path(r), nil } } @@ -326,33 +320,20 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { } // Inspect declared methods of defined types. - if T, ok := o.Type().(*types.Named); ok { + if T, ok := aliases.Unalias(o.Type()).(*types.Named); ok { path = append(path, opType) - if !enc.skipMethodSorting { - // Note that method index here is always with respect - // to canonical ordering of methods, regardless of how - // they appear in the underlying type. - for i, m := range enc.namedMethods(T) { - path2 := appendOpArg(path, opMethod, i) - if m == obj { - return Path(path2), nil // found declared method - } - if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { - return Path(r), nil - } + // The method index here is always with respect + // to the underlying go/types data structures, + // which ultimately derives from source order + // and must be preserved by export data. + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return Path(path2), nil // found declared method } - } else { - // This branch must match the logic in the branch above, using go/types - // APIs without sorting. - for i := 0; i < T.NumMethods(); i++ { - m := T.Method(i) - path2 := appendOpArg(path, opMethod, i) - if m == obj { - return Path(path2), nil // found declared method - } - if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { - return Path(r), nil - } + if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { + return Path(r), nil } } } @@ -420,17 +401,12 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { // of objectpath will only be giving us origin methods, anyway, as referring // to instantiated methods is usually not useful. - if typeparams.OriginMethod(meth) != meth { + if meth.Origin() != meth { return "", false } - recvT := meth.Type().(*types.Signature).Recv().Type() - if ptr, ok := recvT.(*types.Pointer); ok { - recvT = ptr.Elem() - } - - named, ok := recvT.(*types.Named) - if !ok { + _, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv()) + if named == nil { return "", false } @@ -448,22 +424,13 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { path = append(path, name...) path = append(path, opType) - if !enc.skipMethodSorting { - for i, m := range enc.namedMethods(named) { - if m == meth { - path = appendOpArg(path, opMethod, i) - return Path(path), true - } - } - } else { - // This branch must match the logic of the branch above, using go/types - // APIs without sorting. - for i := 0; i < named.NumMethods(); i++ { - m := named.Method(i) - if m == meth { - path = appendOpArg(path, opMethod, i) - return Path(path), true - } + // Method indices are w.r.t. the go/types data structures, + // ultimately deriving from source order, + // which is preserved by export data. + for i := 0; i < named.NumMethods(); i++ { + if named.Method(i) == meth { + path = appendOpArg(path, opMethod, i) + return Path(path), true } } @@ -482,6 +449,8 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { // nil, it will be allocated as necessary. func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { switch T := T.(type) { + case *aliases.Alias: + return find(obj, aliases.Unalias(T), path, seen) case *types.Basic, *types.Named: // Named types belonging to pkg were handled already, // so T must belong to another package. No path. @@ -500,7 +469,10 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] } return find(obj, T.Elem(), append(path, opElem), seen) case *types.Signature: - if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil { + if r := findTypeParam(obj, T.RecvTypeParams(), path, opRecvTypeParam, nil); r != nil { + return r + } + if r := findTypeParam(obj, T.TypeParams(), path, opTypeParam, seen); r != nil { return r } if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { @@ -543,7 +515,7 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] } } return nil - case *typeparams.TypeParam: + case *types.TypeParam: name := T.Obj() if name == obj { return append(path, opObj) @@ -563,10 +535,10 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] panic(T) } -func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { +func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte, seen map[*types.TypeName]bool) []byte { for i := 0; i < list.Len(); i++ { tparam := list.At(i) - path2 := appendOpArg(path, opTypeParam, i) + path2 := appendOpArg(path, op, i) if r := find(obj, tparam, path2, seen); r != nil { return r } @@ -576,12 +548,7 @@ func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte // Object returns the object denoted by path p within the package pkg. func Object(pkg *types.Package, p Path) (types.Object, error) { - return object(pkg, string(p), false) -} - -// Note: the skipMethodSorting parameter must match the value of -// Encoder.skipMethodSorting used during encoding. -func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.Object, error) { + pathstr := string(p) if pathstr == "" { return nil, fmt.Errorf("empty path") } @@ -605,7 +572,7 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O } // abstraction of *types.{Named,Signature} type hasTypeParams interface { - TypeParams() *typeparams.TypeParamList + TypeParams() *types.TypeParamList } // abstraction of *types.{Named,TypeParam} type hasObj interface { @@ -623,10 +590,10 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O code := suffix[0] suffix = suffix[1:] - // Codes [AFM] have an integer operand. + // Codes [AFMTr] have an integer operand. var index int switch code { - case opAt, opField, opMethod, opTypeParam: + case opAt, opField, opMethod, opTypeParam, opRecvTypeParam: rest := strings.TrimLeft(suffix, "0123456789") numerals := suffix[:len(suffix)-len(rest)] suffix = rest @@ -659,6 +626,7 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O // Inv: t != nil, obj == nil + t = aliases.Unalias(t) switch code { case opElem: hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map @@ -695,6 +663,16 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O } t = named.Underlying() + case opRhs: + if alias, ok := t.(*aliases.Alias); ok { + t = aliases.Rhs(alias) + } else if false && aliases.Enabled() { + // The Enabled check is too expensive, so for now we + // simply assume that aliases are not enabled. + // TODO(adonovan): replace with "if true {" when go1.24 is assured. + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want alias)", code, t, t) + } + case opTypeParam: hasTypeParams, ok := t.(hasTypeParams) // Named, Signature if !ok { @@ -706,8 +684,19 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O } t = tparams.At(index) + case opRecvTypeParam: + sig, ok := t.(*types.Signature) // Signature + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + rtparams := sig.RecvTypeParams() + if n := rtparams.Len(); index >= n { + return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + } + t = rtparams.At(index) + case opConstraint: - tparam, ok := t.(*typeparams.TypeParam) + tparam, ok := t.(*types.TypeParam) if !ok { return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) } @@ -747,12 +736,7 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O if index >= t.NumMethods() { return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods()) } - if skipMethodSorting { - obj = t.Method(index) - } else { - methods := namedMethods(t) // (unmemoized) - obj = methods[index] // Id-ordered - } + obj = t.Method(index) default: return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) @@ -772,6 +756,10 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O } } + if obj == nil { + panic(p) // path does not end in an object-valued operator + } + if obj.Pkg() != pkg { return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) } @@ -779,33 +767,6 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O return obj, nil // success } -// namedMethods returns the methods of a Named type in ascending Id order. -func namedMethods(named *types.Named) []*types.Func { - methods := make([]*types.Func, named.NumMethods()) - for i := range methods { - methods[i] = named.Method(i) - } - sort.Slice(methods, func(i, j int) bool { - return methods[i].Id() < methods[j].Id() - }) - return methods -} - -// namedMethods is a memoization of the namedMethods function. Callers must not modify the result. -func (enc *Encoder) namedMethods(named *types.Named) []*types.Func { - m := enc.namedMethodsMemo - if m == nil { - m = make(map[*types.Named][]*types.Func) - enc.namedMethodsMemo = m - } - methods, ok := m[named] - if !ok { - methods = namedMethods(named) // allocates and sorts - m[named] = methods - } - return methods -} - // scopeObjects is a memoization of scope objects. // Callers must not modify the result. func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object { diff --git a/vendor/golang.org/x/tools/internal/aliases/aliases.go b/vendor/golang.org/x/tools/internal/aliases/aliases.go new file mode 100644 index 0000000000..f7798e3354 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/aliases/aliases.go @@ -0,0 +1,38 @@ +// 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 aliases + +import ( + "go/token" + "go/types" +) + +// Package aliases defines backward compatible shims +// for the types.Alias type representation added in 1.22. +// This defines placeholders for x/tools until 1.26. + +// NewAlias creates a new TypeName in Package pkg that +// is an alias for the type rhs. +// +// The enabled parameter determines whether the resulting [TypeName]'s +// type is an [types.Alias]. Its value must be the result of a call to +// [Enabled], which computes the effective value of +// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled +// function is expensive and should be called once per task (e.g. +// package import), not once per call to NewAlias. +// +// Precondition: enabled || len(tparams)==0. +// If materialized aliases are disabled, there must not be any type parameters. +func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName { + if enabled { + tname := types.NewTypeName(pos, pkg, name, nil) + newAlias(tname, rhs, tparams) + return tname + } + if len(tparams) > 0 { + panic("cannot create an alias with type parameters when gotypesalias is not enabled") + } + return types.NewTypeName(pos, pkg, name, rhs) +} diff --git a/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go b/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go new file mode 100644 index 0000000000..a775fcc4be --- /dev/null +++ b/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go @@ -0,0 +1,37 @@ +// 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. + +//go:build !go1.22 +// +build !go1.22 + +package aliases + +import ( + "go/types" +) + +// Alias is a placeholder for a go/types.Alias for <=1.21. +// It will never be created by go/types. +type Alias struct{} + +func (*Alias) String() string { panic("unreachable") } +func (*Alias) Underlying() types.Type { panic("unreachable") } +func (*Alias) Obj() *types.TypeName { panic("unreachable") } +func Rhs(alias *Alias) types.Type { panic("unreachable") } +func TypeParams(alias *Alias) *types.TypeParamList { panic("unreachable") } +func SetTypeParams(alias *Alias, tparams []*types.TypeParam) { panic("unreachable") } +func TypeArgs(alias *Alias) *types.TypeList { panic("unreachable") } +func Origin(alias *Alias) *Alias { panic("unreachable") } + +// Unalias returns the type t for go <=1.21. +func Unalias(t types.Type) types.Type { return t } + +func newAlias(name *types.TypeName, rhs types.Type, tparams []*types.TypeParam) *Alias { + panic("unreachable") +} + +// Enabled reports whether [NewAlias] should create [types.Alias] types. +// +// Before go1.22, this function always returns false. +func Enabled() bool { return false } diff --git a/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go b/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go new file mode 100644 index 0000000000..31c159e42e --- /dev/null +++ b/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go @@ -0,0 +1,98 @@ +// 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. + +//go:build go1.22 +// +build go1.22 + +package aliases + +import ( + "go/ast" + "go/parser" + "go/token" + "go/types" +) + +// Alias is an alias of types.Alias. +type Alias = types.Alias + +// Rhs returns the type on the right-hand side of the alias declaration. +func Rhs(alias *Alias) types.Type { + if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok { + return alias.Rhs() // go1.23+ + } + + // go1.22's Alias didn't have the Rhs method, + // so Unalias is the best we can do. + return Unalias(alias) +} + +// TypeParams returns the type parameter list of the alias. +func TypeParams(alias *Alias) *types.TypeParamList { + if alias, ok := any(alias).(interface{ TypeParams() *types.TypeParamList }); ok { + return alias.TypeParams() // go1.23+ + } + return nil +} + +// SetTypeParams sets the type parameters of the alias type. +func SetTypeParams(alias *Alias, tparams []*types.TypeParam) { + if alias, ok := any(alias).(interface { + SetTypeParams(tparams []*types.TypeParam) + }); ok { + alias.SetTypeParams(tparams) // go1.23+ + } else if len(tparams) > 0 { + panic("cannot set type parameters of an Alias type in go1.22") + } +} + +// TypeArgs returns the type arguments used to instantiate the Alias type. +func TypeArgs(alias *Alias) *types.TypeList { + if alias, ok := any(alias).(interface{ TypeArgs() *types.TypeList }); ok { + return alias.TypeArgs() // go1.23+ + } + return nil // empty (go1.22) +} + +// Origin returns the generic Alias type of which alias is an instance. +// If alias is not an instance of a generic alias, Origin returns alias. +func Origin(alias *Alias) *Alias { + if alias, ok := any(alias).(interface{ Origin() *types.Alias }); ok { + return alias.Origin() // go1.23+ + } + return alias // not an instance of a generic alias (go1.22) +} + +// Unalias is a wrapper of types.Unalias. +func Unalias(t types.Type) types.Type { return types.Unalias(t) } + +// newAlias is an internal alias around types.NewAlias. +// Direct usage is discouraged as the moment. +// Try to use NewAlias instead. +func newAlias(tname *types.TypeName, rhs types.Type, tparams []*types.TypeParam) *Alias { + a := types.NewAlias(tname, rhs) + SetTypeParams(a, tparams) + return a +} + +// Enabled reports whether [NewAlias] should create [types.Alias] types. +// +// This function is expensive! Call it sparingly. +func Enabled() bool { + // The only reliable way to compute the answer is to invoke go/types. + // We don't parse the GODEBUG environment variable, because + // (a) it's tricky to do so in a manner that is consistent + // with the godebug package; in particular, a simple + // substring check is not good enough. The value is a + // rightmost-wins list of options. But more importantly: + // (b) it is impossible to detect changes to the effective + // setting caused by os.Setenv("GODEBUG"), as happens in + // many tests. Therefore any attempt to cache the result + // is just incorrect. + fset := token.NewFileSet() + f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0) + pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil) + _, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias) + return enabled +} diff --git a/vendor/golang.org/x/tools/internal/event/keys/util.go b/vendor/golang.org/x/tools/internal/event/keys/util.go new file mode 100644 index 0000000000..c0e8e731c9 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/event/keys/util.go @@ -0,0 +1,21 @@ +// Copyright 2023 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 keys + +import ( + "sort" + "strings" +) + +// Join returns a canonical join of the keys in S: +// a sorted comma-separated string list. +func Join[S ~[]T, T ~string](s S) string { + strs := make([]string, 0, len(s)) + for _, v := range s { + strs = append(strs, string(v)) + } + sort.Strings(strs) + return strings.Join(strs, ",") +} diff --git a/vendor/golang.org/x/tools/internal/event/tag/tag.go b/vendor/golang.org/x/tools/internal/event/tag/tag.go deleted file mode 100644 index 581b26c204..0000000000 --- a/vendor/golang.org/x/tools/internal/event/tag/tag.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 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 tag provides the labels used for telemetry throughout gopls. -package tag - -import ( - "golang.org/x/tools/internal/event/keys" -) - -var ( - // create the label keys we use - Method = keys.NewString("method", "") - StatusCode = keys.NewString("status.code", "") - StatusMessage = keys.NewString("status.message", "") - RPCID = keys.NewString("id", "") - RPCDirection = keys.NewString("direction", "") - File = keys.NewString("file", "") - Directory = keys.New("directory", "") - URI = keys.New("URI", "") - Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs - PackagePath = keys.NewString("package_path", "") - Query = keys.New("query", "") - Snapshot = keys.NewUInt64("snapshot", "") - Operation = keys.NewString("operation", "") - - Position = keys.New("position", "") - Category = keys.NewString("category", "") - PackageCount = keys.NewInt("packages", "") - Files = keys.New("files", "") - Port = keys.NewInt("port", "") - Type = keys.New("type", "") - HoverKind = keys.NewString("hoverkind", "") - - NewServer = keys.NewString("new_server", "A new server was added") - EndServer = keys.NewString("end_server", "A server was shut down") - - ServerID = keys.NewString("server", "The server ID an event is related to") - Logfile = keys.NewString("logfile", "") - DebugAddress = keys.NewString("debug_address", "") - GoplsPath = keys.NewString("gopls_path", "") - ClientID = keys.NewString("client_id", "") - - Level = keys.NewInt("level", "The logging level") -) - -var ( - // create the stats we measure - Started = keys.NewInt64("started", "Count of started RPCs.") - ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes) - SentBytes = keys.NewInt64("sent_bytes", "Bytes sent.") //, unit.Bytes) - Latency = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds) -) - -const ( - Inbound = "in" - Outbound = "out" -) diff --git a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go index b1223713b9..39df91124a 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go @@ -29,7 +29,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -221,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func switch hdr { case "$$B\n": var data []byte - data, err = ioutil.ReadAll(buf) + data, err = io.ReadAll(buf) if err != nil { break } @@ -260,13 +259,6 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func return } -func deref(typ types.Type) types.Type { - if p, _ := typ.(*types.Pointer); p != nil { - return p.Elem() - } - return typ -} - type byPath []*types.Package func (a byPath) Len() int { return len(a) } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go index 6103dd7102..5f283281a2 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go @@ -2,9 +2,227 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Indexed binary package export. -// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; -// see that file for specification of the format. +// Indexed package export. +// +// The indexed export data format is an evolution of the previous +// binary export data format. Its chief contribution is introducing an +// index table, which allows efficient random access of individual +// declarations and inline function bodies. In turn, this allows +// avoiding unnecessary work for compilation units that import large +// packages. +// +// +// The top-level data format is structured as: +// +// Header struct { +// Tag byte // 'i' +// Version uvarint +// StringSize uvarint +// DataSize uvarint +// } +// +// Strings [StringSize]byte +// Data [DataSize]byte +// +// MainIndex []struct{ +// PkgPath stringOff +// PkgName stringOff +// PkgHeight uvarint +// +// Decls []struct{ +// Name stringOff +// Offset declOff +// } +// } +// +// Fingerprint [8]byte +// +// uvarint means a uint64 written out using uvarint encoding. +// +// []T means a uvarint followed by that many T objects. In other +// words: +// +// Len uvarint +// Elems [Len]T +// +// stringOff means a uvarint that indicates an offset within the +// Strings section. At that offset is another uvarint, followed by +// that many bytes, which form the string value. +// +// declOff means a uvarint that indicates an offset within the Data +// section where the associated declaration can be found. +// +// +// There are five kinds of declarations, distinguished by their first +// byte: +// +// type Var struct { +// Tag byte // 'V' +// Pos Pos +// Type typeOff +// } +// +// type Func struct { +// Tag byte // 'F' or 'G' +// Pos Pos +// TypeParams []typeOff // only present if Tag == 'G' +// Signature Signature +// } +// +// type Const struct { +// Tag byte // 'C' +// Pos Pos +// Value Value +// } +// +// type Type struct { +// Tag byte // 'T' or 'U' +// Pos Pos +// TypeParams []typeOff // only present if Tag == 'U' +// Underlying typeOff +// +// Methods []struct{ // omitted if Underlying is an interface type +// Pos Pos +// Name stringOff +// Recv Param +// Signature Signature +// } +// } +// +// type Alias struct { +// Tag byte // 'A' or 'B' +// Pos Pos +// TypeParams []typeOff // only present if Tag == 'B' +// Type typeOff +// } +// +// // "Automatic" declaration of each typeparam +// type TypeParam struct { +// Tag byte // 'P' +// Pos Pos +// Implicit bool +// Constraint typeOff +// } +// +// typeOff means a uvarint that either indicates a predeclared type, +// or an offset into the Data section. If the uvarint is less than +// predeclReserved, then it indicates the index into the predeclared +// types list (see predeclared in bexport.go for order). Otherwise, +// subtracting predeclReserved yields the offset of a type descriptor. +// +// Value means a type, kind, and type-specific value. See +// (*exportWriter).value for details. +// +// +// There are twelve kinds of type descriptors, distinguished by an itag: +// +// type DefinedType struct { +// Tag itag // definedType +// Name stringOff +// PkgPath stringOff +// } +// +// type PointerType struct { +// Tag itag // pointerType +// Elem typeOff +// } +// +// type SliceType struct { +// Tag itag // sliceType +// Elem typeOff +// } +// +// type ArrayType struct { +// Tag itag // arrayType +// Len uint64 +// Elem typeOff +// } +// +// type ChanType struct { +// Tag itag // chanType +// Dir uint64 // 1 RecvOnly; 2 SendOnly; 3 SendRecv +// Elem typeOff +// } +// +// type MapType struct { +// Tag itag // mapType +// Key typeOff +// Elem typeOff +// } +// +// type FuncType struct { +// Tag itag // signatureType +// PkgPath stringOff +// Signature Signature +// } +// +// type StructType struct { +// Tag itag // structType +// PkgPath stringOff +// Fields []struct { +// Pos Pos +// Name stringOff +// Type typeOff +// Embedded bool +// Note stringOff +// } +// } +// +// type InterfaceType struct { +// Tag itag // interfaceType +// PkgPath stringOff +// Embeddeds []struct { +// Pos Pos +// Type typeOff +// } +// Methods []struct { +// Pos Pos +// Name stringOff +// Signature Signature +// } +// } +// +// // Reference to a type param declaration +// type TypeParamType struct { +// Tag itag // typeParamType +// Name stringOff +// PkgPath stringOff +// } +// +// // Instantiation of a generic type (like List[T2] or List[int]) +// type InstanceType struct { +// Tag itag // instanceType +// Pos pos +// TypeArgs []typeOff +// BaseType typeOff +// } +// +// type UnionType struct { +// Tag itag // interfaceType +// Terms []struct { +// tilde bool +// Type typeOff +// } +// } +// +// +// +// type Signature struct { +// Params []Param +// Results []Param +// Variadic bool // omitted if Results is empty +// } +// +// type Param struct { +// Pos Pos +// Name stringOff +// Type typOff +// } +// +// +// Pos encodes a file:line:column triple, incorporating a simple delta +// encoding scheme within a data object. See exportWriter.pos for +// details. package gcimporter @@ -23,8 +241,8 @@ import ( "strings" "golang.org/x/tools/go/types/objectpath" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/tokeninternal" - "golang.org/x/tools/internal/typeparams" ) // IExportShallow encodes "shallow" export data for the specified package. @@ -464,7 +682,7 @@ func (p *iexporter) doDecl(obj types.Object) { switch obj := obj.(type) { case *types.Var: - w.tag('V') + w.tag(varTag) w.pos(obj.Pos()) w.typ(obj.Type(), obj.Pkg()) @@ -481,10 +699,10 @@ func (p *iexporter) doDecl(obj types.Object) { } // Function. - if typeparams.ForSignature(sig).Len() == 0 { - w.tag('F') + if sig.TypeParams().Len() == 0 { + w.tag(funcTag) } else { - w.tag('G') + w.tag(genericFuncTag) } w.pos(obj.Pos()) // The tparam list of the function type is the declaration of the type @@ -494,27 +712,27 @@ func (p *iexporter) doDecl(obj types.Object) { // // While importing the type parameters, tparamList computes and records // their export name, so that it can be later used when writing the index. - if tparams := typeparams.ForSignature(sig); tparams.Len() > 0 { + if tparams := sig.TypeParams(); tparams.Len() > 0 { w.tparamList(obj.Name(), tparams, obj.Pkg()) } w.signature(sig) case *types.Const: - w.tag('C') + w.tag(constTag) w.pos(obj.Pos()) w.value(obj.Type(), obj.Val()) case *types.TypeName: t := obj.Type() - if tparam, ok := t.(*typeparams.TypeParam); ok { - w.tag('P') + if tparam, ok := aliases.Unalias(t).(*types.TypeParam); ok { + w.tag(typeParamTag) w.pos(obj.Pos()) constraint := tparam.Constraint() if p.version >= iexportVersionGo1_18 { implicit := false - if iface, _ := constraint.(*types.Interface); iface != nil { - implicit = typeparams.IsImplicit(iface) + if iface, _ := aliases.Unalias(constraint).(*types.Interface); iface != nil { + implicit = iface.IsImplicit() } w.bool(implicit) } @@ -523,8 +741,26 @@ func (p *iexporter) doDecl(obj types.Object) { } if obj.IsAlias() { - w.tag('A') + alias, materialized := t.(*aliases.Alias) // may fail when aliases are not enabled + + var tparams *types.TypeParamList + if materialized { + tparams = aliases.TypeParams(alias) + } + if tparams.Len() == 0 { + w.tag(aliasTag) + } else { + w.tag(genericAliasTag) + } w.pos(obj.Pos()) + if tparams.Len() > 0 { + w.tparamList(obj.Name(), tparams, obj.Pkg()) + } + if materialized { + // Preserve materialized aliases, + // even of non-exported types. + t = aliases.Rhs(alias) + } w.typ(t, obj.Pkg()) break } @@ -535,20 +771,20 @@ func (p *iexporter) doDecl(obj types.Object) { panic(internalErrorf("%s is not a defined type", t)) } - if typeparams.ForNamed(named).Len() == 0 { - w.tag('T') + if named.TypeParams().Len() == 0 { + w.tag(typeTag) } else { - w.tag('U') + w.tag(genericTypeTag) } w.pos(obj.Pos()) - if typeparams.ForNamed(named).Len() > 0 { + if named.TypeParams().Len() > 0 { // While importing the type parameters, tparamList computes and records // their export name, so that it can be later used when writing the index. - w.tparamList(obj.Name(), typeparams.ForNamed(named), obj.Pkg()) + w.tparamList(obj.Name(), named.TypeParams(), obj.Pkg()) } - underlying := obj.Type().Underlying() + underlying := named.Underlying() w.typ(underlying, obj.Pkg()) if types.IsInterface(t) { @@ -565,7 +801,7 @@ func (p *iexporter) doDecl(obj types.Object) { // Receiver type parameters are type arguments of the receiver type, so // their name must be qualified before exporting recv. - if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 { + if rparams := sig.RecvTypeParams(); rparams.Len() > 0 { prefix := obj.Name() + "." + m.Name() for i := 0; i < rparams.Len(); i++ { rparam := rparams.At(i) @@ -739,20 +975,31 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { }() } switch t := t.(type) { + case *aliases.Alias: + if targs := aliases.TypeArgs(t); targs.Len() > 0 { + w.startType(instanceType) + w.pos(t.Obj().Pos()) + w.typeList(targs, pkg) + w.typ(aliases.Origin(t), pkg) + return + } + w.startType(aliasType) + w.qualifiedType(t.Obj()) + case *types.Named: - if targs := typeparams.NamedTypeArgs(t); targs.Len() > 0 { + if targs := t.TypeArgs(); targs.Len() > 0 { w.startType(instanceType) // TODO(rfindley): investigate if this position is correct, and if it // matters. w.pos(t.Obj().Pos()) w.typeList(targs, pkg) - w.typ(typeparams.NamedTypeOrigin(t), pkg) + w.typ(t.Origin(), pkg) return } w.startType(definedType) w.qualifiedType(t.Obj()) - case *typeparams.TypeParam: + case *types.TypeParam: w.startType(typeParamType) w.qualifiedType(t.Obj()) @@ -844,7 +1091,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { for i := 0; i < n; i++ { ft := t.EmbeddedType(i) tPkg := pkg - if named, _ := ft.(*types.Named); named != nil { + if named, _ := aliases.Unalias(ft).(*types.Named); named != nil { w.pos(named.Obj().Pos()) } else { w.pos(token.NoPos) @@ -868,7 +1115,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { w.signature(sig) } - case *typeparams.Union: + case *types.Union: w.startType(unionType) nt := t.Len() w.uint64(uint64(nt)) @@ -948,14 +1195,14 @@ func (w *exportWriter) signature(sig *types.Signature) { } } -func (w *exportWriter) typeList(ts *typeparams.TypeList, pkg *types.Package) { +func (w *exportWriter) typeList(ts *types.TypeList, pkg *types.Package) { w.uint64(uint64(ts.Len())) for i := 0; i < ts.Len(); i++ { w.typ(ts.At(i), pkg) } } -func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) { +func (w *exportWriter) tparamList(prefix string, list *types.TypeParamList, pkg *types.Package) { ll := uint64(list.Len()) w.uint64(ll) for i := 0; i < list.Len(); i++ { @@ -973,7 +1220,7 @@ const blankMarker = "$" // differs from its actual object name: it is prefixed with a qualifier, and // blank type parameter names are disambiguated by their index in the type // parameter list. -func tparamExportName(prefix string, tparam *typeparams.TypeParam) string { +func tparamExportName(prefix string, tparam *types.TypeParam) string { assert(prefix != "") name := tparam.Obj().Name() if name == "_" { diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go index 8e64cf644f..ed2d562959 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Indexed package import. -// See cmd/compile/internal/gc/iexport.go for the export data format. +// See iexport.go for the export data format. // This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go. @@ -22,7 +22,8 @@ import ( "strings" "golang.org/x/tools/go/types/objectpath" - "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/aliases" + "golang.org/x/tools/internal/typesinternal" ) type intReader struct { @@ -79,6 +80,20 @@ const ( typeParamType instanceType unionType + aliasType +) + +// Object tags +const ( + varTag = 'V' + funcTag = 'F' + genericFuncTag = 'G' + constTag = 'C' + aliasTag = 'A' + genericAliasTag = 'B' + typeParamTag = 'P' + typeTag = 'T' + genericTypeTag = 'U' ) // IImportData imports a package from the serialized package data @@ -195,6 +210,7 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte p := iimporter{ version: int(version), ipath: path, + aliases: aliases.Enabled(), shallow: shallow, reportf: reportf, @@ -225,6 +241,7 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte // Gather the relevant packages from the manifest. items := make([]GetPackagesItem, r.uint64()) + uniquePkgPaths := make(map[string]bool) for i := range items { pkgPathOff := r.uint64() pkgPath := p.stringAt(pkgPathOff) @@ -249,6 +266,12 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte } items[i].nameIndex = nameIndex + + uniquePkgPaths[pkgPath] = true + } + // Debugging #63822; hypothesis: there are duplicate PkgPaths. + if len(uniquePkgPaths) != len(items) { + reportf("found duplicate PkgPaths while reading export data manifest: %v", items) } // Request packages all at once from the client, @@ -316,12 +339,12 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte } // SetConstraint can't be called if the constraint type is not yet complete. - // When type params are created in the 'P' case of (*importReader).obj(), + // When type params are created in the typeParamTag case of (*importReader).obj(), // the associated constraint type may not be complete due to recursion. // Therefore, we defer calling SetConstraint there, and call it here instead // after all types are complete. for _, d := range p.later { - typeparams.SetTypeParamConstraint(d.t, d.constraint) + d.t.SetConstraint(d.constraint) } for _, typ := range p.interfaceList { @@ -339,7 +362,7 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte } type setConstraintArgs struct { - t *typeparams.TypeParam + t *types.TypeParam constraint types.Type } @@ -347,6 +370,7 @@ type iimporter struct { version int ipath string + aliases bool shallow bool reportf ReportFunc // if non-nil, used to report bugs @@ -516,7 +540,7 @@ func canReuse(def *types.Named, rhs types.Type) bool { if def == nil { return true } - iface, _ := rhs.(*types.Interface) + iface, _ := aliases.Unalias(rhs).(*types.Interface) if iface == nil { return true } @@ -538,25 +562,29 @@ func (r *importReader) obj(name string) { pos := r.pos() switch tag { - case 'A': + case aliasTag, genericAliasTag: + var tparams []*types.TypeParam + if tag == genericAliasTag { + tparams = r.tparamList() + } typ := r.typ() + obj := aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ, tparams) + r.declare(obj) - r.declare(types.NewTypeName(pos, r.currPkg, name, typ)) - - case 'C': + case constTag: typ, val := r.value() r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) - case 'F', 'G': - var tparams []*typeparams.TypeParam - if tag == 'G' { + case funcTag, genericFuncTag: + var tparams []*types.TypeParam + if tag == genericFuncTag { tparams = r.tparamList() } sig := r.signature(nil, nil, tparams) r.declare(types.NewFunc(pos, r.currPkg, name, sig)) - case 'T', 'U': + case typeTag, genericTypeTag: // Types can be recursive. We need to setup a stub // declaration before recursing. obj := types.NewTypeName(pos, r.currPkg, name, nil) @@ -564,9 +592,9 @@ func (r *importReader) obj(name string) { // Declare obj before calling r.tparamList, so the new type name is recognized // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) - if tag == 'U' { + if tag == genericTypeTag { tparams := r.tparamList() - typeparams.SetForNamed(named, tparams) + named.SetTypeParams(tparams) } underlying := r.p.typAt(r.uint64(), named).Underlying() @@ -581,14 +609,13 @@ func (r *importReader) obj(name string) { // If the receiver has any targs, set those as the // rparams of the method (since those are the // typeparams being used in the method sig/body). - base := baseType(recv.Type()) - assert(base != nil) - targs := typeparams.NamedTypeArgs(base) - var rparams []*typeparams.TypeParam + _, recvNamed := typesinternal.ReceiverNamed(recv) + targs := recvNamed.TypeArgs() + var rparams []*types.TypeParam if targs.Len() > 0 { - rparams = make([]*typeparams.TypeParam, targs.Len()) + rparams = make([]*types.TypeParam, targs.Len()) for i := range rparams { - rparams[i] = targs.At(i).(*typeparams.TypeParam) + rparams[i] = aliases.Unalias(targs.At(i)).(*types.TypeParam) } } msig := r.signature(recv, rparams, nil) @@ -597,7 +624,7 @@ func (r *importReader) obj(name string) { } } - case 'P': + case typeParamTag: // We need to "declare" a typeparam in order to have a name that // can be referenced recursively (if needed) in the type param's // bound. @@ -606,7 +633,7 @@ func (r *importReader) obj(name string) { } name0 := tparamName(name) tn := types.NewTypeName(pos, r.currPkg, name0, nil) - t := typeparams.NewTypeParam(tn, nil) + t := types.NewTypeParam(tn, nil) // To handle recursive references to the typeparam within its // bound, save the partial type in tparamIndex before reading the bounds. @@ -618,11 +645,11 @@ func (r *importReader) obj(name string) { } constraint := r.typ() if implicit { - iface, _ := constraint.(*types.Interface) + iface, _ := aliases.Unalias(constraint).(*types.Interface) if iface == nil { errorf("non-interface constraint marked implicit") } - typeparams.MarkImplicit(iface) + iface.MarkImplicit() } // The constraint type may not be complete, if we // are in the middle of a type recursion involving type @@ -630,7 +657,7 @@ func (r *importReader) obj(name string) { // completely set up all types in ImportData. r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint}) - case 'V': + case varTag: typ := r.typ() r.declare(types.NewVar(pos, r.currPkg, name, typ)) @@ -825,7 +852,7 @@ func (r *importReader) typ() types.Type { } func isInterface(t types.Type) bool { - _, ok := t.(*types.Interface) + _, ok := aliases.Unalias(t).(*types.Interface) return ok } @@ -835,7 +862,7 @@ func (r *importReader) string() string { return r.p.stringAt(r.uint64()) } func (r *importReader) doType(base *types.Named) (res types.Type) { k := r.kind() if debug { - r.p.trace("importing type %d (base: %s)", k, base) + r.p.trace("importing type %d (base: %v)", k, base) r.p.indent++ defer func() { r.p.indent-- @@ -847,7 +874,7 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { errorf("unexpected kind tag in %q: %v", r.p.ipath, k) return nil - case definedType: + case aliasType, definedType: pkg, name := r.qualifiedIdent() r.p.doDecl(pkg, name) return pkg.Scope().Lookup(name).(*types.TypeName).Type() @@ -966,7 +993,7 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { // The imported instantiated type doesn't include any methods, so // we must always use the methods of the base (orig) type. // TODO provide a non-nil *Environment - t, _ := typeparams.Instantiate(nil, baseType, targs, false) + t, _ := types.Instantiate(nil, baseType, targs, false) // Workaround for golang/go#61561. See the doc for instanceList for details. r.p.instanceList = append(r.p.instanceList, t) @@ -976,11 +1003,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) { if r.p.version < iexportVersionGenerics { errorf("unexpected instantiation type") } - terms := make([]*typeparams.Term, r.uint64()) + terms := make([]*types.Term, r.uint64()) for i := range terms { - terms[i] = typeparams.NewTerm(r.bool(), r.typ()) + terms[i] = types.NewTerm(r.bool(), r.typ()) } - return typeparams.NewUnion(terms) + return types.NewUnion(terms) } } @@ -1008,23 +1035,23 @@ func (r *importReader) objectPathObject() types.Object { return obj } -func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { +func (r *importReader) signature(recv *types.Var, rparams []*types.TypeParam, tparams []*types.TypeParam) *types.Signature { params := r.paramList() results := r.paramList() variadic := params.Len() > 0 && r.bool() - return typeparams.NewSignatureType(recv, rparams, tparams, params, results, variadic) + return types.NewSignatureType(recv, rparams, tparams, params, results, variadic) } -func (r *importReader) tparamList() []*typeparams.TypeParam { +func (r *importReader) tparamList() []*types.TypeParam { n := r.uint64() if n == 0 { return nil } - xs := make([]*typeparams.TypeParam, n) + xs := make([]*types.TypeParam, n) for i := range xs { // Note: the standard library importer is tolerant of nil types here, // though would panic in SetTypeParams. - xs[i] = r.typ().(*typeparams.TypeParam) + xs[i] = aliases.Unalias(r.typ()).(*types.TypeParam) } return xs } @@ -1071,13 +1098,3 @@ func (r *importReader) byte() byte { } return x } - -func baseType(typ types.Type) *types.Named { - // pointer receivers are never types.Named types - if p, _ := typ.(*types.Pointer); p != nil { - typ = p.Elem() - } - // receiver base types are always (possibly generic) types.Named types - n, _ := typ.(*types.Named) - return n -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/support_go117.go b/vendor/golang.org/x/tools/internal/gcimporter/support_go117.go deleted file mode 100644 index d892273efb..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/support_go117.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 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. - -//go:build !go1.18 -// +build !go1.18 - -package gcimporter - -import "go/types" - -const iexportVersion = iexportVersionGo1_11 - -func additionalPredeclared() []types.Type { - return nil -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go b/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go index edbe6ea704..0cd3b91b65 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/support_go118.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.18 -// +build go1.18 - package gcimporter import "go/types" diff --git a/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go b/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go index 286bf44548..38b624cada 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/unified_no.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !(go1.18 && goexperiment.unified) -// +build !go1.18 !goexperiment.unified +//go:build !goexperiment.unified +// +build !goexperiment.unified package gcimporter diff --git a/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go b/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go index b5d69ffbe6..b5118d0b3a 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/unified_yes.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.18 && goexperiment.unified -// +build go1.18,goexperiment.unified +//go:build goexperiment.unified +// +build goexperiment.unified package gcimporter diff --git a/vendor/golang.org/x/tools/internal/gcimporter/ureader_no.go b/vendor/golang.org/x/tools/internal/gcimporter/ureader_no.go deleted file mode 100644 index 8eb20729c2..0000000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/ureader_no.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.18 -// +build !go1.18 - -package gcimporter - -import ( - "fmt" - "go/token" - "go/types" -) - -func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { - err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data") - return -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go index b977435f62..f0742f5404 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go @@ -4,9 +4,6 @@ // Derived from go/internal/gcimporter/ureader.go -//go:build go1.18 -// +build go1.18 - package gcimporter import ( @@ -16,6 +13,7 @@ import ( "sort" "strings" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/pkgbits" ) @@ -28,6 +26,7 @@ type pkgReader struct { ctxt *types.Context imports map[string]*types.Package // previously imported packages, indexed by path + aliases bool // create types.Alias nodes // lazily initialized arrays corresponding to the unified IR // PosBase, Pkg, and Type sections, respectively. @@ -53,8 +52,7 @@ func (pr *pkgReader) later(fn func()) { // See cmd/compile/internal/noder.derivedInfo. type derivedInfo struct { - idx pkgbits.Index - needed bool + idx pkgbits.Index } // See cmd/compile/internal/noder.typeInfo. @@ -101,6 +99,7 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st ctxt: ctxt, imports: imports, + aliases: aliases.Enabled(), posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)), pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)), @@ -110,13 +109,17 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) pkg := r.pkg() - r.Bool() // has init + if r.Version().Has(pkgbits.HasInit) { + r.Bool() + } for i, n := 0, r.Len(); i < n; i++ { // As if r.obj(), but avoiding the Scope.Lookup call, // to avoid eager loading of imports. r.Sync(pkgbits.SyncObject) - assert(!r.Bool()) + if r.Version().Has(pkgbits.DerivedFuncInstance) { + assert(!r.Bool()) + } r.p.objIdx(r.Reloc(pkgbits.RelocObj)) assert(r.Len() == 0) } @@ -165,7 +168,7 @@ type readerDict struct { // tparams is a slice of the constructed TypeParams for the element. tparams []*types.TypeParam - // devived is a slice of types derived from tparams, which may be + // derived is a slice of types derived from tparams, which may be // instantiated while reading the current element. derived []derivedInfo derivedTypes []types.Type // lazily instantiated from derived @@ -471,7 +474,9 @@ func (r *reader) param() *types.Var { func (r *reader) obj() (types.Object, []types.Type) { r.Sync(pkgbits.SyncObject) - assert(!r.Bool()) + if r.Version().Has(pkgbits.DerivedFuncInstance) { + assert(!r.Bool()) + } pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj)) obj := pkgScope(pkg).Lookup(name) @@ -525,8 +530,12 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { case pkgbits.ObjAlias: pos := r.pos() + var tparams []*types.TypeParam + if r.Version().Has(pkgbits.AliasTypeParamNames) { + tparams = r.typeParamNames() + } typ := r.typ() - declare(types.NewTypeName(pos, objPkg, objName, typ)) + declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ, tparams)) case pkgbits.ObjConst: pos := r.pos() @@ -553,7 +562,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { // If the underlying type is an interface, we need to // duplicate its methods so we can replace the receiver // parameter's type (#49906). - if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 { + if iface, ok := aliases.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 { methods := make([]*types.Func, iface.NumExplicitMethods()) for i := range methods { fn := iface.ExplicitMethod(i) @@ -632,7 +641,10 @@ func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict { dict.derived = make([]derivedInfo, r.Len()) dict.derivedTypes = make([]types.Type, len(dict.derived)) for i := range dict.derived { - dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} + dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)} + if r.Version().Has(pkgbits.DerivedInfoNeeded) { + assert(!r.Bool()) + } } pr.retireReader(r) diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index 53cf66da01..2e59ff8558 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -8,11 +8,14 @@ package gocommand import ( "bytes" "context" + "encoding/json" "errors" "fmt" "io" "log" "os" + "os/exec" + "path/filepath" "reflect" "regexp" "runtime" @@ -21,12 +24,9 @@ import ( "sync" "time" - exec "golang.org/x/sys/execabs" - "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/event/keys" "golang.org/x/tools/internal/event/label" - "golang.org/x/tools/internal/event/tag" ) // An Runner will run go command invocations and serialize @@ -56,11 +56,14 @@ func (runner *Runner) initialize() { // 1.14: go: updating go.mod: existing contents have changed since last read var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) -// verb is an event label for the go command verb. -var verb = keys.NewString("verb", "go command verb") +// event keys for go command invocations +var ( + verb = keys.NewString("verb", "go command verb") + directory = keys.NewString("directory", "") +) func invLabels(inv Invocation) []label.Label { - return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)} + return []label.Label{verb.Of(inv.Verb), directory.Of(inv.WorkingDir)} } // Run is a convenience wrapper around RunRaw. @@ -85,6 +88,7 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde // RunRaw runs the invocation, serializing requests only if they fight over // go.mod changes. +// Postcondition: both error results have same nilness. func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) defer done() @@ -95,23 +99,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) // If we encounter a load concurrency error, we need to retry serially. - if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { - return stdout, stderr, friendlyErr, err + if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) { + event.Error(ctx, "Load concurrency error, will retry serially", err) + + // Run serially by calling runPiped. + stdout.Reset() + stderr.Reset() + friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) } - event.Error(ctx, "Load concurrency error, will retry serially", err) - // Run serially by calling runPiped. - stdout.Reset() - stderr.Reset() - friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { // Wait for 1 worker to become available. select { case <-ctx.Done(): - return nil, nil, nil, ctx.Err() + return nil, nil, ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: defer func() { <-runner.inFlight }() } @@ -121,6 +126,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { // Make sure the runner is always initialized. runner.initialize() @@ -129,7 +135,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde // runPiped commands. select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.serialized <- struct{}{}: defer func() { <-runner.serialized }() } @@ -139,7 +145,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde for i := 0; i < maxInFlight; i++ { select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: // Make sure we always "return" any workers we took. defer func() { <-runner.inFlight }() @@ -156,12 +162,17 @@ type Invocation struct { BuildFlags []string // If ModFlag is set, the go command is invoked with -mod=ModFlag. + // TODO(rfindley): remove, in favor of Args. ModFlag string // If ModFile is set, the go command is invoked with -modfile=ModFile. + // TODO(rfindley): remove, in favor of Args. ModFile string - // If Overlay is set, the go command is invoked with -overlay=Overlay. + // Overlay is the name of the JSON overlay file that describes + // unsaved editor buffers; see [WriteOverlays]. + // If set, the go command is invoked with -overlay=Overlay. + // TODO(rfindley): remove, in favor of Args. Overlay string // If CleanEnv is set, the invocation will run only with the environment @@ -172,6 +183,7 @@ type Invocation struct { Logf func(format string, args ...interface{}) } +// Postcondition: both error results have same nilness. func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { rawError = i.run(ctx, stdout, stderr) if rawError != nil { @@ -188,12 +200,14 @@ func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io return } -func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { - log := i.Logf - if log == nil { - log = func(string, ...interface{}) {} +// logf logs if i.Logf is non-nil. +func (i *Invocation) logf(format string, args ...any) { + if i.Logf != nil { + i.Logf(format, args...) } +} +func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { goArgs := []string{i.Verb} appendModFile := func() { @@ -247,12 +261,15 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { waitDelay.Set(reflect.ValueOf(30 * time.Second)) } - // On darwin the cwd gets resolved to the real path, which breaks anything that - // expects the working directory to keep the original path, including the + // The cwd gets resolved to the real path. On Darwin, where + // /tmp is a symlink, this breaks anything that expects the + // working directory to keep the original path, including the // go command when dealing with modules. - // The Go stdlib has a special feature where if the cwd and the PWD are the - // same node then it trusts the PWD, so by setting it in the env for the child - // process we fix up all the paths returned by the go command. + // + // os.Getwd has a special feature where if the cwd and the PWD + // are the same node then it trusts the PWD, so by setting it + // in the env for the child process we fix up all the paths + // returned by the go command. if !i.CleanEnv { cmd.Env = os.Environ() } @@ -262,7 +279,12 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { cmd.Dir = i.WorkingDir } - defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) + debugStr := cmdDebugStr(cmd) + i.logf("starting %v", debugStr) + start := time.Now() + defer func() { + i.logf("%s for %v", time.Since(start), debugStr) + }() return runCmdContext(ctx, cmd) } @@ -343,6 +365,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { } } + startTime := time.Now() err = cmd.Start() if stdoutW != nil { // The child process has inherited the pipe file, @@ -369,7 +392,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { case err := <-resChan: return err case <-timer.C: - HandleHangingGoCommand(cmd.Process) + HandleHangingGoCommand(startTime, cmd) case <-ctx.Done(): } } else { @@ -403,7 +426,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { return <-resChan } -func HandleHangingGoCommand(proc *os.Process) { +func HandleHangingGoCommand(start time.Time, cmd *exec.Cmd) { switch runtime.GOOS { case "linux", "darwin", "freebsd", "netbsd": fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND @@ -436,7 +459,7 @@ See golang/go#54461 for more details.`) panic(fmt.Sprintf("running %s: %v", listFiles, err)) } } - panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid)) + panic(fmt.Sprintf("detected hanging go command (golang/go#54461); waited %s\n\tcommand:%s\n\tpid:%d", time.Since(start), cmd, cmd.Process.Pid)) } func cmdDebugStr(cmd *exec.Cmd) string { @@ -460,3 +483,73 @@ func cmdDebugStr(cmd *exec.Cmd) string { } return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " ")) } + +// WriteOverlays writes each value in the overlay (see the Overlay +// field of go/packages.Config) to a temporary file and returns the name +// of a JSON file describing the mapping that is suitable for the "go +// list -overlay" flag. +// +// On success, the caller must call the cleanup function exactly once +// when the files are no longer needed. +func WriteOverlays(overlay map[string][]byte) (filename string, cleanup func(), err error) { + // Do nothing if there are no overlays in the config. + if len(overlay) == 0 { + return "", func() {}, nil + } + + dir, err := os.MkdirTemp("", "gocommand-*") + if err != nil { + return "", nil, err + } + + // The caller must clean up this directory, + // unless this function returns an error. + // (The cleanup operand of each return + // statement below is ignored.) + defer func() { + cleanup = func() { + os.RemoveAll(dir) + } + if err != nil { + cleanup() + cleanup = nil + } + }() + + // Write each map entry to a temporary file. + overlays := make(map[string]string) + for k, v := range overlay { + // Use a unique basename for each file (001-foo.go), + // to avoid creating nested directories. + base := fmt.Sprintf("%d-%s", 1+len(overlays), filepath.Base(k)) + filename := filepath.Join(dir, base) + err := os.WriteFile(filename, v, 0666) + if err != nil { + return "", nil, err + } + overlays[k] = filename + } + + // Write the JSON overlay file that maps logical file names to temp files. + // + // OverlayJSON is the format overlay files are expected to be in. + // The Replace map maps from overlaid paths to replacement paths: + // the Go command will forward all reads trying to open + // each overlaid path to its replacement path, or consider the overlaid + // path not to exist if the replacement path is empty. + // + // From golang/go#39958. + type OverlayJSON struct { + Replace map[string]string `json:"replace,omitempty"` + } + b, err := json.Marshal(OverlayJSON{Replace: overlays}) + if err != nil { + return "", nil, err + } + filename = filepath.Join(dir, "overlay.json") + if err := os.WriteFile(filename, b, 0666); err != nil { + return "", nil, err + } + + return filename, nil, nil +} diff --git a/vendor/golang.org/x/tools/internal/gocommand/vendor.go b/vendor/golang.org/x/tools/internal/gocommand/vendor.go index 2d3d408c0b..e38d1fb488 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/vendor.go +++ b/vendor/golang.org/x/tools/internal/gocommand/vendor.go @@ -107,3 +107,57 @@ func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*Modul } return mod, lines[4] == "go1.14", nil } + +// WorkspaceVendorEnabled reports whether workspace vendoring is enabled. It takes a *Runner to execute Go commands +// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields, +// of which only Verb and Args are modified to run the appropriate Go command. +// Inspired by setDefaultBuildMod in modload/init.go +func WorkspaceVendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, []*ModuleJSON, error) { + inv.Verb = "env" + inv.Args = []string{"GOWORK"} + stdout, err := r.Run(ctx, inv) + if err != nil { + return false, nil, err + } + goWork := string(bytes.TrimSpace(stdout.Bytes())) + if fi, err := os.Stat(filepath.Join(filepath.Dir(goWork), "vendor")); err == nil && fi.IsDir() { + mainMods, err := getWorkspaceMainModules(ctx, inv, r) + if err != nil { + return false, nil, err + } + return true, mainMods, nil + } + return false, nil, nil +} + +// getWorkspaceMainModules gets the main modules' information. +// This is the information needed to figure out if vendoring should be enabled. +func getWorkspaceMainModules(ctx context.Context, inv Invocation, r *Runner) ([]*ModuleJSON, error) { + const format = `{{.Path}} +{{.Dir}} +{{.GoMod}} +{{.GoVersion}} +` + inv.Verb = "list" + inv.Args = []string{"-m", "-f", format} + stdout, err := r.Run(ctx, inv) + if err != nil { + return nil, err + } + + lines := strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n") + if len(lines) < 4 { + return nil, fmt.Errorf("unexpected stdout: %q", stdout.String()) + } + mods := make([]*ModuleJSON, 0, len(lines)/4) + for i := 0; i < len(lines); i += 4 { + mods = append(mods, &ModuleJSON{ + Path: lines[i], + Dir: lines[i+1], + GoMod: lines[i+2], + GoVersion: lines[i+3], + Main: true, + }) + } + return mods, nil +} diff --git a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go index d9950b1f0b..44719de173 100644 --- a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go +++ b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go @@ -5,10 +5,6 @@ // Package packagesinternal exposes internal-only fields from go/packages. package packagesinternal -import ( - "golang.org/x/tools/internal/gocommand" -) - var GetForTest = func(p interface{}) string { return "" } var GetDepsErrors = func(p interface{}) []*PackageError { return nil } @@ -18,10 +14,6 @@ type PackageError struct { Err string // the error itself } -var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } - -var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} - var TypecheckCgo int var DepsErrors int // must be set as a LoadMode to call GetDepsErrors var ForTest int // must be set as a LoadMode to call GetForTest diff --git a/vendor/golang.org/x/tools/internal/pkgbits/decoder.go b/vendor/golang.org/x/tools/internal/pkgbits/decoder.go index b92e8e6eb3..f6cb37c5c3 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/decoder.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/decoder.go @@ -21,7 +21,7 @@ import ( // export data. type PkgDecoder struct { // version is the file format version. - version uint32 + version Version // sync indicates whether the file uses sync markers. sync bool @@ -68,8 +68,6 @@ func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } // NewPkgDecoder returns a PkgDecoder initialized to read the Unified // IR export data from input. pkgPath is the package path for the // compilation unit that produced the export data. -// -// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014. func NewPkgDecoder(pkgPath, input string) PkgDecoder { pr := PkgDecoder{ pkgPath: pkgPath, @@ -80,14 +78,15 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder { r := strings.NewReader(input) - assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil) + var ver uint32 + assert(binary.Read(r, binary.LittleEndian, &ver) == nil) + pr.version = Version(ver) - switch pr.version { - default: - panic(fmt.Errorf("unsupported version: %v", pr.version)) - case 0: - // no flags - case 1: + if pr.version >= numVersions { + panic(fmt.Errorf("cannot decode %q, export data version %d is greater than maximum supported version %d", pkgPath, pr.version, numVersions-1)) + } + + if pr.version.Has(Flags) { var flags uint32 assert(binary.Read(r, binary.LittleEndian, &flags) == nil) pr.sync = flags&flagSyncMarkers != 0 @@ -102,7 +101,9 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder { assert(err == nil) pr.elemData = input[pos:] - assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1])) + + const fingerprintSize = 8 + assert(len(pr.elemData)-fingerprintSize == int(pr.elemEnds[len(pr.elemEnds)-1])) return pr } @@ -136,7 +137,7 @@ func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int { absIdx += int(pr.elemEndsEnds[k-1]) } if absIdx >= int(pr.elemEndsEnds[k]) { - errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) + panicf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) } return absIdx } @@ -193,9 +194,7 @@ func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder { Idx: idx, } - // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved. - r.Data = *strings.NewReader(pr.DataIdx(k, idx)) - + r.Data.Reset(pr.DataIdx(k, idx)) r.Sync(SyncRelocs) r.Relocs = make([]RelocEnt, r.Len()) for i := range r.Relocs { @@ -244,7 +243,7 @@ type Decoder struct { func (r *Decoder) checkErr(err error) { if err != nil { - errorf("unexpected decoding error: %w", err) + panicf("unexpected decoding error: %w", err) } } @@ -515,3 +514,6 @@ func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) { return path, name, tag } + +// Version reports the version of the bitstream. +func (w *Decoder) Version() Version { return w.common.version } diff --git a/vendor/golang.org/x/tools/internal/pkgbits/encoder.go b/vendor/golang.org/x/tools/internal/pkgbits/encoder.go index 6482617a4f..c17a12399d 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/encoder.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/encoder.go @@ -12,18 +12,15 @@ import ( "io" "math/big" "runtime" + "strings" ) -// currentVersion is the current version number. -// -// - v0: initial prototype -// -// - v1: adds the flags uint32 word -const currentVersion uint32 = 1 - // A PkgEncoder provides methods for encoding a package's Unified IR // export data. type PkgEncoder struct { + // version of the bitstream. + version Version + // elems holds the bitstream for previously encoded elements. elems [numRelocs][]string @@ -47,8 +44,9 @@ func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 } // export data files, but can help diagnosing desync errors in // higher-level Unified IR reader/writer code. If syncFrames is // negative, then sync markers are omitted entirely. -func NewPkgEncoder(syncFrames int) PkgEncoder { +func NewPkgEncoder(version Version, syncFrames int) PkgEncoder { return PkgEncoder{ + version: version, stringsIdx: make(map[string]Index), syncFrames: syncFrames, } @@ -64,13 +62,15 @@ func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { assert(binary.Write(out, binary.LittleEndian, x) == nil) } - writeUint32(currentVersion) + writeUint32(uint32(pw.version)) - var flags uint32 - if pw.SyncMarkers() { - flags |= flagSyncMarkers + if pw.version.Has(Flags) { + var flags uint32 + if pw.SyncMarkers() { + flags |= flagSyncMarkers + } + writeUint32(flags) } - writeUint32(flags) // Write elemEndsEnds. var sum uint32 @@ -159,7 +159,7 @@ type Encoder struct { // Flush finalizes the element's bitstream and returns its Index. func (w *Encoder) Flush() Index { - var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved + var sb strings.Builder // Backup the data so we write the relocations at the front. var tmp bytes.Buffer @@ -189,7 +189,7 @@ func (w *Encoder) Flush() Index { func (w *Encoder) checkErr(err error) { if err != nil { - errorf("unexpected encoding error: %v", err) + panicf("unexpected encoding error: %v", err) } } @@ -320,8 +320,14 @@ func (w *Encoder) Code(c Code) { // section (if not already present), and then writing a relocation // into the element bitstream. func (w *Encoder) String(s string) { + w.StringRef(w.p.StringIdx(s)) +} + +// StringRef writes a reference to the given index, which must be a +// previously encoded string value. +func (w *Encoder) StringRef(idx Index) { w.Sync(SyncString) - w.Reloc(RelocString, w.p.StringIdx(s)) + w.Reloc(RelocString, idx) } // Strings encodes and writes a variable-length slice of strings into @@ -348,7 +354,7 @@ func (w *Encoder) Value(val constant.Value) { func (w *Encoder) scalar(val constant.Value) { switch v := constant.Val(val).(type) { default: - errorf("unhandled %v (%v)", val, val.Kind()) + panicf("unhandled %v (%v)", val, val.Kind()) case bool: w.Code(ValBool) w.Bool(v) @@ -381,3 +387,6 @@ func (w *Encoder) bigFloat(v *big.Float) { b := v.Append(nil, 'p', -1) w.String(string(b)) // TODO: More efficient encoding. } + +// Version reports the version of the bitstream. +func (w *Encoder) Version() Version { return w.p.version } diff --git a/vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go b/vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go deleted file mode 100644 index 5294f6a63e..0000000000 --- a/vendor/golang.org/x/tools/internal/pkgbits/frames_go1.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2021 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. - -//go:build !go1.7 -// +build !go1.7 - -// TODO(mdempsky): Remove after #44505 is resolved - -package pkgbits - -import "runtime" - -func walkFrames(pcs []uintptr, visit frameVisitor) { - for _, pc := range pcs { - fn := runtime.FuncForPC(pc) - file, line := fn.FileLine(pc) - - visit(file, line, fn.Name(), pc-fn.Entry()) - } -} diff --git a/vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go b/vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go deleted file mode 100644 index 2324ae7adf..0000000000 --- a/vendor/golang.org/x/tools/internal/pkgbits/frames_go17.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 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. - -//go:build go1.7 -// +build go1.7 - -package pkgbits - -import "runtime" - -// walkFrames calls visit for each call frame represented by pcs. -// -// pcs should be a slice of PCs, as returned by runtime.Callers. -func walkFrames(pcs []uintptr, visit frameVisitor) { - if len(pcs) == 0 { - return - } - - frames := runtime.CallersFrames(pcs) - for { - frame, more := frames.Next() - visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) - if !more { - return - } - } -} diff --git a/vendor/golang.org/x/tools/internal/pkgbits/support.go b/vendor/golang.org/x/tools/internal/pkgbits/support.go index ad26d3b28c..50534a2955 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/support.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/support.go @@ -12,6 +12,6 @@ func assert(b bool) { } } -func errorf(format string, args ...interface{}) { +func panicf(format string, args ...any) { panic(fmt.Errorf(format, args...)) } diff --git a/vendor/golang.org/x/tools/internal/pkgbits/sync.go b/vendor/golang.org/x/tools/internal/pkgbits/sync.go index 5bd51ef717..1520b73afb 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/sync.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/sync.go @@ -6,6 +6,7 @@ package pkgbits import ( "fmt" + "runtime" "strings" ) @@ -23,6 +24,24 @@ func fmtFrames(pcs ...uintptr) []string { type frameVisitor func(file string, line int, name string, offset uintptr) +// walkFrames calls visit for each call frame represented by pcs. +// +// pcs should be a slice of PCs, as returned by runtime.Callers. +func walkFrames(pcs []uintptr, visit frameVisitor) { + if len(pcs) == 0 { + return + } + + frames := runtime.CallersFrames(pcs) + for { + frame, more := frames.Next() + visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry) + if !more { + return + } + } +} + // SyncMarker is an enum type that represents markers that may be // written to export data to ensure the reader and writer stay // synchronized. @@ -110,4 +129,8 @@ const ( SyncStmtsEnd SyncLabel SyncOptLabel + + SyncMultiExpr + SyncRType + SyncConvRTTI ) diff --git a/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go b/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go index 4a5b0ca5f2..582ad56d3e 100644 --- a/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go +++ b/vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go @@ -74,11 +74,14 @@ func _() { _ = x[SyncStmtsEnd-64] _ = x[SyncLabel-65] _ = x[SyncOptLabel-66] + _ = x[SyncMultiExpr-67] + _ = x[SyncRType-68] + _ = x[SyncConvRTTI-69] } -const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel" +const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabelMultiExprRTypeConvRTTI" -var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458} +var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480} func (i SyncMarker) String() string { i -= 1 diff --git a/vendor/golang.org/x/tools/internal/pkgbits/version.go b/vendor/golang.org/x/tools/internal/pkgbits/version.go new file mode 100644 index 0000000000..53af9df22b --- /dev/null +++ b/vendor/golang.org/x/tools/internal/pkgbits/version.go @@ -0,0 +1,85 @@ +// 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 pkgbits + +// Version indicates a version of a unified IR bitstream. +// Each Version indicates the addition, removal, or change of +// new data in the bitstream. +// +// These are serialized to disk and the interpretation remains fixed. +type Version uint32 + +const ( + // V0: initial prototype. + // + // All data that is not assigned a Field is in version V0 + // and has not been deprecated. + V0 Version = iota + + // V1: adds the Flags uint32 word + V1 + + // V2: removes unused legacy fields and supports type parameters for aliases. + // - remove the legacy "has init" bool from the public root + // - remove obj's "derived func instance" bool + // - add a TypeParamNames field to ObjAlias + // - remove derived info "needed" bool + V2 + + numVersions = iota +) + +// Field denotes a unit of data in the serialized unified IR bitstream. +// It is conceptually a like field in a structure. +// +// We only really need Fields when the data may or may not be present +// in a stream based on the Version of the bitstream. +// +// Unlike much of pkgbits, Fields are not serialized and +// can change values as needed. +type Field int + +const ( + // Flags in a uint32 in the header of a bitstream + // that is used to indicate whether optional features are enabled. + Flags Field = iota + + // Deprecated: HasInit was a bool indicating whether a package + // has any init functions. + HasInit + + // Deprecated: DerivedFuncInstance was a bool indicating + // whether an object was a function instance. + DerivedFuncInstance + + // ObjAlias has a list of TypeParamNames. + AliasTypeParamNames + + // Deprecated: DerivedInfoNeeded was a bool indicating + // whether a type was a derived type. + DerivedInfoNeeded + + numFields = iota +) + +// introduced is the version a field was added. +var introduced = [numFields]Version{ + Flags: V1, + AliasTypeParamNames: V2, +} + +// removed is the version a field was removed in or 0 for fields +// that have not yet been deprecated. +// (So removed[f]-1 is the last version it is included in.) +var removed = [numFields]Version{ + HasInit: V2, + DerivedFuncInstance: V2, + DerivedInfoNeeded: V2, +} + +// Has reports whether field f is present in a bitstream at version v. +func (v Version) Has(f Field) bool { + return introduced[f] <= v && (v < removed[f] || removed[f] == V0) +} diff --git a/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/vendor/golang.org/x/tools/internal/stdlib/manifest.go new file mode 100644 index 0000000000..cdaac9ab34 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/stdlib/manifest.go @@ -0,0 +1,17431 @@ +// 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. + +// Code generated by generate.go. DO NOT EDIT. + +package stdlib + +var PackageSymbols = map[string][]Symbol{ + "archive/tar": { + {"(*Header).FileInfo", Method, 1}, + {"(*Reader).Next", Method, 0}, + {"(*Reader).Read", Method, 0}, + {"(*Writer).AddFS", Method, 22}, + {"(*Writer).Close", Method, 0}, + {"(*Writer).Flush", Method, 0}, + {"(*Writer).Write", Method, 0}, + {"(*Writer).WriteHeader", Method, 0}, + {"(Format).String", Method, 10}, + {"ErrFieldTooLong", Var, 0}, + {"ErrHeader", Var, 0}, + {"ErrInsecurePath", Var, 20}, + {"ErrWriteAfterClose", Var, 0}, + {"ErrWriteTooLong", Var, 0}, + {"FileInfoHeader", Func, 1}, + {"FileInfoNames", Type, 23}, + {"Format", Type, 10}, + {"FormatGNU", Const, 10}, + {"FormatPAX", Const, 10}, + {"FormatUSTAR", Const, 10}, + {"FormatUnknown", Const, 10}, + {"Header", Type, 0}, + {"Header.AccessTime", Field, 0}, + {"Header.ChangeTime", Field, 0}, + {"Header.Devmajor", Field, 0}, + {"Header.Devminor", Field, 0}, + {"Header.Format", Field, 10}, + {"Header.Gid", Field, 0}, + {"Header.Gname", Field, 0}, + {"Header.Linkname", Field, 0}, + {"Header.ModTime", Field, 0}, + {"Header.Mode", Field, 0}, + {"Header.Name", Field, 0}, + {"Header.PAXRecords", Field, 10}, + {"Header.Size", Field, 0}, + {"Header.Typeflag", Field, 0}, + {"Header.Uid", Field, 0}, + {"Header.Uname", Field, 0}, + {"Header.Xattrs", Field, 3}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"Reader", Type, 0}, + {"TypeBlock", Const, 0}, + {"TypeChar", Const, 0}, + {"TypeCont", Const, 0}, + {"TypeDir", Const, 0}, + {"TypeFifo", Const, 0}, + {"TypeGNULongLink", Const, 1}, + {"TypeGNULongName", Const, 1}, + {"TypeGNUSparse", Const, 3}, + {"TypeLink", Const, 0}, + {"TypeReg", Const, 0}, + {"TypeRegA", Const, 0}, + {"TypeSymlink", Const, 0}, + {"TypeXGlobalHeader", Const, 0}, + {"TypeXHeader", Const, 0}, + {"Writer", Type, 0}, + }, + "archive/zip": { + {"(*File).DataOffset", Method, 2}, + {"(*File).FileInfo", Method, 0}, + {"(*File).ModTime", Method, 0}, + {"(*File).Mode", Method, 0}, + {"(*File).Open", Method, 0}, + {"(*File).OpenRaw", Method, 17}, + {"(*File).SetModTime", Method, 0}, + {"(*File).SetMode", Method, 0}, + {"(*FileHeader).FileInfo", Method, 0}, + {"(*FileHeader).ModTime", Method, 0}, + {"(*FileHeader).Mode", Method, 0}, + {"(*FileHeader).SetModTime", Method, 0}, + {"(*FileHeader).SetMode", Method, 0}, + {"(*ReadCloser).Close", Method, 0}, + {"(*ReadCloser).Open", Method, 16}, + {"(*ReadCloser).RegisterDecompressor", Method, 6}, + {"(*Reader).Open", Method, 16}, + {"(*Reader).RegisterDecompressor", Method, 6}, + {"(*Writer).AddFS", Method, 22}, + {"(*Writer).Close", Method, 0}, + {"(*Writer).Copy", Method, 17}, + {"(*Writer).Create", Method, 0}, + {"(*Writer).CreateHeader", Method, 0}, + {"(*Writer).CreateRaw", Method, 17}, + {"(*Writer).Flush", Method, 4}, + {"(*Writer).RegisterCompressor", Method, 6}, + {"(*Writer).SetComment", Method, 10}, + {"(*Writer).SetOffset", Method, 5}, + {"Compressor", Type, 2}, + {"Decompressor", Type, 2}, + {"Deflate", Const, 0}, + {"ErrAlgorithm", Var, 0}, + {"ErrChecksum", Var, 0}, + {"ErrFormat", Var, 0}, + {"ErrInsecurePath", Var, 20}, + {"File", Type, 0}, + {"File.FileHeader", Field, 0}, + {"FileHeader", Type, 0}, + {"FileHeader.CRC32", Field, 0}, + {"FileHeader.Comment", Field, 0}, + {"FileHeader.CompressedSize", Field, 0}, + {"FileHeader.CompressedSize64", Field, 1}, + {"FileHeader.CreatorVersion", Field, 0}, + {"FileHeader.ExternalAttrs", Field, 0}, + {"FileHeader.Extra", Field, 0}, + {"FileHeader.Flags", Field, 0}, + {"FileHeader.Method", Field, 0}, + {"FileHeader.Modified", Field, 10}, + {"FileHeader.ModifiedDate", Field, 0}, + {"FileHeader.ModifiedTime", Field, 0}, + {"FileHeader.Name", Field, 0}, + {"FileHeader.NonUTF8", Field, 10}, + {"FileHeader.ReaderVersion", Field, 0}, + {"FileHeader.UncompressedSize", Field, 0}, + {"FileHeader.UncompressedSize64", Field, 1}, + {"FileInfoHeader", Func, 0}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"OpenReader", Func, 0}, + {"ReadCloser", Type, 0}, + {"ReadCloser.Reader", Field, 0}, + {"Reader", Type, 0}, + {"Reader.Comment", Field, 0}, + {"Reader.File", Field, 0}, + {"RegisterCompressor", Func, 2}, + {"RegisterDecompressor", Func, 2}, + {"Store", Const, 0}, + {"Writer", Type, 0}, + }, + "bufio": { + {"(*Reader).Buffered", Method, 0}, + {"(*Reader).Discard", Method, 5}, + {"(*Reader).Peek", Method, 0}, + {"(*Reader).Read", Method, 0}, + {"(*Reader).ReadByte", Method, 0}, + {"(*Reader).ReadBytes", Method, 0}, + {"(*Reader).ReadLine", Method, 0}, + {"(*Reader).ReadRune", Method, 0}, + {"(*Reader).ReadSlice", Method, 0}, + {"(*Reader).ReadString", Method, 0}, + {"(*Reader).Reset", Method, 2}, + {"(*Reader).Size", Method, 10}, + {"(*Reader).UnreadByte", Method, 0}, + {"(*Reader).UnreadRune", Method, 0}, + {"(*Reader).WriteTo", Method, 1}, + {"(*Scanner).Buffer", Method, 6}, + {"(*Scanner).Bytes", Method, 1}, + {"(*Scanner).Err", Method, 1}, + {"(*Scanner).Scan", Method, 1}, + {"(*Scanner).Split", Method, 1}, + {"(*Scanner).Text", Method, 1}, + {"(*Writer).Available", Method, 0}, + {"(*Writer).AvailableBuffer", Method, 18}, + {"(*Writer).Buffered", Method, 0}, + {"(*Writer).Flush", Method, 0}, + {"(*Writer).ReadFrom", Method, 1}, + {"(*Writer).Reset", Method, 2}, + {"(*Writer).Size", Method, 10}, + {"(*Writer).Write", Method, 0}, + {"(*Writer).WriteByte", Method, 0}, + {"(*Writer).WriteRune", Method, 0}, + {"(*Writer).WriteString", Method, 0}, + {"(ReadWriter).Available", Method, 0}, + {"(ReadWriter).AvailableBuffer", Method, 18}, + {"(ReadWriter).Discard", Method, 5}, + {"(ReadWriter).Flush", Method, 0}, + {"(ReadWriter).Peek", Method, 0}, + {"(ReadWriter).Read", Method, 0}, + {"(ReadWriter).ReadByte", Method, 0}, + {"(ReadWriter).ReadBytes", Method, 0}, + {"(ReadWriter).ReadFrom", Method, 1}, + {"(ReadWriter).ReadLine", Method, 0}, + {"(ReadWriter).ReadRune", Method, 0}, + {"(ReadWriter).ReadSlice", Method, 0}, + {"(ReadWriter).ReadString", Method, 0}, + {"(ReadWriter).UnreadByte", Method, 0}, + {"(ReadWriter).UnreadRune", Method, 0}, + {"(ReadWriter).Write", Method, 0}, + {"(ReadWriter).WriteByte", Method, 0}, + {"(ReadWriter).WriteRune", Method, 0}, + {"(ReadWriter).WriteString", Method, 0}, + {"(ReadWriter).WriteTo", Method, 1}, + {"ErrAdvanceTooFar", Var, 1}, + {"ErrBadReadCount", Var, 15}, + {"ErrBufferFull", Var, 0}, + {"ErrFinalToken", Var, 6}, + {"ErrInvalidUnreadByte", Var, 0}, + {"ErrInvalidUnreadRune", Var, 0}, + {"ErrNegativeAdvance", Var, 1}, + {"ErrNegativeCount", Var, 0}, + {"ErrTooLong", Var, 1}, + {"MaxScanTokenSize", Const, 1}, + {"NewReadWriter", Func, 0}, + {"NewReader", Func, 0}, + {"NewReaderSize", Func, 0}, + {"NewScanner", Func, 1}, + {"NewWriter", Func, 0}, + {"NewWriterSize", Func, 0}, + {"ReadWriter", Type, 0}, + {"ReadWriter.Reader", Field, 0}, + {"ReadWriter.Writer", Field, 0}, + {"Reader", Type, 0}, + {"ScanBytes", Func, 1}, + {"ScanLines", Func, 1}, + {"ScanRunes", Func, 1}, + {"ScanWords", Func, 1}, + {"Scanner", Type, 1}, + {"SplitFunc", Type, 1}, + {"Writer", Type, 0}, + }, + "bytes": { + {"(*Buffer).Available", Method, 21}, + {"(*Buffer).AvailableBuffer", Method, 21}, + {"(*Buffer).Bytes", Method, 0}, + {"(*Buffer).Cap", Method, 5}, + {"(*Buffer).Grow", Method, 1}, + {"(*Buffer).Len", Method, 0}, + {"(*Buffer).Next", Method, 0}, + {"(*Buffer).Read", Method, 0}, + {"(*Buffer).ReadByte", Method, 0}, + {"(*Buffer).ReadBytes", Method, 0}, + {"(*Buffer).ReadFrom", Method, 0}, + {"(*Buffer).ReadRune", Method, 0}, + {"(*Buffer).ReadString", Method, 0}, + {"(*Buffer).Reset", Method, 0}, + {"(*Buffer).String", Method, 0}, + {"(*Buffer).Truncate", Method, 0}, + {"(*Buffer).UnreadByte", Method, 0}, + {"(*Buffer).UnreadRune", Method, 0}, + {"(*Buffer).Write", Method, 0}, + {"(*Buffer).WriteByte", Method, 0}, + {"(*Buffer).WriteRune", Method, 0}, + {"(*Buffer).WriteString", Method, 0}, + {"(*Buffer).WriteTo", Method, 0}, + {"(*Reader).Len", Method, 0}, + {"(*Reader).Read", Method, 0}, + {"(*Reader).ReadAt", Method, 0}, + {"(*Reader).ReadByte", Method, 0}, + {"(*Reader).ReadRune", Method, 0}, + {"(*Reader).Reset", Method, 7}, + {"(*Reader).Seek", Method, 0}, + {"(*Reader).Size", Method, 5}, + {"(*Reader).UnreadByte", Method, 0}, + {"(*Reader).UnreadRune", Method, 0}, + {"(*Reader).WriteTo", Method, 1}, + {"Buffer", Type, 0}, + {"Clone", Func, 20}, + {"Compare", Func, 0}, + {"Contains", Func, 0}, + {"ContainsAny", Func, 7}, + {"ContainsFunc", Func, 21}, + {"ContainsRune", Func, 7}, + {"Count", Func, 0}, + {"Cut", Func, 18}, + {"CutPrefix", Func, 20}, + {"CutSuffix", Func, 20}, + {"Equal", Func, 0}, + {"EqualFold", Func, 0}, + {"ErrTooLarge", Var, 0}, + {"Fields", Func, 0}, + {"FieldsFunc", Func, 0}, + {"HasPrefix", Func, 0}, + {"HasSuffix", Func, 0}, + {"Index", Func, 0}, + {"IndexAny", Func, 0}, + {"IndexByte", Func, 0}, + {"IndexFunc", Func, 0}, + {"IndexRune", Func, 0}, + {"Join", Func, 0}, + {"LastIndex", Func, 0}, + {"LastIndexAny", Func, 0}, + {"LastIndexByte", Func, 5}, + {"LastIndexFunc", Func, 0}, + {"Map", Func, 0}, + {"MinRead", Const, 0}, + {"NewBuffer", Func, 0}, + {"NewBufferString", Func, 0}, + {"NewReader", Func, 0}, + {"Reader", Type, 0}, + {"Repeat", Func, 0}, + {"Replace", Func, 0}, + {"ReplaceAll", Func, 12}, + {"Runes", Func, 0}, + {"Split", Func, 0}, + {"SplitAfter", Func, 0}, + {"SplitAfterN", Func, 0}, + {"SplitN", Func, 0}, + {"Title", Func, 0}, + {"ToLower", Func, 0}, + {"ToLowerSpecial", Func, 0}, + {"ToTitle", Func, 0}, + {"ToTitleSpecial", Func, 0}, + {"ToUpper", Func, 0}, + {"ToUpperSpecial", Func, 0}, + {"ToValidUTF8", Func, 13}, + {"Trim", Func, 0}, + {"TrimFunc", Func, 0}, + {"TrimLeft", Func, 0}, + {"TrimLeftFunc", Func, 0}, + {"TrimPrefix", Func, 1}, + {"TrimRight", Func, 0}, + {"TrimRightFunc", Func, 0}, + {"TrimSpace", Func, 0}, + {"TrimSuffix", Func, 1}, + }, + "cmp": { + {"Compare", Func, 21}, + {"Less", Func, 21}, + {"Or", Func, 22}, + {"Ordered", Type, 21}, + }, + "compress/bzip2": { + {"(StructuralError).Error", Method, 0}, + {"NewReader", Func, 0}, + {"StructuralError", Type, 0}, + }, + "compress/flate": { + {"(*ReadError).Error", Method, 0}, + {"(*WriteError).Error", Method, 0}, + {"(*Writer).Close", Method, 0}, + {"(*Writer).Flush", Method, 0}, + {"(*Writer).Reset", Method, 2}, + {"(*Writer).Write", Method, 0}, + {"(CorruptInputError).Error", Method, 0}, + {"(InternalError).Error", Method, 0}, + {"BestCompression", Const, 0}, + {"BestSpeed", Const, 0}, + {"CorruptInputError", Type, 0}, + {"DefaultCompression", Const, 0}, + {"HuffmanOnly", Const, 7}, + {"InternalError", Type, 0}, + {"NewReader", Func, 0}, + {"NewReaderDict", Func, 0}, + {"NewWriter", Func, 0}, + {"NewWriterDict", Func, 0}, + {"NoCompression", Const, 0}, + {"ReadError", Type, 0}, + {"ReadError.Err", Field, 0}, + {"ReadError.Offset", Field, 0}, + {"Reader", Type, 0}, + {"Resetter", Type, 4}, + {"WriteError", Type, 0}, + {"WriteError.Err", Field, 0}, + {"WriteError.Offset", Field, 0}, + {"Writer", Type, 0}, + }, + "compress/gzip": { + {"(*Reader).Close", Method, 0}, + {"(*Reader).Multistream", Method, 4}, + {"(*Reader).Read", Method, 0}, + {"(*Reader).Reset", Method, 3}, + {"(*Writer).Close", Method, 0}, + {"(*Writer).Flush", Method, 1}, + {"(*Writer).Reset", Method, 2}, + {"(*Writer).Write", Method, 0}, + {"BestCompression", Const, 0}, + {"BestSpeed", Const, 0}, + {"DefaultCompression", Const, 0}, + {"ErrChecksum", Var, 0}, + {"ErrHeader", Var, 0}, + {"Header", Type, 0}, + {"Header.Comment", Field, 0}, + {"Header.Extra", Field, 0}, + {"Header.ModTime", Field, 0}, + {"Header.Name", Field, 0}, + {"Header.OS", Field, 0}, + {"HuffmanOnly", Const, 8}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"NewWriterLevel", Func, 0}, + {"NoCompression", Const, 0}, + {"Reader", Type, 0}, + {"Reader.Header", Field, 0}, + {"Writer", Type, 0}, + {"Writer.Header", Field, 0}, + }, + "compress/lzw": { + {"(*Reader).Close", Method, 17}, + {"(*Reader).Read", Method, 17}, + {"(*Reader).Reset", Method, 17}, + {"(*Writer).Close", Method, 17}, + {"(*Writer).Reset", Method, 17}, + {"(*Writer).Write", Method, 17}, + {"LSB", Const, 0}, + {"MSB", Const, 0}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"Order", Type, 0}, + {"Reader", Type, 17}, + {"Writer", Type, 17}, + }, + "compress/zlib": { + {"(*Writer).Close", Method, 0}, + {"(*Writer).Flush", Method, 0}, + {"(*Writer).Reset", Method, 2}, + {"(*Writer).Write", Method, 0}, + {"BestCompression", Const, 0}, + {"BestSpeed", Const, 0}, + {"DefaultCompression", Const, 0}, + {"ErrChecksum", Var, 0}, + {"ErrDictionary", Var, 0}, + {"ErrHeader", Var, 0}, + {"HuffmanOnly", Const, 8}, + {"NewReader", Func, 0}, + {"NewReaderDict", Func, 0}, + {"NewWriter", Func, 0}, + {"NewWriterLevel", Func, 0}, + {"NewWriterLevelDict", Func, 0}, + {"NoCompression", Const, 0}, + {"Resetter", Type, 4}, + {"Writer", Type, 0}, + }, + "container/heap": { + {"Fix", Func, 2}, + {"Init", Func, 0}, + {"Interface", Type, 0}, + {"Pop", Func, 0}, + {"Push", Func, 0}, + {"Remove", Func, 0}, + }, + "container/list": { + {"(*Element).Next", Method, 0}, + {"(*Element).Prev", Method, 0}, + {"(*List).Back", Method, 0}, + {"(*List).Front", Method, 0}, + {"(*List).Init", Method, 0}, + {"(*List).InsertAfter", Method, 0}, + {"(*List).InsertBefore", Method, 0}, + {"(*List).Len", Method, 0}, + {"(*List).MoveAfter", Method, 2}, + {"(*List).MoveBefore", Method, 2}, + {"(*List).MoveToBack", Method, 0}, + {"(*List).MoveToFront", Method, 0}, + {"(*List).PushBack", Method, 0}, + {"(*List).PushBackList", Method, 0}, + {"(*List).PushFront", Method, 0}, + {"(*List).PushFrontList", Method, 0}, + {"(*List).Remove", Method, 0}, + {"Element", Type, 0}, + {"Element.Value", Field, 0}, + {"List", Type, 0}, + {"New", Func, 0}, + }, + "container/ring": { + {"(*Ring).Do", Method, 0}, + {"(*Ring).Len", Method, 0}, + {"(*Ring).Link", Method, 0}, + {"(*Ring).Move", Method, 0}, + {"(*Ring).Next", Method, 0}, + {"(*Ring).Prev", Method, 0}, + {"(*Ring).Unlink", Method, 0}, + {"New", Func, 0}, + {"Ring", Type, 0}, + {"Ring.Value", Field, 0}, + }, + "context": { + {"AfterFunc", Func, 21}, + {"Background", Func, 7}, + {"CancelCauseFunc", Type, 20}, + {"CancelFunc", Type, 7}, + {"Canceled", Var, 7}, + {"Cause", Func, 20}, + {"Context", Type, 7}, + {"DeadlineExceeded", Var, 7}, + {"TODO", Func, 7}, + {"WithCancel", Func, 7}, + {"WithCancelCause", Func, 20}, + {"WithDeadline", Func, 7}, + {"WithDeadlineCause", Func, 21}, + {"WithTimeout", Func, 7}, + {"WithTimeoutCause", Func, 21}, + {"WithValue", Func, 7}, + {"WithoutCancel", Func, 21}, + }, + "crypto": { + {"(Hash).Available", Method, 0}, + {"(Hash).HashFunc", Method, 4}, + {"(Hash).New", Method, 0}, + {"(Hash).Size", Method, 0}, + {"(Hash).String", Method, 15}, + {"BLAKE2b_256", Const, 9}, + {"BLAKE2b_384", Const, 9}, + {"BLAKE2b_512", Const, 9}, + {"BLAKE2s_256", Const, 9}, + {"Decrypter", Type, 5}, + {"DecrypterOpts", Type, 5}, + {"Hash", Type, 0}, + {"MD4", Const, 0}, + {"MD5", Const, 0}, + {"MD5SHA1", Const, 0}, + {"PrivateKey", Type, 0}, + {"PublicKey", Type, 2}, + {"RIPEMD160", Const, 0}, + {"RegisterHash", Func, 0}, + {"SHA1", Const, 0}, + {"SHA224", Const, 0}, + {"SHA256", Const, 0}, + {"SHA384", Const, 0}, + {"SHA3_224", Const, 4}, + {"SHA3_256", Const, 4}, + {"SHA3_384", Const, 4}, + {"SHA3_512", Const, 4}, + {"SHA512", Const, 0}, + {"SHA512_224", Const, 5}, + {"SHA512_256", Const, 5}, + {"Signer", Type, 4}, + {"SignerOpts", Type, 4}, + }, + "crypto/aes": { + {"(KeySizeError).Error", Method, 0}, + {"BlockSize", Const, 0}, + {"KeySizeError", Type, 0}, + {"NewCipher", Func, 0}, + }, + "crypto/cipher": { + {"(StreamReader).Read", Method, 0}, + {"(StreamWriter).Close", Method, 0}, + {"(StreamWriter).Write", Method, 0}, + {"AEAD", Type, 2}, + {"Block", Type, 0}, + {"BlockMode", Type, 0}, + {"NewCBCDecrypter", Func, 0}, + {"NewCBCEncrypter", Func, 0}, + {"NewCFBDecrypter", Func, 0}, + {"NewCFBEncrypter", Func, 0}, + {"NewCTR", Func, 0}, + {"NewGCM", Func, 2}, + {"NewGCMWithNonceSize", Func, 5}, + {"NewGCMWithTagSize", Func, 11}, + {"NewOFB", Func, 0}, + {"Stream", Type, 0}, + {"StreamReader", Type, 0}, + {"StreamReader.R", Field, 0}, + {"StreamReader.S", Field, 0}, + {"StreamWriter", Type, 0}, + {"StreamWriter.Err", Field, 0}, + {"StreamWriter.S", Field, 0}, + {"StreamWriter.W", Field, 0}, + }, + "crypto/des": { + {"(KeySizeError).Error", Method, 0}, + {"BlockSize", Const, 0}, + {"KeySizeError", Type, 0}, + {"NewCipher", Func, 0}, + {"NewTripleDESCipher", Func, 0}, + }, + "crypto/dsa": { + {"ErrInvalidPublicKey", Var, 0}, + {"GenerateKey", Func, 0}, + {"GenerateParameters", Func, 0}, + {"L1024N160", Const, 0}, + {"L2048N224", Const, 0}, + {"L2048N256", Const, 0}, + {"L3072N256", Const, 0}, + {"ParameterSizes", Type, 0}, + {"Parameters", Type, 0}, + {"Parameters.G", Field, 0}, + {"Parameters.P", Field, 0}, + {"Parameters.Q", Field, 0}, + {"PrivateKey", Type, 0}, + {"PrivateKey.PublicKey", Field, 0}, + {"PrivateKey.X", Field, 0}, + {"PublicKey", Type, 0}, + {"PublicKey.Parameters", Field, 0}, + {"PublicKey.Y", Field, 0}, + {"Sign", Func, 0}, + {"Verify", Func, 0}, + }, + "crypto/ecdh": { + {"(*PrivateKey).Bytes", Method, 20}, + {"(*PrivateKey).Curve", Method, 20}, + {"(*PrivateKey).ECDH", Method, 20}, + {"(*PrivateKey).Equal", Method, 20}, + {"(*PrivateKey).Public", Method, 20}, + {"(*PrivateKey).PublicKey", Method, 20}, + {"(*PublicKey).Bytes", Method, 20}, + {"(*PublicKey).Curve", Method, 20}, + {"(*PublicKey).Equal", Method, 20}, + {"Curve", Type, 20}, + {"P256", Func, 20}, + {"P384", Func, 20}, + {"P521", Func, 20}, + {"PrivateKey", Type, 20}, + {"PublicKey", Type, 20}, + {"X25519", Func, 20}, + }, + "crypto/ecdsa": { + {"(*PrivateKey).ECDH", Method, 20}, + {"(*PrivateKey).Equal", Method, 15}, + {"(*PrivateKey).Public", Method, 4}, + {"(*PrivateKey).Sign", Method, 4}, + {"(*PublicKey).ECDH", Method, 20}, + {"(*PublicKey).Equal", Method, 15}, + {"(PrivateKey).Add", Method, 0}, + {"(PrivateKey).Double", Method, 0}, + {"(PrivateKey).IsOnCurve", Method, 0}, + {"(PrivateKey).Params", Method, 0}, + {"(PrivateKey).ScalarBaseMult", Method, 0}, + {"(PrivateKey).ScalarMult", Method, 0}, + {"(PublicKey).Add", Method, 0}, + {"(PublicKey).Double", Method, 0}, + {"(PublicKey).IsOnCurve", Method, 0}, + {"(PublicKey).Params", Method, 0}, + {"(PublicKey).ScalarBaseMult", Method, 0}, + {"(PublicKey).ScalarMult", Method, 0}, + {"GenerateKey", Func, 0}, + {"PrivateKey", Type, 0}, + {"PrivateKey.D", Field, 0}, + {"PrivateKey.PublicKey", Field, 0}, + {"PublicKey", Type, 0}, + {"PublicKey.Curve", Field, 0}, + {"PublicKey.X", Field, 0}, + {"PublicKey.Y", Field, 0}, + {"Sign", Func, 0}, + {"SignASN1", Func, 15}, + {"Verify", Func, 0}, + {"VerifyASN1", Func, 15}, + }, + "crypto/ed25519": { + {"(*Options).HashFunc", Method, 20}, + {"(PrivateKey).Equal", Method, 15}, + {"(PrivateKey).Public", Method, 13}, + {"(PrivateKey).Seed", Method, 13}, + {"(PrivateKey).Sign", Method, 13}, + {"(PublicKey).Equal", Method, 15}, + {"GenerateKey", Func, 13}, + {"NewKeyFromSeed", Func, 13}, + {"Options", Type, 20}, + {"Options.Context", Field, 20}, + {"Options.Hash", Field, 20}, + {"PrivateKey", Type, 13}, + {"PrivateKeySize", Const, 13}, + {"PublicKey", Type, 13}, + {"PublicKeySize", Const, 13}, + {"SeedSize", Const, 13}, + {"Sign", Func, 13}, + {"SignatureSize", Const, 13}, + {"Verify", Func, 13}, + {"VerifyWithOptions", Func, 20}, + }, + "crypto/elliptic": { + {"(*CurveParams).Add", Method, 0}, + {"(*CurveParams).Double", Method, 0}, + {"(*CurveParams).IsOnCurve", Method, 0}, + {"(*CurveParams).Params", Method, 0}, + {"(*CurveParams).ScalarBaseMult", Method, 0}, + {"(*CurveParams).ScalarMult", Method, 0}, + {"Curve", Type, 0}, + {"CurveParams", Type, 0}, + {"CurveParams.B", Field, 0}, + {"CurveParams.BitSize", Field, 0}, + {"CurveParams.Gx", Field, 0}, + {"CurveParams.Gy", Field, 0}, + {"CurveParams.N", Field, 0}, + {"CurveParams.Name", Field, 5}, + {"CurveParams.P", Field, 0}, + {"GenerateKey", Func, 0}, + {"Marshal", Func, 0}, + {"MarshalCompressed", Func, 15}, + {"P224", Func, 0}, + {"P256", Func, 0}, + {"P384", Func, 0}, + {"P521", Func, 0}, + {"Unmarshal", Func, 0}, + {"UnmarshalCompressed", Func, 15}, + }, + "crypto/hmac": { + {"Equal", Func, 1}, + {"New", Func, 0}, + }, + "crypto/md5": { + {"BlockSize", Const, 0}, + {"New", Func, 0}, + {"Size", Const, 0}, + {"Sum", Func, 2}, + }, + "crypto/rand": { + {"Int", Func, 0}, + {"Prime", Func, 0}, + {"Read", Func, 0}, + {"Reader", Var, 0}, + }, + "crypto/rc4": { + {"(*Cipher).Reset", Method, 0}, + {"(*Cipher).XORKeyStream", Method, 0}, + {"(KeySizeError).Error", Method, 0}, + {"Cipher", Type, 0}, + {"KeySizeError", Type, 0}, + {"NewCipher", Func, 0}, + }, + "crypto/rsa": { + {"(*PSSOptions).HashFunc", Method, 4}, + {"(*PrivateKey).Decrypt", Method, 5}, + {"(*PrivateKey).Equal", Method, 15}, + {"(*PrivateKey).Precompute", Method, 0}, + {"(*PrivateKey).Public", Method, 4}, + {"(*PrivateKey).Sign", Method, 4}, + {"(*PrivateKey).Size", Method, 11}, + {"(*PrivateKey).Validate", Method, 0}, + {"(*PublicKey).Equal", Method, 15}, + {"(*PublicKey).Size", Method, 11}, + {"CRTValue", Type, 0}, + {"CRTValue.Coeff", Field, 0}, + {"CRTValue.Exp", Field, 0}, + {"CRTValue.R", Field, 0}, + {"DecryptOAEP", Func, 0}, + {"DecryptPKCS1v15", Func, 0}, + {"DecryptPKCS1v15SessionKey", Func, 0}, + {"EncryptOAEP", Func, 0}, + {"EncryptPKCS1v15", Func, 0}, + {"ErrDecryption", Var, 0}, + {"ErrMessageTooLong", Var, 0}, + {"ErrVerification", Var, 0}, + {"GenerateKey", Func, 0}, + {"GenerateMultiPrimeKey", Func, 0}, + {"OAEPOptions", Type, 5}, + {"OAEPOptions.Hash", Field, 5}, + {"OAEPOptions.Label", Field, 5}, + {"OAEPOptions.MGFHash", Field, 20}, + {"PKCS1v15DecryptOptions", Type, 5}, + {"PKCS1v15DecryptOptions.SessionKeyLen", Field, 5}, + {"PSSOptions", Type, 2}, + {"PSSOptions.Hash", Field, 4}, + {"PSSOptions.SaltLength", Field, 2}, + {"PSSSaltLengthAuto", Const, 2}, + {"PSSSaltLengthEqualsHash", Const, 2}, + {"PrecomputedValues", Type, 0}, + {"PrecomputedValues.CRTValues", Field, 0}, + {"PrecomputedValues.Dp", Field, 0}, + {"PrecomputedValues.Dq", Field, 0}, + {"PrecomputedValues.Qinv", Field, 0}, + {"PrivateKey", Type, 0}, + {"PrivateKey.D", Field, 0}, + {"PrivateKey.Precomputed", Field, 0}, + {"PrivateKey.Primes", Field, 0}, + {"PrivateKey.PublicKey", Field, 0}, + {"PublicKey", Type, 0}, + {"PublicKey.E", Field, 0}, + {"PublicKey.N", Field, 0}, + {"SignPKCS1v15", Func, 0}, + {"SignPSS", Func, 2}, + {"VerifyPKCS1v15", Func, 0}, + {"VerifyPSS", Func, 2}, + }, + "crypto/sha1": { + {"BlockSize", Const, 0}, + {"New", Func, 0}, + {"Size", Const, 0}, + {"Sum", Func, 2}, + }, + "crypto/sha256": { + {"BlockSize", Const, 0}, + {"New", Func, 0}, + {"New224", Func, 0}, + {"Size", Const, 0}, + {"Size224", Const, 0}, + {"Sum224", Func, 2}, + {"Sum256", Func, 2}, + }, + "crypto/sha512": { + {"BlockSize", Const, 0}, + {"New", Func, 0}, + {"New384", Func, 0}, + {"New512_224", Func, 5}, + {"New512_256", Func, 5}, + {"Size", Const, 0}, + {"Size224", Const, 5}, + {"Size256", Const, 5}, + {"Size384", Const, 0}, + {"Sum384", Func, 2}, + {"Sum512", Func, 2}, + {"Sum512_224", Func, 5}, + {"Sum512_256", Func, 5}, + }, + "crypto/subtle": { + {"ConstantTimeByteEq", Func, 0}, + {"ConstantTimeCompare", Func, 0}, + {"ConstantTimeCopy", Func, 0}, + {"ConstantTimeEq", Func, 0}, + {"ConstantTimeLessOrEq", Func, 2}, + {"ConstantTimeSelect", Func, 0}, + {"XORBytes", Func, 20}, + }, + "crypto/tls": { + {"(*CertificateRequestInfo).Context", Method, 17}, + {"(*CertificateRequestInfo).SupportsCertificate", Method, 14}, + {"(*CertificateVerificationError).Error", Method, 20}, + {"(*CertificateVerificationError).Unwrap", Method, 20}, + {"(*ClientHelloInfo).Context", Method, 17}, + {"(*ClientHelloInfo).SupportsCertificate", Method, 14}, + {"(*ClientSessionState).ResumptionState", Method, 21}, + {"(*Config).BuildNameToCertificate", Method, 0}, + {"(*Config).Clone", Method, 8}, + {"(*Config).DecryptTicket", Method, 21}, + {"(*Config).EncryptTicket", Method, 21}, + {"(*Config).SetSessionTicketKeys", Method, 5}, + {"(*Conn).Close", Method, 0}, + {"(*Conn).CloseWrite", Method, 8}, + {"(*Conn).ConnectionState", Method, 0}, + {"(*Conn).Handshake", Method, 0}, + {"(*Conn).HandshakeContext", Method, 17}, + {"(*Conn).LocalAddr", Method, 0}, + {"(*Conn).NetConn", Method, 18}, + {"(*Conn).OCSPResponse", Method, 0}, + {"(*Conn).Read", Method, 0}, + {"(*Conn).RemoteAddr", Method, 0}, + {"(*Conn).SetDeadline", Method, 0}, + {"(*Conn).SetReadDeadline", Method, 0}, + {"(*Conn).SetWriteDeadline", Method, 0}, + {"(*Conn).VerifyHostname", Method, 0}, + {"(*Conn).Write", Method, 0}, + {"(*ConnectionState).ExportKeyingMaterial", Method, 11}, + {"(*Dialer).Dial", Method, 15}, + {"(*Dialer).DialContext", Method, 15}, + {"(*ECHRejectionError).Error", Method, 23}, + {"(*QUICConn).Close", Method, 21}, + {"(*QUICConn).ConnectionState", Method, 21}, + {"(*QUICConn).HandleData", Method, 21}, + {"(*QUICConn).NextEvent", Method, 21}, + {"(*QUICConn).SendSessionTicket", Method, 21}, + {"(*QUICConn).SetTransportParameters", Method, 21}, + {"(*QUICConn).Start", Method, 21}, + {"(*QUICConn).StoreSession", Method, 23}, + {"(*SessionState).Bytes", Method, 21}, + {"(AlertError).Error", Method, 21}, + {"(ClientAuthType).String", Method, 15}, + {"(CurveID).String", Method, 15}, + {"(QUICEncryptionLevel).String", Method, 21}, + {"(RecordHeaderError).Error", Method, 6}, + {"(SignatureScheme).String", Method, 15}, + {"AlertError", Type, 21}, + {"Certificate", Type, 0}, + {"Certificate.Certificate", Field, 0}, + {"Certificate.Leaf", Field, 0}, + {"Certificate.OCSPStaple", Field, 0}, + {"Certificate.PrivateKey", Field, 0}, + {"Certificate.SignedCertificateTimestamps", Field, 5}, + {"Certificate.SupportedSignatureAlgorithms", Field, 14}, + {"CertificateRequestInfo", Type, 8}, + {"CertificateRequestInfo.AcceptableCAs", Field, 8}, + {"CertificateRequestInfo.SignatureSchemes", Field, 8}, + {"CertificateRequestInfo.Version", Field, 14}, + {"CertificateVerificationError", Type, 20}, + {"CertificateVerificationError.Err", Field, 20}, + {"CertificateVerificationError.UnverifiedCertificates", Field, 20}, + {"CipherSuite", Type, 14}, + {"CipherSuite.ID", Field, 14}, + {"CipherSuite.Insecure", Field, 14}, + {"CipherSuite.Name", Field, 14}, + {"CipherSuite.SupportedVersions", Field, 14}, + {"CipherSuiteName", Func, 14}, + {"CipherSuites", Func, 14}, + {"Client", Func, 0}, + {"ClientAuthType", Type, 0}, + {"ClientHelloInfo", Type, 4}, + {"ClientHelloInfo.CipherSuites", Field, 4}, + {"ClientHelloInfo.Conn", Field, 8}, + {"ClientHelloInfo.ServerName", Field, 4}, + {"ClientHelloInfo.SignatureSchemes", Field, 8}, + {"ClientHelloInfo.SupportedCurves", Field, 4}, + {"ClientHelloInfo.SupportedPoints", Field, 4}, + {"ClientHelloInfo.SupportedProtos", Field, 8}, + {"ClientHelloInfo.SupportedVersions", Field, 8}, + {"ClientSessionCache", Type, 3}, + {"ClientSessionState", Type, 3}, + {"Config", Type, 0}, + {"Config.Certificates", Field, 0}, + {"Config.CipherSuites", Field, 0}, + {"Config.ClientAuth", Field, 0}, + {"Config.ClientCAs", Field, 0}, + {"Config.ClientSessionCache", Field, 3}, + {"Config.CurvePreferences", Field, 3}, + {"Config.DynamicRecordSizingDisabled", Field, 7}, + {"Config.EncryptedClientHelloConfigList", Field, 23}, + {"Config.EncryptedClientHelloRejectionVerify", Field, 23}, + {"Config.GetCertificate", Field, 4}, + {"Config.GetClientCertificate", Field, 8}, + {"Config.GetConfigForClient", Field, 8}, + {"Config.InsecureSkipVerify", Field, 0}, + {"Config.KeyLogWriter", Field, 8}, + {"Config.MaxVersion", Field, 2}, + {"Config.MinVersion", Field, 2}, + {"Config.NameToCertificate", Field, 0}, + {"Config.NextProtos", Field, 0}, + {"Config.PreferServerCipherSuites", Field, 1}, + {"Config.Rand", Field, 0}, + {"Config.Renegotiation", Field, 7}, + {"Config.RootCAs", Field, 0}, + {"Config.ServerName", Field, 0}, + {"Config.SessionTicketKey", Field, 1}, + {"Config.SessionTicketsDisabled", Field, 1}, + {"Config.Time", Field, 0}, + {"Config.UnwrapSession", Field, 21}, + {"Config.VerifyConnection", Field, 15}, + {"Config.VerifyPeerCertificate", Field, 8}, + {"Config.WrapSession", Field, 21}, + {"Conn", Type, 0}, + {"ConnectionState", Type, 0}, + {"ConnectionState.CipherSuite", Field, 0}, + {"ConnectionState.DidResume", Field, 1}, + {"ConnectionState.ECHAccepted", Field, 23}, + {"ConnectionState.HandshakeComplete", Field, 0}, + {"ConnectionState.NegotiatedProtocol", Field, 0}, + {"ConnectionState.NegotiatedProtocolIsMutual", Field, 0}, + {"ConnectionState.OCSPResponse", Field, 5}, + {"ConnectionState.PeerCertificates", Field, 0}, + {"ConnectionState.ServerName", Field, 0}, + {"ConnectionState.SignedCertificateTimestamps", Field, 5}, + {"ConnectionState.TLSUnique", Field, 4}, + {"ConnectionState.VerifiedChains", Field, 0}, + {"ConnectionState.Version", Field, 3}, + {"CurveID", Type, 3}, + {"CurveP256", Const, 3}, + {"CurveP384", Const, 3}, + {"CurveP521", Const, 3}, + {"Dial", Func, 0}, + {"DialWithDialer", Func, 3}, + {"Dialer", Type, 15}, + {"Dialer.Config", Field, 15}, + {"Dialer.NetDialer", Field, 15}, + {"ECDSAWithP256AndSHA256", Const, 8}, + {"ECDSAWithP384AndSHA384", Const, 8}, + {"ECDSAWithP521AndSHA512", Const, 8}, + {"ECDSAWithSHA1", Const, 10}, + {"ECHRejectionError", Type, 23}, + {"ECHRejectionError.RetryConfigList", Field, 23}, + {"Ed25519", Const, 13}, + {"InsecureCipherSuites", Func, 14}, + {"Listen", Func, 0}, + {"LoadX509KeyPair", Func, 0}, + {"NewLRUClientSessionCache", Func, 3}, + {"NewListener", Func, 0}, + {"NewResumptionState", Func, 21}, + {"NoClientCert", Const, 0}, + {"PKCS1WithSHA1", Const, 8}, + {"PKCS1WithSHA256", Const, 8}, + {"PKCS1WithSHA384", Const, 8}, + {"PKCS1WithSHA512", Const, 8}, + {"PSSWithSHA256", Const, 8}, + {"PSSWithSHA384", Const, 8}, + {"PSSWithSHA512", Const, 8}, + {"ParseSessionState", Func, 21}, + {"QUICClient", Func, 21}, + {"QUICConfig", Type, 21}, + {"QUICConfig.EnableSessionEvents", Field, 23}, + {"QUICConfig.TLSConfig", Field, 21}, + {"QUICConn", Type, 21}, + {"QUICEncryptionLevel", Type, 21}, + {"QUICEncryptionLevelApplication", Const, 21}, + {"QUICEncryptionLevelEarly", Const, 21}, + {"QUICEncryptionLevelHandshake", Const, 21}, + {"QUICEncryptionLevelInitial", Const, 21}, + {"QUICEvent", Type, 21}, + {"QUICEvent.Data", Field, 21}, + {"QUICEvent.Kind", Field, 21}, + {"QUICEvent.Level", Field, 21}, + {"QUICEvent.SessionState", Field, 23}, + {"QUICEvent.Suite", Field, 21}, + {"QUICEventKind", Type, 21}, + {"QUICHandshakeDone", Const, 21}, + {"QUICNoEvent", Const, 21}, + {"QUICRejectedEarlyData", Const, 21}, + {"QUICResumeSession", Const, 23}, + {"QUICServer", Func, 21}, + {"QUICSessionTicketOptions", Type, 21}, + {"QUICSessionTicketOptions.EarlyData", Field, 21}, + {"QUICSessionTicketOptions.Extra", Field, 23}, + {"QUICSetReadSecret", Const, 21}, + {"QUICSetWriteSecret", Const, 21}, + {"QUICStoreSession", Const, 23}, + {"QUICTransportParameters", Const, 21}, + {"QUICTransportParametersRequired", Const, 21}, + {"QUICWriteData", Const, 21}, + {"RecordHeaderError", Type, 6}, + {"RecordHeaderError.Conn", Field, 12}, + {"RecordHeaderError.Msg", Field, 6}, + {"RecordHeaderError.RecordHeader", Field, 6}, + {"RenegotiateFreelyAsClient", Const, 7}, + {"RenegotiateNever", Const, 7}, + {"RenegotiateOnceAsClient", Const, 7}, + {"RenegotiationSupport", Type, 7}, + {"RequestClientCert", Const, 0}, + {"RequireAndVerifyClientCert", Const, 0}, + {"RequireAnyClientCert", Const, 0}, + {"Server", Func, 0}, + {"SessionState", Type, 21}, + {"SessionState.EarlyData", Field, 21}, + {"SessionState.Extra", Field, 21}, + {"SignatureScheme", Type, 8}, + {"TLS_AES_128_GCM_SHA256", Const, 12}, + {"TLS_AES_256_GCM_SHA384", Const, 12}, + {"TLS_CHACHA20_POLY1305_SHA256", Const, 12}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", Const, 2}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", Const, 8}, + {"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", Const, 2}, + {"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", Const, 2}, + {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", Const, 5}, + {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", Const, 8}, + {"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", Const, 14}, + {"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", Const, 2}, + {"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", Const, 0}, + {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", Const, 0}, + {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", Const, 8}, + {"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", Const, 2}, + {"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", Const, 1}, + {"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", Const, 5}, + {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", Const, 8}, + {"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", Const, 14}, + {"TLS_ECDHE_RSA_WITH_RC4_128_SHA", Const, 0}, + {"TLS_FALLBACK_SCSV", Const, 4}, + {"TLS_RSA_WITH_3DES_EDE_CBC_SHA", Const, 0}, + {"TLS_RSA_WITH_AES_128_CBC_SHA", Const, 0}, + {"TLS_RSA_WITH_AES_128_CBC_SHA256", Const, 8}, + {"TLS_RSA_WITH_AES_128_GCM_SHA256", Const, 6}, + {"TLS_RSA_WITH_AES_256_CBC_SHA", Const, 1}, + {"TLS_RSA_WITH_AES_256_GCM_SHA384", Const, 6}, + {"TLS_RSA_WITH_RC4_128_SHA", Const, 0}, + {"VerifyClientCertIfGiven", Const, 0}, + {"VersionName", Func, 21}, + {"VersionSSL30", Const, 2}, + {"VersionTLS10", Const, 2}, + {"VersionTLS11", Const, 2}, + {"VersionTLS12", Const, 2}, + {"VersionTLS13", Const, 12}, + {"X25519", Const, 8}, + {"X509KeyPair", Func, 0}, + }, + "crypto/x509": { + {"(*CertPool).AddCert", Method, 0}, + {"(*CertPool).AddCertWithConstraint", Method, 22}, + {"(*CertPool).AppendCertsFromPEM", Method, 0}, + {"(*CertPool).Clone", Method, 19}, + {"(*CertPool).Equal", Method, 19}, + {"(*CertPool).Subjects", Method, 0}, + {"(*Certificate).CheckCRLSignature", Method, 0}, + {"(*Certificate).CheckSignature", Method, 0}, + {"(*Certificate).CheckSignatureFrom", Method, 0}, + {"(*Certificate).CreateCRL", Method, 0}, + {"(*Certificate).Equal", Method, 0}, + {"(*Certificate).Verify", Method, 0}, + {"(*Certificate).VerifyHostname", Method, 0}, + {"(*CertificateRequest).CheckSignature", Method, 5}, + {"(*OID).UnmarshalBinary", Method, 23}, + {"(*OID).UnmarshalText", Method, 23}, + {"(*RevocationList).CheckSignatureFrom", Method, 19}, + {"(CertificateInvalidError).Error", Method, 0}, + {"(ConstraintViolationError).Error", Method, 0}, + {"(HostnameError).Error", Method, 0}, + {"(InsecureAlgorithmError).Error", Method, 6}, + {"(OID).Equal", Method, 22}, + {"(OID).EqualASN1OID", Method, 22}, + {"(OID).MarshalBinary", Method, 23}, + {"(OID).MarshalText", Method, 23}, + {"(OID).String", Method, 22}, + {"(PublicKeyAlgorithm).String", Method, 10}, + {"(SignatureAlgorithm).String", Method, 6}, + {"(SystemRootsError).Error", Method, 1}, + {"(SystemRootsError).Unwrap", Method, 16}, + {"(UnhandledCriticalExtension).Error", Method, 0}, + {"(UnknownAuthorityError).Error", Method, 0}, + {"CANotAuthorizedForExtKeyUsage", Const, 10}, + {"CANotAuthorizedForThisName", Const, 0}, + {"CertPool", Type, 0}, + {"Certificate", Type, 0}, + {"Certificate.AuthorityKeyId", Field, 0}, + {"Certificate.BasicConstraintsValid", Field, 0}, + {"Certificate.CRLDistributionPoints", Field, 2}, + {"Certificate.DNSNames", Field, 0}, + {"Certificate.EmailAddresses", Field, 0}, + {"Certificate.ExcludedDNSDomains", Field, 9}, + {"Certificate.ExcludedEmailAddresses", Field, 10}, + {"Certificate.ExcludedIPRanges", Field, 10}, + {"Certificate.ExcludedURIDomains", Field, 10}, + {"Certificate.ExtKeyUsage", Field, 0}, + {"Certificate.Extensions", Field, 2}, + {"Certificate.ExtraExtensions", Field, 2}, + {"Certificate.IPAddresses", Field, 1}, + {"Certificate.IsCA", Field, 0}, + {"Certificate.Issuer", Field, 0}, + {"Certificate.IssuingCertificateURL", Field, 2}, + {"Certificate.KeyUsage", Field, 0}, + {"Certificate.MaxPathLen", Field, 0}, + {"Certificate.MaxPathLenZero", Field, 4}, + {"Certificate.NotAfter", Field, 0}, + {"Certificate.NotBefore", Field, 0}, + {"Certificate.OCSPServer", Field, 2}, + {"Certificate.PermittedDNSDomains", Field, 0}, + {"Certificate.PermittedDNSDomainsCritical", Field, 0}, + {"Certificate.PermittedEmailAddresses", Field, 10}, + {"Certificate.PermittedIPRanges", Field, 10}, + {"Certificate.PermittedURIDomains", Field, 10}, + {"Certificate.Policies", Field, 22}, + {"Certificate.PolicyIdentifiers", Field, 0}, + {"Certificate.PublicKey", Field, 0}, + {"Certificate.PublicKeyAlgorithm", Field, 0}, + {"Certificate.Raw", Field, 0}, + {"Certificate.RawIssuer", Field, 0}, + {"Certificate.RawSubject", Field, 0}, + {"Certificate.RawSubjectPublicKeyInfo", Field, 0}, + {"Certificate.RawTBSCertificate", Field, 0}, + {"Certificate.SerialNumber", Field, 0}, + {"Certificate.Signature", Field, 0}, + {"Certificate.SignatureAlgorithm", Field, 0}, + {"Certificate.Subject", Field, 0}, + {"Certificate.SubjectKeyId", Field, 0}, + {"Certificate.URIs", Field, 10}, + {"Certificate.UnhandledCriticalExtensions", Field, 5}, + {"Certificate.UnknownExtKeyUsage", Field, 0}, + {"Certificate.Version", Field, 0}, + {"CertificateInvalidError", Type, 0}, + {"CertificateInvalidError.Cert", Field, 0}, + {"CertificateInvalidError.Detail", Field, 10}, + {"CertificateInvalidError.Reason", Field, 0}, + {"CertificateRequest", Type, 3}, + {"CertificateRequest.Attributes", Field, 3}, + {"CertificateRequest.DNSNames", Field, 3}, + {"CertificateRequest.EmailAddresses", Field, 3}, + {"CertificateRequest.Extensions", Field, 3}, + {"CertificateRequest.ExtraExtensions", Field, 3}, + {"CertificateRequest.IPAddresses", Field, 3}, + {"CertificateRequest.PublicKey", Field, 3}, + {"CertificateRequest.PublicKeyAlgorithm", Field, 3}, + {"CertificateRequest.Raw", Field, 3}, + {"CertificateRequest.RawSubject", Field, 3}, + {"CertificateRequest.RawSubjectPublicKeyInfo", Field, 3}, + {"CertificateRequest.RawTBSCertificateRequest", Field, 3}, + {"CertificateRequest.Signature", Field, 3}, + {"CertificateRequest.SignatureAlgorithm", Field, 3}, + {"CertificateRequest.Subject", Field, 3}, + {"CertificateRequest.URIs", Field, 10}, + {"CertificateRequest.Version", Field, 3}, + {"ConstraintViolationError", Type, 0}, + {"CreateCertificate", Func, 0}, + {"CreateCertificateRequest", Func, 3}, + {"CreateRevocationList", Func, 15}, + {"DSA", Const, 0}, + {"DSAWithSHA1", Const, 0}, + {"DSAWithSHA256", Const, 0}, + {"DecryptPEMBlock", Func, 1}, + {"ECDSA", Const, 1}, + {"ECDSAWithSHA1", Const, 1}, + {"ECDSAWithSHA256", Const, 1}, + {"ECDSAWithSHA384", Const, 1}, + {"ECDSAWithSHA512", Const, 1}, + {"Ed25519", Const, 13}, + {"EncryptPEMBlock", Func, 1}, + {"ErrUnsupportedAlgorithm", Var, 0}, + {"Expired", Const, 0}, + {"ExtKeyUsage", Type, 0}, + {"ExtKeyUsageAny", Const, 0}, + {"ExtKeyUsageClientAuth", Const, 0}, + {"ExtKeyUsageCodeSigning", Const, 0}, + {"ExtKeyUsageEmailProtection", Const, 0}, + {"ExtKeyUsageIPSECEndSystem", Const, 1}, + {"ExtKeyUsageIPSECTunnel", Const, 1}, + {"ExtKeyUsageIPSECUser", Const, 1}, + {"ExtKeyUsageMicrosoftCommercialCodeSigning", Const, 10}, + {"ExtKeyUsageMicrosoftKernelCodeSigning", Const, 10}, + {"ExtKeyUsageMicrosoftServerGatedCrypto", Const, 1}, + {"ExtKeyUsageNetscapeServerGatedCrypto", Const, 1}, + {"ExtKeyUsageOCSPSigning", Const, 0}, + {"ExtKeyUsageServerAuth", Const, 0}, + {"ExtKeyUsageTimeStamping", Const, 0}, + {"HostnameError", Type, 0}, + {"HostnameError.Certificate", Field, 0}, + {"HostnameError.Host", Field, 0}, + {"IncompatibleUsage", Const, 1}, + {"IncorrectPasswordError", Var, 1}, + {"InsecureAlgorithmError", Type, 6}, + {"InvalidReason", Type, 0}, + {"IsEncryptedPEMBlock", Func, 1}, + {"KeyUsage", Type, 0}, + {"KeyUsageCRLSign", Const, 0}, + {"KeyUsageCertSign", Const, 0}, + {"KeyUsageContentCommitment", Const, 0}, + {"KeyUsageDataEncipherment", Const, 0}, + {"KeyUsageDecipherOnly", Const, 0}, + {"KeyUsageDigitalSignature", Const, 0}, + {"KeyUsageEncipherOnly", Const, 0}, + {"KeyUsageKeyAgreement", Const, 0}, + {"KeyUsageKeyEncipherment", Const, 0}, + {"MD2WithRSA", Const, 0}, + {"MD5WithRSA", Const, 0}, + {"MarshalECPrivateKey", Func, 2}, + {"MarshalPKCS1PrivateKey", Func, 0}, + {"MarshalPKCS1PublicKey", Func, 10}, + {"MarshalPKCS8PrivateKey", Func, 10}, + {"MarshalPKIXPublicKey", Func, 0}, + {"NameConstraintsWithoutSANs", Const, 10}, + {"NameMismatch", Const, 8}, + {"NewCertPool", Func, 0}, + {"NotAuthorizedToSign", Const, 0}, + {"OID", Type, 22}, + {"OIDFromInts", Func, 22}, + {"PEMCipher", Type, 1}, + {"PEMCipher3DES", Const, 1}, + {"PEMCipherAES128", Const, 1}, + {"PEMCipherAES192", Const, 1}, + {"PEMCipherAES256", Const, 1}, + {"PEMCipherDES", Const, 1}, + {"ParseCRL", Func, 0}, + {"ParseCertificate", Func, 0}, + {"ParseCertificateRequest", Func, 3}, + {"ParseCertificates", Func, 0}, + {"ParseDERCRL", Func, 0}, + {"ParseECPrivateKey", Func, 1}, + {"ParseOID", Func, 23}, + {"ParsePKCS1PrivateKey", Func, 0}, + {"ParsePKCS1PublicKey", Func, 10}, + {"ParsePKCS8PrivateKey", Func, 0}, + {"ParsePKIXPublicKey", Func, 0}, + {"ParseRevocationList", Func, 19}, + {"PublicKeyAlgorithm", Type, 0}, + {"PureEd25519", Const, 13}, + {"RSA", Const, 0}, + {"RevocationList", Type, 15}, + {"RevocationList.AuthorityKeyId", Field, 19}, + {"RevocationList.Extensions", Field, 19}, + {"RevocationList.ExtraExtensions", Field, 15}, + {"RevocationList.Issuer", Field, 19}, + {"RevocationList.NextUpdate", Field, 15}, + {"RevocationList.Number", Field, 15}, + {"RevocationList.Raw", Field, 19}, + {"RevocationList.RawIssuer", Field, 19}, + {"RevocationList.RawTBSRevocationList", Field, 19}, + {"RevocationList.RevokedCertificateEntries", Field, 21}, + {"RevocationList.RevokedCertificates", Field, 15}, + {"RevocationList.Signature", Field, 19}, + {"RevocationList.SignatureAlgorithm", Field, 15}, + {"RevocationList.ThisUpdate", Field, 15}, + {"RevocationListEntry", Type, 21}, + {"RevocationListEntry.Extensions", Field, 21}, + {"RevocationListEntry.ExtraExtensions", Field, 21}, + {"RevocationListEntry.Raw", Field, 21}, + {"RevocationListEntry.ReasonCode", Field, 21}, + {"RevocationListEntry.RevocationTime", Field, 21}, + {"RevocationListEntry.SerialNumber", Field, 21}, + {"SHA1WithRSA", Const, 0}, + {"SHA256WithRSA", Const, 0}, + {"SHA256WithRSAPSS", Const, 8}, + {"SHA384WithRSA", Const, 0}, + {"SHA384WithRSAPSS", Const, 8}, + {"SHA512WithRSA", Const, 0}, + {"SHA512WithRSAPSS", Const, 8}, + {"SetFallbackRoots", Func, 20}, + {"SignatureAlgorithm", Type, 0}, + {"SystemCertPool", Func, 7}, + {"SystemRootsError", Type, 1}, + {"SystemRootsError.Err", Field, 7}, + {"TooManyConstraints", Const, 10}, + {"TooManyIntermediates", Const, 0}, + {"UnconstrainedName", Const, 10}, + {"UnhandledCriticalExtension", Type, 0}, + {"UnknownAuthorityError", Type, 0}, + {"UnknownAuthorityError.Cert", Field, 8}, + {"UnknownPublicKeyAlgorithm", Const, 0}, + {"UnknownSignatureAlgorithm", Const, 0}, + {"VerifyOptions", Type, 0}, + {"VerifyOptions.CurrentTime", Field, 0}, + {"VerifyOptions.DNSName", Field, 0}, + {"VerifyOptions.Intermediates", Field, 0}, + {"VerifyOptions.KeyUsages", Field, 1}, + {"VerifyOptions.MaxConstraintComparisions", Field, 10}, + {"VerifyOptions.Roots", Field, 0}, + }, + "crypto/x509/pkix": { + {"(*CertificateList).HasExpired", Method, 0}, + {"(*Name).FillFromRDNSequence", Method, 0}, + {"(Name).String", Method, 10}, + {"(Name).ToRDNSequence", Method, 0}, + {"(RDNSequence).String", Method, 10}, + {"AlgorithmIdentifier", Type, 0}, + {"AlgorithmIdentifier.Algorithm", Field, 0}, + {"AlgorithmIdentifier.Parameters", Field, 0}, + {"AttributeTypeAndValue", Type, 0}, + {"AttributeTypeAndValue.Type", Field, 0}, + {"AttributeTypeAndValue.Value", Field, 0}, + {"AttributeTypeAndValueSET", Type, 3}, + {"AttributeTypeAndValueSET.Type", Field, 3}, + {"AttributeTypeAndValueSET.Value", Field, 3}, + {"CertificateList", Type, 0}, + {"CertificateList.SignatureAlgorithm", Field, 0}, + {"CertificateList.SignatureValue", Field, 0}, + {"CertificateList.TBSCertList", Field, 0}, + {"Extension", Type, 0}, + {"Extension.Critical", Field, 0}, + {"Extension.Id", Field, 0}, + {"Extension.Value", Field, 0}, + {"Name", Type, 0}, + {"Name.CommonName", Field, 0}, + {"Name.Country", Field, 0}, + {"Name.ExtraNames", Field, 5}, + {"Name.Locality", Field, 0}, + {"Name.Names", Field, 0}, + {"Name.Organization", Field, 0}, + {"Name.OrganizationalUnit", Field, 0}, + {"Name.PostalCode", Field, 0}, + {"Name.Province", Field, 0}, + {"Name.SerialNumber", Field, 0}, + {"Name.StreetAddress", Field, 0}, + {"RDNSequence", Type, 0}, + {"RelativeDistinguishedNameSET", Type, 0}, + {"RevokedCertificate", Type, 0}, + {"RevokedCertificate.Extensions", Field, 0}, + {"RevokedCertificate.RevocationTime", Field, 0}, + {"RevokedCertificate.SerialNumber", Field, 0}, + {"TBSCertificateList", Type, 0}, + {"TBSCertificateList.Extensions", Field, 0}, + {"TBSCertificateList.Issuer", Field, 0}, + {"TBSCertificateList.NextUpdate", Field, 0}, + {"TBSCertificateList.Raw", Field, 0}, + {"TBSCertificateList.RevokedCertificates", Field, 0}, + {"TBSCertificateList.Signature", Field, 0}, + {"TBSCertificateList.ThisUpdate", Field, 0}, + {"TBSCertificateList.Version", Field, 0}, + }, + "database/sql": { + {"(*ColumnType).DatabaseTypeName", Method, 8}, + {"(*ColumnType).DecimalSize", Method, 8}, + {"(*ColumnType).Length", Method, 8}, + {"(*ColumnType).Name", Method, 8}, + {"(*ColumnType).Nullable", Method, 8}, + {"(*ColumnType).ScanType", Method, 8}, + {"(*Conn).BeginTx", Method, 9}, + {"(*Conn).Close", Method, 9}, + {"(*Conn).ExecContext", Method, 9}, + {"(*Conn).PingContext", Method, 9}, + {"(*Conn).PrepareContext", Method, 9}, + {"(*Conn).QueryContext", Method, 9}, + {"(*Conn).QueryRowContext", Method, 9}, + {"(*Conn).Raw", Method, 13}, + {"(*DB).Begin", Method, 0}, + {"(*DB).BeginTx", Method, 8}, + {"(*DB).Close", Method, 0}, + {"(*DB).Conn", Method, 9}, + {"(*DB).Driver", Method, 0}, + {"(*DB).Exec", Method, 0}, + {"(*DB).ExecContext", Method, 8}, + {"(*DB).Ping", Method, 1}, + {"(*DB).PingContext", Method, 8}, + {"(*DB).Prepare", Method, 0}, + {"(*DB).PrepareContext", Method, 8}, + {"(*DB).Query", Method, 0}, + {"(*DB).QueryContext", Method, 8}, + {"(*DB).QueryRow", Method, 0}, + {"(*DB).QueryRowContext", Method, 8}, + {"(*DB).SetConnMaxIdleTime", Method, 15}, + {"(*DB).SetConnMaxLifetime", Method, 6}, + {"(*DB).SetMaxIdleConns", Method, 1}, + {"(*DB).SetMaxOpenConns", Method, 2}, + {"(*DB).Stats", Method, 5}, + {"(*Null).Scan", Method, 22}, + {"(*NullBool).Scan", Method, 0}, + {"(*NullByte).Scan", Method, 17}, + {"(*NullFloat64).Scan", Method, 0}, + {"(*NullInt16).Scan", Method, 17}, + {"(*NullInt32).Scan", Method, 13}, + {"(*NullInt64).Scan", Method, 0}, + {"(*NullString).Scan", Method, 0}, + {"(*NullTime).Scan", Method, 13}, + {"(*Row).Err", Method, 15}, + {"(*Row).Scan", Method, 0}, + {"(*Rows).Close", Method, 0}, + {"(*Rows).ColumnTypes", Method, 8}, + {"(*Rows).Columns", Method, 0}, + {"(*Rows).Err", Method, 0}, + {"(*Rows).Next", Method, 0}, + {"(*Rows).NextResultSet", Method, 8}, + {"(*Rows).Scan", Method, 0}, + {"(*Stmt).Close", Method, 0}, + {"(*Stmt).Exec", Method, 0}, + {"(*Stmt).ExecContext", Method, 8}, + {"(*Stmt).Query", Method, 0}, + {"(*Stmt).QueryContext", Method, 8}, + {"(*Stmt).QueryRow", Method, 0}, + {"(*Stmt).QueryRowContext", Method, 8}, + {"(*Tx).Commit", Method, 0}, + {"(*Tx).Exec", Method, 0}, + {"(*Tx).ExecContext", Method, 8}, + {"(*Tx).Prepare", Method, 0}, + {"(*Tx).PrepareContext", Method, 8}, + {"(*Tx).Query", Method, 0}, + {"(*Tx).QueryContext", Method, 8}, + {"(*Tx).QueryRow", Method, 0}, + {"(*Tx).QueryRowContext", Method, 8}, + {"(*Tx).Rollback", Method, 0}, + {"(*Tx).Stmt", Method, 0}, + {"(*Tx).StmtContext", Method, 8}, + {"(IsolationLevel).String", Method, 11}, + {"(Null).Value", Method, 22}, + {"(NullBool).Value", Method, 0}, + {"(NullByte).Value", Method, 17}, + {"(NullFloat64).Value", Method, 0}, + {"(NullInt16).Value", Method, 17}, + {"(NullInt32).Value", Method, 13}, + {"(NullInt64).Value", Method, 0}, + {"(NullString).Value", Method, 0}, + {"(NullTime).Value", Method, 13}, + {"ColumnType", Type, 8}, + {"Conn", Type, 9}, + {"DB", Type, 0}, + {"DBStats", Type, 5}, + {"DBStats.Idle", Field, 11}, + {"DBStats.InUse", Field, 11}, + {"DBStats.MaxIdleClosed", Field, 11}, + {"DBStats.MaxIdleTimeClosed", Field, 15}, + {"DBStats.MaxLifetimeClosed", Field, 11}, + {"DBStats.MaxOpenConnections", Field, 11}, + {"DBStats.OpenConnections", Field, 5}, + {"DBStats.WaitCount", Field, 11}, + {"DBStats.WaitDuration", Field, 11}, + {"Drivers", Func, 4}, + {"ErrConnDone", Var, 9}, + {"ErrNoRows", Var, 0}, + {"ErrTxDone", Var, 0}, + {"IsolationLevel", Type, 8}, + {"LevelDefault", Const, 8}, + {"LevelLinearizable", Const, 8}, + {"LevelReadCommitted", Const, 8}, + {"LevelReadUncommitted", Const, 8}, + {"LevelRepeatableRead", Const, 8}, + {"LevelSerializable", Const, 8}, + {"LevelSnapshot", Const, 8}, + {"LevelWriteCommitted", Const, 8}, + {"Named", Func, 8}, + {"NamedArg", Type, 8}, + {"NamedArg.Name", Field, 8}, + {"NamedArg.Value", Field, 8}, + {"Null", Type, 22}, + {"Null.V", Field, 22}, + {"Null.Valid", Field, 22}, + {"NullBool", Type, 0}, + {"NullBool.Bool", Field, 0}, + {"NullBool.Valid", Field, 0}, + {"NullByte", Type, 17}, + {"NullByte.Byte", Field, 17}, + {"NullByte.Valid", Field, 17}, + {"NullFloat64", Type, 0}, + {"NullFloat64.Float64", Field, 0}, + {"NullFloat64.Valid", Field, 0}, + {"NullInt16", Type, 17}, + {"NullInt16.Int16", Field, 17}, + {"NullInt16.Valid", Field, 17}, + {"NullInt32", Type, 13}, + {"NullInt32.Int32", Field, 13}, + {"NullInt32.Valid", Field, 13}, + {"NullInt64", Type, 0}, + {"NullInt64.Int64", Field, 0}, + {"NullInt64.Valid", Field, 0}, + {"NullString", Type, 0}, + {"NullString.String", Field, 0}, + {"NullString.Valid", Field, 0}, + {"NullTime", Type, 13}, + {"NullTime.Time", Field, 13}, + {"NullTime.Valid", Field, 13}, + {"Open", Func, 0}, + {"OpenDB", Func, 10}, + {"Out", Type, 9}, + {"Out.Dest", Field, 9}, + {"Out.In", Field, 9}, + {"RawBytes", Type, 0}, + {"Register", Func, 0}, + {"Result", Type, 0}, + {"Row", Type, 0}, + {"Rows", Type, 0}, + {"Scanner", Type, 0}, + {"Stmt", Type, 0}, + {"Tx", Type, 0}, + {"TxOptions", Type, 8}, + {"TxOptions.Isolation", Field, 8}, + {"TxOptions.ReadOnly", Field, 8}, + }, + "database/sql/driver": { + {"(NotNull).ConvertValue", Method, 0}, + {"(Null).ConvertValue", Method, 0}, + {"(RowsAffected).LastInsertId", Method, 0}, + {"(RowsAffected).RowsAffected", Method, 0}, + {"Bool", Var, 0}, + {"ColumnConverter", Type, 0}, + {"Conn", Type, 0}, + {"ConnBeginTx", Type, 8}, + {"ConnPrepareContext", Type, 8}, + {"Connector", Type, 10}, + {"DefaultParameterConverter", Var, 0}, + {"Driver", Type, 0}, + {"DriverContext", Type, 10}, + {"ErrBadConn", Var, 0}, + {"ErrRemoveArgument", Var, 9}, + {"ErrSkip", Var, 0}, + {"Execer", Type, 0}, + {"ExecerContext", Type, 8}, + {"Int32", Var, 0}, + {"IsScanValue", Func, 0}, + {"IsValue", Func, 0}, + {"IsolationLevel", Type, 8}, + {"NamedValue", Type, 8}, + {"NamedValue.Name", Field, 8}, + {"NamedValue.Ordinal", Field, 8}, + {"NamedValue.Value", Field, 8}, + {"NamedValueChecker", Type, 9}, + {"NotNull", Type, 0}, + {"NotNull.Converter", Field, 0}, + {"Null", Type, 0}, + {"Null.Converter", Field, 0}, + {"Pinger", Type, 8}, + {"Queryer", Type, 1}, + {"QueryerContext", Type, 8}, + {"Result", Type, 0}, + {"ResultNoRows", Var, 0}, + {"Rows", Type, 0}, + {"RowsAffected", Type, 0}, + {"RowsColumnTypeDatabaseTypeName", Type, 8}, + {"RowsColumnTypeLength", Type, 8}, + {"RowsColumnTypeNullable", Type, 8}, + {"RowsColumnTypePrecisionScale", Type, 8}, + {"RowsColumnTypeScanType", Type, 8}, + {"RowsNextResultSet", Type, 8}, + {"SessionResetter", Type, 10}, + {"Stmt", Type, 0}, + {"StmtExecContext", Type, 8}, + {"StmtQueryContext", Type, 8}, + {"String", Var, 0}, + {"Tx", Type, 0}, + {"TxOptions", Type, 8}, + {"TxOptions.Isolation", Field, 8}, + {"TxOptions.ReadOnly", Field, 8}, + {"Validator", Type, 15}, + {"Value", Type, 0}, + {"ValueConverter", Type, 0}, + {"Valuer", Type, 0}, + }, + "debug/buildinfo": { + {"BuildInfo", Type, 18}, + {"Read", Func, 18}, + {"ReadFile", Func, 18}, + }, + "debug/dwarf": { + {"(*AddrType).Basic", Method, 0}, + {"(*AddrType).Common", Method, 0}, + {"(*AddrType).Size", Method, 0}, + {"(*AddrType).String", Method, 0}, + {"(*ArrayType).Common", Method, 0}, + {"(*ArrayType).Size", Method, 0}, + {"(*ArrayType).String", Method, 0}, + {"(*BasicType).Basic", Method, 0}, + {"(*BasicType).Common", Method, 0}, + {"(*BasicType).Size", Method, 0}, + {"(*BasicType).String", Method, 0}, + {"(*BoolType).Basic", Method, 0}, + {"(*BoolType).Common", Method, 0}, + {"(*BoolType).Size", Method, 0}, + {"(*BoolType).String", Method, 0}, + {"(*CharType).Basic", Method, 0}, + {"(*CharType).Common", Method, 0}, + {"(*CharType).Size", Method, 0}, + {"(*CharType).String", Method, 0}, + {"(*CommonType).Common", Method, 0}, + {"(*CommonType).Size", Method, 0}, + {"(*ComplexType).Basic", Method, 0}, + {"(*ComplexType).Common", Method, 0}, + {"(*ComplexType).Size", Method, 0}, + {"(*ComplexType).String", Method, 0}, + {"(*Data).AddSection", Method, 14}, + {"(*Data).AddTypes", Method, 3}, + {"(*Data).LineReader", Method, 5}, + {"(*Data).Ranges", Method, 7}, + {"(*Data).Reader", Method, 0}, + {"(*Data).Type", Method, 0}, + {"(*DotDotDotType).Common", Method, 0}, + {"(*DotDotDotType).Size", Method, 0}, + {"(*DotDotDotType).String", Method, 0}, + {"(*Entry).AttrField", Method, 5}, + {"(*Entry).Val", Method, 0}, + {"(*EnumType).Common", Method, 0}, + {"(*EnumType).Size", Method, 0}, + {"(*EnumType).String", Method, 0}, + {"(*FloatType).Basic", Method, 0}, + {"(*FloatType).Common", Method, 0}, + {"(*FloatType).Size", Method, 0}, + {"(*FloatType).String", Method, 0}, + {"(*FuncType).Common", Method, 0}, + {"(*FuncType).Size", Method, 0}, + {"(*FuncType).String", Method, 0}, + {"(*IntType).Basic", Method, 0}, + {"(*IntType).Common", Method, 0}, + {"(*IntType).Size", Method, 0}, + {"(*IntType).String", Method, 0}, + {"(*LineReader).Files", Method, 14}, + {"(*LineReader).Next", Method, 5}, + {"(*LineReader).Reset", Method, 5}, + {"(*LineReader).Seek", Method, 5}, + {"(*LineReader).SeekPC", Method, 5}, + {"(*LineReader).Tell", Method, 5}, + {"(*PtrType).Common", Method, 0}, + {"(*PtrType).Size", Method, 0}, + {"(*PtrType).String", Method, 0}, + {"(*QualType).Common", Method, 0}, + {"(*QualType).Size", Method, 0}, + {"(*QualType).String", Method, 0}, + {"(*Reader).AddressSize", Method, 5}, + {"(*Reader).ByteOrder", Method, 14}, + {"(*Reader).Next", Method, 0}, + {"(*Reader).Seek", Method, 0}, + {"(*Reader).SeekPC", Method, 7}, + {"(*Reader).SkipChildren", Method, 0}, + {"(*StructType).Common", Method, 0}, + {"(*StructType).Defn", Method, 0}, + {"(*StructType).Size", Method, 0}, + {"(*StructType).String", Method, 0}, + {"(*TypedefType).Common", Method, 0}, + {"(*TypedefType).Size", Method, 0}, + {"(*TypedefType).String", Method, 0}, + {"(*UcharType).Basic", Method, 0}, + {"(*UcharType).Common", Method, 0}, + {"(*UcharType).Size", Method, 0}, + {"(*UcharType).String", Method, 0}, + {"(*UintType).Basic", Method, 0}, + {"(*UintType).Common", Method, 0}, + {"(*UintType).Size", Method, 0}, + {"(*UintType).String", Method, 0}, + {"(*UnspecifiedType).Basic", Method, 4}, + {"(*UnspecifiedType).Common", Method, 4}, + {"(*UnspecifiedType).Size", Method, 4}, + {"(*UnspecifiedType).String", Method, 4}, + {"(*UnsupportedType).Common", Method, 13}, + {"(*UnsupportedType).Size", Method, 13}, + {"(*UnsupportedType).String", Method, 13}, + {"(*VoidType).Common", Method, 0}, + {"(*VoidType).Size", Method, 0}, + {"(*VoidType).String", Method, 0}, + {"(Attr).GoString", Method, 0}, + {"(Attr).String", Method, 0}, + {"(Class).GoString", Method, 5}, + {"(Class).String", Method, 5}, + {"(DecodeError).Error", Method, 0}, + {"(Tag).GoString", Method, 0}, + {"(Tag).String", Method, 0}, + {"AddrType", Type, 0}, + {"AddrType.BasicType", Field, 0}, + {"ArrayType", Type, 0}, + {"ArrayType.CommonType", Field, 0}, + {"ArrayType.Count", Field, 0}, + {"ArrayType.StrideBitSize", Field, 0}, + {"ArrayType.Type", Field, 0}, + {"Attr", Type, 0}, + {"AttrAbstractOrigin", Const, 0}, + {"AttrAccessibility", Const, 0}, + {"AttrAddrBase", Const, 14}, + {"AttrAddrClass", Const, 0}, + {"AttrAlignment", Const, 14}, + {"AttrAllocated", Const, 0}, + {"AttrArtificial", Const, 0}, + {"AttrAssociated", Const, 0}, + {"AttrBaseTypes", Const, 0}, + {"AttrBinaryScale", Const, 14}, + {"AttrBitOffset", Const, 0}, + {"AttrBitSize", Const, 0}, + {"AttrByteSize", Const, 0}, + {"AttrCallAllCalls", Const, 14}, + {"AttrCallAllSourceCalls", Const, 14}, + {"AttrCallAllTailCalls", Const, 14}, + {"AttrCallColumn", Const, 0}, + {"AttrCallDataLocation", Const, 14}, + {"AttrCallDataValue", Const, 14}, + {"AttrCallFile", Const, 0}, + {"AttrCallLine", Const, 0}, + {"AttrCallOrigin", Const, 14}, + {"AttrCallPC", Const, 14}, + {"AttrCallParameter", Const, 14}, + {"AttrCallReturnPC", Const, 14}, + {"AttrCallTailCall", Const, 14}, + {"AttrCallTarget", Const, 14}, + {"AttrCallTargetClobbered", Const, 14}, + {"AttrCallValue", Const, 14}, + {"AttrCalling", Const, 0}, + {"AttrCommonRef", Const, 0}, + {"AttrCompDir", Const, 0}, + {"AttrConstExpr", Const, 14}, + {"AttrConstValue", Const, 0}, + {"AttrContainingType", Const, 0}, + {"AttrCount", Const, 0}, + {"AttrDataBitOffset", Const, 14}, + {"AttrDataLocation", Const, 0}, + {"AttrDataMemberLoc", Const, 0}, + {"AttrDecimalScale", Const, 14}, + {"AttrDecimalSign", Const, 14}, + {"AttrDeclColumn", Const, 0}, + {"AttrDeclFile", Const, 0}, + {"AttrDeclLine", Const, 0}, + {"AttrDeclaration", Const, 0}, + {"AttrDefaultValue", Const, 0}, + {"AttrDefaulted", Const, 14}, + {"AttrDeleted", Const, 14}, + {"AttrDescription", Const, 0}, + {"AttrDigitCount", Const, 14}, + {"AttrDiscr", Const, 0}, + {"AttrDiscrList", Const, 0}, + {"AttrDiscrValue", Const, 0}, + {"AttrDwoName", Const, 14}, + {"AttrElemental", Const, 14}, + {"AttrEncoding", Const, 0}, + {"AttrEndianity", Const, 14}, + {"AttrEntrypc", Const, 0}, + {"AttrEnumClass", Const, 14}, + {"AttrExplicit", Const, 14}, + {"AttrExportSymbols", Const, 14}, + {"AttrExtension", Const, 0}, + {"AttrExternal", Const, 0}, + {"AttrFrameBase", Const, 0}, + {"AttrFriend", Const, 0}, + {"AttrHighpc", Const, 0}, + {"AttrIdentifierCase", Const, 0}, + {"AttrImport", Const, 0}, + {"AttrInline", Const, 0}, + {"AttrIsOptional", Const, 0}, + {"AttrLanguage", Const, 0}, + {"AttrLinkageName", Const, 14}, + {"AttrLocation", Const, 0}, + {"AttrLoclistsBase", Const, 14}, + {"AttrLowerBound", Const, 0}, + {"AttrLowpc", Const, 0}, + {"AttrMacroInfo", Const, 0}, + {"AttrMacros", Const, 14}, + {"AttrMainSubprogram", Const, 14}, + {"AttrMutable", Const, 14}, + {"AttrName", Const, 0}, + {"AttrNamelistItem", Const, 0}, + {"AttrNoreturn", Const, 14}, + {"AttrObjectPointer", Const, 14}, + {"AttrOrdering", Const, 0}, + {"AttrPictureString", Const, 14}, + {"AttrPriority", Const, 0}, + {"AttrProducer", Const, 0}, + {"AttrPrototyped", Const, 0}, + {"AttrPure", Const, 14}, + {"AttrRanges", Const, 0}, + {"AttrRank", Const, 14}, + {"AttrRecursive", Const, 14}, + {"AttrReference", Const, 14}, + {"AttrReturnAddr", Const, 0}, + {"AttrRnglistsBase", Const, 14}, + {"AttrRvalueReference", Const, 14}, + {"AttrSegment", Const, 0}, + {"AttrSibling", Const, 0}, + {"AttrSignature", Const, 14}, + {"AttrSmall", Const, 14}, + {"AttrSpecification", Const, 0}, + {"AttrStartScope", Const, 0}, + {"AttrStaticLink", Const, 0}, + {"AttrStmtList", Const, 0}, + {"AttrStrOffsetsBase", Const, 14}, + {"AttrStride", Const, 0}, + {"AttrStrideSize", Const, 0}, + {"AttrStringLength", Const, 0}, + {"AttrStringLengthBitSize", Const, 14}, + {"AttrStringLengthByteSize", Const, 14}, + {"AttrThreadsScaled", Const, 14}, + {"AttrTrampoline", Const, 0}, + {"AttrType", Const, 0}, + {"AttrUpperBound", Const, 0}, + {"AttrUseLocation", Const, 0}, + {"AttrUseUTF8", Const, 0}, + {"AttrVarParam", Const, 0}, + {"AttrVirtuality", Const, 0}, + {"AttrVisibility", Const, 0}, + {"AttrVtableElemLoc", Const, 0}, + {"BasicType", Type, 0}, + {"BasicType.BitOffset", Field, 0}, + {"BasicType.BitSize", Field, 0}, + {"BasicType.CommonType", Field, 0}, + {"BasicType.DataBitOffset", Field, 18}, + {"BoolType", Type, 0}, + {"BoolType.BasicType", Field, 0}, + {"CharType", Type, 0}, + {"CharType.BasicType", Field, 0}, + {"Class", Type, 5}, + {"ClassAddrPtr", Const, 14}, + {"ClassAddress", Const, 5}, + {"ClassBlock", Const, 5}, + {"ClassConstant", Const, 5}, + {"ClassExprLoc", Const, 5}, + {"ClassFlag", Const, 5}, + {"ClassLinePtr", Const, 5}, + {"ClassLocList", Const, 14}, + {"ClassLocListPtr", Const, 5}, + {"ClassMacPtr", Const, 5}, + {"ClassRangeListPtr", Const, 5}, + {"ClassReference", Const, 5}, + {"ClassReferenceAlt", Const, 5}, + {"ClassReferenceSig", Const, 5}, + {"ClassRngList", Const, 14}, + {"ClassRngListsPtr", Const, 14}, + {"ClassStrOffsetsPtr", Const, 14}, + {"ClassString", Const, 5}, + {"ClassStringAlt", Const, 5}, + {"ClassUnknown", Const, 6}, + {"CommonType", Type, 0}, + {"CommonType.ByteSize", Field, 0}, + {"CommonType.Name", Field, 0}, + {"ComplexType", Type, 0}, + {"ComplexType.BasicType", Field, 0}, + {"Data", Type, 0}, + {"DecodeError", Type, 0}, + {"DecodeError.Err", Field, 0}, + {"DecodeError.Name", Field, 0}, + {"DecodeError.Offset", Field, 0}, + {"DotDotDotType", Type, 0}, + {"DotDotDotType.CommonType", Field, 0}, + {"Entry", Type, 0}, + {"Entry.Children", Field, 0}, + {"Entry.Field", Field, 0}, + {"Entry.Offset", Field, 0}, + {"Entry.Tag", Field, 0}, + {"EnumType", Type, 0}, + {"EnumType.CommonType", Field, 0}, + {"EnumType.EnumName", Field, 0}, + {"EnumType.Val", Field, 0}, + {"EnumValue", Type, 0}, + {"EnumValue.Name", Field, 0}, + {"EnumValue.Val", Field, 0}, + {"ErrUnknownPC", Var, 5}, + {"Field", Type, 0}, + {"Field.Attr", Field, 0}, + {"Field.Class", Field, 5}, + {"Field.Val", Field, 0}, + {"FloatType", Type, 0}, + {"FloatType.BasicType", Field, 0}, + {"FuncType", Type, 0}, + {"FuncType.CommonType", Field, 0}, + {"FuncType.ParamType", Field, 0}, + {"FuncType.ReturnType", Field, 0}, + {"IntType", Type, 0}, + {"IntType.BasicType", Field, 0}, + {"LineEntry", Type, 5}, + {"LineEntry.Address", Field, 5}, + {"LineEntry.BasicBlock", Field, 5}, + {"LineEntry.Column", Field, 5}, + {"LineEntry.Discriminator", Field, 5}, + {"LineEntry.EndSequence", Field, 5}, + {"LineEntry.EpilogueBegin", Field, 5}, + {"LineEntry.File", Field, 5}, + {"LineEntry.ISA", Field, 5}, + {"LineEntry.IsStmt", Field, 5}, + {"LineEntry.Line", Field, 5}, + {"LineEntry.OpIndex", Field, 5}, + {"LineEntry.PrologueEnd", Field, 5}, + {"LineFile", Type, 5}, + {"LineFile.Length", Field, 5}, + {"LineFile.Mtime", Field, 5}, + {"LineFile.Name", Field, 5}, + {"LineReader", Type, 5}, + {"LineReaderPos", Type, 5}, + {"New", Func, 0}, + {"Offset", Type, 0}, + {"PtrType", Type, 0}, + {"PtrType.CommonType", Field, 0}, + {"PtrType.Type", Field, 0}, + {"QualType", Type, 0}, + {"QualType.CommonType", Field, 0}, + {"QualType.Qual", Field, 0}, + {"QualType.Type", Field, 0}, + {"Reader", Type, 0}, + {"StructField", Type, 0}, + {"StructField.BitOffset", Field, 0}, + {"StructField.BitSize", Field, 0}, + {"StructField.ByteOffset", Field, 0}, + {"StructField.ByteSize", Field, 0}, + {"StructField.DataBitOffset", Field, 18}, + {"StructField.Name", Field, 0}, + {"StructField.Type", Field, 0}, + {"StructType", Type, 0}, + {"StructType.CommonType", Field, 0}, + {"StructType.Field", Field, 0}, + {"StructType.Incomplete", Field, 0}, + {"StructType.Kind", Field, 0}, + {"StructType.StructName", Field, 0}, + {"Tag", Type, 0}, + {"TagAccessDeclaration", Const, 0}, + {"TagArrayType", Const, 0}, + {"TagAtomicType", Const, 14}, + {"TagBaseType", Const, 0}, + {"TagCallSite", Const, 14}, + {"TagCallSiteParameter", Const, 14}, + {"TagCatchDwarfBlock", Const, 0}, + {"TagClassType", Const, 0}, + {"TagCoarrayType", Const, 14}, + {"TagCommonDwarfBlock", Const, 0}, + {"TagCommonInclusion", Const, 0}, + {"TagCompileUnit", Const, 0}, + {"TagCondition", Const, 3}, + {"TagConstType", Const, 0}, + {"TagConstant", Const, 0}, + {"TagDwarfProcedure", Const, 0}, + {"TagDynamicType", Const, 14}, + {"TagEntryPoint", Const, 0}, + {"TagEnumerationType", Const, 0}, + {"TagEnumerator", Const, 0}, + {"TagFileType", Const, 0}, + {"TagFormalParameter", Const, 0}, + {"TagFriend", Const, 0}, + {"TagGenericSubrange", Const, 14}, + {"TagImmutableType", Const, 14}, + {"TagImportedDeclaration", Const, 0}, + {"TagImportedModule", Const, 0}, + {"TagImportedUnit", Const, 0}, + {"TagInheritance", Const, 0}, + {"TagInlinedSubroutine", Const, 0}, + {"TagInterfaceType", Const, 0}, + {"TagLabel", Const, 0}, + {"TagLexDwarfBlock", Const, 0}, + {"TagMember", Const, 0}, + {"TagModule", Const, 0}, + {"TagMutableType", Const, 0}, + {"TagNamelist", Const, 0}, + {"TagNamelistItem", Const, 0}, + {"TagNamespace", Const, 0}, + {"TagPackedType", Const, 0}, + {"TagPartialUnit", Const, 0}, + {"TagPointerType", Const, 0}, + {"TagPtrToMemberType", Const, 0}, + {"TagReferenceType", Const, 0}, + {"TagRestrictType", Const, 0}, + {"TagRvalueReferenceType", Const, 3}, + {"TagSetType", Const, 0}, + {"TagSharedType", Const, 3}, + {"TagSkeletonUnit", Const, 14}, + {"TagStringType", Const, 0}, + {"TagStructType", Const, 0}, + {"TagSubprogram", Const, 0}, + {"TagSubrangeType", Const, 0}, + {"TagSubroutineType", Const, 0}, + {"TagTemplateAlias", Const, 3}, + {"TagTemplateTypeParameter", Const, 0}, + {"TagTemplateValueParameter", Const, 0}, + {"TagThrownType", Const, 0}, + {"TagTryDwarfBlock", Const, 0}, + {"TagTypeUnit", Const, 3}, + {"TagTypedef", Const, 0}, + {"TagUnionType", Const, 0}, + {"TagUnspecifiedParameters", Const, 0}, + {"TagUnspecifiedType", Const, 0}, + {"TagVariable", Const, 0}, + {"TagVariant", Const, 0}, + {"TagVariantPart", Const, 0}, + {"TagVolatileType", Const, 0}, + {"TagWithStmt", Const, 0}, + {"Type", Type, 0}, + {"TypedefType", Type, 0}, + {"TypedefType.CommonType", Field, 0}, + {"TypedefType.Type", Field, 0}, + {"UcharType", Type, 0}, + {"UcharType.BasicType", Field, 0}, + {"UintType", Type, 0}, + {"UintType.BasicType", Field, 0}, + {"UnspecifiedType", Type, 4}, + {"UnspecifiedType.BasicType", Field, 4}, + {"UnsupportedType", Type, 13}, + {"UnsupportedType.CommonType", Field, 13}, + {"UnsupportedType.Tag", Field, 13}, + {"VoidType", Type, 0}, + {"VoidType.CommonType", Field, 0}, + }, + "debug/elf": { + {"(*File).Close", Method, 0}, + {"(*File).DWARF", Method, 0}, + {"(*File).DynString", Method, 1}, + {"(*File).DynValue", Method, 21}, + {"(*File).DynamicSymbols", Method, 4}, + {"(*File).ImportedLibraries", Method, 0}, + {"(*File).ImportedSymbols", Method, 0}, + {"(*File).Section", Method, 0}, + {"(*File).SectionByType", Method, 0}, + {"(*File).Symbols", Method, 0}, + {"(*FormatError).Error", Method, 0}, + {"(*Prog).Open", Method, 0}, + {"(*Section).Data", Method, 0}, + {"(*Section).Open", Method, 0}, + {"(Class).GoString", Method, 0}, + {"(Class).String", Method, 0}, + {"(CompressionType).GoString", Method, 6}, + {"(CompressionType).String", Method, 6}, + {"(Data).GoString", Method, 0}, + {"(Data).String", Method, 0}, + {"(DynFlag).GoString", Method, 0}, + {"(DynFlag).String", Method, 0}, + {"(DynFlag1).GoString", Method, 21}, + {"(DynFlag1).String", Method, 21}, + {"(DynTag).GoString", Method, 0}, + {"(DynTag).String", Method, 0}, + {"(Machine).GoString", Method, 0}, + {"(Machine).String", Method, 0}, + {"(NType).GoString", Method, 0}, + {"(NType).String", Method, 0}, + {"(OSABI).GoString", Method, 0}, + {"(OSABI).String", Method, 0}, + {"(Prog).ReadAt", Method, 0}, + {"(ProgFlag).GoString", Method, 0}, + {"(ProgFlag).String", Method, 0}, + {"(ProgType).GoString", Method, 0}, + {"(ProgType).String", Method, 0}, + {"(R_386).GoString", Method, 0}, + {"(R_386).String", Method, 0}, + {"(R_390).GoString", Method, 7}, + {"(R_390).String", Method, 7}, + {"(R_AARCH64).GoString", Method, 4}, + {"(R_AARCH64).String", Method, 4}, + {"(R_ALPHA).GoString", Method, 0}, + {"(R_ALPHA).String", Method, 0}, + {"(R_ARM).GoString", Method, 0}, + {"(R_ARM).String", Method, 0}, + {"(R_LARCH).GoString", Method, 19}, + {"(R_LARCH).String", Method, 19}, + {"(R_MIPS).GoString", Method, 6}, + {"(R_MIPS).String", Method, 6}, + {"(R_PPC).GoString", Method, 0}, + {"(R_PPC).String", Method, 0}, + {"(R_PPC64).GoString", Method, 5}, + {"(R_PPC64).String", Method, 5}, + {"(R_RISCV).GoString", Method, 11}, + {"(R_RISCV).String", Method, 11}, + {"(R_SPARC).GoString", Method, 0}, + {"(R_SPARC).String", Method, 0}, + {"(R_X86_64).GoString", Method, 0}, + {"(R_X86_64).String", Method, 0}, + {"(Section).ReadAt", Method, 0}, + {"(SectionFlag).GoString", Method, 0}, + {"(SectionFlag).String", Method, 0}, + {"(SectionIndex).GoString", Method, 0}, + {"(SectionIndex).String", Method, 0}, + {"(SectionType).GoString", Method, 0}, + {"(SectionType).String", Method, 0}, + {"(SymBind).GoString", Method, 0}, + {"(SymBind).String", Method, 0}, + {"(SymType).GoString", Method, 0}, + {"(SymType).String", Method, 0}, + {"(SymVis).GoString", Method, 0}, + {"(SymVis).String", Method, 0}, + {"(Type).GoString", Method, 0}, + {"(Type).String", Method, 0}, + {"(Version).GoString", Method, 0}, + {"(Version).String", Method, 0}, + {"ARM_MAGIC_TRAMP_NUMBER", Const, 0}, + {"COMPRESS_HIOS", Const, 6}, + {"COMPRESS_HIPROC", Const, 6}, + {"COMPRESS_LOOS", Const, 6}, + {"COMPRESS_LOPROC", Const, 6}, + {"COMPRESS_ZLIB", Const, 6}, + {"COMPRESS_ZSTD", Const, 21}, + {"Chdr32", Type, 6}, + {"Chdr32.Addralign", Field, 6}, + {"Chdr32.Size", Field, 6}, + {"Chdr32.Type", Field, 6}, + {"Chdr64", Type, 6}, + {"Chdr64.Addralign", Field, 6}, + {"Chdr64.Size", Field, 6}, + {"Chdr64.Type", Field, 6}, + {"Class", Type, 0}, + {"CompressionType", Type, 6}, + {"DF_1_CONFALT", Const, 21}, + {"DF_1_DIRECT", Const, 21}, + {"DF_1_DISPRELDNE", Const, 21}, + {"DF_1_DISPRELPND", Const, 21}, + {"DF_1_EDITED", Const, 21}, + {"DF_1_ENDFILTEE", Const, 21}, + {"DF_1_GLOBAL", Const, 21}, + {"DF_1_GLOBAUDIT", Const, 21}, + {"DF_1_GROUP", Const, 21}, + {"DF_1_IGNMULDEF", Const, 21}, + {"DF_1_INITFIRST", Const, 21}, + {"DF_1_INTERPOSE", Const, 21}, + {"DF_1_KMOD", Const, 21}, + {"DF_1_LOADFLTR", Const, 21}, + {"DF_1_NOCOMMON", Const, 21}, + {"DF_1_NODEFLIB", Const, 21}, + {"DF_1_NODELETE", Const, 21}, + {"DF_1_NODIRECT", Const, 21}, + {"DF_1_NODUMP", Const, 21}, + {"DF_1_NOHDR", Const, 21}, + {"DF_1_NOKSYMS", Const, 21}, + {"DF_1_NOOPEN", Const, 21}, + {"DF_1_NORELOC", Const, 21}, + {"DF_1_NOW", Const, 21}, + {"DF_1_ORIGIN", Const, 21}, + {"DF_1_PIE", Const, 21}, + {"DF_1_SINGLETON", Const, 21}, + {"DF_1_STUB", Const, 21}, + {"DF_1_SYMINTPOSE", Const, 21}, + {"DF_1_TRANS", Const, 21}, + {"DF_1_WEAKFILTER", Const, 21}, + {"DF_BIND_NOW", Const, 0}, + {"DF_ORIGIN", Const, 0}, + {"DF_STATIC_TLS", Const, 0}, + {"DF_SYMBOLIC", Const, 0}, + {"DF_TEXTREL", Const, 0}, + {"DT_ADDRRNGHI", Const, 16}, + {"DT_ADDRRNGLO", Const, 16}, + {"DT_AUDIT", Const, 16}, + {"DT_AUXILIARY", Const, 16}, + {"DT_BIND_NOW", Const, 0}, + {"DT_CHECKSUM", Const, 16}, + {"DT_CONFIG", Const, 16}, + {"DT_DEBUG", Const, 0}, + {"DT_DEPAUDIT", Const, 16}, + {"DT_ENCODING", Const, 0}, + {"DT_FEATURE", Const, 16}, + {"DT_FILTER", Const, 16}, + {"DT_FINI", Const, 0}, + {"DT_FINI_ARRAY", Const, 0}, + {"DT_FINI_ARRAYSZ", Const, 0}, + {"DT_FLAGS", Const, 0}, + {"DT_FLAGS_1", Const, 16}, + {"DT_GNU_CONFLICT", Const, 16}, + {"DT_GNU_CONFLICTSZ", Const, 16}, + {"DT_GNU_HASH", Const, 16}, + {"DT_GNU_LIBLIST", Const, 16}, + {"DT_GNU_LIBLISTSZ", Const, 16}, + {"DT_GNU_PRELINKED", Const, 16}, + {"DT_HASH", Const, 0}, + {"DT_HIOS", Const, 0}, + {"DT_HIPROC", Const, 0}, + {"DT_INIT", Const, 0}, + {"DT_INIT_ARRAY", Const, 0}, + {"DT_INIT_ARRAYSZ", Const, 0}, + {"DT_JMPREL", Const, 0}, + {"DT_LOOS", Const, 0}, + {"DT_LOPROC", Const, 0}, + {"DT_MIPS_AUX_DYNAMIC", Const, 16}, + {"DT_MIPS_BASE_ADDRESS", Const, 16}, + {"DT_MIPS_COMPACT_SIZE", Const, 16}, + {"DT_MIPS_CONFLICT", Const, 16}, + {"DT_MIPS_CONFLICTNO", Const, 16}, + {"DT_MIPS_CXX_FLAGS", Const, 16}, + {"DT_MIPS_DELTA_CLASS", Const, 16}, + {"DT_MIPS_DELTA_CLASSSYM", Const, 16}, + {"DT_MIPS_DELTA_CLASSSYM_NO", Const, 16}, + {"DT_MIPS_DELTA_CLASS_NO", Const, 16}, + {"DT_MIPS_DELTA_INSTANCE", Const, 16}, + {"DT_MIPS_DELTA_INSTANCE_NO", Const, 16}, + {"DT_MIPS_DELTA_RELOC", Const, 16}, + {"DT_MIPS_DELTA_RELOC_NO", Const, 16}, + {"DT_MIPS_DELTA_SYM", Const, 16}, + {"DT_MIPS_DELTA_SYM_NO", Const, 16}, + {"DT_MIPS_DYNSTR_ALIGN", Const, 16}, + {"DT_MIPS_FLAGS", Const, 16}, + {"DT_MIPS_GOTSYM", Const, 16}, + {"DT_MIPS_GP_VALUE", Const, 16}, + {"DT_MIPS_HIDDEN_GOTIDX", Const, 16}, + {"DT_MIPS_HIPAGENO", Const, 16}, + {"DT_MIPS_ICHECKSUM", Const, 16}, + {"DT_MIPS_INTERFACE", Const, 16}, + {"DT_MIPS_INTERFACE_SIZE", Const, 16}, + {"DT_MIPS_IVERSION", Const, 16}, + {"DT_MIPS_LIBLIST", Const, 16}, + {"DT_MIPS_LIBLISTNO", Const, 16}, + {"DT_MIPS_LOCALPAGE_GOTIDX", Const, 16}, + {"DT_MIPS_LOCAL_GOTIDX", Const, 16}, + {"DT_MIPS_LOCAL_GOTNO", Const, 16}, + {"DT_MIPS_MSYM", Const, 16}, + {"DT_MIPS_OPTIONS", Const, 16}, + {"DT_MIPS_PERF_SUFFIX", Const, 16}, + {"DT_MIPS_PIXIE_INIT", Const, 16}, + {"DT_MIPS_PLTGOT", Const, 16}, + {"DT_MIPS_PROTECTED_GOTIDX", Const, 16}, + {"DT_MIPS_RLD_MAP", Const, 16}, + {"DT_MIPS_RLD_MAP_REL", Const, 16}, + {"DT_MIPS_RLD_TEXT_RESOLVE_ADDR", Const, 16}, + {"DT_MIPS_RLD_VERSION", Const, 16}, + {"DT_MIPS_RWPLT", Const, 16}, + {"DT_MIPS_SYMBOL_LIB", Const, 16}, + {"DT_MIPS_SYMTABNO", Const, 16}, + {"DT_MIPS_TIME_STAMP", Const, 16}, + {"DT_MIPS_UNREFEXTNO", Const, 16}, + {"DT_MOVEENT", Const, 16}, + {"DT_MOVESZ", Const, 16}, + {"DT_MOVETAB", Const, 16}, + {"DT_NEEDED", Const, 0}, + {"DT_NULL", Const, 0}, + {"DT_PLTGOT", Const, 0}, + {"DT_PLTPAD", Const, 16}, + {"DT_PLTPADSZ", Const, 16}, + {"DT_PLTREL", Const, 0}, + {"DT_PLTRELSZ", Const, 0}, + {"DT_POSFLAG_1", Const, 16}, + {"DT_PPC64_GLINK", Const, 16}, + {"DT_PPC64_OPD", Const, 16}, + {"DT_PPC64_OPDSZ", Const, 16}, + {"DT_PPC64_OPT", Const, 16}, + {"DT_PPC_GOT", Const, 16}, + {"DT_PPC_OPT", Const, 16}, + {"DT_PREINIT_ARRAY", Const, 0}, + {"DT_PREINIT_ARRAYSZ", Const, 0}, + {"DT_REL", Const, 0}, + {"DT_RELA", Const, 0}, + {"DT_RELACOUNT", Const, 16}, + {"DT_RELAENT", Const, 0}, + {"DT_RELASZ", Const, 0}, + {"DT_RELCOUNT", Const, 16}, + {"DT_RELENT", Const, 0}, + {"DT_RELSZ", Const, 0}, + {"DT_RPATH", Const, 0}, + {"DT_RUNPATH", Const, 0}, + {"DT_SONAME", Const, 0}, + {"DT_SPARC_REGISTER", Const, 16}, + {"DT_STRSZ", Const, 0}, + {"DT_STRTAB", Const, 0}, + {"DT_SYMBOLIC", Const, 0}, + {"DT_SYMENT", Const, 0}, + {"DT_SYMINENT", Const, 16}, + {"DT_SYMINFO", Const, 16}, + {"DT_SYMINSZ", Const, 16}, + {"DT_SYMTAB", Const, 0}, + {"DT_SYMTAB_SHNDX", Const, 16}, + {"DT_TEXTREL", Const, 0}, + {"DT_TLSDESC_GOT", Const, 16}, + {"DT_TLSDESC_PLT", Const, 16}, + {"DT_USED", Const, 16}, + {"DT_VALRNGHI", Const, 16}, + {"DT_VALRNGLO", Const, 16}, + {"DT_VERDEF", Const, 16}, + {"DT_VERDEFNUM", Const, 16}, + {"DT_VERNEED", Const, 0}, + {"DT_VERNEEDNUM", Const, 0}, + {"DT_VERSYM", Const, 0}, + {"Data", Type, 0}, + {"Dyn32", Type, 0}, + {"Dyn32.Tag", Field, 0}, + {"Dyn32.Val", Field, 0}, + {"Dyn64", Type, 0}, + {"Dyn64.Tag", Field, 0}, + {"Dyn64.Val", Field, 0}, + {"DynFlag", Type, 0}, + {"DynFlag1", Type, 21}, + {"DynTag", Type, 0}, + {"EI_ABIVERSION", Const, 0}, + {"EI_CLASS", Const, 0}, + {"EI_DATA", Const, 0}, + {"EI_NIDENT", Const, 0}, + {"EI_OSABI", Const, 0}, + {"EI_PAD", Const, 0}, + {"EI_VERSION", Const, 0}, + {"ELFCLASS32", Const, 0}, + {"ELFCLASS64", Const, 0}, + {"ELFCLASSNONE", Const, 0}, + {"ELFDATA2LSB", Const, 0}, + {"ELFDATA2MSB", Const, 0}, + {"ELFDATANONE", Const, 0}, + {"ELFMAG", Const, 0}, + {"ELFOSABI_86OPEN", Const, 0}, + {"ELFOSABI_AIX", Const, 0}, + {"ELFOSABI_ARM", Const, 0}, + {"ELFOSABI_AROS", Const, 11}, + {"ELFOSABI_CLOUDABI", Const, 11}, + {"ELFOSABI_FENIXOS", Const, 11}, + {"ELFOSABI_FREEBSD", Const, 0}, + {"ELFOSABI_HPUX", Const, 0}, + {"ELFOSABI_HURD", Const, 0}, + {"ELFOSABI_IRIX", Const, 0}, + {"ELFOSABI_LINUX", Const, 0}, + {"ELFOSABI_MODESTO", Const, 0}, + {"ELFOSABI_NETBSD", Const, 0}, + {"ELFOSABI_NONE", Const, 0}, + {"ELFOSABI_NSK", Const, 0}, + {"ELFOSABI_OPENBSD", Const, 0}, + {"ELFOSABI_OPENVMS", Const, 0}, + {"ELFOSABI_SOLARIS", Const, 0}, + {"ELFOSABI_STANDALONE", Const, 0}, + {"ELFOSABI_TRU64", Const, 0}, + {"EM_386", Const, 0}, + {"EM_486", Const, 0}, + {"EM_56800EX", Const, 11}, + {"EM_68HC05", Const, 11}, + {"EM_68HC08", Const, 11}, + {"EM_68HC11", Const, 11}, + {"EM_68HC12", Const, 0}, + {"EM_68HC16", Const, 11}, + {"EM_68K", Const, 0}, + {"EM_78KOR", Const, 11}, + {"EM_8051", Const, 11}, + {"EM_860", Const, 0}, + {"EM_88K", Const, 0}, + {"EM_960", Const, 0}, + {"EM_AARCH64", Const, 4}, + {"EM_ALPHA", Const, 0}, + {"EM_ALPHA_STD", Const, 0}, + {"EM_ALTERA_NIOS2", Const, 11}, + {"EM_AMDGPU", Const, 11}, + {"EM_ARC", Const, 0}, + {"EM_ARCA", Const, 11}, + {"EM_ARC_COMPACT", Const, 11}, + {"EM_ARC_COMPACT2", Const, 11}, + {"EM_ARM", Const, 0}, + {"EM_AVR", Const, 11}, + {"EM_AVR32", Const, 11}, + {"EM_BA1", Const, 11}, + {"EM_BA2", Const, 11}, + {"EM_BLACKFIN", Const, 11}, + {"EM_BPF", Const, 11}, + {"EM_C166", Const, 11}, + {"EM_CDP", Const, 11}, + {"EM_CE", Const, 11}, + {"EM_CLOUDSHIELD", Const, 11}, + {"EM_COGE", Const, 11}, + {"EM_COLDFIRE", Const, 0}, + {"EM_COOL", Const, 11}, + {"EM_COREA_1ST", Const, 11}, + {"EM_COREA_2ND", Const, 11}, + {"EM_CR", Const, 11}, + {"EM_CR16", Const, 11}, + {"EM_CRAYNV2", Const, 11}, + {"EM_CRIS", Const, 11}, + {"EM_CRX", Const, 11}, + {"EM_CSR_KALIMBA", Const, 11}, + {"EM_CUDA", Const, 11}, + {"EM_CYPRESS_M8C", Const, 11}, + {"EM_D10V", Const, 11}, + {"EM_D30V", Const, 11}, + {"EM_DSP24", Const, 11}, + {"EM_DSPIC30F", Const, 11}, + {"EM_DXP", Const, 11}, + {"EM_ECOG1", Const, 11}, + {"EM_ECOG16", Const, 11}, + {"EM_ECOG1X", Const, 11}, + {"EM_ECOG2", Const, 11}, + {"EM_ETPU", Const, 11}, + {"EM_EXCESS", Const, 11}, + {"EM_F2MC16", Const, 11}, + {"EM_FIREPATH", Const, 11}, + {"EM_FR20", Const, 0}, + {"EM_FR30", Const, 11}, + {"EM_FT32", Const, 11}, + {"EM_FX66", Const, 11}, + {"EM_H8S", Const, 0}, + {"EM_H8_300", Const, 0}, + {"EM_H8_300H", Const, 0}, + {"EM_H8_500", Const, 0}, + {"EM_HUANY", Const, 11}, + {"EM_IA_64", Const, 0}, + {"EM_INTEL205", Const, 11}, + {"EM_INTEL206", Const, 11}, + {"EM_INTEL207", Const, 11}, + {"EM_INTEL208", Const, 11}, + {"EM_INTEL209", Const, 11}, + {"EM_IP2K", Const, 11}, + {"EM_JAVELIN", Const, 11}, + {"EM_K10M", Const, 11}, + {"EM_KM32", Const, 11}, + {"EM_KMX16", Const, 11}, + {"EM_KMX32", Const, 11}, + {"EM_KMX8", Const, 11}, + {"EM_KVARC", Const, 11}, + {"EM_L10M", Const, 11}, + {"EM_LANAI", Const, 11}, + {"EM_LATTICEMICO32", Const, 11}, + {"EM_LOONGARCH", Const, 19}, + {"EM_M16C", Const, 11}, + {"EM_M32", Const, 0}, + {"EM_M32C", Const, 11}, + {"EM_M32R", Const, 11}, + {"EM_MANIK", Const, 11}, + {"EM_MAX", Const, 11}, + {"EM_MAXQ30", Const, 11}, + {"EM_MCHP_PIC", Const, 11}, + {"EM_MCST_ELBRUS", Const, 11}, + {"EM_ME16", Const, 0}, + {"EM_METAG", Const, 11}, + {"EM_MICROBLAZE", Const, 11}, + {"EM_MIPS", Const, 0}, + {"EM_MIPS_RS3_LE", Const, 0}, + {"EM_MIPS_RS4_BE", Const, 0}, + {"EM_MIPS_X", Const, 0}, + {"EM_MMA", Const, 0}, + {"EM_MMDSP_PLUS", Const, 11}, + {"EM_MMIX", Const, 11}, + {"EM_MN10200", Const, 11}, + {"EM_MN10300", Const, 11}, + {"EM_MOXIE", Const, 11}, + {"EM_MSP430", Const, 11}, + {"EM_NCPU", Const, 0}, + {"EM_NDR1", Const, 0}, + {"EM_NDS32", Const, 11}, + {"EM_NONE", Const, 0}, + {"EM_NORC", Const, 11}, + {"EM_NS32K", Const, 11}, + {"EM_OPEN8", Const, 11}, + {"EM_OPENRISC", Const, 11}, + {"EM_PARISC", Const, 0}, + {"EM_PCP", Const, 0}, + {"EM_PDP10", Const, 11}, + {"EM_PDP11", Const, 11}, + {"EM_PDSP", Const, 11}, + {"EM_PJ", Const, 11}, + {"EM_PPC", Const, 0}, + {"EM_PPC64", Const, 0}, + {"EM_PRISM", Const, 11}, + {"EM_QDSP6", Const, 11}, + {"EM_R32C", Const, 11}, + {"EM_RCE", Const, 0}, + {"EM_RH32", Const, 0}, + {"EM_RISCV", Const, 11}, + {"EM_RL78", Const, 11}, + {"EM_RS08", Const, 11}, + {"EM_RX", Const, 11}, + {"EM_S370", Const, 0}, + {"EM_S390", Const, 0}, + {"EM_SCORE7", Const, 11}, + {"EM_SEP", Const, 11}, + {"EM_SE_C17", Const, 11}, + {"EM_SE_C33", Const, 11}, + {"EM_SH", Const, 0}, + {"EM_SHARC", Const, 11}, + {"EM_SLE9X", Const, 11}, + {"EM_SNP1K", Const, 11}, + {"EM_SPARC", Const, 0}, + {"EM_SPARC32PLUS", Const, 0}, + {"EM_SPARCV9", Const, 0}, + {"EM_ST100", Const, 0}, + {"EM_ST19", Const, 11}, + {"EM_ST200", Const, 11}, + {"EM_ST7", Const, 11}, + {"EM_ST9PLUS", Const, 11}, + {"EM_STARCORE", Const, 0}, + {"EM_STM8", Const, 11}, + {"EM_STXP7X", Const, 11}, + {"EM_SVX", Const, 11}, + {"EM_TILE64", Const, 11}, + {"EM_TILEGX", Const, 11}, + {"EM_TILEPRO", Const, 11}, + {"EM_TINYJ", Const, 0}, + {"EM_TI_ARP32", Const, 11}, + {"EM_TI_C2000", Const, 11}, + {"EM_TI_C5500", Const, 11}, + {"EM_TI_C6000", Const, 11}, + {"EM_TI_PRU", Const, 11}, + {"EM_TMM_GPP", Const, 11}, + {"EM_TPC", Const, 11}, + {"EM_TRICORE", Const, 0}, + {"EM_TRIMEDIA", Const, 11}, + {"EM_TSK3000", Const, 11}, + {"EM_UNICORE", Const, 11}, + {"EM_V800", Const, 0}, + {"EM_V850", Const, 11}, + {"EM_VAX", Const, 11}, + {"EM_VIDEOCORE", Const, 11}, + {"EM_VIDEOCORE3", Const, 11}, + {"EM_VIDEOCORE5", Const, 11}, + {"EM_VISIUM", Const, 11}, + {"EM_VPP500", Const, 0}, + {"EM_X86_64", Const, 0}, + {"EM_XCORE", Const, 11}, + {"EM_XGATE", Const, 11}, + {"EM_XIMO16", Const, 11}, + {"EM_XTENSA", Const, 11}, + {"EM_Z80", Const, 11}, + {"EM_ZSP", Const, 11}, + {"ET_CORE", Const, 0}, + {"ET_DYN", Const, 0}, + {"ET_EXEC", Const, 0}, + {"ET_HIOS", Const, 0}, + {"ET_HIPROC", Const, 0}, + {"ET_LOOS", Const, 0}, + {"ET_LOPROC", Const, 0}, + {"ET_NONE", Const, 0}, + {"ET_REL", Const, 0}, + {"EV_CURRENT", Const, 0}, + {"EV_NONE", Const, 0}, + {"ErrNoSymbols", Var, 4}, + {"File", Type, 0}, + {"File.FileHeader", Field, 0}, + {"File.Progs", Field, 0}, + {"File.Sections", Field, 0}, + {"FileHeader", Type, 0}, + {"FileHeader.ABIVersion", Field, 0}, + {"FileHeader.ByteOrder", Field, 0}, + {"FileHeader.Class", Field, 0}, + {"FileHeader.Data", Field, 0}, + {"FileHeader.Entry", Field, 1}, + {"FileHeader.Machine", Field, 0}, + {"FileHeader.OSABI", Field, 0}, + {"FileHeader.Type", Field, 0}, + {"FileHeader.Version", Field, 0}, + {"FormatError", Type, 0}, + {"Header32", Type, 0}, + {"Header32.Ehsize", Field, 0}, + {"Header32.Entry", Field, 0}, + {"Header32.Flags", Field, 0}, + {"Header32.Ident", Field, 0}, + {"Header32.Machine", Field, 0}, + {"Header32.Phentsize", Field, 0}, + {"Header32.Phnum", Field, 0}, + {"Header32.Phoff", Field, 0}, + {"Header32.Shentsize", Field, 0}, + {"Header32.Shnum", Field, 0}, + {"Header32.Shoff", Field, 0}, + {"Header32.Shstrndx", Field, 0}, + {"Header32.Type", Field, 0}, + {"Header32.Version", Field, 0}, + {"Header64", Type, 0}, + {"Header64.Ehsize", Field, 0}, + {"Header64.Entry", Field, 0}, + {"Header64.Flags", Field, 0}, + {"Header64.Ident", Field, 0}, + {"Header64.Machine", Field, 0}, + {"Header64.Phentsize", Field, 0}, + {"Header64.Phnum", Field, 0}, + {"Header64.Phoff", Field, 0}, + {"Header64.Shentsize", Field, 0}, + {"Header64.Shnum", Field, 0}, + {"Header64.Shoff", Field, 0}, + {"Header64.Shstrndx", Field, 0}, + {"Header64.Type", Field, 0}, + {"Header64.Version", Field, 0}, + {"ImportedSymbol", Type, 0}, + {"ImportedSymbol.Library", Field, 0}, + {"ImportedSymbol.Name", Field, 0}, + {"ImportedSymbol.Version", Field, 0}, + {"Machine", Type, 0}, + {"NT_FPREGSET", Const, 0}, + {"NT_PRPSINFO", Const, 0}, + {"NT_PRSTATUS", Const, 0}, + {"NType", Type, 0}, + {"NewFile", Func, 0}, + {"OSABI", Type, 0}, + {"Open", Func, 0}, + {"PF_MASKOS", Const, 0}, + {"PF_MASKPROC", Const, 0}, + {"PF_R", Const, 0}, + {"PF_W", Const, 0}, + {"PF_X", Const, 0}, + {"PT_AARCH64_ARCHEXT", Const, 16}, + {"PT_AARCH64_UNWIND", Const, 16}, + {"PT_ARM_ARCHEXT", Const, 16}, + {"PT_ARM_EXIDX", Const, 16}, + {"PT_DYNAMIC", Const, 0}, + {"PT_GNU_EH_FRAME", Const, 16}, + {"PT_GNU_MBIND_HI", Const, 16}, + {"PT_GNU_MBIND_LO", Const, 16}, + {"PT_GNU_PROPERTY", Const, 16}, + {"PT_GNU_RELRO", Const, 16}, + {"PT_GNU_STACK", Const, 16}, + {"PT_HIOS", Const, 0}, + {"PT_HIPROC", Const, 0}, + {"PT_INTERP", Const, 0}, + {"PT_LOAD", Const, 0}, + {"PT_LOOS", Const, 0}, + {"PT_LOPROC", Const, 0}, + {"PT_MIPS_ABIFLAGS", Const, 16}, + {"PT_MIPS_OPTIONS", Const, 16}, + {"PT_MIPS_REGINFO", Const, 16}, + {"PT_MIPS_RTPROC", Const, 16}, + {"PT_NOTE", Const, 0}, + {"PT_NULL", Const, 0}, + {"PT_OPENBSD_BOOTDATA", Const, 16}, + {"PT_OPENBSD_NOBTCFI", Const, 23}, + {"PT_OPENBSD_RANDOMIZE", Const, 16}, + {"PT_OPENBSD_WXNEEDED", Const, 16}, + {"PT_PAX_FLAGS", Const, 16}, + {"PT_PHDR", Const, 0}, + {"PT_S390_PGSTE", Const, 16}, + {"PT_SHLIB", Const, 0}, + {"PT_SUNWSTACK", Const, 16}, + {"PT_SUNW_EH_FRAME", Const, 16}, + {"PT_TLS", Const, 0}, + {"Prog", Type, 0}, + {"Prog.ProgHeader", Field, 0}, + {"Prog.ReaderAt", Field, 0}, + {"Prog32", Type, 0}, + {"Prog32.Align", Field, 0}, + {"Prog32.Filesz", Field, 0}, + {"Prog32.Flags", Field, 0}, + {"Prog32.Memsz", Field, 0}, + {"Prog32.Off", Field, 0}, + {"Prog32.Paddr", Field, 0}, + {"Prog32.Type", Field, 0}, + {"Prog32.Vaddr", Field, 0}, + {"Prog64", Type, 0}, + {"Prog64.Align", Field, 0}, + {"Prog64.Filesz", Field, 0}, + {"Prog64.Flags", Field, 0}, + {"Prog64.Memsz", Field, 0}, + {"Prog64.Off", Field, 0}, + {"Prog64.Paddr", Field, 0}, + {"Prog64.Type", Field, 0}, + {"Prog64.Vaddr", Field, 0}, + {"ProgFlag", Type, 0}, + {"ProgHeader", Type, 0}, + {"ProgHeader.Align", Field, 0}, + {"ProgHeader.Filesz", Field, 0}, + {"ProgHeader.Flags", Field, 0}, + {"ProgHeader.Memsz", Field, 0}, + {"ProgHeader.Off", Field, 0}, + {"ProgHeader.Paddr", Field, 0}, + {"ProgHeader.Type", Field, 0}, + {"ProgHeader.Vaddr", Field, 0}, + {"ProgType", Type, 0}, + {"R_386", Type, 0}, + {"R_386_16", Const, 10}, + {"R_386_32", Const, 0}, + {"R_386_32PLT", Const, 10}, + {"R_386_8", Const, 10}, + {"R_386_COPY", Const, 0}, + {"R_386_GLOB_DAT", Const, 0}, + {"R_386_GOT32", Const, 0}, + {"R_386_GOT32X", Const, 10}, + {"R_386_GOTOFF", Const, 0}, + {"R_386_GOTPC", Const, 0}, + {"R_386_IRELATIVE", Const, 10}, + {"R_386_JMP_SLOT", Const, 0}, + {"R_386_NONE", Const, 0}, + {"R_386_PC16", Const, 10}, + {"R_386_PC32", Const, 0}, + {"R_386_PC8", Const, 10}, + {"R_386_PLT32", Const, 0}, + {"R_386_RELATIVE", Const, 0}, + {"R_386_SIZE32", Const, 10}, + {"R_386_TLS_DESC", Const, 10}, + {"R_386_TLS_DESC_CALL", Const, 10}, + {"R_386_TLS_DTPMOD32", Const, 0}, + {"R_386_TLS_DTPOFF32", Const, 0}, + {"R_386_TLS_GD", Const, 0}, + {"R_386_TLS_GD_32", Const, 0}, + {"R_386_TLS_GD_CALL", Const, 0}, + {"R_386_TLS_GD_POP", Const, 0}, + {"R_386_TLS_GD_PUSH", Const, 0}, + {"R_386_TLS_GOTDESC", Const, 10}, + {"R_386_TLS_GOTIE", Const, 0}, + {"R_386_TLS_IE", Const, 0}, + {"R_386_TLS_IE_32", Const, 0}, + {"R_386_TLS_LDM", Const, 0}, + {"R_386_TLS_LDM_32", Const, 0}, + {"R_386_TLS_LDM_CALL", Const, 0}, + {"R_386_TLS_LDM_POP", Const, 0}, + {"R_386_TLS_LDM_PUSH", Const, 0}, + {"R_386_TLS_LDO_32", Const, 0}, + {"R_386_TLS_LE", Const, 0}, + {"R_386_TLS_LE_32", Const, 0}, + {"R_386_TLS_TPOFF", Const, 0}, + {"R_386_TLS_TPOFF32", Const, 0}, + {"R_390", Type, 7}, + {"R_390_12", Const, 7}, + {"R_390_16", Const, 7}, + {"R_390_20", Const, 7}, + {"R_390_32", Const, 7}, + {"R_390_64", Const, 7}, + {"R_390_8", Const, 7}, + {"R_390_COPY", Const, 7}, + {"R_390_GLOB_DAT", Const, 7}, + {"R_390_GOT12", Const, 7}, + {"R_390_GOT16", Const, 7}, + {"R_390_GOT20", Const, 7}, + {"R_390_GOT32", Const, 7}, + {"R_390_GOT64", Const, 7}, + {"R_390_GOTENT", Const, 7}, + {"R_390_GOTOFF", Const, 7}, + {"R_390_GOTOFF16", Const, 7}, + {"R_390_GOTOFF64", Const, 7}, + {"R_390_GOTPC", Const, 7}, + {"R_390_GOTPCDBL", Const, 7}, + {"R_390_GOTPLT12", Const, 7}, + {"R_390_GOTPLT16", Const, 7}, + {"R_390_GOTPLT20", Const, 7}, + {"R_390_GOTPLT32", Const, 7}, + {"R_390_GOTPLT64", Const, 7}, + {"R_390_GOTPLTENT", Const, 7}, + {"R_390_GOTPLTOFF16", Const, 7}, + {"R_390_GOTPLTOFF32", Const, 7}, + {"R_390_GOTPLTOFF64", Const, 7}, + {"R_390_JMP_SLOT", Const, 7}, + {"R_390_NONE", Const, 7}, + {"R_390_PC16", Const, 7}, + {"R_390_PC16DBL", Const, 7}, + {"R_390_PC32", Const, 7}, + {"R_390_PC32DBL", Const, 7}, + {"R_390_PC64", Const, 7}, + {"R_390_PLT16DBL", Const, 7}, + {"R_390_PLT32", Const, 7}, + {"R_390_PLT32DBL", Const, 7}, + {"R_390_PLT64", Const, 7}, + {"R_390_RELATIVE", Const, 7}, + {"R_390_TLS_DTPMOD", Const, 7}, + {"R_390_TLS_DTPOFF", Const, 7}, + {"R_390_TLS_GD32", Const, 7}, + {"R_390_TLS_GD64", Const, 7}, + {"R_390_TLS_GDCALL", Const, 7}, + {"R_390_TLS_GOTIE12", Const, 7}, + {"R_390_TLS_GOTIE20", Const, 7}, + {"R_390_TLS_GOTIE32", Const, 7}, + {"R_390_TLS_GOTIE64", Const, 7}, + {"R_390_TLS_IE32", Const, 7}, + {"R_390_TLS_IE64", Const, 7}, + {"R_390_TLS_IEENT", Const, 7}, + {"R_390_TLS_LDCALL", Const, 7}, + {"R_390_TLS_LDM32", Const, 7}, + {"R_390_TLS_LDM64", Const, 7}, + {"R_390_TLS_LDO32", Const, 7}, + {"R_390_TLS_LDO64", Const, 7}, + {"R_390_TLS_LE32", Const, 7}, + {"R_390_TLS_LE64", Const, 7}, + {"R_390_TLS_LOAD", Const, 7}, + {"R_390_TLS_TPOFF", Const, 7}, + {"R_AARCH64", Type, 4}, + {"R_AARCH64_ABS16", Const, 4}, + {"R_AARCH64_ABS32", Const, 4}, + {"R_AARCH64_ABS64", Const, 4}, + {"R_AARCH64_ADD_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_ADR_GOT_PAGE", Const, 4}, + {"R_AARCH64_ADR_PREL_LO21", Const, 4}, + {"R_AARCH64_ADR_PREL_PG_HI21", Const, 4}, + {"R_AARCH64_ADR_PREL_PG_HI21_NC", Const, 4}, + {"R_AARCH64_CALL26", Const, 4}, + {"R_AARCH64_CONDBR19", Const, 4}, + {"R_AARCH64_COPY", Const, 4}, + {"R_AARCH64_GLOB_DAT", Const, 4}, + {"R_AARCH64_GOT_LD_PREL19", Const, 4}, + {"R_AARCH64_IRELATIVE", Const, 4}, + {"R_AARCH64_JUMP26", Const, 4}, + {"R_AARCH64_JUMP_SLOT", Const, 4}, + {"R_AARCH64_LD64_GOTOFF_LO15", Const, 10}, + {"R_AARCH64_LD64_GOTPAGE_LO15", Const, 10}, + {"R_AARCH64_LD64_GOT_LO12_NC", Const, 4}, + {"R_AARCH64_LDST128_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_LDST16_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_LDST32_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_LDST64_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_LDST8_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_LD_PREL_LO19", Const, 4}, + {"R_AARCH64_MOVW_SABS_G0", Const, 4}, + {"R_AARCH64_MOVW_SABS_G1", Const, 4}, + {"R_AARCH64_MOVW_SABS_G2", Const, 4}, + {"R_AARCH64_MOVW_UABS_G0", Const, 4}, + {"R_AARCH64_MOVW_UABS_G0_NC", Const, 4}, + {"R_AARCH64_MOVW_UABS_G1", Const, 4}, + {"R_AARCH64_MOVW_UABS_G1_NC", Const, 4}, + {"R_AARCH64_MOVW_UABS_G2", Const, 4}, + {"R_AARCH64_MOVW_UABS_G2_NC", Const, 4}, + {"R_AARCH64_MOVW_UABS_G3", Const, 4}, + {"R_AARCH64_NONE", Const, 4}, + {"R_AARCH64_NULL", Const, 4}, + {"R_AARCH64_P32_ABS16", Const, 4}, + {"R_AARCH64_P32_ABS32", Const, 4}, + {"R_AARCH64_P32_ADD_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_P32_ADR_GOT_PAGE", Const, 4}, + {"R_AARCH64_P32_ADR_PREL_LO21", Const, 4}, + {"R_AARCH64_P32_ADR_PREL_PG_HI21", Const, 4}, + {"R_AARCH64_P32_CALL26", Const, 4}, + {"R_AARCH64_P32_CONDBR19", Const, 4}, + {"R_AARCH64_P32_COPY", Const, 4}, + {"R_AARCH64_P32_GLOB_DAT", Const, 4}, + {"R_AARCH64_P32_GOT_LD_PREL19", Const, 4}, + {"R_AARCH64_P32_IRELATIVE", Const, 4}, + {"R_AARCH64_P32_JUMP26", Const, 4}, + {"R_AARCH64_P32_JUMP_SLOT", Const, 4}, + {"R_AARCH64_P32_LD32_GOT_LO12_NC", Const, 4}, + {"R_AARCH64_P32_LDST128_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_P32_LDST16_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_P32_LDST32_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_P32_LDST64_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_P32_LDST8_ABS_LO12_NC", Const, 4}, + {"R_AARCH64_P32_LD_PREL_LO19", Const, 4}, + {"R_AARCH64_P32_MOVW_SABS_G0", Const, 4}, + {"R_AARCH64_P32_MOVW_UABS_G0", Const, 4}, + {"R_AARCH64_P32_MOVW_UABS_G0_NC", Const, 4}, + {"R_AARCH64_P32_MOVW_UABS_G1", Const, 4}, + {"R_AARCH64_P32_PREL16", Const, 4}, + {"R_AARCH64_P32_PREL32", Const, 4}, + {"R_AARCH64_P32_RELATIVE", Const, 4}, + {"R_AARCH64_P32_TLSDESC", Const, 4}, + {"R_AARCH64_P32_TLSDESC_ADD_LO12_NC", Const, 4}, + {"R_AARCH64_P32_TLSDESC_ADR_PAGE21", Const, 4}, + {"R_AARCH64_P32_TLSDESC_ADR_PREL21", Const, 4}, + {"R_AARCH64_P32_TLSDESC_CALL", Const, 4}, + {"R_AARCH64_P32_TLSDESC_LD32_LO12_NC", Const, 4}, + {"R_AARCH64_P32_TLSDESC_LD_PREL19", Const, 4}, + {"R_AARCH64_P32_TLSGD_ADD_LO12_NC", Const, 4}, + {"R_AARCH64_P32_TLSGD_ADR_PAGE21", Const, 4}, + {"R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21", Const, 4}, + {"R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC", Const, 4}, + {"R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19", Const, 4}, + {"R_AARCH64_P32_TLSLE_ADD_TPREL_HI12", Const, 4}, + {"R_AARCH64_P32_TLSLE_ADD_TPREL_LO12", Const, 4}, + {"R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC", Const, 4}, + {"R_AARCH64_P32_TLSLE_MOVW_TPREL_G0", Const, 4}, + {"R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC", Const, 4}, + {"R_AARCH64_P32_TLSLE_MOVW_TPREL_G1", Const, 4}, + {"R_AARCH64_P32_TLS_DTPMOD", Const, 4}, + {"R_AARCH64_P32_TLS_DTPREL", Const, 4}, + {"R_AARCH64_P32_TLS_TPREL", Const, 4}, + {"R_AARCH64_P32_TSTBR14", Const, 4}, + {"R_AARCH64_PREL16", Const, 4}, + {"R_AARCH64_PREL32", Const, 4}, + {"R_AARCH64_PREL64", Const, 4}, + {"R_AARCH64_RELATIVE", Const, 4}, + {"R_AARCH64_TLSDESC", Const, 4}, + {"R_AARCH64_TLSDESC_ADD", Const, 4}, + {"R_AARCH64_TLSDESC_ADD_LO12_NC", Const, 4}, + {"R_AARCH64_TLSDESC_ADR_PAGE21", Const, 4}, + {"R_AARCH64_TLSDESC_ADR_PREL21", Const, 4}, + {"R_AARCH64_TLSDESC_CALL", Const, 4}, + {"R_AARCH64_TLSDESC_LD64_LO12_NC", Const, 4}, + {"R_AARCH64_TLSDESC_LDR", Const, 4}, + {"R_AARCH64_TLSDESC_LD_PREL19", Const, 4}, + {"R_AARCH64_TLSDESC_OFF_G0_NC", Const, 4}, + {"R_AARCH64_TLSDESC_OFF_G1", Const, 4}, + {"R_AARCH64_TLSGD_ADD_LO12_NC", Const, 4}, + {"R_AARCH64_TLSGD_ADR_PAGE21", Const, 4}, + {"R_AARCH64_TLSGD_ADR_PREL21", Const, 10}, + {"R_AARCH64_TLSGD_MOVW_G0_NC", Const, 10}, + {"R_AARCH64_TLSGD_MOVW_G1", Const, 10}, + {"R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21", Const, 4}, + {"R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC", Const, 4}, + {"R_AARCH64_TLSIE_LD_GOTTPREL_PREL19", Const, 4}, + {"R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC", Const, 4}, + {"R_AARCH64_TLSIE_MOVW_GOTTPREL_G1", Const, 4}, + {"R_AARCH64_TLSLD_ADR_PAGE21", Const, 10}, + {"R_AARCH64_TLSLD_ADR_PREL21", Const, 10}, + {"R_AARCH64_TLSLD_LDST128_DTPREL_LO12", Const, 10}, + {"R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC", Const, 10}, + {"R_AARCH64_TLSLE_ADD_TPREL_HI12", Const, 4}, + {"R_AARCH64_TLSLE_ADD_TPREL_LO12", Const, 4}, + {"R_AARCH64_TLSLE_ADD_TPREL_LO12_NC", Const, 4}, + {"R_AARCH64_TLSLE_LDST128_TPREL_LO12", Const, 10}, + {"R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC", Const, 10}, + {"R_AARCH64_TLSLE_MOVW_TPREL_G0", Const, 4}, + {"R_AARCH64_TLSLE_MOVW_TPREL_G0_NC", Const, 4}, + {"R_AARCH64_TLSLE_MOVW_TPREL_G1", Const, 4}, + {"R_AARCH64_TLSLE_MOVW_TPREL_G1_NC", Const, 4}, + {"R_AARCH64_TLSLE_MOVW_TPREL_G2", Const, 4}, + {"R_AARCH64_TLS_DTPMOD64", Const, 4}, + {"R_AARCH64_TLS_DTPREL64", Const, 4}, + {"R_AARCH64_TLS_TPREL64", Const, 4}, + {"R_AARCH64_TSTBR14", Const, 4}, + {"R_ALPHA", Type, 0}, + {"R_ALPHA_BRADDR", Const, 0}, + {"R_ALPHA_COPY", Const, 0}, + {"R_ALPHA_GLOB_DAT", Const, 0}, + {"R_ALPHA_GPDISP", Const, 0}, + {"R_ALPHA_GPREL32", Const, 0}, + {"R_ALPHA_GPRELHIGH", Const, 0}, + {"R_ALPHA_GPRELLOW", Const, 0}, + {"R_ALPHA_GPVALUE", Const, 0}, + {"R_ALPHA_HINT", Const, 0}, + {"R_ALPHA_IMMED_BR_HI32", Const, 0}, + {"R_ALPHA_IMMED_GP_16", Const, 0}, + {"R_ALPHA_IMMED_GP_HI32", Const, 0}, + {"R_ALPHA_IMMED_LO32", Const, 0}, + {"R_ALPHA_IMMED_SCN_HI32", Const, 0}, + {"R_ALPHA_JMP_SLOT", Const, 0}, + {"R_ALPHA_LITERAL", Const, 0}, + {"R_ALPHA_LITUSE", Const, 0}, + {"R_ALPHA_NONE", Const, 0}, + {"R_ALPHA_OP_PRSHIFT", Const, 0}, + {"R_ALPHA_OP_PSUB", Const, 0}, + {"R_ALPHA_OP_PUSH", Const, 0}, + {"R_ALPHA_OP_STORE", Const, 0}, + {"R_ALPHA_REFLONG", Const, 0}, + {"R_ALPHA_REFQUAD", Const, 0}, + {"R_ALPHA_RELATIVE", Const, 0}, + {"R_ALPHA_SREL16", Const, 0}, + {"R_ALPHA_SREL32", Const, 0}, + {"R_ALPHA_SREL64", Const, 0}, + {"R_ARM", Type, 0}, + {"R_ARM_ABS12", Const, 0}, + {"R_ARM_ABS16", Const, 0}, + {"R_ARM_ABS32", Const, 0}, + {"R_ARM_ABS32_NOI", Const, 10}, + {"R_ARM_ABS8", Const, 0}, + {"R_ARM_ALU_PCREL_15_8", Const, 10}, + {"R_ARM_ALU_PCREL_23_15", Const, 10}, + {"R_ARM_ALU_PCREL_7_0", Const, 10}, + {"R_ARM_ALU_PC_G0", Const, 10}, + {"R_ARM_ALU_PC_G0_NC", Const, 10}, + {"R_ARM_ALU_PC_G1", Const, 10}, + {"R_ARM_ALU_PC_G1_NC", Const, 10}, + {"R_ARM_ALU_PC_G2", Const, 10}, + {"R_ARM_ALU_SBREL_19_12_NC", Const, 10}, + {"R_ARM_ALU_SBREL_27_20_CK", Const, 10}, + {"R_ARM_ALU_SB_G0", Const, 10}, + {"R_ARM_ALU_SB_G0_NC", Const, 10}, + {"R_ARM_ALU_SB_G1", Const, 10}, + {"R_ARM_ALU_SB_G1_NC", Const, 10}, + {"R_ARM_ALU_SB_G2", Const, 10}, + {"R_ARM_AMP_VCALL9", Const, 0}, + {"R_ARM_BASE_ABS", Const, 10}, + {"R_ARM_CALL", Const, 10}, + {"R_ARM_COPY", Const, 0}, + {"R_ARM_GLOB_DAT", Const, 0}, + {"R_ARM_GNU_VTENTRY", Const, 0}, + {"R_ARM_GNU_VTINHERIT", Const, 0}, + {"R_ARM_GOT32", Const, 0}, + {"R_ARM_GOTOFF", Const, 0}, + {"R_ARM_GOTOFF12", Const, 10}, + {"R_ARM_GOTPC", Const, 0}, + {"R_ARM_GOTRELAX", Const, 10}, + {"R_ARM_GOT_ABS", Const, 10}, + {"R_ARM_GOT_BREL12", Const, 10}, + {"R_ARM_GOT_PREL", Const, 10}, + {"R_ARM_IRELATIVE", Const, 10}, + {"R_ARM_JUMP24", Const, 10}, + {"R_ARM_JUMP_SLOT", Const, 0}, + {"R_ARM_LDC_PC_G0", Const, 10}, + {"R_ARM_LDC_PC_G1", Const, 10}, + {"R_ARM_LDC_PC_G2", Const, 10}, + {"R_ARM_LDC_SB_G0", Const, 10}, + {"R_ARM_LDC_SB_G1", Const, 10}, + {"R_ARM_LDC_SB_G2", Const, 10}, + {"R_ARM_LDRS_PC_G0", Const, 10}, + {"R_ARM_LDRS_PC_G1", Const, 10}, + {"R_ARM_LDRS_PC_G2", Const, 10}, + {"R_ARM_LDRS_SB_G0", Const, 10}, + {"R_ARM_LDRS_SB_G1", Const, 10}, + {"R_ARM_LDRS_SB_G2", Const, 10}, + {"R_ARM_LDR_PC_G1", Const, 10}, + {"R_ARM_LDR_PC_G2", Const, 10}, + {"R_ARM_LDR_SBREL_11_10_NC", Const, 10}, + {"R_ARM_LDR_SB_G0", Const, 10}, + {"R_ARM_LDR_SB_G1", Const, 10}, + {"R_ARM_LDR_SB_G2", Const, 10}, + {"R_ARM_ME_TOO", Const, 10}, + {"R_ARM_MOVT_ABS", Const, 10}, + {"R_ARM_MOVT_BREL", Const, 10}, + {"R_ARM_MOVT_PREL", Const, 10}, + {"R_ARM_MOVW_ABS_NC", Const, 10}, + {"R_ARM_MOVW_BREL", Const, 10}, + {"R_ARM_MOVW_BREL_NC", Const, 10}, + {"R_ARM_MOVW_PREL_NC", Const, 10}, + {"R_ARM_NONE", Const, 0}, + {"R_ARM_PC13", Const, 0}, + {"R_ARM_PC24", Const, 0}, + {"R_ARM_PLT32", Const, 0}, + {"R_ARM_PLT32_ABS", Const, 10}, + {"R_ARM_PREL31", Const, 10}, + {"R_ARM_PRIVATE_0", Const, 10}, + {"R_ARM_PRIVATE_1", Const, 10}, + {"R_ARM_PRIVATE_10", Const, 10}, + {"R_ARM_PRIVATE_11", Const, 10}, + {"R_ARM_PRIVATE_12", Const, 10}, + {"R_ARM_PRIVATE_13", Const, 10}, + {"R_ARM_PRIVATE_14", Const, 10}, + {"R_ARM_PRIVATE_15", Const, 10}, + {"R_ARM_PRIVATE_2", Const, 10}, + {"R_ARM_PRIVATE_3", Const, 10}, + {"R_ARM_PRIVATE_4", Const, 10}, + {"R_ARM_PRIVATE_5", Const, 10}, + {"R_ARM_PRIVATE_6", Const, 10}, + {"R_ARM_PRIVATE_7", Const, 10}, + {"R_ARM_PRIVATE_8", Const, 10}, + {"R_ARM_PRIVATE_9", Const, 10}, + {"R_ARM_RABS32", Const, 0}, + {"R_ARM_RBASE", Const, 0}, + {"R_ARM_REL32", Const, 0}, + {"R_ARM_REL32_NOI", Const, 10}, + {"R_ARM_RELATIVE", Const, 0}, + {"R_ARM_RPC24", Const, 0}, + {"R_ARM_RREL32", Const, 0}, + {"R_ARM_RSBREL32", Const, 0}, + {"R_ARM_RXPC25", Const, 10}, + {"R_ARM_SBREL31", Const, 10}, + {"R_ARM_SBREL32", Const, 0}, + {"R_ARM_SWI24", Const, 0}, + {"R_ARM_TARGET1", Const, 10}, + {"R_ARM_TARGET2", Const, 10}, + {"R_ARM_THM_ABS5", Const, 0}, + {"R_ARM_THM_ALU_ABS_G0_NC", Const, 10}, + {"R_ARM_THM_ALU_ABS_G1_NC", Const, 10}, + {"R_ARM_THM_ALU_ABS_G2_NC", Const, 10}, + {"R_ARM_THM_ALU_ABS_G3", Const, 10}, + {"R_ARM_THM_ALU_PREL_11_0", Const, 10}, + {"R_ARM_THM_GOT_BREL12", Const, 10}, + {"R_ARM_THM_JUMP11", Const, 10}, + {"R_ARM_THM_JUMP19", Const, 10}, + {"R_ARM_THM_JUMP24", Const, 10}, + {"R_ARM_THM_JUMP6", Const, 10}, + {"R_ARM_THM_JUMP8", Const, 10}, + {"R_ARM_THM_MOVT_ABS", Const, 10}, + {"R_ARM_THM_MOVT_BREL", Const, 10}, + {"R_ARM_THM_MOVT_PREL", Const, 10}, + {"R_ARM_THM_MOVW_ABS_NC", Const, 10}, + {"R_ARM_THM_MOVW_BREL", Const, 10}, + {"R_ARM_THM_MOVW_BREL_NC", Const, 10}, + {"R_ARM_THM_MOVW_PREL_NC", Const, 10}, + {"R_ARM_THM_PC12", Const, 10}, + {"R_ARM_THM_PC22", Const, 0}, + {"R_ARM_THM_PC8", Const, 0}, + {"R_ARM_THM_RPC22", Const, 0}, + {"R_ARM_THM_SWI8", Const, 0}, + {"R_ARM_THM_TLS_CALL", Const, 10}, + {"R_ARM_THM_TLS_DESCSEQ16", Const, 10}, + {"R_ARM_THM_TLS_DESCSEQ32", Const, 10}, + {"R_ARM_THM_XPC22", Const, 0}, + {"R_ARM_TLS_CALL", Const, 10}, + {"R_ARM_TLS_DESCSEQ", Const, 10}, + {"R_ARM_TLS_DTPMOD32", Const, 10}, + {"R_ARM_TLS_DTPOFF32", Const, 10}, + {"R_ARM_TLS_GD32", Const, 10}, + {"R_ARM_TLS_GOTDESC", Const, 10}, + {"R_ARM_TLS_IE12GP", Const, 10}, + {"R_ARM_TLS_IE32", Const, 10}, + {"R_ARM_TLS_LDM32", Const, 10}, + {"R_ARM_TLS_LDO12", Const, 10}, + {"R_ARM_TLS_LDO32", Const, 10}, + {"R_ARM_TLS_LE12", Const, 10}, + {"R_ARM_TLS_LE32", Const, 10}, + {"R_ARM_TLS_TPOFF32", Const, 10}, + {"R_ARM_V4BX", Const, 10}, + {"R_ARM_XPC25", Const, 0}, + {"R_INFO", Func, 0}, + {"R_INFO32", Func, 0}, + {"R_LARCH", Type, 19}, + {"R_LARCH_32", Const, 19}, + {"R_LARCH_32_PCREL", Const, 20}, + {"R_LARCH_64", Const, 19}, + {"R_LARCH_64_PCREL", Const, 22}, + {"R_LARCH_ABS64_HI12", Const, 20}, + {"R_LARCH_ABS64_LO20", Const, 20}, + {"R_LARCH_ABS_HI20", Const, 20}, + {"R_LARCH_ABS_LO12", Const, 20}, + {"R_LARCH_ADD16", Const, 19}, + {"R_LARCH_ADD24", Const, 19}, + {"R_LARCH_ADD32", Const, 19}, + {"R_LARCH_ADD6", Const, 22}, + {"R_LARCH_ADD64", Const, 19}, + {"R_LARCH_ADD8", Const, 19}, + {"R_LARCH_ADD_ULEB128", Const, 22}, + {"R_LARCH_ALIGN", Const, 22}, + {"R_LARCH_B16", Const, 20}, + {"R_LARCH_B21", Const, 20}, + {"R_LARCH_B26", Const, 20}, + {"R_LARCH_CFA", Const, 22}, + {"R_LARCH_COPY", Const, 19}, + {"R_LARCH_DELETE", Const, 22}, + {"R_LARCH_GNU_VTENTRY", Const, 20}, + {"R_LARCH_GNU_VTINHERIT", Const, 20}, + {"R_LARCH_GOT64_HI12", Const, 20}, + {"R_LARCH_GOT64_LO20", Const, 20}, + {"R_LARCH_GOT64_PC_HI12", Const, 20}, + {"R_LARCH_GOT64_PC_LO20", Const, 20}, + {"R_LARCH_GOT_HI20", Const, 20}, + {"R_LARCH_GOT_LO12", Const, 20}, + {"R_LARCH_GOT_PC_HI20", Const, 20}, + {"R_LARCH_GOT_PC_LO12", Const, 20}, + {"R_LARCH_IRELATIVE", Const, 19}, + {"R_LARCH_JUMP_SLOT", Const, 19}, + {"R_LARCH_MARK_LA", Const, 19}, + {"R_LARCH_MARK_PCREL", Const, 19}, + {"R_LARCH_NONE", Const, 19}, + {"R_LARCH_PCALA64_HI12", Const, 20}, + {"R_LARCH_PCALA64_LO20", Const, 20}, + {"R_LARCH_PCALA_HI20", Const, 20}, + {"R_LARCH_PCALA_LO12", Const, 20}, + {"R_LARCH_PCREL20_S2", Const, 22}, + {"R_LARCH_RELATIVE", Const, 19}, + {"R_LARCH_RELAX", Const, 20}, + {"R_LARCH_SOP_ADD", Const, 19}, + {"R_LARCH_SOP_AND", Const, 19}, + {"R_LARCH_SOP_ASSERT", Const, 19}, + {"R_LARCH_SOP_IF_ELSE", Const, 19}, + {"R_LARCH_SOP_NOT", Const, 19}, + {"R_LARCH_SOP_POP_32_S_0_10_10_16_S2", Const, 19}, + {"R_LARCH_SOP_POP_32_S_0_5_10_16_S2", Const, 19}, + {"R_LARCH_SOP_POP_32_S_10_12", Const, 19}, + {"R_LARCH_SOP_POP_32_S_10_16", Const, 19}, + {"R_LARCH_SOP_POP_32_S_10_16_S2", Const, 19}, + {"R_LARCH_SOP_POP_32_S_10_5", Const, 19}, + {"R_LARCH_SOP_POP_32_S_5_20", Const, 19}, + {"R_LARCH_SOP_POP_32_U", Const, 19}, + {"R_LARCH_SOP_POP_32_U_10_12", Const, 19}, + {"R_LARCH_SOP_PUSH_ABSOLUTE", Const, 19}, + {"R_LARCH_SOP_PUSH_DUP", Const, 19}, + {"R_LARCH_SOP_PUSH_GPREL", Const, 19}, + {"R_LARCH_SOP_PUSH_PCREL", Const, 19}, + {"R_LARCH_SOP_PUSH_PLT_PCREL", Const, 19}, + {"R_LARCH_SOP_PUSH_TLS_GD", Const, 19}, + {"R_LARCH_SOP_PUSH_TLS_GOT", Const, 19}, + {"R_LARCH_SOP_PUSH_TLS_TPREL", Const, 19}, + {"R_LARCH_SOP_SL", Const, 19}, + {"R_LARCH_SOP_SR", Const, 19}, + {"R_LARCH_SOP_SUB", Const, 19}, + {"R_LARCH_SUB16", Const, 19}, + {"R_LARCH_SUB24", Const, 19}, + {"R_LARCH_SUB32", Const, 19}, + {"R_LARCH_SUB6", Const, 22}, + {"R_LARCH_SUB64", Const, 19}, + {"R_LARCH_SUB8", Const, 19}, + {"R_LARCH_SUB_ULEB128", Const, 22}, + {"R_LARCH_TLS_DTPMOD32", Const, 19}, + {"R_LARCH_TLS_DTPMOD64", Const, 19}, + {"R_LARCH_TLS_DTPREL32", Const, 19}, + {"R_LARCH_TLS_DTPREL64", Const, 19}, + {"R_LARCH_TLS_GD_HI20", Const, 20}, + {"R_LARCH_TLS_GD_PC_HI20", Const, 20}, + {"R_LARCH_TLS_IE64_HI12", Const, 20}, + {"R_LARCH_TLS_IE64_LO20", Const, 20}, + {"R_LARCH_TLS_IE64_PC_HI12", Const, 20}, + {"R_LARCH_TLS_IE64_PC_LO20", Const, 20}, + {"R_LARCH_TLS_IE_HI20", Const, 20}, + {"R_LARCH_TLS_IE_LO12", Const, 20}, + {"R_LARCH_TLS_IE_PC_HI20", Const, 20}, + {"R_LARCH_TLS_IE_PC_LO12", Const, 20}, + {"R_LARCH_TLS_LD_HI20", Const, 20}, + {"R_LARCH_TLS_LD_PC_HI20", Const, 20}, + {"R_LARCH_TLS_LE64_HI12", Const, 20}, + {"R_LARCH_TLS_LE64_LO20", Const, 20}, + {"R_LARCH_TLS_LE_HI20", Const, 20}, + {"R_LARCH_TLS_LE_LO12", Const, 20}, + {"R_LARCH_TLS_TPREL32", Const, 19}, + {"R_LARCH_TLS_TPREL64", Const, 19}, + {"R_MIPS", Type, 6}, + {"R_MIPS_16", Const, 6}, + {"R_MIPS_26", Const, 6}, + {"R_MIPS_32", Const, 6}, + {"R_MIPS_64", Const, 6}, + {"R_MIPS_ADD_IMMEDIATE", Const, 6}, + {"R_MIPS_CALL16", Const, 6}, + {"R_MIPS_CALL_HI16", Const, 6}, + {"R_MIPS_CALL_LO16", Const, 6}, + {"R_MIPS_DELETE", Const, 6}, + {"R_MIPS_GOT16", Const, 6}, + {"R_MIPS_GOT_DISP", Const, 6}, + {"R_MIPS_GOT_HI16", Const, 6}, + {"R_MIPS_GOT_LO16", Const, 6}, + {"R_MIPS_GOT_OFST", Const, 6}, + {"R_MIPS_GOT_PAGE", Const, 6}, + {"R_MIPS_GPREL16", Const, 6}, + {"R_MIPS_GPREL32", Const, 6}, + {"R_MIPS_HI16", Const, 6}, + {"R_MIPS_HIGHER", Const, 6}, + {"R_MIPS_HIGHEST", Const, 6}, + {"R_MIPS_INSERT_A", Const, 6}, + {"R_MIPS_INSERT_B", Const, 6}, + {"R_MIPS_JALR", Const, 6}, + {"R_MIPS_LITERAL", Const, 6}, + {"R_MIPS_LO16", Const, 6}, + {"R_MIPS_NONE", Const, 6}, + {"R_MIPS_PC16", Const, 6}, + {"R_MIPS_PC32", Const, 22}, + {"R_MIPS_PJUMP", Const, 6}, + {"R_MIPS_REL16", Const, 6}, + {"R_MIPS_REL32", Const, 6}, + {"R_MIPS_RELGOT", Const, 6}, + {"R_MIPS_SCN_DISP", Const, 6}, + {"R_MIPS_SHIFT5", Const, 6}, + {"R_MIPS_SHIFT6", Const, 6}, + {"R_MIPS_SUB", Const, 6}, + {"R_MIPS_TLS_DTPMOD32", Const, 6}, + {"R_MIPS_TLS_DTPMOD64", Const, 6}, + {"R_MIPS_TLS_DTPREL32", Const, 6}, + {"R_MIPS_TLS_DTPREL64", Const, 6}, + {"R_MIPS_TLS_DTPREL_HI16", Const, 6}, + {"R_MIPS_TLS_DTPREL_LO16", Const, 6}, + {"R_MIPS_TLS_GD", Const, 6}, + {"R_MIPS_TLS_GOTTPREL", Const, 6}, + {"R_MIPS_TLS_LDM", Const, 6}, + {"R_MIPS_TLS_TPREL32", Const, 6}, + {"R_MIPS_TLS_TPREL64", Const, 6}, + {"R_MIPS_TLS_TPREL_HI16", Const, 6}, + {"R_MIPS_TLS_TPREL_LO16", Const, 6}, + {"R_PPC", Type, 0}, + {"R_PPC64", Type, 5}, + {"R_PPC64_ADDR14", Const, 5}, + {"R_PPC64_ADDR14_BRNTAKEN", Const, 5}, + {"R_PPC64_ADDR14_BRTAKEN", Const, 5}, + {"R_PPC64_ADDR16", Const, 5}, + {"R_PPC64_ADDR16_DS", Const, 5}, + {"R_PPC64_ADDR16_HA", Const, 5}, + {"R_PPC64_ADDR16_HI", Const, 5}, + {"R_PPC64_ADDR16_HIGH", Const, 10}, + {"R_PPC64_ADDR16_HIGHA", Const, 10}, + {"R_PPC64_ADDR16_HIGHER", Const, 5}, + {"R_PPC64_ADDR16_HIGHER34", Const, 20}, + {"R_PPC64_ADDR16_HIGHERA", Const, 5}, + {"R_PPC64_ADDR16_HIGHERA34", Const, 20}, + {"R_PPC64_ADDR16_HIGHEST", Const, 5}, + {"R_PPC64_ADDR16_HIGHEST34", Const, 20}, + {"R_PPC64_ADDR16_HIGHESTA", Const, 5}, + {"R_PPC64_ADDR16_HIGHESTA34", Const, 20}, + {"R_PPC64_ADDR16_LO", Const, 5}, + {"R_PPC64_ADDR16_LO_DS", Const, 5}, + {"R_PPC64_ADDR24", Const, 5}, + {"R_PPC64_ADDR32", Const, 5}, + {"R_PPC64_ADDR64", Const, 5}, + {"R_PPC64_ADDR64_LOCAL", Const, 10}, + {"R_PPC64_COPY", Const, 20}, + {"R_PPC64_D28", Const, 20}, + {"R_PPC64_D34", Const, 20}, + {"R_PPC64_D34_HA30", Const, 20}, + {"R_PPC64_D34_HI30", Const, 20}, + {"R_PPC64_D34_LO", Const, 20}, + {"R_PPC64_DTPMOD64", Const, 5}, + {"R_PPC64_DTPREL16", Const, 5}, + {"R_PPC64_DTPREL16_DS", Const, 5}, + {"R_PPC64_DTPREL16_HA", Const, 5}, + {"R_PPC64_DTPREL16_HI", Const, 5}, + {"R_PPC64_DTPREL16_HIGH", Const, 10}, + {"R_PPC64_DTPREL16_HIGHA", Const, 10}, + {"R_PPC64_DTPREL16_HIGHER", Const, 5}, + {"R_PPC64_DTPREL16_HIGHERA", Const, 5}, + {"R_PPC64_DTPREL16_HIGHEST", Const, 5}, + {"R_PPC64_DTPREL16_HIGHESTA", Const, 5}, + {"R_PPC64_DTPREL16_LO", Const, 5}, + {"R_PPC64_DTPREL16_LO_DS", Const, 5}, + {"R_PPC64_DTPREL34", Const, 20}, + {"R_PPC64_DTPREL64", Const, 5}, + {"R_PPC64_ENTRY", Const, 10}, + {"R_PPC64_GLOB_DAT", Const, 20}, + {"R_PPC64_GNU_VTENTRY", Const, 20}, + {"R_PPC64_GNU_VTINHERIT", Const, 20}, + {"R_PPC64_GOT16", Const, 5}, + {"R_PPC64_GOT16_DS", Const, 5}, + {"R_PPC64_GOT16_HA", Const, 5}, + {"R_PPC64_GOT16_HI", Const, 5}, + {"R_PPC64_GOT16_LO", Const, 5}, + {"R_PPC64_GOT16_LO_DS", Const, 5}, + {"R_PPC64_GOT_DTPREL16_DS", Const, 5}, + {"R_PPC64_GOT_DTPREL16_HA", Const, 5}, + {"R_PPC64_GOT_DTPREL16_HI", Const, 5}, + {"R_PPC64_GOT_DTPREL16_LO_DS", Const, 5}, + {"R_PPC64_GOT_DTPREL_PCREL34", Const, 20}, + {"R_PPC64_GOT_PCREL34", Const, 20}, + {"R_PPC64_GOT_TLSGD16", Const, 5}, + {"R_PPC64_GOT_TLSGD16_HA", Const, 5}, + {"R_PPC64_GOT_TLSGD16_HI", Const, 5}, + {"R_PPC64_GOT_TLSGD16_LO", Const, 5}, + {"R_PPC64_GOT_TLSGD_PCREL34", Const, 20}, + {"R_PPC64_GOT_TLSLD16", Const, 5}, + {"R_PPC64_GOT_TLSLD16_HA", Const, 5}, + {"R_PPC64_GOT_TLSLD16_HI", Const, 5}, + {"R_PPC64_GOT_TLSLD16_LO", Const, 5}, + {"R_PPC64_GOT_TLSLD_PCREL34", Const, 20}, + {"R_PPC64_GOT_TPREL16_DS", Const, 5}, + {"R_PPC64_GOT_TPREL16_HA", Const, 5}, + {"R_PPC64_GOT_TPREL16_HI", Const, 5}, + {"R_PPC64_GOT_TPREL16_LO_DS", Const, 5}, + {"R_PPC64_GOT_TPREL_PCREL34", Const, 20}, + {"R_PPC64_IRELATIVE", Const, 10}, + {"R_PPC64_JMP_IREL", Const, 10}, + {"R_PPC64_JMP_SLOT", Const, 5}, + {"R_PPC64_NONE", Const, 5}, + {"R_PPC64_PCREL28", Const, 20}, + {"R_PPC64_PCREL34", Const, 20}, + {"R_PPC64_PCREL_OPT", Const, 20}, + {"R_PPC64_PLT16_HA", Const, 20}, + {"R_PPC64_PLT16_HI", Const, 20}, + {"R_PPC64_PLT16_LO", Const, 20}, + {"R_PPC64_PLT16_LO_DS", Const, 10}, + {"R_PPC64_PLT32", Const, 20}, + {"R_PPC64_PLT64", Const, 20}, + {"R_PPC64_PLTCALL", Const, 20}, + {"R_PPC64_PLTCALL_NOTOC", Const, 20}, + {"R_PPC64_PLTGOT16", Const, 10}, + {"R_PPC64_PLTGOT16_DS", Const, 10}, + {"R_PPC64_PLTGOT16_HA", Const, 10}, + {"R_PPC64_PLTGOT16_HI", Const, 10}, + {"R_PPC64_PLTGOT16_LO", Const, 10}, + {"R_PPC64_PLTGOT_LO_DS", Const, 10}, + {"R_PPC64_PLTREL32", Const, 20}, + {"R_PPC64_PLTREL64", Const, 20}, + {"R_PPC64_PLTSEQ", Const, 20}, + {"R_PPC64_PLTSEQ_NOTOC", Const, 20}, + {"R_PPC64_PLT_PCREL34", Const, 20}, + {"R_PPC64_PLT_PCREL34_NOTOC", Const, 20}, + {"R_PPC64_REL14", Const, 5}, + {"R_PPC64_REL14_BRNTAKEN", Const, 5}, + {"R_PPC64_REL14_BRTAKEN", Const, 5}, + {"R_PPC64_REL16", Const, 5}, + {"R_PPC64_REL16DX_HA", Const, 10}, + {"R_PPC64_REL16_HA", Const, 5}, + {"R_PPC64_REL16_HI", Const, 5}, + {"R_PPC64_REL16_HIGH", Const, 20}, + {"R_PPC64_REL16_HIGHA", Const, 20}, + {"R_PPC64_REL16_HIGHER", Const, 20}, + {"R_PPC64_REL16_HIGHER34", Const, 20}, + {"R_PPC64_REL16_HIGHERA", Const, 20}, + {"R_PPC64_REL16_HIGHERA34", Const, 20}, + {"R_PPC64_REL16_HIGHEST", Const, 20}, + {"R_PPC64_REL16_HIGHEST34", Const, 20}, + {"R_PPC64_REL16_HIGHESTA", Const, 20}, + {"R_PPC64_REL16_HIGHESTA34", Const, 20}, + {"R_PPC64_REL16_LO", Const, 5}, + {"R_PPC64_REL24", Const, 5}, + {"R_PPC64_REL24_NOTOC", Const, 10}, + {"R_PPC64_REL24_P9NOTOC", Const, 21}, + {"R_PPC64_REL30", Const, 20}, + {"R_PPC64_REL32", Const, 5}, + {"R_PPC64_REL64", Const, 5}, + {"R_PPC64_RELATIVE", Const, 18}, + {"R_PPC64_SECTOFF", Const, 20}, + {"R_PPC64_SECTOFF_DS", Const, 10}, + {"R_PPC64_SECTOFF_HA", Const, 20}, + {"R_PPC64_SECTOFF_HI", Const, 20}, + {"R_PPC64_SECTOFF_LO", Const, 20}, + {"R_PPC64_SECTOFF_LO_DS", Const, 10}, + {"R_PPC64_TLS", Const, 5}, + {"R_PPC64_TLSGD", Const, 5}, + {"R_PPC64_TLSLD", Const, 5}, + {"R_PPC64_TOC", Const, 5}, + {"R_PPC64_TOC16", Const, 5}, + {"R_PPC64_TOC16_DS", Const, 5}, + {"R_PPC64_TOC16_HA", Const, 5}, + {"R_PPC64_TOC16_HI", Const, 5}, + {"R_PPC64_TOC16_LO", Const, 5}, + {"R_PPC64_TOC16_LO_DS", Const, 5}, + {"R_PPC64_TOCSAVE", Const, 10}, + {"R_PPC64_TPREL16", Const, 5}, + {"R_PPC64_TPREL16_DS", Const, 5}, + {"R_PPC64_TPREL16_HA", Const, 5}, + {"R_PPC64_TPREL16_HI", Const, 5}, + {"R_PPC64_TPREL16_HIGH", Const, 10}, + {"R_PPC64_TPREL16_HIGHA", Const, 10}, + {"R_PPC64_TPREL16_HIGHER", Const, 5}, + {"R_PPC64_TPREL16_HIGHERA", Const, 5}, + {"R_PPC64_TPREL16_HIGHEST", Const, 5}, + {"R_PPC64_TPREL16_HIGHESTA", Const, 5}, + {"R_PPC64_TPREL16_LO", Const, 5}, + {"R_PPC64_TPREL16_LO_DS", Const, 5}, + {"R_PPC64_TPREL34", Const, 20}, + {"R_PPC64_TPREL64", Const, 5}, + {"R_PPC64_UADDR16", Const, 20}, + {"R_PPC64_UADDR32", Const, 20}, + {"R_PPC64_UADDR64", Const, 20}, + {"R_PPC_ADDR14", Const, 0}, + {"R_PPC_ADDR14_BRNTAKEN", Const, 0}, + {"R_PPC_ADDR14_BRTAKEN", Const, 0}, + {"R_PPC_ADDR16", Const, 0}, + {"R_PPC_ADDR16_HA", Const, 0}, + {"R_PPC_ADDR16_HI", Const, 0}, + {"R_PPC_ADDR16_LO", Const, 0}, + {"R_PPC_ADDR24", Const, 0}, + {"R_PPC_ADDR32", Const, 0}, + {"R_PPC_COPY", Const, 0}, + {"R_PPC_DTPMOD32", Const, 0}, + {"R_PPC_DTPREL16", Const, 0}, + {"R_PPC_DTPREL16_HA", Const, 0}, + {"R_PPC_DTPREL16_HI", Const, 0}, + {"R_PPC_DTPREL16_LO", Const, 0}, + {"R_PPC_DTPREL32", Const, 0}, + {"R_PPC_EMB_BIT_FLD", Const, 0}, + {"R_PPC_EMB_MRKREF", Const, 0}, + {"R_PPC_EMB_NADDR16", Const, 0}, + {"R_PPC_EMB_NADDR16_HA", Const, 0}, + {"R_PPC_EMB_NADDR16_HI", Const, 0}, + {"R_PPC_EMB_NADDR16_LO", Const, 0}, + {"R_PPC_EMB_NADDR32", Const, 0}, + {"R_PPC_EMB_RELSDA", Const, 0}, + {"R_PPC_EMB_RELSEC16", Const, 0}, + {"R_PPC_EMB_RELST_HA", Const, 0}, + {"R_PPC_EMB_RELST_HI", Const, 0}, + {"R_PPC_EMB_RELST_LO", Const, 0}, + {"R_PPC_EMB_SDA21", Const, 0}, + {"R_PPC_EMB_SDA2I16", Const, 0}, + {"R_PPC_EMB_SDA2REL", Const, 0}, + {"R_PPC_EMB_SDAI16", Const, 0}, + {"R_PPC_GLOB_DAT", Const, 0}, + {"R_PPC_GOT16", Const, 0}, + {"R_PPC_GOT16_HA", Const, 0}, + {"R_PPC_GOT16_HI", Const, 0}, + {"R_PPC_GOT16_LO", Const, 0}, + {"R_PPC_GOT_TLSGD16", Const, 0}, + {"R_PPC_GOT_TLSGD16_HA", Const, 0}, + {"R_PPC_GOT_TLSGD16_HI", Const, 0}, + {"R_PPC_GOT_TLSGD16_LO", Const, 0}, + {"R_PPC_GOT_TLSLD16", Const, 0}, + {"R_PPC_GOT_TLSLD16_HA", Const, 0}, + {"R_PPC_GOT_TLSLD16_HI", Const, 0}, + {"R_PPC_GOT_TLSLD16_LO", Const, 0}, + {"R_PPC_GOT_TPREL16", Const, 0}, + {"R_PPC_GOT_TPREL16_HA", Const, 0}, + {"R_PPC_GOT_TPREL16_HI", Const, 0}, + {"R_PPC_GOT_TPREL16_LO", Const, 0}, + {"R_PPC_JMP_SLOT", Const, 0}, + {"R_PPC_LOCAL24PC", Const, 0}, + {"R_PPC_NONE", Const, 0}, + {"R_PPC_PLT16_HA", Const, 0}, + {"R_PPC_PLT16_HI", Const, 0}, + {"R_PPC_PLT16_LO", Const, 0}, + {"R_PPC_PLT32", Const, 0}, + {"R_PPC_PLTREL24", Const, 0}, + {"R_PPC_PLTREL32", Const, 0}, + {"R_PPC_REL14", Const, 0}, + {"R_PPC_REL14_BRNTAKEN", Const, 0}, + {"R_PPC_REL14_BRTAKEN", Const, 0}, + {"R_PPC_REL24", Const, 0}, + {"R_PPC_REL32", Const, 0}, + {"R_PPC_RELATIVE", Const, 0}, + {"R_PPC_SDAREL16", Const, 0}, + {"R_PPC_SECTOFF", Const, 0}, + {"R_PPC_SECTOFF_HA", Const, 0}, + {"R_PPC_SECTOFF_HI", Const, 0}, + {"R_PPC_SECTOFF_LO", Const, 0}, + {"R_PPC_TLS", Const, 0}, + {"R_PPC_TPREL16", Const, 0}, + {"R_PPC_TPREL16_HA", Const, 0}, + {"R_PPC_TPREL16_HI", Const, 0}, + {"R_PPC_TPREL16_LO", Const, 0}, + {"R_PPC_TPREL32", Const, 0}, + {"R_PPC_UADDR16", Const, 0}, + {"R_PPC_UADDR32", Const, 0}, + {"R_RISCV", Type, 11}, + {"R_RISCV_32", Const, 11}, + {"R_RISCV_32_PCREL", Const, 12}, + {"R_RISCV_64", Const, 11}, + {"R_RISCV_ADD16", Const, 11}, + {"R_RISCV_ADD32", Const, 11}, + {"R_RISCV_ADD64", Const, 11}, + {"R_RISCV_ADD8", Const, 11}, + {"R_RISCV_ALIGN", Const, 11}, + {"R_RISCV_BRANCH", Const, 11}, + {"R_RISCV_CALL", Const, 11}, + {"R_RISCV_CALL_PLT", Const, 11}, + {"R_RISCV_COPY", Const, 11}, + {"R_RISCV_GNU_VTENTRY", Const, 11}, + {"R_RISCV_GNU_VTINHERIT", Const, 11}, + {"R_RISCV_GOT_HI20", Const, 11}, + {"R_RISCV_GPREL_I", Const, 11}, + {"R_RISCV_GPREL_S", Const, 11}, + {"R_RISCV_HI20", Const, 11}, + {"R_RISCV_JAL", Const, 11}, + {"R_RISCV_JUMP_SLOT", Const, 11}, + {"R_RISCV_LO12_I", Const, 11}, + {"R_RISCV_LO12_S", Const, 11}, + {"R_RISCV_NONE", Const, 11}, + {"R_RISCV_PCREL_HI20", Const, 11}, + {"R_RISCV_PCREL_LO12_I", Const, 11}, + {"R_RISCV_PCREL_LO12_S", Const, 11}, + {"R_RISCV_RELATIVE", Const, 11}, + {"R_RISCV_RELAX", Const, 11}, + {"R_RISCV_RVC_BRANCH", Const, 11}, + {"R_RISCV_RVC_JUMP", Const, 11}, + {"R_RISCV_RVC_LUI", Const, 11}, + {"R_RISCV_SET16", Const, 11}, + {"R_RISCV_SET32", Const, 11}, + {"R_RISCV_SET6", Const, 11}, + {"R_RISCV_SET8", Const, 11}, + {"R_RISCV_SUB16", Const, 11}, + {"R_RISCV_SUB32", Const, 11}, + {"R_RISCV_SUB6", Const, 11}, + {"R_RISCV_SUB64", Const, 11}, + {"R_RISCV_SUB8", Const, 11}, + {"R_RISCV_TLS_DTPMOD32", Const, 11}, + {"R_RISCV_TLS_DTPMOD64", Const, 11}, + {"R_RISCV_TLS_DTPREL32", Const, 11}, + {"R_RISCV_TLS_DTPREL64", Const, 11}, + {"R_RISCV_TLS_GD_HI20", Const, 11}, + {"R_RISCV_TLS_GOT_HI20", Const, 11}, + {"R_RISCV_TLS_TPREL32", Const, 11}, + {"R_RISCV_TLS_TPREL64", Const, 11}, + {"R_RISCV_TPREL_ADD", Const, 11}, + {"R_RISCV_TPREL_HI20", Const, 11}, + {"R_RISCV_TPREL_I", Const, 11}, + {"R_RISCV_TPREL_LO12_I", Const, 11}, + {"R_RISCV_TPREL_LO12_S", Const, 11}, + {"R_RISCV_TPREL_S", Const, 11}, + {"R_SPARC", Type, 0}, + {"R_SPARC_10", Const, 0}, + {"R_SPARC_11", Const, 0}, + {"R_SPARC_13", Const, 0}, + {"R_SPARC_16", Const, 0}, + {"R_SPARC_22", Const, 0}, + {"R_SPARC_32", Const, 0}, + {"R_SPARC_5", Const, 0}, + {"R_SPARC_6", Const, 0}, + {"R_SPARC_64", Const, 0}, + {"R_SPARC_7", Const, 0}, + {"R_SPARC_8", Const, 0}, + {"R_SPARC_COPY", Const, 0}, + {"R_SPARC_DISP16", Const, 0}, + {"R_SPARC_DISP32", Const, 0}, + {"R_SPARC_DISP64", Const, 0}, + {"R_SPARC_DISP8", Const, 0}, + {"R_SPARC_GLOB_DAT", Const, 0}, + {"R_SPARC_GLOB_JMP", Const, 0}, + {"R_SPARC_GOT10", Const, 0}, + {"R_SPARC_GOT13", Const, 0}, + {"R_SPARC_GOT22", Const, 0}, + {"R_SPARC_H44", Const, 0}, + {"R_SPARC_HH22", Const, 0}, + {"R_SPARC_HI22", Const, 0}, + {"R_SPARC_HIPLT22", Const, 0}, + {"R_SPARC_HIX22", Const, 0}, + {"R_SPARC_HM10", Const, 0}, + {"R_SPARC_JMP_SLOT", Const, 0}, + {"R_SPARC_L44", Const, 0}, + {"R_SPARC_LM22", Const, 0}, + {"R_SPARC_LO10", Const, 0}, + {"R_SPARC_LOPLT10", Const, 0}, + {"R_SPARC_LOX10", Const, 0}, + {"R_SPARC_M44", Const, 0}, + {"R_SPARC_NONE", Const, 0}, + {"R_SPARC_OLO10", Const, 0}, + {"R_SPARC_PC10", Const, 0}, + {"R_SPARC_PC22", Const, 0}, + {"R_SPARC_PCPLT10", Const, 0}, + {"R_SPARC_PCPLT22", Const, 0}, + {"R_SPARC_PCPLT32", Const, 0}, + {"R_SPARC_PC_HH22", Const, 0}, + {"R_SPARC_PC_HM10", Const, 0}, + {"R_SPARC_PC_LM22", Const, 0}, + {"R_SPARC_PLT32", Const, 0}, + {"R_SPARC_PLT64", Const, 0}, + {"R_SPARC_REGISTER", Const, 0}, + {"R_SPARC_RELATIVE", Const, 0}, + {"R_SPARC_UA16", Const, 0}, + {"R_SPARC_UA32", Const, 0}, + {"R_SPARC_UA64", Const, 0}, + {"R_SPARC_WDISP16", Const, 0}, + {"R_SPARC_WDISP19", Const, 0}, + {"R_SPARC_WDISP22", Const, 0}, + {"R_SPARC_WDISP30", Const, 0}, + {"R_SPARC_WPLT30", Const, 0}, + {"R_SYM32", Func, 0}, + {"R_SYM64", Func, 0}, + {"R_TYPE32", Func, 0}, + {"R_TYPE64", Func, 0}, + {"R_X86_64", Type, 0}, + {"R_X86_64_16", Const, 0}, + {"R_X86_64_32", Const, 0}, + {"R_X86_64_32S", Const, 0}, + {"R_X86_64_64", Const, 0}, + {"R_X86_64_8", Const, 0}, + {"R_X86_64_COPY", Const, 0}, + {"R_X86_64_DTPMOD64", Const, 0}, + {"R_X86_64_DTPOFF32", Const, 0}, + {"R_X86_64_DTPOFF64", Const, 0}, + {"R_X86_64_GLOB_DAT", Const, 0}, + {"R_X86_64_GOT32", Const, 0}, + {"R_X86_64_GOT64", Const, 10}, + {"R_X86_64_GOTOFF64", Const, 10}, + {"R_X86_64_GOTPC32", Const, 10}, + {"R_X86_64_GOTPC32_TLSDESC", Const, 10}, + {"R_X86_64_GOTPC64", Const, 10}, + {"R_X86_64_GOTPCREL", Const, 0}, + {"R_X86_64_GOTPCREL64", Const, 10}, + {"R_X86_64_GOTPCRELX", Const, 10}, + {"R_X86_64_GOTPLT64", Const, 10}, + {"R_X86_64_GOTTPOFF", Const, 0}, + {"R_X86_64_IRELATIVE", Const, 10}, + {"R_X86_64_JMP_SLOT", Const, 0}, + {"R_X86_64_NONE", Const, 0}, + {"R_X86_64_PC16", Const, 0}, + {"R_X86_64_PC32", Const, 0}, + {"R_X86_64_PC32_BND", Const, 10}, + {"R_X86_64_PC64", Const, 10}, + {"R_X86_64_PC8", Const, 0}, + {"R_X86_64_PLT32", Const, 0}, + {"R_X86_64_PLT32_BND", Const, 10}, + {"R_X86_64_PLTOFF64", Const, 10}, + {"R_X86_64_RELATIVE", Const, 0}, + {"R_X86_64_RELATIVE64", Const, 10}, + {"R_X86_64_REX_GOTPCRELX", Const, 10}, + {"R_X86_64_SIZE32", Const, 10}, + {"R_X86_64_SIZE64", Const, 10}, + {"R_X86_64_TLSDESC", Const, 10}, + {"R_X86_64_TLSDESC_CALL", Const, 10}, + {"R_X86_64_TLSGD", Const, 0}, + {"R_X86_64_TLSLD", Const, 0}, + {"R_X86_64_TPOFF32", Const, 0}, + {"R_X86_64_TPOFF64", Const, 0}, + {"Rel32", Type, 0}, + {"Rel32.Info", Field, 0}, + {"Rel32.Off", Field, 0}, + {"Rel64", Type, 0}, + {"Rel64.Info", Field, 0}, + {"Rel64.Off", Field, 0}, + {"Rela32", Type, 0}, + {"Rela32.Addend", Field, 0}, + {"Rela32.Info", Field, 0}, + {"Rela32.Off", Field, 0}, + {"Rela64", Type, 0}, + {"Rela64.Addend", Field, 0}, + {"Rela64.Info", Field, 0}, + {"Rela64.Off", Field, 0}, + {"SHF_ALLOC", Const, 0}, + {"SHF_COMPRESSED", Const, 6}, + {"SHF_EXECINSTR", Const, 0}, + {"SHF_GROUP", Const, 0}, + {"SHF_INFO_LINK", Const, 0}, + {"SHF_LINK_ORDER", Const, 0}, + {"SHF_MASKOS", Const, 0}, + {"SHF_MASKPROC", Const, 0}, + {"SHF_MERGE", Const, 0}, + {"SHF_OS_NONCONFORMING", Const, 0}, + {"SHF_STRINGS", Const, 0}, + {"SHF_TLS", Const, 0}, + {"SHF_WRITE", Const, 0}, + {"SHN_ABS", Const, 0}, + {"SHN_COMMON", Const, 0}, + {"SHN_HIOS", Const, 0}, + {"SHN_HIPROC", Const, 0}, + {"SHN_HIRESERVE", Const, 0}, + {"SHN_LOOS", Const, 0}, + {"SHN_LOPROC", Const, 0}, + {"SHN_LORESERVE", Const, 0}, + {"SHN_UNDEF", Const, 0}, + {"SHN_XINDEX", Const, 0}, + {"SHT_DYNAMIC", Const, 0}, + {"SHT_DYNSYM", Const, 0}, + {"SHT_FINI_ARRAY", Const, 0}, + {"SHT_GNU_ATTRIBUTES", Const, 0}, + {"SHT_GNU_HASH", Const, 0}, + {"SHT_GNU_LIBLIST", Const, 0}, + {"SHT_GNU_VERDEF", Const, 0}, + {"SHT_GNU_VERNEED", Const, 0}, + {"SHT_GNU_VERSYM", Const, 0}, + {"SHT_GROUP", Const, 0}, + {"SHT_HASH", Const, 0}, + {"SHT_HIOS", Const, 0}, + {"SHT_HIPROC", Const, 0}, + {"SHT_HIUSER", Const, 0}, + {"SHT_INIT_ARRAY", Const, 0}, + {"SHT_LOOS", Const, 0}, + {"SHT_LOPROC", Const, 0}, + {"SHT_LOUSER", Const, 0}, + {"SHT_MIPS_ABIFLAGS", Const, 17}, + {"SHT_NOBITS", Const, 0}, + {"SHT_NOTE", Const, 0}, + {"SHT_NULL", Const, 0}, + {"SHT_PREINIT_ARRAY", Const, 0}, + {"SHT_PROGBITS", Const, 0}, + {"SHT_REL", Const, 0}, + {"SHT_RELA", Const, 0}, + {"SHT_SHLIB", Const, 0}, + {"SHT_STRTAB", Const, 0}, + {"SHT_SYMTAB", Const, 0}, + {"SHT_SYMTAB_SHNDX", Const, 0}, + {"STB_GLOBAL", Const, 0}, + {"STB_HIOS", Const, 0}, + {"STB_HIPROC", Const, 0}, + {"STB_LOCAL", Const, 0}, + {"STB_LOOS", Const, 0}, + {"STB_LOPROC", Const, 0}, + {"STB_WEAK", Const, 0}, + {"STT_COMMON", Const, 0}, + {"STT_FILE", Const, 0}, + {"STT_FUNC", Const, 0}, + {"STT_GNU_IFUNC", Const, 23}, + {"STT_HIOS", Const, 0}, + {"STT_HIPROC", Const, 0}, + {"STT_LOOS", Const, 0}, + {"STT_LOPROC", Const, 0}, + {"STT_NOTYPE", Const, 0}, + {"STT_OBJECT", Const, 0}, + {"STT_RELC", Const, 23}, + {"STT_SECTION", Const, 0}, + {"STT_SRELC", Const, 23}, + {"STT_TLS", Const, 0}, + {"STV_DEFAULT", Const, 0}, + {"STV_HIDDEN", Const, 0}, + {"STV_INTERNAL", Const, 0}, + {"STV_PROTECTED", Const, 0}, + {"ST_BIND", Func, 0}, + {"ST_INFO", Func, 0}, + {"ST_TYPE", Func, 0}, + {"ST_VISIBILITY", Func, 0}, + {"Section", Type, 0}, + {"Section.ReaderAt", Field, 0}, + {"Section.SectionHeader", Field, 0}, + {"Section32", Type, 0}, + {"Section32.Addr", Field, 0}, + {"Section32.Addralign", Field, 0}, + {"Section32.Entsize", Field, 0}, + {"Section32.Flags", Field, 0}, + {"Section32.Info", Field, 0}, + {"Section32.Link", Field, 0}, + {"Section32.Name", Field, 0}, + {"Section32.Off", Field, 0}, + {"Section32.Size", Field, 0}, + {"Section32.Type", Field, 0}, + {"Section64", Type, 0}, + {"Section64.Addr", Field, 0}, + {"Section64.Addralign", Field, 0}, + {"Section64.Entsize", Field, 0}, + {"Section64.Flags", Field, 0}, + {"Section64.Info", Field, 0}, + {"Section64.Link", Field, 0}, + {"Section64.Name", Field, 0}, + {"Section64.Off", Field, 0}, + {"Section64.Size", Field, 0}, + {"Section64.Type", Field, 0}, + {"SectionFlag", Type, 0}, + {"SectionHeader", Type, 0}, + {"SectionHeader.Addr", Field, 0}, + {"SectionHeader.Addralign", Field, 0}, + {"SectionHeader.Entsize", Field, 0}, + {"SectionHeader.FileSize", Field, 6}, + {"SectionHeader.Flags", Field, 0}, + {"SectionHeader.Info", Field, 0}, + {"SectionHeader.Link", Field, 0}, + {"SectionHeader.Name", Field, 0}, + {"SectionHeader.Offset", Field, 0}, + {"SectionHeader.Size", Field, 0}, + {"SectionHeader.Type", Field, 0}, + {"SectionIndex", Type, 0}, + {"SectionType", Type, 0}, + {"Sym32", Type, 0}, + {"Sym32.Info", Field, 0}, + {"Sym32.Name", Field, 0}, + {"Sym32.Other", Field, 0}, + {"Sym32.Shndx", Field, 0}, + {"Sym32.Size", Field, 0}, + {"Sym32.Value", Field, 0}, + {"Sym32Size", Const, 0}, + {"Sym64", Type, 0}, + {"Sym64.Info", Field, 0}, + {"Sym64.Name", Field, 0}, + {"Sym64.Other", Field, 0}, + {"Sym64.Shndx", Field, 0}, + {"Sym64.Size", Field, 0}, + {"Sym64.Value", Field, 0}, + {"Sym64Size", Const, 0}, + {"SymBind", Type, 0}, + {"SymType", Type, 0}, + {"SymVis", Type, 0}, + {"Symbol", Type, 0}, + {"Symbol.Info", Field, 0}, + {"Symbol.Library", Field, 13}, + {"Symbol.Name", Field, 0}, + {"Symbol.Other", Field, 0}, + {"Symbol.Section", Field, 0}, + {"Symbol.Size", Field, 0}, + {"Symbol.Value", Field, 0}, + {"Symbol.Version", Field, 13}, + {"Type", Type, 0}, + {"Version", Type, 0}, + }, + "debug/gosym": { + {"(*DecodingError).Error", Method, 0}, + {"(*LineTable).LineToPC", Method, 0}, + {"(*LineTable).PCToLine", Method, 0}, + {"(*Sym).BaseName", Method, 0}, + {"(*Sym).PackageName", Method, 0}, + {"(*Sym).ReceiverName", Method, 0}, + {"(*Sym).Static", Method, 0}, + {"(*Table).LineToPC", Method, 0}, + {"(*Table).LookupFunc", Method, 0}, + {"(*Table).LookupSym", Method, 0}, + {"(*Table).PCToFunc", Method, 0}, + {"(*Table).PCToLine", Method, 0}, + {"(*Table).SymByAddr", Method, 0}, + {"(*UnknownLineError).Error", Method, 0}, + {"(Func).BaseName", Method, 0}, + {"(Func).PackageName", Method, 0}, + {"(Func).ReceiverName", Method, 0}, + {"(Func).Static", Method, 0}, + {"(UnknownFileError).Error", Method, 0}, + {"DecodingError", Type, 0}, + {"Func", Type, 0}, + {"Func.End", Field, 0}, + {"Func.Entry", Field, 0}, + {"Func.FrameSize", Field, 0}, + {"Func.LineTable", Field, 0}, + {"Func.Locals", Field, 0}, + {"Func.Obj", Field, 0}, + {"Func.Params", Field, 0}, + {"Func.Sym", Field, 0}, + {"LineTable", Type, 0}, + {"LineTable.Data", Field, 0}, + {"LineTable.Line", Field, 0}, + {"LineTable.PC", Field, 0}, + {"NewLineTable", Func, 0}, + {"NewTable", Func, 0}, + {"Obj", Type, 0}, + {"Obj.Funcs", Field, 0}, + {"Obj.Paths", Field, 0}, + {"Sym", Type, 0}, + {"Sym.Func", Field, 0}, + {"Sym.GoType", Field, 0}, + {"Sym.Name", Field, 0}, + {"Sym.Type", Field, 0}, + {"Sym.Value", Field, 0}, + {"Table", Type, 0}, + {"Table.Files", Field, 0}, + {"Table.Funcs", Field, 0}, + {"Table.Objs", Field, 0}, + {"Table.Syms", Field, 0}, + {"UnknownFileError", Type, 0}, + {"UnknownLineError", Type, 0}, + {"UnknownLineError.File", Field, 0}, + {"UnknownLineError.Line", Field, 0}, + }, + "debug/macho": { + {"(*FatFile).Close", Method, 3}, + {"(*File).Close", Method, 0}, + {"(*File).DWARF", Method, 0}, + {"(*File).ImportedLibraries", Method, 0}, + {"(*File).ImportedSymbols", Method, 0}, + {"(*File).Section", Method, 0}, + {"(*File).Segment", Method, 0}, + {"(*FormatError).Error", Method, 0}, + {"(*Section).Data", Method, 0}, + {"(*Section).Open", Method, 0}, + {"(*Segment).Data", Method, 0}, + {"(*Segment).Open", Method, 0}, + {"(Cpu).GoString", Method, 0}, + {"(Cpu).String", Method, 0}, + {"(Dylib).Raw", Method, 0}, + {"(Dysymtab).Raw", Method, 0}, + {"(FatArch).Close", Method, 3}, + {"(FatArch).DWARF", Method, 3}, + {"(FatArch).ImportedLibraries", Method, 3}, + {"(FatArch).ImportedSymbols", Method, 3}, + {"(FatArch).Section", Method, 3}, + {"(FatArch).Segment", Method, 3}, + {"(LoadBytes).Raw", Method, 0}, + {"(LoadCmd).GoString", Method, 0}, + {"(LoadCmd).String", Method, 0}, + {"(RelocTypeARM).GoString", Method, 10}, + {"(RelocTypeARM).String", Method, 10}, + {"(RelocTypeARM64).GoString", Method, 10}, + {"(RelocTypeARM64).String", Method, 10}, + {"(RelocTypeGeneric).GoString", Method, 10}, + {"(RelocTypeGeneric).String", Method, 10}, + {"(RelocTypeX86_64).GoString", Method, 10}, + {"(RelocTypeX86_64).String", Method, 10}, + {"(Rpath).Raw", Method, 10}, + {"(Section).ReadAt", Method, 0}, + {"(Segment).Raw", Method, 0}, + {"(Segment).ReadAt", Method, 0}, + {"(Symtab).Raw", Method, 0}, + {"(Type).GoString", Method, 10}, + {"(Type).String", Method, 10}, + {"ARM64_RELOC_ADDEND", Const, 10}, + {"ARM64_RELOC_BRANCH26", Const, 10}, + {"ARM64_RELOC_GOT_LOAD_PAGE21", Const, 10}, + {"ARM64_RELOC_GOT_LOAD_PAGEOFF12", Const, 10}, + {"ARM64_RELOC_PAGE21", Const, 10}, + {"ARM64_RELOC_PAGEOFF12", Const, 10}, + {"ARM64_RELOC_POINTER_TO_GOT", Const, 10}, + {"ARM64_RELOC_SUBTRACTOR", Const, 10}, + {"ARM64_RELOC_TLVP_LOAD_PAGE21", Const, 10}, + {"ARM64_RELOC_TLVP_LOAD_PAGEOFF12", Const, 10}, + {"ARM64_RELOC_UNSIGNED", Const, 10}, + {"ARM_RELOC_BR24", Const, 10}, + {"ARM_RELOC_HALF", Const, 10}, + {"ARM_RELOC_HALF_SECTDIFF", Const, 10}, + {"ARM_RELOC_LOCAL_SECTDIFF", Const, 10}, + {"ARM_RELOC_PAIR", Const, 10}, + {"ARM_RELOC_PB_LA_PTR", Const, 10}, + {"ARM_RELOC_SECTDIFF", Const, 10}, + {"ARM_RELOC_VANILLA", Const, 10}, + {"ARM_THUMB_32BIT_BRANCH", Const, 10}, + {"ARM_THUMB_RELOC_BR22", Const, 10}, + {"Cpu", Type, 0}, + {"Cpu386", Const, 0}, + {"CpuAmd64", Const, 0}, + {"CpuArm", Const, 3}, + {"CpuArm64", Const, 11}, + {"CpuPpc", Const, 3}, + {"CpuPpc64", Const, 3}, + {"Dylib", Type, 0}, + {"Dylib.CompatVersion", Field, 0}, + {"Dylib.CurrentVersion", Field, 0}, + {"Dylib.LoadBytes", Field, 0}, + {"Dylib.Name", Field, 0}, + {"Dylib.Time", Field, 0}, + {"DylibCmd", Type, 0}, + {"DylibCmd.Cmd", Field, 0}, + {"DylibCmd.CompatVersion", Field, 0}, + {"DylibCmd.CurrentVersion", Field, 0}, + {"DylibCmd.Len", Field, 0}, + {"DylibCmd.Name", Field, 0}, + {"DylibCmd.Time", Field, 0}, + {"Dysymtab", Type, 0}, + {"Dysymtab.DysymtabCmd", Field, 0}, + {"Dysymtab.IndirectSyms", Field, 0}, + {"Dysymtab.LoadBytes", Field, 0}, + {"DysymtabCmd", Type, 0}, + {"DysymtabCmd.Cmd", Field, 0}, + {"DysymtabCmd.Extrefsymoff", Field, 0}, + {"DysymtabCmd.Extreloff", Field, 0}, + {"DysymtabCmd.Iextdefsym", Field, 0}, + {"DysymtabCmd.Ilocalsym", Field, 0}, + {"DysymtabCmd.Indirectsymoff", Field, 0}, + {"DysymtabCmd.Iundefsym", Field, 0}, + {"DysymtabCmd.Len", Field, 0}, + {"DysymtabCmd.Locreloff", Field, 0}, + {"DysymtabCmd.Modtaboff", Field, 0}, + {"DysymtabCmd.Nextdefsym", Field, 0}, + {"DysymtabCmd.Nextrefsyms", Field, 0}, + {"DysymtabCmd.Nextrel", Field, 0}, + {"DysymtabCmd.Nindirectsyms", Field, 0}, + {"DysymtabCmd.Nlocalsym", Field, 0}, + {"DysymtabCmd.Nlocrel", Field, 0}, + {"DysymtabCmd.Nmodtab", Field, 0}, + {"DysymtabCmd.Ntoc", Field, 0}, + {"DysymtabCmd.Nundefsym", Field, 0}, + {"DysymtabCmd.Tocoffset", Field, 0}, + {"ErrNotFat", Var, 3}, + {"FatArch", Type, 3}, + {"FatArch.FatArchHeader", Field, 3}, + {"FatArch.File", Field, 3}, + {"FatArchHeader", Type, 3}, + {"FatArchHeader.Align", Field, 3}, + {"FatArchHeader.Cpu", Field, 3}, + {"FatArchHeader.Offset", Field, 3}, + {"FatArchHeader.Size", Field, 3}, + {"FatArchHeader.SubCpu", Field, 3}, + {"FatFile", Type, 3}, + {"FatFile.Arches", Field, 3}, + {"FatFile.Magic", Field, 3}, + {"File", Type, 0}, + {"File.ByteOrder", Field, 0}, + {"File.Dysymtab", Field, 0}, + {"File.FileHeader", Field, 0}, + {"File.Loads", Field, 0}, + {"File.Sections", Field, 0}, + {"File.Symtab", Field, 0}, + {"FileHeader", Type, 0}, + {"FileHeader.Cmdsz", Field, 0}, + {"FileHeader.Cpu", Field, 0}, + {"FileHeader.Flags", Field, 0}, + {"FileHeader.Magic", Field, 0}, + {"FileHeader.Ncmd", Field, 0}, + {"FileHeader.SubCpu", Field, 0}, + {"FileHeader.Type", Field, 0}, + {"FlagAllModsBound", Const, 10}, + {"FlagAllowStackExecution", Const, 10}, + {"FlagAppExtensionSafe", Const, 10}, + {"FlagBindAtLoad", Const, 10}, + {"FlagBindsToWeak", Const, 10}, + {"FlagCanonical", Const, 10}, + {"FlagDeadStrippableDylib", Const, 10}, + {"FlagDyldLink", Const, 10}, + {"FlagForceFlat", Const, 10}, + {"FlagHasTLVDescriptors", Const, 10}, + {"FlagIncrLink", Const, 10}, + {"FlagLazyInit", Const, 10}, + {"FlagNoFixPrebinding", Const, 10}, + {"FlagNoHeapExecution", Const, 10}, + {"FlagNoMultiDefs", Const, 10}, + {"FlagNoReexportedDylibs", Const, 10}, + {"FlagNoUndefs", Const, 10}, + {"FlagPIE", Const, 10}, + {"FlagPrebindable", Const, 10}, + {"FlagPrebound", Const, 10}, + {"FlagRootSafe", Const, 10}, + {"FlagSetuidSafe", Const, 10}, + {"FlagSplitSegs", Const, 10}, + {"FlagSubsectionsViaSymbols", Const, 10}, + {"FlagTwoLevel", Const, 10}, + {"FlagWeakDefines", Const, 10}, + {"FormatError", Type, 0}, + {"GENERIC_RELOC_LOCAL_SECTDIFF", Const, 10}, + {"GENERIC_RELOC_PAIR", Const, 10}, + {"GENERIC_RELOC_PB_LA_PTR", Const, 10}, + {"GENERIC_RELOC_SECTDIFF", Const, 10}, + {"GENERIC_RELOC_TLV", Const, 10}, + {"GENERIC_RELOC_VANILLA", Const, 10}, + {"Load", Type, 0}, + {"LoadBytes", Type, 0}, + {"LoadCmd", Type, 0}, + {"LoadCmdDylib", Const, 0}, + {"LoadCmdDylinker", Const, 0}, + {"LoadCmdDysymtab", Const, 0}, + {"LoadCmdRpath", Const, 10}, + {"LoadCmdSegment", Const, 0}, + {"LoadCmdSegment64", Const, 0}, + {"LoadCmdSymtab", Const, 0}, + {"LoadCmdThread", Const, 0}, + {"LoadCmdUnixThread", Const, 0}, + {"Magic32", Const, 0}, + {"Magic64", Const, 0}, + {"MagicFat", Const, 3}, + {"NewFatFile", Func, 3}, + {"NewFile", Func, 0}, + {"Nlist32", Type, 0}, + {"Nlist32.Desc", Field, 0}, + {"Nlist32.Name", Field, 0}, + {"Nlist32.Sect", Field, 0}, + {"Nlist32.Type", Field, 0}, + {"Nlist32.Value", Field, 0}, + {"Nlist64", Type, 0}, + {"Nlist64.Desc", Field, 0}, + {"Nlist64.Name", Field, 0}, + {"Nlist64.Sect", Field, 0}, + {"Nlist64.Type", Field, 0}, + {"Nlist64.Value", Field, 0}, + {"Open", Func, 0}, + {"OpenFat", Func, 3}, + {"Regs386", Type, 0}, + {"Regs386.AX", Field, 0}, + {"Regs386.BP", Field, 0}, + {"Regs386.BX", Field, 0}, + {"Regs386.CS", Field, 0}, + {"Regs386.CX", Field, 0}, + {"Regs386.DI", Field, 0}, + {"Regs386.DS", Field, 0}, + {"Regs386.DX", Field, 0}, + {"Regs386.ES", Field, 0}, + {"Regs386.FLAGS", Field, 0}, + {"Regs386.FS", Field, 0}, + {"Regs386.GS", Field, 0}, + {"Regs386.IP", Field, 0}, + {"Regs386.SI", Field, 0}, + {"Regs386.SP", Field, 0}, + {"Regs386.SS", Field, 0}, + {"RegsAMD64", Type, 0}, + {"RegsAMD64.AX", Field, 0}, + {"RegsAMD64.BP", Field, 0}, + {"RegsAMD64.BX", Field, 0}, + {"RegsAMD64.CS", Field, 0}, + {"RegsAMD64.CX", Field, 0}, + {"RegsAMD64.DI", Field, 0}, + {"RegsAMD64.DX", Field, 0}, + {"RegsAMD64.FLAGS", Field, 0}, + {"RegsAMD64.FS", Field, 0}, + {"RegsAMD64.GS", Field, 0}, + {"RegsAMD64.IP", Field, 0}, + {"RegsAMD64.R10", Field, 0}, + {"RegsAMD64.R11", Field, 0}, + {"RegsAMD64.R12", Field, 0}, + {"RegsAMD64.R13", Field, 0}, + {"RegsAMD64.R14", Field, 0}, + {"RegsAMD64.R15", Field, 0}, + {"RegsAMD64.R8", Field, 0}, + {"RegsAMD64.R9", Field, 0}, + {"RegsAMD64.SI", Field, 0}, + {"RegsAMD64.SP", Field, 0}, + {"Reloc", Type, 10}, + {"Reloc.Addr", Field, 10}, + {"Reloc.Extern", Field, 10}, + {"Reloc.Len", Field, 10}, + {"Reloc.Pcrel", Field, 10}, + {"Reloc.Scattered", Field, 10}, + {"Reloc.Type", Field, 10}, + {"Reloc.Value", Field, 10}, + {"RelocTypeARM", Type, 10}, + {"RelocTypeARM64", Type, 10}, + {"RelocTypeGeneric", Type, 10}, + {"RelocTypeX86_64", Type, 10}, + {"Rpath", Type, 10}, + {"Rpath.LoadBytes", Field, 10}, + {"Rpath.Path", Field, 10}, + {"RpathCmd", Type, 10}, + {"RpathCmd.Cmd", Field, 10}, + {"RpathCmd.Len", Field, 10}, + {"RpathCmd.Path", Field, 10}, + {"Section", Type, 0}, + {"Section.ReaderAt", Field, 0}, + {"Section.Relocs", Field, 10}, + {"Section.SectionHeader", Field, 0}, + {"Section32", Type, 0}, + {"Section32.Addr", Field, 0}, + {"Section32.Align", Field, 0}, + {"Section32.Flags", Field, 0}, + {"Section32.Name", Field, 0}, + {"Section32.Nreloc", Field, 0}, + {"Section32.Offset", Field, 0}, + {"Section32.Reloff", Field, 0}, + {"Section32.Reserve1", Field, 0}, + {"Section32.Reserve2", Field, 0}, + {"Section32.Seg", Field, 0}, + {"Section32.Size", Field, 0}, + {"Section64", Type, 0}, + {"Section64.Addr", Field, 0}, + {"Section64.Align", Field, 0}, + {"Section64.Flags", Field, 0}, + {"Section64.Name", Field, 0}, + {"Section64.Nreloc", Field, 0}, + {"Section64.Offset", Field, 0}, + {"Section64.Reloff", Field, 0}, + {"Section64.Reserve1", Field, 0}, + {"Section64.Reserve2", Field, 0}, + {"Section64.Reserve3", Field, 0}, + {"Section64.Seg", Field, 0}, + {"Section64.Size", Field, 0}, + {"SectionHeader", Type, 0}, + {"SectionHeader.Addr", Field, 0}, + {"SectionHeader.Align", Field, 0}, + {"SectionHeader.Flags", Field, 0}, + {"SectionHeader.Name", Field, 0}, + {"SectionHeader.Nreloc", Field, 0}, + {"SectionHeader.Offset", Field, 0}, + {"SectionHeader.Reloff", Field, 0}, + {"SectionHeader.Seg", Field, 0}, + {"SectionHeader.Size", Field, 0}, + {"Segment", Type, 0}, + {"Segment.LoadBytes", Field, 0}, + {"Segment.ReaderAt", Field, 0}, + {"Segment.SegmentHeader", Field, 0}, + {"Segment32", Type, 0}, + {"Segment32.Addr", Field, 0}, + {"Segment32.Cmd", Field, 0}, + {"Segment32.Filesz", Field, 0}, + {"Segment32.Flag", Field, 0}, + {"Segment32.Len", Field, 0}, + {"Segment32.Maxprot", Field, 0}, + {"Segment32.Memsz", Field, 0}, + {"Segment32.Name", Field, 0}, + {"Segment32.Nsect", Field, 0}, + {"Segment32.Offset", Field, 0}, + {"Segment32.Prot", Field, 0}, + {"Segment64", Type, 0}, + {"Segment64.Addr", Field, 0}, + {"Segment64.Cmd", Field, 0}, + {"Segment64.Filesz", Field, 0}, + {"Segment64.Flag", Field, 0}, + {"Segment64.Len", Field, 0}, + {"Segment64.Maxprot", Field, 0}, + {"Segment64.Memsz", Field, 0}, + {"Segment64.Name", Field, 0}, + {"Segment64.Nsect", Field, 0}, + {"Segment64.Offset", Field, 0}, + {"Segment64.Prot", Field, 0}, + {"SegmentHeader", Type, 0}, + {"SegmentHeader.Addr", Field, 0}, + {"SegmentHeader.Cmd", Field, 0}, + {"SegmentHeader.Filesz", Field, 0}, + {"SegmentHeader.Flag", Field, 0}, + {"SegmentHeader.Len", Field, 0}, + {"SegmentHeader.Maxprot", Field, 0}, + {"SegmentHeader.Memsz", Field, 0}, + {"SegmentHeader.Name", Field, 0}, + {"SegmentHeader.Nsect", Field, 0}, + {"SegmentHeader.Offset", Field, 0}, + {"SegmentHeader.Prot", Field, 0}, + {"Symbol", Type, 0}, + {"Symbol.Desc", Field, 0}, + {"Symbol.Name", Field, 0}, + {"Symbol.Sect", Field, 0}, + {"Symbol.Type", Field, 0}, + {"Symbol.Value", Field, 0}, + {"Symtab", Type, 0}, + {"Symtab.LoadBytes", Field, 0}, + {"Symtab.Syms", Field, 0}, + {"Symtab.SymtabCmd", Field, 0}, + {"SymtabCmd", Type, 0}, + {"SymtabCmd.Cmd", Field, 0}, + {"SymtabCmd.Len", Field, 0}, + {"SymtabCmd.Nsyms", Field, 0}, + {"SymtabCmd.Stroff", Field, 0}, + {"SymtabCmd.Strsize", Field, 0}, + {"SymtabCmd.Symoff", Field, 0}, + {"Thread", Type, 0}, + {"Thread.Cmd", Field, 0}, + {"Thread.Data", Field, 0}, + {"Thread.Len", Field, 0}, + {"Thread.Type", Field, 0}, + {"Type", Type, 0}, + {"TypeBundle", Const, 3}, + {"TypeDylib", Const, 3}, + {"TypeExec", Const, 0}, + {"TypeObj", Const, 0}, + {"X86_64_RELOC_BRANCH", Const, 10}, + {"X86_64_RELOC_GOT", Const, 10}, + {"X86_64_RELOC_GOT_LOAD", Const, 10}, + {"X86_64_RELOC_SIGNED", Const, 10}, + {"X86_64_RELOC_SIGNED_1", Const, 10}, + {"X86_64_RELOC_SIGNED_2", Const, 10}, + {"X86_64_RELOC_SIGNED_4", Const, 10}, + {"X86_64_RELOC_SUBTRACTOR", Const, 10}, + {"X86_64_RELOC_TLV", Const, 10}, + {"X86_64_RELOC_UNSIGNED", Const, 10}, + }, + "debug/pe": { + {"(*COFFSymbol).FullName", Method, 8}, + {"(*File).COFFSymbolReadSectionDefAux", Method, 19}, + {"(*File).Close", Method, 0}, + {"(*File).DWARF", Method, 0}, + {"(*File).ImportedLibraries", Method, 0}, + {"(*File).ImportedSymbols", Method, 0}, + {"(*File).Section", Method, 0}, + {"(*FormatError).Error", Method, 0}, + {"(*Section).Data", Method, 0}, + {"(*Section).Open", Method, 0}, + {"(Section).ReadAt", Method, 0}, + {"(StringTable).String", Method, 8}, + {"COFFSymbol", Type, 1}, + {"COFFSymbol.Name", Field, 1}, + {"COFFSymbol.NumberOfAuxSymbols", Field, 1}, + {"COFFSymbol.SectionNumber", Field, 1}, + {"COFFSymbol.StorageClass", Field, 1}, + {"COFFSymbol.Type", Field, 1}, + {"COFFSymbol.Value", Field, 1}, + {"COFFSymbolAuxFormat5", Type, 19}, + {"COFFSymbolAuxFormat5.Checksum", Field, 19}, + {"COFFSymbolAuxFormat5.NumLineNumbers", Field, 19}, + {"COFFSymbolAuxFormat5.NumRelocs", Field, 19}, + {"COFFSymbolAuxFormat5.SecNum", Field, 19}, + {"COFFSymbolAuxFormat5.Selection", Field, 19}, + {"COFFSymbolAuxFormat5.Size", Field, 19}, + {"COFFSymbolSize", Const, 1}, + {"DataDirectory", Type, 3}, + {"DataDirectory.Size", Field, 3}, + {"DataDirectory.VirtualAddress", Field, 3}, + {"File", Type, 0}, + {"File.COFFSymbols", Field, 8}, + {"File.FileHeader", Field, 0}, + {"File.OptionalHeader", Field, 3}, + {"File.Sections", Field, 0}, + {"File.StringTable", Field, 8}, + {"File.Symbols", Field, 1}, + {"FileHeader", Type, 0}, + {"FileHeader.Characteristics", Field, 0}, + {"FileHeader.Machine", Field, 0}, + {"FileHeader.NumberOfSections", Field, 0}, + {"FileHeader.NumberOfSymbols", Field, 0}, + {"FileHeader.PointerToSymbolTable", Field, 0}, + {"FileHeader.SizeOfOptionalHeader", Field, 0}, + {"FileHeader.TimeDateStamp", Field, 0}, + {"FormatError", Type, 0}, + {"IMAGE_COMDAT_SELECT_ANY", Const, 19}, + {"IMAGE_COMDAT_SELECT_ASSOCIATIVE", Const, 19}, + {"IMAGE_COMDAT_SELECT_EXACT_MATCH", Const, 19}, + {"IMAGE_COMDAT_SELECT_LARGEST", Const, 19}, + {"IMAGE_COMDAT_SELECT_NODUPLICATES", Const, 19}, + {"IMAGE_COMDAT_SELECT_SAME_SIZE", Const, 19}, + {"IMAGE_DIRECTORY_ENTRY_ARCHITECTURE", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_BASERELOC", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_DEBUG", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_EXCEPTION", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_EXPORT", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_GLOBALPTR", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_IAT", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_IMPORT", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_RESOURCE", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_SECURITY", Const, 11}, + {"IMAGE_DIRECTORY_ENTRY_TLS", Const, 11}, + {"IMAGE_DLLCHARACTERISTICS_APPCONTAINER", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_GUARD_CF", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_NO_BIND", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_NO_SEH", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_NX_COMPAT", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", Const, 15}, + {"IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", Const, 15}, + {"IMAGE_FILE_32BIT_MACHINE", Const, 15}, + {"IMAGE_FILE_AGGRESIVE_WS_TRIM", Const, 15}, + {"IMAGE_FILE_BYTES_REVERSED_HI", Const, 15}, + {"IMAGE_FILE_BYTES_REVERSED_LO", Const, 15}, + {"IMAGE_FILE_DEBUG_STRIPPED", Const, 15}, + {"IMAGE_FILE_DLL", Const, 15}, + {"IMAGE_FILE_EXECUTABLE_IMAGE", Const, 15}, + {"IMAGE_FILE_LARGE_ADDRESS_AWARE", Const, 15}, + {"IMAGE_FILE_LINE_NUMS_STRIPPED", Const, 15}, + {"IMAGE_FILE_LOCAL_SYMS_STRIPPED", Const, 15}, + {"IMAGE_FILE_MACHINE_AM33", Const, 0}, + {"IMAGE_FILE_MACHINE_AMD64", Const, 0}, + {"IMAGE_FILE_MACHINE_ARM", Const, 0}, + {"IMAGE_FILE_MACHINE_ARM64", Const, 11}, + {"IMAGE_FILE_MACHINE_ARMNT", Const, 12}, + {"IMAGE_FILE_MACHINE_EBC", Const, 0}, + {"IMAGE_FILE_MACHINE_I386", Const, 0}, + {"IMAGE_FILE_MACHINE_IA64", Const, 0}, + {"IMAGE_FILE_MACHINE_LOONGARCH32", Const, 19}, + {"IMAGE_FILE_MACHINE_LOONGARCH64", Const, 19}, + {"IMAGE_FILE_MACHINE_M32R", Const, 0}, + {"IMAGE_FILE_MACHINE_MIPS16", Const, 0}, + {"IMAGE_FILE_MACHINE_MIPSFPU", Const, 0}, + {"IMAGE_FILE_MACHINE_MIPSFPU16", Const, 0}, + {"IMAGE_FILE_MACHINE_POWERPC", Const, 0}, + {"IMAGE_FILE_MACHINE_POWERPCFP", Const, 0}, + {"IMAGE_FILE_MACHINE_R4000", Const, 0}, + {"IMAGE_FILE_MACHINE_RISCV128", Const, 20}, + {"IMAGE_FILE_MACHINE_RISCV32", Const, 20}, + {"IMAGE_FILE_MACHINE_RISCV64", Const, 20}, + {"IMAGE_FILE_MACHINE_SH3", Const, 0}, + {"IMAGE_FILE_MACHINE_SH3DSP", Const, 0}, + {"IMAGE_FILE_MACHINE_SH4", Const, 0}, + {"IMAGE_FILE_MACHINE_SH5", Const, 0}, + {"IMAGE_FILE_MACHINE_THUMB", Const, 0}, + {"IMAGE_FILE_MACHINE_UNKNOWN", Const, 0}, + {"IMAGE_FILE_MACHINE_WCEMIPSV2", Const, 0}, + {"IMAGE_FILE_NET_RUN_FROM_SWAP", Const, 15}, + {"IMAGE_FILE_RELOCS_STRIPPED", Const, 15}, + {"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", Const, 15}, + {"IMAGE_FILE_SYSTEM", Const, 15}, + {"IMAGE_FILE_UP_SYSTEM_ONLY", Const, 15}, + {"IMAGE_SCN_CNT_CODE", Const, 19}, + {"IMAGE_SCN_CNT_INITIALIZED_DATA", Const, 19}, + {"IMAGE_SCN_CNT_UNINITIALIZED_DATA", Const, 19}, + {"IMAGE_SCN_LNK_COMDAT", Const, 19}, + {"IMAGE_SCN_MEM_DISCARDABLE", Const, 19}, + {"IMAGE_SCN_MEM_EXECUTE", Const, 19}, + {"IMAGE_SCN_MEM_READ", Const, 19}, + {"IMAGE_SCN_MEM_WRITE", Const, 19}, + {"IMAGE_SUBSYSTEM_EFI_APPLICATION", Const, 15}, + {"IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", Const, 15}, + {"IMAGE_SUBSYSTEM_EFI_ROM", Const, 15}, + {"IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", Const, 15}, + {"IMAGE_SUBSYSTEM_NATIVE", Const, 15}, + {"IMAGE_SUBSYSTEM_NATIVE_WINDOWS", Const, 15}, + {"IMAGE_SUBSYSTEM_OS2_CUI", Const, 15}, + {"IMAGE_SUBSYSTEM_POSIX_CUI", Const, 15}, + {"IMAGE_SUBSYSTEM_UNKNOWN", Const, 15}, + {"IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION", Const, 15}, + {"IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", Const, 15}, + {"IMAGE_SUBSYSTEM_WINDOWS_CUI", Const, 15}, + {"IMAGE_SUBSYSTEM_WINDOWS_GUI", Const, 15}, + {"IMAGE_SUBSYSTEM_XBOX", Const, 15}, + {"ImportDirectory", Type, 0}, + {"ImportDirectory.FirstThunk", Field, 0}, + {"ImportDirectory.ForwarderChain", Field, 0}, + {"ImportDirectory.Name", Field, 0}, + {"ImportDirectory.OriginalFirstThunk", Field, 0}, + {"ImportDirectory.TimeDateStamp", Field, 0}, + {"NewFile", Func, 0}, + {"Open", Func, 0}, + {"OptionalHeader32", Type, 3}, + {"OptionalHeader32.AddressOfEntryPoint", Field, 3}, + {"OptionalHeader32.BaseOfCode", Field, 3}, + {"OptionalHeader32.BaseOfData", Field, 3}, + {"OptionalHeader32.CheckSum", Field, 3}, + {"OptionalHeader32.DataDirectory", Field, 3}, + {"OptionalHeader32.DllCharacteristics", Field, 3}, + {"OptionalHeader32.FileAlignment", Field, 3}, + {"OptionalHeader32.ImageBase", Field, 3}, + {"OptionalHeader32.LoaderFlags", Field, 3}, + {"OptionalHeader32.Magic", Field, 3}, + {"OptionalHeader32.MajorImageVersion", Field, 3}, + {"OptionalHeader32.MajorLinkerVersion", Field, 3}, + {"OptionalHeader32.MajorOperatingSystemVersion", Field, 3}, + {"OptionalHeader32.MajorSubsystemVersion", Field, 3}, + {"OptionalHeader32.MinorImageVersion", Field, 3}, + {"OptionalHeader32.MinorLinkerVersion", Field, 3}, + {"OptionalHeader32.MinorOperatingSystemVersion", Field, 3}, + {"OptionalHeader32.MinorSubsystemVersion", Field, 3}, + {"OptionalHeader32.NumberOfRvaAndSizes", Field, 3}, + {"OptionalHeader32.SectionAlignment", Field, 3}, + {"OptionalHeader32.SizeOfCode", Field, 3}, + {"OptionalHeader32.SizeOfHeaders", Field, 3}, + {"OptionalHeader32.SizeOfHeapCommit", Field, 3}, + {"OptionalHeader32.SizeOfHeapReserve", Field, 3}, + {"OptionalHeader32.SizeOfImage", Field, 3}, + {"OptionalHeader32.SizeOfInitializedData", Field, 3}, + {"OptionalHeader32.SizeOfStackCommit", Field, 3}, + {"OptionalHeader32.SizeOfStackReserve", Field, 3}, + {"OptionalHeader32.SizeOfUninitializedData", Field, 3}, + {"OptionalHeader32.Subsystem", Field, 3}, + {"OptionalHeader32.Win32VersionValue", Field, 3}, + {"OptionalHeader64", Type, 3}, + {"OptionalHeader64.AddressOfEntryPoint", Field, 3}, + {"OptionalHeader64.BaseOfCode", Field, 3}, + {"OptionalHeader64.CheckSum", Field, 3}, + {"OptionalHeader64.DataDirectory", Field, 3}, + {"OptionalHeader64.DllCharacteristics", Field, 3}, + {"OptionalHeader64.FileAlignment", Field, 3}, + {"OptionalHeader64.ImageBase", Field, 3}, + {"OptionalHeader64.LoaderFlags", Field, 3}, + {"OptionalHeader64.Magic", Field, 3}, + {"OptionalHeader64.MajorImageVersion", Field, 3}, + {"OptionalHeader64.MajorLinkerVersion", Field, 3}, + {"OptionalHeader64.MajorOperatingSystemVersion", Field, 3}, + {"OptionalHeader64.MajorSubsystemVersion", Field, 3}, + {"OptionalHeader64.MinorImageVersion", Field, 3}, + {"OptionalHeader64.MinorLinkerVersion", Field, 3}, + {"OptionalHeader64.MinorOperatingSystemVersion", Field, 3}, + {"OptionalHeader64.MinorSubsystemVersion", Field, 3}, + {"OptionalHeader64.NumberOfRvaAndSizes", Field, 3}, + {"OptionalHeader64.SectionAlignment", Field, 3}, + {"OptionalHeader64.SizeOfCode", Field, 3}, + {"OptionalHeader64.SizeOfHeaders", Field, 3}, + {"OptionalHeader64.SizeOfHeapCommit", Field, 3}, + {"OptionalHeader64.SizeOfHeapReserve", Field, 3}, + {"OptionalHeader64.SizeOfImage", Field, 3}, + {"OptionalHeader64.SizeOfInitializedData", Field, 3}, + {"OptionalHeader64.SizeOfStackCommit", Field, 3}, + {"OptionalHeader64.SizeOfStackReserve", Field, 3}, + {"OptionalHeader64.SizeOfUninitializedData", Field, 3}, + {"OptionalHeader64.Subsystem", Field, 3}, + {"OptionalHeader64.Win32VersionValue", Field, 3}, + {"Reloc", Type, 8}, + {"Reloc.SymbolTableIndex", Field, 8}, + {"Reloc.Type", Field, 8}, + {"Reloc.VirtualAddress", Field, 8}, + {"Section", Type, 0}, + {"Section.ReaderAt", Field, 0}, + {"Section.Relocs", Field, 8}, + {"Section.SectionHeader", Field, 0}, + {"SectionHeader", Type, 0}, + {"SectionHeader.Characteristics", Field, 0}, + {"SectionHeader.Name", Field, 0}, + {"SectionHeader.NumberOfLineNumbers", Field, 0}, + {"SectionHeader.NumberOfRelocations", Field, 0}, + {"SectionHeader.Offset", Field, 0}, + {"SectionHeader.PointerToLineNumbers", Field, 0}, + {"SectionHeader.PointerToRelocations", Field, 0}, + {"SectionHeader.Size", Field, 0}, + {"SectionHeader.VirtualAddress", Field, 0}, + {"SectionHeader.VirtualSize", Field, 0}, + {"SectionHeader32", Type, 0}, + {"SectionHeader32.Characteristics", Field, 0}, + {"SectionHeader32.Name", Field, 0}, + {"SectionHeader32.NumberOfLineNumbers", Field, 0}, + {"SectionHeader32.NumberOfRelocations", Field, 0}, + {"SectionHeader32.PointerToLineNumbers", Field, 0}, + {"SectionHeader32.PointerToRawData", Field, 0}, + {"SectionHeader32.PointerToRelocations", Field, 0}, + {"SectionHeader32.SizeOfRawData", Field, 0}, + {"SectionHeader32.VirtualAddress", Field, 0}, + {"SectionHeader32.VirtualSize", Field, 0}, + {"StringTable", Type, 8}, + {"Symbol", Type, 1}, + {"Symbol.Name", Field, 1}, + {"Symbol.SectionNumber", Field, 1}, + {"Symbol.StorageClass", Field, 1}, + {"Symbol.Type", Field, 1}, + {"Symbol.Value", Field, 1}, + }, + "debug/plan9obj": { + {"(*File).Close", Method, 3}, + {"(*File).Section", Method, 3}, + {"(*File).Symbols", Method, 3}, + {"(*Section).Data", Method, 3}, + {"(*Section).Open", Method, 3}, + {"(Section).ReadAt", Method, 3}, + {"ErrNoSymbols", Var, 18}, + {"File", Type, 3}, + {"File.FileHeader", Field, 3}, + {"File.Sections", Field, 3}, + {"FileHeader", Type, 3}, + {"FileHeader.Bss", Field, 3}, + {"FileHeader.Entry", Field, 3}, + {"FileHeader.HdrSize", Field, 4}, + {"FileHeader.LoadAddress", Field, 4}, + {"FileHeader.Magic", Field, 3}, + {"FileHeader.PtrSize", Field, 3}, + {"Magic386", Const, 3}, + {"Magic64", Const, 3}, + {"MagicAMD64", Const, 3}, + {"MagicARM", Const, 3}, + {"NewFile", Func, 3}, + {"Open", Func, 3}, + {"Section", Type, 3}, + {"Section.ReaderAt", Field, 3}, + {"Section.SectionHeader", Field, 3}, + {"SectionHeader", Type, 3}, + {"SectionHeader.Name", Field, 3}, + {"SectionHeader.Offset", Field, 3}, + {"SectionHeader.Size", Field, 3}, + {"Sym", Type, 3}, + {"Sym.Name", Field, 3}, + {"Sym.Type", Field, 3}, + {"Sym.Value", Field, 3}, + }, + "embed": { + {"(FS).Open", Method, 16}, + {"(FS).ReadDir", Method, 16}, + {"(FS).ReadFile", Method, 16}, + {"FS", Type, 16}, + }, + "encoding": { + {"BinaryMarshaler", Type, 2}, + {"BinaryUnmarshaler", Type, 2}, + {"TextMarshaler", Type, 2}, + {"TextUnmarshaler", Type, 2}, + }, + "encoding/ascii85": { + {"(CorruptInputError).Error", Method, 0}, + {"CorruptInputError", Type, 0}, + {"Decode", Func, 0}, + {"Encode", Func, 0}, + {"MaxEncodedLen", Func, 0}, + {"NewDecoder", Func, 0}, + {"NewEncoder", Func, 0}, + }, + "encoding/asn1": { + {"(BitString).At", Method, 0}, + {"(BitString).RightAlign", Method, 0}, + {"(ObjectIdentifier).Equal", Method, 0}, + {"(ObjectIdentifier).String", Method, 3}, + {"(StructuralError).Error", Method, 0}, + {"(SyntaxError).Error", Method, 0}, + {"BitString", Type, 0}, + {"BitString.BitLength", Field, 0}, + {"BitString.Bytes", Field, 0}, + {"ClassApplication", Const, 6}, + {"ClassContextSpecific", Const, 6}, + {"ClassPrivate", Const, 6}, + {"ClassUniversal", Const, 6}, + {"Enumerated", Type, 0}, + {"Flag", Type, 0}, + {"Marshal", Func, 0}, + {"MarshalWithParams", Func, 10}, + {"NullBytes", Var, 9}, + {"NullRawValue", Var, 9}, + {"ObjectIdentifier", Type, 0}, + {"RawContent", Type, 0}, + {"RawValue", Type, 0}, + {"RawValue.Bytes", Field, 0}, + {"RawValue.Class", Field, 0}, + {"RawValue.FullBytes", Field, 0}, + {"RawValue.IsCompound", Field, 0}, + {"RawValue.Tag", Field, 0}, + {"StructuralError", Type, 0}, + {"StructuralError.Msg", Field, 0}, + {"SyntaxError", Type, 0}, + {"SyntaxError.Msg", Field, 0}, + {"TagBMPString", Const, 14}, + {"TagBitString", Const, 6}, + {"TagBoolean", Const, 6}, + {"TagEnum", Const, 6}, + {"TagGeneralString", Const, 6}, + {"TagGeneralizedTime", Const, 6}, + {"TagIA5String", Const, 6}, + {"TagInteger", Const, 6}, + {"TagNull", Const, 9}, + {"TagNumericString", Const, 10}, + {"TagOID", Const, 6}, + {"TagOctetString", Const, 6}, + {"TagPrintableString", Const, 6}, + {"TagSequence", Const, 6}, + {"TagSet", Const, 6}, + {"TagT61String", Const, 6}, + {"TagUTCTime", Const, 6}, + {"TagUTF8String", Const, 6}, + {"Unmarshal", Func, 0}, + {"UnmarshalWithParams", Func, 0}, + }, + "encoding/base32": { + {"(*Encoding).AppendDecode", Method, 22}, + {"(*Encoding).AppendEncode", Method, 22}, + {"(*Encoding).Decode", Method, 0}, + {"(*Encoding).DecodeString", Method, 0}, + {"(*Encoding).DecodedLen", Method, 0}, + {"(*Encoding).Encode", Method, 0}, + {"(*Encoding).EncodeToString", Method, 0}, + {"(*Encoding).EncodedLen", Method, 0}, + {"(CorruptInputError).Error", Method, 0}, + {"(Encoding).WithPadding", Method, 9}, + {"CorruptInputError", Type, 0}, + {"Encoding", Type, 0}, + {"HexEncoding", Var, 0}, + {"NewDecoder", Func, 0}, + {"NewEncoder", Func, 0}, + {"NewEncoding", Func, 0}, + {"NoPadding", Const, 9}, + {"StdEncoding", Var, 0}, + {"StdPadding", Const, 9}, + }, + "encoding/base64": { + {"(*Encoding).AppendDecode", Method, 22}, + {"(*Encoding).AppendEncode", Method, 22}, + {"(*Encoding).Decode", Method, 0}, + {"(*Encoding).DecodeString", Method, 0}, + {"(*Encoding).DecodedLen", Method, 0}, + {"(*Encoding).Encode", Method, 0}, + {"(*Encoding).EncodeToString", Method, 0}, + {"(*Encoding).EncodedLen", Method, 0}, + {"(CorruptInputError).Error", Method, 0}, + {"(Encoding).Strict", Method, 8}, + {"(Encoding).WithPadding", Method, 5}, + {"CorruptInputError", Type, 0}, + {"Encoding", Type, 0}, + {"NewDecoder", Func, 0}, + {"NewEncoder", Func, 0}, + {"NewEncoding", Func, 0}, + {"NoPadding", Const, 5}, + {"RawStdEncoding", Var, 5}, + {"RawURLEncoding", Var, 5}, + {"StdEncoding", Var, 0}, + {"StdPadding", Const, 5}, + {"URLEncoding", Var, 0}, + }, + "encoding/binary": { + {"Append", Func, 23}, + {"AppendByteOrder", Type, 19}, + {"AppendUvarint", Func, 19}, + {"AppendVarint", Func, 19}, + {"BigEndian", Var, 0}, + {"ByteOrder", Type, 0}, + {"Decode", Func, 23}, + {"Encode", Func, 23}, + {"LittleEndian", Var, 0}, + {"MaxVarintLen16", Const, 0}, + {"MaxVarintLen32", Const, 0}, + {"MaxVarintLen64", Const, 0}, + {"NativeEndian", Var, 21}, + {"PutUvarint", Func, 0}, + {"PutVarint", Func, 0}, + {"Read", Func, 0}, + {"ReadUvarint", Func, 0}, + {"ReadVarint", Func, 0}, + {"Size", Func, 0}, + {"Uvarint", Func, 0}, + {"Varint", Func, 0}, + {"Write", Func, 0}, + }, + "encoding/csv": { + {"(*ParseError).Error", Method, 0}, + {"(*ParseError).Unwrap", Method, 13}, + {"(*Reader).FieldPos", Method, 17}, + {"(*Reader).InputOffset", Method, 19}, + {"(*Reader).Read", Method, 0}, + {"(*Reader).ReadAll", Method, 0}, + {"(*Writer).Error", Method, 1}, + {"(*Writer).Flush", Method, 0}, + {"(*Writer).Write", Method, 0}, + {"(*Writer).WriteAll", Method, 0}, + {"ErrBareQuote", Var, 0}, + {"ErrFieldCount", Var, 0}, + {"ErrQuote", Var, 0}, + {"ErrTrailingComma", Var, 0}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"ParseError", Type, 0}, + {"ParseError.Column", Field, 0}, + {"ParseError.Err", Field, 0}, + {"ParseError.Line", Field, 0}, + {"ParseError.StartLine", Field, 10}, + {"Reader", Type, 0}, + {"Reader.Comma", Field, 0}, + {"Reader.Comment", Field, 0}, + {"Reader.FieldsPerRecord", Field, 0}, + {"Reader.LazyQuotes", Field, 0}, + {"Reader.ReuseRecord", Field, 9}, + {"Reader.TrailingComma", Field, 0}, + {"Reader.TrimLeadingSpace", Field, 0}, + {"Writer", Type, 0}, + {"Writer.Comma", Field, 0}, + {"Writer.UseCRLF", Field, 0}, + }, + "encoding/gob": { + {"(*Decoder).Decode", Method, 0}, + {"(*Decoder).DecodeValue", Method, 0}, + {"(*Encoder).Encode", Method, 0}, + {"(*Encoder).EncodeValue", Method, 0}, + {"CommonType", Type, 0}, + {"CommonType.Id", Field, 0}, + {"CommonType.Name", Field, 0}, + {"Decoder", Type, 0}, + {"Encoder", Type, 0}, + {"GobDecoder", Type, 0}, + {"GobEncoder", Type, 0}, + {"NewDecoder", Func, 0}, + {"NewEncoder", Func, 0}, + {"Register", Func, 0}, + {"RegisterName", Func, 0}, + }, + "encoding/hex": { + {"(InvalidByteError).Error", Method, 0}, + {"AppendDecode", Func, 22}, + {"AppendEncode", Func, 22}, + {"Decode", Func, 0}, + {"DecodeString", Func, 0}, + {"DecodedLen", Func, 0}, + {"Dump", Func, 0}, + {"Dumper", Func, 0}, + {"Encode", Func, 0}, + {"EncodeToString", Func, 0}, + {"EncodedLen", Func, 0}, + {"ErrLength", Var, 0}, + {"InvalidByteError", Type, 0}, + {"NewDecoder", Func, 10}, + {"NewEncoder", Func, 10}, + }, + "encoding/json": { + {"(*Decoder).Buffered", Method, 1}, + {"(*Decoder).Decode", Method, 0}, + {"(*Decoder).DisallowUnknownFields", Method, 10}, + {"(*Decoder).InputOffset", Method, 14}, + {"(*Decoder).More", Method, 5}, + {"(*Decoder).Token", Method, 5}, + {"(*Decoder).UseNumber", Method, 1}, + {"(*Encoder).Encode", Method, 0}, + {"(*Encoder).SetEscapeHTML", Method, 7}, + {"(*Encoder).SetIndent", Method, 7}, + {"(*InvalidUTF8Error).Error", Method, 0}, + {"(*InvalidUnmarshalError).Error", Method, 0}, + {"(*MarshalerError).Error", Method, 0}, + {"(*MarshalerError).Unwrap", Method, 13}, + {"(*RawMessage).MarshalJSON", Method, 0}, + {"(*RawMessage).UnmarshalJSON", Method, 0}, + {"(*SyntaxError).Error", Method, 0}, + {"(*UnmarshalFieldError).Error", Method, 0}, + {"(*UnmarshalTypeError).Error", Method, 0}, + {"(*UnsupportedTypeError).Error", Method, 0}, + {"(*UnsupportedValueError).Error", Method, 0}, + {"(Delim).String", Method, 5}, + {"(Number).Float64", Method, 1}, + {"(Number).Int64", Method, 1}, + {"(Number).String", Method, 1}, + {"(RawMessage).MarshalJSON", Method, 8}, + {"Compact", Func, 0}, + {"Decoder", Type, 0}, + {"Delim", Type, 5}, + {"Encoder", Type, 0}, + {"HTMLEscape", Func, 0}, + {"Indent", Func, 0}, + {"InvalidUTF8Error", Type, 0}, + {"InvalidUTF8Error.S", Field, 0}, + {"InvalidUnmarshalError", Type, 0}, + {"InvalidUnmarshalError.Type", Field, 0}, + {"Marshal", Func, 0}, + {"MarshalIndent", Func, 0}, + {"Marshaler", Type, 0}, + {"MarshalerError", Type, 0}, + {"MarshalerError.Err", Field, 0}, + {"MarshalerError.Type", Field, 0}, + {"NewDecoder", Func, 0}, + {"NewEncoder", Func, 0}, + {"Number", Type, 1}, + {"RawMessage", Type, 0}, + {"SyntaxError", Type, 0}, + {"SyntaxError.Offset", Field, 0}, + {"Token", Type, 5}, + {"Unmarshal", Func, 0}, + {"UnmarshalFieldError", Type, 0}, + {"UnmarshalFieldError.Field", Field, 0}, + {"UnmarshalFieldError.Key", Field, 0}, + {"UnmarshalFieldError.Type", Field, 0}, + {"UnmarshalTypeError", Type, 0}, + {"UnmarshalTypeError.Field", Field, 8}, + {"UnmarshalTypeError.Offset", Field, 5}, + {"UnmarshalTypeError.Struct", Field, 8}, + {"UnmarshalTypeError.Type", Field, 0}, + {"UnmarshalTypeError.Value", Field, 0}, + {"Unmarshaler", Type, 0}, + {"UnsupportedTypeError", Type, 0}, + {"UnsupportedTypeError.Type", Field, 0}, + {"UnsupportedValueError", Type, 0}, + {"UnsupportedValueError.Str", Field, 0}, + {"UnsupportedValueError.Value", Field, 0}, + {"Valid", Func, 9}, + }, + "encoding/pem": { + {"Block", Type, 0}, + {"Block.Bytes", Field, 0}, + {"Block.Headers", Field, 0}, + {"Block.Type", Field, 0}, + {"Decode", Func, 0}, + {"Encode", Func, 0}, + {"EncodeToMemory", Func, 0}, + }, + "encoding/xml": { + {"(*Decoder).Decode", Method, 0}, + {"(*Decoder).DecodeElement", Method, 0}, + {"(*Decoder).InputOffset", Method, 4}, + {"(*Decoder).InputPos", Method, 19}, + {"(*Decoder).RawToken", Method, 0}, + {"(*Decoder).Skip", Method, 0}, + {"(*Decoder).Token", Method, 0}, + {"(*Encoder).Close", Method, 20}, + {"(*Encoder).Encode", Method, 0}, + {"(*Encoder).EncodeElement", Method, 2}, + {"(*Encoder).EncodeToken", Method, 2}, + {"(*Encoder).Flush", Method, 2}, + {"(*Encoder).Indent", Method, 1}, + {"(*SyntaxError).Error", Method, 0}, + {"(*TagPathError).Error", Method, 0}, + {"(*UnsupportedTypeError).Error", Method, 0}, + {"(CharData).Copy", Method, 0}, + {"(Comment).Copy", Method, 0}, + {"(Directive).Copy", Method, 0}, + {"(ProcInst).Copy", Method, 0}, + {"(StartElement).Copy", Method, 0}, + {"(StartElement).End", Method, 2}, + {"(UnmarshalError).Error", Method, 0}, + {"Attr", Type, 0}, + {"Attr.Name", Field, 0}, + {"Attr.Value", Field, 0}, + {"CharData", Type, 0}, + {"Comment", Type, 0}, + {"CopyToken", Func, 0}, + {"Decoder", Type, 0}, + {"Decoder.AutoClose", Field, 0}, + {"Decoder.CharsetReader", Field, 0}, + {"Decoder.DefaultSpace", Field, 1}, + {"Decoder.Entity", Field, 0}, + {"Decoder.Strict", Field, 0}, + {"Directive", Type, 0}, + {"Encoder", Type, 0}, + {"EndElement", Type, 0}, + {"EndElement.Name", Field, 0}, + {"Escape", Func, 0}, + {"EscapeText", Func, 1}, + {"HTMLAutoClose", Var, 0}, + {"HTMLEntity", Var, 0}, + {"Header", Const, 0}, + {"Marshal", Func, 0}, + {"MarshalIndent", Func, 0}, + {"Marshaler", Type, 2}, + {"MarshalerAttr", Type, 2}, + {"Name", Type, 0}, + {"Name.Local", Field, 0}, + {"Name.Space", Field, 0}, + {"NewDecoder", Func, 0}, + {"NewEncoder", Func, 0}, + {"NewTokenDecoder", Func, 10}, + {"ProcInst", Type, 0}, + {"ProcInst.Inst", Field, 0}, + {"ProcInst.Target", Field, 0}, + {"StartElement", Type, 0}, + {"StartElement.Attr", Field, 0}, + {"StartElement.Name", Field, 0}, + {"SyntaxError", Type, 0}, + {"SyntaxError.Line", Field, 0}, + {"SyntaxError.Msg", Field, 0}, + {"TagPathError", Type, 0}, + {"TagPathError.Field1", Field, 0}, + {"TagPathError.Field2", Field, 0}, + {"TagPathError.Struct", Field, 0}, + {"TagPathError.Tag1", Field, 0}, + {"TagPathError.Tag2", Field, 0}, + {"Token", Type, 0}, + {"TokenReader", Type, 10}, + {"Unmarshal", Func, 0}, + {"UnmarshalError", Type, 0}, + {"Unmarshaler", Type, 2}, + {"UnmarshalerAttr", Type, 2}, + {"UnsupportedTypeError", Type, 0}, + {"UnsupportedTypeError.Type", Field, 0}, + }, + "errors": { + {"As", Func, 13}, + {"ErrUnsupported", Var, 21}, + {"Is", Func, 13}, + {"Join", Func, 20}, + {"New", Func, 0}, + {"Unwrap", Func, 13}, + }, + "expvar": { + {"(*Float).Add", Method, 0}, + {"(*Float).Set", Method, 0}, + {"(*Float).String", Method, 0}, + {"(*Float).Value", Method, 8}, + {"(*Int).Add", Method, 0}, + {"(*Int).Set", Method, 0}, + {"(*Int).String", Method, 0}, + {"(*Int).Value", Method, 8}, + {"(*Map).Add", Method, 0}, + {"(*Map).AddFloat", Method, 0}, + {"(*Map).Delete", Method, 12}, + {"(*Map).Do", Method, 0}, + {"(*Map).Get", Method, 0}, + {"(*Map).Init", Method, 0}, + {"(*Map).Set", Method, 0}, + {"(*Map).String", Method, 0}, + {"(*String).Set", Method, 0}, + {"(*String).String", Method, 0}, + {"(*String).Value", Method, 8}, + {"(Func).String", Method, 0}, + {"(Func).Value", Method, 8}, + {"Do", Func, 0}, + {"Float", Type, 0}, + {"Func", Type, 0}, + {"Get", Func, 0}, + {"Handler", Func, 8}, + {"Int", Type, 0}, + {"KeyValue", Type, 0}, + {"KeyValue.Key", Field, 0}, + {"KeyValue.Value", Field, 0}, + {"Map", Type, 0}, + {"NewFloat", Func, 0}, + {"NewInt", Func, 0}, + {"NewMap", Func, 0}, + {"NewString", Func, 0}, + {"Publish", Func, 0}, + {"String", Type, 0}, + {"Var", Type, 0}, + }, + "flag": { + {"(*FlagSet).Arg", Method, 0}, + {"(*FlagSet).Args", Method, 0}, + {"(*FlagSet).Bool", Method, 0}, + {"(*FlagSet).BoolFunc", Method, 21}, + {"(*FlagSet).BoolVar", Method, 0}, + {"(*FlagSet).Duration", Method, 0}, + {"(*FlagSet).DurationVar", Method, 0}, + {"(*FlagSet).ErrorHandling", Method, 10}, + {"(*FlagSet).Float64", Method, 0}, + {"(*FlagSet).Float64Var", Method, 0}, + {"(*FlagSet).Func", Method, 16}, + {"(*FlagSet).Init", Method, 0}, + {"(*FlagSet).Int", Method, 0}, + {"(*FlagSet).Int64", Method, 0}, + {"(*FlagSet).Int64Var", Method, 0}, + {"(*FlagSet).IntVar", Method, 0}, + {"(*FlagSet).Lookup", Method, 0}, + {"(*FlagSet).NArg", Method, 0}, + {"(*FlagSet).NFlag", Method, 0}, + {"(*FlagSet).Name", Method, 10}, + {"(*FlagSet).Output", Method, 10}, + {"(*FlagSet).Parse", Method, 0}, + {"(*FlagSet).Parsed", Method, 0}, + {"(*FlagSet).PrintDefaults", Method, 0}, + {"(*FlagSet).Set", Method, 0}, + {"(*FlagSet).SetOutput", Method, 0}, + {"(*FlagSet).String", Method, 0}, + {"(*FlagSet).StringVar", Method, 0}, + {"(*FlagSet).TextVar", Method, 19}, + {"(*FlagSet).Uint", Method, 0}, + {"(*FlagSet).Uint64", Method, 0}, + {"(*FlagSet).Uint64Var", Method, 0}, + {"(*FlagSet).UintVar", Method, 0}, + {"(*FlagSet).Var", Method, 0}, + {"(*FlagSet).Visit", Method, 0}, + {"(*FlagSet).VisitAll", Method, 0}, + {"Arg", Func, 0}, + {"Args", Func, 0}, + {"Bool", Func, 0}, + {"BoolFunc", Func, 21}, + {"BoolVar", Func, 0}, + {"CommandLine", Var, 2}, + {"ContinueOnError", Const, 0}, + {"Duration", Func, 0}, + {"DurationVar", Func, 0}, + {"ErrHelp", Var, 0}, + {"ErrorHandling", Type, 0}, + {"ExitOnError", Const, 0}, + {"Flag", Type, 0}, + {"Flag.DefValue", Field, 0}, + {"Flag.Name", Field, 0}, + {"Flag.Usage", Field, 0}, + {"Flag.Value", Field, 0}, + {"FlagSet", Type, 0}, + {"FlagSet.Usage", Field, 0}, + {"Float64", Func, 0}, + {"Float64Var", Func, 0}, + {"Func", Func, 16}, + {"Getter", Type, 2}, + {"Int", Func, 0}, + {"Int64", Func, 0}, + {"Int64Var", Func, 0}, + {"IntVar", Func, 0}, + {"Lookup", Func, 0}, + {"NArg", Func, 0}, + {"NFlag", Func, 0}, + {"NewFlagSet", Func, 0}, + {"PanicOnError", Const, 0}, + {"Parse", Func, 0}, + {"Parsed", Func, 0}, + {"PrintDefaults", Func, 0}, + {"Set", Func, 0}, + {"String", Func, 0}, + {"StringVar", Func, 0}, + {"TextVar", Func, 19}, + {"Uint", Func, 0}, + {"Uint64", Func, 0}, + {"Uint64Var", Func, 0}, + {"UintVar", Func, 0}, + {"UnquoteUsage", Func, 5}, + {"Usage", Var, 0}, + {"Value", Type, 0}, + {"Var", Func, 0}, + {"Visit", Func, 0}, + {"VisitAll", Func, 0}, + }, + "fmt": { + {"Append", Func, 19}, + {"Appendf", Func, 19}, + {"Appendln", Func, 19}, + {"Errorf", Func, 0}, + {"FormatString", Func, 20}, + {"Formatter", Type, 0}, + {"Fprint", Func, 0}, + {"Fprintf", Func, 0}, + {"Fprintln", Func, 0}, + {"Fscan", Func, 0}, + {"Fscanf", Func, 0}, + {"Fscanln", Func, 0}, + {"GoStringer", Type, 0}, + {"Print", Func, 0}, + {"Printf", Func, 0}, + {"Println", Func, 0}, + {"Scan", Func, 0}, + {"ScanState", Type, 0}, + {"Scanf", Func, 0}, + {"Scanln", Func, 0}, + {"Scanner", Type, 0}, + {"Sprint", Func, 0}, + {"Sprintf", Func, 0}, + {"Sprintln", Func, 0}, + {"Sscan", Func, 0}, + {"Sscanf", Func, 0}, + {"Sscanln", Func, 0}, + {"State", Type, 0}, + {"Stringer", Type, 0}, + }, + "go/ast": { + {"(*ArrayType).End", Method, 0}, + {"(*ArrayType).Pos", Method, 0}, + {"(*AssignStmt).End", Method, 0}, + {"(*AssignStmt).Pos", Method, 0}, + {"(*BadDecl).End", Method, 0}, + {"(*BadDecl).Pos", Method, 0}, + {"(*BadExpr).End", Method, 0}, + {"(*BadExpr).Pos", Method, 0}, + {"(*BadStmt).End", Method, 0}, + {"(*BadStmt).Pos", Method, 0}, + {"(*BasicLit).End", Method, 0}, + {"(*BasicLit).Pos", Method, 0}, + {"(*BinaryExpr).End", Method, 0}, + {"(*BinaryExpr).Pos", Method, 0}, + {"(*BlockStmt).End", Method, 0}, + {"(*BlockStmt).Pos", Method, 0}, + {"(*BranchStmt).End", Method, 0}, + {"(*BranchStmt).Pos", Method, 0}, + {"(*CallExpr).End", Method, 0}, + {"(*CallExpr).Pos", Method, 0}, + {"(*CaseClause).End", Method, 0}, + {"(*CaseClause).Pos", Method, 0}, + {"(*ChanType).End", Method, 0}, + {"(*ChanType).Pos", Method, 0}, + {"(*CommClause).End", Method, 0}, + {"(*CommClause).Pos", Method, 0}, + {"(*Comment).End", Method, 0}, + {"(*Comment).Pos", Method, 0}, + {"(*CommentGroup).End", Method, 0}, + {"(*CommentGroup).Pos", Method, 0}, + {"(*CommentGroup).Text", Method, 0}, + {"(*CompositeLit).End", Method, 0}, + {"(*CompositeLit).Pos", Method, 0}, + {"(*DeclStmt).End", Method, 0}, + {"(*DeclStmt).Pos", Method, 0}, + {"(*DeferStmt).End", Method, 0}, + {"(*DeferStmt).Pos", Method, 0}, + {"(*Ellipsis).End", Method, 0}, + {"(*Ellipsis).Pos", Method, 0}, + {"(*EmptyStmt).End", Method, 0}, + {"(*EmptyStmt).Pos", Method, 0}, + {"(*ExprStmt).End", Method, 0}, + {"(*ExprStmt).Pos", Method, 0}, + {"(*Field).End", Method, 0}, + {"(*Field).Pos", Method, 0}, + {"(*FieldList).End", Method, 0}, + {"(*FieldList).NumFields", Method, 0}, + {"(*FieldList).Pos", Method, 0}, + {"(*File).End", Method, 0}, + {"(*File).Pos", Method, 0}, + {"(*ForStmt).End", Method, 0}, + {"(*ForStmt).Pos", Method, 0}, + {"(*FuncDecl).End", Method, 0}, + {"(*FuncDecl).Pos", Method, 0}, + {"(*FuncLit).End", Method, 0}, + {"(*FuncLit).Pos", Method, 0}, + {"(*FuncType).End", Method, 0}, + {"(*FuncType).Pos", Method, 0}, + {"(*GenDecl).End", Method, 0}, + {"(*GenDecl).Pos", Method, 0}, + {"(*GoStmt).End", Method, 0}, + {"(*GoStmt).Pos", Method, 0}, + {"(*Ident).End", Method, 0}, + {"(*Ident).IsExported", Method, 0}, + {"(*Ident).Pos", Method, 0}, + {"(*Ident).String", Method, 0}, + {"(*IfStmt).End", Method, 0}, + {"(*IfStmt).Pos", Method, 0}, + {"(*ImportSpec).End", Method, 0}, + {"(*ImportSpec).Pos", Method, 0}, + {"(*IncDecStmt).End", Method, 0}, + {"(*IncDecStmt).Pos", Method, 0}, + {"(*IndexExpr).End", Method, 0}, + {"(*IndexExpr).Pos", Method, 0}, + {"(*IndexListExpr).End", Method, 18}, + {"(*IndexListExpr).Pos", Method, 18}, + {"(*InterfaceType).End", Method, 0}, + {"(*InterfaceType).Pos", Method, 0}, + {"(*KeyValueExpr).End", Method, 0}, + {"(*KeyValueExpr).Pos", Method, 0}, + {"(*LabeledStmt).End", Method, 0}, + {"(*LabeledStmt).Pos", Method, 0}, + {"(*MapType).End", Method, 0}, + {"(*MapType).Pos", Method, 0}, + {"(*Object).Pos", Method, 0}, + {"(*Package).End", Method, 0}, + {"(*Package).Pos", Method, 0}, + {"(*ParenExpr).End", Method, 0}, + {"(*ParenExpr).Pos", Method, 0}, + {"(*RangeStmt).End", Method, 0}, + {"(*RangeStmt).Pos", Method, 0}, + {"(*ReturnStmt).End", Method, 0}, + {"(*ReturnStmt).Pos", Method, 0}, + {"(*Scope).Insert", Method, 0}, + {"(*Scope).Lookup", Method, 0}, + {"(*Scope).String", Method, 0}, + {"(*SelectStmt).End", Method, 0}, + {"(*SelectStmt).Pos", Method, 0}, + {"(*SelectorExpr).End", Method, 0}, + {"(*SelectorExpr).Pos", Method, 0}, + {"(*SendStmt).End", Method, 0}, + {"(*SendStmt).Pos", Method, 0}, + {"(*SliceExpr).End", Method, 0}, + {"(*SliceExpr).Pos", Method, 0}, + {"(*StarExpr).End", Method, 0}, + {"(*StarExpr).Pos", Method, 0}, + {"(*StructType).End", Method, 0}, + {"(*StructType).Pos", Method, 0}, + {"(*SwitchStmt).End", Method, 0}, + {"(*SwitchStmt).Pos", Method, 0}, + {"(*TypeAssertExpr).End", Method, 0}, + {"(*TypeAssertExpr).Pos", Method, 0}, + {"(*TypeSpec).End", Method, 0}, + {"(*TypeSpec).Pos", Method, 0}, + {"(*TypeSwitchStmt).End", Method, 0}, + {"(*TypeSwitchStmt).Pos", Method, 0}, + {"(*UnaryExpr).End", Method, 0}, + {"(*UnaryExpr).Pos", Method, 0}, + {"(*ValueSpec).End", Method, 0}, + {"(*ValueSpec).Pos", Method, 0}, + {"(CommentMap).Comments", Method, 1}, + {"(CommentMap).Filter", Method, 1}, + {"(CommentMap).String", Method, 1}, + {"(CommentMap).Update", Method, 1}, + {"(ObjKind).String", Method, 0}, + {"ArrayType", Type, 0}, + {"ArrayType.Elt", Field, 0}, + {"ArrayType.Lbrack", Field, 0}, + {"ArrayType.Len", Field, 0}, + {"AssignStmt", Type, 0}, + {"AssignStmt.Lhs", Field, 0}, + {"AssignStmt.Rhs", Field, 0}, + {"AssignStmt.Tok", Field, 0}, + {"AssignStmt.TokPos", Field, 0}, + {"Bad", Const, 0}, + {"BadDecl", Type, 0}, + {"BadDecl.From", Field, 0}, + {"BadDecl.To", Field, 0}, + {"BadExpr", Type, 0}, + {"BadExpr.From", Field, 0}, + {"BadExpr.To", Field, 0}, + {"BadStmt", Type, 0}, + {"BadStmt.From", Field, 0}, + {"BadStmt.To", Field, 0}, + {"BasicLit", Type, 0}, + {"BasicLit.Kind", Field, 0}, + {"BasicLit.Value", Field, 0}, + {"BasicLit.ValuePos", Field, 0}, + {"BinaryExpr", Type, 0}, + {"BinaryExpr.Op", Field, 0}, + {"BinaryExpr.OpPos", Field, 0}, + {"BinaryExpr.X", Field, 0}, + {"BinaryExpr.Y", Field, 0}, + {"BlockStmt", Type, 0}, + {"BlockStmt.Lbrace", Field, 0}, + {"BlockStmt.List", Field, 0}, + {"BlockStmt.Rbrace", Field, 0}, + {"BranchStmt", Type, 0}, + {"BranchStmt.Label", Field, 0}, + {"BranchStmt.Tok", Field, 0}, + {"BranchStmt.TokPos", Field, 0}, + {"CallExpr", Type, 0}, + {"CallExpr.Args", Field, 0}, + {"CallExpr.Ellipsis", Field, 0}, + {"CallExpr.Fun", Field, 0}, + {"CallExpr.Lparen", Field, 0}, + {"CallExpr.Rparen", Field, 0}, + {"CaseClause", Type, 0}, + {"CaseClause.Body", Field, 0}, + {"CaseClause.Case", Field, 0}, + {"CaseClause.Colon", Field, 0}, + {"CaseClause.List", Field, 0}, + {"ChanDir", Type, 0}, + {"ChanType", Type, 0}, + {"ChanType.Arrow", Field, 1}, + {"ChanType.Begin", Field, 0}, + {"ChanType.Dir", Field, 0}, + {"ChanType.Value", Field, 0}, + {"CommClause", Type, 0}, + {"CommClause.Body", Field, 0}, + {"CommClause.Case", Field, 0}, + {"CommClause.Colon", Field, 0}, + {"CommClause.Comm", Field, 0}, + {"Comment", Type, 0}, + {"Comment.Slash", Field, 0}, + {"Comment.Text", Field, 0}, + {"CommentGroup", Type, 0}, + {"CommentGroup.List", Field, 0}, + {"CommentMap", Type, 1}, + {"CompositeLit", Type, 0}, + {"CompositeLit.Elts", Field, 0}, + {"CompositeLit.Incomplete", Field, 11}, + {"CompositeLit.Lbrace", Field, 0}, + {"CompositeLit.Rbrace", Field, 0}, + {"CompositeLit.Type", Field, 0}, + {"Con", Const, 0}, + {"Decl", Type, 0}, + {"DeclStmt", Type, 0}, + {"DeclStmt.Decl", Field, 0}, + {"DeferStmt", Type, 0}, + {"DeferStmt.Call", Field, 0}, + {"DeferStmt.Defer", Field, 0}, + {"Ellipsis", Type, 0}, + {"Ellipsis.Ellipsis", Field, 0}, + {"Ellipsis.Elt", Field, 0}, + {"EmptyStmt", Type, 0}, + {"EmptyStmt.Implicit", Field, 5}, + {"EmptyStmt.Semicolon", Field, 0}, + {"Expr", Type, 0}, + {"ExprStmt", Type, 0}, + {"ExprStmt.X", Field, 0}, + {"Field", Type, 0}, + {"Field.Comment", Field, 0}, + {"Field.Doc", Field, 0}, + {"Field.Names", Field, 0}, + {"Field.Tag", Field, 0}, + {"Field.Type", Field, 0}, + {"FieldFilter", Type, 0}, + {"FieldList", Type, 0}, + {"FieldList.Closing", Field, 0}, + {"FieldList.List", Field, 0}, + {"FieldList.Opening", Field, 0}, + {"File", Type, 0}, + {"File.Comments", Field, 0}, + {"File.Decls", Field, 0}, + {"File.Doc", Field, 0}, + {"File.FileEnd", Field, 20}, + {"File.FileStart", Field, 20}, + {"File.GoVersion", Field, 21}, + {"File.Imports", Field, 0}, + {"File.Name", Field, 0}, + {"File.Package", Field, 0}, + {"File.Scope", Field, 0}, + {"File.Unresolved", Field, 0}, + {"FileExports", Func, 0}, + {"Filter", Type, 0}, + {"FilterDecl", Func, 0}, + {"FilterFile", Func, 0}, + {"FilterFuncDuplicates", Const, 0}, + {"FilterImportDuplicates", Const, 0}, + {"FilterPackage", Func, 0}, + {"FilterUnassociatedComments", Const, 0}, + {"ForStmt", Type, 0}, + {"ForStmt.Body", Field, 0}, + {"ForStmt.Cond", Field, 0}, + {"ForStmt.For", Field, 0}, + {"ForStmt.Init", Field, 0}, + {"ForStmt.Post", Field, 0}, + {"Fprint", Func, 0}, + {"Fun", Const, 0}, + {"FuncDecl", Type, 0}, + {"FuncDecl.Body", Field, 0}, + {"FuncDecl.Doc", Field, 0}, + {"FuncDecl.Name", Field, 0}, + {"FuncDecl.Recv", Field, 0}, + {"FuncDecl.Type", Field, 0}, + {"FuncLit", Type, 0}, + {"FuncLit.Body", Field, 0}, + {"FuncLit.Type", Field, 0}, + {"FuncType", Type, 0}, + {"FuncType.Func", Field, 0}, + {"FuncType.Params", Field, 0}, + {"FuncType.Results", Field, 0}, + {"FuncType.TypeParams", Field, 18}, + {"GenDecl", Type, 0}, + {"GenDecl.Doc", Field, 0}, + {"GenDecl.Lparen", Field, 0}, + {"GenDecl.Rparen", Field, 0}, + {"GenDecl.Specs", Field, 0}, + {"GenDecl.Tok", Field, 0}, + {"GenDecl.TokPos", Field, 0}, + {"GoStmt", Type, 0}, + {"GoStmt.Call", Field, 0}, + {"GoStmt.Go", Field, 0}, + {"Ident", Type, 0}, + {"Ident.Name", Field, 0}, + {"Ident.NamePos", Field, 0}, + {"Ident.Obj", Field, 0}, + {"IfStmt", Type, 0}, + {"IfStmt.Body", Field, 0}, + {"IfStmt.Cond", Field, 0}, + {"IfStmt.Else", Field, 0}, + {"IfStmt.If", Field, 0}, + {"IfStmt.Init", Field, 0}, + {"ImportSpec", Type, 0}, + {"ImportSpec.Comment", Field, 0}, + {"ImportSpec.Doc", Field, 0}, + {"ImportSpec.EndPos", Field, 0}, + {"ImportSpec.Name", Field, 0}, + {"ImportSpec.Path", Field, 0}, + {"Importer", Type, 0}, + {"IncDecStmt", Type, 0}, + {"IncDecStmt.Tok", Field, 0}, + {"IncDecStmt.TokPos", Field, 0}, + {"IncDecStmt.X", Field, 0}, + {"IndexExpr", Type, 0}, + {"IndexExpr.Index", Field, 0}, + {"IndexExpr.Lbrack", Field, 0}, + {"IndexExpr.Rbrack", Field, 0}, + {"IndexExpr.X", Field, 0}, + {"IndexListExpr", Type, 18}, + {"IndexListExpr.Indices", Field, 18}, + {"IndexListExpr.Lbrack", Field, 18}, + {"IndexListExpr.Rbrack", Field, 18}, + {"IndexListExpr.X", Field, 18}, + {"Inspect", Func, 0}, + {"InterfaceType", Type, 0}, + {"InterfaceType.Incomplete", Field, 0}, + {"InterfaceType.Interface", Field, 0}, + {"InterfaceType.Methods", Field, 0}, + {"IsExported", Func, 0}, + {"IsGenerated", Func, 21}, + {"KeyValueExpr", Type, 0}, + {"KeyValueExpr.Colon", Field, 0}, + {"KeyValueExpr.Key", Field, 0}, + {"KeyValueExpr.Value", Field, 0}, + {"LabeledStmt", Type, 0}, + {"LabeledStmt.Colon", Field, 0}, + {"LabeledStmt.Label", Field, 0}, + {"LabeledStmt.Stmt", Field, 0}, + {"Lbl", Const, 0}, + {"MapType", Type, 0}, + {"MapType.Key", Field, 0}, + {"MapType.Map", Field, 0}, + {"MapType.Value", Field, 0}, + {"MergeMode", Type, 0}, + {"MergePackageFiles", Func, 0}, + {"NewCommentMap", Func, 1}, + {"NewIdent", Func, 0}, + {"NewObj", Func, 0}, + {"NewPackage", Func, 0}, + {"NewScope", Func, 0}, + {"Node", Type, 0}, + {"NotNilFilter", Func, 0}, + {"ObjKind", Type, 0}, + {"Object", Type, 0}, + {"Object.Data", Field, 0}, + {"Object.Decl", Field, 0}, + {"Object.Kind", Field, 0}, + {"Object.Name", Field, 0}, + {"Object.Type", Field, 0}, + {"Package", Type, 0}, + {"Package.Files", Field, 0}, + {"Package.Imports", Field, 0}, + {"Package.Name", Field, 0}, + {"Package.Scope", Field, 0}, + {"PackageExports", Func, 0}, + {"ParenExpr", Type, 0}, + {"ParenExpr.Lparen", Field, 0}, + {"ParenExpr.Rparen", Field, 0}, + {"ParenExpr.X", Field, 0}, + {"Pkg", Const, 0}, + {"Preorder", Func, 23}, + {"Print", Func, 0}, + {"RECV", Const, 0}, + {"RangeStmt", Type, 0}, + {"RangeStmt.Body", Field, 0}, + {"RangeStmt.For", Field, 0}, + {"RangeStmt.Key", Field, 0}, + {"RangeStmt.Range", Field, 20}, + {"RangeStmt.Tok", Field, 0}, + {"RangeStmt.TokPos", Field, 0}, + {"RangeStmt.Value", Field, 0}, + {"RangeStmt.X", Field, 0}, + {"ReturnStmt", Type, 0}, + {"ReturnStmt.Results", Field, 0}, + {"ReturnStmt.Return", Field, 0}, + {"SEND", Const, 0}, + {"Scope", Type, 0}, + {"Scope.Objects", Field, 0}, + {"Scope.Outer", Field, 0}, + {"SelectStmt", Type, 0}, + {"SelectStmt.Body", Field, 0}, + {"SelectStmt.Select", Field, 0}, + {"SelectorExpr", Type, 0}, + {"SelectorExpr.Sel", Field, 0}, + {"SelectorExpr.X", Field, 0}, + {"SendStmt", Type, 0}, + {"SendStmt.Arrow", Field, 0}, + {"SendStmt.Chan", Field, 0}, + {"SendStmt.Value", Field, 0}, + {"SliceExpr", Type, 0}, + {"SliceExpr.High", Field, 0}, + {"SliceExpr.Lbrack", Field, 0}, + {"SliceExpr.Low", Field, 0}, + {"SliceExpr.Max", Field, 2}, + {"SliceExpr.Rbrack", Field, 0}, + {"SliceExpr.Slice3", Field, 2}, + {"SliceExpr.X", Field, 0}, + {"SortImports", Func, 0}, + {"Spec", Type, 0}, + {"StarExpr", Type, 0}, + {"StarExpr.Star", Field, 0}, + {"StarExpr.X", Field, 0}, + {"Stmt", Type, 0}, + {"StructType", Type, 0}, + {"StructType.Fields", Field, 0}, + {"StructType.Incomplete", Field, 0}, + {"StructType.Struct", Field, 0}, + {"SwitchStmt", Type, 0}, + {"SwitchStmt.Body", Field, 0}, + {"SwitchStmt.Init", Field, 0}, + {"SwitchStmt.Switch", Field, 0}, + {"SwitchStmt.Tag", Field, 0}, + {"Typ", Const, 0}, + {"TypeAssertExpr", Type, 0}, + {"TypeAssertExpr.Lparen", Field, 2}, + {"TypeAssertExpr.Rparen", Field, 2}, + {"TypeAssertExpr.Type", Field, 0}, + {"TypeAssertExpr.X", Field, 0}, + {"TypeSpec", Type, 0}, + {"TypeSpec.Assign", Field, 9}, + {"TypeSpec.Comment", Field, 0}, + {"TypeSpec.Doc", Field, 0}, + {"TypeSpec.Name", Field, 0}, + {"TypeSpec.Type", Field, 0}, + {"TypeSpec.TypeParams", Field, 18}, + {"TypeSwitchStmt", Type, 0}, + {"TypeSwitchStmt.Assign", Field, 0}, + {"TypeSwitchStmt.Body", Field, 0}, + {"TypeSwitchStmt.Init", Field, 0}, + {"TypeSwitchStmt.Switch", Field, 0}, + {"UnaryExpr", Type, 0}, + {"UnaryExpr.Op", Field, 0}, + {"UnaryExpr.OpPos", Field, 0}, + {"UnaryExpr.X", Field, 0}, + {"Unparen", Func, 22}, + {"ValueSpec", Type, 0}, + {"ValueSpec.Comment", Field, 0}, + {"ValueSpec.Doc", Field, 0}, + {"ValueSpec.Names", Field, 0}, + {"ValueSpec.Type", Field, 0}, + {"ValueSpec.Values", Field, 0}, + {"Var", Const, 0}, + {"Visitor", Type, 0}, + {"Walk", Func, 0}, + }, + "go/build": { + {"(*Context).Import", Method, 0}, + {"(*Context).ImportDir", Method, 0}, + {"(*Context).MatchFile", Method, 2}, + {"(*Context).SrcDirs", Method, 0}, + {"(*MultiplePackageError).Error", Method, 4}, + {"(*NoGoError).Error", Method, 0}, + {"(*Package).IsCommand", Method, 0}, + {"AllowBinary", Const, 0}, + {"ArchChar", Func, 0}, + {"Context", Type, 0}, + {"Context.BuildTags", Field, 0}, + {"Context.CgoEnabled", Field, 0}, + {"Context.Compiler", Field, 0}, + {"Context.Dir", Field, 14}, + {"Context.GOARCH", Field, 0}, + {"Context.GOOS", Field, 0}, + {"Context.GOPATH", Field, 0}, + {"Context.GOROOT", Field, 0}, + {"Context.HasSubdir", Field, 0}, + {"Context.InstallSuffix", Field, 1}, + {"Context.IsAbsPath", Field, 0}, + {"Context.IsDir", Field, 0}, + {"Context.JoinPath", Field, 0}, + {"Context.OpenFile", Field, 0}, + {"Context.ReadDir", Field, 0}, + {"Context.ReleaseTags", Field, 1}, + {"Context.SplitPathList", Field, 0}, + {"Context.ToolTags", Field, 17}, + {"Context.UseAllFiles", Field, 0}, + {"Default", Var, 0}, + {"Directive", Type, 21}, + {"Directive.Pos", Field, 21}, + {"Directive.Text", Field, 21}, + {"FindOnly", Const, 0}, + {"IgnoreVendor", Const, 6}, + {"Import", Func, 0}, + {"ImportComment", Const, 4}, + {"ImportDir", Func, 0}, + {"ImportMode", Type, 0}, + {"IsLocalImport", Func, 0}, + {"MultiplePackageError", Type, 4}, + {"MultiplePackageError.Dir", Field, 4}, + {"MultiplePackageError.Files", Field, 4}, + {"MultiplePackageError.Packages", Field, 4}, + {"NoGoError", Type, 0}, + {"NoGoError.Dir", Field, 0}, + {"Package", Type, 0}, + {"Package.AllTags", Field, 2}, + {"Package.BinDir", Field, 0}, + {"Package.BinaryOnly", Field, 7}, + {"Package.CFiles", Field, 0}, + {"Package.CXXFiles", Field, 2}, + {"Package.CgoCFLAGS", Field, 0}, + {"Package.CgoCPPFLAGS", Field, 2}, + {"Package.CgoCXXFLAGS", Field, 2}, + {"Package.CgoFFLAGS", Field, 7}, + {"Package.CgoFiles", Field, 0}, + {"Package.CgoLDFLAGS", Field, 0}, + {"Package.CgoPkgConfig", Field, 0}, + {"Package.ConflictDir", Field, 2}, + {"Package.Dir", Field, 0}, + {"Package.Directives", Field, 21}, + {"Package.Doc", Field, 0}, + {"Package.EmbedPatternPos", Field, 16}, + {"Package.EmbedPatterns", Field, 16}, + {"Package.FFiles", Field, 7}, + {"Package.GoFiles", Field, 0}, + {"Package.Goroot", Field, 0}, + {"Package.HFiles", Field, 0}, + {"Package.IgnoredGoFiles", Field, 1}, + {"Package.IgnoredOtherFiles", Field, 16}, + {"Package.ImportComment", Field, 4}, + {"Package.ImportPath", Field, 0}, + {"Package.ImportPos", Field, 0}, + {"Package.Imports", Field, 0}, + {"Package.InvalidGoFiles", Field, 6}, + {"Package.MFiles", Field, 3}, + {"Package.Name", Field, 0}, + {"Package.PkgObj", Field, 0}, + {"Package.PkgRoot", Field, 0}, + {"Package.PkgTargetRoot", Field, 5}, + {"Package.Root", Field, 0}, + {"Package.SFiles", Field, 0}, + {"Package.SrcRoot", Field, 0}, + {"Package.SwigCXXFiles", Field, 1}, + {"Package.SwigFiles", Field, 1}, + {"Package.SysoFiles", Field, 0}, + {"Package.TestDirectives", Field, 21}, + {"Package.TestEmbedPatternPos", Field, 16}, + {"Package.TestEmbedPatterns", Field, 16}, + {"Package.TestGoFiles", Field, 0}, + {"Package.TestImportPos", Field, 0}, + {"Package.TestImports", Field, 0}, + {"Package.XTestDirectives", Field, 21}, + {"Package.XTestEmbedPatternPos", Field, 16}, + {"Package.XTestEmbedPatterns", Field, 16}, + {"Package.XTestGoFiles", Field, 0}, + {"Package.XTestImportPos", Field, 0}, + {"Package.XTestImports", Field, 0}, + {"ToolDir", Var, 0}, + }, + "go/build/constraint": { + {"(*AndExpr).Eval", Method, 16}, + {"(*AndExpr).String", Method, 16}, + {"(*NotExpr).Eval", Method, 16}, + {"(*NotExpr).String", Method, 16}, + {"(*OrExpr).Eval", Method, 16}, + {"(*OrExpr).String", Method, 16}, + {"(*SyntaxError).Error", Method, 16}, + {"(*TagExpr).Eval", Method, 16}, + {"(*TagExpr).String", Method, 16}, + {"AndExpr", Type, 16}, + {"AndExpr.X", Field, 16}, + {"AndExpr.Y", Field, 16}, + {"Expr", Type, 16}, + {"GoVersion", Func, 21}, + {"IsGoBuild", Func, 16}, + {"IsPlusBuild", Func, 16}, + {"NotExpr", Type, 16}, + {"NotExpr.X", Field, 16}, + {"OrExpr", Type, 16}, + {"OrExpr.X", Field, 16}, + {"OrExpr.Y", Field, 16}, + {"Parse", Func, 16}, + {"PlusBuildLines", Func, 16}, + {"SyntaxError", Type, 16}, + {"SyntaxError.Err", Field, 16}, + {"SyntaxError.Offset", Field, 16}, + {"TagExpr", Type, 16}, + {"TagExpr.Tag", Field, 16}, + }, + "go/constant": { + {"(Kind).String", Method, 18}, + {"BinaryOp", Func, 5}, + {"BitLen", Func, 5}, + {"Bool", Const, 5}, + {"BoolVal", Func, 5}, + {"Bytes", Func, 5}, + {"Compare", Func, 5}, + {"Complex", Const, 5}, + {"Denom", Func, 5}, + {"Float", Const, 5}, + {"Float32Val", Func, 5}, + {"Float64Val", Func, 5}, + {"Imag", Func, 5}, + {"Int", Const, 5}, + {"Int64Val", Func, 5}, + {"Kind", Type, 5}, + {"Make", Func, 13}, + {"MakeBool", Func, 5}, + {"MakeFloat64", Func, 5}, + {"MakeFromBytes", Func, 5}, + {"MakeFromLiteral", Func, 5}, + {"MakeImag", Func, 5}, + {"MakeInt64", Func, 5}, + {"MakeString", Func, 5}, + {"MakeUint64", Func, 5}, + {"MakeUnknown", Func, 5}, + {"Num", Func, 5}, + {"Real", Func, 5}, + {"Shift", Func, 5}, + {"Sign", Func, 5}, + {"String", Const, 5}, + {"StringVal", Func, 5}, + {"ToComplex", Func, 6}, + {"ToFloat", Func, 6}, + {"ToInt", Func, 6}, + {"Uint64Val", Func, 5}, + {"UnaryOp", Func, 5}, + {"Unknown", Const, 5}, + {"Val", Func, 13}, + {"Value", Type, 5}, + }, + "go/doc": { + {"(*Package).Filter", Method, 0}, + {"(*Package).HTML", Method, 19}, + {"(*Package).Markdown", Method, 19}, + {"(*Package).Parser", Method, 19}, + {"(*Package).Printer", Method, 19}, + {"(*Package).Synopsis", Method, 19}, + {"(*Package).Text", Method, 19}, + {"AllDecls", Const, 0}, + {"AllMethods", Const, 0}, + {"Example", Type, 0}, + {"Example.Code", Field, 0}, + {"Example.Comments", Field, 0}, + {"Example.Doc", Field, 0}, + {"Example.EmptyOutput", Field, 1}, + {"Example.Name", Field, 0}, + {"Example.Order", Field, 1}, + {"Example.Output", Field, 0}, + {"Example.Play", Field, 1}, + {"Example.Suffix", Field, 14}, + {"Example.Unordered", Field, 7}, + {"Examples", Func, 0}, + {"Filter", Type, 0}, + {"Func", Type, 0}, + {"Func.Decl", Field, 0}, + {"Func.Doc", Field, 0}, + {"Func.Examples", Field, 14}, + {"Func.Level", Field, 0}, + {"Func.Name", Field, 0}, + {"Func.Orig", Field, 0}, + {"Func.Recv", Field, 0}, + {"IllegalPrefixes", Var, 1}, + {"IsPredeclared", Func, 8}, + {"Mode", Type, 0}, + {"New", Func, 0}, + {"NewFromFiles", Func, 14}, + {"Note", Type, 1}, + {"Note.Body", Field, 1}, + {"Note.End", Field, 1}, + {"Note.Pos", Field, 1}, + {"Note.UID", Field, 1}, + {"Package", Type, 0}, + {"Package.Bugs", Field, 0}, + {"Package.Consts", Field, 0}, + {"Package.Doc", Field, 0}, + {"Package.Examples", Field, 14}, + {"Package.Filenames", Field, 0}, + {"Package.Funcs", Field, 0}, + {"Package.ImportPath", Field, 0}, + {"Package.Imports", Field, 0}, + {"Package.Name", Field, 0}, + {"Package.Notes", Field, 1}, + {"Package.Types", Field, 0}, + {"Package.Vars", Field, 0}, + {"PreserveAST", Const, 12}, + {"Synopsis", Func, 0}, + {"ToHTML", Func, 0}, + {"ToText", Func, 0}, + {"Type", Type, 0}, + {"Type.Consts", Field, 0}, + {"Type.Decl", Field, 0}, + {"Type.Doc", Field, 0}, + {"Type.Examples", Field, 14}, + {"Type.Funcs", Field, 0}, + {"Type.Methods", Field, 0}, + {"Type.Name", Field, 0}, + {"Type.Vars", Field, 0}, + {"Value", Type, 0}, + {"Value.Decl", Field, 0}, + {"Value.Doc", Field, 0}, + {"Value.Names", Field, 0}, + }, + "go/doc/comment": { + {"(*DocLink).DefaultURL", Method, 19}, + {"(*Heading).DefaultID", Method, 19}, + {"(*List).BlankBefore", Method, 19}, + {"(*List).BlankBetween", Method, 19}, + {"(*Parser).Parse", Method, 19}, + {"(*Printer).Comment", Method, 19}, + {"(*Printer).HTML", Method, 19}, + {"(*Printer).Markdown", Method, 19}, + {"(*Printer).Text", Method, 19}, + {"Block", Type, 19}, + {"Code", Type, 19}, + {"Code.Text", Field, 19}, + {"DefaultLookupPackage", Func, 19}, + {"Doc", Type, 19}, + {"Doc.Content", Field, 19}, + {"Doc.Links", Field, 19}, + {"DocLink", Type, 19}, + {"DocLink.ImportPath", Field, 19}, + {"DocLink.Name", Field, 19}, + {"DocLink.Recv", Field, 19}, + {"DocLink.Text", Field, 19}, + {"Heading", Type, 19}, + {"Heading.Text", Field, 19}, + {"Italic", Type, 19}, + {"Link", Type, 19}, + {"Link.Auto", Field, 19}, + {"Link.Text", Field, 19}, + {"Link.URL", Field, 19}, + {"LinkDef", Type, 19}, + {"LinkDef.Text", Field, 19}, + {"LinkDef.URL", Field, 19}, + {"LinkDef.Used", Field, 19}, + {"List", Type, 19}, + {"List.ForceBlankBefore", Field, 19}, + {"List.ForceBlankBetween", Field, 19}, + {"List.Items", Field, 19}, + {"ListItem", Type, 19}, + {"ListItem.Content", Field, 19}, + {"ListItem.Number", Field, 19}, + {"Paragraph", Type, 19}, + {"Paragraph.Text", Field, 19}, + {"Parser", Type, 19}, + {"Parser.LookupPackage", Field, 19}, + {"Parser.LookupSym", Field, 19}, + {"Parser.Words", Field, 19}, + {"Plain", Type, 19}, + {"Printer", Type, 19}, + {"Printer.DocLinkBaseURL", Field, 19}, + {"Printer.DocLinkURL", Field, 19}, + {"Printer.HeadingID", Field, 19}, + {"Printer.HeadingLevel", Field, 19}, + {"Printer.TextCodePrefix", Field, 19}, + {"Printer.TextPrefix", Field, 19}, + {"Printer.TextWidth", Field, 19}, + {"Text", Type, 19}, + }, + "go/format": { + {"Node", Func, 1}, + {"Source", Func, 1}, + }, + "go/importer": { + {"Default", Func, 5}, + {"For", Func, 5}, + {"ForCompiler", Func, 12}, + {"Lookup", Type, 5}, + }, + "go/parser": { + {"AllErrors", Const, 1}, + {"DeclarationErrors", Const, 0}, + {"ImportsOnly", Const, 0}, + {"Mode", Type, 0}, + {"PackageClauseOnly", Const, 0}, + {"ParseComments", Const, 0}, + {"ParseDir", Func, 0}, + {"ParseExpr", Func, 0}, + {"ParseExprFrom", Func, 5}, + {"ParseFile", Func, 0}, + {"SkipObjectResolution", Const, 17}, + {"SpuriousErrors", Const, 0}, + {"Trace", Const, 0}, + }, + "go/printer": { + {"(*Config).Fprint", Method, 0}, + {"CommentedNode", Type, 0}, + {"CommentedNode.Comments", Field, 0}, + {"CommentedNode.Node", Field, 0}, + {"Config", Type, 0}, + {"Config.Indent", Field, 1}, + {"Config.Mode", Field, 0}, + {"Config.Tabwidth", Field, 0}, + {"Fprint", Func, 0}, + {"Mode", Type, 0}, + {"RawFormat", Const, 0}, + {"SourcePos", Const, 0}, + {"TabIndent", Const, 0}, + {"UseSpaces", Const, 0}, + }, + "go/scanner": { + {"(*ErrorList).Add", Method, 0}, + {"(*ErrorList).RemoveMultiples", Method, 0}, + {"(*ErrorList).Reset", Method, 0}, + {"(*Scanner).Init", Method, 0}, + {"(*Scanner).Scan", Method, 0}, + {"(Error).Error", Method, 0}, + {"(ErrorList).Err", Method, 0}, + {"(ErrorList).Error", Method, 0}, + {"(ErrorList).Len", Method, 0}, + {"(ErrorList).Less", Method, 0}, + {"(ErrorList).Sort", Method, 0}, + {"(ErrorList).Swap", Method, 0}, + {"Error", Type, 0}, + {"Error.Msg", Field, 0}, + {"Error.Pos", Field, 0}, + {"ErrorHandler", Type, 0}, + {"ErrorList", Type, 0}, + {"Mode", Type, 0}, + {"PrintError", Func, 0}, + {"ScanComments", Const, 0}, + {"Scanner", Type, 0}, + {"Scanner.ErrorCount", Field, 0}, + }, + "go/token": { + {"(*File).AddLine", Method, 0}, + {"(*File).AddLineColumnInfo", Method, 11}, + {"(*File).AddLineInfo", Method, 0}, + {"(*File).Base", Method, 0}, + {"(*File).Line", Method, 0}, + {"(*File).LineCount", Method, 0}, + {"(*File).LineStart", Method, 12}, + {"(*File).Lines", Method, 21}, + {"(*File).MergeLine", Method, 2}, + {"(*File).Name", Method, 0}, + {"(*File).Offset", Method, 0}, + {"(*File).Pos", Method, 0}, + {"(*File).Position", Method, 0}, + {"(*File).PositionFor", Method, 4}, + {"(*File).SetLines", Method, 0}, + {"(*File).SetLinesForContent", Method, 0}, + {"(*File).Size", Method, 0}, + {"(*FileSet).AddFile", Method, 0}, + {"(*FileSet).Base", Method, 0}, + {"(*FileSet).File", Method, 0}, + {"(*FileSet).Iterate", Method, 0}, + {"(*FileSet).Position", Method, 0}, + {"(*FileSet).PositionFor", Method, 4}, + {"(*FileSet).Read", Method, 0}, + {"(*FileSet).RemoveFile", Method, 20}, + {"(*FileSet).Write", Method, 0}, + {"(*Position).IsValid", Method, 0}, + {"(Pos).IsValid", Method, 0}, + {"(Position).String", Method, 0}, + {"(Token).IsKeyword", Method, 0}, + {"(Token).IsLiteral", Method, 0}, + {"(Token).IsOperator", Method, 0}, + {"(Token).Precedence", Method, 0}, + {"(Token).String", Method, 0}, + {"ADD", Const, 0}, + {"ADD_ASSIGN", Const, 0}, + {"AND", Const, 0}, + {"AND_ASSIGN", Const, 0}, + {"AND_NOT", Const, 0}, + {"AND_NOT_ASSIGN", Const, 0}, + {"ARROW", Const, 0}, + {"ASSIGN", Const, 0}, + {"BREAK", Const, 0}, + {"CASE", Const, 0}, + {"CHAN", Const, 0}, + {"CHAR", Const, 0}, + {"COLON", Const, 0}, + {"COMMA", Const, 0}, + {"COMMENT", Const, 0}, + {"CONST", Const, 0}, + {"CONTINUE", Const, 0}, + {"DEC", Const, 0}, + {"DEFAULT", Const, 0}, + {"DEFER", Const, 0}, + {"DEFINE", Const, 0}, + {"ELLIPSIS", Const, 0}, + {"ELSE", Const, 0}, + {"EOF", Const, 0}, + {"EQL", Const, 0}, + {"FALLTHROUGH", Const, 0}, + {"FLOAT", Const, 0}, + {"FOR", Const, 0}, + {"FUNC", Const, 0}, + {"File", Type, 0}, + {"FileSet", Type, 0}, + {"GEQ", Const, 0}, + {"GO", Const, 0}, + {"GOTO", Const, 0}, + {"GTR", Const, 0}, + {"HighestPrec", Const, 0}, + {"IDENT", Const, 0}, + {"IF", Const, 0}, + {"ILLEGAL", Const, 0}, + {"IMAG", Const, 0}, + {"IMPORT", Const, 0}, + {"INC", Const, 0}, + {"INT", Const, 0}, + {"INTERFACE", Const, 0}, + {"IsExported", Func, 13}, + {"IsIdentifier", Func, 13}, + {"IsKeyword", Func, 13}, + {"LAND", Const, 0}, + {"LBRACE", Const, 0}, + {"LBRACK", Const, 0}, + {"LEQ", Const, 0}, + {"LOR", Const, 0}, + {"LPAREN", Const, 0}, + {"LSS", Const, 0}, + {"Lookup", Func, 0}, + {"LowestPrec", Const, 0}, + {"MAP", Const, 0}, + {"MUL", Const, 0}, + {"MUL_ASSIGN", Const, 0}, + {"NEQ", Const, 0}, + {"NOT", Const, 0}, + {"NewFileSet", Func, 0}, + {"NoPos", Const, 0}, + {"OR", Const, 0}, + {"OR_ASSIGN", Const, 0}, + {"PACKAGE", Const, 0}, + {"PERIOD", Const, 0}, + {"Pos", Type, 0}, + {"Position", Type, 0}, + {"Position.Column", Field, 0}, + {"Position.Filename", Field, 0}, + {"Position.Line", Field, 0}, + {"Position.Offset", Field, 0}, + {"QUO", Const, 0}, + {"QUO_ASSIGN", Const, 0}, + {"RANGE", Const, 0}, + {"RBRACE", Const, 0}, + {"RBRACK", Const, 0}, + {"REM", Const, 0}, + {"REM_ASSIGN", Const, 0}, + {"RETURN", Const, 0}, + {"RPAREN", Const, 0}, + {"SELECT", Const, 0}, + {"SEMICOLON", Const, 0}, + {"SHL", Const, 0}, + {"SHL_ASSIGN", Const, 0}, + {"SHR", Const, 0}, + {"SHR_ASSIGN", Const, 0}, + {"STRING", Const, 0}, + {"STRUCT", Const, 0}, + {"SUB", Const, 0}, + {"SUB_ASSIGN", Const, 0}, + {"SWITCH", Const, 0}, + {"TILDE", Const, 18}, + {"TYPE", Const, 0}, + {"Token", Type, 0}, + {"UnaryPrec", Const, 0}, + {"VAR", Const, 0}, + {"XOR", Const, 0}, + {"XOR_ASSIGN", Const, 0}, + }, + "go/types": { + {"(*Alias).Obj", Method, 22}, + {"(*Alias).Origin", Method, 23}, + {"(*Alias).Rhs", Method, 23}, + {"(*Alias).SetTypeParams", Method, 23}, + {"(*Alias).String", Method, 22}, + {"(*Alias).TypeArgs", Method, 23}, + {"(*Alias).TypeParams", Method, 23}, + {"(*Alias).Underlying", Method, 22}, + {"(*ArgumentError).Error", Method, 18}, + {"(*ArgumentError).Unwrap", Method, 18}, + {"(*Array).Elem", Method, 5}, + {"(*Array).Len", Method, 5}, + {"(*Array).String", Method, 5}, + {"(*Array).Underlying", Method, 5}, + {"(*Basic).Info", Method, 5}, + {"(*Basic).Kind", Method, 5}, + {"(*Basic).Name", Method, 5}, + {"(*Basic).String", Method, 5}, + {"(*Basic).Underlying", Method, 5}, + {"(*Builtin).Exported", Method, 5}, + {"(*Builtin).Id", Method, 5}, + {"(*Builtin).Name", Method, 5}, + {"(*Builtin).Parent", Method, 5}, + {"(*Builtin).Pkg", Method, 5}, + {"(*Builtin).Pos", Method, 5}, + {"(*Builtin).String", Method, 5}, + {"(*Builtin).Type", Method, 5}, + {"(*Chan).Dir", Method, 5}, + {"(*Chan).Elem", Method, 5}, + {"(*Chan).String", Method, 5}, + {"(*Chan).Underlying", Method, 5}, + {"(*Checker).Files", Method, 5}, + {"(*Config).Check", Method, 5}, + {"(*Const).Exported", Method, 5}, + {"(*Const).Id", Method, 5}, + {"(*Const).Name", Method, 5}, + {"(*Const).Parent", Method, 5}, + {"(*Const).Pkg", Method, 5}, + {"(*Const).Pos", Method, 5}, + {"(*Const).String", Method, 5}, + {"(*Const).Type", Method, 5}, + {"(*Const).Val", Method, 5}, + {"(*Func).Exported", Method, 5}, + {"(*Func).FullName", Method, 5}, + {"(*Func).Id", Method, 5}, + {"(*Func).Name", Method, 5}, + {"(*Func).Origin", Method, 19}, + {"(*Func).Parent", Method, 5}, + {"(*Func).Pkg", Method, 5}, + {"(*Func).Pos", Method, 5}, + {"(*Func).Scope", Method, 5}, + {"(*Func).Signature", Method, 23}, + {"(*Func).String", Method, 5}, + {"(*Func).Type", Method, 5}, + {"(*Info).ObjectOf", Method, 5}, + {"(*Info).PkgNameOf", Method, 22}, + {"(*Info).TypeOf", Method, 5}, + {"(*Initializer).String", Method, 5}, + {"(*Interface).Complete", Method, 5}, + {"(*Interface).Embedded", Method, 5}, + {"(*Interface).EmbeddedType", Method, 11}, + {"(*Interface).Empty", Method, 5}, + {"(*Interface).ExplicitMethod", Method, 5}, + {"(*Interface).IsComparable", Method, 18}, + {"(*Interface).IsImplicit", Method, 18}, + {"(*Interface).IsMethodSet", Method, 18}, + {"(*Interface).MarkImplicit", Method, 18}, + {"(*Interface).Method", Method, 5}, + {"(*Interface).NumEmbeddeds", Method, 5}, + {"(*Interface).NumExplicitMethods", Method, 5}, + {"(*Interface).NumMethods", Method, 5}, + {"(*Interface).String", Method, 5}, + {"(*Interface).Underlying", Method, 5}, + {"(*Label).Exported", Method, 5}, + {"(*Label).Id", Method, 5}, + {"(*Label).Name", Method, 5}, + {"(*Label).Parent", Method, 5}, + {"(*Label).Pkg", Method, 5}, + {"(*Label).Pos", Method, 5}, + {"(*Label).String", Method, 5}, + {"(*Label).Type", Method, 5}, + {"(*Map).Elem", Method, 5}, + {"(*Map).Key", Method, 5}, + {"(*Map).String", Method, 5}, + {"(*Map).Underlying", Method, 5}, + {"(*MethodSet).At", Method, 5}, + {"(*MethodSet).Len", Method, 5}, + {"(*MethodSet).Lookup", Method, 5}, + {"(*MethodSet).String", Method, 5}, + {"(*Named).AddMethod", Method, 5}, + {"(*Named).Method", Method, 5}, + {"(*Named).NumMethods", Method, 5}, + {"(*Named).Obj", Method, 5}, + {"(*Named).Origin", Method, 18}, + {"(*Named).SetTypeParams", Method, 18}, + {"(*Named).SetUnderlying", Method, 5}, + {"(*Named).String", Method, 5}, + {"(*Named).TypeArgs", Method, 18}, + {"(*Named).TypeParams", Method, 18}, + {"(*Named).Underlying", Method, 5}, + {"(*Nil).Exported", Method, 5}, + {"(*Nil).Id", Method, 5}, + {"(*Nil).Name", Method, 5}, + {"(*Nil).Parent", Method, 5}, + {"(*Nil).Pkg", Method, 5}, + {"(*Nil).Pos", Method, 5}, + {"(*Nil).String", Method, 5}, + {"(*Nil).Type", Method, 5}, + {"(*Package).Complete", Method, 5}, + {"(*Package).GoVersion", Method, 21}, + {"(*Package).Imports", Method, 5}, + {"(*Package).MarkComplete", Method, 5}, + {"(*Package).Name", Method, 5}, + {"(*Package).Path", Method, 5}, + {"(*Package).Scope", Method, 5}, + {"(*Package).SetImports", Method, 5}, + {"(*Package).SetName", Method, 6}, + {"(*Package).String", Method, 5}, + {"(*PkgName).Exported", Method, 5}, + {"(*PkgName).Id", Method, 5}, + {"(*PkgName).Imported", Method, 5}, + {"(*PkgName).Name", Method, 5}, + {"(*PkgName).Parent", Method, 5}, + {"(*PkgName).Pkg", Method, 5}, + {"(*PkgName).Pos", Method, 5}, + {"(*PkgName).String", Method, 5}, + {"(*PkgName).Type", Method, 5}, + {"(*Pointer).Elem", Method, 5}, + {"(*Pointer).String", Method, 5}, + {"(*Pointer).Underlying", Method, 5}, + {"(*Scope).Child", Method, 5}, + {"(*Scope).Contains", Method, 5}, + {"(*Scope).End", Method, 5}, + {"(*Scope).Innermost", Method, 5}, + {"(*Scope).Insert", Method, 5}, + {"(*Scope).Len", Method, 5}, + {"(*Scope).Lookup", Method, 5}, + {"(*Scope).LookupParent", Method, 5}, + {"(*Scope).Names", Method, 5}, + {"(*Scope).NumChildren", Method, 5}, + {"(*Scope).Parent", Method, 5}, + {"(*Scope).Pos", Method, 5}, + {"(*Scope).String", Method, 5}, + {"(*Scope).WriteTo", Method, 5}, + {"(*Selection).Index", Method, 5}, + {"(*Selection).Indirect", Method, 5}, + {"(*Selection).Kind", Method, 5}, + {"(*Selection).Obj", Method, 5}, + {"(*Selection).Recv", Method, 5}, + {"(*Selection).String", Method, 5}, + {"(*Selection).Type", Method, 5}, + {"(*Signature).Params", Method, 5}, + {"(*Signature).Recv", Method, 5}, + {"(*Signature).RecvTypeParams", Method, 18}, + {"(*Signature).Results", Method, 5}, + {"(*Signature).String", Method, 5}, + {"(*Signature).TypeParams", Method, 18}, + {"(*Signature).Underlying", Method, 5}, + {"(*Signature).Variadic", Method, 5}, + {"(*Slice).Elem", Method, 5}, + {"(*Slice).String", Method, 5}, + {"(*Slice).Underlying", Method, 5}, + {"(*StdSizes).Alignof", Method, 5}, + {"(*StdSizes).Offsetsof", Method, 5}, + {"(*StdSizes).Sizeof", Method, 5}, + {"(*Struct).Field", Method, 5}, + {"(*Struct).NumFields", Method, 5}, + {"(*Struct).String", Method, 5}, + {"(*Struct).Tag", Method, 5}, + {"(*Struct).Underlying", Method, 5}, + {"(*Term).String", Method, 18}, + {"(*Term).Tilde", Method, 18}, + {"(*Term).Type", Method, 18}, + {"(*Tuple).At", Method, 5}, + {"(*Tuple).Len", Method, 5}, + {"(*Tuple).String", Method, 5}, + {"(*Tuple).Underlying", Method, 5}, + {"(*TypeList).At", Method, 18}, + {"(*TypeList).Len", Method, 18}, + {"(*TypeName).Exported", Method, 5}, + {"(*TypeName).Id", Method, 5}, + {"(*TypeName).IsAlias", Method, 9}, + {"(*TypeName).Name", Method, 5}, + {"(*TypeName).Parent", Method, 5}, + {"(*TypeName).Pkg", Method, 5}, + {"(*TypeName).Pos", Method, 5}, + {"(*TypeName).String", Method, 5}, + {"(*TypeName).Type", Method, 5}, + {"(*TypeParam).Constraint", Method, 18}, + {"(*TypeParam).Index", Method, 18}, + {"(*TypeParam).Obj", Method, 18}, + {"(*TypeParam).SetConstraint", Method, 18}, + {"(*TypeParam).String", Method, 18}, + {"(*TypeParam).Underlying", Method, 18}, + {"(*TypeParamList).At", Method, 18}, + {"(*TypeParamList).Len", Method, 18}, + {"(*Union).Len", Method, 18}, + {"(*Union).String", Method, 18}, + {"(*Union).Term", Method, 18}, + {"(*Union).Underlying", Method, 18}, + {"(*Var).Anonymous", Method, 5}, + {"(*Var).Embedded", Method, 11}, + {"(*Var).Exported", Method, 5}, + {"(*Var).Id", Method, 5}, + {"(*Var).IsField", Method, 5}, + {"(*Var).Name", Method, 5}, + {"(*Var).Origin", Method, 19}, + {"(*Var).Parent", Method, 5}, + {"(*Var).Pkg", Method, 5}, + {"(*Var).Pos", Method, 5}, + {"(*Var).String", Method, 5}, + {"(*Var).Type", Method, 5}, + {"(Checker).ObjectOf", Method, 5}, + {"(Checker).PkgNameOf", Method, 22}, + {"(Checker).TypeOf", Method, 5}, + {"(Error).Error", Method, 5}, + {"(TypeAndValue).Addressable", Method, 5}, + {"(TypeAndValue).Assignable", Method, 5}, + {"(TypeAndValue).HasOk", Method, 5}, + {"(TypeAndValue).IsBuiltin", Method, 5}, + {"(TypeAndValue).IsNil", Method, 5}, + {"(TypeAndValue).IsType", Method, 5}, + {"(TypeAndValue).IsValue", Method, 5}, + {"(TypeAndValue).IsVoid", Method, 5}, + {"Alias", Type, 22}, + {"ArgumentError", Type, 18}, + {"ArgumentError.Err", Field, 18}, + {"ArgumentError.Index", Field, 18}, + {"Array", Type, 5}, + {"AssertableTo", Func, 5}, + {"AssignableTo", Func, 5}, + {"Basic", Type, 5}, + {"BasicInfo", Type, 5}, + {"BasicKind", Type, 5}, + {"Bool", Const, 5}, + {"Builtin", Type, 5}, + {"Byte", Const, 5}, + {"Chan", Type, 5}, + {"ChanDir", Type, 5}, + {"CheckExpr", Func, 13}, + {"Checker", Type, 5}, + {"Checker.Info", Field, 5}, + {"Comparable", Func, 5}, + {"Complex128", Const, 5}, + {"Complex64", Const, 5}, + {"Config", Type, 5}, + {"Config.Context", Field, 18}, + {"Config.DisableUnusedImportCheck", Field, 5}, + {"Config.Error", Field, 5}, + {"Config.FakeImportC", Field, 5}, + {"Config.GoVersion", Field, 18}, + {"Config.IgnoreFuncBodies", Field, 5}, + {"Config.Importer", Field, 5}, + {"Config.Sizes", Field, 5}, + {"Const", Type, 5}, + {"Context", Type, 18}, + {"ConvertibleTo", Func, 5}, + {"DefPredeclaredTestFuncs", Func, 5}, + {"Default", Func, 8}, + {"Error", Type, 5}, + {"Error.Fset", Field, 5}, + {"Error.Msg", Field, 5}, + {"Error.Pos", Field, 5}, + {"Error.Soft", Field, 5}, + {"Eval", Func, 5}, + {"ExprString", Func, 5}, + {"FieldVal", Const, 5}, + {"Float32", Const, 5}, + {"Float64", Const, 5}, + {"Func", Type, 5}, + {"Id", Func, 5}, + {"Identical", Func, 5}, + {"IdenticalIgnoreTags", Func, 8}, + {"Implements", Func, 5}, + {"ImportMode", Type, 6}, + {"Importer", Type, 5}, + {"ImporterFrom", Type, 6}, + {"Info", Type, 5}, + {"Info.Defs", Field, 5}, + {"Info.FileVersions", Field, 22}, + {"Info.Implicits", Field, 5}, + {"Info.InitOrder", Field, 5}, + {"Info.Instances", Field, 18}, + {"Info.Scopes", Field, 5}, + {"Info.Selections", Field, 5}, + {"Info.Types", Field, 5}, + {"Info.Uses", Field, 5}, + {"Initializer", Type, 5}, + {"Initializer.Lhs", Field, 5}, + {"Initializer.Rhs", Field, 5}, + {"Instance", Type, 18}, + {"Instance.Type", Field, 18}, + {"Instance.TypeArgs", Field, 18}, + {"Instantiate", Func, 18}, + {"Int", Const, 5}, + {"Int16", Const, 5}, + {"Int32", Const, 5}, + {"Int64", Const, 5}, + {"Int8", Const, 5}, + {"Interface", Type, 5}, + {"Invalid", Const, 5}, + {"IsBoolean", Const, 5}, + {"IsComplex", Const, 5}, + {"IsConstType", Const, 5}, + {"IsFloat", Const, 5}, + {"IsInteger", Const, 5}, + {"IsInterface", Func, 5}, + {"IsNumeric", Const, 5}, + {"IsOrdered", Const, 5}, + {"IsString", Const, 5}, + {"IsUnsigned", Const, 5}, + {"IsUntyped", Const, 5}, + {"Label", Type, 5}, + {"LookupFieldOrMethod", Func, 5}, + {"Map", Type, 5}, + {"MethodExpr", Const, 5}, + {"MethodSet", Type, 5}, + {"MethodVal", Const, 5}, + {"MissingMethod", Func, 5}, + {"Named", Type, 5}, + {"NewAlias", Func, 22}, + {"NewArray", Func, 5}, + {"NewChan", Func, 5}, + {"NewChecker", Func, 5}, + {"NewConst", Func, 5}, + {"NewContext", Func, 18}, + {"NewField", Func, 5}, + {"NewFunc", Func, 5}, + {"NewInterface", Func, 5}, + {"NewInterfaceType", Func, 11}, + {"NewLabel", Func, 5}, + {"NewMap", Func, 5}, + {"NewMethodSet", Func, 5}, + {"NewNamed", Func, 5}, + {"NewPackage", Func, 5}, + {"NewParam", Func, 5}, + {"NewPkgName", Func, 5}, + {"NewPointer", Func, 5}, + {"NewScope", Func, 5}, + {"NewSignature", Func, 5}, + {"NewSignatureType", Func, 18}, + {"NewSlice", Func, 5}, + {"NewStruct", Func, 5}, + {"NewTerm", Func, 18}, + {"NewTuple", Func, 5}, + {"NewTypeName", Func, 5}, + {"NewTypeParam", Func, 18}, + {"NewUnion", Func, 18}, + {"NewVar", Func, 5}, + {"Nil", Type, 5}, + {"Object", Type, 5}, + {"ObjectString", Func, 5}, + {"Package", Type, 5}, + {"PkgName", Type, 5}, + {"Pointer", Type, 5}, + {"Qualifier", Type, 5}, + {"RecvOnly", Const, 5}, + {"RelativeTo", Func, 5}, + {"Rune", Const, 5}, + {"Satisfies", Func, 20}, + {"Scope", Type, 5}, + {"Selection", Type, 5}, + {"SelectionKind", Type, 5}, + {"SelectionString", Func, 5}, + {"SendOnly", Const, 5}, + {"SendRecv", Const, 5}, + {"Signature", Type, 5}, + {"Sizes", Type, 5}, + {"SizesFor", Func, 9}, + {"Slice", Type, 5}, + {"StdSizes", Type, 5}, + {"StdSizes.MaxAlign", Field, 5}, + {"StdSizes.WordSize", Field, 5}, + {"String", Const, 5}, + {"Struct", Type, 5}, + {"Term", Type, 18}, + {"Tuple", Type, 5}, + {"Typ", Var, 5}, + {"Type", Type, 5}, + {"TypeAndValue", Type, 5}, + {"TypeAndValue.Type", Field, 5}, + {"TypeAndValue.Value", Field, 5}, + {"TypeList", Type, 18}, + {"TypeName", Type, 5}, + {"TypeParam", Type, 18}, + {"TypeParamList", Type, 18}, + {"TypeString", Func, 5}, + {"Uint", Const, 5}, + {"Uint16", Const, 5}, + {"Uint32", Const, 5}, + {"Uint64", Const, 5}, + {"Uint8", Const, 5}, + {"Uintptr", Const, 5}, + {"Unalias", Func, 22}, + {"Union", Type, 18}, + {"Universe", Var, 5}, + {"Unsafe", Var, 5}, + {"UnsafePointer", Const, 5}, + {"UntypedBool", Const, 5}, + {"UntypedComplex", Const, 5}, + {"UntypedFloat", Const, 5}, + {"UntypedInt", Const, 5}, + {"UntypedNil", Const, 5}, + {"UntypedRune", Const, 5}, + {"UntypedString", Const, 5}, + {"Var", Type, 5}, + {"WriteExpr", Func, 5}, + {"WriteSignature", Func, 5}, + {"WriteType", Func, 5}, + }, + "go/version": { + {"Compare", Func, 22}, + {"IsValid", Func, 22}, + {"Lang", Func, 22}, + }, + "hash": { + {"Hash", Type, 0}, + {"Hash32", Type, 0}, + {"Hash64", Type, 0}, + }, + "hash/adler32": { + {"Checksum", Func, 0}, + {"New", Func, 0}, + {"Size", Const, 0}, + }, + "hash/crc32": { + {"Castagnoli", Const, 0}, + {"Checksum", Func, 0}, + {"ChecksumIEEE", Func, 0}, + {"IEEE", Const, 0}, + {"IEEETable", Var, 0}, + {"Koopman", Const, 0}, + {"MakeTable", Func, 0}, + {"New", Func, 0}, + {"NewIEEE", Func, 0}, + {"Size", Const, 0}, + {"Table", Type, 0}, + {"Update", Func, 0}, + }, + "hash/crc64": { + {"Checksum", Func, 0}, + {"ECMA", Const, 0}, + {"ISO", Const, 0}, + {"MakeTable", Func, 0}, + {"New", Func, 0}, + {"Size", Const, 0}, + {"Table", Type, 0}, + {"Update", Func, 0}, + }, + "hash/fnv": { + {"New128", Func, 9}, + {"New128a", Func, 9}, + {"New32", Func, 0}, + {"New32a", Func, 0}, + {"New64", Func, 0}, + {"New64a", Func, 0}, + }, + "hash/maphash": { + {"(*Hash).BlockSize", Method, 14}, + {"(*Hash).Reset", Method, 14}, + {"(*Hash).Seed", Method, 14}, + {"(*Hash).SetSeed", Method, 14}, + {"(*Hash).Size", Method, 14}, + {"(*Hash).Sum", Method, 14}, + {"(*Hash).Sum64", Method, 14}, + {"(*Hash).Write", Method, 14}, + {"(*Hash).WriteByte", Method, 14}, + {"(*Hash).WriteString", Method, 14}, + {"Bytes", Func, 19}, + {"Hash", Type, 14}, + {"MakeSeed", Func, 14}, + {"Seed", Type, 14}, + {"String", Func, 19}, + }, + "html": { + {"EscapeString", Func, 0}, + {"UnescapeString", Func, 0}, + }, + "html/template": { + {"(*Error).Error", Method, 0}, + {"(*Template).AddParseTree", Method, 0}, + {"(*Template).Clone", Method, 0}, + {"(*Template).DefinedTemplates", Method, 6}, + {"(*Template).Delims", Method, 0}, + {"(*Template).Execute", Method, 0}, + {"(*Template).ExecuteTemplate", Method, 0}, + {"(*Template).Funcs", Method, 0}, + {"(*Template).Lookup", Method, 0}, + {"(*Template).Name", Method, 0}, + {"(*Template).New", Method, 0}, + {"(*Template).Option", Method, 5}, + {"(*Template).Parse", Method, 0}, + {"(*Template).ParseFS", Method, 16}, + {"(*Template).ParseFiles", Method, 0}, + {"(*Template).ParseGlob", Method, 0}, + {"(*Template).Templates", Method, 0}, + {"CSS", Type, 0}, + {"ErrAmbigContext", Const, 0}, + {"ErrBadHTML", Const, 0}, + {"ErrBranchEnd", Const, 0}, + {"ErrEndContext", Const, 0}, + {"ErrJSTemplate", Const, 21}, + {"ErrNoSuchTemplate", Const, 0}, + {"ErrOutputContext", Const, 0}, + {"ErrPartialCharset", Const, 0}, + {"ErrPartialEscape", Const, 0}, + {"ErrPredefinedEscaper", Const, 9}, + {"ErrRangeLoopReentry", Const, 0}, + {"ErrSlashAmbig", Const, 0}, + {"Error", Type, 0}, + {"Error.Description", Field, 0}, + {"Error.ErrorCode", Field, 0}, + {"Error.Line", Field, 0}, + {"Error.Name", Field, 0}, + {"Error.Node", Field, 4}, + {"ErrorCode", Type, 0}, + {"FuncMap", Type, 0}, + {"HTML", Type, 0}, + {"HTMLAttr", Type, 0}, + {"HTMLEscape", Func, 0}, + {"HTMLEscapeString", Func, 0}, + {"HTMLEscaper", Func, 0}, + {"IsTrue", Func, 6}, + {"JS", Type, 0}, + {"JSEscape", Func, 0}, + {"JSEscapeString", Func, 0}, + {"JSEscaper", Func, 0}, + {"JSStr", Type, 0}, + {"Must", Func, 0}, + {"New", Func, 0}, + {"OK", Const, 0}, + {"ParseFS", Func, 16}, + {"ParseFiles", Func, 0}, + {"ParseGlob", Func, 0}, + {"Srcset", Type, 10}, + {"Template", Type, 0}, + {"Template.Tree", Field, 2}, + {"URL", Type, 0}, + {"URLQueryEscaper", Func, 0}, + }, + "image": { + {"(*Alpha).AlphaAt", Method, 4}, + {"(*Alpha).At", Method, 0}, + {"(*Alpha).Bounds", Method, 0}, + {"(*Alpha).ColorModel", Method, 0}, + {"(*Alpha).Opaque", Method, 0}, + {"(*Alpha).PixOffset", Method, 0}, + {"(*Alpha).RGBA64At", Method, 17}, + {"(*Alpha).Set", Method, 0}, + {"(*Alpha).SetAlpha", Method, 0}, + {"(*Alpha).SetRGBA64", Method, 17}, + {"(*Alpha).SubImage", Method, 0}, + {"(*Alpha16).Alpha16At", Method, 4}, + {"(*Alpha16).At", Method, 0}, + {"(*Alpha16).Bounds", Method, 0}, + {"(*Alpha16).ColorModel", Method, 0}, + {"(*Alpha16).Opaque", Method, 0}, + {"(*Alpha16).PixOffset", Method, 0}, + {"(*Alpha16).RGBA64At", Method, 17}, + {"(*Alpha16).Set", Method, 0}, + {"(*Alpha16).SetAlpha16", Method, 0}, + {"(*Alpha16).SetRGBA64", Method, 17}, + {"(*Alpha16).SubImage", Method, 0}, + {"(*CMYK).At", Method, 5}, + {"(*CMYK).Bounds", Method, 5}, + {"(*CMYK).CMYKAt", Method, 5}, + {"(*CMYK).ColorModel", Method, 5}, + {"(*CMYK).Opaque", Method, 5}, + {"(*CMYK).PixOffset", Method, 5}, + {"(*CMYK).RGBA64At", Method, 17}, + {"(*CMYK).Set", Method, 5}, + {"(*CMYK).SetCMYK", Method, 5}, + {"(*CMYK).SetRGBA64", Method, 17}, + {"(*CMYK).SubImage", Method, 5}, + {"(*Gray).At", Method, 0}, + {"(*Gray).Bounds", Method, 0}, + {"(*Gray).ColorModel", Method, 0}, + {"(*Gray).GrayAt", Method, 4}, + {"(*Gray).Opaque", Method, 0}, + {"(*Gray).PixOffset", Method, 0}, + {"(*Gray).RGBA64At", Method, 17}, + {"(*Gray).Set", Method, 0}, + {"(*Gray).SetGray", Method, 0}, + {"(*Gray).SetRGBA64", Method, 17}, + {"(*Gray).SubImage", Method, 0}, + {"(*Gray16).At", Method, 0}, + {"(*Gray16).Bounds", Method, 0}, + {"(*Gray16).ColorModel", Method, 0}, + {"(*Gray16).Gray16At", Method, 4}, + {"(*Gray16).Opaque", Method, 0}, + {"(*Gray16).PixOffset", Method, 0}, + {"(*Gray16).RGBA64At", Method, 17}, + {"(*Gray16).Set", Method, 0}, + {"(*Gray16).SetGray16", Method, 0}, + {"(*Gray16).SetRGBA64", Method, 17}, + {"(*Gray16).SubImage", Method, 0}, + {"(*NRGBA).At", Method, 0}, + {"(*NRGBA).Bounds", Method, 0}, + {"(*NRGBA).ColorModel", Method, 0}, + {"(*NRGBA).NRGBAAt", Method, 4}, + {"(*NRGBA).Opaque", Method, 0}, + {"(*NRGBA).PixOffset", Method, 0}, + {"(*NRGBA).RGBA64At", Method, 17}, + {"(*NRGBA).Set", Method, 0}, + {"(*NRGBA).SetNRGBA", Method, 0}, + {"(*NRGBA).SetRGBA64", Method, 17}, + {"(*NRGBA).SubImage", Method, 0}, + {"(*NRGBA64).At", Method, 0}, + {"(*NRGBA64).Bounds", Method, 0}, + {"(*NRGBA64).ColorModel", Method, 0}, + {"(*NRGBA64).NRGBA64At", Method, 4}, + {"(*NRGBA64).Opaque", Method, 0}, + {"(*NRGBA64).PixOffset", Method, 0}, + {"(*NRGBA64).RGBA64At", Method, 17}, + {"(*NRGBA64).Set", Method, 0}, + {"(*NRGBA64).SetNRGBA64", Method, 0}, + {"(*NRGBA64).SetRGBA64", Method, 17}, + {"(*NRGBA64).SubImage", Method, 0}, + {"(*NYCbCrA).AOffset", Method, 6}, + {"(*NYCbCrA).At", Method, 6}, + {"(*NYCbCrA).Bounds", Method, 6}, + {"(*NYCbCrA).COffset", Method, 6}, + {"(*NYCbCrA).ColorModel", Method, 6}, + {"(*NYCbCrA).NYCbCrAAt", Method, 6}, + {"(*NYCbCrA).Opaque", Method, 6}, + {"(*NYCbCrA).RGBA64At", Method, 17}, + {"(*NYCbCrA).SubImage", Method, 6}, + {"(*NYCbCrA).YCbCrAt", Method, 6}, + {"(*NYCbCrA).YOffset", Method, 6}, + {"(*Paletted).At", Method, 0}, + {"(*Paletted).Bounds", Method, 0}, + {"(*Paletted).ColorIndexAt", Method, 0}, + {"(*Paletted).ColorModel", Method, 0}, + {"(*Paletted).Opaque", Method, 0}, + {"(*Paletted).PixOffset", Method, 0}, + {"(*Paletted).RGBA64At", Method, 17}, + {"(*Paletted).Set", Method, 0}, + {"(*Paletted).SetColorIndex", Method, 0}, + {"(*Paletted).SetRGBA64", Method, 17}, + {"(*Paletted).SubImage", Method, 0}, + {"(*RGBA).At", Method, 0}, + {"(*RGBA).Bounds", Method, 0}, + {"(*RGBA).ColorModel", Method, 0}, + {"(*RGBA).Opaque", Method, 0}, + {"(*RGBA).PixOffset", Method, 0}, + {"(*RGBA).RGBA64At", Method, 17}, + {"(*RGBA).RGBAAt", Method, 4}, + {"(*RGBA).Set", Method, 0}, + {"(*RGBA).SetRGBA", Method, 0}, + {"(*RGBA).SetRGBA64", Method, 17}, + {"(*RGBA).SubImage", Method, 0}, + {"(*RGBA64).At", Method, 0}, + {"(*RGBA64).Bounds", Method, 0}, + {"(*RGBA64).ColorModel", Method, 0}, + {"(*RGBA64).Opaque", Method, 0}, + {"(*RGBA64).PixOffset", Method, 0}, + {"(*RGBA64).RGBA64At", Method, 4}, + {"(*RGBA64).Set", Method, 0}, + {"(*RGBA64).SetRGBA64", Method, 0}, + {"(*RGBA64).SubImage", Method, 0}, + {"(*Uniform).At", Method, 0}, + {"(*Uniform).Bounds", Method, 0}, + {"(*Uniform).ColorModel", Method, 0}, + {"(*Uniform).Convert", Method, 0}, + {"(*Uniform).Opaque", Method, 0}, + {"(*Uniform).RGBA", Method, 0}, + {"(*Uniform).RGBA64At", Method, 17}, + {"(*YCbCr).At", Method, 0}, + {"(*YCbCr).Bounds", Method, 0}, + {"(*YCbCr).COffset", Method, 0}, + {"(*YCbCr).ColorModel", Method, 0}, + {"(*YCbCr).Opaque", Method, 0}, + {"(*YCbCr).RGBA64At", Method, 17}, + {"(*YCbCr).SubImage", Method, 0}, + {"(*YCbCr).YCbCrAt", Method, 4}, + {"(*YCbCr).YOffset", Method, 0}, + {"(Point).Add", Method, 0}, + {"(Point).Div", Method, 0}, + {"(Point).Eq", Method, 0}, + {"(Point).In", Method, 0}, + {"(Point).Mod", Method, 0}, + {"(Point).Mul", Method, 0}, + {"(Point).String", Method, 0}, + {"(Point).Sub", Method, 0}, + {"(Rectangle).Add", Method, 0}, + {"(Rectangle).At", Method, 5}, + {"(Rectangle).Bounds", Method, 5}, + {"(Rectangle).Canon", Method, 0}, + {"(Rectangle).ColorModel", Method, 5}, + {"(Rectangle).Dx", Method, 0}, + {"(Rectangle).Dy", Method, 0}, + {"(Rectangle).Empty", Method, 0}, + {"(Rectangle).Eq", Method, 0}, + {"(Rectangle).In", Method, 0}, + {"(Rectangle).Inset", Method, 0}, + {"(Rectangle).Intersect", Method, 0}, + {"(Rectangle).Overlaps", Method, 0}, + {"(Rectangle).RGBA64At", Method, 17}, + {"(Rectangle).Size", Method, 0}, + {"(Rectangle).String", Method, 0}, + {"(Rectangle).Sub", Method, 0}, + {"(Rectangle).Union", Method, 0}, + {"(YCbCrSubsampleRatio).String", Method, 0}, + {"Alpha", Type, 0}, + {"Alpha.Pix", Field, 0}, + {"Alpha.Rect", Field, 0}, + {"Alpha.Stride", Field, 0}, + {"Alpha16", Type, 0}, + {"Alpha16.Pix", Field, 0}, + {"Alpha16.Rect", Field, 0}, + {"Alpha16.Stride", Field, 0}, + {"Black", Var, 0}, + {"CMYK", Type, 5}, + {"CMYK.Pix", Field, 5}, + {"CMYK.Rect", Field, 5}, + {"CMYK.Stride", Field, 5}, + {"Config", Type, 0}, + {"Config.ColorModel", Field, 0}, + {"Config.Height", Field, 0}, + {"Config.Width", Field, 0}, + {"Decode", Func, 0}, + {"DecodeConfig", Func, 0}, + {"ErrFormat", Var, 0}, + {"Gray", Type, 0}, + {"Gray.Pix", Field, 0}, + {"Gray.Rect", Field, 0}, + {"Gray.Stride", Field, 0}, + {"Gray16", Type, 0}, + {"Gray16.Pix", Field, 0}, + {"Gray16.Rect", Field, 0}, + {"Gray16.Stride", Field, 0}, + {"Image", Type, 0}, + {"NRGBA", Type, 0}, + {"NRGBA.Pix", Field, 0}, + {"NRGBA.Rect", Field, 0}, + {"NRGBA.Stride", Field, 0}, + {"NRGBA64", Type, 0}, + {"NRGBA64.Pix", Field, 0}, + {"NRGBA64.Rect", Field, 0}, + {"NRGBA64.Stride", Field, 0}, + {"NYCbCrA", Type, 6}, + {"NYCbCrA.A", Field, 6}, + {"NYCbCrA.AStride", Field, 6}, + {"NYCbCrA.YCbCr", Field, 6}, + {"NewAlpha", Func, 0}, + {"NewAlpha16", Func, 0}, + {"NewCMYK", Func, 5}, + {"NewGray", Func, 0}, + {"NewGray16", Func, 0}, + {"NewNRGBA", Func, 0}, + {"NewNRGBA64", Func, 0}, + {"NewNYCbCrA", Func, 6}, + {"NewPaletted", Func, 0}, + {"NewRGBA", Func, 0}, + {"NewRGBA64", Func, 0}, + {"NewUniform", Func, 0}, + {"NewYCbCr", Func, 0}, + {"Opaque", Var, 0}, + {"Paletted", Type, 0}, + {"Paletted.Palette", Field, 0}, + {"Paletted.Pix", Field, 0}, + {"Paletted.Rect", Field, 0}, + {"Paletted.Stride", Field, 0}, + {"PalettedImage", Type, 0}, + {"Point", Type, 0}, + {"Point.X", Field, 0}, + {"Point.Y", Field, 0}, + {"Pt", Func, 0}, + {"RGBA", Type, 0}, + {"RGBA.Pix", Field, 0}, + {"RGBA.Rect", Field, 0}, + {"RGBA.Stride", Field, 0}, + {"RGBA64", Type, 0}, + {"RGBA64.Pix", Field, 0}, + {"RGBA64.Rect", Field, 0}, + {"RGBA64.Stride", Field, 0}, + {"RGBA64Image", Type, 17}, + {"Rect", Func, 0}, + {"Rectangle", Type, 0}, + {"Rectangle.Max", Field, 0}, + {"Rectangle.Min", Field, 0}, + {"RegisterFormat", Func, 0}, + {"Transparent", Var, 0}, + {"Uniform", Type, 0}, + {"Uniform.C", Field, 0}, + {"White", Var, 0}, + {"YCbCr", Type, 0}, + {"YCbCr.CStride", Field, 0}, + {"YCbCr.Cb", Field, 0}, + {"YCbCr.Cr", Field, 0}, + {"YCbCr.Rect", Field, 0}, + {"YCbCr.SubsampleRatio", Field, 0}, + {"YCbCr.Y", Field, 0}, + {"YCbCr.YStride", Field, 0}, + {"YCbCrSubsampleRatio", Type, 0}, + {"YCbCrSubsampleRatio410", Const, 5}, + {"YCbCrSubsampleRatio411", Const, 5}, + {"YCbCrSubsampleRatio420", Const, 0}, + {"YCbCrSubsampleRatio422", Const, 0}, + {"YCbCrSubsampleRatio440", Const, 1}, + {"YCbCrSubsampleRatio444", Const, 0}, + {"ZP", Var, 0}, + {"ZR", Var, 0}, + }, + "image/color": { + {"(Alpha).RGBA", Method, 0}, + {"(Alpha16).RGBA", Method, 0}, + {"(CMYK).RGBA", Method, 5}, + {"(Gray).RGBA", Method, 0}, + {"(Gray16).RGBA", Method, 0}, + {"(NRGBA).RGBA", Method, 0}, + {"(NRGBA64).RGBA", Method, 0}, + {"(NYCbCrA).RGBA", Method, 6}, + {"(Palette).Convert", Method, 0}, + {"(Palette).Index", Method, 0}, + {"(RGBA).RGBA", Method, 0}, + {"(RGBA64).RGBA", Method, 0}, + {"(YCbCr).RGBA", Method, 0}, + {"Alpha", Type, 0}, + {"Alpha.A", Field, 0}, + {"Alpha16", Type, 0}, + {"Alpha16.A", Field, 0}, + {"Alpha16Model", Var, 0}, + {"AlphaModel", Var, 0}, + {"Black", Var, 0}, + {"CMYK", Type, 5}, + {"CMYK.C", Field, 5}, + {"CMYK.K", Field, 5}, + {"CMYK.M", Field, 5}, + {"CMYK.Y", Field, 5}, + {"CMYKModel", Var, 5}, + {"CMYKToRGB", Func, 5}, + {"Color", Type, 0}, + {"Gray", Type, 0}, + {"Gray.Y", Field, 0}, + {"Gray16", Type, 0}, + {"Gray16.Y", Field, 0}, + {"Gray16Model", Var, 0}, + {"GrayModel", Var, 0}, + {"Model", Type, 0}, + {"ModelFunc", Func, 0}, + {"NRGBA", Type, 0}, + {"NRGBA.A", Field, 0}, + {"NRGBA.B", Field, 0}, + {"NRGBA.G", Field, 0}, + {"NRGBA.R", Field, 0}, + {"NRGBA64", Type, 0}, + {"NRGBA64.A", Field, 0}, + {"NRGBA64.B", Field, 0}, + {"NRGBA64.G", Field, 0}, + {"NRGBA64.R", Field, 0}, + {"NRGBA64Model", Var, 0}, + {"NRGBAModel", Var, 0}, + {"NYCbCrA", Type, 6}, + {"NYCbCrA.A", Field, 6}, + {"NYCbCrA.YCbCr", Field, 6}, + {"NYCbCrAModel", Var, 6}, + {"Opaque", Var, 0}, + {"Palette", Type, 0}, + {"RGBA", Type, 0}, + {"RGBA.A", Field, 0}, + {"RGBA.B", Field, 0}, + {"RGBA.G", Field, 0}, + {"RGBA.R", Field, 0}, + {"RGBA64", Type, 0}, + {"RGBA64.A", Field, 0}, + {"RGBA64.B", Field, 0}, + {"RGBA64.G", Field, 0}, + {"RGBA64.R", Field, 0}, + {"RGBA64Model", Var, 0}, + {"RGBAModel", Var, 0}, + {"RGBToCMYK", Func, 5}, + {"RGBToYCbCr", Func, 0}, + {"Transparent", Var, 0}, + {"White", Var, 0}, + {"YCbCr", Type, 0}, + {"YCbCr.Cb", Field, 0}, + {"YCbCr.Cr", Field, 0}, + {"YCbCr.Y", Field, 0}, + {"YCbCrModel", Var, 0}, + {"YCbCrToRGB", Func, 0}, + }, + "image/color/palette": { + {"Plan9", Var, 2}, + {"WebSafe", Var, 2}, + }, + "image/draw": { + {"(Op).Draw", Method, 2}, + {"Draw", Func, 0}, + {"DrawMask", Func, 0}, + {"Drawer", Type, 2}, + {"FloydSteinberg", Var, 2}, + {"Image", Type, 0}, + {"Op", Type, 0}, + {"Over", Const, 0}, + {"Quantizer", Type, 2}, + {"RGBA64Image", Type, 17}, + {"Src", Const, 0}, + }, + "image/gif": { + {"Decode", Func, 0}, + {"DecodeAll", Func, 0}, + {"DecodeConfig", Func, 0}, + {"DisposalBackground", Const, 5}, + {"DisposalNone", Const, 5}, + {"DisposalPrevious", Const, 5}, + {"Encode", Func, 2}, + {"EncodeAll", Func, 2}, + {"GIF", Type, 0}, + {"GIF.BackgroundIndex", Field, 5}, + {"GIF.Config", Field, 5}, + {"GIF.Delay", Field, 0}, + {"GIF.Disposal", Field, 5}, + {"GIF.Image", Field, 0}, + {"GIF.LoopCount", Field, 0}, + {"Options", Type, 2}, + {"Options.Drawer", Field, 2}, + {"Options.NumColors", Field, 2}, + {"Options.Quantizer", Field, 2}, + }, + "image/jpeg": { + {"(FormatError).Error", Method, 0}, + {"(UnsupportedError).Error", Method, 0}, + {"Decode", Func, 0}, + {"DecodeConfig", Func, 0}, + {"DefaultQuality", Const, 0}, + {"Encode", Func, 0}, + {"FormatError", Type, 0}, + {"Options", Type, 0}, + {"Options.Quality", Field, 0}, + {"Reader", Type, 0}, + {"UnsupportedError", Type, 0}, + }, + "image/png": { + {"(*Encoder).Encode", Method, 4}, + {"(FormatError).Error", Method, 0}, + {"(UnsupportedError).Error", Method, 0}, + {"BestCompression", Const, 4}, + {"BestSpeed", Const, 4}, + {"CompressionLevel", Type, 4}, + {"Decode", Func, 0}, + {"DecodeConfig", Func, 0}, + {"DefaultCompression", Const, 4}, + {"Encode", Func, 0}, + {"Encoder", Type, 4}, + {"Encoder.BufferPool", Field, 9}, + {"Encoder.CompressionLevel", Field, 4}, + {"EncoderBuffer", Type, 9}, + {"EncoderBufferPool", Type, 9}, + {"FormatError", Type, 0}, + {"NoCompression", Const, 4}, + {"UnsupportedError", Type, 0}, + }, + "index/suffixarray": { + {"(*Index).Bytes", Method, 0}, + {"(*Index).FindAllIndex", Method, 0}, + {"(*Index).Lookup", Method, 0}, + {"(*Index).Read", Method, 0}, + {"(*Index).Write", Method, 0}, + {"Index", Type, 0}, + {"New", Func, 0}, + }, + "io": { + {"(*LimitedReader).Read", Method, 0}, + {"(*OffsetWriter).Seek", Method, 20}, + {"(*OffsetWriter).Write", Method, 20}, + {"(*OffsetWriter).WriteAt", Method, 20}, + {"(*PipeReader).Close", Method, 0}, + {"(*PipeReader).CloseWithError", Method, 0}, + {"(*PipeReader).Read", Method, 0}, + {"(*PipeWriter).Close", Method, 0}, + {"(*PipeWriter).CloseWithError", Method, 0}, + {"(*PipeWriter).Write", Method, 0}, + {"(*SectionReader).Outer", Method, 22}, + {"(*SectionReader).Read", Method, 0}, + {"(*SectionReader).ReadAt", Method, 0}, + {"(*SectionReader).Seek", Method, 0}, + {"(*SectionReader).Size", Method, 0}, + {"ByteReader", Type, 0}, + {"ByteScanner", Type, 0}, + {"ByteWriter", Type, 1}, + {"Closer", Type, 0}, + {"Copy", Func, 0}, + {"CopyBuffer", Func, 5}, + {"CopyN", Func, 0}, + {"Discard", Var, 16}, + {"EOF", Var, 0}, + {"ErrClosedPipe", Var, 0}, + {"ErrNoProgress", Var, 1}, + {"ErrShortBuffer", Var, 0}, + {"ErrShortWrite", Var, 0}, + {"ErrUnexpectedEOF", Var, 0}, + {"LimitReader", Func, 0}, + {"LimitedReader", Type, 0}, + {"LimitedReader.N", Field, 0}, + {"LimitedReader.R", Field, 0}, + {"MultiReader", Func, 0}, + {"MultiWriter", Func, 0}, + {"NewOffsetWriter", Func, 20}, + {"NewSectionReader", Func, 0}, + {"NopCloser", Func, 16}, + {"OffsetWriter", Type, 20}, + {"Pipe", Func, 0}, + {"PipeReader", Type, 0}, + {"PipeWriter", Type, 0}, + {"ReadAll", Func, 16}, + {"ReadAtLeast", Func, 0}, + {"ReadCloser", Type, 0}, + {"ReadFull", Func, 0}, + {"ReadSeekCloser", Type, 16}, + {"ReadSeeker", Type, 0}, + {"ReadWriteCloser", Type, 0}, + {"ReadWriteSeeker", Type, 0}, + {"ReadWriter", Type, 0}, + {"Reader", Type, 0}, + {"ReaderAt", Type, 0}, + {"ReaderFrom", Type, 0}, + {"RuneReader", Type, 0}, + {"RuneScanner", Type, 0}, + {"SectionReader", Type, 0}, + {"SeekCurrent", Const, 7}, + {"SeekEnd", Const, 7}, + {"SeekStart", Const, 7}, + {"Seeker", Type, 0}, + {"StringWriter", Type, 12}, + {"TeeReader", Func, 0}, + {"WriteCloser", Type, 0}, + {"WriteSeeker", Type, 0}, + {"WriteString", Func, 0}, + {"Writer", Type, 0}, + {"WriterAt", Type, 0}, + {"WriterTo", Type, 0}, + }, + "io/fs": { + {"(*PathError).Error", Method, 16}, + {"(*PathError).Timeout", Method, 16}, + {"(*PathError).Unwrap", Method, 16}, + {"(FileMode).IsDir", Method, 16}, + {"(FileMode).IsRegular", Method, 16}, + {"(FileMode).Perm", Method, 16}, + {"(FileMode).String", Method, 16}, + {"(FileMode).Type", Method, 16}, + {"DirEntry", Type, 16}, + {"ErrClosed", Var, 16}, + {"ErrExist", Var, 16}, + {"ErrInvalid", Var, 16}, + {"ErrNotExist", Var, 16}, + {"ErrPermission", Var, 16}, + {"FS", Type, 16}, + {"File", Type, 16}, + {"FileInfo", Type, 16}, + {"FileInfoToDirEntry", Func, 17}, + {"FileMode", Type, 16}, + {"FormatDirEntry", Func, 21}, + {"FormatFileInfo", Func, 21}, + {"Glob", Func, 16}, + {"GlobFS", Type, 16}, + {"ModeAppend", Const, 16}, + {"ModeCharDevice", Const, 16}, + {"ModeDevice", Const, 16}, + {"ModeDir", Const, 16}, + {"ModeExclusive", Const, 16}, + {"ModeIrregular", Const, 16}, + {"ModeNamedPipe", Const, 16}, + {"ModePerm", Const, 16}, + {"ModeSetgid", Const, 16}, + {"ModeSetuid", Const, 16}, + {"ModeSocket", Const, 16}, + {"ModeSticky", Const, 16}, + {"ModeSymlink", Const, 16}, + {"ModeTemporary", Const, 16}, + {"ModeType", Const, 16}, + {"PathError", Type, 16}, + {"PathError.Err", Field, 16}, + {"PathError.Op", Field, 16}, + {"PathError.Path", Field, 16}, + {"ReadDir", Func, 16}, + {"ReadDirFS", Type, 16}, + {"ReadDirFile", Type, 16}, + {"ReadFile", Func, 16}, + {"ReadFileFS", Type, 16}, + {"SkipAll", Var, 20}, + {"SkipDir", Var, 16}, + {"Stat", Func, 16}, + {"StatFS", Type, 16}, + {"Sub", Func, 16}, + {"SubFS", Type, 16}, + {"ValidPath", Func, 16}, + {"WalkDir", Func, 16}, + {"WalkDirFunc", Type, 16}, + }, + "io/ioutil": { + {"Discard", Var, 0}, + {"NopCloser", Func, 0}, + {"ReadAll", Func, 0}, + {"ReadDir", Func, 0}, + {"ReadFile", Func, 0}, + {"TempDir", Func, 0}, + {"TempFile", Func, 0}, + {"WriteFile", Func, 0}, + }, + "iter": { + {"Pull", Func, 23}, + {"Pull2", Func, 23}, + {"Seq", Type, 23}, + {"Seq2", Type, 23}, + }, + "log": { + {"(*Logger).Fatal", Method, 0}, + {"(*Logger).Fatalf", Method, 0}, + {"(*Logger).Fatalln", Method, 0}, + {"(*Logger).Flags", Method, 0}, + {"(*Logger).Output", Method, 0}, + {"(*Logger).Panic", Method, 0}, + {"(*Logger).Panicf", Method, 0}, + {"(*Logger).Panicln", Method, 0}, + {"(*Logger).Prefix", Method, 0}, + {"(*Logger).Print", Method, 0}, + {"(*Logger).Printf", Method, 0}, + {"(*Logger).Println", Method, 0}, + {"(*Logger).SetFlags", Method, 0}, + {"(*Logger).SetOutput", Method, 5}, + {"(*Logger).SetPrefix", Method, 0}, + {"(*Logger).Writer", Method, 12}, + {"Default", Func, 16}, + {"Fatal", Func, 0}, + {"Fatalf", Func, 0}, + {"Fatalln", Func, 0}, + {"Flags", Func, 0}, + {"LUTC", Const, 5}, + {"Ldate", Const, 0}, + {"Llongfile", Const, 0}, + {"Lmicroseconds", Const, 0}, + {"Lmsgprefix", Const, 14}, + {"Logger", Type, 0}, + {"Lshortfile", Const, 0}, + {"LstdFlags", Const, 0}, + {"Ltime", Const, 0}, + {"New", Func, 0}, + {"Output", Func, 5}, + {"Panic", Func, 0}, + {"Panicf", Func, 0}, + {"Panicln", Func, 0}, + {"Prefix", Func, 0}, + {"Print", Func, 0}, + {"Printf", Func, 0}, + {"Println", Func, 0}, + {"SetFlags", Func, 0}, + {"SetOutput", Func, 0}, + {"SetPrefix", Func, 0}, + {"Writer", Func, 13}, + }, + "log/slog": { + {"(*JSONHandler).Enabled", Method, 21}, + {"(*JSONHandler).Handle", Method, 21}, + {"(*JSONHandler).WithAttrs", Method, 21}, + {"(*JSONHandler).WithGroup", Method, 21}, + {"(*Level).UnmarshalJSON", Method, 21}, + {"(*Level).UnmarshalText", Method, 21}, + {"(*LevelVar).Level", Method, 21}, + {"(*LevelVar).MarshalText", Method, 21}, + {"(*LevelVar).Set", Method, 21}, + {"(*LevelVar).String", Method, 21}, + {"(*LevelVar).UnmarshalText", Method, 21}, + {"(*Logger).Debug", Method, 21}, + {"(*Logger).DebugContext", Method, 21}, + {"(*Logger).Enabled", Method, 21}, + {"(*Logger).Error", Method, 21}, + {"(*Logger).ErrorContext", Method, 21}, + {"(*Logger).Handler", Method, 21}, + {"(*Logger).Info", Method, 21}, + {"(*Logger).InfoContext", Method, 21}, + {"(*Logger).Log", Method, 21}, + {"(*Logger).LogAttrs", Method, 21}, + {"(*Logger).Warn", Method, 21}, + {"(*Logger).WarnContext", Method, 21}, + {"(*Logger).With", Method, 21}, + {"(*Logger).WithGroup", Method, 21}, + {"(*Record).Add", Method, 21}, + {"(*Record).AddAttrs", Method, 21}, + {"(*TextHandler).Enabled", Method, 21}, + {"(*TextHandler).Handle", Method, 21}, + {"(*TextHandler).WithAttrs", Method, 21}, + {"(*TextHandler).WithGroup", Method, 21}, + {"(Attr).Equal", Method, 21}, + {"(Attr).String", Method, 21}, + {"(Kind).String", Method, 21}, + {"(Level).Level", Method, 21}, + {"(Level).MarshalJSON", Method, 21}, + {"(Level).MarshalText", Method, 21}, + {"(Level).String", Method, 21}, + {"(Record).Attrs", Method, 21}, + {"(Record).Clone", Method, 21}, + {"(Record).NumAttrs", Method, 21}, + {"(Value).Any", Method, 21}, + {"(Value).Bool", Method, 21}, + {"(Value).Duration", Method, 21}, + {"(Value).Equal", Method, 21}, + {"(Value).Float64", Method, 21}, + {"(Value).Group", Method, 21}, + {"(Value).Int64", Method, 21}, + {"(Value).Kind", Method, 21}, + {"(Value).LogValuer", Method, 21}, + {"(Value).Resolve", Method, 21}, + {"(Value).String", Method, 21}, + {"(Value).Time", Method, 21}, + {"(Value).Uint64", Method, 21}, + {"Any", Func, 21}, + {"AnyValue", Func, 21}, + {"Attr", Type, 21}, + {"Attr.Key", Field, 21}, + {"Attr.Value", Field, 21}, + {"Bool", Func, 21}, + {"BoolValue", Func, 21}, + {"Debug", Func, 21}, + {"DebugContext", Func, 21}, + {"Default", Func, 21}, + {"Duration", Func, 21}, + {"DurationValue", Func, 21}, + {"Error", Func, 21}, + {"ErrorContext", Func, 21}, + {"Float64", Func, 21}, + {"Float64Value", Func, 21}, + {"Group", Func, 21}, + {"GroupValue", Func, 21}, + {"Handler", Type, 21}, + {"HandlerOptions", Type, 21}, + {"HandlerOptions.AddSource", Field, 21}, + {"HandlerOptions.Level", Field, 21}, + {"HandlerOptions.ReplaceAttr", Field, 21}, + {"Info", Func, 21}, + {"InfoContext", Func, 21}, + {"Int", Func, 21}, + {"Int64", Func, 21}, + {"Int64Value", Func, 21}, + {"IntValue", Func, 21}, + {"JSONHandler", Type, 21}, + {"Kind", Type, 21}, + {"KindAny", Const, 21}, + {"KindBool", Const, 21}, + {"KindDuration", Const, 21}, + {"KindFloat64", Const, 21}, + {"KindGroup", Const, 21}, + {"KindInt64", Const, 21}, + {"KindLogValuer", Const, 21}, + {"KindString", Const, 21}, + {"KindTime", Const, 21}, + {"KindUint64", Const, 21}, + {"Level", Type, 21}, + {"LevelDebug", Const, 21}, + {"LevelError", Const, 21}, + {"LevelInfo", Const, 21}, + {"LevelKey", Const, 21}, + {"LevelVar", Type, 21}, + {"LevelWarn", Const, 21}, + {"Leveler", Type, 21}, + {"Log", Func, 21}, + {"LogAttrs", Func, 21}, + {"LogValuer", Type, 21}, + {"Logger", Type, 21}, + {"MessageKey", Const, 21}, + {"New", Func, 21}, + {"NewJSONHandler", Func, 21}, + {"NewLogLogger", Func, 21}, + {"NewRecord", Func, 21}, + {"NewTextHandler", Func, 21}, + {"Record", Type, 21}, + {"Record.Level", Field, 21}, + {"Record.Message", Field, 21}, + {"Record.PC", Field, 21}, + {"Record.Time", Field, 21}, + {"SetDefault", Func, 21}, + {"SetLogLoggerLevel", Func, 22}, + {"Source", Type, 21}, + {"Source.File", Field, 21}, + {"Source.Function", Field, 21}, + {"Source.Line", Field, 21}, + {"SourceKey", Const, 21}, + {"String", Func, 21}, + {"StringValue", Func, 21}, + {"TextHandler", Type, 21}, + {"Time", Func, 21}, + {"TimeKey", Const, 21}, + {"TimeValue", Func, 21}, + {"Uint64", Func, 21}, + {"Uint64Value", Func, 21}, + {"Value", Type, 21}, + {"Warn", Func, 21}, + {"WarnContext", Func, 21}, + {"With", Func, 21}, + }, + "log/syslog": { + {"(*Writer).Alert", Method, 0}, + {"(*Writer).Close", Method, 0}, + {"(*Writer).Crit", Method, 0}, + {"(*Writer).Debug", Method, 0}, + {"(*Writer).Emerg", Method, 0}, + {"(*Writer).Err", Method, 0}, + {"(*Writer).Info", Method, 0}, + {"(*Writer).Notice", Method, 0}, + {"(*Writer).Warning", Method, 0}, + {"(*Writer).Write", Method, 0}, + {"Dial", Func, 0}, + {"LOG_ALERT", Const, 0}, + {"LOG_AUTH", Const, 1}, + {"LOG_AUTHPRIV", Const, 1}, + {"LOG_CRIT", Const, 0}, + {"LOG_CRON", Const, 1}, + {"LOG_DAEMON", Const, 1}, + {"LOG_DEBUG", Const, 0}, + {"LOG_EMERG", Const, 0}, + {"LOG_ERR", Const, 0}, + {"LOG_FTP", Const, 1}, + {"LOG_INFO", Const, 0}, + {"LOG_KERN", Const, 1}, + {"LOG_LOCAL0", Const, 1}, + {"LOG_LOCAL1", Const, 1}, + {"LOG_LOCAL2", Const, 1}, + {"LOG_LOCAL3", Const, 1}, + {"LOG_LOCAL4", Const, 1}, + {"LOG_LOCAL5", Const, 1}, + {"LOG_LOCAL6", Const, 1}, + {"LOG_LOCAL7", Const, 1}, + {"LOG_LPR", Const, 1}, + {"LOG_MAIL", Const, 1}, + {"LOG_NEWS", Const, 1}, + {"LOG_NOTICE", Const, 0}, + {"LOG_SYSLOG", Const, 1}, + {"LOG_USER", Const, 1}, + {"LOG_UUCP", Const, 1}, + {"LOG_WARNING", Const, 0}, + {"New", Func, 0}, + {"NewLogger", Func, 0}, + {"Priority", Type, 0}, + {"Writer", Type, 0}, + }, + "maps": { + {"All", Func, 23}, + {"Clone", Func, 21}, + {"Collect", Func, 23}, + {"Copy", Func, 21}, + {"DeleteFunc", Func, 21}, + {"Equal", Func, 21}, + {"EqualFunc", Func, 21}, + {"Insert", Func, 23}, + {"Keys", Func, 23}, + {"Values", Func, 23}, + }, + "math": { + {"Abs", Func, 0}, + {"Acos", Func, 0}, + {"Acosh", Func, 0}, + {"Asin", Func, 0}, + {"Asinh", Func, 0}, + {"Atan", Func, 0}, + {"Atan2", Func, 0}, + {"Atanh", Func, 0}, + {"Cbrt", Func, 0}, + {"Ceil", Func, 0}, + {"Copysign", Func, 0}, + {"Cos", Func, 0}, + {"Cosh", Func, 0}, + {"Dim", Func, 0}, + {"E", Const, 0}, + {"Erf", Func, 0}, + {"Erfc", Func, 0}, + {"Erfcinv", Func, 10}, + {"Erfinv", Func, 10}, + {"Exp", Func, 0}, + {"Exp2", Func, 0}, + {"Expm1", Func, 0}, + {"FMA", Func, 14}, + {"Float32bits", Func, 0}, + {"Float32frombits", Func, 0}, + {"Float64bits", Func, 0}, + {"Float64frombits", Func, 0}, + {"Floor", Func, 0}, + {"Frexp", Func, 0}, + {"Gamma", Func, 0}, + {"Hypot", Func, 0}, + {"Ilogb", Func, 0}, + {"Inf", Func, 0}, + {"IsInf", Func, 0}, + {"IsNaN", Func, 0}, + {"J0", Func, 0}, + {"J1", Func, 0}, + {"Jn", Func, 0}, + {"Ldexp", Func, 0}, + {"Lgamma", Func, 0}, + {"Ln10", Const, 0}, + {"Ln2", Const, 0}, + {"Log", Func, 0}, + {"Log10", Func, 0}, + {"Log10E", Const, 0}, + {"Log1p", Func, 0}, + {"Log2", Func, 0}, + {"Log2E", Const, 0}, + {"Logb", Func, 0}, + {"Max", Func, 0}, + {"MaxFloat32", Const, 0}, + {"MaxFloat64", Const, 0}, + {"MaxInt", Const, 17}, + {"MaxInt16", Const, 0}, + {"MaxInt32", Const, 0}, + {"MaxInt64", Const, 0}, + {"MaxInt8", Const, 0}, + {"MaxUint", Const, 17}, + {"MaxUint16", Const, 0}, + {"MaxUint32", Const, 0}, + {"MaxUint64", Const, 0}, + {"MaxUint8", Const, 0}, + {"Min", Func, 0}, + {"MinInt", Const, 17}, + {"MinInt16", Const, 0}, + {"MinInt32", Const, 0}, + {"MinInt64", Const, 0}, + {"MinInt8", Const, 0}, + {"Mod", Func, 0}, + {"Modf", Func, 0}, + {"NaN", Func, 0}, + {"Nextafter", Func, 0}, + {"Nextafter32", Func, 4}, + {"Phi", Const, 0}, + {"Pi", Const, 0}, + {"Pow", Func, 0}, + {"Pow10", Func, 0}, + {"Remainder", Func, 0}, + {"Round", Func, 10}, + {"RoundToEven", Func, 10}, + {"Signbit", Func, 0}, + {"Sin", Func, 0}, + {"Sincos", Func, 0}, + {"Sinh", Func, 0}, + {"SmallestNonzeroFloat32", Const, 0}, + {"SmallestNonzeroFloat64", Const, 0}, + {"Sqrt", Func, 0}, + {"Sqrt2", Const, 0}, + {"SqrtE", Const, 0}, + {"SqrtPhi", Const, 0}, + {"SqrtPi", Const, 0}, + {"Tan", Func, 0}, + {"Tanh", Func, 0}, + {"Trunc", Func, 0}, + {"Y0", Func, 0}, + {"Y1", Func, 0}, + {"Yn", Func, 0}, + }, + "math/big": { + {"(*Float).Abs", Method, 5}, + {"(*Float).Acc", Method, 5}, + {"(*Float).Add", Method, 5}, + {"(*Float).Append", Method, 5}, + {"(*Float).Cmp", Method, 5}, + {"(*Float).Copy", Method, 5}, + {"(*Float).Float32", Method, 5}, + {"(*Float).Float64", Method, 5}, + {"(*Float).Format", Method, 5}, + {"(*Float).GobDecode", Method, 7}, + {"(*Float).GobEncode", Method, 7}, + {"(*Float).Int", Method, 5}, + {"(*Float).Int64", Method, 5}, + {"(*Float).IsInf", Method, 5}, + {"(*Float).IsInt", Method, 5}, + {"(*Float).MantExp", Method, 5}, + {"(*Float).MarshalText", Method, 6}, + {"(*Float).MinPrec", Method, 5}, + {"(*Float).Mode", Method, 5}, + {"(*Float).Mul", Method, 5}, + {"(*Float).Neg", Method, 5}, + {"(*Float).Parse", Method, 5}, + {"(*Float).Prec", Method, 5}, + {"(*Float).Quo", Method, 5}, + {"(*Float).Rat", Method, 5}, + {"(*Float).Scan", Method, 8}, + {"(*Float).Set", Method, 5}, + {"(*Float).SetFloat64", Method, 5}, + {"(*Float).SetInf", Method, 5}, + {"(*Float).SetInt", Method, 5}, + {"(*Float).SetInt64", Method, 5}, + {"(*Float).SetMantExp", Method, 5}, + {"(*Float).SetMode", Method, 5}, + {"(*Float).SetPrec", Method, 5}, + {"(*Float).SetRat", Method, 5}, + {"(*Float).SetString", Method, 5}, + {"(*Float).SetUint64", Method, 5}, + {"(*Float).Sign", Method, 5}, + {"(*Float).Signbit", Method, 5}, + {"(*Float).Sqrt", Method, 10}, + {"(*Float).String", Method, 5}, + {"(*Float).Sub", Method, 5}, + {"(*Float).Text", Method, 5}, + {"(*Float).Uint64", Method, 5}, + {"(*Float).UnmarshalText", Method, 6}, + {"(*Int).Abs", Method, 0}, + {"(*Int).Add", Method, 0}, + {"(*Int).And", Method, 0}, + {"(*Int).AndNot", Method, 0}, + {"(*Int).Append", Method, 6}, + {"(*Int).Binomial", Method, 0}, + {"(*Int).Bit", Method, 0}, + {"(*Int).BitLen", Method, 0}, + {"(*Int).Bits", Method, 0}, + {"(*Int).Bytes", Method, 0}, + {"(*Int).Cmp", Method, 0}, + {"(*Int).CmpAbs", Method, 10}, + {"(*Int).Div", Method, 0}, + {"(*Int).DivMod", Method, 0}, + {"(*Int).Exp", Method, 0}, + {"(*Int).FillBytes", Method, 15}, + {"(*Int).Float64", Method, 21}, + {"(*Int).Format", Method, 0}, + {"(*Int).GCD", Method, 0}, + {"(*Int).GobDecode", Method, 0}, + {"(*Int).GobEncode", Method, 0}, + {"(*Int).Int64", Method, 0}, + {"(*Int).IsInt64", Method, 9}, + {"(*Int).IsUint64", Method, 9}, + {"(*Int).Lsh", Method, 0}, + {"(*Int).MarshalJSON", Method, 1}, + {"(*Int).MarshalText", Method, 3}, + {"(*Int).Mod", Method, 0}, + {"(*Int).ModInverse", Method, 0}, + {"(*Int).ModSqrt", Method, 5}, + {"(*Int).Mul", Method, 0}, + {"(*Int).MulRange", Method, 0}, + {"(*Int).Neg", Method, 0}, + {"(*Int).Not", Method, 0}, + {"(*Int).Or", Method, 0}, + {"(*Int).ProbablyPrime", Method, 0}, + {"(*Int).Quo", Method, 0}, + {"(*Int).QuoRem", Method, 0}, + {"(*Int).Rand", Method, 0}, + {"(*Int).Rem", Method, 0}, + {"(*Int).Rsh", Method, 0}, + {"(*Int).Scan", Method, 0}, + {"(*Int).Set", Method, 0}, + {"(*Int).SetBit", Method, 0}, + {"(*Int).SetBits", Method, 0}, + {"(*Int).SetBytes", Method, 0}, + {"(*Int).SetInt64", Method, 0}, + {"(*Int).SetString", Method, 0}, + {"(*Int).SetUint64", Method, 1}, + {"(*Int).Sign", Method, 0}, + {"(*Int).Sqrt", Method, 8}, + {"(*Int).String", Method, 0}, + {"(*Int).Sub", Method, 0}, + {"(*Int).Text", Method, 6}, + {"(*Int).TrailingZeroBits", Method, 13}, + {"(*Int).Uint64", Method, 1}, + {"(*Int).UnmarshalJSON", Method, 1}, + {"(*Int).UnmarshalText", Method, 3}, + {"(*Int).Xor", Method, 0}, + {"(*Rat).Abs", Method, 0}, + {"(*Rat).Add", Method, 0}, + {"(*Rat).Cmp", Method, 0}, + {"(*Rat).Denom", Method, 0}, + {"(*Rat).Float32", Method, 4}, + {"(*Rat).Float64", Method, 1}, + {"(*Rat).FloatPrec", Method, 22}, + {"(*Rat).FloatString", Method, 0}, + {"(*Rat).GobDecode", Method, 0}, + {"(*Rat).GobEncode", Method, 0}, + {"(*Rat).Inv", Method, 0}, + {"(*Rat).IsInt", Method, 0}, + {"(*Rat).MarshalText", Method, 3}, + {"(*Rat).Mul", Method, 0}, + {"(*Rat).Neg", Method, 0}, + {"(*Rat).Num", Method, 0}, + {"(*Rat).Quo", Method, 0}, + {"(*Rat).RatString", Method, 0}, + {"(*Rat).Scan", Method, 0}, + {"(*Rat).Set", Method, 0}, + {"(*Rat).SetFloat64", Method, 1}, + {"(*Rat).SetFrac", Method, 0}, + {"(*Rat).SetFrac64", Method, 0}, + {"(*Rat).SetInt", Method, 0}, + {"(*Rat).SetInt64", Method, 0}, + {"(*Rat).SetString", Method, 0}, + {"(*Rat).SetUint64", Method, 13}, + {"(*Rat).Sign", Method, 0}, + {"(*Rat).String", Method, 0}, + {"(*Rat).Sub", Method, 0}, + {"(*Rat).UnmarshalText", Method, 3}, + {"(Accuracy).String", Method, 5}, + {"(ErrNaN).Error", Method, 5}, + {"(RoundingMode).String", Method, 5}, + {"Above", Const, 5}, + {"Accuracy", Type, 5}, + {"AwayFromZero", Const, 5}, + {"Below", Const, 5}, + {"ErrNaN", Type, 5}, + {"Exact", Const, 5}, + {"Float", Type, 5}, + {"Int", Type, 0}, + {"Jacobi", Func, 5}, + {"MaxBase", Const, 0}, + {"MaxExp", Const, 5}, + {"MaxPrec", Const, 5}, + {"MinExp", Const, 5}, + {"NewFloat", Func, 5}, + {"NewInt", Func, 0}, + {"NewRat", Func, 0}, + {"ParseFloat", Func, 5}, + {"Rat", Type, 0}, + {"RoundingMode", Type, 5}, + {"ToNearestAway", Const, 5}, + {"ToNearestEven", Const, 5}, + {"ToNegativeInf", Const, 5}, + {"ToPositiveInf", Const, 5}, + {"ToZero", Const, 5}, + {"Word", Type, 0}, + }, + "math/bits": { + {"Add", Func, 12}, + {"Add32", Func, 12}, + {"Add64", Func, 12}, + {"Div", Func, 12}, + {"Div32", Func, 12}, + {"Div64", Func, 12}, + {"LeadingZeros", Func, 9}, + {"LeadingZeros16", Func, 9}, + {"LeadingZeros32", Func, 9}, + {"LeadingZeros64", Func, 9}, + {"LeadingZeros8", Func, 9}, + {"Len", Func, 9}, + {"Len16", Func, 9}, + {"Len32", Func, 9}, + {"Len64", Func, 9}, + {"Len8", Func, 9}, + {"Mul", Func, 12}, + {"Mul32", Func, 12}, + {"Mul64", Func, 12}, + {"OnesCount", Func, 9}, + {"OnesCount16", Func, 9}, + {"OnesCount32", Func, 9}, + {"OnesCount64", Func, 9}, + {"OnesCount8", Func, 9}, + {"Rem", Func, 14}, + {"Rem32", Func, 14}, + {"Rem64", Func, 14}, + {"Reverse", Func, 9}, + {"Reverse16", Func, 9}, + {"Reverse32", Func, 9}, + {"Reverse64", Func, 9}, + {"Reverse8", Func, 9}, + {"ReverseBytes", Func, 9}, + {"ReverseBytes16", Func, 9}, + {"ReverseBytes32", Func, 9}, + {"ReverseBytes64", Func, 9}, + {"RotateLeft", Func, 9}, + {"RotateLeft16", Func, 9}, + {"RotateLeft32", Func, 9}, + {"RotateLeft64", Func, 9}, + {"RotateLeft8", Func, 9}, + {"Sub", Func, 12}, + {"Sub32", Func, 12}, + {"Sub64", Func, 12}, + {"TrailingZeros", Func, 9}, + {"TrailingZeros16", Func, 9}, + {"TrailingZeros32", Func, 9}, + {"TrailingZeros64", Func, 9}, + {"TrailingZeros8", Func, 9}, + {"UintSize", Const, 9}, + }, + "math/cmplx": { + {"Abs", Func, 0}, + {"Acos", Func, 0}, + {"Acosh", Func, 0}, + {"Asin", Func, 0}, + {"Asinh", Func, 0}, + {"Atan", Func, 0}, + {"Atanh", Func, 0}, + {"Conj", Func, 0}, + {"Cos", Func, 0}, + {"Cosh", Func, 0}, + {"Cot", Func, 0}, + {"Exp", Func, 0}, + {"Inf", Func, 0}, + {"IsInf", Func, 0}, + {"IsNaN", Func, 0}, + {"Log", Func, 0}, + {"Log10", Func, 0}, + {"NaN", Func, 0}, + {"Phase", Func, 0}, + {"Polar", Func, 0}, + {"Pow", Func, 0}, + {"Rect", Func, 0}, + {"Sin", Func, 0}, + {"Sinh", Func, 0}, + {"Sqrt", Func, 0}, + {"Tan", Func, 0}, + {"Tanh", Func, 0}, + }, + "math/rand": { + {"(*Rand).ExpFloat64", Method, 0}, + {"(*Rand).Float32", Method, 0}, + {"(*Rand).Float64", Method, 0}, + {"(*Rand).Int", Method, 0}, + {"(*Rand).Int31", Method, 0}, + {"(*Rand).Int31n", Method, 0}, + {"(*Rand).Int63", Method, 0}, + {"(*Rand).Int63n", Method, 0}, + {"(*Rand).Intn", Method, 0}, + {"(*Rand).NormFloat64", Method, 0}, + {"(*Rand).Perm", Method, 0}, + {"(*Rand).Read", Method, 6}, + {"(*Rand).Seed", Method, 0}, + {"(*Rand).Shuffle", Method, 10}, + {"(*Rand).Uint32", Method, 0}, + {"(*Rand).Uint64", Method, 8}, + {"(*Zipf).Uint64", Method, 0}, + {"ExpFloat64", Func, 0}, + {"Float32", Func, 0}, + {"Float64", Func, 0}, + {"Int", Func, 0}, + {"Int31", Func, 0}, + {"Int31n", Func, 0}, + {"Int63", Func, 0}, + {"Int63n", Func, 0}, + {"Intn", Func, 0}, + {"New", Func, 0}, + {"NewSource", Func, 0}, + {"NewZipf", Func, 0}, + {"NormFloat64", Func, 0}, + {"Perm", Func, 0}, + {"Rand", Type, 0}, + {"Read", Func, 6}, + {"Seed", Func, 0}, + {"Shuffle", Func, 10}, + {"Source", Type, 0}, + {"Source64", Type, 8}, + {"Uint32", Func, 0}, + {"Uint64", Func, 8}, + {"Zipf", Type, 0}, + }, + "math/rand/v2": { + {"(*ChaCha8).MarshalBinary", Method, 22}, + {"(*ChaCha8).Read", Method, 23}, + {"(*ChaCha8).Seed", Method, 22}, + {"(*ChaCha8).Uint64", Method, 22}, + {"(*ChaCha8).UnmarshalBinary", Method, 22}, + {"(*PCG).MarshalBinary", Method, 22}, + {"(*PCG).Seed", Method, 22}, + {"(*PCG).Uint64", Method, 22}, + {"(*PCG).UnmarshalBinary", Method, 22}, + {"(*Rand).ExpFloat64", Method, 22}, + {"(*Rand).Float32", Method, 22}, + {"(*Rand).Float64", Method, 22}, + {"(*Rand).Int", Method, 22}, + {"(*Rand).Int32", Method, 22}, + {"(*Rand).Int32N", Method, 22}, + {"(*Rand).Int64", Method, 22}, + {"(*Rand).Int64N", Method, 22}, + {"(*Rand).IntN", Method, 22}, + {"(*Rand).NormFloat64", Method, 22}, + {"(*Rand).Perm", Method, 22}, + {"(*Rand).Shuffle", Method, 22}, + {"(*Rand).Uint", Method, 23}, + {"(*Rand).Uint32", Method, 22}, + {"(*Rand).Uint32N", Method, 22}, + {"(*Rand).Uint64", Method, 22}, + {"(*Rand).Uint64N", Method, 22}, + {"(*Rand).UintN", Method, 22}, + {"(*Zipf).Uint64", Method, 22}, + {"ChaCha8", Type, 22}, + {"ExpFloat64", Func, 22}, + {"Float32", Func, 22}, + {"Float64", Func, 22}, + {"Int", Func, 22}, + {"Int32", Func, 22}, + {"Int32N", Func, 22}, + {"Int64", Func, 22}, + {"Int64N", Func, 22}, + {"IntN", Func, 22}, + {"N", Func, 22}, + {"New", Func, 22}, + {"NewChaCha8", Func, 22}, + {"NewPCG", Func, 22}, + {"NewZipf", Func, 22}, + {"NormFloat64", Func, 22}, + {"PCG", Type, 22}, + {"Perm", Func, 22}, + {"Rand", Type, 22}, + {"Shuffle", Func, 22}, + {"Source", Type, 22}, + {"Uint", Func, 23}, + {"Uint32", Func, 22}, + {"Uint32N", Func, 22}, + {"Uint64", Func, 22}, + {"Uint64N", Func, 22}, + {"UintN", Func, 22}, + {"Zipf", Type, 22}, + }, + "mime": { + {"(*WordDecoder).Decode", Method, 5}, + {"(*WordDecoder).DecodeHeader", Method, 5}, + {"(WordEncoder).Encode", Method, 5}, + {"AddExtensionType", Func, 0}, + {"BEncoding", Const, 5}, + {"ErrInvalidMediaParameter", Var, 9}, + {"ExtensionsByType", Func, 5}, + {"FormatMediaType", Func, 0}, + {"ParseMediaType", Func, 0}, + {"QEncoding", Const, 5}, + {"TypeByExtension", Func, 0}, + {"WordDecoder", Type, 5}, + {"WordDecoder.CharsetReader", Field, 5}, + {"WordEncoder", Type, 5}, + }, + "mime/multipart": { + {"(*FileHeader).Open", Method, 0}, + {"(*Form).RemoveAll", Method, 0}, + {"(*Part).Close", Method, 0}, + {"(*Part).FileName", Method, 0}, + {"(*Part).FormName", Method, 0}, + {"(*Part).Read", Method, 0}, + {"(*Reader).NextPart", Method, 0}, + {"(*Reader).NextRawPart", Method, 14}, + {"(*Reader).ReadForm", Method, 0}, + {"(*Writer).Boundary", Method, 0}, + {"(*Writer).Close", Method, 0}, + {"(*Writer).CreateFormField", Method, 0}, + {"(*Writer).CreateFormFile", Method, 0}, + {"(*Writer).CreatePart", Method, 0}, + {"(*Writer).FormDataContentType", Method, 0}, + {"(*Writer).SetBoundary", Method, 1}, + {"(*Writer).WriteField", Method, 0}, + {"ErrMessageTooLarge", Var, 9}, + {"File", Type, 0}, + {"FileHeader", Type, 0}, + {"FileHeader.Filename", Field, 0}, + {"FileHeader.Header", Field, 0}, + {"FileHeader.Size", Field, 9}, + {"Form", Type, 0}, + {"Form.File", Field, 0}, + {"Form.Value", Field, 0}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"Part", Type, 0}, + {"Part.Header", Field, 0}, + {"Reader", Type, 0}, + {"Writer", Type, 0}, + }, + "mime/quotedprintable": { + {"(*Reader).Read", Method, 5}, + {"(*Writer).Close", Method, 5}, + {"(*Writer).Write", Method, 5}, + {"NewReader", Func, 5}, + {"NewWriter", Func, 5}, + {"Reader", Type, 5}, + {"Writer", Type, 5}, + {"Writer.Binary", Field, 5}, + }, + "net": { + {"(*AddrError).Error", Method, 0}, + {"(*AddrError).Temporary", Method, 0}, + {"(*AddrError).Timeout", Method, 0}, + {"(*Buffers).Read", Method, 8}, + {"(*Buffers).WriteTo", Method, 8}, + {"(*DNSConfigError).Error", Method, 0}, + {"(*DNSConfigError).Temporary", Method, 0}, + {"(*DNSConfigError).Timeout", Method, 0}, + {"(*DNSConfigError).Unwrap", Method, 13}, + {"(*DNSError).Error", Method, 0}, + {"(*DNSError).Temporary", Method, 0}, + {"(*DNSError).Timeout", Method, 0}, + {"(*DNSError).Unwrap", Method, 23}, + {"(*Dialer).Dial", Method, 1}, + {"(*Dialer).DialContext", Method, 7}, + {"(*Dialer).MultipathTCP", Method, 21}, + {"(*Dialer).SetMultipathTCP", Method, 21}, + {"(*IP).UnmarshalText", Method, 2}, + {"(*IPAddr).Network", Method, 0}, + {"(*IPAddr).String", Method, 0}, + {"(*IPConn).Close", Method, 0}, + {"(*IPConn).File", Method, 0}, + {"(*IPConn).LocalAddr", Method, 0}, + {"(*IPConn).Read", Method, 0}, + {"(*IPConn).ReadFrom", Method, 0}, + {"(*IPConn).ReadFromIP", Method, 0}, + {"(*IPConn).ReadMsgIP", Method, 1}, + {"(*IPConn).RemoteAddr", Method, 0}, + {"(*IPConn).SetDeadline", Method, 0}, + {"(*IPConn).SetReadBuffer", Method, 0}, + {"(*IPConn).SetReadDeadline", Method, 0}, + {"(*IPConn).SetWriteBuffer", Method, 0}, + {"(*IPConn).SetWriteDeadline", Method, 0}, + {"(*IPConn).SyscallConn", Method, 9}, + {"(*IPConn).Write", Method, 0}, + {"(*IPConn).WriteMsgIP", Method, 1}, + {"(*IPConn).WriteTo", Method, 0}, + {"(*IPConn).WriteToIP", Method, 0}, + {"(*IPNet).Contains", Method, 0}, + {"(*IPNet).Network", Method, 0}, + {"(*IPNet).String", Method, 0}, + {"(*Interface).Addrs", Method, 0}, + {"(*Interface).MulticastAddrs", Method, 0}, + {"(*ListenConfig).Listen", Method, 11}, + {"(*ListenConfig).ListenPacket", Method, 11}, + {"(*ListenConfig).MultipathTCP", Method, 21}, + {"(*ListenConfig).SetMultipathTCP", Method, 21}, + {"(*OpError).Error", Method, 0}, + {"(*OpError).Temporary", Method, 0}, + {"(*OpError).Timeout", Method, 0}, + {"(*OpError).Unwrap", Method, 13}, + {"(*ParseError).Error", Method, 0}, + {"(*ParseError).Temporary", Method, 17}, + {"(*ParseError).Timeout", Method, 17}, + {"(*Resolver).LookupAddr", Method, 8}, + {"(*Resolver).LookupCNAME", Method, 8}, + {"(*Resolver).LookupHost", Method, 8}, + {"(*Resolver).LookupIP", Method, 15}, + {"(*Resolver).LookupIPAddr", Method, 8}, + {"(*Resolver).LookupMX", Method, 8}, + {"(*Resolver).LookupNS", Method, 8}, + {"(*Resolver).LookupNetIP", Method, 18}, + {"(*Resolver).LookupPort", Method, 8}, + {"(*Resolver).LookupSRV", Method, 8}, + {"(*Resolver).LookupTXT", Method, 8}, + {"(*TCPAddr).AddrPort", Method, 18}, + {"(*TCPAddr).Network", Method, 0}, + {"(*TCPAddr).String", Method, 0}, + {"(*TCPConn).Close", Method, 0}, + {"(*TCPConn).CloseRead", Method, 0}, + {"(*TCPConn).CloseWrite", Method, 0}, + {"(*TCPConn).File", Method, 0}, + {"(*TCPConn).LocalAddr", Method, 0}, + {"(*TCPConn).MultipathTCP", Method, 21}, + {"(*TCPConn).Read", Method, 0}, + {"(*TCPConn).ReadFrom", Method, 0}, + {"(*TCPConn).RemoteAddr", Method, 0}, + {"(*TCPConn).SetDeadline", Method, 0}, + {"(*TCPConn).SetKeepAlive", Method, 0}, + {"(*TCPConn).SetKeepAliveConfig", Method, 23}, + {"(*TCPConn).SetKeepAlivePeriod", Method, 2}, + {"(*TCPConn).SetLinger", Method, 0}, + {"(*TCPConn).SetNoDelay", Method, 0}, + {"(*TCPConn).SetReadBuffer", Method, 0}, + {"(*TCPConn).SetReadDeadline", Method, 0}, + {"(*TCPConn).SetWriteBuffer", Method, 0}, + {"(*TCPConn).SetWriteDeadline", Method, 0}, + {"(*TCPConn).SyscallConn", Method, 9}, + {"(*TCPConn).Write", Method, 0}, + {"(*TCPConn).WriteTo", Method, 22}, + {"(*TCPListener).Accept", Method, 0}, + {"(*TCPListener).AcceptTCP", Method, 0}, + {"(*TCPListener).Addr", Method, 0}, + {"(*TCPListener).Close", Method, 0}, + {"(*TCPListener).File", Method, 0}, + {"(*TCPListener).SetDeadline", Method, 0}, + {"(*TCPListener).SyscallConn", Method, 10}, + {"(*UDPAddr).AddrPort", Method, 18}, + {"(*UDPAddr).Network", Method, 0}, + {"(*UDPAddr).String", Method, 0}, + {"(*UDPConn).Close", Method, 0}, + {"(*UDPConn).File", Method, 0}, + {"(*UDPConn).LocalAddr", Method, 0}, + {"(*UDPConn).Read", Method, 0}, + {"(*UDPConn).ReadFrom", Method, 0}, + {"(*UDPConn).ReadFromUDP", Method, 0}, + {"(*UDPConn).ReadFromUDPAddrPort", Method, 18}, + {"(*UDPConn).ReadMsgUDP", Method, 1}, + {"(*UDPConn).ReadMsgUDPAddrPort", Method, 18}, + {"(*UDPConn).RemoteAddr", Method, 0}, + {"(*UDPConn).SetDeadline", Method, 0}, + {"(*UDPConn).SetReadBuffer", Method, 0}, + {"(*UDPConn).SetReadDeadline", Method, 0}, + {"(*UDPConn).SetWriteBuffer", Method, 0}, + {"(*UDPConn).SetWriteDeadline", Method, 0}, + {"(*UDPConn).SyscallConn", Method, 9}, + {"(*UDPConn).Write", Method, 0}, + {"(*UDPConn).WriteMsgUDP", Method, 1}, + {"(*UDPConn).WriteMsgUDPAddrPort", Method, 18}, + {"(*UDPConn).WriteTo", Method, 0}, + {"(*UDPConn).WriteToUDP", Method, 0}, + {"(*UDPConn).WriteToUDPAddrPort", Method, 18}, + {"(*UnixAddr).Network", Method, 0}, + {"(*UnixAddr).String", Method, 0}, + {"(*UnixConn).Close", Method, 0}, + {"(*UnixConn).CloseRead", Method, 1}, + {"(*UnixConn).CloseWrite", Method, 1}, + {"(*UnixConn).File", Method, 0}, + {"(*UnixConn).LocalAddr", Method, 0}, + {"(*UnixConn).Read", Method, 0}, + {"(*UnixConn).ReadFrom", Method, 0}, + {"(*UnixConn).ReadFromUnix", Method, 0}, + {"(*UnixConn).ReadMsgUnix", Method, 0}, + {"(*UnixConn).RemoteAddr", Method, 0}, + {"(*UnixConn).SetDeadline", Method, 0}, + {"(*UnixConn).SetReadBuffer", Method, 0}, + {"(*UnixConn).SetReadDeadline", Method, 0}, + {"(*UnixConn).SetWriteBuffer", Method, 0}, + {"(*UnixConn).SetWriteDeadline", Method, 0}, + {"(*UnixConn).SyscallConn", Method, 9}, + {"(*UnixConn).Write", Method, 0}, + {"(*UnixConn).WriteMsgUnix", Method, 0}, + {"(*UnixConn).WriteTo", Method, 0}, + {"(*UnixConn).WriteToUnix", Method, 0}, + {"(*UnixListener).Accept", Method, 0}, + {"(*UnixListener).AcceptUnix", Method, 0}, + {"(*UnixListener).Addr", Method, 0}, + {"(*UnixListener).Close", Method, 0}, + {"(*UnixListener).File", Method, 0}, + {"(*UnixListener).SetDeadline", Method, 0}, + {"(*UnixListener).SetUnlinkOnClose", Method, 8}, + {"(*UnixListener).SyscallConn", Method, 10}, + {"(Flags).String", Method, 0}, + {"(HardwareAddr).String", Method, 0}, + {"(IP).DefaultMask", Method, 0}, + {"(IP).Equal", Method, 0}, + {"(IP).IsGlobalUnicast", Method, 0}, + {"(IP).IsInterfaceLocalMulticast", Method, 0}, + {"(IP).IsLinkLocalMulticast", Method, 0}, + {"(IP).IsLinkLocalUnicast", Method, 0}, + {"(IP).IsLoopback", Method, 0}, + {"(IP).IsMulticast", Method, 0}, + {"(IP).IsPrivate", Method, 17}, + {"(IP).IsUnspecified", Method, 0}, + {"(IP).MarshalText", Method, 2}, + {"(IP).Mask", Method, 0}, + {"(IP).String", Method, 0}, + {"(IP).To16", Method, 0}, + {"(IP).To4", Method, 0}, + {"(IPMask).Size", Method, 0}, + {"(IPMask).String", Method, 0}, + {"(InvalidAddrError).Error", Method, 0}, + {"(InvalidAddrError).Temporary", Method, 0}, + {"(InvalidAddrError).Timeout", Method, 0}, + {"(UnknownNetworkError).Error", Method, 0}, + {"(UnknownNetworkError).Temporary", Method, 0}, + {"(UnknownNetworkError).Timeout", Method, 0}, + {"Addr", Type, 0}, + {"AddrError", Type, 0}, + {"AddrError.Addr", Field, 0}, + {"AddrError.Err", Field, 0}, + {"Buffers", Type, 8}, + {"CIDRMask", Func, 0}, + {"Conn", Type, 0}, + {"DNSConfigError", Type, 0}, + {"DNSConfigError.Err", Field, 0}, + {"DNSError", Type, 0}, + {"DNSError.Err", Field, 0}, + {"DNSError.IsNotFound", Field, 13}, + {"DNSError.IsTemporary", Field, 6}, + {"DNSError.IsTimeout", Field, 0}, + {"DNSError.Name", Field, 0}, + {"DNSError.Server", Field, 0}, + {"DNSError.UnwrapErr", Field, 23}, + {"DefaultResolver", Var, 8}, + {"Dial", Func, 0}, + {"DialIP", Func, 0}, + {"DialTCP", Func, 0}, + {"DialTimeout", Func, 0}, + {"DialUDP", Func, 0}, + {"DialUnix", Func, 0}, + {"Dialer", Type, 1}, + {"Dialer.Cancel", Field, 6}, + {"Dialer.Control", Field, 11}, + {"Dialer.ControlContext", Field, 20}, + {"Dialer.Deadline", Field, 1}, + {"Dialer.DualStack", Field, 2}, + {"Dialer.FallbackDelay", Field, 5}, + {"Dialer.KeepAlive", Field, 3}, + {"Dialer.KeepAliveConfig", Field, 23}, + {"Dialer.LocalAddr", Field, 1}, + {"Dialer.Resolver", Field, 8}, + {"Dialer.Timeout", Field, 1}, + {"ErrClosed", Var, 16}, + {"ErrWriteToConnected", Var, 0}, + {"Error", Type, 0}, + {"FileConn", Func, 0}, + {"FileListener", Func, 0}, + {"FilePacketConn", Func, 0}, + {"FlagBroadcast", Const, 0}, + {"FlagLoopback", Const, 0}, + {"FlagMulticast", Const, 0}, + {"FlagPointToPoint", Const, 0}, + {"FlagRunning", Const, 20}, + {"FlagUp", Const, 0}, + {"Flags", Type, 0}, + {"HardwareAddr", Type, 0}, + {"IP", Type, 0}, + {"IPAddr", Type, 0}, + {"IPAddr.IP", Field, 0}, + {"IPAddr.Zone", Field, 1}, + {"IPConn", Type, 0}, + {"IPMask", Type, 0}, + {"IPNet", Type, 0}, + {"IPNet.IP", Field, 0}, + {"IPNet.Mask", Field, 0}, + {"IPv4", Func, 0}, + {"IPv4Mask", Func, 0}, + {"IPv4allrouter", Var, 0}, + {"IPv4allsys", Var, 0}, + {"IPv4bcast", Var, 0}, + {"IPv4len", Const, 0}, + {"IPv4zero", Var, 0}, + {"IPv6interfacelocalallnodes", Var, 0}, + {"IPv6len", Const, 0}, + {"IPv6linklocalallnodes", Var, 0}, + {"IPv6linklocalallrouters", Var, 0}, + {"IPv6loopback", Var, 0}, + {"IPv6unspecified", Var, 0}, + {"IPv6zero", Var, 0}, + {"Interface", Type, 0}, + {"Interface.Flags", Field, 0}, + {"Interface.HardwareAddr", Field, 0}, + {"Interface.Index", Field, 0}, + {"Interface.MTU", Field, 0}, + {"Interface.Name", Field, 0}, + {"InterfaceAddrs", Func, 0}, + {"InterfaceByIndex", Func, 0}, + {"InterfaceByName", Func, 0}, + {"Interfaces", Func, 0}, + {"InvalidAddrError", Type, 0}, + {"JoinHostPort", Func, 0}, + {"KeepAliveConfig", Type, 23}, + {"KeepAliveConfig.Count", Field, 23}, + {"KeepAliveConfig.Enable", Field, 23}, + {"KeepAliveConfig.Idle", Field, 23}, + {"KeepAliveConfig.Interval", Field, 23}, + {"Listen", Func, 0}, + {"ListenConfig", Type, 11}, + {"ListenConfig.Control", Field, 11}, + {"ListenConfig.KeepAlive", Field, 13}, + {"ListenConfig.KeepAliveConfig", Field, 23}, + {"ListenIP", Func, 0}, + {"ListenMulticastUDP", Func, 0}, + {"ListenPacket", Func, 0}, + {"ListenTCP", Func, 0}, + {"ListenUDP", Func, 0}, + {"ListenUnix", Func, 0}, + {"ListenUnixgram", Func, 0}, + {"Listener", Type, 0}, + {"LookupAddr", Func, 0}, + {"LookupCNAME", Func, 0}, + {"LookupHost", Func, 0}, + {"LookupIP", Func, 0}, + {"LookupMX", Func, 0}, + {"LookupNS", Func, 1}, + {"LookupPort", Func, 0}, + {"LookupSRV", Func, 0}, + {"LookupTXT", Func, 0}, + {"MX", Type, 0}, + {"MX.Host", Field, 0}, + {"MX.Pref", Field, 0}, + {"NS", Type, 1}, + {"NS.Host", Field, 1}, + {"OpError", Type, 0}, + {"OpError.Addr", Field, 0}, + {"OpError.Err", Field, 0}, + {"OpError.Net", Field, 0}, + {"OpError.Op", Field, 0}, + {"OpError.Source", Field, 5}, + {"PacketConn", Type, 0}, + {"ParseCIDR", Func, 0}, + {"ParseError", Type, 0}, + {"ParseError.Text", Field, 0}, + {"ParseError.Type", Field, 0}, + {"ParseIP", Func, 0}, + {"ParseMAC", Func, 0}, + {"Pipe", Func, 0}, + {"ResolveIPAddr", Func, 0}, + {"ResolveTCPAddr", Func, 0}, + {"ResolveUDPAddr", Func, 0}, + {"ResolveUnixAddr", Func, 0}, + {"Resolver", Type, 8}, + {"Resolver.Dial", Field, 9}, + {"Resolver.PreferGo", Field, 8}, + {"Resolver.StrictErrors", Field, 9}, + {"SRV", Type, 0}, + {"SRV.Port", Field, 0}, + {"SRV.Priority", Field, 0}, + {"SRV.Target", Field, 0}, + {"SRV.Weight", Field, 0}, + {"SplitHostPort", Func, 0}, + {"TCPAddr", Type, 0}, + {"TCPAddr.IP", Field, 0}, + {"TCPAddr.Port", Field, 0}, + {"TCPAddr.Zone", Field, 1}, + {"TCPAddrFromAddrPort", Func, 18}, + {"TCPConn", Type, 0}, + {"TCPListener", Type, 0}, + {"UDPAddr", Type, 0}, + {"UDPAddr.IP", Field, 0}, + {"UDPAddr.Port", Field, 0}, + {"UDPAddr.Zone", Field, 1}, + {"UDPAddrFromAddrPort", Func, 18}, + {"UDPConn", Type, 0}, + {"UnixAddr", Type, 0}, + {"UnixAddr.Name", Field, 0}, + {"UnixAddr.Net", Field, 0}, + {"UnixConn", Type, 0}, + {"UnixListener", Type, 0}, + {"UnknownNetworkError", Type, 0}, + }, + "net/http": { + {"(*Client).CloseIdleConnections", Method, 12}, + {"(*Client).Do", Method, 0}, + {"(*Client).Get", Method, 0}, + {"(*Client).Head", Method, 0}, + {"(*Client).Post", Method, 0}, + {"(*Client).PostForm", Method, 0}, + {"(*Cookie).String", Method, 0}, + {"(*Cookie).Valid", Method, 18}, + {"(*MaxBytesError).Error", Method, 19}, + {"(*ProtocolError).Error", Method, 0}, + {"(*ProtocolError).Is", Method, 21}, + {"(*Request).AddCookie", Method, 0}, + {"(*Request).BasicAuth", Method, 4}, + {"(*Request).Clone", Method, 13}, + {"(*Request).Context", Method, 7}, + {"(*Request).Cookie", Method, 0}, + {"(*Request).Cookies", Method, 0}, + {"(*Request).CookiesNamed", Method, 23}, + {"(*Request).FormFile", Method, 0}, + {"(*Request).FormValue", Method, 0}, + {"(*Request).MultipartReader", Method, 0}, + {"(*Request).ParseForm", Method, 0}, + {"(*Request).ParseMultipartForm", Method, 0}, + {"(*Request).PathValue", Method, 22}, + {"(*Request).PostFormValue", Method, 1}, + {"(*Request).ProtoAtLeast", Method, 0}, + {"(*Request).Referer", Method, 0}, + {"(*Request).SetBasicAuth", Method, 0}, + {"(*Request).SetPathValue", Method, 22}, + {"(*Request).UserAgent", Method, 0}, + {"(*Request).WithContext", Method, 7}, + {"(*Request).Write", Method, 0}, + {"(*Request).WriteProxy", Method, 0}, + {"(*Response).Cookies", Method, 0}, + {"(*Response).Location", Method, 0}, + {"(*Response).ProtoAtLeast", Method, 0}, + {"(*Response).Write", Method, 0}, + {"(*ResponseController).EnableFullDuplex", Method, 21}, + {"(*ResponseController).Flush", Method, 20}, + {"(*ResponseController).Hijack", Method, 20}, + {"(*ResponseController).SetReadDeadline", Method, 20}, + {"(*ResponseController).SetWriteDeadline", Method, 20}, + {"(*ServeMux).Handle", Method, 0}, + {"(*ServeMux).HandleFunc", Method, 0}, + {"(*ServeMux).Handler", Method, 1}, + {"(*ServeMux).ServeHTTP", Method, 0}, + {"(*Server).Close", Method, 8}, + {"(*Server).ListenAndServe", Method, 0}, + {"(*Server).ListenAndServeTLS", Method, 0}, + {"(*Server).RegisterOnShutdown", Method, 9}, + {"(*Server).Serve", Method, 0}, + {"(*Server).ServeTLS", Method, 9}, + {"(*Server).SetKeepAlivesEnabled", Method, 3}, + {"(*Server).Shutdown", Method, 8}, + {"(*Transport).CancelRequest", Method, 1}, + {"(*Transport).Clone", Method, 13}, + {"(*Transport).CloseIdleConnections", Method, 0}, + {"(*Transport).RegisterProtocol", Method, 0}, + {"(*Transport).RoundTrip", Method, 0}, + {"(ConnState).String", Method, 3}, + {"(Dir).Open", Method, 0}, + {"(HandlerFunc).ServeHTTP", Method, 0}, + {"(Header).Add", Method, 0}, + {"(Header).Clone", Method, 13}, + {"(Header).Del", Method, 0}, + {"(Header).Get", Method, 0}, + {"(Header).Set", Method, 0}, + {"(Header).Values", Method, 14}, + {"(Header).Write", Method, 0}, + {"(Header).WriteSubset", Method, 0}, + {"AllowQuerySemicolons", Func, 17}, + {"CanonicalHeaderKey", Func, 0}, + {"Client", Type, 0}, + {"Client.CheckRedirect", Field, 0}, + {"Client.Jar", Field, 0}, + {"Client.Timeout", Field, 3}, + {"Client.Transport", Field, 0}, + {"CloseNotifier", Type, 1}, + {"ConnState", Type, 3}, + {"Cookie", Type, 0}, + {"Cookie.Domain", Field, 0}, + {"Cookie.Expires", Field, 0}, + {"Cookie.HttpOnly", Field, 0}, + {"Cookie.MaxAge", Field, 0}, + {"Cookie.Name", Field, 0}, + {"Cookie.Partitioned", Field, 23}, + {"Cookie.Path", Field, 0}, + {"Cookie.Quoted", Field, 23}, + {"Cookie.Raw", Field, 0}, + {"Cookie.RawExpires", Field, 0}, + {"Cookie.SameSite", Field, 11}, + {"Cookie.Secure", Field, 0}, + {"Cookie.Unparsed", Field, 0}, + {"Cookie.Value", Field, 0}, + {"CookieJar", Type, 0}, + {"DefaultClient", Var, 0}, + {"DefaultMaxHeaderBytes", Const, 0}, + {"DefaultMaxIdleConnsPerHost", Const, 0}, + {"DefaultServeMux", Var, 0}, + {"DefaultTransport", Var, 0}, + {"DetectContentType", Func, 0}, + {"Dir", Type, 0}, + {"ErrAbortHandler", Var, 8}, + {"ErrBodyNotAllowed", Var, 0}, + {"ErrBodyReadAfterClose", Var, 0}, + {"ErrContentLength", Var, 0}, + {"ErrHandlerTimeout", Var, 0}, + {"ErrHeaderTooLong", Var, 0}, + {"ErrHijacked", Var, 0}, + {"ErrLineTooLong", Var, 0}, + {"ErrMissingBoundary", Var, 0}, + {"ErrMissingContentLength", Var, 0}, + {"ErrMissingFile", Var, 0}, + {"ErrNoCookie", Var, 0}, + {"ErrNoLocation", Var, 0}, + {"ErrNotMultipart", Var, 0}, + {"ErrNotSupported", Var, 0}, + {"ErrSchemeMismatch", Var, 21}, + {"ErrServerClosed", Var, 8}, + {"ErrShortBody", Var, 0}, + {"ErrSkipAltProtocol", Var, 6}, + {"ErrUnexpectedTrailer", Var, 0}, + {"ErrUseLastResponse", Var, 7}, + {"ErrWriteAfterFlush", Var, 0}, + {"Error", Func, 0}, + {"FS", Func, 16}, + {"File", Type, 0}, + {"FileServer", Func, 0}, + {"FileServerFS", Func, 22}, + {"FileSystem", Type, 0}, + {"Flusher", Type, 0}, + {"Get", Func, 0}, + {"Handle", Func, 0}, + {"HandleFunc", Func, 0}, + {"Handler", Type, 0}, + {"HandlerFunc", Type, 0}, + {"Head", Func, 0}, + {"Header", Type, 0}, + {"Hijacker", Type, 0}, + {"ListenAndServe", Func, 0}, + {"ListenAndServeTLS", Func, 0}, + {"LocalAddrContextKey", Var, 7}, + {"MaxBytesError", Type, 19}, + {"MaxBytesError.Limit", Field, 19}, + {"MaxBytesHandler", Func, 18}, + {"MaxBytesReader", Func, 0}, + {"MethodConnect", Const, 6}, + {"MethodDelete", Const, 6}, + {"MethodGet", Const, 6}, + {"MethodHead", Const, 6}, + {"MethodOptions", Const, 6}, + {"MethodPatch", Const, 6}, + {"MethodPost", Const, 6}, + {"MethodPut", Const, 6}, + {"MethodTrace", Const, 6}, + {"NewFileTransport", Func, 0}, + {"NewFileTransportFS", Func, 22}, + {"NewRequest", Func, 0}, + {"NewRequestWithContext", Func, 13}, + {"NewResponseController", Func, 20}, + {"NewServeMux", Func, 0}, + {"NoBody", Var, 8}, + {"NotFound", Func, 0}, + {"NotFoundHandler", Func, 0}, + {"ParseCookie", Func, 23}, + {"ParseHTTPVersion", Func, 0}, + {"ParseSetCookie", Func, 23}, + {"ParseTime", Func, 1}, + {"Post", Func, 0}, + {"PostForm", Func, 0}, + {"ProtocolError", Type, 0}, + {"ProtocolError.ErrorString", Field, 0}, + {"ProxyFromEnvironment", Func, 0}, + {"ProxyURL", Func, 0}, + {"PushOptions", Type, 8}, + {"PushOptions.Header", Field, 8}, + {"PushOptions.Method", Field, 8}, + {"Pusher", Type, 8}, + {"ReadRequest", Func, 0}, + {"ReadResponse", Func, 0}, + {"Redirect", Func, 0}, + {"RedirectHandler", Func, 0}, + {"Request", Type, 0}, + {"Request.Body", Field, 0}, + {"Request.Cancel", Field, 5}, + {"Request.Close", Field, 0}, + {"Request.ContentLength", Field, 0}, + {"Request.Form", Field, 0}, + {"Request.GetBody", Field, 8}, + {"Request.Header", Field, 0}, + {"Request.Host", Field, 0}, + {"Request.Method", Field, 0}, + {"Request.MultipartForm", Field, 0}, + {"Request.Pattern", Field, 23}, + {"Request.PostForm", Field, 1}, + {"Request.Proto", Field, 0}, + {"Request.ProtoMajor", Field, 0}, + {"Request.ProtoMinor", Field, 0}, + {"Request.RemoteAddr", Field, 0}, + {"Request.RequestURI", Field, 0}, + {"Request.Response", Field, 7}, + {"Request.TLS", Field, 0}, + {"Request.Trailer", Field, 0}, + {"Request.TransferEncoding", Field, 0}, + {"Request.URL", Field, 0}, + {"Response", Type, 0}, + {"Response.Body", Field, 0}, + {"Response.Close", Field, 0}, + {"Response.ContentLength", Field, 0}, + {"Response.Header", Field, 0}, + {"Response.Proto", Field, 0}, + {"Response.ProtoMajor", Field, 0}, + {"Response.ProtoMinor", Field, 0}, + {"Response.Request", Field, 0}, + {"Response.Status", Field, 0}, + {"Response.StatusCode", Field, 0}, + {"Response.TLS", Field, 3}, + {"Response.Trailer", Field, 0}, + {"Response.TransferEncoding", Field, 0}, + {"Response.Uncompressed", Field, 7}, + {"ResponseController", Type, 20}, + {"ResponseWriter", Type, 0}, + {"RoundTripper", Type, 0}, + {"SameSite", Type, 11}, + {"SameSiteDefaultMode", Const, 11}, + {"SameSiteLaxMode", Const, 11}, + {"SameSiteNoneMode", Const, 13}, + {"SameSiteStrictMode", Const, 11}, + {"Serve", Func, 0}, + {"ServeContent", Func, 0}, + {"ServeFile", Func, 0}, + {"ServeFileFS", Func, 22}, + {"ServeMux", Type, 0}, + {"ServeTLS", Func, 9}, + {"Server", Type, 0}, + {"Server.Addr", Field, 0}, + {"Server.BaseContext", Field, 13}, + {"Server.ConnContext", Field, 13}, + {"Server.ConnState", Field, 3}, + {"Server.DisableGeneralOptionsHandler", Field, 20}, + {"Server.ErrorLog", Field, 3}, + {"Server.Handler", Field, 0}, + {"Server.IdleTimeout", Field, 8}, + {"Server.MaxHeaderBytes", Field, 0}, + {"Server.ReadHeaderTimeout", Field, 8}, + {"Server.ReadTimeout", Field, 0}, + {"Server.TLSConfig", Field, 0}, + {"Server.TLSNextProto", Field, 1}, + {"Server.WriteTimeout", Field, 0}, + {"ServerContextKey", Var, 7}, + {"SetCookie", Func, 0}, + {"StateActive", Const, 3}, + {"StateClosed", Const, 3}, + {"StateHijacked", Const, 3}, + {"StateIdle", Const, 3}, + {"StateNew", Const, 3}, + {"StatusAccepted", Const, 0}, + {"StatusAlreadyReported", Const, 7}, + {"StatusBadGateway", Const, 0}, + {"StatusBadRequest", Const, 0}, + {"StatusConflict", Const, 0}, + {"StatusContinue", Const, 0}, + {"StatusCreated", Const, 0}, + {"StatusEarlyHints", Const, 13}, + {"StatusExpectationFailed", Const, 0}, + {"StatusFailedDependency", Const, 7}, + {"StatusForbidden", Const, 0}, + {"StatusFound", Const, 0}, + {"StatusGatewayTimeout", Const, 0}, + {"StatusGone", Const, 0}, + {"StatusHTTPVersionNotSupported", Const, 0}, + {"StatusIMUsed", Const, 7}, + {"StatusInsufficientStorage", Const, 7}, + {"StatusInternalServerError", Const, 0}, + {"StatusLengthRequired", Const, 0}, + {"StatusLocked", Const, 7}, + {"StatusLoopDetected", Const, 7}, + {"StatusMethodNotAllowed", Const, 0}, + {"StatusMisdirectedRequest", Const, 11}, + {"StatusMovedPermanently", Const, 0}, + {"StatusMultiStatus", Const, 7}, + {"StatusMultipleChoices", Const, 0}, + {"StatusNetworkAuthenticationRequired", Const, 6}, + {"StatusNoContent", Const, 0}, + {"StatusNonAuthoritativeInfo", Const, 0}, + {"StatusNotAcceptable", Const, 0}, + {"StatusNotExtended", Const, 7}, + {"StatusNotFound", Const, 0}, + {"StatusNotImplemented", Const, 0}, + {"StatusNotModified", Const, 0}, + {"StatusOK", Const, 0}, + {"StatusPartialContent", Const, 0}, + {"StatusPaymentRequired", Const, 0}, + {"StatusPermanentRedirect", Const, 7}, + {"StatusPreconditionFailed", Const, 0}, + {"StatusPreconditionRequired", Const, 6}, + {"StatusProcessing", Const, 7}, + {"StatusProxyAuthRequired", Const, 0}, + {"StatusRequestEntityTooLarge", Const, 0}, + {"StatusRequestHeaderFieldsTooLarge", Const, 6}, + {"StatusRequestTimeout", Const, 0}, + {"StatusRequestURITooLong", Const, 0}, + {"StatusRequestedRangeNotSatisfiable", Const, 0}, + {"StatusResetContent", Const, 0}, + {"StatusSeeOther", Const, 0}, + {"StatusServiceUnavailable", Const, 0}, + {"StatusSwitchingProtocols", Const, 0}, + {"StatusTeapot", Const, 0}, + {"StatusTemporaryRedirect", Const, 0}, + {"StatusText", Func, 0}, + {"StatusTooEarly", Const, 12}, + {"StatusTooManyRequests", Const, 6}, + {"StatusUnauthorized", Const, 0}, + {"StatusUnavailableForLegalReasons", Const, 6}, + {"StatusUnprocessableEntity", Const, 7}, + {"StatusUnsupportedMediaType", Const, 0}, + {"StatusUpgradeRequired", Const, 7}, + {"StatusUseProxy", Const, 0}, + {"StatusVariantAlsoNegotiates", Const, 7}, + {"StripPrefix", Func, 0}, + {"TimeFormat", Const, 0}, + {"TimeoutHandler", Func, 0}, + {"TrailerPrefix", Const, 8}, + {"Transport", Type, 0}, + {"Transport.Dial", Field, 0}, + {"Transport.DialContext", Field, 7}, + {"Transport.DialTLS", Field, 4}, + {"Transport.DialTLSContext", Field, 14}, + {"Transport.DisableCompression", Field, 0}, + {"Transport.DisableKeepAlives", Field, 0}, + {"Transport.ExpectContinueTimeout", Field, 6}, + {"Transport.ForceAttemptHTTP2", Field, 13}, + {"Transport.GetProxyConnectHeader", Field, 16}, + {"Transport.IdleConnTimeout", Field, 7}, + {"Transport.MaxConnsPerHost", Field, 11}, + {"Transport.MaxIdleConns", Field, 7}, + {"Transport.MaxIdleConnsPerHost", Field, 0}, + {"Transport.MaxResponseHeaderBytes", Field, 7}, + {"Transport.OnProxyConnectResponse", Field, 20}, + {"Transport.Proxy", Field, 0}, + {"Transport.ProxyConnectHeader", Field, 8}, + {"Transport.ReadBufferSize", Field, 13}, + {"Transport.ResponseHeaderTimeout", Field, 1}, + {"Transport.TLSClientConfig", Field, 0}, + {"Transport.TLSHandshakeTimeout", Field, 3}, + {"Transport.TLSNextProto", Field, 6}, + {"Transport.WriteBufferSize", Field, 13}, + }, + "net/http/cgi": { + {"(*Handler).ServeHTTP", Method, 0}, + {"Handler", Type, 0}, + {"Handler.Args", Field, 0}, + {"Handler.Dir", Field, 0}, + {"Handler.Env", Field, 0}, + {"Handler.InheritEnv", Field, 0}, + {"Handler.Logger", Field, 0}, + {"Handler.Path", Field, 0}, + {"Handler.PathLocationHandler", Field, 0}, + {"Handler.Root", Field, 0}, + {"Handler.Stderr", Field, 7}, + {"Request", Func, 0}, + {"RequestFromMap", Func, 0}, + {"Serve", Func, 0}, + }, + "net/http/cookiejar": { + {"(*Jar).Cookies", Method, 1}, + {"(*Jar).SetCookies", Method, 1}, + {"Jar", Type, 1}, + {"New", Func, 1}, + {"Options", Type, 1}, + {"Options.PublicSuffixList", Field, 1}, + {"PublicSuffixList", Type, 1}, + }, + "net/http/fcgi": { + {"ErrConnClosed", Var, 5}, + {"ErrRequestAborted", Var, 5}, + {"ProcessEnv", Func, 9}, + {"Serve", Func, 0}, + }, + "net/http/httptest": { + {"(*ResponseRecorder).Flush", Method, 0}, + {"(*ResponseRecorder).Header", Method, 0}, + {"(*ResponseRecorder).Result", Method, 7}, + {"(*ResponseRecorder).Write", Method, 0}, + {"(*ResponseRecorder).WriteHeader", Method, 0}, + {"(*ResponseRecorder).WriteString", Method, 6}, + {"(*Server).Certificate", Method, 9}, + {"(*Server).Client", Method, 9}, + {"(*Server).Close", Method, 0}, + {"(*Server).CloseClientConnections", Method, 0}, + {"(*Server).Start", Method, 0}, + {"(*Server).StartTLS", Method, 0}, + {"DefaultRemoteAddr", Const, 0}, + {"NewRecorder", Func, 0}, + {"NewRequest", Func, 7}, + {"NewRequestWithContext", Func, 23}, + {"NewServer", Func, 0}, + {"NewTLSServer", Func, 0}, + {"NewUnstartedServer", Func, 0}, + {"ResponseRecorder", Type, 0}, + {"ResponseRecorder.Body", Field, 0}, + {"ResponseRecorder.Code", Field, 0}, + {"ResponseRecorder.Flushed", Field, 0}, + {"ResponseRecorder.HeaderMap", Field, 0}, + {"Server", Type, 0}, + {"Server.Config", Field, 0}, + {"Server.EnableHTTP2", Field, 14}, + {"Server.Listener", Field, 0}, + {"Server.TLS", Field, 0}, + {"Server.URL", Field, 0}, + }, + "net/http/httptrace": { + {"ClientTrace", Type, 7}, + {"ClientTrace.ConnectDone", Field, 7}, + {"ClientTrace.ConnectStart", Field, 7}, + {"ClientTrace.DNSDone", Field, 7}, + {"ClientTrace.DNSStart", Field, 7}, + {"ClientTrace.GetConn", Field, 7}, + {"ClientTrace.Got100Continue", Field, 7}, + {"ClientTrace.Got1xxResponse", Field, 11}, + {"ClientTrace.GotConn", Field, 7}, + {"ClientTrace.GotFirstResponseByte", Field, 7}, + {"ClientTrace.PutIdleConn", Field, 7}, + {"ClientTrace.TLSHandshakeDone", Field, 8}, + {"ClientTrace.TLSHandshakeStart", Field, 8}, + {"ClientTrace.Wait100Continue", Field, 7}, + {"ClientTrace.WroteHeaderField", Field, 11}, + {"ClientTrace.WroteHeaders", Field, 7}, + {"ClientTrace.WroteRequest", Field, 7}, + {"ContextClientTrace", Func, 7}, + {"DNSDoneInfo", Type, 7}, + {"DNSDoneInfo.Addrs", Field, 7}, + {"DNSDoneInfo.Coalesced", Field, 7}, + {"DNSDoneInfo.Err", Field, 7}, + {"DNSStartInfo", Type, 7}, + {"DNSStartInfo.Host", Field, 7}, + {"GotConnInfo", Type, 7}, + {"GotConnInfo.Conn", Field, 7}, + {"GotConnInfo.IdleTime", Field, 7}, + {"GotConnInfo.Reused", Field, 7}, + {"GotConnInfo.WasIdle", Field, 7}, + {"WithClientTrace", Func, 7}, + {"WroteRequestInfo", Type, 7}, + {"WroteRequestInfo.Err", Field, 7}, + }, + "net/http/httputil": { + {"(*ClientConn).Close", Method, 0}, + {"(*ClientConn).Do", Method, 0}, + {"(*ClientConn).Hijack", Method, 0}, + {"(*ClientConn).Pending", Method, 0}, + {"(*ClientConn).Read", Method, 0}, + {"(*ClientConn).Write", Method, 0}, + {"(*ProxyRequest).SetURL", Method, 20}, + {"(*ProxyRequest).SetXForwarded", Method, 20}, + {"(*ReverseProxy).ServeHTTP", Method, 0}, + {"(*ServerConn).Close", Method, 0}, + {"(*ServerConn).Hijack", Method, 0}, + {"(*ServerConn).Pending", Method, 0}, + {"(*ServerConn).Read", Method, 0}, + {"(*ServerConn).Write", Method, 0}, + {"BufferPool", Type, 6}, + {"ClientConn", Type, 0}, + {"DumpRequest", Func, 0}, + {"DumpRequestOut", Func, 0}, + {"DumpResponse", Func, 0}, + {"ErrClosed", Var, 0}, + {"ErrLineTooLong", Var, 0}, + {"ErrPersistEOF", Var, 0}, + {"ErrPipeline", Var, 0}, + {"NewChunkedReader", Func, 0}, + {"NewChunkedWriter", Func, 0}, + {"NewClientConn", Func, 0}, + {"NewProxyClientConn", Func, 0}, + {"NewServerConn", Func, 0}, + {"NewSingleHostReverseProxy", Func, 0}, + {"ProxyRequest", Type, 20}, + {"ProxyRequest.In", Field, 20}, + {"ProxyRequest.Out", Field, 20}, + {"ReverseProxy", Type, 0}, + {"ReverseProxy.BufferPool", Field, 6}, + {"ReverseProxy.Director", Field, 0}, + {"ReverseProxy.ErrorHandler", Field, 11}, + {"ReverseProxy.ErrorLog", Field, 4}, + {"ReverseProxy.FlushInterval", Field, 0}, + {"ReverseProxy.ModifyResponse", Field, 8}, + {"ReverseProxy.Rewrite", Field, 20}, + {"ReverseProxy.Transport", Field, 0}, + {"ServerConn", Type, 0}, + }, + "net/http/pprof": { + {"Cmdline", Func, 0}, + {"Handler", Func, 0}, + {"Index", Func, 0}, + {"Profile", Func, 0}, + {"Symbol", Func, 0}, + {"Trace", Func, 5}, + }, + "net/mail": { + {"(*Address).String", Method, 0}, + {"(*AddressParser).Parse", Method, 5}, + {"(*AddressParser).ParseList", Method, 5}, + {"(Header).AddressList", Method, 0}, + {"(Header).Date", Method, 0}, + {"(Header).Get", Method, 0}, + {"Address", Type, 0}, + {"Address.Address", Field, 0}, + {"Address.Name", Field, 0}, + {"AddressParser", Type, 5}, + {"AddressParser.WordDecoder", Field, 5}, + {"ErrHeaderNotPresent", Var, 0}, + {"Header", Type, 0}, + {"Message", Type, 0}, + {"Message.Body", Field, 0}, + {"Message.Header", Field, 0}, + {"ParseAddress", Func, 1}, + {"ParseAddressList", Func, 1}, + {"ParseDate", Func, 8}, + {"ReadMessage", Func, 0}, + }, + "net/netip": { + {"(*Addr).UnmarshalBinary", Method, 18}, + {"(*Addr).UnmarshalText", Method, 18}, + {"(*AddrPort).UnmarshalBinary", Method, 18}, + {"(*AddrPort).UnmarshalText", Method, 18}, + {"(*Prefix).UnmarshalBinary", Method, 18}, + {"(*Prefix).UnmarshalText", Method, 18}, + {"(Addr).AppendTo", Method, 18}, + {"(Addr).As16", Method, 18}, + {"(Addr).As4", Method, 18}, + {"(Addr).AsSlice", Method, 18}, + {"(Addr).BitLen", Method, 18}, + {"(Addr).Compare", Method, 18}, + {"(Addr).Is4", Method, 18}, + {"(Addr).Is4In6", Method, 18}, + {"(Addr).Is6", Method, 18}, + {"(Addr).IsGlobalUnicast", Method, 18}, + {"(Addr).IsInterfaceLocalMulticast", Method, 18}, + {"(Addr).IsLinkLocalMulticast", Method, 18}, + {"(Addr).IsLinkLocalUnicast", Method, 18}, + {"(Addr).IsLoopback", Method, 18}, + {"(Addr).IsMulticast", Method, 18}, + {"(Addr).IsPrivate", Method, 18}, + {"(Addr).IsUnspecified", Method, 18}, + {"(Addr).IsValid", Method, 18}, + {"(Addr).Less", Method, 18}, + {"(Addr).MarshalBinary", Method, 18}, + {"(Addr).MarshalText", Method, 18}, + {"(Addr).Next", Method, 18}, + {"(Addr).Prefix", Method, 18}, + {"(Addr).Prev", Method, 18}, + {"(Addr).String", Method, 18}, + {"(Addr).StringExpanded", Method, 18}, + {"(Addr).Unmap", Method, 18}, + {"(Addr).WithZone", Method, 18}, + {"(Addr).Zone", Method, 18}, + {"(AddrPort).Addr", Method, 18}, + {"(AddrPort).AppendTo", Method, 18}, + {"(AddrPort).Compare", Method, 22}, + {"(AddrPort).IsValid", Method, 18}, + {"(AddrPort).MarshalBinary", Method, 18}, + {"(AddrPort).MarshalText", Method, 18}, + {"(AddrPort).Port", Method, 18}, + {"(AddrPort).String", Method, 18}, + {"(Prefix).Addr", Method, 18}, + {"(Prefix).AppendTo", Method, 18}, + {"(Prefix).Bits", Method, 18}, + {"(Prefix).Contains", Method, 18}, + {"(Prefix).IsSingleIP", Method, 18}, + {"(Prefix).IsValid", Method, 18}, + {"(Prefix).MarshalBinary", Method, 18}, + {"(Prefix).MarshalText", Method, 18}, + {"(Prefix).Masked", Method, 18}, + {"(Prefix).Overlaps", Method, 18}, + {"(Prefix).String", Method, 18}, + {"Addr", Type, 18}, + {"AddrFrom16", Func, 18}, + {"AddrFrom4", Func, 18}, + {"AddrFromSlice", Func, 18}, + {"AddrPort", Type, 18}, + {"AddrPortFrom", Func, 18}, + {"IPv4Unspecified", Func, 18}, + {"IPv6LinkLocalAllNodes", Func, 18}, + {"IPv6LinkLocalAllRouters", Func, 20}, + {"IPv6Loopback", Func, 20}, + {"IPv6Unspecified", Func, 18}, + {"MustParseAddr", Func, 18}, + {"MustParseAddrPort", Func, 18}, + {"MustParsePrefix", Func, 18}, + {"ParseAddr", Func, 18}, + {"ParseAddrPort", Func, 18}, + {"ParsePrefix", Func, 18}, + {"Prefix", Type, 18}, + {"PrefixFrom", Func, 18}, + }, + "net/rpc": { + {"(*Client).Call", Method, 0}, + {"(*Client).Close", Method, 0}, + {"(*Client).Go", Method, 0}, + {"(*Server).Accept", Method, 0}, + {"(*Server).HandleHTTP", Method, 0}, + {"(*Server).Register", Method, 0}, + {"(*Server).RegisterName", Method, 0}, + {"(*Server).ServeCodec", Method, 0}, + {"(*Server).ServeConn", Method, 0}, + {"(*Server).ServeHTTP", Method, 0}, + {"(*Server).ServeRequest", Method, 0}, + {"(ServerError).Error", Method, 0}, + {"Accept", Func, 0}, + {"Call", Type, 0}, + {"Call.Args", Field, 0}, + {"Call.Done", Field, 0}, + {"Call.Error", Field, 0}, + {"Call.Reply", Field, 0}, + {"Call.ServiceMethod", Field, 0}, + {"Client", Type, 0}, + {"ClientCodec", Type, 0}, + {"DefaultDebugPath", Const, 0}, + {"DefaultRPCPath", Const, 0}, + {"DefaultServer", Var, 0}, + {"Dial", Func, 0}, + {"DialHTTP", Func, 0}, + {"DialHTTPPath", Func, 0}, + {"ErrShutdown", Var, 0}, + {"HandleHTTP", Func, 0}, + {"NewClient", Func, 0}, + {"NewClientWithCodec", Func, 0}, + {"NewServer", Func, 0}, + {"Register", Func, 0}, + {"RegisterName", Func, 0}, + {"Request", Type, 0}, + {"Request.Seq", Field, 0}, + {"Request.ServiceMethod", Field, 0}, + {"Response", Type, 0}, + {"Response.Error", Field, 0}, + {"Response.Seq", Field, 0}, + {"Response.ServiceMethod", Field, 0}, + {"ServeCodec", Func, 0}, + {"ServeConn", Func, 0}, + {"ServeRequest", Func, 0}, + {"Server", Type, 0}, + {"ServerCodec", Type, 0}, + {"ServerError", Type, 0}, + }, + "net/rpc/jsonrpc": { + {"Dial", Func, 0}, + {"NewClient", Func, 0}, + {"NewClientCodec", Func, 0}, + {"NewServerCodec", Func, 0}, + {"ServeConn", Func, 0}, + }, + "net/smtp": { + {"(*Client).Auth", Method, 0}, + {"(*Client).Close", Method, 2}, + {"(*Client).Data", Method, 0}, + {"(*Client).Extension", Method, 0}, + {"(*Client).Hello", Method, 1}, + {"(*Client).Mail", Method, 0}, + {"(*Client).Noop", Method, 10}, + {"(*Client).Quit", Method, 0}, + {"(*Client).Rcpt", Method, 0}, + {"(*Client).Reset", Method, 0}, + {"(*Client).StartTLS", Method, 0}, + {"(*Client).TLSConnectionState", Method, 5}, + {"(*Client).Verify", Method, 0}, + {"Auth", Type, 0}, + {"CRAMMD5Auth", Func, 0}, + {"Client", Type, 0}, + {"Client.Text", Field, 0}, + {"Dial", Func, 0}, + {"NewClient", Func, 0}, + {"PlainAuth", Func, 0}, + {"SendMail", Func, 0}, + {"ServerInfo", Type, 0}, + {"ServerInfo.Auth", Field, 0}, + {"ServerInfo.Name", Field, 0}, + {"ServerInfo.TLS", Field, 0}, + }, + "net/textproto": { + {"(*Conn).Close", Method, 0}, + {"(*Conn).Cmd", Method, 0}, + {"(*Conn).DotReader", Method, 0}, + {"(*Conn).DotWriter", Method, 0}, + {"(*Conn).EndRequest", Method, 0}, + {"(*Conn).EndResponse", Method, 0}, + {"(*Conn).Next", Method, 0}, + {"(*Conn).PrintfLine", Method, 0}, + {"(*Conn).ReadCodeLine", Method, 0}, + {"(*Conn).ReadContinuedLine", Method, 0}, + {"(*Conn).ReadContinuedLineBytes", Method, 0}, + {"(*Conn).ReadDotBytes", Method, 0}, + {"(*Conn).ReadDotLines", Method, 0}, + {"(*Conn).ReadLine", Method, 0}, + {"(*Conn).ReadLineBytes", Method, 0}, + {"(*Conn).ReadMIMEHeader", Method, 0}, + {"(*Conn).ReadResponse", Method, 0}, + {"(*Conn).StartRequest", Method, 0}, + {"(*Conn).StartResponse", Method, 0}, + {"(*Error).Error", Method, 0}, + {"(*Pipeline).EndRequest", Method, 0}, + {"(*Pipeline).EndResponse", Method, 0}, + {"(*Pipeline).Next", Method, 0}, + {"(*Pipeline).StartRequest", Method, 0}, + {"(*Pipeline).StartResponse", Method, 0}, + {"(*Reader).DotReader", Method, 0}, + {"(*Reader).ReadCodeLine", Method, 0}, + {"(*Reader).ReadContinuedLine", Method, 0}, + {"(*Reader).ReadContinuedLineBytes", Method, 0}, + {"(*Reader).ReadDotBytes", Method, 0}, + {"(*Reader).ReadDotLines", Method, 0}, + {"(*Reader).ReadLine", Method, 0}, + {"(*Reader).ReadLineBytes", Method, 0}, + {"(*Reader).ReadMIMEHeader", Method, 0}, + {"(*Reader).ReadResponse", Method, 0}, + {"(*Writer).DotWriter", Method, 0}, + {"(*Writer).PrintfLine", Method, 0}, + {"(MIMEHeader).Add", Method, 0}, + {"(MIMEHeader).Del", Method, 0}, + {"(MIMEHeader).Get", Method, 0}, + {"(MIMEHeader).Set", Method, 0}, + {"(MIMEHeader).Values", Method, 14}, + {"(ProtocolError).Error", Method, 0}, + {"CanonicalMIMEHeaderKey", Func, 0}, + {"Conn", Type, 0}, + {"Conn.Pipeline", Field, 0}, + {"Conn.Reader", Field, 0}, + {"Conn.Writer", Field, 0}, + {"Dial", Func, 0}, + {"Error", Type, 0}, + {"Error.Code", Field, 0}, + {"Error.Msg", Field, 0}, + {"MIMEHeader", Type, 0}, + {"NewConn", Func, 0}, + {"NewReader", Func, 0}, + {"NewWriter", Func, 0}, + {"Pipeline", Type, 0}, + {"ProtocolError", Type, 0}, + {"Reader", Type, 0}, + {"Reader.R", Field, 0}, + {"TrimBytes", Func, 1}, + {"TrimString", Func, 1}, + {"Writer", Type, 0}, + {"Writer.W", Field, 0}, + }, + "net/url": { + {"(*Error).Error", Method, 0}, + {"(*Error).Temporary", Method, 6}, + {"(*Error).Timeout", Method, 6}, + {"(*Error).Unwrap", Method, 13}, + {"(*URL).EscapedFragment", Method, 15}, + {"(*URL).EscapedPath", Method, 5}, + {"(*URL).Hostname", Method, 8}, + {"(*URL).IsAbs", Method, 0}, + {"(*URL).JoinPath", Method, 19}, + {"(*URL).MarshalBinary", Method, 8}, + {"(*URL).Parse", Method, 0}, + {"(*URL).Port", Method, 8}, + {"(*URL).Query", Method, 0}, + {"(*URL).Redacted", Method, 15}, + {"(*URL).RequestURI", Method, 0}, + {"(*URL).ResolveReference", Method, 0}, + {"(*URL).String", Method, 0}, + {"(*URL).UnmarshalBinary", Method, 8}, + {"(*Userinfo).Password", Method, 0}, + {"(*Userinfo).String", Method, 0}, + {"(*Userinfo).Username", Method, 0}, + {"(EscapeError).Error", Method, 0}, + {"(InvalidHostError).Error", Method, 6}, + {"(Values).Add", Method, 0}, + {"(Values).Del", Method, 0}, + {"(Values).Encode", Method, 0}, + {"(Values).Get", Method, 0}, + {"(Values).Has", Method, 17}, + {"(Values).Set", Method, 0}, + {"Error", Type, 0}, + {"Error.Err", Field, 0}, + {"Error.Op", Field, 0}, + {"Error.URL", Field, 0}, + {"EscapeError", Type, 0}, + {"InvalidHostError", Type, 6}, + {"JoinPath", Func, 19}, + {"Parse", Func, 0}, + {"ParseQuery", Func, 0}, + {"ParseRequestURI", Func, 0}, + {"PathEscape", Func, 8}, + {"PathUnescape", Func, 8}, + {"QueryEscape", Func, 0}, + {"QueryUnescape", Func, 0}, + {"URL", Type, 0}, + {"URL.ForceQuery", Field, 7}, + {"URL.Fragment", Field, 0}, + {"URL.Host", Field, 0}, + {"URL.OmitHost", Field, 19}, + {"URL.Opaque", Field, 0}, + {"URL.Path", Field, 0}, + {"URL.RawFragment", Field, 15}, + {"URL.RawPath", Field, 5}, + {"URL.RawQuery", Field, 0}, + {"URL.Scheme", Field, 0}, + {"URL.User", Field, 0}, + {"User", Func, 0}, + {"UserPassword", Func, 0}, + {"Userinfo", Type, 0}, + {"Values", Type, 0}, + }, + "os": { + {"(*File).Chdir", Method, 0}, + {"(*File).Chmod", Method, 0}, + {"(*File).Chown", Method, 0}, + {"(*File).Close", Method, 0}, + {"(*File).Fd", Method, 0}, + {"(*File).Name", Method, 0}, + {"(*File).Read", Method, 0}, + {"(*File).ReadAt", Method, 0}, + {"(*File).ReadDir", Method, 16}, + {"(*File).ReadFrom", Method, 15}, + {"(*File).Readdir", Method, 0}, + {"(*File).Readdirnames", Method, 0}, + {"(*File).Seek", Method, 0}, + {"(*File).SetDeadline", Method, 10}, + {"(*File).SetReadDeadline", Method, 10}, + {"(*File).SetWriteDeadline", Method, 10}, + {"(*File).Stat", Method, 0}, + {"(*File).Sync", Method, 0}, + {"(*File).SyscallConn", Method, 12}, + {"(*File).Truncate", Method, 0}, + {"(*File).Write", Method, 0}, + {"(*File).WriteAt", Method, 0}, + {"(*File).WriteString", Method, 0}, + {"(*File).WriteTo", Method, 22}, + {"(*LinkError).Error", Method, 0}, + {"(*LinkError).Unwrap", Method, 13}, + {"(*PathError).Error", Method, 0}, + {"(*PathError).Timeout", Method, 10}, + {"(*PathError).Unwrap", Method, 13}, + {"(*Process).Kill", Method, 0}, + {"(*Process).Release", Method, 0}, + {"(*Process).Signal", Method, 0}, + {"(*Process).Wait", Method, 0}, + {"(*ProcessState).ExitCode", Method, 12}, + {"(*ProcessState).Exited", Method, 0}, + {"(*ProcessState).Pid", Method, 0}, + {"(*ProcessState).String", Method, 0}, + {"(*ProcessState).Success", Method, 0}, + {"(*ProcessState).Sys", Method, 0}, + {"(*ProcessState).SysUsage", Method, 0}, + {"(*ProcessState).SystemTime", Method, 0}, + {"(*ProcessState).UserTime", Method, 0}, + {"(*SyscallError).Error", Method, 0}, + {"(*SyscallError).Timeout", Method, 10}, + {"(*SyscallError).Unwrap", Method, 13}, + {"(FileMode).IsDir", Method, 0}, + {"(FileMode).IsRegular", Method, 1}, + {"(FileMode).Perm", Method, 0}, + {"(FileMode).String", Method, 0}, + {"Args", Var, 0}, + {"Chdir", Func, 0}, + {"Chmod", Func, 0}, + {"Chown", Func, 0}, + {"Chtimes", Func, 0}, + {"Clearenv", Func, 0}, + {"CopyFS", Func, 23}, + {"Create", Func, 0}, + {"CreateTemp", Func, 16}, + {"DevNull", Const, 0}, + {"DirEntry", Type, 16}, + {"DirFS", Func, 16}, + {"Environ", Func, 0}, + {"ErrClosed", Var, 8}, + {"ErrDeadlineExceeded", Var, 15}, + {"ErrExist", Var, 0}, + {"ErrInvalid", Var, 0}, + {"ErrNoDeadline", Var, 10}, + {"ErrNotExist", Var, 0}, + {"ErrPermission", Var, 0}, + {"ErrProcessDone", Var, 16}, + {"Executable", Func, 8}, + {"Exit", Func, 0}, + {"Expand", Func, 0}, + {"ExpandEnv", Func, 0}, + {"File", Type, 0}, + {"FileInfo", Type, 0}, + {"FileMode", Type, 0}, + {"FindProcess", Func, 0}, + {"Getegid", Func, 0}, + {"Getenv", Func, 0}, + {"Geteuid", Func, 0}, + {"Getgid", Func, 0}, + {"Getgroups", Func, 0}, + {"Getpagesize", Func, 0}, + {"Getpid", Func, 0}, + {"Getppid", Func, 0}, + {"Getuid", Func, 0}, + {"Getwd", Func, 0}, + {"Hostname", Func, 0}, + {"Interrupt", Var, 0}, + {"IsExist", Func, 0}, + {"IsNotExist", Func, 0}, + {"IsPathSeparator", Func, 0}, + {"IsPermission", Func, 0}, + {"IsTimeout", Func, 10}, + {"Kill", Var, 0}, + {"Lchown", Func, 0}, + {"Link", Func, 0}, + {"LinkError", Type, 0}, + {"LinkError.Err", Field, 0}, + {"LinkError.New", Field, 0}, + {"LinkError.Old", Field, 0}, + {"LinkError.Op", Field, 0}, + {"LookupEnv", Func, 5}, + {"Lstat", Func, 0}, + {"Mkdir", Func, 0}, + {"MkdirAll", Func, 0}, + {"MkdirTemp", Func, 16}, + {"ModeAppend", Const, 0}, + {"ModeCharDevice", Const, 0}, + {"ModeDevice", Const, 0}, + {"ModeDir", Const, 0}, + {"ModeExclusive", Const, 0}, + {"ModeIrregular", Const, 11}, + {"ModeNamedPipe", Const, 0}, + {"ModePerm", Const, 0}, + {"ModeSetgid", Const, 0}, + {"ModeSetuid", Const, 0}, + {"ModeSocket", Const, 0}, + {"ModeSticky", Const, 0}, + {"ModeSymlink", Const, 0}, + {"ModeTemporary", Const, 0}, + {"ModeType", Const, 0}, + {"NewFile", Func, 0}, + {"NewSyscallError", Func, 0}, + {"O_APPEND", Const, 0}, + {"O_CREATE", Const, 0}, + {"O_EXCL", Const, 0}, + {"O_RDONLY", Const, 0}, + {"O_RDWR", Const, 0}, + {"O_SYNC", Const, 0}, + {"O_TRUNC", Const, 0}, + {"O_WRONLY", Const, 0}, + {"Open", Func, 0}, + {"OpenFile", Func, 0}, + {"PathError", Type, 0}, + {"PathError.Err", Field, 0}, + {"PathError.Op", Field, 0}, + {"PathError.Path", Field, 0}, + {"PathListSeparator", Const, 0}, + {"PathSeparator", Const, 0}, + {"Pipe", Func, 0}, + {"ProcAttr", Type, 0}, + {"ProcAttr.Dir", Field, 0}, + {"ProcAttr.Env", Field, 0}, + {"ProcAttr.Files", Field, 0}, + {"ProcAttr.Sys", Field, 0}, + {"Process", Type, 0}, + {"Process.Pid", Field, 0}, + {"ProcessState", Type, 0}, + {"ReadDir", Func, 16}, + {"ReadFile", Func, 16}, + {"Readlink", Func, 0}, + {"Remove", Func, 0}, + {"RemoveAll", Func, 0}, + {"Rename", Func, 0}, + {"SEEK_CUR", Const, 0}, + {"SEEK_END", Const, 0}, + {"SEEK_SET", Const, 0}, + {"SameFile", Func, 0}, + {"Setenv", Func, 0}, + {"Signal", Type, 0}, + {"StartProcess", Func, 0}, + {"Stat", Func, 0}, + {"Stderr", Var, 0}, + {"Stdin", Var, 0}, + {"Stdout", Var, 0}, + {"Symlink", Func, 0}, + {"SyscallError", Type, 0}, + {"SyscallError.Err", Field, 0}, + {"SyscallError.Syscall", Field, 0}, + {"TempDir", Func, 0}, + {"Truncate", Func, 0}, + {"Unsetenv", Func, 4}, + {"UserCacheDir", Func, 11}, + {"UserConfigDir", Func, 13}, + {"UserHomeDir", Func, 12}, + {"WriteFile", Func, 16}, + }, + "os/exec": { + {"(*Cmd).CombinedOutput", Method, 0}, + {"(*Cmd).Environ", Method, 19}, + {"(*Cmd).Output", Method, 0}, + {"(*Cmd).Run", Method, 0}, + {"(*Cmd).Start", Method, 0}, + {"(*Cmd).StderrPipe", Method, 0}, + {"(*Cmd).StdinPipe", Method, 0}, + {"(*Cmd).StdoutPipe", Method, 0}, + {"(*Cmd).String", Method, 13}, + {"(*Cmd).Wait", Method, 0}, + {"(*Error).Error", Method, 0}, + {"(*Error).Unwrap", Method, 13}, + {"(*ExitError).Error", Method, 0}, + {"(ExitError).ExitCode", Method, 12}, + {"(ExitError).Exited", Method, 0}, + {"(ExitError).Pid", Method, 0}, + {"(ExitError).String", Method, 0}, + {"(ExitError).Success", Method, 0}, + {"(ExitError).Sys", Method, 0}, + {"(ExitError).SysUsage", Method, 0}, + {"(ExitError).SystemTime", Method, 0}, + {"(ExitError).UserTime", Method, 0}, + {"Cmd", Type, 0}, + {"Cmd.Args", Field, 0}, + {"Cmd.Cancel", Field, 20}, + {"Cmd.Dir", Field, 0}, + {"Cmd.Env", Field, 0}, + {"Cmd.Err", Field, 19}, + {"Cmd.ExtraFiles", Field, 0}, + {"Cmd.Path", Field, 0}, + {"Cmd.Process", Field, 0}, + {"Cmd.ProcessState", Field, 0}, + {"Cmd.Stderr", Field, 0}, + {"Cmd.Stdin", Field, 0}, + {"Cmd.Stdout", Field, 0}, + {"Cmd.SysProcAttr", Field, 0}, + {"Cmd.WaitDelay", Field, 20}, + {"Command", Func, 0}, + {"CommandContext", Func, 7}, + {"ErrDot", Var, 19}, + {"ErrNotFound", Var, 0}, + {"ErrWaitDelay", Var, 20}, + {"Error", Type, 0}, + {"Error.Err", Field, 0}, + {"Error.Name", Field, 0}, + {"ExitError", Type, 0}, + {"ExitError.ProcessState", Field, 0}, + {"ExitError.Stderr", Field, 6}, + {"LookPath", Func, 0}, + }, + "os/signal": { + {"Ignore", Func, 5}, + {"Ignored", Func, 11}, + {"Notify", Func, 0}, + {"NotifyContext", Func, 16}, + {"Reset", Func, 5}, + {"Stop", Func, 1}, + }, + "os/user": { + {"(*User).GroupIds", Method, 7}, + {"(UnknownGroupError).Error", Method, 7}, + {"(UnknownGroupIdError).Error", Method, 7}, + {"(UnknownUserError).Error", Method, 0}, + {"(UnknownUserIdError).Error", Method, 0}, + {"Current", Func, 0}, + {"Group", Type, 7}, + {"Group.Gid", Field, 7}, + {"Group.Name", Field, 7}, + {"Lookup", Func, 0}, + {"LookupGroup", Func, 7}, + {"LookupGroupId", Func, 7}, + {"LookupId", Func, 0}, + {"UnknownGroupError", Type, 7}, + {"UnknownGroupIdError", Type, 7}, + {"UnknownUserError", Type, 0}, + {"UnknownUserIdError", Type, 0}, + {"User", Type, 0}, + {"User.Gid", Field, 0}, + {"User.HomeDir", Field, 0}, + {"User.Name", Field, 0}, + {"User.Uid", Field, 0}, + {"User.Username", Field, 0}, + }, + "path": { + {"Base", Func, 0}, + {"Clean", Func, 0}, + {"Dir", Func, 0}, + {"ErrBadPattern", Var, 0}, + {"Ext", Func, 0}, + {"IsAbs", Func, 0}, + {"Join", Func, 0}, + {"Match", Func, 0}, + {"Split", Func, 0}, + }, + "path/filepath": { + {"Abs", Func, 0}, + {"Base", Func, 0}, + {"Clean", Func, 0}, + {"Dir", Func, 0}, + {"ErrBadPattern", Var, 0}, + {"EvalSymlinks", Func, 0}, + {"Ext", Func, 0}, + {"FromSlash", Func, 0}, + {"Glob", Func, 0}, + {"HasPrefix", Func, 0}, + {"IsAbs", Func, 0}, + {"IsLocal", Func, 20}, + {"Join", Func, 0}, + {"ListSeparator", Const, 0}, + {"Localize", Func, 23}, + {"Match", Func, 0}, + {"Rel", Func, 0}, + {"Separator", Const, 0}, + {"SkipAll", Var, 20}, + {"SkipDir", Var, 0}, + {"Split", Func, 0}, + {"SplitList", Func, 0}, + {"ToSlash", Func, 0}, + {"VolumeName", Func, 0}, + {"Walk", Func, 0}, + {"WalkDir", Func, 16}, + {"WalkFunc", Type, 0}, + }, + "plugin": { + {"(*Plugin).Lookup", Method, 8}, + {"Open", Func, 8}, + {"Plugin", Type, 8}, + {"Symbol", Type, 8}, + }, + "reflect": { + {"(*MapIter).Key", Method, 12}, + {"(*MapIter).Next", Method, 12}, + {"(*MapIter).Reset", Method, 18}, + {"(*MapIter).Value", Method, 12}, + {"(*ValueError).Error", Method, 0}, + {"(ChanDir).String", Method, 0}, + {"(Kind).String", Method, 0}, + {"(Method).IsExported", Method, 17}, + {"(StructField).IsExported", Method, 17}, + {"(StructTag).Get", Method, 0}, + {"(StructTag).Lookup", Method, 7}, + {"(Value).Addr", Method, 0}, + {"(Value).Bool", Method, 0}, + {"(Value).Bytes", Method, 0}, + {"(Value).Call", Method, 0}, + {"(Value).CallSlice", Method, 0}, + {"(Value).CanAddr", Method, 0}, + {"(Value).CanComplex", Method, 18}, + {"(Value).CanConvert", Method, 17}, + {"(Value).CanFloat", Method, 18}, + {"(Value).CanInt", Method, 18}, + {"(Value).CanInterface", Method, 0}, + {"(Value).CanSet", Method, 0}, + {"(Value).CanUint", Method, 18}, + {"(Value).Cap", Method, 0}, + {"(Value).Clear", Method, 21}, + {"(Value).Close", Method, 0}, + {"(Value).Comparable", Method, 20}, + {"(Value).Complex", Method, 0}, + {"(Value).Convert", Method, 1}, + {"(Value).Elem", Method, 0}, + {"(Value).Equal", Method, 20}, + {"(Value).Field", Method, 0}, + {"(Value).FieldByIndex", Method, 0}, + {"(Value).FieldByIndexErr", Method, 18}, + {"(Value).FieldByName", Method, 0}, + {"(Value).FieldByNameFunc", Method, 0}, + {"(Value).Float", Method, 0}, + {"(Value).Grow", Method, 20}, + {"(Value).Index", Method, 0}, + {"(Value).Int", Method, 0}, + {"(Value).Interface", Method, 0}, + {"(Value).InterfaceData", Method, 0}, + {"(Value).IsNil", Method, 0}, + {"(Value).IsValid", Method, 0}, + {"(Value).IsZero", Method, 13}, + {"(Value).Kind", Method, 0}, + {"(Value).Len", Method, 0}, + {"(Value).MapIndex", Method, 0}, + {"(Value).MapKeys", Method, 0}, + {"(Value).MapRange", Method, 12}, + {"(Value).Method", Method, 0}, + {"(Value).MethodByName", Method, 0}, + {"(Value).NumField", Method, 0}, + {"(Value).NumMethod", Method, 0}, + {"(Value).OverflowComplex", Method, 0}, + {"(Value).OverflowFloat", Method, 0}, + {"(Value).OverflowInt", Method, 0}, + {"(Value).OverflowUint", Method, 0}, + {"(Value).Pointer", Method, 0}, + {"(Value).Recv", Method, 0}, + {"(Value).Send", Method, 0}, + {"(Value).Seq", Method, 23}, + {"(Value).Seq2", Method, 23}, + {"(Value).Set", Method, 0}, + {"(Value).SetBool", Method, 0}, + {"(Value).SetBytes", Method, 0}, + {"(Value).SetCap", Method, 2}, + {"(Value).SetComplex", Method, 0}, + {"(Value).SetFloat", Method, 0}, + {"(Value).SetInt", Method, 0}, + {"(Value).SetIterKey", Method, 18}, + {"(Value).SetIterValue", Method, 18}, + {"(Value).SetLen", Method, 0}, + {"(Value).SetMapIndex", Method, 0}, + {"(Value).SetPointer", Method, 0}, + {"(Value).SetString", Method, 0}, + {"(Value).SetUint", Method, 0}, + {"(Value).SetZero", Method, 20}, + {"(Value).Slice", Method, 0}, + {"(Value).Slice3", Method, 2}, + {"(Value).String", Method, 0}, + {"(Value).TryRecv", Method, 0}, + {"(Value).TrySend", Method, 0}, + {"(Value).Type", Method, 0}, + {"(Value).Uint", Method, 0}, + {"(Value).UnsafeAddr", Method, 0}, + {"(Value).UnsafePointer", Method, 18}, + {"Append", Func, 0}, + {"AppendSlice", Func, 0}, + {"Array", Const, 0}, + {"ArrayOf", Func, 5}, + {"Bool", Const, 0}, + {"BothDir", Const, 0}, + {"Chan", Const, 0}, + {"ChanDir", Type, 0}, + {"ChanOf", Func, 1}, + {"Complex128", Const, 0}, + {"Complex64", Const, 0}, + {"Copy", Func, 0}, + {"DeepEqual", Func, 0}, + {"Float32", Const, 0}, + {"Float64", Const, 0}, + {"Func", Const, 0}, + {"FuncOf", Func, 5}, + {"Indirect", Func, 0}, + {"Int", Const, 0}, + {"Int16", Const, 0}, + {"Int32", Const, 0}, + {"Int64", Const, 0}, + {"Int8", Const, 0}, + {"Interface", Const, 0}, + {"Invalid", Const, 0}, + {"Kind", Type, 0}, + {"MakeChan", Func, 0}, + {"MakeFunc", Func, 1}, + {"MakeMap", Func, 0}, + {"MakeMapWithSize", Func, 9}, + {"MakeSlice", Func, 0}, + {"Map", Const, 0}, + {"MapIter", Type, 12}, + {"MapOf", Func, 1}, + {"Method", Type, 0}, + {"Method.Func", Field, 0}, + {"Method.Index", Field, 0}, + {"Method.Name", Field, 0}, + {"Method.PkgPath", Field, 0}, + {"Method.Type", Field, 0}, + {"New", Func, 0}, + {"NewAt", Func, 0}, + {"Pointer", Const, 18}, + {"PointerTo", Func, 18}, + {"Ptr", Const, 0}, + {"PtrTo", Func, 0}, + {"RecvDir", Const, 0}, + {"Select", Func, 1}, + {"SelectCase", Type, 1}, + {"SelectCase.Chan", Field, 1}, + {"SelectCase.Dir", Field, 1}, + {"SelectCase.Send", Field, 1}, + {"SelectDefault", Const, 1}, + {"SelectDir", Type, 1}, + {"SelectRecv", Const, 1}, + {"SelectSend", Const, 1}, + {"SendDir", Const, 0}, + {"Slice", Const, 0}, + {"SliceAt", Func, 23}, + {"SliceHeader", Type, 0}, + {"SliceHeader.Cap", Field, 0}, + {"SliceHeader.Data", Field, 0}, + {"SliceHeader.Len", Field, 0}, + {"SliceOf", Func, 1}, + {"String", Const, 0}, + {"StringHeader", Type, 0}, + {"StringHeader.Data", Field, 0}, + {"StringHeader.Len", Field, 0}, + {"Struct", Const, 0}, + {"StructField", Type, 0}, + {"StructField.Anonymous", Field, 0}, + {"StructField.Index", Field, 0}, + {"StructField.Name", Field, 0}, + {"StructField.Offset", Field, 0}, + {"StructField.PkgPath", Field, 0}, + {"StructField.Tag", Field, 0}, + {"StructField.Type", Field, 0}, + {"StructOf", Func, 7}, + {"StructTag", Type, 0}, + {"Swapper", Func, 8}, + {"Type", Type, 0}, + {"TypeFor", Func, 22}, + {"TypeOf", Func, 0}, + {"Uint", Const, 0}, + {"Uint16", Const, 0}, + {"Uint32", Const, 0}, + {"Uint64", Const, 0}, + {"Uint8", Const, 0}, + {"Uintptr", Const, 0}, + {"UnsafePointer", Const, 0}, + {"Value", Type, 0}, + {"ValueError", Type, 0}, + {"ValueError.Kind", Field, 0}, + {"ValueError.Method", Field, 0}, + {"ValueOf", Func, 0}, + {"VisibleFields", Func, 17}, + {"Zero", Func, 0}, + }, + "regexp": { + {"(*Regexp).Copy", Method, 6}, + {"(*Regexp).Expand", Method, 0}, + {"(*Regexp).ExpandString", Method, 0}, + {"(*Regexp).Find", Method, 0}, + {"(*Regexp).FindAll", Method, 0}, + {"(*Regexp).FindAllIndex", Method, 0}, + {"(*Regexp).FindAllString", Method, 0}, + {"(*Regexp).FindAllStringIndex", Method, 0}, + {"(*Regexp).FindAllStringSubmatch", Method, 0}, + {"(*Regexp).FindAllStringSubmatchIndex", Method, 0}, + {"(*Regexp).FindAllSubmatch", Method, 0}, + {"(*Regexp).FindAllSubmatchIndex", Method, 0}, + {"(*Regexp).FindIndex", Method, 0}, + {"(*Regexp).FindReaderIndex", Method, 0}, + {"(*Regexp).FindReaderSubmatchIndex", Method, 0}, + {"(*Regexp).FindString", Method, 0}, + {"(*Regexp).FindStringIndex", Method, 0}, + {"(*Regexp).FindStringSubmatch", Method, 0}, + {"(*Regexp).FindStringSubmatchIndex", Method, 0}, + {"(*Regexp).FindSubmatch", Method, 0}, + {"(*Regexp).FindSubmatchIndex", Method, 0}, + {"(*Regexp).LiteralPrefix", Method, 0}, + {"(*Regexp).Longest", Method, 1}, + {"(*Regexp).MarshalText", Method, 21}, + {"(*Regexp).Match", Method, 0}, + {"(*Regexp).MatchReader", Method, 0}, + {"(*Regexp).MatchString", Method, 0}, + {"(*Regexp).NumSubexp", Method, 0}, + {"(*Regexp).ReplaceAll", Method, 0}, + {"(*Regexp).ReplaceAllFunc", Method, 0}, + {"(*Regexp).ReplaceAllLiteral", Method, 0}, + {"(*Regexp).ReplaceAllLiteralString", Method, 0}, + {"(*Regexp).ReplaceAllString", Method, 0}, + {"(*Regexp).ReplaceAllStringFunc", Method, 0}, + {"(*Regexp).Split", Method, 1}, + {"(*Regexp).String", Method, 0}, + {"(*Regexp).SubexpIndex", Method, 15}, + {"(*Regexp).SubexpNames", Method, 0}, + {"(*Regexp).UnmarshalText", Method, 21}, + {"Compile", Func, 0}, + {"CompilePOSIX", Func, 0}, + {"Match", Func, 0}, + {"MatchReader", Func, 0}, + {"MatchString", Func, 0}, + {"MustCompile", Func, 0}, + {"MustCompilePOSIX", Func, 0}, + {"QuoteMeta", Func, 0}, + {"Regexp", Type, 0}, + }, + "regexp/syntax": { + {"(*Error).Error", Method, 0}, + {"(*Inst).MatchEmptyWidth", Method, 0}, + {"(*Inst).MatchRune", Method, 0}, + {"(*Inst).MatchRunePos", Method, 3}, + {"(*Inst).String", Method, 0}, + {"(*Prog).Prefix", Method, 0}, + {"(*Prog).StartCond", Method, 0}, + {"(*Prog).String", Method, 0}, + {"(*Regexp).CapNames", Method, 0}, + {"(*Regexp).Equal", Method, 0}, + {"(*Regexp).MaxCap", Method, 0}, + {"(*Regexp).Simplify", Method, 0}, + {"(*Regexp).String", Method, 0}, + {"(ErrorCode).String", Method, 0}, + {"(InstOp).String", Method, 3}, + {"(Op).String", Method, 11}, + {"ClassNL", Const, 0}, + {"Compile", Func, 0}, + {"DotNL", Const, 0}, + {"EmptyBeginLine", Const, 0}, + {"EmptyBeginText", Const, 0}, + {"EmptyEndLine", Const, 0}, + {"EmptyEndText", Const, 0}, + {"EmptyNoWordBoundary", Const, 0}, + {"EmptyOp", Type, 0}, + {"EmptyOpContext", Func, 0}, + {"EmptyWordBoundary", Const, 0}, + {"ErrInternalError", Const, 0}, + {"ErrInvalidCharClass", Const, 0}, + {"ErrInvalidCharRange", Const, 0}, + {"ErrInvalidEscape", Const, 0}, + {"ErrInvalidNamedCapture", Const, 0}, + {"ErrInvalidPerlOp", Const, 0}, + {"ErrInvalidRepeatOp", Const, 0}, + {"ErrInvalidRepeatSize", Const, 0}, + {"ErrInvalidUTF8", Const, 0}, + {"ErrLarge", Const, 20}, + {"ErrMissingBracket", Const, 0}, + {"ErrMissingParen", Const, 0}, + {"ErrMissingRepeatArgument", Const, 0}, + {"ErrNestingDepth", Const, 19}, + {"ErrTrailingBackslash", Const, 0}, + {"ErrUnexpectedParen", Const, 1}, + {"Error", Type, 0}, + {"Error.Code", Field, 0}, + {"Error.Expr", Field, 0}, + {"ErrorCode", Type, 0}, + {"Flags", Type, 0}, + {"FoldCase", Const, 0}, + {"Inst", Type, 0}, + {"Inst.Arg", Field, 0}, + {"Inst.Op", Field, 0}, + {"Inst.Out", Field, 0}, + {"Inst.Rune", Field, 0}, + {"InstAlt", Const, 0}, + {"InstAltMatch", Const, 0}, + {"InstCapture", Const, 0}, + {"InstEmptyWidth", Const, 0}, + {"InstFail", Const, 0}, + {"InstMatch", Const, 0}, + {"InstNop", Const, 0}, + {"InstOp", Type, 0}, + {"InstRune", Const, 0}, + {"InstRune1", Const, 0}, + {"InstRuneAny", Const, 0}, + {"InstRuneAnyNotNL", Const, 0}, + {"IsWordChar", Func, 0}, + {"Literal", Const, 0}, + {"MatchNL", Const, 0}, + {"NonGreedy", Const, 0}, + {"OneLine", Const, 0}, + {"Op", Type, 0}, + {"OpAlternate", Const, 0}, + {"OpAnyChar", Const, 0}, + {"OpAnyCharNotNL", Const, 0}, + {"OpBeginLine", Const, 0}, + {"OpBeginText", Const, 0}, + {"OpCapture", Const, 0}, + {"OpCharClass", Const, 0}, + {"OpConcat", Const, 0}, + {"OpEmptyMatch", Const, 0}, + {"OpEndLine", Const, 0}, + {"OpEndText", Const, 0}, + {"OpLiteral", Const, 0}, + {"OpNoMatch", Const, 0}, + {"OpNoWordBoundary", Const, 0}, + {"OpPlus", Const, 0}, + {"OpQuest", Const, 0}, + {"OpRepeat", Const, 0}, + {"OpStar", Const, 0}, + {"OpWordBoundary", Const, 0}, + {"POSIX", Const, 0}, + {"Parse", Func, 0}, + {"Perl", Const, 0}, + {"PerlX", Const, 0}, + {"Prog", Type, 0}, + {"Prog.Inst", Field, 0}, + {"Prog.NumCap", Field, 0}, + {"Prog.Start", Field, 0}, + {"Regexp", Type, 0}, + {"Regexp.Cap", Field, 0}, + {"Regexp.Flags", Field, 0}, + {"Regexp.Max", Field, 0}, + {"Regexp.Min", Field, 0}, + {"Regexp.Name", Field, 0}, + {"Regexp.Op", Field, 0}, + {"Regexp.Rune", Field, 0}, + {"Regexp.Rune0", Field, 0}, + {"Regexp.Sub", Field, 0}, + {"Regexp.Sub0", Field, 0}, + {"Simple", Const, 0}, + {"UnicodeGroups", Const, 0}, + {"WasDollar", Const, 0}, + }, + "runtime": { + {"(*BlockProfileRecord).Stack", Method, 1}, + {"(*Frames).Next", Method, 7}, + {"(*Func).Entry", Method, 0}, + {"(*Func).FileLine", Method, 0}, + {"(*Func).Name", Method, 0}, + {"(*MemProfileRecord).InUseBytes", Method, 0}, + {"(*MemProfileRecord).InUseObjects", Method, 0}, + {"(*MemProfileRecord).Stack", Method, 0}, + {"(*PanicNilError).Error", Method, 21}, + {"(*PanicNilError).RuntimeError", Method, 21}, + {"(*Pinner).Pin", Method, 21}, + {"(*Pinner).Unpin", Method, 21}, + {"(*StackRecord).Stack", Method, 0}, + {"(*TypeAssertionError).Error", Method, 0}, + {"(*TypeAssertionError).RuntimeError", Method, 0}, + {"BlockProfile", Func, 1}, + {"BlockProfileRecord", Type, 1}, + {"BlockProfileRecord.Count", Field, 1}, + {"BlockProfileRecord.Cycles", Field, 1}, + {"BlockProfileRecord.StackRecord", Field, 1}, + {"Breakpoint", Func, 0}, + {"CPUProfile", Func, 0}, + {"Caller", Func, 0}, + {"Callers", Func, 0}, + {"CallersFrames", Func, 7}, + {"Compiler", Const, 0}, + {"Error", Type, 0}, + {"Frame", Type, 7}, + {"Frame.Entry", Field, 7}, + {"Frame.File", Field, 7}, + {"Frame.Func", Field, 7}, + {"Frame.Function", Field, 7}, + {"Frame.Line", Field, 7}, + {"Frame.PC", Field, 7}, + {"Frames", Type, 7}, + {"Func", Type, 0}, + {"FuncForPC", Func, 0}, + {"GC", Func, 0}, + {"GOARCH", Const, 0}, + {"GOMAXPROCS", Func, 0}, + {"GOOS", Const, 0}, + {"GOROOT", Func, 0}, + {"Goexit", Func, 0}, + {"GoroutineProfile", Func, 0}, + {"Gosched", Func, 0}, + {"KeepAlive", Func, 7}, + {"LockOSThread", Func, 0}, + {"MemProfile", Func, 0}, + {"MemProfileRate", Var, 0}, + {"MemProfileRecord", Type, 0}, + {"MemProfileRecord.AllocBytes", Field, 0}, + {"MemProfileRecord.AllocObjects", Field, 0}, + {"MemProfileRecord.FreeBytes", Field, 0}, + {"MemProfileRecord.FreeObjects", Field, 0}, + {"MemProfileRecord.Stack0", Field, 0}, + {"MemStats", Type, 0}, + {"MemStats.Alloc", Field, 0}, + {"MemStats.BuckHashSys", Field, 0}, + {"MemStats.BySize", Field, 0}, + {"MemStats.DebugGC", Field, 0}, + {"MemStats.EnableGC", Field, 0}, + {"MemStats.Frees", Field, 0}, + {"MemStats.GCCPUFraction", Field, 5}, + {"MemStats.GCSys", Field, 2}, + {"MemStats.HeapAlloc", Field, 0}, + {"MemStats.HeapIdle", Field, 0}, + {"MemStats.HeapInuse", Field, 0}, + {"MemStats.HeapObjects", Field, 0}, + {"MemStats.HeapReleased", Field, 0}, + {"MemStats.HeapSys", Field, 0}, + {"MemStats.LastGC", Field, 0}, + {"MemStats.Lookups", Field, 0}, + {"MemStats.MCacheInuse", Field, 0}, + {"MemStats.MCacheSys", Field, 0}, + {"MemStats.MSpanInuse", Field, 0}, + {"MemStats.MSpanSys", Field, 0}, + {"MemStats.Mallocs", Field, 0}, + {"MemStats.NextGC", Field, 0}, + {"MemStats.NumForcedGC", Field, 8}, + {"MemStats.NumGC", Field, 0}, + {"MemStats.OtherSys", Field, 2}, + {"MemStats.PauseEnd", Field, 4}, + {"MemStats.PauseNs", Field, 0}, + {"MemStats.PauseTotalNs", Field, 0}, + {"MemStats.StackInuse", Field, 0}, + {"MemStats.StackSys", Field, 0}, + {"MemStats.Sys", Field, 0}, + {"MemStats.TotalAlloc", Field, 0}, + {"MutexProfile", Func, 8}, + {"NumCPU", Func, 0}, + {"NumCgoCall", Func, 0}, + {"NumGoroutine", Func, 0}, + {"PanicNilError", Type, 21}, + {"Pinner", Type, 21}, + {"ReadMemStats", Func, 0}, + {"ReadTrace", Func, 5}, + {"SetBlockProfileRate", Func, 1}, + {"SetCPUProfileRate", Func, 0}, + {"SetCgoTraceback", Func, 7}, + {"SetFinalizer", Func, 0}, + {"SetMutexProfileFraction", Func, 8}, + {"Stack", Func, 0}, + {"StackRecord", Type, 0}, + {"StackRecord.Stack0", Field, 0}, + {"StartTrace", Func, 5}, + {"StopTrace", Func, 5}, + {"ThreadCreateProfile", Func, 0}, + {"TypeAssertionError", Type, 0}, + {"UnlockOSThread", Func, 0}, + {"Version", Func, 0}, + }, + "runtime/cgo": { + {"(Handle).Delete", Method, 17}, + {"(Handle).Value", Method, 17}, + {"Handle", Type, 17}, + {"Incomplete", Type, 20}, + {"NewHandle", Func, 17}, + }, + "runtime/coverage": { + {"ClearCounters", Func, 20}, + {"WriteCounters", Func, 20}, + {"WriteCountersDir", Func, 20}, + {"WriteMeta", Func, 20}, + {"WriteMetaDir", Func, 20}, + }, + "runtime/debug": { + {"(*BuildInfo).String", Method, 18}, + {"BuildInfo", Type, 12}, + {"BuildInfo.Deps", Field, 12}, + {"BuildInfo.GoVersion", Field, 18}, + {"BuildInfo.Main", Field, 12}, + {"BuildInfo.Path", Field, 12}, + {"BuildInfo.Settings", Field, 18}, + {"BuildSetting", Type, 18}, + {"BuildSetting.Key", Field, 18}, + {"BuildSetting.Value", Field, 18}, + {"CrashOptions", Type, 23}, + {"FreeOSMemory", Func, 1}, + {"GCStats", Type, 1}, + {"GCStats.LastGC", Field, 1}, + {"GCStats.NumGC", Field, 1}, + {"GCStats.Pause", Field, 1}, + {"GCStats.PauseEnd", Field, 4}, + {"GCStats.PauseQuantiles", Field, 1}, + {"GCStats.PauseTotal", Field, 1}, + {"Module", Type, 12}, + {"Module.Path", Field, 12}, + {"Module.Replace", Field, 12}, + {"Module.Sum", Field, 12}, + {"Module.Version", Field, 12}, + {"ParseBuildInfo", Func, 18}, + {"PrintStack", Func, 0}, + {"ReadBuildInfo", Func, 12}, + {"ReadGCStats", Func, 1}, + {"SetCrashOutput", Func, 23}, + {"SetGCPercent", Func, 1}, + {"SetMaxStack", Func, 2}, + {"SetMaxThreads", Func, 2}, + {"SetMemoryLimit", Func, 19}, + {"SetPanicOnFault", Func, 3}, + {"SetTraceback", Func, 6}, + {"Stack", Func, 0}, + {"WriteHeapDump", Func, 3}, + }, + "runtime/metrics": { + {"(Value).Float64", Method, 16}, + {"(Value).Float64Histogram", Method, 16}, + {"(Value).Kind", Method, 16}, + {"(Value).Uint64", Method, 16}, + {"All", Func, 16}, + {"Description", Type, 16}, + {"Description.Cumulative", Field, 16}, + {"Description.Description", Field, 16}, + {"Description.Kind", Field, 16}, + {"Description.Name", Field, 16}, + {"Float64Histogram", Type, 16}, + {"Float64Histogram.Buckets", Field, 16}, + {"Float64Histogram.Counts", Field, 16}, + {"KindBad", Const, 16}, + {"KindFloat64", Const, 16}, + {"KindFloat64Histogram", Const, 16}, + {"KindUint64", Const, 16}, + {"Read", Func, 16}, + {"Sample", Type, 16}, + {"Sample.Name", Field, 16}, + {"Sample.Value", Field, 16}, + {"Value", Type, 16}, + {"ValueKind", Type, 16}, + }, + "runtime/pprof": { + {"(*Profile).Add", Method, 0}, + {"(*Profile).Count", Method, 0}, + {"(*Profile).Name", Method, 0}, + {"(*Profile).Remove", Method, 0}, + {"(*Profile).WriteTo", Method, 0}, + {"Do", Func, 9}, + {"ForLabels", Func, 9}, + {"Label", Func, 9}, + {"LabelSet", Type, 9}, + {"Labels", Func, 9}, + {"Lookup", Func, 0}, + {"NewProfile", Func, 0}, + {"Profile", Type, 0}, + {"Profiles", Func, 0}, + {"SetGoroutineLabels", Func, 9}, + {"StartCPUProfile", Func, 0}, + {"StopCPUProfile", Func, 0}, + {"WithLabels", Func, 9}, + {"WriteHeapProfile", Func, 0}, + }, + "runtime/trace": { + {"(*Region).End", Method, 11}, + {"(*Task).End", Method, 11}, + {"IsEnabled", Func, 11}, + {"Log", Func, 11}, + {"Logf", Func, 11}, + {"NewTask", Func, 11}, + {"Region", Type, 11}, + {"Start", Func, 5}, + {"StartRegion", Func, 11}, + {"Stop", Func, 5}, + {"Task", Type, 11}, + {"WithRegion", Func, 11}, + }, + "slices": { + {"All", Func, 23}, + {"AppendSeq", Func, 23}, + {"Backward", Func, 23}, + {"BinarySearch", Func, 21}, + {"BinarySearchFunc", Func, 21}, + {"Chunk", Func, 23}, + {"Clip", Func, 21}, + {"Clone", Func, 21}, + {"Collect", Func, 23}, + {"Compact", Func, 21}, + {"CompactFunc", Func, 21}, + {"Compare", Func, 21}, + {"CompareFunc", Func, 21}, + {"Concat", Func, 22}, + {"Contains", Func, 21}, + {"ContainsFunc", Func, 21}, + {"Delete", Func, 21}, + {"DeleteFunc", Func, 21}, + {"Equal", Func, 21}, + {"EqualFunc", Func, 21}, + {"Grow", Func, 21}, + {"Index", Func, 21}, + {"IndexFunc", Func, 21}, + {"Insert", Func, 21}, + {"IsSorted", Func, 21}, + {"IsSortedFunc", Func, 21}, + {"Max", Func, 21}, + {"MaxFunc", Func, 21}, + {"Min", Func, 21}, + {"MinFunc", Func, 21}, + {"Repeat", Func, 23}, + {"Replace", Func, 21}, + {"Reverse", Func, 21}, + {"Sort", Func, 21}, + {"SortFunc", Func, 21}, + {"SortStableFunc", Func, 21}, + {"Sorted", Func, 23}, + {"SortedFunc", Func, 23}, + {"SortedStableFunc", Func, 23}, + {"Values", Func, 23}, + }, + "sort": { + {"(Float64Slice).Len", Method, 0}, + {"(Float64Slice).Less", Method, 0}, + {"(Float64Slice).Search", Method, 0}, + {"(Float64Slice).Sort", Method, 0}, + {"(Float64Slice).Swap", Method, 0}, + {"(IntSlice).Len", Method, 0}, + {"(IntSlice).Less", Method, 0}, + {"(IntSlice).Search", Method, 0}, + {"(IntSlice).Sort", Method, 0}, + {"(IntSlice).Swap", Method, 0}, + {"(StringSlice).Len", Method, 0}, + {"(StringSlice).Less", Method, 0}, + {"(StringSlice).Search", Method, 0}, + {"(StringSlice).Sort", Method, 0}, + {"(StringSlice).Swap", Method, 0}, + {"Find", Func, 19}, + {"Float64Slice", Type, 0}, + {"Float64s", Func, 0}, + {"Float64sAreSorted", Func, 0}, + {"IntSlice", Type, 0}, + {"Interface", Type, 0}, + {"Ints", Func, 0}, + {"IntsAreSorted", Func, 0}, + {"IsSorted", Func, 0}, + {"Reverse", Func, 1}, + {"Search", Func, 0}, + {"SearchFloat64s", Func, 0}, + {"SearchInts", Func, 0}, + {"SearchStrings", Func, 0}, + {"Slice", Func, 8}, + {"SliceIsSorted", Func, 8}, + {"SliceStable", Func, 8}, + {"Sort", Func, 0}, + {"Stable", Func, 2}, + {"StringSlice", Type, 0}, + {"Strings", Func, 0}, + {"StringsAreSorted", Func, 0}, + }, + "strconv": { + {"(*NumError).Error", Method, 0}, + {"(*NumError).Unwrap", Method, 14}, + {"AppendBool", Func, 0}, + {"AppendFloat", Func, 0}, + {"AppendInt", Func, 0}, + {"AppendQuote", Func, 0}, + {"AppendQuoteRune", Func, 0}, + {"AppendQuoteRuneToASCII", Func, 0}, + {"AppendQuoteRuneToGraphic", Func, 6}, + {"AppendQuoteToASCII", Func, 0}, + {"AppendQuoteToGraphic", Func, 6}, + {"AppendUint", Func, 0}, + {"Atoi", Func, 0}, + {"CanBackquote", Func, 0}, + {"ErrRange", Var, 0}, + {"ErrSyntax", Var, 0}, + {"FormatBool", Func, 0}, + {"FormatComplex", Func, 15}, + {"FormatFloat", Func, 0}, + {"FormatInt", Func, 0}, + {"FormatUint", Func, 0}, + {"IntSize", Const, 0}, + {"IsGraphic", Func, 6}, + {"IsPrint", Func, 0}, + {"Itoa", Func, 0}, + {"NumError", Type, 0}, + {"NumError.Err", Field, 0}, + {"NumError.Func", Field, 0}, + {"NumError.Num", Field, 0}, + {"ParseBool", Func, 0}, + {"ParseComplex", Func, 15}, + {"ParseFloat", Func, 0}, + {"ParseInt", Func, 0}, + {"ParseUint", Func, 0}, + {"Quote", Func, 0}, + {"QuoteRune", Func, 0}, + {"QuoteRuneToASCII", Func, 0}, + {"QuoteRuneToGraphic", Func, 6}, + {"QuoteToASCII", Func, 0}, + {"QuoteToGraphic", Func, 6}, + {"QuotedPrefix", Func, 17}, + {"Unquote", Func, 0}, + {"UnquoteChar", Func, 0}, + }, + "strings": { + {"(*Builder).Cap", Method, 12}, + {"(*Builder).Grow", Method, 10}, + {"(*Builder).Len", Method, 10}, + {"(*Builder).Reset", Method, 10}, + {"(*Builder).String", Method, 10}, + {"(*Builder).Write", Method, 10}, + {"(*Builder).WriteByte", Method, 10}, + {"(*Builder).WriteRune", Method, 10}, + {"(*Builder).WriteString", Method, 10}, + {"(*Reader).Len", Method, 0}, + {"(*Reader).Read", Method, 0}, + {"(*Reader).ReadAt", Method, 0}, + {"(*Reader).ReadByte", Method, 0}, + {"(*Reader).ReadRune", Method, 0}, + {"(*Reader).Reset", Method, 7}, + {"(*Reader).Seek", Method, 0}, + {"(*Reader).Size", Method, 5}, + {"(*Reader).UnreadByte", Method, 0}, + {"(*Reader).UnreadRune", Method, 0}, + {"(*Reader).WriteTo", Method, 1}, + {"(*Replacer).Replace", Method, 0}, + {"(*Replacer).WriteString", Method, 0}, + {"Builder", Type, 10}, + {"Clone", Func, 18}, + {"Compare", Func, 5}, + {"Contains", Func, 0}, + {"ContainsAny", Func, 0}, + {"ContainsFunc", Func, 21}, + {"ContainsRune", Func, 0}, + {"Count", Func, 0}, + {"Cut", Func, 18}, + {"CutPrefix", Func, 20}, + {"CutSuffix", Func, 20}, + {"EqualFold", Func, 0}, + {"Fields", Func, 0}, + {"FieldsFunc", Func, 0}, + {"HasPrefix", Func, 0}, + {"HasSuffix", Func, 0}, + {"Index", Func, 0}, + {"IndexAny", Func, 0}, + {"IndexByte", Func, 2}, + {"IndexFunc", Func, 0}, + {"IndexRune", Func, 0}, + {"Join", Func, 0}, + {"LastIndex", Func, 0}, + {"LastIndexAny", Func, 0}, + {"LastIndexByte", Func, 5}, + {"LastIndexFunc", Func, 0}, + {"Map", Func, 0}, + {"NewReader", Func, 0}, + {"NewReplacer", Func, 0}, + {"Reader", Type, 0}, + {"Repeat", Func, 0}, + {"Replace", Func, 0}, + {"ReplaceAll", Func, 12}, + {"Replacer", Type, 0}, + {"Split", Func, 0}, + {"SplitAfter", Func, 0}, + {"SplitAfterN", Func, 0}, + {"SplitN", Func, 0}, + {"Title", Func, 0}, + {"ToLower", Func, 0}, + {"ToLowerSpecial", Func, 0}, + {"ToTitle", Func, 0}, + {"ToTitleSpecial", Func, 0}, + {"ToUpper", Func, 0}, + {"ToUpperSpecial", Func, 0}, + {"ToValidUTF8", Func, 13}, + {"Trim", Func, 0}, + {"TrimFunc", Func, 0}, + {"TrimLeft", Func, 0}, + {"TrimLeftFunc", Func, 0}, + {"TrimPrefix", Func, 1}, + {"TrimRight", Func, 0}, + {"TrimRightFunc", Func, 0}, + {"TrimSpace", Func, 0}, + {"TrimSuffix", Func, 1}, + }, + "structs": { + {"HostLayout", Type, 23}, + }, + "sync": { + {"(*Cond).Broadcast", Method, 0}, + {"(*Cond).Signal", Method, 0}, + {"(*Cond).Wait", Method, 0}, + {"(*Map).Clear", Method, 23}, + {"(*Map).CompareAndDelete", Method, 20}, + {"(*Map).CompareAndSwap", Method, 20}, + {"(*Map).Delete", Method, 9}, + {"(*Map).Load", Method, 9}, + {"(*Map).LoadAndDelete", Method, 15}, + {"(*Map).LoadOrStore", Method, 9}, + {"(*Map).Range", Method, 9}, + {"(*Map).Store", Method, 9}, + {"(*Map).Swap", Method, 20}, + {"(*Mutex).Lock", Method, 0}, + {"(*Mutex).TryLock", Method, 18}, + {"(*Mutex).Unlock", Method, 0}, + {"(*Once).Do", Method, 0}, + {"(*Pool).Get", Method, 3}, + {"(*Pool).Put", Method, 3}, + {"(*RWMutex).Lock", Method, 0}, + {"(*RWMutex).RLock", Method, 0}, + {"(*RWMutex).RLocker", Method, 0}, + {"(*RWMutex).RUnlock", Method, 0}, + {"(*RWMutex).TryLock", Method, 18}, + {"(*RWMutex).TryRLock", Method, 18}, + {"(*RWMutex).Unlock", Method, 0}, + {"(*WaitGroup).Add", Method, 0}, + {"(*WaitGroup).Done", Method, 0}, + {"(*WaitGroup).Wait", Method, 0}, + {"Cond", Type, 0}, + {"Cond.L", Field, 0}, + {"Locker", Type, 0}, + {"Map", Type, 9}, + {"Mutex", Type, 0}, + {"NewCond", Func, 0}, + {"Once", Type, 0}, + {"OnceFunc", Func, 21}, + {"OnceValue", Func, 21}, + {"OnceValues", Func, 21}, + {"Pool", Type, 3}, + {"Pool.New", Field, 3}, + {"RWMutex", Type, 0}, + {"WaitGroup", Type, 0}, + }, + "sync/atomic": { + {"(*Bool).CompareAndSwap", Method, 19}, + {"(*Bool).Load", Method, 19}, + {"(*Bool).Store", Method, 19}, + {"(*Bool).Swap", Method, 19}, + {"(*Int32).Add", Method, 19}, + {"(*Int32).And", Method, 23}, + {"(*Int32).CompareAndSwap", Method, 19}, + {"(*Int32).Load", Method, 19}, + {"(*Int32).Or", Method, 23}, + {"(*Int32).Store", Method, 19}, + {"(*Int32).Swap", Method, 19}, + {"(*Int64).Add", Method, 19}, + {"(*Int64).And", Method, 23}, + {"(*Int64).CompareAndSwap", Method, 19}, + {"(*Int64).Load", Method, 19}, + {"(*Int64).Or", Method, 23}, + {"(*Int64).Store", Method, 19}, + {"(*Int64).Swap", Method, 19}, + {"(*Pointer).CompareAndSwap", Method, 19}, + {"(*Pointer).Load", Method, 19}, + {"(*Pointer).Store", Method, 19}, + {"(*Pointer).Swap", Method, 19}, + {"(*Uint32).Add", Method, 19}, + {"(*Uint32).And", Method, 23}, + {"(*Uint32).CompareAndSwap", Method, 19}, + {"(*Uint32).Load", Method, 19}, + {"(*Uint32).Or", Method, 23}, + {"(*Uint32).Store", Method, 19}, + {"(*Uint32).Swap", Method, 19}, + {"(*Uint64).Add", Method, 19}, + {"(*Uint64).And", Method, 23}, + {"(*Uint64).CompareAndSwap", Method, 19}, + {"(*Uint64).Load", Method, 19}, + {"(*Uint64).Or", Method, 23}, + {"(*Uint64).Store", Method, 19}, + {"(*Uint64).Swap", Method, 19}, + {"(*Uintptr).Add", Method, 19}, + {"(*Uintptr).And", Method, 23}, + {"(*Uintptr).CompareAndSwap", Method, 19}, + {"(*Uintptr).Load", Method, 19}, + {"(*Uintptr).Or", Method, 23}, + {"(*Uintptr).Store", Method, 19}, + {"(*Uintptr).Swap", Method, 19}, + {"(*Value).CompareAndSwap", Method, 17}, + {"(*Value).Load", Method, 4}, + {"(*Value).Store", Method, 4}, + {"(*Value).Swap", Method, 17}, + {"AddInt32", Func, 0}, + {"AddInt64", Func, 0}, + {"AddUint32", Func, 0}, + {"AddUint64", Func, 0}, + {"AddUintptr", Func, 0}, + {"AndInt32", Func, 23}, + {"AndInt64", Func, 23}, + {"AndUint32", Func, 23}, + {"AndUint64", Func, 23}, + {"AndUintptr", Func, 23}, + {"Bool", Type, 19}, + {"CompareAndSwapInt32", Func, 0}, + {"CompareAndSwapInt64", Func, 0}, + {"CompareAndSwapPointer", Func, 0}, + {"CompareAndSwapUint32", Func, 0}, + {"CompareAndSwapUint64", Func, 0}, + {"CompareAndSwapUintptr", Func, 0}, + {"Int32", Type, 19}, + {"Int64", Type, 19}, + {"LoadInt32", Func, 0}, + {"LoadInt64", Func, 0}, + {"LoadPointer", Func, 0}, + {"LoadUint32", Func, 0}, + {"LoadUint64", Func, 0}, + {"LoadUintptr", Func, 0}, + {"OrInt32", Func, 23}, + {"OrInt64", Func, 23}, + {"OrUint32", Func, 23}, + {"OrUint64", Func, 23}, + {"OrUintptr", Func, 23}, + {"Pointer", Type, 19}, + {"StoreInt32", Func, 0}, + {"StoreInt64", Func, 0}, + {"StorePointer", Func, 0}, + {"StoreUint32", Func, 0}, + {"StoreUint64", Func, 0}, + {"StoreUintptr", Func, 0}, + {"SwapInt32", Func, 2}, + {"SwapInt64", Func, 2}, + {"SwapPointer", Func, 2}, + {"SwapUint32", Func, 2}, + {"SwapUint64", Func, 2}, + {"SwapUintptr", Func, 2}, + {"Uint32", Type, 19}, + {"Uint64", Type, 19}, + {"Uintptr", Type, 19}, + {"Value", Type, 4}, + }, + "syscall": { + {"(*Cmsghdr).SetLen", Method, 0}, + {"(*DLL).FindProc", Method, 0}, + {"(*DLL).MustFindProc", Method, 0}, + {"(*DLL).Release", Method, 0}, + {"(*DLLError).Error", Method, 0}, + {"(*DLLError).Unwrap", Method, 16}, + {"(*Filetime).Nanoseconds", Method, 0}, + {"(*Iovec).SetLen", Method, 0}, + {"(*LazyDLL).Handle", Method, 0}, + {"(*LazyDLL).Load", Method, 0}, + {"(*LazyDLL).NewProc", Method, 0}, + {"(*LazyProc).Addr", Method, 0}, + {"(*LazyProc).Call", Method, 0}, + {"(*LazyProc).Find", Method, 0}, + {"(*Msghdr).SetControllen", Method, 0}, + {"(*Proc).Addr", Method, 0}, + {"(*Proc).Call", Method, 0}, + {"(*PtraceRegs).PC", Method, 0}, + {"(*PtraceRegs).SetPC", Method, 0}, + {"(*RawSockaddrAny).Sockaddr", Method, 0}, + {"(*SID).Copy", Method, 0}, + {"(*SID).Len", Method, 0}, + {"(*SID).LookupAccount", Method, 0}, + {"(*SID).String", Method, 0}, + {"(*Timespec).Nano", Method, 0}, + {"(*Timespec).Unix", Method, 0}, + {"(*Timeval).Nano", Method, 0}, + {"(*Timeval).Nanoseconds", Method, 0}, + {"(*Timeval).Unix", Method, 0}, + {"(Errno).Error", Method, 0}, + {"(Errno).Is", Method, 13}, + {"(Errno).Temporary", Method, 0}, + {"(Errno).Timeout", Method, 0}, + {"(Signal).Signal", Method, 0}, + {"(Signal).String", Method, 0}, + {"(Token).Close", Method, 0}, + {"(Token).GetTokenPrimaryGroup", Method, 0}, + {"(Token).GetTokenUser", Method, 0}, + {"(Token).GetUserProfileDirectory", Method, 0}, + {"(WaitStatus).Continued", Method, 0}, + {"(WaitStatus).CoreDump", Method, 0}, + {"(WaitStatus).ExitStatus", Method, 0}, + {"(WaitStatus).Exited", Method, 0}, + {"(WaitStatus).Signal", Method, 0}, + {"(WaitStatus).Signaled", Method, 0}, + {"(WaitStatus).StopSignal", Method, 0}, + {"(WaitStatus).Stopped", Method, 0}, + {"(WaitStatus).TrapCause", Method, 0}, + {"AF_ALG", Const, 0}, + {"AF_APPLETALK", Const, 0}, + {"AF_ARP", Const, 0}, + {"AF_ASH", Const, 0}, + {"AF_ATM", Const, 0}, + {"AF_ATMPVC", Const, 0}, + {"AF_ATMSVC", Const, 0}, + {"AF_AX25", Const, 0}, + {"AF_BLUETOOTH", Const, 0}, + {"AF_BRIDGE", Const, 0}, + {"AF_CAIF", Const, 0}, + {"AF_CAN", Const, 0}, + {"AF_CCITT", Const, 0}, + {"AF_CHAOS", Const, 0}, + {"AF_CNT", Const, 0}, + {"AF_COIP", Const, 0}, + {"AF_DATAKIT", Const, 0}, + {"AF_DECnet", Const, 0}, + {"AF_DLI", Const, 0}, + {"AF_E164", Const, 0}, + {"AF_ECMA", Const, 0}, + {"AF_ECONET", Const, 0}, + {"AF_ENCAP", Const, 1}, + {"AF_FILE", Const, 0}, + {"AF_HYLINK", Const, 0}, + {"AF_IEEE80211", Const, 0}, + {"AF_IEEE802154", Const, 0}, + {"AF_IMPLINK", Const, 0}, + {"AF_INET", Const, 0}, + {"AF_INET6", Const, 0}, + {"AF_INET6_SDP", Const, 3}, + {"AF_INET_SDP", Const, 3}, + {"AF_IPX", Const, 0}, + {"AF_IRDA", Const, 0}, + {"AF_ISDN", Const, 0}, + {"AF_ISO", Const, 0}, + {"AF_IUCV", Const, 0}, + {"AF_KEY", Const, 0}, + {"AF_LAT", Const, 0}, + {"AF_LINK", Const, 0}, + {"AF_LLC", Const, 0}, + {"AF_LOCAL", Const, 0}, + {"AF_MAX", Const, 0}, + {"AF_MPLS", Const, 1}, + {"AF_NATM", Const, 0}, + {"AF_NDRV", Const, 0}, + {"AF_NETBEUI", Const, 0}, + {"AF_NETBIOS", Const, 0}, + {"AF_NETGRAPH", Const, 0}, + {"AF_NETLINK", Const, 0}, + {"AF_NETROM", Const, 0}, + {"AF_NS", Const, 0}, + {"AF_OROUTE", Const, 1}, + {"AF_OSI", Const, 0}, + {"AF_PACKET", Const, 0}, + {"AF_PHONET", Const, 0}, + {"AF_PPP", Const, 0}, + {"AF_PPPOX", Const, 0}, + {"AF_PUP", Const, 0}, + {"AF_RDS", Const, 0}, + {"AF_RESERVED_36", Const, 0}, + {"AF_ROSE", Const, 0}, + {"AF_ROUTE", Const, 0}, + {"AF_RXRPC", Const, 0}, + {"AF_SCLUSTER", Const, 0}, + {"AF_SECURITY", Const, 0}, + {"AF_SIP", Const, 0}, + {"AF_SLOW", Const, 0}, + {"AF_SNA", Const, 0}, + {"AF_SYSTEM", Const, 0}, + {"AF_TIPC", Const, 0}, + {"AF_UNIX", Const, 0}, + {"AF_UNSPEC", Const, 0}, + {"AF_UTUN", Const, 16}, + {"AF_VENDOR00", Const, 0}, + {"AF_VENDOR01", Const, 0}, + {"AF_VENDOR02", Const, 0}, + {"AF_VENDOR03", Const, 0}, + {"AF_VENDOR04", Const, 0}, + {"AF_VENDOR05", Const, 0}, + {"AF_VENDOR06", Const, 0}, + {"AF_VENDOR07", Const, 0}, + {"AF_VENDOR08", Const, 0}, + {"AF_VENDOR09", Const, 0}, + {"AF_VENDOR10", Const, 0}, + {"AF_VENDOR11", Const, 0}, + {"AF_VENDOR12", Const, 0}, + {"AF_VENDOR13", Const, 0}, + {"AF_VENDOR14", Const, 0}, + {"AF_VENDOR15", Const, 0}, + {"AF_VENDOR16", Const, 0}, + {"AF_VENDOR17", Const, 0}, + {"AF_VENDOR18", Const, 0}, + {"AF_VENDOR19", Const, 0}, + {"AF_VENDOR20", Const, 0}, + {"AF_VENDOR21", Const, 0}, + {"AF_VENDOR22", Const, 0}, + {"AF_VENDOR23", Const, 0}, + {"AF_VENDOR24", Const, 0}, + {"AF_VENDOR25", Const, 0}, + {"AF_VENDOR26", Const, 0}, + {"AF_VENDOR27", Const, 0}, + {"AF_VENDOR28", Const, 0}, + {"AF_VENDOR29", Const, 0}, + {"AF_VENDOR30", Const, 0}, + {"AF_VENDOR31", Const, 0}, + {"AF_VENDOR32", Const, 0}, + {"AF_VENDOR33", Const, 0}, + {"AF_VENDOR34", Const, 0}, + {"AF_VENDOR35", Const, 0}, + {"AF_VENDOR36", Const, 0}, + {"AF_VENDOR37", Const, 0}, + {"AF_VENDOR38", Const, 0}, + {"AF_VENDOR39", Const, 0}, + {"AF_VENDOR40", Const, 0}, + {"AF_VENDOR41", Const, 0}, + {"AF_VENDOR42", Const, 0}, + {"AF_VENDOR43", Const, 0}, + {"AF_VENDOR44", Const, 0}, + {"AF_VENDOR45", Const, 0}, + {"AF_VENDOR46", Const, 0}, + {"AF_VENDOR47", Const, 0}, + {"AF_WANPIPE", Const, 0}, + {"AF_X25", Const, 0}, + {"AI_CANONNAME", Const, 1}, + {"AI_NUMERICHOST", Const, 1}, + {"AI_PASSIVE", Const, 1}, + {"APPLICATION_ERROR", Const, 0}, + {"ARPHRD_ADAPT", Const, 0}, + {"ARPHRD_APPLETLK", Const, 0}, + {"ARPHRD_ARCNET", Const, 0}, + {"ARPHRD_ASH", Const, 0}, + {"ARPHRD_ATM", Const, 0}, + {"ARPHRD_AX25", Const, 0}, + {"ARPHRD_BIF", Const, 0}, + {"ARPHRD_CHAOS", Const, 0}, + {"ARPHRD_CISCO", Const, 0}, + {"ARPHRD_CSLIP", Const, 0}, + {"ARPHRD_CSLIP6", Const, 0}, + {"ARPHRD_DDCMP", Const, 0}, + {"ARPHRD_DLCI", Const, 0}, + {"ARPHRD_ECONET", Const, 0}, + {"ARPHRD_EETHER", Const, 0}, + {"ARPHRD_ETHER", Const, 0}, + {"ARPHRD_EUI64", Const, 0}, + {"ARPHRD_FCAL", Const, 0}, + {"ARPHRD_FCFABRIC", Const, 0}, + {"ARPHRD_FCPL", Const, 0}, + {"ARPHRD_FCPP", Const, 0}, + {"ARPHRD_FDDI", Const, 0}, + {"ARPHRD_FRAD", Const, 0}, + {"ARPHRD_FRELAY", Const, 1}, + {"ARPHRD_HDLC", Const, 0}, + {"ARPHRD_HIPPI", Const, 0}, + {"ARPHRD_HWX25", Const, 0}, + {"ARPHRD_IEEE1394", Const, 0}, + {"ARPHRD_IEEE802", Const, 0}, + {"ARPHRD_IEEE80211", Const, 0}, + {"ARPHRD_IEEE80211_PRISM", Const, 0}, + {"ARPHRD_IEEE80211_RADIOTAP", Const, 0}, + {"ARPHRD_IEEE802154", Const, 0}, + {"ARPHRD_IEEE802154_PHY", Const, 0}, + {"ARPHRD_IEEE802_TR", Const, 0}, + {"ARPHRD_INFINIBAND", Const, 0}, + {"ARPHRD_IPDDP", Const, 0}, + {"ARPHRD_IPGRE", Const, 0}, + {"ARPHRD_IRDA", Const, 0}, + {"ARPHRD_LAPB", Const, 0}, + {"ARPHRD_LOCALTLK", Const, 0}, + {"ARPHRD_LOOPBACK", Const, 0}, + {"ARPHRD_METRICOM", Const, 0}, + {"ARPHRD_NETROM", Const, 0}, + {"ARPHRD_NONE", Const, 0}, + {"ARPHRD_PIMREG", Const, 0}, + {"ARPHRD_PPP", Const, 0}, + {"ARPHRD_PRONET", Const, 0}, + {"ARPHRD_RAWHDLC", Const, 0}, + {"ARPHRD_ROSE", Const, 0}, + {"ARPHRD_RSRVD", Const, 0}, + {"ARPHRD_SIT", Const, 0}, + {"ARPHRD_SKIP", Const, 0}, + {"ARPHRD_SLIP", Const, 0}, + {"ARPHRD_SLIP6", Const, 0}, + {"ARPHRD_STRIP", Const, 1}, + {"ARPHRD_TUNNEL", Const, 0}, + {"ARPHRD_TUNNEL6", Const, 0}, + {"ARPHRD_VOID", Const, 0}, + {"ARPHRD_X25", Const, 0}, + {"AUTHTYPE_CLIENT", Const, 0}, + {"AUTHTYPE_SERVER", Const, 0}, + {"Accept", Func, 0}, + {"Accept4", Func, 1}, + {"AcceptEx", Func, 0}, + {"Access", Func, 0}, + {"Acct", Func, 0}, + {"AddrinfoW", Type, 1}, + {"AddrinfoW.Addr", Field, 1}, + {"AddrinfoW.Addrlen", Field, 1}, + {"AddrinfoW.Canonname", Field, 1}, + {"AddrinfoW.Family", Field, 1}, + {"AddrinfoW.Flags", Field, 1}, + {"AddrinfoW.Next", Field, 1}, + {"AddrinfoW.Protocol", Field, 1}, + {"AddrinfoW.Socktype", Field, 1}, + {"Adjtime", Func, 0}, + {"Adjtimex", Func, 0}, + {"AllThreadsSyscall", Func, 16}, + {"AllThreadsSyscall6", Func, 16}, + {"AttachLsf", Func, 0}, + {"B0", Const, 0}, + {"B1000000", Const, 0}, + {"B110", Const, 0}, + {"B115200", Const, 0}, + {"B1152000", Const, 0}, + {"B1200", Const, 0}, + {"B134", Const, 0}, + {"B14400", Const, 1}, + {"B150", Const, 0}, + {"B1500000", Const, 0}, + {"B1800", Const, 0}, + {"B19200", Const, 0}, + {"B200", Const, 0}, + {"B2000000", Const, 0}, + {"B230400", Const, 0}, + {"B2400", Const, 0}, + {"B2500000", Const, 0}, + {"B28800", Const, 1}, + {"B300", Const, 0}, + {"B3000000", Const, 0}, + {"B3500000", Const, 0}, + {"B38400", Const, 0}, + {"B4000000", Const, 0}, + {"B460800", Const, 0}, + {"B4800", Const, 0}, + {"B50", Const, 0}, + {"B500000", Const, 0}, + {"B57600", Const, 0}, + {"B576000", Const, 0}, + {"B600", Const, 0}, + {"B7200", Const, 1}, + {"B75", Const, 0}, + {"B76800", Const, 1}, + {"B921600", Const, 0}, + {"B9600", Const, 0}, + {"BASE_PROTOCOL", Const, 2}, + {"BIOCFEEDBACK", Const, 0}, + {"BIOCFLUSH", Const, 0}, + {"BIOCGBLEN", Const, 0}, + {"BIOCGDIRECTION", Const, 0}, + {"BIOCGDIRFILT", Const, 1}, + {"BIOCGDLT", Const, 0}, + {"BIOCGDLTLIST", Const, 0}, + {"BIOCGETBUFMODE", Const, 0}, + {"BIOCGETIF", Const, 0}, + {"BIOCGETZMAX", Const, 0}, + {"BIOCGFEEDBACK", Const, 1}, + {"BIOCGFILDROP", Const, 1}, + {"BIOCGHDRCMPLT", Const, 0}, + {"BIOCGRSIG", Const, 0}, + {"BIOCGRTIMEOUT", Const, 0}, + {"BIOCGSEESENT", Const, 0}, + {"BIOCGSTATS", Const, 0}, + {"BIOCGSTATSOLD", Const, 1}, + {"BIOCGTSTAMP", Const, 1}, + {"BIOCIMMEDIATE", Const, 0}, + {"BIOCLOCK", Const, 0}, + {"BIOCPROMISC", Const, 0}, + {"BIOCROTZBUF", Const, 0}, + {"BIOCSBLEN", Const, 0}, + {"BIOCSDIRECTION", Const, 0}, + {"BIOCSDIRFILT", Const, 1}, + {"BIOCSDLT", Const, 0}, + {"BIOCSETBUFMODE", Const, 0}, + {"BIOCSETF", Const, 0}, + {"BIOCSETFNR", Const, 0}, + {"BIOCSETIF", Const, 0}, + {"BIOCSETWF", Const, 0}, + {"BIOCSETZBUF", Const, 0}, + {"BIOCSFEEDBACK", Const, 1}, + {"BIOCSFILDROP", Const, 1}, + {"BIOCSHDRCMPLT", Const, 0}, + {"BIOCSRSIG", Const, 0}, + {"BIOCSRTIMEOUT", Const, 0}, + {"BIOCSSEESENT", Const, 0}, + {"BIOCSTCPF", Const, 1}, + {"BIOCSTSTAMP", Const, 1}, + {"BIOCSUDPF", Const, 1}, + {"BIOCVERSION", Const, 0}, + {"BPF_A", Const, 0}, + {"BPF_ABS", Const, 0}, + {"BPF_ADD", Const, 0}, + {"BPF_ALIGNMENT", Const, 0}, + {"BPF_ALIGNMENT32", Const, 1}, + {"BPF_ALU", Const, 0}, + {"BPF_AND", Const, 0}, + {"BPF_B", Const, 0}, + {"BPF_BUFMODE_BUFFER", Const, 0}, + {"BPF_BUFMODE_ZBUF", Const, 0}, + {"BPF_DFLTBUFSIZE", Const, 1}, + {"BPF_DIRECTION_IN", Const, 1}, + {"BPF_DIRECTION_OUT", Const, 1}, + {"BPF_DIV", Const, 0}, + {"BPF_H", Const, 0}, + {"BPF_IMM", Const, 0}, + {"BPF_IND", Const, 0}, + {"BPF_JA", Const, 0}, + {"BPF_JEQ", Const, 0}, + {"BPF_JGE", Const, 0}, + {"BPF_JGT", Const, 0}, + {"BPF_JMP", Const, 0}, + {"BPF_JSET", Const, 0}, + {"BPF_K", Const, 0}, + {"BPF_LD", Const, 0}, + {"BPF_LDX", Const, 0}, + {"BPF_LEN", Const, 0}, + {"BPF_LSH", Const, 0}, + {"BPF_MAJOR_VERSION", Const, 0}, + {"BPF_MAXBUFSIZE", Const, 0}, + {"BPF_MAXINSNS", Const, 0}, + {"BPF_MEM", Const, 0}, + {"BPF_MEMWORDS", Const, 0}, + {"BPF_MINBUFSIZE", Const, 0}, + {"BPF_MINOR_VERSION", Const, 0}, + {"BPF_MISC", Const, 0}, + {"BPF_MSH", Const, 0}, + {"BPF_MUL", Const, 0}, + {"BPF_NEG", Const, 0}, + {"BPF_OR", Const, 0}, + {"BPF_RELEASE", Const, 0}, + {"BPF_RET", Const, 0}, + {"BPF_RSH", Const, 0}, + {"BPF_ST", Const, 0}, + {"BPF_STX", Const, 0}, + {"BPF_SUB", Const, 0}, + {"BPF_TAX", Const, 0}, + {"BPF_TXA", Const, 0}, + {"BPF_T_BINTIME", Const, 1}, + {"BPF_T_BINTIME_FAST", Const, 1}, + {"BPF_T_BINTIME_MONOTONIC", Const, 1}, + {"BPF_T_BINTIME_MONOTONIC_FAST", Const, 1}, + {"BPF_T_FAST", Const, 1}, + {"BPF_T_FLAG_MASK", Const, 1}, + {"BPF_T_FORMAT_MASK", Const, 1}, + {"BPF_T_MICROTIME", Const, 1}, + {"BPF_T_MICROTIME_FAST", Const, 1}, + {"BPF_T_MICROTIME_MONOTONIC", Const, 1}, + {"BPF_T_MICROTIME_MONOTONIC_FAST", Const, 1}, + {"BPF_T_MONOTONIC", Const, 1}, + {"BPF_T_MONOTONIC_FAST", Const, 1}, + {"BPF_T_NANOTIME", Const, 1}, + {"BPF_T_NANOTIME_FAST", Const, 1}, + {"BPF_T_NANOTIME_MONOTONIC", Const, 1}, + {"BPF_T_NANOTIME_MONOTONIC_FAST", Const, 1}, + {"BPF_T_NONE", Const, 1}, + {"BPF_T_NORMAL", Const, 1}, + {"BPF_W", Const, 0}, + {"BPF_X", Const, 0}, + {"BRKINT", Const, 0}, + {"Bind", Func, 0}, + {"BindToDevice", Func, 0}, + {"BpfBuflen", Func, 0}, + {"BpfDatalink", Func, 0}, + {"BpfHdr", Type, 0}, + {"BpfHdr.Caplen", Field, 0}, + {"BpfHdr.Datalen", Field, 0}, + {"BpfHdr.Hdrlen", Field, 0}, + {"BpfHdr.Pad_cgo_0", Field, 0}, + {"BpfHdr.Tstamp", Field, 0}, + {"BpfHeadercmpl", Func, 0}, + {"BpfInsn", Type, 0}, + {"BpfInsn.Code", Field, 0}, + {"BpfInsn.Jf", Field, 0}, + {"BpfInsn.Jt", Field, 0}, + {"BpfInsn.K", Field, 0}, + {"BpfInterface", Func, 0}, + {"BpfJump", Func, 0}, + {"BpfProgram", Type, 0}, + {"BpfProgram.Insns", Field, 0}, + {"BpfProgram.Len", Field, 0}, + {"BpfProgram.Pad_cgo_0", Field, 0}, + {"BpfStat", Type, 0}, + {"BpfStat.Capt", Field, 2}, + {"BpfStat.Drop", Field, 0}, + {"BpfStat.Padding", Field, 2}, + {"BpfStat.Recv", Field, 0}, + {"BpfStats", Func, 0}, + {"BpfStmt", Func, 0}, + {"BpfTimeout", Func, 0}, + {"BpfTimeval", Type, 2}, + {"BpfTimeval.Sec", Field, 2}, + {"BpfTimeval.Usec", Field, 2}, + {"BpfVersion", Type, 0}, + {"BpfVersion.Major", Field, 0}, + {"BpfVersion.Minor", Field, 0}, + {"BpfZbuf", Type, 0}, + {"BpfZbuf.Bufa", Field, 0}, + {"BpfZbuf.Bufb", Field, 0}, + {"BpfZbuf.Buflen", Field, 0}, + {"BpfZbufHeader", Type, 0}, + {"BpfZbufHeader.Kernel_gen", Field, 0}, + {"BpfZbufHeader.Kernel_len", Field, 0}, + {"BpfZbufHeader.User_gen", Field, 0}, + {"BpfZbufHeader.X_bzh_pad", Field, 0}, + {"ByHandleFileInformation", Type, 0}, + {"ByHandleFileInformation.CreationTime", Field, 0}, + {"ByHandleFileInformation.FileAttributes", Field, 0}, + {"ByHandleFileInformation.FileIndexHigh", Field, 0}, + {"ByHandleFileInformation.FileIndexLow", Field, 0}, + {"ByHandleFileInformation.FileSizeHigh", Field, 0}, + {"ByHandleFileInformation.FileSizeLow", Field, 0}, + {"ByHandleFileInformation.LastAccessTime", Field, 0}, + {"ByHandleFileInformation.LastWriteTime", Field, 0}, + {"ByHandleFileInformation.NumberOfLinks", Field, 0}, + {"ByHandleFileInformation.VolumeSerialNumber", Field, 0}, + {"BytePtrFromString", Func, 1}, + {"ByteSliceFromString", Func, 1}, + {"CCR0_FLUSH", Const, 1}, + {"CERT_CHAIN_POLICY_AUTHENTICODE", Const, 0}, + {"CERT_CHAIN_POLICY_AUTHENTICODE_TS", Const, 0}, + {"CERT_CHAIN_POLICY_BASE", Const, 0}, + {"CERT_CHAIN_POLICY_BASIC_CONSTRAINTS", Const, 0}, + {"CERT_CHAIN_POLICY_EV", Const, 0}, + {"CERT_CHAIN_POLICY_MICROSOFT_ROOT", Const, 0}, + {"CERT_CHAIN_POLICY_NT_AUTH", Const, 0}, + {"CERT_CHAIN_POLICY_SSL", Const, 0}, + {"CERT_E_CN_NO_MATCH", Const, 0}, + {"CERT_E_EXPIRED", Const, 0}, + {"CERT_E_PURPOSE", Const, 0}, + {"CERT_E_ROLE", Const, 0}, + {"CERT_E_UNTRUSTEDROOT", Const, 0}, + {"CERT_STORE_ADD_ALWAYS", Const, 0}, + {"CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG", Const, 0}, + {"CERT_STORE_PROV_MEMORY", Const, 0}, + {"CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT", Const, 0}, + {"CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT", Const, 0}, + {"CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT", Const, 0}, + {"CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT", Const, 0}, + {"CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT", Const, 0}, + {"CERT_TRUST_INVALID_BASIC_CONSTRAINTS", Const, 0}, + {"CERT_TRUST_INVALID_EXTENSION", Const, 0}, + {"CERT_TRUST_INVALID_NAME_CONSTRAINTS", Const, 0}, + {"CERT_TRUST_INVALID_POLICY_CONSTRAINTS", Const, 0}, + {"CERT_TRUST_IS_CYCLIC", Const, 0}, + {"CERT_TRUST_IS_EXPLICIT_DISTRUST", Const, 0}, + {"CERT_TRUST_IS_NOT_SIGNATURE_VALID", Const, 0}, + {"CERT_TRUST_IS_NOT_TIME_VALID", Const, 0}, + {"CERT_TRUST_IS_NOT_VALID_FOR_USAGE", Const, 0}, + {"CERT_TRUST_IS_OFFLINE_REVOCATION", Const, 0}, + {"CERT_TRUST_IS_REVOKED", Const, 0}, + {"CERT_TRUST_IS_UNTRUSTED_ROOT", Const, 0}, + {"CERT_TRUST_NO_ERROR", Const, 0}, + {"CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY", Const, 0}, + {"CERT_TRUST_REVOCATION_STATUS_UNKNOWN", Const, 0}, + {"CFLUSH", Const, 1}, + {"CLOCAL", Const, 0}, + {"CLONE_CHILD_CLEARTID", Const, 2}, + {"CLONE_CHILD_SETTID", Const, 2}, + {"CLONE_CLEAR_SIGHAND", Const, 20}, + {"CLONE_CSIGNAL", Const, 3}, + {"CLONE_DETACHED", Const, 2}, + {"CLONE_FILES", Const, 2}, + {"CLONE_FS", Const, 2}, + {"CLONE_INTO_CGROUP", Const, 20}, + {"CLONE_IO", Const, 2}, + {"CLONE_NEWCGROUP", Const, 20}, + {"CLONE_NEWIPC", Const, 2}, + {"CLONE_NEWNET", Const, 2}, + {"CLONE_NEWNS", Const, 2}, + {"CLONE_NEWPID", Const, 2}, + {"CLONE_NEWTIME", Const, 20}, + {"CLONE_NEWUSER", Const, 2}, + {"CLONE_NEWUTS", Const, 2}, + {"CLONE_PARENT", Const, 2}, + {"CLONE_PARENT_SETTID", Const, 2}, + {"CLONE_PID", Const, 3}, + {"CLONE_PIDFD", Const, 20}, + {"CLONE_PTRACE", Const, 2}, + {"CLONE_SETTLS", Const, 2}, + {"CLONE_SIGHAND", Const, 2}, + {"CLONE_SYSVSEM", Const, 2}, + {"CLONE_THREAD", Const, 2}, + {"CLONE_UNTRACED", Const, 2}, + {"CLONE_VFORK", Const, 2}, + {"CLONE_VM", Const, 2}, + {"CPUID_CFLUSH", Const, 1}, + {"CREAD", Const, 0}, + {"CREATE_ALWAYS", Const, 0}, + {"CREATE_NEW", Const, 0}, + {"CREATE_NEW_PROCESS_GROUP", Const, 1}, + {"CREATE_UNICODE_ENVIRONMENT", Const, 0}, + {"CRYPT_DEFAULT_CONTAINER_OPTIONAL", Const, 0}, + {"CRYPT_DELETEKEYSET", Const, 0}, + {"CRYPT_MACHINE_KEYSET", Const, 0}, + {"CRYPT_NEWKEYSET", Const, 0}, + {"CRYPT_SILENT", Const, 0}, + {"CRYPT_VERIFYCONTEXT", Const, 0}, + {"CS5", Const, 0}, + {"CS6", Const, 0}, + {"CS7", Const, 0}, + {"CS8", Const, 0}, + {"CSIZE", Const, 0}, + {"CSTART", Const, 1}, + {"CSTATUS", Const, 1}, + {"CSTOP", Const, 1}, + {"CSTOPB", Const, 0}, + {"CSUSP", Const, 1}, + {"CTL_MAXNAME", Const, 0}, + {"CTL_NET", Const, 0}, + {"CTL_QUERY", Const, 1}, + {"CTRL_BREAK_EVENT", Const, 1}, + {"CTRL_CLOSE_EVENT", Const, 14}, + {"CTRL_C_EVENT", Const, 1}, + {"CTRL_LOGOFF_EVENT", Const, 14}, + {"CTRL_SHUTDOWN_EVENT", Const, 14}, + {"CancelIo", Func, 0}, + {"CancelIoEx", Func, 1}, + {"CertAddCertificateContextToStore", Func, 0}, + {"CertChainContext", Type, 0}, + {"CertChainContext.ChainCount", Field, 0}, + {"CertChainContext.Chains", Field, 0}, + {"CertChainContext.HasRevocationFreshnessTime", Field, 0}, + {"CertChainContext.LowerQualityChainCount", Field, 0}, + {"CertChainContext.LowerQualityChains", Field, 0}, + {"CertChainContext.RevocationFreshnessTime", Field, 0}, + {"CertChainContext.Size", Field, 0}, + {"CertChainContext.TrustStatus", Field, 0}, + {"CertChainElement", Type, 0}, + {"CertChainElement.ApplicationUsage", Field, 0}, + {"CertChainElement.CertContext", Field, 0}, + {"CertChainElement.ExtendedErrorInfo", Field, 0}, + {"CertChainElement.IssuanceUsage", Field, 0}, + {"CertChainElement.RevocationInfo", Field, 0}, + {"CertChainElement.Size", Field, 0}, + {"CertChainElement.TrustStatus", Field, 0}, + {"CertChainPara", Type, 0}, + {"CertChainPara.CacheResync", Field, 0}, + {"CertChainPara.CheckRevocationFreshnessTime", Field, 0}, + {"CertChainPara.RequestedUsage", Field, 0}, + {"CertChainPara.RequstedIssuancePolicy", Field, 0}, + {"CertChainPara.RevocationFreshnessTime", Field, 0}, + {"CertChainPara.Size", Field, 0}, + {"CertChainPara.URLRetrievalTimeout", Field, 0}, + {"CertChainPolicyPara", Type, 0}, + {"CertChainPolicyPara.ExtraPolicyPara", Field, 0}, + {"CertChainPolicyPara.Flags", Field, 0}, + {"CertChainPolicyPara.Size", Field, 0}, + {"CertChainPolicyStatus", Type, 0}, + {"CertChainPolicyStatus.ChainIndex", Field, 0}, + {"CertChainPolicyStatus.ElementIndex", Field, 0}, + {"CertChainPolicyStatus.Error", Field, 0}, + {"CertChainPolicyStatus.ExtraPolicyStatus", Field, 0}, + {"CertChainPolicyStatus.Size", Field, 0}, + {"CertCloseStore", Func, 0}, + {"CertContext", Type, 0}, + {"CertContext.CertInfo", Field, 0}, + {"CertContext.EncodedCert", Field, 0}, + {"CertContext.EncodingType", Field, 0}, + {"CertContext.Length", Field, 0}, + {"CertContext.Store", Field, 0}, + {"CertCreateCertificateContext", Func, 0}, + {"CertEnhKeyUsage", Type, 0}, + {"CertEnhKeyUsage.Length", Field, 0}, + {"CertEnhKeyUsage.UsageIdentifiers", Field, 0}, + {"CertEnumCertificatesInStore", Func, 0}, + {"CertFreeCertificateChain", Func, 0}, + {"CertFreeCertificateContext", Func, 0}, + {"CertGetCertificateChain", Func, 0}, + {"CertInfo", Type, 11}, + {"CertOpenStore", Func, 0}, + {"CertOpenSystemStore", Func, 0}, + {"CertRevocationCrlInfo", Type, 11}, + {"CertRevocationInfo", Type, 0}, + {"CertRevocationInfo.CrlInfo", Field, 0}, + {"CertRevocationInfo.FreshnessTime", Field, 0}, + {"CertRevocationInfo.HasFreshnessTime", Field, 0}, + {"CertRevocationInfo.OidSpecificInfo", Field, 0}, + {"CertRevocationInfo.RevocationOid", Field, 0}, + {"CertRevocationInfo.RevocationResult", Field, 0}, + {"CertRevocationInfo.Size", Field, 0}, + {"CertSimpleChain", Type, 0}, + {"CertSimpleChain.Elements", Field, 0}, + {"CertSimpleChain.HasRevocationFreshnessTime", Field, 0}, + {"CertSimpleChain.NumElements", Field, 0}, + {"CertSimpleChain.RevocationFreshnessTime", Field, 0}, + {"CertSimpleChain.Size", Field, 0}, + {"CertSimpleChain.TrustListInfo", Field, 0}, + {"CertSimpleChain.TrustStatus", Field, 0}, + {"CertTrustListInfo", Type, 11}, + {"CertTrustStatus", Type, 0}, + {"CertTrustStatus.ErrorStatus", Field, 0}, + {"CertTrustStatus.InfoStatus", Field, 0}, + {"CertUsageMatch", Type, 0}, + {"CertUsageMatch.Type", Field, 0}, + {"CertUsageMatch.Usage", Field, 0}, + {"CertVerifyCertificateChainPolicy", Func, 0}, + {"Chdir", Func, 0}, + {"CheckBpfVersion", Func, 0}, + {"Chflags", Func, 0}, + {"Chmod", Func, 0}, + {"Chown", Func, 0}, + {"Chroot", Func, 0}, + {"Clearenv", Func, 0}, + {"Close", Func, 0}, + {"CloseHandle", Func, 0}, + {"CloseOnExec", Func, 0}, + {"Closesocket", Func, 0}, + {"CmsgLen", Func, 0}, + {"CmsgSpace", Func, 0}, + {"Cmsghdr", Type, 0}, + {"Cmsghdr.Len", Field, 0}, + {"Cmsghdr.Level", Field, 0}, + {"Cmsghdr.Type", Field, 0}, + {"Cmsghdr.X__cmsg_data", Field, 0}, + {"CommandLineToArgv", Func, 0}, + {"ComputerName", Func, 0}, + {"Conn", Type, 9}, + {"Connect", Func, 0}, + {"ConnectEx", Func, 1}, + {"ConvertSidToStringSid", Func, 0}, + {"ConvertStringSidToSid", Func, 0}, + {"CopySid", Func, 0}, + {"Creat", Func, 0}, + {"CreateDirectory", Func, 0}, + {"CreateFile", Func, 0}, + {"CreateFileMapping", Func, 0}, + {"CreateHardLink", Func, 4}, + {"CreateIoCompletionPort", Func, 0}, + {"CreatePipe", Func, 0}, + {"CreateProcess", Func, 0}, + {"CreateProcessAsUser", Func, 10}, + {"CreateSymbolicLink", Func, 4}, + {"CreateToolhelp32Snapshot", Func, 4}, + {"Credential", Type, 0}, + {"Credential.Gid", Field, 0}, + {"Credential.Groups", Field, 0}, + {"Credential.NoSetGroups", Field, 9}, + {"Credential.Uid", Field, 0}, + {"CryptAcquireContext", Func, 0}, + {"CryptGenRandom", Func, 0}, + {"CryptReleaseContext", Func, 0}, + {"DIOCBSFLUSH", Const, 1}, + {"DIOCOSFPFLUSH", Const, 1}, + {"DLL", Type, 0}, + {"DLL.Handle", Field, 0}, + {"DLL.Name", Field, 0}, + {"DLLError", Type, 0}, + {"DLLError.Err", Field, 0}, + {"DLLError.Msg", Field, 0}, + {"DLLError.ObjName", Field, 0}, + {"DLT_A429", Const, 0}, + {"DLT_A653_ICM", Const, 0}, + {"DLT_AIRONET_HEADER", Const, 0}, + {"DLT_AOS", Const, 1}, + {"DLT_APPLE_IP_OVER_IEEE1394", Const, 0}, + {"DLT_ARCNET", Const, 0}, + {"DLT_ARCNET_LINUX", Const, 0}, + {"DLT_ATM_CLIP", Const, 0}, + {"DLT_ATM_RFC1483", Const, 0}, + {"DLT_AURORA", Const, 0}, + {"DLT_AX25", Const, 0}, + {"DLT_AX25_KISS", Const, 0}, + {"DLT_BACNET_MS_TP", Const, 0}, + {"DLT_BLUETOOTH_HCI_H4", Const, 0}, + {"DLT_BLUETOOTH_HCI_H4_WITH_PHDR", Const, 0}, + {"DLT_CAN20B", Const, 0}, + {"DLT_CAN_SOCKETCAN", Const, 1}, + {"DLT_CHAOS", Const, 0}, + {"DLT_CHDLC", Const, 0}, + {"DLT_CISCO_IOS", Const, 0}, + {"DLT_C_HDLC", Const, 0}, + {"DLT_C_HDLC_WITH_DIR", Const, 0}, + {"DLT_DBUS", Const, 1}, + {"DLT_DECT", Const, 1}, + {"DLT_DOCSIS", Const, 0}, + {"DLT_DVB_CI", Const, 1}, + {"DLT_ECONET", Const, 0}, + {"DLT_EN10MB", Const, 0}, + {"DLT_EN3MB", Const, 0}, + {"DLT_ENC", Const, 0}, + {"DLT_ERF", Const, 0}, + {"DLT_ERF_ETH", Const, 0}, + {"DLT_ERF_POS", Const, 0}, + {"DLT_FC_2", Const, 1}, + {"DLT_FC_2_WITH_FRAME_DELIMS", Const, 1}, + {"DLT_FDDI", Const, 0}, + {"DLT_FLEXRAY", Const, 0}, + {"DLT_FRELAY", Const, 0}, + {"DLT_FRELAY_WITH_DIR", Const, 0}, + {"DLT_GCOM_SERIAL", Const, 0}, + {"DLT_GCOM_T1E1", Const, 0}, + {"DLT_GPF_F", Const, 0}, + {"DLT_GPF_T", Const, 0}, + {"DLT_GPRS_LLC", Const, 0}, + {"DLT_GSMTAP_ABIS", Const, 1}, + {"DLT_GSMTAP_UM", Const, 1}, + {"DLT_HDLC", Const, 1}, + {"DLT_HHDLC", Const, 0}, + {"DLT_HIPPI", Const, 1}, + {"DLT_IBM_SN", Const, 0}, + {"DLT_IBM_SP", Const, 0}, + {"DLT_IEEE802", Const, 0}, + {"DLT_IEEE802_11", Const, 0}, + {"DLT_IEEE802_11_RADIO", Const, 0}, + {"DLT_IEEE802_11_RADIO_AVS", Const, 0}, + {"DLT_IEEE802_15_4", Const, 0}, + {"DLT_IEEE802_15_4_LINUX", Const, 0}, + {"DLT_IEEE802_15_4_NOFCS", Const, 1}, + {"DLT_IEEE802_15_4_NONASK_PHY", Const, 0}, + {"DLT_IEEE802_16_MAC_CPS", Const, 0}, + {"DLT_IEEE802_16_MAC_CPS_RADIO", Const, 0}, + {"DLT_IPFILTER", Const, 0}, + {"DLT_IPMB", Const, 0}, + {"DLT_IPMB_LINUX", Const, 0}, + {"DLT_IPNET", Const, 1}, + {"DLT_IPOIB", Const, 1}, + {"DLT_IPV4", Const, 1}, + {"DLT_IPV6", Const, 1}, + {"DLT_IP_OVER_FC", Const, 0}, + {"DLT_JUNIPER_ATM1", Const, 0}, + {"DLT_JUNIPER_ATM2", Const, 0}, + {"DLT_JUNIPER_ATM_CEMIC", Const, 1}, + {"DLT_JUNIPER_CHDLC", Const, 0}, + {"DLT_JUNIPER_ES", Const, 0}, + {"DLT_JUNIPER_ETHER", Const, 0}, + {"DLT_JUNIPER_FIBRECHANNEL", Const, 1}, + {"DLT_JUNIPER_FRELAY", Const, 0}, + {"DLT_JUNIPER_GGSN", Const, 0}, + {"DLT_JUNIPER_ISM", Const, 0}, + {"DLT_JUNIPER_MFR", Const, 0}, + {"DLT_JUNIPER_MLFR", Const, 0}, + {"DLT_JUNIPER_MLPPP", Const, 0}, + {"DLT_JUNIPER_MONITOR", Const, 0}, + {"DLT_JUNIPER_PIC_PEER", Const, 0}, + {"DLT_JUNIPER_PPP", Const, 0}, + {"DLT_JUNIPER_PPPOE", Const, 0}, + {"DLT_JUNIPER_PPPOE_ATM", Const, 0}, + {"DLT_JUNIPER_SERVICES", Const, 0}, + {"DLT_JUNIPER_SRX_E2E", Const, 1}, + {"DLT_JUNIPER_ST", Const, 0}, + {"DLT_JUNIPER_VP", Const, 0}, + {"DLT_JUNIPER_VS", Const, 1}, + {"DLT_LAPB_WITH_DIR", Const, 0}, + {"DLT_LAPD", Const, 0}, + {"DLT_LIN", Const, 0}, + {"DLT_LINUX_EVDEV", Const, 1}, + {"DLT_LINUX_IRDA", Const, 0}, + {"DLT_LINUX_LAPD", Const, 0}, + {"DLT_LINUX_PPP_WITHDIRECTION", Const, 0}, + {"DLT_LINUX_SLL", Const, 0}, + {"DLT_LOOP", Const, 0}, + {"DLT_LTALK", Const, 0}, + {"DLT_MATCHING_MAX", Const, 1}, + {"DLT_MATCHING_MIN", Const, 1}, + {"DLT_MFR", Const, 0}, + {"DLT_MOST", Const, 0}, + {"DLT_MPEG_2_TS", Const, 1}, + {"DLT_MPLS", Const, 1}, + {"DLT_MTP2", Const, 0}, + {"DLT_MTP2_WITH_PHDR", Const, 0}, + {"DLT_MTP3", Const, 0}, + {"DLT_MUX27010", Const, 1}, + {"DLT_NETANALYZER", Const, 1}, + {"DLT_NETANALYZER_TRANSPARENT", Const, 1}, + {"DLT_NFC_LLCP", Const, 1}, + {"DLT_NFLOG", Const, 1}, + {"DLT_NG40", Const, 1}, + {"DLT_NULL", Const, 0}, + {"DLT_PCI_EXP", Const, 0}, + {"DLT_PFLOG", Const, 0}, + {"DLT_PFSYNC", Const, 0}, + {"DLT_PPI", Const, 0}, + {"DLT_PPP", Const, 0}, + {"DLT_PPP_BSDOS", Const, 0}, + {"DLT_PPP_ETHER", Const, 0}, + {"DLT_PPP_PPPD", Const, 0}, + {"DLT_PPP_SERIAL", Const, 0}, + {"DLT_PPP_WITH_DIR", Const, 0}, + {"DLT_PPP_WITH_DIRECTION", Const, 0}, + {"DLT_PRISM_HEADER", Const, 0}, + {"DLT_PRONET", Const, 0}, + {"DLT_RAIF1", Const, 0}, + {"DLT_RAW", Const, 0}, + {"DLT_RAWAF_MASK", Const, 1}, + {"DLT_RIO", Const, 0}, + {"DLT_SCCP", Const, 0}, + {"DLT_SITA", Const, 0}, + {"DLT_SLIP", Const, 0}, + {"DLT_SLIP_BSDOS", Const, 0}, + {"DLT_STANAG_5066_D_PDU", Const, 1}, + {"DLT_SUNATM", Const, 0}, + {"DLT_SYMANTEC_FIREWALL", Const, 0}, + {"DLT_TZSP", Const, 0}, + {"DLT_USB", Const, 0}, + {"DLT_USB_LINUX", Const, 0}, + {"DLT_USB_LINUX_MMAPPED", Const, 1}, + {"DLT_USER0", Const, 0}, + {"DLT_USER1", Const, 0}, + {"DLT_USER10", Const, 0}, + {"DLT_USER11", Const, 0}, + {"DLT_USER12", Const, 0}, + {"DLT_USER13", Const, 0}, + {"DLT_USER14", Const, 0}, + {"DLT_USER15", Const, 0}, + {"DLT_USER2", Const, 0}, + {"DLT_USER3", Const, 0}, + {"DLT_USER4", Const, 0}, + {"DLT_USER5", Const, 0}, + {"DLT_USER6", Const, 0}, + {"DLT_USER7", Const, 0}, + {"DLT_USER8", Const, 0}, + {"DLT_USER9", Const, 0}, + {"DLT_WIHART", Const, 1}, + {"DLT_X2E_SERIAL", Const, 0}, + {"DLT_X2E_XORAYA", Const, 0}, + {"DNSMXData", Type, 0}, + {"DNSMXData.NameExchange", Field, 0}, + {"DNSMXData.Pad", Field, 0}, + {"DNSMXData.Preference", Field, 0}, + {"DNSPTRData", Type, 0}, + {"DNSPTRData.Host", Field, 0}, + {"DNSRecord", Type, 0}, + {"DNSRecord.Data", Field, 0}, + {"DNSRecord.Dw", Field, 0}, + {"DNSRecord.Length", Field, 0}, + {"DNSRecord.Name", Field, 0}, + {"DNSRecord.Next", Field, 0}, + {"DNSRecord.Reserved", Field, 0}, + {"DNSRecord.Ttl", Field, 0}, + {"DNSRecord.Type", Field, 0}, + {"DNSSRVData", Type, 0}, + {"DNSSRVData.Pad", Field, 0}, + {"DNSSRVData.Port", Field, 0}, + {"DNSSRVData.Priority", Field, 0}, + {"DNSSRVData.Target", Field, 0}, + {"DNSSRVData.Weight", Field, 0}, + {"DNSTXTData", Type, 0}, + {"DNSTXTData.StringArray", Field, 0}, + {"DNSTXTData.StringCount", Field, 0}, + {"DNS_INFO_NO_RECORDS", Const, 4}, + {"DNS_TYPE_A", Const, 0}, + {"DNS_TYPE_A6", Const, 0}, + {"DNS_TYPE_AAAA", Const, 0}, + {"DNS_TYPE_ADDRS", Const, 0}, + {"DNS_TYPE_AFSDB", Const, 0}, + {"DNS_TYPE_ALL", Const, 0}, + {"DNS_TYPE_ANY", Const, 0}, + {"DNS_TYPE_ATMA", Const, 0}, + {"DNS_TYPE_AXFR", Const, 0}, + {"DNS_TYPE_CERT", Const, 0}, + {"DNS_TYPE_CNAME", Const, 0}, + {"DNS_TYPE_DHCID", Const, 0}, + {"DNS_TYPE_DNAME", Const, 0}, + {"DNS_TYPE_DNSKEY", Const, 0}, + {"DNS_TYPE_DS", Const, 0}, + {"DNS_TYPE_EID", Const, 0}, + {"DNS_TYPE_GID", Const, 0}, + {"DNS_TYPE_GPOS", Const, 0}, + {"DNS_TYPE_HINFO", Const, 0}, + {"DNS_TYPE_ISDN", Const, 0}, + {"DNS_TYPE_IXFR", Const, 0}, + {"DNS_TYPE_KEY", Const, 0}, + {"DNS_TYPE_KX", Const, 0}, + {"DNS_TYPE_LOC", Const, 0}, + {"DNS_TYPE_MAILA", Const, 0}, + {"DNS_TYPE_MAILB", Const, 0}, + {"DNS_TYPE_MB", Const, 0}, + {"DNS_TYPE_MD", Const, 0}, + {"DNS_TYPE_MF", Const, 0}, + {"DNS_TYPE_MG", Const, 0}, + {"DNS_TYPE_MINFO", Const, 0}, + {"DNS_TYPE_MR", Const, 0}, + {"DNS_TYPE_MX", Const, 0}, + {"DNS_TYPE_NAPTR", Const, 0}, + {"DNS_TYPE_NBSTAT", Const, 0}, + {"DNS_TYPE_NIMLOC", Const, 0}, + {"DNS_TYPE_NS", Const, 0}, + {"DNS_TYPE_NSAP", Const, 0}, + {"DNS_TYPE_NSAPPTR", Const, 0}, + {"DNS_TYPE_NSEC", Const, 0}, + {"DNS_TYPE_NULL", Const, 0}, + {"DNS_TYPE_NXT", Const, 0}, + {"DNS_TYPE_OPT", Const, 0}, + {"DNS_TYPE_PTR", Const, 0}, + {"DNS_TYPE_PX", Const, 0}, + {"DNS_TYPE_RP", Const, 0}, + {"DNS_TYPE_RRSIG", Const, 0}, + {"DNS_TYPE_RT", Const, 0}, + {"DNS_TYPE_SIG", Const, 0}, + {"DNS_TYPE_SINK", Const, 0}, + {"DNS_TYPE_SOA", Const, 0}, + {"DNS_TYPE_SRV", Const, 0}, + {"DNS_TYPE_TEXT", Const, 0}, + {"DNS_TYPE_TKEY", Const, 0}, + {"DNS_TYPE_TSIG", Const, 0}, + {"DNS_TYPE_UID", Const, 0}, + {"DNS_TYPE_UINFO", Const, 0}, + {"DNS_TYPE_UNSPEC", Const, 0}, + {"DNS_TYPE_WINS", Const, 0}, + {"DNS_TYPE_WINSR", Const, 0}, + {"DNS_TYPE_WKS", Const, 0}, + {"DNS_TYPE_X25", Const, 0}, + {"DT_BLK", Const, 0}, + {"DT_CHR", Const, 0}, + {"DT_DIR", Const, 0}, + {"DT_FIFO", Const, 0}, + {"DT_LNK", Const, 0}, + {"DT_REG", Const, 0}, + {"DT_SOCK", Const, 0}, + {"DT_UNKNOWN", Const, 0}, + {"DT_WHT", Const, 0}, + {"DUPLICATE_CLOSE_SOURCE", Const, 0}, + {"DUPLICATE_SAME_ACCESS", Const, 0}, + {"DeleteFile", Func, 0}, + {"DetachLsf", Func, 0}, + {"DeviceIoControl", Func, 4}, + {"Dirent", Type, 0}, + {"Dirent.Fileno", Field, 0}, + {"Dirent.Ino", Field, 0}, + {"Dirent.Name", Field, 0}, + {"Dirent.Namlen", Field, 0}, + {"Dirent.Off", Field, 0}, + {"Dirent.Pad0", Field, 12}, + {"Dirent.Pad1", Field, 12}, + {"Dirent.Pad_cgo_0", Field, 0}, + {"Dirent.Reclen", Field, 0}, + {"Dirent.Seekoff", Field, 0}, + {"Dirent.Type", Field, 0}, + {"Dirent.X__d_padding", Field, 3}, + {"DnsNameCompare", Func, 4}, + {"DnsQuery", Func, 0}, + {"DnsRecordListFree", Func, 0}, + {"DnsSectionAdditional", Const, 4}, + {"DnsSectionAnswer", Const, 4}, + {"DnsSectionAuthority", Const, 4}, + {"DnsSectionQuestion", Const, 4}, + {"Dup", Func, 0}, + {"Dup2", Func, 0}, + {"Dup3", Func, 2}, + {"DuplicateHandle", Func, 0}, + {"E2BIG", Const, 0}, + {"EACCES", Const, 0}, + {"EADDRINUSE", Const, 0}, + {"EADDRNOTAVAIL", Const, 0}, + {"EADV", Const, 0}, + {"EAFNOSUPPORT", Const, 0}, + {"EAGAIN", Const, 0}, + {"EALREADY", Const, 0}, + {"EAUTH", Const, 0}, + {"EBADARCH", Const, 0}, + {"EBADE", Const, 0}, + {"EBADEXEC", Const, 0}, + {"EBADF", Const, 0}, + {"EBADFD", Const, 0}, + {"EBADMACHO", Const, 0}, + {"EBADMSG", Const, 0}, + {"EBADR", Const, 0}, + {"EBADRPC", Const, 0}, + {"EBADRQC", Const, 0}, + {"EBADSLT", Const, 0}, + {"EBFONT", Const, 0}, + {"EBUSY", Const, 0}, + {"ECANCELED", Const, 0}, + {"ECAPMODE", Const, 1}, + {"ECHILD", Const, 0}, + {"ECHO", Const, 0}, + {"ECHOCTL", Const, 0}, + {"ECHOE", Const, 0}, + {"ECHOK", Const, 0}, + {"ECHOKE", Const, 0}, + {"ECHONL", Const, 0}, + {"ECHOPRT", Const, 0}, + {"ECHRNG", Const, 0}, + {"ECOMM", Const, 0}, + {"ECONNABORTED", Const, 0}, + {"ECONNREFUSED", Const, 0}, + {"ECONNRESET", Const, 0}, + {"EDEADLK", Const, 0}, + {"EDEADLOCK", Const, 0}, + {"EDESTADDRREQ", Const, 0}, + {"EDEVERR", Const, 0}, + {"EDOM", Const, 0}, + {"EDOOFUS", Const, 0}, + {"EDOTDOT", Const, 0}, + {"EDQUOT", Const, 0}, + {"EEXIST", Const, 0}, + {"EFAULT", Const, 0}, + {"EFBIG", Const, 0}, + {"EFER_LMA", Const, 1}, + {"EFER_LME", Const, 1}, + {"EFER_NXE", Const, 1}, + {"EFER_SCE", Const, 1}, + {"EFTYPE", Const, 0}, + {"EHOSTDOWN", Const, 0}, + {"EHOSTUNREACH", Const, 0}, + {"EHWPOISON", Const, 0}, + {"EIDRM", Const, 0}, + {"EILSEQ", Const, 0}, + {"EINPROGRESS", Const, 0}, + {"EINTR", Const, 0}, + {"EINVAL", Const, 0}, + {"EIO", Const, 0}, + {"EIPSEC", Const, 1}, + {"EISCONN", Const, 0}, + {"EISDIR", Const, 0}, + {"EISNAM", Const, 0}, + {"EKEYEXPIRED", Const, 0}, + {"EKEYREJECTED", Const, 0}, + {"EKEYREVOKED", Const, 0}, + {"EL2HLT", Const, 0}, + {"EL2NSYNC", Const, 0}, + {"EL3HLT", Const, 0}, + {"EL3RST", Const, 0}, + {"ELAST", Const, 0}, + {"ELF_NGREG", Const, 0}, + {"ELF_PRARGSZ", Const, 0}, + {"ELIBACC", Const, 0}, + {"ELIBBAD", Const, 0}, + {"ELIBEXEC", Const, 0}, + {"ELIBMAX", Const, 0}, + {"ELIBSCN", Const, 0}, + {"ELNRNG", Const, 0}, + {"ELOOP", Const, 0}, + {"EMEDIUMTYPE", Const, 0}, + {"EMFILE", Const, 0}, + {"EMLINK", Const, 0}, + {"EMSGSIZE", Const, 0}, + {"EMT_TAGOVF", Const, 1}, + {"EMULTIHOP", Const, 0}, + {"EMUL_ENABLED", Const, 1}, + {"EMUL_LINUX", Const, 1}, + {"EMUL_LINUX32", Const, 1}, + {"EMUL_MAXID", Const, 1}, + {"EMUL_NATIVE", Const, 1}, + {"ENAMETOOLONG", Const, 0}, + {"ENAVAIL", Const, 0}, + {"ENDRUNDISC", Const, 1}, + {"ENEEDAUTH", Const, 0}, + {"ENETDOWN", Const, 0}, + {"ENETRESET", Const, 0}, + {"ENETUNREACH", Const, 0}, + {"ENFILE", Const, 0}, + {"ENOANO", Const, 0}, + {"ENOATTR", Const, 0}, + {"ENOBUFS", Const, 0}, + {"ENOCSI", Const, 0}, + {"ENODATA", Const, 0}, + {"ENODEV", Const, 0}, + {"ENOENT", Const, 0}, + {"ENOEXEC", Const, 0}, + {"ENOKEY", Const, 0}, + {"ENOLCK", Const, 0}, + {"ENOLINK", Const, 0}, + {"ENOMEDIUM", Const, 0}, + {"ENOMEM", Const, 0}, + {"ENOMSG", Const, 0}, + {"ENONET", Const, 0}, + {"ENOPKG", Const, 0}, + {"ENOPOLICY", Const, 0}, + {"ENOPROTOOPT", Const, 0}, + {"ENOSPC", Const, 0}, + {"ENOSR", Const, 0}, + {"ENOSTR", Const, 0}, + {"ENOSYS", Const, 0}, + {"ENOTBLK", Const, 0}, + {"ENOTCAPABLE", Const, 0}, + {"ENOTCONN", Const, 0}, + {"ENOTDIR", Const, 0}, + {"ENOTEMPTY", Const, 0}, + {"ENOTNAM", Const, 0}, + {"ENOTRECOVERABLE", Const, 0}, + {"ENOTSOCK", Const, 0}, + {"ENOTSUP", Const, 0}, + {"ENOTTY", Const, 0}, + {"ENOTUNIQ", Const, 0}, + {"ENXIO", Const, 0}, + {"EN_SW_CTL_INF", Const, 1}, + {"EN_SW_CTL_PREC", Const, 1}, + {"EN_SW_CTL_ROUND", Const, 1}, + {"EN_SW_DATACHAIN", Const, 1}, + {"EN_SW_DENORM", Const, 1}, + {"EN_SW_INVOP", Const, 1}, + {"EN_SW_OVERFLOW", Const, 1}, + {"EN_SW_PRECLOSS", Const, 1}, + {"EN_SW_UNDERFLOW", Const, 1}, + {"EN_SW_ZERODIV", Const, 1}, + {"EOPNOTSUPP", Const, 0}, + {"EOVERFLOW", Const, 0}, + {"EOWNERDEAD", Const, 0}, + {"EPERM", Const, 0}, + {"EPFNOSUPPORT", Const, 0}, + {"EPIPE", Const, 0}, + {"EPOLLERR", Const, 0}, + {"EPOLLET", Const, 0}, + {"EPOLLHUP", Const, 0}, + {"EPOLLIN", Const, 0}, + {"EPOLLMSG", Const, 0}, + {"EPOLLONESHOT", Const, 0}, + {"EPOLLOUT", Const, 0}, + {"EPOLLPRI", Const, 0}, + {"EPOLLRDBAND", Const, 0}, + {"EPOLLRDHUP", Const, 0}, + {"EPOLLRDNORM", Const, 0}, + {"EPOLLWRBAND", Const, 0}, + {"EPOLLWRNORM", Const, 0}, + {"EPOLL_CLOEXEC", Const, 0}, + {"EPOLL_CTL_ADD", Const, 0}, + {"EPOLL_CTL_DEL", Const, 0}, + {"EPOLL_CTL_MOD", Const, 0}, + {"EPOLL_NONBLOCK", Const, 0}, + {"EPROCLIM", Const, 0}, + {"EPROCUNAVAIL", Const, 0}, + {"EPROGMISMATCH", Const, 0}, + {"EPROGUNAVAIL", Const, 0}, + {"EPROTO", Const, 0}, + {"EPROTONOSUPPORT", Const, 0}, + {"EPROTOTYPE", Const, 0}, + {"EPWROFF", Const, 0}, + {"EQFULL", Const, 16}, + {"ERANGE", Const, 0}, + {"EREMCHG", Const, 0}, + {"EREMOTE", Const, 0}, + {"EREMOTEIO", Const, 0}, + {"ERESTART", Const, 0}, + {"ERFKILL", Const, 0}, + {"EROFS", Const, 0}, + {"ERPCMISMATCH", Const, 0}, + {"ERROR_ACCESS_DENIED", Const, 0}, + {"ERROR_ALREADY_EXISTS", Const, 0}, + {"ERROR_BROKEN_PIPE", Const, 0}, + {"ERROR_BUFFER_OVERFLOW", Const, 0}, + {"ERROR_DIR_NOT_EMPTY", Const, 8}, + {"ERROR_ENVVAR_NOT_FOUND", Const, 0}, + {"ERROR_FILE_EXISTS", Const, 0}, + {"ERROR_FILE_NOT_FOUND", Const, 0}, + {"ERROR_HANDLE_EOF", Const, 2}, + {"ERROR_INSUFFICIENT_BUFFER", Const, 0}, + {"ERROR_IO_PENDING", Const, 0}, + {"ERROR_MOD_NOT_FOUND", Const, 0}, + {"ERROR_MORE_DATA", Const, 3}, + {"ERROR_NETNAME_DELETED", Const, 3}, + {"ERROR_NOT_FOUND", Const, 1}, + {"ERROR_NO_MORE_FILES", Const, 0}, + {"ERROR_OPERATION_ABORTED", Const, 0}, + {"ERROR_PATH_NOT_FOUND", Const, 0}, + {"ERROR_PRIVILEGE_NOT_HELD", Const, 4}, + {"ERROR_PROC_NOT_FOUND", Const, 0}, + {"ESHLIBVERS", Const, 0}, + {"ESHUTDOWN", Const, 0}, + {"ESOCKTNOSUPPORT", Const, 0}, + {"ESPIPE", Const, 0}, + {"ESRCH", Const, 0}, + {"ESRMNT", Const, 0}, + {"ESTALE", Const, 0}, + {"ESTRPIPE", Const, 0}, + {"ETHERCAP_JUMBO_MTU", Const, 1}, + {"ETHERCAP_VLAN_HWTAGGING", Const, 1}, + {"ETHERCAP_VLAN_MTU", Const, 1}, + {"ETHERMIN", Const, 1}, + {"ETHERMTU", Const, 1}, + {"ETHERMTU_JUMBO", Const, 1}, + {"ETHERTYPE_8023", Const, 1}, + {"ETHERTYPE_AARP", Const, 1}, + {"ETHERTYPE_ACCTON", Const, 1}, + {"ETHERTYPE_AEONIC", Const, 1}, + {"ETHERTYPE_ALPHA", Const, 1}, + {"ETHERTYPE_AMBER", Const, 1}, + {"ETHERTYPE_AMOEBA", Const, 1}, + {"ETHERTYPE_AOE", Const, 1}, + {"ETHERTYPE_APOLLO", Const, 1}, + {"ETHERTYPE_APOLLODOMAIN", Const, 1}, + {"ETHERTYPE_APPLETALK", Const, 1}, + {"ETHERTYPE_APPLITEK", Const, 1}, + {"ETHERTYPE_ARGONAUT", Const, 1}, + {"ETHERTYPE_ARP", Const, 1}, + {"ETHERTYPE_AT", Const, 1}, + {"ETHERTYPE_ATALK", Const, 1}, + {"ETHERTYPE_ATOMIC", Const, 1}, + {"ETHERTYPE_ATT", Const, 1}, + {"ETHERTYPE_ATTSTANFORD", Const, 1}, + {"ETHERTYPE_AUTOPHON", Const, 1}, + {"ETHERTYPE_AXIS", Const, 1}, + {"ETHERTYPE_BCLOOP", Const, 1}, + {"ETHERTYPE_BOFL", Const, 1}, + {"ETHERTYPE_CABLETRON", Const, 1}, + {"ETHERTYPE_CHAOS", Const, 1}, + {"ETHERTYPE_COMDESIGN", Const, 1}, + {"ETHERTYPE_COMPUGRAPHIC", Const, 1}, + {"ETHERTYPE_COUNTERPOINT", Const, 1}, + {"ETHERTYPE_CRONUS", Const, 1}, + {"ETHERTYPE_CRONUSVLN", Const, 1}, + {"ETHERTYPE_DCA", Const, 1}, + {"ETHERTYPE_DDE", Const, 1}, + {"ETHERTYPE_DEBNI", Const, 1}, + {"ETHERTYPE_DECAM", Const, 1}, + {"ETHERTYPE_DECCUST", Const, 1}, + {"ETHERTYPE_DECDIAG", Const, 1}, + {"ETHERTYPE_DECDNS", Const, 1}, + {"ETHERTYPE_DECDTS", Const, 1}, + {"ETHERTYPE_DECEXPER", Const, 1}, + {"ETHERTYPE_DECLAST", Const, 1}, + {"ETHERTYPE_DECLTM", Const, 1}, + {"ETHERTYPE_DECMUMPS", Const, 1}, + {"ETHERTYPE_DECNETBIOS", Const, 1}, + {"ETHERTYPE_DELTACON", Const, 1}, + {"ETHERTYPE_DIDDLE", Const, 1}, + {"ETHERTYPE_DLOG1", Const, 1}, + {"ETHERTYPE_DLOG2", Const, 1}, + {"ETHERTYPE_DN", Const, 1}, + {"ETHERTYPE_DOGFIGHT", Const, 1}, + {"ETHERTYPE_DSMD", Const, 1}, + {"ETHERTYPE_ECMA", Const, 1}, + {"ETHERTYPE_ENCRYPT", Const, 1}, + {"ETHERTYPE_ES", Const, 1}, + {"ETHERTYPE_EXCELAN", Const, 1}, + {"ETHERTYPE_EXPERDATA", Const, 1}, + {"ETHERTYPE_FLIP", Const, 1}, + {"ETHERTYPE_FLOWCONTROL", Const, 1}, + {"ETHERTYPE_FRARP", Const, 1}, + {"ETHERTYPE_GENDYN", Const, 1}, + {"ETHERTYPE_HAYES", Const, 1}, + {"ETHERTYPE_HIPPI_FP", Const, 1}, + {"ETHERTYPE_HITACHI", Const, 1}, + {"ETHERTYPE_HP", Const, 1}, + {"ETHERTYPE_IEEEPUP", Const, 1}, + {"ETHERTYPE_IEEEPUPAT", Const, 1}, + {"ETHERTYPE_IMLBL", Const, 1}, + {"ETHERTYPE_IMLBLDIAG", Const, 1}, + {"ETHERTYPE_IP", Const, 1}, + {"ETHERTYPE_IPAS", Const, 1}, + {"ETHERTYPE_IPV6", Const, 1}, + {"ETHERTYPE_IPX", Const, 1}, + {"ETHERTYPE_IPXNEW", Const, 1}, + {"ETHERTYPE_KALPANA", Const, 1}, + {"ETHERTYPE_LANBRIDGE", Const, 1}, + {"ETHERTYPE_LANPROBE", Const, 1}, + {"ETHERTYPE_LAT", Const, 1}, + {"ETHERTYPE_LBACK", Const, 1}, + {"ETHERTYPE_LITTLE", Const, 1}, + {"ETHERTYPE_LLDP", Const, 1}, + {"ETHERTYPE_LOGICRAFT", Const, 1}, + {"ETHERTYPE_LOOPBACK", Const, 1}, + {"ETHERTYPE_MATRA", Const, 1}, + {"ETHERTYPE_MAX", Const, 1}, + {"ETHERTYPE_MERIT", Const, 1}, + {"ETHERTYPE_MICP", Const, 1}, + {"ETHERTYPE_MOPDL", Const, 1}, + {"ETHERTYPE_MOPRC", Const, 1}, + {"ETHERTYPE_MOTOROLA", Const, 1}, + {"ETHERTYPE_MPLS", Const, 1}, + {"ETHERTYPE_MPLS_MCAST", Const, 1}, + {"ETHERTYPE_MUMPS", Const, 1}, + {"ETHERTYPE_NBPCC", Const, 1}, + {"ETHERTYPE_NBPCLAIM", Const, 1}, + {"ETHERTYPE_NBPCLREQ", Const, 1}, + {"ETHERTYPE_NBPCLRSP", Const, 1}, + {"ETHERTYPE_NBPCREQ", Const, 1}, + {"ETHERTYPE_NBPCRSP", Const, 1}, + {"ETHERTYPE_NBPDG", Const, 1}, + {"ETHERTYPE_NBPDGB", Const, 1}, + {"ETHERTYPE_NBPDLTE", Const, 1}, + {"ETHERTYPE_NBPRAR", Const, 1}, + {"ETHERTYPE_NBPRAS", Const, 1}, + {"ETHERTYPE_NBPRST", Const, 1}, + {"ETHERTYPE_NBPSCD", Const, 1}, + {"ETHERTYPE_NBPVCD", Const, 1}, + {"ETHERTYPE_NBS", Const, 1}, + {"ETHERTYPE_NCD", Const, 1}, + {"ETHERTYPE_NESTAR", Const, 1}, + {"ETHERTYPE_NETBEUI", Const, 1}, + {"ETHERTYPE_NOVELL", Const, 1}, + {"ETHERTYPE_NS", Const, 1}, + {"ETHERTYPE_NSAT", Const, 1}, + {"ETHERTYPE_NSCOMPAT", Const, 1}, + {"ETHERTYPE_NTRAILER", Const, 1}, + {"ETHERTYPE_OS9", Const, 1}, + {"ETHERTYPE_OS9NET", Const, 1}, + {"ETHERTYPE_PACER", Const, 1}, + {"ETHERTYPE_PAE", Const, 1}, + {"ETHERTYPE_PCS", Const, 1}, + {"ETHERTYPE_PLANNING", Const, 1}, + {"ETHERTYPE_PPP", Const, 1}, + {"ETHERTYPE_PPPOE", Const, 1}, + {"ETHERTYPE_PPPOEDISC", Const, 1}, + {"ETHERTYPE_PRIMENTS", Const, 1}, + {"ETHERTYPE_PUP", Const, 1}, + {"ETHERTYPE_PUPAT", Const, 1}, + {"ETHERTYPE_QINQ", Const, 1}, + {"ETHERTYPE_RACAL", Const, 1}, + {"ETHERTYPE_RATIONAL", Const, 1}, + {"ETHERTYPE_RAWFR", Const, 1}, + {"ETHERTYPE_RCL", Const, 1}, + {"ETHERTYPE_RDP", Const, 1}, + {"ETHERTYPE_RETIX", Const, 1}, + {"ETHERTYPE_REVARP", Const, 1}, + {"ETHERTYPE_SCA", Const, 1}, + {"ETHERTYPE_SECTRA", Const, 1}, + {"ETHERTYPE_SECUREDATA", Const, 1}, + {"ETHERTYPE_SGITW", Const, 1}, + {"ETHERTYPE_SG_BOUNCE", Const, 1}, + {"ETHERTYPE_SG_DIAG", Const, 1}, + {"ETHERTYPE_SG_NETGAMES", Const, 1}, + {"ETHERTYPE_SG_RESV", Const, 1}, + {"ETHERTYPE_SIMNET", Const, 1}, + {"ETHERTYPE_SLOW", Const, 1}, + {"ETHERTYPE_SLOWPROTOCOLS", Const, 1}, + {"ETHERTYPE_SNA", Const, 1}, + {"ETHERTYPE_SNMP", Const, 1}, + {"ETHERTYPE_SONIX", Const, 1}, + {"ETHERTYPE_SPIDER", Const, 1}, + {"ETHERTYPE_SPRITE", Const, 1}, + {"ETHERTYPE_STP", Const, 1}, + {"ETHERTYPE_TALARIS", Const, 1}, + {"ETHERTYPE_TALARISMC", Const, 1}, + {"ETHERTYPE_TCPCOMP", Const, 1}, + {"ETHERTYPE_TCPSM", Const, 1}, + {"ETHERTYPE_TEC", Const, 1}, + {"ETHERTYPE_TIGAN", Const, 1}, + {"ETHERTYPE_TRAIL", Const, 1}, + {"ETHERTYPE_TRANSETHER", Const, 1}, + {"ETHERTYPE_TYMSHARE", Const, 1}, + {"ETHERTYPE_UBBST", Const, 1}, + {"ETHERTYPE_UBDEBUG", Const, 1}, + {"ETHERTYPE_UBDIAGLOOP", Const, 1}, + {"ETHERTYPE_UBDL", Const, 1}, + {"ETHERTYPE_UBNIU", Const, 1}, + {"ETHERTYPE_UBNMC", Const, 1}, + {"ETHERTYPE_VALID", Const, 1}, + {"ETHERTYPE_VARIAN", Const, 1}, + {"ETHERTYPE_VAXELN", Const, 1}, + {"ETHERTYPE_VEECO", Const, 1}, + {"ETHERTYPE_VEXP", Const, 1}, + {"ETHERTYPE_VGLAB", Const, 1}, + {"ETHERTYPE_VINES", Const, 1}, + {"ETHERTYPE_VINESECHO", Const, 1}, + {"ETHERTYPE_VINESLOOP", Const, 1}, + {"ETHERTYPE_VITAL", Const, 1}, + {"ETHERTYPE_VLAN", Const, 1}, + {"ETHERTYPE_VLTLMAN", Const, 1}, + {"ETHERTYPE_VPROD", Const, 1}, + {"ETHERTYPE_VURESERVED", Const, 1}, + {"ETHERTYPE_WATERLOO", Const, 1}, + {"ETHERTYPE_WELLFLEET", Const, 1}, + {"ETHERTYPE_X25", Const, 1}, + {"ETHERTYPE_X75", Const, 1}, + {"ETHERTYPE_XNSSM", Const, 1}, + {"ETHERTYPE_XTP", Const, 1}, + {"ETHER_ADDR_LEN", Const, 1}, + {"ETHER_ALIGN", Const, 1}, + {"ETHER_CRC_LEN", Const, 1}, + {"ETHER_CRC_POLY_BE", Const, 1}, + {"ETHER_CRC_POLY_LE", Const, 1}, + {"ETHER_HDR_LEN", Const, 1}, + {"ETHER_MAX_DIX_LEN", Const, 1}, + {"ETHER_MAX_LEN", Const, 1}, + {"ETHER_MAX_LEN_JUMBO", Const, 1}, + {"ETHER_MIN_LEN", Const, 1}, + {"ETHER_PPPOE_ENCAP_LEN", Const, 1}, + {"ETHER_TYPE_LEN", Const, 1}, + {"ETHER_VLAN_ENCAP_LEN", Const, 1}, + {"ETH_P_1588", Const, 0}, + {"ETH_P_8021Q", Const, 0}, + {"ETH_P_802_2", Const, 0}, + {"ETH_P_802_3", Const, 0}, + {"ETH_P_AARP", Const, 0}, + {"ETH_P_ALL", Const, 0}, + {"ETH_P_AOE", Const, 0}, + {"ETH_P_ARCNET", Const, 0}, + {"ETH_P_ARP", Const, 0}, + {"ETH_P_ATALK", Const, 0}, + {"ETH_P_ATMFATE", Const, 0}, + {"ETH_P_ATMMPOA", Const, 0}, + {"ETH_P_AX25", Const, 0}, + {"ETH_P_BPQ", Const, 0}, + {"ETH_P_CAIF", Const, 0}, + {"ETH_P_CAN", Const, 0}, + {"ETH_P_CONTROL", Const, 0}, + {"ETH_P_CUST", Const, 0}, + {"ETH_P_DDCMP", Const, 0}, + {"ETH_P_DEC", Const, 0}, + {"ETH_P_DIAG", Const, 0}, + {"ETH_P_DNA_DL", Const, 0}, + {"ETH_P_DNA_RC", Const, 0}, + {"ETH_P_DNA_RT", Const, 0}, + {"ETH_P_DSA", Const, 0}, + {"ETH_P_ECONET", Const, 0}, + {"ETH_P_EDSA", Const, 0}, + {"ETH_P_FCOE", Const, 0}, + {"ETH_P_FIP", Const, 0}, + {"ETH_P_HDLC", Const, 0}, + {"ETH_P_IEEE802154", Const, 0}, + {"ETH_P_IEEEPUP", Const, 0}, + {"ETH_P_IEEEPUPAT", Const, 0}, + {"ETH_P_IP", Const, 0}, + {"ETH_P_IPV6", Const, 0}, + {"ETH_P_IPX", Const, 0}, + {"ETH_P_IRDA", Const, 0}, + {"ETH_P_LAT", Const, 0}, + {"ETH_P_LINK_CTL", Const, 0}, + {"ETH_P_LOCALTALK", Const, 0}, + {"ETH_P_LOOP", Const, 0}, + {"ETH_P_MOBITEX", Const, 0}, + {"ETH_P_MPLS_MC", Const, 0}, + {"ETH_P_MPLS_UC", Const, 0}, + {"ETH_P_PAE", Const, 0}, + {"ETH_P_PAUSE", Const, 0}, + {"ETH_P_PHONET", Const, 0}, + {"ETH_P_PPPTALK", Const, 0}, + {"ETH_P_PPP_DISC", Const, 0}, + {"ETH_P_PPP_MP", Const, 0}, + {"ETH_P_PPP_SES", Const, 0}, + {"ETH_P_PUP", Const, 0}, + {"ETH_P_PUPAT", Const, 0}, + {"ETH_P_RARP", Const, 0}, + {"ETH_P_SCA", Const, 0}, + {"ETH_P_SLOW", Const, 0}, + {"ETH_P_SNAP", Const, 0}, + {"ETH_P_TEB", Const, 0}, + {"ETH_P_TIPC", Const, 0}, + {"ETH_P_TRAILER", Const, 0}, + {"ETH_P_TR_802_2", Const, 0}, + {"ETH_P_WAN_PPP", Const, 0}, + {"ETH_P_WCCP", Const, 0}, + {"ETH_P_X25", Const, 0}, + {"ETIME", Const, 0}, + {"ETIMEDOUT", Const, 0}, + {"ETOOMANYREFS", Const, 0}, + {"ETXTBSY", Const, 0}, + {"EUCLEAN", Const, 0}, + {"EUNATCH", Const, 0}, + {"EUSERS", Const, 0}, + {"EVFILT_AIO", Const, 0}, + {"EVFILT_FS", Const, 0}, + {"EVFILT_LIO", Const, 0}, + {"EVFILT_MACHPORT", Const, 0}, + {"EVFILT_PROC", Const, 0}, + {"EVFILT_READ", Const, 0}, + {"EVFILT_SIGNAL", Const, 0}, + {"EVFILT_SYSCOUNT", Const, 0}, + {"EVFILT_THREADMARKER", Const, 0}, + {"EVFILT_TIMER", Const, 0}, + {"EVFILT_USER", Const, 0}, + {"EVFILT_VM", Const, 0}, + {"EVFILT_VNODE", Const, 0}, + {"EVFILT_WRITE", Const, 0}, + {"EV_ADD", Const, 0}, + {"EV_CLEAR", Const, 0}, + {"EV_DELETE", Const, 0}, + {"EV_DISABLE", Const, 0}, + {"EV_DISPATCH", Const, 0}, + {"EV_DROP", Const, 3}, + {"EV_ENABLE", Const, 0}, + {"EV_EOF", Const, 0}, + {"EV_ERROR", Const, 0}, + {"EV_FLAG0", Const, 0}, + {"EV_FLAG1", Const, 0}, + {"EV_ONESHOT", Const, 0}, + {"EV_OOBAND", Const, 0}, + {"EV_POLL", Const, 0}, + {"EV_RECEIPT", Const, 0}, + {"EV_SYSFLAGS", Const, 0}, + {"EWINDOWS", Const, 0}, + {"EWOULDBLOCK", Const, 0}, + {"EXDEV", Const, 0}, + {"EXFULL", Const, 0}, + {"EXTA", Const, 0}, + {"EXTB", Const, 0}, + {"EXTPROC", Const, 0}, + {"Environ", Func, 0}, + {"EpollCreate", Func, 0}, + {"EpollCreate1", Func, 0}, + {"EpollCtl", Func, 0}, + {"EpollEvent", Type, 0}, + {"EpollEvent.Events", Field, 0}, + {"EpollEvent.Fd", Field, 0}, + {"EpollEvent.Pad", Field, 0}, + {"EpollEvent.PadFd", Field, 0}, + {"EpollWait", Func, 0}, + {"Errno", Type, 0}, + {"EscapeArg", Func, 0}, + {"Exchangedata", Func, 0}, + {"Exec", Func, 0}, + {"Exit", Func, 0}, + {"ExitProcess", Func, 0}, + {"FD_CLOEXEC", Const, 0}, + {"FD_SETSIZE", Const, 0}, + {"FILE_ACTION_ADDED", Const, 0}, + {"FILE_ACTION_MODIFIED", Const, 0}, + {"FILE_ACTION_REMOVED", Const, 0}, + {"FILE_ACTION_RENAMED_NEW_NAME", Const, 0}, + {"FILE_ACTION_RENAMED_OLD_NAME", Const, 0}, + {"FILE_APPEND_DATA", Const, 0}, + {"FILE_ATTRIBUTE_ARCHIVE", Const, 0}, + {"FILE_ATTRIBUTE_DIRECTORY", Const, 0}, + {"FILE_ATTRIBUTE_HIDDEN", Const, 0}, + {"FILE_ATTRIBUTE_NORMAL", Const, 0}, + {"FILE_ATTRIBUTE_READONLY", Const, 0}, + {"FILE_ATTRIBUTE_REPARSE_POINT", Const, 4}, + {"FILE_ATTRIBUTE_SYSTEM", Const, 0}, + {"FILE_BEGIN", Const, 0}, + {"FILE_CURRENT", Const, 0}, + {"FILE_END", Const, 0}, + {"FILE_FLAG_BACKUP_SEMANTICS", Const, 0}, + {"FILE_FLAG_OPEN_REPARSE_POINT", Const, 4}, + {"FILE_FLAG_OVERLAPPED", Const, 0}, + {"FILE_LIST_DIRECTORY", Const, 0}, + {"FILE_MAP_COPY", Const, 0}, + {"FILE_MAP_EXECUTE", Const, 0}, + {"FILE_MAP_READ", Const, 0}, + {"FILE_MAP_WRITE", Const, 0}, + {"FILE_NOTIFY_CHANGE_ATTRIBUTES", Const, 0}, + {"FILE_NOTIFY_CHANGE_CREATION", Const, 0}, + {"FILE_NOTIFY_CHANGE_DIR_NAME", Const, 0}, + {"FILE_NOTIFY_CHANGE_FILE_NAME", Const, 0}, + {"FILE_NOTIFY_CHANGE_LAST_ACCESS", Const, 0}, + {"FILE_NOTIFY_CHANGE_LAST_WRITE", Const, 0}, + {"FILE_NOTIFY_CHANGE_SIZE", Const, 0}, + {"FILE_SHARE_DELETE", Const, 0}, + {"FILE_SHARE_READ", Const, 0}, + {"FILE_SHARE_WRITE", Const, 0}, + {"FILE_SKIP_COMPLETION_PORT_ON_SUCCESS", Const, 2}, + {"FILE_SKIP_SET_EVENT_ON_HANDLE", Const, 2}, + {"FILE_TYPE_CHAR", Const, 0}, + {"FILE_TYPE_DISK", Const, 0}, + {"FILE_TYPE_PIPE", Const, 0}, + {"FILE_TYPE_REMOTE", Const, 0}, + {"FILE_TYPE_UNKNOWN", Const, 0}, + {"FILE_WRITE_ATTRIBUTES", Const, 0}, + {"FLUSHO", Const, 0}, + {"FORMAT_MESSAGE_ALLOCATE_BUFFER", Const, 0}, + {"FORMAT_MESSAGE_ARGUMENT_ARRAY", Const, 0}, + {"FORMAT_MESSAGE_FROM_HMODULE", Const, 0}, + {"FORMAT_MESSAGE_FROM_STRING", Const, 0}, + {"FORMAT_MESSAGE_FROM_SYSTEM", Const, 0}, + {"FORMAT_MESSAGE_IGNORE_INSERTS", Const, 0}, + {"FORMAT_MESSAGE_MAX_WIDTH_MASK", Const, 0}, + {"FSCTL_GET_REPARSE_POINT", Const, 4}, + {"F_ADDFILESIGS", Const, 0}, + {"F_ADDSIGS", Const, 0}, + {"F_ALLOCATEALL", Const, 0}, + {"F_ALLOCATECONTIG", Const, 0}, + {"F_CANCEL", Const, 0}, + {"F_CHKCLEAN", Const, 0}, + {"F_CLOSEM", Const, 1}, + {"F_DUP2FD", Const, 0}, + {"F_DUP2FD_CLOEXEC", Const, 1}, + {"F_DUPFD", Const, 0}, + {"F_DUPFD_CLOEXEC", Const, 0}, + {"F_EXLCK", Const, 0}, + {"F_FINDSIGS", Const, 16}, + {"F_FLUSH_DATA", Const, 0}, + {"F_FREEZE_FS", Const, 0}, + {"F_FSCTL", Const, 1}, + {"F_FSDIRMASK", Const, 1}, + {"F_FSIN", Const, 1}, + {"F_FSINOUT", Const, 1}, + {"F_FSOUT", Const, 1}, + {"F_FSPRIV", Const, 1}, + {"F_FSVOID", Const, 1}, + {"F_FULLFSYNC", Const, 0}, + {"F_GETCODEDIR", Const, 16}, + {"F_GETFD", Const, 0}, + {"F_GETFL", Const, 0}, + {"F_GETLEASE", Const, 0}, + {"F_GETLK", Const, 0}, + {"F_GETLK64", Const, 0}, + {"F_GETLKPID", Const, 0}, + {"F_GETNOSIGPIPE", Const, 0}, + {"F_GETOWN", Const, 0}, + {"F_GETOWN_EX", Const, 0}, + {"F_GETPATH", Const, 0}, + {"F_GETPATH_MTMINFO", Const, 0}, + {"F_GETPIPE_SZ", Const, 0}, + {"F_GETPROTECTIONCLASS", Const, 0}, + {"F_GETPROTECTIONLEVEL", Const, 16}, + {"F_GETSIG", Const, 0}, + {"F_GLOBAL_NOCACHE", Const, 0}, + {"F_LOCK", Const, 0}, + {"F_LOG2PHYS", Const, 0}, + {"F_LOG2PHYS_EXT", Const, 0}, + {"F_MARKDEPENDENCY", Const, 0}, + {"F_MAXFD", Const, 1}, + {"F_NOCACHE", Const, 0}, + {"F_NODIRECT", Const, 0}, + {"F_NOTIFY", Const, 0}, + {"F_OGETLK", Const, 0}, + {"F_OK", Const, 0}, + {"F_OSETLK", Const, 0}, + {"F_OSETLKW", Const, 0}, + {"F_PARAM_MASK", Const, 1}, + {"F_PARAM_MAX", Const, 1}, + {"F_PATHPKG_CHECK", Const, 0}, + {"F_PEOFPOSMODE", Const, 0}, + {"F_PREALLOCATE", Const, 0}, + {"F_RDADVISE", Const, 0}, + {"F_RDAHEAD", Const, 0}, + {"F_RDLCK", Const, 0}, + {"F_READAHEAD", Const, 0}, + {"F_READBOOTSTRAP", Const, 0}, + {"F_SETBACKINGSTORE", Const, 0}, + {"F_SETFD", Const, 0}, + {"F_SETFL", Const, 0}, + {"F_SETLEASE", Const, 0}, + {"F_SETLK", Const, 0}, + {"F_SETLK64", Const, 0}, + {"F_SETLKW", Const, 0}, + {"F_SETLKW64", Const, 0}, + {"F_SETLKWTIMEOUT", Const, 16}, + {"F_SETLK_REMOTE", Const, 0}, + {"F_SETNOSIGPIPE", Const, 0}, + {"F_SETOWN", Const, 0}, + {"F_SETOWN_EX", Const, 0}, + {"F_SETPIPE_SZ", Const, 0}, + {"F_SETPROTECTIONCLASS", Const, 0}, + {"F_SETSIG", Const, 0}, + {"F_SETSIZE", Const, 0}, + {"F_SHLCK", Const, 0}, + {"F_SINGLE_WRITER", Const, 16}, + {"F_TEST", Const, 0}, + {"F_THAW_FS", Const, 0}, + {"F_TLOCK", Const, 0}, + {"F_TRANSCODEKEY", Const, 16}, + {"F_ULOCK", Const, 0}, + {"F_UNLCK", Const, 0}, + {"F_UNLCKSYS", Const, 0}, + {"F_VOLPOSMODE", Const, 0}, + {"F_WRITEBOOTSTRAP", Const, 0}, + {"F_WRLCK", Const, 0}, + {"Faccessat", Func, 0}, + {"Fallocate", Func, 0}, + {"Fbootstraptransfer_t", Type, 0}, + {"Fbootstraptransfer_t.Buffer", Field, 0}, + {"Fbootstraptransfer_t.Length", Field, 0}, + {"Fbootstraptransfer_t.Offset", Field, 0}, + {"Fchdir", Func, 0}, + {"Fchflags", Func, 0}, + {"Fchmod", Func, 0}, + {"Fchmodat", Func, 0}, + {"Fchown", Func, 0}, + {"Fchownat", Func, 0}, + {"FcntlFlock", Func, 3}, + {"FdSet", Type, 0}, + {"FdSet.Bits", Field, 0}, + {"FdSet.X__fds_bits", Field, 0}, + {"Fdatasync", Func, 0}, + {"FileNotifyInformation", Type, 0}, + {"FileNotifyInformation.Action", Field, 0}, + {"FileNotifyInformation.FileName", Field, 0}, + {"FileNotifyInformation.FileNameLength", Field, 0}, + {"FileNotifyInformation.NextEntryOffset", Field, 0}, + {"Filetime", Type, 0}, + {"Filetime.HighDateTime", Field, 0}, + {"Filetime.LowDateTime", Field, 0}, + {"FindClose", Func, 0}, + {"FindFirstFile", Func, 0}, + {"FindNextFile", Func, 0}, + {"Flock", Func, 0}, + {"Flock_t", Type, 0}, + {"Flock_t.Len", Field, 0}, + {"Flock_t.Pad_cgo_0", Field, 0}, + {"Flock_t.Pad_cgo_1", Field, 3}, + {"Flock_t.Pid", Field, 0}, + {"Flock_t.Start", Field, 0}, + {"Flock_t.Sysid", Field, 0}, + {"Flock_t.Type", Field, 0}, + {"Flock_t.Whence", Field, 0}, + {"FlushBpf", Func, 0}, + {"FlushFileBuffers", Func, 0}, + {"FlushViewOfFile", Func, 0}, + {"ForkExec", Func, 0}, + {"ForkLock", Var, 0}, + {"FormatMessage", Func, 0}, + {"Fpathconf", Func, 0}, + {"FreeAddrInfoW", Func, 1}, + {"FreeEnvironmentStrings", Func, 0}, + {"FreeLibrary", Func, 0}, + {"Fsid", Type, 0}, + {"Fsid.Val", Field, 0}, + {"Fsid.X__fsid_val", Field, 2}, + {"Fsid.X__val", Field, 0}, + {"Fstat", Func, 0}, + {"Fstatat", Func, 12}, + {"Fstatfs", Func, 0}, + {"Fstore_t", Type, 0}, + {"Fstore_t.Bytesalloc", Field, 0}, + {"Fstore_t.Flags", Field, 0}, + {"Fstore_t.Length", Field, 0}, + {"Fstore_t.Offset", Field, 0}, + {"Fstore_t.Posmode", Field, 0}, + {"Fsync", Func, 0}, + {"Ftruncate", Func, 0}, + {"FullPath", Func, 4}, + {"Futimes", Func, 0}, + {"Futimesat", Func, 0}, + {"GENERIC_ALL", Const, 0}, + {"GENERIC_EXECUTE", Const, 0}, + {"GENERIC_READ", Const, 0}, + {"GENERIC_WRITE", Const, 0}, + {"GUID", Type, 1}, + {"GUID.Data1", Field, 1}, + {"GUID.Data2", Field, 1}, + {"GUID.Data3", Field, 1}, + {"GUID.Data4", Field, 1}, + {"GetAcceptExSockaddrs", Func, 0}, + {"GetAdaptersInfo", Func, 0}, + {"GetAddrInfoW", Func, 1}, + {"GetCommandLine", Func, 0}, + {"GetComputerName", Func, 0}, + {"GetConsoleMode", Func, 1}, + {"GetCurrentDirectory", Func, 0}, + {"GetCurrentProcess", Func, 0}, + {"GetEnvironmentStrings", Func, 0}, + {"GetEnvironmentVariable", Func, 0}, + {"GetExitCodeProcess", Func, 0}, + {"GetFileAttributes", Func, 0}, + {"GetFileAttributesEx", Func, 0}, + {"GetFileExInfoStandard", Const, 0}, + {"GetFileExMaxInfoLevel", Const, 0}, + {"GetFileInformationByHandle", Func, 0}, + {"GetFileType", Func, 0}, + {"GetFullPathName", Func, 0}, + {"GetHostByName", Func, 0}, + {"GetIfEntry", Func, 0}, + {"GetLastError", Func, 0}, + {"GetLengthSid", Func, 0}, + {"GetLongPathName", Func, 0}, + {"GetProcAddress", Func, 0}, + {"GetProcessTimes", Func, 0}, + {"GetProtoByName", Func, 0}, + {"GetQueuedCompletionStatus", Func, 0}, + {"GetServByName", Func, 0}, + {"GetShortPathName", Func, 0}, + {"GetStartupInfo", Func, 0}, + {"GetStdHandle", Func, 0}, + {"GetSystemTimeAsFileTime", Func, 0}, + {"GetTempPath", Func, 0}, + {"GetTimeZoneInformation", Func, 0}, + {"GetTokenInformation", Func, 0}, + {"GetUserNameEx", Func, 0}, + {"GetUserProfileDirectory", Func, 0}, + {"GetVersion", Func, 0}, + {"Getcwd", Func, 0}, + {"Getdents", Func, 0}, + {"Getdirentries", Func, 0}, + {"Getdtablesize", Func, 0}, + {"Getegid", Func, 0}, + {"Getenv", Func, 0}, + {"Geteuid", Func, 0}, + {"Getfsstat", Func, 0}, + {"Getgid", Func, 0}, + {"Getgroups", Func, 0}, + {"Getpagesize", Func, 0}, + {"Getpeername", Func, 0}, + {"Getpgid", Func, 0}, + {"Getpgrp", Func, 0}, + {"Getpid", Func, 0}, + {"Getppid", Func, 0}, + {"Getpriority", Func, 0}, + {"Getrlimit", Func, 0}, + {"Getrusage", Func, 0}, + {"Getsid", Func, 0}, + {"Getsockname", Func, 0}, + {"Getsockopt", Func, 1}, + {"GetsockoptByte", Func, 0}, + {"GetsockoptICMPv6Filter", Func, 2}, + {"GetsockoptIPMreq", Func, 0}, + {"GetsockoptIPMreqn", Func, 0}, + {"GetsockoptIPv6MTUInfo", Func, 2}, + {"GetsockoptIPv6Mreq", Func, 0}, + {"GetsockoptInet4Addr", Func, 0}, + {"GetsockoptInt", Func, 0}, + {"GetsockoptUcred", Func, 1}, + {"Gettid", Func, 0}, + {"Gettimeofday", Func, 0}, + {"Getuid", Func, 0}, + {"Getwd", Func, 0}, + {"Getxattr", Func, 1}, + {"HANDLE_FLAG_INHERIT", Const, 0}, + {"HKEY_CLASSES_ROOT", Const, 0}, + {"HKEY_CURRENT_CONFIG", Const, 0}, + {"HKEY_CURRENT_USER", Const, 0}, + {"HKEY_DYN_DATA", Const, 0}, + {"HKEY_LOCAL_MACHINE", Const, 0}, + {"HKEY_PERFORMANCE_DATA", Const, 0}, + {"HKEY_USERS", Const, 0}, + {"HUPCL", Const, 0}, + {"Handle", Type, 0}, + {"Hostent", Type, 0}, + {"Hostent.AddrList", Field, 0}, + {"Hostent.AddrType", Field, 0}, + {"Hostent.Aliases", Field, 0}, + {"Hostent.Length", Field, 0}, + {"Hostent.Name", Field, 0}, + {"ICANON", Const, 0}, + {"ICMP6_FILTER", Const, 2}, + {"ICMPV6_FILTER", Const, 2}, + {"ICMPv6Filter", Type, 2}, + {"ICMPv6Filter.Data", Field, 2}, + {"ICMPv6Filter.Filt", Field, 2}, + {"ICRNL", Const, 0}, + {"IEXTEN", Const, 0}, + {"IFAN_ARRIVAL", Const, 1}, + {"IFAN_DEPARTURE", Const, 1}, + {"IFA_ADDRESS", Const, 0}, + {"IFA_ANYCAST", Const, 0}, + {"IFA_BROADCAST", Const, 0}, + {"IFA_CACHEINFO", Const, 0}, + {"IFA_F_DADFAILED", Const, 0}, + {"IFA_F_DEPRECATED", Const, 0}, + {"IFA_F_HOMEADDRESS", Const, 0}, + {"IFA_F_NODAD", Const, 0}, + {"IFA_F_OPTIMISTIC", Const, 0}, + {"IFA_F_PERMANENT", Const, 0}, + {"IFA_F_SECONDARY", Const, 0}, + {"IFA_F_TEMPORARY", Const, 0}, + {"IFA_F_TENTATIVE", Const, 0}, + {"IFA_LABEL", Const, 0}, + {"IFA_LOCAL", Const, 0}, + {"IFA_MAX", Const, 0}, + {"IFA_MULTICAST", Const, 0}, + {"IFA_ROUTE", Const, 1}, + {"IFA_UNSPEC", Const, 0}, + {"IFF_ALLMULTI", Const, 0}, + {"IFF_ALTPHYS", Const, 0}, + {"IFF_AUTOMEDIA", Const, 0}, + {"IFF_BROADCAST", Const, 0}, + {"IFF_CANTCHANGE", Const, 0}, + {"IFF_CANTCONFIG", Const, 1}, + {"IFF_DEBUG", Const, 0}, + {"IFF_DRV_OACTIVE", Const, 0}, + {"IFF_DRV_RUNNING", Const, 0}, + {"IFF_DYING", Const, 0}, + {"IFF_DYNAMIC", Const, 0}, + {"IFF_LINK0", Const, 0}, + {"IFF_LINK1", Const, 0}, + {"IFF_LINK2", Const, 0}, + {"IFF_LOOPBACK", Const, 0}, + {"IFF_MASTER", Const, 0}, + {"IFF_MONITOR", Const, 0}, + {"IFF_MULTICAST", Const, 0}, + {"IFF_NOARP", Const, 0}, + {"IFF_NOTRAILERS", Const, 0}, + {"IFF_NO_PI", Const, 0}, + {"IFF_OACTIVE", Const, 0}, + {"IFF_ONE_QUEUE", Const, 0}, + {"IFF_POINTOPOINT", Const, 0}, + {"IFF_POINTTOPOINT", Const, 0}, + {"IFF_PORTSEL", Const, 0}, + {"IFF_PPROMISC", Const, 0}, + {"IFF_PROMISC", Const, 0}, + {"IFF_RENAMING", Const, 0}, + {"IFF_RUNNING", Const, 0}, + {"IFF_SIMPLEX", Const, 0}, + {"IFF_SLAVE", Const, 0}, + {"IFF_SMART", Const, 0}, + {"IFF_STATICARP", Const, 0}, + {"IFF_TAP", Const, 0}, + {"IFF_TUN", Const, 0}, + {"IFF_TUN_EXCL", Const, 0}, + {"IFF_UP", Const, 0}, + {"IFF_VNET_HDR", Const, 0}, + {"IFLA_ADDRESS", Const, 0}, + {"IFLA_BROADCAST", Const, 0}, + {"IFLA_COST", Const, 0}, + {"IFLA_IFALIAS", Const, 0}, + {"IFLA_IFNAME", Const, 0}, + {"IFLA_LINK", Const, 0}, + {"IFLA_LINKINFO", Const, 0}, + {"IFLA_LINKMODE", Const, 0}, + {"IFLA_MAP", Const, 0}, + {"IFLA_MASTER", Const, 0}, + {"IFLA_MAX", Const, 0}, + {"IFLA_MTU", Const, 0}, + {"IFLA_NET_NS_PID", Const, 0}, + {"IFLA_OPERSTATE", Const, 0}, + {"IFLA_PRIORITY", Const, 0}, + {"IFLA_PROTINFO", Const, 0}, + {"IFLA_QDISC", Const, 0}, + {"IFLA_STATS", Const, 0}, + {"IFLA_TXQLEN", Const, 0}, + {"IFLA_UNSPEC", Const, 0}, + {"IFLA_WEIGHT", Const, 0}, + {"IFLA_WIRELESS", Const, 0}, + {"IFNAMSIZ", Const, 0}, + {"IFT_1822", Const, 0}, + {"IFT_A12MPPSWITCH", Const, 0}, + {"IFT_AAL2", Const, 0}, + {"IFT_AAL5", Const, 0}, + {"IFT_ADSL", Const, 0}, + {"IFT_AFLANE8023", Const, 0}, + {"IFT_AFLANE8025", Const, 0}, + {"IFT_ARAP", Const, 0}, + {"IFT_ARCNET", Const, 0}, + {"IFT_ARCNETPLUS", Const, 0}, + {"IFT_ASYNC", Const, 0}, + {"IFT_ATM", Const, 0}, + {"IFT_ATMDXI", Const, 0}, + {"IFT_ATMFUNI", Const, 0}, + {"IFT_ATMIMA", Const, 0}, + {"IFT_ATMLOGICAL", Const, 0}, + {"IFT_ATMRADIO", Const, 0}, + {"IFT_ATMSUBINTERFACE", Const, 0}, + {"IFT_ATMVCIENDPT", Const, 0}, + {"IFT_ATMVIRTUAL", Const, 0}, + {"IFT_BGPPOLICYACCOUNTING", Const, 0}, + {"IFT_BLUETOOTH", Const, 1}, + {"IFT_BRIDGE", Const, 0}, + {"IFT_BSC", Const, 0}, + {"IFT_CARP", Const, 0}, + {"IFT_CCTEMUL", Const, 0}, + {"IFT_CELLULAR", Const, 0}, + {"IFT_CEPT", Const, 0}, + {"IFT_CES", Const, 0}, + {"IFT_CHANNEL", Const, 0}, + {"IFT_CNR", Const, 0}, + {"IFT_COFFEE", Const, 0}, + {"IFT_COMPOSITELINK", Const, 0}, + {"IFT_DCN", Const, 0}, + {"IFT_DIGITALPOWERLINE", Const, 0}, + {"IFT_DIGITALWRAPPEROVERHEADCHANNEL", Const, 0}, + {"IFT_DLSW", Const, 0}, + {"IFT_DOCSCABLEDOWNSTREAM", Const, 0}, + {"IFT_DOCSCABLEMACLAYER", Const, 0}, + {"IFT_DOCSCABLEUPSTREAM", Const, 0}, + {"IFT_DOCSCABLEUPSTREAMCHANNEL", Const, 1}, + {"IFT_DS0", Const, 0}, + {"IFT_DS0BUNDLE", Const, 0}, + {"IFT_DS1FDL", Const, 0}, + {"IFT_DS3", Const, 0}, + {"IFT_DTM", Const, 0}, + {"IFT_DUMMY", Const, 1}, + {"IFT_DVBASILN", Const, 0}, + {"IFT_DVBASIOUT", Const, 0}, + {"IFT_DVBRCCDOWNSTREAM", Const, 0}, + {"IFT_DVBRCCMACLAYER", Const, 0}, + {"IFT_DVBRCCUPSTREAM", Const, 0}, + {"IFT_ECONET", Const, 1}, + {"IFT_ENC", Const, 0}, + {"IFT_EON", Const, 0}, + {"IFT_EPLRS", Const, 0}, + {"IFT_ESCON", Const, 0}, + {"IFT_ETHER", Const, 0}, + {"IFT_FAITH", Const, 0}, + {"IFT_FAST", Const, 0}, + {"IFT_FASTETHER", Const, 0}, + {"IFT_FASTETHERFX", Const, 0}, + {"IFT_FDDI", Const, 0}, + {"IFT_FIBRECHANNEL", Const, 0}, + {"IFT_FRAMERELAYINTERCONNECT", Const, 0}, + {"IFT_FRAMERELAYMPI", Const, 0}, + {"IFT_FRDLCIENDPT", Const, 0}, + {"IFT_FRELAY", Const, 0}, + {"IFT_FRELAYDCE", Const, 0}, + {"IFT_FRF16MFRBUNDLE", Const, 0}, + {"IFT_FRFORWARD", Const, 0}, + {"IFT_G703AT2MB", Const, 0}, + {"IFT_G703AT64K", Const, 0}, + {"IFT_GIF", Const, 0}, + {"IFT_GIGABITETHERNET", Const, 0}, + {"IFT_GR303IDT", Const, 0}, + {"IFT_GR303RDT", Const, 0}, + {"IFT_H323GATEKEEPER", Const, 0}, + {"IFT_H323PROXY", Const, 0}, + {"IFT_HDH1822", Const, 0}, + {"IFT_HDLC", Const, 0}, + {"IFT_HDSL2", Const, 0}, + {"IFT_HIPERLAN2", Const, 0}, + {"IFT_HIPPI", Const, 0}, + {"IFT_HIPPIINTERFACE", Const, 0}, + {"IFT_HOSTPAD", Const, 0}, + {"IFT_HSSI", Const, 0}, + {"IFT_HY", Const, 0}, + {"IFT_IBM370PARCHAN", Const, 0}, + {"IFT_IDSL", Const, 0}, + {"IFT_IEEE1394", Const, 0}, + {"IFT_IEEE80211", Const, 0}, + {"IFT_IEEE80212", Const, 0}, + {"IFT_IEEE8023ADLAG", Const, 0}, + {"IFT_IFGSN", Const, 0}, + {"IFT_IMT", Const, 0}, + {"IFT_INFINIBAND", Const, 1}, + {"IFT_INTERLEAVE", Const, 0}, + {"IFT_IP", Const, 0}, + {"IFT_IPFORWARD", Const, 0}, + {"IFT_IPOVERATM", Const, 0}, + {"IFT_IPOVERCDLC", Const, 0}, + {"IFT_IPOVERCLAW", Const, 0}, + {"IFT_IPSWITCH", Const, 0}, + {"IFT_IPXIP", Const, 0}, + {"IFT_ISDN", Const, 0}, + {"IFT_ISDNBASIC", Const, 0}, + {"IFT_ISDNPRIMARY", Const, 0}, + {"IFT_ISDNS", Const, 0}, + {"IFT_ISDNU", Const, 0}, + {"IFT_ISO88022LLC", Const, 0}, + {"IFT_ISO88023", Const, 0}, + {"IFT_ISO88024", Const, 0}, + {"IFT_ISO88025", Const, 0}, + {"IFT_ISO88025CRFPINT", Const, 0}, + {"IFT_ISO88025DTR", Const, 0}, + {"IFT_ISO88025FIBER", Const, 0}, + {"IFT_ISO88026", Const, 0}, + {"IFT_ISUP", Const, 0}, + {"IFT_L2VLAN", Const, 0}, + {"IFT_L3IPVLAN", Const, 0}, + {"IFT_L3IPXVLAN", Const, 0}, + {"IFT_LAPB", Const, 0}, + {"IFT_LAPD", Const, 0}, + {"IFT_LAPF", Const, 0}, + {"IFT_LINEGROUP", Const, 1}, + {"IFT_LOCALTALK", Const, 0}, + {"IFT_LOOP", Const, 0}, + {"IFT_MEDIAMAILOVERIP", Const, 0}, + {"IFT_MFSIGLINK", Const, 0}, + {"IFT_MIOX25", Const, 0}, + {"IFT_MODEM", Const, 0}, + {"IFT_MPC", Const, 0}, + {"IFT_MPLS", Const, 0}, + {"IFT_MPLSTUNNEL", Const, 0}, + {"IFT_MSDSL", Const, 0}, + {"IFT_MVL", Const, 0}, + {"IFT_MYRINET", Const, 0}, + {"IFT_NFAS", Const, 0}, + {"IFT_NSIP", Const, 0}, + {"IFT_OPTICALCHANNEL", Const, 0}, + {"IFT_OPTICALTRANSPORT", Const, 0}, + {"IFT_OTHER", Const, 0}, + {"IFT_P10", Const, 0}, + {"IFT_P80", Const, 0}, + {"IFT_PARA", Const, 0}, + {"IFT_PDP", Const, 0}, + {"IFT_PFLOG", Const, 0}, + {"IFT_PFLOW", Const, 1}, + {"IFT_PFSYNC", Const, 0}, + {"IFT_PLC", Const, 0}, + {"IFT_PON155", Const, 1}, + {"IFT_PON622", Const, 1}, + {"IFT_POS", Const, 0}, + {"IFT_PPP", Const, 0}, + {"IFT_PPPMULTILINKBUNDLE", Const, 0}, + {"IFT_PROPATM", Const, 1}, + {"IFT_PROPBWAP2MP", Const, 0}, + {"IFT_PROPCNLS", Const, 0}, + {"IFT_PROPDOCSWIRELESSDOWNSTREAM", Const, 0}, + {"IFT_PROPDOCSWIRELESSMACLAYER", Const, 0}, + {"IFT_PROPDOCSWIRELESSUPSTREAM", Const, 0}, + {"IFT_PROPMUX", Const, 0}, + {"IFT_PROPVIRTUAL", Const, 0}, + {"IFT_PROPWIRELESSP2P", Const, 0}, + {"IFT_PTPSERIAL", Const, 0}, + {"IFT_PVC", Const, 0}, + {"IFT_Q2931", Const, 1}, + {"IFT_QLLC", Const, 0}, + {"IFT_RADIOMAC", Const, 0}, + {"IFT_RADSL", Const, 0}, + {"IFT_REACHDSL", Const, 0}, + {"IFT_RFC1483", Const, 0}, + {"IFT_RS232", Const, 0}, + {"IFT_RSRB", Const, 0}, + {"IFT_SDLC", Const, 0}, + {"IFT_SDSL", Const, 0}, + {"IFT_SHDSL", Const, 0}, + {"IFT_SIP", Const, 0}, + {"IFT_SIPSIG", Const, 1}, + {"IFT_SIPTG", Const, 1}, + {"IFT_SLIP", Const, 0}, + {"IFT_SMDSDXI", Const, 0}, + {"IFT_SMDSICIP", Const, 0}, + {"IFT_SONET", Const, 0}, + {"IFT_SONETOVERHEADCHANNEL", Const, 0}, + {"IFT_SONETPATH", Const, 0}, + {"IFT_SONETVT", Const, 0}, + {"IFT_SRP", Const, 0}, + {"IFT_SS7SIGLINK", Const, 0}, + {"IFT_STACKTOSTACK", Const, 0}, + {"IFT_STARLAN", Const, 0}, + {"IFT_STF", Const, 0}, + {"IFT_T1", Const, 0}, + {"IFT_TDLC", Const, 0}, + {"IFT_TELINK", Const, 1}, + {"IFT_TERMPAD", Const, 0}, + {"IFT_TR008", Const, 0}, + {"IFT_TRANSPHDLC", Const, 0}, + {"IFT_TUNNEL", Const, 0}, + {"IFT_ULTRA", Const, 0}, + {"IFT_USB", Const, 0}, + {"IFT_V11", Const, 0}, + {"IFT_V35", Const, 0}, + {"IFT_V36", Const, 0}, + {"IFT_V37", Const, 0}, + {"IFT_VDSL", Const, 0}, + {"IFT_VIRTUALIPADDRESS", Const, 0}, + {"IFT_VIRTUALTG", Const, 1}, + {"IFT_VOICEDID", Const, 1}, + {"IFT_VOICEEM", Const, 0}, + {"IFT_VOICEEMFGD", Const, 1}, + {"IFT_VOICEENCAP", Const, 0}, + {"IFT_VOICEFGDEANA", Const, 1}, + {"IFT_VOICEFXO", Const, 0}, + {"IFT_VOICEFXS", Const, 0}, + {"IFT_VOICEOVERATM", Const, 0}, + {"IFT_VOICEOVERCABLE", Const, 1}, + {"IFT_VOICEOVERFRAMERELAY", Const, 0}, + {"IFT_VOICEOVERIP", Const, 0}, + {"IFT_X213", Const, 0}, + {"IFT_X25", Const, 0}, + {"IFT_X25DDN", Const, 0}, + {"IFT_X25HUNTGROUP", Const, 0}, + {"IFT_X25MLP", Const, 0}, + {"IFT_X25PLE", Const, 0}, + {"IFT_XETHER", Const, 0}, + {"IGNBRK", Const, 0}, + {"IGNCR", Const, 0}, + {"IGNORE", Const, 0}, + {"IGNPAR", Const, 0}, + {"IMAXBEL", Const, 0}, + {"INFINITE", Const, 0}, + {"INLCR", Const, 0}, + {"INPCK", Const, 0}, + {"INVALID_FILE_ATTRIBUTES", Const, 0}, + {"IN_ACCESS", Const, 0}, + {"IN_ALL_EVENTS", Const, 0}, + {"IN_ATTRIB", Const, 0}, + {"IN_CLASSA_HOST", Const, 0}, + {"IN_CLASSA_MAX", Const, 0}, + {"IN_CLASSA_NET", Const, 0}, + {"IN_CLASSA_NSHIFT", Const, 0}, + {"IN_CLASSB_HOST", Const, 0}, + {"IN_CLASSB_MAX", Const, 0}, + {"IN_CLASSB_NET", Const, 0}, + {"IN_CLASSB_NSHIFT", Const, 0}, + {"IN_CLASSC_HOST", Const, 0}, + {"IN_CLASSC_NET", Const, 0}, + {"IN_CLASSC_NSHIFT", Const, 0}, + {"IN_CLASSD_HOST", Const, 0}, + {"IN_CLASSD_NET", Const, 0}, + {"IN_CLASSD_NSHIFT", Const, 0}, + {"IN_CLOEXEC", Const, 0}, + {"IN_CLOSE", Const, 0}, + {"IN_CLOSE_NOWRITE", Const, 0}, + {"IN_CLOSE_WRITE", Const, 0}, + {"IN_CREATE", Const, 0}, + {"IN_DELETE", Const, 0}, + {"IN_DELETE_SELF", Const, 0}, + {"IN_DONT_FOLLOW", Const, 0}, + {"IN_EXCL_UNLINK", Const, 0}, + {"IN_IGNORED", Const, 0}, + {"IN_ISDIR", Const, 0}, + {"IN_LINKLOCALNETNUM", Const, 0}, + {"IN_LOOPBACKNET", Const, 0}, + {"IN_MASK_ADD", Const, 0}, + {"IN_MODIFY", Const, 0}, + {"IN_MOVE", Const, 0}, + {"IN_MOVED_FROM", Const, 0}, + {"IN_MOVED_TO", Const, 0}, + {"IN_MOVE_SELF", Const, 0}, + {"IN_NONBLOCK", Const, 0}, + {"IN_ONESHOT", Const, 0}, + {"IN_ONLYDIR", Const, 0}, + {"IN_OPEN", Const, 0}, + {"IN_Q_OVERFLOW", Const, 0}, + {"IN_RFC3021_HOST", Const, 1}, + {"IN_RFC3021_MASK", Const, 1}, + {"IN_RFC3021_NET", Const, 1}, + {"IN_RFC3021_NSHIFT", Const, 1}, + {"IN_UNMOUNT", Const, 0}, + {"IOC_IN", Const, 1}, + {"IOC_INOUT", Const, 1}, + {"IOC_OUT", Const, 1}, + {"IOC_VENDOR", Const, 3}, + {"IOC_WS2", Const, 1}, + {"IO_REPARSE_TAG_SYMLINK", Const, 4}, + {"IPMreq", Type, 0}, + {"IPMreq.Interface", Field, 0}, + {"IPMreq.Multiaddr", Field, 0}, + {"IPMreqn", Type, 0}, + {"IPMreqn.Address", Field, 0}, + {"IPMreqn.Ifindex", Field, 0}, + {"IPMreqn.Multiaddr", Field, 0}, + {"IPPROTO_3PC", Const, 0}, + {"IPPROTO_ADFS", Const, 0}, + {"IPPROTO_AH", Const, 0}, + {"IPPROTO_AHIP", Const, 0}, + {"IPPROTO_APES", Const, 0}, + {"IPPROTO_ARGUS", Const, 0}, + {"IPPROTO_AX25", Const, 0}, + {"IPPROTO_BHA", Const, 0}, + {"IPPROTO_BLT", Const, 0}, + {"IPPROTO_BRSATMON", Const, 0}, + {"IPPROTO_CARP", Const, 0}, + {"IPPROTO_CFTP", Const, 0}, + {"IPPROTO_CHAOS", Const, 0}, + {"IPPROTO_CMTP", Const, 0}, + {"IPPROTO_COMP", Const, 0}, + {"IPPROTO_CPHB", Const, 0}, + {"IPPROTO_CPNX", Const, 0}, + {"IPPROTO_DCCP", Const, 0}, + {"IPPROTO_DDP", Const, 0}, + {"IPPROTO_DGP", Const, 0}, + {"IPPROTO_DIVERT", Const, 0}, + {"IPPROTO_DIVERT_INIT", Const, 3}, + {"IPPROTO_DIVERT_RESP", Const, 3}, + {"IPPROTO_DONE", Const, 0}, + {"IPPROTO_DSTOPTS", Const, 0}, + {"IPPROTO_EGP", Const, 0}, + {"IPPROTO_EMCON", Const, 0}, + {"IPPROTO_ENCAP", Const, 0}, + {"IPPROTO_EON", Const, 0}, + {"IPPROTO_ESP", Const, 0}, + {"IPPROTO_ETHERIP", Const, 0}, + {"IPPROTO_FRAGMENT", Const, 0}, + {"IPPROTO_GGP", Const, 0}, + {"IPPROTO_GMTP", Const, 0}, + {"IPPROTO_GRE", Const, 0}, + {"IPPROTO_HELLO", Const, 0}, + {"IPPROTO_HMP", Const, 0}, + {"IPPROTO_HOPOPTS", Const, 0}, + {"IPPROTO_ICMP", Const, 0}, + {"IPPROTO_ICMPV6", Const, 0}, + {"IPPROTO_IDP", Const, 0}, + {"IPPROTO_IDPR", Const, 0}, + {"IPPROTO_IDRP", Const, 0}, + {"IPPROTO_IGMP", Const, 0}, + {"IPPROTO_IGP", Const, 0}, + {"IPPROTO_IGRP", Const, 0}, + {"IPPROTO_IL", Const, 0}, + {"IPPROTO_INLSP", Const, 0}, + {"IPPROTO_INP", Const, 0}, + {"IPPROTO_IP", Const, 0}, + {"IPPROTO_IPCOMP", Const, 0}, + {"IPPROTO_IPCV", Const, 0}, + {"IPPROTO_IPEIP", Const, 0}, + {"IPPROTO_IPIP", Const, 0}, + {"IPPROTO_IPPC", Const, 0}, + {"IPPROTO_IPV4", Const, 0}, + {"IPPROTO_IPV6", Const, 0}, + {"IPPROTO_IPV6_ICMP", Const, 1}, + {"IPPROTO_IRTP", Const, 0}, + {"IPPROTO_KRYPTOLAN", Const, 0}, + {"IPPROTO_LARP", Const, 0}, + {"IPPROTO_LEAF1", Const, 0}, + {"IPPROTO_LEAF2", Const, 0}, + {"IPPROTO_MAX", Const, 0}, + {"IPPROTO_MAXID", Const, 0}, + {"IPPROTO_MEAS", Const, 0}, + {"IPPROTO_MH", Const, 1}, + {"IPPROTO_MHRP", Const, 0}, + {"IPPROTO_MICP", Const, 0}, + {"IPPROTO_MOBILE", Const, 0}, + {"IPPROTO_MPLS", Const, 1}, + {"IPPROTO_MTP", Const, 0}, + {"IPPROTO_MUX", Const, 0}, + {"IPPROTO_ND", Const, 0}, + {"IPPROTO_NHRP", Const, 0}, + {"IPPROTO_NONE", Const, 0}, + {"IPPROTO_NSP", Const, 0}, + {"IPPROTO_NVPII", Const, 0}, + {"IPPROTO_OLD_DIVERT", Const, 0}, + {"IPPROTO_OSPFIGP", Const, 0}, + {"IPPROTO_PFSYNC", Const, 0}, + {"IPPROTO_PGM", Const, 0}, + {"IPPROTO_PIGP", Const, 0}, + {"IPPROTO_PIM", Const, 0}, + {"IPPROTO_PRM", Const, 0}, + {"IPPROTO_PUP", Const, 0}, + {"IPPROTO_PVP", Const, 0}, + {"IPPROTO_RAW", Const, 0}, + {"IPPROTO_RCCMON", Const, 0}, + {"IPPROTO_RDP", Const, 0}, + {"IPPROTO_ROUTING", Const, 0}, + {"IPPROTO_RSVP", Const, 0}, + {"IPPROTO_RVD", Const, 0}, + {"IPPROTO_SATEXPAK", Const, 0}, + {"IPPROTO_SATMON", Const, 0}, + {"IPPROTO_SCCSP", Const, 0}, + {"IPPROTO_SCTP", Const, 0}, + {"IPPROTO_SDRP", Const, 0}, + {"IPPROTO_SEND", Const, 1}, + {"IPPROTO_SEP", Const, 0}, + {"IPPROTO_SKIP", Const, 0}, + {"IPPROTO_SPACER", Const, 0}, + {"IPPROTO_SRPC", Const, 0}, + {"IPPROTO_ST", Const, 0}, + {"IPPROTO_SVMTP", Const, 0}, + {"IPPROTO_SWIPE", Const, 0}, + {"IPPROTO_TCF", Const, 0}, + {"IPPROTO_TCP", Const, 0}, + {"IPPROTO_TLSP", Const, 0}, + {"IPPROTO_TP", Const, 0}, + {"IPPROTO_TPXX", Const, 0}, + {"IPPROTO_TRUNK1", Const, 0}, + {"IPPROTO_TRUNK2", Const, 0}, + {"IPPROTO_TTP", Const, 0}, + {"IPPROTO_UDP", Const, 0}, + {"IPPROTO_UDPLITE", Const, 0}, + {"IPPROTO_VINES", Const, 0}, + {"IPPROTO_VISA", Const, 0}, + {"IPPROTO_VMTP", Const, 0}, + {"IPPROTO_VRRP", Const, 1}, + {"IPPROTO_WBEXPAK", Const, 0}, + {"IPPROTO_WBMON", Const, 0}, + {"IPPROTO_WSN", Const, 0}, + {"IPPROTO_XNET", Const, 0}, + {"IPPROTO_XTP", Const, 0}, + {"IPV6_2292DSTOPTS", Const, 0}, + {"IPV6_2292HOPLIMIT", Const, 0}, + {"IPV6_2292HOPOPTS", Const, 0}, + {"IPV6_2292NEXTHOP", Const, 0}, + {"IPV6_2292PKTINFO", Const, 0}, + {"IPV6_2292PKTOPTIONS", Const, 0}, + {"IPV6_2292RTHDR", Const, 0}, + {"IPV6_ADDRFORM", Const, 0}, + {"IPV6_ADD_MEMBERSHIP", Const, 0}, + {"IPV6_AUTHHDR", Const, 0}, + {"IPV6_AUTH_LEVEL", Const, 1}, + {"IPV6_AUTOFLOWLABEL", Const, 0}, + {"IPV6_BINDANY", Const, 0}, + {"IPV6_BINDV6ONLY", Const, 0}, + {"IPV6_BOUND_IF", Const, 0}, + {"IPV6_CHECKSUM", Const, 0}, + {"IPV6_DEFAULT_MULTICAST_HOPS", Const, 0}, + {"IPV6_DEFAULT_MULTICAST_LOOP", Const, 0}, + {"IPV6_DEFHLIM", Const, 0}, + {"IPV6_DONTFRAG", Const, 0}, + {"IPV6_DROP_MEMBERSHIP", Const, 0}, + {"IPV6_DSTOPTS", Const, 0}, + {"IPV6_ESP_NETWORK_LEVEL", Const, 1}, + {"IPV6_ESP_TRANS_LEVEL", Const, 1}, + {"IPV6_FAITH", Const, 0}, + {"IPV6_FLOWINFO_MASK", Const, 0}, + {"IPV6_FLOWLABEL_MASK", Const, 0}, + {"IPV6_FRAGTTL", Const, 0}, + {"IPV6_FW_ADD", Const, 0}, + {"IPV6_FW_DEL", Const, 0}, + {"IPV6_FW_FLUSH", Const, 0}, + {"IPV6_FW_GET", Const, 0}, + {"IPV6_FW_ZERO", Const, 0}, + {"IPV6_HLIMDEC", Const, 0}, + {"IPV6_HOPLIMIT", Const, 0}, + {"IPV6_HOPOPTS", Const, 0}, + {"IPV6_IPCOMP_LEVEL", Const, 1}, + {"IPV6_IPSEC_POLICY", Const, 0}, + {"IPV6_JOIN_ANYCAST", Const, 0}, + {"IPV6_JOIN_GROUP", Const, 0}, + {"IPV6_LEAVE_ANYCAST", Const, 0}, + {"IPV6_LEAVE_GROUP", Const, 0}, + {"IPV6_MAXHLIM", Const, 0}, + {"IPV6_MAXOPTHDR", Const, 0}, + {"IPV6_MAXPACKET", Const, 0}, + {"IPV6_MAX_GROUP_SRC_FILTER", Const, 0}, + {"IPV6_MAX_MEMBERSHIPS", Const, 0}, + {"IPV6_MAX_SOCK_SRC_FILTER", Const, 0}, + {"IPV6_MIN_MEMBERSHIPS", Const, 0}, + {"IPV6_MMTU", Const, 0}, + {"IPV6_MSFILTER", Const, 0}, + {"IPV6_MTU", Const, 0}, + {"IPV6_MTU_DISCOVER", Const, 0}, + {"IPV6_MULTICAST_HOPS", Const, 0}, + {"IPV6_MULTICAST_IF", Const, 0}, + {"IPV6_MULTICAST_LOOP", Const, 0}, + {"IPV6_NEXTHOP", Const, 0}, + {"IPV6_OPTIONS", Const, 1}, + {"IPV6_PATHMTU", Const, 0}, + {"IPV6_PIPEX", Const, 1}, + {"IPV6_PKTINFO", Const, 0}, + {"IPV6_PMTUDISC_DO", Const, 0}, + {"IPV6_PMTUDISC_DONT", Const, 0}, + {"IPV6_PMTUDISC_PROBE", Const, 0}, + {"IPV6_PMTUDISC_WANT", Const, 0}, + {"IPV6_PORTRANGE", Const, 0}, + {"IPV6_PORTRANGE_DEFAULT", Const, 0}, + {"IPV6_PORTRANGE_HIGH", Const, 0}, + {"IPV6_PORTRANGE_LOW", Const, 0}, + {"IPV6_PREFER_TEMPADDR", Const, 0}, + {"IPV6_RECVDSTOPTS", Const, 0}, + {"IPV6_RECVDSTPORT", Const, 3}, + {"IPV6_RECVERR", Const, 0}, + {"IPV6_RECVHOPLIMIT", Const, 0}, + {"IPV6_RECVHOPOPTS", Const, 0}, + {"IPV6_RECVPATHMTU", Const, 0}, + {"IPV6_RECVPKTINFO", Const, 0}, + {"IPV6_RECVRTHDR", Const, 0}, + {"IPV6_RECVTCLASS", Const, 0}, + {"IPV6_ROUTER_ALERT", Const, 0}, + {"IPV6_RTABLE", Const, 1}, + {"IPV6_RTHDR", Const, 0}, + {"IPV6_RTHDRDSTOPTS", Const, 0}, + {"IPV6_RTHDR_LOOSE", Const, 0}, + {"IPV6_RTHDR_STRICT", Const, 0}, + {"IPV6_RTHDR_TYPE_0", Const, 0}, + {"IPV6_RXDSTOPTS", Const, 0}, + {"IPV6_RXHOPOPTS", Const, 0}, + {"IPV6_SOCKOPT_RESERVED1", Const, 0}, + {"IPV6_TCLASS", Const, 0}, + {"IPV6_UNICAST_HOPS", Const, 0}, + {"IPV6_USE_MIN_MTU", Const, 0}, + {"IPV6_V6ONLY", Const, 0}, + {"IPV6_VERSION", Const, 0}, + {"IPV6_VERSION_MASK", Const, 0}, + {"IPV6_XFRM_POLICY", Const, 0}, + {"IP_ADD_MEMBERSHIP", Const, 0}, + {"IP_ADD_SOURCE_MEMBERSHIP", Const, 0}, + {"IP_AUTH_LEVEL", Const, 1}, + {"IP_BINDANY", Const, 0}, + {"IP_BLOCK_SOURCE", Const, 0}, + {"IP_BOUND_IF", Const, 0}, + {"IP_DEFAULT_MULTICAST_LOOP", Const, 0}, + {"IP_DEFAULT_MULTICAST_TTL", Const, 0}, + {"IP_DF", Const, 0}, + {"IP_DIVERTFL", Const, 3}, + {"IP_DONTFRAG", Const, 0}, + {"IP_DROP_MEMBERSHIP", Const, 0}, + {"IP_DROP_SOURCE_MEMBERSHIP", Const, 0}, + {"IP_DUMMYNET3", Const, 0}, + {"IP_DUMMYNET_CONFIGURE", Const, 0}, + {"IP_DUMMYNET_DEL", Const, 0}, + {"IP_DUMMYNET_FLUSH", Const, 0}, + {"IP_DUMMYNET_GET", Const, 0}, + {"IP_EF", Const, 1}, + {"IP_ERRORMTU", Const, 1}, + {"IP_ESP_NETWORK_LEVEL", Const, 1}, + {"IP_ESP_TRANS_LEVEL", Const, 1}, + {"IP_FAITH", Const, 0}, + {"IP_FREEBIND", Const, 0}, + {"IP_FW3", Const, 0}, + {"IP_FW_ADD", Const, 0}, + {"IP_FW_DEL", Const, 0}, + {"IP_FW_FLUSH", Const, 0}, + {"IP_FW_GET", Const, 0}, + {"IP_FW_NAT_CFG", Const, 0}, + {"IP_FW_NAT_DEL", Const, 0}, + {"IP_FW_NAT_GET_CONFIG", Const, 0}, + {"IP_FW_NAT_GET_LOG", Const, 0}, + {"IP_FW_RESETLOG", Const, 0}, + {"IP_FW_TABLE_ADD", Const, 0}, + {"IP_FW_TABLE_DEL", Const, 0}, + {"IP_FW_TABLE_FLUSH", Const, 0}, + {"IP_FW_TABLE_GETSIZE", Const, 0}, + {"IP_FW_TABLE_LIST", Const, 0}, + {"IP_FW_ZERO", Const, 0}, + {"IP_HDRINCL", Const, 0}, + {"IP_IPCOMP_LEVEL", Const, 1}, + {"IP_IPSECFLOWINFO", Const, 1}, + {"IP_IPSEC_LOCAL_AUTH", Const, 1}, + {"IP_IPSEC_LOCAL_CRED", Const, 1}, + {"IP_IPSEC_LOCAL_ID", Const, 1}, + {"IP_IPSEC_POLICY", Const, 0}, + {"IP_IPSEC_REMOTE_AUTH", Const, 1}, + {"IP_IPSEC_REMOTE_CRED", Const, 1}, + {"IP_IPSEC_REMOTE_ID", Const, 1}, + {"IP_MAXPACKET", Const, 0}, + {"IP_MAX_GROUP_SRC_FILTER", Const, 0}, + {"IP_MAX_MEMBERSHIPS", Const, 0}, + {"IP_MAX_SOCK_MUTE_FILTER", Const, 0}, + {"IP_MAX_SOCK_SRC_FILTER", Const, 0}, + {"IP_MAX_SOURCE_FILTER", Const, 0}, + {"IP_MF", Const, 0}, + {"IP_MINFRAGSIZE", Const, 1}, + {"IP_MINTTL", Const, 0}, + {"IP_MIN_MEMBERSHIPS", Const, 0}, + {"IP_MSFILTER", Const, 0}, + {"IP_MSS", Const, 0}, + {"IP_MTU", Const, 0}, + {"IP_MTU_DISCOVER", Const, 0}, + {"IP_MULTICAST_IF", Const, 0}, + {"IP_MULTICAST_IFINDEX", Const, 0}, + {"IP_MULTICAST_LOOP", Const, 0}, + {"IP_MULTICAST_TTL", Const, 0}, + {"IP_MULTICAST_VIF", Const, 0}, + {"IP_NAT__XXX", Const, 0}, + {"IP_OFFMASK", Const, 0}, + {"IP_OLD_FW_ADD", Const, 0}, + {"IP_OLD_FW_DEL", Const, 0}, + {"IP_OLD_FW_FLUSH", Const, 0}, + {"IP_OLD_FW_GET", Const, 0}, + {"IP_OLD_FW_RESETLOG", Const, 0}, + {"IP_OLD_FW_ZERO", Const, 0}, + {"IP_ONESBCAST", Const, 0}, + {"IP_OPTIONS", Const, 0}, + {"IP_ORIGDSTADDR", Const, 0}, + {"IP_PASSSEC", Const, 0}, + {"IP_PIPEX", Const, 1}, + {"IP_PKTINFO", Const, 0}, + {"IP_PKTOPTIONS", Const, 0}, + {"IP_PMTUDISC", Const, 0}, + {"IP_PMTUDISC_DO", Const, 0}, + {"IP_PMTUDISC_DONT", Const, 0}, + {"IP_PMTUDISC_PROBE", Const, 0}, + {"IP_PMTUDISC_WANT", Const, 0}, + {"IP_PORTRANGE", Const, 0}, + {"IP_PORTRANGE_DEFAULT", Const, 0}, + {"IP_PORTRANGE_HIGH", Const, 0}, + {"IP_PORTRANGE_LOW", Const, 0}, + {"IP_RECVDSTADDR", Const, 0}, + {"IP_RECVDSTPORT", Const, 1}, + {"IP_RECVERR", Const, 0}, + {"IP_RECVIF", Const, 0}, + {"IP_RECVOPTS", Const, 0}, + {"IP_RECVORIGDSTADDR", Const, 0}, + {"IP_RECVPKTINFO", Const, 0}, + {"IP_RECVRETOPTS", Const, 0}, + {"IP_RECVRTABLE", Const, 1}, + {"IP_RECVTOS", Const, 0}, + {"IP_RECVTTL", Const, 0}, + {"IP_RETOPTS", Const, 0}, + {"IP_RF", Const, 0}, + {"IP_ROUTER_ALERT", Const, 0}, + {"IP_RSVP_OFF", Const, 0}, + {"IP_RSVP_ON", Const, 0}, + {"IP_RSVP_VIF_OFF", Const, 0}, + {"IP_RSVP_VIF_ON", Const, 0}, + {"IP_RTABLE", Const, 1}, + {"IP_SENDSRCADDR", Const, 0}, + {"IP_STRIPHDR", Const, 0}, + {"IP_TOS", Const, 0}, + {"IP_TRAFFIC_MGT_BACKGROUND", Const, 0}, + {"IP_TRANSPARENT", Const, 0}, + {"IP_TTL", Const, 0}, + {"IP_UNBLOCK_SOURCE", Const, 0}, + {"IP_XFRM_POLICY", Const, 0}, + {"IPv6MTUInfo", Type, 2}, + {"IPv6MTUInfo.Addr", Field, 2}, + {"IPv6MTUInfo.Mtu", Field, 2}, + {"IPv6Mreq", Type, 0}, + {"IPv6Mreq.Interface", Field, 0}, + {"IPv6Mreq.Multiaddr", Field, 0}, + {"ISIG", Const, 0}, + {"ISTRIP", Const, 0}, + {"IUCLC", Const, 0}, + {"IUTF8", Const, 0}, + {"IXANY", Const, 0}, + {"IXOFF", Const, 0}, + {"IXON", Const, 0}, + {"IfAddrmsg", Type, 0}, + {"IfAddrmsg.Family", Field, 0}, + {"IfAddrmsg.Flags", Field, 0}, + {"IfAddrmsg.Index", Field, 0}, + {"IfAddrmsg.Prefixlen", Field, 0}, + {"IfAddrmsg.Scope", Field, 0}, + {"IfAnnounceMsghdr", Type, 1}, + {"IfAnnounceMsghdr.Hdrlen", Field, 2}, + {"IfAnnounceMsghdr.Index", Field, 1}, + {"IfAnnounceMsghdr.Msglen", Field, 1}, + {"IfAnnounceMsghdr.Name", Field, 1}, + {"IfAnnounceMsghdr.Type", Field, 1}, + {"IfAnnounceMsghdr.Version", Field, 1}, + {"IfAnnounceMsghdr.What", Field, 1}, + {"IfData", Type, 0}, + {"IfData.Addrlen", Field, 0}, + {"IfData.Baudrate", Field, 0}, + {"IfData.Capabilities", Field, 2}, + {"IfData.Collisions", Field, 0}, + {"IfData.Datalen", Field, 0}, + {"IfData.Epoch", Field, 0}, + {"IfData.Hdrlen", Field, 0}, + {"IfData.Hwassist", Field, 0}, + {"IfData.Ibytes", Field, 0}, + {"IfData.Ierrors", Field, 0}, + {"IfData.Imcasts", Field, 0}, + {"IfData.Ipackets", Field, 0}, + {"IfData.Iqdrops", Field, 0}, + {"IfData.Lastchange", Field, 0}, + {"IfData.Link_state", Field, 0}, + {"IfData.Mclpool", Field, 2}, + {"IfData.Metric", Field, 0}, + {"IfData.Mtu", Field, 0}, + {"IfData.Noproto", Field, 0}, + {"IfData.Obytes", Field, 0}, + {"IfData.Oerrors", Field, 0}, + {"IfData.Omcasts", Field, 0}, + {"IfData.Opackets", Field, 0}, + {"IfData.Pad", Field, 2}, + {"IfData.Pad_cgo_0", Field, 2}, + {"IfData.Pad_cgo_1", Field, 2}, + {"IfData.Physical", Field, 0}, + {"IfData.Recvquota", Field, 0}, + {"IfData.Recvtiming", Field, 0}, + {"IfData.Reserved1", Field, 0}, + {"IfData.Reserved2", Field, 0}, + {"IfData.Spare_char1", Field, 0}, + {"IfData.Spare_char2", Field, 0}, + {"IfData.Type", Field, 0}, + {"IfData.Typelen", Field, 0}, + {"IfData.Unused1", Field, 0}, + {"IfData.Unused2", Field, 0}, + {"IfData.Xmitquota", Field, 0}, + {"IfData.Xmittiming", Field, 0}, + {"IfInfomsg", Type, 0}, + {"IfInfomsg.Change", Field, 0}, + {"IfInfomsg.Family", Field, 0}, + {"IfInfomsg.Flags", Field, 0}, + {"IfInfomsg.Index", Field, 0}, + {"IfInfomsg.Type", Field, 0}, + {"IfInfomsg.X__ifi_pad", Field, 0}, + {"IfMsghdr", Type, 0}, + {"IfMsghdr.Addrs", Field, 0}, + {"IfMsghdr.Data", Field, 0}, + {"IfMsghdr.Flags", Field, 0}, + {"IfMsghdr.Hdrlen", Field, 2}, + {"IfMsghdr.Index", Field, 0}, + {"IfMsghdr.Msglen", Field, 0}, + {"IfMsghdr.Pad1", Field, 2}, + {"IfMsghdr.Pad2", Field, 2}, + {"IfMsghdr.Pad_cgo_0", Field, 0}, + {"IfMsghdr.Pad_cgo_1", Field, 2}, + {"IfMsghdr.Tableid", Field, 2}, + {"IfMsghdr.Type", Field, 0}, + {"IfMsghdr.Version", Field, 0}, + {"IfMsghdr.Xflags", Field, 2}, + {"IfaMsghdr", Type, 0}, + {"IfaMsghdr.Addrs", Field, 0}, + {"IfaMsghdr.Flags", Field, 0}, + {"IfaMsghdr.Hdrlen", Field, 2}, + {"IfaMsghdr.Index", Field, 0}, + {"IfaMsghdr.Metric", Field, 0}, + {"IfaMsghdr.Msglen", Field, 0}, + {"IfaMsghdr.Pad1", Field, 2}, + {"IfaMsghdr.Pad2", Field, 2}, + {"IfaMsghdr.Pad_cgo_0", Field, 0}, + {"IfaMsghdr.Tableid", Field, 2}, + {"IfaMsghdr.Type", Field, 0}, + {"IfaMsghdr.Version", Field, 0}, + {"IfmaMsghdr", Type, 0}, + {"IfmaMsghdr.Addrs", Field, 0}, + {"IfmaMsghdr.Flags", Field, 0}, + {"IfmaMsghdr.Index", Field, 0}, + {"IfmaMsghdr.Msglen", Field, 0}, + {"IfmaMsghdr.Pad_cgo_0", Field, 0}, + {"IfmaMsghdr.Type", Field, 0}, + {"IfmaMsghdr.Version", Field, 0}, + {"IfmaMsghdr2", Type, 0}, + {"IfmaMsghdr2.Addrs", Field, 0}, + {"IfmaMsghdr2.Flags", Field, 0}, + {"IfmaMsghdr2.Index", Field, 0}, + {"IfmaMsghdr2.Msglen", Field, 0}, + {"IfmaMsghdr2.Pad_cgo_0", Field, 0}, + {"IfmaMsghdr2.Refcount", Field, 0}, + {"IfmaMsghdr2.Type", Field, 0}, + {"IfmaMsghdr2.Version", Field, 0}, + {"ImplementsGetwd", Const, 0}, + {"Inet4Pktinfo", Type, 0}, + {"Inet4Pktinfo.Addr", Field, 0}, + {"Inet4Pktinfo.Ifindex", Field, 0}, + {"Inet4Pktinfo.Spec_dst", Field, 0}, + {"Inet6Pktinfo", Type, 0}, + {"Inet6Pktinfo.Addr", Field, 0}, + {"Inet6Pktinfo.Ifindex", Field, 0}, + {"InotifyAddWatch", Func, 0}, + {"InotifyEvent", Type, 0}, + {"InotifyEvent.Cookie", Field, 0}, + {"InotifyEvent.Len", Field, 0}, + {"InotifyEvent.Mask", Field, 0}, + {"InotifyEvent.Name", Field, 0}, + {"InotifyEvent.Wd", Field, 0}, + {"InotifyInit", Func, 0}, + {"InotifyInit1", Func, 0}, + {"InotifyRmWatch", Func, 0}, + {"InterfaceAddrMessage", Type, 0}, + {"InterfaceAddrMessage.Data", Field, 0}, + {"InterfaceAddrMessage.Header", Field, 0}, + {"InterfaceAnnounceMessage", Type, 1}, + {"InterfaceAnnounceMessage.Header", Field, 1}, + {"InterfaceInfo", Type, 0}, + {"InterfaceInfo.Address", Field, 0}, + {"InterfaceInfo.BroadcastAddress", Field, 0}, + {"InterfaceInfo.Flags", Field, 0}, + {"InterfaceInfo.Netmask", Field, 0}, + {"InterfaceMessage", Type, 0}, + {"InterfaceMessage.Data", Field, 0}, + {"InterfaceMessage.Header", Field, 0}, + {"InterfaceMulticastAddrMessage", Type, 0}, + {"InterfaceMulticastAddrMessage.Data", Field, 0}, + {"InterfaceMulticastAddrMessage.Header", Field, 0}, + {"InvalidHandle", Const, 0}, + {"Ioperm", Func, 0}, + {"Iopl", Func, 0}, + {"Iovec", Type, 0}, + {"Iovec.Base", Field, 0}, + {"Iovec.Len", Field, 0}, + {"IpAdapterInfo", Type, 0}, + {"IpAdapterInfo.AdapterName", Field, 0}, + {"IpAdapterInfo.Address", Field, 0}, + {"IpAdapterInfo.AddressLength", Field, 0}, + {"IpAdapterInfo.ComboIndex", Field, 0}, + {"IpAdapterInfo.CurrentIpAddress", Field, 0}, + {"IpAdapterInfo.Description", Field, 0}, + {"IpAdapterInfo.DhcpEnabled", Field, 0}, + {"IpAdapterInfo.DhcpServer", Field, 0}, + {"IpAdapterInfo.GatewayList", Field, 0}, + {"IpAdapterInfo.HaveWins", Field, 0}, + {"IpAdapterInfo.Index", Field, 0}, + {"IpAdapterInfo.IpAddressList", Field, 0}, + {"IpAdapterInfo.LeaseExpires", Field, 0}, + {"IpAdapterInfo.LeaseObtained", Field, 0}, + {"IpAdapterInfo.Next", Field, 0}, + {"IpAdapterInfo.PrimaryWinsServer", Field, 0}, + {"IpAdapterInfo.SecondaryWinsServer", Field, 0}, + {"IpAdapterInfo.Type", Field, 0}, + {"IpAddrString", Type, 0}, + {"IpAddrString.Context", Field, 0}, + {"IpAddrString.IpAddress", Field, 0}, + {"IpAddrString.IpMask", Field, 0}, + {"IpAddrString.Next", Field, 0}, + {"IpAddressString", Type, 0}, + {"IpAddressString.String", Field, 0}, + {"IpMaskString", Type, 0}, + {"IpMaskString.String", Field, 2}, + {"Issetugid", Func, 0}, + {"KEY_ALL_ACCESS", Const, 0}, + {"KEY_CREATE_LINK", Const, 0}, + {"KEY_CREATE_SUB_KEY", Const, 0}, + {"KEY_ENUMERATE_SUB_KEYS", Const, 0}, + {"KEY_EXECUTE", Const, 0}, + {"KEY_NOTIFY", Const, 0}, + {"KEY_QUERY_VALUE", Const, 0}, + {"KEY_READ", Const, 0}, + {"KEY_SET_VALUE", Const, 0}, + {"KEY_WOW64_32KEY", Const, 0}, + {"KEY_WOW64_64KEY", Const, 0}, + {"KEY_WRITE", Const, 0}, + {"Kevent", Func, 0}, + {"Kevent_t", Type, 0}, + {"Kevent_t.Data", Field, 0}, + {"Kevent_t.Fflags", Field, 0}, + {"Kevent_t.Filter", Field, 0}, + {"Kevent_t.Flags", Field, 0}, + {"Kevent_t.Ident", Field, 0}, + {"Kevent_t.Pad_cgo_0", Field, 2}, + {"Kevent_t.Udata", Field, 0}, + {"Kill", Func, 0}, + {"Klogctl", Func, 0}, + {"Kqueue", Func, 0}, + {"LANG_ENGLISH", Const, 0}, + {"LAYERED_PROTOCOL", Const, 2}, + {"LCNT_OVERLOAD_FLUSH", Const, 1}, + {"LINUX_REBOOT_CMD_CAD_OFF", Const, 0}, + {"LINUX_REBOOT_CMD_CAD_ON", Const, 0}, + {"LINUX_REBOOT_CMD_HALT", Const, 0}, + {"LINUX_REBOOT_CMD_KEXEC", Const, 0}, + {"LINUX_REBOOT_CMD_POWER_OFF", Const, 0}, + {"LINUX_REBOOT_CMD_RESTART", Const, 0}, + {"LINUX_REBOOT_CMD_RESTART2", Const, 0}, + {"LINUX_REBOOT_CMD_SW_SUSPEND", Const, 0}, + {"LINUX_REBOOT_MAGIC1", Const, 0}, + {"LINUX_REBOOT_MAGIC2", Const, 0}, + {"LOCK_EX", Const, 0}, + {"LOCK_NB", Const, 0}, + {"LOCK_SH", Const, 0}, + {"LOCK_UN", Const, 0}, + {"LazyDLL", Type, 0}, + {"LazyDLL.Name", Field, 0}, + {"LazyProc", Type, 0}, + {"LazyProc.Name", Field, 0}, + {"Lchown", Func, 0}, + {"Linger", Type, 0}, + {"Linger.Linger", Field, 0}, + {"Linger.Onoff", Field, 0}, + {"Link", Func, 0}, + {"Listen", Func, 0}, + {"Listxattr", Func, 1}, + {"LoadCancelIoEx", Func, 1}, + {"LoadConnectEx", Func, 1}, + {"LoadCreateSymbolicLink", Func, 4}, + {"LoadDLL", Func, 0}, + {"LoadGetAddrInfo", Func, 1}, + {"LoadLibrary", Func, 0}, + {"LoadSetFileCompletionNotificationModes", Func, 2}, + {"LocalFree", Func, 0}, + {"Log2phys_t", Type, 0}, + {"Log2phys_t.Contigbytes", Field, 0}, + {"Log2phys_t.Devoffset", Field, 0}, + {"Log2phys_t.Flags", Field, 0}, + {"LookupAccountName", Func, 0}, + {"LookupAccountSid", Func, 0}, + {"LookupSID", Func, 0}, + {"LsfJump", Func, 0}, + {"LsfSocket", Func, 0}, + {"LsfStmt", Func, 0}, + {"Lstat", Func, 0}, + {"MADV_AUTOSYNC", Const, 1}, + {"MADV_CAN_REUSE", Const, 0}, + {"MADV_CORE", Const, 1}, + {"MADV_DOFORK", Const, 0}, + {"MADV_DONTFORK", Const, 0}, + {"MADV_DONTNEED", Const, 0}, + {"MADV_FREE", Const, 0}, + {"MADV_FREE_REUSABLE", Const, 0}, + {"MADV_FREE_REUSE", Const, 0}, + {"MADV_HUGEPAGE", Const, 0}, + {"MADV_HWPOISON", Const, 0}, + {"MADV_MERGEABLE", Const, 0}, + {"MADV_NOCORE", Const, 1}, + {"MADV_NOHUGEPAGE", Const, 0}, + {"MADV_NORMAL", Const, 0}, + {"MADV_NOSYNC", Const, 1}, + {"MADV_PROTECT", Const, 1}, + {"MADV_RANDOM", Const, 0}, + {"MADV_REMOVE", Const, 0}, + {"MADV_SEQUENTIAL", Const, 0}, + {"MADV_SPACEAVAIL", Const, 3}, + {"MADV_UNMERGEABLE", Const, 0}, + {"MADV_WILLNEED", Const, 0}, + {"MADV_ZERO_WIRED_PAGES", Const, 0}, + {"MAP_32BIT", Const, 0}, + {"MAP_ALIGNED_SUPER", Const, 3}, + {"MAP_ALIGNMENT_16MB", Const, 3}, + {"MAP_ALIGNMENT_1TB", Const, 3}, + {"MAP_ALIGNMENT_256TB", Const, 3}, + {"MAP_ALIGNMENT_4GB", Const, 3}, + {"MAP_ALIGNMENT_64KB", Const, 3}, + {"MAP_ALIGNMENT_64PB", Const, 3}, + {"MAP_ALIGNMENT_MASK", Const, 3}, + {"MAP_ALIGNMENT_SHIFT", Const, 3}, + {"MAP_ANON", Const, 0}, + {"MAP_ANONYMOUS", Const, 0}, + {"MAP_COPY", Const, 0}, + {"MAP_DENYWRITE", Const, 0}, + {"MAP_EXECUTABLE", Const, 0}, + {"MAP_FILE", Const, 0}, + {"MAP_FIXED", Const, 0}, + {"MAP_FLAGMASK", Const, 3}, + {"MAP_GROWSDOWN", Const, 0}, + {"MAP_HASSEMAPHORE", Const, 0}, + {"MAP_HUGETLB", Const, 0}, + {"MAP_INHERIT", Const, 3}, + {"MAP_INHERIT_COPY", Const, 3}, + {"MAP_INHERIT_DEFAULT", Const, 3}, + {"MAP_INHERIT_DONATE_COPY", Const, 3}, + {"MAP_INHERIT_NONE", Const, 3}, + {"MAP_INHERIT_SHARE", Const, 3}, + {"MAP_JIT", Const, 0}, + {"MAP_LOCKED", Const, 0}, + {"MAP_NOCACHE", Const, 0}, + {"MAP_NOCORE", Const, 1}, + {"MAP_NOEXTEND", Const, 0}, + {"MAP_NONBLOCK", Const, 0}, + {"MAP_NORESERVE", Const, 0}, + {"MAP_NOSYNC", Const, 1}, + {"MAP_POPULATE", Const, 0}, + {"MAP_PREFAULT_READ", Const, 1}, + {"MAP_PRIVATE", Const, 0}, + {"MAP_RENAME", Const, 0}, + {"MAP_RESERVED0080", Const, 0}, + {"MAP_RESERVED0100", Const, 1}, + {"MAP_SHARED", Const, 0}, + {"MAP_STACK", Const, 0}, + {"MAP_TRYFIXED", Const, 3}, + {"MAP_TYPE", Const, 0}, + {"MAP_WIRED", Const, 3}, + {"MAXIMUM_REPARSE_DATA_BUFFER_SIZE", Const, 4}, + {"MAXLEN_IFDESCR", Const, 0}, + {"MAXLEN_PHYSADDR", Const, 0}, + {"MAX_ADAPTER_ADDRESS_LENGTH", Const, 0}, + {"MAX_ADAPTER_DESCRIPTION_LENGTH", Const, 0}, + {"MAX_ADAPTER_NAME_LENGTH", Const, 0}, + {"MAX_COMPUTERNAME_LENGTH", Const, 0}, + {"MAX_INTERFACE_NAME_LEN", Const, 0}, + {"MAX_LONG_PATH", Const, 0}, + {"MAX_PATH", Const, 0}, + {"MAX_PROTOCOL_CHAIN", Const, 2}, + {"MCL_CURRENT", Const, 0}, + {"MCL_FUTURE", Const, 0}, + {"MNT_DETACH", Const, 0}, + {"MNT_EXPIRE", Const, 0}, + {"MNT_FORCE", Const, 0}, + {"MSG_BCAST", Const, 1}, + {"MSG_CMSG_CLOEXEC", Const, 0}, + {"MSG_COMPAT", Const, 0}, + {"MSG_CONFIRM", Const, 0}, + {"MSG_CONTROLMBUF", Const, 1}, + {"MSG_CTRUNC", Const, 0}, + {"MSG_DONTROUTE", Const, 0}, + {"MSG_DONTWAIT", Const, 0}, + {"MSG_EOF", Const, 0}, + {"MSG_EOR", Const, 0}, + {"MSG_ERRQUEUE", Const, 0}, + {"MSG_FASTOPEN", Const, 1}, + {"MSG_FIN", Const, 0}, + {"MSG_FLUSH", Const, 0}, + {"MSG_HAVEMORE", Const, 0}, + {"MSG_HOLD", Const, 0}, + {"MSG_IOVUSRSPACE", Const, 1}, + {"MSG_LENUSRSPACE", Const, 1}, + {"MSG_MCAST", Const, 1}, + {"MSG_MORE", Const, 0}, + {"MSG_NAMEMBUF", Const, 1}, + {"MSG_NBIO", Const, 0}, + {"MSG_NEEDSA", Const, 0}, + {"MSG_NOSIGNAL", Const, 0}, + {"MSG_NOTIFICATION", Const, 0}, + {"MSG_OOB", Const, 0}, + {"MSG_PEEK", Const, 0}, + {"MSG_PROXY", Const, 0}, + {"MSG_RCVMORE", Const, 0}, + {"MSG_RST", Const, 0}, + {"MSG_SEND", Const, 0}, + {"MSG_SYN", Const, 0}, + {"MSG_TRUNC", Const, 0}, + {"MSG_TRYHARD", Const, 0}, + {"MSG_USERFLAGS", Const, 1}, + {"MSG_WAITALL", Const, 0}, + {"MSG_WAITFORONE", Const, 0}, + {"MSG_WAITSTREAM", Const, 0}, + {"MS_ACTIVE", Const, 0}, + {"MS_ASYNC", Const, 0}, + {"MS_BIND", Const, 0}, + {"MS_DEACTIVATE", Const, 0}, + {"MS_DIRSYNC", Const, 0}, + {"MS_INVALIDATE", Const, 0}, + {"MS_I_VERSION", Const, 0}, + {"MS_KERNMOUNT", Const, 0}, + {"MS_KILLPAGES", Const, 0}, + {"MS_MANDLOCK", Const, 0}, + {"MS_MGC_MSK", Const, 0}, + {"MS_MGC_VAL", Const, 0}, + {"MS_MOVE", Const, 0}, + {"MS_NOATIME", Const, 0}, + {"MS_NODEV", Const, 0}, + {"MS_NODIRATIME", Const, 0}, + {"MS_NOEXEC", Const, 0}, + {"MS_NOSUID", Const, 0}, + {"MS_NOUSER", Const, 0}, + {"MS_POSIXACL", Const, 0}, + {"MS_PRIVATE", Const, 0}, + {"MS_RDONLY", Const, 0}, + {"MS_REC", Const, 0}, + {"MS_RELATIME", Const, 0}, + {"MS_REMOUNT", Const, 0}, + {"MS_RMT_MASK", Const, 0}, + {"MS_SHARED", Const, 0}, + {"MS_SILENT", Const, 0}, + {"MS_SLAVE", Const, 0}, + {"MS_STRICTATIME", Const, 0}, + {"MS_SYNC", Const, 0}, + {"MS_SYNCHRONOUS", Const, 0}, + {"MS_UNBINDABLE", Const, 0}, + {"Madvise", Func, 0}, + {"MapViewOfFile", Func, 0}, + {"MaxTokenInfoClass", Const, 0}, + {"Mclpool", Type, 2}, + {"Mclpool.Alive", Field, 2}, + {"Mclpool.Cwm", Field, 2}, + {"Mclpool.Grown", Field, 2}, + {"Mclpool.Hwm", Field, 2}, + {"Mclpool.Lwm", Field, 2}, + {"MibIfRow", Type, 0}, + {"MibIfRow.AdminStatus", Field, 0}, + {"MibIfRow.Descr", Field, 0}, + {"MibIfRow.DescrLen", Field, 0}, + {"MibIfRow.InDiscards", Field, 0}, + {"MibIfRow.InErrors", Field, 0}, + {"MibIfRow.InNUcastPkts", Field, 0}, + {"MibIfRow.InOctets", Field, 0}, + {"MibIfRow.InUcastPkts", Field, 0}, + {"MibIfRow.InUnknownProtos", Field, 0}, + {"MibIfRow.Index", Field, 0}, + {"MibIfRow.LastChange", Field, 0}, + {"MibIfRow.Mtu", Field, 0}, + {"MibIfRow.Name", Field, 0}, + {"MibIfRow.OperStatus", Field, 0}, + {"MibIfRow.OutDiscards", Field, 0}, + {"MibIfRow.OutErrors", Field, 0}, + {"MibIfRow.OutNUcastPkts", Field, 0}, + {"MibIfRow.OutOctets", Field, 0}, + {"MibIfRow.OutQLen", Field, 0}, + {"MibIfRow.OutUcastPkts", Field, 0}, + {"MibIfRow.PhysAddr", Field, 0}, + {"MibIfRow.PhysAddrLen", Field, 0}, + {"MibIfRow.Speed", Field, 0}, + {"MibIfRow.Type", Field, 0}, + {"Mkdir", Func, 0}, + {"Mkdirat", Func, 0}, + {"Mkfifo", Func, 0}, + {"Mknod", Func, 0}, + {"Mknodat", Func, 0}, + {"Mlock", Func, 0}, + {"Mlockall", Func, 0}, + {"Mmap", Func, 0}, + {"Mount", Func, 0}, + {"MoveFile", Func, 0}, + {"Mprotect", Func, 0}, + {"Msghdr", Type, 0}, + {"Msghdr.Control", Field, 0}, + {"Msghdr.Controllen", Field, 0}, + {"Msghdr.Flags", Field, 0}, + {"Msghdr.Iov", Field, 0}, + {"Msghdr.Iovlen", Field, 0}, + {"Msghdr.Name", Field, 0}, + {"Msghdr.Namelen", Field, 0}, + {"Msghdr.Pad_cgo_0", Field, 0}, + {"Msghdr.Pad_cgo_1", Field, 0}, + {"Munlock", Func, 0}, + {"Munlockall", Func, 0}, + {"Munmap", Func, 0}, + {"MustLoadDLL", Func, 0}, + {"NAME_MAX", Const, 0}, + {"NETLINK_ADD_MEMBERSHIP", Const, 0}, + {"NETLINK_AUDIT", Const, 0}, + {"NETLINK_BROADCAST_ERROR", Const, 0}, + {"NETLINK_CONNECTOR", Const, 0}, + {"NETLINK_DNRTMSG", Const, 0}, + {"NETLINK_DROP_MEMBERSHIP", Const, 0}, + {"NETLINK_ECRYPTFS", Const, 0}, + {"NETLINK_FIB_LOOKUP", Const, 0}, + {"NETLINK_FIREWALL", Const, 0}, + {"NETLINK_GENERIC", Const, 0}, + {"NETLINK_INET_DIAG", Const, 0}, + {"NETLINK_IP6_FW", Const, 0}, + {"NETLINK_ISCSI", Const, 0}, + {"NETLINK_KOBJECT_UEVENT", Const, 0}, + {"NETLINK_NETFILTER", Const, 0}, + {"NETLINK_NFLOG", Const, 0}, + {"NETLINK_NO_ENOBUFS", Const, 0}, + {"NETLINK_PKTINFO", Const, 0}, + {"NETLINK_RDMA", Const, 0}, + {"NETLINK_ROUTE", Const, 0}, + {"NETLINK_SCSITRANSPORT", Const, 0}, + {"NETLINK_SELINUX", Const, 0}, + {"NETLINK_UNUSED", Const, 0}, + {"NETLINK_USERSOCK", Const, 0}, + {"NETLINK_XFRM", Const, 0}, + {"NET_RT_DUMP", Const, 0}, + {"NET_RT_DUMP2", Const, 0}, + {"NET_RT_FLAGS", Const, 0}, + {"NET_RT_IFLIST", Const, 0}, + {"NET_RT_IFLIST2", Const, 0}, + {"NET_RT_IFLISTL", Const, 1}, + {"NET_RT_IFMALIST", Const, 0}, + {"NET_RT_MAXID", Const, 0}, + {"NET_RT_OIFLIST", Const, 1}, + {"NET_RT_OOIFLIST", Const, 1}, + {"NET_RT_STAT", Const, 0}, + {"NET_RT_STATS", Const, 1}, + {"NET_RT_TABLE", Const, 1}, + {"NET_RT_TRASH", Const, 0}, + {"NLA_ALIGNTO", Const, 0}, + {"NLA_F_NESTED", Const, 0}, + {"NLA_F_NET_BYTEORDER", Const, 0}, + {"NLA_HDRLEN", Const, 0}, + {"NLMSG_ALIGNTO", Const, 0}, + {"NLMSG_DONE", Const, 0}, + {"NLMSG_ERROR", Const, 0}, + {"NLMSG_HDRLEN", Const, 0}, + {"NLMSG_MIN_TYPE", Const, 0}, + {"NLMSG_NOOP", Const, 0}, + {"NLMSG_OVERRUN", Const, 0}, + {"NLM_F_ACK", Const, 0}, + {"NLM_F_APPEND", Const, 0}, + {"NLM_F_ATOMIC", Const, 0}, + {"NLM_F_CREATE", Const, 0}, + {"NLM_F_DUMP", Const, 0}, + {"NLM_F_ECHO", Const, 0}, + {"NLM_F_EXCL", Const, 0}, + {"NLM_F_MATCH", Const, 0}, + {"NLM_F_MULTI", Const, 0}, + {"NLM_F_REPLACE", Const, 0}, + {"NLM_F_REQUEST", Const, 0}, + {"NLM_F_ROOT", Const, 0}, + {"NOFLSH", Const, 0}, + {"NOTE_ABSOLUTE", Const, 0}, + {"NOTE_ATTRIB", Const, 0}, + {"NOTE_BACKGROUND", Const, 16}, + {"NOTE_CHILD", Const, 0}, + {"NOTE_CRITICAL", Const, 16}, + {"NOTE_DELETE", Const, 0}, + {"NOTE_EOF", Const, 1}, + {"NOTE_EXEC", Const, 0}, + {"NOTE_EXIT", Const, 0}, + {"NOTE_EXITSTATUS", Const, 0}, + {"NOTE_EXIT_CSERROR", Const, 16}, + {"NOTE_EXIT_DECRYPTFAIL", Const, 16}, + {"NOTE_EXIT_DETAIL", Const, 16}, + {"NOTE_EXIT_DETAIL_MASK", Const, 16}, + {"NOTE_EXIT_MEMORY", Const, 16}, + {"NOTE_EXIT_REPARENTED", Const, 16}, + {"NOTE_EXTEND", Const, 0}, + {"NOTE_FFAND", Const, 0}, + {"NOTE_FFCOPY", Const, 0}, + {"NOTE_FFCTRLMASK", Const, 0}, + {"NOTE_FFLAGSMASK", Const, 0}, + {"NOTE_FFNOP", Const, 0}, + {"NOTE_FFOR", Const, 0}, + {"NOTE_FORK", Const, 0}, + {"NOTE_LEEWAY", Const, 16}, + {"NOTE_LINK", Const, 0}, + {"NOTE_LOWAT", Const, 0}, + {"NOTE_NONE", Const, 0}, + {"NOTE_NSECONDS", Const, 0}, + {"NOTE_PCTRLMASK", Const, 0}, + {"NOTE_PDATAMASK", Const, 0}, + {"NOTE_REAP", Const, 0}, + {"NOTE_RENAME", Const, 0}, + {"NOTE_RESOURCEEND", Const, 0}, + {"NOTE_REVOKE", Const, 0}, + {"NOTE_SECONDS", Const, 0}, + {"NOTE_SIGNAL", Const, 0}, + {"NOTE_TRACK", Const, 0}, + {"NOTE_TRACKERR", Const, 0}, + {"NOTE_TRIGGER", Const, 0}, + {"NOTE_TRUNCATE", Const, 1}, + {"NOTE_USECONDS", Const, 0}, + {"NOTE_VM_ERROR", Const, 0}, + {"NOTE_VM_PRESSURE", Const, 0}, + {"NOTE_VM_PRESSURE_SUDDEN_TERMINATE", Const, 0}, + {"NOTE_VM_PRESSURE_TERMINATE", Const, 0}, + {"NOTE_WRITE", Const, 0}, + {"NameCanonical", Const, 0}, + {"NameCanonicalEx", Const, 0}, + {"NameDisplay", Const, 0}, + {"NameDnsDomain", Const, 0}, + {"NameFullyQualifiedDN", Const, 0}, + {"NameSamCompatible", Const, 0}, + {"NameServicePrincipal", Const, 0}, + {"NameUniqueId", Const, 0}, + {"NameUnknown", Const, 0}, + {"NameUserPrincipal", Const, 0}, + {"Nanosleep", Func, 0}, + {"NetApiBufferFree", Func, 0}, + {"NetGetJoinInformation", Func, 2}, + {"NetSetupDomainName", Const, 2}, + {"NetSetupUnjoined", Const, 2}, + {"NetSetupUnknownStatus", Const, 2}, + {"NetSetupWorkgroupName", Const, 2}, + {"NetUserGetInfo", Func, 0}, + {"NetlinkMessage", Type, 0}, + {"NetlinkMessage.Data", Field, 0}, + {"NetlinkMessage.Header", Field, 0}, + {"NetlinkRIB", Func, 0}, + {"NetlinkRouteAttr", Type, 0}, + {"NetlinkRouteAttr.Attr", Field, 0}, + {"NetlinkRouteAttr.Value", Field, 0}, + {"NetlinkRouteRequest", Type, 0}, + {"NetlinkRouteRequest.Data", Field, 0}, + {"NetlinkRouteRequest.Header", Field, 0}, + {"NewCallback", Func, 0}, + {"NewCallbackCDecl", Func, 3}, + {"NewLazyDLL", Func, 0}, + {"NlAttr", Type, 0}, + {"NlAttr.Len", Field, 0}, + {"NlAttr.Type", Field, 0}, + {"NlMsgerr", Type, 0}, + {"NlMsgerr.Error", Field, 0}, + {"NlMsgerr.Msg", Field, 0}, + {"NlMsghdr", Type, 0}, + {"NlMsghdr.Flags", Field, 0}, + {"NlMsghdr.Len", Field, 0}, + {"NlMsghdr.Pid", Field, 0}, + {"NlMsghdr.Seq", Field, 0}, + {"NlMsghdr.Type", Field, 0}, + {"NsecToFiletime", Func, 0}, + {"NsecToTimespec", Func, 0}, + {"NsecToTimeval", Func, 0}, + {"Ntohs", Func, 0}, + {"OCRNL", Const, 0}, + {"OFDEL", Const, 0}, + {"OFILL", Const, 0}, + {"OFIOGETBMAP", Const, 1}, + {"OID_PKIX_KP_SERVER_AUTH", Var, 0}, + {"OID_SERVER_GATED_CRYPTO", Var, 0}, + {"OID_SGC_NETSCAPE", Var, 0}, + {"OLCUC", Const, 0}, + {"ONLCR", Const, 0}, + {"ONLRET", Const, 0}, + {"ONOCR", Const, 0}, + {"ONOEOT", Const, 1}, + {"OPEN_ALWAYS", Const, 0}, + {"OPEN_EXISTING", Const, 0}, + {"OPOST", Const, 0}, + {"O_ACCMODE", Const, 0}, + {"O_ALERT", Const, 0}, + {"O_ALT_IO", Const, 1}, + {"O_APPEND", Const, 0}, + {"O_ASYNC", Const, 0}, + {"O_CLOEXEC", Const, 0}, + {"O_CREAT", Const, 0}, + {"O_DIRECT", Const, 0}, + {"O_DIRECTORY", Const, 0}, + {"O_DP_GETRAWENCRYPTED", Const, 16}, + {"O_DSYNC", Const, 0}, + {"O_EVTONLY", Const, 0}, + {"O_EXCL", Const, 0}, + {"O_EXEC", Const, 0}, + {"O_EXLOCK", Const, 0}, + {"O_FSYNC", Const, 0}, + {"O_LARGEFILE", Const, 0}, + {"O_NDELAY", Const, 0}, + {"O_NOATIME", Const, 0}, + {"O_NOCTTY", Const, 0}, + {"O_NOFOLLOW", Const, 0}, + {"O_NONBLOCK", Const, 0}, + {"O_NOSIGPIPE", Const, 1}, + {"O_POPUP", Const, 0}, + {"O_RDONLY", Const, 0}, + {"O_RDWR", Const, 0}, + {"O_RSYNC", Const, 0}, + {"O_SHLOCK", Const, 0}, + {"O_SYMLINK", Const, 0}, + {"O_SYNC", Const, 0}, + {"O_TRUNC", Const, 0}, + {"O_TTY_INIT", Const, 0}, + {"O_WRONLY", Const, 0}, + {"Open", Func, 0}, + {"OpenCurrentProcessToken", Func, 0}, + {"OpenProcess", Func, 0}, + {"OpenProcessToken", Func, 0}, + {"Openat", Func, 0}, + {"Overlapped", Type, 0}, + {"Overlapped.HEvent", Field, 0}, + {"Overlapped.Internal", Field, 0}, + {"Overlapped.InternalHigh", Field, 0}, + {"Overlapped.Offset", Field, 0}, + {"Overlapped.OffsetHigh", Field, 0}, + {"PACKET_ADD_MEMBERSHIP", Const, 0}, + {"PACKET_BROADCAST", Const, 0}, + {"PACKET_DROP_MEMBERSHIP", Const, 0}, + {"PACKET_FASTROUTE", Const, 0}, + {"PACKET_HOST", Const, 0}, + {"PACKET_LOOPBACK", Const, 0}, + {"PACKET_MR_ALLMULTI", Const, 0}, + {"PACKET_MR_MULTICAST", Const, 0}, + {"PACKET_MR_PROMISC", Const, 0}, + {"PACKET_MULTICAST", Const, 0}, + {"PACKET_OTHERHOST", Const, 0}, + {"PACKET_OUTGOING", Const, 0}, + {"PACKET_RECV_OUTPUT", Const, 0}, + {"PACKET_RX_RING", Const, 0}, + {"PACKET_STATISTICS", Const, 0}, + {"PAGE_EXECUTE_READ", Const, 0}, + {"PAGE_EXECUTE_READWRITE", Const, 0}, + {"PAGE_EXECUTE_WRITECOPY", Const, 0}, + {"PAGE_READONLY", Const, 0}, + {"PAGE_READWRITE", Const, 0}, + {"PAGE_WRITECOPY", Const, 0}, + {"PARENB", Const, 0}, + {"PARMRK", Const, 0}, + {"PARODD", Const, 0}, + {"PENDIN", Const, 0}, + {"PFL_HIDDEN", Const, 2}, + {"PFL_MATCHES_PROTOCOL_ZERO", Const, 2}, + {"PFL_MULTIPLE_PROTO_ENTRIES", Const, 2}, + {"PFL_NETWORKDIRECT_PROVIDER", Const, 2}, + {"PFL_RECOMMENDED_PROTO_ENTRY", Const, 2}, + {"PF_FLUSH", Const, 1}, + {"PKCS_7_ASN_ENCODING", Const, 0}, + {"PMC5_PIPELINE_FLUSH", Const, 1}, + {"PRIO_PGRP", Const, 2}, + {"PRIO_PROCESS", Const, 2}, + {"PRIO_USER", Const, 2}, + {"PRI_IOFLUSH", Const, 1}, + {"PROCESS_QUERY_INFORMATION", Const, 0}, + {"PROCESS_TERMINATE", Const, 2}, + {"PROT_EXEC", Const, 0}, + {"PROT_GROWSDOWN", Const, 0}, + {"PROT_GROWSUP", Const, 0}, + {"PROT_NONE", Const, 0}, + {"PROT_READ", Const, 0}, + {"PROT_WRITE", Const, 0}, + {"PROV_DH_SCHANNEL", Const, 0}, + {"PROV_DSS", Const, 0}, + {"PROV_DSS_DH", Const, 0}, + {"PROV_EC_ECDSA_FULL", Const, 0}, + {"PROV_EC_ECDSA_SIG", Const, 0}, + {"PROV_EC_ECNRA_FULL", Const, 0}, + {"PROV_EC_ECNRA_SIG", Const, 0}, + {"PROV_FORTEZZA", Const, 0}, + {"PROV_INTEL_SEC", Const, 0}, + {"PROV_MS_EXCHANGE", Const, 0}, + {"PROV_REPLACE_OWF", Const, 0}, + {"PROV_RNG", Const, 0}, + {"PROV_RSA_AES", Const, 0}, + {"PROV_RSA_FULL", Const, 0}, + {"PROV_RSA_SCHANNEL", Const, 0}, + {"PROV_RSA_SIG", Const, 0}, + {"PROV_SPYRUS_LYNKS", Const, 0}, + {"PROV_SSL", Const, 0}, + {"PR_CAPBSET_DROP", Const, 0}, + {"PR_CAPBSET_READ", Const, 0}, + {"PR_CLEAR_SECCOMP_FILTER", Const, 0}, + {"PR_ENDIAN_BIG", Const, 0}, + {"PR_ENDIAN_LITTLE", Const, 0}, + {"PR_ENDIAN_PPC_LITTLE", Const, 0}, + {"PR_FPEMU_NOPRINT", Const, 0}, + {"PR_FPEMU_SIGFPE", Const, 0}, + {"PR_FP_EXC_ASYNC", Const, 0}, + {"PR_FP_EXC_DISABLED", Const, 0}, + {"PR_FP_EXC_DIV", Const, 0}, + {"PR_FP_EXC_INV", Const, 0}, + {"PR_FP_EXC_NONRECOV", Const, 0}, + {"PR_FP_EXC_OVF", Const, 0}, + {"PR_FP_EXC_PRECISE", Const, 0}, + {"PR_FP_EXC_RES", Const, 0}, + {"PR_FP_EXC_SW_ENABLE", Const, 0}, + {"PR_FP_EXC_UND", Const, 0}, + {"PR_GET_DUMPABLE", Const, 0}, + {"PR_GET_ENDIAN", Const, 0}, + {"PR_GET_FPEMU", Const, 0}, + {"PR_GET_FPEXC", Const, 0}, + {"PR_GET_KEEPCAPS", Const, 0}, + {"PR_GET_NAME", Const, 0}, + {"PR_GET_PDEATHSIG", Const, 0}, + {"PR_GET_SECCOMP", Const, 0}, + {"PR_GET_SECCOMP_FILTER", Const, 0}, + {"PR_GET_SECUREBITS", Const, 0}, + {"PR_GET_TIMERSLACK", Const, 0}, + {"PR_GET_TIMING", Const, 0}, + {"PR_GET_TSC", Const, 0}, + {"PR_GET_UNALIGN", Const, 0}, + {"PR_MCE_KILL", Const, 0}, + {"PR_MCE_KILL_CLEAR", Const, 0}, + {"PR_MCE_KILL_DEFAULT", Const, 0}, + {"PR_MCE_KILL_EARLY", Const, 0}, + {"PR_MCE_KILL_GET", Const, 0}, + {"PR_MCE_KILL_LATE", Const, 0}, + {"PR_MCE_KILL_SET", Const, 0}, + {"PR_SECCOMP_FILTER_EVENT", Const, 0}, + {"PR_SECCOMP_FILTER_SYSCALL", Const, 0}, + {"PR_SET_DUMPABLE", Const, 0}, + {"PR_SET_ENDIAN", Const, 0}, + {"PR_SET_FPEMU", Const, 0}, + {"PR_SET_FPEXC", Const, 0}, + {"PR_SET_KEEPCAPS", Const, 0}, + {"PR_SET_NAME", Const, 0}, + {"PR_SET_PDEATHSIG", Const, 0}, + {"PR_SET_PTRACER", Const, 0}, + {"PR_SET_SECCOMP", Const, 0}, + {"PR_SET_SECCOMP_FILTER", Const, 0}, + {"PR_SET_SECUREBITS", Const, 0}, + {"PR_SET_TIMERSLACK", Const, 0}, + {"PR_SET_TIMING", Const, 0}, + {"PR_SET_TSC", Const, 0}, + {"PR_SET_UNALIGN", Const, 0}, + {"PR_TASK_PERF_EVENTS_DISABLE", Const, 0}, + {"PR_TASK_PERF_EVENTS_ENABLE", Const, 0}, + {"PR_TIMING_STATISTICAL", Const, 0}, + {"PR_TIMING_TIMESTAMP", Const, 0}, + {"PR_TSC_ENABLE", Const, 0}, + {"PR_TSC_SIGSEGV", Const, 0}, + {"PR_UNALIGN_NOPRINT", Const, 0}, + {"PR_UNALIGN_SIGBUS", Const, 0}, + {"PTRACE_ARCH_PRCTL", Const, 0}, + {"PTRACE_ATTACH", Const, 0}, + {"PTRACE_CONT", Const, 0}, + {"PTRACE_DETACH", Const, 0}, + {"PTRACE_EVENT_CLONE", Const, 0}, + {"PTRACE_EVENT_EXEC", Const, 0}, + {"PTRACE_EVENT_EXIT", Const, 0}, + {"PTRACE_EVENT_FORK", Const, 0}, + {"PTRACE_EVENT_VFORK", Const, 0}, + {"PTRACE_EVENT_VFORK_DONE", Const, 0}, + {"PTRACE_GETCRUNCHREGS", Const, 0}, + {"PTRACE_GETEVENTMSG", Const, 0}, + {"PTRACE_GETFPREGS", Const, 0}, + {"PTRACE_GETFPXREGS", Const, 0}, + {"PTRACE_GETHBPREGS", Const, 0}, + {"PTRACE_GETREGS", Const, 0}, + {"PTRACE_GETREGSET", Const, 0}, + {"PTRACE_GETSIGINFO", Const, 0}, + {"PTRACE_GETVFPREGS", Const, 0}, + {"PTRACE_GETWMMXREGS", Const, 0}, + {"PTRACE_GET_THREAD_AREA", Const, 0}, + {"PTRACE_KILL", Const, 0}, + {"PTRACE_OLDSETOPTIONS", Const, 0}, + {"PTRACE_O_MASK", Const, 0}, + {"PTRACE_O_TRACECLONE", Const, 0}, + {"PTRACE_O_TRACEEXEC", Const, 0}, + {"PTRACE_O_TRACEEXIT", Const, 0}, + {"PTRACE_O_TRACEFORK", Const, 0}, + {"PTRACE_O_TRACESYSGOOD", Const, 0}, + {"PTRACE_O_TRACEVFORK", Const, 0}, + {"PTRACE_O_TRACEVFORKDONE", Const, 0}, + {"PTRACE_PEEKDATA", Const, 0}, + {"PTRACE_PEEKTEXT", Const, 0}, + {"PTRACE_PEEKUSR", Const, 0}, + {"PTRACE_POKEDATA", Const, 0}, + {"PTRACE_POKETEXT", Const, 0}, + {"PTRACE_POKEUSR", Const, 0}, + {"PTRACE_SETCRUNCHREGS", Const, 0}, + {"PTRACE_SETFPREGS", Const, 0}, + {"PTRACE_SETFPXREGS", Const, 0}, + {"PTRACE_SETHBPREGS", Const, 0}, + {"PTRACE_SETOPTIONS", Const, 0}, + {"PTRACE_SETREGS", Const, 0}, + {"PTRACE_SETREGSET", Const, 0}, + {"PTRACE_SETSIGINFO", Const, 0}, + {"PTRACE_SETVFPREGS", Const, 0}, + {"PTRACE_SETWMMXREGS", Const, 0}, + {"PTRACE_SET_SYSCALL", Const, 0}, + {"PTRACE_SET_THREAD_AREA", Const, 0}, + {"PTRACE_SINGLEBLOCK", Const, 0}, + {"PTRACE_SINGLESTEP", Const, 0}, + {"PTRACE_SYSCALL", Const, 0}, + {"PTRACE_SYSEMU", Const, 0}, + {"PTRACE_SYSEMU_SINGLESTEP", Const, 0}, + {"PTRACE_TRACEME", Const, 0}, + {"PT_ATTACH", Const, 0}, + {"PT_ATTACHEXC", Const, 0}, + {"PT_CONTINUE", Const, 0}, + {"PT_DATA_ADDR", Const, 0}, + {"PT_DENY_ATTACH", Const, 0}, + {"PT_DETACH", Const, 0}, + {"PT_FIRSTMACH", Const, 0}, + {"PT_FORCEQUOTA", Const, 0}, + {"PT_KILL", Const, 0}, + {"PT_MASK", Const, 1}, + {"PT_READ_D", Const, 0}, + {"PT_READ_I", Const, 0}, + {"PT_READ_U", Const, 0}, + {"PT_SIGEXC", Const, 0}, + {"PT_STEP", Const, 0}, + {"PT_TEXT_ADDR", Const, 0}, + {"PT_TEXT_END_ADDR", Const, 0}, + {"PT_THUPDATE", Const, 0}, + {"PT_TRACE_ME", Const, 0}, + {"PT_WRITE_D", Const, 0}, + {"PT_WRITE_I", Const, 0}, + {"PT_WRITE_U", Const, 0}, + {"ParseDirent", Func, 0}, + {"ParseNetlinkMessage", Func, 0}, + {"ParseNetlinkRouteAttr", Func, 0}, + {"ParseRoutingMessage", Func, 0}, + {"ParseRoutingSockaddr", Func, 0}, + {"ParseSocketControlMessage", Func, 0}, + {"ParseUnixCredentials", Func, 0}, + {"ParseUnixRights", Func, 0}, + {"PathMax", Const, 0}, + {"Pathconf", Func, 0}, + {"Pause", Func, 0}, + {"Pipe", Func, 0}, + {"Pipe2", Func, 1}, + {"PivotRoot", Func, 0}, + {"Pointer", Type, 11}, + {"PostQueuedCompletionStatus", Func, 0}, + {"Pread", Func, 0}, + {"Proc", Type, 0}, + {"Proc.Dll", Field, 0}, + {"Proc.Name", Field, 0}, + {"ProcAttr", Type, 0}, + {"ProcAttr.Dir", Field, 0}, + {"ProcAttr.Env", Field, 0}, + {"ProcAttr.Files", Field, 0}, + {"ProcAttr.Sys", Field, 0}, + {"Process32First", Func, 4}, + {"Process32Next", Func, 4}, + {"ProcessEntry32", Type, 4}, + {"ProcessEntry32.DefaultHeapID", Field, 4}, + {"ProcessEntry32.ExeFile", Field, 4}, + {"ProcessEntry32.Flags", Field, 4}, + {"ProcessEntry32.ModuleID", Field, 4}, + {"ProcessEntry32.ParentProcessID", Field, 4}, + {"ProcessEntry32.PriClassBase", Field, 4}, + {"ProcessEntry32.ProcessID", Field, 4}, + {"ProcessEntry32.Size", Field, 4}, + {"ProcessEntry32.Threads", Field, 4}, + {"ProcessEntry32.Usage", Field, 4}, + {"ProcessInformation", Type, 0}, + {"ProcessInformation.Process", Field, 0}, + {"ProcessInformation.ProcessId", Field, 0}, + {"ProcessInformation.Thread", Field, 0}, + {"ProcessInformation.ThreadId", Field, 0}, + {"Protoent", Type, 0}, + {"Protoent.Aliases", Field, 0}, + {"Protoent.Name", Field, 0}, + {"Protoent.Proto", Field, 0}, + {"PtraceAttach", Func, 0}, + {"PtraceCont", Func, 0}, + {"PtraceDetach", Func, 0}, + {"PtraceGetEventMsg", Func, 0}, + {"PtraceGetRegs", Func, 0}, + {"PtracePeekData", Func, 0}, + {"PtracePeekText", Func, 0}, + {"PtracePokeData", Func, 0}, + {"PtracePokeText", Func, 0}, + {"PtraceRegs", Type, 0}, + {"PtraceRegs.Cs", Field, 0}, + {"PtraceRegs.Ds", Field, 0}, + {"PtraceRegs.Eax", Field, 0}, + {"PtraceRegs.Ebp", Field, 0}, + {"PtraceRegs.Ebx", Field, 0}, + {"PtraceRegs.Ecx", Field, 0}, + {"PtraceRegs.Edi", Field, 0}, + {"PtraceRegs.Edx", Field, 0}, + {"PtraceRegs.Eflags", Field, 0}, + {"PtraceRegs.Eip", Field, 0}, + {"PtraceRegs.Es", Field, 0}, + {"PtraceRegs.Esi", Field, 0}, + {"PtraceRegs.Esp", Field, 0}, + {"PtraceRegs.Fs", Field, 0}, + {"PtraceRegs.Fs_base", Field, 0}, + {"PtraceRegs.Gs", Field, 0}, + {"PtraceRegs.Gs_base", Field, 0}, + {"PtraceRegs.Orig_eax", Field, 0}, + {"PtraceRegs.Orig_rax", Field, 0}, + {"PtraceRegs.R10", Field, 0}, + {"PtraceRegs.R11", Field, 0}, + {"PtraceRegs.R12", Field, 0}, + {"PtraceRegs.R13", Field, 0}, + {"PtraceRegs.R14", Field, 0}, + {"PtraceRegs.R15", Field, 0}, + {"PtraceRegs.R8", Field, 0}, + {"PtraceRegs.R9", Field, 0}, + {"PtraceRegs.Rax", Field, 0}, + {"PtraceRegs.Rbp", Field, 0}, + {"PtraceRegs.Rbx", Field, 0}, + {"PtraceRegs.Rcx", Field, 0}, + {"PtraceRegs.Rdi", Field, 0}, + {"PtraceRegs.Rdx", Field, 0}, + {"PtraceRegs.Rip", Field, 0}, + {"PtraceRegs.Rsi", Field, 0}, + {"PtraceRegs.Rsp", Field, 0}, + {"PtraceRegs.Ss", Field, 0}, + {"PtraceRegs.Uregs", Field, 0}, + {"PtraceRegs.Xcs", Field, 0}, + {"PtraceRegs.Xds", Field, 0}, + {"PtraceRegs.Xes", Field, 0}, + {"PtraceRegs.Xfs", Field, 0}, + {"PtraceRegs.Xgs", Field, 0}, + {"PtraceRegs.Xss", Field, 0}, + {"PtraceSetOptions", Func, 0}, + {"PtraceSetRegs", Func, 0}, + {"PtraceSingleStep", Func, 0}, + {"PtraceSyscall", Func, 1}, + {"Pwrite", Func, 0}, + {"REG_BINARY", Const, 0}, + {"REG_DWORD", Const, 0}, + {"REG_DWORD_BIG_ENDIAN", Const, 0}, + {"REG_DWORD_LITTLE_ENDIAN", Const, 0}, + {"REG_EXPAND_SZ", Const, 0}, + {"REG_FULL_RESOURCE_DESCRIPTOR", Const, 0}, + {"REG_LINK", Const, 0}, + {"REG_MULTI_SZ", Const, 0}, + {"REG_NONE", Const, 0}, + {"REG_QWORD", Const, 0}, + {"REG_QWORD_LITTLE_ENDIAN", Const, 0}, + {"REG_RESOURCE_LIST", Const, 0}, + {"REG_RESOURCE_REQUIREMENTS_LIST", Const, 0}, + {"REG_SZ", Const, 0}, + {"RLIMIT_AS", Const, 0}, + {"RLIMIT_CORE", Const, 0}, + {"RLIMIT_CPU", Const, 0}, + {"RLIMIT_CPU_USAGE_MONITOR", Const, 16}, + {"RLIMIT_DATA", Const, 0}, + {"RLIMIT_FSIZE", Const, 0}, + {"RLIMIT_NOFILE", Const, 0}, + {"RLIMIT_STACK", Const, 0}, + {"RLIM_INFINITY", Const, 0}, + {"RTAX_ADVMSS", Const, 0}, + {"RTAX_AUTHOR", Const, 0}, + {"RTAX_BRD", Const, 0}, + {"RTAX_CWND", Const, 0}, + {"RTAX_DST", Const, 0}, + {"RTAX_FEATURES", Const, 0}, + {"RTAX_FEATURE_ALLFRAG", Const, 0}, + {"RTAX_FEATURE_ECN", Const, 0}, + {"RTAX_FEATURE_SACK", Const, 0}, + {"RTAX_FEATURE_TIMESTAMP", Const, 0}, + {"RTAX_GATEWAY", Const, 0}, + {"RTAX_GENMASK", Const, 0}, + {"RTAX_HOPLIMIT", Const, 0}, + {"RTAX_IFA", Const, 0}, + {"RTAX_IFP", Const, 0}, + {"RTAX_INITCWND", Const, 0}, + {"RTAX_INITRWND", Const, 0}, + {"RTAX_LABEL", Const, 1}, + {"RTAX_LOCK", Const, 0}, + {"RTAX_MAX", Const, 0}, + {"RTAX_MTU", Const, 0}, + {"RTAX_NETMASK", Const, 0}, + {"RTAX_REORDERING", Const, 0}, + {"RTAX_RTO_MIN", Const, 0}, + {"RTAX_RTT", Const, 0}, + {"RTAX_RTTVAR", Const, 0}, + {"RTAX_SRC", Const, 1}, + {"RTAX_SRCMASK", Const, 1}, + {"RTAX_SSTHRESH", Const, 0}, + {"RTAX_TAG", Const, 1}, + {"RTAX_UNSPEC", Const, 0}, + {"RTAX_WINDOW", Const, 0}, + {"RTA_ALIGNTO", Const, 0}, + {"RTA_AUTHOR", Const, 0}, + {"RTA_BRD", Const, 0}, + {"RTA_CACHEINFO", Const, 0}, + {"RTA_DST", Const, 0}, + {"RTA_FLOW", Const, 0}, + {"RTA_GATEWAY", Const, 0}, + {"RTA_GENMASK", Const, 0}, + {"RTA_IFA", Const, 0}, + {"RTA_IFP", Const, 0}, + {"RTA_IIF", Const, 0}, + {"RTA_LABEL", Const, 1}, + {"RTA_MAX", Const, 0}, + {"RTA_METRICS", Const, 0}, + {"RTA_MULTIPATH", Const, 0}, + {"RTA_NETMASK", Const, 0}, + {"RTA_OIF", Const, 0}, + {"RTA_PREFSRC", Const, 0}, + {"RTA_PRIORITY", Const, 0}, + {"RTA_SRC", Const, 0}, + {"RTA_SRCMASK", Const, 1}, + {"RTA_TABLE", Const, 0}, + {"RTA_TAG", Const, 1}, + {"RTA_UNSPEC", Const, 0}, + {"RTCF_DIRECTSRC", Const, 0}, + {"RTCF_DOREDIRECT", Const, 0}, + {"RTCF_LOG", Const, 0}, + {"RTCF_MASQ", Const, 0}, + {"RTCF_NAT", Const, 0}, + {"RTCF_VALVE", Const, 0}, + {"RTF_ADDRCLASSMASK", Const, 0}, + {"RTF_ADDRCONF", Const, 0}, + {"RTF_ALLONLINK", Const, 0}, + {"RTF_ANNOUNCE", Const, 1}, + {"RTF_BLACKHOLE", Const, 0}, + {"RTF_BROADCAST", Const, 0}, + {"RTF_CACHE", Const, 0}, + {"RTF_CLONED", Const, 1}, + {"RTF_CLONING", Const, 0}, + {"RTF_CONDEMNED", Const, 0}, + {"RTF_DEFAULT", Const, 0}, + {"RTF_DELCLONE", Const, 0}, + {"RTF_DONE", Const, 0}, + {"RTF_DYNAMIC", Const, 0}, + {"RTF_FLOW", Const, 0}, + {"RTF_FMASK", Const, 0}, + {"RTF_GATEWAY", Const, 0}, + {"RTF_GWFLAG_COMPAT", Const, 3}, + {"RTF_HOST", Const, 0}, + {"RTF_IFREF", Const, 0}, + {"RTF_IFSCOPE", Const, 0}, + {"RTF_INTERFACE", Const, 0}, + {"RTF_IRTT", Const, 0}, + {"RTF_LINKRT", Const, 0}, + {"RTF_LLDATA", Const, 0}, + {"RTF_LLINFO", Const, 0}, + {"RTF_LOCAL", Const, 0}, + {"RTF_MASK", Const, 1}, + {"RTF_MODIFIED", Const, 0}, + {"RTF_MPATH", Const, 1}, + {"RTF_MPLS", Const, 1}, + {"RTF_MSS", Const, 0}, + {"RTF_MTU", Const, 0}, + {"RTF_MULTICAST", Const, 0}, + {"RTF_NAT", Const, 0}, + {"RTF_NOFORWARD", Const, 0}, + {"RTF_NONEXTHOP", Const, 0}, + {"RTF_NOPMTUDISC", Const, 0}, + {"RTF_PERMANENT_ARP", Const, 1}, + {"RTF_PINNED", Const, 0}, + {"RTF_POLICY", Const, 0}, + {"RTF_PRCLONING", Const, 0}, + {"RTF_PROTO1", Const, 0}, + {"RTF_PROTO2", Const, 0}, + {"RTF_PROTO3", Const, 0}, + {"RTF_PROXY", Const, 16}, + {"RTF_REINSTATE", Const, 0}, + {"RTF_REJECT", Const, 0}, + {"RTF_RNH_LOCKED", Const, 0}, + {"RTF_ROUTER", Const, 16}, + {"RTF_SOURCE", Const, 1}, + {"RTF_SRC", Const, 1}, + {"RTF_STATIC", Const, 0}, + {"RTF_STICKY", Const, 0}, + {"RTF_THROW", Const, 0}, + {"RTF_TUNNEL", Const, 1}, + {"RTF_UP", Const, 0}, + {"RTF_USETRAILERS", Const, 1}, + {"RTF_WASCLONED", Const, 0}, + {"RTF_WINDOW", Const, 0}, + {"RTF_XRESOLVE", Const, 0}, + {"RTM_ADD", Const, 0}, + {"RTM_BASE", Const, 0}, + {"RTM_CHANGE", Const, 0}, + {"RTM_CHGADDR", Const, 1}, + {"RTM_DELACTION", Const, 0}, + {"RTM_DELADDR", Const, 0}, + {"RTM_DELADDRLABEL", Const, 0}, + {"RTM_DELETE", Const, 0}, + {"RTM_DELLINK", Const, 0}, + {"RTM_DELMADDR", Const, 0}, + {"RTM_DELNEIGH", Const, 0}, + {"RTM_DELQDISC", Const, 0}, + {"RTM_DELROUTE", Const, 0}, + {"RTM_DELRULE", Const, 0}, + {"RTM_DELTCLASS", Const, 0}, + {"RTM_DELTFILTER", Const, 0}, + {"RTM_DESYNC", Const, 1}, + {"RTM_F_CLONED", Const, 0}, + {"RTM_F_EQUALIZE", Const, 0}, + {"RTM_F_NOTIFY", Const, 0}, + {"RTM_F_PREFIX", Const, 0}, + {"RTM_GET", Const, 0}, + {"RTM_GET2", Const, 0}, + {"RTM_GETACTION", Const, 0}, + {"RTM_GETADDR", Const, 0}, + {"RTM_GETADDRLABEL", Const, 0}, + {"RTM_GETANYCAST", Const, 0}, + {"RTM_GETDCB", Const, 0}, + {"RTM_GETLINK", Const, 0}, + {"RTM_GETMULTICAST", Const, 0}, + {"RTM_GETNEIGH", Const, 0}, + {"RTM_GETNEIGHTBL", Const, 0}, + {"RTM_GETQDISC", Const, 0}, + {"RTM_GETROUTE", Const, 0}, + {"RTM_GETRULE", Const, 0}, + {"RTM_GETTCLASS", Const, 0}, + {"RTM_GETTFILTER", Const, 0}, + {"RTM_IEEE80211", Const, 0}, + {"RTM_IFANNOUNCE", Const, 0}, + {"RTM_IFINFO", Const, 0}, + {"RTM_IFINFO2", Const, 0}, + {"RTM_LLINFO_UPD", Const, 1}, + {"RTM_LOCK", Const, 0}, + {"RTM_LOSING", Const, 0}, + {"RTM_MAX", Const, 0}, + {"RTM_MAXSIZE", Const, 1}, + {"RTM_MISS", Const, 0}, + {"RTM_NEWACTION", Const, 0}, + {"RTM_NEWADDR", Const, 0}, + {"RTM_NEWADDRLABEL", Const, 0}, + {"RTM_NEWLINK", Const, 0}, + {"RTM_NEWMADDR", Const, 0}, + {"RTM_NEWMADDR2", Const, 0}, + {"RTM_NEWNDUSEROPT", Const, 0}, + {"RTM_NEWNEIGH", Const, 0}, + {"RTM_NEWNEIGHTBL", Const, 0}, + {"RTM_NEWPREFIX", Const, 0}, + {"RTM_NEWQDISC", Const, 0}, + {"RTM_NEWROUTE", Const, 0}, + {"RTM_NEWRULE", Const, 0}, + {"RTM_NEWTCLASS", Const, 0}, + {"RTM_NEWTFILTER", Const, 0}, + {"RTM_NR_FAMILIES", Const, 0}, + {"RTM_NR_MSGTYPES", Const, 0}, + {"RTM_OIFINFO", Const, 1}, + {"RTM_OLDADD", Const, 0}, + {"RTM_OLDDEL", Const, 0}, + {"RTM_OOIFINFO", Const, 1}, + {"RTM_REDIRECT", Const, 0}, + {"RTM_RESOLVE", Const, 0}, + {"RTM_RTTUNIT", Const, 0}, + {"RTM_SETDCB", Const, 0}, + {"RTM_SETGATE", Const, 1}, + {"RTM_SETLINK", Const, 0}, + {"RTM_SETNEIGHTBL", Const, 0}, + {"RTM_VERSION", Const, 0}, + {"RTNH_ALIGNTO", Const, 0}, + {"RTNH_F_DEAD", Const, 0}, + {"RTNH_F_ONLINK", Const, 0}, + {"RTNH_F_PERVASIVE", Const, 0}, + {"RTNLGRP_IPV4_IFADDR", Const, 1}, + {"RTNLGRP_IPV4_MROUTE", Const, 1}, + {"RTNLGRP_IPV4_ROUTE", Const, 1}, + {"RTNLGRP_IPV4_RULE", Const, 1}, + {"RTNLGRP_IPV6_IFADDR", Const, 1}, + {"RTNLGRP_IPV6_IFINFO", Const, 1}, + {"RTNLGRP_IPV6_MROUTE", Const, 1}, + {"RTNLGRP_IPV6_PREFIX", Const, 1}, + {"RTNLGRP_IPV6_ROUTE", Const, 1}, + {"RTNLGRP_IPV6_RULE", Const, 1}, + {"RTNLGRP_LINK", Const, 1}, + {"RTNLGRP_ND_USEROPT", Const, 1}, + {"RTNLGRP_NEIGH", Const, 1}, + {"RTNLGRP_NONE", Const, 1}, + {"RTNLGRP_NOTIFY", Const, 1}, + {"RTNLGRP_TC", Const, 1}, + {"RTN_ANYCAST", Const, 0}, + {"RTN_BLACKHOLE", Const, 0}, + {"RTN_BROADCAST", Const, 0}, + {"RTN_LOCAL", Const, 0}, + {"RTN_MAX", Const, 0}, + {"RTN_MULTICAST", Const, 0}, + {"RTN_NAT", Const, 0}, + {"RTN_PROHIBIT", Const, 0}, + {"RTN_THROW", Const, 0}, + {"RTN_UNICAST", Const, 0}, + {"RTN_UNREACHABLE", Const, 0}, + {"RTN_UNSPEC", Const, 0}, + {"RTN_XRESOLVE", Const, 0}, + {"RTPROT_BIRD", Const, 0}, + {"RTPROT_BOOT", Const, 0}, + {"RTPROT_DHCP", Const, 0}, + {"RTPROT_DNROUTED", Const, 0}, + {"RTPROT_GATED", Const, 0}, + {"RTPROT_KERNEL", Const, 0}, + {"RTPROT_MRT", Const, 0}, + {"RTPROT_NTK", Const, 0}, + {"RTPROT_RA", Const, 0}, + {"RTPROT_REDIRECT", Const, 0}, + {"RTPROT_STATIC", Const, 0}, + {"RTPROT_UNSPEC", Const, 0}, + {"RTPROT_XORP", Const, 0}, + {"RTPROT_ZEBRA", Const, 0}, + {"RTV_EXPIRE", Const, 0}, + {"RTV_HOPCOUNT", Const, 0}, + {"RTV_MTU", Const, 0}, + {"RTV_RPIPE", Const, 0}, + {"RTV_RTT", Const, 0}, + {"RTV_RTTVAR", Const, 0}, + {"RTV_SPIPE", Const, 0}, + {"RTV_SSTHRESH", Const, 0}, + {"RTV_WEIGHT", Const, 0}, + {"RT_CACHING_CONTEXT", Const, 1}, + {"RT_CLASS_DEFAULT", Const, 0}, + {"RT_CLASS_LOCAL", Const, 0}, + {"RT_CLASS_MAIN", Const, 0}, + {"RT_CLASS_MAX", Const, 0}, + {"RT_CLASS_UNSPEC", Const, 0}, + {"RT_DEFAULT_FIB", Const, 1}, + {"RT_NORTREF", Const, 1}, + {"RT_SCOPE_HOST", Const, 0}, + {"RT_SCOPE_LINK", Const, 0}, + {"RT_SCOPE_NOWHERE", Const, 0}, + {"RT_SCOPE_SITE", Const, 0}, + {"RT_SCOPE_UNIVERSE", Const, 0}, + {"RT_TABLEID_MAX", Const, 1}, + {"RT_TABLE_COMPAT", Const, 0}, + {"RT_TABLE_DEFAULT", Const, 0}, + {"RT_TABLE_LOCAL", Const, 0}, + {"RT_TABLE_MAIN", Const, 0}, + {"RT_TABLE_MAX", Const, 0}, + {"RT_TABLE_UNSPEC", Const, 0}, + {"RUSAGE_CHILDREN", Const, 0}, + {"RUSAGE_SELF", Const, 0}, + {"RUSAGE_THREAD", Const, 0}, + {"Radvisory_t", Type, 0}, + {"Radvisory_t.Count", Field, 0}, + {"Radvisory_t.Offset", Field, 0}, + {"Radvisory_t.Pad_cgo_0", Field, 0}, + {"RawConn", Type, 9}, + {"RawSockaddr", Type, 0}, + {"RawSockaddr.Data", Field, 0}, + {"RawSockaddr.Family", Field, 0}, + {"RawSockaddr.Len", Field, 0}, + {"RawSockaddrAny", Type, 0}, + {"RawSockaddrAny.Addr", Field, 0}, + {"RawSockaddrAny.Pad", Field, 0}, + {"RawSockaddrDatalink", Type, 0}, + {"RawSockaddrDatalink.Alen", Field, 0}, + {"RawSockaddrDatalink.Data", Field, 0}, + {"RawSockaddrDatalink.Family", Field, 0}, + {"RawSockaddrDatalink.Index", Field, 0}, + {"RawSockaddrDatalink.Len", Field, 0}, + {"RawSockaddrDatalink.Nlen", Field, 0}, + {"RawSockaddrDatalink.Pad_cgo_0", Field, 2}, + {"RawSockaddrDatalink.Slen", Field, 0}, + {"RawSockaddrDatalink.Type", Field, 0}, + {"RawSockaddrInet4", Type, 0}, + {"RawSockaddrInet4.Addr", Field, 0}, + {"RawSockaddrInet4.Family", Field, 0}, + {"RawSockaddrInet4.Len", Field, 0}, + {"RawSockaddrInet4.Port", Field, 0}, + {"RawSockaddrInet4.Zero", Field, 0}, + {"RawSockaddrInet6", Type, 0}, + {"RawSockaddrInet6.Addr", Field, 0}, + {"RawSockaddrInet6.Family", Field, 0}, + {"RawSockaddrInet6.Flowinfo", Field, 0}, + {"RawSockaddrInet6.Len", Field, 0}, + {"RawSockaddrInet6.Port", Field, 0}, + {"RawSockaddrInet6.Scope_id", Field, 0}, + {"RawSockaddrLinklayer", Type, 0}, + {"RawSockaddrLinklayer.Addr", Field, 0}, + {"RawSockaddrLinklayer.Family", Field, 0}, + {"RawSockaddrLinklayer.Halen", Field, 0}, + {"RawSockaddrLinklayer.Hatype", Field, 0}, + {"RawSockaddrLinklayer.Ifindex", Field, 0}, + {"RawSockaddrLinklayer.Pkttype", Field, 0}, + {"RawSockaddrLinklayer.Protocol", Field, 0}, + {"RawSockaddrNetlink", Type, 0}, + {"RawSockaddrNetlink.Family", Field, 0}, + {"RawSockaddrNetlink.Groups", Field, 0}, + {"RawSockaddrNetlink.Pad", Field, 0}, + {"RawSockaddrNetlink.Pid", Field, 0}, + {"RawSockaddrUnix", Type, 0}, + {"RawSockaddrUnix.Family", Field, 0}, + {"RawSockaddrUnix.Len", Field, 0}, + {"RawSockaddrUnix.Pad_cgo_0", Field, 2}, + {"RawSockaddrUnix.Path", Field, 0}, + {"RawSyscall", Func, 0}, + {"RawSyscall6", Func, 0}, + {"Read", Func, 0}, + {"ReadConsole", Func, 1}, + {"ReadDirectoryChanges", Func, 0}, + {"ReadDirent", Func, 0}, + {"ReadFile", Func, 0}, + {"Readlink", Func, 0}, + {"Reboot", Func, 0}, + {"Recvfrom", Func, 0}, + {"Recvmsg", Func, 0}, + {"RegCloseKey", Func, 0}, + {"RegEnumKeyEx", Func, 0}, + {"RegOpenKeyEx", Func, 0}, + {"RegQueryInfoKey", Func, 0}, + {"RegQueryValueEx", Func, 0}, + {"RemoveDirectory", Func, 0}, + {"Removexattr", Func, 1}, + {"Rename", Func, 0}, + {"Renameat", Func, 0}, + {"Revoke", Func, 0}, + {"Rlimit", Type, 0}, + {"Rlimit.Cur", Field, 0}, + {"Rlimit.Max", Field, 0}, + {"Rmdir", Func, 0}, + {"RouteMessage", Type, 0}, + {"RouteMessage.Data", Field, 0}, + {"RouteMessage.Header", Field, 0}, + {"RouteRIB", Func, 0}, + {"RoutingMessage", Type, 0}, + {"RtAttr", Type, 0}, + {"RtAttr.Len", Field, 0}, + {"RtAttr.Type", Field, 0}, + {"RtGenmsg", Type, 0}, + {"RtGenmsg.Family", Field, 0}, + {"RtMetrics", Type, 0}, + {"RtMetrics.Expire", Field, 0}, + {"RtMetrics.Filler", Field, 0}, + {"RtMetrics.Hopcount", Field, 0}, + {"RtMetrics.Locks", Field, 0}, + {"RtMetrics.Mtu", Field, 0}, + {"RtMetrics.Pad", Field, 3}, + {"RtMetrics.Pksent", Field, 0}, + {"RtMetrics.Recvpipe", Field, 0}, + {"RtMetrics.Refcnt", Field, 2}, + {"RtMetrics.Rtt", Field, 0}, + {"RtMetrics.Rttvar", Field, 0}, + {"RtMetrics.Sendpipe", Field, 0}, + {"RtMetrics.Ssthresh", Field, 0}, + {"RtMetrics.Weight", Field, 0}, + {"RtMsg", Type, 0}, + {"RtMsg.Dst_len", Field, 0}, + {"RtMsg.Family", Field, 0}, + {"RtMsg.Flags", Field, 0}, + {"RtMsg.Protocol", Field, 0}, + {"RtMsg.Scope", Field, 0}, + {"RtMsg.Src_len", Field, 0}, + {"RtMsg.Table", Field, 0}, + {"RtMsg.Tos", Field, 0}, + {"RtMsg.Type", Field, 0}, + {"RtMsghdr", Type, 0}, + {"RtMsghdr.Addrs", Field, 0}, + {"RtMsghdr.Errno", Field, 0}, + {"RtMsghdr.Flags", Field, 0}, + {"RtMsghdr.Fmask", Field, 0}, + {"RtMsghdr.Hdrlen", Field, 2}, + {"RtMsghdr.Index", Field, 0}, + {"RtMsghdr.Inits", Field, 0}, + {"RtMsghdr.Mpls", Field, 2}, + {"RtMsghdr.Msglen", Field, 0}, + {"RtMsghdr.Pad_cgo_0", Field, 0}, + {"RtMsghdr.Pad_cgo_1", Field, 2}, + {"RtMsghdr.Pid", Field, 0}, + {"RtMsghdr.Priority", Field, 2}, + {"RtMsghdr.Rmx", Field, 0}, + {"RtMsghdr.Seq", Field, 0}, + {"RtMsghdr.Tableid", Field, 2}, + {"RtMsghdr.Type", Field, 0}, + {"RtMsghdr.Use", Field, 0}, + {"RtMsghdr.Version", Field, 0}, + {"RtNexthop", Type, 0}, + {"RtNexthop.Flags", Field, 0}, + {"RtNexthop.Hops", Field, 0}, + {"RtNexthop.Ifindex", Field, 0}, + {"RtNexthop.Len", Field, 0}, + {"Rusage", Type, 0}, + {"Rusage.CreationTime", Field, 0}, + {"Rusage.ExitTime", Field, 0}, + {"Rusage.Idrss", Field, 0}, + {"Rusage.Inblock", Field, 0}, + {"Rusage.Isrss", Field, 0}, + {"Rusage.Ixrss", Field, 0}, + {"Rusage.KernelTime", Field, 0}, + {"Rusage.Majflt", Field, 0}, + {"Rusage.Maxrss", Field, 0}, + {"Rusage.Minflt", Field, 0}, + {"Rusage.Msgrcv", Field, 0}, + {"Rusage.Msgsnd", Field, 0}, + {"Rusage.Nivcsw", Field, 0}, + {"Rusage.Nsignals", Field, 0}, + {"Rusage.Nswap", Field, 0}, + {"Rusage.Nvcsw", Field, 0}, + {"Rusage.Oublock", Field, 0}, + {"Rusage.Stime", Field, 0}, + {"Rusage.UserTime", Field, 0}, + {"Rusage.Utime", Field, 0}, + {"SCM_BINTIME", Const, 0}, + {"SCM_CREDENTIALS", Const, 0}, + {"SCM_CREDS", Const, 0}, + {"SCM_RIGHTS", Const, 0}, + {"SCM_TIMESTAMP", Const, 0}, + {"SCM_TIMESTAMPING", Const, 0}, + {"SCM_TIMESTAMPNS", Const, 0}, + {"SCM_TIMESTAMP_MONOTONIC", Const, 0}, + {"SHUT_RD", Const, 0}, + {"SHUT_RDWR", Const, 0}, + {"SHUT_WR", Const, 0}, + {"SID", Type, 0}, + {"SIDAndAttributes", Type, 0}, + {"SIDAndAttributes.Attributes", Field, 0}, + {"SIDAndAttributes.Sid", Field, 0}, + {"SIGABRT", Const, 0}, + {"SIGALRM", Const, 0}, + {"SIGBUS", Const, 0}, + {"SIGCHLD", Const, 0}, + {"SIGCLD", Const, 0}, + {"SIGCONT", Const, 0}, + {"SIGEMT", Const, 0}, + {"SIGFPE", Const, 0}, + {"SIGHUP", Const, 0}, + {"SIGILL", Const, 0}, + {"SIGINFO", Const, 0}, + {"SIGINT", Const, 0}, + {"SIGIO", Const, 0}, + {"SIGIOT", Const, 0}, + {"SIGKILL", Const, 0}, + {"SIGLIBRT", Const, 1}, + {"SIGLWP", Const, 0}, + {"SIGPIPE", Const, 0}, + {"SIGPOLL", Const, 0}, + {"SIGPROF", Const, 0}, + {"SIGPWR", Const, 0}, + {"SIGQUIT", Const, 0}, + {"SIGSEGV", Const, 0}, + {"SIGSTKFLT", Const, 0}, + {"SIGSTOP", Const, 0}, + {"SIGSYS", Const, 0}, + {"SIGTERM", Const, 0}, + {"SIGTHR", Const, 0}, + {"SIGTRAP", Const, 0}, + {"SIGTSTP", Const, 0}, + {"SIGTTIN", Const, 0}, + {"SIGTTOU", Const, 0}, + {"SIGUNUSED", Const, 0}, + {"SIGURG", Const, 0}, + {"SIGUSR1", Const, 0}, + {"SIGUSR2", Const, 0}, + {"SIGVTALRM", Const, 0}, + {"SIGWINCH", Const, 0}, + {"SIGXCPU", Const, 0}, + {"SIGXFSZ", Const, 0}, + {"SIOCADDDLCI", Const, 0}, + {"SIOCADDMULTI", Const, 0}, + {"SIOCADDRT", Const, 0}, + {"SIOCAIFADDR", Const, 0}, + {"SIOCAIFGROUP", Const, 0}, + {"SIOCALIFADDR", Const, 0}, + {"SIOCARPIPLL", Const, 0}, + {"SIOCATMARK", Const, 0}, + {"SIOCAUTOADDR", Const, 0}, + {"SIOCAUTONETMASK", Const, 0}, + {"SIOCBRDGADD", Const, 1}, + {"SIOCBRDGADDS", Const, 1}, + {"SIOCBRDGARL", Const, 1}, + {"SIOCBRDGDADDR", Const, 1}, + {"SIOCBRDGDEL", Const, 1}, + {"SIOCBRDGDELS", Const, 1}, + {"SIOCBRDGFLUSH", Const, 1}, + {"SIOCBRDGFRL", Const, 1}, + {"SIOCBRDGGCACHE", Const, 1}, + {"SIOCBRDGGFD", Const, 1}, + {"SIOCBRDGGHT", Const, 1}, + {"SIOCBRDGGIFFLGS", Const, 1}, + {"SIOCBRDGGMA", Const, 1}, + {"SIOCBRDGGPARAM", Const, 1}, + {"SIOCBRDGGPRI", Const, 1}, + {"SIOCBRDGGRL", Const, 1}, + {"SIOCBRDGGSIFS", Const, 1}, + {"SIOCBRDGGTO", Const, 1}, + {"SIOCBRDGIFS", Const, 1}, + {"SIOCBRDGRTS", Const, 1}, + {"SIOCBRDGSADDR", Const, 1}, + {"SIOCBRDGSCACHE", Const, 1}, + {"SIOCBRDGSFD", Const, 1}, + {"SIOCBRDGSHT", Const, 1}, + {"SIOCBRDGSIFCOST", Const, 1}, + {"SIOCBRDGSIFFLGS", Const, 1}, + {"SIOCBRDGSIFPRIO", Const, 1}, + {"SIOCBRDGSMA", Const, 1}, + {"SIOCBRDGSPRI", Const, 1}, + {"SIOCBRDGSPROTO", Const, 1}, + {"SIOCBRDGSTO", Const, 1}, + {"SIOCBRDGSTXHC", Const, 1}, + {"SIOCDARP", Const, 0}, + {"SIOCDELDLCI", Const, 0}, + {"SIOCDELMULTI", Const, 0}, + {"SIOCDELRT", Const, 0}, + {"SIOCDEVPRIVATE", Const, 0}, + {"SIOCDIFADDR", Const, 0}, + {"SIOCDIFGROUP", Const, 0}, + {"SIOCDIFPHYADDR", Const, 0}, + {"SIOCDLIFADDR", Const, 0}, + {"SIOCDRARP", Const, 0}, + {"SIOCGARP", Const, 0}, + {"SIOCGDRVSPEC", Const, 0}, + {"SIOCGETKALIVE", Const, 1}, + {"SIOCGETLABEL", Const, 1}, + {"SIOCGETPFLOW", Const, 1}, + {"SIOCGETPFSYNC", Const, 1}, + {"SIOCGETSGCNT", Const, 0}, + {"SIOCGETVIFCNT", Const, 0}, + {"SIOCGETVLAN", Const, 0}, + {"SIOCGHIWAT", Const, 0}, + {"SIOCGIFADDR", Const, 0}, + {"SIOCGIFADDRPREF", Const, 1}, + {"SIOCGIFALIAS", Const, 1}, + {"SIOCGIFALTMTU", Const, 0}, + {"SIOCGIFASYNCMAP", Const, 0}, + {"SIOCGIFBOND", Const, 0}, + {"SIOCGIFBR", Const, 0}, + {"SIOCGIFBRDADDR", Const, 0}, + {"SIOCGIFCAP", Const, 0}, + {"SIOCGIFCONF", Const, 0}, + {"SIOCGIFCOUNT", Const, 0}, + {"SIOCGIFDATA", Const, 1}, + {"SIOCGIFDESCR", Const, 0}, + {"SIOCGIFDEVMTU", Const, 0}, + {"SIOCGIFDLT", Const, 1}, + {"SIOCGIFDSTADDR", Const, 0}, + {"SIOCGIFENCAP", Const, 0}, + {"SIOCGIFFIB", Const, 1}, + {"SIOCGIFFLAGS", Const, 0}, + {"SIOCGIFGATTR", Const, 1}, + {"SIOCGIFGENERIC", Const, 0}, + {"SIOCGIFGMEMB", Const, 0}, + {"SIOCGIFGROUP", Const, 0}, + {"SIOCGIFHARDMTU", Const, 3}, + {"SIOCGIFHWADDR", Const, 0}, + {"SIOCGIFINDEX", Const, 0}, + {"SIOCGIFKPI", Const, 0}, + {"SIOCGIFMAC", Const, 0}, + {"SIOCGIFMAP", Const, 0}, + {"SIOCGIFMEDIA", Const, 0}, + {"SIOCGIFMEM", Const, 0}, + {"SIOCGIFMETRIC", Const, 0}, + {"SIOCGIFMTU", Const, 0}, + {"SIOCGIFNAME", Const, 0}, + {"SIOCGIFNETMASK", Const, 0}, + {"SIOCGIFPDSTADDR", Const, 0}, + {"SIOCGIFPFLAGS", Const, 0}, + {"SIOCGIFPHYS", Const, 0}, + {"SIOCGIFPRIORITY", Const, 1}, + {"SIOCGIFPSRCADDR", Const, 0}, + {"SIOCGIFRDOMAIN", Const, 1}, + {"SIOCGIFRTLABEL", Const, 1}, + {"SIOCGIFSLAVE", Const, 0}, + {"SIOCGIFSTATUS", Const, 0}, + {"SIOCGIFTIMESLOT", Const, 1}, + {"SIOCGIFTXQLEN", Const, 0}, + {"SIOCGIFVLAN", Const, 0}, + {"SIOCGIFWAKEFLAGS", Const, 0}, + {"SIOCGIFXFLAGS", Const, 1}, + {"SIOCGLIFADDR", Const, 0}, + {"SIOCGLIFPHYADDR", Const, 0}, + {"SIOCGLIFPHYRTABLE", Const, 1}, + {"SIOCGLIFPHYTTL", Const, 3}, + {"SIOCGLINKSTR", Const, 1}, + {"SIOCGLOWAT", Const, 0}, + {"SIOCGPGRP", Const, 0}, + {"SIOCGPRIVATE_0", Const, 0}, + {"SIOCGPRIVATE_1", Const, 0}, + {"SIOCGRARP", Const, 0}, + {"SIOCGSPPPPARAMS", Const, 3}, + {"SIOCGSTAMP", Const, 0}, + {"SIOCGSTAMPNS", Const, 0}, + {"SIOCGVH", Const, 1}, + {"SIOCGVNETID", Const, 3}, + {"SIOCIFCREATE", Const, 0}, + {"SIOCIFCREATE2", Const, 0}, + {"SIOCIFDESTROY", Const, 0}, + {"SIOCIFGCLONERS", Const, 0}, + {"SIOCINITIFADDR", Const, 1}, + {"SIOCPROTOPRIVATE", Const, 0}, + {"SIOCRSLVMULTI", Const, 0}, + {"SIOCRTMSG", Const, 0}, + {"SIOCSARP", Const, 0}, + {"SIOCSDRVSPEC", Const, 0}, + {"SIOCSETKALIVE", Const, 1}, + {"SIOCSETLABEL", Const, 1}, + {"SIOCSETPFLOW", Const, 1}, + {"SIOCSETPFSYNC", Const, 1}, + {"SIOCSETVLAN", Const, 0}, + {"SIOCSHIWAT", Const, 0}, + {"SIOCSIFADDR", Const, 0}, + {"SIOCSIFADDRPREF", Const, 1}, + {"SIOCSIFALTMTU", Const, 0}, + {"SIOCSIFASYNCMAP", Const, 0}, + {"SIOCSIFBOND", Const, 0}, + {"SIOCSIFBR", Const, 0}, + {"SIOCSIFBRDADDR", Const, 0}, + {"SIOCSIFCAP", Const, 0}, + {"SIOCSIFDESCR", Const, 0}, + {"SIOCSIFDSTADDR", Const, 0}, + {"SIOCSIFENCAP", Const, 0}, + {"SIOCSIFFIB", Const, 1}, + {"SIOCSIFFLAGS", Const, 0}, + {"SIOCSIFGATTR", Const, 1}, + {"SIOCSIFGENERIC", Const, 0}, + {"SIOCSIFHWADDR", Const, 0}, + {"SIOCSIFHWBROADCAST", Const, 0}, + {"SIOCSIFKPI", Const, 0}, + {"SIOCSIFLINK", Const, 0}, + {"SIOCSIFLLADDR", Const, 0}, + {"SIOCSIFMAC", Const, 0}, + {"SIOCSIFMAP", Const, 0}, + {"SIOCSIFMEDIA", Const, 0}, + {"SIOCSIFMEM", Const, 0}, + {"SIOCSIFMETRIC", Const, 0}, + {"SIOCSIFMTU", Const, 0}, + {"SIOCSIFNAME", Const, 0}, + {"SIOCSIFNETMASK", Const, 0}, + {"SIOCSIFPFLAGS", Const, 0}, + {"SIOCSIFPHYADDR", Const, 0}, + {"SIOCSIFPHYS", Const, 0}, + {"SIOCSIFPRIORITY", Const, 1}, + {"SIOCSIFRDOMAIN", Const, 1}, + {"SIOCSIFRTLABEL", Const, 1}, + {"SIOCSIFRVNET", Const, 0}, + {"SIOCSIFSLAVE", Const, 0}, + {"SIOCSIFTIMESLOT", Const, 1}, + {"SIOCSIFTXQLEN", Const, 0}, + {"SIOCSIFVLAN", Const, 0}, + {"SIOCSIFVNET", Const, 0}, + {"SIOCSIFXFLAGS", Const, 1}, + {"SIOCSLIFPHYADDR", Const, 0}, + {"SIOCSLIFPHYRTABLE", Const, 1}, + {"SIOCSLIFPHYTTL", Const, 3}, + {"SIOCSLINKSTR", Const, 1}, + {"SIOCSLOWAT", Const, 0}, + {"SIOCSPGRP", Const, 0}, + {"SIOCSRARP", Const, 0}, + {"SIOCSSPPPPARAMS", Const, 3}, + {"SIOCSVH", Const, 1}, + {"SIOCSVNETID", Const, 3}, + {"SIOCZIFDATA", Const, 1}, + {"SIO_GET_EXTENSION_FUNCTION_POINTER", Const, 1}, + {"SIO_GET_INTERFACE_LIST", Const, 0}, + {"SIO_KEEPALIVE_VALS", Const, 3}, + {"SIO_UDP_CONNRESET", Const, 4}, + {"SOCK_CLOEXEC", Const, 0}, + {"SOCK_DCCP", Const, 0}, + {"SOCK_DGRAM", Const, 0}, + {"SOCK_FLAGS_MASK", Const, 1}, + {"SOCK_MAXADDRLEN", Const, 0}, + {"SOCK_NONBLOCK", Const, 0}, + {"SOCK_NOSIGPIPE", Const, 1}, + {"SOCK_PACKET", Const, 0}, + {"SOCK_RAW", Const, 0}, + {"SOCK_RDM", Const, 0}, + {"SOCK_SEQPACKET", Const, 0}, + {"SOCK_STREAM", Const, 0}, + {"SOL_AAL", Const, 0}, + {"SOL_ATM", Const, 0}, + {"SOL_DECNET", Const, 0}, + {"SOL_ICMPV6", Const, 0}, + {"SOL_IP", Const, 0}, + {"SOL_IPV6", Const, 0}, + {"SOL_IRDA", Const, 0}, + {"SOL_PACKET", Const, 0}, + {"SOL_RAW", Const, 0}, + {"SOL_SOCKET", Const, 0}, + {"SOL_TCP", Const, 0}, + {"SOL_X25", Const, 0}, + {"SOMAXCONN", Const, 0}, + {"SO_ACCEPTCONN", Const, 0}, + {"SO_ACCEPTFILTER", Const, 0}, + {"SO_ATTACH_FILTER", Const, 0}, + {"SO_BINDANY", Const, 1}, + {"SO_BINDTODEVICE", Const, 0}, + {"SO_BINTIME", Const, 0}, + {"SO_BROADCAST", Const, 0}, + {"SO_BSDCOMPAT", Const, 0}, + {"SO_DEBUG", Const, 0}, + {"SO_DETACH_FILTER", Const, 0}, + {"SO_DOMAIN", Const, 0}, + {"SO_DONTROUTE", Const, 0}, + {"SO_DONTTRUNC", Const, 0}, + {"SO_ERROR", Const, 0}, + {"SO_KEEPALIVE", Const, 0}, + {"SO_LABEL", Const, 0}, + {"SO_LINGER", Const, 0}, + {"SO_LINGER_SEC", Const, 0}, + {"SO_LISTENINCQLEN", Const, 0}, + {"SO_LISTENQLEN", Const, 0}, + {"SO_LISTENQLIMIT", Const, 0}, + {"SO_MARK", Const, 0}, + {"SO_NETPROC", Const, 1}, + {"SO_NKE", Const, 0}, + {"SO_NOADDRERR", Const, 0}, + {"SO_NOHEADER", Const, 1}, + {"SO_NOSIGPIPE", Const, 0}, + {"SO_NOTIFYCONFLICT", Const, 0}, + {"SO_NO_CHECK", Const, 0}, + {"SO_NO_DDP", Const, 0}, + {"SO_NO_OFFLOAD", Const, 0}, + {"SO_NP_EXTENSIONS", Const, 0}, + {"SO_NREAD", Const, 0}, + {"SO_NUMRCVPKT", Const, 16}, + {"SO_NWRITE", Const, 0}, + {"SO_OOBINLINE", Const, 0}, + {"SO_OVERFLOWED", Const, 1}, + {"SO_PASSCRED", Const, 0}, + {"SO_PASSSEC", Const, 0}, + {"SO_PEERCRED", Const, 0}, + {"SO_PEERLABEL", Const, 0}, + {"SO_PEERNAME", Const, 0}, + {"SO_PEERSEC", Const, 0}, + {"SO_PRIORITY", Const, 0}, + {"SO_PROTOCOL", Const, 0}, + {"SO_PROTOTYPE", Const, 1}, + {"SO_RANDOMPORT", Const, 0}, + {"SO_RCVBUF", Const, 0}, + {"SO_RCVBUFFORCE", Const, 0}, + {"SO_RCVLOWAT", Const, 0}, + {"SO_RCVTIMEO", Const, 0}, + {"SO_RESTRICTIONS", Const, 0}, + {"SO_RESTRICT_DENYIN", Const, 0}, + {"SO_RESTRICT_DENYOUT", Const, 0}, + {"SO_RESTRICT_DENYSET", Const, 0}, + {"SO_REUSEADDR", Const, 0}, + {"SO_REUSEPORT", Const, 0}, + {"SO_REUSESHAREUID", Const, 0}, + {"SO_RTABLE", Const, 1}, + {"SO_RXQ_OVFL", Const, 0}, + {"SO_SECURITY_AUTHENTICATION", Const, 0}, + {"SO_SECURITY_ENCRYPTION_NETWORK", Const, 0}, + {"SO_SECURITY_ENCRYPTION_TRANSPORT", Const, 0}, + {"SO_SETFIB", Const, 0}, + {"SO_SNDBUF", Const, 0}, + {"SO_SNDBUFFORCE", Const, 0}, + {"SO_SNDLOWAT", Const, 0}, + {"SO_SNDTIMEO", Const, 0}, + {"SO_SPLICE", Const, 1}, + {"SO_TIMESTAMP", Const, 0}, + {"SO_TIMESTAMPING", Const, 0}, + {"SO_TIMESTAMPNS", Const, 0}, + {"SO_TIMESTAMP_MONOTONIC", Const, 0}, + {"SO_TYPE", Const, 0}, + {"SO_UPCALLCLOSEWAIT", Const, 0}, + {"SO_UPDATE_ACCEPT_CONTEXT", Const, 0}, + {"SO_UPDATE_CONNECT_CONTEXT", Const, 1}, + {"SO_USELOOPBACK", Const, 0}, + {"SO_USER_COOKIE", Const, 1}, + {"SO_VENDOR", Const, 3}, + {"SO_WANTMORE", Const, 0}, + {"SO_WANTOOBFLAG", Const, 0}, + {"SSLExtraCertChainPolicyPara", Type, 0}, + {"SSLExtraCertChainPolicyPara.AuthType", Field, 0}, + {"SSLExtraCertChainPolicyPara.Checks", Field, 0}, + {"SSLExtraCertChainPolicyPara.ServerName", Field, 0}, + {"SSLExtraCertChainPolicyPara.Size", Field, 0}, + {"STANDARD_RIGHTS_ALL", Const, 0}, + {"STANDARD_RIGHTS_EXECUTE", Const, 0}, + {"STANDARD_RIGHTS_READ", Const, 0}, + {"STANDARD_RIGHTS_REQUIRED", Const, 0}, + {"STANDARD_RIGHTS_WRITE", Const, 0}, + {"STARTF_USESHOWWINDOW", Const, 0}, + {"STARTF_USESTDHANDLES", Const, 0}, + {"STD_ERROR_HANDLE", Const, 0}, + {"STD_INPUT_HANDLE", Const, 0}, + {"STD_OUTPUT_HANDLE", Const, 0}, + {"SUBLANG_ENGLISH_US", Const, 0}, + {"SW_FORCEMINIMIZE", Const, 0}, + {"SW_HIDE", Const, 0}, + {"SW_MAXIMIZE", Const, 0}, + {"SW_MINIMIZE", Const, 0}, + {"SW_NORMAL", Const, 0}, + {"SW_RESTORE", Const, 0}, + {"SW_SHOW", Const, 0}, + {"SW_SHOWDEFAULT", Const, 0}, + {"SW_SHOWMAXIMIZED", Const, 0}, + {"SW_SHOWMINIMIZED", Const, 0}, + {"SW_SHOWMINNOACTIVE", Const, 0}, + {"SW_SHOWNA", Const, 0}, + {"SW_SHOWNOACTIVATE", Const, 0}, + {"SW_SHOWNORMAL", Const, 0}, + {"SYMBOLIC_LINK_FLAG_DIRECTORY", Const, 4}, + {"SYNCHRONIZE", Const, 0}, + {"SYSCTL_VERSION", Const, 1}, + {"SYSCTL_VERS_0", Const, 1}, + {"SYSCTL_VERS_1", Const, 1}, + {"SYSCTL_VERS_MASK", Const, 1}, + {"SYS_ABORT2", Const, 0}, + {"SYS_ACCEPT", Const, 0}, + {"SYS_ACCEPT4", Const, 0}, + {"SYS_ACCEPT_NOCANCEL", Const, 0}, + {"SYS_ACCESS", Const, 0}, + {"SYS_ACCESS_EXTENDED", Const, 0}, + {"SYS_ACCT", Const, 0}, + {"SYS_ADD_KEY", Const, 0}, + {"SYS_ADD_PROFIL", Const, 0}, + {"SYS_ADJFREQ", Const, 1}, + {"SYS_ADJTIME", Const, 0}, + {"SYS_ADJTIMEX", Const, 0}, + {"SYS_AFS_SYSCALL", Const, 0}, + {"SYS_AIO_CANCEL", Const, 0}, + {"SYS_AIO_ERROR", Const, 0}, + {"SYS_AIO_FSYNC", Const, 0}, + {"SYS_AIO_MLOCK", Const, 14}, + {"SYS_AIO_READ", Const, 0}, + {"SYS_AIO_RETURN", Const, 0}, + {"SYS_AIO_SUSPEND", Const, 0}, + {"SYS_AIO_SUSPEND_NOCANCEL", Const, 0}, + {"SYS_AIO_WAITCOMPLETE", Const, 14}, + {"SYS_AIO_WRITE", Const, 0}, + {"SYS_ALARM", Const, 0}, + {"SYS_ARCH_PRCTL", Const, 0}, + {"SYS_ARM_FADVISE64_64", Const, 0}, + {"SYS_ARM_SYNC_FILE_RANGE", Const, 0}, + {"SYS_ATGETMSG", Const, 0}, + {"SYS_ATPGETREQ", Const, 0}, + {"SYS_ATPGETRSP", Const, 0}, + {"SYS_ATPSNDREQ", Const, 0}, + {"SYS_ATPSNDRSP", Const, 0}, + {"SYS_ATPUTMSG", Const, 0}, + {"SYS_ATSOCKET", Const, 0}, + {"SYS_AUDIT", Const, 0}, + {"SYS_AUDITCTL", Const, 0}, + {"SYS_AUDITON", Const, 0}, + {"SYS_AUDIT_SESSION_JOIN", Const, 0}, + {"SYS_AUDIT_SESSION_PORT", Const, 0}, + {"SYS_AUDIT_SESSION_SELF", Const, 0}, + {"SYS_BDFLUSH", Const, 0}, + {"SYS_BIND", Const, 0}, + {"SYS_BINDAT", Const, 3}, + {"SYS_BREAK", Const, 0}, + {"SYS_BRK", Const, 0}, + {"SYS_BSDTHREAD_CREATE", Const, 0}, + {"SYS_BSDTHREAD_REGISTER", Const, 0}, + {"SYS_BSDTHREAD_TERMINATE", Const, 0}, + {"SYS_CAPGET", Const, 0}, + {"SYS_CAPSET", Const, 0}, + {"SYS_CAP_ENTER", Const, 0}, + {"SYS_CAP_FCNTLS_GET", Const, 1}, + {"SYS_CAP_FCNTLS_LIMIT", Const, 1}, + {"SYS_CAP_GETMODE", Const, 0}, + {"SYS_CAP_GETRIGHTS", Const, 0}, + {"SYS_CAP_IOCTLS_GET", Const, 1}, + {"SYS_CAP_IOCTLS_LIMIT", Const, 1}, + {"SYS_CAP_NEW", Const, 0}, + {"SYS_CAP_RIGHTS_GET", Const, 1}, + {"SYS_CAP_RIGHTS_LIMIT", Const, 1}, + {"SYS_CHDIR", Const, 0}, + {"SYS_CHFLAGS", Const, 0}, + {"SYS_CHFLAGSAT", Const, 3}, + {"SYS_CHMOD", Const, 0}, + {"SYS_CHMOD_EXTENDED", Const, 0}, + {"SYS_CHOWN", Const, 0}, + {"SYS_CHOWN32", Const, 0}, + {"SYS_CHROOT", Const, 0}, + {"SYS_CHUD", Const, 0}, + {"SYS_CLOCK_ADJTIME", Const, 0}, + {"SYS_CLOCK_GETCPUCLOCKID2", Const, 1}, + {"SYS_CLOCK_GETRES", Const, 0}, + {"SYS_CLOCK_GETTIME", Const, 0}, + {"SYS_CLOCK_NANOSLEEP", Const, 0}, + {"SYS_CLOCK_SETTIME", Const, 0}, + {"SYS_CLONE", Const, 0}, + {"SYS_CLOSE", Const, 0}, + {"SYS_CLOSEFROM", Const, 0}, + {"SYS_CLOSE_NOCANCEL", Const, 0}, + {"SYS_CONNECT", Const, 0}, + {"SYS_CONNECTAT", Const, 3}, + {"SYS_CONNECT_NOCANCEL", Const, 0}, + {"SYS_COPYFILE", Const, 0}, + {"SYS_CPUSET", Const, 0}, + {"SYS_CPUSET_GETAFFINITY", Const, 0}, + {"SYS_CPUSET_GETID", Const, 0}, + {"SYS_CPUSET_SETAFFINITY", Const, 0}, + {"SYS_CPUSET_SETID", Const, 0}, + {"SYS_CREAT", Const, 0}, + {"SYS_CREATE_MODULE", Const, 0}, + {"SYS_CSOPS", Const, 0}, + {"SYS_CSOPS_AUDITTOKEN", Const, 16}, + {"SYS_DELETE", Const, 0}, + {"SYS_DELETE_MODULE", Const, 0}, + {"SYS_DUP", Const, 0}, + {"SYS_DUP2", Const, 0}, + {"SYS_DUP3", Const, 0}, + {"SYS_EACCESS", Const, 0}, + {"SYS_EPOLL_CREATE", Const, 0}, + {"SYS_EPOLL_CREATE1", Const, 0}, + {"SYS_EPOLL_CTL", Const, 0}, + {"SYS_EPOLL_CTL_OLD", Const, 0}, + {"SYS_EPOLL_PWAIT", Const, 0}, + {"SYS_EPOLL_WAIT", Const, 0}, + {"SYS_EPOLL_WAIT_OLD", Const, 0}, + {"SYS_EVENTFD", Const, 0}, + {"SYS_EVENTFD2", Const, 0}, + {"SYS_EXCHANGEDATA", Const, 0}, + {"SYS_EXECVE", Const, 0}, + {"SYS_EXIT", Const, 0}, + {"SYS_EXIT_GROUP", Const, 0}, + {"SYS_EXTATTRCTL", Const, 0}, + {"SYS_EXTATTR_DELETE_FD", Const, 0}, + {"SYS_EXTATTR_DELETE_FILE", Const, 0}, + {"SYS_EXTATTR_DELETE_LINK", Const, 0}, + {"SYS_EXTATTR_GET_FD", Const, 0}, + {"SYS_EXTATTR_GET_FILE", Const, 0}, + {"SYS_EXTATTR_GET_LINK", Const, 0}, + {"SYS_EXTATTR_LIST_FD", Const, 0}, + {"SYS_EXTATTR_LIST_FILE", Const, 0}, + {"SYS_EXTATTR_LIST_LINK", Const, 0}, + {"SYS_EXTATTR_SET_FD", Const, 0}, + {"SYS_EXTATTR_SET_FILE", Const, 0}, + {"SYS_EXTATTR_SET_LINK", Const, 0}, + {"SYS_FACCESSAT", Const, 0}, + {"SYS_FADVISE64", Const, 0}, + {"SYS_FADVISE64_64", Const, 0}, + {"SYS_FALLOCATE", Const, 0}, + {"SYS_FANOTIFY_INIT", Const, 0}, + {"SYS_FANOTIFY_MARK", Const, 0}, + {"SYS_FCHDIR", Const, 0}, + {"SYS_FCHFLAGS", Const, 0}, + {"SYS_FCHMOD", Const, 0}, + {"SYS_FCHMODAT", Const, 0}, + {"SYS_FCHMOD_EXTENDED", Const, 0}, + {"SYS_FCHOWN", Const, 0}, + {"SYS_FCHOWN32", Const, 0}, + {"SYS_FCHOWNAT", Const, 0}, + {"SYS_FCHROOT", Const, 1}, + {"SYS_FCNTL", Const, 0}, + {"SYS_FCNTL64", Const, 0}, + {"SYS_FCNTL_NOCANCEL", Const, 0}, + {"SYS_FDATASYNC", Const, 0}, + {"SYS_FEXECVE", Const, 0}, + {"SYS_FFCLOCK_GETCOUNTER", Const, 0}, + {"SYS_FFCLOCK_GETESTIMATE", Const, 0}, + {"SYS_FFCLOCK_SETESTIMATE", Const, 0}, + {"SYS_FFSCTL", Const, 0}, + {"SYS_FGETATTRLIST", Const, 0}, + {"SYS_FGETXATTR", Const, 0}, + {"SYS_FHOPEN", Const, 0}, + {"SYS_FHSTAT", Const, 0}, + {"SYS_FHSTATFS", Const, 0}, + {"SYS_FILEPORT_MAKEFD", Const, 0}, + {"SYS_FILEPORT_MAKEPORT", Const, 0}, + {"SYS_FKTRACE", Const, 1}, + {"SYS_FLISTXATTR", Const, 0}, + {"SYS_FLOCK", Const, 0}, + {"SYS_FORK", Const, 0}, + {"SYS_FPATHCONF", Const, 0}, + {"SYS_FREEBSD6_FTRUNCATE", Const, 0}, + {"SYS_FREEBSD6_LSEEK", Const, 0}, + {"SYS_FREEBSD6_MMAP", Const, 0}, + {"SYS_FREEBSD6_PREAD", Const, 0}, + {"SYS_FREEBSD6_PWRITE", Const, 0}, + {"SYS_FREEBSD6_TRUNCATE", Const, 0}, + {"SYS_FREMOVEXATTR", Const, 0}, + {"SYS_FSCTL", Const, 0}, + {"SYS_FSETATTRLIST", Const, 0}, + {"SYS_FSETXATTR", Const, 0}, + {"SYS_FSGETPATH", Const, 0}, + {"SYS_FSTAT", Const, 0}, + {"SYS_FSTAT64", Const, 0}, + {"SYS_FSTAT64_EXTENDED", Const, 0}, + {"SYS_FSTATAT", Const, 0}, + {"SYS_FSTATAT64", Const, 0}, + {"SYS_FSTATFS", Const, 0}, + {"SYS_FSTATFS64", Const, 0}, + {"SYS_FSTATV", Const, 0}, + {"SYS_FSTATVFS1", Const, 1}, + {"SYS_FSTAT_EXTENDED", Const, 0}, + {"SYS_FSYNC", Const, 0}, + {"SYS_FSYNC_NOCANCEL", Const, 0}, + {"SYS_FSYNC_RANGE", Const, 1}, + {"SYS_FTIME", Const, 0}, + {"SYS_FTRUNCATE", Const, 0}, + {"SYS_FTRUNCATE64", Const, 0}, + {"SYS_FUTEX", Const, 0}, + {"SYS_FUTIMENS", Const, 1}, + {"SYS_FUTIMES", Const, 0}, + {"SYS_FUTIMESAT", Const, 0}, + {"SYS_GETATTRLIST", Const, 0}, + {"SYS_GETAUDIT", Const, 0}, + {"SYS_GETAUDIT_ADDR", Const, 0}, + {"SYS_GETAUID", Const, 0}, + {"SYS_GETCONTEXT", Const, 0}, + {"SYS_GETCPU", Const, 0}, + {"SYS_GETCWD", Const, 0}, + {"SYS_GETDENTS", Const, 0}, + {"SYS_GETDENTS64", Const, 0}, + {"SYS_GETDIRENTRIES", Const, 0}, + {"SYS_GETDIRENTRIES64", Const, 0}, + {"SYS_GETDIRENTRIESATTR", Const, 0}, + {"SYS_GETDTABLECOUNT", Const, 1}, + {"SYS_GETDTABLESIZE", Const, 0}, + {"SYS_GETEGID", Const, 0}, + {"SYS_GETEGID32", Const, 0}, + {"SYS_GETEUID", Const, 0}, + {"SYS_GETEUID32", Const, 0}, + {"SYS_GETFH", Const, 0}, + {"SYS_GETFSSTAT", Const, 0}, + {"SYS_GETFSSTAT64", Const, 0}, + {"SYS_GETGID", Const, 0}, + {"SYS_GETGID32", Const, 0}, + {"SYS_GETGROUPS", Const, 0}, + {"SYS_GETGROUPS32", Const, 0}, + {"SYS_GETHOSTUUID", Const, 0}, + {"SYS_GETITIMER", Const, 0}, + {"SYS_GETLCID", Const, 0}, + {"SYS_GETLOGIN", Const, 0}, + {"SYS_GETLOGINCLASS", Const, 0}, + {"SYS_GETPEERNAME", Const, 0}, + {"SYS_GETPGID", Const, 0}, + {"SYS_GETPGRP", Const, 0}, + {"SYS_GETPID", Const, 0}, + {"SYS_GETPMSG", Const, 0}, + {"SYS_GETPPID", Const, 0}, + {"SYS_GETPRIORITY", Const, 0}, + {"SYS_GETRESGID", Const, 0}, + {"SYS_GETRESGID32", Const, 0}, + {"SYS_GETRESUID", Const, 0}, + {"SYS_GETRESUID32", Const, 0}, + {"SYS_GETRLIMIT", Const, 0}, + {"SYS_GETRTABLE", Const, 1}, + {"SYS_GETRUSAGE", Const, 0}, + {"SYS_GETSGROUPS", Const, 0}, + {"SYS_GETSID", Const, 0}, + {"SYS_GETSOCKNAME", Const, 0}, + {"SYS_GETSOCKOPT", Const, 0}, + {"SYS_GETTHRID", Const, 1}, + {"SYS_GETTID", Const, 0}, + {"SYS_GETTIMEOFDAY", Const, 0}, + {"SYS_GETUID", Const, 0}, + {"SYS_GETUID32", Const, 0}, + {"SYS_GETVFSSTAT", Const, 1}, + {"SYS_GETWGROUPS", Const, 0}, + {"SYS_GETXATTR", Const, 0}, + {"SYS_GET_KERNEL_SYMS", Const, 0}, + {"SYS_GET_MEMPOLICY", Const, 0}, + {"SYS_GET_ROBUST_LIST", Const, 0}, + {"SYS_GET_THREAD_AREA", Const, 0}, + {"SYS_GSSD_SYSCALL", Const, 14}, + {"SYS_GTTY", Const, 0}, + {"SYS_IDENTITYSVC", Const, 0}, + {"SYS_IDLE", Const, 0}, + {"SYS_INITGROUPS", Const, 0}, + {"SYS_INIT_MODULE", Const, 0}, + {"SYS_INOTIFY_ADD_WATCH", Const, 0}, + {"SYS_INOTIFY_INIT", Const, 0}, + {"SYS_INOTIFY_INIT1", Const, 0}, + {"SYS_INOTIFY_RM_WATCH", Const, 0}, + {"SYS_IOCTL", Const, 0}, + {"SYS_IOPERM", Const, 0}, + {"SYS_IOPL", Const, 0}, + {"SYS_IOPOLICYSYS", Const, 0}, + {"SYS_IOPRIO_GET", Const, 0}, + {"SYS_IOPRIO_SET", Const, 0}, + {"SYS_IO_CANCEL", Const, 0}, + {"SYS_IO_DESTROY", Const, 0}, + {"SYS_IO_GETEVENTS", Const, 0}, + {"SYS_IO_SETUP", Const, 0}, + {"SYS_IO_SUBMIT", Const, 0}, + {"SYS_IPC", Const, 0}, + {"SYS_ISSETUGID", Const, 0}, + {"SYS_JAIL", Const, 0}, + {"SYS_JAIL_ATTACH", Const, 0}, + {"SYS_JAIL_GET", Const, 0}, + {"SYS_JAIL_REMOVE", Const, 0}, + {"SYS_JAIL_SET", Const, 0}, + {"SYS_KAS_INFO", Const, 16}, + {"SYS_KDEBUG_TRACE", Const, 0}, + {"SYS_KENV", Const, 0}, + {"SYS_KEVENT", Const, 0}, + {"SYS_KEVENT64", Const, 0}, + {"SYS_KEXEC_LOAD", Const, 0}, + {"SYS_KEYCTL", Const, 0}, + {"SYS_KILL", Const, 0}, + {"SYS_KLDFIND", Const, 0}, + {"SYS_KLDFIRSTMOD", Const, 0}, + {"SYS_KLDLOAD", Const, 0}, + {"SYS_KLDNEXT", Const, 0}, + {"SYS_KLDSTAT", Const, 0}, + {"SYS_KLDSYM", Const, 0}, + {"SYS_KLDUNLOAD", Const, 0}, + {"SYS_KLDUNLOADF", Const, 0}, + {"SYS_KMQ_NOTIFY", Const, 14}, + {"SYS_KMQ_OPEN", Const, 14}, + {"SYS_KMQ_SETATTR", Const, 14}, + {"SYS_KMQ_TIMEDRECEIVE", Const, 14}, + {"SYS_KMQ_TIMEDSEND", Const, 14}, + {"SYS_KMQ_UNLINK", Const, 14}, + {"SYS_KQUEUE", Const, 0}, + {"SYS_KQUEUE1", Const, 1}, + {"SYS_KSEM_CLOSE", Const, 14}, + {"SYS_KSEM_DESTROY", Const, 14}, + {"SYS_KSEM_GETVALUE", Const, 14}, + {"SYS_KSEM_INIT", Const, 14}, + {"SYS_KSEM_OPEN", Const, 14}, + {"SYS_KSEM_POST", Const, 14}, + {"SYS_KSEM_TIMEDWAIT", Const, 14}, + {"SYS_KSEM_TRYWAIT", Const, 14}, + {"SYS_KSEM_UNLINK", Const, 14}, + {"SYS_KSEM_WAIT", Const, 14}, + {"SYS_KTIMER_CREATE", Const, 0}, + {"SYS_KTIMER_DELETE", Const, 0}, + {"SYS_KTIMER_GETOVERRUN", Const, 0}, + {"SYS_KTIMER_GETTIME", Const, 0}, + {"SYS_KTIMER_SETTIME", Const, 0}, + {"SYS_KTRACE", Const, 0}, + {"SYS_LCHFLAGS", Const, 0}, + {"SYS_LCHMOD", Const, 0}, + {"SYS_LCHOWN", Const, 0}, + {"SYS_LCHOWN32", Const, 0}, + {"SYS_LEDGER", Const, 16}, + {"SYS_LGETFH", Const, 0}, + {"SYS_LGETXATTR", Const, 0}, + {"SYS_LINK", Const, 0}, + {"SYS_LINKAT", Const, 0}, + {"SYS_LIO_LISTIO", Const, 0}, + {"SYS_LISTEN", Const, 0}, + {"SYS_LISTXATTR", Const, 0}, + {"SYS_LLISTXATTR", Const, 0}, + {"SYS_LOCK", Const, 0}, + {"SYS_LOOKUP_DCOOKIE", Const, 0}, + {"SYS_LPATHCONF", Const, 0}, + {"SYS_LREMOVEXATTR", Const, 0}, + {"SYS_LSEEK", Const, 0}, + {"SYS_LSETXATTR", Const, 0}, + {"SYS_LSTAT", Const, 0}, + {"SYS_LSTAT64", Const, 0}, + {"SYS_LSTAT64_EXTENDED", Const, 0}, + {"SYS_LSTATV", Const, 0}, + {"SYS_LSTAT_EXTENDED", Const, 0}, + {"SYS_LUTIMES", Const, 0}, + {"SYS_MAC_SYSCALL", Const, 0}, + {"SYS_MADVISE", Const, 0}, + {"SYS_MADVISE1", Const, 0}, + {"SYS_MAXSYSCALL", Const, 0}, + {"SYS_MBIND", Const, 0}, + {"SYS_MIGRATE_PAGES", Const, 0}, + {"SYS_MINCORE", Const, 0}, + {"SYS_MINHERIT", Const, 0}, + {"SYS_MKCOMPLEX", Const, 0}, + {"SYS_MKDIR", Const, 0}, + {"SYS_MKDIRAT", Const, 0}, + {"SYS_MKDIR_EXTENDED", Const, 0}, + {"SYS_MKFIFO", Const, 0}, + {"SYS_MKFIFOAT", Const, 0}, + {"SYS_MKFIFO_EXTENDED", Const, 0}, + {"SYS_MKNOD", Const, 0}, + {"SYS_MKNODAT", Const, 0}, + {"SYS_MLOCK", Const, 0}, + {"SYS_MLOCKALL", Const, 0}, + {"SYS_MMAP", Const, 0}, + {"SYS_MMAP2", Const, 0}, + {"SYS_MODCTL", Const, 1}, + {"SYS_MODFIND", Const, 0}, + {"SYS_MODFNEXT", Const, 0}, + {"SYS_MODIFY_LDT", Const, 0}, + {"SYS_MODNEXT", Const, 0}, + {"SYS_MODSTAT", Const, 0}, + {"SYS_MODWATCH", Const, 0}, + {"SYS_MOUNT", Const, 0}, + {"SYS_MOVE_PAGES", Const, 0}, + {"SYS_MPROTECT", Const, 0}, + {"SYS_MPX", Const, 0}, + {"SYS_MQUERY", Const, 1}, + {"SYS_MQ_GETSETATTR", Const, 0}, + {"SYS_MQ_NOTIFY", Const, 0}, + {"SYS_MQ_OPEN", Const, 0}, + {"SYS_MQ_TIMEDRECEIVE", Const, 0}, + {"SYS_MQ_TIMEDSEND", Const, 0}, + {"SYS_MQ_UNLINK", Const, 0}, + {"SYS_MREMAP", Const, 0}, + {"SYS_MSGCTL", Const, 0}, + {"SYS_MSGGET", Const, 0}, + {"SYS_MSGRCV", Const, 0}, + {"SYS_MSGRCV_NOCANCEL", Const, 0}, + {"SYS_MSGSND", Const, 0}, + {"SYS_MSGSND_NOCANCEL", Const, 0}, + {"SYS_MSGSYS", Const, 0}, + {"SYS_MSYNC", Const, 0}, + {"SYS_MSYNC_NOCANCEL", Const, 0}, + {"SYS_MUNLOCK", Const, 0}, + {"SYS_MUNLOCKALL", Const, 0}, + {"SYS_MUNMAP", Const, 0}, + {"SYS_NAME_TO_HANDLE_AT", Const, 0}, + {"SYS_NANOSLEEP", Const, 0}, + {"SYS_NEWFSTATAT", Const, 0}, + {"SYS_NFSCLNT", Const, 0}, + {"SYS_NFSSERVCTL", Const, 0}, + {"SYS_NFSSVC", Const, 0}, + {"SYS_NFSTAT", Const, 0}, + {"SYS_NICE", Const, 0}, + {"SYS_NLM_SYSCALL", Const, 14}, + {"SYS_NLSTAT", Const, 0}, + {"SYS_NMOUNT", Const, 0}, + {"SYS_NSTAT", Const, 0}, + {"SYS_NTP_ADJTIME", Const, 0}, + {"SYS_NTP_GETTIME", Const, 0}, + {"SYS_NUMA_GETAFFINITY", Const, 14}, + {"SYS_NUMA_SETAFFINITY", Const, 14}, + {"SYS_OABI_SYSCALL_BASE", Const, 0}, + {"SYS_OBREAK", Const, 0}, + {"SYS_OLDFSTAT", Const, 0}, + {"SYS_OLDLSTAT", Const, 0}, + {"SYS_OLDOLDUNAME", Const, 0}, + {"SYS_OLDSTAT", Const, 0}, + {"SYS_OLDUNAME", Const, 0}, + {"SYS_OPEN", Const, 0}, + {"SYS_OPENAT", Const, 0}, + {"SYS_OPENBSD_POLL", Const, 0}, + {"SYS_OPEN_BY_HANDLE_AT", Const, 0}, + {"SYS_OPEN_DPROTECTED_NP", Const, 16}, + {"SYS_OPEN_EXTENDED", Const, 0}, + {"SYS_OPEN_NOCANCEL", Const, 0}, + {"SYS_OVADVISE", Const, 0}, + {"SYS_PACCEPT", Const, 1}, + {"SYS_PATHCONF", Const, 0}, + {"SYS_PAUSE", Const, 0}, + {"SYS_PCICONFIG_IOBASE", Const, 0}, + {"SYS_PCICONFIG_READ", Const, 0}, + {"SYS_PCICONFIG_WRITE", Const, 0}, + {"SYS_PDFORK", Const, 0}, + {"SYS_PDGETPID", Const, 0}, + {"SYS_PDKILL", Const, 0}, + {"SYS_PERF_EVENT_OPEN", Const, 0}, + {"SYS_PERSONALITY", Const, 0}, + {"SYS_PID_HIBERNATE", Const, 0}, + {"SYS_PID_RESUME", Const, 0}, + {"SYS_PID_SHUTDOWN_SOCKETS", Const, 0}, + {"SYS_PID_SUSPEND", Const, 0}, + {"SYS_PIPE", Const, 0}, + {"SYS_PIPE2", Const, 0}, + {"SYS_PIVOT_ROOT", Const, 0}, + {"SYS_PMC_CONTROL", Const, 1}, + {"SYS_PMC_GET_INFO", Const, 1}, + {"SYS_POLL", Const, 0}, + {"SYS_POLLTS", Const, 1}, + {"SYS_POLL_NOCANCEL", Const, 0}, + {"SYS_POSIX_FADVISE", Const, 0}, + {"SYS_POSIX_FALLOCATE", Const, 0}, + {"SYS_POSIX_OPENPT", Const, 0}, + {"SYS_POSIX_SPAWN", Const, 0}, + {"SYS_PPOLL", Const, 0}, + {"SYS_PRCTL", Const, 0}, + {"SYS_PREAD", Const, 0}, + {"SYS_PREAD64", Const, 0}, + {"SYS_PREADV", Const, 0}, + {"SYS_PREAD_NOCANCEL", Const, 0}, + {"SYS_PRLIMIT64", Const, 0}, + {"SYS_PROCCTL", Const, 3}, + {"SYS_PROCESS_POLICY", Const, 0}, + {"SYS_PROCESS_VM_READV", Const, 0}, + {"SYS_PROCESS_VM_WRITEV", Const, 0}, + {"SYS_PROC_INFO", Const, 0}, + {"SYS_PROF", Const, 0}, + {"SYS_PROFIL", Const, 0}, + {"SYS_PSELECT", Const, 0}, + {"SYS_PSELECT6", Const, 0}, + {"SYS_PSET_ASSIGN", Const, 1}, + {"SYS_PSET_CREATE", Const, 1}, + {"SYS_PSET_DESTROY", Const, 1}, + {"SYS_PSYNCH_CVBROAD", Const, 0}, + {"SYS_PSYNCH_CVCLRPREPOST", Const, 0}, + {"SYS_PSYNCH_CVSIGNAL", Const, 0}, + {"SYS_PSYNCH_CVWAIT", Const, 0}, + {"SYS_PSYNCH_MUTEXDROP", Const, 0}, + {"SYS_PSYNCH_MUTEXWAIT", Const, 0}, + {"SYS_PSYNCH_RW_DOWNGRADE", Const, 0}, + {"SYS_PSYNCH_RW_LONGRDLOCK", Const, 0}, + {"SYS_PSYNCH_RW_RDLOCK", Const, 0}, + {"SYS_PSYNCH_RW_UNLOCK", Const, 0}, + {"SYS_PSYNCH_RW_UNLOCK2", Const, 0}, + {"SYS_PSYNCH_RW_UPGRADE", Const, 0}, + {"SYS_PSYNCH_RW_WRLOCK", Const, 0}, + {"SYS_PSYNCH_RW_YIELDWRLOCK", Const, 0}, + {"SYS_PTRACE", Const, 0}, + {"SYS_PUTPMSG", Const, 0}, + {"SYS_PWRITE", Const, 0}, + {"SYS_PWRITE64", Const, 0}, + {"SYS_PWRITEV", Const, 0}, + {"SYS_PWRITE_NOCANCEL", Const, 0}, + {"SYS_QUERY_MODULE", Const, 0}, + {"SYS_QUOTACTL", Const, 0}, + {"SYS_RASCTL", Const, 1}, + {"SYS_RCTL_ADD_RULE", Const, 0}, + {"SYS_RCTL_GET_LIMITS", Const, 0}, + {"SYS_RCTL_GET_RACCT", Const, 0}, + {"SYS_RCTL_GET_RULES", Const, 0}, + {"SYS_RCTL_REMOVE_RULE", Const, 0}, + {"SYS_READ", Const, 0}, + {"SYS_READAHEAD", Const, 0}, + {"SYS_READDIR", Const, 0}, + {"SYS_READLINK", Const, 0}, + {"SYS_READLINKAT", Const, 0}, + {"SYS_READV", Const, 0}, + {"SYS_READV_NOCANCEL", Const, 0}, + {"SYS_READ_NOCANCEL", Const, 0}, + {"SYS_REBOOT", Const, 0}, + {"SYS_RECV", Const, 0}, + {"SYS_RECVFROM", Const, 0}, + {"SYS_RECVFROM_NOCANCEL", Const, 0}, + {"SYS_RECVMMSG", Const, 0}, + {"SYS_RECVMSG", Const, 0}, + {"SYS_RECVMSG_NOCANCEL", Const, 0}, + {"SYS_REMAP_FILE_PAGES", Const, 0}, + {"SYS_REMOVEXATTR", Const, 0}, + {"SYS_RENAME", Const, 0}, + {"SYS_RENAMEAT", Const, 0}, + {"SYS_REQUEST_KEY", Const, 0}, + {"SYS_RESTART_SYSCALL", Const, 0}, + {"SYS_REVOKE", Const, 0}, + {"SYS_RFORK", Const, 0}, + {"SYS_RMDIR", Const, 0}, + {"SYS_RTPRIO", Const, 0}, + {"SYS_RTPRIO_THREAD", Const, 0}, + {"SYS_RT_SIGACTION", Const, 0}, + {"SYS_RT_SIGPENDING", Const, 0}, + {"SYS_RT_SIGPROCMASK", Const, 0}, + {"SYS_RT_SIGQUEUEINFO", Const, 0}, + {"SYS_RT_SIGRETURN", Const, 0}, + {"SYS_RT_SIGSUSPEND", Const, 0}, + {"SYS_RT_SIGTIMEDWAIT", Const, 0}, + {"SYS_RT_TGSIGQUEUEINFO", Const, 0}, + {"SYS_SBRK", Const, 0}, + {"SYS_SCHED_GETAFFINITY", Const, 0}, + {"SYS_SCHED_GETPARAM", Const, 0}, + {"SYS_SCHED_GETSCHEDULER", Const, 0}, + {"SYS_SCHED_GET_PRIORITY_MAX", Const, 0}, + {"SYS_SCHED_GET_PRIORITY_MIN", Const, 0}, + {"SYS_SCHED_RR_GET_INTERVAL", Const, 0}, + {"SYS_SCHED_SETAFFINITY", Const, 0}, + {"SYS_SCHED_SETPARAM", Const, 0}, + {"SYS_SCHED_SETSCHEDULER", Const, 0}, + {"SYS_SCHED_YIELD", Const, 0}, + {"SYS_SCTP_GENERIC_RECVMSG", Const, 0}, + {"SYS_SCTP_GENERIC_SENDMSG", Const, 0}, + {"SYS_SCTP_GENERIC_SENDMSG_IOV", Const, 0}, + {"SYS_SCTP_PEELOFF", Const, 0}, + {"SYS_SEARCHFS", Const, 0}, + {"SYS_SECURITY", Const, 0}, + {"SYS_SELECT", Const, 0}, + {"SYS_SELECT_NOCANCEL", Const, 0}, + {"SYS_SEMCONFIG", Const, 1}, + {"SYS_SEMCTL", Const, 0}, + {"SYS_SEMGET", Const, 0}, + {"SYS_SEMOP", Const, 0}, + {"SYS_SEMSYS", Const, 0}, + {"SYS_SEMTIMEDOP", Const, 0}, + {"SYS_SEM_CLOSE", Const, 0}, + {"SYS_SEM_DESTROY", Const, 0}, + {"SYS_SEM_GETVALUE", Const, 0}, + {"SYS_SEM_INIT", Const, 0}, + {"SYS_SEM_OPEN", Const, 0}, + {"SYS_SEM_POST", Const, 0}, + {"SYS_SEM_TRYWAIT", Const, 0}, + {"SYS_SEM_UNLINK", Const, 0}, + {"SYS_SEM_WAIT", Const, 0}, + {"SYS_SEM_WAIT_NOCANCEL", Const, 0}, + {"SYS_SEND", Const, 0}, + {"SYS_SENDFILE", Const, 0}, + {"SYS_SENDFILE64", Const, 0}, + {"SYS_SENDMMSG", Const, 0}, + {"SYS_SENDMSG", Const, 0}, + {"SYS_SENDMSG_NOCANCEL", Const, 0}, + {"SYS_SENDTO", Const, 0}, + {"SYS_SENDTO_NOCANCEL", Const, 0}, + {"SYS_SETATTRLIST", Const, 0}, + {"SYS_SETAUDIT", Const, 0}, + {"SYS_SETAUDIT_ADDR", Const, 0}, + {"SYS_SETAUID", Const, 0}, + {"SYS_SETCONTEXT", Const, 0}, + {"SYS_SETDOMAINNAME", Const, 0}, + {"SYS_SETEGID", Const, 0}, + {"SYS_SETEUID", Const, 0}, + {"SYS_SETFIB", Const, 0}, + {"SYS_SETFSGID", Const, 0}, + {"SYS_SETFSGID32", Const, 0}, + {"SYS_SETFSUID", Const, 0}, + {"SYS_SETFSUID32", Const, 0}, + {"SYS_SETGID", Const, 0}, + {"SYS_SETGID32", Const, 0}, + {"SYS_SETGROUPS", Const, 0}, + {"SYS_SETGROUPS32", Const, 0}, + {"SYS_SETHOSTNAME", Const, 0}, + {"SYS_SETITIMER", Const, 0}, + {"SYS_SETLCID", Const, 0}, + {"SYS_SETLOGIN", Const, 0}, + {"SYS_SETLOGINCLASS", Const, 0}, + {"SYS_SETNS", Const, 0}, + {"SYS_SETPGID", Const, 0}, + {"SYS_SETPRIORITY", Const, 0}, + {"SYS_SETPRIVEXEC", Const, 0}, + {"SYS_SETREGID", Const, 0}, + {"SYS_SETREGID32", Const, 0}, + {"SYS_SETRESGID", Const, 0}, + {"SYS_SETRESGID32", Const, 0}, + {"SYS_SETRESUID", Const, 0}, + {"SYS_SETRESUID32", Const, 0}, + {"SYS_SETREUID", Const, 0}, + {"SYS_SETREUID32", Const, 0}, + {"SYS_SETRLIMIT", Const, 0}, + {"SYS_SETRTABLE", Const, 1}, + {"SYS_SETSGROUPS", Const, 0}, + {"SYS_SETSID", Const, 0}, + {"SYS_SETSOCKOPT", Const, 0}, + {"SYS_SETTID", Const, 0}, + {"SYS_SETTID_WITH_PID", Const, 0}, + {"SYS_SETTIMEOFDAY", Const, 0}, + {"SYS_SETUID", Const, 0}, + {"SYS_SETUID32", Const, 0}, + {"SYS_SETWGROUPS", Const, 0}, + {"SYS_SETXATTR", Const, 0}, + {"SYS_SET_MEMPOLICY", Const, 0}, + {"SYS_SET_ROBUST_LIST", Const, 0}, + {"SYS_SET_THREAD_AREA", Const, 0}, + {"SYS_SET_TID_ADDRESS", Const, 0}, + {"SYS_SGETMASK", Const, 0}, + {"SYS_SHARED_REGION_CHECK_NP", Const, 0}, + {"SYS_SHARED_REGION_MAP_AND_SLIDE_NP", Const, 0}, + {"SYS_SHMAT", Const, 0}, + {"SYS_SHMCTL", Const, 0}, + {"SYS_SHMDT", Const, 0}, + {"SYS_SHMGET", Const, 0}, + {"SYS_SHMSYS", Const, 0}, + {"SYS_SHM_OPEN", Const, 0}, + {"SYS_SHM_UNLINK", Const, 0}, + {"SYS_SHUTDOWN", Const, 0}, + {"SYS_SIGACTION", Const, 0}, + {"SYS_SIGALTSTACK", Const, 0}, + {"SYS_SIGNAL", Const, 0}, + {"SYS_SIGNALFD", Const, 0}, + {"SYS_SIGNALFD4", Const, 0}, + {"SYS_SIGPENDING", Const, 0}, + {"SYS_SIGPROCMASK", Const, 0}, + {"SYS_SIGQUEUE", Const, 0}, + {"SYS_SIGQUEUEINFO", Const, 1}, + {"SYS_SIGRETURN", Const, 0}, + {"SYS_SIGSUSPEND", Const, 0}, + {"SYS_SIGSUSPEND_NOCANCEL", Const, 0}, + {"SYS_SIGTIMEDWAIT", Const, 0}, + {"SYS_SIGWAIT", Const, 0}, + {"SYS_SIGWAITINFO", Const, 0}, + {"SYS_SOCKET", Const, 0}, + {"SYS_SOCKETCALL", Const, 0}, + {"SYS_SOCKETPAIR", Const, 0}, + {"SYS_SPLICE", Const, 0}, + {"SYS_SSETMASK", Const, 0}, + {"SYS_SSTK", Const, 0}, + {"SYS_STACK_SNAPSHOT", Const, 0}, + {"SYS_STAT", Const, 0}, + {"SYS_STAT64", Const, 0}, + {"SYS_STAT64_EXTENDED", Const, 0}, + {"SYS_STATFS", Const, 0}, + {"SYS_STATFS64", Const, 0}, + {"SYS_STATV", Const, 0}, + {"SYS_STATVFS1", Const, 1}, + {"SYS_STAT_EXTENDED", Const, 0}, + {"SYS_STIME", Const, 0}, + {"SYS_STTY", Const, 0}, + {"SYS_SWAPCONTEXT", Const, 0}, + {"SYS_SWAPCTL", Const, 1}, + {"SYS_SWAPOFF", Const, 0}, + {"SYS_SWAPON", Const, 0}, + {"SYS_SYMLINK", Const, 0}, + {"SYS_SYMLINKAT", Const, 0}, + {"SYS_SYNC", Const, 0}, + {"SYS_SYNCFS", Const, 0}, + {"SYS_SYNC_FILE_RANGE", Const, 0}, + {"SYS_SYSARCH", Const, 0}, + {"SYS_SYSCALL", Const, 0}, + {"SYS_SYSCALL_BASE", Const, 0}, + {"SYS_SYSFS", Const, 0}, + {"SYS_SYSINFO", Const, 0}, + {"SYS_SYSLOG", Const, 0}, + {"SYS_TEE", Const, 0}, + {"SYS_TGKILL", Const, 0}, + {"SYS_THREAD_SELFID", Const, 0}, + {"SYS_THR_CREATE", Const, 0}, + {"SYS_THR_EXIT", Const, 0}, + {"SYS_THR_KILL", Const, 0}, + {"SYS_THR_KILL2", Const, 0}, + {"SYS_THR_NEW", Const, 0}, + {"SYS_THR_SELF", Const, 0}, + {"SYS_THR_SET_NAME", Const, 0}, + {"SYS_THR_SUSPEND", Const, 0}, + {"SYS_THR_WAKE", Const, 0}, + {"SYS_TIME", Const, 0}, + {"SYS_TIMERFD_CREATE", Const, 0}, + {"SYS_TIMERFD_GETTIME", Const, 0}, + {"SYS_TIMERFD_SETTIME", Const, 0}, + {"SYS_TIMER_CREATE", Const, 0}, + {"SYS_TIMER_DELETE", Const, 0}, + {"SYS_TIMER_GETOVERRUN", Const, 0}, + {"SYS_TIMER_GETTIME", Const, 0}, + {"SYS_TIMER_SETTIME", Const, 0}, + {"SYS_TIMES", Const, 0}, + {"SYS_TKILL", Const, 0}, + {"SYS_TRUNCATE", Const, 0}, + {"SYS_TRUNCATE64", Const, 0}, + {"SYS_TUXCALL", Const, 0}, + {"SYS_UGETRLIMIT", Const, 0}, + {"SYS_ULIMIT", Const, 0}, + {"SYS_UMASK", Const, 0}, + {"SYS_UMASK_EXTENDED", Const, 0}, + {"SYS_UMOUNT", Const, 0}, + {"SYS_UMOUNT2", Const, 0}, + {"SYS_UNAME", Const, 0}, + {"SYS_UNDELETE", Const, 0}, + {"SYS_UNLINK", Const, 0}, + {"SYS_UNLINKAT", Const, 0}, + {"SYS_UNMOUNT", Const, 0}, + {"SYS_UNSHARE", Const, 0}, + {"SYS_USELIB", Const, 0}, + {"SYS_USTAT", Const, 0}, + {"SYS_UTIME", Const, 0}, + {"SYS_UTIMENSAT", Const, 0}, + {"SYS_UTIMES", Const, 0}, + {"SYS_UTRACE", Const, 0}, + {"SYS_UUIDGEN", Const, 0}, + {"SYS_VADVISE", Const, 1}, + {"SYS_VFORK", Const, 0}, + {"SYS_VHANGUP", Const, 0}, + {"SYS_VM86", Const, 0}, + {"SYS_VM86OLD", Const, 0}, + {"SYS_VMSPLICE", Const, 0}, + {"SYS_VM_PRESSURE_MONITOR", Const, 0}, + {"SYS_VSERVER", Const, 0}, + {"SYS_WAIT4", Const, 0}, + {"SYS_WAIT4_NOCANCEL", Const, 0}, + {"SYS_WAIT6", Const, 1}, + {"SYS_WAITEVENT", Const, 0}, + {"SYS_WAITID", Const, 0}, + {"SYS_WAITID_NOCANCEL", Const, 0}, + {"SYS_WAITPID", Const, 0}, + {"SYS_WATCHEVENT", Const, 0}, + {"SYS_WORKQ_KERNRETURN", Const, 0}, + {"SYS_WORKQ_OPEN", Const, 0}, + {"SYS_WRITE", Const, 0}, + {"SYS_WRITEV", Const, 0}, + {"SYS_WRITEV_NOCANCEL", Const, 0}, + {"SYS_WRITE_NOCANCEL", Const, 0}, + {"SYS_YIELD", Const, 0}, + {"SYS__LLSEEK", Const, 0}, + {"SYS__LWP_CONTINUE", Const, 1}, + {"SYS__LWP_CREATE", Const, 1}, + {"SYS__LWP_CTL", Const, 1}, + {"SYS__LWP_DETACH", Const, 1}, + {"SYS__LWP_EXIT", Const, 1}, + {"SYS__LWP_GETNAME", Const, 1}, + {"SYS__LWP_GETPRIVATE", Const, 1}, + {"SYS__LWP_KILL", Const, 1}, + {"SYS__LWP_PARK", Const, 1}, + {"SYS__LWP_SELF", Const, 1}, + {"SYS__LWP_SETNAME", Const, 1}, + {"SYS__LWP_SETPRIVATE", Const, 1}, + {"SYS__LWP_SUSPEND", Const, 1}, + {"SYS__LWP_UNPARK", Const, 1}, + {"SYS__LWP_UNPARK_ALL", Const, 1}, + {"SYS__LWP_WAIT", Const, 1}, + {"SYS__LWP_WAKEUP", Const, 1}, + {"SYS__NEWSELECT", Const, 0}, + {"SYS__PSET_BIND", Const, 1}, + {"SYS__SCHED_GETAFFINITY", Const, 1}, + {"SYS__SCHED_GETPARAM", Const, 1}, + {"SYS__SCHED_SETAFFINITY", Const, 1}, + {"SYS__SCHED_SETPARAM", Const, 1}, + {"SYS__SYSCTL", Const, 0}, + {"SYS__UMTX_LOCK", Const, 0}, + {"SYS__UMTX_OP", Const, 0}, + {"SYS__UMTX_UNLOCK", Const, 0}, + {"SYS___ACL_ACLCHECK_FD", Const, 0}, + {"SYS___ACL_ACLCHECK_FILE", Const, 0}, + {"SYS___ACL_ACLCHECK_LINK", Const, 0}, + {"SYS___ACL_DELETE_FD", Const, 0}, + {"SYS___ACL_DELETE_FILE", Const, 0}, + {"SYS___ACL_DELETE_LINK", Const, 0}, + {"SYS___ACL_GET_FD", Const, 0}, + {"SYS___ACL_GET_FILE", Const, 0}, + {"SYS___ACL_GET_LINK", Const, 0}, + {"SYS___ACL_SET_FD", Const, 0}, + {"SYS___ACL_SET_FILE", Const, 0}, + {"SYS___ACL_SET_LINK", Const, 0}, + {"SYS___CAP_RIGHTS_GET", Const, 14}, + {"SYS___CLONE", Const, 1}, + {"SYS___DISABLE_THREADSIGNAL", Const, 0}, + {"SYS___GETCWD", Const, 0}, + {"SYS___GETLOGIN", Const, 1}, + {"SYS___GET_TCB", Const, 1}, + {"SYS___MAC_EXECVE", Const, 0}, + {"SYS___MAC_GETFSSTAT", Const, 0}, + {"SYS___MAC_GET_FD", Const, 0}, + {"SYS___MAC_GET_FILE", Const, 0}, + {"SYS___MAC_GET_LCID", Const, 0}, + {"SYS___MAC_GET_LCTX", Const, 0}, + {"SYS___MAC_GET_LINK", Const, 0}, + {"SYS___MAC_GET_MOUNT", Const, 0}, + {"SYS___MAC_GET_PID", Const, 0}, + {"SYS___MAC_GET_PROC", Const, 0}, + {"SYS___MAC_MOUNT", Const, 0}, + {"SYS___MAC_SET_FD", Const, 0}, + {"SYS___MAC_SET_FILE", Const, 0}, + {"SYS___MAC_SET_LCTX", Const, 0}, + {"SYS___MAC_SET_LINK", Const, 0}, + {"SYS___MAC_SET_PROC", Const, 0}, + {"SYS___MAC_SYSCALL", Const, 0}, + {"SYS___OLD_SEMWAIT_SIGNAL", Const, 0}, + {"SYS___OLD_SEMWAIT_SIGNAL_NOCANCEL", Const, 0}, + {"SYS___POSIX_CHOWN", Const, 1}, + {"SYS___POSIX_FCHOWN", Const, 1}, + {"SYS___POSIX_LCHOWN", Const, 1}, + {"SYS___POSIX_RENAME", Const, 1}, + {"SYS___PTHREAD_CANCELED", Const, 0}, + {"SYS___PTHREAD_CHDIR", Const, 0}, + {"SYS___PTHREAD_FCHDIR", Const, 0}, + {"SYS___PTHREAD_KILL", Const, 0}, + {"SYS___PTHREAD_MARKCANCEL", Const, 0}, + {"SYS___PTHREAD_SIGMASK", Const, 0}, + {"SYS___QUOTACTL", Const, 1}, + {"SYS___SEMCTL", Const, 1}, + {"SYS___SEMWAIT_SIGNAL", Const, 0}, + {"SYS___SEMWAIT_SIGNAL_NOCANCEL", Const, 0}, + {"SYS___SETLOGIN", Const, 1}, + {"SYS___SETUGID", Const, 0}, + {"SYS___SET_TCB", Const, 1}, + {"SYS___SIGACTION_SIGTRAMP", Const, 1}, + {"SYS___SIGTIMEDWAIT", Const, 1}, + {"SYS___SIGWAIT", Const, 0}, + {"SYS___SIGWAIT_NOCANCEL", Const, 0}, + {"SYS___SYSCTL", Const, 0}, + {"SYS___TFORK", Const, 1}, + {"SYS___THREXIT", Const, 1}, + {"SYS___THRSIGDIVERT", Const, 1}, + {"SYS___THRSLEEP", Const, 1}, + {"SYS___THRWAKEUP", Const, 1}, + {"S_ARCH1", Const, 1}, + {"S_ARCH2", Const, 1}, + {"S_BLKSIZE", Const, 0}, + {"S_IEXEC", Const, 0}, + {"S_IFBLK", Const, 0}, + {"S_IFCHR", Const, 0}, + {"S_IFDIR", Const, 0}, + {"S_IFIFO", Const, 0}, + {"S_IFLNK", Const, 0}, + {"S_IFMT", Const, 0}, + {"S_IFREG", Const, 0}, + {"S_IFSOCK", Const, 0}, + {"S_IFWHT", Const, 0}, + {"S_IREAD", Const, 0}, + {"S_IRGRP", Const, 0}, + {"S_IROTH", Const, 0}, + {"S_IRUSR", Const, 0}, + {"S_IRWXG", Const, 0}, + {"S_IRWXO", Const, 0}, + {"S_IRWXU", Const, 0}, + {"S_ISGID", Const, 0}, + {"S_ISTXT", Const, 0}, + {"S_ISUID", Const, 0}, + {"S_ISVTX", Const, 0}, + {"S_IWGRP", Const, 0}, + {"S_IWOTH", Const, 0}, + {"S_IWRITE", Const, 0}, + {"S_IWUSR", Const, 0}, + {"S_IXGRP", Const, 0}, + {"S_IXOTH", Const, 0}, + {"S_IXUSR", Const, 0}, + {"S_LOGIN_SET", Const, 1}, + {"SecurityAttributes", Type, 0}, + {"SecurityAttributes.InheritHandle", Field, 0}, + {"SecurityAttributes.Length", Field, 0}, + {"SecurityAttributes.SecurityDescriptor", Field, 0}, + {"Seek", Func, 0}, + {"Select", Func, 0}, + {"Sendfile", Func, 0}, + {"Sendmsg", Func, 0}, + {"SendmsgN", Func, 3}, + {"Sendto", Func, 0}, + {"Servent", Type, 0}, + {"Servent.Aliases", Field, 0}, + {"Servent.Name", Field, 0}, + {"Servent.Port", Field, 0}, + {"Servent.Proto", Field, 0}, + {"SetBpf", Func, 0}, + {"SetBpfBuflen", Func, 0}, + {"SetBpfDatalink", Func, 0}, + {"SetBpfHeadercmpl", Func, 0}, + {"SetBpfImmediate", Func, 0}, + {"SetBpfInterface", Func, 0}, + {"SetBpfPromisc", Func, 0}, + {"SetBpfTimeout", Func, 0}, + {"SetCurrentDirectory", Func, 0}, + {"SetEndOfFile", Func, 0}, + {"SetEnvironmentVariable", Func, 0}, + {"SetFileAttributes", Func, 0}, + {"SetFileCompletionNotificationModes", Func, 2}, + {"SetFilePointer", Func, 0}, + {"SetFileTime", Func, 0}, + {"SetHandleInformation", Func, 0}, + {"SetKevent", Func, 0}, + {"SetLsfPromisc", Func, 0}, + {"SetNonblock", Func, 0}, + {"Setdomainname", Func, 0}, + {"Setegid", Func, 0}, + {"Setenv", Func, 0}, + {"Seteuid", Func, 0}, + {"Setfsgid", Func, 0}, + {"Setfsuid", Func, 0}, + {"Setgid", Func, 0}, + {"Setgroups", Func, 0}, + {"Sethostname", Func, 0}, + {"Setlogin", Func, 0}, + {"Setpgid", Func, 0}, + {"Setpriority", Func, 0}, + {"Setprivexec", Func, 0}, + {"Setregid", Func, 0}, + {"Setresgid", Func, 0}, + {"Setresuid", Func, 0}, + {"Setreuid", Func, 0}, + {"Setrlimit", Func, 0}, + {"Setsid", Func, 0}, + {"Setsockopt", Func, 0}, + {"SetsockoptByte", Func, 0}, + {"SetsockoptICMPv6Filter", Func, 2}, + {"SetsockoptIPMreq", Func, 0}, + {"SetsockoptIPMreqn", Func, 0}, + {"SetsockoptIPv6Mreq", Func, 0}, + {"SetsockoptInet4Addr", Func, 0}, + {"SetsockoptInt", Func, 0}, + {"SetsockoptLinger", Func, 0}, + {"SetsockoptString", Func, 0}, + {"SetsockoptTimeval", Func, 0}, + {"Settimeofday", Func, 0}, + {"Setuid", Func, 0}, + {"Setxattr", Func, 1}, + {"Shutdown", Func, 0}, + {"SidTypeAlias", Const, 0}, + {"SidTypeComputer", Const, 0}, + {"SidTypeDeletedAccount", Const, 0}, + {"SidTypeDomain", Const, 0}, + {"SidTypeGroup", Const, 0}, + {"SidTypeInvalid", Const, 0}, + {"SidTypeLabel", Const, 0}, + {"SidTypeUnknown", Const, 0}, + {"SidTypeUser", Const, 0}, + {"SidTypeWellKnownGroup", Const, 0}, + {"Signal", Type, 0}, + {"SizeofBpfHdr", Const, 0}, + {"SizeofBpfInsn", Const, 0}, + {"SizeofBpfProgram", Const, 0}, + {"SizeofBpfStat", Const, 0}, + {"SizeofBpfVersion", Const, 0}, + {"SizeofBpfZbuf", Const, 0}, + {"SizeofBpfZbufHeader", Const, 0}, + {"SizeofCmsghdr", Const, 0}, + {"SizeofICMPv6Filter", Const, 2}, + {"SizeofIPMreq", Const, 0}, + {"SizeofIPMreqn", Const, 0}, + {"SizeofIPv6MTUInfo", Const, 2}, + {"SizeofIPv6Mreq", Const, 0}, + {"SizeofIfAddrmsg", Const, 0}, + {"SizeofIfAnnounceMsghdr", Const, 1}, + {"SizeofIfData", Const, 0}, + {"SizeofIfInfomsg", Const, 0}, + {"SizeofIfMsghdr", Const, 0}, + {"SizeofIfaMsghdr", Const, 0}, + {"SizeofIfmaMsghdr", Const, 0}, + {"SizeofIfmaMsghdr2", Const, 0}, + {"SizeofInet4Pktinfo", Const, 0}, + {"SizeofInet6Pktinfo", Const, 0}, + {"SizeofInotifyEvent", Const, 0}, + {"SizeofLinger", Const, 0}, + {"SizeofMsghdr", Const, 0}, + {"SizeofNlAttr", Const, 0}, + {"SizeofNlMsgerr", Const, 0}, + {"SizeofNlMsghdr", Const, 0}, + {"SizeofRtAttr", Const, 0}, + {"SizeofRtGenmsg", Const, 0}, + {"SizeofRtMetrics", Const, 0}, + {"SizeofRtMsg", Const, 0}, + {"SizeofRtMsghdr", Const, 0}, + {"SizeofRtNexthop", Const, 0}, + {"SizeofSockFilter", Const, 0}, + {"SizeofSockFprog", Const, 0}, + {"SizeofSockaddrAny", Const, 0}, + {"SizeofSockaddrDatalink", Const, 0}, + {"SizeofSockaddrInet4", Const, 0}, + {"SizeofSockaddrInet6", Const, 0}, + {"SizeofSockaddrLinklayer", Const, 0}, + {"SizeofSockaddrNetlink", Const, 0}, + {"SizeofSockaddrUnix", Const, 0}, + {"SizeofTCPInfo", Const, 1}, + {"SizeofUcred", Const, 0}, + {"SlicePtrFromStrings", Func, 1}, + {"SockFilter", Type, 0}, + {"SockFilter.Code", Field, 0}, + {"SockFilter.Jf", Field, 0}, + {"SockFilter.Jt", Field, 0}, + {"SockFilter.K", Field, 0}, + {"SockFprog", Type, 0}, + {"SockFprog.Filter", Field, 0}, + {"SockFprog.Len", Field, 0}, + {"SockFprog.Pad_cgo_0", Field, 0}, + {"Sockaddr", Type, 0}, + {"SockaddrDatalink", Type, 0}, + {"SockaddrDatalink.Alen", Field, 0}, + {"SockaddrDatalink.Data", Field, 0}, + {"SockaddrDatalink.Family", Field, 0}, + {"SockaddrDatalink.Index", Field, 0}, + {"SockaddrDatalink.Len", Field, 0}, + {"SockaddrDatalink.Nlen", Field, 0}, + {"SockaddrDatalink.Slen", Field, 0}, + {"SockaddrDatalink.Type", Field, 0}, + {"SockaddrGen", Type, 0}, + {"SockaddrInet4", Type, 0}, + {"SockaddrInet4.Addr", Field, 0}, + {"SockaddrInet4.Port", Field, 0}, + {"SockaddrInet6", Type, 0}, + {"SockaddrInet6.Addr", Field, 0}, + {"SockaddrInet6.Port", Field, 0}, + {"SockaddrInet6.ZoneId", Field, 0}, + {"SockaddrLinklayer", Type, 0}, + {"SockaddrLinklayer.Addr", Field, 0}, + {"SockaddrLinklayer.Halen", Field, 0}, + {"SockaddrLinklayer.Hatype", Field, 0}, + {"SockaddrLinklayer.Ifindex", Field, 0}, + {"SockaddrLinklayer.Pkttype", Field, 0}, + {"SockaddrLinklayer.Protocol", Field, 0}, + {"SockaddrNetlink", Type, 0}, + {"SockaddrNetlink.Family", Field, 0}, + {"SockaddrNetlink.Groups", Field, 0}, + {"SockaddrNetlink.Pad", Field, 0}, + {"SockaddrNetlink.Pid", Field, 0}, + {"SockaddrUnix", Type, 0}, + {"SockaddrUnix.Name", Field, 0}, + {"Socket", Func, 0}, + {"SocketControlMessage", Type, 0}, + {"SocketControlMessage.Data", Field, 0}, + {"SocketControlMessage.Header", Field, 0}, + {"SocketDisableIPv6", Var, 0}, + {"Socketpair", Func, 0}, + {"Splice", Func, 0}, + {"StartProcess", Func, 0}, + {"StartupInfo", Type, 0}, + {"StartupInfo.Cb", Field, 0}, + {"StartupInfo.Desktop", Field, 0}, + {"StartupInfo.FillAttribute", Field, 0}, + {"StartupInfo.Flags", Field, 0}, + {"StartupInfo.ShowWindow", Field, 0}, + {"StartupInfo.StdErr", Field, 0}, + {"StartupInfo.StdInput", Field, 0}, + {"StartupInfo.StdOutput", Field, 0}, + {"StartupInfo.Title", Field, 0}, + {"StartupInfo.X", Field, 0}, + {"StartupInfo.XCountChars", Field, 0}, + {"StartupInfo.XSize", Field, 0}, + {"StartupInfo.Y", Field, 0}, + {"StartupInfo.YCountChars", Field, 0}, + {"StartupInfo.YSize", Field, 0}, + {"Stat", Func, 0}, + {"Stat_t", Type, 0}, + {"Stat_t.Atim", Field, 0}, + {"Stat_t.Atim_ext", Field, 12}, + {"Stat_t.Atimespec", Field, 0}, + {"Stat_t.Birthtimespec", Field, 0}, + {"Stat_t.Blksize", Field, 0}, + {"Stat_t.Blocks", Field, 0}, + {"Stat_t.Btim_ext", Field, 12}, + {"Stat_t.Ctim", Field, 0}, + {"Stat_t.Ctim_ext", Field, 12}, + {"Stat_t.Ctimespec", Field, 0}, + {"Stat_t.Dev", Field, 0}, + {"Stat_t.Flags", Field, 0}, + {"Stat_t.Gen", Field, 0}, + {"Stat_t.Gid", Field, 0}, + {"Stat_t.Ino", Field, 0}, + {"Stat_t.Lspare", Field, 0}, + {"Stat_t.Lspare0", Field, 2}, + {"Stat_t.Lspare1", Field, 2}, + {"Stat_t.Mode", Field, 0}, + {"Stat_t.Mtim", Field, 0}, + {"Stat_t.Mtim_ext", Field, 12}, + {"Stat_t.Mtimespec", Field, 0}, + {"Stat_t.Nlink", Field, 0}, + {"Stat_t.Pad_cgo_0", Field, 0}, + {"Stat_t.Pad_cgo_1", Field, 0}, + {"Stat_t.Pad_cgo_2", Field, 0}, + {"Stat_t.Padding0", Field, 12}, + {"Stat_t.Padding1", Field, 12}, + {"Stat_t.Qspare", Field, 0}, + {"Stat_t.Rdev", Field, 0}, + {"Stat_t.Size", Field, 0}, + {"Stat_t.Spare", Field, 2}, + {"Stat_t.Uid", Field, 0}, + {"Stat_t.X__pad0", Field, 0}, + {"Stat_t.X__pad1", Field, 0}, + {"Stat_t.X__pad2", Field, 0}, + {"Stat_t.X__st_birthtim", Field, 2}, + {"Stat_t.X__st_ino", Field, 0}, + {"Stat_t.X__unused", Field, 0}, + {"Statfs", Func, 0}, + {"Statfs_t", Type, 0}, + {"Statfs_t.Asyncreads", Field, 0}, + {"Statfs_t.Asyncwrites", Field, 0}, + {"Statfs_t.Bavail", Field, 0}, + {"Statfs_t.Bfree", Field, 0}, + {"Statfs_t.Blocks", Field, 0}, + {"Statfs_t.Bsize", Field, 0}, + {"Statfs_t.Charspare", Field, 0}, + {"Statfs_t.F_asyncreads", Field, 2}, + {"Statfs_t.F_asyncwrites", Field, 2}, + {"Statfs_t.F_bavail", Field, 2}, + {"Statfs_t.F_bfree", Field, 2}, + {"Statfs_t.F_blocks", Field, 2}, + {"Statfs_t.F_bsize", Field, 2}, + {"Statfs_t.F_ctime", Field, 2}, + {"Statfs_t.F_favail", Field, 2}, + {"Statfs_t.F_ffree", Field, 2}, + {"Statfs_t.F_files", Field, 2}, + {"Statfs_t.F_flags", Field, 2}, + {"Statfs_t.F_fsid", Field, 2}, + {"Statfs_t.F_fstypename", Field, 2}, + {"Statfs_t.F_iosize", Field, 2}, + {"Statfs_t.F_mntfromname", Field, 2}, + {"Statfs_t.F_mntfromspec", Field, 3}, + {"Statfs_t.F_mntonname", Field, 2}, + {"Statfs_t.F_namemax", Field, 2}, + {"Statfs_t.F_owner", Field, 2}, + {"Statfs_t.F_spare", Field, 2}, + {"Statfs_t.F_syncreads", Field, 2}, + {"Statfs_t.F_syncwrites", Field, 2}, + {"Statfs_t.Ffree", Field, 0}, + {"Statfs_t.Files", Field, 0}, + {"Statfs_t.Flags", Field, 0}, + {"Statfs_t.Frsize", Field, 0}, + {"Statfs_t.Fsid", Field, 0}, + {"Statfs_t.Fssubtype", Field, 0}, + {"Statfs_t.Fstypename", Field, 0}, + {"Statfs_t.Iosize", Field, 0}, + {"Statfs_t.Mntfromname", Field, 0}, + {"Statfs_t.Mntonname", Field, 0}, + {"Statfs_t.Mount_info", Field, 2}, + {"Statfs_t.Namelen", Field, 0}, + {"Statfs_t.Namemax", Field, 0}, + {"Statfs_t.Owner", Field, 0}, + {"Statfs_t.Pad_cgo_0", Field, 0}, + {"Statfs_t.Pad_cgo_1", Field, 2}, + {"Statfs_t.Reserved", Field, 0}, + {"Statfs_t.Spare", Field, 0}, + {"Statfs_t.Syncreads", Field, 0}, + {"Statfs_t.Syncwrites", Field, 0}, + {"Statfs_t.Type", Field, 0}, + {"Statfs_t.Version", Field, 0}, + {"Stderr", Var, 0}, + {"Stdin", Var, 0}, + {"Stdout", Var, 0}, + {"StringBytePtr", Func, 0}, + {"StringByteSlice", Func, 0}, + {"StringSlicePtr", Func, 0}, + {"StringToSid", Func, 0}, + {"StringToUTF16", Func, 0}, + {"StringToUTF16Ptr", Func, 0}, + {"Symlink", Func, 0}, + {"Sync", Func, 0}, + {"SyncFileRange", Func, 0}, + {"SysProcAttr", Type, 0}, + {"SysProcAttr.AdditionalInheritedHandles", Field, 17}, + {"SysProcAttr.AmbientCaps", Field, 9}, + {"SysProcAttr.CgroupFD", Field, 20}, + {"SysProcAttr.Chroot", Field, 0}, + {"SysProcAttr.Cloneflags", Field, 2}, + {"SysProcAttr.CmdLine", Field, 0}, + {"SysProcAttr.CreationFlags", Field, 1}, + {"SysProcAttr.Credential", Field, 0}, + {"SysProcAttr.Ctty", Field, 1}, + {"SysProcAttr.Foreground", Field, 5}, + {"SysProcAttr.GidMappings", Field, 4}, + {"SysProcAttr.GidMappingsEnableSetgroups", Field, 5}, + {"SysProcAttr.HideWindow", Field, 0}, + {"SysProcAttr.Jail", Field, 21}, + {"SysProcAttr.NoInheritHandles", Field, 16}, + {"SysProcAttr.Noctty", Field, 0}, + {"SysProcAttr.ParentProcess", Field, 17}, + {"SysProcAttr.Pdeathsig", Field, 0}, + {"SysProcAttr.Pgid", Field, 5}, + {"SysProcAttr.PidFD", Field, 22}, + {"SysProcAttr.ProcessAttributes", Field, 13}, + {"SysProcAttr.Ptrace", Field, 0}, + {"SysProcAttr.Setctty", Field, 0}, + {"SysProcAttr.Setpgid", Field, 0}, + {"SysProcAttr.Setsid", Field, 0}, + {"SysProcAttr.ThreadAttributes", Field, 13}, + {"SysProcAttr.Token", Field, 10}, + {"SysProcAttr.UidMappings", Field, 4}, + {"SysProcAttr.Unshareflags", Field, 7}, + {"SysProcAttr.UseCgroupFD", Field, 20}, + {"SysProcIDMap", Type, 4}, + {"SysProcIDMap.ContainerID", Field, 4}, + {"SysProcIDMap.HostID", Field, 4}, + {"SysProcIDMap.Size", Field, 4}, + {"Syscall", Func, 0}, + {"Syscall12", Func, 0}, + {"Syscall15", Func, 0}, + {"Syscall18", Func, 12}, + {"Syscall6", Func, 0}, + {"Syscall9", Func, 0}, + {"SyscallN", Func, 18}, + {"Sysctl", Func, 0}, + {"SysctlUint32", Func, 0}, + {"Sysctlnode", Type, 2}, + {"Sysctlnode.Flags", Field, 2}, + {"Sysctlnode.Name", Field, 2}, + {"Sysctlnode.Num", Field, 2}, + {"Sysctlnode.Un", Field, 2}, + {"Sysctlnode.Ver", Field, 2}, + {"Sysctlnode.X__rsvd", Field, 2}, + {"Sysctlnode.X_sysctl_desc", Field, 2}, + {"Sysctlnode.X_sysctl_func", Field, 2}, + {"Sysctlnode.X_sysctl_parent", Field, 2}, + {"Sysctlnode.X_sysctl_size", Field, 2}, + {"Sysinfo", Func, 0}, + {"Sysinfo_t", Type, 0}, + {"Sysinfo_t.Bufferram", Field, 0}, + {"Sysinfo_t.Freehigh", Field, 0}, + {"Sysinfo_t.Freeram", Field, 0}, + {"Sysinfo_t.Freeswap", Field, 0}, + {"Sysinfo_t.Loads", Field, 0}, + {"Sysinfo_t.Pad", Field, 0}, + {"Sysinfo_t.Pad_cgo_0", Field, 0}, + {"Sysinfo_t.Pad_cgo_1", Field, 0}, + {"Sysinfo_t.Procs", Field, 0}, + {"Sysinfo_t.Sharedram", Field, 0}, + {"Sysinfo_t.Totalhigh", Field, 0}, + {"Sysinfo_t.Totalram", Field, 0}, + {"Sysinfo_t.Totalswap", Field, 0}, + {"Sysinfo_t.Unit", Field, 0}, + {"Sysinfo_t.Uptime", Field, 0}, + {"Sysinfo_t.X_f", Field, 0}, + {"Systemtime", Type, 0}, + {"Systemtime.Day", Field, 0}, + {"Systemtime.DayOfWeek", Field, 0}, + {"Systemtime.Hour", Field, 0}, + {"Systemtime.Milliseconds", Field, 0}, + {"Systemtime.Minute", Field, 0}, + {"Systemtime.Month", Field, 0}, + {"Systemtime.Second", Field, 0}, + {"Systemtime.Year", Field, 0}, + {"TCGETS", Const, 0}, + {"TCIFLUSH", Const, 1}, + {"TCIOFLUSH", Const, 1}, + {"TCOFLUSH", Const, 1}, + {"TCPInfo", Type, 1}, + {"TCPInfo.Advmss", Field, 1}, + {"TCPInfo.Ato", Field, 1}, + {"TCPInfo.Backoff", Field, 1}, + {"TCPInfo.Ca_state", Field, 1}, + {"TCPInfo.Fackets", Field, 1}, + {"TCPInfo.Last_ack_recv", Field, 1}, + {"TCPInfo.Last_ack_sent", Field, 1}, + {"TCPInfo.Last_data_recv", Field, 1}, + {"TCPInfo.Last_data_sent", Field, 1}, + {"TCPInfo.Lost", Field, 1}, + {"TCPInfo.Options", Field, 1}, + {"TCPInfo.Pad_cgo_0", Field, 1}, + {"TCPInfo.Pmtu", Field, 1}, + {"TCPInfo.Probes", Field, 1}, + {"TCPInfo.Rcv_mss", Field, 1}, + {"TCPInfo.Rcv_rtt", Field, 1}, + {"TCPInfo.Rcv_space", Field, 1}, + {"TCPInfo.Rcv_ssthresh", Field, 1}, + {"TCPInfo.Reordering", Field, 1}, + {"TCPInfo.Retrans", Field, 1}, + {"TCPInfo.Retransmits", Field, 1}, + {"TCPInfo.Rto", Field, 1}, + {"TCPInfo.Rtt", Field, 1}, + {"TCPInfo.Rttvar", Field, 1}, + {"TCPInfo.Sacked", Field, 1}, + {"TCPInfo.Snd_cwnd", Field, 1}, + {"TCPInfo.Snd_mss", Field, 1}, + {"TCPInfo.Snd_ssthresh", Field, 1}, + {"TCPInfo.State", Field, 1}, + {"TCPInfo.Total_retrans", Field, 1}, + {"TCPInfo.Unacked", Field, 1}, + {"TCPKeepalive", Type, 3}, + {"TCPKeepalive.Interval", Field, 3}, + {"TCPKeepalive.OnOff", Field, 3}, + {"TCPKeepalive.Time", Field, 3}, + {"TCP_CA_NAME_MAX", Const, 0}, + {"TCP_CONGCTL", Const, 1}, + {"TCP_CONGESTION", Const, 0}, + {"TCP_CONNECTIONTIMEOUT", Const, 0}, + {"TCP_CORK", Const, 0}, + {"TCP_DEFER_ACCEPT", Const, 0}, + {"TCP_ENABLE_ECN", Const, 16}, + {"TCP_INFO", Const, 0}, + {"TCP_KEEPALIVE", Const, 0}, + {"TCP_KEEPCNT", Const, 0}, + {"TCP_KEEPIDLE", Const, 0}, + {"TCP_KEEPINIT", Const, 1}, + {"TCP_KEEPINTVL", Const, 0}, + {"TCP_LINGER2", Const, 0}, + {"TCP_MAXBURST", Const, 0}, + {"TCP_MAXHLEN", Const, 0}, + {"TCP_MAXOLEN", Const, 0}, + {"TCP_MAXSEG", Const, 0}, + {"TCP_MAXWIN", Const, 0}, + {"TCP_MAX_SACK", Const, 0}, + {"TCP_MAX_WINSHIFT", Const, 0}, + {"TCP_MD5SIG", Const, 0}, + {"TCP_MD5SIG_MAXKEYLEN", Const, 0}, + {"TCP_MINMSS", Const, 0}, + {"TCP_MINMSSOVERLOAD", Const, 0}, + {"TCP_MSS", Const, 0}, + {"TCP_NODELAY", Const, 0}, + {"TCP_NOOPT", Const, 0}, + {"TCP_NOPUSH", Const, 0}, + {"TCP_NOTSENT_LOWAT", Const, 16}, + {"TCP_NSTATES", Const, 1}, + {"TCP_QUICKACK", Const, 0}, + {"TCP_RXT_CONNDROPTIME", Const, 0}, + {"TCP_RXT_FINDROP", Const, 0}, + {"TCP_SACK_ENABLE", Const, 1}, + {"TCP_SENDMOREACKS", Const, 16}, + {"TCP_SYNCNT", Const, 0}, + {"TCP_VENDOR", Const, 3}, + {"TCP_WINDOW_CLAMP", Const, 0}, + {"TCSAFLUSH", Const, 1}, + {"TCSETS", Const, 0}, + {"TF_DISCONNECT", Const, 0}, + {"TF_REUSE_SOCKET", Const, 0}, + {"TF_USE_DEFAULT_WORKER", Const, 0}, + {"TF_USE_KERNEL_APC", Const, 0}, + {"TF_USE_SYSTEM_THREAD", Const, 0}, + {"TF_WRITE_BEHIND", Const, 0}, + {"TH32CS_INHERIT", Const, 4}, + {"TH32CS_SNAPALL", Const, 4}, + {"TH32CS_SNAPHEAPLIST", Const, 4}, + {"TH32CS_SNAPMODULE", Const, 4}, + {"TH32CS_SNAPMODULE32", Const, 4}, + {"TH32CS_SNAPPROCESS", Const, 4}, + {"TH32CS_SNAPTHREAD", Const, 4}, + {"TIME_ZONE_ID_DAYLIGHT", Const, 0}, + {"TIME_ZONE_ID_STANDARD", Const, 0}, + {"TIME_ZONE_ID_UNKNOWN", Const, 0}, + {"TIOCCBRK", Const, 0}, + {"TIOCCDTR", Const, 0}, + {"TIOCCONS", Const, 0}, + {"TIOCDCDTIMESTAMP", Const, 0}, + {"TIOCDRAIN", Const, 0}, + {"TIOCDSIMICROCODE", Const, 0}, + {"TIOCEXCL", Const, 0}, + {"TIOCEXT", Const, 0}, + {"TIOCFLAG_CDTRCTS", Const, 1}, + {"TIOCFLAG_CLOCAL", Const, 1}, + {"TIOCFLAG_CRTSCTS", Const, 1}, + {"TIOCFLAG_MDMBUF", Const, 1}, + {"TIOCFLAG_PPS", Const, 1}, + {"TIOCFLAG_SOFTCAR", Const, 1}, + {"TIOCFLUSH", Const, 0}, + {"TIOCGDEV", Const, 0}, + {"TIOCGDRAINWAIT", Const, 0}, + {"TIOCGETA", Const, 0}, + {"TIOCGETD", Const, 0}, + {"TIOCGFLAGS", Const, 1}, + {"TIOCGICOUNT", Const, 0}, + {"TIOCGLCKTRMIOS", Const, 0}, + {"TIOCGLINED", Const, 1}, + {"TIOCGPGRP", Const, 0}, + {"TIOCGPTN", Const, 0}, + {"TIOCGQSIZE", Const, 1}, + {"TIOCGRANTPT", Const, 1}, + {"TIOCGRS485", Const, 0}, + {"TIOCGSERIAL", Const, 0}, + {"TIOCGSID", Const, 0}, + {"TIOCGSIZE", Const, 1}, + {"TIOCGSOFTCAR", Const, 0}, + {"TIOCGTSTAMP", Const, 1}, + {"TIOCGWINSZ", Const, 0}, + {"TIOCINQ", Const, 0}, + {"TIOCIXOFF", Const, 0}, + {"TIOCIXON", Const, 0}, + {"TIOCLINUX", Const, 0}, + {"TIOCMBIC", Const, 0}, + {"TIOCMBIS", Const, 0}, + {"TIOCMGDTRWAIT", Const, 0}, + {"TIOCMGET", Const, 0}, + {"TIOCMIWAIT", Const, 0}, + {"TIOCMODG", Const, 0}, + {"TIOCMODS", Const, 0}, + {"TIOCMSDTRWAIT", Const, 0}, + {"TIOCMSET", Const, 0}, + {"TIOCM_CAR", Const, 0}, + {"TIOCM_CD", Const, 0}, + {"TIOCM_CTS", Const, 0}, + {"TIOCM_DCD", Const, 0}, + {"TIOCM_DSR", Const, 0}, + {"TIOCM_DTR", Const, 0}, + {"TIOCM_LE", Const, 0}, + {"TIOCM_RI", Const, 0}, + {"TIOCM_RNG", Const, 0}, + {"TIOCM_RTS", Const, 0}, + {"TIOCM_SR", Const, 0}, + {"TIOCM_ST", Const, 0}, + {"TIOCNOTTY", Const, 0}, + {"TIOCNXCL", Const, 0}, + {"TIOCOUTQ", Const, 0}, + {"TIOCPKT", Const, 0}, + {"TIOCPKT_DATA", Const, 0}, + {"TIOCPKT_DOSTOP", Const, 0}, + {"TIOCPKT_FLUSHREAD", Const, 0}, + {"TIOCPKT_FLUSHWRITE", Const, 0}, + {"TIOCPKT_IOCTL", Const, 0}, + {"TIOCPKT_NOSTOP", Const, 0}, + {"TIOCPKT_START", Const, 0}, + {"TIOCPKT_STOP", Const, 0}, + {"TIOCPTMASTER", Const, 0}, + {"TIOCPTMGET", Const, 1}, + {"TIOCPTSNAME", Const, 1}, + {"TIOCPTYGNAME", Const, 0}, + {"TIOCPTYGRANT", Const, 0}, + {"TIOCPTYUNLK", Const, 0}, + {"TIOCRCVFRAME", Const, 1}, + {"TIOCREMOTE", Const, 0}, + {"TIOCSBRK", Const, 0}, + {"TIOCSCONS", Const, 0}, + {"TIOCSCTTY", Const, 0}, + {"TIOCSDRAINWAIT", Const, 0}, + {"TIOCSDTR", Const, 0}, + {"TIOCSERCONFIG", Const, 0}, + {"TIOCSERGETLSR", Const, 0}, + {"TIOCSERGETMULTI", Const, 0}, + {"TIOCSERGSTRUCT", Const, 0}, + {"TIOCSERGWILD", Const, 0}, + {"TIOCSERSETMULTI", Const, 0}, + {"TIOCSERSWILD", Const, 0}, + {"TIOCSER_TEMT", Const, 0}, + {"TIOCSETA", Const, 0}, + {"TIOCSETAF", Const, 0}, + {"TIOCSETAW", Const, 0}, + {"TIOCSETD", Const, 0}, + {"TIOCSFLAGS", Const, 1}, + {"TIOCSIG", Const, 0}, + {"TIOCSLCKTRMIOS", Const, 0}, + {"TIOCSLINED", Const, 1}, + {"TIOCSPGRP", Const, 0}, + {"TIOCSPTLCK", Const, 0}, + {"TIOCSQSIZE", Const, 1}, + {"TIOCSRS485", Const, 0}, + {"TIOCSSERIAL", Const, 0}, + {"TIOCSSIZE", Const, 1}, + {"TIOCSSOFTCAR", Const, 0}, + {"TIOCSTART", Const, 0}, + {"TIOCSTAT", Const, 0}, + {"TIOCSTI", Const, 0}, + {"TIOCSTOP", Const, 0}, + {"TIOCSTSTAMP", Const, 1}, + {"TIOCSWINSZ", Const, 0}, + {"TIOCTIMESTAMP", Const, 0}, + {"TIOCUCNTL", Const, 0}, + {"TIOCVHANGUP", Const, 0}, + {"TIOCXMTFRAME", Const, 1}, + {"TOKEN_ADJUST_DEFAULT", Const, 0}, + {"TOKEN_ADJUST_GROUPS", Const, 0}, + {"TOKEN_ADJUST_PRIVILEGES", Const, 0}, + {"TOKEN_ADJUST_SESSIONID", Const, 11}, + {"TOKEN_ALL_ACCESS", Const, 0}, + {"TOKEN_ASSIGN_PRIMARY", Const, 0}, + {"TOKEN_DUPLICATE", Const, 0}, + {"TOKEN_EXECUTE", Const, 0}, + {"TOKEN_IMPERSONATE", Const, 0}, + {"TOKEN_QUERY", Const, 0}, + {"TOKEN_QUERY_SOURCE", Const, 0}, + {"TOKEN_READ", Const, 0}, + {"TOKEN_WRITE", Const, 0}, + {"TOSTOP", Const, 0}, + {"TRUNCATE_EXISTING", Const, 0}, + {"TUNATTACHFILTER", Const, 0}, + {"TUNDETACHFILTER", Const, 0}, + {"TUNGETFEATURES", Const, 0}, + {"TUNGETIFF", Const, 0}, + {"TUNGETSNDBUF", Const, 0}, + {"TUNGETVNETHDRSZ", Const, 0}, + {"TUNSETDEBUG", Const, 0}, + {"TUNSETGROUP", Const, 0}, + {"TUNSETIFF", Const, 0}, + {"TUNSETLINK", Const, 0}, + {"TUNSETNOCSUM", Const, 0}, + {"TUNSETOFFLOAD", Const, 0}, + {"TUNSETOWNER", Const, 0}, + {"TUNSETPERSIST", Const, 0}, + {"TUNSETSNDBUF", Const, 0}, + {"TUNSETTXFILTER", Const, 0}, + {"TUNSETVNETHDRSZ", Const, 0}, + {"Tee", Func, 0}, + {"TerminateProcess", Func, 0}, + {"Termios", Type, 0}, + {"Termios.Cc", Field, 0}, + {"Termios.Cflag", Field, 0}, + {"Termios.Iflag", Field, 0}, + {"Termios.Ispeed", Field, 0}, + {"Termios.Lflag", Field, 0}, + {"Termios.Line", Field, 0}, + {"Termios.Oflag", Field, 0}, + {"Termios.Ospeed", Field, 0}, + {"Termios.Pad_cgo_0", Field, 0}, + {"Tgkill", Func, 0}, + {"Time", Func, 0}, + {"Time_t", Type, 0}, + {"Times", Func, 0}, + {"Timespec", Type, 0}, + {"Timespec.Nsec", Field, 0}, + {"Timespec.Pad_cgo_0", Field, 2}, + {"Timespec.Sec", Field, 0}, + {"TimespecToNsec", Func, 0}, + {"Timeval", Type, 0}, + {"Timeval.Pad_cgo_0", Field, 0}, + {"Timeval.Sec", Field, 0}, + {"Timeval.Usec", Field, 0}, + {"Timeval32", Type, 0}, + {"Timeval32.Sec", Field, 0}, + {"Timeval32.Usec", Field, 0}, + {"TimevalToNsec", Func, 0}, + {"Timex", Type, 0}, + {"Timex.Calcnt", Field, 0}, + {"Timex.Constant", Field, 0}, + {"Timex.Errcnt", Field, 0}, + {"Timex.Esterror", Field, 0}, + {"Timex.Freq", Field, 0}, + {"Timex.Jitcnt", Field, 0}, + {"Timex.Jitter", Field, 0}, + {"Timex.Maxerror", Field, 0}, + {"Timex.Modes", Field, 0}, + {"Timex.Offset", Field, 0}, + {"Timex.Pad_cgo_0", Field, 0}, + {"Timex.Pad_cgo_1", Field, 0}, + {"Timex.Pad_cgo_2", Field, 0}, + {"Timex.Pad_cgo_3", Field, 0}, + {"Timex.Ppsfreq", Field, 0}, + {"Timex.Precision", Field, 0}, + {"Timex.Shift", Field, 0}, + {"Timex.Stabil", Field, 0}, + {"Timex.Status", Field, 0}, + {"Timex.Stbcnt", Field, 0}, + {"Timex.Tai", Field, 0}, + {"Timex.Tick", Field, 0}, + {"Timex.Time", Field, 0}, + {"Timex.Tolerance", Field, 0}, + {"Timezoneinformation", Type, 0}, + {"Timezoneinformation.Bias", Field, 0}, + {"Timezoneinformation.DaylightBias", Field, 0}, + {"Timezoneinformation.DaylightDate", Field, 0}, + {"Timezoneinformation.DaylightName", Field, 0}, + {"Timezoneinformation.StandardBias", Field, 0}, + {"Timezoneinformation.StandardDate", Field, 0}, + {"Timezoneinformation.StandardName", Field, 0}, + {"Tms", Type, 0}, + {"Tms.Cstime", Field, 0}, + {"Tms.Cutime", Field, 0}, + {"Tms.Stime", Field, 0}, + {"Tms.Utime", Field, 0}, + {"Token", Type, 0}, + {"TokenAccessInformation", Const, 0}, + {"TokenAuditPolicy", Const, 0}, + {"TokenDefaultDacl", Const, 0}, + {"TokenElevation", Const, 0}, + {"TokenElevationType", Const, 0}, + {"TokenGroups", Const, 0}, + {"TokenGroupsAndPrivileges", Const, 0}, + {"TokenHasRestrictions", Const, 0}, + {"TokenImpersonationLevel", Const, 0}, + {"TokenIntegrityLevel", Const, 0}, + {"TokenLinkedToken", Const, 0}, + {"TokenLogonSid", Const, 0}, + {"TokenMandatoryPolicy", Const, 0}, + {"TokenOrigin", Const, 0}, + {"TokenOwner", Const, 0}, + {"TokenPrimaryGroup", Const, 0}, + {"TokenPrivileges", Const, 0}, + {"TokenRestrictedSids", Const, 0}, + {"TokenSandBoxInert", Const, 0}, + {"TokenSessionId", Const, 0}, + {"TokenSessionReference", Const, 0}, + {"TokenSource", Const, 0}, + {"TokenStatistics", Const, 0}, + {"TokenType", Const, 0}, + {"TokenUIAccess", Const, 0}, + {"TokenUser", Const, 0}, + {"TokenVirtualizationAllowed", Const, 0}, + {"TokenVirtualizationEnabled", Const, 0}, + {"Tokenprimarygroup", Type, 0}, + {"Tokenprimarygroup.PrimaryGroup", Field, 0}, + {"Tokenuser", Type, 0}, + {"Tokenuser.User", Field, 0}, + {"TranslateAccountName", Func, 0}, + {"TranslateName", Func, 0}, + {"TransmitFile", Func, 0}, + {"TransmitFileBuffers", Type, 0}, + {"TransmitFileBuffers.Head", Field, 0}, + {"TransmitFileBuffers.HeadLength", Field, 0}, + {"TransmitFileBuffers.Tail", Field, 0}, + {"TransmitFileBuffers.TailLength", Field, 0}, + {"Truncate", Func, 0}, + {"UNIX_PATH_MAX", Const, 12}, + {"USAGE_MATCH_TYPE_AND", Const, 0}, + {"USAGE_MATCH_TYPE_OR", Const, 0}, + {"UTF16FromString", Func, 1}, + {"UTF16PtrFromString", Func, 1}, + {"UTF16ToString", Func, 0}, + {"Ucred", Type, 0}, + {"Ucred.Gid", Field, 0}, + {"Ucred.Pid", Field, 0}, + {"Ucred.Uid", Field, 0}, + {"Umask", Func, 0}, + {"Uname", Func, 0}, + {"Undelete", Func, 0}, + {"UnixCredentials", Func, 0}, + {"UnixRights", Func, 0}, + {"Unlink", Func, 0}, + {"Unlinkat", Func, 0}, + {"UnmapViewOfFile", Func, 0}, + {"Unmount", Func, 0}, + {"Unsetenv", Func, 4}, + {"Unshare", Func, 0}, + {"UserInfo10", Type, 0}, + {"UserInfo10.Comment", Field, 0}, + {"UserInfo10.FullName", Field, 0}, + {"UserInfo10.Name", Field, 0}, + {"UserInfo10.UsrComment", Field, 0}, + {"Ustat", Func, 0}, + {"Ustat_t", Type, 0}, + {"Ustat_t.Fname", Field, 0}, + {"Ustat_t.Fpack", Field, 0}, + {"Ustat_t.Pad_cgo_0", Field, 0}, + {"Ustat_t.Pad_cgo_1", Field, 0}, + {"Ustat_t.Tfree", Field, 0}, + {"Ustat_t.Tinode", Field, 0}, + {"Utimbuf", Type, 0}, + {"Utimbuf.Actime", Field, 0}, + {"Utimbuf.Modtime", Field, 0}, + {"Utime", Func, 0}, + {"Utimes", Func, 0}, + {"UtimesNano", Func, 1}, + {"Utsname", Type, 0}, + {"Utsname.Domainname", Field, 0}, + {"Utsname.Machine", Field, 0}, + {"Utsname.Nodename", Field, 0}, + {"Utsname.Release", Field, 0}, + {"Utsname.Sysname", Field, 0}, + {"Utsname.Version", Field, 0}, + {"VDISCARD", Const, 0}, + {"VDSUSP", Const, 1}, + {"VEOF", Const, 0}, + {"VEOL", Const, 0}, + {"VEOL2", Const, 0}, + {"VERASE", Const, 0}, + {"VERASE2", Const, 1}, + {"VINTR", Const, 0}, + {"VKILL", Const, 0}, + {"VLNEXT", Const, 0}, + {"VMIN", Const, 0}, + {"VQUIT", Const, 0}, + {"VREPRINT", Const, 0}, + {"VSTART", Const, 0}, + {"VSTATUS", Const, 1}, + {"VSTOP", Const, 0}, + {"VSUSP", Const, 0}, + {"VSWTC", Const, 0}, + {"VT0", Const, 1}, + {"VT1", Const, 1}, + {"VTDLY", Const, 1}, + {"VTIME", Const, 0}, + {"VWERASE", Const, 0}, + {"VirtualLock", Func, 0}, + {"VirtualUnlock", Func, 0}, + {"WAIT_ABANDONED", Const, 0}, + {"WAIT_FAILED", Const, 0}, + {"WAIT_OBJECT_0", Const, 0}, + {"WAIT_TIMEOUT", Const, 0}, + {"WALL", Const, 0}, + {"WALLSIG", Const, 1}, + {"WALTSIG", Const, 1}, + {"WCLONE", Const, 0}, + {"WCONTINUED", Const, 0}, + {"WCOREFLAG", Const, 0}, + {"WEXITED", Const, 0}, + {"WLINUXCLONE", Const, 0}, + {"WNOHANG", Const, 0}, + {"WNOTHREAD", Const, 0}, + {"WNOWAIT", Const, 0}, + {"WNOZOMBIE", Const, 1}, + {"WOPTSCHECKED", Const, 1}, + {"WORDSIZE", Const, 0}, + {"WSABuf", Type, 0}, + {"WSABuf.Buf", Field, 0}, + {"WSABuf.Len", Field, 0}, + {"WSACleanup", Func, 0}, + {"WSADESCRIPTION_LEN", Const, 0}, + {"WSAData", Type, 0}, + {"WSAData.Description", Field, 0}, + {"WSAData.HighVersion", Field, 0}, + {"WSAData.MaxSockets", Field, 0}, + {"WSAData.MaxUdpDg", Field, 0}, + {"WSAData.SystemStatus", Field, 0}, + {"WSAData.VendorInfo", Field, 0}, + {"WSAData.Version", Field, 0}, + {"WSAEACCES", Const, 2}, + {"WSAECONNABORTED", Const, 9}, + {"WSAECONNRESET", Const, 3}, + {"WSAENOPROTOOPT", Const, 23}, + {"WSAEnumProtocols", Func, 2}, + {"WSAID_CONNECTEX", Var, 1}, + {"WSAIoctl", Func, 0}, + {"WSAPROTOCOL_LEN", Const, 2}, + {"WSAProtocolChain", Type, 2}, + {"WSAProtocolChain.ChainEntries", Field, 2}, + {"WSAProtocolChain.ChainLen", Field, 2}, + {"WSAProtocolInfo", Type, 2}, + {"WSAProtocolInfo.AddressFamily", Field, 2}, + {"WSAProtocolInfo.CatalogEntryId", Field, 2}, + {"WSAProtocolInfo.MaxSockAddr", Field, 2}, + {"WSAProtocolInfo.MessageSize", Field, 2}, + {"WSAProtocolInfo.MinSockAddr", Field, 2}, + {"WSAProtocolInfo.NetworkByteOrder", Field, 2}, + {"WSAProtocolInfo.Protocol", Field, 2}, + {"WSAProtocolInfo.ProtocolChain", Field, 2}, + {"WSAProtocolInfo.ProtocolMaxOffset", Field, 2}, + {"WSAProtocolInfo.ProtocolName", Field, 2}, + {"WSAProtocolInfo.ProviderFlags", Field, 2}, + {"WSAProtocolInfo.ProviderId", Field, 2}, + {"WSAProtocolInfo.ProviderReserved", Field, 2}, + {"WSAProtocolInfo.SecurityScheme", Field, 2}, + {"WSAProtocolInfo.ServiceFlags1", Field, 2}, + {"WSAProtocolInfo.ServiceFlags2", Field, 2}, + {"WSAProtocolInfo.ServiceFlags3", Field, 2}, + {"WSAProtocolInfo.ServiceFlags4", Field, 2}, + {"WSAProtocolInfo.SocketType", Field, 2}, + {"WSAProtocolInfo.Version", Field, 2}, + {"WSARecv", Func, 0}, + {"WSARecvFrom", Func, 0}, + {"WSASYS_STATUS_LEN", Const, 0}, + {"WSASend", Func, 0}, + {"WSASendTo", Func, 0}, + {"WSASendto", Func, 0}, + {"WSAStartup", Func, 0}, + {"WSTOPPED", Const, 0}, + {"WTRAPPED", Const, 1}, + {"WUNTRACED", Const, 0}, + {"Wait4", Func, 0}, + {"WaitForSingleObject", Func, 0}, + {"WaitStatus", Type, 0}, + {"WaitStatus.ExitCode", Field, 0}, + {"Win32FileAttributeData", Type, 0}, + {"Win32FileAttributeData.CreationTime", Field, 0}, + {"Win32FileAttributeData.FileAttributes", Field, 0}, + {"Win32FileAttributeData.FileSizeHigh", Field, 0}, + {"Win32FileAttributeData.FileSizeLow", Field, 0}, + {"Win32FileAttributeData.LastAccessTime", Field, 0}, + {"Win32FileAttributeData.LastWriteTime", Field, 0}, + {"Win32finddata", Type, 0}, + {"Win32finddata.AlternateFileName", Field, 0}, + {"Win32finddata.CreationTime", Field, 0}, + {"Win32finddata.FileAttributes", Field, 0}, + {"Win32finddata.FileName", Field, 0}, + {"Win32finddata.FileSizeHigh", Field, 0}, + {"Win32finddata.FileSizeLow", Field, 0}, + {"Win32finddata.LastAccessTime", Field, 0}, + {"Win32finddata.LastWriteTime", Field, 0}, + {"Win32finddata.Reserved0", Field, 0}, + {"Win32finddata.Reserved1", Field, 0}, + {"Write", Func, 0}, + {"WriteConsole", Func, 1}, + {"WriteFile", Func, 0}, + {"X509_ASN_ENCODING", Const, 0}, + {"XCASE", Const, 0}, + {"XP1_CONNECTIONLESS", Const, 2}, + {"XP1_CONNECT_DATA", Const, 2}, + {"XP1_DISCONNECT_DATA", Const, 2}, + {"XP1_EXPEDITED_DATA", Const, 2}, + {"XP1_GRACEFUL_CLOSE", Const, 2}, + {"XP1_GUARANTEED_DELIVERY", Const, 2}, + {"XP1_GUARANTEED_ORDER", Const, 2}, + {"XP1_IFS_HANDLES", Const, 2}, + {"XP1_MESSAGE_ORIENTED", Const, 2}, + {"XP1_MULTIPOINT_CONTROL_PLANE", Const, 2}, + {"XP1_MULTIPOINT_DATA_PLANE", Const, 2}, + {"XP1_PARTIAL_MESSAGE", Const, 2}, + {"XP1_PSEUDO_STREAM", Const, 2}, + {"XP1_QOS_SUPPORTED", Const, 2}, + {"XP1_SAN_SUPPORT_SDP", Const, 2}, + {"XP1_SUPPORT_BROADCAST", Const, 2}, + {"XP1_SUPPORT_MULTIPOINT", Const, 2}, + {"XP1_UNI_RECV", Const, 2}, + {"XP1_UNI_SEND", Const, 2}, + }, + "syscall/js": { + {"CopyBytesToGo", Func, 0}, + {"CopyBytesToJS", Func, 0}, + {"Error", Type, 0}, + {"Func", Type, 0}, + {"FuncOf", Func, 0}, + {"Global", Func, 0}, + {"Null", Func, 0}, + {"Type", Type, 0}, + {"TypeBoolean", Const, 0}, + {"TypeFunction", Const, 0}, + {"TypeNull", Const, 0}, + {"TypeNumber", Const, 0}, + {"TypeObject", Const, 0}, + {"TypeString", Const, 0}, + {"TypeSymbol", Const, 0}, + {"TypeUndefined", Const, 0}, + {"Undefined", Func, 0}, + {"Value", Type, 0}, + {"ValueError", Type, 0}, + {"ValueOf", Func, 0}, + }, + "testing": { + {"(*B).Cleanup", Method, 14}, + {"(*B).Elapsed", Method, 20}, + {"(*B).Error", Method, 0}, + {"(*B).Errorf", Method, 0}, + {"(*B).Fail", Method, 0}, + {"(*B).FailNow", Method, 0}, + {"(*B).Failed", Method, 0}, + {"(*B).Fatal", Method, 0}, + {"(*B).Fatalf", Method, 0}, + {"(*B).Helper", Method, 9}, + {"(*B).Log", Method, 0}, + {"(*B).Logf", Method, 0}, + {"(*B).Name", Method, 8}, + {"(*B).ReportAllocs", Method, 1}, + {"(*B).ReportMetric", Method, 13}, + {"(*B).ResetTimer", Method, 0}, + {"(*B).Run", Method, 7}, + {"(*B).RunParallel", Method, 3}, + {"(*B).SetBytes", Method, 0}, + {"(*B).SetParallelism", Method, 3}, + {"(*B).Setenv", Method, 17}, + {"(*B).Skip", Method, 1}, + {"(*B).SkipNow", Method, 1}, + {"(*B).Skipf", Method, 1}, + {"(*B).Skipped", Method, 1}, + {"(*B).StartTimer", Method, 0}, + {"(*B).StopTimer", Method, 0}, + {"(*B).TempDir", Method, 15}, + {"(*F).Add", Method, 18}, + {"(*F).Cleanup", Method, 18}, + {"(*F).Error", Method, 18}, + {"(*F).Errorf", Method, 18}, + {"(*F).Fail", Method, 18}, + {"(*F).FailNow", Method, 18}, + {"(*F).Failed", Method, 18}, + {"(*F).Fatal", Method, 18}, + {"(*F).Fatalf", Method, 18}, + {"(*F).Fuzz", Method, 18}, + {"(*F).Helper", Method, 18}, + {"(*F).Log", Method, 18}, + {"(*F).Logf", Method, 18}, + {"(*F).Name", Method, 18}, + {"(*F).Setenv", Method, 18}, + {"(*F).Skip", Method, 18}, + {"(*F).SkipNow", Method, 18}, + {"(*F).Skipf", Method, 18}, + {"(*F).Skipped", Method, 18}, + {"(*F).TempDir", Method, 18}, + {"(*M).Run", Method, 4}, + {"(*PB).Next", Method, 3}, + {"(*T).Cleanup", Method, 14}, + {"(*T).Deadline", Method, 15}, + {"(*T).Error", Method, 0}, + {"(*T).Errorf", Method, 0}, + {"(*T).Fail", Method, 0}, + {"(*T).FailNow", Method, 0}, + {"(*T).Failed", Method, 0}, + {"(*T).Fatal", Method, 0}, + {"(*T).Fatalf", Method, 0}, + {"(*T).Helper", Method, 9}, + {"(*T).Log", Method, 0}, + {"(*T).Logf", Method, 0}, + {"(*T).Name", Method, 8}, + {"(*T).Parallel", Method, 0}, + {"(*T).Run", Method, 7}, + {"(*T).Setenv", Method, 17}, + {"(*T).Skip", Method, 1}, + {"(*T).SkipNow", Method, 1}, + {"(*T).Skipf", Method, 1}, + {"(*T).Skipped", Method, 1}, + {"(*T).TempDir", Method, 15}, + {"(BenchmarkResult).AllocedBytesPerOp", Method, 1}, + {"(BenchmarkResult).AllocsPerOp", Method, 1}, + {"(BenchmarkResult).MemString", Method, 1}, + {"(BenchmarkResult).NsPerOp", Method, 0}, + {"(BenchmarkResult).String", Method, 0}, + {"AllocsPerRun", Func, 1}, + {"B", Type, 0}, + {"B.N", Field, 0}, + {"Benchmark", Func, 0}, + {"BenchmarkResult", Type, 0}, + {"BenchmarkResult.Bytes", Field, 0}, + {"BenchmarkResult.Extra", Field, 13}, + {"BenchmarkResult.MemAllocs", Field, 1}, + {"BenchmarkResult.MemBytes", Field, 1}, + {"BenchmarkResult.N", Field, 0}, + {"BenchmarkResult.T", Field, 0}, + {"Cover", Type, 2}, + {"Cover.Blocks", Field, 2}, + {"Cover.Counters", Field, 2}, + {"Cover.CoveredPackages", Field, 2}, + {"Cover.Mode", Field, 2}, + {"CoverBlock", Type, 2}, + {"CoverBlock.Col0", Field, 2}, + {"CoverBlock.Col1", Field, 2}, + {"CoverBlock.Line0", Field, 2}, + {"CoverBlock.Line1", Field, 2}, + {"CoverBlock.Stmts", Field, 2}, + {"CoverMode", Func, 8}, + {"Coverage", Func, 4}, + {"F", Type, 18}, + {"Init", Func, 13}, + {"InternalBenchmark", Type, 0}, + {"InternalBenchmark.F", Field, 0}, + {"InternalBenchmark.Name", Field, 0}, + {"InternalExample", Type, 0}, + {"InternalExample.F", Field, 0}, + {"InternalExample.Name", Field, 0}, + {"InternalExample.Output", Field, 0}, + {"InternalExample.Unordered", Field, 7}, + {"InternalFuzzTarget", Type, 18}, + {"InternalFuzzTarget.Fn", Field, 18}, + {"InternalFuzzTarget.Name", Field, 18}, + {"InternalTest", Type, 0}, + {"InternalTest.F", Field, 0}, + {"InternalTest.Name", Field, 0}, + {"M", Type, 4}, + {"Main", Func, 0}, + {"MainStart", Func, 4}, + {"PB", Type, 3}, + {"RegisterCover", Func, 2}, + {"RunBenchmarks", Func, 0}, + {"RunExamples", Func, 0}, + {"RunTests", Func, 0}, + {"Short", Func, 0}, + {"T", Type, 0}, + {"TB", Type, 2}, + {"Testing", Func, 21}, + {"Verbose", Func, 1}, + }, + "testing/fstest": { + {"(MapFS).Glob", Method, 16}, + {"(MapFS).Open", Method, 16}, + {"(MapFS).ReadDir", Method, 16}, + {"(MapFS).ReadFile", Method, 16}, + {"(MapFS).Stat", Method, 16}, + {"(MapFS).Sub", Method, 16}, + {"MapFS", Type, 16}, + {"MapFile", Type, 16}, + {"MapFile.Data", Field, 16}, + {"MapFile.ModTime", Field, 16}, + {"MapFile.Mode", Field, 16}, + {"MapFile.Sys", Field, 16}, + {"TestFS", Func, 16}, + }, + "testing/iotest": { + {"DataErrReader", Func, 0}, + {"ErrReader", Func, 16}, + {"ErrTimeout", Var, 0}, + {"HalfReader", Func, 0}, + {"NewReadLogger", Func, 0}, + {"NewWriteLogger", Func, 0}, + {"OneByteReader", Func, 0}, + {"TestReader", Func, 16}, + {"TimeoutReader", Func, 0}, + {"TruncateWriter", Func, 0}, + }, + "testing/quick": { + {"(*CheckEqualError).Error", Method, 0}, + {"(*CheckError).Error", Method, 0}, + {"(SetupError).Error", Method, 0}, + {"Check", Func, 0}, + {"CheckEqual", Func, 0}, + {"CheckEqualError", Type, 0}, + {"CheckEqualError.CheckError", Field, 0}, + {"CheckEqualError.Out1", Field, 0}, + {"CheckEqualError.Out2", Field, 0}, + {"CheckError", Type, 0}, + {"CheckError.Count", Field, 0}, + {"CheckError.In", Field, 0}, + {"Config", Type, 0}, + {"Config.MaxCount", Field, 0}, + {"Config.MaxCountScale", Field, 0}, + {"Config.Rand", Field, 0}, + {"Config.Values", Field, 0}, + {"Generator", Type, 0}, + {"SetupError", Type, 0}, + {"Value", Func, 0}, + }, + "testing/slogtest": { + {"Run", Func, 22}, + {"TestHandler", Func, 21}, + }, + "text/scanner": { + {"(*Position).IsValid", Method, 0}, + {"(*Scanner).Init", Method, 0}, + {"(*Scanner).IsValid", Method, 0}, + {"(*Scanner).Next", Method, 0}, + {"(*Scanner).Peek", Method, 0}, + {"(*Scanner).Pos", Method, 0}, + {"(*Scanner).Scan", Method, 0}, + {"(*Scanner).TokenText", Method, 0}, + {"(Position).String", Method, 0}, + {"(Scanner).String", Method, 0}, + {"Char", Const, 0}, + {"Comment", Const, 0}, + {"EOF", Const, 0}, + {"Float", Const, 0}, + {"GoTokens", Const, 0}, + {"GoWhitespace", Const, 0}, + {"Ident", Const, 0}, + {"Int", Const, 0}, + {"Position", Type, 0}, + {"Position.Column", Field, 0}, + {"Position.Filename", Field, 0}, + {"Position.Line", Field, 0}, + {"Position.Offset", Field, 0}, + {"RawString", Const, 0}, + {"ScanChars", Const, 0}, + {"ScanComments", Const, 0}, + {"ScanFloats", Const, 0}, + {"ScanIdents", Const, 0}, + {"ScanInts", Const, 0}, + {"ScanRawStrings", Const, 0}, + {"ScanStrings", Const, 0}, + {"Scanner", Type, 0}, + {"Scanner.Error", Field, 0}, + {"Scanner.ErrorCount", Field, 0}, + {"Scanner.IsIdentRune", Field, 4}, + {"Scanner.Mode", Field, 0}, + {"Scanner.Position", Field, 0}, + {"Scanner.Whitespace", Field, 0}, + {"SkipComments", Const, 0}, + {"String", Const, 0}, + {"TokenString", Func, 0}, + }, + "text/tabwriter": { + {"(*Writer).Flush", Method, 0}, + {"(*Writer).Init", Method, 0}, + {"(*Writer).Write", Method, 0}, + {"AlignRight", Const, 0}, + {"Debug", Const, 0}, + {"DiscardEmptyColumns", Const, 0}, + {"Escape", Const, 0}, + {"FilterHTML", Const, 0}, + {"NewWriter", Func, 0}, + {"StripEscape", Const, 0}, + {"TabIndent", Const, 0}, + {"Writer", Type, 0}, + }, + "text/template": { + {"(*Template).AddParseTree", Method, 0}, + {"(*Template).Clone", Method, 0}, + {"(*Template).DefinedTemplates", Method, 5}, + {"(*Template).Delims", Method, 0}, + {"(*Template).Execute", Method, 0}, + {"(*Template).ExecuteTemplate", Method, 0}, + {"(*Template).Funcs", Method, 0}, + {"(*Template).Lookup", Method, 0}, + {"(*Template).Name", Method, 0}, + {"(*Template).New", Method, 0}, + {"(*Template).Option", Method, 5}, + {"(*Template).Parse", Method, 0}, + {"(*Template).ParseFS", Method, 16}, + {"(*Template).ParseFiles", Method, 0}, + {"(*Template).ParseGlob", Method, 0}, + {"(*Template).Templates", Method, 0}, + {"(ExecError).Error", Method, 6}, + {"(ExecError).Unwrap", Method, 13}, + {"(Template).Copy", Method, 2}, + {"(Template).ErrorContext", Method, 1}, + {"ExecError", Type, 6}, + {"ExecError.Err", Field, 6}, + {"ExecError.Name", Field, 6}, + {"FuncMap", Type, 0}, + {"HTMLEscape", Func, 0}, + {"HTMLEscapeString", Func, 0}, + {"HTMLEscaper", Func, 0}, + {"IsTrue", Func, 6}, + {"JSEscape", Func, 0}, + {"JSEscapeString", Func, 0}, + {"JSEscaper", Func, 0}, + {"Must", Func, 0}, + {"New", Func, 0}, + {"ParseFS", Func, 16}, + {"ParseFiles", Func, 0}, + {"ParseGlob", Func, 0}, + {"Template", Type, 0}, + {"Template.Tree", Field, 0}, + {"URLQueryEscaper", Func, 0}, + }, + "text/template/parse": { + {"(*ActionNode).Copy", Method, 0}, + {"(*ActionNode).String", Method, 0}, + {"(*BoolNode).Copy", Method, 0}, + {"(*BoolNode).String", Method, 0}, + {"(*BranchNode).Copy", Method, 4}, + {"(*BranchNode).String", Method, 0}, + {"(*BreakNode).Copy", Method, 18}, + {"(*BreakNode).String", Method, 18}, + {"(*ChainNode).Add", Method, 1}, + {"(*ChainNode).Copy", Method, 1}, + {"(*ChainNode).String", Method, 1}, + {"(*CommandNode).Copy", Method, 0}, + {"(*CommandNode).String", Method, 0}, + {"(*CommentNode).Copy", Method, 16}, + {"(*CommentNode).String", Method, 16}, + {"(*ContinueNode).Copy", Method, 18}, + {"(*ContinueNode).String", Method, 18}, + {"(*DotNode).Copy", Method, 0}, + {"(*DotNode).String", Method, 0}, + {"(*DotNode).Type", Method, 0}, + {"(*FieldNode).Copy", Method, 0}, + {"(*FieldNode).String", Method, 0}, + {"(*IdentifierNode).Copy", Method, 0}, + {"(*IdentifierNode).SetPos", Method, 1}, + {"(*IdentifierNode).SetTree", Method, 4}, + {"(*IdentifierNode).String", Method, 0}, + {"(*IfNode).Copy", Method, 0}, + {"(*IfNode).String", Method, 0}, + {"(*ListNode).Copy", Method, 0}, + {"(*ListNode).CopyList", Method, 0}, + {"(*ListNode).String", Method, 0}, + {"(*NilNode).Copy", Method, 1}, + {"(*NilNode).String", Method, 1}, + {"(*NilNode).Type", Method, 1}, + {"(*NumberNode).Copy", Method, 0}, + {"(*NumberNode).String", Method, 0}, + {"(*PipeNode).Copy", Method, 0}, + {"(*PipeNode).CopyPipe", Method, 0}, + {"(*PipeNode).String", Method, 0}, + {"(*RangeNode).Copy", Method, 0}, + {"(*RangeNode).String", Method, 0}, + {"(*StringNode).Copy", Method, 0}, + {"(*StringNode).String", Method, 0}, + {"(*TemplateNode).Copy", Method, 0}, + {"(*TemplateNode).String", Method, 0}, + {"(*TextNode).Copy", Method, 0}, + {"(*TextNode).String", Method, 0}, + {"(*Tree).Copy", Method, 2}, + {"(*Tree).ErrorContext", Method, 1}, + {"(*Tree).Parse", Method, 0}, + {"(*VariableNode).Copy", Method, 0}, + {"(*VariableNode).String", Method, 0}, + {"(*WithNode).Copy", Method, 0}, + {"(*WithNode).String", Method, 0}, + {"(ActionNode).Position", Method, 1}, + {"(ActionNode).Type", Method, 0}, + {"(BoolNode).Position", Method, 1}, + {"(BoolNode).Type", Method, 0}, + {"(BranchNode).Position", Method, 1}, + {"(BranchNode).Type", Method, 0}, + {"(BreakNode).Position", Method, 18}, + {"(BreakNode).Type", Method, 18}, + {"(ChainNode).Position", Method, 1}, + {"(ChainNode).Type", Method, 1}, + {"(CommandNode).Position", Method, 1}, + {"(CommandNode).Type", Method, 0}, + {"(CommentNode).Position", Method, 16}, + {"(CommentNode).Type", Method, 16}, + {"(ContinueNode).Position", Method, 18}, + {"(ContinueNode).Type", Method, 18}, + {"(DotNode).Position", Method, 1}, + {"(FieldNode).Position", Method, 1}, + {"(FieldNode).Type", Method, 0}, + {"(IdentifierNode).Position", Method, 1}, + {"(IdentifierNode).Type", Method, 0}, + {"(IfNode).Position", Method, 1}, + {"(IfNode).Type", Method, 0}, + {"(ListNode).Position", Method, 1}, + {"(ListNode).Type", Method, 0}, + {"(NilNode).Position", Method, 1}, + {"(NodeType).Type", Method, 0}, + {"(NumberNode).Position", Method, 1}, + {"(NumberNode).Type", Method, 0}, + {"(PipeNode).Position", Method, 1}, + {"(PipeNode).Type", Method, 0}, + {"(Pos).Position", Method, 1}, + {"(RangeNode).Position", Method, 1}, + {"(RangeNode).Type", Method, 0}, + {"(StringNode).Position", Method, 1}, + {"(StringNode).Type", Method, 0}, + {"(TemplateNode).Position", Method, 1}, + {"(TemplateNode).Type", Method, 0}, + {"(TextNode).Position", Method, 1}, + {"(TextNode).Type", Method, 0}, + {"(VariableNode).Position", Method, 1}, + {"(VariableNode).Type", Method, 0}, + {"(WithNode).Position", Method, 1}, + {"(WithNode).Type", Method, 0}, + {"ActionNode", Type, 0}, + {"ActionNode.Line", Field, 0}, + {"ActionNode.NodeType", Field, 0}, + {"ActionNode.Pipe", Field, 0}, + {"ActionNode.Pos", Field, 1}, + {"BoolNode", Type, 0}, + {"BoolNode.NodeType", Field, 0}, + {"BoolNode.Pos", Field, 1}, + {"BoolNode.True", Field, 0}, + {"BranchNode", Type, 0}, + {"BranchNode.ElseList", Field, 0}, + {"BranchNode.Line", Field, 0}, + {"BranchNode.List", Field, 0}, + {"BranchNode.NodeType", Field, 0}, + {"BranchNode.Pipe", Field, 0}, + {"BranchNode.Pos", Field, 1}, + {"BreakNode", Type, 18}, + {"BreakNode.Line", Field, 18}, + {"BreakNode.NodeType", Field, 18}, + {"BreakNode.Pos", Field, 18}, + {"ChainNode", Type, 1}, + {"ChainNode.Field", Field, 1}, + {"ChainNode.Node", Field, 1}, + {"ChainNode.NodeType", Field, 1}, + {"ChainNode.Pos", Field, 1}, + {"CommandNode", Type, 0}, + {"CommandNode.Args", Field, 0}, + {"CommandNode.NodeType", Field, 0}, + {"CommandNode.Pos", Field, 1}, + {"CommentNode", Type, 16}, + {"CommentNode.NodeType", Field, 16}, + {"CommentNode.Pos", Field, 16}, + {"CommentNode.Text", Field, 16}, + {"ContinueNode", Type, 18}, + {"ContinueNode.Line", Field, 18}, + {"ContinueNode.NodeType", Field, 18}, + {"ContinueNode.Pos", Field, 18}, + {"DotNode", Type, 0}, + {"DotNode.NodeType", Field, 4}, + {"DotNode.Pos", Field, 1}, + {"FieldNode", Type, 0}, + {"FieldNode.Ident", Field, 0}, + {"FieldNode.NodeType", Field, 0}, + {"FieldNode.Pos", Field, 1}, + {"IdentifierNode", Type, 0}, + {"IdentifierNode.Ident", Field, 0}, + {"IdentifierNode.NodeType", Field, 0}, + {"IdentifierNode.Pos", Field, 1}, + {"IfNode", Type, 0}, + {"IfNode.BranchNode", Field, 0}, + {"IsEmptyTree", Func, 0}, + {"ListNode", Type, 0}, + {"ListNode.NodeType", Field, 0}, + {"ListNode.Nodes", Field, 0}, + {"ListNode.Pos", Field, 1}, + {"Mode", Type, 16}, + {"New", Func, 0}, + {"NewIdentifier", Func, 0}, + {"NilNode", Type, 1}, + {"NilNode.NodeType", Field, 4}, + {"NilNode.Pos", Field, 1}, + {"Node", Type, 0}, + {"NodeAction", Const, 0}, + {"NodeBool", Const, 0}, + {"NodeBreak", Const, 18}, + {"NodeChain", Const, 1}, + {"NodeCommand", Const, 0}, + {"NodeComment", Const, 16}, + {"NodeContinue", Const, 18}, + {"NodeDot", Const, 0}, + {"NodeField", Const, 0}, + {"NodeIdentifier", Const, 0}, + {"NodeIf", Const, 0}, + {"NodeList", Const, 0}, + {"NodeNil", Const, 1}, + {"NodeNumber", Const, 0}, + {"NodePipe", Const, 0}, + {"NodeRange", Const, 0}, + {"NodeString", Const, 0}, + {"NodeTemplate", Const, 0}, + {"NodeText", Const, 0}, + {"NodeType", Type, 0}, + {"NodeVariable", Const, 0}, + {"NodeWith", Const, 0}, + {"NumberNode", Type, 0}, + {"NumberNode.Complex128", Field, 0}, + {"NumberNode.Float64", Field, 0}, + {"NumberNode.Int64", Field, 0}, + {"NumberNode.IsComplex", Field, 0}, + {"NumberNode.IsFloat", Field, 0}, + {"NumberNode.IsInt", Field, 0}, + {"NumberNode.IsUint", Field, 0}, + {"NumberNode.NodeType", Field, 0}, + {"NumberNode.Pos", Field, 1}, + {"NumberNode.Text", Field, 0}, + {"NumberNode.Uint64", Field, 0}, + {"Parse", Func, 0}, + {"ParseComments", Const, 16}, + {"PipeNode", Type, 0}, + {"PipeNode.Cmds", Field, 0}, + {"PipeNode.Decl", Field, 0}, + {"PipeNode.IsAssign", Field, 11}, + {"PipeNode.Line", Field, 0}, + {"PipeNode.NodeType", Field, 0}, + {"PipeNode.Pos", Field, 1}, + {"Pos", Type, 1}, + {"RangeNode", Type, 0}, + {"RangeNode.BranchNode", Field, 0}, + {"SkipFuncCheck", Const, 17}, + {"StringNode", Type, 0}, + {"StringNode.NodeType", Field, 0}, + {"StringNode.Pos", Field, 1}, + {"StringNode.Quoted", Field, 0}, + {"StringNode.Text", Field, 0}, + {"TemplateNode", Type, 0}, + {"TemplateNode.Line", Field, 0}, + {"TemplateNode.Name", Field, 0}, + {"TemplateNode.NodeType", Field, 0}, + {"TemplateNode.Pipe", Field, 0}, + {"TemplateNode.Pos", Field, 1}, + {"TextNode", Type, 0}, + {"TextNode.NodeType", Field, 0}, + {"TextNode.Pos", Field, 1}, + {"TextNode.Text", Field, 0}, + {"Tree", Type, 0}, + {"Tree.Mode", Field, 16}, + {"Tree.Name", Field, 0}, + {"Tree.ParseName", Field, 1}, + {"Tree.Root", Field, 0}, + {"VariableNode", Type, 0}, + {"VariableNode.Ident", Field, 0}, + {"VariableNode.NodeType", Field, 0}, + {"VariableNode.Pos", Field, 1}, + {"WithNode", Type, 0}, + {"WithNode.BranchNode", Field, 0}, + }, + "time": { + {"(*Location).String", Method, 0}, + {"(*ParseError).Error", Method, 0}, + {"(*Ticker).Reset", Method, 15}, + {"(*Ticker).Stop", Method, 0}, + {"(*Time).GobDecode", Method, 0}, + {"(*Time).UnmarshalBinary", Method, 2}, + {"(*Time).UnmarshalJSON", Method, 0}, + {"(*Time).UnmarshalText", Method, 2}, + {"(*Timer).Reset", Method, 1}, + {"(*Timer).Stop", Method, 0}, + {"(Duration).Abs", Method, 19}, + {"(Duration).Hours", Method, 0}, + {"(Duration).Microseconds", Method, 13}, + {"(Duration).Milliseconds", Method, 13}, + {"(Duration).Minutes", Method, 0}, + {"(Duration).Nanoseconds", Method, 0}, + {"(Duration).Round", Method, 9}, + {"(Duration).Seconds", Method, 0}, + {"(Duration).String", Method, 0}, + {"(Duration).Truncate", Method, 9}, + {"(Month).String", Method, 0}, + {"(Time).Add", Method, 0}, + {"(Time).AddDate", Method, 0}, + {"(Time).After", Method, 0}, + {"(Time).AppendFormat", Method, 5}, + {"(Time).Before", Method, 0}, + {"(Time).Clock", Method, 0}, + {"(Time).Compare", Method, 20}, + {"(Time).Date", Method, 0}, + {"(Time).Day", Method, 0}, + {"(Time).Equal", Method, 0}, + {"(Time).Format", Method, 0}, + {"(Time).GoString", Method, 17}, + {"(Time).GobEncode", Method, 0}, + {"(Time).Hour", Method, 0}, + {"(Time).ISOWeek", Method, 0}, + {"(Time).In", Method, 0}, + {"(Time).IsDST", Method, 17}, + {"(Time).IsZero", Method, 0}, + {"(Time).Local", Method, 0}, + {"(Time).Location", Method, 0}, + {"(Time).MarshalBinary", Method, 2}, + {"(Time).MarshalJSON", Method, 0}, + {"(Time).MarshalText", Method, 2}, + {"(Time).Minute", Method, 0}, + {"(Time).Month", Method, 0}, + {"(Time).Nanosecond", Method, 0}, + {"(Time).Round", Method, 1}, + {"(Time).Second", Method, 0}, + {"(Time).String", Method, 0}, + {"(Time).Sub", Method, 0}, + {"(Time).Truncate", Method, 1}, + {"(Time).UTC", Method, 0}, + {"(Time).Unix", Method, 0}, + {"(Time).UnixMicro", Method, 17}, + {"(Time).UnixMilli", Method, 17}, + {"(Time).UnixNano", Method, 0}, + {"(Time).Weekday", Method, 0}, + {"(Time).Year", Method, 0}, + {"(Time).YearDay", Method, 1}, + {"(Time).Zone", Method, 0}, + {"(Time).ZoneBounds", Method, 19}, + {"(Weekday).String", Method, 0}, + {"ANSIC", Const, 0}, + {"After", Func, 0}, + {"AfterFunc", Func, 0}, + {"April", Const, 0}, + {"August", Const, 0}, + {"Date", Func, 0}, + {"DateOnly", Const, 20}, + {"DateTime", Const, 20}, + {"December", Const, 0}, + {"Duration", Type, 0}, + {"February", Const, 0}, + {"FixedZone", Func, 0}, + {"Friday", Const, 0}, + {"Hour", Const, 0}, + {"January", Const, 0}, + {"July", Const, 0}, + {"June", Const, 0}, + {"Kitchen", Const, 0}, + {"Layout", Const, 17}, + {"LoadLocation", Func, 0}, + {"LoadLocationFromTZData", Func, 10}, + {"Local", Var, 0}, + {"Location", Type, 0}, + {"March", Const, 0}, + {"May", Const, 0}, + {"Microsecond", Const, 0}, + {"Millisecond", Const, 0}, + {"Minute", Const, 0}, + {"Monday", Const, 0}, + {"Month", Type, 0}, + {"Nanosecond", Const, 0}, + {"NewTicker", Func, 0}, + {"NewTimer", Func, 0}, + {"November", Const, 0}, + {"Now", Func, 0}, + {"October", Const, 0}, + {"Parse", Func, 0}, + {"ParseDuration", Func, 0}, + {"ParseError", Type, 0}, + {"ParseError.Layout", Field, 0}, + {"ParseError.LayoutElem", Field, 0}, + {"ParseError.Message", Field, 0}, + {"ParseError.Value", Field, 0}, + {"ParseError.ValueElem", Field, 0}, + {"ParseInLocation", Func, 1}, + {"RFC1123", Const, 0}, + {"RFC1123Z", Const, 0}, + {"RFC3339", Const, 0}, + {"RFC3339Nano", Const, 0}, + {"RFC822", Const, 0}, + {"RFC822Z", Const, 0}, + {"RFC850", Const, 0}, + {"RubyDate", Const, 0}, + {"Saturday", Const, 0}, + {"Second", Const, 0}, + {"September", Const, 0}, + {"Since", Func, 0}, + {"Sleep", Func, 0}, + {"Stamp", Const, 0}, + {"StampMicro", Const, 0}, + {"StampMilli", Const, 0}, + {"StampNano", Const, 0}, + {"Sunday", Const, 0}, + {"Thursday", Const, 0}, + {"Tick", Func, 0}, + {"Ticker", Type, 0}, + {"Ticker.C", Field, 0}, + {"Time", Type, 0}, + {"TimeOnly", Const, 20}, + {"Timer", Type, 0}, + {"Timer.C", Field, 0}, + {"Tuesday", Const, 0}, + {"UTC", Var, 0}, + {"Unix", Func, 0}, + {"UnixDate", Const, 0}, + {"UnixMicro", Func, 17}, + {"UnixMilli", Func, 17}, + {"Until", Func, 8}, + {"Wednesday", Const, 0}, + {"Weekday", Type, 0}, + }, + "unicode": { + {"(SpecialCase).ToLower", Method, 0}, + {"(SpecialCase).ToTitle", Method, 0}, + {"(SpecialCase).ToUpper", Method, 0}, + {"ASCII_Hex_Digit", Var, 0}, + {"Adlam", Var, 7}, + {"Ahom", Var, 5}, + {"Anatolian_Hieroglyphs", Var, 5}, + {"Arabic", Var, 0}, + {"Armenian", Var, 0}, + {"Avestan", Var, 0}, + {"AzeriCase", Var, 0}, + {"Balinese", Var, 0}, + {"Bamum", Var, 0}, + {"Bassa_Vah", Var, 4}, + {"Batak", Var, 0}, + {"Bengali", Var, 0}, + {"Bhaiksuki", Var, 7}, + {"Bidi_Control", Var, 0}, + {"Bopomofo", Var, 0}, + {"Brahmi", Var, 0}, + {"Braille", Var, 0}, + {"Buginese", Var, 0}, + {"Buhid", Var, 0}, + {"C", Var, 0}, + {"Canadian_Aboriginal", Var, 0}, + {"Carian", Var, 0}, + {"CaseRange", Type, 0}, + {"CaseRange.Delta", Field, 0}, + {"CaseRange.Hi", Field, 0}, + {"CaseRange.Lo", Field, 0}, + {"CaseRanges", Var, 0}, + {"Categories", Var, 0}, + {"Caucasian_Albanian", Var, 4}, + {"Cc", Var, 0}, + {"Cf", Var, 0}, + {"Chakma", Var, 1}, + {"Cham", Var, 0}, + {"Cherokee", Var, 0}, + {"Chorasmian", Var, 16}, + {"Co", Var, 0}, + {"Common", Var, 0}, + {"Coptic", Var, 0}, + {"Cs", Var, 0}, + {"Cuneiform", Var, 0}, + {"Cypriot", Var, 0}, + {"Cypro_Minoan", Var, 21}, + {"Cyrillic", Var, 0}, + {"Dash", Var, 0}, + {"Deprecated", Var, 0}, + {"Deseret", Var, 0}, + {"Devanagari", Var, 0}, + {"Diacritic", Var, 0}, + {"Digit", Var, 0}, + {"Dives_Akuru", Var, 16}, + {"Dogra", Var, 13}, + {"Duployan", Var, 4}, + {"Egyptian_Hieroglyphs", Var, 0}, + {"Elbasan", Var, 4}, + {"Elymaic", Var, 14}, + {"Ethiopic", Var, 0}, + {"Extender", Var, 0}, + {"FoldCategory", Var, 0}, + {"FoldScript", Var, 0}, + {"Georgian", Var, 0}, + {"Glagolitic", Var, 0}, + {"Gothic", Var, 0}, + {"Grantha", Var, 4}, + {"GraphicRanges", Var, 0}, + {"Greek", Var, 0}, + {"Gujarati", Var, 0}, + {"Gunjala_Gondi", Var, 13}, + {"Gurmukhi", Var, 0}, + {"Han", Var, 0}, + {"Hangul", Var, 0}, + {"Hanifi_Rohingya", Var, 13}, + {"Hanunoo", Var, 0}, + {"Hatran", Var, 5}, + {"Hebrew", Var, 0}, + {"Hex_Digit", Var, 0}, + {"Hiragana", Var, 0}, + {"Hyphen", Var, 0}, + {"IDS_Binary_Operator", Var, 0}, + {"IDS_Trinary_Operator", Var, 0}, + {"Ideographic", Var, 0}, + {"Imperial_Aramaic", Var, 0}, + {"In", Func, 2}, + {"Inherited", Var, 0}, + {"Inscriptional_Pahlavi", Var, 0}, + {"Inscriptional_Parthian", Var, 0}, + {"Is", Func, 0}, + {"IsControl", Func, 0}, + {"IsDigit", Func, 0}, + {"IsGraphic", Func, 0}, + {"IsLetter", Func, 0}, + {"IsLower", Func, 0}, + {"IsMark", Func, 0}, + {"IsNumber", Func, 0}, + {"IsOneOf", Func, 0}, + {"IsPrint", Func, 0}, + {"IsPunct", Func, 0}, + {"IsSpace", Func, 0}, + {"IsSymbol", Func, 0}, + {"IsTitle", Func, 0}, + {"IsUpper", Func, 0}, + {"Javanese", Var, 0}, + {"Join_Control", Var, 0}, + {"Kaithi", Var, 0}, + {"Kannada", Var, 0}, + {"Katakana", Var, 0}, + {"Kawi", Var, 21}, + {"Kayah_Li", Var, 0}, + {"Kharoshthi", Var, 0}, + {"Khitan_Small_Script", Var, 16}, + {"Khmer", Var, 0}, + {"Khojki", Var, 4}, + {"Khudawadi", Var, 4}, + {"L", Var, 0}, + {"Lao", Var, 0}, + {"Latin", Var, 0}, + {"Lepcha", Var, 0}, + {"Letter", Var, 0}, + {"Limbu", Var, 0}, + {"Linear_A", Var, 4}, + {"Linear_B", Var, 0}, + {"Lisu", Var, 0}, + {"Ll", Var, 0}, + {"Lm", Var, 0}, + {"Lo", Var, 0}, + {"Logical_Order_Exception", Var, 0}, + {"Lower", Var, 0}, + {"LowerCase", Const, 0}, + {"Lt", Var, 0}, + {"Lu", Var, 0}, + {"Lycian", Var, 0}, + {"Lydian", Var, 0}, + {"M", Var, 0}, + {"Mahajani", Var, 4}, + {"Makasar", Var, 13}, + {"Malayalam", Var, 0}, + {"Mandaic", Var, 0}, + {"Manichaean", Var, 4}, + {"Marchen", Var, 7}, + {"Mark", Var, 0}, + {"Masaram_Gondi", Var, 10}, + {"MaxASCII", Const, 0}, + {"MaxCase", Const, 0}, + {"MaxLatin1", Const, 0}, + {"MaxRune", Const, 0}, + {"Mc", Var, 0}, + {"Me", Var, 0}, + {"Medefaidrin", Var, 13}, + {"Meetei_Mayek", Var, 0}, + {"Mende_Kikakui", Var, 4}, + {"Meroitic_Cursive", Var, 1}, + {"Meroitic_Hieroglyphs", Var, 1}, + {"Miao", Var, 1}, + {"Mn", Var, 0}, + {"Modi", Var, 4}, + {"Mongolian", Var, 0}, + {"Mro", Var, 4}, + {"Multani", Var, 5}, + {"Myanmar", Var, 0}, + {"N", Var, 0}, + {"Nabataean", Var, 4}, + {"Nag_Mundari", Var, 21}, + {"Nandinagari", Var, 14}, + {"Nd", Var, 0}, + {"New_Tai_Lue", Var, 0}, + {"Newa", Var, 7}, + {"Nko", Var, 0}, + {"Nl", Var, 0}, + {"No", Var, 0}, + {"Noncharacter_Code_Point", Var, 0}, + {"Number", Var, 0}, + {"Nushu", Var, 10}, + {"Nyiakeng_Puachue_Hmong", Var, 14}, + {"Ogham", Var, 0}, + {"Ol_Chiki", Var, 0}, + {"Old_Hungarian", Var, 5}, + {"Old_Italic", Var, 0}, + {"Old_North_Arabian", Var, 4}, + {"Old_Permic", Var, 4}, + {"Old_Persian", Var, 0}, + {"Old_Sogdian", Var, 13}, + {"Old_South_Arabian", Var, 0}, + {"Old_Turkic", Var, 0}, + {"Old_Uyghur", Var, 21}, + {"Oriya", Var, 0}, + {"Osage", Var, 7}, + {"Osmanya", Var, 0}, + {"Other", Var, 0}, + {"Other_Alphabetic", Var, 0}, + {"Other_Default_Ignorable_Code_Point", Var, 0}, + {"Other_Grapheme_Extend", Var, 0}, + {"Other_ID_Continue", Var, 0}, + {"Other_ID_Start", Var, 0}, + {"Other_Lowercase", Var, 0}, + {"Other_Math", Var, 0}, + {"Other_Uppercase", Var, 0}, + {"P", Var, 0}, + {"Pahawh_Hmong", Var, 4}, + {"Palmyrene", Var, 4}, + {"Pattern_Syntax", Var, 0}, + {"Pattern_White_Space", Var, 0}, + {"Pau_Cin_Hau", Var, 4}, + {"Pc", Var, 0}, + {"Pd", Var, 0}, + {"Pe", Var, 0}, + {"Pf", Var, 0}, + {"Phags_Pa", Var, 0}, + {"Phoenician", Var, 0}, + {"Pi", Var, 0}, + {"Po", Var, 0}, + {"Prepended_Concatenation_Mark", Var, 7}, + {"PrintRanges", Var, 0}, + {"Properties", Var, 0}, + {"Ps", Var, 0}, + {"Psalter_Pahlavi", Var, 4}, + {"Punct", Var, 0}, + {"Quotation_Mark", Var, 0}, + {"Radical", Var, 0}, + {"Range16", Type, 0}, + {"Range16.Hi", Field, 0}, + {"Range16.Lo", Field, 0}, + {"Range16.Stride", Field, 0}, + {"Range32", Type, 0}, + {"Range32.Hi", Field, 0}, + {"Range32.Lo", Field, 0}, + {"Range32.Stride", Field, 0}, + {"RangeTable", Type, 0}, + {"RangeTable.LatinOffset", Field, 1}, + {"RangeTable.R16", Field, 0}, + {"RangeTable.R32", Field, 0}, + {"Regional_Indicator", Var, 10}, + {"Rejang", Var, 0}, + {"ReplacementChar", Const, 0}, + {"Runic", Var, 0}, + {"S", Var, 0}, + {"STerm", Var, 0}, + {"Samaritan", Var, 0}, + {"Saurashtra", Var, 0}, + {"Sc", Var, 0}, + {"Scripts", Var, 0}, + {"Sentence_Terminal", Var, 7}, + {"Sharada", Var, 1}, + {"Shavian", Var, 0}, + {"Siddham", Var, 4}, + {"SignWriting", Var, 5}, + {"SimpleFold", Func, 0}, + {"Sinhala", Var, 0}, + {"Sk", Var, 0}, + {"Sm", Var, 0}, + {"So", Var, 0}, + {"Soft_Dotted", Var, 0}, + {"Sogdian", Var, 13}, + {"Sora_Sompeng", Var, 1}, + {"Soyombo", Var, 10}, + {"Space", Var, 0}, + {"SpecialCase", Type, 0}, + {"Sundanese", Var, 0}, + {"Syloti_Nagri", Var, 0}, + {"Symbol", Var, 0}, + {"Syriac", Var, 0}, + {"Tagalog", Var, 0}, + {"Tagbanwa", Var, 0}, + {"Tai_Le", Var, 0}, + {"Tai_Tham", Var, 0}, + {"Tai_Viet", Var, 0}, + {"Takri", Var, 1}, + {"Tamil", Var, 0}, + {"Tangsa", Var, 21}, + {"Tangut", Var, 7}, + {"Telugu", Var, 0}, + {"Terminal_Punctuation", Var, 0}, + {"Thaana", Var, 0}, + {"Thai", Var, 0}, + {"Tibetan", Var, 0}, + {"Tifinagh", Var, 0}, + {"Tirhuta", Var, 4}, + {"Title", Var, 0}, + {"TitleCase", Const, 0}, + {"To", Func, 0}, + {"ToLower", Func, 0}, + {"ToTitle", Func, 0}, + {"ToUpper", Func, 0}, + {"Toto", Var, 21}, + {"TurkishCase", Var, 0}, + {"Ugaritic", Var, 0}, + {"Unified_Ideograph", Var, 0}, + {"Upper", Var, 0}, + {"UpperCase", Const, 0}, + {"UpperLower", Const, 0}, + {"Vai", Var, 0}, + {"Variation_Selector", Var, 0}, + {"Version", Const, 0}, + {"Vithkuqi", Var, 21}, + {"Wancho", Var, 14}, + {"Warang_Citi", Var, 4}, + {"White_Space", Var, 0}, + {"Yezidi", Var, 16}, + {"Yi", Var, 0}, + {"Z", Var, 0}, + {"Zanabazar_Square", Var, 10}, + {"Zl", Var, 0}, + {"Zp", Var, 0}, + {"Zs", Var, 0}, + }, + "unicode/utf16": { + {"AppendRune", Func, 20}, + {"Decode", Func, 0}, + {"DecodeRune", Func, 0}, + {"Encode", Func, 0}, + {"EncodeRune", Func, 0}, + {"IsSurrogate", Func, 0}, + {"RuneLen", Func, 23}, + }, + "unicode/utf8": { + {"AppendRune", Func, 18}, + {"DecodeLastRune", Func, 0}, + {"DecodeLastRuneInString", Func, 0}, + {"DecodeRune", Func, 0}, + {"DecodeRuneInString", Func, 0}, + {"EncodeRune", Func, 0}, + {"FullRune", Func, 0}, + {"FullRuneInString", Func, 0}, + {"MaxRune", Const, 0}, + {"RuneCount", Func, 0}, + {"RuneCountInString", Func, 0}, + {"RuneError", Const, 0}, + {"RuneLen", Func, 0}, + {"RuneSelf", Const, 0}, + {"RuneStart", Func, 0}, + {"UTFMax", Const, 0}, + {"Valid", Func, 0}, + {"ValidRune", Func, 1}, + {"ValidString", Func, 0}, + }, + "unique": { + {"(Handle).Value", Method, 23}, + {"Handle", Type, 23}, + {"Make", Func, 23}, + }, + "unsafe": { + {"Add", Func, 0}, + {"Alignof", Func, 0}, + {"Offsetof", Func, 0}, + {"Pointer", Type, 0}, + {"Sizeof", Func, 0}, + {"Slice", Func, 0}, + {"SliceData", Func, 0}, + {"String", Func, 0}, + {"StringData", Func, 0}, + }, +} diff --git a/vendor/golang.org/x/tools/internal/stdlib/stdlib.go b/vendor/golang.org/x/tools/internal/stdlib/stdlib.go new file mode 100644 index 0000000000..98904017f2 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/stdlib/stdlib.go @@ -0,0 +1,97 @@ +// Copyright 2022 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. + +//go:generate go run generate.go + +// Package stdlib provides a table of all exported symbols in the +// standard library, along with the version at which they first +// appeared. +package stdlib + +import ( + "fmt" + "strings" +) + +type Symbol struct { + Name string + Kind Kind + Version Version // Go version that first included the symbol +} + +// A Kind indicates the kind of a symbol: +// function, variable, constant, type, and so on. +type Kind int8 + +const ( + Invalid Kind = iota // Example name: + Type // "Buffer" + Func // "Println" + Var // "EOF" + Const // "Pi" + Field // "Point.X" + Method // "(*Buffer).Grow" +) + +func (kind Kind) String() string { + return [...]string{ + Invalid: "invalid", + Type: "type", + Func: "func", + Var: "var", + Const: "const", + Field: "field", + Method: "method", + }[kind] +} + +// A Version represents a version of Go of the form "go1.%d". +type Version int8 + +// String returns a version string of the form "go1.23", without allocating. +func (v Version) String() string { return versions[v] } + +var versions [30]string // (increase constant as needed) + +func init() { + for i := range versions { + versions[i] = fmt.Sprintf("go1.%d", i) + } +} + +// HasPackage reports whether the specified package path is part of +// the standard library's public API. +func HasPackage(path string) bool { + _, ok := PackageSymbols[path] + return ok +} + +// SplitField splits the field symbol name into type and field +// components. It must be called only on Field symbols. +// +// Example: "File.Package" -> ("File", "Package") +func (sym *Symbol) SplitField() (typename, name string) { + if sym.Kind != Field { + panic("not a field") + } + typename, name, _ = strings.Cut(sym.Name, ".") + return +} + +// SplitMethod splits the method symbol name into pointer, receiver, +// and method components. It must be called only on Method symbols. +// +// Example: "(*Buffer).Grow" -> (true, "Buffer", "Grow") +func (sym *Symbol) SplitMethod() (ptr bool, recv, name string) { + if sym.Kind != Method { + panic("not a method") + } + recv, name, _ = strings.Cut(sym.Name, ".") + recv = recv[len("(") : len(recv)-len(")")] + ptr = recv[0] == '*' + if ptr { + recv = recv[len("*"):] + } + return +} diff --git a/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go b/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go index 7e638ec24f..ff9437a36c 100644 --- a/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go +++ b/vendor/golang.org/x/tools/internal/tokeninternal/tokeninternal.go @@ -34,30 +34,16 @@ func GetLines(file *token.File) []int { lines []int _ []struct{} } - type tokenFile118 struct { - _ *token.FileSet // deleted in go1.19 - tokenFile119 - } - - type uP = unsafe.Pointer - switch unsafe.Sizeof(*file) { - case unsafe.Sizeof(tokenFile118{}): - var ptr *tokenFile118 - *(*uP)(uP(&ptr)) = uP(file) - ptr.mu.Lock() - defer ptr.mu.Unlock() - return ptr.lines - case unsafe.Sizeof(tokenFile119{}): - var ptr *tokenFile119 - *(*uP)(uP(&ptr)) = uP(file) - ptr.mu.Lock() - defer ptr.mu.Unlock() - return ptr.lines - - default: + if unsafe.Sizeof(*file) != unsafe.Sizeof(tokenFile119{}) { panic("unexpected token.File size") } + var ptr *tokenFile119 + type uP = unsafe.Pointer + *(*uP)(uP(&ptr)) = uP(file) + ptr.mu.Lock() + defer ptr.mu.Unlock() + return ptr.lines } // AddExistingFiles adds the specified files to the FileSet if they diff --git a/vendor/golang.org/x/tools/internal/typeparams/common.go b/vendor/golang.org/x/tools/internal/typeparams/common.go deleted file mode 100644 index d0d0649fe2..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/common.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2021 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 typeparams contains common utilities for writing tools that interact -// with generic Go code, as introduced with Go 1.18. -// -// Many of the types and functions in this package are proxies for the new APIs -// introduced in the standard library with Go 1.18. For example, the -// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec -// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go -// versions older than 1.18 these helpers are implemented as stubs, allowing -// users of this package to write code that handles generic constructs inline, -// even if the Go version being used to compile does not support generics. -// -// Additionally, this package contains common utilities for working with the -// new generic constructs, to supplement the standard library APIs. Notably, -// the StructuralTerms API computes a minimal representation of the structural -// restrictions on a type parameter. -// -// An external version of these APIs is available in the -// golang.org/x/exp/typeparams module. -package typeparams - -import ( - "fmt" - "go/ast" - "go/token" - "go/types" -) - -// UnpackIndexExpr extracts data from AST nodes that represent index -// expressions. -// -// For an ast.IndexExpr, the resulting indices slice will contain exactly one -// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable -// number of index expressions. -// -// For nodes that don't represent index expressions, the first return value of -// UnpackIndexExpr will be nil. -func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) { - switch e := n.(type) { - case *ast.IndexExpr: - return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack - case *IndexListExpr: - return e.X, e.Lbrack, e.Indices, e.Rbrack - } - return nil, token.NoPos, nil, token.NoPos -} - -// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on -// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0 -// will panic. -func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr { - switch len(indices) { - case 0: - panic("empty indices") - case 1: - return &ast.IndexExpr{ - X: x, - Lbrack: lbrack, - Index: indices[0], - Rbrack: rbrack, - } - default: - return &IndexListExpr{ - X: x, - Lbrack: lbrack, - Indices: indices, - Rbrack: rbrack, - } - } -} - -// IsTypeParam reports whether t is a type parameter. -func IsTypeParam(t types.Type) bool { - _, ok := t.(*TypeParam) - return ok -} - -// OriginMethod returns the origin method associated with the method fn. -// For methods on a non-generic receiver base type, this is just -// fn. However, for methods with a generic receiver, OriginMethod returns the -// corresponding method in the method set of the origin type. -// -// As a special case, if fn is not a method (has no receiver), OriginMethod -// returns fn. -func OriginMethod(fn *types.Func) *types.Func { - recv := fn.Type().(*types.Signature).Recv() - if recv == nil { - return fn - } - base := recv.Type() - p, isPtr := base.(*types.Pointer) - if isPtr { - base = p.Elem() - } - named, isNamed := base.(*types.Named) - if !isNamed { - // Receiver is a *types.Interface. - return fn - } - if ForNamed(named).Len() == 0 { - // Receiver base has no type parameters, so we can avoid the lookup below. - return fn - } - orig := NamedTypeOrigin(named) - gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) - - // This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In: - // package p - // type T *int - // func (*T) f() {} - // LookupFieldOrMethod(T, true, p, f)=nil, but NewMethodSet(*T)={(*T).f}. - // Here we make them consistent by force. - // (The go/types bug is general, but this workaround is reached only - // for generic T thanks to the early return above.) - if gfn == nil { - mset := types.NewMethodSet(types.NewPointer(orig)) - for i := 0; i < mset.Len(); i++ { - m := mset.At(i) - if m.Obj().Id() == fn.Id() { - gfn = m.Obj() - break - } - } - } - - // In golang/go#61196, we observe another crash, this time inexplicable. - if gfn == nil { - panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods())) - } - - return gfn.(*types.Func) -} - -// GenericAssignableTo is a generalization of types.AssignableTo that -// implements the following rule for uninstantiated generic types: -// -// If V and T are generic named types, then V is considered assignable to T if, -// for every possible instantation of V[A_1, ..., A_N], the instantiation -// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N]. -// -// If T has structural constraints, they must be satisfied by V. -// -// For example, consider the following type declarations: -// -// type Interface[T any] interface { -// Accept(T) -// } -// -// type Container[T any] struct { -// Element T -// } -// -// func (c Container[T]) Accept(t T) { c.Element = t } -// -// In this case, GenericAssignableTo reports that instantiations of Container -// are assignable to the corresponding instantiation of Interface. -func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { - // If V and T are not both named, or do not have matching non-empty type - // parameter lists, fall back on types.AssignableTo. - - VN, Vnamed := V.(*types.Named) - TN, Tnamed := T.(*types.Named) - if !Vnamed || !Tnamed { - return types.AssignableTo(V, T) - } - - vtparams := ForNamed(VN) - ttparams := ForNamed(TN) - if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { - return types.AssignableTo(V, T) - } - - // V and T have the same (non-zero) number of type params. Instantiate both - // with the type parameters of V. This must always succeed for V, and will - // succeed for T if and only if the type set of each type parameter of V is a - // subset of the type set of the corresponding type parameter of T, meaning - // that every instantiation of V corresponds to a valid instantiation of T. - - // Minor optimization: ensure we share a context across the two - // instantiations below. - if ctxt == nil { - ctxt = NewContext() - } - - var targs []types.Type - for i := 0; i < vtparams.Len(); i++ { - targs = append(targs, vtparams.At(i)) - } - - vinst, err := Instantiate(ctxt, V, targs, true) - if err != nil { - panic("type parameters should satisfy their own constraints") - } - - tinst, err := Instantiate(ctxt, T, targs, true) - if err != nil { - return false - } - - return types.AssignableTo(vinst, tinst) -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/vendor/golang.org/x/tools/internal/typeparams/coretype.go deleted file mode 100644 index 71248209ee..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/coretype.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2022 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 typeparams - -import ( - "go/types" -) - -// CoreType returns the core type of T or nil if T does not have a core type. -// -// See https://go.dev/ref/spec#Core_types for the definition of a core type. -func CoreType(T types.Type) types.Type { - U := T.Underlying() - if _, ok := U.(*types.Interface); !ok { - return U // for non-interface types, - } - - terms, err := _NormalTerms(U) - if len(terms) == 0 || err != nil { - // len(terms) -> empty type set of interface. - // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set. - return nil // no core type. - } - - U = terms[0].Type().Underlying() - var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying()) - for identical = 1; identical < len(terms); identical++ { - if !types.Identical(U, terms[identical].Type().Underlying()) { - break - } - } - - if identical == len(terms) { - // https://go.dev/ref/spec#Core_types - // "There is a single type U which is the underlying type of all types in the type set of T" - return U - } - ch, ok := U.(*types.Chan) - if !ok { - return nil // no core type as identical < len(terms) and U is not a channel. - } - // https://go.dev/ref/spec#Core_types - // "the type chan E if T contains only bidirectional channels, or the type chan<- E or - // <-chan E depending on the direction of the directional channels present." - for chans := identical; chans < len(terms); chans++ { - curr, ok := terms[chans].Type().Underlying().(*types.Chan) - if !ok { - return nil - } - if !types.Identical(ch.Elem(), curr.Elem()) { - return nil // channel elements are not identical. - } - if ch.Dir() == types.SendRecv { - // ch is bidirectional. We can safely always use curr's direction. - ch = curr - } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() { - // ch and curr are not bidirectional and not the same direction. - return nil - } - } - return ch -} - -// _NormalTerms returns a slice of terms representing the normalized structural -// type restrictions of a type, if any. -// -// For all types other than *types.TypeParam, *types.Interface, and -// *types.Union, this is just a single term with Tilde() == false and -// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see -// below. -// -// Structural type restrictions of a type parameter are created via -// non-interface types embedded in its constraint interface (directly, or via a -// chain of interface embeddings). For example, in the declaration type -// T[P interface{~int; m()}] int the structural restriction of the type -// parameter P is ~int. -// -// With interface embedding and unions, the specification of structural type -// restrictions may be arbitrarily complex. For example, consider the -// following: -// -// type A interface{ ~string|~[]byte } -// -// type B interface{ int|string } -// -// type C interface { ~string|~int } -// -// type T[P interface{ A|B; C }] int -// -// In this example, the structural type restriction of P is ~string|int: A|B -// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, -// which when intersected with C (~string|~int) yields ~string|int. -// -// _NormalTerms computes these expansions and reductions, producing a -// "normalized" form of the embeddings. A structural restriction is normalized -// if it is a single union containing no interface terms, and is minimal in the -// sense that removing any term changes the set of types satisfying the -// constraint. It is left as a proof for the reader that, modulo sorting, there -// is exactly one such normalized form. -// -// Because the minimal representation always takes this form, _NormalTerms -// returns a slice of tilde terms corresponding to the terms of the union in -// the normalized structural restriction. An error is returned if the type is -// invalid, exceeds complexity bounds, or has an empty type set. In the latter -// case, _NormalTerms returns ErrEmptyTypeSet. -// -// _NormalTerms makes no guarantees about the order of terms, except that it -// is deterministic. -func _NormalTerms(typ types.Type) ([]*Term, error) { - switch typ := typ.(type) { - case *TypeParam: - return StructuralTerms(typ) - case *Union: - return UnionTermSet(typ) - case *types.Interface: - return InterfaceTermSet(typ) - default: - return []*Term{NewTerm(false, typ)}, nil - } -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go b/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go deleted file mode 100644 index 18212390e1..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2021 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. - -//go:build !go1.18 -// +build !go1.18 - -package typeparams - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = false diff --git a/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go b/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go deleted file mode 100644 index d67148823c..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2021 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. - -//go:build go1.18 -// +build go1.18 - -package typeparams - -// Note: this constant is in a separate file as this is the only acceptable -// diff between the <1.18 API of this package and the 1.18 API. - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = true diff --git a/vendor/golang.org/x/tools/internal/typeparams/normalize.go b/vendor/golang.org/x/tools/internal/typeparams/normalize.go deleted file mode 100644 index 9c631b6512..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/normalize.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2021 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 typeparams - -import ( - "errors" - "fmt" - "go/types" - "os" - "strings" -) - -//go:generate go run copytermlist.go - -const debug = false - -var ErrEmptyTypeSet = errors.New("empty type set") - -// StructuralTerms returns a slice of terms representing the normalized -// structural type restrictions of a type parameter, if any. -// -// Structural type restrictions of a type parameter are created via -// non-interface types embedded in its constraint interface (directly, or via a -// chain of interface embeddings). For example, in the declaration -// -// type T[P interface{~int; m()}] int -// -// the structural restriction of the type parameter P is ~int. -// -// With interface embedding and unions, the specification of structural type -// restrictions may be arbitrarily complex. For example, consider the -// following: -// -// type A interface{ ~string|~[]byte } -// -// type B interface{ int|string } -// -// type C interface { ~string|~int } -// -// type T[P interface{ A|B; C }] int -// -// In this example, the structural type restriction of P is ~string|int: A|B -// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, -// which when intersected with C (~string|~int) yields ~string|int. -// -// StructuralTerms computes these expansions and reductions, producing a -// "normalized" form of the embeddings. A structural restriction is normalized -// if it is a single union containing no interface terms, and is minimal in the -// sense that removing any term changes the set of types satisfying the -// constraint. It is left as a proof for the reader that, modulo sorting, there -// is exactly one such normalized form. -// -// Because the minimal representation always takes this form, StructuralTerms -// returns a slice of tilde terms corresponding to the terms of the union in -// the normalized structural restriction. An error is returned if the -// constraint interface is invalid, exceeds complexity bounds, or has an empty -// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet. -// -// StructuralTerms makes no guarantees about the order of terms, except that it -// is deterministic. -func StructuralTerms(tparam *TypeParam) ([]*Term, error) { - constraint := tparam.Constraint() - if constraint == nil { - return nil, fmt.Errorf("%s has nil constraint", tparam) - } - iface, _ := constraint.Underlying().(*types.Interface) - if iface == nil { - return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying()) - } - return InterfaceTermSet(iface) -} - -// InterfaceTermSet computes the normalized terms for a constraint interface, -// returning an error if the term set cannot be computed or is empty. In the -// latter case, the error will be ErrEmptyTypeSet. -// -// See the documentation of StructuralTerms for more information on -// normalization. -func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { - return computeTermSet(iface) -} - -// UnionTermSet computes the normalized terms for a union, returning an error -// if the term set cannot be computed or is empty. In the latter case, the -// error will be ErrEmptyTypeSet. -// -// See the documentation of StructuralTerms for more information on -// normalization. -func UnionTermSet(union *Union) ([]*Term, error) { - return computeTermSet(union) -} - -func computeTermSet(typ types.Type) ([]*Term, error) { - tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) - if err != nil { - return nil, err - } - if tset.terms.isEmpty() { - return nil, ErrEmptyTypeSet - } - if tset.terms.isAll() { - return nil, nil - } - var terms []*Term - for _, term := range tset.terms { - terms = append(terms, NewTerm(term.tilde, term.typ)) - } - return terms, nil -} - -// A termSet holds the normalized set of terms for a given type. -// -// The name termSet is intentionally distinct from 'type set': a type set is -// all types that implement a type (and includes method restrictions), whereas -// a term set just represents the structural restrictions on a type. -type termSet struct { - complete bool - terms termlist -} - -func indentf(depth int, format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...) -} - -func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) { - if t == nil { - panic("nil type") - } - - if debug { - indentf(depth, "%s", t.String()) - defer func() { - if err != nil { - indentf(depth, "=> %s", err) - } else { - indentf(depth, "=> %s", res.terms.String()) - } - }() - } - - const maxTermCount = 100 - if tset, ok := seen[t]; ok { - if !tset.complete { - return nil, fmt.Errorf("cycle detected in the declaration of %s", t) - } - return tset, nil - } - - // Mark the current type as seen to avoid infinite recursion. - tset := new(termSet) - defer func() { - tset.complete = true - }() - seen[t] = tset - - switch u := t.Underlying().(type) { - case *types.Interface: - // The term set of an interface is the intersection of the term sets of its - // embedded types. - tset.terms = allTermlist - for i := 0; i < u.NumEmbeddeds(); i++ { - embedded := u.EmbeddedType(i) - if _, ok := embedded.Underlying().(*TypeParam); ok { - return nil, fmt.Errorf("invalid embedded type %T", embedded) - } - tset2, err := computeTermSetInternal(embedded, seen, depth+1) - if err != nil { - return nil, err - } - tset.terms = tset.terms.intersect(tset2.terms) - } - case *Union: - // The term set of a union is the union of term sets of its terms. - tset.terms = nil - for i := 0; i < u.Len(); i++ { - t := u.Term(i) - var terms termlist - switch t.Type().Underlying().(type) { - case *types.Interface: - tset2, err := computeTermSetInternal(t.Type(), seen, depth+1) - if err != nil { - return nil, err - } - terms = tset2.terms - case *TypeParam, *Union: - // A stand-alone type parameter or union is not permitted as union - // term. - return nil, fmt.Errorf("invalid union term %T", t) - default: - if t.Type() == types.Typ[types.Invalid] { - continue - } - terms = termlist{{t.Tilde(), t.Type()}} - } - tset.terms = tset.terms.union(terms) - if len(tset.terms) > maxTermCount { - return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) - } - } - case *TypeParam: - panic("unreachable") - default: - // For all other types, the term set is just a single non-tilde term - // holding the type itself. - if u != types.Typ[types.Invalid] { - tset.terms = termlist{{false, t}} - } - } - return tset, nil -} - -// under is a facade for the go/types internal function of the same name. It is -// used by typeterm.go. -func under(t types.Type) types.Type { - return t.Underlying() -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/termlist.go b/vendor/golang.org/x/tools/internal/typeparams/termlist.go deleted file mode 100644 index cbd12f8013..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/termlist.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2021 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. - -// Code generated by copytermlist.go DO NOT EDIT. - -package typeparams - -import ( - "bytes" - "go/types" -) - -// A termlist represents the type set represented by the union -// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn. -// A termlist is in normal form if all terms are disjoint. -// termlist operations don't require the operands to be in -// normal form. -type termlist []*term - -// allTermlist represents the set of all types. -// It is in normal form. -var allTermlist = termlist{new(term)} - -// String prints the termlist exactly (without normalization). -func (xl termlist) String() string { - if len(xl) == 0 { - return "∅" - } - var buf bytes.Buffer - for i, x := range xl { - if i > 0 { - buf.WriteString(" | ") - } - buf.WriteString(x.String()) - } - return buf.String() -} - -// isEmpty reports whether the termlist xl represents the empty set of types. -func (xl termlist) isEmpty() bool { - // If there's a non-nil term, the entire list is not empty. - // If the termlist is in normal form, this requires at most - // one iteration. - for _, x := range xl { - if x != nil { - return false - } - } - return true -} - -// isAll reports whether the termlist xl represents the set of all types. -func (xl termlist) isAll() bool { - // If there's a 𝓤 term, the entire list is 𝓤. - // If the termlist is in normal form, this requires at most - // one iteration. - for _, x := range xl { - if x != nil && x.typ == nil { - return true - } - } - return false -} - -// norm returns the normal form of xl. -func (xl termlist) norm() termlist { - // Quadratic algorithm, but good enough for now. - // TODO(gri) fix asymptotic performance - used := make([]bool, len(xl)) - var rl termlist - for i, xi := range xl { - if xi == nil || used[i] { - continue - } - for j := i + 1; j < len(xl); j++ { - xj := xl[j] - if xj == nil || used[j] { - continue - } - if u1, u2 := xi.union(xj); u2 == nil { - // If we encounter a 𝓤 term, the entire list is 𝓤. - // Exit early. - // (Note that this is not just an optimization; - // if we continue, we may end up with a 𝓤 term - // and other terms and the result would not be - // in normal form.) - if u1.typ == nil { - return allTermlist - } - xi = u1 - used[j] = true // xj is now unioned into xi - ignore it in future iterations - } - } - rl = append(rl, xi) - } - return rl -} - -// union returns the union xl ∪ yl. -func (xl termlist) union(yl termlist) termlist { - return append(xl, yl...).norm() -} - -// intersect returns the intersection xl ∩ yl. -func (xl termlist) intersect(yl termlist) termlist { - if xl.isEmpty() || yl.isEmpty() { - return nil - } - - // Quadratic algorithm, but good enough for now. - // TODO(gri) fix asymptotic performance - var rl termlist - for _, x := range xl { - for _, y := range yl { - if r := x.intersect(y); r != nil { - rl = append(rl, r) - } - } - } - return rl.norm() -} - -// equal reports whether xl and yl represent the same type set. -func (xl termlist) equal(yl termlist) bool { - // TODO(gri) this should be more efficient - return xl.subsetOf(yl) && yl.subsetOf(xl) -} - -// includes reports whether t ∈ xl. -func (xl termlist) includes(t types.Type) bool { - for _, x := range xl { - if x.includes(t) { - return true - } - } - return false -} - -// supersetOf reports whether y ⊆ xl. -func (xl termlist) supersetOf(y *term) bool { - for _, x := range xl { - if y.subsetOf(x) { - return true - } - } - return false -} - -// subsetOf reports whether xl ⊆ yl. -func (xl termlist) subsetOf(yl termlist) bool { - if yl.isEmpty() { - return xl.isEmpty() - } - - // each term x of xl must be a subset of yl - for _, x := range xl { - if !yl.supersetOf(x) { - return false // x is not a subset yl - } - } - return true -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go deleted file mode 100644 index 7ed86e1711..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2021 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. - -//go:build !go1.18 -// +build !go1.18 - -package typeparams - -import ( - "go/ast" - "go/token" - "go/types" -) - -func unsupported() { - panic("type parameters are unsupported at this go version") -} - -// IndexListExpr is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type IndexListExpr struct { - ast.Expr - X ast.Expr // expression - Lbrack token.Pos // position of "[" - Indices []ast.Expr // index expressions - Rbrack token.Pos // position of "]" -} - -// ForTypeSpec returns an empty field list, as type parameters on not supported -// at this Go version. -func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { - return nil -} - -// ForFuncType returns an empty field list, as type parameters are not -// supported at this Go version. -func ForFuncType(*ast.FuncType) *ast.FieldList { - return nil -} - -// TypeParam is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type TypeParam struct{ types.Type } - -func (*TypeParam) Index() int { unsupported(); return 0 } -func (*TypeParam) Constraint() types.Type { unsupported(); return nil } -func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil } - -// TypeParamList is a placeholder for an empty type parameter list. -type TypeParamList struct{} - -func (*TypeParamList) Len() int { return 0 } -func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } - -// TypeList is a placeholder for an empty type list. -type TypeList struct{} - -func (*TypeList) Len() int { return 0 } -func (*TypeList) At(int) types.Type { unsupported(); return nil } - -// NewTypeParam is unsupported at this Go version, and panics. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - unsupported() - return nil -} - -// SetTypeParamConstraint is unsupported at this Go version, and panics. -func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { - unsupported() -} - -// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or -// typeParams is non-empty. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - if len(recvTypeParams) != 0 || len(typeParams) != 0 { - panic("signatures cannot have type parameters at this Go version") - } - return types.NewSignature(recv, params, results, variadic) -} - -// ForSignature returns an empty slice. -func ForSignature(*types.Signature) *TypeParamList { - return nil -} - -// RecvTypeParams returns a nil slice. -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return nil -} - -// IsComparable returns false, as no interfaces are type-restricted at this Go -// version. -func IsComparable(*types.Interface) bool { - return false -} - -// IsMethodSet returns true, as no interfaces are type-restricted at this Go -// version. -func IsMethodSet(*types.Interface) bool { - return true -} - -// IsImplicit returns false, as no interfaces are implicit at this Go version. -func IsImplicit(*types.Interface) bool { - return false -} - -// MarkImplicit does nothing, because this Go version does not have implicit -// interfaces. -func MarkImplicit(*types.Interface) {} - -// ForNamed returns an empty type parameter list, as type parameters are not -// supported at this Go version. -func ForNamed(*types.Named) *TypeParamList { - return nil -} - -// SetForNamed panics if tparams is non-empty. -func SetForNamed(_ *types.Named, tparams []*TypeParam) { - if len(tparams) > 0 { - unsupported() - } -} - -// NamedTypeArgs returns nil. -func NamedTypeArgs(*types.Named) *TypeList { - return nil -} - -// NamedTypeOrigin is the identity method at this Go version. -func NamedTypeOrigin(named *types.Named) *types.Named { - return named -} - -// Term holds information about a structural type restriction. -type Term struct { - tilde bool - typ types.Type -} - -func (m *Term) Tilde() bool { return m.tilde } -func (m *Term) Type() types.Type { return m.typ } -func (m *Term) String() string { - pre := "" - if m.tilde { - pre = "~" - } - return pre + m.typ.String() -} - -// NewTerm is unsupported at this Go version, and panics. -func NewTerm(tilde bool, typ types.Type) *Term { - return &Term{tilde, typ} -} - -// Union is a placeholder type, as type parameters are not supported at this Go -// version. Its methods panic on use. -type Union struct{ types.Type } - -func (*Union) Len() int { return 0 } -func (*Union) Term(i int) *Term { unsupported(); return nil } - -// NewUnion is unsupported at this Go version, and panics. -func NewUnion(terms []*Term) *Union { - unsupported() - return nil -} - -// InitInstanceInfo is a noop at this Go version. -func InitInstanceInfo(*types.Info) {} - -// Instance is a placeholder type, as type parameters are not supported at this -// Go version. -type Instance struct { - TypeArgs *TypeList - Type types.Type -} - -// GetInstances returns a nil map, as type parameters are not supported at this -// Go version. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil } - -// Context is a placeholder type, as type parameters are not supported at -// this Go version. -type Context struct{} - -// NewContext returns a placeholder Context instance. -func NewContext() *Context { - return &Context{} -} - -// Instantiate is unsupported on this Go version, and panics. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - unsupported() - return nil, nil -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go b/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go deleted file mode 100644 index cf301af1db..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2021 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. - -//go:build go1.18 -// +build go1.18 - -package typeparams - -import ( - "go/ast" - "go/types" -) - -// IndexListExpr is an alias for ast.IndexListExpr. -type IndexListExpr = ast.IndexListExpr - -// ForTypeSpec returns n.TypeParams. -func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList { - if n == nil { - return nil - } - return n.TypeParams -} - -// ForFuncType returns n.TypeParams. -func ForFuncType(n *ast.FuncType) *ast.FieldList { - if n == nil { - return nil - } - return n.TypeParams -} - -// TypeParam is an alias for types.TypeParam -type TypeParam = types.TypeParam - -// TypeParamList is an alias for types.TypeParamList -type TypeParamList = types.TypeParamList - -// TypeList is an alias for types.TypeList -type TypeList = types.TypeList - -// NewTypeParam calls types.NewTypeParam. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - return types.NewTypeParam(name, constraint) -} - -// SetTypeParamConstraint calls tparam.SetConstraint(constraint). -func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { - tparam.SetConstraint(constraint) -} - -// NewSignatureType calls types.NewSignatureType. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic) -} - -// ForSignature returns sig.TypeParams() -func ForSignature(sig *types.Signature) *TypeParamList { - return sig.TypeParams() -} - -// RecvTypeParams returns sig.RecvTypeParams(). -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return sig.RecvTypeParams() -} - -// IsComparable calls iface.IsComparable(). -func IsComparable(iface *types.Interface) bool { - return iface.IsComparable() -} - -// IsMethodSet calls iface.IsMethodSet(). -func IsMethodSet(iface *types.Interface) bool { - return iface.IsMethodSet() -} - -// IsImplicit calls iface.IsImplicit(). -func IsImplicit(iface *types.Interface) bool { - return iface.IsImplicit() -} - -// MarkImplicit calls iface.MarkImplicit(). -func MarkImplicit(iface *types.Interface) { - iface.MarkImplicit() -} - -// ForNamed extracts the (possibly empty) type parameter object list from -// named. -func ForNamed(named *types.Named) *TypeParamList { - return named.TypeParams() -} - -// SetForNamed sets the type params tparams on n. Each tparam must be of -// dynamic type *types.TypeParam. -func SetForNamed(n *types.Named, tparams []*TypeParam) { - n.SetTypeParams(tparams) -} - -// NamedTypeArgs returns named.TypeArgs(). -func NamedTypeArgs(named *types.Named) *TypeList { - return named.TypeArgs() -} - -// NamedTypeOrigin returns named.Orig(). -func NamedTypeOrigin(named *types.Named) *types.Named { - return named.Origin() -} - -// Term is an alias for types.Term. -type Term = types.Term - -// NewTerm calls types.NewTerm. -func NewTerm(tilde bool, typ types.Type) *Term { - return types.NewTerm(tilde, typ) -} - -// Union is an alias for types.Union -type Union = types.Union - -// NewUnion calls types.NewUnion. -func NewUnion(terms []*Term) *Union { - return types.NewUnion(terms) -} - -// InitInstanceInfo initializes info to record information about type and -// function instances. -func InitInstanceInfo(info *types.Info) { - info.Instances = make(map[*ast.Ident]types.Instance) -} - -// Instance is an alias for types.Instance. -type Instance = types.Instance - -// GetInstances returns info.Instances. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { - return info.Instances -} - -// Context is an alias for types.Context. -type Context = types.Context - -// NewContext calls types.NewContext. -func NewContext() *Context { - return types.NewContext() -} - -// Instantiate calls types.Instantiate. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - return types.Instantiate(ctxt, typ, targs, validate) -} diff --git a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go b/vendor/golang.org/x/tools/internal/typeparams/typeterm.go deleted file mode 100644 index 7350bb702a..0000000000 --- a/vendor/golang.org/x/tools/internal/typeparams/typeterm.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2021 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. - -// Code generated by copytermlist.go DO NOT EDIT. - -package typeparams - -import "go/types" - -// A term describes elementary type sets: -// -// ∅: (*term)(nil) == ∅ // set of no types (empty set) -// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) -// T: &term{false, T} == {T} // set of type T -// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t -type term struct { - tilde bool // valid if typ != nil - typ types.Type -} - -func (x *term) String() string { - switch { - case x == nil: - return "∅" - case x.typ == nil: - return "𝓤" - case x.tilde: - return "~" + x.typ.String() - default: - return x.typ.String() - } -} - -// equal reports whether x and y represent the same type set. -func (x *term) equal(y *term) bool { - // easy cases - switch { - case x == nil || y == nil: - return x == y - case x.typ == nil || y.typ == nil: - return x.typ == y.typ - } - // ∅ ⊂ x, y ⊂ 𝓤 - - return x.tilde == y.tilde && types.Identical(x.typ, y.typ) -} - -// union returns the union x ∪ y: zero, one, or two non-nil terms. -func (x *term) union(y *term) (_, _ *term) { - // easy cases - switch { - case x == nil && y == nil: - return nil, nil // ∅ ∪ ∅ == ∅ - case x == nil: - return y, nil // ∅ ∪ y == y - case y == nil: - return x, nil // x ∪ ∅ == x - case x.typ == nil: - return x, nil // 𝓤 ∪ y == 𝓤 - case y.typ == nil: - return y, nil // x ∪ 𝓤 == 𝓤 - } - // ∅ ⊂ x, y ⊂ 𝓤 - - if x.disjoint(y) { - return x, y // x ∪ y == (x, y) if x ∩ y == ∅ - } - // x.typ == y.typ - - // ~t ∪ ~t == ~t - // ~t ∪ T == ~t - // T ∪ ~t == ~t - // T ∪ T == T - if x.tilde || !y.tilde { - return x, nil - } - return y, nil -} - -// intersect returns the intersection x ∩ y. -func (x *term) intersect(y *term) *term { - // easy cases - switch { - case x == nil || y == nil: - return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅ - case x.typ == nil: - return y // 𝓤 ∩ y == y - case y.typ == nil: - return x // x ∩ 𝓤 == x - } - // ∅ ⊂ x, y ⊂ 𝓤 - - if x.disjoint(y) { - return nil // x ∩ y == ∅ if x ∩ y == ∅ - } - // x.typ == y.typ - - // ~t ∩ ~t == ~t - // ~t ∩ T == T - // T ∩ ~t == T - // T ∩ T == T - if !x.tilde || y.tilde { - return x - } - return y -} - -// includes reports whether t ∈ x. -func (x *term) includes(t types.Type) bool { - // easy cases - switch { - case x == nil: - return false // t ∈ ∅ == false - case x.typ == nil: - return true // t ∈ 𝓤 == true - } - // ∅ ⊂ x ⊂ 𝓤 - - u := t - if x.tilde { - u = under(u) - } - return types.Identical(x.typ, u) -} - -// subsetOf reports whether x ⊆ y. -func (x *term) subsetOf(y *term) bool { - // easy cases - switch { - case x == nil: - return true // ∅ ⊆ y == true - case y == nil: - return false // x ⊆ ∅ == false since x != ∅ - case y.typ == nil: - return true // x ⊆ 𝓤 == true - case x.typ == nil: - return false // 𝓤 ⊆ y == false since y != 𝓤 - } - // ∅ ⊂ x, y ⊂ 𝓤 - - if x.disjoint(y) { - return false // x ⊆ y == false if x ∩ y == ∅ - } - // x.typ == y.typ - - // ~t ⊆ ~t == true - // ~t ⊆ T == false - // T ⊆ ~t == true - // T ⊆ T == true - return !x.tilde || y.tilde -} - -// disjoint reports whether x ∩ y == ∅. -// x.typ and y.typ must not be nil. -func (x *term) disjoint(y *term) bool { - if debug && (x.typ == nil || y.typ == nil) { - panic("invalid argument(s)") - } - ux := x.typ - if y.tilde { - ux = under(ux) - } - uy := y.typ - if x.tilde { - uy = under(uy) - } - return !types.Identical(ux, uy) -} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go b/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go index 07484073a5..131caab284 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go @@ -167,7 +167,7 @@ const ( UntypedNilUse // WrongAssignCount occurs when the number of values on the right-hand side - // of an assignment or or initialization expression does not match the number + // of an assignment or initialization expression does not match the number // of variables on the left-hand side. // // Example: @@ -838,7 +838,7 @@ const ( // InvalidCap occurs when an argument to the cap built-in function is not of // supported type. // - // See https://golang.org/ref/spec#Lengthand_capacity for information on + // See https://golang.org/ref/spec#Length_and_capacity for information on // which underlying types are supported as arguments to cap and len. // // Example: @@ -859,7 +859,7 @@ const ( // InvalidCopy occurs when the arguments are not of slice type or do not // have compatible type. // - // See https://golang.org/ref/spec#Appendingand_copying_slices for more + // See https://golang.org/ref/spec#Appending_and_copying_slices for more // information on the type requirements for the copy built-in. // // Example: @@ -897,7 +897,7 @@ const ( // InvalidLen occurs when an argument to the len built-in function is not of // supported type. // - // See https://golang.org/ref/spec#Lengthand_capacity for information on + // See https://golang.org/ref/spec#Length_and_capacity for information on // which underlying types are supported as arguments to cap and len. // // Example: @@ -914,7 +914,7 @@ const ( // InvalidMake occurs when make is called with an unsupported type argument. // - // See https://golang.org/ref/spec#Makingslices_maps_and_channels for + // See https://golang.org/ref/spec#Making_slices_maps_and_channels for // information on the types that may be created using make. // // Example: @@ -1449,10 +1449,10 @@ const ( NotAGenericType // WrongTypeArgCount occurs when a type or function is instantiated with an - // incorrent number of type arguments, including when a generic type or + // incorrect number of type arguments, including when a generic type or // function is used without instantiation. // - // Errors inolving failed type inference are assigned other error codes. + // Errors involving failed type inference are assigned other error codes. // // Example: // type T[p any] int diff --git a/vendor/golang.org/x/tools/internal/typesinternal/objectpath.go b/vendor/golang.org/x/tools/internal/typesinternal/objectpath.go deleted file mode 100644 index 5e96e89557..0000000000 --- a/vendor/golang.org/x/tools/internal/typesinternal/objectpath.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2023 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 typesinternal - -import "go/types" - -// This file contains back doors that allow gopls to avoid method sorting when -// using the objectpath package. -// -// This is performance-critical in certain repositories, but changing the -// behavior of the objectpath package is still being discussed in -// golang/go#61443. If we decide to remove the sorting in objectpath we can -// simply delete these back doors. Otherwise, we should add a new API to -// objectpath that allows controlling the sorting. - -// SkipEncoderMethodSorting marks enc (which must be an *objectpath.Encoder) as -// not requiring sorted methods. -var SkipEncoderMethodSorting func(enc interface{}) - -// ObjectpathObject is like objectpath.Object, but allows suppressing method -// sorting. -var ObjectpathObject func(pkg *types.Package, p string, skipMethodSorting bool) (types.Object, error) diff --git a/vendor/golang.org/x/tools/internal/typesinternal/recv.go b/vendor/golang.org/x/tools/internal/typesinternal/recv.go new file mode 100644 index 0000000000..fea7c8b75e --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typesinternal/recv.go @@ -0,0 +1,43 @@ +// 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 typesinternal + +import ( + "go/types" + + "golang.org/x/tools/internal/aliases" +) + +// ReceiverNamed returns the named type (if any) associated with the +// type of recv, which may be of the form N or *N, or aliases thereof. +// It also reports whether a Pointer was present. +func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) { + t := recv.Type() + if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok { + isPtr = true + t = ptr.Elem() + } + named, _ = aliases.Unalias(t).(*types.Named) + return +} + +// Unpointer returns T given *T or an alias thereof. +// For all other types it is the identity function. +// It does not look at underlying types. +// The result may be an alias. +// +// Use this function to strip off the optional pointer on a receiver +// in a field or method selection, without losing the named type +// (which is needed to compute the method set). +// +// See also [typeparams.MustDeref], which removes one level of +// indirection from the type, regardless of named types (analogous to +// a LOAD instruction). +func Unpointer(t types.Type) types.Type { + if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok { + return ptr.Elem() + } + return t +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/toonew.go b/vendor/golang.org/x/tools/internal/typesinternal/toonew.go new file mode 100644 index 0000000000..cc86487eaa --- /dev/null +++ b/vendor/golang.org/x/tools/internal/typesinternal/toonew.go @@ -0,0 +1,89 @@ +// 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 typesinternal + +import ( + "go/types" + + "golang.org/x/tools/internal/stdlib" + "golang.org/x/tools/internal/versions" +) + +// TooNewStdSymbols computes the set of package-level symbols +// exported by pkg that are not available at the specified version. +// The result maps each symbol to its minimum version. +// +// The pkg is allowed to contain type errors. +func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]string { + disallowed := make(map[types.Object]string) + + // Pass 1: package-level symbols. + symbols := stdlib.PackageSymbols[pkg.Path()] + for _, sym := range symbols { + symver := sym.Version.String() + if versions.Before(version, symver) { + switch sym.Kind { + case stdlib.Func, stdlib.Var, stdlib.Const, stdlib.Type: + disallowed[pkg.Scope().Lookup(sym.Name)] = symver + } + } + } + + // Pass 2: fields and methods. + // + // We allow fields and methods if their associated type is + // disallowed, as otherwise we would report false positives + // for compatibility shims. Consider: + // + // //go:build go1.22 + // type T struct { F std.Real } // correct new API + // + // //go:build !go1.22 + // type T struct { F fake } // shim + // type fake struct { ... } + // func (fake) M () {} + // + // These alternative declarations of T use either the std.Real + // type, introduced in go1.22, or a fake type, for the field + // F. (The fakery could be arbitrarily deep, involving more + // nested fields and methods than are shown here.) Clients + // that use the compatibility shim T will compile with any + // version of go, whether older or newer than go1.22, but only + // the newer version will use the std.Real implementation. + // + // Now consider a reference to method M in new(T).F.M() in a + // module that requires a minimum of go1.21. The analysis may + // occur using a version of Go higher than 1.21, selecting the + // first version of T, so the method M is Real.M. This would + // spuriously cause the analyzer to report a reference to a + // too-new symbol even though this expression compiles just + // fine (with the fake implementation) using go1.21. + for _, sym := range symbols { + symVersion := sym.Version.String() + if !versions.Before(version, symVersion) { + continue // allowed + } + + var obj types.Object + switch sym.Kind { + case stdlib.Field: + typename, name := sym.SplitField() + if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == "" { + obj, _, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name) + } + + case stdlib.Method: + ptr, recvname, name := sym.SplitMethod() + if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == "" { + obj, _, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name) + } + } + if obj != nil { + disallowed[obj] = symVersion + } + } + + return disallowed +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types.go b/vendor/golang.org/x/tools/internal/typesinternal/types.go index ce7d4351b2..8392328612 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/types.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/types.go @@ -49,4 +49,17 @@ func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos, return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true } -var SetGoVersion = func(conf *types.Config, version string) bool { return false } +// NameRelativeTo returns a types.Qualifier that qualifies members of +// all packages other than pkg, using only the package name. +// (By contrast, [types.RelativeTo] uses the complete package path, +// which is often excessive.) +// +// If pkg is nil, it is equivalent to [*types.Package.Name]. +func NameRelativeTo(pkg *types.Package) types.Qualifier { + return func(other *types.Package) string { + if pkg != nil && pkg == other { + return "" // same package; unqualified + } + return other.Name() + } +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types_118.go b/vendor/golang.org/x/tools/internal/typesinternal/types_118.go deleted file mode 100644 index a42b072a67..0000000000 --- a/vendor/golang.org/x/tools/internal/typesinternal/types_118.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2021 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. - -//go:build go1.18 -// +build go1.18 - -package typesinternal - -import ( - "go/types" -) - -func init() { - SetGoVersion = func(conf *types.Config, version string) bool { - conf.GoVersion = version - return true - } -} diff --git a/vendor/golang.org/x/tools/internal/versions/constraint.go b/vendor/golang.org/x/tools/internal/versions/constraint.go new file mode 100644 index 0000000000..179063d484 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/constraint.go @@ -0,0 +1,13 @@ +// 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 versions + +import "go/build/constraint" + +// ConstraintGoVersion is constraint.GoVersion (if built with go1.21+). +// Otherwise nil. +// +// Deprecate once x/tools is after go1.21. +var ConstraintGoVersion func(x constraint.Expr) string diff --git a/vendor/golang.org/x/tools/internal/versions/constraint_go121.go b/vendor/golang.org/x/tools/internal/versions/constraint_go121.go new file mode 100644 index 0000000000..38011407d5 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/constraint_go121.go @@ -0,0 +1,14 @@ +// 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. + +//go:build go1.21 +// +build go1.21 + +package versions + +import "go/build/constraint" + +func init() { + ConstraintGoVersion = constraint.GoVersion +} diff --git a/vendor/golang.org/x/tools/internal/versions/features.go b/vendor/golang.org/x/tools/internal/versions/features.go new file mode 100644 index 0000000000..b53f178616 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/features.go @@ -0,0 +1,43 @@ +// Copyright 2023 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 versions + +// This file contains predicates for working with file versions to +// decide when a tool should consider a language feature enabled. + +// GoVersions that features in x/tools can be gated to. +const ( + Go1_18 = "go1.18" + Go1_19 = "go1.19" + Go1_20 = "go1.20" + Go1_21 = "go1.21" + Go1_22 = "go1.22" +) + +// Future is an invalid unknown Go version sometime in the future. +// Do not use directly with Compare. +const Future = "" + +// AtLeast reports whether the file version v comes after a Go release. +// +// Use this predicate to enable a behavior once a certain Go release +// has happened (and stays enabled in the future). +func AtLeast(v, release string) bool { + if v == Future { + return true // an unknown future version is always after y. + } + return Compare(Lang(v), Lang(release)) >= 0 +} + +// Before reports whether the file version v is strictly before a Go release. +// +// Use this predicate to disable a behavior once a certain Go release +// has happened (and stays enabled in the future). +func Before(v, release string) bool { + if v == Future { + return false // an unknown future version happens after y. + } + return Compare(Lang(v), Lang(release)) < 0 +} diff --git a/vendor/golang.org/x/tools/internal/versions/gover.go b/vendor/golang.org/x/tools/internal/versions/gover.go new file mode 100644 index 0000000000..bbabcd22e9 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/gover.go @@ -0,0 +1,172 @@ +// Copyright 2023 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. + +// This is a fork of internal/gover for use by x/tools until +// go1.21 and earlier are no longer supported by x/tools. + +package versions + +import "strings" + +// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]] +// The numbers are the original decimal strings to avoid integer overflows +// and since there is very little actual math. (Probably overflow doesn't matter in practice, +// but at the time this code was written, there was an existing test that used +// go1.99999999999, which does not fit in an int on 32-bit platforms. +// The "big decimal" representation avoids the problem entirely.) +type gover struct { + major string // decimal + minor string // decimal or "" + patch string // decimal or "" + kind string // "", "alpha", "beta", "rc" + pre string // decimal or "" +} + +// compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as toolchain versions. +// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21". +// Malformed versions compare less than well-formed versions and equal to each other. +// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0". +func compare(x, y string) int { + vx := parse(x) + vy := parse(y) + + if c := cmpInt(vx.major, vy.major); c != 0 { + return c + } + if c := cmpInt(vx.minor, vy.minor); c != 0 { + return c + } + if c := cmpInt(vx.patch, vy.patch); c != 0 { + return c + } + if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc + return c + } + if c := cmpInt(vx.pre, vy.pre); c != 0 { + return c + } + return 0 +} + +// lang returns the Go language version. For example, lang("1.2.3") == "1.2". +func lang(x string) string { + v := parse(x) + if v.minor == "" || v.major == "1" && v.minor == "0" { + return v.major + } + return v.major + "." + v.minor +} + +// isValid reports whether the version x is valid. +func isValid(x string) bool { + return parse(x) != gover{} +} + +// parse parses the Go version string x into a version. +// It returns the zero version if x is malformed. +func parse(x string) gover { + var v gover + + // Parse major version. + var ok bool + v.major, x, ok = cutInt(x) + if !ok { + return gover{} + } + if x == "" { + // Interpret "1" as "1.0.0". + v.minor = "0" + v.patch = "0" + return v + } + + // Parse . before minor version. + if x[0] != '.' { + return gover{} + } + + // Parse minor version. + v.minor, x, ok = cutInt(x[1:]) + if !ok { + return gover{} + } + if x == "" { + // Patch missing is same as "0" for older versions. + // Starting in Go 1.21, patch missing is different from explicit .0. + if cmpInt(v.minor, "21") < 0 { + v.patch = "0" + } + return v + } + + // Parse patch if present. + if x[0] == '.' { + v.patch, x, ok = cutInt(x[1:]) + if !ok || x != "" { + // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). + // Allowing them would be a bit confusing because we already have: + // 1.21 < 1.21rc1 + // But a prerelease of a patch would have the opposite effect: + // 1.21.3rc1 < 1.21.3 + // We've never needed them before, so let's not start now. + return gover{} + } + return v + } + + // Parse prerelease. + i := 0 + for i < len(x) && (x[i] < '0' || '9' < x[i]) { + if x[i] < 'a' || 'z' < x[i] { + return gover{} + } + i++ + } + if i == 0 { + return gover{} + } + v.kind, x = x[:i], x[i:] + if x == "" { + return v + } + v.pre, x, ok = cutInt(x) + if !ok || x != "" { + return gover{} + } + + return v +} + +// cutInt scans the leading decimal number at the start of x to an integer +// and returns that value and the rest of the string. +func cutInt(x string) (n, rest string, ok bool) { + i := 0 + for i < len(x) && '0' <= x[i] && x[i] <= '9' { + i++ + } + if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero + return "", "", false + } + return x[:i], x[i:], true +} + +// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. +// (Copied from golang.org/x/mod/semver's compareInt.) +func cmpInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain.go b/vendor/golang.org/x/tools/internal/versions/toolchain.go new file mode 100644 index 0000000000..377bf7a53b --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/toolchain.go @@ -0,0 +1,14 @@ +// 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 versions + +// toolchain is maximum version (<1.22) that the go toolchain used +// to build the current tool is known to support. +// +// When a tool is built with >=1.22, the value of toolchain is unused. +// +// x/tools does not support building with go <1.18. So we take this +// as the minimum possible maximum. +var toolchain string = Go1_18 diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain_go119.go b/vendor/golang.org/x/tools/internal/versions/toolchain_go119.go new file mode 100644 index 0000000000..f65beed9d8 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/toolchain_go119.go @@ -0,0 +1,14 @@ +// 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. + +//go:build go1.19 +// +build go1.19 + +package versions + +func init() { + if Compare(toolchain, Go1_19) < 0 { + toolchain = Go1_19 + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain_go120.go b/vendor/golang.org/x/tools/internal/versions/toolchain_go120.go new file mode 100644 index 0000000000..1a9efa126c --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/toolchain_go120.go @@ -0,0 +1,14 @@ +// 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. + +//go:build go1.20 +// +build go1.20 + +package versions + +func init() { + if Compare(toolchain, Go1_20) < 0 { + toolchain = Go1_20 + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/toolchain_go121.go b/vendor/golang.org/x/tools/internal/versions/toolchain_go121.go new file mode 100644 index 0000000000..b7ef216dfe --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/toolchain_go121.go @@ -0,0 +1,14 @@ +// 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. + +//go:build go1.21 +// +build go1.21 + +package versions + +func init() { + if Compare(toolchain, Go1_21) < 0 { + toolchain = Go1_21 + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/types.go b/vendor/golang.org/x/tools/internal/versions/types.go new file mode 100644 index 0000000000..562eef21fa --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types.go @@ -0,0 +1,19 @@ +// Copyright 2023 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 versions + +import ( + "go/types" +) + +// GoVersion returns the Go version of the type package. +// It returns zero if no version can be determined. +func GoVersion(pkg *types.Package) string { + // TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25. + if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok { + return pkg.GoVersion() + } + return "" +} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go121.go b/vendor/golang.org/x/tools/internal/versions/types_go121.go new file mode 100644 index 0000000000..b4345d3349 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types_go121.go @@ -0,0 +1,30 @@ +// Copyright 2023 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. + +//go:build !go1.22 +// +build !go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersion returns a language version (<=1.21) derived from runtime.Version() +// or an unknown future version. +func FileVersion(info *types.Info, file *ast.File) string { + // In x/tools built with Go <= 1.21, we do not have Info.FileVersions + // available. We use a go version derived from the toolchain used to + // compile the tool by default. + // This will be <= go1.21. We take this as the maximum version that + // this tool can support. + // + // There are no features currently in x/tools that need to tell fine grained + // differences for versions <1.22. + return toolchain +} + +// InitFileVersions is a noop when compiled with this Go version. +func InitFileVersions(*types.Info) {} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go122.go b/vendor/golang.org/x/tools/internal/versions/types_go122.go new file mode 100644 index 0000000000..aac5db62c9 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types_go122.go @@ -0,0 +1,41 @@ +// Copyright 2023 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. + +//go:build go1.22 +// +build go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersion returns a file's Go version. +// The reported version is an unknown Future version if a +// version cannot be determined. +func FileVersion(info *types.Info, file *ast.File) string { + // In tools built with Go >= 1.22, the Go version of a file + // follow a cascades of sources: + // 1) types.Info.FileVersion, which follows the cascade: + // 1.a) file version (ast.File.GoVersion), + // 1.b) the package version (types.Config.GoVersion), or + // 2) is some unknown Future version. + // + // File versions require a valid package version to be provided to types + // in Config.GoVersion. Config.GoVersion is either from the package's module + // or the toolchain (go run). This value should be provided by go/packages + // or unitchecker.Config.GoVersion. + if v := info.FileVersions[file]; IsValid(v) { + return v + } + // Note: we could instead return runtime.Version() [if valid]. + // This would act as a max version on what a tool can support. + return Future +} + +// InitFileVersions initializes info to record Go versions for Go files. +func InitFileVersions(info *types.Info) { + info.FileVersions = make(map[*ast.File]string) +} diff --git a/vendor/golang.org/x/tools/internal/versions/versions.go b/vendor/golang.org/x/tools/internal/versions/versions.go new file mode 100644 index 0000000000..8d1f7453db --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/versions.go @@ -0,0 +1,57 @@ +// Copyright 2023 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 versions + +import ( + "strings" +) + +// Note: If we use build tags to use go/versions when go >=1.22, +// we run into go.dev/issue/53737. Under some operations users would see an +// import of "go/versions" even if they would not compile the file. +// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include +// For this reason, this library just a clone of go/versions for the moment. + +// Lang returns the Go language version for version x. +// If x is not a valid version, Lang returns the empty string. +// For example: +// +// Lang("go1.21rc2") = "go1.21" +// Lang("go1.21.2") = "go1.21" +// Lang("go1.21") = "go1.21" +// Lang("go1") = "go1" +// Lang("bad") = "" +// Lang("1.21") = "" +func Lang(x string) string { + v := lang(stripGo(x)) + if v == "" { + return "" + } + return x[:2+len(v)] // "go"+v without allocation +} + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as Go versions. +// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". +// Invalid versions, including the empty string, compare less than +// valid versions and equal to each other. +// The language version "go1.21" compares less than the +// release candidate and eventual releases "go1.21rc1" and "go1.21.0". +// Custom toolchain suffixes are ignored during comparison: +// "go1.21.0" and "go1.21.0-bigcorp" are equal. +func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) } + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { return isValid(stripGo(x)) } + +// stripGo converts from a "go1.21" version to a "1.21" version. +// If v does not start with "go", stripGo returns the empty string (a known invalid version). +func stripGo(v string) string { + v, _, _ = strings.Cut(v, "-") // strip -bigcorp suffix. + if len(v) < 2 || v[:2] != "go" { + return "" + } + return v[2:] +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 02f2a28442..1cca12b881 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,8 +1,8 @@ # dario.cat/mergo v1.0.0 ## explicit; go 1.13 dario.cat/mergo -# github.com/99designs/gqlgen v0.17.19 -## explicit; go 1.16 +# github.com/99designs/gqlgen v0.17.46 +## explicit; go 1.20 github.com/99designs/gqlgen/complexity github.com/99designs/gqlgen/graphql github.com/99designs/gqlgen/graphql/errcode @@ -29,7 +29,7 @@ github.com/ActiveState/termtest ## explicit github.com/Azure/go-ansiterm github.com/Azure/go-ansiterm/winterm -# github.com/BurntSushi/toml v1.2.1 +# github.com/BurntSushi/toml v1.3.2 ## explicit; go 1.16 # github.com/Microsoft/go-winio v0.6.1 ## explicit; go 1.17 @@ -151,6 +151,9 @@ github.com/aymanbagabas/go-osc52/v2 # github.com/blang/semver v3.5.1+incompatible ## explicit github.com/blang/semver +# github.com/cespare/xxhash v1.1.0 +## explicit +github.com/cespare/xxhash # github.com/charmbracelet/bubbles v0.18.0 ## explicit; go 1.18 github.com/charmbracelet/bubbles/key @@ -356,7 +359,7 @@ github.com/google/go-querystring/query # github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 ## explicit; go 1.14 github.com/google/pprof/profile -# github.com/google/uuid v1.3.0 +# github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid # github.com/gorilla/websocket v1.5.0 @@ -371,10 +374,11 @@ github.com/hashicorp/go-retryablehttp # github.com/hashicorp/go-version v1.1.0 ## explicit github.com/hashicorp/go-version -# github.com/hashicorp/golang-lru v0.5.4 -## explicit; go 1.12 -github.com/hashicorp/golang-lru -github.com/hashicorp/golang-lru/simplelru +# github.com/hashicorp/golang-lru/v2 v2.0.7 +## explicit; go 1.18 +github.com/hashicorp/golang-lru/v2 +github.com/hashicorp/golang-lru/v2/internal +github.com/hashicorp/golang-lru/v2/simplelru # github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 ## explicit; go 1.14 github.com/hinshun/vt10x @@ -583,6 +587,9 @@ github.com/skeema/knownhosts # github.com/skratchdot/open-golang v0.0.0-20190104022628-a2dfa6d0dab6 ## explicit github.com/skratchdot/open-golang/open +# github.com/sosodev/duration v1.3.1 +## explicit; go 1.17 +github.com/sosodev/duration # github.com/spf13/cast v1.3.0 ## explicit github.com/spf13/cast @@ -662,8 +669,8 @@ go.mongodb.org/mongo-driver/bson/bsonrw go.mongodb.org/mongo-driver/bson/bsontype go.mongodb.org/mongo-driver/bson/primitive go.mongodb.org/mongo-driver/x/bsonx/bsoncore -# golang.org/x/crypto v0.23.0 -## explicit; go 1.18 +# golang.org/x/crypto v0.27.0 +## explicit; go 1.20 golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert golang.org/x/crypto/argon2 @@ -672,7 +679,6 @@ golang.org/x/crypto/blowfish golang.org/x/crypto/cast5 golang.org/x/crypto/chacha20 golang.org/x/crypto/curve25519 -golang.org/x/crypto/curve25519/internal/field golang.org/x/crypto/hkdf golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 @@ -687,10 +693,10 @@ golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/knownhosts -# golang.org/x/mod v0.12.0 -## explicit; go 1.17 +# golang.org/x/mod v0.21.0 +## explicit; go 1.22.0 golang.org/x/mod/semver -# golang.org/x/net v0.25.0 +# golang.org/x/net v0.29.0 ## explicit; go 1.18 golang.org/x/net/context golang.org/x/net/http/httpguts @@ -700,10 +706,10 @@ golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/socks golang.org/x/net/proxy -# golang.org/x/sync v0.3.0 -## explicit; go 1.17 +# golang.org/x/sync v0.8.0 +## explicit; go 1.18 golang.org/x/sync/errgroup -# golang.org/x/sys v0.20.0 +# golang.org/x/sys v0.25.0 ## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/execabs @@ -711,10 +717,10 @@ golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows golang.org/x/sys/windows/registry -# golang.org/x/term v0.20.0 +# golang.org/x/term v0.24.0 ## explicit; go 1.18 golang.org/x/term -# golang.org/x/text v0.15.0 +# golang.org/x/text v0.18.0 ## explicit; go 1.18 golang.org/x/text/cases golang.org/x/text/internal @@ -730,25 +736,25 @@ golang.org/x/text/width # golang.org/x/time v0.1.0 ## explicit golang.org/x/time/rate -# golang.org/x/tools v0.13.0 -## explicit; go 1.18 +# golang.org/x/tools v0.25.0 +## explicit; go 1.22.0 golang.org/x/tools/cmd/stringer golang.org/x/tools/go/gcexportdata -golang.org/x/tools/go/internal/packagesdriver golang.org/x/tools/go/packages golang.org/x/tools/go/types/objectpath +golang.org/x/tools/internal/aliases golang.org/x/tools/internal/event golang.org/x/tools/internal/event/core golang.org/x/tools/internal/event/keys golang.org/x/tools/internal/event/label -golang.org/x/tools/internal/event/tag golang.org/x/tools/internal/gcimporter golang.org/x/tools/internal/gocommand golang.org/x/tools/internal/packagesinternal golang.org/x/tools/internal/pkgbits +golang.org/x/tools/internal/stdlib golang.org/x/tools/internal/tokeninternal -golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal +golang.org/x/tools/internal/versions # gopkg.in/AlecAivazis/survey.v1 v1.8.8 ## explicit; go 1.13 gopkg.in/AlecAivazis/survey.v1 From 0dd74ee3d6a1c72fe16c40baf24edff297295895 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 9 Sep 2024 16:22:55 -0700 Subject: [PATCH 073/440] Rename and always close file --- internal/hash/file_hasher.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/internal/hash/file_hasher.go b/internal/hash/file_hasher.go index 5b19622776..649b1ea481 100644 --- a/internal/hash/file_hasher.go +++ b/internal/hash/file_hasher.go @@ -28,42 +28,42 @@ func NewFileHasher() *FileHasher { } } -func (f *FileHasher) HashFiles(files []string) (string, error) { +func (fh *FileHasher) HashFiles(files []string) (string, error) { sort.Strings(files) hasher := xxhash.New() - for _, file := range files { - openFile, err := os.Open(file) + for _, f := range files { + file, err := os.Open(f) if err != nil { - return "", errs.Wrap(err, "Could not open file: %s", file) + return "", errs.Wrap(err, "Could not open file: %s", file.Name()) } - fileInfo, err := openFile.Stat() + fileInfo, err := file.Stat() if err != nil { - return "", errs.Wrap(err, "Could not stat file: %s", file) + return "", errs.Wrap(err, "Could not stat file: %s", file.Name()) } var hash string - fh, ok := f.cache.Get(cacheKey(file, fileInfo.ModTime().String())) + cachedHash, ok := fh.cache.Get(cacheKey(file.Name(), fileInfo.ModTime().String())) if ok { - hash, ok = fh.(string) + hash, ok = cachedHash.(string) if !ok { return "", errs.New("Could not convert cache value to string") } } else { fileHasher := xxhash.New() - if _, err := io.Copy(fileHasher, openFile); err != nil { - return "", errs.Wrap(err, "Could not hash file: %s", file) - } - - if err := openFile.Close(); err != nil { - return "", errs.Wrap(err, "Could not close file: %s", file) + if _, err := io.Copy(fileHasher, file); err != nil { + return "", errs.Wrap(err, "Could not hash file: %s", file.Name()) } hash = fmt.Sprintf("%x", fileHasher.Sum(nil)) } - f.cache.Set(cacheKey(file, fileInfo.ModTime().String()), hash, cache.NoExpiration) + if err := file.Close(); err != nil { + return "", errs.Wrap(err, "Could not close file: %s", f) + } + + fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime().String()), hash, cache.NoExpiration) fmt.Fprintf(hasher, "%x", hash) } From 6eb609b8e833afc633d540a14c9e171ef2458bc7 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 10 Sep 2024 09:51:15 -0700 Subject: [PATCH 074/440] Add more tests --- internal/hash/file_hasher_test.go | 80 +++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/internal/hash/file_hasher_test.go b/internal/hash/file_hasher_test.go index 5917e610ab..f45c54f5f5 100644 --- a/internal/hash/file_hasher_test.go +++ b/internal/hash/file_hasher_test.go @@ -127,6 +127,86 @@ func TestFileHasher_ContentAgnostic(t *testing.T) { assert.Len(t, tc.misses, 2) } +func TestFileHasher_NotEqualFileAdded(t *testing.T) { + file1 := createTempFile(t, "file1") + file2 := createTempFile(t, "file2") + file3 := createTempFile(t, "file3") + + tc := &testCache{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } + + hasher := &FileHasher{ + cache: tc, + } + + hash1, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2, file3}) + assert.NoError(t, err) + + assert.NotEqual(t, hash1, hash2) + assert.Len(t, tc.hits, 2) + assert.Len(t, tc.misses, 3) +} + +func TestFileHasher_NotEqualFileRemoved(t *testing.T) { + file1 := createTempFile(t, "file1") + file2 := createTempFile(t, "file2") + file3 := createTempFile(t, "file3") + + tc := &testCache{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } + + hasher := &FileHasher{ + cache: tc, + } + + hash1, err := hasher.HashFiles([]string{file1, file2, file3}) + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.NotEqual(t, hash1, hash2) + assert.Len(t, tc.hits, 2) + assert.Len(t, tc.misses, 3) +} + +func TestFileHasher_NotEqualContentChanged(t *testing.T) { + file1 := createTempFile(t, "file1") + file2 := createTempFile(t, "file2") + + tc := &testCache{ + cache: cache.New(cache.NoExpiration, cache.NoExpiration), + } + + hasher := &FileHasher{ + cache: tc, + } + + hash1, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + hash2, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.Equal(t, hash1, hash2) + + if err := os.WriteFile(file1, []byte("file1_changed"), 0644); err != nil { + t.Fatal(err) + } + + hash2Modified, err := hasher.HashFiles([]string{file1, file2}) + assert.NoError(t, err) + + assert.NotEqual(t, hash1, hash2Modified) + assert.Len(t, tc.hits, 3) + assert.Len(t, tc.misses, 3) +} + func createTempFile(t *testing.T, content string) string { tmpfile, err := os.CreateTemp("", "testfile") if err != nil { From 1d1911aa246fdc9e178feebe9cffcdf7f77f1f47 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 10 Sep 2024 09:52:54 -0700 Subject: [PATCH 075/440] Update upload-artifact action --- .github/workflows/build.yml | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 258da124a9..5aabd1cf02 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,7 @@ name: Build-Test-Deploy # === Triggers === -"on": +'on': push: branches: - master @@ -20,7 +20,7 @@ name: Build-Test-Deploy # === Workflow Permissions === permissions: id-token: write # This is required for requesting the JWT - contents: read # This is required for actions/checkout + contents: read # This is required for actions/checkout # === Workflow-level environment variables === env: @@ -38,9 +38,9 @@ jobs: go-version: - 1.22.x sys: - - {os: ubuntu-latest} - - {os: macos-12, shell: zsh} - - {os: windows-2019} + - { os: ubuntu-latest } + - { os: macos-12, shell: zsh } + - { os: windows-2019 } fail-fast: false runs-on: ${{ matrix.sys.os }} env: @@ -174,7 +174,7 @@ jobs: name: Check Format id: check_format shell: bash - if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\", \"refs/heads/master\"]'), github.ref) && !startsWith(github.event.pull_request.head.ref, 'version/')" + if: '!contains(fromJSON(''["refs/heads/beta", "refs/heads/release", "refs/heads/LTS", "refs/heads/master"]''), github.ref) && !startsWith(github.event.pull_request.head.ref, ''version/'')' run: parallelize results Check-Format - # === Unit Tests === @@ -185,32 +185,32 @@ jobs: continue-on-error: ${{ github.event_name != 'schedule' }} - # === "Build: CLI" === - name: "Build: CLI" + name: 'Build: CLI' shell: bash run: parallelize results Build-CLI - # === "Build: Service" === - name: "Build: Service" + name: 'Build: Service' shell: bash run: parallelize results Build-Service - # === "Build: Installer" === - name: "Build: Installer" + name: 'Build: Installer' shell: bash run: parallelize results Build-Installer - # === "Build: Remote Installer" === - name: "Build: Remote Installer" + name: 'Build: Remote Installer' shell: bash run: parallelize results Build-Remote-Installer - # === "Build: Install Scripts" === - name: "Build: Install Scripts" + name: 'Build: Install Scripts' shell: bash run: parallelize results Build-Install-Scripts - # === "Build: Executor" === - name: "Build: Executor" + name: 'Build: Executor' shell: bash run: parallelize results Build-Executor @@ -271,7 +271,7 @@ jobs: - # === Deploy for Integration Tests # NEVER run this against production branches. This is meant for PR deployments. === name: Deploy for Integration Tests # NEVER run this against production branches. This is meant for PR deployments. - if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\"]'), github.ref)" + if: '!contains(fromJSON(''["refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]''), github.ref)' shell: bash run: | if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then @@ -296,7 +296,7 @@ jobs: - # === Integration Tests === name: Integration Tests id: integration_tests - if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\"]'), github.ref)" + if: '!contains(fromJSON(''["refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]''), github.ref)' shell: bash run: | if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then @@ -392,7 +392,7 @@ jobs: - # === Upload Session Artifacts === name: Upload Session Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: session-build-${{ matrix.sys.os }} path: build/ @@ -435,7 +435,6 @@ jobs: # === Deploy Steps === steps: - - # === Checkout code === name: Checkout code uses: actions/checkout@v4 @@ -510,7 +509,7 @@ jobs: - # === Upload Artifacts === name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: build path: build/ From db3c6c6d69a2f1f11bbbe78d22fb1ba15dcf5c54 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 10:01:01 -0700 Subject: [PATCH 076/440] Added `--expand` flag to `state manifest` --- cmd/state/internal/cmdtree/manifest.go | 12 ++++++++++-- internal/runners/manifest/manifest.go | 8 ++++++-- internal/runners/manifest/requirements.go | 24 +++++++++++------------ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/cmd/state/internal/cmdtree/manifest.go b/cmd/state/internal/cmdtree/manifest.go index 4f61330343..367dfba44c 100644 --- a/cmd/state/internal/cmdtree/manifest.go +++ b/cmd/state/internal/cmdtree/manifest.go @@ -10,15 +10,23 @@ import ( func newManifestCommmand(prime *primer.Values) *captain.Command { runner := manifest.NewManifest(prime) + params := manifest.Params{} + cmd := captain.NewCommand( "manifest", locale.Tl("manifest_title", "Listing Requirements For Your Project"), locale.Tl("manifest_cmd_description", "List the requirements of the current project"), prime, - []*captain.Flag{}, + []*captain.Flag{ + { + Name: "expand", + Description: locale.Tl("manifest_flag_expand", "Expand requirement names to include their namespace"), + Value: ¶ms.Expand, + }, + }, []*captain.Argument{}, func(_ *captain.Command, _ []string) error { - return runner.Run() + return runner.Run(params) }, ) diff --git a/internal/runners/manifest/manifest.go b/internal/runners/manifest/manifest.go index 073182a6b2..9d165bbef7 100644 --- a/internal/runners/manifest/manifest.go +++ b/internal/runners/manifest/manifest.go @@ -29,6 +29,10 @@ type primeable interface { primer.Configurer } +type Params struct { + Expand bool +} + type Manifest struct { prime primeable // The remainder is redundant with the above. Refactoring this will follow in a later story so as not to blow @@ -54,7 +58,7 @@ func NewManifest(prime primeable) *Manifest { } } -func (m *Manifest) Run() (rerr error) { +func (m *Manifest) Run(params Params) (rerr error) { defer rationalizeError(m.project, m.auth, &rerr) if m.project == nil { @@ -78,7 +82,7 @@ func (m *Manifest) Run() (rerr error) { return errs.Wrap(err, "Could not fetch vulnerabilities") } - reqOut := newRequirements(reqs, bpReqs, vulns, !m.out.Type().IsStructured()) + reqOut := newRequirements(reqs, bpReqs, vulns, !m.out.Type().IsStructured(), params.Expand) if m.out.Type().IsStructured() { m.out.Print(reqOut) } else { diff --git a/internal/runners/manifest/requirements.go b/internal/runners/manifest/requirements.go index e93b719bdc..c85cd6b1a1 100644 --- a/internal/runners/manifest/requirements.go +++ b/internal/runners/manifest/requirements.go @@ -1,6 +1,8 @@ package manifest import ( + "fmt" + "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/pkg/buildplan" @@ -18,9 +20,10 @@ type requirement struct { type requirements struct { Requirements []requirement `json:"requirements"` UnknownRequirements []buildscript.UnknownRequirement `json:"unknown_requirements,omitempty"` + expand bool // Whether to show requirements by their full namespace } -func newRequirements(reqs []buildscript.Requirement, bpReqs buildplan.Ingredients, vulns vulnerabilities, shortRevIDs bool) requirements { +func newRequirements(reqs []buildscript.Requirement, bpReqs buildplan.Ingredients, vulns vulnerabilities, shortRevIDs bool, expand bool) requirements { var knownReqs []requirement var unknownReqs []buildscript.UnknownRequirement for _, req := range reqs { @@ -28,7 +31,7 @@ func newRequirements(reqs []buildscript.Requirement, bpReqs buildplan.Ingredient case buildscript.DependencyRequirement: knownReqs = append(knownReqs, requirement{ Name: r.Name, - Namespace: processNamespace(r.Namespace), + Namespace: r.Namespace, ResolvedVersion: resolveVersion(r.Requirement, bpReqs), Vulnerabilities: vulns.get(r.Name, r.Namespace), }) @@ -49,6 +52,7 @@ func newRequirements(reqs []buildscript.Requirement, bpReqs buildplan.Ingredient return requirements{ Requirements: knownReqs, UnknownRequirements: unknownReqs, + expand: expand, } } @@ -63,13 +67,17 @@ func (o requirements) Print(out output.Outputer) { var requirementsOutput []*requirementOutput for _, req := range o.Requirements { + name := req.Name + if o.expand && req.Namespace != "" { + name = req.Namespace + "/" + req.Name + } requirementOutput := &requirementOutput{ - Name: locale.Tl("manifest_name", "[ACTIONABLE]{{.V0}}[/RESET]", req.Name), + Name: fmt.Sprintf("[ACTIONABLE]%s[/RESET]", name), Version: req.ResolvedVersion.String(), Vulnerabilities: req.Vulnerabilities.String(), } - if req.Namespace != "" { + if isCustomNamespace(req.Namespace) { requirementOutput.Namespace = locale.Tr("namespace_row", output.TreeEnd, req.Namespace) } @@ -108,14 +116,6 @@ func (o requirements) MarshalStructured(f output.Format) interface{} { return o } -func processNamespace(namespace string) string { - if !isCustomNamespace(namespace) { - return "" - } - - return namespace -} - func isCustomNamespace(ns string) bool { supportedNamespaces := []platformModel.NamespaceType{ platformModel.NamespacePackage, From f72b5b195bbd6bb870e6aa771680ba7a9b185bc2 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 10:01:44 -0700 Subject: [PATCH 077/440] Tests and error conditions for `state uninstall` --- internal/locale/locales/en-us.yaml | 5 +++ internal/runners/uninstall/rationalize.go | 14 +++----- internal/runners/uninstall/uninstall.go | 25 ++++++++++++++ pkg/buildscript/mutations.go | 2 +- pkg/buildscript/queries.go | 17 ++++++++++ test/integration/package_int_test.go | 40 +++++++++++++++++++++++ 6 files changed, 92 insertions(+), 11 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 10154aa5a5..48dfe399e2 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1591,5 +1591,10 @@ platform_add_not_found: other: Could not find a platform matching your criteria err_uninstall_nomatch: other: "The following package(s) could not be found in your project: {{.V0}}" +err_uninstall_multimatch: + other: | + The following terms match multiple requirements in your project: [ACTIONABLE]{{.V0}}[/RESET]. + Please specify the requirement to uninstall by using its full namespace. + To view the namespace of your project requirements run: [ACTIONABLE]state manifest[/RESET]. progress_requirements: other: "• Updating requirements" diff --git a/internal/runners/uninstall/rationalize.go b/internal/runners/uninstall/rationalize.go index 7efcdcdce7..767b1c3136 100644 --- a/internal/runners/uninstall/rationalize.go +++ b/internal/runners/uninstall/rationalize.go @@ -2,7 +2,6 @@ package uninstall import ( "errors" - "fmt" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" @@ -13,23 +12,18 @@ import ( func (u *Uninstall) rationalizeError(rerr *error) { var noMatchesErr *errNoMatches + var multipleMatchesErr *errMultipleMatches switch { case rerr == nil: return - // Error staging a commit during uninstall. case errors.As(*rerr, &noMatchesErr): - pkgs := []string{} - for _, pkg := range noMatchesErr.packages { - name := pkg.Name - if pkg.Namespace != "" { - name = fmt.Sprintf("%s/%s", pkg.Namespace, pkg.Name) - } - pkgs = append(pkgs, fmt.Sprintf("[ACTIONABLE]%s[/RESET]", name)) - } *rerr = errs.WrapUserFacing(*rerr, locale.Tr("err_uninstall_nomatch", noMatchesErr.packages.String())) + case errors.As(*rerr, &multipleMatchesErr): + *rerr = errs.WrapUserFacing(*rerr, locale.Tr("err_uninstall_multimatch", multipleMatchesErr.packages.String())) + // Error staging a commit during install. case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): rationalizers.HandleCommitErrors(rerr) diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index 9f4968b650..94d05bc9a0 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -13,6 +13,7 @@ import ( "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/internal/sliceutils" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" @@ -51,6 +52,11 @@ type errNoMatches struct { packages captain.PackagesValue } +type errMultipleMatches struct { + error + packages captain.PackagesValue +} + // Run executes the install behavior. func (u *Uninstall) Run(params Params) (rerr error) { defer u.rationalizeError(&rerr) @@ -113,6 +119,25 @@ func (u *Uninstall) Run(params Params) (rerr error) { } func prepareBuildScript(script *buildscript.BuildScript, pkgs captain.PackagesValue) error { + reqs, err := script.DependencyRequirements() + if err != nil { + return errs.Wrap(err, "Unable to get requirements") + } + + // Check that we're not matching multiple packages + multipleMatches := captain.PackagesValue{} + for _, pkg := range pkgs { + matches := sliceutils.Filter(reqs, func(req types.Requirement) bool { + return pkg.Name == req.Name && (pkg.Namespace == "" || pkg.Namespace == req.Namespace) + }) + if len(matches) > 1 { + multipleMatches = append(multipleMatches, pkg) + } + } + if len(multipleMatches) > 0 { + return &errMultipleMatches{error: errs.New("Could not find all requested packages"), packages: multipleMatches} + } + // Remove requirements var removeErrs error notFound := captain.PackagesValue{} diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 1edcb6d32c..de3bba9169 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -94,7 +94,7 @@ func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { for _, arg := range req.FuncCall.Arguments { if arg.Assignment.Key == requirementNameKey { - match := strValue(arg.Assignment.Value) == requirement.Name + match = strValue(arg.Assignment.Value) == requirement.Name if !match || requirement.Namespace == "" { break } diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 0d81d8f3b0..2f4a1a6c54 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -92,6 +92,23 @@ func (b *BuildScript) Requirements() ([]Requirement, error) { return requirements, nil } +// DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, +// which are the most common. +// ONLY use this when you know you only need to care about dependencies. +func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { + reqs, err := b.Requirements() + if err != nil { + return nil, errs.Wrap(err, "Could not get requirements") + } + var deps []types.Requirement + for _, req := range reqs { + if dep, ok := req.(DependencyRequirement); ok { + deps = append(deps, dep.Requirement) + } + } + return deps, nil +} + func (b *BuildScript) getRequirementsNode() (*Value, error) { node, err := b.getSolveNode() if err != nil { diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 22ac663936..b3da5de5b9 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -431,6 +431,22 @@ scripts: ts.PrepareCommitIdFile("a9d0bc88-585a-49cf-89c1-6c07af781cff") } +func (suite *PackageIntegrationTestSuite) TestPackage_Uninstall() { + suite.OnlyRunForTags(tagsuite.Package) + + ts := e2e.New(suite.T(), true) + defer ts.Close() + + ts.PrepareProject("ActiveState-CLI-Testing/small-python-with-pkg", "a2115792-2620-4217-89ed-b596c8c11ce3") + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("uninstall", "requests") + // cp = ts.SpawnDebuggerForCmdWithOpts(e2e.OptArgs("uninstall", "requests")) + cp.Expect("project has been updated") // , termtest.OptExpectTimeout(600*time.Second)) + cp.ExpectExitCode(0) +} + func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDoesNotExist() { suite.OnlyRunForTags(tagsuite.Package) @@ -449,6 +465,30 @@ func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDoesNotExist() { } } +func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDupeMatch() { + suite.OnlyRunForTags(tagsuite.Package) + + ts := e2e.New(suite.T(), true) + defer ts.Close() + + ts.PrepareProject("ActiveState-CLI-Testing/duplicate-pkg-name", "e5a15d59-9192-446a-a133-9f4c2ebe0898") + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("uninstall", "oauth") + cp.Expect("match multiple requirements") + cp.ExpectExitCode(1) + ts.IgnoreLogErrors() + + if strings.Count(cp.Snapshot(), " x ") != 2 { // 2 because "Creating commit x Failed" is also printed + suite.Fail("Expected exactly ONE error message, got: ", cp.Snapshot()) + } + + cp = ts.Spawn("uninstall", "language/python/oauth") + cp.Expect("project has been updated") + cp.ExpectExitCode(0) +} + func (suite *PackageIntegrationTestSuite) TestJSON() { suite.OnlyRunForTags(tagsuite.Package, tagsuite.JSON) ts := e2e.New(suite.T(), false) From dabd14ae013e5eee2d64684cdd9fa6652134c62c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 10:02:03 -0700 Subject: [PATCH 078/440] Added convenience method to run termtest command in debugger --- internal/testhelpers/e2e/session.go | 75 ++++++++++++++++++---------- test/integration/package_int_test.go | 3 +- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 030e279c23..699e12aa8b 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -7,6 +7,7 @@ import ( "path/filepath" "regexp" "runtime" + "slices" "strings" "testing" "time" @@ -209,6 +210,20 @@ func (s *Session) Spawn(args ...string) *SpawnedCmd { return s.SpawnCmdWithOpts(s.Exe, OptArgs(args...)) } +func (s *Session) SpawnDebuggerForCmdWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { + spawnOpts := s.newSpawnOpts(opts...) + args := slices.Clone(spawnOpts.Args) + + workDir := spawnOpts.Dir + spawnOpts.Args = []string{"debug", "--wd", workDir, "--headless", "--listen=:2345", "--api-version=2", "github.com/ActiveState/cli/cmd/state", "--"} + spawnOpts.Args = append(spawnOpts.Args, args...) + spawnOpts.Dir = environment.GetRootPathUnsafe() + + return s.SpawnCmdWithOpts("dlv", func(opts *SpawnOpts) { + *opts = spawnOpts + }) +} + // SpawnWithOpts spawns the state tool executable to be tested with arguments func (s *Session) SpawnWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { return s.SpawnCmdWithOpts(s.Exe, opts...) @@ -231,33 +246,7 @@ func (s *Session) SpawnShellWithOpts(shell Shell, opts ...SpawnOptSetter) *Spawn // SpawnCmdWithOpts executes an executable in a pseudo-terminal for integration tests // Arguments and other parameters can be specified by specifying SpawnOptSetter func (s *Session) SpawnCmdWithOpts(exe string, optSetters ...SpawnOptSetter) *SpawnedCmd { - spawnOpts := NewSpawnOpts() - spawnOpts.Env = s.Env - spawnOpts.Dir = s.Dirs.Work - - spawnOpts.TermtestOpts = append(spawnOpts.TermtestOpts, - termtest.OptErrorHandler(func(tt *termtest.TermTest, err error) error { - s.T.Fatal(s.DebugMessage(errs.JoinMessage(err))) - return err - }), - termtest.OptDefaultTimeout(defaultTimeout), - termtest.OptCols(140), - termtest.OptRows(30), // Needs to be able to accommodate most JSON output - ) - - // TTYs output newlines in two steps: '\r' (CR) to move the caret to the beginning of the line, - // and '\n' (LF) to move the caret one line down. Terminal emulators do the same thing, so the - // raw terminal output will contain "\r\n". Since our multi-line expectation messages often use - // '\n', normalize line endings to that for convenience, regardless of platform ('\n' for Linux - // and macOS, "\r\n" for Windows). - // More info: https://superuser.com/a/1774370 - spawnOpts.TermtestOpts = append(spawnOpts.TermtestOpts, - termtest.OptNormalizedLineEnds(true), - ) - - for _, optSet := range optSetters { - optSet(&spawnOpts) - } + spawnOpts := s.newSpawnOpts(optSetters...) var shell string var args []string @@ -319,6 +308,38 @@ func (s *Session) SpawnCmdWithOpts(exe string, optSetters ...SpawnOptSetter) *Sp return spawn } +func (s *Session) newSpawnOpts(optSetters ...SpawnOptSetter) SpawnOpts { + spawnOpts := NewSpawnOpts() + spawnOpts.Env = s.Env + spawnOpts.Dir = s.Dirs.Work + + spawnOpts.TermtestOpts = append(spawnOpts.TermtestOpts, + termtest.OptErrorHandler(func(tt *termtest.TermTest, err error) error { + s.T.Fatal(s.DebugMessage(errs.JoinMessage(err))) + return err + }), + termtest.OptDefaultTimeout(defaultTimeout), + termtest.OptCols(140), + termtest.OptRows(30), // Needs to be able to accommodate most JSON output + ) + + // TTYs output newlines in two steps: '\r' (CR) to move the caret to the beginning of the line, + // and '\n' (LF) to move the caret one line down. Terminal emulators do the same thing, so the + // raw terminal output will contain "\r\n". Since our multi-line expectation messages often use + // '\n', normalize line endings to that for convenience, regardless of platform ('\n' for Linux + // and macOS, "\r\n" for Windows). + // More info: https://superuser.com/a/1774370 + spawnOpts.TermtestOpts = append(spawnOpts.TermtestOpts, + termtest.OptNormalizedLineEnds(true), + ) + + for _, optSet := range optSetters { + optSet(&spawnOpts) + } + + return spawnOpts +} + // PrepareActiveStateYAML creates an activestate.yaml in the session's work directory from the // given YAML contents. func (s *Session) PrepareActiveStateYAML(contents string) { diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index b3da5de5b9..77268712cb 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -442,8 +442,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_Uninstall() { cp.ExpectExitCode(0) cp = ts.Spawn("uninstall", "requests") - // cp = ts.SpawnDebuggerForCmdWithOpts(e2e.OptArgs("uninstall", "requests")) - cp.Expect("project has been updated") // , termtest.OptExpectTimeout(600*time.Second)) + cp.Expect("project has been updated") cp.ExpectExitCode(0) } From a8e99ce9e7a83e19b66f8ce29b29c06dd91bb730 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 10:08:51 -0700 Subject: [PATCH 079/440] Enforce namespace type --- internal/runners/uninstall/uninstall.go | 51 +++++++++++++++---------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index 94d05bc9a0..731938070a 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -1,8 +1,6 @@ package uninstall import ( - "errors" - "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" @@ -10,7 +8,6 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/reqop_runbit" "github.com/ActiveState/cli/internal/sliceutils" @@ -99,7 +96,7 @@ func (u *Uninstall) Run(params Params) (rerr error) { // Update buildscript script := oldCommit.BuildScript() - if err := prepareBuildScript(script, params.Packages); err != nil { + if err := u.prepareBuildScript(script, params.Packages); err != nil { return errs.Wrap(err, "Could not prepare build script") } @@ -118,42 +115,56 @@ func (u *Uninstall) Run(params Params) (rerr error) { return nil } -func prepareBuildScript(script *buildscript.BuildScript, pkgs captain.PackagesValue) error { +func (u *Uninstall) prepareBuildScript(script *buildscript.BuildScript, pkgs captain.PackagesValue) error { reqs, err := script.DependencyRequirements() if err != nil { return errs.Wrap(err, "Unable to get requirements") } - // Check that we're not matching multiple packages + // Resolve requirements and check for errors + toRemove := []types.Requirement{} + notFound := captain.PackagesValue{} multipleMatches := captain.PackagesValue{} for _, pkg := range pkgs { + // Filter matching requirements matches := sliceutils.Filter(reqs, func(req types.Requirement) bool { - return pkg.Name == req.Name && (pkg.Namespace == "" || pkg.Namespace == req.Namespace) + if pkg.Name != req.Name { + return false + } + if pkg.Namespace != "" { + return req.Namespace == pkg.Namespace + } + return model.NamespaceMatch(req.Namespace, u.nsType.Matchable()) }) + toRemove = append(toRemove, matches...) + + // Check for duplicate matches if len(matches) > 1 { multipleMatches = append(multipleMatches, pkg) } + + // Check for no matches + if len(matches) == 0 { + notFound = append(notFound, pkg) + } } + + // Error out on duplicate matches if len(multipleMatches) > 0 { return &errMultipleMatches{error: errs.New("Could not find all requested packages"), packages: multipleMatches} } + // Error out on no matches + if len(notFound) > 0 { + return &errNoMatches{error: errs.New("Could not find all requested packages"), packages: notFound} + } + // Remove requirements - var removeErrs error - notFound := captain.PackagesValue{} - for _, pkg := range pkgs { - if err := script.RemoveRequirement(types.Requirement{Name: pkg.Name, Namespace: pkg.Namespace}); err != nil { - if errors.As(err, ptr.To(&buildscript.RequirementNotFoundError{})) { - notFound = append(notFound, pkg) - removeErrs = errs.Pack(removeErrs, err) - } else { - return errs.Wrap(err, "Unable to remove requirement") - } + for _, req := range toRemove { + if err := script.RemoveRequirement(req); err != nil { + return errs.Wrap(err, "Unable to remove requirement") } } - if len(notFound) > 0 { - return errs.Pack(&errNoMatches{error: errs.New("Could not find all requested packages"), packages: notFound}, removeErrs) - } return nil } From c2aea6b290f97bd2f3462caae41e629fc8a1acaf Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 10:11:05 -0700 Subject: [PATCH 080/440] Bundle uninstall uses new runner --- cmd/state/internal/cmdtree/bundles.go | 7 ++++--- test/integration/bundle_int_test.go | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd/state/internal/cmdtree/bundles.go b/cmd/state/internal/cmdtree/bundles.go index 8351a2bc02..a04715ff88 100644 --- a/cmd/state/internal/cmdtree/bundles.go +++ b/cmd/state/internal/cmdtree/bundles.go @@ -6,6 +6,7 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runners/install" "github.com/ActiveState/cli/internal/runners/packages" + "github.com/ActiveState/cli/internal/runners/uninstall" "github.com/ActiveState/cli/pkg/platform/model" ) @@ -74,9 +75,9 @@ func newBundleInstallCommand(prime *primer.Values) *captain.Command { } func newBundleUninstallCommand(prime *primer.Values) *captain.Command { - runner := packages.NewUninstall(prime) + runner := uninstall.New(prime, model.NamespaceBundle) - params := packages.UninstallRunParams{} + params := uninstall.Params{} return captain.NewCommand( "uninstall", @@ -93,7 +94,7 @@ func newBundleUninstallCommand(prime *primer.Values) *captain.Command { }, }, func(_ *captain.Command, _ []string) error { - return runner.Run(params, model.NamespaceBundle) + return runner.Run(params) }, ).SetSupportsStructuredOutput() } diff --git a/test/integration/bundle_int_test.go b/test/integration/bundle_int_test.go index 6207c8f5de..d5254d71a0 100644 --- a/test/integration/bundle_int_test.go +++ b/test/integration/bundle_int_test.go @@ -36,7 +36,7 @@ func (suite *BundleIntegrationTestSuite) TestBundle_project_name_noData() { cp.ExpectExitCode(0) } -func (suite *BundleIntegrationTestSuite) TestBundle_install() { +func (suite *BundleIntegrationTestSuite) TestBundle_install_uninstall() { suite.OnlyRunForTags(tagsuite.Bundle) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -48,6 +48,10 @@ func (suite *BundleIntegrationTestSuite) TestBundle_install() { cp = ts.Spawn("bundles", "install", "python-module-build-support") cp.Expect("project has been updated") cp.ExpectExitCode(0) + + cp = ts.Spawn("bundles", "uninstall", "python-module-build-support") + cp.Expect("project has been updated") + cp.ExpectExitCode(0) } func (suite *BundleIntegrationTestSuite) TestBundle_searchSimple() { From f8416a0b11f45b4ce1d449d9eb30e1f575de25c8 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 10 Sep 2024 13:27:36 -0400 Subject: [PATCH 081/440] Sort artifact changesets by name. --- pkg/buildplan/buildplan.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/buildplan/buildplan.go b/pkg/buildplan/buildplan.go index 70480f9443..0f30cac869 100644 --- a/pkg/buildplan/buildplan.go +++ b/pkg/buildplan/buildplan.go @@ -2,6 +2,7 @@ package buildplan import ( "encoding/json" + "sort" "github.com/go-openapi/strfmt" @@ -131,6 +132,8 @@ func (b *BuildPlan) DiffArtifacts(oldBp *BuildPlan, requestedOnly bool) Artifact }) } + sort.SliceStable(changeset, func(i, j int) bool { return changeset[i].Artifact.Name() < changeset[j].Artifact.Name() }) + return changeset } From 86596b6234e4e73963387da84ab41604315837e0 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 10 Sep 2024 14:22:16 -0400 Subject: [PATCH 082/440] Update deprecated GitHub Actions. --- .github/workflows/build.yml | 16 +++++++++------- .github/workflows/propagate.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/verify.yml | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 258da124a9..8169649d28 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,7 +68,7 @@ jobs: - # === Install Go === name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} @@ -392,7 +392,7 @@ jobs: - # === Upload Session Artifacts === name: Upload Session Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: session-build-${{ matrix.sys.os }} path: build/ @@ -404,9 +404,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Download All Build Session Artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: path: build/ + merge-multiple: true - name: Scan for CVEs if: runner.os == 'Linux' @@ -444,7 +445,7 @@ jobs: - # === Install Go === name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: 1.22.x @@ -454,9 +455,10 @@ jobs: - # === Download All Build Session Artifacts === name: Download All Build Session Artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: path: build/ + merge-multiple: true - # === Sanitize All Session Artifacts === name: Sanitize All Session Artifacts @@ -501,7 +503,7 @@ jobs: - # === Cleanup Session Artifacts === name: Cleanup Session Artifacts - uses: geekyeggo/delete-artifact@v1 + uses: geekyeggo/delete-artifact@v5 with: name: | session-build-ubuntu-20.04 @@ -510,7 +512,7 @@ jobs: - # === Upload Artifacts === name: Upload Artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: build path: build/ diff --git a/.github/workflows/propagate.yml b/.github/workflows/propagate.yml index 5b44a7cad4..6e5f7247bf 100644 --- a/.github/workflows/propagate.yml +++ b/.github/workflows/propagate.yml @@ -25,14 +25,14 @@ jobs: - # === Checkout code === name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GH_AUTOMATION_TOKEN }} - # === Install Go === name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: '1.22.x' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48c9728f30..b76d7809b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: steps: - # Checkout Code name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v5 - # === Install Go === name: Install Go diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index f3c81c6357..1f72fcb663 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -34,7 +34,7 @@ jobs: - # === Install Go === name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: '1.22.x' From 75dfc8055b062cbfe1b4e31c3e0eb482e6992e35 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 10 Sep 2024 14:41:15 -0400 Subject: [PATCH 083/440] Update unit test to reflect new bash path on GitHub Actions Windows runner. --- internal/subshell/subshell_win_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/subshell/subshell_win_test.go b/internal/subshell/subshell_win_test.go index da2b2e4d11..b52a4db931 100644 --- a/internal/subshell/subshell_win_test.go +++ b/internal/subshell/subshell_win_test.go @@ -27,11 +27,12 @@ func setup(t *testing.T) { func TestBash(t *testing.T) { setup(t) - os.Setenv("SHELL", `C:\Program Files\bash.exe`) + shellPath := `C:\Program Files\Git\usr\bin\bash.exe` + os.Setenv("SHELL", shellPath) cfg, err := config.New() require.NoError(t, err) subs := New(cfg) - assert.Equal(t, `C:\Program Files\bash.exe`, subs.Binary()) + assert.Equal(t, shellPath, subs.Binary()) } @@ -39,11 +40,11 @@ func TestBashDontEscapeSpace(t *testing.T) { setup(t) // Reproduce bug in which paths are being incorrectly escaped on windows - os.Setenv("SHELL", `C:\Program\ Files\bash.exe`) + os.Setenv("SHELL", `C:\Program\ Files\Git\usr\bin\bash.exe`) cfg, err := config.New() require.NoError(t, err) subs := New(cfg) - assert.Equal(t, `C:\Program Files\bash.exe`, subs.Binary()) + assert.Equal(t, `C:\Program Files\Git\usr\bin\bash.exe`, subs.Binary()) } func TestRunCommandError(t *testing.T) { From e8e9355f5282a5ae5974b4bcd7b67146004d72c6 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 11:42:28 -0700 Subject: [PATCH 084/440] Implemented new runner for `state platforms remove` --- internal/locale/locales/en-us.yaml | 9 ++ internal/runners/platforms/add.go | 7 +- internal/runners/platforms/remove.go | 124 ++++++++++++++++++++----- pkg/platform/model/inventory.go | 57 ++++++------ test/integration/platforms_int_test.go | 18 ++-- 5 files changed, 150 insertions(+), 65 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 48dfe399e2..211dce172e 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1596,5 +1596,14 @@ err_uninstall_multimatch: The following terms match multiple requirements in your project: [ACTIONABLE]{{.V0}}[/RESET]. Please specify the requirement to uninstall by using its full namespace. To view the namespace of your project requirements run: [ACTIONABLE]state manifest[/RESET]. +err_uninstall_platform_nomatch: + other: "The specified platform does not exist in your project" +err_uninstall_platform_multimatch: + other: | + The platform query you provided matches multiple platforms in your project. + Please specify the platform to uninstall by using its version and bit-width. + To view the platforms your project uses run: [ACTIONABLE]state platforms[/RESET]. progress_requirements: other: "• Updating requirements" +progress_platforms: + other: "• Updating platforms" diff --git a/internal/runners/platforms/add.go b/internal/runners/platforms/add.go index dcbe238fe3..3f963ee00c 100644 --- a/internal/runners/platforms/add.go +++ b/internal/runners/platforms/add.go @@ -48,16 +48,11 @@ func NewAdd(prime primeable) *Add { } // Run executes the add behavior. -func (a *Add) Run(ps AddRunParams) (rerr error) { +func (a *Add) Run(params AddRunParams) (rerr error) { defer rationalizeAddPlatformError(&rerr) logging.Debug("Execute platforms add") - params, err := prepareParams(ps.Params) - if err != nil { - return err - } - if a.prime.Project() == nil { return rationalize.ErrNoProject } diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index b8fe20c01f..070204744c 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -1,13 +1,22 @@ package platforms import ( + "errors" + + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" - "github.com/ActiveState/cli/internal/runbits/runtime/requirements" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + "github.com/ActiveState/cli/internal/runbits/rationalizers" + "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/pkg/localcommit" + bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/model" + bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/go-openapi/strfmt" ) // RemoveRunParams tracks the info required for running Remove. @@ -15,45 +24,118 @@ type RemoveRunParams struct { Params } -// Remove manages the removeing execution context. +// Remove manages the adding execution context. type Remove struct { prime primeable } -// NewRemove prepares a remove execution context for use. +// NewRemove prepares an add execution context for use. func NewRemove(prime primeable) *Remove { return &Remove{ prime: prime, } } -// Run executes the remove behavior. -func (r *Remove) Run(ps RemoveRunParams) error { +var errNoMatch = errors.New("no platform matched the search criteria") +var errMultiMatch = errors.New("multiple platforms matched the search criteria") + +// Run executes the add behavior. +func (a *Remove) Run(params RemoveRunParams) (rerr error) { + defer rationalizeRemovePlatformError(&rerr) + logging.Debug("Execute platforms remove") - if r.prime.Project() == nil { + if a.prime.Project() == nil { return rationalize.ErrNoProject } - params, err := prepareParams(ps.Params) + pj := a.prime.Project() + out := a.prime.Output() + bp := bpModel.NewBuildPlannerModel(a.prime.Auth()) + + var pg *output.Spinner + defer func() { + if pg != nil { + pg.Stop(locale.T("progress_fail")) + } + }() + + pg = output.StartSpinner(out, locale.T("progress_platforms"), constants.TerminalAnimationInterval) + + // Grab local commit info + localCommitID, err := localcommit.Get(pj.Dir()) + if err != nil { + return errs.Wrap(err, "Unable to get local commit") + } + oldCommit, err := bp.FetchCommit(localCommitID, pj.Owner(), pj.Name(), nil) + if err != nil { + return errs.Wrap(err, "Failed to fetch old build result") + } + + pg.Stop(locale.T("progress_found")) + pg = nil + + // Prepare updated buildscript + script := oldCommit.BuildScript() + platforms, err := script.Platforms() if err != nil { - return errs.Wrap(err, "Could not prepare parameters.") + return errs.Wrap(err, "Failed to get platforms") + } + toRemove := []strfmt.UUID{} + for _, uid := range platforms { + platform, err := model.FetchPlatformByUID(uid) + if err != nil { + return errs.Wrap(err, "Failed to get platform") + } + if model.IsPlatformMatch(platform, params.Platform.Name(), params.Platform.Version(), params.BitWidth) { + toRemove = append(toRemove, uid) + } + } + if len(toRemove) == 0 { + return errNoMatch + } + if len(toRemove) > 1 { + return errMultiMatch } - if err := requirements.NewRequirementOperation(r.prime).ExecuteRequirementOperation( - nil, - &requirements.Requirement{ - Name: params.resolvedName, - Version: params.resolvedVersion, - Operation: types.OperationRemoved, - BitWidth: params.BitWidth, - NamespaceType: &model.NamespacePlatform, - }, - ); err != nil { - return locale.WrapError(err, "err_remove_platform", "Could not remove platform.") + if err := script.RemovePlatform(toRemove[0]); err != nil { + return errs.Wrap(err, "Failed to remove platform") } - r.prime.Output().Notice(locale.Tr("platform_removed", params.resolvedName, params.resolvedVersion)) + // Update local checkout and source runtime changes + if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", params.Platform.String())); err != nil { + return errs.Wrap(err, "Failed to update local checkout") + } + + out.Notice(locale.Tr("platform_added", params.Platform.String())) return nil } + +func rationalizeRemovePlatformError(rerr *error) { + switch { + case rerr == nil: + return + + // No matches found + case errors.Is(*rerr, errNoMatch): + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("err_uninstall_platform_nomatch"), + errs.SetInput(), + ) + + // Multiple matches found + case errors.Is(*rerr, errMultiMatch): + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("err_uninstall_platform_multimatch"), + errs.SetInput(), + ) + + // Error staging a commit during install. + case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): + rationalizers.HandleCommitErrors(rerr) + + } +} diff --git a/pkg/platform/model/inventory.go b/pkg/platform/model/inventory.go index 951869db4f..7cb0c806cd 100644 --- a/pkg/platform/model/inventory.go +++ b/pkg/platform/model/inventory.go @@ -456,10 +456,10 @@ func FetchPlatformByUID(uid strfmt.UUID) (*Platform, error) { var ErrPlatformNotFound = errors.New("could not find platform matching provided criteria") func FetchPlatformByDetails(name, version string, bitwidth int) (*Platform, error) { - var platformID string + // For backward compatibility we still want to raise ErrPlatformNotFound due to name ID matching if version == "" && bitwidth == 0 { var err error - platformID, err = PlatformNameToPlatformID(name) + _, err = PlatformNameToPlatformID(name) if err != nil { return nil, errs.Wrap(err, "platform id from name failed") } @@ -470,40 +470,41 @@ func FetchPlatformByDetails(name, version string, bitwidth int) (*Platform, erro return nil, err } - lower := strings.ToLower - for _, rtPf := range runtimePlatforms { - if platformID != "" { - if rtPf.PlatformID.String() == platformID { - return rtPf, nil - } - continue - } - if rtPf.Kernel == nil || rtPf.Kernel.Name == nil { - continue - } - if lower(*rtPf.Kernel.Name) != lower(name) { - continue + if IsPlatformMatch(rtPf, name, version, bitwidth) { + return rtPf, nil } + } - if rtPf.KernelVersion == nil || rtPf.KernelVersion.Version == nil { - continue - } - if lower(*rtPf.KernelVersion.Version) != lower(version) { - continue - } + return nil, ErrPlatformNotFound +} - if rtPf.CPUArchitecture == nil { - continue - } - if rtPf.CPUArchitecture.BitWidth == nil || *rtPf.CPUArchitecture.BitWidth != strconv.Itoa(bitwidth) { - continue +func IsPlatformMatch(platform *Platform, name, version string, bitwidth int) bool { + var platformID string + if version == "" && bitwidth == 0 { + var err error + platformID, err = PlatformNameToPlatformID(name) + if err != nil || platformID == "" { + return false } + return platform.PlatformID.String() == platformID + } - return rtPf, nil + if platform.Kernel == nil || platform.Kernel.Name == nil || + !strings.EqualFold(*platform.Kernel.Name, name) { + return false + } + if version != "" && (platform.KernelVersion == nil || platform.KernelVersion.Version == nil || + !strings.EqualFold(*platform.KernelVersion.Version, version)) { + return false + } + if bitwidth != 0 && (platform.CPUArchitecture == nil || + platform.CPUArchitecture.BitWidth == nil || + !strings.EqualFold(*platform.CPUArchitecture.BitWidth, strconv.Itoa(bitwidth))) { + return false } - return nil, ErrPlatformNotFound + return true } func FetchLanguageForCommit(commitID strfmt.UUID, auth *authentication.Auth) (*Language, error) { diff --git a/test/integration/platforms_int_test.go b/test/integration/platforms_int_test.go index 4968a5dc59..937810f8c2 100644 --- a/test/integration/platforms_int_test.go +++ b/test/integration/platforms_int_test.go @@ -3,11 +3,13 @@ package integration import ( "fmt" "testing" + "time" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" + "github.com/ActiveState/termtest" ) type PlatformsIntegrationTestSuite struct { @@ -118,18 +120,14 @@ func (suite *PlatformsIntegrationTestSuite) TestPlatforms_addRemoveLatest() { suite.Require().NotContains(output, "Windows", "Windows platform should not be present after removal") cp = ts.Spawn("platforms", "add", "windows") - cp.ExpectExitCode(0) + // cp = ts.SpawnDebuggerWithOpts(e2e.OptArgs("platforms", "add", "windows")) + cp.ExpectExitCode(0, termtest.OptExpectTimeout(10*time.Minute)) cp = ts.Spawn("platforms") - expectations := []string{ - platform, - version, - "64", - } - for _, expectation := range expectations { - cp.Expect(expectation) - } - + cp.Expect(platform) + cp.Expect(version) + cp.Expect("64") + cp.ExpectExitCode(0) } func (suite *PlatformsIntegrationTestSuite) TestPlatforms_addNotFound() { From 7dc751182062197487f556a18647f53b0474a34d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 11:42:55 -0700 Subject: [PATCH 085/440] Fixed debug output not supporting multiple invocations of same command, and make ordering consistent --- internal/testhelpers/e2e/session.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 699e12aa8b..5c780a145a 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -210,7 +210,7 @@ func (s *Session) Spawn(args ...string) *SpawnedCmd { return s.SpawnCmdWithOpts(s.Exe, OptArgs(args...)) } -func (s *Session) SpawnDebuggerForCmdWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { +func (s *Session) SpawnDebuggerWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { spawnOpts := s.newSpawnOpts(opts...) args := slices.Clone(spawnOpts.Args) @@ -490,7 +490,7 @@ func (s *Session) DebugMessage(prefix string) string { prefix = prefix + "\n" } - output := map[string]string{} + output := []string{} for _, spawn := range s.spawned { name := spawn.Cmd().String() if spawn.opts.HideCmdArgs { @@ -501,24 +501,25 @@ func (s *Session) DebugMessage(prefix string) string { // If we encountered a panic it's unlikely the snapshot has enough information to be useful, so in this // case we include the full output. Which we don't normally do as it is just the dump of pty data, and // tends to be overly verbose and difficult to grok. - output[name] = strings.TrimSpace(out) + output = append(output, fmt.Sprintf("Snapshot for Cmd '%s':\n%s", name, strings.TrimSpace(out))) } else { - output[name] = strings.TrimSpace(spawn.Snapshot()) + output = append(output, fmt.Sprintf("Snapshot for Cmd '%s':\n%s", name, strings.TrimSpace(spawn.Snapshot()))) } } + logs := []string{} + for name, log := range s.DebugLogs() { + logs = append(logs, fmt.Sprintf("Log for '%s':\n%s", name, log)) + } + v, err := strutils.ParseTemplate(` {{.Prefix}}Stack: {{.Stacktrace}} -{{range $title, $value := .Outputs}} -{{$.A}}Snapshot for Cmd '{{$title}}': -{{$value}} -{{$.Z}} +{{range $value := .Outputs}} +{{$.A}}{{$value}}{{$.Z}} {{end}} -{{range $title, $value := .Logs}} -{{$.A}}Log '{{$title}}': -{{$value}} -{{$.Z}} +{{range $value := .Logs}} +{{$.A}}{{$value}}{{$.Z}} {{else}} No logs {{end}} @@ -526,7 +527,7 @@ No logs "Prefix": prefix, "Stacktrace": stacktrace.Get().String(), "Outputs": output, - "Logs": s.DebugLogs(), + "Logs": logs, "A": sectionStart, "Z": sectionEnd, }, nil) From 670b763f4acc3b429c80797afa382ed85b41396b Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 10 Sep 2024 11:43:52 -0700 Subject: [PATCH 086/440] Unify timestamp and add sleep to test --- internal/hash/file_hasher.go | 8 ++++---- internal/hash/file_hasher_test.go | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/hash/file_hasher.go b/internal/hash/file_hasher.go index 649b1ea481..e1e8cfc963 100644 --- a/internal/hash/file_hasher.go +++ b/internal/hash/file_hasher.go @@ -44,7 +44,7 @@ func (fh *FileHasher) HashFiles(files []string) (string, error) { } var hash string - cachedHash, ok := fh.cache.Get(cacheKey(file.Name(), fileInfo.ModTime().String())) + cachedHash, ok := fh.cache.Get(cacheKey(file.Name(), fileInfo.ModTime())) if ok { hash, ok = cachedHash.(string) if !ok { @@ -63,13 +63,13 @@ func (fh *FileHasher) HashFiles(files []string) (string, error) { return "", errs.Wrap(err, "Could not close file: %s", f) } - fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime().String()), hash, cache.NoExpiration) + fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) fmt.Fprintf(hasher, "%x", hash) } return base64.StdEncoding.EncodeToString(hasher.Sum(nil)), nil } -func cacheKey(file string, modTime string) string { - return fmt.Sprintf("%s-%s", file, modTime) +func cacheKey(file string, modTime time.Time) string { + return fmt.Sprintf("%s-%d", file, modTime.UTC().UnixNano()) } diff --git a/internal/hash/file_hasher_test.go b/internal/hash/file_hasher_test.go index f45c54f5f5..421b5dff17 100644 --- a/internal/hash/file_hasher_test.go +++ b/internal/hash/file_hasher_test.go @@ -195,6 +195,10 @@ func TestFileHasher_NotEqualContentChanged(t *testing.T) { assert.Equal(t, hash1, hash2) + // Change content of file1 and ensure mod time is different to avoid a cache hit. + // The time these tests take as well as the accuracy of the file system's mod time + // resolution may cause the mod time to be the same. + time.Sleep(10 * time.Millisecond) if err := os.WriteFile(file1, []byte("file1_changed"), 0644); err != nil { t.Fatal(err) } From 3f582c4009dc4d556b96a4cde2a4937da1e44872 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 11:53:03 -0700 Subject: [PATCH 087/440] Remove debug code --- test/integration/platforms_int_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/integration/platforms_int_test.go b/test/integration/platforms_int_test.go index 937810f8c2..3b21fb75b9 100644 --- a/test/integration/platforms_int_test.go +++ b/test/integration/platforms_int_test.go @@ -3,13 +3,11 @@ package integration import ( "fmt" "testing" - "time" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" - "github.com/ActiveState/termtest" ) type PlatformsIntegrationTestSuite struct { @@ -120,8 +118,7 @@ func (suite *PlatformsIntegrationTestSuite) TestPlatforms_addRemoveLatest() { suite.Require().NotContains(output, "Windows", "Windows platform should not be present after removal") cp = ts.Spawn("platforms", "add", "windows") - // cp = ts.SpawnDebuggerWithOpts(e2e.OptArgs("platforms", "add", "windows")) - cp.ExpectExitCode(0, termtest.OptExpectTimeout(10*time.Minute)) + cp.ExpectExitCode(0) cp = ts.Spawn("platforms") cp.Expect(platform) From 448d3432bf01d50a2822f74299965f6b8804809d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 12:03:47 -0700 Subject: [PATCH 088/440] Drop requirement operation and clean up --- internal/locale/locales/en-us.yaml | 27 - internal/runbits/reqop_runbit/update.go | 4 +- .../runtime/requirements/rationalize.go | 91 --- .../runtime/requirements/requirements.go | 755 ------------------ internal/runbits/runtime/trigger/trigger.go | 37 +- internal/runners/install/install.go | 3 +- internal/runners/install/rationalize.go | 2 +- internal/runners/languages/install.go | 101 --- internal/runners/languages/install_test.go | 39 - internal/runners/languages/languages.go | 11 + internal/runners/packages/uninstall.go | 60 -- internal/runners/platforms/add.go | 3 +- internal/runners/platforms/remove.go | 3 +- internal/runners/uninstall/uninstall.go | 3 +- 14 files changed, 40 insertions(+), 1099 deletions(-) delete mode 100644 internal/runbits/runtime/requirements/rationalize.go delete mode 100644 internal/runbits/runtime/requirements/requirements.go delete mode 100644 internal/runners/languages/install.go delete mode 100644 internal/runners/languages/install_test.go delete mode 100644 internal/runners/packages/uninstall.go diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 211dce172e..31698718d7 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -484,18 +484,6 @@ field_version: other: Version field_bitwidth: other: Bit Width -commit_message_added_platform: - other: "Added platform: {{.V0}} {{.V1}}bit {{.V2}}" -commit_message_updated_platform: - other: "Updated platform: {{.V0}} to {{.V1}}" -commit_message_removed_platform: - other: "Removed platform: {{.V0}} {{.V1}}bit {{.V2}}" -commit_message_added_language: - other: "Added language: {{.V0}} {{.V1}}" -commit_message_updated_language: - other: "Updated language: {{.V0}} to {{.V1}}" -commit_message_removed_language: - other: "Removed language: {{.V0}} {{.V1}}" fileutils_err_amend_file: other: "Could not edit file: [NOTICE]{{.V0}}[/RESET]" err_auth_empty_token: @@ -608,12 +596,6 @@ commit_message_added: other: "Added: {{.V0}}" commit_message_removed: other: "Removed: {{.V0}}" -commit_message_added_package: - other: "Added package: {{.V0}}@{{.V1}}" -commit_message_removed_package: - other: "Removed package: {{.V0}}" -commit_message_updated_package: - other: "Updated package: {{.V0}} to {{.V1}}" commit_message_add_initial: other: Initialize project via the State Tool commit_reqstext_message: @@ -864,10 +846,6 @@ languages_install_cmd_description: other: Update the language of a project arg_languages_install_description: other: The language to update in the form of @ -err_language_format: - other: The language and version provided is not formatting correctly. It must be in the form of @ -err_language_mismatch: - other: Cannot change languages. Only changes to the current project's language version are allowed err_no_recipes: other: No build recipes could be generated for the current project err_order_unknown: @@ -1521,11 +1499,6 @@ err_headless: other: Cannot operate on a headless project. Please visit {{.V0}} to convert your project and try again. notice_needs_buildscript_reset: other: Your project is missing its buildscript file. Please run '[ACTIONABLE]state reset LOCAL[/RESET]' to recreate it. - -err_initial_no_requirement: - other: | - Could not find compatible requirement for initial commit. Please ensure that you are trying to install a valid package. - To check for available packages run '[ACTIONABLE]state search [/RESET]'. manifest_deprecation_warning: other: | [WARNING]Warning:[/RESET] This command is deprecated. Please use '[ACTIONABLE]state manifest[/RESET]' instead. diff --git a/internal/runbits/reqop_runbit/update.go b/internal/runbits/reqop_runbit/update.go index 447cb85c5b..9964a5a7c9 100644 --- a/internal/runbits/reqop_runbit/update.go +++ b/internal/runbits/reqop_runbit/update.go @@ -52,7 +52,7 @@ type Requirement struct { Version []types.VersionRequirement } -func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit *buildplanner.Commit, commitMsg string) error { +func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit *buildplanner.Commit, commitMsg string, trigger trigger.Trigger) error { pj := prime.Project() out := prime.Output() cfg := prime.Config() @@ -96,7 +96,7 @@ func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit // Start runtime sourcing UI if !cfg.GetBool(constants.AsyncRuntimeConfig) { // refresh or install runtime - _, err := runtime_runbit.Update(prime, trigger.TriggerInstall, + _, err := runtime_runbit.Update(prime, trigger, runtime_runbit.WithCommit(newCommit), runtime_runbit.WithoutBuildscriptValidation(), ) diff --git a/internal/runbits/runtime/requirements/rationalize.go b/internal/runbits/runtime/requirements/rationalize.go deleted file mode 100644 index 962eebfcf4..0000000000 --- a/internal/runbits/runtime/requirements/rationalize.go +++ /dev/null @@ -1,91 +0,0 @@ -package requirements - -import ( - "errors" - - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/runbits/rationalize" - runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" - bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" - "github.com/ActiveState/cli/pkg/platform/model" -) - -func (r *RequirementOperation) rationalizeError(err *error) { - var tooManyMatchesErr *model.ErrTooManyMatches - var noMatchesErr *ErrNoMatches - var buildPlannerErr *bpResp.BuildPlannerError - var resolveNamespaceErr *ResolveNamespaceError - - switch { - case err == nil: - return - - // Too many matches - case errors.As(*err, &tooManyMatchesErr): - *err = errs.WrapUserFacing(*err, - locale.Tr("err_searchingredient_toomany", tooManyMatchesErr.Query), - errs.SetInput()) - - // No matches, and no alternate suggestions - case errors.As(*err, &noMatchesErr) && noMatchesErr.Alternatives == nil: - *err = errs.WrapUserFacing(*err, - locale.Tr("package_ingredient_alternatives_nosuggest", noMatchesErr.Query), - errs.SetInput()) - - // No matches, but have alternate suggestions - case errors.As(*err, &noMatchesErr) && noMatchesErr.Alternatives != nil: - *err = errs.WrapUserFacing(*err, - locale.Tr("package_ingredient_alternatives", noMatchesErr.Query, *noMatchesErr.Alternatives), - errs.SetInput()) - - // We communicate buildplanner errors verbatim as the intend is that these are curated by the buildplanner - case errors.As(*err, &buildPlannerErr): - *err = errs.WrapUserFacing(*err, - buildPlannerErr.LocaleError(), - errs.SetIf(buildPlannerErr.InputError(), errs.SetInput())) - - // Headless - case errors.Is(*err, rationalize.ErrHeadless): - *err = errs.WrapUserFacing(*err, - locale.Tl( - "err_requirement_headless", - "Cannot update requirements for a headless project. Please visit {{.V0}} to convert your project and try again.", - r.Project.URL(), - ), - errs.SetInput()) - - case errors.Is(*err, errNoRequirements): - *err = errs.WrapUserFacing(*err, - locale.Tl("err_no_requirements", - "No requirements have been provided for this operation.", - ), - errs.SetInput(), - ) - - case errors.As(*err, &resolveNamespaceErr): - *err = errs.WrapUserFacing(*err, - locale.Tl("err_resolve_namespace", - "Could not resolve namespace for requirement '{{.V0}}'.", - resolveNamespaceErr.Name, - ), - errs.SetInput(), - ) - - case errors.Is(*err, errInitialNoRequirement): - *err = errs.WrapUserFacing(*err, - locale.T("err_initial_no_requirement"), - errs.SetInput(), - ) - - case errors.Is(*err, errNoLanguage): - *err = errs.WrapUserFacing(*err, - locale.Tl("err_no_language", "Could not determine which language namespace to search for packages in. Please supply the language flag."), - errs.SetInput(), - ) - - default: - runtime_runbit.RationalizeSolveError(r.Project, r.Auth, err) - - } -} diff --git a/internal/runbits/runtime/requirements/requirements.go b/internal/runbits/runtime/requirements/requirements.go deleted file mode 100644 index f7625b5e1d..0000000000 --- a/internal/runbits/runtime/requirements/requirements.go +++ /dev/null @@ -1,755 +0,0 @@ -package requirements - -import ( - "errors" - "fmt" - "regexp" - "strconv" - "strings" - "time" - - "github.com/ActiveState/cli/internal/analytics" - anaConsts "github.com/ActiveState/cli/internal/analytics/constants" - "github.com/ActiveState/cli/internal/captain" - "github.com/ActiveState/cli/internal/config" - "github.com/ActiveState/cli/internal/constants" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/multilog" - "github.com/ActiveState/cli/internal/output" - "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/prompt" - "github.com/ActiveState/cli/internal/rtutils/ptr" - buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" - "github.com/ActiveState/cli/internal/runbits/cves" - "github.com/ActiveState/cli/internal/runbits/dependencies" - "github.com/ActiveState/cli/internal/runbits/rationalize" - runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" - "github.com/ActiveState/cli/internal/runbits/runtime/trigger" - "github.com/ActiveState/cli/pkg/buildscript" - "github.com/ActiveState/cli/pkg/localcommit" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - medmodel "github.com/ActiveState/cli/pkg/platform/api/mediator/model" - "github.com/ActiveState/cli/pkg/platform/authentication" - "github.com/ActiveState/cli/pkg/platform/model" - bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" - "github.com/ActiveState/cli/pkg/project" - "github.com/ActiveState/cli/pkg/runtime" - "github.com/ActiveState/cli/pkg/sysinfo" - "github.com/go-openapi/strfmt" - "github.com/thoas/go-funk" -) - -type PackageVersion struct { - captain.NameVersionValue -} - -func (pv *PackageVersion) Set(arg string) error { - err := pv.NameVersionValue.Set(arg) - if err != nil { - return locale.WrapInputError(err, "err_package_format", "The package and version provided is not formatting correctly. It must be in the form of @") - } - return nil -} - -type RequirementOperation struct { - prime primeable - // The remainder is redundant with the above. Refactoring this will follow in a later story so as not to blow - // up the one that necessitates adding the primer at this level. - // https://activestatef.atlassian.net/browse/DX-2869 - Output output.Outputer - Prompt prompt.Prompter - Project *project.Project - Auth *authentication.Auth - Config *config.Instance - Analytics analytics.Dispatcher - SvcModel *model.SvcModel -} - -type primeable interface { - primer.Outputer - primer.Prompter - primer.Projecter - primer.Auther - primer.Configurer - primer.Analyticer - primer.SvcModeler -} - -func NewRequirementOperation(prime primeable) *RequirementOperation { - return &RequirementOperation{ - prime, - prime.Output(), - prime.Prompt(), - prime.Project(), - prime.Auth(), - prime.Config(), - prime.Analytics(), - prime.SvcModel(), - } -} - -const latestVersion = "latest" - -type ErrNoMatches struct { - *locale.LocalizedError - Query string - Alternatives *string -} - -var errNoRequirements = errs.New("No requirements were provided") - -var errInitialNoRequirement = errs.New("Could not find compatible requirement for initial commit") - -var errNoLanguage = errs.New("No language") - -var versionRe = regexp.MustCompile(`^\d(\.\d+)*$`) - -// Requirement represents a package, language or platform requirement -// For now, be aware that you should never provide BOTH ns AND nsType, one or the other should always be nil, but never both. -// The refactor should clean this up. -type Requirement struct { - Name string - Version string - Revision *int - BitWidth int // Only needed for platform requirements - Namespace *model.Namespace - NamespaceType *model.NamespaceType - Operation types.Operation - - // The following fields are set during execution - langName string - langVersion string - validatePkg bool - appendVersionWildcard bool - originalRequirementName string - versionRequirements []types.VersionRequirement -} - -// ExecuteRequirementOperation executes the operation on the requirement -// This has become quite unwieldy, and is ripe for a refactor - https://activestatef.atlassian.net/browse/DX-1897 -func (r *RequirementOperation) ExecuteRequirementOperation(ts *time.Time, requirements ...*Requirement) (rerr error) { - defer r.rationalizeError(&rerr) - - if len(requirements) == 0 { - return errNoRequirements - } - - out := r.Output - var pg *output.Spinner - defer func() { - if pg != nil { - // This is a bit awkward, but it would be even more awkward to manually address this for every error condition - pg.Stop(locale.T("progress_fail")) - } - }() - - if r.Project == nil { - return rationalize.ErrNoProject - } - if r.Project.IsHeadless() { - return rationalize.ErrHeadless - } - out.Notice(locale.Tr("operating_message", r.Project.NamespaceString(), r.Project.Dir())) - - if err := r.resolveNamespaces(ts, requirements...); err != nil { - return errs.Wrap(err, "Could not resolve namespaces") - } - - if err := r.validatePackages(requirements...); err != nil { - return errs.Wrap(err, "Could not validate packages") - } - - parentCommitID, err := localcommit.Get(r.Project.Dir()) - if err != nil { - return errs.Wrap(err, "Unable to get local commit") - } - hasParentCommit := parentCommitID != "" - - pg = output.StartSpinner(r.Output, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval) - - if err := r.checkForUpdate(parentCommitID, requirements...); err != nil { - return locale.WrapError(err, "err_check_for_update", "Could not check for requirements updates") - } - - if !hasParentCommit { - // Use first requirement to extract language for initial commit - var requirement *Requirement - for _, r := range requirements { - if r.Namespace.Type() == model.NamespacePackage || r.Namespace.Type() == model.NamespaceBundle { - requirement = r - break - } - } - - if requirement == nil { - return errInitialNoRequirement - } - - languageFromNs := model.LanguageFromNamespace(requirement.Namespace.String()) - parentCommitID, err = model.CommitInitial(sysinfo.OS().String(), languageFromNs, requirement.langVersion, r.Auth) - if err != nil { - return locale.WrapError(err, "err_install_no_project_commit", "Could not create initial commit for new project") - } - } - - if err := r.resolveRequirements(requirements...); err != nil { - return locale.WrapError(err, "err_resolve_requirements", "Could not resolve one or more requirements") - } - - bp := bpModel.NewBuildPlannerModel(r.Auth) - script, err := r.prepareBuildScript(bp, parentCommitID, requirements, ts) - if err != nil { - return errs.Wrap(err, "Could not prepare build script") - } - - params := bpModel.StageCommitParams{ - Owner: r.Project.Owner(), - Project: r.Project.Name(), - ParentCommit: string(parentCommitID), - Description: commitMessage(requirements...), - Script: script, - } - - // Solve runtime - commit, err := bp.StageCommit(params) - if err != nil { - return errs.Wrap(err, "Could not stage commit") - } - - ns := requirements[0].Namespace - var trig trigger.Trigger - switch ns.Type() { - case model.NamespaceLanguage: - trig = trigger.TriggerLanguage - case model.NamespacePlatform: - trig = trigger.TriggerPlatform - default: - trig = trigger.TriggerPackage - } - - oldCommit, err := bp.FetchCommit(parentCommitID, r.Project.Owner(), r.Project.Name(), nil) - if err != nil { - return errs.Wrap(err, "Failed to fetch old build result") - } - - pg.Stop(locale.T("progress_success")) - pg = nil - - dependencies.OutputChangeSummary(r.prime.Output(), commit.BuildPlan(), oldCommit.BuildPlan()) - - // Report CVEs - if err := cves.NewCveReport(r.prime).Report(commit.BuildPlan(), oldCommit.BuildPlan()); err != nil { - return errs.Wrap(err, "Could not report CVEs") - } - - // Start runtime update UI - if !r.Config.GetBool(constants.AsyncRuntimeConfig) { - out.Notice("") - - // refresh or install runtime - _, err = runtime_runbit.Update(r.prime, trig, - runtime_runbit.WithCommit(commit), - runtime_runbit.WithoutBuildscriptValidation(), - ) - if err != nil { - if !IsBuildError(err) { - // If the error is not a build error we want to retain the changes - if err2 := r.updateCommitID(commit.CommitID); err2 != nil { - return errs.Pack(err, locale.WrapError(err2, "err_package_update_commit_id")) - } - } - return errs.Wrap(err, "Failed to refresh runtime") - } - } - - if err := r.updateCommitID(commit.CommitID); err != nil { - return locale.WrapError(err, "err_package_update_commit_id") - } - - if !hasParentCommit { - out.Notice(locale.Tr("install_initial_success", r.Project.Source().Path())) - } - - // Print the result - r.outputResults(requirements...) - - out.Notice(locale.T("operation_success_local")) - - return nil -} - -func (r *RequirementOperation) prepareBuildScript(bp *bpModel.BuildPlanner, parentCommit strfmt.UUID, requirements []*Requirement, ts *time.Time) (*buildscript.BuildScript, error) { - script, err := bp.GetBuildScript(string(parentCommit)) - if err != nil { - return nil, errs.Wrap(err, "Failed to get build expression") - } - - if ts != nil { - script.SetAtTime(*ts) - } else { - // If no atTime was provided then we need to ensure that the atTime in the script is updated to use - // the most recent, which is either the current value or the platform latest. - latest, err := model.FetchLatestTimeStamp(r.Auth) - if err != nil { - return nil, errs.Wrap(err, "Unable to fetch latest Platform timestamp") - } - atTime := script.AtTime() - if atTime == nil || latest.After(*atTime) { - script.SetAtTime(latest) - } - } - - for _, req := range requirements { - if req.Namespace.String() == types.NamespacePlatform { - err = script.UpdatePlatform(req.Operation, strfmt.UUID(req.Name)) - if err != nil { - return nil, errs.Wrap(err, "Failed to update build expression with platform") - } - } else { - requirement := types.Requirement{ - Namespace: req.Namespace.String(), - Name: req.Name, - VersionRequirement: req.versionRequirements, - Revision: req.Revision, - } - - err = script.UpdateRequirement(req.Operation, requirement) - if err != nil { - return nil, errs.Wrap(err, "Failed to update build expression with requirement") - } - } - } - - return script, nil -} - -type ResolveNamespaceError struct { - Name string -} - -func (e ResolveNamespaceError) Error() string { - return "unable to resolve namespace" -} - -func (r *RequirementOperation) resolveNamespaces(ts *time.Time, requirements ...*Requirement) error { - for _, requirement := range requirements { - if err := r.resolveNamespace(ts, requirement); err != nil { - if err != errNoLanguage { - err = errs.Pack(err, &ResolveNamespaceError{requirement.Name}) - } - return errs.Wrap(err, "Unable to resolve namespace") - } - } - return nil -} - -func (r *RequirementOperation) resolveNamespace(ts *time.Time, requirement *Requirement) error { - requirement.langName = "undetermined" - - if requirement.NamespaceType != nil { - switch *requirement.NamespaceType { - case model.NamespacePackage, model.NamespaceBundle: - commitID, err := localcommit.Get(r.Project.Dir()) - if err != nil { - return errs.Wrap(err, "Unable to get local commit") - } - - language, err := model.LanguageByCommit(commitID, r.Auth) - if err != nil { - logging.Debug("Could not get language from project: %v", err) - } - if language.Name == "" { - return errNoLanguage - } - requirement.langName = language.Name - requirement.Namespace = ptr.To(model.NewNamespacePkgOrBundle(requirement.langName, *requirement.NamespaceType)) - case model.NamespaceLanguage: - requirement.Namespace = ptr.To(model.NewNamespaceLanguage()) - case model.NamespacePlatform: - requirement.Namespace = ptr.To(model.NewNamespacePlatform()) - } - } - - ns := requirement.Namespace - nsType := requirement.NamespaceType - requirement.validatePkg = requirement.Operation == types.OperationAdded && ns != nil && (ns.Type() == model.NamespacePackage || ns.Type() == model.NamespaceBundle || ns.Type() == model.NamespaceLanguage) - if (ns == nil || !ns.IsValid()) && nsType != nil && (*nsType == model.NamespacePackage || *nsType == model.NamespaceBundle) { - pg := output.StartSpinner(r.Output, locale.Tr("progress_pkg_nolang", requirement.Name), constants.TerminalAnimationInterval) - - supported, err := model.FetchSupportedLanguages(sysinfo.OS().String()) - if err != nil { - return errs.Wrap(err, "Failed to retrieve the list of supported languages") - } - - var nsv model.Namespace - var supportedLang *medmodel.SupportedLanguage - requirement.Name, nsv, supportedLang, err = resolvePkgAndNamespace(r.Prompt, requirement.Name, *requirement.NamespaceType, supported, ts, r.Auth) - if err != nil { - return errs.Wrap(err, "Could not resolve pkg and namespace") - } - requirement.Namespace = &nsv - requirement.langVersion = supportedLang.DefaultVersion - requirement.langName = supportedLang.Name - - requirement.validatePkg = false - - pg.Stop(locale.T("progress_found")) - } - - if requirement.Namespace == nil { - return locale.NewError("err_package_invalid_namespace_detected", "No valid namespace could be detected") - } - - return nil -} - -func (r *RequirementOperation) validatePackages(requirements ...*Requirement) error { - var requirementsToValidate []*Requirement - for _, requirement := range requirements { - if !requirement.validatePkg { - continue - } - requirementsToValidate = append(requirementsToValidate, requirement) - } - - if len(requirementsToValidate) == 0 { - return nil - } - - pg := output.StartSpinner(r.Output, locale.Tr("progress_search", strings.Join(requirementNames(requirementsToValidate...), ", ")), constants.TerminalAnimationInterval) - for _, requirement := range requirementsToValidate { - if err := r.validatePackage(requirement); err != nil { - return errs.Wrap(err, "Could not validate package") - } - } - pg.Stop(locale.T("progress_found")) - - return nil -} - -func (r *RequirementOperation) validatePackage(requirement *Requirement) error { - if strings.ToLower(requirement.Version) == latestVersion { - requirement.Version = "" - } - - requirement.originalRequirementName = requirement.Name - normalized, err := model.FetchNormalizedName(*requirement.Namespace, requirement.Name, r.Auth) - if err != nil { - multilog.Error("Failed to normalize '%s': %v", requirement.Name, err) - } - - packages, err := model.SearchIngredientsStrict(requirement.Namespace.String(), normalized, false, false, nil, r.Auth) // ideally case-sensitive would be true (PB-4371) - if err != nil { - return locale.WrapError(err, "package_err_cannot_obtain_search_results") - } - - if len(packages) == 0 { - suggestions, err := getSuggestions(*requirement.Namespace, requirement.Name, r.Auth) - if err != nil { - multilog.Error("Failed to retrieve suggestions: %v", err) - } - - if len(suggestions) == 0 { - return &ErrNoMatches{ - locale.WrapExternalError(err, "package_ingredient_alternatives_nosuggest", "", requirement.Name), - requirement.Name, nil} - } - - return &ErrNoMatches{ - locale.WrapExternalError(err, "package_ingredient_alternatives", "", requirement.Name, strings.Join(suggestions, "\n")), - requirement.Name, ptr.To(strings.Join(suggestions, "\n"))} - } - - if normalized != "" && normalized != requirement.Name { - requirement.Name = normalized - } - - // If a bare version number was given, and if it is a partial version number (e.g. requests@2), - // we'll want to ultimately append a '.x' suffix. - if versionRe.MatchString(requirement.Version) { - for _, knownVersion := range packages[0].Versions { - if knownVersion.Version == requirement.Version { - break - } else if strings.HasPrefix(knownVersion.Version, requirement.Version) { - requirement.appendVersionWildcard = true - } - } - } - - return nil -} - -func (r *RequirementOperation) checkForUpdate(parentCommitID strfmt.UUID, requirements ...*Requirement) error { - for _, requirement := range requirements { - // Check if this is an addition or an update - if requirement.Operation == types.OperationAdded && parentCommitID != "" { - req, err := model.GetRequirement(parentCommitID, *requirement.Namespace, requirement.Name, r.Auth) - if err != nil { - return errs.Wrap(err, "Could not get requirement") - } - if req != nil { - requirement.Operation = types.OperationUpdated - } - } - - r.Analytics.EventWithLabel( - anaConsts.CatPackageOp, fmt.Sprintf("%s-%s", requirement.Operation, requirement.langName), requirement.Name, - ) - } - - return nil -} - -func (r *RequirementOperation) resolveRequirements(requirements ...*Requirement) error { - for _, requirement := range requirements { - if err := r.resolveRequirement(requirement); err != nil { - return errs.Wrap(err, "Could not resolve requirement") - } - } - return nil -} - -func (r *RequirementOperation) resolveRequirement(requirement *Requirement) error { - var err error - requirement.Name, requirement.Version, err = model.ResolveRequirementNameAndVersion(requirement.Name, requirement.Version, requirement.BitWidth, *requirement.Namespace, r.Auth) - if err != nil { - return errs.Wrap(err, "Could not resolve requirement name and version") - } - - versionString := requirement.Version - if requirement.appendVersionWildcard { - versionString += ".x" - } - - requirement.versionRequirements, err = bpModel.VersionStringToRequirements(versionString) - if err != nil { - return errs.Wrap(err, "Could not process version string into requirements") - } - - return nil -} - -func (r *RequirementOperation) updateCommitID(commitID strfmt.UUID) error { - if err := localcommit.Set(r.Project.Dir(), commitID.String()); err != nil { - return locale.WrapError(err, "err_package_update_commit_id") - } - - if r.Config.GetBool(constants.OptinBuildscriptsConfig) { - bp := bpModel.NewBuildPlannerModel(r.Auth) - script, err := bp.GetBuildScript(commitID.String()) - if err != nil { - return errs.Wrap(err, "Could not get remote build expr and time") - } - - err = buildscript_runbit.Update(r.Project, script) - if err != nil { - return locale.WrapError(err, "err_update_build_script") - } - } - - return nil -} - -func (r *RequirementOperation) outputResults(requirements ...*Requirement) { - for _, requirement := range requirements { - r.outputResult(requirement) - } -} - -func (r *RequirementOperation) outputResult(requirement *Requirement) { - // Print the result - message := locale.Tr(fmt.Sprintf("%s_version_%s", requirement.Namespace.Type(), requirement.Operation), requirement.Name, requirement.Version) - if requirement.Version == "" { - message = locale.Tr(fmt.Sprintf("%s_%s", requirement.Namespace.Type(), requirement.Operation), requirement.Name) - } - - r.Output.Print(output.Prepare( - message, - &struct { - Name string `json:"name"` - Version string `json:"version,omitempty"` - Type string `json:"type"` - Operation string `json:"operation"` - }{ - requirement.Name, - requirement.Version, - requirement.Namespace.Type().String(), - requirement.Operation.String(), - })) - - if requirement.originalRequirementName != requirement.Name && requirement.Operation != types.OperationRemoved { - r.Output.Notice(locale.Tl("package_version_differs", - "Note: the actual package name ({{.V0}}) is different from the requested package name ({{.V1}})", - requirement.Name, requirement.originalRequirementName)) - } -} - -func supportedLanguageByName(supported []medmodel.SupportedLanguage, langName string) medmodel.SupportedLanguage { - return funk.Find(supported, func(l medmodel.SupportedLanguage) bool { return l.Name == langName }).(medmodel.SupportedLanguage) -} - -func resolvePkgAndNamespace(prompt prompt.Prompter, packageName string, nsType model.NamespaceType, supported []medmodel.SupportedLanguage, ts *time.Time, auth *authentication.Auth) (string, model.Namespace, *medmodel.SupportedLanguage, error) { - ns := model.NewBlankNamespace() - - // Find ingredients that match the input query - ingredients, err := model.SearchIngredientsStrict("", packageName, false, false, ts, auth) - if err != nil { - return "", ns, nil, locale.WrapError(err, "err_pkgop_search_err", "Failed to check for ingredients.") - } - - ingredients, err = model.FilterSupportedIngredients(supported, ingredients) - if err != nil { - return "", ns, nil, errs.Wrap(err, "Failed to filter out unsupported packages") - } - - choices := []string{} - values := map[string][]string{} - for _, i := range ingredients { - language := model.LanguageFromNamespace(*i.Ingredient.PrimaryNamespace) - - // Generate ingredient choices to present to the user - name := fmt.Sprintf("%s (%s)", *i.Ingredient.Name, language) - choices = append(choices, name) - values[name] = []string{*i.Ingredient.Name, language} - } - - if len(choices) == 0 { - return "", ns, nil, locale.WrapExternalError(err, "package_ingredient_alternatives_nolang", "", packageName) - } - - // If we only have one ingredient match we're done; return it. - if len(choices) == 1 { - language := values[choices[0]][1] - supportedLang := supportedLanguageByName(supported, language) - return values[choices[0]][0], model.NewNamespacePkgOrBundle(language, nsType), &supportedLang, nil - } - - // Prompt the user with the ingredient choices - choice, err := prompt.Select( - locale.Tl("prompt_pkgop_ingredient", "Multiple Matches"), - locale.Tl("prompt_pkgop_ingredient_msg", "Your query has multiple matches. Which one would you like to use?"), - choices, &choices[0], - ) - if err != nil { - return "", ns, nil, locale.WrapError(err, "err_pkgop_select", "Need a selection.") - } - - // Return the user selected ingredient - language := values[choice][1] - supportedLang := supportedLanguageByName(supported, language) - return values[choice][0], model.NewNamespacePkgOrBundle(language, nsType), &supportedLang, nil -} - -func getSuggestions(ns model.Namespace, name string, auth *authentication.Auth) ([]string, error) { - results, err := model.SearchIngredients(ns.String(), name, false, nil, auth) - if err != nil { - return []string{}, locale.WrapError(err, "package_ingredient_err_search", "Failed to resolve ingredient named: {{.V0}}", name) - } - - maxResults := 5 - if len(results) > maxResults { - results = results[:maxResults] - } - - suggestions := make([]string, 0, maxResults+1) - for _, result := range results { - suggestions = append(suggestions, fmt.Sprintf(" - %s", *result.Ingredient.Name)) - } - - return suggestions, nil -} - -func commitMessage(requirements ...*Requirement) string { - switch len(requirements) { - case 0: - return "" - case 1: - return requirementCommitMessage(requirements[0]) - default: - return commitMessageMultiple(requirements...) - } -} - -func requirementCommitMessage(req *Requirement) string { - switch req.Namespace.Type() { - case model.NamespaceLanguage: - return languageCommitMessage(req.Operation, req.Name, req.Version) - case model.NamespacePlatform: - return platformCommitMessage(req.Operation, req.Name, req.Version, req.BitWidth) - case model.NamespacePackage, model.NamespaceBundle: - return packageCommitMessage(req.Operation, req.Name, req.Version) - } - return "" -} - -func languageCommitMessage(op types.Operation, name, version string) string { - var msgL10nKey string - switch op { - case types.OperationAdded: - msgL10nKey = "commit_message_added_language" - case types.OperationUpdated: - msgL10nKey = "commit_message_updated_language" - case types.OperationRemoved: - msgL10nKey = "commit_message_removed_language" - } - - return locale.Tr(msgL10nKey, name, version) -} - -func platformCommitMessage(op types.Operation, name, version string, word int) string { - var msgL10nKey string - switch op { - case types.OperationAdded: - msgL10nKey = "commit_message_added_platform" - case types.OperationUpdated: - msgL10nKey = "commit_message_updated_platform" - case types.OperationRemoved: - msgL10nKey = "commit_message_removed_platform" - } - - return locale.Tr(msgL10nKey, name, strconv.Itoa(word), version) -} - -func packageCommitMessage(op types.Operation, name, version string) string { - var msgL10nKey string - switch op { - case types.OperationAdded: - msgL10nKey = "commit_message_added_package" - case types.OperationUpdated: - msgL10nKey = "commit_message_updated_package" - case types.OperationRemoved: - msgL10nKey = "commit_message_removed_package" - } - - if version == "" { - version = locale.Tl("package_version_auto", "auto") - } - return locale.Tr(msgL10nKey, name, version) -} - -func commitMessageMultiple(requirements ...*Requirement) string { - var commitDetails []string - for _, req := range requirements { - commitDetails = append(commitDetails, requirementCommitMessage(req)) - } - - return locale.Tl("commit_message_multiple", "Committing changes to multiple requirements: {{.V0}}", strings.Join(commitDetails, ", ")) -} - -func requirementNames(requirements ...*Requirement) []string { - var names []string - for _, requirement := range requirements { - names = append(names, requirement.Name) - } - return names -} - -func IsBuildError(err error) bool { - var errBuild *runtime.BuildError - var errBuildPlanner *response.BuildPlannerError - - return errors.As(err, &errBuild) || errors.As(err, &errBuildPlanner) -} diff --git a/internal/runbits/runtime/trigger/trigger.go b/internal/runbits/runtime/trigger/trigger.go index a64f2aa1a5..80c752d9b7 100644 --- a/internal/runbits/runtime/trigger/trigger.go +++ b/internal/runbits/runtime/trigger/trigger.go @@ -11,25 +11,24 @@ func (t Trigger) String() string { } const ( - TriggerActivate Trigger = "activate" - TriggerScript Trigger = "script" - TriggerDeploy Trigger = "deploy" - TriggerExec Trigger = "exec-cmd" - TriggerExecutor Trigger = "exec" - TriggerSwitch Trigger = "switch" - TriggerImport Trigger = "import" - TriggerInit Trigger = "init" - TriggerPackage Trigger = "package" - TriggerLanguage Trigger = "language" - TriggerPlatform Trigger = "platform" - TriggerPull Trigger = "pull" - TriggerRefresh Trigger = "refresh" - TriggerReset Trigger = "reset" - TriggerRevert Trigger = "revert" - TriggerShell Trigger = "shell" - TriggerCheckout Trigger = "checkout" - TriggerUse Trigger = "use" - TriggerInstall Trigger = "install" + TriggerActivate Trigger = "activate" + TriggerScript Trigger = "script" + TriggerDeploy Trigger = "deploy" + TriggerExec Trigger = "exec-cmd" + TriggerExecutor Trigger = "exec" + TriggerSwitch Trigger = "switch" + TriggerImport Trigger = "import" + TriggerInit Trigger = "init" + TriggerPlatform Trigger = "platform" + TriggerPull Trigger = "pull" + TriggerRefresh Trigger = "refresh" + TriggerReset Trigger = "reset" + TriggerRevert Trigger = "revert" + TriggerShell Trigger = "shell" + TriggerCheckout Trigger = "checkout" + TriggerUse Trigger = "use" + TriggerInstall Trigger = "install" + TriggerUninstall Trigger = "uninstall" ) func NewExecTrigger(cmd string) Trigger { diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index d0aaa90432..c723e66057 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -17,6 +17,7 @@ import ( "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/internal/runbits/runtime/trigger" "github.com/ActiveState/cli/internal/sliceutils" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" @@ -149,7 +150,7 @@ func (i *Install) Run(params Params) (rerr error) { } // Update local checkout and source runtime changes - if err := reqop_runbit.UpdateAndReload(i.prime, script, oldCommit, locale.Tr("commit_message_added", reqs.String())); err != nil { + if err := reqop_runbit.UpdateAndReload(i.prime, script, oldCommit, locale.Tr("commit_message_added", reqs.String()), trigger.TriggerInstall); err != nil { return errs.Wrap(err, "Failed to update local checkout") } diff --git a/internal/runners/install/rationalize.go b/internal/runners/install/rationalize.go index ecad652a7d..4f92736b7b 100644 --- a/internal/runners/install/rationalize.go +++ b/internal/runners/install/rationalize.go @@ -54,7 +54,7 @@ func (i *Install) rationalizeError(rerr *error) { func (i *Install) getSuggestions(req *requirement, languages []model.Language) ([]string, error) { ingredients, err := model.SearchIngredients(req.input.Namespace, req.input.Name, false, nil, i.prime.Auth()) if err != nil { - return []string{}, locale.WrapError(err, "package_ingredient_err_search", "Failed to resolve ingredient named: {{.V0}}", req.input.Name) + return []string{}, locale.WrapError(err, "err_package_ingredient_search", "Failed to resolve ingredient named: {{.V0}}", req.input.Name) } // Filter out irrelevant ingredients diff --git a/internal/runners/languages/install.go b/internal/runners/languages/install.go deleted file mode 100644 index d2f208dae6..0000000000 --- a/internal/runners/languages/install.go +++ /dev/null @@ -1,101 +0,0 @@ -package languages - -import ( - "strings" - - "github.com/ActiveState/cli/internal/runbits/runtime/requirements" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - - "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/runbits/rationalize" - "github.com/ActiveState/cli/pkg/platform/authentication" - "github.com/ActiveState/cli/pkg/platform/model" - "github.com/ActiveState/cli/pkg/project" -) - -type Update struct { - prime primeable -} - -type primeable interface { - primer.Outputer - primer.Prompter - primer.Projecter - primer.Auther - primer.Configurer - primer.Analyticer - primer.SvcModeler -} - -func NewUpdate(prime primeable) *Update { - return &Update{ - prime: prime, - } -} - -type UpdateParams struct { - Language string -} - -func (u *Update) Run(params *UpdateParams) error { - lang, err := parseLanguage(params.Language) - if err != nil { - return err - } - - if u.prime.Project() == nil { - return rationalize.ErrNoProject - } - - err = ensureLanguageProject(lang, u.prime.Project(), u.prime.Auth()) - if err != nil { - return err - } - - op := requirements.NewRequirementOperation(u.prime) - return op.ExecuteRequirementOperation(nil, &requirements.Requirement{ - Name: lang.Name, - Version: lang.Version, - NamespaceType: &model.NamespaceLanguage, - Operation: types.OperationAdded, - }) -} - -func parseLanguage(langName string) (*model.Language, error) { - if !strings.Contains(langName, "@") { - return &model.Language{ - Name: langName, - Version: "", - }, nil - } - - split := strings.Split(langName, "@") - if len(split) != 2 { - return nil, locale.NewError("err_language_format") - } - name := split[0] - version := split[1] - - return &model.Language{ - Name: name, - Version: version, - }, nil -} - -func ensureLanguageProject(language *model.Language, project *project.Project, auth *authentication.Auth) error { - targetCommitID, err := model.BranchCommitID(project.Owner(), project.Name(), project.BranchName()) - if err != nil { - return err - } - - platformLanguage, err := model.FetchLanguageForCommit(*targetCommitID, auth) - if err != nil { - return err - } - - if platformLanguage.Name != language.Name { - return locale.NewInputError("err_language_mismatch") - } - return nil -} diff --git a/internal/runners/languages/install_test.go b/internal/runners/languages/install_test.go deleted file mode 100644 index b41d0f6aaa..0000000000 --- a/internal/runners/languages/install_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package languages - -import ( - "reflect" - "testing" - - "github.com/ActiveState/cli/pkg/platform/model" -) - -func Test_parseLanguage(t *testing.T) { - type args struct { - langName string - } - tests := []struct { - name string - args args - want *model.Language - wantErr bool - }{ - { - "Language with version", - args{"Python@2"}, - &model.Language{Name: "Python", Version: "2"}, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := parseLanguage(tt.args.langName) - if (err != nil) != tt.wantErr { - t.Errorf("parseLanguage() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("parseLanguage() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/runners/languages/languages.go b/internal/runners/languages/languages.go index c890c2a3e5..47dbae1864 100644 --- a/internal/runners/languages/languages.go +++ b/internal/runners/languages/languages.go @@ -7,6 +7,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/buildplan" "github.com/ActiveState/cli/pkg/localcommit" @@ -16,6 +17,16 @@ import ( "github.com/ActiveState/cli/pkg/project" ) +type primeable interface { + primer.Outputer + primer.Prompter + primer.Projecter + primer.Auther + primer.Configurer + primer.Analyticer + primer.SvcModeler +} + // Languages manages the listing execution context. type Languages struct { out output.Outputer diff --git a/internal/runners/packages/uninstall.go b/internal/runners/packages/uninstall.go deleted file mode 100644 index 6deeccae73..0000000000 --- a/internal/runners/packages/uninstall.go +++ /dev/null @@ -1,60 +0,0 @@ -package packages - -import ( - "github.com/ActiveState/cli/internal/captain" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/rtutils/ptr" - "github.com/ActiveState/cli/internal/runbits/commits_runbit" - "github.com/ActiveState/cli/internal/runbits/rationalize" - "github.com/ActiveState/cli/internal/runbits/runtime/requirements" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - "github.com/ActiveState/cli/pkg/platform/model" -) - -// UninstallRunParams tracks the info required for running Uninstall. -type UninstallRunParams struct { - Packages captain.PackagesValueNoVersion -} - -// Uninstall manages the uninstalling execution context. -type Uninstall struct { - prime primeable -} - -// NewUninstall prepares an uninstallation execution context for use. -func NewUninstall(prime primeable) *Uninstall { - return &Uninstall{prime} -} - -// Run executes the uninstall behavior. -func (u *Uninstall) Run(params UninstallRunParams, nsType model.NamespaceType) (rerr error) { - defer rationalizeError(u.prime.Auth(), &rerr) - logging.Debug("ExecuteUninstall") - if u.prime.Project() == nil { - return rationalize.ErrNoProject - } - - var reqs []*requirements.Requirement - for _, p := range params.Packages { - req := &requirements.Requirement{ - Name: p.Name, - Operation: types.OperationRemoved, - } - - if p.Namespace != "" { - req.Namespace = ptr.To(model.NewNamespaceRaw(p.Namespace)) - } else { - req.NamespaceType = &nsType - } - - reqs = append(reqs, req) - } - - ts, err := commits_runbit.ExpandTimeForProject(&captain.TimeValue{}, u.prime.Auth(), u.prime.Project()) - if err != nil { - return errs.Wrap(err, "Unable to get timestamp from params") - } - - return requirements.NewRequirementOperation(u.prime).ExecuteRequirementOperation(&ts, reqs...) -} diff --git a/internal/runners/platforms/add.go b/internal/runners/platforms/add.go index 3f963ee00c..3cc02607eb 100644 --- a/internal/runners/platforms/add.go +++ b/internal/runners/platforms/add.go @@ -14,6 +14,7 @@ import ( "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/rationalizers" "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/internal/runbits/runtime/trigger" "github.com/ActiveState/cli/pkg/localcommit" bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/model" @@ -101,7 +102,7 @@ func (a *Add) Run(params AddRunParams) (rerr error) { script.AddPlatform(*platform.PlatformID) // Update local checkout and source runtime changes - if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", *platform.DisplayName)); err != nil { + if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", *platform.DisplayName), trigger.TriggerPlatform); err != nil { return errs.Wrap(err, "Failed to update local checkout") } diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index 070204744c..a4cd094e54 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -12,6 +12,7 @@ import ( "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/rationalizers" "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/internal/runbits/runtime/trigger" "github.com/ActiveState/cli/pkg/localcommit" bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/model" @@ -103,7 +104,7 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { } // Update local checkout and source runtime changes - if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", params.Platform.String())); err != nil { + if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", params.Platform.String()), trigger.TriggerPlatform); err != nil { return errs.Wrap(err, "Failed to update local checkout") } diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index 731938070a..c443af1237 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -10,6 +10,7 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/reqop_runbit" + "github.com/ActiveState/cli/internal/runbits/runtime/trigger" "github.com/ActiveState/cli/internal/sliceutils" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" @@ -105,7 +106,7 @@ func (u *Uninstall) Run(params Params) (rerr error) { pg = nil // Update local checkout and source runtime changes - if err := reqop_runbit.UpdateAndReload(u.prime, script, oldCommit, locale.Tr("commit_message_added", params.Packages.String())); err != nil { + if err := reqop_runbit.UpdateAndReload(u.prime, script, oldCommit, locale.Tr("commit_message_added", params.Packages.String()), trigger.TriggerUninstall); err != nil { return errs.Wrap(err, "Failed to update local checkout") } From 6b825d75a9363074fec108fbdfa9f816d568fd3f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 13:51:54 -0700 Subject: [PATCH 089/440] Add test for ParseNamespace --- pkg/platform/model/vcs.go | 2 +- pkg/platform/model/vcs_test.go | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 7aef7f2fb4..12b78d1941 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -83,7 +83,7 @@ const ( NamespaceCamelFlagsMatch = `^camel-flags$` // NamespaceOrgMatch is the namespace used for org specific requirements - NamespaceOrgMatch = `^org\/` + NamespaceOrgMatch = `^private\/` // NamespaceBuildFlagsMatch is the namespace used for passing build flags NamespaceBuildFlagsMatch = `^build-flags$` diff --git a/pkg/platform/model/vcs_test.go b/pkg/platform/model/vcs_test.go index 15483e8180..944d4f85a4 100644 --- a/pkg/platform/model/vcs_test.go +++ b/pkg/platform/model/vcs_test.go @@ -161,3 +161,42 @@ func (suite *VCSTestSuite) TestVersionStringToConstraints() { func TestVCSTestSuite(t *testing.T) { suite.Run(t, new(VCSTestSuite)) } + +func TestParseNamespace(t *testing.T) { + tests := []struct { + ns string + want NamespaceType + }{ + { + "language/python", + NamespacePackage, + }, + { + "bundles/python", + NamespaceBundle, + }, + { + "language", + NamespaceLanguage, + }, + { + "platform", + NamespacePlatform, + }, + { + "private/org", + NamespaceOrg, + }, + { + "raw/foo/bar", + NamespaceRaw, + }, + } + for _, tt := range tests { + t.Run(tt.ns, func(t *testing.T) { + if got := ParseNamespace(tt.ns); got.Type().name != tt.want.name { + t.Errorf("ParseNamespace() = %v, want %v", got.Type().name, tt.want.name) + } + }) + } +} From 05927a650fadd0058a5135b43a5a576cea7fad0a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 14:47:14 -0700 Subject: [PATCH 090/440] Fix bundle uninstall not working --- cmd/state/internal/cmdtree/bundles.go | 7 ++++++- internal/runbits/rationalizers/commit.go | 2 +- pkg/buildscript/mutations.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/state/internal/cmdtree/bundles.go b/cmd/state/internal/cmdtree/bundles.go index a04715ff88..e71be1e68c 100644 --- a/cmd/state/internal/cmdtree/bundles.go +++ b/cmd/state/internal/cmdtree/bundles.go @@ -93,7 +93,12 @@ func newBundleUninstallCommand(prime *primer.Values) *captain.Command { Required: true, }, }, - func(_ *captain.Command, _ []string) error { + func(_ *captain.Command, args []string) error { + for _, p := range args { + if _, err := params.Packages.Add(p); err != nil { + return locale.WrapInputError(err, "err_uninstall_packages_args", "Invalid package uninstall arguments") + } + } return runner.Run(params) }, ).SetSupportsStructuredOutput() diff --git a/internal/runbits/rationalizers/commit.go b/internal/runbits/rationalizers/commit.go index dfa26018b0..eff0a3a151 100644 --- a/internal/runbits/rationalizers/commit.go +++ b/internal/runbits/rationalizers/commit.go @@ -34,7 +34,7 @@ func HandleCommitErrors(rerr *error) { ) case types.NoChangeSinceLastCommitErrorType: *rerr = errs.WrapUserFacing(*rerr, - locale.Tl("err_packages_exist", "The requested package is already installed."), + locale.Tl("err_commit_nochanges", "There are no changes since the last commit."), errs.SetInput(), ) default: diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index de3bba9169..f1a6992d89 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -99,7 +99,7 @@ func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { break } } - if arg.Assignment.Key == requirementNamespaceKey { + if requirement.Namespace != "" && arg.Assignment.Key == requirementNamespaceKey { match = strValue(arg.Assignment.Value) == requirement.Namespace if !match { break From c8ef37f1934a58bc04ce0644313d47459bfc5b10 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 10 Sep 2024 14:47:28 -0700 Subject: [PATCH 091/440] Centralize commit error handling --- internal/runners/packages/rationalize.go | 32 ++---------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/internal/runners/packages/rationalize.go b/internal/runners/packages/rationalize.go index b96ba8a285..ccf6503943 100644 --- a/internal/runners/packages/rationalize.go +++ b/internal/runners/packages/rationalize.go @@ -6,9 +6,9 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/internal/runbits/rationalizers" "github.com/ActiveState/cli/pkg/buildscript" bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/authentication" ) @@ -29,35 +29,7 @@ func rationalizeError(auth *authentication.Auth, err *error) { // Error staging a commit during install. case errors.As(*err, &commitError): - switch commitError.Type { - case types.NotFoundErrorType: - *err = errs.WrapUserFacing(*err, - locale.Tl("err_packages_not_found", "Could not make runtime changes because your project was not found."), - errs.SetInput(), - errs.SetTips(locale.T("tip_private_project_auth")), - ) - case types.ForbiddenErrorType: - *err = errs.WrapUserFacing(*err, - locale.Tl("err_packages_forbidden", "Could not make runtime changes because you do not have permission to do so."), - errs.SetInput(), - errs.SetTips(locale.T("tip_private_project_auth")), - ) - case types.HeadOnBranchMovedErrorType: - *err = errs.WrapUserFacing(*err, - locale.T("err_buildplanner_head_on_branch_moved"), - errs.SetInput(), - ) - case types.NoChangeSinceLastCommitErrorType: - *err = errs.WrapUserFacing(*err, - locale.Tl("err_packages_exist", "The requested package(s) is already installed."), - errs.SetInput(), - ) - default: - *err = errs.WrapUserFacing(*err, - locale.Tl("err_packages_buildplanner_error", "Could not make runtime changes due to the following error: {{.V0}}", commitError.Message), - errs.SetInput(), - ) - } + rationalizers.HandleCommitErrors(err) // Requirement not found for uninstall. case errors.As(*err, &requirementNotFoundErr): From 38b3f86a4fa7a862aa1b047fb47d1e0b921fb7de Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 10:08:29 -0700 Subject: [PATCH 092/440] Added JSON output for `state install` --- internal/captain/values.go | 6 +- internal/locale/locales/en-us.yaml | 8 ++ internal/runbits/reqop_runbit/update.go | 4 - internal/runners/install/install.go | 119 ++++++++++++------ internal/runners/install/rationalize.go | 8 +- pkg/platform/api/buildplanner/types/commit.go | 6 + .../api/buildplanner/types/requirement.go | 2 +- pkg/platform/model/vcs.go | 5 + 8 files changed, 106 insertions(+), 52 deletions(-) diff --git a/internal/captain/values.go b/internal/captain/values.go index a819291c3f..1abb4c464c 100644 --- a/internal/captain/values.go +++ b/internal/captain/values.go @@ -124,9 +124,9 @@ func (u *UsersValue) Type() string { // - / // - /@ type PackageValue struct { - Namespace string - Name string - Version string + Namespace string `json:"namespace"` + Name string `json:"name"` + Version string `json:"version"` } var _ FlagMarshaler = &PackageValue{} diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 31698718d7..b88e90e09e 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1580,3 +1580,11 @@ progress_requirements: other: "• Updating requirements" progress_platforms: other: "• Updating platforms" +prompt_pkgop_ingredient: + other: Multiple Matches +prompt_pkgop_ingredient_msg: + other: "Your query for [ACTIONABLE]{{.V0}}[/RESET] has multiple matches. Which one would you like to use?" +install_report_added: + other: "Added: [NOTICE]{{.V0}}[/RESET]" +install_report_updated: + other: "Updated: [NOTICE]{{.V0}}[/RESET]" diff --git a/internal/runbits/reqop_runbit/update.go b/internal/runbits/reqop_runbit/update.go index 9964a5a7c9..d56437339d 100644 --- a/internal/runbits/reqop_runbit/update.go +++ b/internal/runbits/reqop_runbit/update.go @@ -8,7 +8,6 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/buildscript" @@ -66,9 +65,6 @@ func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit }() pg = output.StartSpinner(out, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval) - bsv, _ := script.Marshal() - logging.Debug("Buildscript: %s", string(bsv)) - commitParams := buildplanner.StageCommitParams{ Owner: pj.Owner(), Project: pj.Name(), diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index c723e66057..929a894495 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -1,6 +1,7 @@ package install import ( + "errors" "fmt" "strconv" "strings" @@ -42,11 +43,18 @@ type Params struct { Timestamp captain.TimeValue } +type resolvedRequirement struct { + types.Requirement + Version string `json:"version"` +} + type requirement struct { - input *captain.PackageValue - resolvedVersionReq []types.VersionRequirement - resolvedNamespace *model.Namespace - matchedIngredients []*model.IngredientAndVersion + Requested *captain.PackageValue `json:"requested"` + Resolved resolvedRequirement `json:"resolved"` + + // Remainder are for display purposes only + Type model.NamespaceType `json:"type"` + Operation types.Operation `json:"operation"` } type requirements []*requirement @@ -54,10 +62,10 @@ type requirements []*requirement func (r requirements) String() string { result := []string{} for _, req := range r { - if req.resolvedNamespace != nil { - result = append(result, fmt.Sprintf("%s/%s", req.resolvedNamespace.String(), req.input.Name)) + if req.Resolved.Namespace != "" { + result = append(result, fmt.Sprintf("%s/%s", req.Resolved.Namespace, req.Requested.Name)) } else { - result = append(result, req.input.Name) + result = append(result, req.Requested.Name) } } return strings.Join(result, ", ") @@ -154,6 +162,12 @@ func (i *Install) Run(params Params) (rerr error) { return errs.Wrap(err, "Failed to update local checkout") } + if out.Type().IsStructured() { + out.Print(output.Structured(reqs)) + } else { + i.renderUserFacing(reqs) + } + // All done out.Notice(locale.T("operation_success_local")) @@ -168,13 +182,14 @@ type errNoMatches struct { // resolveRequirements will attempt to resolve the ingredient and namespace for each requested package func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Time, languages []model.Language) (requirements, error) { - var disambiguate []*requirement - var failed []*requirement + disambiguate := map[*requirement][]*model.IngredientAndVersion{} + failed := []*requirement{} reqs := []*requirement{} for _, pkg := range packages { - req := &requirement{input: pkg} + req := &requirement{Requested: pkg} if pkg.Namespace != "" { - req.resolvedNamespace = ptr.To(model.NewNamespaceRaw(pkg.Namespace)) + req.Resolved.Name = pkg.Name + req.Resolved.Namespace = pkg.Namespace } // Find ingredients that match the pkg query @@ -199,18 +214,13 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti return false }) } - req.matchedIngredients = ingredients // Validate that the ingredient is resolved, and prompt the user if multiple ingredients matched - if req.resolvedNamespace == nil { - len := len(ingredients) - switch { - case len == 1: - req.resolvedNamespace = ptr.To(model.ParseNamespace(*ingredients[0].Ingredient.PrimaryNamespace)) - case len > 1: - disambiguate = append(disambiguate, req) - case len == 0: + if req.Resolved.Namespace == "" { + if len(ingredients) == 0 { failed = append(failed, req) + } else { + disambiguate[req] = ingredients } } @@ -222,22 +232,31 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti return nil, errNoMatches{error: errs.New("Failed to resolve requirements"), requirements: failed, languages: languages} } - // Disambiguate requirements that match multiple ingredients + // Disambiguate requirements that had to be resolved with an ingredient lookup if len(disambiguate) > 0 { - for _, req := range disambiguate { - ingredient, err := i.promptForMatchingIngredient(req) - if err != nil { - return nil, errs.Wrap(err, "Prompting for namespace failed") + for req, ingredients := range disambiguate { + var ingredient *model.IngredientAndVersion + if len(ingredients) == 1 { + ingredient = ingredients[0] + } else { + var err error + ingredient, err = i.promptForMatchingIngredient(req, ingredients) + if err != nil { + return nil, errs.Wrap(err, "Prompting for namespace failed") + } } - req.matchedIngredients = []*model.IngredientAndVersion{ingredient} - req.resolvedNamespace = ptr.To(model.ParseNamespace(*ingredient.Ingredient.PrimaryNamespace)) + req.Resolved.Name = ingredient.Ingredient.NormalizedName + req.Resolved.Namespace = *ingredient.Ingredient.PrimaryNamespace } } - // Now that we have the ingredient resolved we can also resolve the version requirement + // Now that we have the ingredient resolved we can also resolve the version requirement. + // We can also set the type and operation, which are used for conveying what happened to the user. for _, req := range reqs { - version := req.input.Version - if req.input.Version == "" { + req.Type = model.ParseNamespace(req.Resolved.Namespace).Type() + version := req.Requested.Version + if req.Requested.Version == "" { + req.Resolved.Version = locale.T("constraint_auto") continue } if _, err := strconv.Atoi(version); err == nil { @@ -245,7 +264,8 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti version = fmt.Sprintf("%d.x", version) } var err error - req.resolvedVersionReq, err = bpModel.VersionStringToRequirements(version) + req.Resolved.Version = version + req.Resolved.VersionRequirement, err = bpModel.VersionStringToRequirements(version) if err != nil { return nil, errs.Wrap(err, "Could not process version string into requirements") } @@ -254,24 +274,24 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti return reqs, nil } -func (i *Install) promptForMatchingIngredient(req *requirement) (*model.IngredientAndVersion, error) { - if len(req.matchedIngredients) <= 1 { +func (i *Install) promptForMatchingIngredient(req *requirement, ingredients []*model.IngredientAndVersion) (*model.IngredientAndVersion, error) { + if len(ingredients) <= 1 { return nil, errs.New("promptForNamespace should never be called if there are no multiple ingredient matches") } choices := []string{} values := map[string]*model.IngredientAndVersion{} - for _, i := range req.matchedIngredients { + for _, i := range ingredients { // Generate ingredient choices to present to the user - name := fmt.Sprintf("%s (%s)", *i.Ingredient.Name, i.Ingredient.PrimaryNamespace) + name := fmt.Sprintf("%s (%s)", *i.Ingredient.Name, *i.Ingredient.PrimaryNamespace) choices = append(choices, name) values[name] = i } // Prompt the user with the ingredient choices choice, err := i.prime.Prompt().Select( - locale.Tl("prompt_pkgop_ingredient", "Multiple Matches"), - locale.Tl("prompt_pkgop_ingredient_msg", "Your query has multiple matches. Which one would you like to use?"), + locale.T("prompt_pkgop_ingredient"), + locale.Tr("prompt_pkgop_ingredient_msg", req.Requested.String()), choices, &choices[0], ) if err != nil { @@ -282,13 +302,32 @@ func (i *Install) promptForMatchingIngredient(req *requirement) (*model.Ingredie return values[choice], nil } +func (i *Install) renderUserFacing(reqs requirements) { + for _, req := range reqs { + l := "install_report_added" + if req.Operation == types.OperationUpdated { + l = "install_report_updated" + } + i.prime.Output().Notice(locale.Tr(l, fmt.Sprintf("%s/%s@%s", req.Resolved.Namespace, req.Resolved.Name, req.Resolved.Version))) + } + i.prime.Output().Notice("") +} + func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) error { script.SetAtTime(ts) for _, req := range requirements { requirement := types.Requirement{ - Namespace: req.resolvedNamespace.String(), - Name: req.input.Name, - VersionRequirement: req.resolvedVersionReq, + Namespace: req.Resolved.Namespace, + Name: req.Requested.Name, + VersionRequirement: req.Resolved.VersionRequirement, + } + + req.Operation = types.OperationUpdated + if err := script.RemoveRequirement(requirement); err != nil { + if !errors.As(err, ptr.To(&buildscript.RequirementNotFoundError{})) { + return errs.Wrap(err, "Could not remove requirement") + } + req.Operation = types.OperationAdded // If req could not be found it means this is an addition } err := script.AddRequirement(requirement) diff --git a/internal/runners/install/rationalize.go b/internal/runners/install/rationalize.go index 4f92736b7b..2ef363548d 100644 --- a/internal/runners/install/rationalize.go +++ b/internal/runners/install/rationalize.go @@ -26,7 +26,7 @@ func (i *Install) rationalizeError(rerr *error) { case errors.As(*rerr, &noMatchErr): names := []string{} for _, r := range noMatchErr.requirements { - names = append(names, fmt.Sprintf(`[ACTIONABLE]%s[/RESET]`, r.input.Name)) + names = append(names, fmt.Sprintf(`[ACTIONABLE]%s[/RESET]`, r.Requested.Name)) } if len(noMatchErr.requirements) > 1 { *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_requirements_no_match", strings.Join(names, ", "))) @@ -52,13 +52,13 @@ func (i *Install) rationalizeError(rerr *error) { } func (i *Install) getSuggestions(req *requirement, languages []model.Language) ([]string, error) { - ingredients, err := model.SearchIngredients(req.input.Namespace, req.input.Name, false, nil, i.prime.Auth()) + ingredients, err := model.SearchIngredients(req.Requested.Namespace, req.Requested.Name, false, nil, i.prime.Auth()) if err != nil { - return []string{}, locale.WrapError(err, "err_package_ingredient_search", "Failed to resolve ingredient named: {{.V0}}", req.input.Name) + return []string{}, locale.WrapError(err, "err_package_ingredient_search", "Failed to resolve ingredient named: {{.V0}}", req.Requested.Name) } // Filter out irrelevant ingredients - if req.input.Namespace == "" { + if req.Requested.Namespace == "" { // Filter out ingredients that don't target one of the supported languages ingredients = sliceutils.Filter(ingredients, func(iv *model.IngredientAndVersion) bool { if !model.NamespaceMatch(*iv.Ingredient.PrimaryNamespace, i.nsType.Matchable()) { diff --git a/pkg/platform/api/buildplanner/types/commit.go b/pkg/platform/api/buildplanner/types/commit.go index f95da2a9df..cdba27da7d 100644 --- a/pkg/platform/api/buildplanner/types/commit.go +++ b/pkg/platform/api/buildplanner/types/commit.go @@ -1,6 +1,8 @@ package types import ( + "encoding/json" + "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" ) @@ -28,6 +30,10 @@ func (o Operation) String() string { } } +func (o *Operation) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + func (o *Operation) Unmarshal(v string) error { switch v { case mono_models.CommitChangeEditableOperationAdded: diff --git a/pkg/platform/api/buildplanner/types/requirement.go b/pkg/platform/api/buildplanner/types/requirement.go index b21a28cc09..33a7a367c5 100644 --- a/pkg/platform/api/buildplanner/types/requirement.go +++ b/pkg/platform/api/buildplanner/types/requirement.go @@ -3,7 +3,7 @@ package types type Requirement struct { Name string `json:"name"` Namespace string `json:"namespace"` - VersionRequirement []VersionRequirement `json:"version_requirements,omitempty"` + VersionRequirement []VersionRequirement `json:"-"` Revision *int `json:"revision,omitempty"` } diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 12b78d1941..ce79938f59 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -1,6 +1,7 @@ package model import ( + "encoding/json" "errors" "fmt" "regexp" @@ -143,6 +144,10 @@ func (t NamespaceType) String() string { return t.name } +func (t NamespaceType) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} + func (t NamespaceType) Prefix() string { return t.prefix } From 48418db9ae0ad0505086cbc17b34e58535134d83 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 10:52:22 -0700 Subject: [PATCH 093/440] Add json output for uninstall --- internal/captain/values.go | 2 +- internal/locale/locales/en-us.yaml | 2 + internal/runners/uninstall/uninstall.go | 79 ++++++++++++++++++++----- 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/internal/captain/values.go b/internal/captain/values.go index 1abb4c464c..5f772b13ee 100644 --- a/internal/captain/values.go +++ b/internal/captain/values.go @@ -126,7 +126,7 @@ func (u *UsersValue) Type() string { type PackageValue struct { Namespace string `json:"namespace"` Name string `json:"name"` - Version string `json:"version"` + Version string `json:"version,omitempty"` } var _ FlagMarshaler = &PackageValue{} diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index b88e90e09e..6827b515a5 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1588,3 +1588,5 @@ install_report_added: other: "Added: [NOTICE]{{.V0}}[/RESET]" install_report_updated: other: "Updated: [NOTICE]{{.V0}}[/RESET]" +install_report_removed: + other: "Removed: [NOTICE]{{.V0}}[/RESET]" diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index c443af1237..9a35e6661f 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -1,6 +1,9 @@ package uninstall import ( + "fmt" + "strings" + "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" @@ -34,6 +37,28 @@ type Params struct { Packages captain.PackagesValue } +type requirement struct { + Requested *captain.PackageValue `json:"requested"` + Resolved types.Requirement `json:"resolved"` + + // Remainder are for display purposes only + Type model.NamespaceType `json:"type"` +} + +type requirements []*requirement + +func (r requirements) String() string { + result := []string{} + for _, req := range r { + if req.Resolved.Namespace != "" { + result = append(result, fmt.Sprintf("%s/%s", req.Resolved.Namespace, req.Requested.Name)) + } else { + result = append(result, req.Requested.Name) + } + } + return strings.Join(result, ", ") +} + // Uninstall manages the installing execution context. type Uninstall struct { prime primeable @@ -97,8 +122,14 @@ func (u *Uninstall) Run(params Params) (rerr error) { // Update buildscript script := oldCommit.BuildScript() - if err := u.prepareBuildScript(script, params.Packages); err != nil { - return errs.Wrap(err, "Could not prepare build script") + reqs, err := u.resolveRequirements(script, params.Packages) + if err != nil { + return errs.Wrap(err, "Failed to resolve requirements") + } + for _, req := range reqs { + if err := script.RemoveRequirement(req.Resolved); err != nil { + return errs.Wrap(err, "Unable to remove requirement") + } } // Done updating requirements @@ -110,20 +141,36 @@ func (u *Uninstall) Run(params Params) (rerr error) { return errs.Wrap(err, "Failed to update local checkout") } + if out.Type().IsStructured() { + out.Print(output.Structured(reqs)) + } else { + u.renderUserFacing(reqs) + } + // All done out.Notice(locale.T("operation_success_local")) return nil } -func (u *Uninstall) prepareBuildScript(script *buildscript.BuildScript, pkgs captain.PackagesValue) error { +func (u *Uninstall) renderUserFacing(reqs requirements) { + u.prime.Output().Notice("") + for _, req := range reqs { + l := "install_report_removed" + u.prime.Output().Notice(locale.Tr(l, fmt.Sprintf("%s/%s", req.Resolved.Namespace, req.Resolved.Name))) + } + u.prime.Output().Notice("") +} + +func (u *Uninstall) resolveRequirements(script *buildscript.BuildScript, pkgs captain.PackagesValue) (requirements, error) { + result := requirements{} + reqs, err := script.DependencyRequirements() if err != nil { - return errs.Wrap(err, "Unable to get requirements") + return nil, errs.Wrap(err, "Unable to get requirements") } // Resolve requirements and check for errors - toRemove := []types.Requirement{} notFound := captain.PackagesValue{} multipleMatches := captain.PackagesValue{} for _, pkg := range pkgs { @@ -137,35 +184,35 @@ func (u *Uninstall) prepareBuildScript(script *buildscript.BuildScript, pkgs cap } return model.NamespaceMatch(req.Namespace, u.nsType.Matchable()) }) - toRemove = append(toRemove, matches...) // Check for duplicate matches if len(matches) > 1 { multipleMatches = append(multipleMatches, pkg) + continue } // Check for no matches if len(matches) == 0 { notFound = append(notFound, pkg) + continue } + + result = append(result, &requirement{ + Requested: pkg, + Resolved: matches[0], + Type: model.ParseNamespace(matches[0].Namespace).Type(), + }) } // Error out on duplicate matches if len(multipleMatches) > 0 { - return &errMultipleMatches{error: errs.New("Could not find all requested packages"), packages: multipleMatches} + return result, &errMultipleMatches{error: errs.New("Could not find all requested packages"), packages: multipleMatches} } // Error out on no matches if len(notFound) > 0 { - return &errNoMatches{error: errs.New("Could not find all requested packages"), packages: notFound} - } - - // Remove requirements - for _, req := range toRemove { - if err := script.RemoveRequirement(req); err != nil { - return errs.Wrap(err, "Unable to remove requirement") - } + return result, &errNoMatches{error: errs.New("Could not find all requested packages"), packages: notFound} } - return nil + return result, nil } From 6fef4538fc3ab3d76f6202e2b7d51560efe34ccc Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 10:53:17 -0700 Subject: [PATCH 094/440] Added structured output for platforms add/remove --- internal/runners/platforms/add.go | 4 ++++ internal/runners/platforms/remove.go | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/internal/runners/platforms/add.go b/internal/runners/platforms/add.go index 3cc02607eb..cb84a58518 100644 --- a/internal/runners/platforms/add.go +++ b/internal/runners/platforms/add.go @@ -108,6 +108,10 @@ func (a *Add) Run(params AddRunParams) (rerr error) { out.Notice(locale.Tr("platform_added", *platform.DisplayName)) + if out.Type().IsStructured() { + out.Print(output.Structured(platform)) + } + return nil } diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index a4cd094e54..61369deb3d 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -17,7 +17,6 @@ import ( bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/model" bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" - "github.com/go-openapi/strfmt" ) // RemoveRunParams tracks the info required for running Remove. @@ -82,14 +81,14 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { if err != nil { return errs.Wrap(err, "Failed to get platforms") } - toRemove := []strfmt.UUID{} + toRemove := []*model.Platform{} for _, uid := range platforms { platform, err := model.FetchPlatformByUID(uid) if err != nil { return errs.Wrap(err, "Failed to get platform") } if model.IsPlatformMatch(platform, params.Platform.Name(), params.Platform.Version(), params.BitWidth) { - toRemove = append(toRemove, uid) + toRemove = append(toRemove, platform) } } if len(toRemove) == 0 { @@ -99,7 +98,7 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { return errMultiMatch } - if err := script.RemovePlatform(toRemove[0]); err != nil { + if err := script.RemovePlatform(*toRemove[0].PlatformID); err != nil { return errs.Wrap(err, "Failed to remove platform") } @@ -110,6 +109,10 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { out.Notice(locale.Tr("platform_added", params.Platform.String())) + if out.Type().IsStructured() { + out.Print(output.Structured(toRemove[0])) + } + return nil } From d3339da3c13bd20f923def70f4cedd2938a56dc7 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 11:00:57 -0700 Subject: [PATCH 095/440] Added standalone install test --- test/integration/package_int_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 77268712cb..dfd03764f6 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -431,6 +431,21 @@ scripts: ts.PrepareCommitIdFile("a9d0bc88-585a-49cf-89c1-6c07af781cff") } +func (suite *PackageIntegrationTestSuite) TestPackage_Install() { + suite.OnlyRunForTags(tagsuite.Package) + + ts := e2e.New(suite.T(), true) + defer ts.Close() + + ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("install", "requests") + cp.Expect("project has been updated") + cp.ExpectExitCode(0) +} + func (suite *PackageIntegrationTestSuite) TestPackage_Uninstall() { suite.OnlyRunForTags(tagsuite.Package) From 1277e45f8204009903dda3e0fd5a583673b2fc14 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 11:01:27 -0700 Subject: [PATCH 096/440] Drop redundant test code --- test/integration/bundle_int_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/integration/bundle_int_test.go b/test/integration/bundle_int_test.go index d5254d71a0..58ca8caa0a 100644 --- a/test/integration/bundle_int_test.go +++ b/test/integration/bundle_int_test.go @@ -91,16 +91,6 @@ func (suite *BundleIntegrationTestSuite) TestJSON() { cp = ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") cp.ExpectExitCode(0) - - cp = ts.Spawn("bundles", "install", "Testing", "--output", "json") - cp.Expect(`"name":"Testing"`) - cp.ExpectExitCode(0) - AssertValidJSON(suite.T(), cp) - - cp = ts.Spawn("bundles", "uninstall", "Testing", "-o", "editor") - cp.Expect(`"name":"Testing"`) - cp.ExpectExitCode(0) - AssertValidJSON(suite.T(), cp) } func TestBundleIntegrationTestSuite(t *testing.T) { From c3efc4200de4ffbea838fe330819f24fa62ab5a0 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 11:42:26 -0700 Subject: [PATCH 097/440] Fix assertions --- test/integration/languages_int_test.go | 4 ++-- test/integration/package_int_test.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/integration/languages_int_test.go b/test/integration/languages_int_test.go index d75f1b641d..c10aaf0107 100644 --- a/test/integration/languages_int_test.go +++ b/test/integration/languages_int_test.go @@ -125,7 +125,7 @@ func (suite *LanguagesIntegrationTestSuite) TestWildcards() { // Test explicit wildcard. cp = ts.Spawn("languages", "install", "python@3.9.x") - cp.Expect("Language updated: python@3.9.x") + cp.Expect("Updated: language/python@3.9.x") cp.ExpectExitCode(0) cp = ts.Spawn("history") cp.Expect("→ >=3.9,<3.10") @@ -137,7 +137,7 @@ func (suite *LanguagesIntegrationTestSuite) TestWildcards() { // Test implicit wildcard. cp = ts.Spawn("languages", "install", "python@3.9") - cp.Expect("Language updated: python@3.9") + cp.Expect("Updated: python@3.9") cp.ExpectExitCode(0) cp = ts.Spawn("history") cp.Expect("→ >=3.9,<3.10") diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index dfd03764f6..c72a8b1948 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -720,7 +720,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_NoPrompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2") - cp.Expect("Warning: Dependency has 2 known vulnerabilities", e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has \d+ known vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) } @@ -742,8 +742,8 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Prompt() { cp = ts.Spawn("config", "set", constants.SecurityPromptConfig, "true") cp.ExpectExitCode(0) - cp = ts.Spawn("install", "urllib3@2.0.2") - cp.Expect("Warning: Dependency has 2 known vulnerabilities") + cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z") + cp.ExpectRe(`Warning: Dependency has \d+ known vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("y") cp.ExpectExitCode(0) @@ -764,8 +764,8 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Indirect() { cp = ts.Spawn("config", "set", constants.SecurityPromptConfig, "true") cp.ExpectExitCode(0) - cp = ts.Spawn("install", "private/ActiveState-CLI-Testing/language/python/django_dep", "--ts=now") - cp.ExpectRe(`Warning: Dependency has \d indirect known vulnerabilities`) + cp = ts.Spawn("install", "private/ActiveState-CLI-Testing/language/python/django_dep", "--ts=2024-09-10T16:36:34.393Z") + cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("n") cp.ExpectExitCode(1) From 63914e7757ca08b729aaf57b0ad76e881b454510 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 14:29:16 -0700 Subject: [PATCH 098/440] Ensure our default platforms are prioritized --- pkg/platform/model/inventory.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pkg/platform/model/inventory.go b/pkg/platform/model/inventory.go index 7cb0c806cd..780ff5bccb 100644 --- a/pkg/platform/model/inventory.go +++ b/pkg/platform/model/inventory.go @@ -456,13 +456,9 @@ func FetchPlatformByUID(uid strfmt.UUID) (*Platform, error) { var ErrPlatformNotFound = errors.New("could not find platform matching provided criteria") func FetchPlatformByDetails(name, version string, bitwidth int) (*Platform, error) { - // For backward compatibility we still want to raise ErrPlatformNotFound due to name ID matching - if version == "" && bitwidth == 0 { - var err error - _, err = PlatformNameToPlatformID(name) - if err != nil { - return nil, errs.Wrap(err, "platform id from name failed") - } + platformID, err := PlatformNameToPlatformID(name) + if err != nil { + return nil, errs.Wrap(err, "platform id from name failed") } runtimePlatforms, err := FetchPlatforms() @@ -470,6 +466,18 @@ func FetchPlatformByDetails(name, version string, bitwidth int) (*Platform, erro return nil, err } + // Prioritize the platform that we record as default + for _, rtPf := range runtimePlatforms { + if rtPf.PlatformID.String() != platformID { + continue + } + if IsPlatformMatch(rtPf, name, version, bitwidth) { + return rtPf, nil + } + break + } + + // Return the first platform whose criteria match for _, rtPf := range runtimePlatforms { if IsPlatformMatch(rtPf, name, version, bitwidth) { return rtPf, nil From 20e326dd080adc23ea724827e4ef380974c20990 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 14:29:35 -0700 Subject: [PATCH 099/440] Drop tests that are no longer relevant or redundant --- test/integration/package_int_test.go | 179 ++------------------------- 1 file changed, 13 insertions(+), 166 deletions(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index c72a8b1948..9880a4c50d 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -1,19 +1,12 @@ package integration import ( - "fmt" - "path/filepath" - "runtime" "strings" "testing" - "time" - - "github.com/ActiveState/cli/internal/testhelpers/suite" - "github.com/ActiveState/termtest" "github.com/ActiveState/cli/internal/constants" - "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" + "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" ) @@ -267,7 +260,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_info() { cp.ExpectExitCode(0) } -func (suite *PackageIntegrationTestSuite) TestPackage_detached_operation() { +func (suite *PackageIntegrationTestSuite) TestPackage_operation_multiple() { suite.OnlyRunForTags(tagsuite.Package) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -277,122 +270,26 @@ func (suite *PackageIntegrationTestSuite) TestPackage_detached_operation() { cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") cp.ExpectExitCode(0) - suite.Run("install non-existing", func() { - cp := ts.Spawn("install", "json") - cp.Expect("No results found for search term") - cp.Expect("json2") - cp.Wait() - }) - - suite.Run("install", func() { - cp := ts.Spawn("install", "dateparser@0.7.2") - cp.ExpectRe("(?:Package added|being built)", termtest.OptExpectTimeout(30*time.Second)) - cp.Wait() - }) - - suite.Run("install (update)", func() { - cp := ts.Spawn("install", "dateparser@0.7.6") - cp.ExpectRe("(?:Package updated|being built)", termtest.OptExpectTimeout(50*time.Second)) - cp.Wait() - }) - - suite.Run("uninstall", func() { - cp := ts.Spawn("uninstall", "dateparser") - cp.ExpectRe("(?:Package uninstalled|being built)", termtest.OptExpectTimeout(30*time.Second)) - cp.Wait() - }) -} - -func (suite *PackageIntegrationTestSuite) TestPackage_operation() { - suite.OnlyRunForTags(tagsuite.Package) - if runtime.GOOS == "darwin" { - suite.T().Skip("Skipping mac for now as the builds are still too unreliable") - return - } - ts := e2e.New(suite.T(), false) - defer ts.Close() - - user := ts.CreateNewUser() - namespace := fmt.Sprintf("%s/%s", user.Username, "python3-pkgtest") - - cp := ts.Spawn("fork", "ActiveState-CLI/Packages", "--org", user.Username, "--name", "python3-pkgtest") - cp.ExpectExitCode(0) - - cp = ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") - cp.ExpectExitCode(0) - - cp = ts.Spawn("checkout", namespace, ".") - cp.Expect("Checked out project") - cp.ExpectExitCode(0) - - cp = ts.Spawn("history", "--output=json") - cp.ExpectExitCode(0) - - suite.Run("install", func() { - cp := ts.Spawn("install", "urllib3@1.25.6") - cp.Expect(fmt.Sprintf("Operating on project %s/python3-pkgtest", user.Username)) - cp.ExpectRe("(?:Package added|being built)", termtest.OptExpectTimeout(30*time.Second)) - cp.Wait() - }) - - suite.Run("install (update)", func() { - cp := ts.Spawn("install", "urllib3@1.25.8") - cp.Expect(fmt.Sprintf("Operating on project %s/python3-pkgtest", user.Username)) - cp.ExpectRe("(?:Package updated|being built)", termtest.OptExpectTimeout(30*time.Second)) - cp.Wait() - }) - - suite.Run("uninstall", func() { - cp := ts.Spawn("uninstall", "urllib3") - cp.Expect(fmt.Sprintf("Operating on project %s/python3-pkgtest", user.Username)) - cp.ExpectRe("(?:Package uninstalled|being built)", termtest.OptExpectTimeout(30*time.Second)) - cp.Wait() - }) -} - -func (suite *PackageIntegrationTestSuite) TestPackage_operation_multiple() { - suite.OnlyRunForTags(tagsuite.Package) - if runtime.GOOS == "darwin" { - suite.T().Skip("Skipping mac for now as the builds are still too unreliable") - return - } - ts := e2e.New(suite.T(), false) - defer ts.Close() - - user := ts.CreateNewUser() - namespace := fmt.Sprintf("%s/%s", user.Username, "python3-pkgtest") - - cp := ts.Spawn("fork", "ActiveState-CLI/Packages", "--org", user.Username, "--name", "python3-pkgtest") - cp.ExpectExitCode(0) - - cp = ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") - cp.ExpectExitCode(0) - - cp = ts.Spawn("checkout", namespace, ".") - cp.Expect("Checked out project") - cp.ExpectExitCode(0) - - cp = ts.Spawn("history", "--output=json") - cp.ExpectExitCode(0) - suite.Run("install", func() { cp := ts.Spawn("install", "requests", "urllib3@1.25.6") - cp.Expect(fmt.Sprintf("Operating on project %s/python3-pkgtest", user.Username)) - cp.ExpectRe("(?:Package added|being built)", termtest.OptExpectTimeout(30*time.Second)) + cp.Expect("Operating on project ActiveState-CLI/small-python") + cp.Expect("Added: language/python/requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added: language/python/urllib3") cp.Wait() }) suite.Run("install (update)", func() { cp := ts.Spawn("install", "urllib3@1.25.8") - cp.Expect(fmt.Sprintf("Operating on project %s/python3-pkgtest", user.Username)) - cp.ExpectRe("(?:Package updated|being built)", termtest.OptExpectTimeout(30*time.Second)) + cp.Expect("Operating on project ActiveState-CLI/small-python") + cp.Expect("Updated: language/python/urllib3", e2e.RuntimeSourcingTimeoutOpt) cp.Wait() }) suite.Run("uninstall", func() { cp := ts.Spawn("uninstall", "requests", "urllib3") - cp.Expect(fmt.Sprintf("Operating on project %s/python3-pkgtest", user.Username)) - cp.ExpectRe("(?:Package uninstalled|being built)", termtest.OptExpectTimeout(30*time.Second)) + cp.Expect("Operating on project ActiveState-CLI/small-python") + cp.Expect("Removed: language/python/requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Removed: language/python/urllib3") cp.Wait() }) } @@ -409,7 +306,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_Duplicate() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "shared/zlib") // install again - cp.Expect("already installed") + cp.Expect(" no changes") cp.ExpectNotExitCode(0) ts.IgnoreLogErrors() @@ -534,56 +431,6 @@ func (suite *PackageIntegrationTestSuite) TestJSON() { AssertValidJSON(suite.T(), cp) } -func (suite *PackageIntegrationTestSuite) TestNormalize() { - suite.OnlyRunForTags(tagsuite.Package) - if runtime.GOOS == "darwin" { - suite.T().Skip("Skipping mac for now as the builds are still too unreliable") - return - } - ts := e2e.New(suite.T(), false) - defer ts.Close() - - cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") - cp.ExpectExitCode(0) - - dir := filepath.Join(ts.Dirs.Work, "normalized") - suite.Require().NoError(fileutils.Mkdir(dir)) - cp = ts.SpawnWithOpts( - e2e.OptArgs("checkout", "ActiveState-CLI/small-python", "."), - e2e.OptWD(dir), - ) - cp.Expect("Checked out project") - cp.ExpectExitCode(0) - - cp = ts.SpawnWithOpts( - e2e.OptArgs("install", "Charset_normalizer"), - e2e.OptWD(dir), - ) - // Even though we are not sourcing a runtime it can still take time to resolve - // the dependencies and create the commit - cp.Expect("charset-normalizer", e2e.RuntimeSourcingTimeoutOpt) - cp.Expect("is different") - cp.Expect("Charset_normalizer") - cp.ExpectExitCode(0) - - anotherDir := filepath.Join(ts.Dirs.Work, "not-normalized") - suite.Require().NoError(fileutils.Mkdir(anotherDir)) - cp = ts.SpawnWithOpts( - e2e.OptArgs("checkout", "ActiveState-CLI/small-python", "."), - e2e.OptWD(anotherDir), - ) - cp.Expect("Checked out project") - cp.ExpectExitCode(0) - - cp = ts.SpawnWithOpts( - e2e.OptArgs("install", "charset-normalizer"), - e2e.OptWD(anotherDir), - ) - cp.Expect("charset-normalizer", e2e.RuntimeSourcingTimeoutOpt) - cp.ExpectExitCode(0, e2e.RuntimeSourcingTimeoutOpt) - suite.NotContains(cp.Output(), "is different") -} - func (suite *PackageIntegrationTestSuite) TestInstall_InvalidVersion() { suite.OnlyRunForTags(tagsuite.Package) ts := e2e.New(suite.T(), false) @@ -743,7 +590,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Prompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z") - cp.ExpectRe(`Warning: Dependency has \d+ known vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("y") cp.ExpectExitCode(0) @@ -790,7 +637,7 @@ func (suite *PackageIntegrationTestSuite) TestChangeSummary() { cp.Expect("├─ ") cp.Expect("├─ ") cp.Expect("└─ ") - cp.Expect("Package added: requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added: requests", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) } From 98aca2e0240f5509da78cb1d687359aab2067894 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 11 Sep 2024 14:29:44 -0700 Subject: [PATCH 100/440] Fix assertion --- test/integration/revert_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/revert_int_test.go b/test/integration/revert_int_test.go index dcb8028bfe..7b619531af 100644 --- a/test/integration/revert_int_test.go +++ b/test/integration/revert_int_test.go @@ -65,7 +65,7 @@ func (suite *RevertIntegrationTestSuite) TestRevertRemote() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "requests") - cp.Expect("Package added") + cp.Expect("Added: requests") cp.ExpectExitCode(0) cp = ts.Spawn("revert", "REMOTE", "--non-interactive") From 24cc190e9e42fd1458087b9849476ebe37627094 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 12 Sep 2024 09:24:00 -0700 Subject: [PATCH 101/440] Fix assertions --- test/integration/package_int_test.go | 6 +++--- test/integration/runtime_int_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 9880a4c50d..33764bf230 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -637,7 +637,7 @@ func (suite *PackageIntegrationTestSuite) TestChangeSummary() { cp.Expect("├─ ") cp.Expect("├─ ") cp.Expect("└─ ") - cp.Expect("Added: requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added: language/python/requests", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) } @@ -653,13 +653,13 @@ func (suite *PackageIntegrationTestSuite) TestChangeSummaryShowsAddedForUpdate() ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") cp = ts.Spawn("install", "jinja2@2.0") - cp.Expect("Package added: jinja2") + cp.Expect("Added: language/python/jinja2") cp.ExpectExitCode(0) cp = ts.Spawn("install", "jinja2@3.1.4") cp.Expect("Installing jinja2@3.1.4 includes 1 direct dep") cp.Expect("└─ markupsafe@2.1.5") - cp.Expect("Package updated: jinja2") + cp.Expect("Updated: language/python/jinja2") cp.ExpectExitCode(0) } diff --git a/test/integration/runtime_int_test.go b/test/integration/runtime_int_test.go index 83ab55cfa4..2e444ece84 100644 --- a/test/integration/runtime_int_test.go +++ b/test/integration/runtime_int_test.go @@ -171,7 +171,7 @@ func (suite *RuntimeIntegrationTestSuite) TestBuildInProgress() { cp.Expect("Build Log") cp.Expect("Building") cp.Expect("All dependencies have been installed and verified", e2e.RuntimeBuildSourcingTimeoutOpt) - cp.Expect("Package added: hello-world") + cp.Expect("Added: private/" + e2e.PersistentUsername + "/hello-world") cp.ExpectExitCode(0) cp = ts.Spawn("exec", "main") From 8f7920ad927e0d63b6dc2abde16594a4a7bd560d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 12 Sep 2024 09:24:08 -0700 Subject: [PATCH 102/440] Remove unused locale --- internal/locale/locales/en-us.yaml | 44 ------------------------------ 1 file changed, 44 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 6827b515a5..97b13b1c4d 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -628,56 +628,12 @@ package_name: other: Name package_version: other: Version -package_added: - other: "Package added: [NOTICE]{{.V0}}[/RESET]" -package_version_added: - other: "Package added: [NOTICE]{{.V0}}@{{.V1}}[/RESET]" -bundle_added: - other: "[SUCCESS]✔ {{.V0}} Bundle successfully installed![/RESET]" -bundle_version_added: - other: "[SUCCESS]✔ {{.V0}} Bundle @ version {{.V1}} successfully installed![/RESET]" -language_updated: - other: "Language updated: [NOTICE]{{.V0}}[/RESET]" -language_version_updated: - other: "Language updated: [NOTICE]{{.V0}}@{{.V1}}[/RESET]" -err_package_updated: - other: Failed to update package -err_bundle_updated: - other: Failed to update bundle -package_updated: - other: "Package updated: [NOTICE]{{.V0}}[/RESET]" -package_version_updated: - other: "Package updated: [NOTICE]{{.V0}}@{{.V1}}[/RESET]" -bundle_updated: - other: "Bundle updated: [NOTICE]{{.V0}}[/RESET]" -bundle_version_updated: - other: "Bundle updated: [NOTICE]{{.V0}}@{{.V1}}[/RESET]" -err_package_removed: - other: Failed to remove package err_remove_requirement_not_found: other: Could not remove requirement '[ACTIONABLE]{{.V0}}[/RESET]', because it does not exist. err_remove_platform_not_found: other: Could not remove platform '[ACTIONABLE]{{.V0}}[/RESET]', because it does not exist. -err_bundle_removed: - other: Failed to remove bundle -err_packages_removed: - other: Failed to remove packages -package_removed: - other: "Package uninstalled: [NOTICE]{{.V0}}[/RESET]" -bundle_removed: - other: "Bundle uninstalled: [NOTICE]{{.V0}}[/RESET]" err_update_build_script: other: Could not update runtime build script -raw_version_added: - other: "Package added: [NOTICE]{{.V0}}@{{.V1}}[/RESET]" -raw_added: - other: "Package added: [NOTICE]{{.V0}}[/RESET]" -raw_version_updated: - other: "Package updated: [NOTICE]{{.V0}}@{{.V1}}[/RESET]" -raw_updated: - other: "Package updated: [NOTICE]{{.V0}}[/RESET]" -raw_removed: - other: "Package removed: [NOTICE]{{.V0}}[/RESET]" notice_commit_build_script: other: | Your local build script has changes that should be committed. Please run '[ACTIONABLE]state commit[/RESET]' to do so. From d35e45b53ba349e8ac9e76879a981c472b7302ae Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 12 Sep 2024 09:24:28 -0700 Subject: [PATCH 103/440] Make all our scripts standalone --- activestate.yaml | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index 518f404ffb..88dd7f8c29 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -48,6 +48,7 @@ constants: scripts: - name: install-deps-dev language: bash + standalone: true if: ne .Shell "cmd" value: | if ! type "go" &> /dev/null; then @@ -73,11 +74,13 @@ scripts: fi - name: install-deps-os language: bash + standalone: true if: ne .OS.Name "Linux" description: Install OS specific deps value: "" - name: install-deps-ci language: bash + standalone: true if: ne .Shell "cmd" value: | if { [[ "$GOOS" == "windows" ]] || [[ "$OS" == "Windows_NT" ]]; } && ! type "goversioninfo" &> /dev/null; then @@ -86,6 +89,7 @@ scripts: fi - name: preprocess language: bash + standalone: true description: Generates assets required by the project that aren't just specific to the build value: | set -e @@ -97,6 +101,7 @@ scripts: fi - name: build language: bash + standalone: true description: Builds the project with the host OS as the target OS. value: | set -e @@ -111,6 +116,7 @@ scripts: go build -tags "$GO_BUILD_TAGS" -o $BUILD_TARGET_DIR/$constants.BUILD_TARGET $constants.CLI_BUILDFLAGS $constants.CLI_PKGS - name: build-for language: bash + standalone: true description: Builds the project with the specified OS as the target OS. (valid values darwin, linux, windows) value: | set -e @@ -121,6 +127,7 @@ scripts: go build -tags "internal $GO_BUILD_TAGS" -o ${2} $constants.CLI_BUILDFLAGS $constants.CLI_PKGS - name: build-svc language: bash + standalone: true description: Builds the state-svc daemon value: | set -e @@ -136,6 +143,7 @@ scripts: - name: build-exec description: Builds the State Executor application language: bash + standalone: true value: | set -e $constants.SET_ENV @@ -143,8 +151,8 @@ scripts: go build -tags "$GO_BUILD_TAGS" -o $BUILD_TARGET_DIR/$constants.BUILD_EXEC_TARGET $constants.CLI_BUILDFLAGS $constants.EXECUTOR_PKGS - name: build-all description: Builds all our tools - standalone: true language: bash + standalone: true value: | set -e echo "Building State Tool" @@ -159,6 +167,7 @@ scripts: $scripts.build-exec.path() - name: build-installer language: bash + standalone: true description: Builds the state-installer value: | set -e @@ -167,6 +176,7 @@ scripts: go build -tags "$GO_BUILD_TAGS" -o $BUILD_TARGET_DIR/$constants.BUILD_INSTALLER_TARGET $constants.INSTALLER_PKGS - name: build-remote-installer language: bash + standalone: true description: Builds the state-remote-installer value: | set -e @@ -181,12 +191,14 @@ scripts: go build -tags "$GO_BUILD_TAGS" -o ../../$BUILD_TARGET_DIR/$TARGET . - name: install language: bash + standalone: true description: Installs the current HEAD version into GOBIN value: | $constants.SET_ENV go install $constants.CLI_BUILDFLAGS $CLI_PKGS - name: deploy-updates language: bash + standalone: true description: Deploys update files to S3. This steps is automated by CI and should never be ran manually unless you KNOW WHAT YOU'RE DOING. value: | set -e @@ -232,17 +244,20 @@ scripts: cp installers/stop${constants.SCRIPT_EXT} $INSTALLERS_DIR/stop${constants.SCRIPT_EXT} - name: deploy-installers language: bash + standalone: true description: Deploys update files to S3. This steps is automated by CI and should never be ran manually unless you KNOW WHAT YOU'RE DOING. value: | go run scripts/ci/s3-deployer/main.go build/installers us-east-1 state-tool update/state - name: deploy-remote-installer language: bash + standalone: true value: | set -e $constants.SET_ENV go run scripts/ci/s3-deployer/main.go $BUILD_TARGET_DIR/remote-installer us-east-1 state-tool remote-installer - name: build-workflow-assets language: bash + standalone: true description: Generates our github workflows value: | $scripts.build-for.path() "windows" ./.github/deps/Windows/bin/state.exe @@ -254,6 +269,7 @@ scripts: GOOS=darwin go build -o .github/deps/macOS/bin/parallelize github.com/ActiveState/cli/scripts/ci/parallelize/ - name: update-workflow-assets language: bash + standalone: true description: Generates our github workflows value: | [ -z "${2}" ] && >&2 echo "Usage: update-workflow-assets [branch] [version]" && exit 1 @@ -286,12 +302,13 @@ scripts: rm -Rf $tmpDir - name: test language: bash + standalone: true description: Runs unit tests (not integration tests) value: | go test -v `go list ./... | grep -v integration | grep -v automation | grep -v expect | grep -v state-svc | grep -v state-offline` $@ - standalone: true - name: integration-tests language: bash + standalone: true description: Runs integration tests. value: | unset ACTIVESTATE_ACTIVATED @@ -300,6 +317,7 @@ scripts: go test `go list ./... | grep "${INTEGRATION_TEST_REGEX}"` -v "${@:1}" -timeout 20m - name: integration-tests-build-check language: bash + standalone: true description: Builds integration tests and removes the executable artifact(s). value: | out="x.test" @@ -307,28 +325,31 @@ scripts: [ -f $out ] && rm $out - name: clean language: bash + standalone: true description: Cleans out the build dir. value: | go clean rm -Rf build - name: run language: bash + standalone: true description: Builds the State Tool and runs it with `--help` value: | $scripts.build.path() build/state --help - name: debug language: bash - description: "Runs a remote debugger that can be hooked into from your IDE. Example usage: `state run debug activate` (will debug `state activate`)" standalone: true + description: "Runs a remote debugger that can be hooked into from your IDE. Example usage: `state run debug activate` (will debug `state activate`)" value: dlv debug --headless --listen=:2346 --api-version=2 github.com/ActiveState/cli/cmd/state -- $@ - name: scripted language: bash - description: "Runs a command via 'go run'" standalone: true + description: "Runs a command via 'go run'" value: go run github.com/ActiveState/cli/cmd/state $@ - name: story-cleanup language: bash + standalone: true description: "Runs Python script to move old stories from primary project to storage project" value: | export PT_API_TOKEN=$secrets.project.PT_API_TOKEN @@ -338,12 +359,14 @@ scripts: python3 ./scripts/story-cleanup/story-cleanup.py - name: lint language: bash + standalone: true description: "Runs linting for untracked and unstaged changes (if any), or staged changes" value: | golangci-lint run --new actionlint - name: lint-staged language: bash + standalone: true description: "Runs linting for staged changes (skipping untracked and unstaged-only files)" value: | golangci-lint run \ @@ -356,12 +379,14 @@ scripts: actionlint $actionfiles - name: lint-all language: bash + standalone: true description: "Runs linting for all files" value: | golangci-lint run --no-config actionlint - name: check-format language: bash + standalone: true description: "Checks if the code is formatted correctly" value: | set -e @@ -392,6 +417,7 @@ scripts: fi - name: grab-mergecommits language: bash + standalone: true value: | export JIRA_USERNAME=${secrets.user.JIRA_USERNAME} export JIRA_TOKEN=${secrets.user.JIRA_TOKEN} @@ -399,6 +425,7 @@ scripts: go run $project.path()/scripts/grab-mergecommits/main.go $1 - name: target-version-pr language: bash + standalone: true value: | export JIRA_USERNAME=${secrets.user.JIRA_USERNAME} export JIRA_TOKEN=${secrets.user.JIRA_TOKEN} @@ -406,6 +433,7 @@ scripts: go run $project.path()/scripts/ci/target-version-pr/main.go $1 - name: create-version-pr language: bash + standalone: true value: | export JIRA_USERNAME=${secrets.user.JIRA_USERNAME} export JIRA_TOKEN=${secrets.user.JIRA_TOKEN} @@ -413,6 +441,7 @@ scripts: go run $project.path()/scripts/create-version-pr/main.go $1 - name: propagate-pr language: bash + standalone: true value: | export JIRA_USERNAME=${secrets.user.JIRA_USERNAME} export JIRA_TOKEN=${secrets.user.JIRA_TOKEN} @@ -420,6 +449,7 @@ scripts: go run $project.path()/scripts/ci/propagate-pr/main.go $1 - name: verify-pr language: bash + standalone: true value: | export JIRA_USERNAME=${secrets.user.JIRA_USERNAME} export JIRA_TOKEN=${secrets.user.JIRA_TOKEN} @@ -427,6 +457,7 @@ scripts: go run $project.path()/scripts/ci/verify-pr/main.go $1 - name: start-story language: bash + standalone: true value: | export JIRA_USERNAME=${secrets.user.JIRA_USERNAME} export JIRA_TOKEN=${secrets.user.JIRA_TOKEN} @@ -434,6 +465,7 @@ scripts: go run $project.path()/scripts/start-story/main.go "$@" - name: ghapi language: bash + standalone: true value: | curl \ -H "Accept: application/vnd.github+json" \ @@ -442,6 +474,7 @@ scripts: - name: benchmark-exec if: eq .OS.Name "Linux" language: bash + standalone: true description: "Benchmarks executable leveraging highly sensitive/accurate tooling" value: | # example usage: From 76eb66532a23fd1c96b7bd8e764a9f2584c3cb39 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 12 Sep 2024 09:24:39 -0700 Subject: [PATCH 104/440] Move hash library --- {internal => cmd/state-svc/internal}/hash/file_hasher.go | 0 {internal => cmd/state-svc/internal}/hash/file_hasher_test.go | 0 cmd/state-svc/internal/resolver/resolver.go | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename {internal => cmd/state-svc/internal}/hash/file_hasher.go (100%) rename {internal => cmd/state-svc/internal}/hash/file_hasher_test.go (100%) diff --git a/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go similarity index 100% rename from internal/hash/file_hasher.go rename to cmd/state-svc/internal/hash/file_hasher.go diff --git a/internal/hash/file_hasher_test.go b/cmd/state-svc/internal/hash/file_hasher_test.go similarity index 100% rename from internal/hash/file_hasher_test.go rename to cmd/state-svc/internal/hash/file_hasher_test.go diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index cc784ed121..4cabfbf2f2 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -10,6 +10,7 @@ import ( "strconv" "time" + "github.com/ActiveState/cli/cmd/state-svc/internal/hash" "github.com/ActiveState/cli/cmd/state-svc/internal/messages" "github.com/ActiveState/cli/cmd/state-svc/internal/rtwatcher" genserver "github.com/ActiveState/cli/cmd/state-svc/internal/server/generated" @@ -21,7 +22,6 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/graph" - "github.com/ActiveState/cli/internal/hash" "github.com/ActiveState/cli/internal/logging" configMediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/multilog" From 608b55e7abfb202640f35a0d5260246df733ab95 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 12 Sep 2024 09:26:02 -0700 Subject: [PATCH 105/440] Revert yaml changes --- .github/workflows/build.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 264e878b07..a581aaff3d 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,7 @@ name: Build-Test-Deploy # === Triggers === -'on': +"on": push: branches: - master @@ -20,7 +20,7 @@ name: Build-Test-Deploy # === Workflow Permissions === permissions: id-token: write # This is required for requesting the JWT - contents: read # This is required for actions/checkout + contents: read # This is required for actions/checkout # === Workflow-level environment variables === env: @@ -38,9 +38,9 @@ jobs: go-version: - 1.22.x sys: - - { os: ubuntu-latest } - - { os: macos-12, shell: zsh } - - { os: windows-2019 } + - {os: ubuntu-latest} + - {os: macos-12, shell: zsh} + - {os: windows-2019} fail-fast: false runs-on: ${{ matrix.sys.os }} env: @@ -174,7 +174,7 @@ jobs: name: Check Format id: check_format shell: bash - if: '!contains(fromJSON(''["refs/heads/beta", "refs/heads/release", "refs/heads/LTS", "refs/heads/master"]''), github.ref) && !startsWith(github.event.pull_request.head.ref, ''version/'')' + if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\", \"refs/heads/master\"]'), github.ref) && !startsWith(github.event.pull_request.head.ref, 'version/')" run: parallelize results Check-Format - # === Unit Tests === @@ -185,32 +185,32 @@ jobs: continue-on-error: ${{ github.event_name != 'schedule' }} - # === "Build: CLI" === - name: 'Build: CLI' + name: "Build: CLI" shell: bash run: parallelize results Build-CLI - # === "Build: Service" === - name: 'Build: Service' + name: "Build: Service" shell: bash run: parallelize results Build-Service - # === "Build: Installer" === - name: 'Build: Installer' + name: "Build: Installer" shell: bash run: parallelize results Build-Installer - # === "Build: Remote Installer" === - name: 'Build: Remote Installer' + name: "Build: Remote Installer" shell: bash run: parallelize results Build-Remote-Installer - # === "Build: Install Scripts" === - name: 'Build: Install Scripts' + name: "Build: Install Scripts" shell: bash run: parallelize results Build-Install-Scripts - # === "Build: Executor" === - name: 'Build: Executor' + name: "Build: Executor" shell: bash run: parallelize results Build-Executor @@ -271,7 +271,7 @@ jobs: - # === Deploy for Integration Tests # NEVER run this against production branches. This is meant for PR deployments. === name: Deploy for Integration Tests # NEVER run this against production branches. This is meant for PR deployments. - if: '!contains(fromJSON(''["refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]''), github.ref)' + if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\"]'), github.ref)" shell: bash run: | if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then @@ -296,7 +296,7 @@ jobs: - # === Integration Tests === name: Integration Tests id: integration_tests - if: '!contains(fromJSON(''["refs/heads/beta", "refs/heads/release", "refs/heads/LTS"]''), github.ref)' + if: "!contains(fromJSON('[\"refs/heads/beta\", \"refs/heads/release\", \"refs/heads/LTS\"]'), github.ref)" shell: bash run: | if [ "$GITHUB_EVENT_NAME" != "schedule" ]; then @@ -436,6 +436,7 @@ jobs: # === Deploy Steps === steps: + - # === Checkout code === name: Checkout code uses: actions/checkout@v4 @@ -514,4 +515,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: build - path: build/ + path: build/ \ No newline at end of file From b9601317ce31a180b9d254fab3b013e7f7e942fe Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 12 Sep 2024 09:39:34 -0700 Subject: [PATCH 106/440] Defer file closure --- cmd/state-svc/internal/hash/file_hasher.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index e1e8cfc963..204ad3e126 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/rtutils" "github.com/cespare/xxhash" "github.com/patrickmn/go-cache" ) @@ -28,7 +29,7 @@ func NewFileHasher() *FileHasher { } } -func (fh *FileHasher) HashFiles(files []string) (string, error) { +func (fh *FileHasher) HashFiles(files []string) (hash string, rerr error) { sort.Strings(files) hasher := xxhash.New() @@ -37,6 +38,7 @@ func (fh *FileHasher) HashFiles(files []string) (string, error) { if err != nil { return "", errs.Wrap(err, "Could not open file: %s", file.Name()) } + defer rtutils.Closer(file.Close, &rerr) fileInfo, err := file.Stat() if err != nil { @@ -59,10 +61,6 @@ func (fh *FileHasher) HashFiles(files []string) (string, error) { hash = fmt.Sprintf("%x", fileHasher.Sum(nil)) } - if err := file.Close(); err != nil { - return "", errs.Wrap(err, "Could not close file: %s", f) - } - fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) fmt.Fprintf(hasher, "%x", hash) } From b78adce0982958053e97f0e7a2313ed265adf38f Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 12 Sep 2024 09:53:48 -0700 Subject: [PATCH 107/440] Clean cache every 24 hours --- cmd/state-svc/internal/hash/file_hasher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 204ad3e126..722b3f9b38 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -25,7 +25,7 @@ type FileHasher struct { func NewFileHasher() *FileHasher { return &FileHasher{ - cache: cache.New(cache.NoExpiration, cache.NoExpiration), + cache: cache.New(cache.NoExpiration, 24*time.Hour), } } From 99c348a52207a675ef9fd5846369fed4fbd5ffea Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 12 Sep 2024 10:11:30 -0700 Subject: [PATCH 108/440] Add comment --- cmd/state-svc/internal/hash/file_hasher.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 722b3f9b38..8018f956e8 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -62,6 +62,8 @@ func (fh *FileHasher) HashFiles(files []string) (hash string, rerr error) { } fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) + + // Incorporate the individual file hash into the overall hash in hex format fmt.Fprintf(hasher, "%x", hash) } From 0021f0390533bd64b500286411a4d0771d57cdee Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 12 Sep 2024 10:15:37 -0700 Subject: [PATCH 109/440] Add back auto wildcarding --- internal/runners/install/install.go | 124 +++++++++++++++---------- pkg/platform/model/vcs.go | 13 --- test/integration/languages_int_test.go | 2 +- 3 files changed, 77 insertions(+), 62 deletions(-) diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index 929a894495..722e79afc3 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -3,7 +3,7 @@ package install import ( "errors" "fmt" - "strconv" + "regexp" "strings" "time" @@ -45,7 +45,8 @@ type Params struct { type resolvedRequirement struct { types.Requirement - Version string `json:"version"` + VersionLocale string `json:"version"` // VersionLocale represents the version as we want to show it to the user + ingredient *model.IngredientAndVersion } type requirement struct { @@ -182,7 +183,6 @@ type errNoMatches struct { // resolveRequirements will attempt to resolve the ingredient and namespace for each requested package func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Time, languages []model.Language) (requirements, error) { - disambiguate := map[*requirement][]*model.IngredientAndVersion{} failed := []*requirement{} reqs := []*requirement{} for _, pkg := range packages { @@ -198,31 +198,48 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti return nil, locale.WrapError(err, "err_pkgop_search_err", "Failed to check for ingredients.") } - // Resolve matched ingredients + // Filter out ingredients that don't target one of the supported languages if pkg.Namespace == "" { - // Filter out ingredients that don't target one of the supported languages ingredients = sliceutils.Filter(ingredients, func(iv *model.IngredientAndVersion) bool { + // Ensure namespace type matches if !model.NamespaceMatch(*iv.Ingredient.PrimaryNamespace, i.nsType.Matchable()) { return false } - il := model.LanguageFromNamespace(*iv.Ingredient.PrimaryNamespace) - for _, l := range languages { - if l.Name == il { - return true + + // Ensure that this is namespace covers one of the languages in our project + // But only if we're aiming to install a package or bundle, because otherwise the namespace is not + // guaranteed to hold the language. + if i.nsType == model.NamespacePackage || i.nsType == model.NamespaceBundle { + il := model.LanguageFromNamespace(*iv.Ingredient.PrimaryNamespace) + for _, l := range languages { + if l.Name == il { + return true + } } + return false } - return false + return true }) } - // Validate that the ingredient is resolved, and prompt the user if multiple ingredients matched - if req.Resolved.Namespace == "" { - if len(ingredients) == 0 { - failed = append(failed, req) - } else { - disambiguate[req] = ingredients + // Resolve matched ingredients + var ingredient *model.IngredientAndVersion + if len(ingredients) == 1 { + ingredient = ingredients[0] + } else if len(ingredients) > 1 { // This wouldn't ever trigger if namespace was provided as that should guarantee a single result + var err error + ingredient, err = i.promptForMatchingIngredient(req, ingredients) + if err != nil { + return nil, errs.Wrap(err, "Prompting for namespace failed") } } + if ingredient == nil { + failed = append(failed, req) + } else { + req.Resolved.Name = ingredient.Ingredient.NormalizedName + req.Resolved.Namespace = *ingredient.Ingredient.PrimaryNamespace + req.Resolved.ingredient = ingredient + } reqs = append(reqs, req) } @@ -232,46 +249,57 @@ func (i *Install) resolveRequirements(packages captain.PackagesValue, ts time.Ti return nil, errNoMatches{error: errs.New("Failed to resolve requirements"), requirements: failed, languages: languages} } - // Disambiguate requirements that had to be resolved with an ingredient lookup - if len(disambiguate) > 0 { - for req, ingredients := range disambiguate { - var ingredient *model.IngredientAndVersion - if len(ingredients) == 1 { - ingredient = ingredients[0] - } else { - var err error - ingredient, err = i.promptForMatchingIngredient(req, ingredients) - if err != nil { - return nil, errs.Wrap(err, "Prompting for namespace failed") - } - } - req.Resolved.Name = ingredient.Ingredient.NormalizedName - req.Resolved.Namespace = *ingredient.Ingredient.PrimaryNamespace - } - } - // Now that we have the ingredient resolved we can also resolve the version requirement. // We can also set the type and operation, which are used for conveying what happened to the user. for _, req := range reqs { + // Set requirement type req.Type = model.ParseNamespace(req.Resolved.Namespace).Type() - version := req.Requested.Version - if req.Requested.Version == "" { - req.Resolved.Version = locale.T("constraint_auto") - continue + + if err := resolveVersion(req); err != nil { + return nil, errs.Wrap(err, "Could not resolve version") } - if _, err := strconv.Atoi(version); err == nil { - // If the version number provided is a straight up integer (no dots or dashes) then assume it's a wildcard - version = fmt.Sprintf("%d.x", version) + } + + return reqs, nil +} + +var versionRe = regexp.MustCompile(`^\d(\.\d+)*$`) + +func resolveVersion(req *requirement) error { + version := req.Requested.Version + + // An empty version means "Auto" + if req.Requested.Version == "" { + req.Resolved.VersionLocale = locale.T("constraint_auto") + return nil + } + + // Verify that the version provided can be resolved + if versionRe.MatchString(version) { + match := false + for _, knownVersion := range req.Resolved.ingredient.Versions { + if knownVersion.Version == version { + match = true + break + } } - var err error - req.Resolved.Version = version - req.Resolved.VersionRequirement, err = bpModel.VersionStringToRequirements(version) - if err != nil { - return nil, errs.Wrap(err, "Could not process version string into requirements") + if !match { + for _, knownVersion := range req.Resolved.ingredient.Versions { + if strings.HasPrefix(knownVersion.Version, version) { + version = version + ".x" // The user supplied a partial version, resolve it as a wildcard + } + } } } - return reqs, nil + var err error + req.Resolved.VersionLocale = version + req.Resolved.VersionRequirement, err = bpModel.VersionStringToRequirements(version) + if err != nil { + return errs.Wrap(err, "Could not process version string into requirements") + } + + return nil } func (i *Install) promptForMatchingIngredient(req *requirement, ingredients []*model.IngredientAndVersion) (*model.IngredientAndVersion, error) { @@ -308,7 +336,7 @@ func (i *Install) renderUserFacing(reqs requirements) { if req.Operation == types.OperationUpdated { l = "install_report_updated" } - i.prime.Output().Notice(locale.Tr(l, fmt.Sprintf("%s/%s@%s", req.Resolved.Namespace, req.Resolved.Name, req.Resolved.Version))) + i.prime.Output().Notice(locale.Tr(l, fmt.Sprintf("%s/%s@%s", req.Resolved.Namespace, req.Resolved.Name, req.Resolved.VersionLocale))) } i.prime.Output().Notice("") } diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index ce79938f59..65e9db352e 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -671,19 +671,6 @@ func (cs indexedCommits) countBetween(first, last string) (int, error) { return ct, nil } -func ResolveRequirementNameAndVersion(name, version string, word int, namespace Namespace, auth *authentication.Auth) (string, string, error) { - if namespace.Type() == NamespacePlatform { - platform, err := FetchPlatformByDetails(name, version, word) - if err != nil { - return "", "", errs.Wrap(err, "Could not fetch platform") - } - name = platform.PlatformID.String() - version = "" - } - - return name, version, nil -} - func ChangesetFromRequirements(op Operation, reqs []*gqlModel.Requirement) Changeset { var changeset Changeset diff --git a/test/integration/languages_int_test.go b/test/integration/languages_int_test.go index c10aaf0107..9103fd9231 100644 --- a/test/integration/languages_int_test.go +++ b/test/integration/languages_int_test.go @@ -137,7 +137,7 @@ func (suite *LanguagesIntegrationTestSuite) TestWildcards() { // Test implicit wildcard. cp = ts.Spawn("languages", "install", "python@3.9") - cp.Expect("Updated: python@3.9") + cp.Expect("Updated: language/python@3.9.x") cp.ExpectExitCode(0) cp = ts.Spawn("history") cp.Expect("→ >=3.9,<3.10") From e12c61583999724c23104f245ffa074d26477c81 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 12 Sep 2024 10:26:43 -0700 Subject: [PATCH 110/440] Reduce API requests --- internal/runbits/commits_runbit/time.go | 17 +++++++++++++++++ internal/runners/install/install.go | 16 ++++++++-------- pkg/platform/model/checkpoints.go | 22 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/internal/runbits/commits_runbit/time.go b/internal/runbits/commits_runbit/time.go index ac9afb9717..af7f2e33e9 100644 --- a/internal/runbits/commits_runbit/time.go +++ b/internal/runbits/commits_runbit/time.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" @@ -63,3 +64,19 @@ func ExpandTimeForProject(ts *captain.TimeValue, auth *authentication.Auth, proj return timestamp, nil } + +// ExpandTimeForBuildScript is the same as ExpandTimeForProject except that it works off of a buildscript, allowing for +// fewer API round trips. +func ExpandTimeForBuildScript(ts *captain.TimeValue, auth *authentication.Auth, script *buildscript.BuildScript) (time.Time, error) { + timestamp, err := ExpandTime(ts, auth) + if err != nil { + return time.Time{}, errs.Wrap(err, "Unable to expand time") + } + + atTime := script.AtTime() + if atTime.After(timestamp) { + return *atTime, nil + } + + return timestamp, nil +} diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index 722e79afc3..db12bb874c 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -118,13 +118,6 @@ func (i *Install) Run(params Params) (rerr error) { { pg = output.StartSpinner(out, locale.T("progress_search"), constants.TerminalAnimationInterval) - // Resolve timestamp, commit and languages used for current project. - // This will be used to resolve the requirements. - ts, err = commits_runbit.ExpandTimeForProject(¶ms.Timestamp, i.prime.Auth(), i.prime.Project()) - if err != nil { - return errs.Wrap(err, "Unable to get timestamp from params") - } - // Grab local commit info localCommitID, err := localcommit.Get(i.prime.Project().Dir()) if err != nil { @@ -135,8 +128,15 @@ func (i *Install) Run(params Params) (rerr error) { return errs.Wrap(err, "Failed to fetch old build result") } + // Resolve timestamp, commit and languages used for current project. + // This will be used to resolve the requirements. + ts, err = commits_runbit.ExpandTimeForBuildScript(¶ms.Timestamp, i.prime.Auth(), oldCommit.BuildScript()) + if err != nil { + return errs.Wrap(err, "Unable to get timestamp from params") + } + // Get languages used in current project - languages, err := model.FetchLanguagesForCommit(localCommitID, i.prime.Auth()) + languages, err := model.FetchLanguagesForBuildScript(oldCommit.BuildScript()) if err != nil { logging.Debug("Could not get language from project: %v", err) } diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index f7beccbc5d..87f491a940 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -3,6 +3,7 @@ package model import ( "strings" + "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" "github.com/go-openapi/strfmt" @@ -72,6 +73,27 @@ func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([ return languages, nil } +// FetchLanguagesForBuildScript fetches a list of language names for the given buildscript +func FetchLanguagesForBuildScript(script *buildscript.BuildScript) ([]Language, error) { + languages := []Language{} + reqs, err := script.DependencyRequirements() + if err != nil { + return nil, errs.Wrap(err, "failed to get dependency requirements") + } + + for _, requirement := range reqs { + if NamespaceMatch(requirement.Namespace, NamespaceLanguageMatch) { + lang := Language{ + Name: requirement.Name, + Version: BuildPlannerVersionConstraintsToString(requirement.VersionRequirement), + } + languages = append(languages, lang) + } + } + + return languages, nil +} + // FetchCheckpointForCommit fetches the checkpoint for the given commit func FetchCheckpointForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]*gqlModel.Requirement, strfmt.DateTime, error) { logging.Debug("fetching checkpoint (%s)", commitID.String()) From 465a7e82ba8193334ba430f0a3195c2784363b4c Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 12 Sep 2024 11:02:23 -0700 Subject: [PATCH 111/440] Add back newline --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a581aaff3d..8169649d28 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -515,4 +515,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: build - path: build/ \ No newline at end of file + path: build/ From 74e7e6ab289ae0483a9aa447a09d8d621da01e23 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 12 Sep 2024 13:17:09 -0700 Subject: [PATCH 112/440] Remove unused var --- internal/runners/install/install.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index db12bb874c..25837289d5 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -111,7 +111,6 @@ func (i *Install) Run(params Params) (rerr error) { }() // Start process of resolving requirements - var err error var oldCommit *bpModel.Commit var reqs requirements var ts time.Time From 6d344d916bb931c631e0b725893483aa8bd9db44 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 09:04:56 -0700 Subject: [PATCH 113/440] Fix assertion --- test/integration/package_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 33764bf230..8e63340c10 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -567,7 +567,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_NoPrompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2") - cp.ExpectRe(`Warning: Dependency has \d+ known vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) } From 252009849ba303004d3d77c932c9e7697e85710e Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 09:07:56 -0700 Subject: [PATCH 114/440] Mark input errors --- internal/runners/install/rationalize.go | 15 ++++++++++++--- internal/runners/uninstall/rationalize.go | 10 ++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/internal/runners/install/rationalize.go b/internal/runners/install/rationalize.go index 2ef363548d..be4a6d3a5d 100644 --- a/internal/runners/install/rationalize.go +++ b/internal/runners/install/rationalize.go @@ -29,7 +29,10 @@ func (i *Install) rationalizeError(rerr *error) { names = append(names, fmt.Sprintf(`[ACTIONABLE]%s[/RESET]`, r.Requested.Name)) } if len(noMatchErr.requirements) > 1 { - *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_requirements_no_match", strings.Join(names, ", "))) + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("package_requirements_no_match", strings.Join(names, ", ")), + errs.SetInput()) return } suggestions, err := i.getSuggestions(noMatchErr.requirements[0], noMatchErr.languages) @@ -38,11 +41,17 @@ func (i *Install) rationalizeError(rerr *error) { } if len(suggestions) == 0 { - *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives_nosuggest", strings.Join(names, ", "))) + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("package_ingredient_alternatives_nosuggest", strings.Join(names, ", ")), + errs.SetInput()) return } - *rerr = errs.WrapUserFacing(*rerr, locale.Tr("package_ingredient_alternatives", strings.Join(names, ", "), strings.Join(suggestions, "\n"))) + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("package_ingredient_alternatives", strings.Join(names, ", "), strings.Join(suggestions, "\n")), + errs.SetInput()) // Error staging a commit during install. case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): diff --git a/internal/runners/uninstall/rationalize.go b/internal/runners/uninstall/rationalize.go index 767b1c3136..387c9869a2 100644 --- a/internal/runners/uninstall/rationalize.go +++ b/internal/runners/uninstall/rationalize.go @@ -19,10 +19,16 @@ func (u *Uninstall) rationalizeError(rerr *error) { return case errors.As(*rerr, &noMatchesErr): - *rerr = errs.WrapUserFacing(*rerr, locale.Tr("err_uninstall_nomatch", noMatchesErr.packages.String())) + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("err_uninstall_nomatch", noMatchesErr.packages.String()), + errs.SetInput()) case errors.As(*rerr, &multipleMatchesErr): - *rerr = errs.WrapUserFacing(*rerr, locale.Tr("err_uninstall_multimatch", multipleMatchesErr.packages.String())) + *rerr = errs.WrapUserFacing( + *rerr, + locale.Tr("err_uninstall_multimatch", multipleMatchesErr.packages.String()), + errs.SetInput()) // Error staging a commit during install. case errors.As(*rerr, ptr.To(&bpResp.CommitError{})): From 02ba52766043b37a4fa9ebca874a7d8b9bf054ff Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 09:30:59 -0700 Subject: [PATCH 115/440] Increase search timeout --- pkg/platform/model/inventory.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/platform/model/inventory.go b/pkg/platform/model/inventory.go index 780ff5bccb..aa4176e151 100644 --- a/pkg/platform/model/inventory.go +++ b/pkg/platform/model/inventory.go @@ -205,6 +205,7 @@ func searchIngredientsNamespace(ns string, name string, includeVersions bool, ex } params.SetLimit(&limit) params.SetHTTPClient(api.NewHTTPClient()) + params.WithTimeout(60 * time.Second) if ts != nil { dt := strfmt.DateTime(*ts) From a8a0bc0517f4360ba0377716a07abd697ee89f5f Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Fri, 13 Sep 2024 12:18:22 -0700 Subject: [PATCH 116/440] Apply suggestions from code review Co-authored-by: Nathan Rijksen --- cmd/state-svc/internal/hash/file_hasher.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 8018f956e8..159baef05b 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -25,11 +25,11 @@ type FileHasher struct { func NewFileHasher() *FileHasher { return &FileHasher{ - cache: cache.New(cache.NoExpiration, 24*time.Hour), + cache: cache.New(24*time.Hour, 24*time.Hour), } } -func (fh *FileHasher) HashFiles(files []string) (hash string, rerr error) { +func (fh *FileHasher) HashFiles(files []string) (string, error) { sort.Strings(files) hasher := xxhash.New() From 201fd9a4c8a32ca4ebac3d4884881a50c3a7e296 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 12 Sep 2024 11:19:08 -0400 Subject: [PATCH 117/440] Fix nightly failures due to CVE database update. Try to make these tests more future-proof. --- test/integration/package_int_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index d7243b1167..abddc257f3 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -665,8 +665,10 @@ func (suite *PackageIntegrationTestSuite) TestCVE_NoPrompt() { cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") cp.ExpectExitCode(0) + // Note: this version has 2 direct vulnerabilities, and 3 indirect vulnerabilities, but since + // we're not prompting, we're only showing a single count. cp = ts.Spawn("install", "urllib3@2.0.2") - cp.Expect("Warning: Dependency has 2 known vulnerabilities", e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has \d+ known vulnerabilities`) cp.ExpectExitCode(0) } @@ -689,7 +691,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Prompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2") - cp.Expect("Warning: Dependency has 2 known vulnerabilities") + cp.ExpectRe(`Warning: Dependency has \d+ direct and \d+ indirect known vulnerabilities`) cp.Expect("Do you want to continue") cp.SendLine("y") cp.ExpectExitCode(0) @@ -711,7 +713,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Indirect() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "private/ActiveState-CLI-Testing/language/python/django_dep", "--ts=now") - cp.ExpectRe(`Warning: Dependency has \d indirect known vulnerabilities`) + cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`) cp.Expect("Do you want to continue") cp.SendLine("n") cp.ExpectExitCode(1) From 4f56d4c66dde9ba8f23456cac8c66c3ad599e626 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 13 Sep 2024 11:02:29 -0400 Subject: [PATCH 118/440] Increase test timeout for --ts=now. --- test/integration/package_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index abddc257f3..dab9f5a0b0 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -713,7 +713,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Indirect() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "private/ActiveState-CLI-Testing/language/python/django_dep", "--ts=now") - cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`) + cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`, termtest.OptExpectTimeout(60*time.Second)) cp.Expect("Do you want to continue") cp.SendLine("n") cp.ExpectExitCode(1) From 39fdf53259312f037837fc09fb00f200182af15a Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 13 Sep 2024 12:29:16 -0700 Subject: [PATCH 119/440] Add back return value names --- cmd/state-svc/internal/hash/file_hasher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 159baef05b..fa1f836bd3 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -29,7 +29,7 @@ func NewFileHasher() *FileHasher { } } -func (fh *FileHasher) HashFiles(files []string) (string, error) { +func (fh *FileHasher) HashFiles(files []string) (hash string, rerr error) { sort.Strings(files) hasher := xxhash.New() From e28c63b17ce83c4d55818867ac3f596ae9baa7b4 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 13 Sep 2024 13:59:03 -0700 Subject: [PATCH 120/440] Use blank identifier --- cmd/state-svc/internal/hash/file_hasher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index fa1f836bd3..d237bf0c62 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -29,7 +29,7 @@ func NewFileHasher() *FileHasher { } } -func (fh *FileHasher) HashFiles(files []string) (hash string, rerr error) { +func (fh *FileHasher) HashFiles(files []string) (_ string, rerr error) { sort.Strings(files) hasher := xxhash.New() From d43b31a671faec2e7972adcb508a6c42971ef1c8 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:01:37 -0700 Subject: [PATCH 121/440] Add comment explaining async config condition --- internal/runbits/reqop_runbit/update.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/runbits/reqop_runbit/update.go b/internal/runbits/reqop_runbit/update.go index d56437339d..99333a0a90 100644 --- a/internal/runbits/reqop_runbit/update.go +++ b/internal/runbits/reqop_runbit/update.go @@ -90,6 +90,9 @@ func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit } // Start runtime sourcing UI + // Note normally we'd defer to Update's logic of async runtimes, but the reason we do this is to allow for solve + // errors to still be relayed even when using async. In this particular case the solving logic already happened + // when we created the commit, so running it again doesn't provide any value and only would slow things down. if !cfg.GetBool(constants.AsyncRuntimeConfig) { // refresh or install runtime _, err := runtime_runbit.Update(prime, trigger, From 0fa03bf4997d07ef471f9d6bc51cf87cf4786af5 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:05:02 -0700 Subject: [PATCH 122/440] Drop badly worded but also useless comments --- internal/runners/platforms/remove.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index 61369deb3d..7aab41b28a 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -19,17 +19,14 @@ import ( bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" ) -// RemoveRunParams tracks the info required for running Remove. type RemoveRunParams struct { Params } -// Remove manages the adding execution context. type Remove struct { prime primeable } -// NewRemove prepares an add execution context for use. func NewRemove(prime primeable) *Remove { return &Remove{ prime: prime, @@ -39,7 +36,6 @@ func NewRemove(prime primeable) *Remove { var errNoMatch = errors.New("no platform matched the search criteria") var errMultiMatch = errors.New("multiple platforms matched the search criteria") -// Run executes the add behavior. func (a *Remove) Run(params RemoveRunParams) (rerr error) { defer rationalizeRemovePlatformError(&rerr) From 9d7a94ac04e3362503a1843977a63d9d08ffbb99 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:08:41 -0700 Subject: [PATCH 123/440] Add comment for `SpawnDebuggerWithOpts` --- internal/testhelpers/e2e/session.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 5c780a145a..ca6c19217a 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -210,6 +210,12 @@ func (s *Session) Spawn(args ...string) *SpawnedCmd { return s.SpawnCmdWithOpts(s.Exe, OptArgs(args...)) } +// SpawnDebuggerWithOpts will spawn a state tool command with the dlv debugger in remote debugging port. +// It uses the default dlv port of `2345`. It has been tested in Goland (intellij), see instructions here: +// https://www.jetbrains.com/help/go/attach-to-running-go-processes-with-debugger.html#step-3-create-the-remote-run-debug-configuration-on-the-client-computer +// Note remote debugging seems a bit unreliable. I've found it works best to start the test code first, and once it is +// running then start the remote debugger. When I launch the remote debugger first it often doesn't take. But even +// when using this trickery it may at times not work; try restarting goland, your machine, or dlv. func (s *Session) SpawnDebuggerWithOpts(opts ...SpawnOptSetter) *SpawnedCmd { spawnOpts := s.newSpawnOpts(opts...) args := slices.Clone(spawnOpts.Args) From b4ada371b5e4287d6a25c7545ab08986be46f867 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:12:33 -0700 Subject: [PATCH 124/440] Remove unused function --- pkg/buildscript/mutations.go | 16 ---------------- pkg/platform/model/buildplanner/project.go | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index f1a6992d89..6c23b7a0d3 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -125,22 +125,6 @@ func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { return nil } -func (b *BuildScript) UpdatePlatform(operation types.Operation, platformID strfmt.UUID) error { - var err error - switch operation { - case types.OperationAdded: - err = b.AddPlatform(platformID) - case types.OperationRemoved: - err = b.RemovePlatform(platformID) - default: - return errs.New("Unsupported operation") - } - if err != nil { - return errs.Wrap(err, "Could not update BuildScript's platform") - } - return nil -} - func (b *BuildScript) AddPlatform(platformID strfmt.UUID) error { platformsNode, err := b.getPlatformsNode() if err != nil { diff --git a/pkg/platform/model/buildplanner/project.go b/pkg/platform/model/buildplanner/project.go index 6f52dcc06d..c9e84647b2 100644 --- a/pkg/platform/model/buildplanner/project.go +++ b/pkg/platform/model/buildplanner/project.go @@ -39,7 +39,7 @@ func (b *BuildPlanner) CreateProject(params *CreateProjectParams) (strfmt.UUID, } // Add the platform. - if err := script.UpdatePlatform(types.OperationAdded, params.PlatformID); err != nil { + if err := script.AddPlatform(params.PlatformID); err != nil { return "", errs.Wrap(err, "Unable to add platform") } From 70a6f307afa2fe8d00a407b76c0786e696009ce9 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:13:00 -0700 Subject: [PATCH 125/440] Rename method to be consistent --- internal/runners/publish/publish.go | 2 +- pkg/platform/model/vcs.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/publish/publish.go b/internal/runners/publish/publish.go index d75084e25d..a02ff27620 100644 --- a/internal/runners/publish/publish.go +++ b/internal/runners/publish/publish.go @@ -125,7 +125,7 @@ func (r *Runner) Run(params *Params) error { if params.Namespace != "" { reqVars.Namespace = params.Namespace } else if reqVars.Namespace == "" && r.project != nil && r.project.Owner() != "" { - reqVars.Namespace = model.NewOrgNamespace(r.project.Owner()).String() + reqVars.Namespace = model.NewNamespaceOrg(r.project.Owner()).String() } // Name diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 65e9db352e..4efaf98749 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -221,7 +221,7 @@ func NewNamespacePlatform() Namespace { return Namespace{NamespacePlatform, "platform"} } -func NewOrgNamespace(orgName string) Namespace { +func NewNamespaceOrg(orgName string) Namespace { return Namespace{ nsType: NamespaceOrg, value: NamespaceOrg.prefix + "/" + orgName, From 60bc05993bc66ea89cfdbd0823c953459ce1f259 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:14:18 -0700 Subject: [PATCH 126/440] Expectation can use full word --- test/integration/install_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index 3adbf3939c..f75de95ffa 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -42,7 +42,7 @@ func (suite *InstallIntegrationTestSuite) TestInstallSuggest() { cp = ts.Spawn("install", "djang") cp.Expect("No results found") cp.Expect("Did you mean") - cp.Expect("language/python/djang") + cp.Expect("language/python/django") cp.ExpectExitCode(1) } From 1ee70c93b7250f56a501d4ee7ab17f5f6454d7f3 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:17:47 -0700 Subject: [PATCH 127/440] Differentiate between solving and sourcing runtime timeouts --- internal/testhelpers/e2e/session.go | 6 ++++++ internal/testhelpers/e2e/session_unix.go | 15 --------------- internal/testhelpers/e2e/session_windows.go | 15 --------------- test/integration/package_int_test.go | 14 +++++++------- 4 files changed, 13 insertions(+), 37 deletions(-) delete mode 100644 internal/testhelpers/e2e/session_unix.go delete mode 100644 internal/testhelpers/e2e/session_windows.go diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index ca6c19217a..a0c02d6184 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -44,6 +44,12 @@ import ( "github.com/stretchr/testify/require" ) +var ( + RuntimeSolvingTimeoutOpt = termtest.OptExpectTimeout(1 * time.Minute) + RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) + RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) +) + // Session represents an end-to-end testing session during which several console process can be spawned and tested // It provides a consistent environment (environment variables and temporary // directories) that is shared by processes spawned during this session. diff --git a/internal/testhelpers/e2e/session_unix.go b/internal/testhelpers/e2e/session_unix.go deleted file mode 100644 index 970a8fbec0..0000000000 --- a/internal/testhelpers/e2e/session_unix.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build !windows -// +build !windows - -package e2e - -import ( - "time" - - "github.com/ActiveState/termtest" -) - -var ( - RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) - RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) -) diff --git a/internal/testhelpers/e2e/session_windows.go b/internal/testhelpers/e2e/session_windows.go deleted file mode 100644 index 6c1830eadd..0000000000 --- a/internal/testhelpers/e2e/session_windows.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build windows -// +build windows - -package e2e - -import ( - "time" - - "github.com/ActiveState/termtest" -) - -var ( - RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) - RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) -) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 8e63340c10..1b4ea761c3 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -273,7 +273,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_operation_multiple() { suite.Run("install", func() { cp := ts.Spawn("install", "requests", "urllib3@1.25.6") cp.Expect("Operating on project ActiveState-CLI/small-python") - cp.Expect("Added: language/python/requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added: language/python/requests", e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Added: language/python/urllib3") cp.Wait() }) @@ -281,14 +281,14 @@ func (suite *PackageIntegrationTestSuite) TestPackage_operation_multiple() { suite.Run("install (update)", func() { cp := ts.Spawn("install", "urllib3@1.25.8") cp.Expect("Operating on project ActiveState-CLI/small-python") - cp.Expect("Updated: language/python/urllib3", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Updated: language/python/urllib3", e2e.RuntimeSolvingTimeoutOpt) cp.Wait() }) suite.Run("uninstall", func() { cp := ts.Spawn("uninstall", "requests", "urllib3") cp.Expect("Operating on project ActiveState-CLI/small-python") - cp.Expect("Removed: language/python/requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Removed: language/python/requests", e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Removed: language/python/urllib3") cp.Wait() }) @@ -567,7 +567,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_NoPrompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2") - cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.ExpectExitCode(0) } @@ -590,7 +590,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Prompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z") - cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("y") cp.ExpectExitCode(0) @@ -612,7 +612,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Indirect() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "private/ActiveState-CLI-Testing/language/python/django_dep", "--ts=2024-09-10T16:36:34.393Z") - cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`, e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("n") cp.ExpectExitCode(1) @@ -637,7 +637,7 @@ func (suite *PackageIntegrationTestSuite) TestChangeSummary() { cp.Expect("├─ ") cp.Expect("├─ ") cp.Expect("└─ ") - cp.Expect("Added: language/python/requests", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added: language/python/requests", e2e.RuntimeSolvingTimeoutOpt) cp.ExpectExitCode(0) } From 2f767aa759c9cefaeb8db7e80ffeceedd3d0bf7e Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 14:19:07 -0700 Subject: [PATCH 128/440] Retaining dirs is by definition something we ought to only do for debugging --- test/integration/exec_int_test.go | 2 +- test/integration/executor_int_test.go | 2 +- test/integration/package_int_test.go | 8 ++++---- test/integration/prepare_int_test.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/integration/exec_int_test.go b/test/integration/exec_int_test.go index f3cfff58d7..1bc3009ac5 100644 --- a/test/integration/exec_int_test.go +++ b/test/integration/exec_int_test.go @@ -161,7 +161,7 @@ func (suite *ExecIntegrationTestSuite) TestExeBatArguments() { suite.T().Skip("This test is only for windows") } - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) defer ts.Close() ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") diff --git a/test/integration/executor_int_test.go b/test/integration/executor_int_test.go index 3ff24dd223..96a46a3dfe 100644 --- a/test/integration/executor_int_test.go +++ b/test/integration/executor_int_test.go @@ -96,7 +96,7 @@ func (suite *ExecutorIntegrationTestSuite) TestExecutorBatArguments() { suite.T().Skip("This test is only for windows") } - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) defer ts.Close() root := environment.GetRootPathUnsafe() diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 1b4ea761c3..7cafcc21e3 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -331,7 +331,7 @@ scripts: func (suite *PackageIntegrationTestSuite) TestPackage_Install() { suite.OnlyRunForTags(tagsuite.Package) - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) defer ts.Close() ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") @@ -346,7 +346,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_Install() { func (suite *PackageIntegrationTestSuite) TestPackage_Uninstall() { suite.OnlyRunForTags(tagsuite.Package) - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) defer ts.Close() ts.PrepareProject("ActiveState-CLI-Testing/small-python-with-pkg", "a2115792-2620-4217-89ed-b596c8c11ce3") @@ -361,7 +361,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_Uninstall() { func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDoesNotExist() { suite.OnlyRunForTags(tagsuite.Package) - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) defer ts.Close() suite.PrepareActiveStateYAML(ts) @@ -379,7 +379,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDoesNotExist() { func (suite *PackageIntegrationTestSuite) TestPackage_UninstallDupeMatch() { suite.OnlyRunForTags(tagsuite.Package) - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) defer ts.Close() ts.PrepareProject("ActiveState-CLI-Testing/duplicate-pkg-name", "e5a15d59-9192-446a-a133-9f4c2ebe0898") diff --git a/test/integration/prepare_int_test.go b/test/integration/prepare_int_test.go index 900e2ae7c5..0b68b9822e 100644 --- a/test/integration/prepare_int_test.go +++ b/test/integration/prepare_int_test.go @@ -124,7 +124,7 @@ func (suite *PrepareIntegrationTestSuite) AssertConfig(target string) { func (suite *PrepareIntegrationTestSuite) TestResetExecutors() { suite.OnlyRunForTags(tagsuite.Prepare) - ts := e2e.New(suite.T(), true) + ts := e2e.New(suite.T(), false) err := ts.ClearCache() suite.Require().NoError(err) defer ts.Close() From 05e069ccaea5c1e4c00fb4041b20be90451b155c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 13 Sep 2024 15:01:51 -0700 Subject: [PATCH 129/440] Add increased timeout --- test/integration/install_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index f75de95ffa..602631e592 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -40,7 +40,7 @@ func (suite *InstallIntegrationTestSuite) TestInstallSuggest() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "djang") - cp.Expect("No results found") + cp.Expect("No results found", e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Did you mean") cp.Expect("language/python/django") cp.ExpectExitCode(1) From 067dd4cb9fb3bffcf4660f9dd4d6a0557ccac31d Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 17 Sep 2024 10:53:41 -0400 Subject: [PATCH 130/440] Remove shell PS1/prompt modifications. --- internal/assets/contents/shells/bashrc.sh | 4 --- .../assets/contents/shells/bashrc_global.sh | 4 --- internal/assets/contents/shells/pwsh.ps1 | 10 ------- .../assets/contents/shells/pwsh_global.ps1 | 10 ------- internal/assets/contents/shells/zshrc.sh | 4 --- .../assets/contents/shells/zshrc_global.sh | 6 +--- internal/constants/constants.go | 3 -- internal/subshell/sscommon/rcfile.go | 11 ++----- test/integration/shell_int_test.go | 30 ------------------- test/integration/shells_int_test.go | 6 ---- 10 files changed, 3 insertions(+), 85 deletions(-) diff --git a/internal/assets/contents/shells/bashrc.sh b/internal/assets/contents/shells/bashrc.sh index 0f3094cbba..c632d14b1d 100644 --- a/internal/assets/contents/shells/bashrc.sh +++ b/internal/assets/contents/shells/bashrc.sh @@ -1,9 +1,5 @@ if [ -f ~/.bashrc ]; then source ~/.bashrc; fi -{{if and (ne .Owner "") (not .PreservePs1) }} -export PS1="[{{.Owner}}/{{.Name}}] $PS1" -{{end}} - cd "{{.WD}}" {{- range $K, $V := .Env}} diff --git a/internal/assets/contents/shells/bashrc_global.sh b/internal/assets/contents/shells/bashrc_global.sh index 61daa66d21..1ab77e375f 100644 --- a/internal/assets/contents/shells/bashrc_global.sh +++ b/internal/assets/contents/shells/bashrc_global.sh @@ -1,7 +1,3 @@ -{{if and (ne .Project "") (not .PreservePs1)}} -export PS1="[{{.Project}}] $PS1" -{{end}} - {{- range $K, $V := .Env}} {{- if eq $K "PATH"}} export {{$K}}="{{$V}}:$PATH" diff --git a/internal/assets/contents/shells/pwsh.ps1 b/internal/assets/contents/shells/pwsh.ps1 index 8d26682314..fbbe14f2ff 100644 --- a/internal/assets/contents/shells/pwsh.ps1 +++ b/internal/assets/contents/shells/pwsh.ps1 @@ -1,13 +1,3 @@ -{{if and (ne .Project "") (not .PreservePs1) }} -$prevPrompt = $ExecutionContext.SessionState.PSVariable.GetValue('prompt') -if ($prevPrompt -eq $null) { - $prevPrompt = "PS $PWD> " -} -function prompt { - "[{{.Project}}] $prevPrompt" -} -{{end}} - cd "{{.WD}}" {{- range $K, $V := .Env}} diff --git a/internal/assets/contents/shells/pwsh_global.ps1 b/internal/assets/contents/shells/pwsh_global.ps1 index 0f248f1e12..6ea4b04943 100644 --- a/internal/assets/contents/shells/pwsh_global.ps1 +++ b/internal/assets/contents/shells/pwsh_global.ps1 @@ -1,13 +1,3 @@ -{{if and (ne .Project "") (not .PreservePs1) }} -$prevPrompt = $ExecutionContext.SessionState.PSVariable.GetValue('prompt') -if ($prevPrompt -eq $null) { - $prevPrompt = "PS $PWD> " -} -function prompt { - "[{{.Project}}] $prevPrompt" -} -{{end}} - {{- range $K, $V := .Env}} {{- if eq $K "PATH"}} $env:{{$K}} = "{{ escapePwsh $V }};$env:PATH" diff --git a/internal/assets/contents/shells/zshrc.sh b/internal/assets/contents/shells/zshrc.sh index 7adb9a133c..9de475b8dd 100644 --- a/internal/assets/contents/shells/zshrc.sh +++ b/internal/assets/contents/shells/zshrc.sh @@ -2,10 +2,6 @@ if [ -f $ZDOTDIR/.zshrc ]; then source $ZDOTDIR/.zshrc; fi cd "{{.WD}}" -{{if and (ne .Owner "") (not .PreservePs1)}} -export PS1="[{{.Owner}}/{{.Name}}] $PS1" -{{end}} - {{- range $K, $V := .Env}} {{- if eq $K "PATH"}} export {{$K}}="{{$V}}:$PATH" diff --git a/internal/assets/contents/shells/zshrc_global.sh b/internal/assets/contents/shells/zshrc_global.sh index 9f19cbfbe8..1ab77e375f 100644 --- a/internal/assets/contents/shells/zshrc_global.sh +++ b/internal/assets/contents/shells/zshrc_global.sh @@ -1,11 +1,7 @@ -{{if and (ne .Project "") (not .PreservePs1)}} -export PS1="[{{.Project}}] $PS1" -{{- end}} - {{- range $K, $V := .Env}} {{- if eq $K "PATH"}} export {{$K}}="{{$V}}:$PATH" {{- else}} export {{$K}}="{{$V}}" {{- end}} -{{- end}} \ No newline at end of file +{{- end}} diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 34718abd9c..4c996b5a5b 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -470,9 +470,6 @@ const PipShim = "pip" // AutoUpdateConfigKey is the config key for storing whether or not autoupdates can be performed const AutoUpdateConfigKey = "autoupdate" -// PreservePs1ConfigKey is the config key that specifies whether to modify the shell PS1/prompt to show [org/project] info. -const PreservePs1ConfigKey = "shell.preserve.prompt" - // DefaultAnalyticsPixel is the default url for the analytics pixel const DefaultAnalyticsPixel = "https://state-tool.s3.amazonaws.com/pixel" diff --git a/internal/subshell/sscommon/rcfile.go b/internal/subshell/sscommon/rcfile.go index 189aab7719..80100c26a0 100644 --- a/internal/subshell/sscommon/rcfile.go +++ b/internal/subshell/sscommon/rcfile.go @@ -21,7 +21,6 @@ import ( "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" - configMediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/osutils" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/pkg/project" @@ -55,10 +54,6 @@ var ( } ) -func init() { - configMediator.RegisterOption(constants.PreservePs1ConfigKey, configMediator.Bool, false) -} - // Configurable defines an interface to store and get configuration data type Configurable interface { Set(string, interface{}) error @@ -215,9 +210,8 @@ func SetupShellRcFile(rcFileName, templateName string, env map[string]string, na var out bytes.Buffer rcData := map[string]interface{}{ - "Env": env, - "Project": projectValue, - "PreservePs1": cfg.GetBool(constants.PreservePs1ConfigKey), + "Env": env, + "Project": projectValue, } err = t.Execute(&out, rcData) if err != nil { @@ -338,7 +332,6 @@ func SetupProjectRcFile(prj *project.Project, templateName, ext string, env map[ "ExecName": constants.CommandName, "ActivatedMessage": colorize.ColorizedOrStrip(locale.Tl("project_activated", "[SUCCESS]✔ Project \"{{.V0}}\" Has Been Activated[/RESET]", prj.Namespace().String()), isConsole), - "PreservePs1": cfg.GetBool(constants.PreservePs1ConfigKey), } currExec := osutils.Executable() diff --git a/test/integration/shell_int_test.go b/test/integration/shell_int_test.go index b5d0114856..0ed1db7e7d 100644 --- a/test/integration/shell_int_test.go +++ b/test/integration/shell_int_test.go @@ -307,36 +307,6 @@ func (suite *ShellIntegrationTestSuite) TestNestedShellNotification() { cp.ExpectExitCode(0) } -func (suite *ShellIntegrationTestSuite) TestPs1() { - if runtime.GOOS == "windows" { - return // cmd.exe does not have a PS1 to modify - } - suite.OnlyRunForTags(tagsuite.Shell) - ts := e2e.New(suite.T(), false) - defer ts.Close() - - cp := ts.Spawn("checkout", "ActiveState-CLI/Empty") - cp.Expect("Checked out project") - cp.ExpectExitCode(0) - - cp = ts.SpawnWithOpts( - e2e.OptArgs("shell", "Empty"), - ) - cp.Expect("Activated") - cp.Expect("[ActiveState-CLI/Empty]") - cp.SendLine("exit") - cp.ExpectExitCode(0) - - cp = ts.Spawn("config", "set", constants.PreservePs1ConfigKey, "true") - cp.ExpectExitCode(0) - - cp = ts.Spawn("shell", "Empty") - cp.Expect("Activated") - suite.Assert().NotContains(cp.Snapshot(), "[ActiveState-CLI/Empty]") - cp.SendLine("exit") - cp.ExpectExitCode(0) -} - func (suite *ShellIntegrationTestSuite) TestProjectOrder() { suite.OnlyRunForTags(tagsuite.Critical, tagsuite.Shell) ts := e2e.New(suite.T(), false) diff --git a/test/integration/shells_int_test.go b/test/integration/shells_int_test.go index b2a2b3b045..45ee8d0942 100644 --- a/test/integration/shells_int_test.go +++ b/test/integration/shells_int_test.go @@ -82,12 +82,6 @@ func (suite *ShellsIntegrationTestSuite) TestShells() { cp.SendEnter() cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) - // Verify that the command prompt contains the right info, except for tcsh, whose prompt does - // not behave like other shells'. - if shell != e2e.Tcsh { - cp.Expect("[ActiveState-CLI/small-python]") - } - // Verify the runtime is functioning properly. cp.SendLine("python3 --version") cp.Expect("Python 3.10") From 55d90ba719b640b7135ebdfef50e5e49185a043b Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 17 Sep 2024 11:35:00 -0400 Subject: [PATCH 131/440] Added test that verifies a bare `state` is not making any API calls. --- test/integration/api_int_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/integration/api_int_test.go b/test/integration/api_int_test.go index cb656871cc..c00cf5c6a6 100644 --- a/test/integration/api_int_test.go +++ b/test/integration/api_int_test.go @@ -1,9 +1,12 @@ package integration import ( + "path/filepath" + "strings" "testing" "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" @@ -29,6 +32,30 @@ func (suite *ApiIntegrationTestSuite) TestRequestHeaders() { cp.ExpectExitCode(0) } +// TestNoApiCallsForPlainInvocation asserts that a bare `state` does not make any API calls. +func (suite *ApiIntegrationTestSuite) TestNoApiCallsForPlainInvocation() { + suite.OnlyRunForTags(tagsuite.Critical) + + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.SpawnWithOpts( + e2e.OptAppendEnv(constants.DebugServiceRequestsEnvVarName + "=true"), + ) + cp.ExpectExitCode(0) + + readLogFile := false + for _, path := range ts.LogFiles() { + if !strings.HasPrefix(filepath.Base(path), "state-") { + continue + } + contents := string(fileutils.ReadFileUnsafe(path)) + suite.Assert().NotContains(contents, "URL: ") // pkg/platform/api logs URL, User-Agent, and X-Requestor for API calls + readLogFile = true + } + suite.Assert().True(readLogFile, "did not read log file") +} + func TestApiIntegrationTestSuite(t *testing.T) { suite.Run(t, new(ApiIntegrationTestSuite)) } From d893ec6d386118897fdf4ce87dde6931687c87d5 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 17 Sep 2024 12:29:48 -0400 Subject: [PATCH 132/440] Added `ACTIVESTATE_CLI_IGNORE_ENV` for ignoring env vars during runtime setup. For example, PYTHONPATH. --- internal/constants/constants.go | 3 +++ internal/testhelpers/tagsuite/tagsuite.go | 1 + pkg/runtime/runtime.go | 10 ++++++++ test/integration/runtime_int_test.go | 29 +++++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 34718abd9c..79b8399a48 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -493,3 +493,6 @@ const PlatformPrivateNamespace = "private" // OverrideShellEnvVarName is the environment variable to set when overriding the shell for shell detection. const OverrideShellEnvVarName = "ACTIVESTATE_CLI_SHELL_OVERRIDE" + +// IgnoreEnvEnvVarName is the environment variable to set for skipping specific environment variables during runtime setup. +const IgnoreEnvEnvVarName = "ACTIVESTATE_CLI_IGNORE_ENV" diff --git a/internal/testhelpers/tagsuite/tagsuite.go b/internal/testhelpers/tagsuite/tagsuite.go index ce3a5d6968..3066469670 100644 --- a/internal/testhelpers/tagsuite/tagsuite.go +++ b/internal/testhelpers/tagsuite/tagsuite.go @@ -32,6 +32,7 @@ const ( DeleteProjects = "delete-uuid-projects" Deploy = "deploy" Edit = "edit" + Environment = "environment" Errors = "error" Events = "events" Exec = "exec" diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 1ce0c63fad..4caee1f0d9 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -4,7 +4,9 @@ import ( "maps" "os" "path/filepath" + "strings" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/logging" @@ -157,6 +159,14 @@ func (r *Runtime) getEnv(inherit bool) (map[string]string, map[string]string, er return empty, empty, errs.Wrap(err, "Failed to get environment variables") } + if ignores := os.Getenv(constants.IgnoreEnvEnvVarName); ignores != "" { + for _, name := range strings.Split(ignores, ",") { + if _, exists := vars[name]; exists { + delete(vars, name) + } + } + } + executorsPath := ExecutorsPath(r.path) execVars := maps.Clone(vars) diff --git a/test/integration/runtime_int_test.go b/test/integration/runtime_int_test.go index 83ab55cfa4..b48bca7aa9 100644 --- a/test/integration/runtime_int_test.go +++ b/test/integration/runtime_int_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/osutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/osutil" @@ -179,6 +180,34 @@ func (suite *RuntimeIntegrationTestSuite) TestBuildInProgress() { cp.ExpectExitCode(0) } +func (suite *RuntimeIntegrationTestSuite) TestIgnoreEnvironmentVars() { + suite.OnlyRunForTags(tagsuite.Environment) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("checkout", "ActiveState-CLI/small-python", ".") + cp.Expect("Checked out project", e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectExitCode(0) + + pythonPath := "my/path" + + cp = ts.SpawnWithOpts( + e2e.OptArgs("exec", "python3", "--", "-c", `print(__import__("os").environ["PYTHONPATH"])`), + e2e.OptAppendEnv("PYTHONPATH="+pythonPath), + ) + cp.ExpectExitCode(0) + suite.Assert().NotContains(cp.Snapshot(), pythonPath) + + cp = ts.SpawnWithOpts( + e2e.OptArgs("exec", "python3", "--", "-c", `print(__import__("os").environ["PYTHONPATH"])`), + e2e.OptAppendEnv( + "PYTHONPATH="+pythonPath, + constants.IgnoreEnvEnvVarName+"=PYTHONPATH", + )) + cp.Expect(pythonPath) + cp.ExpectExitCode(0) +} + func TestRuntimeIntegrationTestSuite(t *testing.T) { suite.Run(t, new(RuntimeIntegrationTestSuite)) } From a630247b9032eac41abe09d472f8a98509b5fe56 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 17 Sep 2024 14:22:34 -0400 Subject: [PATCH 133/440] Allow config options to be an enum. Use it for 'security.prompt.level'. --- internal/mediators/config/registry.go | 19 +++++++++++++++++- internal/runbits/cves/cves.go | 8 +++++++- internal/runners/config/set.go | 11 ++++++++++ test/integration/config_int_test.go | 29 +++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/internal/mediators/config/registry.go b/internal/mediators/config/registry.go index 767d87caa6..d2af1f32d3 100644 --- a/internal/mediators/config/registry.go +++ b/internal/mediators/config/registry.go @@ -6,12 +6,20 @@ const ( String Type = iota Int Bool + Enum ) // Event is run when a user tries to set or get a config value via `state config` type Event func(value interface{}) (interface{}, error) -var EmptyEvent = func(value interface{}) (interface{}, error) { return value, nil } +var EmptyEvent = func(value interface{}) (interface{}, error) { + if enum, ok := value.(*Enums); ok { + // In case this config option is not set, return its default value instead + // of the Enums struct itself. + return enum.Default, nil + } + return value, nil +} // Option defines what a config value's name and type should be, along with any get/set events type Option struct { @@ -27,6 +35,15 @@ type Registry map[string]Option var registry = make(Registry) +type Enums struct { + Options []string + Default string +} + +func NewEnum(options []string, default_ string) *Enums { + return &Enums{options, default_} +} + // GetOption returns a config option, regardless of whether or not it has been registered. // Use KnownOption to determine if the returned option has been previously registered. func GetOption(key string) Option { diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index e3cdf029e2..96c88135f1 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -21,7 +21,13 @@ import ( func init() { configMediator.RegisterOption(constants.SecurityPromptConfig, configMediator.Bool, true) - configMediator.RegisterOption(constants.SecurityPromptLevelConfig, configMediator.String, vulnModel.SeverityCritical) + severities := configMediator.NewEnum([]string{ + vulnModel.SeverityCritical, + vulnModel.SeverityHigh, + vulnModel.SeverityMedium, + vulnModel.SeverityLow, + }, vulnModel.SeverityCritical) + configMediator.RegisterOption(constants.SecurityPromptLevelConfig, configMediator.Enum, severities) } type primeable interface { diff --git a/internal/runners/config/set.go b/internal/runners/config/set.go index eeb1719804..5c2b0d4d44 100644 --- a/internal/runners/config/set.go +++ b/internal/runners/config/set.go @@ -4,6 +4,9 @@ import ( "context" "fmt" "strconv" + "strings" + + "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/analytics" "github.com/ActiveState/cli/internal/analytics/constants" @@ -52,6 +55,14 @@ func (s *Set) Run(params SetParams) error { if err != nil { return locale.WrapInputError(err, "Invalid integer value") } + case configMediator.Enum: + enums := option.Default.(*configMediator.Enums) + if !funk.Contains(enums.Options, params.Value) { + return locale.NewInputError( + "err_config_set_enum_invalid_value", "Invalid value '{{.V0}}': expected one of: {{.V1}}", + params.Value, strings.Join(enums.Options, ", ")) + } + value = params.Value default: value = params.Value } diff --git a/test/integration/config_int_test.go b/test/integration/config_int_test.go index 4a2bdd8b00..db1bb3dfe9 100644 --- a/test/integration/config_int_test.go +++ b/test/integration/config_int_test.go @@ -1,12 +1,14 @@ package integration import ( + "strings" "testing" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" + vulnModel "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/model" ) type ConfigIntegrationTestSuite struct { @@ -41,6 +43,33 @@ func (suite *ConfigIntegrationTestSuite) TestConfig() { cp.Expect("Invalid boolean value") } +func (suite *ConfigIntegrationTestSuite) TestEnum() { + suite.OnlyRunForTags(tagsuite.Config) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("config", "get", constants.SecurityPromptLevelConfig) + cp.Expect(vulnModel.SeverityCritical) + + severities := []string{ + vulnModel.SeverityCritical, + vulnModel.SeverityHigh, + vulnModel.SeverityMedium, + vulnModel.SeverityLow, + } + + cp = ts.Spawn("config", "set", constants.SecurityPromptLevelConfig, "invalid") + cp.Expect("Invalid value 'invalid': expected one of: " + strings.Join(severities, ", ")) + cp.ExpectNotExitCode(0) + + cp = ts.Spawn("config", "set", constants.SecurityPromptLevelConfig, vulnModel.SeverityLow) + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "get", constants.SecurityPromptLevelConfig) + cp.Expect(vulnModel.SeverityLow) + cp.ExpectExitCode(0) +} + func (suite *ConfigIntegrationTestSuite) TestJSON() { suite.OnlyRunForTags(tagsuite.Config, tagsuite.JSON) ts := e2e.New(suite.T(), false) From 73390a5fb2caf882feea9d52d09a98901f96e742 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 18 Sep 2024 14:23:08 -0700 Subject: [PATCH 134/440] Add `state export deptree` commands --- cmd/state/internal/cmdtree/cmdtree.go | 7 + cmd/state/internal/cmdtree/export.go | 104 +++++++++++++ internal/runners/export/deptree/artifacts.go | 142 ++++++++++++++++++ internal/runners/export/deptree/common.go | 56 +++++++ .../runners/export/deptree/ingredients.go | 126 ++++++++++++++++ pkg/buildplan/artifact.go | 4 +- pkg/buildplan/filters.go | 13 +- pkg/buildplan/hydrate.go | 8 +- 8 files changed, 452 insertions(+), 8 deletions(-) create mode 100644 internal/runners/export/deptree/artifacts.go create mode 100644 internal/runners/export/deptree/common.go create mode 100644 internal/runners/export/deptree/ingredients.go diff --git a/cmd/state/internal/cmdtree/cmdtree.go b/cmd/state/internal/cmdtree/cmdtree.go index cfc4dd6b71..b1aaa6477e 100644 --- a/cmd/state/internal/cmdtree/cmdtree.go +++ b/cmd/state/internal/cmdtree/cmdtree.go @@ -37,6 +37,12 @@ func New(prime *primer.Values, args ...string) *CmdTree { newOpenCommand(prime), ) + deptree := newExportDepTreeCommand(prime) + deptree.AddChildren( + newExportDepTreeArtifactsCommand(prime), + newExportDepTreeIngredientsCommand(prime), + ) + exportCmd := newExportCommand(prime) exportCmd.AddChildren( newJWTCommand(prime), @@ -49,6 +55,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { newExportLogCommand(prime), newExportRuntimeCommand(prime), newExportBuildPlanCommand(prime), + deptree, ) platformsCmd := newPlatformsCommand(prime) diff --git a/cmd/state/internal/cmdtree/export.go b/cmd/state/internal/cmdtree/export.go index 2d027e8788..924f82c801 100644 --- a/cmd/state/internal/cmdtree/export.go +++ b/cmd/state/internal/cmdtree/export.go @@ -8,6 +8,7 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runners/export" "github.com/ActiveState/cli/internal/runners/export/config" + "github.com/ActiveState/cli/internal/runners/export/deptree" "github.com/ActiveState/cli/internal/runners/export/docs" "github.com/ActiveState/cli/internal/runners/export/ghactions" "github.com/ActiveState/cli/pkg/project" @@ -267,3 +268,106 @@ func newExportBuildPlanCommand(prime *primer.Values) *captain.Command { return cmd } + +func newExportDepTreeCommand(prime *primer.Values) *captain.Command { + cmd := captain.NewCommand( + "deptree", + locale.Tl("export_dep_tree_title", "Export Dependency Tree"), + locale.Tl("export_dep_tree_description", "Export the dependency tree for your project"), + prime, + []*captain.Flag{}, + []*captain.Argument{}, + func(ccmd *captain.Command, _ []string) error { + prime.Output().Print(ccmd.Help()) + return nil + }, + ) + cmd.SetHidden(true) // For development purposes only at the moment + cmd.SetUnstable(true) + return cmd +} + +func newExportDepTreeArtifactsCommand(prime *primer.Values) *captain.Command { + params := deptree.ArtifactParams{Namespace: &project.Namespaced{}} + runner := deptree.NewByArtifacts(prime) + cmd := captain.NewCommand( + "artifacts", + locale.Tl("export_dep_tree_title", "Export Dependency Tree"), + locale.Tl("export_dep_tree_description", "Export the dependency tree for your project"), + prime, + []*captain.Flag{ + { + Name: "namespace", + Description: locale.Tl("export_dep_tree_flags_namespace_description", "The namespace of the project to inspect dependencies for"), + Value: params.Namespace, + }, + { + Name: "commit", + Description: locale.Tl("export_dep_tree_flags_commit_description", "The commit ID to inspect dependencies for"), + Value: ¶ms.CommitID, + }, + { + Name: "req", + Description: locale.Tl("export_dep_tree_flag_req_description", "Requirement name to filter for"), + Value: ¶ms.Req, + }, + { + Name: "platform", + Description: locale.Tl("export_dep_tree_flag_platform_description", "Platform ID to filter for (defaults to host platform)"), + Value: ¶ms.PlatformID, + }, + { + Name: "limit", + Description: locale.Tl("export_dep_tree_flag_limit_description", "Limit the recursion level"), + Value: ¶ms.LevelLimit, + }, + }, + []*captain.Argument{}, + func(ccmd *captain.Command, _ []string) error { + return runner.Run(params) + }, + ) + cmd.SetHidden(true) // For development purposes only at the moment + cmd.SetUnstable(true) + return cmd +} + +func newExportDepTreeIngredientsCommand(prime *primer.Values) *captain.Command { + params := deptree.IngredientParams{Namespace: &project.Namespaced{}} + runner := deptree.NewByIngredients(prime) + cmd := captain.NewCommand( + "ingredients", + locale.Tl("export_dep_tree_title", "Export Dependency Tree"), + locale.Tl("export_dep_tree_description", "Export the dependency tree for your project"), + prime, + []*captain.Flag{ + { + Name: "namespace", + Description: locale.Tl("export_dep_tree_flags_namespace_description", "The namespace of the project to inspect dependencies for"), + Value: params.Namespace, + }, + { + Name: "commit", + Description: locale.Tl("export_dep_tree_flags_commit_description", "The commit ID to inspect dependencies for"), + Value: ¶ms.CommitID, + }, + { + Name: "req", + Description: locale.Tl("export_dep_tree_flag_req_description", "Requirement name to filter for"), + Value: ¶ms.Req, + }, + { + Name: "limit", + Description: locale.Tl("export_dep_tree_flag_limit_description", "Limit the recursion level"), + Value: ¶ms.LevelLimit, + }, + }, + []*captain.Argument{}, + func(ccmd *captain.Command, _ []string) error { + return runner.Run(params) + }, + ) + cmd.SetHidden(true) // For development purposes only at the moment + cmd.SetUnstable(true) + return cmd +} diff --git a/internal/runners/export/deptree/artifacts.go b/internal/runners/export/deptree/artifacts.go new file mode 100644 index 0000000000..7e09164fcc --- /dev/null +++ b/internal/runners/export/deptree/artifacts.go @@ -0,0 +1,142 @@ +package deptree + +import ( + "fmt" + "strings" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/internal/sliceutils" + "github.com/ActiveState/cli/pkg/buildplan" + "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/ActiveState/cli/pkg/project" + "github.com/ActiveState/cli/pkg/sysinfo" + "github.com/go-openapi/strfmt" +) + +type primeable interface { + primer.Auther + primer.Outputer + primer.Configurer + primer.Projecter + primer.Analyticer + primer.SvcModeler +} + +type ArtifactParams struct { + Namespace *project.Namespaced + CommitID string + Req string + PlatformID string + LevelLimit int +} + +type DeptreeByArtifacts struct { + prime primeable +} + +func NewByArtifacts(prime primeable) *DeptreeByArtifacts { + return &DeptreeByArtifacts{ + prime: prime, + } +} + +func (d *DeptreeByArtifacts) Run(params ArtifactParams) error { + logging.Debug("Execute DepTree") + + out := d.prime.Output() + proj := d.prime.Project() + if proj == nil { + return rationalize.ErrNoProject + } + + ns, err := resolveNamespace(params.Namespace, params.CommitID, d.prime) + if err != nil { + return errs.Wrap(err, "Could not resolve namespace") + } + + bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth()) + commit, err := bpm.FetchCommit(*ns.CommitID, ns.Owner, ns.Project, nil) + if err != nil { + return errs.Wrap(err, "Could not get remote build expr and time for provided commit") + } + + bp := commit.BuildPlan() + + platformID := strfmt.UUID(params.PlatformID) + if platformID == "" { + platformID, err = model.FilterCurrentPlatform(sysinfo.OS().String(), bp.Platforms(), "") + if err != nil { + return errs.Wrap(err, "Could not get platform ID") + } + } + + levelLimit := params.LevelLimit + if levelLimit == 0 { + levelLimit = 10 + } + + ingredients := bp.RequestedIngredients() + for _, ingredient := range ingredients { + if params.Req != "" && ingredient.Name != params.Req { + continue + } + out.Print(fmt.Sprintf("• [ACTIONABLE]%s/%s[/RESET] ([DISABLED]%s[/RESET])", ingredient.Namespace, ingredient.Name, ingredient.IngredientID)) + d.printArtifacts( + nil, + ingredient.Artifacts.Filter( + buildplan.FilterPlatformArtifacts(platformID), + ), + platformID, + 1, + levelLimit, + ) + } + + return nil +} + +func (d *DeptreeByArtifacts) printArtifacts( + parents []*buildplan.Artifact, + as buildplan.Artifacts, + platformID strfmt.UUID, + level int, + levelLimit int) { + indent := strings.Repeat(" ", level) + if level == levelLimit { + d.prime.Output().Print(indent + indentValue + "[ORANGE]Recursion limit reached[/RESET]") + return + } + count := 0 + for _, a := range as { + if len(sliceutils.Filter(parents, func(p *buildplan.Artifact) bool { return p.ArtifactID == a.ArtifactID })) != 0 { + d.prime.Output().Print(fmt.Sprintf("%s • Recurse to [CYAN]%s[/RESET] ([DISABLED]%s[/RESET])", indent, a.DisplayName, a.ArtifactID)) + continue + } + depTypes := []string{} + if a.IsRuntimeDependency { + depTypes = append(depTypes, "[GREEN]Runtime[/RESET]") + } + if a.IsBuildtimeDependency { + depTypes = append(depTypes, "[ORANGE]Buildtime[/RESET]") + } + mime := "" + if !buildplanner.IsStateToolArtifact(a.MimeType) { + mime = fmt.Sprintf(" ([DISABLED]%s[/RESET])", a.MimeType) + } + count = count + 1 + d.prime.Output().Print(fmt.Sprintf("%s%d. [CYAN]%s[/RESET] [%s] ([DISABLED]%s[/RESET]) %s", indent, count, a.DisplayName, strings.Join(depTypes, "|"), a.ArtifactID, mime)) + d.printArtifacts( + append(parents, a), + a.Dependencies(false).Filter( + buildplan.FilterPlatformArtifacts(platformID), + ), + platformID, + level+1, + levelLimit, + ) + } +} diff --git a/internal/runners/export/deptree/common.go b/internal/runners/export/deptree/common.go new file mode 100644 index 0000000000..865bbef076 --- /dev/null +++ b/internal/runners/export/deptree/common.go @@ -0,0 +1,56 @@ +package deptree + +import ( + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/pkg/localcommit" + "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/project" + "github.com/go-openapi/strfmt" +) + +func resolveNamespace(inputNs *project.Namespaced, inputCommitID string, prime primeable) (*project.Namespaced, error) { + out := prime.Output() + proj := prime.Project() + if proj == nil { + return nil, rationalize.ErrNoProject + } + + ns := inputNs + dir := "https://" + constants.PlatformURL + "/" + ns.String() + if !ns.IsValid() { + ns = proj.Namespace() + dir = proj.Dir() + } + + commitID := strfmt.UUID(inputCommitID) + if commitID == "" { + if inputNs.IsValid() { + p, err := model.FetchProjectByName(ns.Owner, ns.Project, prime.Auth()) + if err != nil { + return nil, errs.Wrap(err, "Unable to get project") + } + branch, err := model.DefaultBranchForProject(p) + if err != nil { + return nil, errs.Wrap(err, "Could not grab branch for project") + } + if branch.CommitID == nil { + return nil, errs.New("branch has not commit") + } + ns.CommitID = branch.CommitID + } else { + var err error + commitID, err = localcommit.Get(proj.Dir()) + if err != nil { + return nil, errs.Wrap(err, "Unable to get local commit ID") + } + ns.CommitID = &commitID + } + } + + out.Notice(locale.Tr("operating_message", ns.String(), dir)) + + return ns, nil +} diff --git a/internal/runners/export/deptree/ingredients.go b/internal/runners/export/deptree/ingredients.go new file mode 100644 index 0000000000..f116a00c07 --- /dev/null +++ b/internal/runners/export/deptree/ingredients.go @@ -0,0 +1,126 @@ +package deptree + +import ( + "fmt" + "sort" + "strings" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/internal/sliceutils" + "github.com/ActiveState/cli/pkg/buildplan" + "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/ActiveState/cli/pkg/project" + "github.com/go-openapi/strfmt" +) + +type DeptreeByIngredients struct { + prime primeable +} + +func NewByIngredients(prime primeable) *DeptreeByIngredients { + return &DeptreeByIngredients{ + prime: prime, + } +} + +type IngredientParams struct { + Namespace *project.Namespaced + CommitID string + Req string + LevelLimit int +} + +func (d *DeptreeByIngredients) Run(params IngredientParams) error { + logging.Debug("Execute DeptreeByIngredients") + + proj := d.prime.Project() + if proj == nil { + return rationalize.ErrNoProject + } + + ns, err := resolveNamespace(params.Namespace, params.CommitID, d.prime) + if err != nil { + return errs.Wrap(err, "Could not resolve namespace") + } + + bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth()) + commit, err := bpm.FetchCommit(*ns.CommitID, ns.Owner, ns.Project, nil) + if err != nil { + return errs.Wrap(err, "Could not get remote build expr and time for provided commit") + } + + bp := commit.BuildPlan() + + levelLimit := params.LevelLimit + if levelLimit == 0 { + levelLimit = 10 + } + + ingredients := bp.RequestedIngredients() + common := ingredients.CommonRuntimeDependencies().ToIDMap() + + // Ensure languages are listed first, because they tend to themselves be dependencies + sort.Slice(ingredients, func(i, j int) bool { return ingredients[i].Namespace == model.NamespaceLanguage.String() }) + + if params.Req != "" { + ingredients = sliceutils.Filter(ingredients, func(i *buildplan.Ingredient) bool { + return i.Name == params.Req + }) + } + + d.printIngredients( + ingredients, + common, + 0, + levelLimit, + make(map[strfmt.UUID]struct{}), + ) + + return nil +} + +const indentValue = " " + +func (d *DeptreeByIngredients) printIngredients( + is buildplan.Ingredients, + common buildplan.IngredientIDMap, + level int, + levelLimit int, + seen map[strfmt.UUID]struct{}, +) { + indent := strings.Repeat(indentValue, level) + if level == levelLimit { + d.prime.Output().Print(indent + indentValue + "[ORANGE]Recursion limit reached[/RESET]") + return + } + count := 0 + for _, i := range is { + count = count + 1 + + color := "CYAN" + if _, ok := common[i.IngredientID]; ok { + color = "YELLOW" + } + d.prime.Output().Print(fmt.Sprintf("%s%d. [%s]%s/%s[/RESET] ([DISABLED]%s[/RESET])", + indent, count, color, i.Namespace, i.Name, i.IngredientID)) + + if _, ok := seen[i.IngredientID]; ok { + d.prime.Output().Print(fmt.Sprintf( + indent + indentValue + indentValue + "[DISABLED]Already listed[/RESET]", + )) + continue + } + seen[i.IngredientID] = struct{}{} + + d.printIngredients( + i.RuntimeDependencies(false), + common, + level+1, + levelLimit, + seen, + ) + } +} diff --git a/pkg/buildplan/artifact.go b/pkg/buildplan/artifact.go index 8a2c58436e..28d178c8e6 100644 --- a/pkg/buildplan/artifact.go +++ b/pkg/buildplan/artifact.go @@ -26,8 +26,8 @@ type Artifact struct { Ingredients []*Ingredient `json:"-"` // While most artifacts only have a single ingredient, some artifacts such as installers can have multiple. - isRuntimeDependency bool - isBuildtimeDependency bool + IsRuntimeDependency bool + IsBuildtimeDependency bool platforms []strfmt.UUID children []ArtifactRelation diff --git a/pkg/buildplan/filters.go b/pkg/buildplan/filters.go index 242ef56e19..0c3bf6dbc1 100644 --- a/pkg/buildplan/filters.go +++ b/pkg/buildplan/filters.go @@ -22,13 +22,13 @@ func FilterPlatformArtifacts(platformID strfmt.UUID) FilterArtifact { func FilterBuildtimeArtifacts() FilterArtifact { return func(a *Artifact) bool { - return a.isBuildtimeDependency + return a.IsBuildtimeDependency } } func FilterRuntimeArtifacts() FilterArtifact { return func(a *Artifact) bool { - return a.isRuntimeDependency + return a.IsRuntimeDependency } } @@ -76,3 +76,12 @@ func FilterNotBuild() FilterArtifact { return a.Status != types.ArtifactSucceeded } } + +type FilterOutIngredients struct { + Ingredients IngredientIDMap +} + +func (f FilterOutIngredients) Filter(i *Ingredient) bool { + _, blacklist := f.Ingredients[i.IngredientID] + return !blacklist +} diff --git a/pkg/buildplan/hydrate.go b/pkg/buildplan/hydrate.go index 216bcb7616..fcb7e40110 100644 --- a/pkg/buildplan/hydrate.go +++ b/pkg/buildplan/hydrate.go @@ -92,7 +92,7 @@ func (b *BuildPlan) hydrateWithBuildClosure(nodeIDs []strfmt.UUID, platformID *s } artifact.platforms = sliceutils.Unique(append(artifact.platforms, *platformID)) - artifact.isBuildtimeDependency = true + artifact.IsBuildtimeDependency = true if parent != nil { parentArtifact, ok := artifactLookup[parent.NodeID] @@ -137,7 +137,7 @@ func (b *BuildPlan) hydrateWithRuntimeClosure(nodeIDs []strfmt.UUID, platformID } artifact.platforms = sliceutils.Unique(append(artifact.platforms, *platformID)) - artifact.isRuntimeDependency = true + artifact.IsRuntimeDependency = true return nil default: @@ -182,10 +182,10 @@ func (b *BuildPlan) hydrateWithIngredients(artifact *Artifact, platformID *strfm ingredient.platforms = append(ingredient.platforms, *platformID) } - if artifact.isBuildtimeDependency { + if artifact.IsBuildtimeDependency { ingredient.IsBuildtimeDependency = true } - if artifact.isRuntimeDependency { + if artifact.IsRuntimeDependency { ingredient.IsRuntimeDependency = true } From a8dbb268da0b84b589b6b0218731435b356d413e Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 18 Sep 2024 14:34:03 -0700 Subject: [PATCH 135/440] Drop FilterOutIngredients; meant for other PR --- pkg/buildplan/filters.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pkg/buildplan/filters.go b/pkg/buildplan/filters.go index 0c3bf6dbc1..9d56906a0a 100644 --- a/pkg/buildplan/filters.go +++ b/pkg/buildplan/filters.go @@ -76,12 +76,3 @@ func FilterNotBuild() FilterArtifact { return a.Status != types.ArtifactSucceeded } } - -type FilterOutIngredients struct { - Ingredients IngredientIDMap -} - -func (f FilterOutIngredients) Filter(i *Ingredient) bool { - _, blacklist := f.Ingredients[i.IngredientID] - return !blacklist -} From 070ee59a38dd8af43388cb5f19aefe2eeef2e19a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 18 Sep 2024 14:35:10 -0700 Subject: [PATCH 136/440] Add new argument --- internal/runners/export/deptree/artifacts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runners/export/deptree/artifacts.go b/internal/runners/export/deptree/artifacts.go index 7e09164fcc..26184a1b2c 100644 --- a/internal/runners/export/deptree/artifacts.go +++ b/internal/runners/export/deptree/artifacts.go @@ -131,7 +131,7 @@ func (d *DeptreeByArtifacts) printArtifacts( d.prime.Output().Print(fmt.Sprintf("%s%d. [CYAN]%s[/RESET] [%s] ([DISABLED]%s[/RESET]) %s", indent, count, a.DisplayName, strings.Join(depTypes, "|"), a.ArtifactID, mime)) d.printArtifacts( append(parents, a), - a.Dependencies(false).Filter( + a.Dependencies(false, nil).Filter( buildplan.FilterPlatformArtifacts(platformID), ), platformID, From 7cf6a237902b96c2e2facb89aec2075bb0cdc397 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 18 Sep 2024 14:33:32 -0700 Subject: [PATCH 137/440] Add FilterOutIngredients --- pkg/buildplan/filters.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/buildplan/filters.go b/pkg/buildplan/filters.go index 9d56906a0a..0c3bf6dbc1 100644 --- a/pkg/buildplan/filters.go +++ b/pkg/buildplan/filters.go @@ -76,3 +76,12 @@ func FilterNotBuild() FilterArtifact { return a.Status != types.ArtifactSucceeded } } + +type FilterOutIngredients struct { + Ingredients IngredientIDMap +} + +func (f FilterOutIngredients) Filter(i *Ingredient) bool { + _, blacklist := f.Ingredients[i.IngredientID] + return !blacklist +} From 6256c88907e2fd9760c30e0db6fc2d082ef55414 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 18 Sep 2024 15:03:38 -0700 Subject: [PATCH 138/440] Fix exec should bypass async; not export env --- internal/runners/exec/exec.go | 2 +- internal/runners/export/env.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/exec/exec.go b/internal/runners/exec/exec.go index 6ddca60d0a..038d1354a9 100644 --- a/internal/runners/exec/exec.go +++ b/internal/runners/exec/exec.go @@ -126,7 +126,7 @@ func (s *Exec) Run(params *Params, args ...string) (rerr error) { s.out.Notice(locale.Tr("operating_message", projectNamespace, projectDir)) - rt, err := runtime_runbit.Update(s.prime, trigger, runtime_runbit.WithoutHeaders()) + rt, err := runtime_runbit.Update(s.prime, trigger, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) if err != nil { return errs.Wrap(err, "Could not initialize runtime") } diff --git a/internal/runners/export/env.go b/internal/runners/export/env.go index c2c9e86c92..73f2f073f3 100644 --- a/internal/runners/export/env.go +++ b/internal/runners/export/env.go @@ -47,7 +47,7 @@ func (e *Env) Run() error { e.project.Dir()), ) - rt, err := runtime_runbit.Update(e.prime, trigger.TriggerActivate, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) + rt, err := runtime_runbit.Update(e.prime, trigger.TriggerActivate, runtime_runbit.WithoutHeaders()) if err != nil { return locale.WrapError(err, "err_export_new_runtime", "Could not initialize runtime") } From 7a0c29daf63770dd70e89af6840997df42f1fb80 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 19 Sep 2024 09:15:17 -0700 Subject: [PATCH 139/440] Fix test; not guaranteed to return django --- test/integration/install_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index 602631e592..c47d2c2d84 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -42,7 +42,7 @@ func (suite *InstallIntegrationTestSuite) TestInstallSuggest() { cp = ts.Spawn("install", "djang") cp.Expect("No results found", e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Did you mean") - cp.Expect("language/python/django") + cp.Expect("language/python/djang") cp.ExpectExitCode(1) } From ed949e5a3ebcc0356a9fad975f7a302868cbd7be Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 19 Sep 2024 09:39:01 -0700 Subject: [PATCH 140/440] Satisfy newline checker which is apparently bugged cause I didn't touch this file --- internal/assets/contents/shells/zshrc_global.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/assets/contents/shells/zshrc_global.sh b/internal/assets/contents/shells/zshrc_global.sh index 9f19cbfbe8..c2cee91028 100644 --- a/internal/assets/contents/shells/zshrc_global.sh +++ b/internal/assets/contents/shells/zshrc_global.sh @@ -8,4 +8,4 @@ export {{$K}}="{{$V}}:$PATH" {{- else}} export {{$K}}="{{$V}}" {{- end}} -{{- end}} \ No newline at end of file +{{- end}} From 4b0cca855aae984421c97e5fe64713fe34b204d7 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 19 Sep 2024 10:54:53 -0700 Subject: [PATCH 141/440] Fix unit test using removed function --- pkg/buildscript/mutations_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/buildscript/mutations_test.go b/pkg/buildscript/mutations_test.go index b0dc20479b..f3cff0996f 100644 --- a/pkg/buildscript/mutations_test.go +++ b/pkg/buildscript/mutations_test.go @@ -347,7 +347,11 @@ func TestUpdatePlatform(t *testing.T) { script, err := UnmarshalBuildExpression(data, nil) assert.NoError(t, err) - err = script.UpdatePlatform(tt.args.operation, tt.args.platform) + if tt.args.operation == types.OperationAdded { + err = script.AddPlatform(tt.args.platform) + } else { + err = script.RemovePlatform(tt.args.platform) + } if err != nil { if tt.wantErr { return From 2ae56e031c5f9a4cfdda781417e1de4598dbdfe1 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 19 Sep 2024 10:55:15 -0700 Subject: [PATCH 142/440] Give more time for double solve --- test/integration/package_int_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index e50de9db0a..9660ab1891 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -3,11 +3,13 @@ package integration import ( "strings" "testing" + "time" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" + "github.com/ActiveState/termtest" ) type PackageIntegrationTestSuite struct { @@ -273,7 +275,7 @@ func (suite *PackageIntegrationTestSuite) TestPackage_operation_multiple() { suite.Run("install", func() { cp := ts.Spawn("install", "requests", "urllib3@1.25.6") cp.Expect("Operating on project ActiveState-CLI/small-python") - cp.Expect("Added: language/python/requests", e2e.RuntimeSolvingTimeoutOpt) + cp.Expect("Added: language/python/requests", termtest.OptExpectTimeout(2*time.Minute)) // Extra time because 2 packages cp.Expect("Added: language/python/urllib3") cp.Wait() }) From 4729ae53add01cae8af0cc9f11b3226715e3cd75 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:47:14 -0700 Subject: [PATCH 143/440] Update gqlgen - old version has checksum mismatch --- activestate.generators.yaml | 3 +- go.mod | 8 ++--- go.sum | 10 ++++--- .../99designs/gqlgen/complexity/complexity.go | 8 ++--- .../99designs/gqlgen/graphql/any.go | 4 +-- .../99designs/gqlgen/graphql/bool.go | 13 ++++---- .../99designs/gqlgen/graphql/cache.go | 20 +++++++------ .../99designs/gqlgen/graphql/coercion.go | 28 ++++++++--------- .../99designs/gqlgen/graphql/context_field.go | 6 ++-- .../gqlgen/graphql/context_operation.go | 8 ++--- .../gqlgen/graphql/context_response.go | 16 +++++----- .../99designs/gqlgen/graphql/duration.go | 6 ++-- .../99designs/gqlgen/graphql/errcode/codes.go | 2 +- .../gqlgen/graphql/executable_schema.go | 10 +++---- .../gqlgen/graphql/executable_schema_mock.go | 14 ++++----- .../gqlgen/graphql/executor/executor.go | 30 ++++++++++++------- .../gqlgen/graphql/executor/extensions.go | 19 ++++++------ .../99designs/gqlgen/graphql/float.go | 13 ++++---- .../99designs/gqlgen/graphql/handler.go | 26 ++++++++-------- .../gqlgen/graphql/handler/extension/apq.go | 10 +++---- .../graphql/handler/extension/complexity.go | 4 +-- .../gqlgen/graphql/handler/lru/lru.go | 16 +++++----- .../gqlgen/graphql/handler/server.go | 28 ++++++++++------- .../gqlgen/graphql/handler/transport/error.go | 4 +-- .../handler/transport/http_form_urlencoded.go | 8 ++--- .../graphql/handler/transport/http_get.go | 2 +- .../graphql/handler/transport/http_graphql.go | 9 +++--- .../graphql/handler/transport/http_post.go | 8 ++--- .../gqlgen/graphql/handler/transport/util.go | 2 +- .../graphql/handler/transport/websocket.go | 17 +++++------ .../handler/transport/websocket_init.go | 2 +- .../github.com/99designs/gqlgen/graphql/id.go | 14 ++++----- .../99designs/gqlgen/graphql/input.go | 4 +-- .../99designs/gqlgen/graphql/int.go | 12 ++++++-- .../99designs/gqlgen/graphql/jsonw.go | 4 +-- .../graphql/playground/altair_playground.go | 2 +- .../playground/apollo_sandbox_playground.go | 2 +- .../gqlgen/graphql/playground/playground.go | 6 ++-- .../99designs/gqlgen/graphql/recovery.go | 4 +-- .../99designs/gqlgen/graphql/response.go | 14 ++++----- .../99designs/gqlgen/graphql/stats.go | 8 ++--- .../99designs/gqlgen/graphql/string.go | 10 ++----- .../99designs/gqlgen/graphql/time.go | 2 +- .../99designs/gqlgen/graphql/uint.go | 12 ++++++-- .../99designs/gqlgen/graphql/upload.go | 2 +- .../99designs/gqlgen/graphql/version.go | 2 +- vendor/modules.txt | 4 +-- 47 files changed, 241 insertions(+), 215 deletions(-) diff --git a/activestate.generators.yaml b/activestate.generators.yaml index 510aa47eb2..e84712a169 100644 --- a/activestate.generators.yaml +++ b/activestate.generators.yaml @@ -84,7 +84,8 @@ scripts: language: bash description: Generates graph server and client files value: | - go install github.com/99designs/gqlgen@v0.17.46 + set -e + go install github.com/99designs/gqlgen@v0.17.48 cd ./cmd/state-svc && gqlgen --verbose - name: generate-test-update language: bash diff --git a/go.mod b/go.mod index d8efcc9208..b9f3db72ba 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/ActiveState/cli -go 1.22.0 +go 1.22.5 -toolchain go1.22.4 +toolchain go1.23.1 require ( - github.com/99designs/gqlgen v0.17.46 + github.com/99designs/gqlgen v0.17.54 github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527 github.com/ActiveState/termtest v0.7.3-0.20240703202616-34f7899287a4 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 @@ -70,6 +70,7 @@ require ( require ( github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 + github.com/cespare/xxhash v1.1.0 github.com/charmbracelet/bubbles v0.18.0 github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/lipgloss v0.9.1 @@ -86,7 +87,6 @@ require ( github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/cespare/xxhash v1.1.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect diff --git a/go.sum b/go.sum index 672d9e8bf3..f154260889 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/99designs/gqlgen v0.17.46 h1:Dk/pSCMVp57z/vd6gwZ/wmqbPOL3i5iz4YQHTDfxyuw= -github.com/99designs/gqlgen v0.17.46/go.mod h1:qRtiAeVPgkBBSPzZtoZXRRl5WkNrUTpp1OeVt61TmGU= +github.com/99designs/gqlgen v0.17.54 h1:AsF49k/7RJlwA00RQYsYN0T8cQuaosnV/7G1dHC3Uh8= +github.com/99designs/gqlgen v0.17.54/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc= github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527 h1:lW+qgVXf/iAnSs8SgagWxh8d6nsbpmwyhmeg9/fp0Os= github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527/go.mod h1:/9SyzKLlJSuIa7WAsLUUhHqTK9+PtZD8cKF8G4SLpa0= github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 h1:UCx/ObpVRgC4fp2OlJM2iNdMMu+K87/aPxKrQ1WRLj4= @@ -36,11 +36,12 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20201125194554-85d881c3777e h1:YYUPbL3iB9+Y/JYEXjCi9AolqiKIIJX/2aRR9TuKD6w= github.com/Netflix/go-expect v0.0.0-20201125194554-85d881c3777e/go.mod h1:68ORG0HSEWDuH5Eh73AFbYWZ1zT4Y+b0vhOa+vZRUdI= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= -github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= +github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0= +github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -621,6 +622,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= diff --git a/vendor/github.com/99designs/gqlgen/complexity/complexity.go b/vendor/github.com/99designs/gqlgen/complexity/complexity.go index aa0f86432e..288bb539b5 100644 --- a/vendor/github.com/99designs/gqlgen/complexity/complexity.go +++ b/vendor/github.com/99designs/gqlgen/complexity/complexity.go @@ -6,7 +6,7 @@ import ( "github.com/99designs/gqlgen/graphql" ) -func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]interface{}) int { +func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]any) int { walker := complexityWalker{ es: es, schema: es.Schema(), @@ -18,7 +18,7 @@ func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars ma type complexityWalker struct { es graphql.ExecutableSchema schema *ast.Schema - vars map[string]interface{} + vars map[string]any } func (cw complexityWalker) selectionSetComplexity(selectionSet ast.SelectionSet) int { @@ -57,7 +57,7 @@ func (cw complexityWalker) selectionSetComplexity(selectionSet ast.SelectionSet) return complexity } -func (cw complexityWalker) interfaceFieldComplexity(def *ast.Definition, field string, childComplexity int, args map[string]interface{}) int { +func (cw complexityWalker) interfaceFieldComplexity(def *ast.Definition, field string, childComplexity int, args map[string]any) int { // Interfaces don't have their own separate field costs, so they have to assume the worst case. // We iterate over all implementors and choose the most expensive one. maxComplexity := 0 @@ -71,7 +71,7 @@ func (cw complexityWalker) interfaceFieldComplexity(def *ast.Definition, field s return maxComplexity } -func (cw complexityWalker) fieldComplexity(object, field string, childComplexity int, args map[string]interface{}) int { +func (cw complexityWalker) fieldComplexity(object, field string, childComplexity int, args map[string]any) int { if customComplexity, ok := cw.es.Complexity(object, field, childComplexity, args); ok && customComplexity >= childComplexity { return customComplexity } diff --git a/vendor/github.com/99designs/gqlgen/graphql/any.go b/vendor/github.com/99designs/gqlgen/graphql/any.go index 6ea8bf2eae..be600b2f42 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/any.go +++ b/vendor/github.com/99designs/gqlgen/graphql/any.go @@ -5,7 +5,7 @@ import ( "io" ) -func MarshalAny(v interface{}) Marshaler { +func MarshalAny(v any) Marshaler { return WriterFunc(func(w io.Writer) { err := json.NewEncoder(w).Encode(v) if err != nil { @@ -14,6 +14,6 @@ func MarshalAny(v interface{}) Marshaler { }) } -func UnmarshalAny(v interface{}) (interface{}, error) { +func UnmarshalAny(v any) (any, error) { return v, nil } diff --git a/vendor/github.com/99designs/gqlgen/graphql/bool.go b/vendor/github.com/99designs/gqlgen/graphql/bool.go index f435e0c098..d9797a38e9 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/bool.go +++ b/vendor/github.com/99designs/gqlgen/graphql/bool.go @@ -3,24 +3,25 @@ package graphql import ( "fmt" "io" + "strconv" "strings" ) func MarshalBoolean(b bool) Marshaler { - if b { - return WriterFunc(func(w io.Writer) { w.Write(trueLit) }) - } - return WriterFunc(func(w io.Writer) { w.Write(falseLit) }) + str := strconv.FormatBool(b) + return WriterFunc(func(w io.Writer) { w.Write([]byte(str)) }) } -func UnmarshalBoolean(v interface{}) (bool, error) { +func UnmarshalBoolean(v any) (bool, error) { switch v := v.(type) { case string: - return strings.ToLower(v) == "true", nil + return strings.EqualFold(v, "true"), nil case int: return v != 0, nil case bool: return v, nil + case nil: + return false, nil default: return false, fmt.Errorf("%T is not a bool", v) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/cache.go b/vendor/github.com/99designs/gqlgen/graphql/cache.go index e552ce6722..8804cfe04f 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/cache.go +++ b/vendor/github.com/99designs/gqlgen/graphql/cache.go @@ -3,27 +3,29 @@ package graphql import "context" // Cache is a shared store for APQ and query AST caching -type Cache interface { +type Cache[T any] interface { // Get looks up a key's value from the cache. - Get(ctx context.Context, key string) (value interface{}, ok bool) + Get(ctx context.Context, key string) (value T, ok bool) // Add adds a value to the cache. - Add(ctx context.Context, key string, value interface{}) + Add(ctx context.Context, key string, value T) } // MapCache is the simplest implementation of a cache, because it can not evict it should only be used in tests -type MapCache map[string]interface{} +type MapCache[T any] map[string]T // Get looks up a key's value from the cache. -func (m MapCache) Get(_ context.Context, key string) (value interface{}, ok bool) { +func (m MapCache[T]) Get(_ context.Context, key string) (value T, ok bool) { v, ok := m[key] return v, ok } // Add adds a value to the cache. -func (m MapCache) Add(_ context.Context, key string, value interface{}) { m[key] = value } +func (m MapCache[T]) Add(_ context.Context, key string, value T) { m[key] = value } -type NoCache struct{} +type NoCache[T any, T2 *T] struct{} -func (n NoCache) Get(_ context.Context, _ string) (value interface{}, ok bool) { return nil, false } -func (n NoCache) Add(_ context.Context, _ string, _ interface{}) {} +var _ Cache[*string] = (*NoCache[string, *string])(nil) + +func (n NoCache[T, T2]) Get(_ context.Context, _ string) (value T2, ok bool) { return nil, false } +func (n NoCache[T, T2]) Add(_ context.Context, _ string, _ T2) {} diff --git a/vendor/github.com/99designs/gqlgen/graphql/coercion.go b/vendor/github.com/99designs/gqlgen/graphql/coercion.go index d3d3c18b2b..533ab82149 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/coercion.go +++ b/vendor/github.com/99designs/gqlgen/graphql/coercion.go @@ -5,51 +5,51 @@ import ( ) // CoerceList applies coercion from a single value to a list. -func CoerceList(v interface{}) []interface{} { - var vSlice []interface{} +func CoerceList(v any) []any { + var vSlice []any if v != nil { switch v := v.(type) { - case []interface{}: + case []any: // already a slice no coercion required vSlice = v case []string: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []json.Number: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []bool: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } - case []map[string]interface{}: + case []map[string]any: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []float64: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []float32: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []int: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []int32: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } case []int64: if len(v) > 0 { - vSlice = []interface{}{v[0]} + vSlice = []any{v[0]} } default: - vSlice = []interface{}{v} + vSlice = []any{v} } } return vSlice diff --git a/vendor/github.com/99designs/gqlgen/graphql/context_field.go b/vendor/github.com/99designs/gqlgen/graphql/context_field.go index 1f9a6e88db..b3fab91017 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context_field.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context_field.go @@ -19,13 +19,13 @@ type FieldContext struct { // The name of the type this field belongs to Object string // These are the args after processing, they can be mutated in middleware to change what the resolver will get. - Args map[string]interface{} + Args map[string]any // The raw field Field CollectedField // The index of array in path. Index *int // The result object of resolver - Result interface{} + Result any // IsMethod indicates if the resolver is a method IsMethod bool // IsResolver indicates if the field has a user-specified resolver @@ -98,7 +98,7 @@ func WithFieldContext(ctx context.Context, rc *FieldContext) context.Context { return context.WithValue(ctx, resolverCtx, rc) } -func equalPath(a ast.Path, b ast.Path) bool { +func equalPath(a, b ast.Path) bool { if len(a) != len(b) { return false } diff --git a/vendor/github.com/99designs/gqlgen/graphql/context_operation.go b/vendor/github.com/99designs/gqlgen/graphql/context_operation.go index 3e6a221b0b..d515acce18 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context_operation.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context_operation.go @@ -14,7 +14,7 @@ type RequestContext = OperationContext type OperationContext struct { RawQuery string - Variables map[string]interface{} + Variables map[string]any OperationName string Doc *ast.QueryDocument Headers http.Header @@ -36,7 +36,7 @@ func (c *OperationContext) Validate(ctx context.Context) error { return errors.New("field 'RawQuery' is required") } if c.Variables == nil { - c.Variables = make(map[string]interface{}) + c.Variables = make(map[string]any) } if c.ResolverMiddleware == nil { return errors.New("field 'ResolverMiddleware' is required") @@ -103,7 +103,7 @@ Next: // Errorf sends an error string to the client, passing it through the formatter. // Deprecated: use graphql.AddErrorf(ctx, err) instead -func (c *OperationContext) Errorf(ctx context.Context, format string, args ...interface{}) { +func (c *OperationContext) Errorf(ctx context.Context, format string, args ...any) { AddErrorf(ctx, format, args...) } @@ -120,6 +120,6 @@ func (c *OperationContext) Error(ctx context.Context, err error) { AddError(ctx, err) } -func (c *OperationContext) Recover(ctx context.Context, err interface{}) error { +func (c *OperationContext) Recover(ctx context.Context, err any) error { return ErrorOnPath(ctx, c.RecoverFunc(ctx, err)) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/context_response.go b/vendor/github.com/99designs/gqlgen/graphql/context_response.go index 6d223c8a94..e0f3285fb0 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context_response.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context_response.go @@ -15,7 +15,7 @@ type responseContext struct { errors gqlerror.List errorsMu sync.Mutex - extensions map[string]interface{} + extensions map[string]any extensionsMu sync.Mutex } @@ -45,7 +45,7 @@ func WithFreshResponseContext(ctx context.Context) context.Context { } // AddErrorf writes a formatted error to the client, first passing it through the error presenter. -func AddErrorf(ctx context.Context, format string, args ...interface{}) { +func AddErrorf(ctx context.Context, format string, args ...any) { AddError(ctx, fmt.Errorf(format, args...)) } @@ -60,7 +60,7 @@ func AddError(ctx context.Context, err error) { c.errors = append(c.errors, presentedError) } -func Recover(ctx context.Context, err interface{}) (userMessage error) { +func Recover(ctx context.Context, err any) (userMessage error) { c := getResponseContext(ctx) return ErrorOnPath(ctx, c.recover(ctx, err)) } @@ -125,13 +125,13 @@ func GetErrors(ctx context.Context) gqlerror.List { } // RegisterExtension allows you to add a new extension into the graphql response -func RegisterExtension(ctx context.Context, key string, value interface{}) { +func RegisterExtension(ctx context.Context, key string, value any) { c := getResponseContext(ctx) c.extensionsMu.Lock() defer c.extensionsMu.Unlock() if c.extensions == nil { - c.extensions = make(map[string]interface{}) + c.extensions = make(map[string]any) } if _, ok := c.extensions[key]; ok { @@ -142,16 +142,16 @@ func RegisterExtension(ctx context.Context, key string, value interface{}) { } // GetExtensions returns any extensions registered in the current result context -func GetExtensions(ctx context.Context) map[string]interface{} { +func GetExtensions(ctx context.Context) map[string]any { ext := getResponseContext(ctx).extensions if ext == nil { - return map[string]interface{}{} + return map[string]any{} } return ext } -func GetExtension(ctx context.Context, name string) interface{} { +func GetExtension(ctx context.Context, name string) any { ext := getResponseContext(ctx).extensions if ext == nil { return nil diff --git a/vendor/github.com/99designs/gqlgen/graphql/duration.go b/vendor/github.com/99designs/gqlgen/graphql/duration.go index 3eb392db87..bf2b564776 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/duration.go +++ b/vendor/github.com/99designs/gqlgen/graphql/duration.go @@ -1,17 +1,17 @@ package graphql import ( - "fmt" + "errors" "time" dur "github.com/sosodev/duration" ) // UnmarshalDuration returns the duration from a string in ISO8601 format -func UnmarshalDuration(v interface{}) (time.Duration, error) { +func UnmarshalDuration(v any) (time.Duration, error) { input, ok := v.(string) if !ok { - return 0, fmt.Errorf("input must be a string") + return 0, errors.New("input must be a string") } d2, err := dur.Parse(input) diff --git a/vendor/github.com/99designs/gqlgen/graphql/errcode/codes.go b/vendor/github.com/99designs/gqlgen/graphql/errcode/codes.go index 854b206f4e..58ca7cbee7 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/errcode/codes.go +++ b/vendor/github.com/99designs/gqlgen/graphql/errcode/codes.go @@ -40,7 +40,7 @@ func Set(err error, value string) { } if gqlErr.Extensions == nil { - gqlErr.Extensions = map[string]interface{}{} + gqlErr.Extensions = map[string]any{} } gqlErr.Extensions["code"] = value diff --git a/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go b/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go index 58f942a104..aa9d7c44c3 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executable_schema.go @@ -12,7 +12,7 @@ import ( type ExecutableSchema interface { Schema() *ast.Schema - Complexity(typeName, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) + Complexity(typeName, fieldName string, childComplexity int, args map[string]any) (int, bool) Exec(ctx context.Context) ResponseHandler } @@ -116,7 +116,7 @@ func instanceOf(val string, satisfies []string) bool { return false } -func getOrCreateAndAppendField(c *[]CollectedField, name string, alias string, objectDefinition *ast.Definition, creator func() CollectedField) *CollectedField { +func getOrCreateAndAppendField(c *[]CollectedField, name, alias string, objectDefinition *ast.Definition, creator func() CollectedField) *CollectedField { for i, cf := range *c { if cf.Name == name && cf.Alias == alias { if cf.ObjectDefinition == objectDefinition { @@ -150,7 +150,7 @@ func getOrCreateAndAppendField(c *[]CollectedField, name string, alias string, o return &(*c)[len(*c)-1] } -func shouldIncludeNode(directives ast.DirectiveList, variables map[string]interface{}) bool { +func shouldIncludeNode(directives ast.DirectiveList, variables map[string]any) bool { if len(directives) == 0 { return true } @@ -168,7 +168,7 @@ func shouldIncludeNode(directives ast.DirectiveList, variables map[string]interf return !skip && include } -func deferrable(directives ast.DirectiveList, variables map[string]interface{}) (shouldDefer bool, label string) { +func deferrable(directives ast.DirectiveList, variables map[string]any) (shouldDefer bool, label string) { d := directives.ForName("defer") if d == nil { return false, "" @@ -194,7 +194,7 @@ func deferrable(directives ast.DirectiveList, variables map[string]interface{}) return shouldDefer, label } -func resolveIfArgument(d *ast.Directive, variables map[string]interface{}) bool { +func resolveIfArgument(d *ast.Directive, variables map[string]any) bool { arg := d.Arguments.ForName("if") if arg == nil { panic(fmt.Sprintf("%s: argument 'if' not defined", d.Name)) diff --git a/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go b/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go index 5e71cb8304..c4c4118975 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executable_schema_mock.go @@ -19,7 +19,7 @@ var _ ExecutableSchema = &ExecutableSchemaMock{} // // // make and configure a mocked ExecutableSchema // mockedExecutableSchema := &ExecutableSchemaMock{ -// ComplexityFunc: func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) { +// ComplexityFunc: func(typeName string, fieldName string, childComplexity int, args map[string]any) (int, bool) { // panic("mock out the Complexity method") // }, // ExecFunc: func(ctx context.Context) ResponseHandler { @@ -36,7 +36,7 @@ var _ ExecutableSchema = &ExecutableSchemaMock{} // } type ExecutableSchemaMock struct { // ComplexityFunc mocks the Complexity method. - ComplexityFunc func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) + ComplexityFunc func(typeName string, fieldName string, childComplexity int, args map[string]any) (int, bool) // ExecFunc mocks the Exec method. ExecFunc func(ctx context.Context) ResponseHandler @@ -55,7 +55,7 @@ type ExecutableSchemaMock struct { // ChildComplexity is the childComplexity argument value. ChildComplexity int // Args is the args argument value. - Args map[string]interface{} + Args map[string]any } // Exec holds details about calls to the Exec method. Exec []struct { @@ -72,7 +72,7 @@ type ExecutableSchemaMock struct { } // Complexity calls ComplexityFunc. -func (mock *ExecutableSchemaMock) Complexity(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) { +func (mock *ExecutableSchemaMock) Complexity(typeName string, fieldName string, childComplexity int, args map[string]any) (int, bool) { if mock.ComplexityFunc == nil { panic("ExecutableSchemaMock.ComplexityFunc: method is nil but ExecutableSchema.Complexity was just called") } @@ -80,7 +80,7 @@ func (mock *ExecutableSchemaMock) Complexity(typeName string, fieldName string, TypeName string FieldName string ChildComplexity int - Args map[string]interface{} + Args map[string]any }{ TypeName: typeName, FieldName: fieldName, @@ -101,13 +101,13 @@ func (mock *ExecutableSchemaMock) ComplexityCalls() []struct { TypeName string FieldName string ChildComplexity int - Args map[string]interface{} + Args map[string]any } { var calls []struct { TypeName string FieldName string ChildComplexity int - Args map[string]interface{} + Args map[string]any } mock.lockComplexity.RLock() calls = mock.calls.Complexity diff --git a/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go b/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go index ef0603eaa0..566b04763e 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executor/executor.go @@ -12,6 +12,8 @@ import ( "github.com/99designs/gqlgen/graphql/errcode" ) +const parserTokenNoLimit = 0 + // Executor executes graphql queries against a schema. type Executor struct { es graphql.ExecutableSchema @@ -20,7 +22,9 @@ type Executor struct { errorPresenter graphql.ErrorPresenterFunc recoverFunc graphql.RecoverFunc - queryCache graphql.Cache + queryCache graphql.Cache[*ast.QueryDocument] + + parserTokenLimit int } var _ graphql.GraphExecutor = &Executor{} @@ -29,11 +33,12 @@ var _ graphql.GraphExecutor = &Executor{} // recovery callbacks, and no query cache or extensions. func New(es graphql.ExecutableSchema) *Executor { e := &Executor{ - es: es, - errorPresenter: graphql.DefaultErrorPresenter, - recoverFunc: graphql.DefaultRecover, - queryCache: graphql.NoCache{}, - ext: processExtensions(nil), + es: es, + errorPresenter: graphql.DefaultErrorPresenter, + recoverFunc: graphql.DefaultRecover, + queryCache: graphql.NoCache[ast.QueryDocument, *ast.QueryDocument]{}, + ext: processExtensions(nil), + parserTokenLimit: parserTokenNoLimit, } return e } @@ -79,7 +84,6 @@ func (e *Executor) CreateOperationContext( var err error rc.Variables, err = validator.VariableValues(e.es.Schema(), rc.Operation, params.Variables) - if err != nil { gqlErr, ok := err.(*gqlerror.Error) if ok { @@ -153,11 +157,11 @@ func (e *Executor) DispatchError(ctx context.Context, list gqlerror.List) *graph return resp } -func (e *Executor) PresentRecoveredError(ctx context.Context, err interface{}) error { +func (e *Executor) PresentRecoveredError(ctx context.Context, err any) error { return e.errorPresenter(ctx, e.recoverFunc(ctx, err)) } -func (e *Executor) SetQueryCache(cache graphql.Cache) { +func (e *Executor) SetQueryCache(cache graphql.Cache[*ast.QueryDocument]) { e.queryCache = cache } @@ -169,6 +173,10 @@ func (e *Executor) SetRecoverFunc(f graphql.RecoverFunc) { e.recoverFunc = f } +func (e *Executor) SetParserTokenLimit(limit int) { + e.parserTokenLimit = limit +} + // parseQuery decodes the incoming query and validates it, pulling from cache if present. // // NOTE: This should NOT look at variables, they will change per request. It should only parse and @@ -186,10 +194,10 @@ func (e *Executor) parseQuery( stats.Parsing.End = now stats.Validation.Start = now - return doc.(*ast.QueryDocument), nil + return doc, nil } - doc, err := parser.ParseQuery(&ast.Source{Input: query}) + doc, err := parser.ParseQueryWithTokenLimit(&ast.Source{Input: query}, e.parserTokenLimit) if err != nil { gqlErr, ok := err.(*gqlerror.Error) if ok { diff --git a/vendor/github.com/99designs/gqlgen/graphql/executor/extensions.go b/vendor/github.com/99designs/gqlgen/graphql/executor/extensions.go index a8eebf110c..758d8e4ec8 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/executor/extensions.go +++ b/vendor/github.com/99designs/gqlgen/graphql/executor/extensions.go @@ -2,6 +2,7 @@ package executor import ( "context" + "errors" "fmt" "github.com/99designs/gqlgen/graphql" @@ -68,7 +69,7 @@ func processExtensions(exts []graphql.HandlerExtension) extensions { rootFieldMiddleware: func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler { return next(ctx) }, - fieldMiddleware: func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { + fieldMiddleware: func(ctx context.Context, next graphql.Resolver) (res any, err error) { return next(ctx) }, } @@ -105,8 +106,8 @@ func processExtensions(exts []graphql.HandlerExtension) extensions { if p, ok := p.(graphql.FieldInterceptor); ok { previous := e.fieldMiddleware - e.fieldMiddleware = func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { - return p.InterceptField(ctx, func(ctx context.Context) (res interface{}, err error) { + e.fieldMiddleware = func(ctx context.Context, next graphql.Resolver) (res any, err error) { + return p.InterceptField(ctx, func(ctx context.Context) (res any, err error) { return previous(ctx, next) }) } @@ -134,7 +135,7 @@ func (r aroundOpFunc) ExtensionName() string { func (r aroundOpFunc) Validate(schema graphql.ExecutableSchema) error { if r == nil { - return fmt.Errorf("OperationFunc can not be nil") + return errors.New("OperationFunc can not be nil") } return nil } @@ -151,7 +152,7 @@ func (r aroundRespFunc) ExtensionName() string { func (r aroundRespFunc) Validate(schema graphql.ExecutableSchema) error { if r == nil { - return fmt.Errorf("ResponseFunc can not be nil") + return errors.New("ResponseFunc can not be nil") } return nil } @@ -160,7 +161,7 @@ func (r aroundRespFunc) InterceptResponse(ctx context.Context, next graphql.Resp return r(ctx, next) } -type aroundFieldFunc func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) +type aroundFieldFunc func(ctx context.Context, next graphql.Resolver) (res any, err error) func (f aroundFieldFunc) ExtensionName() string { return "InlineFieldFunc" @@ -168,12 +169,12 @@ func (f aroundFieldFunc) ExtensionName() string { func (f aroundFieldFunc) Validate(schema graphql.ExecutableSchema) error { if f == nil { - return fmt.Errorf("FieldFunc can not be nil") + return errors.New("FieldFunc can not be nil") } return nil } -func (f aroundFieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { +func (f aroundFieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res any, err error) { return f(ctx, next) } @@ -185,7 +186,7 @@ func (f aroundRootFieldFunc) ExtensionName() string { func (f aroundRootFieldFunc) Validate(schema graphql.ExecutableSchema) error { if f == nil { - return fmt.Errorf("RootFieldFunc can not be nil") + return errors.New("RootFieldFunc can not be nil") } return nil } diff --git a/vendor/github.com/99designs/gqlgen/graphql/float.go b/vendor/github.com/99designs/gqlgen/graphql/float.go index ccb825ddb8..b140d5bc74 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/float.go +++ b/vendor/github.com/99designs/gqlgen/graphql/float.go @@ -3,6 +3,7 @@ package graphql import ( "context" "encoding/json" + "errors" "fmt" "io" "math" @@ -11,11 +12,11 @@ import ( func MarshalFloat(f float64) Marshaler { return WriterFunc(func(w io.Writer) { - io.WriteString(w, fmt.Sprintf("%g", f)) + fmt.Fprintf(w, "%g", f) }) } -func UnmarshalFloat(v interface{}) (float64, error) { +func UnmarshalFloat(v any) (float64, error) { switch v := v.(type) { case string: return strconv.ParseFloat(v, 64) @@ -27,6 +28,8 @@ func UnmarshalFloat(v interface{}) (float64, error) { return v, nil case json.Number: return strconv.ParseFloat(string(v), 64) + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an float", v) } @@ -35,13 +38,13 @@ func UnmarshalFloat(v interface{}) (float64, error) { func MarshalFloatContext(f float64) ContextMarshaler { return ContextWriterFunc(func(ctx context.Context, w io.Writer) error { if math.IsInf(f, 0) || math.IsNaN(f) { - return fmt.Errorf("cannot marshal infinite no NaN float values") + return errors.New("cannot marshal infinite no NaN float values") } - io.WriteString(w, fmt.Sprintf("%g", f)) + fmt.Fprintf(w, "%g", f) return nil }) } -func UnmarshalFloatContext(ctx context.Context, v interface{}) (float64, error) { +func UnmarshalFloatContext(ctx context.Context, v any) (float64, error) { return UnmarshalFloat(v) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler.go b/vendor/github.com/99designs/gqlgen/graphql/handler.go index cd358740c8..4df36117b8 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler.go @@ -16,18 +16,18 @@ type ( ResponseHandler func(ctx context.Context) *Response ResponseMiddleware func(ctx context.Context, next ResponseHandler) *Response - Resolver func(ctx context.Context) (res interface{}, err error) - FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error) + Resolver func(ctx context.Context) (res any, err error) + FieldMiddleware func(ctx context.Context, next Resolver) (res any, err error) RootResolver func(ctx context.Context) Marshaler RootFieldMiddleware func(ctx context.Context, next RootResolver) Marshaler RawParams struct { - Query string `json:"query"` - OperationName string `json:"operationName"` - Variables map[string]interface{} `json:"variables"` - Extensions map[string]interface{} `json:"extensions"` - Headers http.Header `json:"headers"` + Query string `json:"query"` + OperationName string `json:"operationName"` + Variables map[string]any `json:"variables"` + Extensions map[string]any `json:"extensions"` + Headers http.Header `json:"headers"` ReadTime TraceTiming `json:"-"` } @@ -86,7 +86,7 @@ type ( // FieldInterceptor called around each field FieldInterceptor interface { - InterceptField(ctx context.Context, next Resolver) (res interface{}, err error) + InterceptField(ctx context.Context, next Resolver) (res any, err error) } // Transport provides support for different wire level encodings of graphql requests, eg Form, Get, Post, Websocket @@ -103,7 +103,7 @@ func (p *RawParams) AddUpload(upload Upload, key, path string) *gqlerror.Error { return gqlerror.Errorf("invalid operations paths for key %s", key) } - var ptr interface{} = p.Variables + var ptr any = p.Variables parts := strings.Split(path, ".") // skip the first part (variables) because we started there @@ -114,15 +114,15 @@ func (p *RawParams) AddUpload(upload Upload, key, path string) *gqlerror.Error { } if index, parseNbrErr := strconv.Atoi(p); parseNbrErr == nil { if last { - ptr.([]interface{})[index] = upload + ptr.([]any)[index] = upload } else { - ptr = ptr.([]interface{})[index] + ptr = ptr.([]any)[index] } } else { if last { - ptr.(map[string]interface{})[p] = upload + ptr.(map[string]any)[p] = upload } else { - ptr = ptr.(map[string]interface{})[p] + ptr = ptr.(map[string]any)[p] } } } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go index 465c2ada61..a4cb32c955 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/apq.go @@ -4,7 +4,7 @@ import ( "context" "crypto/sha256" "encoding/hex" - "fmt" + "errors" "github.com/mitchellh/mapstructure" "github.com/vektah/gqlparser/v2/gqlerror" @@ -23,7 +23,7 @@ const ( // hash in the next request. // see https://github.com/apollographql/apollo-link-persisted-queries type AutomaticPersistedQuery struct { - Cache graphql.Cache + Cache graphql.Cache[string] } type ApqStats struct { @@ -47,7 +47,7 @@ func (a AutomaticPersistedQuery) ExtensionName() string { func (a AutomaticPersistedQuery) Validate(schema graphql.ExecutableSchema) error { if a.Cache == nil { - return fmt.Errorf("AutomaticPersistedQuery.Cache can not be nil") + return errors.New("AutomaticPersistedQuery.Cache can not be nil") } return nil } @@ -72,14 +72,14 @@ func (a AutomaticPersistedQuery) MutateOperationParameters(ctx context.Context, fullQuery := false if rawParams.Query == "" { + var ok bool // client sent optimistic query hash without query string, get it from the cache - query, ok := a.Cache.Get(ctx, extension.Sha256) + rawParams.Query, ok = a.Cache.Get(ctx, extension.Sha256) if !ok { err := gqlerror.Errorf(errPersistedQueryNotFound) errcode.Set(err, errPersistedQueryNotFoundCode) return err } - rawParams.Query = query.(string) } else { // client sent optimistic query hash with query string, verify and store it if computeQueryHash(rawParams.Query) != extension.Sha256 { diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go index a5b6a60409..af1e002fc1 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/extension/complexity.go @@ -2,7 +2,7 @@ package extension import ( "context" - "fmt" + "errors" "github.com/vektah/gqlparser/v2/gqlerror" @@ -52,7 +52,7 @@ func (c ComplexityLimit) ExtensionName() string { func (c *ComplexityLimit) Validate(schema graphql.ExecutableSchema) error { if c.Func == nil { - return fmt.Errorf("ComplexityLimit func can not be nil") + return errors.New("ComplexityLimit func can not be nil") } c.es = schema return nil diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go b/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go index 6ae8a38e64..946022bfeb 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/lru/lru.go @@ -8,26 +8,26 @@ import ( "github.com/99designs/gqlgen/graphql" ) -type LRU struct { - lru *lru.Cache[string, any] +type LRU[T any] struct { + lru *lru.Cache[string, T] } -var _ graphql.Cache = &LRU{} +var _ graphql.Cache[any] = &LRU[any]{} -func New(size int) *LRU { - cache, err := lru.New[string, any](size) +func New[T any](size int) *LRU[T] { + cache, err := lru.New[string, T](size) if err != nil { // An error is only returned for non-positive cache size // and we already checked for that. panic("unexpected error creating cache: " + err.Error()) } - return &LRU{cache} + return &LRU[T]{cache} } -func (l LRU) Get(ctx context.Context, key string) (value interface{}, ok bool) { +func (l LRU[T]) Get(ctx context.Context, key string) (value T, ok bool) { return l.lru.Get(key) } -func (l LRU) Add(ctx context.Context, key string, value interface{}) { +func (l LRU[T]) Add(ctx context.Context, key string, value T) { l.lru.Add(key, value) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/server.go b/vendor/github.com/99designs/gqlgen/graphql/handler/server.go index fd365ccbbf..644bad8d99 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/server.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/server.go @@ -3,10 +3,12 @@ package handler import ( "context" "encoding/json" + "errors" "fmt" "net/http" "time" + "github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/gqlerror" "github.com/99designs/gqlgen/graphql" @@ -40,11 +42,11 @@ func NewDefaultServer(es graphql.ExecutableSchema) *Server { srv.AddTransport(transport.POST{}) srv.AddTransport(transport.MultipartForm{}) - srv.SetQueryCache(lru.New(1000)) + srv.SetQueryCache(lru.New[*ast.QueryDocument](1000)) srv.Use(extension.Introspection{}) srv.Use(extension.AutomaticPersistedQuery{ - Cache: lru.New(100), + Cache: lru.New[string](100), }) return srv @@ -62,10 +64,14 @@ func (s *Server) SetRecoverFunc(f graphql.RecoverFunc) { s.exec.SetRecoverFunc(f) } -func (s *Server) SetQueryCache(cache graphql.Cache) { +func (s *Server) SetQueryCache(cache graphql.Cache[*ast.QueryDocument]) { s.exec.SetQueryCache(cache) } +func (s *Server) SetParserTokenLimit(limit int) { + s.exec.SetParserTokenLimit(limit) +} + func (s *Server) Use(extension graphql.HandlerExtension) { s.exec.Use(extension) } @@ -107,7 +113,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { resp := &graphql.Response{Errors: []*gqlerror.Error{gqlErr}} b, _ := json.Marshal(resp) w.WriteHeader(http.StatusUnprocessableEntity) - w.Write(b) + _, _ = w.Write(b) } }() @@ -128,10 +134,10 @@ func sendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { if err != nil { panic(err) } - w.Write(b) + _, _ = w.Write(b) } -func sendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { +func sendErrorf(w http.ResponseWriter, code int, format string, args ...any) { sendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) } @@ -143,7 +149,7 @@ func (r OperationFunc) ExtensionName() string { func (r OperationFunc) Validate(schema graphql.ExecutableSchema) error { if r == nil { - return fmt.Errorf("OperationFunc can not be nil") + return errors.New("OperationFunc can not be nil") } return nil } @@ -160,7 +166,7 @@ func (r ResponseFunc) ExtensionName() string { func (r ResponseFunc) Validate(schema graphql.ExecutableSchema) error { if r == nil { - return fmt.Errorf("ResponseFunc can not be nil") + return errors.New("ResponseFunc can not be nil") } return nil } @@ -169,7 +175,7 @@ func (r ResponseFunc) InterceptResponse(ctx context.Context, next graphql.Respon return r(ctx, next) } -type FieldFunc func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) +type FieldFunc func(ctx context.Context, next graphql.Resolver) (res any, err error) func (f FieldFunc) ExtensionName() string { return "InlineFieldFunc" @@ -177,11 +183,11 @@ func (f FieldFunc) ExtensionName() string { func (f FieldFunc) Validate(schema graphql.ExecutableSchema) error { if f == nil { - return fmt.Errorf("FieldFunc can not be nil") + return errors.New("FieldFunc can not be nil") } return nil } -func (f FieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { +func (f FieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res any, err error) { return f(ctx, next) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go index 18f09f5567..1fefb5738c 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/error.go @@ -18,10 +18,10 @@ func SendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { if err != nil { panic(err) } - w.Write(b) + _, _ = w.Write(b) } // SendErrorf wraps SendError to add formatted messages -func SendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { +func SendErrorf(w http.ResponseWriter, code int, format string, args ...any) { SendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go index 73317e4bea..f877c2dd26 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_form_urlencoded.go @@ -63,10 +63,10 @@ func (h UrlEncodedForm) Do(w http.ResponseWriter, r *http.Request, exec graphql. return } - rc, OpErr := exec.CreateOperationContext(ctx, params) - if OpErr != nil { - w.WriteHeader(statusFor(OpErr)) - resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) + rc, opErr := exec.CreateOperationContext(ctx, params) + if opErr != nil { + w.WriteHeader(statusFor(opErr)) + resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), opErr) writeJson(w, resp) return } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go index 9a47bfbef8..470a0fbec2 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_get.go @@ -84,7 +84,7 @@ func (h GET) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecut writeJson(w, responses(ctx)) } -func jsonDecode(r io.Reader, val interface{}) error { +func jsonDecode(r io.Reader, val any) error { dec := json.NewDecoder(r) dec.UseNumber() return dec.Decode(val) diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go index b54a27d043..0bad1110de 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_graphql.go @@ -64,10 +64,10 @@ func (h GRAPHQL) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphEx return } - rc, OpErr := exec.CreateOperationContext(ctx, params) - if OpErr != nil { - w.WriteHeader(statusFor(OpErr)) - resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) + rc, opErr := exec.CreateOperationContext(ctx, params) + if opErr != nil { + w.WriteHeader(statusFor(opErr)) + resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), opErr) writeJson(w, resp) return } @@ -88,7 +88,6 @@ func cleanupBody(body string) (out string, err error) { // is where query starts. If it is, query is url encoded. if strings.HasPrefix(body, "%7B") { body, err = url.QueryUnescape(body) - if err != nil { return body, err } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go index a37010ab74..985f8db294 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/http_post.go @@ -78,10 +78,10 @@ func (h POST) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecu return } - rc, OpErr := exec.CreateOperationContext(ctx, params) - if OpErr != nil { - w.WriteHeader(statusFor(OpErr)) - resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr) + rc, opErr := exec.CreateOperationContext(ctx, params) + if opErr != nil { + w.WriteHeader(statusFor(opErr)) + resp := exec.DispatchError(graphql.WithOperationContext(ctx, rc), opErr) writeJson(w, resp) return } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go index 19b7521c08..aca7207eb9 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/util.go @@ -22,7 +22,7 @@ func writeJsonError(w io.Writer, msg string) { writeJson(w, &graphql.Response{Errors: gqlerror.List{{Message: msg}}}) } -func writeJsonErrorf(w io.Writer, format string, args ...interface{}) { +func writeJsonErrorf(w io.Writer, format string, args ...any) { writeJson(w, &graphql.Response{Errors: gqlerror.List{{Message: fmt.Sprintf(format, args...)}}}) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go index e1334b9290..32e31c7c75 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket.go @@ -103,7 +103,7 @@ func (t Websocket) Do(w http.ResponseWriter, r *http.Request, exec graphql.Graph switch ws.Subprotocol() { default: msg := websocket.FormatCloseMessage(websocket.CloseProtocolError, fmt.Sprintf("unsupported negotiated subprotocol %s", ws.Subprotocol())) - ws.WriteMessage(websocket.CloseMessage, msg) + _ = ws.WriteMessage(websocket.CloseMessage, msg) return case graphqlwsSubprotocol, "": // clients are required to send a subprotocol, to be backward compatible with the previous implementation we select @@ -193,12 +193,12 @@ func (c *wsConnection) init() bool { } } - var initAckPayload *InitPayload = nil + var initAckPayload *InitPayload if c.InitFunc != nil { var ctx context.Context ctx, initAckPayload, err = c.InitFunc(c.ctx, c.initPayload) if err != nil { - c.sendConnectionError(err.Error()) + c.sendConnectionError("%s", err.Error()) c.close(websocket.CloseNormalClosure, "terminated") return false } @@ -239,7 +239,6 @@ func (c *wsConnection) run() { ctx, cancel := context.WithCancel(c.ctx) defer func() { cancel() - c.close(websocket.CloseAbnormalClosure, "unexpected closure") }() // If we're running in graphql-ws mode, create a timer that will trigger a @@ -272,7 +271,7 @@ func (c *wsConnection) run() { if !c.MissingPongOk { // Note: when the connection is closed by this deadline, the client // will receive an "invalid close code" - c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) + _ = c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) } go c.ping(ctx) } @@ -312,7 +311,7 @@ func (c *wsConnection) run() { c.receivedPong = true c.mu.Unlock() // Clear ReadTimeout -- 0 time val clears. - c.conn.SetReadDeadline(time.Time{}) + _ = c.conn.SetReadDeadline(time.Time{}) default: c.sendConnectionError("unexpected message %s", m.t) c.close(websocket.CloseProtocolError, "unexpected message") @@ -357,7 +356,7 @@ func (c *wsConnection) ping(ctx context.Context) { // if we have not yet received a pong, don't reset the deadline. c.mu.Lock() if !c.MissingPongOk && c.receivedPong { - c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) + _ = c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval)) } c.receivedPong = false c.mu.Unlock() @@ -369,7 +368,7 @@ func (c *wsConnection) closeOnCancel(ctx context.Context) { <-ctx.Done() if r := closeReasonForContext(ctx); r != "" { - c.sendConnectionError(r) + c.sendConnectionError("%s", r) } c.close(websocket.CloseNormalClosure, "terminated") } @@ -480,7 +479,7 @@ func (c *wsConnection) sendError(id string, errors ...*gqlerror.Error) { c.write(&message{t: errorMessageType, id: id, payload: b}) } -func (c *wsConnection) sendConnectionError(format string, args ...interface{}) { +func (c *wsConnection) sendConnectionError(format string, args ...any) { b, err := json.Marshal(&gqlerror.Error{Message: fmt.Sprintf(format, args...)}) if err != nil { panic(err) diff --git a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_init.go b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_init.go index a5f84ba2dc..35105535e7 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_init.go +++ b/vendor/github.com/99designs/gqlgen/graphql/handler/transport/websocket_init.go @@ -10,7 +10,7 @@ const ( // InitPayload is a structure that is parsed from the websocket init message payload. TO use // request headers for non-websocket, instead wrap the graphql handler in a middleware. -type InitPayload map[string]interface{} +type InitPayload map[string]any // GetString safely gets a string value from the payload. It returns an empty string if the // payload is nil or the value isn't set. diff --git a/vendor/github.com/99designs/gqlgen/graphql/id.go b/vendor/github.com/99designs/gqlgen/graphql/id.go index 0583995f78..2a946dfa74 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/id.go +++ b/vendor/github.com/99designs/gqlgen/graphql/id.go @@ -11,7 +11,7 @@ func MarshalID(s string) Marshaler { return MarshalString(s) } -func UnmarshalID(v interface{}) (string, error) { +func UnmarshalID(v any) (string, error) { switch v := v.(type) { case string: return v, nil @@ -22,13 +22,9 @@ func UnmarshalID(v interface{}) (string, error) { case int64: return strconv.FormatInt(v, 10), nil case float64: - return fmt.Sprintf("%f", v), nil + return strconv.FormatFloat(v, 'f', 6, 64), nil case bool: - if v { - return "true", nil - } else { - return "false", nil - } + return strconv.FormatBool(v), nil case nil: return "null", nil default: @@ -42,7 +38,7 @@ func MarshalIntID(i int) Marshaler { }) } -func UnmarshalIntID(v interface{}) (int, error) { +func UnmarshalIntID(v any) (int, error) { switch v := v.(type) { case string: return strconv.Atoi(v) @@ -63,7 +59,7 @@ func MarshalUintID(i uint) Marshaler { }) } -func UnmarshalUintID(v interface{}) (uint, error) { +func UnmarshalUintID(v any) (uint, error) { switch v := v.(type) { case string: result, err := strconv.ParseUint(v, 10, 64) diff --git a/vendor/github.com/99designs/gqlgen/graphql/input.go b/vendor/github.com/99designs/gqlgen/graphql/input.go index 88c3efaa6e..681fe08017 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/input.go +++ b/vendor/github.com/99designs/gqlgen/graphql/input.go @@ -10,7 +10,7 @@ const unmarshalInputCtx key = "unmarshal_input_context" // BuildUnmarshalerMap returns a map of unmarshal functions of the ExecutableContext // to use with the WithUnmarshalerMap function. -func BuildUnmarshalerMap(unmarshaler ...interface{}) map[reflect.Type]reflect.Value { +func BuildUnmarshalerMap(unmarshaler ...any) map[reflect.Type]reflect.Value { maps := make(map[reflect.Type]reflect.Value) for _, v := range unmarshaler { ft := reflect.TypeOf(v) @@ -28,7 +28,7 @@ func WithUnmarshalerMap(ctx context.Context, maps map[reflect.Type]reflect.Value } // UnmarshalInputFromContext allows unmarshaling input object from a context. -func UnmarshalInputFromContext(ctx context.Context, raw, v interface{}) error { +func UnmarshalInputFromContext(ctx context.Context, raw, v any) error { m, ok := ctx.Value(unmarshalInputCtx).(map[reflect.Type]reflect.Value) if m == nil || !ok { return errors.New("graphql: the input context is empty") diff --git a/vendor/github.com/99designs/gqlgen/graphql/int.go b/vendor/github.com/99designs/gqlgen/graphql/int.go index 57d0d589ba..41cad3f1f3 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/int.go +++ b/vendor/github.com/99designs/gqlgen/graphql/int.go @@ -13,7 +13,7 @@ func MarshalInt(i int) Marshaler { }) } -func UnmarshalInt(v interface{}) (int, error) { +func UnmarshalInt(v any) (int, error) { switch v := v.(type) { case string: return strconv.Atoi(v) @@ -23,6 +23,8 @@ func UnmarshalInt(v interface{}) (int, error) { return int(v), nil case json.Number: return strconv.Atoi(string(v)) + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an int", v) } @@ -34,7 +36,7 @@ func MarshalInt64(i int64) Marshaler { }) } -func UnmarshalInt64(v interface{}) (int64, error) { +func UnmarshalInt64(v any) (int64, error) { switch v := v.(type) { case string: return strconv.ParseInt(v, 10, 64) @@ -44,6 +46,8 @@ func UnmarshalInt64(v interface{}) (int64, error) { return v, nil case json.Number: return strconv.ParseInt(string(v), 10, 64) + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an int", v) } @@ -55,7 +59,7 @@ func MarshalInt32(i int32) Marshaler { }) } -func UnmarshalInt32(v interface{}) (int32, error) { +func UnmarshalInt32(v any) (int32, error) { switch v := v.(type) { case string: iv, err := strconv.ParseInt(v, 10, 32) @@ -73,6 +77,8 @@ func UnmarshalInt32(v interface{}) (int32, error) { return 0, err } return int32(iv), nil + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an int", v) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/jsonw.go b/vendor/github.com/99designs/gqlgen/graphql/jsonw.go index 54e293f1ad..16bb63b730 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/jsonw.go +++ b/vendor/github.com/99designs/gqlgen/graphql/jsonw.go @@ -28,7 +28,7 @@ type Marshaler interface { } type Unmarshaler interface { - UnmarshalGQL(v interface{}) error + UnmarshalGQL(v any) error } type ContextMarshaler interface { @@ -36,7 +36,7 @@ type ContextMarshaler interface { } type ContextUnmarshaler interface { - UnmarshalGQLContext(ctx context.Context, v interface{}) error + UnmarshalGQLContext(ctx context.Context, v any) error } type contextMarshalerAdapter struct { diff --git a/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go b/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go index 6928828cdf..f7c55cbd00 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go +++ b/vendor/github.com/99designs/gqlgen/graphql/playground/altair_playground.go @@ -66,7 +66,7 @@ var altairPage = template.Must(template.New("altair").Parse(` // AltairHandler responsible for setting up the altair playground func AltairHandler(title, endpoint string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - err := altairPage.Execute(w, map[string]interface{}{ + err := altairPage.Execute(w, map[string]any{ "title": title, "endpoint": endpoint, "endpointIsAbsolute": endpointHasScheme(endpoint), diff --git a/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go b/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go index 750420b480..f998b4d8c3 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go +++ b/vendor/github.com/99designs/gqlgen/graphql/playground/apollo_sandbox_playground.go @@ -64,7 +64,7 @@ func ApolloSandboxHandler(title, endpoint string, opts ...ApolloSandboxOption) h } return func(w http.ResponseWriter, r *http.Request) { - err := apolloSandboxPage.Execute(w, map[string]interface{}{ + err := apolloSandboxPage.Execute(w, map[string]any{ "title": title, "endpoint": endpoint, "endpointIsAbsolute": endpointHasScheme(endpoint), diff --git a/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go b/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go index 05ad02332f..816fcca39d 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go +++ b/vendor/github.com/99designs/gqlgen/graphql/playground/playground.go @@ -85,17 +85,17 @@ var page = template.Must(template.New("graphiql").Parse(` `)) // Handler responsible for setting up the playground -func Handler(title string, endpoint string) http.HandlerFunc { +func Handler(title, endpoint string) http.HandlerFunc { return HandlerWithHeaders(title, endpoint, nil, nil) } // HandlerWithHeaders sets up the playground. // fetcherHeaders are used by the playground's fetcher instance and will not be visible in the UI. // uiHeaders are default headers that will show up in the UI headers editor. -func HandlerWithHeaders(title string, endpoint string, fetcherHeaders map[string]string, uiHeaders map[string]string) http.HandlerFunc { +func HandlerWithHeaders(title, endpoint string, fetcherHeaders, uiHeaders map[string]string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/html; charset=UTF-8") - err := page.Execute(w, map[string]interface{}{ + err := page.Execute(w, map[string]any{ "title": title, "endpoint": endpoint, "fetcherHeaders": fetcherHeaders, diff --git a/vendor/github.com/99designs/gqlgen/graphql/recovery.go b/vendor/github.com/99designs/gqlgen/graphql/recovery.go index 9bc0e47e1d..4aae69195d 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/recovery.go +++ b/vendor/github.com/99designs/gqlgen/graphql/recovery.go @@ -9,9 +9,9 @@ import ( "github.com/vektah/gqlparser/v2/gqlerror" ) -type RecoverFunc func(ctx context.Context, err interface{}) (userMessage error) +type RecoverFunc func(ctx context.Context, err any) (userMessage error) -func DefaultRecover(ctx context.Context, err interface{}) error { +func DefaultRecover(ctx context.Context, err any) error { fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr) debug.PrintStack() diff --git a/vendor/github.com/99designs/gqlgen/graphql/response.go b/vendor/github.com/99designs/gqlgen/graphql/response.go index a82f27e27c..e37b5cfc1e 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/response.go +++ b/vendor/github.com/99designs/gqlgen/graphql/response.go @@ -13,15 +13,15 @@ import ( // https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107 // and https://github.com/facebook/graphql/pull/384 type Response struct { - Errors gqlerror.List `json:"errors,omitempty"` - Data json.RawMessage `json:"data"` - Label string `json:"label,omitempty"` - Path ast.Path `json:"path,omitempty"` - HasNext *bool `json:"hasNext,omitempty"` - Extensions map[string]interface{} `json:"extensions,omitempty"` + Errors gqlerror.List `json:"errors,omitempty"` + Data json.RawMessage `json:"data"` + Label string `json:"label,omitempty"` + Path ast.Path `json:"path,omitempty"` + HasNext *bool `json:"hasNext,omitempty"` + Extensions map[string]any `json:"extensions,omitempty"` } -func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response { +func ErrorResponse(ctx context.Context, messagef string, args ...any) *Response { return &Response{ Errors: gqlerror.List{{Message: fmt.Sprintf(messagef, args...)}}, } diff --git a/vendor/github.com/99designs/gqlgen/graphql/stats.go b/vendor/github.com/99designs/gqlgen/graphql/stats.go index 31b9e6055b..1bf2ad9e66 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/stats.go +++ b/vendor/github.com/99designs/gqlgen/graphql/stats.go @@ -14,7 +14,7 @@ type Stats struct { // Stats collected by handler extensions. Don't use directly, the extension should provide a type safe way to // access this. - extension map[string]interface{} + extension map[string]any } type TraceTiming struct { @@ -42,14 +42,14 @@ func GetStartTime(ctx context.Context) time.Time { return t } -func (c *Stats) SetExtension(name string, data interface{}) { +func (c *Stats) SetExtension(name string, data any) { if c.extension == nil { - c.extension = map[string]interface{}{} + c.extension = map[string]any{} } c.extension[name] = data } -func (c *Stats) GetExtension(name string) interface{} { +func (c *Stats) GetExtension(name string) any { if c.extension == nil { return nil } diff --git a/vendor/github.com/99designs/gqlgen/graphql/string.go b/vendor/github.com/99designs/gqlgen/graphql/string.go index 4da4706478..6622734e3e 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/string.go +++ b/vendor/github.com/99designs/gqlgen/graphql/string.go @@ -47,7 +47,7 @@ func writeQuotedString(w io.Writer, s string) { io.WriteString(w, `"`) } -func UnmarshalString(v interface{}) (string, error) { +func UnmarshalString(v any) (string, error) { switch v := v.(type) { case string: return v, nil @@ -60,13 +60,9 @@ func UnmarshalString(v interface{}) (string, error) { case json.Number: return string(v), nil case bool: - if v { - return "true", nil - } else { - return "false", nil - } + return strconv.FormatBool(v), nil case nil: - return "null", nil + return "", nil default: return "", fmt.Errorf("%T is not a string", v) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/time.go b/vendor/github.com/99designs/gqlgen/graphql/time.go index ef3d17da32..a5fe903013 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/time.go +++ b/vendor/github.com/99designs/gqlgen/graphql/time.go @@ -17,7 +17,7 @@ func MarshalTime(t time.Time) Marshaler { }) } -func UnmarshalTime(v interface{}) (time.Time, error) { +func UnmarshalTime(v any) (time.Time, error) { if tmpStr, ok := v.(string); ok { return time.Parse(time.RFC3339Nano, tmpStr) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/uint.go b/vendor/github.com/99designs/gqlgen/graphql/uint.go index 8730d90042..cd5d235503 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/uint.go +++ b/vendor/github.com/99designs/gqlgen/graphql/uint.go @@ -14,7 +14,7 @@ func MarshalUint(i uint) Marshaler { }) } -func UnmarshalUint(v interface{}) (uint, error) { +func UnmarshalUint(v any) (uint, error) { switch v := v.(type) { case string: u64, err := strconv.ParseUint(v, 10, 64) @@ -34,6 +34,8 @@ func UnmarshalUint(v interface{}) (uint, error) { case json.Number: u64, err := strconv.ParseUint(string(v), 10, 64) return uint(u64), err + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an uint", v) } @@ -45,7 +47,7 @@ func MarshalUint64(i uint64) Marshaler { }) } -func UnmarshalUint64(v interface{}) (uint64, error) { +func UnmarshalUint64(v any) (uint64, error) { switch v := v.(type) { case string: return strconv.ParseUint(v, 10, 64) @@ -63,6 +65,8 @@ func UnmarshalUint64(v interface{}) (uint64, error) { return uint64(v), nil case json.Number: return strconv.ParseUint(string(v), 10, 64) + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an uint", v) } @@ -74,7 +78,7 @@ func MarshalUint32(i uint32) Marshaler { }) } -func UnmarshalUint32(v interface{}) (uint32, error) { +func UnmarshalUint32(v any) (uint32, error) { switch v := v.(type) { case string: iv, err := strconv.ParseUint(v, 10, 32) @@ -100,6 +104,8 @@ func UnmarshalUint32(v interface{}) (uint32, error) { return 0, err } return uint32(iv), nil + case nil: + return 0, nil default: return 0, fmt.Errorf("%T is not an uint", v) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/upload.go b/vendor/github.com/99designs/gqlgen/graphql/upload.go index dafbde6508..b603ab04c8 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/upload.go +++ b/vendor/github.com/99designs/gqlgen/graphql/upload.go @@ -18,7 +18,7 @@ func MarshalUpload(f Upload) Marshaler { }) } -func UnmarshalUpload(v interface{}) (Upload, error) { +func UnmarshalUpload(v any) (Upload, error) { upload, ok := v.(Upload) if !ok { return Upload{}, fmt.Errorf("%T is not an Upload", v) diff --git a/vendor/github.com/99designs/gqlgen/graphql/version.go b/vendor/github.com/99designs/gqlgen/graphql/version.go index 9e108750e5..1b5912b419 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/version.go +++ b/vendor/github.com/99designs/gqlgen/graphql/version.go @@ -1,3 +1,3 @@ package graphql -const Version = "v0.17.46" +const Version = "v0.17.54" diff --git a/vendor/modules.txt b/vendor/modules.txt index 1cca12b881..13ea3eed09 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,8 +1,8 @@ # dario.cat/mergo v1.0.0 ## explicit; go 1.13 dario.cat/mergo -# github.com/99designs/gqlgen v0.17.46 -## explicit; go 1.20 +# github.com/99designs/gqlgen v0.17.54 +## explicit; go 1.22.5 github.com/99designs/gqlgen/complexity github.com/99designs/gqlgen/graphql github.com/99designs/gqlgen/graphql/errcode From 38c559a616225eb6c8e2eaaf6df9c4ab243d1beb Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:47:53 -0700 Subject: [PATCH 144/440] Drop code that doesn't work with latest version of gqlgen and doesn't address any use-case we have --- cmd/state-svc/internal/server/server.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/state-svc/internal/server/server.go b/cmd/state-svc/internal/server/server.go index 9750b77711..0a64c9d534 100644 --- a/cmd/state-svc/internal/server/server.go +++ b/cmd/state-svc/internal/server/server.go @@ -10,7 +10,6 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/extension" - "github.com/99designs/gqlgen/graphql/handler/lru" "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/ActiveState/cli/internal/analytics/client/sync" "github.com/ActiveState/cli/pkg/platform/authentication" @@ -111,11 +110,7 @@ func newGraphServer(r *resolver.Resolver) *handler.Server { return gqlErr }) graphServer.AddTransport(&transport.Websocket{}) - graphServer.SetQueryCache(lru.New(1000)) graphServer.Use(extension.Introspection{}) - graphServer.Use(extension.AutomaticPersistedQuery{ - Cache: lru.New(100), - }) return graphServer } From 852955c1fae695b0282fff7e974f5717446099df Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:49:37 -0700 Subject: [PATCH 145/440] Added GetCache/SetCache secret-svc resolvers --- cmd/state-svc/gqlgen.yml | 2 + cmd/state-svc/internal/graphqltypes/void.go | 13 + cmd/state-svc/internal/resolver/resolver.go | 19 ++ .../internal/server/generated/generated.go | 296 ++++++++++++++++++ cmd/state-svc/schema/schema.graphqls | 7 + internal/graph/generated.go | 3 + internal/graph/response.go | 4 +- pkg/platform/api/svc/request/cache.go | 47 +++ pkg/platform/model/svc.go | 27 ++ 9 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 cmd/state-svc/internal/graphqltypes/void.go create mode 100644 pkg/platform/api/svc/request/cache.go diff --git a/cmd/state-svc/gqlgen.yml b/cmd/state-svc/gqlgen.yml index 6e113118ea..47a84e2939 100644 --- a/cmd/state-svc/gqlgen.yml +++ b/cmd/state-svc/gqlgen.yml @@ -59,3 +59,5 @@ models: - github.com/99designs/gqlgen/graphql.Int - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 + Void: + model: github.com/ActiveState/cli/cmd/state-svc/internal/graphql.Void diff --git a/cmd/state-svc/internal/graphqltypes/void.go b/cmd/state-svc/internal/graphqltypes/void.go new file mode 100644 index 0000000000..3b0ee407df --- /dev/null +++ b/cmd/state-svc/internal/graphqltypes/void.go @@ -0,0 +1,13 @@ +package graphqltypes + +import "io" + +type Void struct{} + +func (Void) MarshalGQL(w io.Writer) { + _, _ = w.Write([]byte("null")) +} + +func (v *Void) UnmarshalGQL(input interface{}) error { + return nil +} diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index 4cabfbf2f2..9682619368 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -10,6 +10,7 @@ import ( "strconv" "time" + "github.com/ActiveState/cli/cmd/state-svc/internal/graphqltypes" "github.com/ActiveState/cli/cmd/state-svc/internal/hash" "github.com/ActiveState/cli/cmd/state-svc/internal/messages" "github.com/ActiveState/cli/cmd/state-svc/internal/rtwatcher" @@ -30,6 +31,7 @@ import ( "github.com/ActiveState/cli/internal/updater" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/projectfile" + "github.com/patrickmn/go-cache" ) type Resolver struct { @@ -43,6 +45,7 @@ type Resolver struct { anForClient *sync.Client // Use separate client for events sent through service so we don't contaminate one with the other rtwatch *rtwatcher.Watcher auth *authentication.Auth + globalCache *cache.Cache } // var _ genserver.ResolverRoot = &Resolver{} // Must implement ResolverRoot @@ -89,6 +92,7 @@ func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Res anForClient, rtwatcher.New(cfg, anForClient), auth, + cache.New(time.Hour, 10*time.Minute), }, nil } @@ -104,6 +108,8 @@ func (r *Resolver) Close() error { // So far no need for this, so we're pointing back at ourselves.. func (r *Resolver) Query() genserver.QueryResolver { return r } +func (r *Resolver) Mutation() genserver.MutationResolver { return r } + func (r *Resolver) Version(ctx context.Context) (*graph.Version, error) { defer func() { handlePanics(recover(), debug.Stack()) }() @@ -317,6 +323,19 @@ func (r *Resolver) HashGlobs(ctx context.Context, globs []string) (string, error return r.fileHasher.HashFiles(files) } +func (r *Resolver) GetCache(ctx context.Context, key string) (string, error) { + v, exists := r.globalCache.Get(key) + if !exists { + return "", nil + } + return v.(string), nil +} + +func (r *Resolver) SetCache(ctx context.Context, key string, value string, expiry int) (*graphqltypes.Void, error) { + r.globalCache.Set(key, value, time.Duration(expiry)*time.Second) + return &graphqltypes.Void{}, nil +} + func handlePanics(recovered interface{}, stack []byte) { if recovered != nil { multilog.Error("Panic: %v", recovered) diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index 551c6c0d4a..5d24da09e2 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -13,6 +13,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" + graphql1 "github.com/ActiveState/cli/cmd/state-svc/internal/graphqltypes" "github.com/ActiveState/cli/internal/graph" gqlparser "github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2/ast" @@ -38,6 +39,7 @@ type Config struct { } type ResolverRoot interface { + Mutation() MutationResolver Query() QueryResolver } @@ -75,6 +77,10 @@ type ComplexityRoot struct { Repeat func(childComplexity int) int } + Mutation struct { + SetCache func(childComplexity int, key string, value string, expiry int) int + } + Organization struct { Role func(childComplexity int) int URLname func(childComplexity int) int @@ -96,6 +102,7 @@ type ComplexityRoot struct { CheckMessages func(childComplexity int, command string, flags []string) int ConfigChanged func(childComplexity int, key string) int FetchLogTail func(childComplexity int) int + GetCache func(childComplexity int, key string) int GetJwt func(childComplexity int) int GetProcessesInUse func(childComplexity int, execDir string) int HashGlobs func(childComplexity int, globs []string) int @@ -128,6 +135,9 @@ type ComplexityRoot struct { } } +type MutationResolver interface { + SetCache(ctx context.Context, key string, value string, expiry int) (*graphql1.Void, error) +} type QueryResolver interface { Version(ctx context.Context) (*graph.Version, error) AvailableUpdate(ctx context.Context, desiredChannel string, desiredVersion string) (*graph.AvailableUpdate, error) @@ -140,6 +150,7 @@ type QueryResolver interface { GetProcessesInUse(ctx context.Context, execDir string) ([]*graph.ProcessInfo, error) GetJwt(ctx context.Context) (*graph.Jwt, error) HashGlobs(ctx context.Context, globs []string) (string, error) + GetCache(ctx context.Context, key string) (string, error) } type executableSchema struct { @@ -266,6 +277,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.MessageInfo.Repeat(childComplexity), true + case "Mutation.setCache": + if e.complexity.Mutation.SetCache == nil { + break + } + + args, err := ec.field_Mutation_setCache_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.SetCache(childComplexity, args["key"].(string), args["value"].(string), args["expiry"].(int)), true + case "Organization.role": if e.complexity.Organization.Role == nil { break @@ -363,6 +386,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.FetchLogTail(childComplexity), true + case "Query.getCache": + if e.complexity.Query.GetCache == nil { + break + } + + args, err := ec.field_Query_getCache_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.GetCache(childComplexity, args["key"].(string)), true + case "Query.getJWT": if e.complexity.Query.GetJwt == nil { break @@ -538,6 +573,21 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { return &response } + case ast.Mutation: + return func(ctx context.Context) *graphql.Response { + if !first { + return nil + } + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data := ec._Mutation(ctx, rc.Operation.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + + return &graphql.Response{ + Data: buf.Bytes(), + } + } default: return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) @@ -677,6 +727,11 @@ type Query { getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT hashGlobs(globs: [String!]!): String! + getCache(key: String!): String! +} + +type Mutation { + setCache(key: String!, value: String!, expiry: Int!): Void } type ConfigChangedResponse { @@ -687,6 +742,8 @@ type ProcessInfo { exe: String! pid: Int! } + +scalar Void `, BuiltIn: false}, } var parsedSchema = gqlparser.MustLoadSchema(sources...) @@ -695,6 +752,39 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) // region ***************************** args.gotpl ***************************** +func (ec *executionContext) field_Mutation_setCache_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["key"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["key"] = arg0 + var arg1 string + if tmp, ok := rawArgs["value"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["value"] = arg1 + var arg2 int + if tmp, ok := rawArgs["expiry"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expiry")) + arg2, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } + } + args["expiry"] = arg2 + return args, nil +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -824,6 +914,21 @@ func (ec *executionContext) field_Query_configChanged_args(ctx context.Context, return args, nil } +func (ec *executionContext) field_Query_getCache_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["key"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["key"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_getProcessesInUse_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1604,6 +1709,58 @@ func (ec *executionContext) fieldContext_MessageInfo_placement(_ context.Context return fc, nil } +func (ec *executionContext) _Mutation_setCache(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_setCache(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().SetCache(rctx, fc.Args["key"].(string), fc.Args["value"].(string), fc.Args["expiry"].(int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*graphql1.Void) + fc.Result = res + return ec.marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqlᚐVoid(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_setCache(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Void does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_setCache_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Organization_URLname(ctx context.Context, field graphql.CollectedField, obj *graph.Organization) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Organization_URLname(ctx, field) if err != nil { @@ -2471,6 +2628,61 @@ func (ec *executionContext) fieldContext_Query_hashGlobs(ctx context.Context, fi return fc, nil } +func (ec *executionContext) _Query_getCache(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getCache(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetCache(rctx, fc.Args["key"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getCache(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_getCache_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -5128,6 +5340,52 @@ func (ec *executionContext) _MessageInfo(ctx context.Context, sel ast.SelectionS return out } +var mutationImplementors = []string{"Mutation"} + +func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Mutation", + }) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ + Object: field.Name, + Field: field, + }) + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Mutation") + case "setCache": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_setCache(ctx, field) + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var organizationImplementors = []string{"Organization"} func (ec *executionContext) _Organization(ctx context.Context, sel ast.SelectionSet, obj *graph.Organization) graphql.Marshaler { @@ -5502,6 +5760,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "getCache": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_getCache(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -6722,6 +7002,22 @@ func (ec *executionContext) marshalOVersion2ᚖgithubᚗcomᚋActiveStateᚋcli return ec._Version(ctx, sel, v) } +func (ec *executionContext) unmarshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqlᚐVoid(ctx context.Context, v interface{}) (*graphql1.Void, error) { + if v == nil { + return nil, nil + } + var res = new(graphql1.Void) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqlᚐVoid(ctx context.Context, sel ast.SelectionSet, v *graphql1.Void) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index f91b34b3c4..332b01823d 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -89,6 +89,11 @@ type Query { getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT hashGlobs(globs: [String!]!): String! + getCache(key: String!): String! +} + +type Mutation { + setCache(key: String!, value: String!, expiry: Int!): Void } type ConfigChangedResponse { @@ -99,3 +104,5 @@ type ProcessInfo { exe: String! pid: Int! } + +scalar Void diff --git a/internal/graph/generated.go b/internal/graph/generated.go index df667c86f3..9a51e2a224 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -38,6 +38,9 @@ type MessageInfo struct { Placement MessagePlacementType `json:"placement"` } +type Mutation struct { +} + type Organization struct { URLname string `json:"URLname"` Role string `json:"role"` diff --git a/internal/graph/response.go b/internal/graph/response.go index ed1ee4fb2c..c766e4d2a0 100644 --- a/internal/graph/response.go +++ b/internal/graph/response.go @@ -1,6 +1,8 @@ package graph -import "encoding/json" +import ( + "encoding/json" +) type VersionResponse struct { Version Version `json:"version"` diff --git a/pkg/platform/api/svc/request/cache.go b/pkg/platform/api/svc/request/cache.go new file mode 100644 index 0000000000..4a2dfc524f --- /dev/null +++ b/pkg/platform/api/svc/request/cache.go @@ -0,0 +1,47 @@ +package request + +import "time" + +type GetCache struct { + key string +} + +func NewGetCache(key string) *GetCache { + return &GetCache{key: key} +} + +func (c *GetCache) Query() string { + return `query($key: String!) { + getCache(key: $key) + }` +} + +func (c *GetCache) Vars() (map[string]interface{}, error) { + return map[string]interface{}{ + "key": c.key, + }, nil +} + +type SetCache struct { + key string + value string + expiry time.Duration +} + +func NewSetCache(key, value string, expiry time.Duration) *SetCache { + return &SetCache{key: key, value: value, expiry: expiry} +} + +func (c *SetCache) Query() string { + return `mutation($key: String!, $value: String!, $expiry: Int!) { + setCache(key: $key, value: $value, expiry: $expiry) + }` +} + +func (c *SetCache) Vars() (map[string]interface{}, error) { + return map[string]interface{}{ + "key": c.key, + "value": c.value, + "expiry": c.expiry.Seconds(), + }, nil +} diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 620a8fd0fc..8816c15035 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -14,6 +14,7 @@ import ( "github.com/ActiveState/cli/internal/graph" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/profile" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" "github.com/ActiveState/cli/pkg/platform/api/svc/request" "github.com/ActiveState/graphql" @@ -199,6 +200,32 @@ func (m *SvcModel) GetJWT(ctx context.Context) (*mono_models.JWT, error) { return jwt, nil } +func (m *SvcModel) GetCache(key string) (result string, _ error) { + defer func() { logging.Debug("GetCache %s, result size: %d", key, len(result)) }() + defer profile.Measure("svc:GetCache", time.Now()) + + req := request.NewGetCache(key) + response := make(map[string]string) + if err := m.request(context.Background(), req, &response); err != nil { + return "", errs.Wrap(err, "Error sending GetCache request to state-svc") + } + if entry, ok := response["getCache"]; ok { + return entry, nil + } + return "", errs.New("svcModel.GetCache() did not return an expected value") +} + +func (m *SvcModel) SetCache(key, value string, expiry time.Duration) error { + logging.Debug("SetCache %s, value size: %d", key, len(value)) + defer profile.Measure("svc:SetCache", time.Now()) + + req := request.NewSetCache(key, value, expiry) + if err := m.request(context.Background(), req, ptr.To(make(map[string]string))); err != nil { + return errs.Wrap(err, "Error sending SetCache request to state-svc") + } + return nil +} + func jsonFromMap(m map[string]interface{}) string { d, err := json.Marshal(m) if err != nil { From f0fcb98a63606d4280ac79f51576b2653c80f226 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:51:42 -0700 Subject: [PATCH 146/440] Buildplanner model now depends on svcModel --- cmd/state/main.go | 2 +- internal/migrator/migrator.go | 5 +-- internal/runbits/buildplanner/buildplanner.go | 34 ++++++++++++------- internal/runbits/buildscript/file.go | 11 ++++-- internal/runbits/checkout/checkout.go | 27 ++++++--------- internal/runbits/checkout/rationalize.go | 2 +- .../runtime/requirements/requirements.go | 4 +-- internal/runbits/runtime/runtime.go | 2 +- internal/runners/artifacts/artifacts.go | 4 ++- internal/runners/artifacts/download.go | 4 ++- internal/runners/checkout/checkout.go | 4 +-- internal/runners/commit/commit.go | 2 +- internal/runners/eval/eval.go | 33 +++++++++--------- internal/runners/export/buildplan.go | 2 +- internal/runners/initialize/init.go | 6 ++-- internal/runners/languages/languages.go | 2 +- internal/runners/packages/import.go | 2 +- internal/runners/packages/list.go | 2 +- internal/runners/pull/pull.go | 6 ++-- internal/runners/push/push.go | 6 ++-- internal/runners/reset/reset.go | 2 +- internal/runners/revert/revert.go | 2 +- internal/runners/upgrade/upgrade.go | 3 +- internal/testhelpers/e2e/session.go | 14 +++++++- .../model/buildplanner/buildplanner.go | 2 +- 25 files changed, 107 insertions(+), 76 deletions(-) diff --git a/cmd/state/main.go b/cmd/state/main.go index efc3885642..65e11674d2 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -179,7 +179,7 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out } } - projectfile.RegisterMigrator(migrator.NewMigrator(auth, cfg)) + projectfile.RegisterMigrator(migrator.NewMigrator(auth, cfg, svcmodel)) // Retrieve project file if os.Getenv("ACTIVESTATE_PROJECT") != "" { diff --git a/internal/migrator/migrator.go b/internal/migrator/migrator.go index 0ac67f134e..23c7b7fa1f 100644 --- a/internal/migrator/migrator.go +++ b/internal/migrator/migrator.go @@ -10,10 +10,11 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/pkg/platform/authentication" + "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/projectfile" ) -func NewMigrator(auth *authentication.Auth, cfg *config.Instance) projectfile.MigratorFunc { +func NewMigrator(auth *authentication.Auth, cfg *config.Instance, svcm *model.SvcModel) projectfile.MigratorFunc { return func(project *projectfile.Project, configVersion int) (v int, rerr error) { defer func() { if rerr != nil { @@ -28,7 +29,7 @@ func NewMigrator(auth *authentication.Auth, cfg *config.Instance) projectfile.Mi case 0: if cfg.GetBool(constants.OptinBuildscriptsConfig) { logging.Debug("Creating buildscript") - if err := buildscript_runbit.Initialize(filepath.Dir(project.Path()), auth); err != nil { + if err := buildscript_runbit.Initialize(filepath.Dir(project.Path()), auth, svcm); err != nil { return v, errs.Wrap(err, "Failed to initialize buildscript") } } diff --git a/internal/runbits/buildplanner/buildplanner.go b/internal/runbits/buildplanner/buildplanner.go index 9877b54821..85db3935c2 100644 --- a/internal/runbits/buildplanner/buildplanner.go +++ b/internal/runbits/buildplanner/buildplanner.go @@ -5,12 +5,12 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/buildplan" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/request" - "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" "github.com/ActiveState/cli/pkg/project" @@ -34,16 +34,27 @@ func (e *ErrCommitDoesNotExistInProject) Error() string { return "Commit does not exist in project" } +type primeable interface { + primer.Projecter + primer.Auther + primer.Outputer + primer.SvcModeler +} + // GetCommit returns a commit from the given arguments. By default, the local commit for the // current project is returned, but a commit for a given commitID for the current project can be // returned, as can the commit for a remote project (and optional commitID). func GetCommit( - pj *project.Project, namespace *project.Namespaced, commitID string, target string, - auth *authentication.Auth, - out output.Outputer) (commit *bpModel.Commit, rerr error) { + prime primeable, +) (commit *bpModel.Commit, rerr error) { + pj := prime.Project() + out := prime.Output() + auth := prime.Auth() + svcm := prime.SvcModel() + if pj == nil && !namespace.IsValid() { return nil, rationalize.ErrNoProject } @@ -81,7 +92,7 @@ func GetCommit( return nil, errs.Wrap(err, "Could not get local commit") } - bp := bpModel.NewBuildPlannerModel(auth) + bp := bpModel.NewBuildPlannerModel(auth, svcm) commit, err = bp.FetchCommit(localCommitID, pj.Owner(), pj.Name(), targetPtr) if err != nil { return nil, errs.Wrap(err, "Failed to fetch commit") @@ -89,7 +100,7 @@ func GetCommit( // Return buildplan from the given commitID for the current project. case !namespaceProvided && commitIdProvided: - bp := bpModel.NewBuildPlannerModel(auth) + bp := bpModel.NewBuildPlannerModel(auth, svcm) commit, err = bp.FetchCommit(commitUUID, pj.Owner(), pj.Name(), targetPtr) if err != nil { return nil, errs.Wrap(err, "Failed to fetch commit") @@ -113,7 +124,7 @@ func GetCommit( } commitUUID = *branchCommitUUID - bp := bpModel.NewBuildPlannerModel(auth) + bp := bpModel.NewBuildPlannerModel(auth, svcm) commit, err = bp.FetchCommit(commitUUID, namespace.Owner, namespace.Project, targetPtr) if err != nil { return nil, errs.Wrap(err, "Failed to fetch commit") @@ -121,7 +132,7 @@ func GetCommit( // Return the buildplan for the given commitID of the given project. case namespaceProvided && commitIdProvided: - bp := bpModel.NewBuildPlannerModel(auth) + bp := bpModel.NewBuildPlannerModel(auth, svcm) commit, err = bp.FetchCommit(commitUUID, namespace.Owner, namespace.Project, targetPtr) if err != nil { return nil, errs.Wrap(err, "Failed to fetch commit") @@ -165,13 +176,12 @@ func GetCommit( // current project can be returned, as can the buildplan for a remote project (and optional // commitID). func GetBuildPlan( - pj *project.Project, namespace *project.Namespaced, commitID string, target string, - auth *authentication.Auth, - out output.Outputer) (bp *buildplan.BuildPlan, rerr error) { - commit, err := GetCommit(pj, namespace, commitID, target, auth, out) + prime primeable, +) (bp *buildplan.BuildPlan, rerr error) { + commit, err := GetCommit(namespace, commitID, target, prime) if err != nil { return nil, errs.Wrap(err, "Could not get commit") } diff --git a/internal/runbits/buildscript/file.go b/internal/runbits/buildscript/file.go index 36868e34c5..72e66d5ff6 100644 --- a/internal/runbits/buildscript/file.go +++ b/internal/runbits/buildscript/file.go @@ -9,9 +9,11 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/authentication" + "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/platform/model/buildplanner" ) @@ -40,7 +42,12 @@ func ScriptFromFile(path string) (*buildscript.BuildScript, error) { return buildscript.Unmarshal(data) } -func Initialize(path string, auth *authentication.Auth) error { +type primeable interface { + primer.Auther + primer.SvcModeler +} + +func Initialize(path string, auth *authentication.Auth, svcm *model.SvcModel) error { scriptPath := filepath.Join(path, constants.BuildScriptFileName) script, err := ScriptFromFile(scriptPath) if err == nil { @@ -56,7 +63,7 @@ func Initialize(path string, auth *authentication.Auth) error { return errs.Wrap(err, "Unable to get the local commit ID") } - buildplanner := buildplanner.NewBuildPlannerModel(auth) + buildplanner := buildplanner.NewBuildPlannerModel(auth, svcm) script, err = buildplanner.GetBuildScript(commitId.String()) if err != nil { return errs.Wrap(err, "Unable to get the remote build expression and time") diff --git a/internal/runbits/checkout/checkout.go b/internal/runbits/checkout/checkout.go index 68a9fd3e37..5c505f4cc4 100644 --- a/internal/runbits/checkout/checkout.go +++ b/internal/runbits/checkout/checkout.go @@ -3,8 +3,6 @@ package checkout import ( "path/filepath" - "github.com/ActiveState/cli/internal/analytics" - "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/buildscript" @@ -15,7 +13,6 @@ import ( "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/language" "github.com/ActiveState/cli/internal/osutils" - "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/runbits/git" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" @@ -30,17 +27,15 @@ type primeable interface { primer.Analyticer primer.Configurer primer.Auther + primer.SvcModeler } // Checkout will checkout the given platform project at the given path // This includes cloning an associated repository and creating the activestate.yaml // It does not activate any environment type Checkout struct { - repo git.Repository - output.Outputer - config *config.Instance - analytics analytics.Dispatcher - auth *authentication.Auth + repo git.Repository + prime primeable } type errCommitDoesNotBelong struct { @@ -54,7 +49,7 @@ func (e errCommitDoesNotBelong) Error() string { var errNoCommitID = errs.New("commitID is nil") func New(repo git.Repository, prime primeable) *Checkout { - return &Checkout{repo, prime.Output(), prime.Config(), prime.Analytics(), prime.Auth()} + return &Checkout{repo, prime} } func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone, bareCheckout bool) (_ string, rerr error) { @@ -90,7 +85,7 @@ func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath // Clone the related repo, if it is defined if !noClone && repoURL != nil && *repoURL != "" { - err := r.repo.CloneProject(ns.Owner, ns.Project, path, r.Outputer, r.analytics) + err := r.repo.CloneProject(ns.Owner, ns.Project, path, r.prime.Output(), r.prime.Analytics()) if err != nil { return "", locale.WrapError(err, "err_clone_project", "Could not clone associated git repository") } @@ -103,8 +98,8 @@ func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath return "", errs.Wrap(err, "Could not create project files") } - if r.config.GetBool(constants.OptinBuildscriptsConfig) { - if err := buildscript_runbit.Initialize(path, r.auth); err != nil { + if r.prime.Config().GetBool(constants.OptinBuildscriptsConfig) { + if err := buildscript_runbit.Initialize(path, r.prime.Auth(), r.prime.SvcModel()); err != nil { return "", errs.Wrap(err, "Unable to initialize buildscript") } } @@ -117,7 +112,7 @@ func (r *Checkout) fetchProject( // If project does not exist at path then we must checkout // the project and create the project file - pj, err := model.FetchProjectByName(ns.Owner, ns.Project, r.auth) + pj, err := model.FetchProjectByName(ns.Owner, ns.Project, r.prime.Auth()) if err != nil { return "", "", nil, "", "", nil, locale.WrapError(err, "err_fetch_project", "", ns.String()) } @@ -129,7 +124,7 @@ func (r *Checkout) fetchProject( // Fetch the branch the given commitID is on. case commitID != nil: for _, b := range pj.Branches { - if belongs, err := model.CommitBelongsToBranch(ns.Owner, ns.Project, b.Label, *commitID, r.auth); err == nil && belongs { + if belongs, err := model.CommitBelongsToBranch(ns.Owner, ns.Project, b.Label, *commitID, r.prime.Auth()); err == nil && belongs { branch = b break } else if err != nil { @@ -162,7 +157,7 @@ func (r *Checkout) fetchProject( return "", "", nil, "", "", nil, errNoCommitID } - lang, err := getLanguage(*commitID, r.auth) + lang, err := getLanguage(*commitID, r.prime.Auth()) if err != nil { return "", "", nil, "", "", nil, errs.Wrap(err, "Could not get language from commitID") } @@ -170,7 +165,7 @@ func (r *Checkout) fetchProject( // Match the case of the organization. // Otherwise the incorrect case will be written to the project file. - owners, err := model.FetchOrganizationsByIDs([]strfmt.UUID{pj.OrganizationID}, r.auth) + owners, err := model.FetchOrganizationsByIDs([]strfmt.UUID{pj.OrganizationID}, r.prime.Auth()) if err != nil { return "", "", nil, "", "", nil, errs.Wrap(err, "Unable to get the project's org") } diff --git a/internal/runbits/checkout/rationalize.go b/internal/runbits/checkout/rationalize.go index d704d70901..4473917fc7 100644 --- a/internal/runbits/checkout/rationalize.go +++ b/internal/runbits/checkout/rationalize.go @@ -25,7 +25,7 @@ func (c *Checkout) rationalizeError(err *error) { case errors.As(*err, &errProjectNotFound): *err = errs.WrapUserFacing(*err, locale.Tr("err_api_project_not_found", errProjectNotFound.Organization, errProjectNotFound.Project), - errs.SetIf(!c.auth.Authenticated(), errs.SetTips(locale.T("tip_private_project_auth"))), + errs.SetIf(!c.prime.Auth().Authenticated(), errs.SetTips(locale.T("tip_private_project_auth"))), errs.SetInput(), ) case errors.As(*err, &errNoPermssion): diff --git a/internal/runbits/runtime/requirements/requirements.go b/internal/runbits/runtime/requirements/requirements.go index f7625b5e1d..69bd71baf6 100644 --- a/internal/runbits/runtime/requirements/requirements.go +++ b/internal/runbits/runtime/requirements/requirements.go @@ -199,7 +199,7 @@ func (r *RequirementOperation) ExecuteRequirementOperation(ts *time.Time, requir return locale.WrapError(err, "err_resolve_requirements", "Could not resolve one or more requirements") } - bp := bpModel.NewBuildPlannerModel(r.Auth) + bp := bpModel.NewBuildPlannerModel(r.Auth, r.SvcModel) script, err := r.prepareBuildScript(bp, parentCommitID, requirements, ts) if err != nil { return errs.Wrap(err, "Could not prepare build script") @@ -538,7 +538,7 @@ func (r *RequirementOperation) updateCommitID(commitID strfmt.UUID) error { } if r.Config.GetBool(constants.OptinBuildscriptsConfig) { - bp := bpModel.NewBuildPlannerModel(r.Auth) + bp := bpModel.NewBuildPlannerModel(r.Auth, r.SvcModel) script, err := bp.GetBuildScript(commitID.String()) if err != nil { return errs.Wrap(err, "Could not get remote build expr and time") diff --git a/internal/runbits/runtime/runtime.go b/internal/runbits/runtime/runtime.go index a872b8bd47..589551bfb0 100644 --- a/internal/runbits/runtime/runtime.go +++ b/internal/runbits/runtime/runtime.go @@ -195,7 +195,7 @@ func Update( // Solve solveSpinner := output.StartSpinner(prime.Output(), locale.T("progress_solve"), constants.TerminalAnimationInterval) - bpm := bpModel.NewBuildPlannerModel(prime.Auth()) + bpm := bpModel.NewBuildPlannerModel(prime.Auth(), prime.SvcModel()) commit, err = bpm.FetchCommit(commitID, proj.Owner(), proj.Name(), nil) if err != nil { solveSpinner.Stop(locale.T("progress_fail")) diff --git a/internal/runners/artifacts/artifacts.go b/internal/runners/artifacts/artifacts.go index 06fb06b513..078d2668a1 100644 --- a/internal/runners/artifacts/artifacts.go +++ b/internal/runners/artifacts/artifacts.go @@ -47,6 +47,7 @@ type Configurable interface { } type Artifacts struct { + prime primeable out output.Outputer project *project.Project analytics analytics.Dispatcher @@ -83,6 +84,7 @@ type structuredArtifact struct { func New(p primeable) *Artifacts { return &Artifacts{ + prime: p, out: p.Output(), project: p.Project(), auth: p.Auth(), @@ -116,7 +118,7 @@ func (b *Artifacts) Run(params *Params) (rerr error) { } bp, err := buildplanner_runbit.GetBuildPlan( - b.project, params.Namespace, params.CommitID, params.Target, b.auth, b.out) + params.Namespace, params.CommitID, params.Target, b.prime) if err != nil { return errs.Wrap(err, "Could not get buildplan") } diff --git a/internal/runners/artifacts/download.go b/internal/runners/artifacts/download.go index 4558500a15..22484c9c54 100644 --- a/internal/runners/artifacts/download.go +++ b/internal/runners/artifacts/download.go @@ -35,6 +35,7 @@ type DownloadParams struct { } type Download struct { + prime primeable out output.Outputer project *project.Project analytics analytics.Dispatcher @@ -45,6 +46,7 @@ type Download struct { func NewDownload(prime primeable) *Download { return &Download{ + prime: prime, out: prime.Output(), project: prime.Project(), analytics: prime.Analytics(), @@ -92,7 +94,7 @@ func (d *Download) Run(params *DownloadParams) (rerr error) { } bp, err := buildplanner_runbit.GetBuildPlan( - d.project, params.Namespace, params.CommitID, target, d.auth, d.out) + params.Namespace, params.CommitID, target, d.prime) if err != nil { return errs.Wrap(err, "Could not get build plan map") } diff --git a/internal/runners/checkout/checkout.go b/internal/runners/checkout/checkout.go index fb798fce92..576dcbd453 100644 --- a/internal/runners/checkout/checkout.go +++ b/internal/runners/checkout/checkout.go @@ -149,7 +149,7 @@ func (u *Checkout) Run(params *Params) (rerr error) { // Solve runtime solveSpinner := output.StartSpinner(u.out, locale.T("progress_solve"), constants.TerminalAnimationInterval) - bpm := bpModel.NewBuildPlannerModel(u.auth) + bpm := bpModel.NewBuildPlannerModel(u.auth, u.svcModel) commit, err := bpm.FetchCommit(commitID, proj.Owner(), proj.Name(), nil) if err != nil { solveSpinner.Stop(locale.T("progress_fail")) @@ -174,7 +174,7 @@ func (u *Checkout) Run(params *Params) (rerr error) { if err := cves.NewCveReport(u.prime).Report(buildPlan, nil); err != nil { return errs.Wrap(err, "Could not report CVEs") } - + rti, err := runtime_runbit.Update(u.prime, trigger.TriggerCheckout, rtOpts...) if err != nil { return errs.Wrap(err, "Could not setup runtime") diff --git a/internal/runners/commit/commit.go b/internal/runners/commit/commit.go index 93985af9c8..d0edf547d6 100644 --- a/internal/runners/commit/commit.go +++ b/internal/runners/commit/commit.go @@ -83,7 +83,7 @@ func (c *Commit) Run() (rerr error) { if err != nil { return errs.Wrap(err, "Unable to get local commit ID") } - bp := buildplanner.NewBuildPlannerModel(c.prime.Auth()) + bp := buildplanner.NewBuildPlannerModel(c.prime.Auth(), c.prime.SvcModel()) remoteScript, err := bp.GetBuildScript(localCommitID.String()) if err != nil { return errs.Wrap(err, "Could not get remote build expr and time for provided commit") diff --git a/internal/runners/eval/eval.go b/internal/runners/eval/eval.go index 2f45cc1448..d55d00b2c2 100644 --- a/internal/runners/eval/eval.go +++ b/internal/runners/eval/eval.go @@ -8,15 +8,14 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/localcommit" - "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model/buildplanner" - "github.com/ActiveState/cli/pkg/project" ) type primeable interface { primer.Outputer primer.Auther primer.Projecter + primer.SvcModeler } type Params struct { @@ -24,57 +23,57 @@ type Params struct { } type Eval struct { - out output.Outputer - project *project.Project - auth *authentication.Auth + prime primeable } func New(p primeable) *Eval { return &Eval{ - out: p.Output(), - project: p.Project(), - auth: p.Auth(), + prime: p, } } func (e *Eval) Run(params *Params) (rerr error) { defer rationalizeError(&rerr) - e.out.Notice(output.Title(locale.Tl("title_eval", "Evaluating target: {{.V0}}", params.Target))) + out := e.prime.Output() + auth := e.prime.Auth() + proj := e.prime.Project() - if !e.auth.Authenticated() { + out.Notice(output.Title(locale.Tl("title_eval", "Evaluating target: {{.V0}}", params.Target))) + + if !auth.Authenticated() { return rationalize.ErrNotAuthenticated } - if e.project == nil { + if proj == nil { return rationalize.ErrNoProject } - commitID, err := localcommit.Get(e.project.Dir()) + commitID, err := localcommit.Get(proj.Dir()) if err != nil { return errs.Wrap(err, "Unable to get commit ID") } - pg := output.StartSpinner(e.out, locale.Tl("progress_eval", "Evaluating ... "), constants.TerminalAnimationInterval) + pg := output.StartSpinner(out, locale.Tl("progress_eval", "Evaluating ... "), constants.TerminalAnimationInterval) defer func() { if pg != nil { pg.Stop(locale.T("progress_fail") + "\n") } }() - bp := buildplanner.NewBuildPlannerModel(e.auth) - if err := bp.BuildTarget(e.project.Owner(), e.project.Name(), commitID.String(), params.Target); err != nil { + bp := buildplanner.NewBuildPlannerModel(auth, e.prime.SvcModel()) + if err := bp.BuildTarget(proj.Owner(), proj.Name(), commitID.String(), params.Target); err != nil { return locale.WrapError(err, "err_eval", "Failed to evaluate target '{{.V0}}'", params.Target) } - if err := bp.WaitForBuild(commitID, e.project.Owner(), e.project.Name(), ¶ms.Target); err != nil { + if err := bp.WaitForBuild(commitID, proj.Owner(), proj.Name(), ¶ms.Target); err != nil { return locale.WrapError(err, "err_eval_wait_for_build", "Failed to build target: '{{.V)}}'", params.Target) } pg.Stop("OK") pg = nil - e.out.Notice(locale.Tl("notice_eval_success", "Target successfully evaluated")) + out.Notice(locale.Tl("notice_eval_success", "Target successfully evaluated")) return nil } diff --git a/internal/runners/export/buildplan.go b/internal/runners/export/buildplan.go index a48c2eafd2..80a1c3afb9 100644 --- a/internal/runners/export/buildplan.go +++ b/internal/runners/export/buildplan.go @@ -34,7 +34,7 @@ func (b *BuildPlan) Run(params *BuildPlanParams) (rerr error) { } commit, err := buildplanner.GetCommit( - proj, params.Namespace, params.CommitID, params.Target, b.prime.Auth(), out) + params.Namespace, params.CommitID, params.Target, b.prime) if err != nil { return errs.Wrap(err, "Could not get commit") } diff --git a/internal/runners/initialize/init.go b/internal/runners/initialize/init.go index 1951a206f8..85ecd17d1f 100644 --- a/internal/runners/initialize/init.go +++ b/internal/runners/initialize/init.go @@ -262,7 +262,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { return errs.Wrap(err, "Unable to determine Platform ID from %s", sysinfo.OS().String()) } - bp := bpModel.NewBuildPlannerModel(r.auth) + bp := bpModel.NewBuildPlannerModel(r.auth, r.svcModel) commitID, err := bp.CreateProject(&bpModel.CreateProjectParams{ Owner: namespace.Owner, Project: namespace.Project, @@ -281,14 +281,14 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { } if r.config.GetBool(constants.OptinBuildscriptsConfig) { - if err := buildscript_runbit.Initialize(proj.Dir(), r.auth); err != nil { + if err := buildscript_runbit.Initialize(proj.Dir(), r.auth, r.svcModel); err != nil { return errs.Wrap(err, "Unable to initialize buildscript") } } // Solve runtime solveSpinner := output.StartSpinner(r.out, locale.T("progress_solve"), constants.TerminalAnimationInterval) - bpm := bpModel.NewBuildPlannerModel(r.auth) + bpm := bpModel.NewBuildPlannerModel(r.auth, r.svcModel) commit, err := bpm.FetchCommit(commitID, r.prime.Project().Owner(), r.prime.Project().Name(), nil) if err != nil { solveSpinner.Stop(locale.T("progress_fail")) diff --git a/internal/runners/languages/languages.go b/internal/runners/languages/languages.go index c890c2a3e5..3ba17820b3 100644 --- a/internal/runners/languages/languages.go +++ b/internal/runners/languages/languages.go @@ -78,7 +78,7 @@ func (l *Languages) Run() error { } // Fetch commit and buildplan, which will give us access to ingredients, and ingredients can be languages.. - bpm := bpModel.NewBuildPlannerModel(l.auth) + bpm := bpModel.NewBuildPlannerModel(l.auth, l.svcModel) commit, err := bpm.FetchCommit(commitID, l.project.Owner(), l.project.Name(), nil) if err != nil { return errs.Wrap(err, "could not fetch commit") diff --git a/internal/runners/packages/import.go b/internal/runners/packages/import.go index ee2f2f3831..ef7f4436ea 100644 --- a/internal/runners/packages/import.go +++ b/internal/runners/packages/import.go @@ -115,7 +115,7 @@ func (i *Import) Run(params *ImportRunParams) (rerr error) { return errs.Wrap(err, "Could not import changeset") } - bp := buildplanner.NewBuildPlannerModel(auth) + bp := buildplanner.NewBuildPlannerModel(auth, i.prime.SvcModel()) bs, err := bp.GetBuildScript(localCommitId.String()) if err != nil { return locale.WrapError(err, "err_cannot_get_build_expression", "Could not get build expression") diff --git a/internal/runners/packages/list.go b/internal/runners/packages/list.go index 993858cb64..4c39904eb9 100644 --- a/internal/runners/packages/list.go +++ b/internal/runners/packages/list.go @@ -112,7 +112,7 @@ func (l *List) Run(params ListRunParams, nstype model.NamespaceType) error { // Fetch resolved artifacts list for showing full version numbers, if possible. var artifacts buildplan.Artifacts if l.project != nil && params.Project == "" { - bpm := bpModel.NewBuildPlannerModel(l.auth) + bpm := bpModel.NewBuildPlannerModel(l.auth, l.svcModel) commit, err := bpm.FetchCommit(*commitID, l.project.Owner(), l.project.Name(), nil) if err != nil { return errs.Wrap(err, "could not fetch commit") diff --git a/internal/runners/pull/pull.go b/internal/runners/pull/pull.go index e409b0264a..0e9ea15c92 100644 --- a/internal/runners/pull/pull.go +++ b/internal/runners/pull/pull.go @@ -155,7 +155,7 @@ func (p *Pull) Run(params *PullParams) (rerr error) { // If this call fails then we will try a recursive merge. strategy := types.MergeCommitStrategyFastForward - bp := buildplanner.NewBuildPlannerModel(p.auth) + bp := buildplanner.NewBuildPlannerModel(p.auth, p.svcModel) params := &buildplanner.MergeCommitParams{ Owner: remoteProject.Owner, Project: remoteProject.Project, @@ -233,7 +233,7 @@ func (p *Pull) performMerge(remoteCommit, localCommit strfmt.UUID, namespace *pr namespace.String(), branchName, localCommit.String(), remoteCommit.String()), ) - bp := buildplanner.NewBuildPlannerModel(p.auth) + bp := buildplanner.NewBuildPlannerModel(p.auth, p.svcModel) params := &buildplanner.MergeCommitParams{ Owner: namespace.Owner, Project: namespace.Project, @@ -269,7 +269,7 @@ func (p *Pull) mergeBuildScript(remoteCommit, localCommit strfmt.UUID) error { } // Get the local and remote build expressions to merge. - bp := buildplanner.NewBuildPlannerModel(p.auth) + bp := buildplanner.NewBuildPlannerModel(p.auth, p.svcModel) scriptB, err := bp.GetBuildScript(remoteCommit.String()) if err != nil { return errs.Wrap(err, "Unable to get buildexpression and time for remote commit") diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index 812d899173..df61f99e2f 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -29,6 +29,7 @@ type configGetter interface { } type Push struct { + prime primeable config configGetter out output.Outputer project *project.Project @@ -46,10 +47,11 @@ type primeable interface { primer.Configurer primer.Prompter primer.Auther + primer.SvcModeler } func NewPush(prime primeable) *Push { - return &Push{prime.Config(), prime.Output(), prime.Project(), prime.Prompt(), prime.Auth()} + return &Push{prime, prime.Config(), prime.Output(), prime.Project(), prime.Prompt(), prime.Auth()} } type intention uint16 @@ -153,7 +155,7 @@ func (r *Push) Run(params PushParams) (rerr error) { } } - bp := buildplanner.NewBuildPlannerModel(r.auth) + bp := buildplanner.NewBuildPlannerModel(r.auth, r.prime.SvcModel()) var branch *mono_models.Branch // the branch to write to as.yaml if it changed // Create remote project diff --git a/internal/runners/reset/reset.go b/internal/runners/reset/reset.go index 42682440b7..0e2b5c89fb 100644 --- a/internal/runners/reset/reset.go +++ b/internal/runners/reset/reset.go @@ -134,7 +134,7 @@ func (r *Reset) Run(params *Params) error { // Ensure the buildscript exists. Normally we should never do this, but reset is used for resetting from a corrupted // state, so it is appropriate. if r.cfg.GetBool(constants.OptinBuildscriptsConfig) { - if err := buildscript_runbit.Initialize(r.project.Dir(), r.auth); err != nil { + if err := buildscript_runbit.Initialize(r.project.Dir(), r.auth, r.svcModel); err != nil { return errs.Wrap(err, "Unable to initialize buildscript") } } diff --git a/internal/runners/revert/revert.go b/internal/runners/revert/revert.go index 6ac4641c91..60da9a2441 100644 --- a/internal/runners/revert/revert.go +++ b/internal/runners/revert/revert.go @@ -97,7 +97,7 @@ func (r *Revert) Run(params *Params) (rerr error) { } r.out.Notice(locale.Tr("operating_message", r.project.NamespaceString(), r.project.Dir())) - bp := buildplanner.NewBuildPlannerModel(r.auth) + bp := buildplanner.NewBuildPlannerModel(r.auth, r.prime.SvcModel()) targetCommitID := commitID // the commit to revert the contents of, or the commit to revert to revertParams := revertParams{ organization: r.project.Owner(), diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index 9730b39361..1f6dd89ce5 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -31,6 +31,7 @@ type primeable interface { primer.Auther primer.Projecter primer.Prompter + primer.SvcModeler } type Params struct { @@ -95,7 +96,7 @@ func (u *Upgrade) Run(params *Params) (rerr error) { return errs.Wrap(err, "Failed to get local commit") } - bpm := bpModel.NewBuildPlannerModel(u.prime.Auth()) + bpm := bpModel.NewBuildPlannerModel(u.prime.Auth(), u.prime.SvcModel()) localCommit, err := bpm.FetchCommit(localCommitID, proj.Owner(), proj.Name(), nil) if err != nil { return errs.Wrap(err, "Failed to fetch build result") diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 030e279c23..04ef1a8558 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -61,6 +61,18 @@ type Session struct { ExecutorExe string spawned []*SpawnedCmd ignoreLogErrors bool + cache keyCache +} + +type keyCache map[string]string + +func (k keyCache) GetCache(key string) (string, error) { + return k[key], nil +} + +func (k keyCache) SetCache(key, value string, _ time.Duration) error { + k[key] = value + return nil } var ( @@ -358,7 +370,7 @@ func (s *Session) PrepareProject(namespace, commitID string) { func (s *Session) PrepareProjectAndBuildScript(namespace, commitID string) { s.PrepareProject(namespace, commitID) - bp := buildplanner.NewBuildPlannerModel(nil) + bp := buildplanner.NewBuildPlannerModel(nil, s.cache) script, err := bp.GetBuildScript(commitID) require.NoError(s.T, err) b, err := script.Marshal() diff --git a/pkg/platform/model/buildplanner/buildplanner.go b/pkg/platform/model/buildplanner/buildplanner.go index 064144f014..4f6c653b91 100644 --- a/pkg/platform/model/buildplanner/buildplanner.go +++ b/pkg/platform/model/buildplanner/buildplanner.go @@ -19,7 +19,7 @@ type BuildPlanner struct { client *client } -func NewBuildPlannerModel(auth *authentication.Auth) *BuildPlanner { +func NewBuildPlannerModel(auth *authentication.Auth, cache cacher) *BuildPlanner { bpURL := api.GetServiceURL(api.ServiceBuildPlanner).String() logging.Debug("Using build planner at: %s", bpURL) From abb6c8e5f7184f4b13855392593af733ef912a92 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:52:09 -0700 Subject: [PATCH 147/440] Slight refactor of `state manifest` to take advantage of caching by dropping redundant call --- internal/runners/manifest/manifest.go | 67 +++++++++++++-------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/internal/runners/manifest/manifest.go b/internal/runners/manifest/manifest.go index 073182a6b2..7f7f7fa4d0 100644 --- a/internal/runners/manifest/manifest.go +++ b/internal/runners/manifest/manifest.go @@ -1,6 +1,8 @@ package manifest import ( + "time" + "github.com/ActiveState/cli/internal/analytics" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/constants" @@ -8,8 +10,10 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/profile" buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/runbits/rationalize" + runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" "github.com/ActiveState/cli/pkg/buildplan" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" @@ -56,6 +60,7 @@ func NewManifest(prime primeable) *Manifest { func (m *Manifest) Run() (rerr error) { defer rationalizeError(m.project, m.auth, &rerr) + defer profile.Measure("Manifest:Run", time.Now()) if m.project == nil { return rationalize.ErrNoProject @@ -63,15 +68,32 @@ func (m *Manifest) Run() (rerr error) { m.out.Notice(locale.Tl("manifest_operating_on_project", "Operating on project: [ACTIONABLE]{{.V0}}[/RESET], located at [ACTIONABLE]{{.V1}}[/RESET]\n", m.project.Namespace().String(), m.project.Dir())) - reqs, err := m.fetchRequirements() + commit, err := m.fetchCommit() if err != nil { - return errs.Wrap(err, "Could not fetch requirements") + return errs.Wrap(err, "Could not fetch commit") } - bpReqs, err := m.fetchBuildplanRequirements() + // Manually verify our buildscript is up to date; this normally happens during updates + if m.prime.Config().GetBool(constants.OptinBuildscriptsConfig) { + bs, err := buildscript_runbit.ScriptFromProject(m.project) + if err != nil { + return errs.Wrap(err, "Failed to get buildscript") + } + isClean, err := bs.Equals(commit.BuildScript()) + if err != nil { + return errs.Wrap(err, "Failed to compare buildscript") + } + if !isClean { + return runtime_runbit.ErrBuildScriptNeedsCommit + } + } + + // Collect requirements and what they resolved to + reqs, err := commit.BuildScript().Requirements() if err != nil { - return errs.Wrap(err, "Could not fetch artifacts") + return errs.Wrap(err, "Failed to get requirements") } + bpReqs := commit.BuildPlan().RequestedIngredients() vulns, err := m.fetchVulnerabilities(reqs, bpReqs) if err != nil { @@ -92,36 +114,9 @@ func (m *Manifest) Run() (rerr error) { return nil } -func (m *Manifest) fetchRequirements() ([]buildscript.Requirement, error) { - var script *buildscript.BuildScript - if m.cfg.GetBool(constants.OptinBuildscriptsConfig) { - var err error - script, err = buildscript_runbit.ScriptFromProject(m.project) - if err != nil { - return nil, errs.Wrap(err, "Could not get buildscript") - } - } else { - commitID, err := localcommit.Get(m.project.Dir()) - if err != nil { - return nil, errs.Wrap(err, "Could not get commit ID") - } - - bp := bpModel.NewBuildPlannerModel(m.auth) - script, err = bp.GetBuildScript(commitID.String()) - if err != nil { - return nil, errs.Wrap(err, "Could not get remote build expr and time") - } - } - - reqs, err := script.Requirements() - if err != nil { - return nil, errs.Wrap(err, "Could not get requirements") - } - - return reqs, nil -} +func (m *Manifest) fetchCommit() (*bpModel.Commit, error) { + defer profile.Measure("Manifest:fetchCommit", time.Now()) -func (m *Manifest) fetchBuildplanRequirements() (buildplan.Ingredients, error) { commitID, err := localcommit.Get(m.project.Dir()) if err != nil { return nil, errs.Wrap(err, "Failed to get local commit") @@ -129,15 +124,15 @@ func (m *Manifest) fetchBuildplanRequirements() (buildplan.Ingredients, error) { // Solve runtime solveSpinner := output.StartSpinner(m.out, locale.T("progress_solve"), constants.TerminalAnimationInterval) - bpm := bpModel.NewBuildPlannerModel(m.auth) + bpm := bpModel.NewBuildPlannerModel(m.auth, m.svcModel) commit, err := bpm.FetchCommit(commitID, m.project.Owner(), m.project.Name(), nil) if err != nil { solveSpinner.Stop(locale.T("progress_fail")) - return nil, errs.Wrap(err, "Failed to fetch build result") + return nil, errs.Wrap(err, "Failed to fetch commit") } solveSpinner.Stop(locale.T("progress_success")) - return commit.BuildPlan().RequestedIngredients(), nil + return commit, nil } func (m *Manifest) fetchVulnerabilities(reqs []buildscript.Requirement, bpReqs buildplan.Ingredients) (vulnerabilities, error) { From 3e54242909c9d24b8ac8e91ed486a98868748c6f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:52:43 -0700 Subject: [PATCH 148/440] Fix BuildResponse cannot unmarshal and re-marshal --- pkg/platform/api/buildplanner/response/build.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/platform/api/buildplanner/response/build.go b/pkg/platform/api/buildplanner/response/build.go index 5a32a87645..063cdaf7c3 100644 --- a/pkg/platform/api/buildplanner/response/build.go +++ b/pkg/platform/api/buildplanner/response/build.go @@ -15,12 +15,16 @@ type ArtifactResponse struct { } type BuildResponse struct { - Type string `json:"__typename"` - Artifacts []ArtifactResponse `json:"artifacts"` - Status string `json:"status"` *Error *PlanningError - RawMessage json.RawMessage + Type string `json:"__typename"` + Artifacts []ArtifactResponse `json:"artifacts"` + Status string `json:"status"` + RawMessage json.RawMessage `json:"rawMessage"` +} + +func (b *BuildResponse) MarshalJSON() ([]byte, error) { + return b.RawMessage.MarshalJSON() } // UnmarshalJSON lets us record both the raw json message as well as unmarshal the parts we care about From f0567371578ec8efcdb559122e921cde748c6ba6 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 20 Sep 2024 14:53:06 -0700 Subject: [PATCH 149/440] Implement caching of buildscripts/commits --- pkg/platform/model/buildplanner/build.go | 34 +++++++++++++++---- .../model/buildplanner/buildplanner.go | 24 +++++++++++++ .../model/buildplanner/buildscript.go | 27 +++++++++++++-- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index a87cffe152..8763ec6506 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -1,6 +1,7 @@ package buildplanner import ( + "encoding/json" "errors" "regexp" "strconv" @@ -53,16 +54,37 @@ func (c *client) Run(req gqlclient.Request, resp interface{}) error { return c.gqlClient.Run(req, resp) } +const fetchCommitCacheExpiry = time.Hour * 12 + func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, target *string) (*Commit, error) { - logging.Debug("FetchBuildResult, commitID: %s, owner: %s, project: %s", commitID, owner, project) + logging.Debug("FetchCommit, commitID: %s, owner: %s, project: %s", commitID, owner, project) resp := &response.ProjectCommitResponse{} - err := b.client.Run(request.ProjectCommit(commitID.String(), owner, project, target), resp) + + cacheKey := strings.Join([]string{"FetchCommit", commitID.String(), owner, project, ptr.From(target, "")}, "-") + respRaw, err := b.cache.GetCache(cacheKey) if err != nil { - err = processBuildPlannerError(err, "failed to fetch commit") - if !b.auth.Authenticated() { - err = errs.AddTips(err, locale.T("tip_private_project_auth")) + return nil, errs.Wrap(err, "failed to get cache") + } + if respRaw != "" { + if err := json.Unmarshal([]byte(respRaw), resp); err != nil { + return nil, errs.Wrap(err, "failed to unmarshal cache: %s", cacheKey) + } + } else { + err := b.client.Run(request.ProjectCommit(commitID.String(), owner, project, target), resp) + if err != nil { + err = processBuildPlannerError(err, "failed to fetch commit") + if !b.auth.Authenticated() { + err = errs.AddTips(err, locale.T("tip_private_project_auth")) + } + return nil, err + } + respBytes, err := json.Marshal(resp) + if err != nil { + return nil, errs.Wrap(err, "failed to marshal cache") + } + if err := b.cache.SetCache(cacheKey, string(respBytes), fetchCommitCacheExpiry); err != nil { + return nil, errs.Wrap(err, "failed to set cache") } - return nil, err } // The BuildPlanner will return a build plan with a status of diff --git a/pkg/platform/model/buildplanner/buildplanner.go b/pkg/platform/model/buildplanner/buildplanner.go index 4f6c653b91..72468b839b 100644 --- a/pkg/platform/model/buildplanner/buildplanner.go +++ b/pkg/platform/model/buildplanner/buildplanner.go @@ -1,6 +1,8 @@ package buildplanner import ( + "time" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/pkg/platform/api" @@ -17,6 +19,22 @@ type client struct { type BuildPlanner struct { auth *authentication.Auth client *client + cache cacher +} + +type cacher interface { + GetCache(key string) (string, error) + SetCache(key, value string, expiry time.Duration) error +} + +type VoidCacher struct{} + +func (v VoidCacher) GetCache(key string) (string, error) { + return "", nil +} + +func (v VoidCacher) SetCache(key, value string, expiry time.Duration) error { + return nil } func NewBuildPlannerModel(auth *authentication.Auth, cache cacher) *BuildPlanner { @@ -29,10 +47,16 @@ func NewBuildPlannerModel(auth *authentication.Auth, cache cacher) *BuildPlanner gqlClient.SetTokenProvider(auth) } + // To avoid error prone nil checks all over the place + if cache == nil { + cache = VoidCacher{} + } + return &BuildPlanner{ auth: auth, client: &client{ gqlClient: gqlClient, }, + cache: cache, } } diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index 7a29cc8852..afd48bbba5 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -1,6 +1,8 @@ package buildplanner import ( + "encoding/json" + "strings" "time" "github.com/ActiveState/cli/internal/errs" @@ -12,11 +14,30 @@ import ( ) func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript, error) { - logging.Debug("GetBuildExpression, commitID: %s", commitID) + logging.Debug("GetBuildScript, commitID: %s", commitID) resp := &bpResp.BuildExpressionResponse{} - err := b.client.Run(request.BuildExpression(commitID), resp) + + cacheKey := strings.Join([]string{"GetBuildScript", commitID}, "-") + respRaw, err := b.cache.GetCache(cacheKey) if err != nil { - return nil, processBuildPlannerError(err, "failed to fetch build expression") + return nil, errs.Wrap(err, "failed to get cache") + } + if respRaw != "" { + if err := json.Unmarshal([]byte(respRaw), resp); err != nil { + return nil, errs.Wrap(err, "failed to unmarshal cache: %s", cacheKey) + } + } else { + err := b.client.Run(request.BuildExpression(commitID), resp) + if err != nil { + return nil, processBuildPlannerError(err, "failed to fetch build expression") + } + respBytes, err := json.Marshal(resp) + if err != nil { + return nil, errs.Wrap(err, "failed to marshal cache") + } + if err := b.cache.SetCache(cacheKey, string(respBytes), fetchCommitCacheExpiry); err != nil { + return nil, errs.Wrap(err, "failed to set cache") + } } if resp.Commit == nil { From 8eabdcf39ab452fcbb5fc50406eb742a9df41dc3 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Sep 2024 11:22:28 -0400 Subject: [PATCH 150/440] Catch programming error. --- internal/runners/config/set.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/runners/config/set.go b/internal/runners/config/set.go index 5c2b0d4d44..9cf451d26a 100644 --- a/internal/runners/config/set.go +++ b/internal/runners/config/set.go @@ -56,7 +56,10 @@ func (s *Set) Run(params SetParams) error { return locale.WrapInputError(err, "Invalid integer value") } case configMediator.Enum: - enums := option.Default.(*configMediator.Enums) + enums, ok := option.Default.(*configMediator.Enums) + if !ok { + return errs.New("Programming error: config key '%s' was registered as an enum, but the default was not an enum", params.Key.String()) + } if !funk.Contains(enums.Options, params.Value) { return locale.NewInputError( "err_config_set_enum_invalid_value", "Invalid value '{{.V0}}': expected one of: {{.V1}}", From 7768e1b1fe1fb79d167bdb10e25e24e2514a08af Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Sep 2024 11:52:28 -0400 Subject: [PATCH 151/440] Fixed failing integration test. Apparently we don't mention ActiveState in the Python --version anymore. --- test/integration/prepare_int_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/prepare_int_test.go b/test/integration/prepare_int_test.go index 900e2ae7c5..d97be4f491 100644 --- a/test/integration/prepare_int_test.go +++ b/test/integration/prepare_int_test.go @@ -166,8 +166,9 @@ func (suite *PrepareIntegrationTestSuite) TestResetExecutors() { cp = ts.Spawn("activate") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) cp.SendLine("which python3") - cp.SendLine("python3 --version") + cp.SendLine("python3") cp.Expect("ActiveState") + cp.SendLine("exit()") // exit from Python interpreter cp.SendLine("exit") cp.ExpectExitCode(0) From cf96bd812587564aa33260a075eab668abdb2fc3 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Sep 2024 11:57:08 -0400 Subject: [PATCH 152/440] Increase test timeout. --- test/integration/import_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/import_int_test.go b/test/integration/import_int_test.go index 7fd0e0d9ea..ff7bc055d5 100644 --- a/test/integration/import_int_test.go +++ b/test/integration/import_int_test.go @@ -125,7 +125,7 @@ func (suite *ImportIntegrationTestSuite) TestImport() { cp.ExpectExitCode(0) cp = ts.Spawn("import", "requirements.txt") - cp.ExpectExitCode(0, termtest.OptExpectTimeout(30*time.Second)) + cp.ExpectExitCode(0, termtest.OptExpectTimeout(2*time.Minute)) // wait twice as long as a normal solve cp = ts.Spawn("packages") cp.Expect("coverage") From b80f157cd2bc6a61a19a06858626f3eb336dc69c Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Sep 2024 13:32:03 -0400 Subject: [PATCH 153/440] Ignore environment variables at load-time instead of at post-process time. --- pkg/runtime/internal/envdef/environment.go | 19 +++++++++++++++++++ pkg/runtime/runtime.go | 10 ---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/pkg/runtime/internal/envdef/environment.go b/pkg/runtime/internal/envdef/environment.go index 0d379dcef5..298e0fac5d 100644 --- a/pkg/runtime/internal/envdef/environment.go +++ b/pkg/runtime/internal/envdef/environment.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/osutils" "github.com/thoas/go-funk" @@ -125,6 +126,24 @@ func NewEnvironmentDefinition(fp string) (*EnvironmentDefinition, error) { if err != nil { return nil, errs.Wrap(err, "could not unmarshal environment definition file: %s", fp) } + + if ignores := os.Getenv(constants.IgnoreEnvEnvVarName); ignores != "" { + ignore := make(map[string]bool) + for _, name := range strings.Split(ignores, ",") { + ignore[name] = true + } + + // Remove any environment variables to ignore. + for i := 0; i < len(ed.Env); { + if _, exists := ignore[ed.Env[i].Name]; !exists { + i++ + continue + } + ed.Env[i] = ed.Env[len(ed.Env)-1] + ed.Env = ed.Env[:len(ed.Env)-1] + } + } + return ed, nil } diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 4caee1f0d9..1ce0c63fad 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -4,9 +4,7 @@ import ( "maps" "os" "path/filepath" - "strings" - "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/logging" @@ -159,14 +157,6 @@ func (r *Runtime) getEnv(inherit bool) (map[string]string, map[string]string, er return empty, empty, errs.Wrap(err, "Failed to get environment variables") } - if ignores := os.Getenv(constants.IgnoreEnvEnvVarName); ignores != "" { - for _, name := range strings.Split(ignores, ",") { - if _, exists := vars[name]; exists { - delete(vars, name) - } - } - } - executorsPath := ExecutorsPath(r.path) execVars := maps.Clone(vars) From 75fd51c4a7060453cf602c7b3b5113a5d211f094 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Sep 2024 14:41:09 -0400 Subject: [PATCH 154/440] Use `sliceutils.Filter()` for removing ignored env vars. --- pkg/runtime/internal/envdef/environment.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pkg/runtime/internal/envdef/environment.go b/pkg/runtime/internal/envdef/environment.go index 298e0fac5d..9cda4e2cbc 100644 --- a/pkg/runtime/internal/envdef/environment.go +++ b/pkg/runtime/internal/envdef/environment.go @@ -11,6 +11,7 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/osutils" + "github.com/ActiveState/cli/internal/sliceutils" "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/fileutils" @@ -134,14 +135,10 @@ func NewEnvironmentDefinition(fp string) (*EnvironmentDefinition, error) { } // Remove any environment variables to ignore. - for i := 0; i < len(ed.Env); { - if _, exists := ignore[ed.Env[i].Name]; !exists { - i++ - continue - } - ed.Env[i] = ed.Env[len(ed.Env)-1] - ed.Env = ed.Env[:len(ed.Env)-1] - } + ed.Env = sliceutils.Filter(ed.Env, func(e EnvironmentVariable) bool { + _, exists := ignore[e.Name] + return !exists + }) } return ed, nil From 8277978d83aa37b003f4612505e0e3635727da71 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 23 Sep 2024 15:01:07 -0700 Subject: [PATCH 155/440] Add test --- internal/testhelpers/e2e/session.go | 8 ++++++++ test/integration/manifest_int_test.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 04ef1a8558..a2e89e5c77 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -7,6 +7,7 @@ import ( "path/filepath" "regexp" "runtime" + "slices" "strings" "testing" "time" @@ -693,6 +694,13 @@ func (s *Session) LogFiles() []string { fmt.Printf("Error walking log dir: %v", err) } + // Sort by filename timestamp (filenames are `[executable]-[processid]-[timestamp].log`) + slices.SortFunc(result, func(a, b string) int { + aa := strings.Split(a, "-") + bb := strings.Split(b, "-") + return strings.Compare(bb[len(bb)-1], aa[len(aa)-1]) + }) + return result } diff --git a/test/integration/manifest_int_test.go b/test/integration/manifest_int_test.go index 89c9882a58..8a8e780100 100644 --- a/test/integration/manifest_int_test.go +++ b/test/integration/manifest_int_test.go @@ -3,6 +3,7 @@ package integration import ( "fmt" "path/filepath" + "regexp" "strings" "testing" @@ -37,6 +38,19 @@ func (suite *ManifestIntegrationTestSuite) TestManifest() { cp.Expect("auto → 5.9.0") cp.Expect("None detected") cp.ExpectExitCode(0) + + // Ensure that `state manifest` utilized the cache (checkout should've warmed it) + logFile := ts.LogFiles()[0] + log := string(fileutils.ReadFileUnsafe(logFile)) + matched := false + for _, line := range strings.Split(log, "\n") { + if strings.Contains(line, "GetCache FetchCommit-") { + suite.Require().Regexp(regexp.MustCompile(`FetchCommit-.*result size: [1-9]`), line) + matched = true + break + } + } + suite.Require().True(matched, "log file should contain a line with the FetchCommit call", log) } func (suite *ManifestIntegrationTestSuite) TestManifest_JSON() { From ff7c5690b8d152233a421a98824c74785afe0d4d Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 24 Sep 2024 09:45:57 -0400 Subject: [PATCH 156/440] Fixed failing integration tests. --- test/integration/pull_int_test.go | 2 +- test/integration/push_int_test.go | 12 ++++++------ test/integration/reset_int_test.go | 2 +- test/integration/revert_int_test.go | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/integration/pull_int_test.go b/test/integration/pull_int_test.go index 89d542c9b2..e2a7eea4a8 100644 --- a/test/integration/pull_int_test.go +++ b/test/integration/pull_int_test.go @@ -90,7 +90,7 @@ func (suite *PullIntegrationTestSuite) TestMergeBuildScript() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "shared/zlib") - cp.Expect("Package added", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) proj, err := project.FromPath(ts.Dirs.Work) diff --git a/test/integration/push_int_test.go b/test/integration/push_int_test.go index 591a249818..4904db239f 100644 --- a/test/integration/push_int_test.go +++ b/test/integration/push_int_test.go @@ -87,10 +87,10 @@ func (suite *PushIntegrationTestSuite) TestInitAndPush() { cp = ts.Spawn("install", suite.extraPackage) switch runtime.GOOS { case "darwin": - cp.ExpectRe("added|being built", termtest.OptExpectTimeout(60*time.Second)) // while cold storage is off + cp.ExpectRe("Added|being built", termtest.OptExpectTimeout(60*time.Second)) // while cold storage is off cp.Wait() default: - cp.Expect("added", termtest.OptExpectTimeout(60*time.Second)) + cp.Expect("Added", termtest.OptExpectTimeout(60*time.Second)) cp.ExpectExitCode(0) } @@ -129,10 +129,10 @@ func (suite *PushIntegrationTestSuite) TestPush_NoPermission_NewProject() { cp = ts.Spawn("install", suite.extraPackage) switch runtime.GOOS { case "darwin": - cp.ExpectRe("added|being built", termtest.OptExpectTimeout(60*time.Second)) // while cold storage is off + cp.ExpectRe("Added|being built", termtest.OptExpectTimeout(60*time.Second)) // while cold storage is off cp.Wait() default: - cp.Expect("added", termtest.OptExpectTimeout(60*time.Second)) + cp.Expect("Added", termtest.OptExpectTimeout(60*time.Second)) cp.ExpectExitCode(0) } @@ -192,10 +192,10 @@ func (suite *PushIntegrationTestSuite) TestCarlisle() { ) switch runtime.GOOS { case "darwin": - cp.ExpectRe("added|being built", e2e.RuntimeSourcingTimeoutOpt) // while cold storage is off + cp.ExpectRe("Added|being built", e2e.RuntimeSourcingTimeoutOpt) // while cold storage is off cp.Wait() default: - cp.Expect("added", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Added", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) } diff --git a/test/integration/reset_int_test.go b/test/integration/reset_int_test.go index aa30793878..e7cc862ea9 100644 --- a/test/integration/reset_int_test.go +++ b/test/integration/reset_int_test.go @@ -30,7 +30,7 @@ func (suite *ResetIntegrationTestSuite) TestReset() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "shared/zlib") - cp.Expect("Package added") + cp.Expect("Added") cp.ExpectExitCode(0) cp = ts.Spawn("history") diff --git a/test/integration/revert_int_test.go b/test/integration/revert_int_test.go index 7b619531af..15bc46617d 100644 --- a/test/integration/revert_int_test.go +++ b/test/integration/revert_int_test.go @@ -65,7 +65,7 @@ func (suite *RevertIntegrationTestSuite) TestRevertRemote() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "requests") - cp.Expect("Added: requests") + cp.Expect("Added: language/python/requests") cp.ExpectExitCode(0) cp = ts.Spawn("revert", "REMOTE", "--non-interactive") From 1a5592163a4a1ba38c4d054997b35a80be18e576 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 24 Sep 2024 09:27:37 -0700 Subject: [PATCH 157/440] Revert "Slight refactor of `state manifest` to take advantage of caching by dropping redundant call" This reverts commit abb6c8e5f7184f4b13855392593af733ef912a92. --- internal/runners/manifest/manifest.go | 67 ++++++++++++++------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/internal/runners/manifest/manifest.go b/internal/runners/manifest/manifest.go index b943b6590f..9d165bbef7 100644 --- a/internal/runners/manifest/manifest.go +++ b/internal/runners/manifest/manifest.go @@ -1,8 +1,6 @@ package manifest import ( - "time" - "github.com/ActiveState/cli/internal/analytics" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/constants" @@ -10,10 +8,8 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/profile" buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/runbits/rationalize" - runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" "github.com/ActiveState/cli/pkg/buildplan" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" @@ -64,7 +60,6 @@ func NewManifest(prime primeable) *Manifest { func (m *Manifest) Run(params Params) (rerr error) { defer rationalizeError(m.project, m.auth, &rerr) - defer profile.Measure("Manifest:Run", time.Now()) if m.project == nil { return rationalize.ErrNoProject @@ -72,32 +67,15 @@ func (m *Manifest) Run(params Params) (rerr error) { m.out.Notice(locale.Tl("manifest_operating_on_project", "Operating on project: [ACTIONABLE]{{.V0}}[/RESET], located at [ACTIONABLE]{{.V1}}[/RESET]\n", m.project.Namespace().String(), m.project.Dir())) - commit, err := m.fetchCommit() + reqs, err := m.fetchRequirements() if err != nil { - return errs.Wrap(err, "Could not fetch commit") + return errs.Wrap(err, "Could not fetch requirements") } - // Manually verify our buildscript is up to date; this normally happens during updates - if m.prime.Config().GetBool(constants.OptinBuildscriptsConfig) { - bs, err := buildscript_runbit.ScriptFromProject(m.project) - if err != nil { - return errs.Wrap(err, "Failed to get buildscript") - } - isClean, err := bs.Equals(commit.BuildScript()) - if err != nil { - return errs.Wrap(err, "Failed to compare buildscript") - } - if !isClean { - return runtime_runbit.ErrBuildScriptNeedsCommit - } - } - - // Collect requirements and what they resolved to - reqs, err := commit.BuildScript().Requirements() + bpReqs, err := m.fetchBuildplanRequirements() if err != nil { - return errs.Wrap(err, "Failed to get requirements") + return errs.Wrap(err, "Could not fetch artifacts") } - bpReqs := commit.BuildPlan().RequestedIngredients() vulns, err := m.fetchVulnerabilities(reqs, bpReqs) if err != nil { @@ -118,9 +96,36 @@ func (m *Manifest) Run(params Params) (rerr error) { return nil } -func (m *Manifest) fetchCommit() (*bpModel.Commit, error) { - defer profile.Measure("Manifest:fetchCommit", time.Now()) +func (m *Manifest) fetchRequirements() ([]buildscript.Requirement, error) { + var script *buildscript.BuildScript + if m.cfg.GetBool(constants.OptinBuildscriptsConfig) { + var err error + script, err = buildscript_runbit.ScriptFromProject(m.project) + if err != nil { + return nil, errs.Wrap(err, "Could not get buildscript") + } + } else { + commitID, err := localcommit.Get(m.project.Dir()) + if err != nil { + return nil, errs.Wrap(err, "Could not get commit ID") + } + + bp := bpModel.NewBuildPlannerModel(m.auth) + script, err = bp.GetBuildScript(commitID.String()) + if err != nil { + return nil, errs.Wrap(err, "Could not get remote build expr and time") + } + } + + reqs, err := script.Requirements() + if err != nil { + return nil, errs.Wrap(err, "Could not get requirements") + } + + return reqs, nil +} +func (m *Manifest) fetchBuildplanRequirements() (buildplan.Ingredients, error) { commitID, err := localcommit.Get(m.project.Dir()) if err != nil { return nil, errs.Wrap(err, "Failed to get local commit") @@ -128,15 +133,15 @@ func (m *Manifest) fetchCommit() (*bpModel.Commit, error) { // Solve runtime solveSpinner := output.StartSpinner(m.out, locale.T("progress_solve"), constants.TerminalAnimationInterval) - bpm := bpModel.NewBuildPlannerModel(m.auth, m.svcModel) + bpm := bpModel.NewBuildPlannerModel(m.auth) commit, err := bpm.FetchCommit(commitID, m.project.Owner(), m.project.Name(), nil) if err != nil { solveSpinner.Stop(locale.T("progress_fail")) - return nil, errs.Wrap(err, "Failed to fetch commit") + return nil, errs.Wrap(err, "Failed to fetch build result") } solveSpinner.Stop(locale.T("progress_success")) - return commit, nil + return commit.BuildPlan().RequestedIngredients(), nil } func (m *Manifest) fetchVulnerabilities(reqs []buildscript.Requirement, bpReqs buildplan.Ingredients) (vulnerabilities, error) { From 03c9a284e63e9db40ac61ce19ec71f551c5789aa Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 24 Sep 2024 09:28:04 -0700 Subject: [PATCH 158/440] Add svcmodel calls --- internal/runners/manifest/manifest.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/manifest/manifest.go b/internal/runners/manifest/manifest.go index 9d165bbef7..19765d7257 100644 --- a/internal/runners/manifest/manifest.go +++ b/internal/runners/manifest/manifest.go @@ -110,7 +110,7 @@ func (m *Manifest) fetchRequirements() ([]buildscript.Requirement, error) { return nil, errs.Wrap(err, "Could not get commit ID") } - bp := bpModel.NewBuildPlannerModel(m.auth) + bp := bpModel.NewBuildPlannerModel(m.auth, m.svcModel) script, err = bp.GetBuildScript(commitID.String()) if err != nil { return nil, errs.Wrap(err, "Could not get remote build expr and time") @@ -133,7 +133,7 @@ func (m *Manifest) fetchBuildplanRequirements() (buildplan.Ingredients, error) { // Solve runtime solveSpinner := output.StartSpinner(m.out, locale.T("progress_solve"), constants.TerminalAnimationInterval) - bpm := bpModel.NewBuildPlannerModel(m.auth) + bpm := bpModel.NewBuildPlannerModel(m.auth, m.svcModel) commit, err := bpm.FetchCommit(commitID, m.project.Owner(), m.project.Name(), nil) if err != nil { solveSpinner.Stop(locale.T("progress_fail")) From 34c04216b7e70560fac2506ea4169c3ce0453c2d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 24 Sep 2024 11:23:16 -0700 Subject: [PATCH 159/440] Drop debugging code and redundant code caused by bad merge --- pkg/buildplan/buildplan.go | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/pkg/buildplan/buildplan.go b/pkg/buildplan/buildplan.go index a4ee6ca621..83f0ee353e 100644 --- a/pkg/buildplan/buildplan.go +++ b/pkg/buildplan/buildplan.go @@ -2,11 +2,8 @@ package buildplan import ( "encoding/json" - "fmt" - "time" "sort" - "github.com/ActiveState/cli/internal/fileutils" "github.com/go-openapi/strfmt" "github.com/ActiveState/cli/internal/errs" @@ -36,33 +33,6 @@ func Unmarshal(data []byte) (*BuildPlan, error) { b.sanitize() - // Sort buildplan slices to ensure consistency, because the API does not guarantee a consistent ordering - sort.Slice(b.raw.Sources, func(i, j int) bool { return b.raw.Sources[i].NodeID < b.raw.Sources[j].NodeID }) - sort.Slice(b.raw.Steps, func(i, j int) bool { return b.raw.Steps[i].StepID < b.raw.Steps[j].StepID }) - sort.Slice(b.raw.Artifacts, func(i, j int) bool { return b.raw.Artifacts[i].NodeID < b.raw.Artifacts[j].NodeID }) - sort.Slice(b.raw.Terminals, func(i, j int) bool { return b.raw.Terminals[i].Tag < b.raw.Terminals[j].Tag }) - sort.Slice(b.raw.ResolvedRequirements, func(i, j int) bool { - return b.raw.ResolvedRequirements[i].Source < b.raw.ResolvedRequirements[j].Source - }) - for _, t := range b.raw.Terminals { - sort.Slice(t.NodeIDs, func(i, j int) bool { return t.NodeIDs[i] < t.NodeIDs[j] }) - } - for _, a := range b.raw.Artifacts { - sort.Slice(a.RuntimeDependencies, func(i, j int) bool { return a.RuntimeDependencies[i] < a.RuntimeDependencies[j] }) - } - for _, step := range b.raw.Steps { - sort.Slice(step.Inputs, func(i, j int) bool { return step.Inputs[i].Tag < step.Inputs[j].Tag }) - sort.Slice(step.Outputs, func(i, j int) bool { return step.Outputs[i] < step.Outputs[j] }) - for _, input := range step.Inputs { - sort.Slice(input.NodeIDs, func(i, j int) bool { return input.NodeIDs[i] < input.NodeIDs[j] }) - } - } - - v, _ := b.Marshal() - vs := string(v) - _ = vs - fileutils.WriteFile(fmt.Sprintf("/tmp/buildplan-%d.json", time.Now().Unix()), v) - if err := b.hydrate(); err != nil { return nil, errs.Wrap(err, "error hydrating build plan") } From 0f4e5e73efbde8f1d9728ba37cfd5b6467ad64b2 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 24 Sep 2024 11:36:11 -0700 Subject: [PATCH 160/440] Add language to script --- internal/scriptrun/test/integration/scriptrun_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/scriptrun/test/integration/scriptrun_test.go b/internal/scriptrun/test/integration/scriptrun_test.go index 630e243711..b93b25cea8 100644 --- a/internal/scriptrun/test/integration/scriptrun_test.go +++ b/internal/scriptrun/test/integration/scriptrun_test.go @@ -353,6 +353,7 @@ project: "https://platform.activestate.com/ActiveState/project" scripts: - name: %s standalone: true + language: batch value: | echo "ARGS|%%1|%%2|%%3|%%4|"`, cmdName) } @@ -363,15 +364,12 @@ scripts: } func captureExecCommand(t *testing.T, tmplCmdName, cmdName string, cmdArgs []string) (string, error) { - auth, err := authentication.LegacyGet() require.NoError(t, err) pjfile := setupProjectWithScriptsExpectingArgs(t, tmplCmdName) - proj, err := project.New(pjfile, nil) require.NoError(t, err) - cfg, err := config.New() require.NoError(t, err) defer func() { require.NoError(t, cfg.Close()) }() @@ -384,7 +382,6 @@ func captureExecCommand(t *testing.T, tmplCmdName, cmdName string, cmdArgs []str } }) require.NoError(t, outErr, "error capturing stdout") - return outStr, err } From bc4f65cab1848da3041d9967b715be0d038f33b5 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 24 Sep 2024 11:41:54 -0700 Subject: [PATCH 161/440] Delete requirements.go - accidentally added back through bad merge --- .../runtime/requirements/requirements.go | 755 ------------------ 1 file changed, 755 deletions(-) delete mode 100644 internal/runbits/runtime/requirements/requirements.go diff --git a/internal/runbits/runtime/requirements/requirements.go b/internal/runbits/runtime/requirements/requirements.go deleted file mode 100644 index 69bd71baf6..0000000000 --- a/internal/runbits/runtime/requirements/requirements.go +++ /dev/null @@ -1,755 +0,0 @@ -package requirements - -import ( - "errors" - "fmt" - "regexp" - "strconv" - "strings" - "time" - - "github.com/ActiveState/cli/internal/analytics" - anaConsts "github.com/ActiveState/cli/internal/analytics/constants" - "github.com/ActiveState/cli/internal/captain" - "github.com/ActiveState/cli/internal/config" - "github.com/ActiveState/cli/internal/constants" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/multilog" - "github.com/ActiveState/cli/internal/output" - "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/prompt" - "github.com/ActiveState/cli/internal/rtutils/ptr" - buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" - "github.com/ActiveState/cli/internal/runbits/cves" - "github.com/ActiveState/cli/internal/runbits/dependencies" - "github.com/ActiveState/cli/internal/runbits/rationalize" - runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" - "github.com/ActiveState/cli/internal/runbits/runtime/trigger" - "github.com/ActiveState/cli/pkg/buildscript" - "github.com/ActiveState/cli/pkg/localcommit" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - medmodel "github.com/ActiveState/cli/pkg/platform/api/mediator/model" - "github.com/ActiveState/cli/pkg/platform/authentication" - "github.com/ActiveState/cli/pkg/platform/model" - bpModel "github.com/ActiveState/cli/pkg/platform/model/buildplanner" - "github.com/ActiveState/cli/pkg/project" - "github.com/ActiveState/cli/pkg/runtime" - "github.com/ActiveState/cli/pkg/sysinfo" - "github.com/go-openapi/strfmt" - "github.com/thoas/go-funk" -) - -type PackageVersion struct { - captain.NameVersionValue -} - -func (pv *PackageVersion) Set(arg string) error { - err := pv.NameVersionValue.Set(arg) - if err != nil { - return locale.WrapInputError(err, "err_package_format", "The package and version provided is not formatting correctly. It must be in the form of @") - } - return nil -} - -type RequirementOperation struct { - prime primeable - // The remainder is redundant with the above. Refactoring this will follow in a later story so as not to blow - // up the one that necessitates adding the primer at this level. - // https://activestatef.atlassian.net/browse/DX-2869 - Output output.Outputer - Prompt prompt.Prompter - Project *project.Project - Auth *authentication.Auth - Config *config.Instance - Analytics analytics.Dispatcher - SvcModel *model.SvcModel -} - -type primeable interface { - primer.Outputer - primer.Prompter - primer.Projecter - primer.Auther - primer.Configurer - primer.Analyticer - primer.SvcModeler -} - -func NewRequirementOperation(prime primeable) *RequirementOperation { - return &RequirementOperation{ - prime, - prime.Output(), - prime.Prompt(), - prime.Project(), - prime.Auth(), - prime.Config(), - prime.Analytics(), - prime.SvcModel(), - } -} - -const latestVersion = "latest" - -type ErrNoMatches struct { - *locale.LocalizedError - Query string - Alternatives *string -} - -var errNoRequirements = errs.New("No requirements were provided") - -var errInitialNoRequirement = errs.New("Could not find compatible requirement for initial commit") - -var errNoLanguage = errs.New("No language") - -var versionRe = regexp.MustCompile(`^\d(\.\d+)*$`) - -// Requirement represents a package, language or platform requirement -// For now, be aware that you should never provide BOTH ns AND nsType, one or the other should always be nil, but never both. -// The refactor should clean this up. -type Requirement struct { - Name string - Version string - Revision *int - BitWidth int // Only needed for platform requirements - Namespace *model.Namespace - NamespaceType *model.NamespaceType - Operation types.Operation - - // The following fields are set during execution - langName string - langVersion string - validatePkg bool - appendVersionWildcard bool - originalRequirementName string - versionRequirements []types.VersionRequirement -} - -// ExecuteRequirementOperation executes the operation on the requirement -// This has become quite unwieldy, and is ripe for a refactor - https://activestatef.atlassian.net/browse/DX-1897 -func (r *RequirementOperation) ExecuteRequirementOperation(ts *time.Time, requirements ...*Requirement) (rerr error) { - defer r.rationalizeError(&rerr) - - if len(requirements) == 0 { - return errNoRequirements - } - - out := r.Output - var pg *output.Spinner - defer func() { - if pg != nil { - // This is a bit awkward, but it would be even more awkward to manually address this for every error condition - pg.Stop(locale.T("progress_fail")) - } - }() - - if r.Project == nil { - return rationalize.ErrNoProject - } - if r.Project.IsHeadless() { - return rationalize.ErrHeadless - } - out.Notice(locale.Tr("operating_message", r.Project.NamespaceString(), r.Project.Dir())) - - if err := r.resolveNamespaces(ts, requirements...); err != nil { - return errs.Wrap(err, "Could not resolve namespaces") - } - - if err := r.validatePackages(requirements...); err != nil { - return errs.Wrap(err, "Could not validate packages") - } - - parentCommitID, err := localcommit.Get(r.Project.Dir()) - if err != nil { - return errs.Wrap(err, "Unable to get local commit") - } - hasParentCommit := parentCommitID != "" - - pg = output.StartSpinner(r.Output, locale.T("progress_solve_preruntime"), constants.TerminalAnimationInterval) - - if err := r.checkForUpdate(parentCommitID, requirements...); err != nil { - return locale.WrapError(err, "err_check_for_update", "Could not check for requirements updates") - } - - if !hasParentCommit { - // Use first requirement to extract language for initial commit - var requirement *Requirement - for _, r := range requirements { - if r.Namespace.Type() == model.NamespacePackage || r.Namespace.Type() == model.NamespaceBundle { - requirement = r - break - } - } - - if requirement == nil { - return errInitialNoRequirement - } - - languageFromNs := model.LanguageFromNamespace(requirement.Namespace.String()) - parentCommitID, err = model.CommitInitial(sysinfo.OS().String(), languageFromNs, requirement.langVersion, r.Auth) - if err != nil { - return locale.WrapError(err, "err_install_no_project_commit", "Could not create initial commit for new project") - } - } - - if err := r.resolveRequirements(requirements...); err != nil { - return locale.WrapError(err, "err_resolve_requirements", "Could not resolve one or more requirements") - } - - bp := bpModel.NewBuildPlannerModel(r.Auth, r.SvcModel) - script, err := r.prepareBuildScript(bp, parentCommitID, requirements, ts) - if err != nil { - return errs.Wrap(err, "Could not prepare build script") - } - - params := bpModel.StageCommitParams{ - Owner: r.Project.Owner(), - Project: r.Project.Name(), - ParentCommit: string(parentCommitID), - Description: commitMessage(requirements...), - Script: script, - } - - // Solve runtime - commit, err := bp.StageCommit(params) - if err != nil { - return errs.Wrap(err, "Could not stage commit") - } - - ns := requirements[0].Namespace - var trig trigger.Trigger - switch ns.Type() { - case model.NamespaceLanguage: - trig = trigger.TriggerLanguage - case model.NamespacePlatform: - trig = trigger.TriggerPlatform - default: - trig = trigger.TriggerPackage - } - - oldCommit, err := bp.FetchCommit(parentCommitID, r.Project.Owner(), r.Project.Name(), nil) - if err != nil { - return errs.Wrap(err, "Failed to fetch old build result") - } - - pg.Stop(locale.T("progress_success")) - pg = nil - - dependencies.OutputChangeSummary(r.prime.Output(), commit.BuildPlan(), oldCommit.BuildPlan()) - - // Report CVEs - if err := cves.NewCveReport(r.prime).Report(commit.BuildPlan(), oldCommit.BuildPlan()); err != nil { - return errs.Wrap(err, "Could not report CVEs") - } - - // Start runtime update UI - if !r.Config.GetBool(constants.AsyncRuntimeConfig) { - out.Notice("") - - // refresh or install runtime - _, err = runtime_runbit.Update(r.prime, trig, - runtime_runbit.WithCommit(commit), - runtime_runbit.WithoutBuildscriptValidation(), - ) - if err != nil { - if !IsBuildError(err) { - // If the error is not a build error we want to retain the changes - if err2 := r.updateCommitID(commit.CommitID); err2 != nil { - return errs.Pack(err, locale.WrapError(err2, "err_package_update_commit_id")) - } - } - return errs.Wrap(err, "Failed to refresh runtime") - } - } - - if err := r.updateCommitID(commit.CommitID); err != nil { - return locale.WrapError(err, "err_package_update_commit_id") - } - - if !hasParentCommit { - out.Notice(locale.Tr("install_initial_success", r.Project.Source().Path())) - } - - // Print the result - r.outputResults(requirements...) - - out.Notice(locale.T("operation_success_local")) - - return nil -} - -func (r *RequirementOperation) prepareBuildScript(bp *bpModel.BuildPlanner, parentCommit strfmt.UUID, requirements []*Requirement, ts *time.Time) (*buildscript.BuildScript, error) { - script, err := bp.GetBuildScript(string(parentCommit)) - if err != nil { - return nil, errs.Wrap(err, "Failed to get build expression") - } - - if ts != nil { - script.SetAtTime(*ts) - } else { - // If no atTime was provided then we need to ensure that the atTime in the script is updated to use - // the most recent, which is either the current value or the platform latest. - latest, err := model.FetchLatestTimeStamp(r.Auth) - if err != nil { - return nil, errs.Wrap(err, "Unable to fetch latest Platform timestamp") - } - atTime := script.AtTime() - if atTime == nil || latest.After(*atTime) { - script.SetAtTime(latest) - } - } - - for _, req := range requirements { - if req.Namespace.String() == types.NamespacePlatform { - err = script.UpdatePlatform(req.Operation, strfmt.UUID(req.Name)) - if err != nil { - return nil, errs.Wrap(err, "Failed to update build expression with platform") - } - } else { - requirement := types.Requirement{ - Namespace: req.Namespace.String(), - Name: req.Name, - VersionRequirement: req.versionRequirements, - Revision: req.Revision, - } - - err = script.UpdateRequirement(req.Operation, requirement) - if err != nil { - return nil, errs.Wrap(err, "Failed to update build expression with requirement") - } - } - } - - return script, nil -} - -type ResolveNamespaceError struct { - Name string -} - -func (e ResolveNamespaceError) Error() string { - return "unable to resolve namespace" -} - -func (r *RequirementOperation) resolveNamespaces(ts *time.Time, requirements ...*Requirement) error { - for _, requirement := range requirements { - if err := r.resolveNamespace(ts, requirement); err != nil { - if err != errNoLanguage { - err = errs.Pack(err, &ResolveNamespaceError{requirement.Name}) - } - return errs.Wrap(err, "Unable to resolve namespace") - } - } - return nil -} - -func (r *RequirementOperation) resolveNamespace(ts *time.Time, requirement *Requirement) error { - requirement.langName = "undetermined" - - if requirement.NamespaceType != nil { - switch *requirement.NamespaceType { - case model.NamespacePackage, model.NamespaceBundle: - commitID, err := localcommit.Get(r.Project.Dir()) - if err != nil { - return errs.Wrap(err, "Unable to get local commit") - } - - language, err := model.LanguageByCommit(commitID, r.Auth) - if err != nil { - logging.Debug("Could not get language from project: %v", err) - } - if language.Name == "" { - return errNoLanguage - } - requirement.langName = language.Name - requirement.Namespace = ptr.To(model.NewNamespacePkgOrBundle(requirement.langName, *requirement.NamespaceType)) - case model.NamespaceLanguage: - requirement.Namespace = ptr.To(model.NewNamespaceLanguage()) - case model.NamespacePlatform: - requirement.Namespace = ptr.To(model.NewNamespacePlatform()) - } - } - - ns := requirement.Namespace - nsType := requirement.NamespaceType - requirement.validatePkg = requirement.Operation == types.OperationAdded && ns != nil && (ns.Type() == model.NamespacePackage || ns.Type() == model.NamespaceBundle || ns.Type() == model.NamespaceLanguage) - if (ns == nil || !ns.IsValid()) && nsType != nil && (*nsType == model.NamespacePackage || *nsType == model.NamespaceBundle) { - pg := output.StartSpinner(r.Output, locale.Tr("progress_pkg_nolang", requirement.Name), constants.TerminalAnimationInterval) - - supported, err := model.FetchSupportedLanguages(sysinfo.OS().String()) - if err != nil { - return errs.Wrap(err, "Failed to retrieve the list of supported languages") - } - - var nsv model.Namespace - var supportedLang *medmodel.SupportedLanguage - requirement.Name, nsv, supportedLang, err = resolvePkgAndNamespace(r.Prompt, requirement.Name, *requirement.NamespaceType, supported, ts, r.Auth) - if err != nil { - return errs.Wrap(err, "Could not resolve pkg and namespace") - } - requirement.Namespace = &nsv - requirement.langVersion = supportedLang.DefaultVersion - requirement.langName = supportedLang.Name - - requirement.validatePkg = false - - pg.Stop(locale.T("progress_found")) - } - - if requirement.Namespace == nil { - return locale.NewError("err_package_invalid_namespace_detected", "No valid namespace could be detected") - } - - return nil -} - -func (r *RequirementOperation) validatePackages(requirements ...*Requirement) error { - var requirementsToValidate []*Requirement - for _, requirement := range requirements { - if !requirement.validatePkg { - continue - } - requirementsToValidate = append(requirementsToValidate, requirement) - } - - if len(requirementsToValidate) == 0 { - return nil - } - - pg := output.StartSpinner(r.Output, locale.Tr("progress_search", strings.Join(requirementNames(requirementsToValidate...), ", ")), constants.TerminalAnimationInterval) - for _, requirement := range requirementsToValidate { - if err := r.validatePackage(requirement); err != nil { - return errs.Wrap(err, "Could not validate package") - } - } - pg.Stop(locale.T("progress_found")) - - return nil -} - -func (r *RequirementOperation) validatePackage(requirement *Requirement) error { - if strings.ToLower(requirement.Version) == latestVersion { - requirement.Version = "" - } - - requirement.originalRequirementName = requirement.Name - normalized, err := model.FetchNormalizedName(*requirement.Namespace, requirement.Name, r.Auth) - if err != nil { - multilog.Error("Failed to normalize '%s': %v", requirement.Name, err) - } - - packages, err := model.SearchIngredientsStrict(requirement.Namespace.String(), normalized, false, false, nil, r.Auth) // ideally case-sensitive would be true (PB-4371) - if err != nil { - return locale.WrapError(err, "package_err_cannot_obtain_search_results") - } - - if len(packages) == 0 { - suggestions, err := getSuggestions(*requirement.Namespace, requirement.Name, r.Auth) - if err != nil { - multilog.Error("Failed to retrieve suggestions: %v", err) - } - - if len(suggestions) == 0 { - return &ErrNoMatches{ - locale.WrapExternalError(err, "package_ingredient_alternatives_nosuggest", "", requirement.Name), - requirement.Name, nil} - } - - return &ErrNoMatches{ - locale.WrapExternalError(err, "package_ingredient_alternatives", "", requirement.Name, strings.Join(suggestions, "\n")), - requirement.Name, ptr.To(strings.Join(suggestions, "\n"))} - } - - if normalized != "" && normalized != requirement.Name { - requirement.Name = normalized - } - - // If a bare version number was given, and if it is a partial version number (e.g. requests@2), - // we'll want to ultimately append a '.x' suffix. - if versionRe.MatchString(requirement.Version) { - for _, knownVersion := range packages[0].Versions { - if knownVersion.Version == requirement.Version { - break - } else if strings.HasPrefix(knownVersion.Version, requirement.Version) { - requirement.appendVersionWildcard = true - } - } - } - - return nil -} - -func (r *RequirementOperation) checkForUpdate(parentCommitID strfmt.UUID, requirements ...*Requirement) error { - for _, requirement := range requirements { - // Check if this is an addition or an update - if requirement.Operation == types.OperationAdded && parentCommitID != "" { - req, err := model.GetRequirement(parentCommitID, *requirement.Namespace, requirement.Name, r.Auth) - if err != nil { - return errs.Wrap(err, "Could not get requirement") - } - if req != nil { - requirement.Operation = types.OperationUpdated - } - } - - r.Analytics.EventWithLabel( - anaConsts.CatPackageOp, fmt.Sprintf("%s-%s", requirement.Operation, requirement.langName), requirement.Name, - ) - } - - return nil -} - -func (r *RequirementOperation) resolveRequirements(requirements ...*Requirement) error { - for _, requirement := range requirements { - if err := r.resolveRequirement(requirement); err != nil { - return errs.Wrap(err, "Could not resolve requirement") - } - } - return nil -} - -func (r *RequirementOperation) resolveRequirement(requirement *Requirement) error { - var err error - requirement.Name, requirement.Version, err = model.ResolveRequirementNameAndVersion(requirement.Name, requirement.Version, requirement.BitWidth, *requirement.Namespace, r.Auth) - if err != nil { - return errs.Wrap(err, "Could not resolve requirement name and version") - } - - versionString := requirement.Version - if requirement.appendVersionWildcard { - versionString += ".x" - } - - requirement.versionRequirements, err = bpModel.VersionStringToRequirements(versionString) - if err != nil { - return errs.Wrap(err, "Could not process version string into requirements") - } - - return nil -} - -func (r *RequirementOperation) updateCommitID(commitID strfmt.UUID) error { - if err := localcommit.Set(r.Project.Dir(), commitID.String()); err != nil { - return locale.WrapError(err, "err_package_update_commit_id") - } - - if r.Config.GetBool(constants.OptinBuildscriptsConfig) { - bp := bpModel.NewBuildPlannerModel(r.Auth, r.SvcModel) - script, err := bp.GetBuildScript(commitID.String()) - if err != nil { - return errs.Wrap(err, "Could not get remote build expr and time") - } - - err = buildscript_runbit.Update(r.Project, script) - if err != nil { - return locale.WrapError(err, "err_update_build_script") - } - } - - return nil -} - -func (r *RequirementOperation) outputResults(requirements ...*Requirement) { - for _, requirement := range requirements { - r.outputResult(requirement) - } -} - -func (r *RequirementOperation) outputResult(requirement *Requirement) { - // Print the result - message := locale.Tr(fmt.Sprintf("%s_version_%s", requirement.Namespace.Type(), requirement.Operation), requirement.Name, requirement.Version) - if requirement.Version == "" { - message = locale.Tr(fmt.Sprintf("%s_%s", requirement.Namespace.Type(), requirement.Operation), requirement.Name) - } - - r.Output.Print(output.Prepare( - message, - &struct { - Name string `json:"name"` - Version string `json:"version,omitempty"` - Type string `json:"type"` - Operation string `json:"operation"` - }{ - requirement.Name, - requirement.Version, - requirement.Namespace.Type().String(), - requirement.Operation.String(), - })) - - if requirement.originalRequirementName != requirement.Name && requirement.Operation != types.OperationRemoved { - r.Output.Notice(locale.Tl("package_version_differs", - "Note: the actual package name ({{.V0}}) is different from the requested package name ({{.V1}})", - requirement.Name, requirement.originalRequirementName)) - } -} - -func supportedLanguageByName(supported []medmodel.SupportedLanguage, langName string) medmodel.SupportedLanguage { - return funk.Find(supported, func(l medmodel.SupportedLanguage) bool { return l.Name == langName }).(medmodel.SupportedLanguage) -} - -func resolvePkgAndNamespace(prompt prompt.Prompter, packageName string, nsType model.NamespaceType, supported []medmodel.SupportedLanguage, ts *time.Time, auth *authentication.Auth) (string, model.Namespace, *medmodel.SupportedLanguage, error) { - ns := model.NewBlankNamespace() - - // Find ingredients that match the input query - ingredients, err := model.SearchIngredientsStrict("", packageName, false, false, ts, auth) - if err != nil { - return "", ns, nil, locale.WrapError(err, "err_pkgop_search_err", "Failed to check for ingredients.") - } - - ingredients, err = model.FilterSupportedIngredients(supported, ingredients) - if err != nil { - return "", ns, nil, errs.Wrap(err, "Failed to filter out unsupported packages") - } - - choices := []string{} - values := map[string][]string{} - for _, i := range ingredients { - language := model.LanguageFromNamespace(*i.Ingredient.PrimaryNamespace) - - // Generate ingredient choices to present to the user - name := fmt.Sprintf("%s (%s)", *i.Ingredient.Name, language) - choices = append(choices, name) - values[name] = []string{*i.Ingredient.Name, language} - } - - if len(choices) == 0 { - return "", ns, nil, locale.WrapExternalError(err, "package_ingredient_alternatives_nolang", "", packageName) - } - - // If we only have one ingredient match we're done; return it. - if len(choices) == 1 { - language := values[choices[0]][1] - supportedLang := supportedLanguageByName(supported, language) - return values[choices[0]][0], model.NewNamespacePkgOrBundle(language, nsType), &supportedLang, nil - } - - // Prompt the user with the ingredient choices - choice, err := prompt.Select( - locale.Tl("prompt_pkgop_ingredient", "Multiple Matches"), - locale.Tl("prompt_pkgop_ingredient_msg", "Your query has multiple matches. Which one would you like to use?"), - choices, &choices[0], - ) - if err != nil { - return "", ns, nil, locale.WrapError(err, "err_pkgop_select", "Need a selection.") - } - - // Return the user selected ingredient - language := values[choice][1] - supportedLang := supportedLanguageByName(supported, language) - return values[choice][0], model.NewNamespacePkgOrBundle(language, nsType), &supportedLang, nil -} - -func getSuggestions(ns model.Namespace, name string, auth *authentication.Auth) ([]string, error) { - results, err := model.SearchIngredients(ns.String(), name, false, nil, auth) - if err != nil { - return []string{}, locale.WrapError(err, "package_ingredient_err_search", "Failed to resolve ingredient named: {{.V0}}", name) - } - - maxResults := 5 - if len(results) > maxResults { - results = results[:maxResults] - } - - suggestions := make([]string, 0, maxResults+1) - for _, result := range results { - suggestions = append(suggestions, fmt.Sprintf(" - %s", *result.Ingredient.Name)) - } - - return suggestions, nil -} - -func commitMessage(requirements ...*Requirement) string { - switch len(requirements) { - case 0: - return "" - case 1: - return requirementCommitMessage(requirements[0]) - default: - return commitMessageMultiple(requirements...) - } -} - -func requirementCommitMessage(req *Requirement) string { - switch req.Namespace.Type() { - case model.NamespaceLanguage: - return languageCommitMessage(req.Operation, req.Name, req.Version) - case model.NamespacePlatform: - return platformCommitMessage(req.Operation, req.Name, req.Version, req.BitWidth) - case model.NamespacePackage, model.NamespaceBundle: - return packageCommitMessage(req.Operation, req.Name, req.Version) - } - return "" -} - -func languageCommitMessage(op types.Operation, name, version string) string { - var msgL10nKey string - switch op { - case types.OperationAdded: - msgL10nKey = "commit_message_added_language" - case types.OperationUpdated: - msgL10nKey = "commit_message_updated_language" - case types.OperationRemoved: - msgL10nKey = "commit_message_removed_language" - } - - return locale.Tr(msgL10nKey, name, version) -} - -func platformCommitMessage(op types.Operation, name, version string, word int) string { - var msgL10nKey string - switch op { - case types.OperationAdded: - msgL10nKey = "commit_message_added_platform" - case types.OperationUpdated: - msgL10nKey = "commit_message_updated_platform" - case types.OperationRemoved: - msgL10nKey = "commit_message_removed_platform" - } - - return locale.Tr(msgL10nKey, name, strconv.Itoa(word), version) -} - -func packageCommitMessage(op types.Operation, name, version string) string { - var msgL10nKey string - switch op { - case types.OperationAdded: - msgL10nKey = "commit_message_added_package" - case types.OperationUpdated: - msgL10nKey = "commit_message_updated_package" - case types.OperationRemoved: - msgL10nKey = "commit_message_removed_package" - } - - if version == "" { - version = locale.Tl("package_version_auto", "auto") - } - return locale.Tr(msgL10nKey, name, version) -} - -func commitMessageMultiple(requirements ...*Requirement) string { - var commitDetails []string - for _, req := range requirements { - commitDetails = append(commitDetails, requirementCommitMessage(req)) - } - - return locale.Tl("commit_message_multiple", "Committing changes to multiple requirements: {{.V0}}", strings.Join(commitDetails, ", ")) -} - -func requirementNames(requirements ...*Requirement) []string { - var names []string - for _, requirement := range requirements { - names = append(names, requirement.Name) - } - return names -} - -func IsBuildError(err error) bool { - var errBuild *runtime.BuildError - var errBuildPlanner *response.BuildPlannerError - - return errors.As(err, &errBuild) || errors.As(err, &errBuildPlanner) -} From fac08a8c68b8bc8c00204ca9a1feb10412db054f Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 24 Sep 2024 13:09:58 -0700 Subject: [PATCH 162/440] Fix panic --- internal/scriptrun/test/integration/scriptrun_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/scriptrun/test/integration/scriptrun_test.go b/internal/scriptrun/test/integration/scriptrun_test.go index b93b25cea8..506a7da279 100644 --- a/internal/scriptrun/test/integration/scriptrun_test.go +++ b/internal/scriptrun/test/integration/scriptrun_test.go @@ -29,6 +29,7 @@ import ( "github.com/ActiveState/cli/internal/testhelpers/osutil" "github.com/ActiveState/cli/internal/testhelpers/outputhelper" "github.com/ActiveState/cli/pkg/platform/authentication" + "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/project" "github.com/ActiveState/cli/pkg/projectfile" ) @@ -119,7 +120,7 @@ func (suite *ScriptRunSuite) TestEnvIsSet() { cfg.Set(constants.AsyncRuntimeConfig, true) out := capturer.CaptureOutput(func() { - scriptRun := scriptrun.New(primer.New(auth, outputhelper.NewCatcher(), subshell.New(cfg), proj, cfg, blackhole.New())) + scriptRun := scriptrun.New(primer.New(auth, outputhelper.NewCatcher(), subshell.New(cfg), proj, cfg, blackhole.New(), model.NewSvcModel(""))) script, err := proj.ScriptByName("run") require.NoError(t, err, "Error: "+errs.JoinMessage(err)) err = scriptRun.Run(script, nil) From cc53ca425f595d81834577b69294d2f4b7c69b04 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 24 Sep 2024 14:21:38 -0400 Subject: [PATCH 163/440] The only commands that should ignore async runtimes are `refresh`, `deploy`, and `activate`. Show a warning to the user when async runtime cases update skips. --- internal/locale/locales/en-us.yaml | 5 ++++- internal/runbits/reqop_runbit/update.go | 3 +++ internal/runbits/runtime/runtime.go | 2 ++ internal/runners/exec/exec.go | 2 +- internal/runners/shell/shell.go | 2 +- internal/runners/use/use.go | 2 +- internal/scriptrun/scriptrun.go | 2 +- 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index c33b900cad..50558aa40b 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1448,6 +1448,9 @@ err_packages_update_runtime_init: other: Could not initialize runtime. pkg_already_uptodate: other: Dependencies for your project are already configured and installed. +notice_async_runtime: + other: | + [WARNING]Warning:[/RESET] Skipping runtime sourcing since {{.V0}} is enabled. Please run '[ACTIONABLE]state refresh[/RESET]' to manually source the runtime. install_runtime: other: Sourcing Runtime install_runtime_info: @@ -1541,7 +1544,7 @@ err_uninstall_platform_nomatch: err_uninstall_platform_multimatch: other: | The platform query you provided matches multiple platforms in your project. - Please specify the platform to uninstall by using its version and bit-width. + Please specify the platform to uninstall by using its version and bit-width. To view the platforms your project uses run: [ACTIONABLE]state platforms[/RESET]. progress_requirements: other: "• Updating requirements" diff --git a/internal/runbits/reqop_runbit/update.go b/internal/runbits/reqop_runbit/update.go index 99333a0a90..350aaddbff 100644 --- a/internal/runbits/reqop_runbit/update.go +++ b/internal/runbits/reqop_runbit/update.go @@ -108,6 +108,9 @@ func UpdateAndReload(prime primeable, script *buildscript.BuildScript, oldCommit } return errs.Wrap(err, "Failed to refresh runtime") } + } else { + prime.Output().Notice("") // blank line + prime.Output().Notice(locale.Tr("notice_async_runtime", constants.AsyncRuntimeConfig)) } // Update commit ID diff --git a/internal/runbits/runtime/runtime.go b/internal/runbits/runtime/runtime.go index a872b8bd47..7cea9664f0 100644 --- a/internal/runbits/runtime/runtime.go +++ b/internal/runbits/runtime/runtime.go @@ -225,6 +225,8 @@ func Update( // any errors regarding solves, buildscripts, etc. if prime.Config().GetBool(constants.AsyncRuntimeConfig) && !opts.IgnoreAsync { logging.Debug("Skipping runtime update due to async runtime") + prime.Output().Notice("") // blank line + prime.Output().Notice(locale.Tr("notice_async_runtime", constants.AsyncRuntimeConfig)) return rt, nil } diff --git a/internal/runners/exec/exec.go b/internal/runners/exec/exec.go index 038d1354a9..6ddca60d0a 100644 --- a/internal/runners/exec/exec.go +++ b/internal/runners/exec/exec.go @@ -126,7 +126,7 @@ func (s *Exec) Run(params *Params, args ...string) (rerr error) { s.out.Notice(locale.Tr("operating_message", projectNamespace, projectDir)) - rt, err := runtime_runbit.Update(s.prime, trigger, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) + rt, err := runtime_runbit.Update(s.prime, trigger, runtime_runbit.WithoutHeaders()) if err != nil { return errs.Wrap(err, "Could not initialize runtime") } diff --git a/internal/runners/shell/shell.go b/internal/runners/shell/shell.go index 3c469ddb8d..43cc179806 100644 --- a/internal/runners/shell/shell.go +++ b/internal/runners/shell/shell.go @@ -93,7 +93,7 @@ func (u *Shell) Run(params *Params) error { return locale.NewInputError("err_shell_commit_id_mismatch") } - rti, err := runtime_runbit.Update(u.prime, trigger.TriggerShell, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) + rti, err := runtime_runbit.Update(u.prime, trigger.TriggerShell, runtime_runbit.WithoutHeaders()) if err != nil { return locale.WrapExternalError(err, "err_shell_runtime_new", "Could not start a shell/prompt for this project.") } diff --git a/internal/runners/use/use.go b/internal/runners/use/use.go index 821d53bf02..1169a16deb 100644 --- a/internal/runners/use/use.go +++ b/internal/runners/use/use.go @@ -90,7 +90,7 @@ func (u *Use) Run(params *Params) error { return locale.NewInputError("err_use_commit_id_mismatch") } - rti, err := runtime_runbit.Update(u.prime, trigger.TriggerUse, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) + rti, err := runtime_runbit.Update(u.prime, trigger.TriggerUse, runtime_runbit.WithoutHeaders()) if err != nil { return locale.WrapError(err, "err_use_runtime_new", "Cannot use this project.") } diff --git a/internal/scriptrun/scriptrun.go b/internal/scriptrun/scriptrun.go index 1671fca444..12f94c17c9 100644 --- a/internal/scriptrun/scriptrun.go +++ b/internal/scriptrun/scriptrun.go @@ -82,7 +82,7 @@ func (s *ScriptRun) NeedsActivation() bool { // PrepareVirtualEnv sets up the relevant runtime and prepares the environment. func (s *ScriptRun) PrepareVirtualEnv() (rerr error) { - rt, err := runtime_runbit.Update(s.prime, trigger.TriggerScript, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) + rt, err := runtime_runbit.Update(s.prime, trigger.TriggerScript, runtime_runbit.WithoutHeaders()) if err != nil { return locale.WrapError(err, "err_activate_runtime", "Could not initialize a runtime for this project.") } From b4c35a8f9971c0cb3e0d74a43a770a7b37a2c6f2 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 24 Sep 2024 16:13:24 -0700 Subject: [PATCH 164/440] Skip integration test --- test/integration/install_int_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index c47d2c2d84..4f50a4ced8 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -76,6 +76,7 @@ func (suite *InstallIntegrationTestSuite) TestInstall_NoMatches_NoAlternatives() } func (suite *InstallIntegrationTestSuite) TestInstall_NoMatches_Alternatives() { + suite.T().Skip("Requires https://activestatef.atlassian.net/browse/DX-3074 to be resolved.") suite.OnlyRunForTags(tagsuite.Install) ts := e2e.New(suite.T(), false) defer ts.Close() From ba57bb2de08fce499079088fe835961f65ad2a5a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 25 Sep 2024 11:34:39 -0700 Subject: [PATCH 165/440] Fix missing arguments --- internal/runners/export/deptree/artifacts.go | 2 +- internal/runners/export/deptree/ingredients.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/export/deptree/artifacts.go b/internal/runners/export/deptree/artifacts.go index 26184a1b2c..f750bb4eb1 100644 --- a/internal/runners/export/deptree/artifacts.go +++ b/internal/runners/export/deptree/artifacts.go @@ -58,7 +58,7 @@ func (d *DeptreeByArtifacts) Run(params ArtifactParams) error { return errs.Wrap(err, "Could not resolve namespace") } - bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth()) + bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth(), d.prime.SvcModel()) commit, err := bpm.FetchCommit(*ns.CommitID, ns.Owner, ns.Project, nil) if err != nil { return errs.Wrap(err, "Could not get remote build expr and time for provided commit") diff --git a/internal/runners/export/deptree/ingredients.go b/internal/runners/export/deptree/ingredients.go index f116a00c07..1003f25abc 100644 --- a/internal/runners/export/deptree/ingredients.go +++ b/internal/runners/export/deptree/ingredients.go @@ -46,7 +46,7 @@ func (d *DeptreeByIngredients) Run(params IngredientParams) error { return errs.Wrap(err, "Could not resolve namespace") } - bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth()) + bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth(), d.prime.SvcModel()) commit, err := bpm.FetchCommit(*ns.CommitID, ns.Owner, ns.Project, nil) if err != nil { return errs.Wrap(err, "Could not get remote build expr and time for provided commit") From e3fabd46506a8fd0a093a1bf502039aeb4e21cd5 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 25 Sep 2024 14:24:59 -0400 Subject: [PATCH 166/440] Fixed config.Get for enum types. --- internal/config/instance.go | 2 +- internal/mediators/config/registry.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/config/instance.go b/internal/config/instance.go index 00a2cb3ea0..fd6d68fe48 100644 --- a/internal/config/instance.go +++ b/internal/config/instance.go @@ -185,7 +185,7 @@ func (i *Instance) Get(key string) interface{} { return result } if opt := mediator.GetOption(key); mediator.KnownOption(opt) { - return opt.Default + return mediator.GetDefault(opt) } return nil } diff --git a/internal/mediators/config/registry.go b/internal/mediators/config/registry.go index d2af1f32d3..526a641c88 100644 --- a/internal/mediators/config/registry.go +++ b/internal/mediators/config/registry.go @@ -67,3 +67,10 @@ func RegisterOptionWithEvents(key string, t Type, defaultValue interface{}, get, func KnownOption(rule Option) bool { return rule.isRegistered } + +func GetDefault(opt Option) interface{} { + if enum, ok := opt.Default.(*Enums); ok { + return enum.Default + } + return opt.Default +} From 49d2ea017b5a015877de7d88213527e1fb534aec Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 25 Sep 2024 14:33:01 -0400 Subject: [PATCH 167/440] Fix build failure. --- internal/runners/export/deptree/artifacts.go | 2 +- internal/runners/export/deptree/ingredients.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/export/deptree/artifacts.go b/internal/runners/export/deptree/artifacts.go index 26184a1b2c..f750bb4eb1 100644 --- a/internal/runners/export/deptree/artifacts.go +++ b/internal/runners/export/deptree/artifacts.go @@ -58,7 +58,7 @@ func (d *DeptreeByArtifacts) Run(params ArtifactParams) error { return errs.Wrap(err, "Could not resolve namespace") } - bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth()) + bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth(), d.prime.SvcModel()) commit, err := bpm.FetchCommit(*ns.CommitID, ns.Owner, ns.Project, nil) if err != nil { return errs.Wrap(err, "Could not get remote build expr and time for provided commit") diff --git a/internal/runners/export/deptree/ingredients.go b/internal/runners/export/deptree/ingredients.go index f116a00c07..1003f25abc 100644 --- a/internal/runners/export/deptree/ingredients.go +++ b/internal/runners/export/deptree/ingredients.go @@ -46,7 +46,7 @@ func (d *DeptreeByIngredients) Run(params IngredientParams) error { return errs.Wrap(err, "Could not resolve namespace") } - bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth()) + bpm := buildplanner.NewBuildPlannerModel(d.prime.Auth(), d.prime.SvcModel()) commit, err := bpm.FetchCommit(*ns.CommitID, ns.Owner, ns.Project, nil) if err != nil { return errs.Wrap(err, "Could not get remote build expr and time for provided commit") From 52dd8fa81d4ed0e01188479adf58b6a849811229 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 1 Oct 2024 14:33:00 -0400 Subject: [PATCH 168/440] Added ceremonial break. --- pkg/buildscript/unmarshal_buildexpression.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 59479995b2..b157bef1ce 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -283,6 +283,7 @@ func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, erro } argsInterface = value + break // technically this is not needed since there's only one element in m } args := []*Value{} From 40886ba487172c909dc78a103040f4fee7cf8e6b Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 2 Oct 2024 06:55:31 -0400 Subject: [PATCH 169/440] Only cache completed build plans. --- pkg/platform/model/buildplanner/build.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index 8763ec6506..f3c38cd9bb 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -78,12 +78,14 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, } return nil, err } - respBytes, err := json.Marshal(resp) - if err != nil { - return nil, errs.Wrap(err, "failed to marshal cache") - } - if err := b.cache.SetCache(cacheKey, string(respBytes), fetchCommitCacheExpiry); err != nil { - return nil, errs.Wrap(err, "failed to set cache") + if resp.Project.Commit.Build.Status == raw.Completed { + respBytes, err := json.Marshal(resp) + if err != nil { + return nil, errs.Wrap(err, "failed to marshal cache") + } + if err := b.cache.SetCache(cacheKey, string(respBytes), fetchCommitCacheExpiry); err != nil { + return nil, errs.Wrap(err, "failed to set cache") + } } } From a2bff928e9b521472edffd6c8a832e7b87cf4e6f Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 2 Oct 2024 06:59:10 -0400 Subject: [PATCH 170/440] Drop unnecessary debug logging. --- internal/process/process.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/process/process.go b/internal/process/process.go index b331a8c44b..eb5f267f78 100644 --- a/internal/process/process.go +++ b/internal/process/process.go @@ -29,14 +29,10 @@ func ActivationPID(cfg Configurable) int32 { seen := map[int32]bool{} for pid != 0 && pid != ppid { - logging.Debug("Current PID: %d, Parent PID: %d", pid, ppid) pidFileName := ActivationPIDFileName(cfg.ConfigPath(), int(pid)) - logging.Debug("Looking for activation pid file: %s", pidFileName) if fileutils.FileExists(pidFileName) { - logging.Debug("Found activation pid file: %s", pidFileName) return pid } - logging.Debug("Activation pid file not found") if ppid == 0 { logging.Debug("Parent PID is 0") From 853f28f1649b37295cc50289c8073f99218631bc Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 2 Oct 2024 08:40:40 -0400 Subject: [PATCH 171/440] Use `e2e.Runtime*TimeoutOpt` where reasonable. --- test/integration/activate_int_test.go | 4 ++-- test/integration/analytics_int_test.go | 9 ++++----- test/integration/languages_int_test.go | 6 ++---- test/integration/push_int_test.go | 8 ++++---- test/integration/run_int_test.go | 4 ++-- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index d91ed46918..f6690f2dbc 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -182,7 +182,7 @@ func (suite *ActivateIntegrationTestSuite) TestActivatePythonByHostOnly() { if runtime.GOOS == "linux" { cp.Expect("Creating a Virtual Environment") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) - cp.ExpectInput(termtest.OptExpectTimeout(40 * time.Second)) + cp.ExpectInput() cp.SendLine("exit") cp.ExpectExitCode(0) } else { @@ -248,7 +248,7 @@ func (suite *ActivateIntegrationTestSuite) activatePython(version string, extraE cp.SendLine("state activate --default") cp.Expect("Creating a Virtual Environment") - cp.ExpectInput(termtest.OptExpectTimeout(40 * time.Second)) + cp.ExpectInput(e2e.RuntimeSourcingTimeoutOpt) pythonShim := pythonExe + osutils.ExeExtension // test that existing environment variables are inherited by the activated shell diff --git a/test/integration/analytics_int_test.go b/test/integration/analytics_int_test.go index 61fdd3b705..3abc9dc148 100644 --- a/test/integration/analytics_int_test.go +++ b/test/integration/analytics_int_test.go @@ -12,7 +12,6 @@ import ( "github.com/ActiveState/cli/internal/runbits/runtime/trigger" "github.com/ActiveState/cli/internal/testhelpers/suite" - "github.com/ActiveState/termtest" "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/analytics/client/sync/reporters" @@ -77,7 +76,7 @@ func (suite *AnalyticsIntegrationTestSuite) TestHeartbeats() { cp.Expect("Creating a Virtual Environment") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) - cp.ExpectInput(termtest.OptExpectTimeout(120 * time.Second)) + cp.ExpectInput() time.Sleep(time.Second) // Ensure state-svc has time to report events @@ -479,7 +478,7 @@ func (suite *AnalyticsIntegrationTestSuite) TestAttempts() { cp.Expect("Creating a Virtual Environment") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) - cp.ExpectInput(termtest.OptExpectTimeout(120 * time.Second)) + cp.ExpectInput() cp.SendLine("python3 --version") cp.Expect("Python 3.") @@ -522,8 +521,8 @@ func (suite *AnalyticsIntegrationTestSuite) TestHeapEvents() { ) cp.Expect("Creating a Virtual Environment") - cp.Expect("Activated") - cp.ExpectInput(termtest.OptExpectTimeout(120 * time.Second)) + cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectInput() time.Sleep(time.Second) // Ensure state-svc has time to report events diff --git a/test/integration/languages_int_test.go b/test/integration/languages_int_test.go index 9103fd9231..c9238e7834 100644 --- a/test/integration/languages_int_test.go +++ b/test/integration/languages_int_test.go @@ -3,11 +3,9 @@ package integration import ( "regexp" "testing" - "time" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/suite" - "github.com/ActiveState/termtest" goversion "github.com/hashicorp/go-version" "github.com/ActiveState/cli/internal/testhelpers/e2e" @@ -52,7 +50,7 @@ func (suite *LanguagesIntegrationTestSuite) TestLanguages_install() { ts.PrepareProject("ActiveState-CLI/Languages", "1eb82b25-a564-42ee-a7d4-d51d2ea73cd5") cp := ts.Spawn("languages") - cp.Expect("Name", termtest.OptExpectTimeout(60*time.Second)) // Cached solves are often slow too + cp.Expect("Name", e2e.RuntimeSolvingTimeoutOpt) // Cached solves are often slow too cp.Expect("python") cp.ExpectExitCode(0) @@ -62,7 +60,7 @@ func (suite *LanguagesIntegrationTestSuite) TestLanguages_install() { cp = ts.Spawn("languages", "install", "python@3.9.16") cp.Expect("project has been updated") // This can take a little while - cp.ExpectExitCode(0, termtest.OptExpectTimeout(60*time.Second)) + cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt) cp = ts.Spawn("languages") cp.Expect("Name") diff --git a/test/integration/push_int_test.go b/test/integration/push_int_test.go index 4904db239f..a19dd5323b 100644 --- a/test/integration/push_int_test.go +++ b/test/integration/push_int_test.go @@ -87,10 +87,10 @@ func (suite *PushIntegrationTestSuite) TestInitAndPush() { cp = ts.Spawn("install", suite.extraPackage) switch runtime.GOOS { case "darwin": - cp.ExpectRe("Added|being built", termtest.OptExpectTimeout(60*time.Second)) // while cold storage is off + cp.ExpectRe("Added|being built", e2e.RuntimeSolvingTimeoutOpt) // while cold storage is off cp.Wait() default: - cp.Expect("Added", termtest.OptExpectTimeout(60*time.Second)) + cp.Expect("Added", e2e.RuntimeSolvingTimeoutOpt) cp.ExpectExitCode(0) } @@ -129,10 +129,10 @@ func (suite *PushIntegrationTestSuite) TestPush_NoPermission_NewProject() { cp = ts.Spawn("install", suite.extraPackage) switch runtime.GOOS { case "darwin": - cp.ExpectRe("Added|being built", termtest.OptExpectTimeout(60*time.Second)) // while cold storage is off + cp.ExpectRe("Added|being built", e2e.RuntimeSolvingTimeoutOpt) // while cold storage is off cp.Wait() default: - cp.Expect("Added", termtest.OptExpectTimeout(60*time.Second)) + cp.Expect("Added", e2e.RuntimeSolvingTimeoutOpt) cp.ExpectExitCode(0) } diff --git a/test/integration/run_int_test.go b/test/integration/run_int_test.go index 61a16d4893..f7ce434f1f 100644 --- a/test/integration/run_int_test.go +++ b/test/integration/run_int_test.go @@ -230,7 +230,7 @@ func (suite *RunIntegrationTestSuite) TestRun_Unauthenticated() { cp := ts.Spawn("activate") cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) - cp.ExpectInput(termtest.OptExpectTimeout(10 * time.Second)) + cp.ExpectInput() cp.SendLine(fmt.Sprintf("%s run testMultipleLanguages", cp.Executable())) cp.Expect("2") @@ -291,7 +291,7 @@ func (suite *RunIntegrationTestSuite) TestRun_Perl_Variable() { e2e.OptAppendEnv("PERL_VERSION=does_not_exist"), ) cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) - cp.ExpectInput(termtest.OptExpectTimeout(10 * time.Second)) + cp.ExpectInput() cp.SendLine("perl -MEnglish -e 'print $PERL_VERSION'") cp.Expect("v5.32.0") From bcd3f828322c1273d8eeb57c1255b61aaa0330be Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 2 Oct 2024 09:02:27 -0400 Subject: [PATCH 172/440] Fixed test failure. The solver is getting faster! --- test/integration/progress_int_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integration/progress_int_test.go b/test/integration/progress_int_test.go index a29d5ac7b0..f8102edc38 100644 --- a/test/integration/progress_int_test.go +++ b/test/integration/progress_int_test.go @@ -19,16 +19,17 @@ func (suite *ProgressIntegrationTestSuite) TestProgress() { defer ts.Close() cp := ts.Spawn("checkout", "ActiveState-CLI/Empty") + cp.Expect("Resolving Dependencies") + cp.ExpectRe(`[^.]+?✔ Done`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect(locale.T("install_runtime")) cp.Expect("Checked out", e2e.RuntimeSourcingTimeoutOpt) - suite.Assert().NotContains(cp.Output(), "...") cp.ExpectExitCode(0) cp = ts.Spawn("checkout", "ActiveState-CLI/Empty", "Empty2", "--non-interactive") - cp.Expect("...") + cp.Expect("Resolving Dependencies") + cp.ExpectRe(`\.+ ✔ Done`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Checked out", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) - } func TestProgressIntegrationTestSuite(t *testing.T) { From a8b70946f42b6cb375c3182be94b2dd86ff98093 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 2 Oct 2024 10:09:51 -0400 Subject: [PATCH 173/440] Fixed failing integration test. --- test/integration/import_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/import_int_test.go b/test/integration/import_int_test.go index ff7bc055d5..47c9c9cd7b 100644 --- a/test/integration/import_int_test.go +++ b/test/integration/import_int_test.go @@ -113,7 +113,7 @@ func (suite *ImportIntegrationTestSuite) TestImport() { cp.ExpectExitCode(0) cp = ts.Spawn("import", "requirements.txt") - cp.Expect("already installed") + cp.Expect("no changes") cp.ExpectNotExitCode(0) }) From d322bdbc78fdc75f2971a7c1c1f27d70aa64dece Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 2 Oct 2024 14:17:40 -0700 Subject: [PATCH 174/440] Set cache in test --- internal/testhelpers/e2e/session.go | 2 +- test/integration/commit_int_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 25054de1b8..e1cb5d2963 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -190,7 +190,7 @@ func new(t *testing.T, retainDirs, updatePath bool, extraEnv ...string) *Session require.NoError(t, err) env := sandboxedTestEnvironment(t, dirs, updatePath, extraEnv...) - session := &Session{Dirs: dirs, Env: env, retainDirs: retainDirs, T: t} + session := &Session{Dirs: dirs, Env: env, retainDirs: retainDirs, T: t, cache: keyCache{}} // Mock installation directory exe, svcExe, execExe := executablePaths(t) diff --git a/test/integration/commit_int_test.go b/test/integration/commit_int_test.go index 325adf9934..9dbe1c22b4 100644 --- a/test/integration/commit_int_test.go +++ b/test/integration/commit_int_test.go @@ -8,7 +8,7 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/fileutils" - "github.com/ActiveState/cli/internal/runbits/buildscript" + buildscript_runbit "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" From b7cd50787a55ca98633dcb67ac2cf00b5a00bccd Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 2 Oct 2024 15:01:47 -0700 Subject: [PATCH 175/440] Pass port to service model --- .../scriptrun/test/integration/scriptrun_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/internal/scriptrun/test/integration/scriptrun_test.go b/internal/scriptrun/test/integration/scriptrun_test.go index 506a7da279..191ec4236a 100644 --- a/internal/scriptrun/test/integration/scriptrun_test.go +++ b/internal/scriptrun/test/integration/scriptrun_test.go @@ -9,8 +9,11 @@ import ( "testing" "github.com/ActiveState/cli/internal/analytics/client/blackhole" + "github.com/ActiveState/cli/internal/installation" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/scriptrun" + "github.com/ActiveState/cli/internal/svcctl" + "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" "github.com/kami-zh/go-capturer" @@ -87,6 +90,8 @@ scripts: func (suite *ScriptRunSuite) TestEnvIsSet() { suite.OnlyRunForTags(tagsuite.Scripts) t := suite.T() + ts := e2e.New(suite.T(), false) + defer ts.Close() if runtime.GOOS == "windows" { // For some reason this test hangs on Windows when ran via CI. I cannot reproduce the issue when manually invoking the @@ -119,8 +124,17 @@ func (suite *ScriptRunSuite) TestEnvIsSet() { cfg.Set(constants.AsyncRuntimeConfig, true) + ipcClient := svcctl.NewDefaultIPCClient() + var svcPort string + + svcExec, err := installation.ServiceExecFromDir(ts.Dirs.Bin) + suite.Require().NoError(err, errs.JoinMessage(err)) + + svcPort, err = svcctl.EnsureExecStartedAndLocateHTTP(ipcClient, svcExec, "from test", nil) + suite.Require().NoError(err, errs.JoinMessage(err)) + out := capturer.CaptureOutput(func() { - scriptRun := scriptrun.New(primer.New(auth, outputhelper.NewCatcher(), subshell.New(cfg), proj, cfg, blackhole.New(), model.NewSvcModel(""))) + scriptRun := scriptrun.New(primer.New(auth, outputhelper.NewCatcher(), subshell.New(cfg), proj, cfg, blackhole.New(), model.NewSvcModel(svcPort))) script, err := proj.ScriptByName("run") require.NoError(t, err, "Error: "+errs.JoinMessage(err)) err = scriptRun.Run(script, nil) From aed322e5d0a8ca51be8476b157ccb17d57d13d68 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 3 Oct 2024 09:48:35 -0700 Subject: [PATCH 176/440] Pass in t --- internal/scriptrun/test/integration/scriptrun_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/scriptrun/test/integration/scriptrun_test.go b/internal/scriptrun/test/integration/scriptrun_test.go index 191ec4236a..e7cc915608 100644 --- a/internal/scriptrun/test/integration/scriptrun_test.go +++ b/internal/scriptrun/test/integration/scriptrun_test.go @@ -90,7 +90,7 @@ scripts: func (suite *ScriptRunSuite) TestEnvIsSet() { suite.OnlyRunForTags(tagsuite.Scripts) t := suite.T() - ts := e2e.New(suite.T(), false) + ts := e2e.New(t, false) defer ts.Close() if runtime.GOOS == "windows" { From e8b137d73840c43a5eadc6707688273670f32b30 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 3 Oct 2024 15:08:20 -0700 Subject: [PATCH 177/440] Add list output to state config --- cmd/state/internal/cmdtree/config.go | 4 +- internal/config/instance.go | 26 +++++++++ internal/locale/locales/en-us.yaml | 4 ++ internal/mediators/config/registry.go | 27 ++++++++- internal/runbits/runtime/runtime.go | 2 +- internal/runners/config/config.go | 81 +++++++++++++++++++++++++-- 6 files changed, 133 insertions(+), 11 deletions(-) diff --git a/cmd/state/internal/cmdtree/config.go b/cmd/state/internal/cmdtree/config.go index e213eca340..1753585602 100644 --- a/cmd/state/internal/cmdtree/config.go +++ b/cmd/state/internal/cmdtree/config.go @@ -10,13 +10,13 @@ import ( func newConfigCommand(prime *primer.Values) *captain.Command { return captain.NewCommand( "config", - locale.Tl("config_title", "Config"), + locale.Tl("config_title", "Listing Configuration Keys and Values"), locale.Tl("config_description", "Manage the State Tool configuration"), prime, []*captain.Flag{}, []*captain.Argument{}, func(ccmd *captain.Command, _ []string) error { - runner, err := config.NewConfig(prime) + runner, err := config.NewList(prime) if err != nil { return err } diff --git a/internal/config/instance.go b/internal/config/instance.go index fd6d68fe48..2658b2ee2b 100644 --- a/internal/config/instance.go +++ b/internal/config/instance.go @@ -190,6 +190,10 @@ func (i *Instance) Get(key string) interface{} { return nil } +func (i *Instance) Default(key string) interface{} { + return mediator.GetDefault(mediator.GetOption(key)) +} + // GetString retrieves a string for a given key func (i *Instance) GetString(key string) string { return cast.ToString(i.Get(key)) @@ -220,6 +224,28 @@ func (i *Instance) AllKeys() []string { return keys } +// AllValues returns all of the current config keys and values +func (i *Instance) AllValues() map[string]interface{} { + rows, err := i.db.Query(`SELECT key, value FROM config`) + if err != nil { + multilog.Error("config:AllValues query failed: %s", errs.JoinMessage(err)) + return nil + } + defer rows.Close() + + values := make(map[string]interface{}) + for rows.Next() { + var key string + var value string + if err := rows.Scan(&key, &value); err != nil { + multilog.Error("config:AllValues scan failed: %s", errs.JoinMessage(err)) + return nil + } + values[key] = value + } + return values +} + // GetStringMapStringSlice retrieves a map of string slices for a given key func (i *Instance) GetStringMapStringSlice(key string) map[string][]string { return cast.ToStringMapStringSlice(i.Get(key)) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 50558aa40b..60da73f045 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1560,3 +1560,7 @@ install_report_updated: other: "Updated: [NOTICE]{{.V0}}[/RESET]" install_report_removed: other: "Removed: [NOTICE]{{.V0}}[/RESET]" +config_get_help: + other: "To GET the value for a specific config key run '[ACTIONABLE]state config get [/RESET]'" +config_set_help: + other: "To SET the value for a specific config key run '[ACTIONABLE]state config set [/RESET]'" diff --git a/internal/mediators/config/registry.go b/internal/mediators/config/registry.go index 526a641c88..8c0bc5a9b3 100644 --- a/internal/mediators/config/registry.go +++ b/internal/mediators/config/registry.go @@ -29,6 +29,7 @@ type Option struct { GetEvent Event SetEvent Event isRegistered bool + isHidden bool } type Registry map[string]Option @@ -49,19 +50,28 @@ func NewEnum(options []string, default_ string) *Enums { func GetOption(key string) Option { rule, ok := registry[key] if !ok { - return Option{key, String, "", EmptyEvent, EmptyEvent, false} + return Option{key, String, "", EmptyEvent, EmptyEvent, false, false} } return rule } // Registers a config option without get/set events. func RegisterOption(key string, t Type, defaultValue interface{}) { - RegisterOptionWithEvents(key, t, defaultValue, EmptyEvent, EmptyEvent) + registerOption(key, t, defaultValue, EmptyEvent, EmptyEvent, false) +} + +// Registers a hidden config option without get/set events. +func RegisterHiddenOption(key string, t Type, defaultValue interface{}) { + registerOption(key, t, defaultValue, EmptyEvent, EmptyEvent, true) } // Registers a config option with get/set events. func RegisterOptionWithEvents(key string, t Type, defaultValue interface{}, get, set Event) { - registry[key] = Option{key, t, defaultValue, get, set, true} + registerOption(key, t, defaultValue, get, set, false) +} + +func registerOption(key string, t Type, defaultValue interface{}, get, set Event, hidden bool) { + registry[key] = Option{key, t, defaultValue, get, set, true, hidden} } func KnownOption(rule Option) bool { @@ -74,3 +84,14 @@ func GetDefault(opt Option) interface{} { } return opt.Default } + +func AllRegistered() []Option { + var opts []Option + for _, opt := range registry { + if opt.isHidden { + continue + } + opts = append(opts, opt) + } + return opts +} diff --git a/internal/runbits/runtime/runtime.go b/internal/runbits/runtime/runtime.go index 8a1256ed99..f4b72ba59b 100644 --- a/internal/runbits/runtime/runtime.go +++ b/internal/runbits/runtime/runtime.go @@ -35,7 +35,7 @@ import ( ) func init() { - configMediator.RegisterOption(constants.AsyncRuntimeConfig, configMediator.Bool, false) + configMediator.RegisterHiddenOption(constants.AsyncRuntimeConfig, configMediator.Bool, false) } type Opts struct { diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index 9734b6553d..904f5607ad 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -1,12 +1,17 @@ package config import ( + "fmt" + "sort" + "github.com/ActiveState/cli/internal/config" + "github.com/ActiveState/cli/internal/locale" + mediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" ) -type Config struct { +type List struct { out output.Outputer cfg *config.Instance } @@ -18,13 +23,79 @@ type primeable interface { primer.Analyticer } -func NewConfig(prime primeable) (*Config, error) { - return &Config{ +func NewList(prime primeable) (*List, error) { + return &List{ out: prime.Output(), cfg: prime.Config(), }, nil } -func (c *Config) Run(usageFunc func() error) error { - return usageFunc() +type report struct { + Key string `locale:"key,Key"` + Value string `locale:"value,Value"` + Default string `locale:"default,Default"` +} + +func (c *List) Run(usageFunc func() error) error { + registered := mediator.AllRegistered() + + sort.SliceStable(registered, func(i, j int) bool { + return registered[i].Name < registered[j].Name + }) + + var reports []report + for _, opt := range registered { + configuredValue := c.cfg.Get(opt.Name) + reports = append(reports, report{ + Key: fmt.Sprintf("[CYAN]%s[/RESET]", opt.Name), + Value: formatValue(opt, configuredValue), + Default: formatDefault(mediator.GetDefault(opt)), + }) + } + + c.out.Print(struct { + Reports []report `opts:"table,hideDash,omitKey"` + }{reports}) + + c.out.Print("") + c.out.Print(locale.T("config_get_help")) + c.out.Print(locale.T("config_set_help")) + + return nil +} + +func formatValue(opt mediator.Option, value interface{}) string { + var v string + switch opt.Type { + case mediator.Enum: + return fmt.Sprintf("\"%s\"", value) + default: + v = fmt.Sprintf("%v", value) + } + + if v == "" { + return "\"\"" + } + + if len(v) > 100 { + v = v[:100] + "..." + } + + if isDefault(value, opt.Default) { + return fmt.Sprintf("[GREEN]%s[/RESET]", v) + } + + return fmt.Sprintf("[BOLD][RED]%s*[/RESET]", v) +} + +func isDefault[T comparable](configured, defaultValue T) bool { + return configured == defaultValue +} + +func formatDefault[T any](defaultValue T) string { + v := fmt.Sprintf("%v", defaultValue) + if v == "" { + v = "\"\"" + } + return fmt.Sprintf("[DISABLED]%s[/RESET]", v) } From cedee00ae4900d015d3cffd0ce1ed6e4dacf4c80 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 3 Oct 2024 15:23:33 -0700 Subject: [PATCH 178/440] Ensure structured output works --- internal/runners/config/config.go | 52 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index 904f5607ad..fad84c861e 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -30,40 +30,68 @@ func NewList(prime primeable) (*List, error) { }, nil } -type report struct { +type configData struct { Key string `locale:"key,Key"` Value string `locale:"value,Value"` Default string `locale:"default,Default"` } +type configOutput struct { + out output.Outputer + cfg *config.Instance + options []mediator.Option + data []configData +} + +func (c *configOutput) MarshalOutput(format output.Format) interface{} { + if format != output.PlainFormatName { + return c.data + } + + c.out.Print(struct { + Data []configData `opts:"table,hideDash,omitKey"` + }{c.data}) + c.out.Print("") + c.out.Print(locale.T("config_get_help")) + c.out.Print(locale.T("config_set_help")) + + return output.Suppress +} + +func (c *configOutput) MarshalStructured(format output.Format) interface{} { + return c.data +} + func (c *List) Run(usageFunc func() error) error { registered := mediator.AllRegistered() - sort.SliceStable(registered, func(i, j int) bool { return registered[i].Name < registered[j].Name }) - var reports []report + var data []configData for _, opt := range registered { configuredValue := c.cfg.Get(opt.Name) - reports = append(reports, report{ - Key: fmt.Sprintf("[CYAN]%s[/RESET]", opt.Name), + data = append(data, configData{ + Key: formatKey(opt.Name), Value: formatValue(opt, configuredValue), Default: formatDefault(mediator.GetDefault(opt)), }) } - c.out.Print(struct { - Reports []report `opts:"table,hideDash,omitKey"` - }{reports}) - - c.out.Print("") - c.out.Print(locale.T("config_get_help")) - c.out.Print(locale.T("config_set_help")) + c.out.Print(&configOutput{ + out: c.out, + cfg: c.cfg, + options: registered, + data: data, + }) return nil } +func formatKey(key string) string { + return fmt.Sprintf("[CYAN]%s[/RESET]", key) +} + func formatValue(opt mediator.Option, value interface{}) string { var v string switch opt.Type { From 0aa822ab2f13b8c04ffe05bb970d4bea3f1de293 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 3 Oct 2024 16:08:29 -0700 Subject: [PATCH 179/440] Add integration test --- test/integration/config_int_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/integration/config_int_test.go b/test/integration/config_int_test.go index db1bb3dfe9..df2021309e 100644 --- a/test/integration/config_int_test.go +++ b/test/integration/config_int_test.go @@ -88,6 +88,33 @@ func (suite *ConfigIntegrationTestSuite) TestJSON() { AssertValidJSON(suite.T(), cp) } +func (suite *ConfigIntegrationTestSuite) TestList() { + suite.OnlyRunForTags(tagsuite.Config) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("config") + cp.Expect("Key") + cp.Expect("Value") + cp.Expect("Default") + cp.Expect("optin.buildscripts") + cp.Expect("false") + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "set", "optin.buildscripts", "true") + cp.Expect("Successfully") + cp.ExpectExitCode(0) + + cp = ts.Spawn("config") + cp.Expect("Key") + cp.Expect("Value") + cp.Expect("Default") + cp.Expect("optin.buildscripts") + cp.Expect("true*") + cp.ExpectExitCode(0) + + suite.Require().NotContains(cp.Snapshot(), constants.AsyncRuntimeConfig) +} func TestConfigIntegrationTestSuite(t *testing.T) { suite.Run(t, new(ConfigIntegrationTestSuite)) } From ad3188d9c223823873d0207fb43580db41137a59 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 4 Oct 2024 15:01:26 -0400 Subject: [PATCH 180/440] Added installable githook for creating a platform commit with the buildscript changes. The hook also auto-updates the buildscript with any resulting changes prior to commit. --- scripts/githooks/pre-commit | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 scripts/githooks/pre-commit diff --git a/scripts/githooks/pre-commit b/scripts/githooks/pre-commit new file mode 100755 index 0000000000..373e0f5114 --- /dev/null +++ b/scripts/githooks/pre-commit @@ -0,0 +1,16 @@ +#!/bin/bash + +if git diff --name-only --cached | grep -q buildscript.as; then + echo "Running 'state commit', as buildscript.as is being committed." + tmpfile=`mktemp -t activestate.XXXX` + state commit --non-interactive 2> >(tee "$tmpfile" >&2) + if [[ $? -ne 0 ]] && ! grep -q 'no new changes' "$tmpfile"; then + rm $tmpfile + echo "Attempting to abort git commit process." + exit 1 + fi + rm $tmpfile + # Update the buildscript.as being committed. + git add buildscript.as + echo "Continuing git commit process." +fi From 28a5f8ca8473d548d224edf3a7b68527f0f3ea76 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 4 Oct 2024 15:55:40 -0400 Subject: [PATCH 181/440] Remove no-op pops. --- pkg/buildscript/unmarshal_buildexpression.go | 31 -------------------- 1 file changed, 31 deletions(-) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index b157bef1ce..7515205e10 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -13,7 +13,6 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/sliceutils" ) @@ -128,12 +127,6 @@ const ( func unmarshalAssignments(path []string, m map[string]interface{}) ([]*Assignment, error) { path = append(path, ctxAssignments) - defer func() { - _, _, err := sliceutils.Pop(path) - if err != nil { - multilog.Error("Could not pop context: %v", err) - } - }() assignments := []*Assignment{} for key, valueInterface := range m { @@ -160,12 +153,6 @@ func unmarshalAssignments(path []string, m map[string]interface{}) ([]*Assignmen func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { path = append(path, ctxValue) - defer func() { - _, _, err := sliceutils.Pop(path) - if err != nil { - multilog.Error("Could not pop context: %v", err) - } - }() value := &Value{} @@ -236,12 +223,6 @@ func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { func isAp(path []string, value map[string]interface{}) bool { path = append(path, ctxIsAp) - defer func() { - _, _, err := sliceutils.Pop(path) - if err != nil { - multilog.Error("Could not pop context: %v", err) - } - }() _, hasIn := value[inKey] return !hasIn || sliceutils.Contains(path, ctxAssignments) @@ -249,12 +230,6 @@ func isAp(path []string, value map[string]interface{}) bool { func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, error) { path = append(path, ctxFuncCall) - defer func() { - _, _, err := sliceutils.Pop(path) - if err != nil { - multilog.Error("Could not pop context: %v", err) - } - }() // m is a mapping of function name to arguments. There should only be one // set of arguments. Since the arguments are key-value pairs, it should be @@ -317,12 +292,6 @@ func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, erro func unmarshalIn(path []string, inValue interface{}) (*Value, error) { path = append(path, ctxIn) - defer func() { - _, _, err := sliceutils.Pop(path) - if err != nil { - multilog.Error("Could not pop context: %v", err) - } - }() in := &Value{} From e39fc709364575af881233185e94bb6a0b1e02d3 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:09:34 -0700 Subject: [PATCH 182/440] Added working directory to hashglobs --- cmd/state-svc/gqlgen.yml | 2 +- cmd/state-svc/internal/hash/file_hasher.go | 10 ++++- cmd/state-svc/internal/resolver/resolver.go | 4 +- .../internal/server/generated/generated.go | 39 ++++++++++++------- cmd/state-svc/schema/schema.graphqls | 2 +- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/cmd/state-svc/gqlgen.yml b/cmd/state-svc/gqlgen.yml index 47a84e2939..0c6a28fb70 100644 --- a/cmd/state-svc/gqlgen.yml +++ b/cmd/state-svc/gqlgen.yml @@ -60,4 +60,4 @@ models: - github.com/99designs/gqlgen/graphql.Int64 - github.com/99designs/gqlgen/graphql.Int32 Void: - model: github.com/ActiveState/cli/cmd/state-svc/internal/graphql.Void + model: github.com/ActiveState/cli/cmd/state-svc/internal/graphqltypes.Void diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index d237bf0c62..8e99220366 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "path/filepath" "sort" "time" @@ -29,11 +30,18 @@ func NewFileHasher() *FileHasher { } } -func (fh *FileHasher) HashFiles(files []string) (_ string, rerr error) { +func (fh *FileHasher) HashFiles(wd string, files []string) (_ string, rerr error) { sort.Strings(files) hasher := xxhash.New() for _, f := range files { + if !filepath.IsAbs(f) { + af, err := filepath.Abs(filepath.Join(wd, f)) + if err != nil { + return "", errs.Wrap(err, "Could not get absolute path for file: %s", f) + } + f = af + } file, err := os.Open(f) if err != nil { return "", errs.Wrap(err, "Could not open file: %s", file.Name()) diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index 9682619368..ed6cae022d 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -308,7 +308,7 @@ func (r *Resolver) GetJwt(ctx context.Context) (*graph.Jwt, error) { return jwt, nil } -func (r *Resolver) HashGlobs(ctx context.Context, globs []string) (string, error) { +func (r *Resolver) HashGlobs(ctx context.Context, wd string, globs []string) (string, error) { defer func() { handlePanics(recover(), debug.Stack()) }() var files []string @@ -320,7 +320,7 @@ func (r *Resolver) HashGlobs(ctx context.Context, globs []string) (string, error files = append(files, matches...) } - return r.fileHasher.HashFiles(files) + return r.fileHasher.HashFiles(wd, files) } func (r *Resolver) GetCache(ctx context.Context, key string) (string, error) { diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index 5d24da09e2..43d27bdb9c 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -13,7 +13,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" - graphql1 "github.com/ActiveState/cli/cmd/state-svc/internal/graphqltypes" + "github.com/ActiveState/cli/cmd/state-svc/internal/graphqltypes" "github.com/ActiveState/cli/internal/graph" gqlparser "github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2/ast" @@ -105,7 +105,7 @@ type ComplexityRoot struct { GetCache func(childComplexity int, key string) int GetJwt func(childComplexity int) int GetProcessesInUse func(childComplexity int, execDir string) int - HashGlobs func(childComplexity int, globs []string) int + HashGlobs func(childComplexity int, wd string, globs []string) int Projects func(childComplexity int) int ReportRuntimeUsage func(childComplexity int, pid int, exec string, source string, dimensionsJSON string) int Version func(childComplexity int) int @@ -136,7 +136,7 @@ type ComplexityRoot struct { } type MutationResolver interface { - SetCache(ctx context.Context, key string, value string, expiry int) (*graphql1.Void, error) + SetCache(ctx context.Context, key string, value string, expiry int) (*graphqltypes.Void, error) } type QueryResolver interface { Version(ctx context.Context) (*graph.Version, error) @@ -149,7 +149,7 @@ type QueryResolver interface { FetchLogTail(ctx context.Context) (string, error) GetProcessesInUse(ctx context.Context, execDir string) ([]*graph.ProcessInfo, error) GetJwt(ctx context.Context) (*graph.Jwt, error) - HashGlobs(ctx context.Context, globs []string) (string, error) + HashGlobs(ctx context.Context, wd string, globs []string) (string, error) GetCache(ctx context.Context, key string) (string, error) } @@ -427,7 +427,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.HashGlobs(childComplexity, args["globs"].([]string)), true + return e.complexity.Query.HashGlobs(childComplexity, args["wd"].(string), args["globs"].([]string)), true case "Query.projects": if e.complexity.Query.Projects == nil { @@ -726,7 +726,7 @@ type Query { fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT - hashGlobs(globs: [String!]!): String! + hashGlobs(wd: String!, globs: [String!]!): String! getCache(key: String!): String! } @@ -947,15 +947,24 @@ func (ec *executionContext) field_Query_getProcessesInUse_args(ctx context.Conte func (ec *executionContext) field_Query_hashGlobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []string + var arg0 string + if tmp, ok := rawArgs["wd"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("wd")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["wd"] = arg0 + var arg1 []string if tmp, ok := rawArgs["globs"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("globs")) - arg0, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + arg1, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) if err != nil { return nil, err } } - args["globs"] = arg0 + args["globs"] = arg1 return args, nil } @@ -1732,9 +1741,9 @@ func (ec *executionContext) _Mutation_setCache(ctx context.Context, field graphq if resTmp == nil { return graphql.Null } - res := resTmp.(*graphql1.Void) + res := resTmp.(*graphqltypes.Void) fc.Result = res - return ec.marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqlᚐVoid(ctx, field.Selections, res) + return ec.marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqltypesᚐVoid(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_setCache(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -2587,7 +2596,7 @@ func (ec *executionContext) _Query_hashGlobs(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().HashGlobs(rctx, fc.Args["globs"].([]string)) + return ec.resolvers.Query().HashGlobs(rctx, fc.Args["wd"].(string), fc.Args["globs"].([]string)) }) if err != nil { ec.Error(ctx, err) @@ -7002,16 +7011,16 @@ func (ec *executionContext) marshalOVersion2ᚖgithubᚗcomᚋActiveStateᚋcli return ec._Version(ctx, sel, v) } -func (ec *executionContext) unmarshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqlᚐVoid(ctx context.Context, v interface{}) (*graphql1.Void, error) { +func (ec *executionContext) unmarshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqltypesᚐVoid(ctx context.Context, v interface{}) (*graphqltypes.Void, error) { if v == nil { return nil, nil } - var res = new(graphql1.Void) + var res = new(graphqltypes.Void) err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqlᚐVoid(ctx context.Context, sel ast.SelectionSet, v *graphql1.Void) graphql.Marshaler { +func (ec *executionContext) marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqltypesᚐVoid(ctx context.Context, sel ast.SelectionSet, v *graphqltypes.Void) graphql.Marshaler { if v == nil { return graphql.Null } diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index 332b01823d..c689289931 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -88,7 +88,7 @@ type Query { fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT - hashGlobs(globs: [String!]!): String! + hashGlobs(wd: String!, globs: [String!]!): String! getCache(key: String!): String! } From e3852b744b31ee222df4ff0e58f6a9afa3b4b091 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:09:58 -0700 Subject: [PATCH 183/440] Added client side model/request for hashglobs --- pkg/platform/api/svc/request/hashglobs.go | 23 +++++++++++++++++++++++ pkg/platform/model/svc.go | 14 ++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 pkg/platform/api/svc/request/hashglobs.go diff --git a/pkg/platform/api/svc/request/hashglobs.go b/pkg/platform/api/svc/request/hashglobs.go new file mode 100644 index 0000000000..41a69a1ee9 --- /dev/null +++ b/pkg/platform/api/svc/request/hashglobs.go @@ -0,0 +1,23 @@ +package request + +type HashGlobs struct { + wd string + globs []string +} + +func NewHashGlobs(wd string, globs []string) *HashGlobs { + return &HashGlobs{wd: wd, globs: globs} +} + +func (c *HashGlobs) Query() string { + return `query(wd: String!, $globs: [String!]!) { + hashGlobs(wd: $wd, globs: $globs) + }` +} + +func (c *HashGlobs) Vars() (map[string]interface{}, error) { + return map[string]interface{}{ + "wd": c.wd, + "globs": c.globs, + }, nil +} diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 8816c15035..3b5db843ca 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -226,6 +226,20 @@ func (m *SvcModel) SetCache(key, value string, expiry time.Duration) error { return nil } +func (m *SvcModel) HashGlobs(wd string, globs []string) (string, error) { + defer profile.Measure("svc:HashGlobs", time.Now()) + + req := request.NewHashGlobs(wd, globs) + response := make(map[string]string) + if err := m.request(context.Background(), req, &response); err != nil { + return "", errs.Wrap(err, "Error sending HashGlobs request to state-svc") + } + if entry, ok := response["hashGlobs"]; ok { + return entry, nil + } + return "", errs.New("svcModel.HashGlobs() did not return an expected value") +} + func jsonFromMap(m map[string]interface{}) string { d, err := json.Marshal(m) if err != nil { From 2f0af004ea7e82f89887b54a2520776c4a8e1ce8 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:10:46 -0700 Subject: [PATCH 184/440] Added `FuncCalls()` method to buildscripts.raw --- pkg/buildscript/raw.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 52a8bdab72..832f6df3c4 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -15,6 +15,37 @@ type rawBuildScript struct { AtTime *time.Time // set after initial read } +func (r *rawBuildScript) FuncCalls() []*FuncCall { + result := []*FuncCall{} + for _, a := range r.Assignments { + result = append(result, a.Value.funcCalls()...) + } + return result +} + +// funcCalls will return all function calls recursively under the given value. +func (v *Value) funcCalls() []*FuncCall { + result := []*FuncCall{} + switch { + case v.FuncCall != nil: + result = append(result, v.FuncCall) + for _, arg := range v.FuncCall.Arguments { + result = append(result, arg.funcCalls()...) + } + case v.List != nil: + for _, v := range *v.List { + result = append(result, v.funcCalls()...) + } + case v.Assignment != nil: + result = append(result, v.Assignment.Value.funcCalls()...) + case v.Object != nil: + for _, a := range *v.Object { + result = append(result, a.Value.funcCalls()...) + } + } + return result +} + type Assignment struct { Key string `parser:"@Ident '='"` Value *Value `parser:"@@"` From 4cefb889cb1271e543a301aa5bead0e815ee8959 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:11:30 -0700 Subject: [PATCH 185/440] buildscript scripts allow for filepath as input --- scripts/to-buildexpression/main.go | 31 ++++++++++++--------- scripts/to-buildscript/main.go | 43 +++++++++++++++++++----------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/scripts/to-buildexpression/main.go b/scripts/to-buildexpression/main.go index dccf2c5179..0d5cbae0de 100644 --- a/scripts/to-buildexpression/main.go +++ b/scripts/to-buildexpression/main.go @@ -7,29 +7,34 @@ import ( "os" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/pkg/buildscript" ) func main() { - scanner := bufio.NewScanner(os.Stdin) var input string - for scanner.Scan() { - if errors.Is(scanner.Err(), bufio.ErrFinalToken) { - break + if len(os.Args) == 2 && fileutils.FileExists(os.Args[1]) { + input = string(fileutils.ReadFileUnsafe(os.Args[1])) + } else { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + if errors.Is(scanner.Err(), bufio.ErrFinalToken) { + break + } + line := scanner.Text() + if line == "\x04" { // Ctrl+D character + break + } + input += line + "\n" } - line := scanner.Text() - if line == "\x04" { // Ctrl+D character - break - } - input += line + "\n" - } - if err := scanner.Err(); err != nil { - panic(fmt.Sprintf("error reading standard input: %v\n", err)) + if err := scanner.Err(); err != nil { + panic(fmt.Sprintf("error reading standard input: %v\n", err)) + } } if input == "" { - fmt.Printf("Usage: %s << \n", os.Args[0]) + fmt.Printf("Usage: %s [<< | ]\n", os.Args[0]) os.Exit(1) } diff --git a/scripts/to-buildscript/main.go b/scripts/to-buildscript/main.go index 6752489c4b..9817cf8d1a 100644 --- a/scripts/to-buildscript/main.go +++ b/scripts/to-buildscript/main.go @@ -8,43 +8,54 @@ import ( "time" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/pkg/buildscript" "github.com/go-openapi/strfmt" ) func main() { - scanner := bufio.NewScanner(os.Stdin) var input string - for scanner.Scan() { - if errors.Is(scanner.Err(), bufio.ErrFinalToken) { - break + var argOffset = 0 + if len(os.Args) == 2 && fileutils.FileExists(os.Args[1]) { + input = string(fileutils.ReadFileUnsafe(os.Args[1])) + argOffset = 1 + } else { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + if errors.Is(scanner.Err(), bufio.ErrFinalToken) { + break + } + line := scanner.Text() + if line == "\x04" { // Ctrl+D character + break + } + input += line + "\n" } - line := scanner.Text() - if line == "\x04" { // Ctrl+D character - break - } - input += line + "\n" - } - if err := scanner.Err(); err != nil { - panic(fmt.Sprintf("error reading standard input: %v\n", err)) + if err := scanner.Err(); err != nil { + panic(fmt.Sprintf("error reading standard input: %v\n", err)) + } } if input == "" { - fmt.Printf("Usage: %s [] << \n", os.Args[0]) + fmt.Printf("Usage: %s [<< | ] []\n", os.Args[0]) os.Exit(1) } var atTime *time.Time - if len(os.Args) == 2 { - t, err := time.Parse(strfmt.RFC3339Millis, os.Args[1]) + if len(os.Args) == (2 + argOffset) { + t, err := time.Parse(strfmt.RFC3339Millis, os.Args[1+argOffset]) if err != nil { panic(errs.JoinMessage(err)) } atTime = &t } - bs, err := buildscript.UnmarshalBuildExpression([]byte(input), atTime) + bs := buildscript.New() + if atTime != nil { + bs.SetAtTime(*atTime) + } + err := bs.UnmarshalBuildExpression([]byte(input)) if err != nil { panic(errs.JoinMessage(err)) } From 76865a17f6fdf1d3e102102b43bdbc57ded9038a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:12:10 -0700 Subject: [PATCH 186/440] Remove unused function --- pkg/platform/model/buildplanner/buildengine.go | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 pkg/platform/model/buildplanner/buildengine.go diff --git a/pkg/platform/model/buildplanner/buildengine.go b/pkg/platform/model/buildplanner/buildengine.go deleted file mode 100644 index 20f7636e01..0000000000 --- a/pkg/platform/model/buildplanner/buildengine.go +++ /dev/null @@ -1,16 +0,0 @@ -package buildplanner - -import ( - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" -) - -func ParseBuildEngine(be string) types.BuildEngine { - switch be { - case types.Alternative.String(): - return types.Alternative - case types.Camel.String(): - return types.Camel - default: - return types.UnknownEngine - } -} From f7c02460f88b06ddfc90eed6b0ace9a429887cbe Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:17:44 -0700 Subject: [PATCH 187/440] Refactored buildscript so it can carry context better --- go.mod | 1 + go.sum | 2 + pkg/buildscript/buildscript.go | 34 ++- pkg/buildscript/buildscript_test.go | 8 +- pkg/buildscript/marshal_buildexpression.go | 47 ++-- pkg/buildscript/mutations_test.go | 4 +- pkg/buildscript/queries_test.go | 8 +- pkg/buildscript/raw.go | 8 + pkg/buildscript/unmarshal_buildexpression.go | 72 ++---- .../unmarshal_buildexpression_test.go | 11 +- pkg/platform/model/buildplanner/build.go | 5 +- .../model/buildplanner/buildscript.go | 6 +- pkg/platform/model/buildplanner/commit.go | 6 +- pkg/platform/model/buildplanner/project.go | 6 +- vendor/github.com/brunoga/deep/LICENSE | 201 +++++++++++++++ vendor/github.com/brunoga/deep/README.md | 29 +++ vendor/github.com/brunoga/deep/deep.go | 239 ++++++++++++++++++ vendor/github.com/brunoga/deep/disable_ro.go | 56 ++++ vendor/modules.txt | 3 + 19 files changed, 629 insertions(+), 117 deletions(-) create mode 100644 vendor/github.com/brunoga/deep/LICENSE create mode 100644 vendor/github.com/brunoga/deep/README.md create mode 100644 vendor/github.com/brunoga/deep/deep.go create mode 100644 vendor/github.com/brunoga/deep/disable_ro.go diff --git a/go.mod b/go.mod index b9f3db72ba..b512502678 100644 --- a/go.mod +++ b/go.mod @@ -70,6 +70,7 @@ require ( require ( github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 + github.com/brunoga/deep v1.2.4 github.com/cespare/xxhash v1.1.0 github.com/charmbracelet/bubbles v0.18.0 github.com/charmbracelet/bubbletea v0.25.0 diff --git a/go.sum b/go.sum index f154260889..f68dcce819 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/brunoga/deep v1.2.4 h1:Aj9E9oUbE+ccbyh35VC/NHlzzjfIVU69BXu2mt2LmL8= +github.com/brunoga/deep v1.2.4/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 159c15b1ff..747f2c4709 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -14,8 +14,27 @@ type BuildScript struct { raw *rawBuildScript } -func New() (*BuildScript, error) { - return UnmarshalBuildExpression([]byte(emptyBuildExpression), nil) +func init() { + // Guard against emptyBuildExpression having parsing issues + if !condition.BuiltViaCI() || condition.InActiveStateCI() { + err := New().UnmarshalBuildExpression([]byte(emptyBuildExpression)) + if err != nil { + panic(err) + } + } +} + +func Create() *BuildScript { + bs := New() + // We don't handle unmarshalling errors here, see the init function for that. + // Since the empty build expression is a constant there's really no need to error check this each time. + _ = bs.UnmarshalBuildExpression([]byte(emptyBuildExpression)) + return bs +} + +func New() *BuildScript { + bs := Create() + return bs } func (b *BuildScript) AtTime() *time.Time { @@ -39,14 +58,9 @@ func (b *BuildScript) Equals(other *BuildScript) (bool, error) { } func (b *BuildScript) Clone() (*BuildScript, error) { - m, err := b.Marshal() - if err != nil { - return nil, errs.Wrap(err, "unable to marshal this buildscript") - } - - u, err := Unmarshal(m) + bb, err := deep.Copy(b) if err != nil { - return nil, errs.Wrap(err, "unable to unmarshal buildscript") + return nil, errs.Wrap(err, "unable to clone buildscript") } - return u, nil + return bb, nil } diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index d327bcdddc..d4cff2a3b4 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -87,7 +87,8 @@ func TestRoundTripFromBuildScript(t *testing.T) { // expression and then immediately construct another build expression from that build script, the // build expressions are identical. func TestRoundTripFromBuildExpression(t *testing.T) { - script, err := UnmarshalBuildExpression(basicBuildExpression, nil) + script := New() + err := script.UnmarshalBuildExpression(basicBuildExpression) require.NoError(t, err) data, err := script.MarshalBuildExpression() @@ -102,8 +103,9 @@ func TestExpressionToScript(t *testing.T) { ts, err := time.Parse(strfmt.RFC3339Millis, atTime) require.NoError(t, err) - script, err := UnmarshalBuildExpression(basicBuildExpression, &ts) - require.NoError(t, err) + script := New() + script.SetAtTime(ts) + require.NoError(t, script.UnmarshalBuildExpression(basicBuildExpression)) data, err := script.Marshal() require.NoError(t, err) diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index 1f55f87133..0e4bd481ad 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -5,11 +5,10 @@ import ( "strings" "time" - "github.com/go-openapi/strfmt" - "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/go-openapi/strfmt" ) const ( @@ -21,36 +20,17 @@ const ( requirementComparatorKey = "comparator" ) -type MarshalerFunc func([]*Value) ([]byte, error) - -var marshalers map[string]MarshalerFunc - -func init() { - marshalers = make(map[string]MarshalerFunc) - RegisterFunctionMarshaler("Req", marshalReq) // marshal into legacy object format for now -} - -// RegisterFunctionMarshaler registers a buildexpression marshaler for a buildscript function. -// Marshalers accept a buildscript Value, and marshals it to buildexpression JSON (e.g. an object). -func RegisterFunctionMarshaler(name string, marshalJSON MarshalerFunc) { - marshalers[name] = marshalJSON -} - -// MarshalJSON returns this structure as a build expression in JSON format, suitable for sending to -// the Platform. +// MarshalBuildExpression returns this structure as a build expression in JSON format, suitable for sending to the Platform. func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { - return json.MarshalIndent(b, "", " ") -} + raw, err := b.raw.clone() + if err != nil { + return nil, errs.Wrap(err, "Cannot clone raw build script") + } -// Note: all of the MarshalJSON functions are named the way they are because Go's JSON package -// specifically looks for them. -// MarshalJSON returns this structure as a build expression in JSON format, suitable for sending to -// the Platform. -func (b *BuildScript) MarshalJSON() ([]byte, error) { m := make(map[string]interface{}) let := make(map[string]interface{}) - for _, assignment := range b.raw.Assignments { + for _, assignment := range raw.Assignments { key := assignment.Key value := assignment.Value switch key { @@ -73,6 +53,9 @@ func (b *BuildScript) MarshalJSON() ([]byte, error) { return json.Marshal(m) } +// Note: all of the MarshalJSON functions are named the way they are because Go's JSON package +// specifically looks for them. + func (a *Assignment) MarshalJSON() ([]byte, error) { m := make(map[string]interface{}) m[a.Key] = a.Value @@ -106,8 +89,8 @@ func (v *Value) MarshalJSON() ([]byte, error) { } func (f *FuncCall) MarshalJSON() ([]byte, error) { - if marshalJSON, exists := marshalers[f.Name]; exists { - return marshalJSON(f.Arguments) + if f.Name == reqFuncName { + return marshalReq(f) } m := make(map[string]interface{}) @@ -130,7 +113,11 @@ func (f *FuncCall) MarshalJSON() ([]byte, error) { // marshalReq translates a Req() function into its equivalent buildexpression requirement object. // This is needed until buildexpressions support functions as requirements. Once they do, we can // remove this method entirely. -func marshalReq(args []*Value) ([]byte, error) { +func marshalReq(fn *FuncCall) ([]byte, error) { + if fn.Name == reqFuncName { + return marshalReq(fn.Arguments[0].FuncCall) + } + args := fn.Arguments requirement := make(map[string]interface{}) for _, arg := range args { diff --git a/pkg/buildscript/mutations_test.go b/pkg/buildscript/mutations_test.go index f3cff0996f..948e338bca 100644 --- a/pkg/buildscript/mutations_test.go +++ b/pkg/buildscript/mutations_test.go @@ -264,8 +264,8 @@ func TestUpdateRequirements(t *testing.T) { data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", tt.args.filename)) assert.NoError(t, err) - script, err := UnmarshalBuildExpression(data, nil) - assert.NoError(t, err) + script := New() + assert.NoError(t, script.UnmarshalBuildExpression(data)) err = script.UpdateRequirement(tt.args.operation, tt.args.requirement.Requirement) if err != nil { diff --git a/pkg/buildscript/queries_test.go b/pkg/buildscript/queries_test.go index 9a107276ef..1b135f82f1 100644 --- a/pkg/buildscript/queries_test.go +++ b/pkg/buildscript/queries_test.go @@ -110,8 +110,8 @@ func TestRequirements(t *testing.T) { data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", tt.args.filename)) assert.NoError(t, err) - script, err := UnmarshalBuildExpression(data, nil) - assert.NoError(t, err) + script := New() + assert.NoError(t, script.UnmarshalBuildExpression(data)) got, err := script.Requirements() assert.NoError(t, err) @@ -164,8 +164,8 @@ func TestRevision(t *testing.T) { data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", tt.args.filename)) assert.NoError(t, err) - script, err := UnmarshalBuildExpression(data, nil) - assert.NoError(t, err) + script := New() + assert.NoError(t, script.UnmarshalBuildExpression(data)) got, err := script.Requirements() assert.NoError(t, err) diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 832f6df3c4..17070ef4ca 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -6,6 +6,7 @@ import ( "time" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/brunoga/deep" ) // Tagged fields will be filled in by Participle. @@ -15,6 +16,13 @@ type rawBuildScript struct { AtTime *time.Time // set after initial read } +// clone is meant to facilitate making modifications to functions at marshal time. The idea is that these modifications +// are only intended to be made for the purpose of marshalling, meaning we do not want to mutate the original object. +// This is an antipattern, but addressing it requires significant refactoring that we're not committing to atm. +func (r *rawBuildScript) clone() (*rawBuildScript, error) { + return deep.Copy(r) +} + func (r *rawBuildScript) FuncCalls() []*FuncCall { result := []*FuncCall{} for _, a := range r.Assignments { diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index b157bef1ce..69483ddb4e 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -43,86 +43,66 @@ const ( inKey = "in" ) -type PreUnmarshalerFunc func(map[string]interface{}) (map[string]interface{}, error) - -var preUnmarshalers map[string]PreUnmarshalerFunc - -func init() { - preUnmarshalers = make(map[string]PreUnmarshalerFunc) -} - -// RegisterFunctionPreUnmarshaler registers a buildscript pre-unmarshaler for a buildexpression -// function. -// Pre-unmarshalers accept a JSON object of function arguments, transform those arguments as -// necessary, and return a JSON object for final unmarshaling to buildscript. -func RegisterFunctionPreUnmarshaler(name string, preUnmarshal PreUnmarshalerFunc) { - preUnmarshalers[name] = preUnmarshal -} - // UnmarshalBuildExpression returns a BuildScript constructed from the given build expression in // JSON format. // Build scripts and build expressions are almost identical, with the exception of the atTime field. // Build expressions ALWAYS set at_time to `$at_time`, which refers to the timestamp on the commit, // while buildscripts encode this timestamp as part of their definition. For this reason we have // to supply the timestamp as a separate argument. -func UnmarshalBuildExpression(data []byte, atTime *time.Time) (*BuildScript, error) { +func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { expr := make(map[string]interface{}) err := json.Unmarshal(data, &expr) if err != nil { - return nil, errs.Wrap(err, "Could not unmarshal build expression") + return errs.Wrap(err, "Could not unmarshal build expression") } let, ok := expr[letKey].(map[string]interface{}) if !ok { - return nil, errs.New("Invalid build expression: 'let' value is not an object") + return errs.New("Invalid build expression: 'let' value is not an object") } var path []string assignments, err := unmarshalAssignments(path, let) if err != nil { - return nil, errs.Wrap(err, "Could not parse assignments") + return errs.Wrap(err, "Could not parse assignments") } - - script := &BuildScript{&rawBuildScript{Assignments: assignments}} + b.raw.Assignments = assignments // Extract the 'at_time' from the solve node, if it exists, and change its value to be a // reference to "$at_time", which is how we want to show it in AScript format. - if atTimeNode, err := script.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { + if atTimeNode, err := b.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { atTime, err := strfmt.ParseDateTime(strValue(atTimeNode)) if err != nil { - return nil, errs.Wrap(err, "Invalid timestamp: %s", strValue(atTimeNode)) + return errs.Wrap(err, "Invalid timestamp: %s", strValue(atTimeNode)) } atTimeNode.Str = nil atTimeNode.Ident = ptr.To("at_time") - script.raw.AtTime = ptr.To(time.Time(atTime)) + b.raw.AtTime = ptr.To(time.Time(atTime)) } else if err != nil { - return nil, errs.Wrap(err, "Could not get at_time node") - } - - if atTime != nil { - script.raw.AtTime = atTime + return errs.Wrap(err, "Could not get at_time node") } // If the requirements are in legacy object form, e.g. // requirements = [{"name": "", "namespace": ""}, {...}, ...] // then transform them into function call form for the AScript format, e.g. // requirements = [Req(name = "", namespace = ""), Req(...), ...] - requirements, err := script.getRequirementsNode() + requirements, err := b.getRequirementsNode() if err != nil { - return nil, errs.Wrap(err, "Could not get requirements node") + return errs.Wrap(err, "Could not get requirements node") } if isLegacyRequirementsList(requirements) { requirements.List = transformRequirements(requirements).List } - return script, nil + + return nil } const ( ctxAssignments = "assignments" ctxValue = "value" ctxFuncCall = "funcCall" - ctxIsAp = "isAp" + ctxFuncDef = "funcDef" ctxIn = "in" ) @@ -184,7 +164,7 @@ func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { continue } - if isAp(path, val.(map[string]interface{})) { + if isFuncCall(path, val.(map[string]interface{})) { f, err := unmarshalFuncCall(path, v) if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's value: %v", key, v) @@ -234,8 +214,8 @@ func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { return value, nil } -func isAp(path []string, value map[string]interface{}) bool { - path = append(path, ctxIsAp) +func isFuncCall(path []string, value map[string]interface{}) bool { + path = append(path, ctxFuncDef) defer func() { _, _, err := sliceutils.Pop(path) if err != nil { @@ -247,7 +227,7 @@ func isAp(path []string, value map[string]interface{}) bool { return !hasIn || sliceutils.Contains(path, ctxAssignments) } -func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, error) { +func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*FuncCall, error) { path = append(path, ctxFuncCall) defer func() { _, _, err := sliceutils.Pop(path) @@ -259,29 +239,19 @@ func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, erro // m is a mapping of function name to arguments. There should only be one // set of arguments. Since the arguments are key-value pairs, it should be // a map[string]interface{}. - if len(m) > 1 { + if len(funcCall) > 1 { return nil, errs.New("Function call has more than one argument mapping") } // Look in the given object for the function's name and argument mapping. var name string var argsInterface interface{} - for key, value := range m { - m, ok := value.(map[string]interface{}) - if !ok { + for key, value := range funcCall { + if _, ok := value.(map[string]interface{}); !ok { return nil, errs.New("Incorrect argument format") } name = key - - if preUnmarshal, exists := preUnmarshalers[name]; exists { - var err error - value, err = preUnmarshal(m) - if err != nil { - return nil, errs.Wrap(err, "Unable to pre-unmarshal function '%s'", name) - } - } - argsInterface = value break // technically this is not needed since there's only one element in m } diff --git a/pkg/buildscript/unmarshal_buildexpression_test.go b/pkg/buildscript/unmarshal_buildexpression_test.go index e05cda4ae0..ef20550c0d 100644 --- a/pkg/buildscript/unmarshal_buildexpression_test.go +++ b/pkg/buildscript/unmarshal_buildexpression_test.go @@ -86,10 +86,13 @@ func TestUnmarshalBuildExpression(t *testing.T) { data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", tt.args.filename)) assert.NoError(t, err) - _, err = UnmarshalBuildExpression(data, nil) - if (err != nil) != tt.wantErr { - t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) - return + script := New() + { + err := script.UnmarshalBuildExpression(data) + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + return + } } }) } diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index 8763ec6506..9d4d1be148 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -104,10 +104,11 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, return nil, errs.Wrap(err, "failed to unmarshal build plan") } - script, err := buildscript.UnmarshalBuildExpression(commit.Expression, ptr.To(time.Time(commit.AtTime))) - if err != nil { + script := buildscript.New() + if err := script.UnmarshalBuildExpression(commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } + script.SetAtTime(time.Time(commit.AtTime)) return &Commit{commit, bp, script}, nil } diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index afd48bbba5..28bd980ffc 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -7,7 +7,6 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/request" bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" @@ -52,8 +51,9 @@ func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript return nil, errs.New("Commit does not contain expression") } - script, err := buildscript.UnmarshalBuildExpression(resp.Commit.Expression, ptr.To(time.Time(resp.Commit.AtTime))) - if err != nil { + script := buildscript.New() + script.SetAtTime(time.Time(resp.Commit.AtTime)) + if err := script.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } diff --git a/pkg/platform/model/buildplanner/commit.go b/pkg/platform/model/buildplanner/commit.go index 50f6ef98e3..387296b1ff 100644 --- a/pkg/platform/model/buildplanner/commit.go +++ b/pkg/platform/model/buildplanner/commit.go @@ -5,7 +5,6 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/buildplan" "github.com/ActiveState/cli/pkg/buildplan/raw" "github.com/ActiveState/cli/pkg/buildscript" @@ -82,8 +81,9 @@ func (b *BuildPlanner) StageCommit(params StageCommitParams) (*Commit, error) { return nil, errs.Wrap(err, "failed to unmarshal build plan") } - stagedScript, err := buildscript.UnmarshalBuildExpression(resp.Commit.Expression, ptr.To(time.Time(resp.Commit.AtTime))) - if err != nil { + stagedScript := buildscript.New() + stagedScript.SetAtTime(time.Time(resp.Commit.AtTime)) + if err := stagedScript.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } diff --git a/pkg/platform/model/buildplanner/project.go b/pkg/platform/model/buildplanner/project.go index c9e84647b2..9eb7dcb34d 100644 --- a/pkg/platform/model/buildplanner/project.go +++ b/pkg/platform/model/buildplanner/project.go @@ -32,11 +32,7 @@ func (b *BuildPlanner) CreateProject(params *CreateProjectParams) (strfmt.UUID, script := params.Script if script == nil { // Construct an initial buildexpression for the new project. - var err error - script, err = buildscript.New() - if err != nil { - return "", errs.Wrap(err, "Unable to create initial buildexpression") - } + script = buildscript.Create() // Add the platform. if err := script.AddPlatform(params.PlatformID); err != nil { diff --git a/vendor/github.com/brunoga/deep/LICENSE b/vendor/github.com/brunoga/deep/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/brunoga/deep/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/brunoga/deep/README.md b/vendor/github.com/brunoga/deep/README.md new file mode 100644 index 0000000000..ad37b09595 --- /dev/null +++ b/vendor/github.com/brunoga/deep/README.md @@ -0,0 +1,29 @@ +# deep +Support for doing deep copies of (almost all) Go types. + +This is a from scratch implementation of the ideas from https://github.com/barkimedes/go-deepcopy (which, unfortunatelly appears to be dead) but it is faster, simpler to use for callers and supports copying unexported fields. + +It should support most Go types. Specificaly, it does not support functions, channels and unsafe.Pointers unless they are nil. Also it might have weird interactions with structs that include any synchronization primitives (mutexes, for example. They should still be copied but if they are usable after that is left as an exercise to the reader). + +One possible usage scenario would be, for example, to negate the use of the deepcopy-gen tool described in [Kubernetes code generation](https://www.redhat.com/en/blog/kubernetes-deep-dive-code-generation-customresources). For any type T, the DeepEqual method can be implemented more or less like this: + +``` +func (t* T) DeepCopy() *T { + return deep.MustCopy(t) // This would panic on errors. +} +``` + +## Benchmarks + +| Benchmark | Iterations | Time | Bytes Allocated | Allocations | +|------------------------------------|------------|----------------|-----------------|------------------| +| **BenchmarkCopy_Deep-16** | **830553** | **1273 ns/op** | **1264 B/op** | **21 allocs/op** | +| BenchmarkCopy_DeepCopy-16 (1) | 458072 | 2466 ns/op | 1912 B/op | 50 allocs/op | +| BenchmarkCopy_CopyStructure-16 (2) | 149685 | 7836 ns/op | 6392 B/op | 168 allocs/op | +| BenchmarkCopy_Clone-16 (3) | 510760 | 2188 ns/op | 1656 B/op | 22 allocs/op | + +(1) https://github.com/barkimedes/go-deepcopy (does not support unexported fields) + +(2) https://github.com/mitchellh/copystructure (does not support cycles) + +(3) https://github.com/huandu/go-clone diff --git a/vendor/github.com/brunoga/deep/deep.go b/vendor/github.com/brunoga/deep/deep.go new file mode 100644 index 0000000000..1b1c14a13c --- /dev/null +++ b/vendor/github.com/brunoga/deep/deep.go @@ -0,0 +1,239 @@ +package deep + +import ( + "fmt" + "reflect" +) + +// Copy creates a deep copy of src. It returns the copy and a nil error in case +// of success and the zero value for the type and a non-nil error on failure. +func Copy[T any](src T) (T, error) { + return copy(src, false) +} + +// CopySkipUnsupported creates a deep copy of src. It returns the copy and a nil +// errorin case of success and the zero value for the type and a non-nil error +// on failure. Unsupported types are skipped (the copy will have the zero value +// for the type) instead of returning an error. +func CopySkipUnsupported[T any](src T) (T, error) { + return copy(src, true) +} + +// MustCopy creates a deep copy of src. It returns the copy on success or panics +// in case of any failure. +func MustCopy[T any](src T) T { + dst, err := copy(src, false) + if err != nil { + panic(err) + } + + return dst +} + +type pointersMap map[uintptr]map[string]reflect.Value + +func copy[T any](src T, skipUnsupported bool) (T, error) { + v := reflect.ValueOf(src) + + // We might have a zero value, so we check for this here otherwise + // calling interface below will panic. + if v.Kind() == reflect.Invalid { + // This amounts to returning the zero value for T. + var t T + return t, nil + } + + dst, err := recursiveCopy(v, make(pointersMap), + skipUnsupported) + if err != nil { + var t T + return t, err + } + + return dst.Interface().(T), nil +} + +func recursiveCopy(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, + reflect.Complex64, reflect.Complex128, reflect.String: + // Direct type, just copy it. + return v, nil + case reflect.Array: + return recursiveCopyArray(v, pointers, skipUnsupported) + case reflect.Interface: + return recursiveCopyInterface(v, pointers, skipUnsupported) + case reflect.Map: + return recursiveCopyMap(v, pointers, skipUnsupported) + case reflect.Ptr: + return recursiveCopyPtr(v, pointers, skipUnsupported) + case reflect.Slice: + return recursiveCopySlice(v, pointers, skipUnsupported) + case reflect.Struct: + return recursiveCopyStruct(v, pointers, skipUnsupported) + case reflect.Func, reflect.Chan, reflect.UnsafePointer: + if v.IsNil() { + // If we have a nil function, unsafe pointer or channel, then we + // can copy it. + return v, nil + } else { + if skipUnsupported { + return reflect.Zero(v.Type()), nil + } else { + return reflect.Value{}, fmt.Errorf("unsuported non-nil value for type: %s", v.Type()) + } + } + default: + if skipUnsupported { + return reflect.Zero(v.Type()), nil + } else { + return reflect.Value{}, fmt.Errorf("unsuported type: %s", v.Type()) + } + } +} + +func recursiveCopyArray(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + dst := reflect.New(v.Type()).Elem() + + for i := 0; i < v.Len(); i++ { + elem := v.Index(i) + elemDst, err := recursiveCopy(elem, pointers, skipUnsupported) + if err != nil { + return reflect.Value{}, err + } + + dst.Index(i).Set(elemDst) + } + + return dst, nil +} + +func recursiveCopyInterface(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + if v.IsNil() { + // If the interface is nil, just return it. + return v, nil + } + + return recursiveCopy(v.Elem(), pointers, skipUnsupported) +} + +func recursiveCopyMap(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + if v.IsNil() { + // If the slice is nil, just return it. + return v, nil + } + + dst := reflect.MakeMap(v.Type()) + + for _, key := range v.MapKeys() { + elem := v.MapIndex(key) + elemDst, err := recursiveCopy(elem, pointers, + skipUnsupported) + if err != nil { + return reflect.Value{}, err + } + + dst.SetMapIndex(key, elemDst) + } + + return dst, nil +} + +func recursiveCopyPtr(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + // If the pointer is nil, just return it. + if v.IsNil() { + return v, nil + } + + typeName := v.Type().String() + + // If the pointer is already in the pointers map, return it. + ptr := v.Pointer() + if dstMap, ok := pointers[ptr]; ok { + if dst, ok := dstMap[typeName]; ok { + return dst, nil + } + } + + // Otherwise, create a new pointer and add it to the pointers map. + dst := reflect.New(v.Type().Elem()) + + if pointers[ptr] == nil { + pointers[ptr] = make(map[string]reflect.Value) + } + + pointers[ptr][typeName] = dst + + // Proceed with the copy. + elem := v.Elem() + elemDst, err := recursiveCopy(elem, pointers, skipUnsupported) + if err != nil { + return reflect.Value{}, err + } + + dst.Elem().Set(elemDst) + + return dst, nil +} + +func recursiveCopySlice(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + if v.IsNil() { + // If the slice is nil, just return it. + return v, nil + } + + dst := reflect.MakeSlice(v.Type(), v.Len(), v.Cap()) + + for i := 0; i < v.Len(); i++ { + elem := v.Index(i) + elemDst, err := recursiveCopy(elem, pointers, + skipUnsupported) + if err != nil { + return reflect.Value{}, err + } + + dst.Index(i).Set(elemDst) + } + + return dst, nil +} + +func recursiveCopyStruct(v reflect.Value, pointers pointersMap, + skipUnsupported bool) (reflect.Value, error) { + dst := reflect.New(v.Type()).Elem() + + for i := 0; i < v.NumField(); i++ { + elem := v.Field(i) + + // If the field is unexported, we need to disable read-only mode. If it + // is exported, doing this changes nothing so we just do it. We need to + // do this here not because we are writting to the field (this is the + // source), but because Interface() does not work if the read-only bits + // are set. + disableRO(&elem) + + elemDst, err := recursiveCopy(elem, pointers, + skipUnsupported) + if err != nil { + return reflect.Value{}, err + } + + dstField := dst.Field(i) + + // If the field is unexported, we need to disable read-only mode so we + // can actually write to it. + disableRO(&dstField) + + dstField.Set(elemDst) + } + + return dst, nil +} diff --git a/vendor/github.com/brunoga/deep/disable_ro.go b/vendor/github.com/brunoga/deep/disable_ro.go new file mode 100644 index 0000000000..8800545367 --- /dev/null +++ b/vendor/github.com/brunoga/deep/disable_ro.go @@ -0,0 +1,56 @@ +package deep + +import ( + "reflect" + "unsafe" +) + +func init() { + // Try to make sure we can use the trick in this file. It is still possible + // that things will break but we should be able to detect most of them. + t := reflect.TypeOf(reflect.Value{}) + if t.Kind() != reflect.Struct { + panic("deep: reflect.Value is not a struct") + } + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Name == "flag" { + // Check that the field is a uintptr. + if f.Type.Kind() != reflect.Uintptr { + panic("deep: reflect.Value.flag is not a uintptr") + } + + flagOffset = f.Offset + + return + } + } + + panic("deep: reflect.Value has no flag field") +} + +var flagOffset uintptr + +const ( + // Lifted from go/src/reflect/value.go. + flagStickyRO uintptr = 1 << 5 + flagEmbedRO uintptr = 1 << 6 + flagRO uintptr = flagStickyRO | flagEmbedRO +) + +// disableRO disables the read-only flag of a reflect.Value object. We use this +// to allow the reflect package to access the value of an unexported field as +// if it was exported (so we can copy it). +// +// DANGER! HERE BE DRAGONS! This is brittle and depends on internal state of a +// reflect.Value object. It is not guaranteed to work in future (or previous) +// versions of Go although we try to detect changes and panic immediatelly +// during initialization. +func disableRO(v *reflect.Value) { + // Get pointer to flags. + flags := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagOffset)) + + // Clear the read-only flags. + *flags &^= flagRO +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 13ea3eed09..c9d9d75f5b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -151,6 +151,9 @@ github.com/aymanbagabas/go-osc52/v2 # github.com/blang/semver v3.5.1+incompatible ## explicit github.com/blang/semver +# github.com/brunoga/deep v1.2.4 +## explicit; go 1.20.0 +github.com/brunoga/deep # github.com/cespare/xxhash v1.1.0 ## explicit github.com/cespare/xxhash From 58985eb7a8f60625442ccb1079e2e4559b531ad6 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:18:16 -0700 Subject: [PATCH 188/440] Implemented initial processor pattern for buildscripts --- cmd/state/main.go | 10 ++- internal/locale/locales/en-us.yaml | 6 ++ pkg/buildscript/buildscript.go | 10 ++- pkg/buildscript/function_test.go | 55 ++++++++++++++ pkg/buildscript/marshal_buildexpression.go | 10 +++ pkg/buildscript/processor.go | 33 +++++++++ .../processors/ingredient/processor.go | 73 +++++++++++++++++++ pkg/buildscript/raw.go | 6 +- pkg/buildscript/unmarshal.go | 6 +- pkg/buildscript/unmarshal_buildexpression.go | 10 +++ 10 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 pkg/buildscript/function_test.go create mode 100644 pkg/buildscript/processor.go create mode 100644 pkg/buildscript/processors/ingredient/processor.go diff --git a/cmd/state/main.go b/cmd/state/main.go index 65e11674d2..0f5210bfbe 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -37,6 +37,8 @@ import ( "github.com/ActiveState/cli/internal/runbits/panics" "github.com/ActiveState/cli/internal/subshell" "github.com/ActiveState/cli/internal/svcctl" + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/buildscript/processors/ingredient" secretsapi "github.com/ActiveState/cli/pkg/platform/api/secrets" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" @@ -210,6 +212,7 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out pjNamespace = pj.Namespace().String() } + // Analytics an := anAsync.New(anaConst.SrcStateTool, svcmodel, cfg, auth, out, pjNamespace) defer func() { if err := events.WaitForEvents(time.Second, an.Wait); err != nil { @@ -233,8 +236,13 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out logging.Debug("Could not register secrets expander: %v", err) } + prime := primer.New(pj, out, auth, prompter, sshell, conditional, cfg, ipcClient, svcmodel, an) + + // Register buildscript functions + buildscript.RegisterDefaultProcessor(ingredient.NewProcessor(prime)) + // Run the actual command - cmds := cmdtree.New(primer.New(pj, out, auth, prompter, sshell, conditional, cfg, ipcClient, svcmodel, an), args...) + cmds := cmdtree.New(prime, args...) childCmd, err := cmds.Command().FindChild(args[1:]) if err != nil { diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 50558aa40b..29939137a5 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1560,3 +1560,9 @@ install_report_updated: other: "Updated: [NOTICE]{{.V0}}[/RESET]" install_report_removed: other: "Removed: [NOTICE]{{.V0}}[/RESET]" +err_marshalbuildexp_src_missing: + other: "Your buildscript contains an ingredient function that's missing the 'src' argument" +err_marshalbuildexp_src_invalid_type: + other: "Your buildscript contains an ingredient function that has an invalid value for the 'src' argument" +err_marshalbuildexp_src_item_invalid_type: + other: "Your buildscript contains an ingredient function that has an invalid value for the 'src' argument entry or entries" diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 747f2c4709..11e9fd4e4a 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -3,7 +3,9 @@ package buildscript import ( "time" + "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" + "github.com/brunoga/deep" ) // BuildScript is what we want consuming code to work with. This specifically makes the raw @@ -11,7 +13,8 @@ import ( // Instead this package should facilitate the use-case of the consuming code through convenience // methods that are easy to understand and work with. type BuildScript struct { - raw *rawBuildScript + raw *rawBuildScript + processors FuncProcessorMap } func init() { @@ -33,7 +36,12 @@ func Create() *BuildScript { } func New() *BuildScript { + return NewWithProcessors(DefaultProcessors) +} + +func NewWithProcessors(processors FuncProcessorMap) *BuildScript { bs := Create() + bs.processors = processors return bs } diff --git a/pkg/buildscript/function_test.go b/pkg/buildscript/function_test.go new file mode 100644 index 0000000000..63adc463d5 --- /dev/null +++ b/pkg/buildscript/function_test.go @@ -0,0 +1,55 @@ +package buildscript_test + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/buildscript/processors/ingredient" + "github.com/stretchr/testify/require" +) + +func TestMarshalBEIngredientAndReqFunc(t *testing.T) { + bs, err := buildscript.Unmarshal([]byte(` +main = ingredient( + name = "pytorch", + src = ["*/**.py", "pyproject.toml"], + deps = [ + Req(name="python", version=Eq(value="3.7.10")) + ] +) +`)) + require.NoError(t, err) + + marshaller := ingredient.NewProcessor(nil) + buildscript.RegisterFunctionProcessor("ingredient", marshaller.MarshalBuildExpression) + + data, err := bs.MarshalBuildExpression() + require.NoError(t, err) + + result := map[string]interface{}{} + require.NoError(t, json.Unmarshal(data, &result)) + + hash := getKey(t, result, "let", "in", "ingredient", "hash") + require.NotEmpty(t, hash) + + _ = data +} + +func getKey(t *testing.T, data map[string]interface{}, keys ...string) any { + var next any + var ok bool + for i, key := range keys { + next, ok = data[key] + if !ok { + t.Fatalf("key %s not found in data", strings.Join(keys[0:i+1], ".")) + } + if len(keys) > i+1 { + if data, ok = next.(map[string]interface{}); !ok { + t.Fatalf("key %s has non-map value: '%v'", strings.Join(keys[0:i+1], "."), next) + } + } + } + return next +} diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index 0e4bd481ad..80aba8bf50 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -27,6 +27,16 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { return nil, errs.Wrap(err, "Cannot clone raw build script") } + // Invoke processors, which may modify function calls + for _, fn := range raw.FuncCalls() { + if processors, ok := b.processors[fn.Name]; ok { + for _, processor := range processors { + if err := processor.ToBuildExpression(b, fn); err != nil { + return nil, errs.Wrap(err, "Custom marshaler for '%s' function failed", fn.Name) + } + } + } + } m := make(map[string]interface{}) let := make(map[string]interface{}) diff --git a/pkg/buildscript/processor.go b/pkg/buildscript/processor.go new file mode 100644 index 0000000000..bfe9344d97 --- /dev/null +++ b/pkg/buildscript/processor.go @@ -0,0 +1,33 @@ +package buildscript + +type FuncProcessor interface { + FuncName() string + ToBuildExpression(*BuildScript, *FuncCall) error + FromBuildExpression(*BuildScript, *FuncCall) error +} + +type FuncProcessorMap map[string][]FuncProcessor + +var DefaultProcessors FuncProcessorMap + +func init() { + DefaultProcessors = make(FuncProcessorMap) +} + +func RegisterDefaultProcessor(marshaler FuncProcessor) { + name := marshaler.FuncName() + if _, ok := DefaultProcessors[name]; !ok { + DefaultProcessors[name] = []FuncProcessor{} + } + DefaultProcessors[name] = append(DefaultProcessors[name], marshaler) +} + +// RegisterProcessor registers a buildexpression marshaler for a buildscript function. +// Marshalers accept a buildscript Value, and marshals it to buildexpression JSON (e.g. an object). +// This is mainly (if not ONLY) used by tests, because for our main business logic we use the DefaultProcessors. +func (b *BuildScript) RegisterProcessor(name string, marshaler FuncProcessor) { + if _, ok := b.processors[name]; !ok { + b.processors[name] = []FuncProcessor{} + } + b.processors[name] = append(b.processors[name], marshaler) +} diff --git a/pkg/buildscript/processors/ingredient/processor.go b/pkg/buildscript/processors/ingredient/processor.go new file mode 100644 index 0000000000..78693740da --- /dev/null +++ b/pkg/buildscript/processors/ingredient/processor.go @@ -0,0 +1,73 @@ +package ingredient + +import ( + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/pkg/buildscript" +) + +type Processor struct { + prime primeable +} + +type primeable interface { + primer.SvcModeler + primer.Projecter +} + +func NewProcessor(prime primeable) *Processor { + return &Processor{prime} +} + +func (p *Processor) FuncName() string { + return "ingredient" +} + +func (p *Processor) ToBuildExpression(script *buildscript.BuildScript, fn *buildscript.FuncCall) error { + pj := p.prime.Project() + if pj == nil { + return errs.Wrap(rationalize.ErrNoProject, "Need project to hash globs (for cwd)") + } + + var src *buildscript.Value + for _, arg := range fn.Arguments { + if arg.Assignment != nil && arg.Assignment.Key == "src" { + src = arg.Assignment.Value + } + } + if src == nil { + return locale.NewInputError("err_marshalbuildexp_src_missing") + } + if src.List == nil { + return locale.NewInputError("err_marshalbuildexp_src_invalid_type") + } + patterns := []string{} + for _, value := range *src.List { + if value.Str == nil { + return locale.NewInputError("err_marshalbuildexp_src_item_invalid_type") + } + patterns = append(patterns, *value.Str) + } + + hash, err := p.prime.SvcModel().HashGlobs(pj.Dir(), patterns) + if err != nil { + return errs.Wrap(err, "Could not hash globs") + } + + fn.Arguments = append(fn.Arguments, &buildscript.Value{ + Assignment: &buildscript.Assignment{ + Key: "hash", + Value: &buildscript.Value{ + Str: &hash, + }, + }, + }) + + return nil +} + +func (p *Processor) FromBuildExpression(script *buildscript.BuildScript, call *buildscript.FuncCall) error { + return nil +} diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 17070ef4ca..75085176d7 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -75,9 +75,11 @@ type Null struct { Null string `parser:"'null'"` } +type Values []*Value + type FuncCall struct { - Name string `parser:"@Ident"` - Arguments []*Value `parser:"'(' @@ (',' @@)* ','? ')'"` + Name string `parser:"@Ident"` + Arguments Values `parser:"'(' @@ (',' @@)* ','? ')'"` } // newString is a convenience function for constructing a string Value from an unquoted string. diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 8e3fbe8b03..540aaafba3 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -17,6 +17,10 @@ const atTimeKey = "at_time" // Unmarshal returns a structured form of the given AScript (on-disk format). func Unmarshal(data []byte) (*BuildScript, error) { + return UnmarshalWithProcessors(data, DefaultProcessors) +} + +func UnmarshalWithProcessors(data []byte, processors FuncProcessorMap) (*BuildScript, error) { parser, err := participle.Build[rawBuildScript]() if err != nil { return nil, errs.Wrap(err, "Could not create parser for build script") @@ -50,5 +54,5 @@ func Unmarshal(data []byte) (*BuildScript, error) { break } - return &BuildScript{raw}, nil + return &BuildScript{raw, processors}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 69483ddb4e..ba0896a7de 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -94,6 +94,16 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { requirements.List = transformRequirements(requirements).List } + // Invoke processors, which may modify function calls + for _, fn := range b.raw.FuncCalls() { + if processors, ok := DefaultProcessors[fn.Name]; ok { + for _, processor := range processors { + if err := processor.FromBuildExpression(b, fn); err != nil { + return errs.Wrap(err, "Custom marshaler for '%s' function failed", fn.Name) + } + } + } + } return nil } From d8e37a25285c5d08793bc7c5bf538f62e7bab553 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:18:37 -0700 Subject: [PATCH 189/440] Revert "Implemented initial processor pattern for buildscripts" This reverts commit 58985eb7a8f60625442ccb1079e2e4559b531ad6. --- cmd/state/main.go | 10 +-- internal/locale/locales/en-us.yaml | 6 -- pkg/buildscript/buildscript.go | 10 +-- pkg/buildscript/function_test.go | 55 -------------- pkg/buildscript/marshal_buildexpression.go | 10 --- pkg/buildscript/processor.go | 33 --------- .../processors/ingredient/processor.go | 73 ------------------- pkg/buildscript/raw.go | 6 +- pkg/buildscript/unmarshal.go | 6 +- pkg/buildscript/unmarshal_buildexpression.go | 10 --- 10 files changed, 5 insertions(+), 214 deletions(-) delete mode 100644 pkg/buildscript/function_test.go delete mode 100644 pkg/buildscript/processor.go delete mode 100644 pkg/buildscript/processors/ingredient/processor.go diff --git a/cmd/state/main.go b/cmd/state/main.go index 0f5210bfbe..65e11674d2 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -37,8 +37,6 @@ import ( "github.com/ActiveState/cli/internal/runbits/panics" "github.com/ActiveState/cli/internal/subshell" "github.com/ActiveState/cli/internal/svcctl" - "github.com/ActiveState/cli/pkg/buildscript" - "github.com/ActiveState/cli/pkg/buildscript/processors/ingredient" secretsapi "github.com/ActiveState/cli/pkg/platform/api/secrets" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" @@ -212,7 +210,6 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out pjNamespace = pj.Namespace().String() } - // Analytics an := anAsync.New(anaConst.SrcStateTool, svcmodel, cfg, auth, out, pjNamespace) defer func() { if err := events.WaitForEvents(time.Second, an.Wait); err != nil { @@ -236,13 +233,8 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out logging.Debug("Could not register secrets expander: %v", err) } - prime := primer.New(pj, out, auth, prompter, sshell, conditional, cfg, ipcClient, svcmodel, an) - - // Register buildscript functions - buildscript.RegisterDefaultProcessor(ingredient.NewProcessor(prime)) - // Run the actual command - cmds := cmdtree.New(prime, args...) + cmds := cmdtree.New(primer.New(pj, out, auth, prompter, sshell, conditional, cfg, ipcClient, svcmodel, an), args...) childCmd, err := cmds.Command().FindChild(args[1:]) if err != nil { diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 29939137a5..50558aa40b 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1560,9 +1560,3 @@ install_report_updated: other: "Updated: [NOTICE]{{.V0}}[/RESET]" install_report_removed: other: "Removed: [NOTICE]{{.V0}}[/RESET]" -err_marshalbuildexp_src_missing: - other: "Your buildscript contains an ingredient function that's missing the 'src' argument" -err_marshalbuildexp_src_invalid_type: - other: "Your buildscript contains an ingredient function that has an invalid value for the 'src' argument" -err_marshalbuildexp_src_item_invalid_type: - other: "Your buildscript contains an ingredient function that has an invalid value for the 'src' argument entry or entries" diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 11e9fd4e4a..747f2c4709 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -3,9 +3,7 @@ package buildscript import ( "time" - "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" - "github.com/brunoga/deep" ) // BuildScript is what we want consuming code to work with. This specifically makes the raw @@ -13,8 +11,7 @@ import ( // Instead this package should facilitate the use-case of the consuming code through convenience // methods that are easy to understand and work with. type BuildScript struct { - raw *rawBuildScript - processors FuncProcessorMap + raw *rawBuildScript } func init() { @@ -36,12 +33,7 @@ func Create() *BuildScript { } func New() *BuildScript { - return NewWithProcessors(DefaultProcessors) -} - -func NewWithProcessors(processors FuncProcessorMap) *BuildScript { bs := Create() - bs.processors = processors return bs } diff --git a/pkg/buildscript/function_test.go b/pkg/buildscript/function_test.go deleted file mode 100644 index 63adc463d5..0000000000 --- a/pkg/buildscript/function_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package buildscript_test - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/ActiveState/cli/pkg/buildscript" - "github.com/ActiveState/cli/pkg/buildscript/processors/ingredient" - "github.com/stretchr/testify/require" -) - -func TestMarshalBEIngredientAndReqFunc(t *testing.T) { - bs, err := buildscript.Unmarshal([]byte(` -main = ingredient( - name = "pytorch", - src = ["*/**.py", "pyproject.toml"], - deps = [ - Req(name="python", version=Eq(value="3.7.10")) - ] -) -`)) - require.NoError(t, err) - - marshaller := ingredient.NewProcessor(nil) - buildscript.RegisterFunctionProcessor("ingredient", marshaller.MarshalBuildExpression) - - data, err := bs.MarshalBuildExpression() - require.NoError(t, err) - - result := map[string]interface{}{} - require.NoError(t, json.Unmarshal(data, &result)) - - hash := getKey(t, result, "let", "in", "ingredient", "hash") - require.NotEmpty(t, hash) - - _ = data -} - -func getKey(t *testing.T, data map[string]interface{}, keys ...string) any { - var next any - var ok bool - for i, key := range keys { - next, ok = data[key] - if !ok { - t.Fatalf("key %s not found in data", strings.Join(keys[0:i+1], ".")) - } - if len(keys) > i+1 { - if data, ok = next.(map[string]interface{}); !ok { - t.Fatalf("key %s has non-map value: '%v'", strings.Join(keys[0:i+1], "."), next) - } - } - } - return next -} diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index 80aba8bf50..0e4bd481ad 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -27,16 +27,6 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { return nil, errs.Wrap(err, "Cannot clone raw build script") } - // Invoke processors, which may modify function calls - for _, fn := range raw.FuncCalls() { - if processors, ok := b.processors[fn.Name]; ok { - for _, processor := range processors { - if err := processor.ToBuildExpression(b, fn); err != nil { - return nil, errs.Wrap(err, "Custom marshaler for '%s' function failed", fn.Name) - } - } - } - } m := make(map[string]interface{}) let := make(map[string]interface{}) diff --git a/pkg/buildscript/processor.go b/pkg/buildscript/processor.go deleted file mode 100644 index bfe9344d97..0000000000 --- a/pkg/buildscript/processor.go +++ /dev/null @@ -1,33 +0,0 @@ -package buildscript - -type FuncProcessor interface { - FuncName() string - ToBuildExpression(*BuildScript, *FuncCall) error - FromBuildExpression(*BuildScript, *FuncCall) error -} - -type FuncProcessorMap map[string][]FuncProcessor - -var DefaultProcessors FuncProcessorMap - -func init() { - DefaultProcessors = make(FuncProcessorMap) -} - -func RegisterDefaultProcessor(marshaler FuncProcessor) { - name := marshaler.FuncName() - if _, ok := DefaultProcessors[name]; !ok { - DefaultProcessors[name] = []FuncProcessor{} - } - DefaultProcessors[name] = append(DefaultProcessors[name], marshaler) -} - -// RegisterProcessor registers a buildexpression marshaler for a buildscript function. -// Marshalers accept a buildscript Value, and marshals it to buildexpression JSON (e.g. an object). -// This is mainly (if not ONLY) used by tests, because for our main business logic we use the DefaultProcessors. -func (b *BuildScript) RegisterProcessor(name string, marshaler FuncProcessor) { - if _, ok := b.processors[name]; !ok { - b.processors[name] = []FuncProcessor{} - } - b.processors[name] = append(b.processors[name], marshaler) -} diff --git a/pkg/buildscript/processors/ingredient/processor.go b/pkg/buildscript/processors/ingredient/processor.go deleted file mode 100644 index 78693740da..0000000000 --- a/pkg/buildscript/processors/ingredient/processor.go +++ /dev/null @@ -1,73 +0,0 @@ -package ingredient - -import ( - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/runbits/rationalize" - "github.com/ActiveState/cli/pkg/buildscript" -) - -type Processor struct { - prime primeable -} - -type primeable interface { - primer.SvcModeler - primer.Projecter -} - -func NewProcessor(prime primeable) *Processor { - return &Processor{prime} -} - -func (p *Processor) FuncName() string { - return "ingredient" -} - -func (p *Processor) ToBuildExpression(script *buildscript.BuildScript, fn *buildscript.FuncCall) error { - pj := p.prime.Project() - if pj == nil { - return errs.Wrap(rationalize.ErrNoProject, "Need project to hash globs (for cwd)") - } - - var src *buildscript.Value - for _, arg := range fn.Arguments { - if arg.Assignment != nil && arg.Assignment.Key == "src" { - src = arg.Assignment.Value - } - } - if src == nil { - return locale.NewInputError("err_marshalbuildexp_src_missing") - } - if src.List == nil { - return locale.NewInputError("err_marshalbuildexp_src_invalid_type") - } - patterns := []string{} - for _, value := range *src.List { - if value.Str == nil { - return locale.NewInputError("err_marshalbuildexp_src_item_invalid_type") - } - patterns = append(patterns, *value.Str) - } - - hash, err := p.prime.SvcModel().HashGlobs(pj.Dir(), patterns) - if err != nil { - return errs.Wrap(err, "Could not hash globs") - } - - fn.Arguments = append(fn.Arguments, &buildscript.Value{ - Assignment: &buildscript.Assignment{ - Key: "hash", - Value: &buildscript.Value{ - Str: &hash, - }, - }, - }) - - return nil -} - -func (p *Processor) FromBuildExpression(script *buildscript.BuildScript, call *buildscript.FuncCall) error { - return nil -} diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 75085176d7..17070ef4ca 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -75,11 +75,9 @@ type Null struct { Null string `parser:"'null'"` } -type Values []*Value - type FuncCall struct { - Name string `parser:"@Ident"` - Arguments Values `parser:"'(' @@ (',' @@)* ','? ')'"` + Name string `parser:"@Ident"` + Arguments []*Value `parser:"'(' @@ (',' @@)* ','? ')'"` } // newString is a convenience function for constructing a string Value from an unquoted string. diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 540aaafba3..8e3fbe8b03 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -17,10 +17,6 @@ const atTimeKey = "at_time" // Unmarshal returns a structured form of the given AScript (on-disk format). func Unmarshal(data []byte) (*BuildScript, error) { - return UnmarshalWithProcessors(data, DefaultProcessors) -} - -func UnmarshalWithProcessors(data []byte, processors FuncProcessorMap) (*BuildScript, error) { parser, err := participle.Build[rawBuildScript]() if err != nil { return nil, errs.Wrap(err, "Could not create parser for build script") @@ -54,5 +50,5 @@ func UnmarshalWithProcessors(data []byte, processors FuncProcessorMap) (*BuildSc break } - return &BuildScript{raw, processors}, nil + return &BuildScript{raw}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index ba0896a7de..69483ddb4e 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -94,16 +94,6 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { requirements.List = transformRequirements(requirements).List } - // Invoke processors, which may modify function calls - for _, fn := range b.raw.FuncCalls() { - if processors, ok := DefaultProcessors[fn.Name]; ok { - for _, processor := range processors { - if err := processor.FromBuildExpression(b, fn); err != nil { - return errs.Wrap(err, "Custom marshaler for '%s' function failed", fn.Name) - } - } - } - } return nil } From a4de458e16d382371f69d07952ed775e2b3e344a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 10:49:41 -0700 Subject: [PATCH 190/440] Add missing imports --- pkg/buildscript/buildscript.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 747f2c4709..7a9e76f781 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -3,7 +3,9 @@ package buildscript import ( "time" + "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" + "github.com/brunoga/deep" ) // BuildScript is what we want consuming code to work with. This specifically makes the raw From 27d4e9d7ef42e24d796848bdfbde9fb90dbc5f50 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 7 Oct 2024 14:32:37 -0400 Subject: [PATCH 191/440] Build scripts can distinguish between solve nodes for different targets. Clients need to pass the target when querying requirements, platforms, etc. Use "" for the default target (i.e. the name assigned to 'main'). --- internal/runners/manifest/manifest.go | 2 +- internal/runners/platforms/remove.go | 2 +- internal/runners/uninstall/uninstall.go | 2 +- pkg/buildscript/mutations.go | 8 +- pkg/buildscript/mutations_test.go | 4 +- pkg/buildscript/queries.go | 83 ++++++++++++++++---- pkg/buildscript/queries_test.go | 4 +- pkg/buildscript/unmarshal.go | 10 +++ pkg/buildscript/unmarshal_buildexpression.go | 4 +- pkg/platform/model/checkpoints.go | 2 +- 10 files changed, 93 insertions(+), 28 deletions(-) diff --git a/internal/runners/manifest/manifest.go b/internal/runners/manifest/manifest.go index 19765d7257..627308b21e 100644 --- a/internal/runners/manifest/manifest.go +++ b/internal/runners/manifest/manifest.go @@ -117,7 +117,7 @@ func (m *Manifest) fetchRequirements() ([]buildscript.Requirement, error) { } } - reqs, err := script.Requirements() + reqs, err := script.Requirements("") if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index 85a56d33db..9fe28df540 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -73,7 +73,7 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { // Prepare updated buildscript script := oldCommit.BuildScript() - platforms, err := script.Platforms() + platforms, err := script.Platforms("") if err != nil { return errs.Wrap(err, "Failed to get platforms") } diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index be85debf5e..1b779d2a6b 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -165,7 +165,7 @@ func (u *Uninstall) renderUserFacing(reqs requirements) { func (u *Uninstall) resolveRequirements(script *buildscript.BuildScript, pkgs captain.PackagesValue) (requirements, error) { result := requirements{} - reqs, err := script.DependencyRequirements() + reqs, err := script.DependencyRequirements("") if err != nil { return nil, errs.Wrap(err, "Unable to get requirements") } diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 6c23b7a0d3..9bb269674d 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -61,7 +61,7 @@ func (b *BuildScript) AddRequirement(requirement types.Requirement) error { obj = append(obj, &Assignment{requirementVersionRequirementsKey, &Value{List: &values}}) } - requirementsNode, err := b.getRequirementsNode() + requirementsNode, err := b.getRequirementsNode("") if err != nil { return errs.Wrap(err, "Could not get requirements node") } @@ -81,7 +81,7 @@ type RequirementNotFoundError struct { // RemoveRequirement will remove any matching requirement. Note that it only operates on the Name and Namespace fields. // It will not verify if revision or version match. func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { - requirementsNode, err := b.getRequirementsNode() + requirementsNode, err := b.getRequirementsNode("") if err != nil { return errs.Wrap(err, "Could not get requirements node") } @@ -126,7 +126,7 @@ func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { } func (b *BuildScript) AddPlatform(platformID strfmt.UUID) error { - platformsNode, err := b.getPlatformsNode() + platformsNode, err := b.getPlatformsNode("") if err != nil { return errs.Wrap(err, "Could not get platforms node") } @@ -144,7 +144,7 @@ type PlatformNotFoundError struct { } func (b *BuildScript) RemovePlatform(platformID strfmt.UUID) error { - platformsNode, err := b.getPlatformsNode() + platformsNode, err := b.getPlatformsNode("") if err != nil { return errs.Wrap(err, "Could not get platforms node") } diff --git a/pkg/buildscript/mutations_test.go b/pkg/buildscript/mutations_test.go index f3cff0996f..69a671864e 100644 --- a/pkg/buildscript/mutations_test.go +++ b/pkg/buildscript/mutations_test.go @@ -277,7 +277,7 @@ func TestUpdateRequirements(t *testing.T) { return } - got, err := script.Requirements() + got, err := script.Requirements("") assert.NoError(t, err) gotReqs := []DependencyRequirement{} @@ -361,7 +361,7 @@ func TestUpdatePlatform(t *testing.T) { return } - got, err := script.Platforms() + got, err := script.Platforms("") assert.NoError(t, err) sort.Slice(got, func(i, j int) bool { return got[i] < got[j] }) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 2f4a1a6c54..9c6e60b678 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -12,6 +12,7 @@ import ( const ( solveFuncName = "solve" solveLegacyFuncName = "solve_legacy" + srcKey = "src" requirementsKey = "requirements" platformsKey = "platforms" ) @@ -43,8 +44,10 @@ type UnknownRequirement struct { func (r UnknownRequirement) IsRequirement() {} -func (b *BuildScript) Requirements() ([]Requirement, error) { - requirementsNode, err := b.getRequirementsNode() +// Returns the requirements for the given target. +// If the given target is the empty string, uses the default target (i.e. the name assigned to 'main'). +func (b *BuildScript) Requirements(target string) ([]Requirement, error) { + requirementsNode, err := b.getRequirementsNode(target) if err != nil { return nil, errs.Wrap(err, "Could not get requirements node") } @@ -95,8 +98,8 @@ func (b *BuildScript) Requirements() ([]Requirement, error) { // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, // which are the most common. // ONLY use this when you know you only need to care about dependencies. -func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { - reqs, err := b.Requirements() +func (b *BuildScript) DependencyRequirements(target string) ([]types.Requirement, error) { + reqs, err := b.Requirements(target) if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } @@ -109,8 +112,8 @@ func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { return deps, nil } -func (b *BuildScript) getRequirementsNode() (*Value, error) { - node, err := b.getSolveNode() +func (b *BuildScript) getRequirementsNode(target string) (*Value, error) { + node, err := b.getSolveNode(target) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -147,7 +150,23 @@ func getVersionRequirements(v *Value) []types.VersionRequirement { return reqs } -func (b *BuildScript) getSolveNode() (*Value, error) { +func isSolveFuncName(name string) bool { + return name == solveFuncName || name == solveLegacyFuncName +} + +func (b *BuildScript) getTargetNode(target string) (*Value, error) { + if target == "" { + for _, assignment := range b.raw.Assignments { + if assignment.Key != mainKey { + continue + } + if assignment.Value.Ident != nil { + target = *assignment.Value.Ident + break + } + } + } + var search func([]*Assignment) *Value search = func(assignments []*Assignment) *Value { var nextLet []*Assignment @@ -157,7 +176,13 @@ func (b *BuildScript) getSolveNode() (*Value, error) { continue } - if f := a.Value.FuncCall; f != nil && (f.Name == solveFuncName || f.Name == solveLegacyFuncName) { + if a.Key == target && a.Value.FuncCall != nil { + return a.Value + } + + if f := a.Value.FuncCall; target == "" && f != nil && isSolveFuncName(f.Name) { + // This is coming from a complex build expression with no straightforward way to determine + // a default target. Fall back on a top-level solve node. return a.Value } } @@ -169,15 +194,45 @@ func (b *BuildScript) getSolveNode() (*Value, error) { return nil } + if node := search(b.raw.Assignments); node != nil { return node, nil } + return nil, errNodeNotFound +} + +func (b *BuildScript) getSolveNode(target string) (*Value, error) { + node, err := b.getTargetNode(target) + if err != nil { + return nil, errs.Wrap(err, "Could not get target node") + } + + // If the target is the solve function, we're done. + if isSolveFuncName(node.FuncCall.Name) { + return node, nil + } + + // Otherwise, the "src" key contains a reference to the solve node. Look over the build expression + // again for that referenced node. + for _, arg := range node.FuncCall.Arguments { + if arg.Assignment == nil { + continue + } + a := arg.Assignment + if a.Key == srcKey && a.Value.Ident != nil { + node, err := b.getSolveNode(*a.Value.Ident) + if err != nil { + return nil, errs.Wrap(err, "Could not get solve node from target") + } + return node, nil + } + } return nil, errNodeNotFound } -func (b *BuildScript) getSolveAtTimeValue() (*Value, error) { - node, err := b.getSolveNode() +func (b *BuildScript) getSolveAtTimeValue(target string) (*Value, error) { + node, err := b.getSolveNode(target) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -191,8 +246,8 @@ func (b *BuildScript) getSolveAtTimeValue() (*Value, error) { return nil, errValueNotFound } -func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { - node, err := b.getPlatformsNode() +func (b *BuildScript) Platforms(target string) ([]strfmt.UUID, error) { + node, err := b.getPlatformsNode(target) if err != nil { return nil, errs.Wrap(err, "Could not get platform node") } @@ -204,8 +259,8 @@ func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { return list, nil } -func (b *BuildScript) getPlatformsNode() (*Value, error) { - node, err := b.getSolveNode() +func (b *BuildScript) getPlatformsNode(target string) (*Value, error) { + node, err := b.getSolveNode(target) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } diff --git a/pkg/buildscript/queries_test.go b/pkg/buildscript/queries_test.go index 9a107276ef..6aec6ef388 100644 --- a/pkg/buildscript/queries_test.go +++ b/pkg/buildscript/queries_test.go @@ -113,7 +113,7 @@ func TestRequirements(t *testing.T) { script, err := UnmarshalBuildExpression(data, nil) assert.NoError(t, err) - got, err := script.Requirements() + got, err := script.Requirements("") assert.NoError(t, err) gotReqs := []types.Requirement{} @@ -167,7 +167,7 @@ func TestRevision(t *testing.T) { script, err := UnmarshalBuildExpression(data, nil) assert.NoError(t, err) - got, err := script.Requirements() + got, err := script.Requirements("") assert.NoError(t, err) gotReqs := []RevisionRequirement{} diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 8e3fbe8b03..ffbab27f9e 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -50,5 +50,15 @@ func Unmarshal(data []byte) (*BuildScript, error) { break } + // Verify there are no duplicate key assignments. + // This is primarily to catch duplicate solve nodes for a given target. + seen := make(map[string]bool) + for _, assignment := range raw.Assignments { + if _, exists := seen[assignment.Key]; exists { + return nil, locale.NewInputError(locale.Tl("err_buildscript_duplicate_keys", "Build script has duplicate '{{.V0}}' assignments", assignment.Key)) + } + seen[assignment.Key] = true + } + return &BuildScript{raw}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index b157bef1ce..e392ea8669 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -87,7 +87,7 @@ func UnmarshalBuildExpression(data []byte, atTime *time.Time) (*BuildScript, err // Extract the 'at_time' from the solve node, if it exists, and change its value to be a // reference to "$at_time", which is how we want to show it in AScript format. - if atTimeNode, err := script.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { + if atTimeNode, err := script.getSolveAtTimeValue(""); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { atTime, err := strfmt.ParseDateTime(strValue(atTimeNode)) if err != nil { return nil, errs.Wrap(err, "Invalid timestamp: %s", strValue(atTimeNode)) @@ -107,7 +107,7 @@ func UnmarshalBuildExpression(data []byte, atTime *time.Time) (*BuildScript, err // requirements = [{"name": "", "namespace": ""}, {...}, ...] // then transform them into function call form for the AScript format, e.g. // requirements = [Req(name = "", namespace = ""), Req(...), ...] - requirements, err := script.getRequirementsNode() + requirements, err := script.getRequirementsNode("") if err != nil { return nil, errs.Wrap(err, "Could not get requirements node") } diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index 87f491a940..b34554deeb 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -76,7 +76,7 @@ func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([ // FetchLanguagesForBuildScript fetches a list of language names for the given buildscript func FetchLanguagesForBuildScript(script *buildscript.BuildScript) ([]Language, error) { languages := []Language{} - reqs, err := script.DependencyRequirements() + reqs, err := script.DependencyRequirements("") if err != nil { return nil, errs.Wrap(err, "failed to get dependency requirements") } From aed5512f24e083329597d619bc2e27fcc711b114 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 7 Oct 2024 15:49:32 -0400 Subject: [PATCH 192/440] Transform all requirement lists from legacy format into the Req() format. Previously, build scripts with multiple targets only had their default target's requirements transformed. --- pkg/buildscript/unmarshal_buildexpression.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index e392ea8669..6adec4b1f1 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -103,18 +103,6 @@ func UnmarshalBuildExpression(data []byte, atTime *time.Time) (*BuildScript, err script.raw.AtTime = atTime } - // If the requirements are in legacy object form, e.g. - // requirements = [{"name": "", "namespace": ""}, {...}, ...] - // then transform them into function call form for the AScript format, e.g. - // requirements = [Req(name = "", namespace = ""), Req(...), ...] - requirements, err := script.getRequirementsNode("") - if err != nil { - return nil, errs.Wrap(err, "Could not get requirements node") - } - if isLegacyRequirementsList(requirements) { - requirements.List = transformRequirements(requirements).List - } - return script, nil } @@ -295,6 +283,13 @@ func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, erro if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface) } + if key == requirementsKey && isSolveFuncName(name) && isLegacyRequirementsList(value) { + // If the requirements are in legacy object form, e.g. + // requirements = [{"name": "", "namespace": ""}, {...}, ...] + // then transform them into function call form for the AScript format, e.g. + // requirements = [Req(name = "", namespace = ""), Req(...), ...] + value.List = transformRequirements(value).List + } args = append(args, &Value{Assignment: &Assignment{key, value}}) } sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Key < args[j].Assignment.Key }) From bf5765e3e64e2061d6e899f409ce747ed165d5af Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Mon, 7 Oct 2024 13:30:03 -0700 Subject: [PATCH 193/440] Expose function calls without exposing low level symbols --- pkg/buildscript/buildscript.go | 26 +++ pkg/buildscript/marshal.go | 12 +- pkg/buildscript/marshal_buildexpression.go | 15 +- pkg/buildscript/mutations.go | 12 +- pkg/buildscript/queries.go | 16 +- pkg/buildscript/raw.go | 67 +++++--- pkg/buildscript/raw_test.go | 168 +++++++++---------- pkg/buildscript/unmarshal_buildexpression.go | 73 ++++---- 8 files changed, 218 insertions(+), 171 deletions(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 7a9e76f781..7384f378d5 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -66,3 +66,29 @@ func (b *BuildScript) Clone() (*BuildScript, error) { } return bb, nil } + +// FuncCall is the exportable version of funcCall, because we do not want to expose low level buildscript functionality +// outside of the buildscript package. +type FuncCall struct { + fc *funcCall +} + +func (f FuncCall) Argument(name string) any { + for _, a := range f.fc.Arguments { + if a.Assignment == nil || a.Assignment.Key != name { + continue + } + return a.Assignment.Value.Value() + } + return nil +} + +func (b *BuildScript) FunctionCalls(name string) []FuncCall { + result := []FuncCall{} + for _, f := range b.raw.FuncCalls() { + if f.Name == name { + result = append(result, FuncCall{f}) + } + } + return result +} diff --git a/pkg/buildscript/marshal.go b/pkg/buildscript/marshal.go index dfb8e2ff2b..c647616f47 100644 --- a/pkg/buildscript/marshal.go +++ b/pkg/buildscript/marshal.go @@ -30,11 +30,11 @@ func (b *BuildScript) Marshal() ([]byte, error) { if b.raw.AtTime != nil { buf.WriteString(assignmentString( - &Assignment{atTimeKey, newString(b.raw.AtTime.Format(strfmt.RFC3339Millis))})) + &assignment{atTimeKey, newString(b.raw.AtTime.Format(strfmt.RFC3339Millis))})) buf.WriteString("\n") } - var main *Assignment + var main *assignment for _, assignment := range b.raw.Assignments { if assignment.Key == mainKey { main = assignment @@ -50,7 +50,7 @@ func (b *BuildScript) Marshal() ([]byte, error) { return []byte(buf.String()), nil } -func assignmentString(a *Assignment) string { +func assignmentString(a *assignment) string { return fmt.Sprintf("%s = %s", a.Key, valueString(a.Value)) } @@ -58,7 +58,7 @@ func indentByTab(s string) string { return fmt.Sprintf("\t%s", strings.ReplaceAll(s, "\n", "\n\t")) } -func valueString(v *Value) string { +func valueString(v *value) string { switch { case v.FuncCall != nil: return funcCallString(v.FuncCall) @@ -118,7 +118,7 @@ var inlineFunctions = []string{ andFuncName, } -func funcCallString(f *FuncCall) string { +func funcCallString(f *funcCall) string { var ( newline = "\n" comma = "," @@ -142,7 +142,7 @@ func funcCallString(f *FuncCall) string { return buf.String() } -func argsToString(args []*Value, newline, comma string, indent func(string) string) string { +func argsToString(args []*value, newline, comma string, indent func(string) string) string { buf := bytes.Buffer{} for i, argument := range args { buf.WriteString(indent(valueString(argument))) diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index 0e4bd481ad..513380852f 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -27,7 +27,6 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { return nil, errs.Wrap(err, "Cannot clone raw build script") } - m := make(map[string]interface{}) let := make(map[string]interface{}) for _, assignment := range raw.Assignments { @@ -56,13 +55,13 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { // Note: all of the MarshalJSON functions are named the way they are because Go's JSON package // specifically looks for them. -func (a *Assignment) MarshalJSON() ([]byte, error) { +func (a *assignment) MarshalJSON() ([]byte, error) { m := make(map[string]interface{}) m[a.Key] = a.Value return json.Marshal(m) } -func (v *Value) MarshalJSON() ([]byte, error) { +func (v *value) MarshalJSON() ([]byte, error) { switch { case v.FuncCall != nil: return json.Marshal(v.FuncCall) @@ -85,10 +84,10 @@ func (v *Value) MarshalJSON() ([]byte, error) { case v.Ident != nil: return json.Marshal("$" + *v.Ident) } - return json.Marshal([]*Value{}) // participle does not create v.List if it's empty + return json.Marshal([]*value{}) // participle does not create v.List if it's empty } -func (f *FuncCall) MarshalJSON() ([]byte, error) { +func (f *funcCall) MarshalJSON() ([]byte, error) { if f.Name == reqFuncName { return marshalReq(f) } @@ -113,7 +112,7 @@ func (f *FuncCall) MarshalJSON() ([]byte, error) { // marshalReq translates a Req() function into its equivalent buildexpression requirement object. // This is needed until buildexpressions support functions as requirements. Once they do, we can // remove this method entirely. -func marshalReq(fn *FuncCall) ([]byte, error) { +func marshalReq(fn *funcCall) ([]byte, error) { if fn.Name == reqFuncName { return marshalReq(fn.Arguments[0].FuncCall) } @@ -140,8 +139,8 @@ func marshalReq(fn *FuncCall) ([]byte, error) { // {"version_requirements": [{"comparator": "", "version": ""}]} case assignment.Key == requirementVersionKey && assignment.Value.FuncCall != nil: requirements := make([]interface{}, 0) - var addRequirement func(*FuncCall) error // recursive function for adding to requirements list - addRequirement = func(funcCall *FuncCall) error { + var addRequirement func(*funcCall) error // recursive function for adding to requirements list + addRequirement = func(funcCall *funcCall) error { switch name := funcCall.Name; name { case eqFuncName, neFuncName, gtFuncName, gteFuncName, ltFuncName, lteFuncName: req := make(map[string]string) diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 6c23b7a0d3..5114f1afb0 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -41,24 +41,24 @@ func (b *BuildScript) AddRequirement(requirement types.Requirement) error { } // Use object form for now, and then transform it into function form later. - obj := []*Assignment{ + obj := []*assignment{ {requirementNameKey, newString(requirement.Name)}, {requirementNamespaceKey, newString(requirement.Namespace)}, } if requirement.Revision != nil { - obj = append(obj, &Assignment{requirementRevisionKey, &Value{Number: ptr.To(float64(*requirement.Revision))}}) + obj = append(obj, &assignment{requirementRevisionKey, &value{Number: ptr.To(float64(*requirement.Revision))}}) } if requirement.VersionRequirement != nil { - values := []*Value{} + values := []*value{} for _, req := range requirement.VersionRequirement { - values = append(values, &Value{Object: &[]*Assignment{ + values = append(values, &value{Object: &[]*assignment{ {requirementComparatorKey, newString(req[requirementComparatorKey])}, {requirementVersionKey, newString(req[requirementVersionKey])}, }}) } - obj = append(obj, &Assignment{requirementVersionRequirementsKey, &Value{List: &values}}) + obj = append(obj, &assignment{requirementVersionRequirementsKey, &value{List: &values}}) } requirementsNode, err := b.getRequirementsNode() @@ -67,7 +67,7 @@ func (b *BuildScript) AddRequirement(requirement types.Requirement) error { } list := *requirementsNode.List - list = append(list, transformRequirement(&Value{Object: &obj})) + list = append(list, transformRequirement(&value{Object: &obj})) requirementsNode.List = &list return nil diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 2f4a1a6c54..4364c9611f 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -109,7 +109,7 @@ func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { return deps, nil } -func (b *BuildScript) getRequirementsNode() (*Value, error) { +func (b *BuildScript) getRequirementsNode() (*value, error) { node, err := b.getSolveNode() if err != nil { return nil, errs.Wrap(err, "Could not get solve node") @@ -124,7 +124,7 @@ func (b *BuildScript) getRequirementsNode() (*Value, error) { return nil, errNodeNotFound } -func getVersionRequirements(v *Value) []types.VersionRequirement { +func getVersionRequirements(v *value) []types.VersionRequirement { reqs := []types.VersionRequirement{} switch v.FuncCall.Name { @@ -147,10 +147,10 @@ func getVersionRequirements(v *Value) []types.VersionRequirement { return reqs } -func (b *BuildScript) getSolveNode() (*Value, error) { - var search func([]*Assignment) *Value - search = func(assignments []*Assignment) *Value { - var nextLet []*Assignment +func (b *BuildScript) getSolveNode() (*value, error) { + var search func([]*assignment) *value + search = func(assignments []*assignment) *value { + var nextLet []*assignment for _, a := range assignments { if a.Key == letKey { nextLet = *a.Value.Object // nested 'let' to search next @@ -176,7 +176,7 @@ func (b *BuildScript) getSolveNode() (*Value, error) { return nil, errNodeNotFound } -func (b *BuildScript) getSolveAtTimeValue() (*Value, error) { +func (b *BuildScript) getSolveAtTimeValue() (*value, error) { node, err := b.getSolveNode() if err != nil { return nil, errs.Wrap(err, "Could not get solve node") @@ -204,7 +204,7 @@ func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { return list, nil } -func (b *BuildScript) getPlatformsNode() (*Value, error) { +func (b *BuildScript) getPlatformsNode() (*value, error) { node, err := b.getSolveNode() if err != nil { return nil, errs.Wrap(err, "Could not get solve node") diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 17070ef4ca..f9749650c8 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -1,6 +1,8 @@ package buildscript import ( + "errors" + "fmt" "strconv" "strings" "time" @@ -11,7 +13,7 @@ import ( // Tagged fields will be filled in by Participle. type rawBuildScript struct { - Assignments []*Assignment `parser:"@@+"` + Assignments []*assignment `parser:"@@+"` AtTime *time.Time // set after initial read } @@ -23,8 +25,8 @@ func (r *rawBuildScript) clone() (*rawBuildScript, error) { return deep.Copy(r) } -func (r *rawBuildScript) FuncCalls() []*FuncCall { - result := []*FuncCall{} +func (r *rawBuildScript) FuncCalls() []*funcCall { + result := []*funcCall{} for _, a := range r.Assignments { result = append(result, a.Value.funcCalls()...) } @@ -32,8 +34,8 @@ func (r *rawBuildScript) FuncCalls() []*FuncCall { } // funcCalls will return all function calls recursively under the given value. -func (v *Value) funcCalls() []*FuncCall { - result := []*FuncCall{} +func (v *value) funcCalls() []*funcCall { + result := []*funcCall{} switch { case v.FuncCall != nil: result = append(result, v.FuncCall) @@ -54,40 +56,61 @@ func (v *Value) funcCalls() []*FuncCall { return result } -type Assignment struct { +type assignment struct { Key string `parser:"@Ident '='"` - Value *Value `parser:"@@"` + Value *value `parser:"@@"` } -type Value struct { - FuncCall *FuncCall `parser:"@@"` - List *[]*Value `parser:"| '[' (@@ (',' @@)* ','?)? ']'"` +type value struct { + FuncCall *funcCall `parser:"@@"` + List *[]*value `parser:"| '[' (@@ (',' @@)* ','?)? ']'"` Str *string `parser:"| @String"` // note: this value is ALWAYS quoted Number *float64 `parser:"| (@Float | @Int)"` - Null *Null `parser:"| @@"` + Null *null `parser:"| @@"` - Assignment *Assignment `parser:"| @@"` // only in FuncCall - Object *[]*Assignment `parser:"| '{' @@ (',' @@)* ','? '}'"` // only in List + Assignment *assignment `parser:"| @@"` // only in FuncCall + Object *[]*assignment `parser:"| '{' @@ (',' @@)* ','? '}'"` // only in List Ident *string `parser:"| @Ident"` // only in FuncCall or Assignment } -type Null struct { +// Value conveniently returns the property that holds the actual value +func (v *value) Value() interface{} { + switch { + case v.FuncCall != nil: + return v.FuncCall + case v.List != nil: + return *v.List + case v.Str != nil: + return strValue(v) + case v.Number != nil: + return *v.Number + case v.Null != nil: + return nil + case v.Assignment != nil: + return v.Assignment + case v.Object != nil: + return v.Object + } + return errors.New(fmt.Sprintf("unknown value type: %#v", v)) +} + +type null struct { Null string `parser:"'null'"` } -type FuncCall struct { +type funcCall struct { Name string `parser:"@Ident"` - Arguments []*Value `parser:"'(' @@ (',' @@)* ','? ')'"` + Arguments []*value `parser:"'(' @@ (',' @@)* ','? ')'"` } -// newString is a convenience function for constructing a string Value from an unquoted string. -// Use this instead of &Value{Str: ptr.To(strconv.Quote(s))} -func newString(s string) *Value { - return &Value{Str: ptr.To(strconv.Quote(s))} +// newString is a convenience function for constructing a string value from an unquoted string. +// Use this instead of &value{Str: ptr.To(strconv.Quote(s))} +func newString(s string) *value { + return &value{Str: ptr.To(strconv.Quote(s))} } -// strValue is a convenience function for retrieving an unquoted string from Value. +// strValue is a convenience function for retrieving an unquoted string from value. // Use this instead of strings.Trim(*v.Str, `"`) -func strValue(v *Value) string { +func strValue(v *value) string { return strings.Trim(*v.Str, `"`) } diff --git a/pkg/buildscript/raw_test.go b/pkg/buildscript/raw_test.go index 1c90fd9948..fb56c6f46c 100644 --- a/pkg/buildscript/raw_test.go +++ b/pkg/buildscript/raw_test.go @@ -32,34 +32,34 @@ main = runtime atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ - []*Assignment{ - {"runtime", &Value{ - FuncCall: &FuncCall{"solve", []*Value{ - {Assignment: &Assignment{"at_time", &Value{Ident: ptr.To(`at_time`)}}}, - {Assignment: &Assignment{ - "platforms", &Value{List: &[]*Value{ + []*assignment{ + {"runtime", &value{ + FuncCall: &funcCall{"solve", []*value{ + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{ + "platforms", &value{List: &[]*value{ {Str: ptr.To(`"linux"`)}, {Str: ptr.To(`"windows"`)}, }}, }}, - {Assignment: &Assignment{ - "requirements", &Value{List: &[]*Value{ - {FuncCall: &FuncCall{ + {Assignment: &assignment{ + "requirements", &value{List: &[]*value{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("python")}}, - {Assignment: &Assignment{"namespace", newString("language")}}, + Arguments: []*value{ + {Assignment: &assignment{"name", newString("python")}}, + {Assignment: &assignment{"namespace", newString("language")}}, }}}, - {FuncCall: &FuncCall{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("requests")}}, - {Assignment: &Assignment{"namespace", newString("language/python")}}, - {Assignment: &Assignment{ - "version", &Value{FuncCall: &FuncCall{ + Arguments: []*value{ + {Assignment: &assignment{"name", newString("requests")}}, + {Assignment: &assignment{"namespace", newString("language/python")}}, + {Assignment: &assignment{ + "version", &value{FuncCall: &funcCall{ Name: "Eq", - Arguments: []*Value{ - {Assignment: &Assignment{"value", newString("3.10.10")}}, + Arguments: []*value{ + {Assignment: &assignment{"value", newString("3.10.10")}}, }, }}, }}, @@ -67,10 +67,10 @@ main = runtime }}, }}, }}, - {Assignment: &Assignment{"solver_version", &Value{Null: &Null{}}}}, + {Assignment: &assignment{"solver_version", &value{Null: &null{}}}}, }}, }}, - {"main", &Value{Ident: ptr.To("runtime")}}, + {"main", &value{Ident: ptr.To("runtime")}}, }, &atTime, }, script.raw) @@ -107,49 +107,49 @@ main = merge( atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ - []*Assignment{ - {"linux_runtime", &Value{ - FuncCall: &FuncCall{"solve", []*Value{ - {Assignment: &Assignment{"at_time", &Value{Ident: ptr.To(`at_time`)}}}, - {Assignment: &Assignment{ - "requirements", &Value{List: &[]*Value{ - {FuncCall: &FuncCall{ + []*assignment{ + {"linux_runtime", &value{ + FuncCall: &funcCall{"solve", []*value{ + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{ + "requirements", &value{List: &[]*value{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("python")}}, - {Assignment: &Assignment{"namespace", newString("language")}}, + Arguments: []*value{ + {Assignment: &assignment{"name", newString("python")}}, + {Assignment: &assignment{"namespace", newString("language")}}, }, }}, }}, }}, - {Assignment: &Assignment{ - "platforms", &Value{List: &[]*Value{{Str: ptr.To(`"67890"`)}}}, + {Assignment: &assignment{ + "platforms", &value{List: &[]*value{{Str: ptr.To(`"67890"`)}}}, }}, }}, }}, - {"win_runtime", &Value{ - FuncCall: &FuncCall{"solve", []*Value{ - {Assignment: &Assignment{"at_time", &Value{Ident: ptr.To(`at_time`)}}}, - {Assignment: &Assignment{ - "requirements", &Value{List: &[]*Value{ - {FuncCall: &FuncCall{ + {"win_runtime", &value{ + FuncCall: &funcCall{"solve", []*value{ + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{ + "requirements", &value{List: &[]*value{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("perl")}}, - {Assignment: &Assignment{"namespace", newString("language")}}, + Arguments: []*value{ + {Assignment: &assignment{"name", newString("perl")}}, + {Assignment: &assignment{"namespace", newString("language")}}, }, }}, }}, }}, - {Assignment: &Assignment{ - "platforms", &Value{List: &[]*Value{{Str: ptr.To(`"12345"`)}}}, + {Assignment: &assignment{ + "platforms", &value{List: &[]*value{{Str: ptr.To(`"12345"`)}}}, }}, }}, }}, - {"main", &Value{ - FuncCall: &FuncCall{"merge", []*Value{ - {FuncCall: &FuncCall{"win_installer", []*Value{{Ident: ptr.To("win_runtime")}}}}, - {FuncCall: &FuncCall{"tar_installer", []*Value{{Ident: ptr.To("linux_runtime")}}}}, + {"main", &value{ + FuncCall: &funcCall{"merge", []*value{ + {FuncCall: &funcCall{"win_installer", []*value{{Ident: ptr.To("win_runtime")}}}}, + {FuncCall: &funcCall{"tar_installer", []*value{{Ident: ptr.To("linux_runtime")}}}}, }}}}, }, &atTime, @@ -179,59 +179,59 @@ func TestComplexVersions(t *testing.T) { atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ - []*Assignment{ - {"runtime", &Value{ - FuncCall: &FuncCall{"solve", []*Value{ - {Assignment: &Assignment{"at_time", &Value{Ident: ptr.To(`at_time`)}}}, - {Assignment: &Assignment{ - "platforms", &Value{List: &[]*Value{ + []*assignment{ + {"runtime", &value{ + FuncCall: &funcCall{"solve", []*value{ + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{ + "platforms", &value{List: &[]*value{ {Str: ptr.To(`"96b7e6f2-bebf-564c-bc1c-f04482398f38"`)}, {Str: ptr.To(`"96b7e6f2-bebf-564c-bc1c-f04482398f38"`)}, }}, }}, - {Assignment: &Assignment{ - "requirements", &Value{List: &[]*Value{ - {FuncCall: &FuncCall{ + {Assignment: &assignment{ + "requirements", &value{List: &[]*value{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("python")}}, - {Assignment: &Assignment{"namespace", newString("language")}}, + Arguments: []*value{ + {Assignment: &assignment{"name", newString("python")}}, + {Assignment: &assignment{"namespace", newString("language")}}, }, }}, - {FuncCall: &FuncCall{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("requests")}}, - {Assignment: &Assignment{"namespace", newString("language/python")}}, - {Assignment: &Assignment{ - "version", &Value{FuncCall: &FuncCall{ + Arguments: []*value{ + {Assignment: &assignment{"name", newString("requests")}}, + {Assignment: &assignment{"namespace", newString("language/python")}}, + {Assignment: &assignment{ + "version", &value{FuncCall: &funcCall{ Name: "Eq", - Arguments: []*Value{ - {Assignment: &Assignment{Key: "value", Value: newString("3.10.10")}}, + Arguments: []*value{ + {Assignment: &assignment{Key: "value", Value: newString("3.10.10")}}, }, }}, }}, }, }}, - {FuncCall: &FuncCall{ + {FuncCall: &funcCall{ Name: "Req", - Arguments: []*Value{ - {Assignment: &Assignment{"name", newString("argparse")}}, - {Assignment: &Assignment{"namespace", newString("language/python")}}, - {Assignment: &Assignment{ - "version", &Value{FuncCall: &FuncCall{ + Arguments: []*value{ + {Assignment: &assignment{"name", newString("argparse")}}, + {Assignment: &assignment{"namespace", newString("language/python")}}, + {Assignment: &assignment{ + "version", &value{FuncCall: &funcCall{ Name: "And", - Arguments: []*Value{ - {Assignment: &Assignment{Key: "left", Value: &Value{FuncCall: &FuncCall{ + Arguments: []*value{ + {Assignment: &assignment{Key: "left", Value: &value{FuncCall: &funcCall{ Name: "Gt", - Arguments: []*Value{ - {Assignment: &Assignment{Key: "value", Value: newString("1.0")}}, + Arguments: []*value{ + {Assignment: &assignment{Key: "value", Value: newString("1.0")}}, }, }}}}, - {Assignment: &Assignment{Key: "right", Value: &Value{FuncCall: &FuncCall{ + {Assignment: &assignment{Key: "right", Value: &value{FuncCall: &funcCall{ Name: "Lt", - Arguments: []*Value{ - {Assignment: &Assignment{Key: "value", Value: newString("2.0")}}, + Arguments: []*value{ + {Assignment: &assignment{Key: "value", Value: newString("2.0")}}, }, }}}}, }, @@ -241,10 +241,10 @@ func TestComplexVersions(t *testing.T) { }}, }}, }}, - {Assignment: &Assignment{"solver_version", &Value{Number: ptr.To(float64(0))}}}, + {Assignment: &assignment{"solver_version", &value{Number: ptr.To(float64(0))}}}, }}, }}, - {"main", &Value{Ident: ptr.To("runtime")}}, + {"main", &value{Ident: ptr.To("runtime")}}, }, &atTime, }, script.raw) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 69483ddb4e..6c0fda3656 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -94,7 +94,6 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { requirements.List = transformRequirements(requirements).List } - return nil } @@ -106,7 +105,7 @@ const ( ctxIn = "in" ) -func unmarshalAssignments(path []string, m map[string]interface{}) ([]*Assignment, error) { +func unmarshalAssignments(path []string, m map[string]interface{}) ([]*assignment, error) { path = append(path, ctxAssignments) defer func() { _, _, err := sliceutils.Pop(path) @@ -115,9 +114,9 @@ func unmarshalAssignments(path []string, m map[string]interface{}) ([]*Assignmen } }() - assignments := []*Assignment{} + assignments := []*assignment{} for key, valueInterface := range m { - var value *Value + var value *value var err error if key != inKey { value, err = unmarshalValue(path, valueInterface) @@ -129,7 +128,7 @@ func unmarshalAssignments(path []string, m map[string]interface{}) ([]*Assignmen if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' key's value: %v", key, valueInterface) } - assignments = append(assignments, &Assignment{key, value}) + assignments = append(assignments, &assignment{key, value}) } sort.SliceStable(assignments, func(i, j int) bool { @@ -138,7 +137,7 @@ func unmarshalAssignments(path []string, m map[string]interface{}) ([]*Assignmen return assignments, nil } -func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { +func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { path = append(path, ctxValue) defer func() { _, _, err := sliceutils.Pop(path) @@ -147,7 +146,7 @@ func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { } }() - value := &Value{} + value := &value{} switch v := valueInterface.(type) { case map[string]interface{}: @@ -183,7 +182,7 @@ func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { } case []interface{}: - values := []*Value{} + values := []*value{} for _, item := range v { value, err := unmarshalValue(path, item) if err != nil { @@ -204,11 +203,11 @@ func unmarshalValue(path []string, valueInterface interface{}) (*Value, error) { value.Number = ptr.To(v) case nil: - value.Null = &Null{} + value.Null = &null{} default: logging.Debug("Unknown type: %T at path %s", v, strings.Join(path, ".")) - value.Null = &Null{} + value.Null = &null{} } return value, nil @@ -227,7 +226,7 @@ func isFuncCall(path []string, value map[string]interface{}) bool { return !hasIn || sliceutils.Contains(path, ctxAssignments) } -func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*FuncCall, error) { +func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*funcCall, error) { path = append(path, ctxFuncCall) defer func() { _, _, err := sliceutils.Pop(path) @@ -256,7 +255,7 @@ func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*FuncCal break // technically this is not needed since there's only one element in m } - args := []*Value{} + args := []*value{} switch v := argsInterface.(type) { case map[string]interface{}: @@ -265,7 +264,7 @@ func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*FuncCal if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface) } - args = append(args, &Value{Assignment: &Assignment{key, value}}) + args = append(args, &value{Assignment: &assignment{key, value}}) } sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Key < args[j].Assignment.Key }) @@ -282,10 +281,10 @@ func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*FuncCal return nil, errs.New("Function '%s' expected to be object or list", name) } - return &FuncCall{Name: name, Arguments: args}, nil + return &funcCall{Name: name, Arguments: args}, nil } -func unmarshalIn(path []string, inValue interface{}) (*Value, error) { +func unmarshalIn(path []string, inValue interface{}) (*value, error) { path = append(path, ctxIn) defer func() { _, _, err := sliceutils.Pop(path) @@ -294,7 +293,7 @@ func unmarshalIn(path []string, inValue interface{}) (*Value, error) { } }() - in := &Value{} + in := &value{} switch v := inValue.(type) { case map[string]interface{}: @@ -321,19 +320,19 @@ func unmarshalIn(path []string, inValue interface{}) (*Value, error) { // {"name": "", "namespace": ""}, // ..., // ] -func isLegacyRequirementsList(value *Value) bool { +func isLegacyRequirementsList(value *value) bool { return len(*value.List) > 0 && (*value.List)[0].Object != nil } // transformRequirements transforms a build expression list of requirements in object form into a // list of requirements in function-call form, which is how requirements are represented in // buildscripts. -func transformRequirements(reqs *Value) *Value { - newReqs := []*Value{} +func transformRequirements(reqs *value) *value { + newReqs := []*value{} for _, req := range *reqs.List { newReqs = append(newReqs, transformRequirement(req)) } - return &Value{List: &newReqs} + return &value{List: &newReqs} } // transformRequirement transforms a build expression requirement in object form into a requirement @@ -346,8 +345,8 @@ func transformRequirements(reqs *Value) *Value { // into something like // // Req(name = "", namespace = "", version = (value = "")) -func transformRequirement(req *Value) *Value { - args := []*Value{} +func transformRequirement(req *value) *value { + args := []*value{} for _, arg := range *req.Object { key := arg.Key @@ -356,14 +355,14 @@ func transformRequirement(req *Value) *Value { // Transform the version value from the requirement object. if key == requirementVersionRequirementsKey { key = requirementVersionKey - value = &Value{FuncCall: transformVersion(arg)} + value = &value{funcCall: transformVersion(arg)} } // Add the argument to the function transformation. - args = append(args, &Value{Assignment: &Assignment{key, value}}) + args = append(args, &value{Assignment: &assignment{key, value}}) } - return &Value{FuncCall: &FuncCall{reqFuncName, args}} + return &value{FuncCall: &funcCall{reqFuncName, args}} } // transformVersion transforms a build expression version_requirements list in object form into @@ -375,15 +374,15 @@ func transformRequirement(req *Value) *Value { // into something like // // And((value = ""), (value = "")) -func transformVersion(requirements *Assignment) *FuncCall { - var funcs []*FuncCall +func transformVersion(requirements *assignment) *funcCall { + var funcs []*funcCall for _, constraint := range *requirements.Value.List { - f := &FuncCall{} + f := &funcCall{} for _, o := range *constraint.Object { switch o.Key { case requirementVersionKey: - f.Arguments = []*Value{ - {Assignment: &Assignment{"value", o.Value}}, + f.Arguments = []*value{ + {Assignment: &assignment{"value", o.Value}}, } case requirementComparatorKey: f.Name = cases.Title(language.English).String(strValue(o.Value)) @@ -400,17 +399,17 @@ func transformVersion(requirements *Assignment) *FuncCall { // Iterate backwards over the requirements array and construct a binary tree of 'And()' functions. // For example, given [Gt(value = "1.0"), Ne(value = "2.0"), Lt(value = "3.0")], produce: // And(left = Gt(value = "1.0"), right = And(left = Ne(value = "2.0"), right = Lt(value = "3.0"))) - var f *FuncCall + var f *funcCall for i := len(funcs) - 2; i >= 0; i-- { - right := &Value{FuncCall: funcs[i+1]} + right := &value{FuncCall: funcs[i+1]} if f != nil { - right = &Value{FuncCall: f} + right = &value{FuncCall: f} } - args := []*Value{ - {Assignment: &Assignment{"left", &Value{FuncCall: funcs[i]}}}, - {Assignment: &Assignment{"right", right}}, + args := []*value{ + {Assignment: &assignment{"left", &value{FuncCall: funcs[i]}}}, + {Assignment: &assignment{"right", right}}, } - f = &FuncCall{andFuncName, args} + f = &funcCall{andFuncName, args} } return f } From 1b4f3f3918d6aec9795be8ebbe70c5b9f6401d8a Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 8 Oct 2024 10:45:57 -0400 Subject: [PATCH 194/440] Apply suggested name from PR review. --- scripts/{githooks/pre-commit => git-hooks/buildscript-commit} | 2 ++ 1 file changed, 2 insertions(+) rename scripts/{githooks/pre-commit => git-hooks/buildscript-commit} (73%) diff --git a/scripts/githooks/pre-commit b/scripts/git-hooks/buildscript-commit similarity index 73% rename from scripts/githooks/pre-commit rename to scripts/git-hooks/buildscript-commit index 373e0f5114..5077f0a0ed 100755 --- a/scripts/githooks/pre-commit +++ b/scripts/git-hooks/buildscript-commit @@ -1,4 +1,6 @@ #!/bin/bash +# Git pre-commit hook to run `state commit` prior to committing any build script changes. +# Usage: call this script from your git pre-commit hook or use it as your pre-commit hook. if git diff --name-only --cached | grep -q buildscript.as; then echo "Running 'state commit', as buildscript.as is being committed." From a91c2c8fe474deb30661dd04b7958422488c1b92 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 8 Oct 2024 11:30:04 -0400 Subject: [PATCH 195/440] Do not consider blocked artifacts as failed. They're waiting on something else. --- pkg/buildplan/filters.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/buildplan/filters.go b/pkg/buildplan/filters.go index b68fa0e93c..7bf552ce34 100644 --- a/pkg/buildplan/filters.go +++ b/pkg/buildplan/filters.go @@ -65,8 +65,7 @@ func FilterSuccessfulArtifacts() FilterArtifact { func FilterFailedArtifacts() FilterArtifact { return func(a *Artifact) bool { - return a.Status == types.ArtifactBlocked || - a.Status == types.ArtifactFailedTransiently || + return a.Status == types.ArtifactFailedTransiently || a.Status == types.ArtifactFailedPermanently } } From 683a4577614c08a0e0e8403a3d39c219addb9ba1 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 8 Oct 2024 13:24:27 -0400 Subject: [PATCH 196/440] Make targets variadic. --- internal/runners/manifest/manifest.go | 2 +- internal/runners/platforms/remove.go | 2 +- internal/runners/uninstall/uninstall.go | 2 +- pkg/buildscript/mutations.go | 8 +-- pkg/buildscript/mutations_test.go | 4 +- pkg/buildscript/queries.go | 52 +++++++++++--------- pkg/buildscript/queries_test.go | 4 +- pkg/buildscript/unmarshal_buildexpression.go | 2 +- pkg/platform/model/checkpoints.go | 2 +- 9 files changed, 42 insertions(+), 36 deletions(-) diff --git a/internal/runners/manifest/manifest.go b/internal/runners/manifest/manifest.go index 627308b21e..19765d7257 100644 --- a/internal/runners/manifest/manifest.go +++ b/internal/runners/manifest/manifest.go @@ -117,7 +117,7 @@ func (m *Manifest) fetchRequirements() ([]buildscript.Requirement, error) { } } - reqs, err := script.Requirements("") + reqs, err := script.Requirements() if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index 9fe28df540..85a56d33db 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -73,7 +73,7 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { // Prepare updated buildscript script := oldCommit.BuildScript() - platforms, err := script.Platforms("") + platforms, err := script.Platforms() if err != nil { return errs.Wrap(err, "Failed to get platforms") } diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index 1b779d2a6b..be85debf5e 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -165,7 +165,7 @@ func (u *Uninstall) renderUserFacing(reqs requirements) { func (u *Uninstall) resolveRequirements(script *buildscript.BuildScript, pkgs captain.PackagesValue) (requirements, error) { result := requirements{} - reqs, err := script.DependencyRequirements("") + reqs, err := script.DependencyRequirements() if err != nil { return nil, errs.Wrap(err, "Unable to get requirements") } diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 9bb269674d..6c23b7a0d3 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -61,7 +61,7 @@ func (b *BuildScript) AddRequirement(requirement types.Requirement) error { obj = append(obj, &Assignment{requirementVersionRequirementsKey, &Value{List: &values}}) } - requirementsNode, err := b.getRequirementsNode("") + requirementsNode, err := b.getRequirementsNode() if err != nil { return errs.Wrap(err, "Could not get requirements node") } @@ -81,7 +81,7 @@ type RequirementNotFoundError struct { // RemoveRequirement will remove any matching requirement. Note that it only operates on the Name and Namespace fields. // It will not verify if revision or version match. func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { - requirementsNode, err := b.getRequirementsNode("") + requirementsNode, err := b.getRequirementsNode() if err != nil { return errs.Wrap(err, "Could not get requirements node") } @@ -126,7 +126,7 @@ func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { } func (b *BuildScript) AddPlatform(platformID strfmt.UUID) error { - platformsNode, err := b.getPlatformsNode("") + platformsNode, err := b.getPlatformsNode() if err != nil { return errs.Wrap(err, "Could not get platforms node") } @@ -144,7 +144,7 @@ type PlatformNotFoundError struct { } func (b *BuildScript) RemovePlatform(platformID strfmt.UUID) error { - platformsNode, err := b.getPlatformsNode("") + platformsNode, err := b.getPlatformsNode() if err != nil { return errs.Wrap(err, "Could not get platforms node") } diff --git a/pkg/buildscript/mutations_test.go b/pkg/buildscript/mutations_test.go index 69a671864e..f3cff0996f 100644 --- a/pkg/buildscript/mutations_test.go +++ b/pkg/buildscript/mutations_test.go @@ -277,7 +277,7 @@ func TestUpdateRequirements(t *testing.T) { return } - got, err := script.Requirements("") + got, err := script.Requirements() assert.NoError(t, err) gotReqs := []DependencyRequirement{} @@ -361,7 +361,7 @@ func TestUpdatePlatform(t *testing.T) { return } - got, err := script.Platforms("") + got, err := script.Platforms() assert.NoError(t, err) sort.Slice(got, func(i, j int) bool { return got[i] < got[j] }) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 9c6e60b678..c397b3f953 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/go-openapi/strfmt" + "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" @@ -45,9 +46,9 @@ type UnknownRequirement struct { func (r UnknownRequirement) IsRequirement() {} // Returns the requirements for the given target. -// If the given target is the empty string, uses the default target (i.e. the name assigned to 'main'). -func (b *BuildScript) Requirements(target string) ([]Requirement, error) { - requirementsNode, err := b.getRequirementsNode(target) +// If no target is given, uses the default target (i.e. the name assigned to 'main'). +func (b *BuildScript) Requirements(target ...string) ([]Requirement, error) { + requirementsNode, err := b.getRequirementsNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get requirements node") } @@ -98,8 +99,8 @@ func (b *BuildScript) Requirements(target string) ([]Requirement, error) { // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, // which are the most common. // ONLY use this when you know you only need to care about dependencies. -func (b *BuildScript) DependencyRequirements(target string) ([]types.Requirement, error) { - reqs, err := b.Requirements(target) +func (b *BuildScript) DependencyRequirements(target ...string) ([]types.Requirement, error) { + reqs, err := b.Requirements(target...) if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } @@ -112,8 +113,8 @@ func (b *BuildScript) DependencyRequirements(target string) ([]types.Requirement return deps, nil } -func (b *BuildScript) getRequirementsNode(target string) (*Value, error) { - node, err := b.getSolveNode(target) +func (b *BuildScript) getRequirementsNode(target ...string) (*Value, error) { + node, err := b.getSolveNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -154,14 +155,14 @@ func isSolveFuncName(name string) bool { return name == solveFuncName || name == solveLegacyFuncName } -func (b *BuildScript) getTargetNode(target string) (*Value, error) { - if target == "" { +func (b *BuildScript) getTargetNode(target ...string) (*Value, error) { + if len(target) == 0 { for _, assignment := range b.raw.Assignments { if assignment.Key != mainKey { continue } - if assignment.Value.Ident != nil { - target = *assignment.Value.Ident + if assignment.Value.Ident != nil && *assignment.Value.Ident != "" { + target = []string{*assignment.Value.Ident} break } } @@ -176,11 +177,11 @@ func (b *BuildScript) getTargetNode(target string) (*Value, error) { continue } - if a.Key == target && a.Value.FuncCall != nil { + if funk.Contains(target, a.Key) && a.Value.FuncCall != nil { return a.Value } - if f := a.Value.FuncCall; target == "" && f != nil && isSolveFuncName(f.Name) { + if f := a.Value.FuncCall; len(target) == 0 && f != nil && isSolveFuncName(f.Name) { // This is coming from a complex build expression with no straightforward way to determine // a default target. Fall back on a top-level solve node. return a.Value @@ -201,8 +202,8 @@ func (b *BuildScript) getTargetNode(target string) (*Value, error) { return nil, errNodeNotFound } -func (b *BuildScript) getSolveNode(target string) (*Value, error) { - node, err := b.getTargetNode(target) +func (b *BuildScript) getSolveNode(target ...string) (*Value, error) { + node, err := b.getTargetNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get target node") } @@ -212,8 +213,13 @@ func (b *BuildScript) getSolveNode(target string) (*Value, error) { return node, nil } - // Otherwise, the "src" key contains a reference to the solve node. Look over the build expression - // again for that referenced node. + // Otherwise, the "src" key contains a reference to the solve node. + // For example: + // + // runtime = state_tool_artifacts_v1(src = sources) + // sources = solve(at_time = ..., platforms = [...], requirements = [...], ...) + // + // Look over the build expression again for that referenced node. for _, arg := range node.FuncCall.Arguments { if arg.Assignment == nil { continue @@ -231,8 +237,8 @@ func (b *BuildScript) getSolveNode(target string) (*Value, error) { return nil, errNodeNotFound } -func (b *BuildScript) getSolveAtTimeValue(target string) (*Value, error) { - node, err := b.getSolveNode(target) +func (b *BuildScript) getSolveAtTimeValue(target ...string) (*Value, error) { + node, err := b.getSolveNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -246,8 +252,8 @@ func (b *BuildScript) getSolveAtTimeValue(target string) (*Value, error) { return nil, errValueNotFound } -func (b *BuildScript) Platforms(target string) ([]strfmt.UUID, error) { - node, err := b.getPlatformsNode(target) +func (b *BuildScript) Platforms(target ...string) ([]strfmt.UUID, error) { + node, err := b.getPlatformsNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get platform node") } @@ -259,8 +265,8 @@ func (b *BuildScript) Platforms(target string) ([]strfmt.UUID, error) { return list, nil } -func (b *BuildScript) getPlatformsNode(target string) (*Value, error) { - node, err := b.getSolveNode(target) +func (b *BuildScript) getPlatformsNode(target ...string) (*Value, error) { + node, err := b.getSolveNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } diff --git a/pkg/buildscript/queries_test.go b/pkg/buildscript/queries_test.go index 6aec6ef388..9a107276ef 100644 --- a/pkg/buildscript/queries_test.go +++ b/pkg/buildscript/queries_test.go @@ -113,7 +113,7 @@ func TestRequirements(t *testing.T) { script, err := UnmarshalBuildExpression(data, nil) assert.NoError(t, err) - got, err := script.Requirements("") + got, err := script.Requirements() assert.NoError(t, err) gotReqs := []types.Requirement{} @@ -167,7 +167,7 @@ func TestRevision(t *testing.T) { script, err := UnmarshalBuildExpression(data, nil) assert.NoError(t, err) - got, err := script.Requirements("") + got, err := script.Requirements() assert.NoError(t, err) gotReqs := []RevisionRequirement{} diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 6adec4b1f1..4b587fd575 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -87,7 +87,7 @@ func UnmarshalBuildExpression(data []byte, atTime *time.Time) (*BuildScript, err // Extract the 'at_time' from the solve node, if it exists, and change its value to be a // reference to "$at_time", which is how we want to show it in AScript format. - if atTimeNode, err := script.getSolveAtTimeValue(""); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { + if atTimeNode, err := script.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { atTime, err := strfmt.ParseDateTime(strValue(atTimeNode)) if err != nil { return nil, errs.Wrap(err, "Invalid timestamp: %s", strValue(atTimeNode)) diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index b34554deeb..87f491a940 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -76,7 +76,7 @@ func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([ // FetchLanguagesForBuildScript fetches a list of language names for the given buildscript func FetchLanguagesForBuildScript(script *buildscript.BuildScript) ([]Language, error) { languages := []Language{} - reqs, err := script.DependencyRequirements("") + reqs, err := script.DependencyRequirements() if err != nil { return nil, errs.Wrap(err, "failed to get dependency requirements") } From bdf9fd51737ac5b300853e6a14090e27ffac863a Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 8 Oct 2024 13:35:09 -0400 Subject: [PATCH 197/440] Rename function to clarify it's looking for a solve node, not just any named node. --- pkg/buildscript/queries.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index c397b3f953..d92c5e4f72 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -155,7 +155,7 @@ func isSolveFuncName(name string) bool { return name == solveFuncName || name == solveLegacyFuncName } -func (b *BuildScript) getTargetNode(target ...string) (*Value, error) { +func (b *BuildScript) getTargetSolveNode(target ...string) (*Value, error) { if len(target) == 0 { for _, assignment := range b.raw.Assignments { if assignment.Key != mainKey { @@ -203,7 +203,7 @@ func (b *BuildScript) getTargetNode(target ...string) (*Value, error) { } func (b *BuildScript) getSolveNode(target ...string) (*Value, error) { - node, err := b.getTargetNode(target...) + node, err := b.getTargetSolveNode(target...) if err != nil { return nil, errs.Wrap(err, "Could not get target node") } From 378a436fd26edc51afd7c520b80b914ab6c7a78b Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 8 Oct 2024 11:25:54 -0700 Subject: [PATCH 198/440] Reorganize --- internal/runners/config/config.go | 44 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index fad84c861e..ff68585a4e 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -43,25 +43,6 @@ type configOutput struct { data []configData } -func (c *configOutput) MarshalOutput(format output.Format) interface{} { - if format != output.PlainFormatName { - return c.data - } - - c.out.Print(struct { - Data []configData `opts:"table,hideDash,omitKey"` - }{c.data}) - c.out.Print("") - c.out.Print(locale.T("config_get_help")) - c.out.Print(locale.T("config_set_help")) - - return output.Suppress -} - -func (c *configOutput) MarshalStructured(format output.Format) interface{} { - return c.data -} - func (c *List) Run(usageFunc func() error) error { registered := mediator.AllRegistered() sort.SliceStable(registered, func(i, j int) bool { @@ -109,17 +90,13 @@ func formatValue(opt mediator.Option, value interface{}) string { v = v[:100] + "..." } - if isDefault(value, opt.Default) { + if value == opt.Default { return fmt.Sprintf("[GREEN]%s[/RESET]", v) } return fmt.Sprintf("[BOLD][RED]%s*[/RESET]", v) } -func isDefault[T comparable](configured, defaultValue T) bool { - return configured == defaultValue -} - func formatDefault[T any](defaultValue T) string { v := fmt.Sprintf("%v", defaultValue) if v == "" { @@ -127,3 +104,22 @@ func formatDefault[T any](defaultValue T) string { } return fmt.Sprintf("[DISABLED]%s[/RESET]", v) } + +func (c *configOutput) MarshalOutput(format output.Format) interface{} { + if format != output.PlainFormatName { + return c.data + } + + c.out.Print(struct { + Data []configData `opts:"table,hideDash,omitKey"` + }{c.data}) + c.out.Print("") + c.out.Print(locale.T("config_get_help")) + c.out.Print(locale.T("config_set_help")) + + return output.Suppress +} + +func (c *configOutput) MarshalStructured(format output.Format) interface{} { + return c.data +} From 910581496f549ec0f6f9ea19c690456ac7140bed Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 8 Oct 2024 11:26:58 -0700 Subject: [PATCH 199/440] Revert config instance changes --- internal/config/instance.go | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/internal/config/instance.go b/internal/config/instance.go index 2658b2ee2b..fd6d68fe48 100644 --- a/internal/config/instance.go +++ b/internal/config/instance.go @@ -190,10 +190,6 @@ func (i *Instance) Get(key string) interface{} { return nil } -func (i *Instance) Default(key string) interface{} { - return mediator.GetDefault(mediator.GetOption(key)) -} - // GetString retrieves a string for a given key func (i *Instance) GetString(key string) string { return cast.ToString(i.Get(key)) @@ -224,28 +220,6 @@ func (i *Instance) AllKeys() []string { return keys } -// AllValues returns all of the current config keys and values -func (i *Instance) AllValues() map[string]interface{} { - rows, err := i.db.Query(`SELECT key, value FROM config`) - if err != nil { - multilog.Error("config:AllValues query failed: %s", errs.JoinMessage(err)) - return nil - } - defer rows.Close() - - values := make(map[string]interface{}) - for rows.Next() { - var key string - var value string - if err := rows.Scan(&key, &value); err != nil { - multilog.Error("config:AllValues scan failed: %s", errs.JoinMessage(err)) - return nil - } - values[key] = value - } - return values -} - // GetStringMapStringSlice retrieves a map of string slices for a given key func (i *Instance) GetStringMapStringSlice(key string) map[string][]string { return cast.ToStringMapStringSlice(i.Get(key)) From d43bc4921ea8fccf7f1848f4ba2e22004121e145 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 8 Oct 2024 11:27:23 -0700 Subject: [PATCH 200/440] Add comment --- internal/mediators/config/registry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/mediators/config/registry.go b/internal/mediators/config/registry.go index 8c0bc5a9b3..2d962f4ebe 100644 --- a/internal/mediators/config/registry.go +++ b/internal/mediators/config/registry.go @@ -85,6 +85,7 @@ func GetDefault(opt Option) interface{} { return opt.Default } +// AllRegistered returns all registered options, excluding hidden ones func AllRegistered() []Option { var opts []Option for _, opt := range registry { From b17383dc8b57a5474c2613b2cfbc8ed5fc95f431 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 8 Oct 2024 14:59:23 -0400 Subject: [PATCH 201/440] Use plural targets for varargs. --- pkg/buildscript/queries.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index d92c5e4f72..55e07927dc 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -47,8 +47,8 @@ func (r UnknownRequirement) IsRequirement() {} // Returns the requirements for the given target. // If no target is given, uses the default target (i.e. the name assigned to 'main'). -func (b *BuildScript) Requirements(target ...string) ([]Requirement, error) { - requirementsNode, err := b.getRequirementsNode(target...) +func (b *BuildScript) Requirements(targets ...string) ([]Requirement, error) { + requirementsNode, err := b.getRequirementsNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get requirements node") } @@ -99,8 +99,8 @@ func (b *BuildScript) Requirements(target ...string) ([]Requirement, error) { // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, // which are the most common. // ONLY use this when you know you only need to care about dependencies. -func (b *BuildScript) DependencyRequirements(target ...string) ([]types.Requirement, error) { - reqs, err := b.Requirements(target...) +func (b *BuildScript) DependencyRequirements(targets ...string) ([]types.Requirement, error) { + reqs, err := b.Requirements(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } @@ -113,8 +113,8 @@ func (b *BuildScript) DependencyRequirements(target ...string) ([]types.Requirem return deps, nil } -func (b *BuildScript) getRequirementsNode(target ...string) (*Value, error) { - node, err := b.getSolveNode(target...) +func (b *BuildScript) getRequirementsNode(targets ...string) (*Value, error) { + node, err := b.getSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -155,14 +155,14 @@ func isSolveFuncName(name string) bool { return name == solveFuncName || name == solveLegacyFuncName } -func (b *BuildScript) getTargetSolveNode(target ...string) (*Value, error) { - if len(target) == 0 { +func (b *BuildScript) getTargetSolveNode(targets ...string) (*Value, error) { + if len(targets) == 0 { for _, assignment := range b.raw.Assignments { if assignment.Key != mainKey { continue } if assignment.Value.Ident != nil && *assignment.Value.Ident != "" { - target = []string{*assignment.Value.Ident} + targets = []string{*assignment.Value.Ident} break } } @@ -177,11 +177,11 @@ func (b *BuildScript) getTargetSolveNode(target ...string) (*Value, error) { continue } - if funk.Contains(target, a.Key) && a.Value.FuncCall != nil { + if funk.Contains(targets, a.Key) && a.Value.FuncCall != nil { return a.Value } - if f := a.Value.FuncCall; len(target) == 0 && f != nil && isSolveFuncName(f.Name) { + if f := a.Value.FuncCall; len(targets) == 0 && f != nil && isSolveFuncName(f.Name) { // This is coming from a complex build expression with no straightforward way to determine // a default target. Fall back on a top-level solve node. return a.Value @@ -202,8 +202,8 @@ func (b *BuildScript) getTargetSolveNode(target ...string) (*Value, error) { return nil, errNodeNotFound } -func (b *BuildScript) getSolveNode(target ...string) (*Value, error) { - node, err := b.getTargetSolveNode(target...) +func (b *BuildScript) getSolveNode(targets ...string) (*Value, error) { + node, err := b.getTargetSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get target node") } @@ -237,8 +237,8 @@ func (b *BuildScript) getSolveNode(target ...string) (*Value, error) { return nil, errNodeNotFound } -func (b *BuildScript) getSolveAtTimeValue(target ...string) (*Value, error) { - node, err := b.getSolveNode(target...) +func (b *BuildScript) getSolveAtTimeValue(targets ...string) (*Value, error) { + node, err := b.getSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -252,8 +252,8 @@ func (b *BuildScript) getSolveAtTimeValue(target ...string) (*Value, error) { return nil, errValueNotFound } -func (b *BuildScript) Platforms(target ...string) ([]strfmt.UUID, error) { - node, err := b.getPlatformsNode(target...) +func (b *BuildScript) Platforms(targets ...string) ([]strfmt.UUID, error) { + node, err := b.getPlatformsNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get platform node") } @@ -265,8 +265,8 @@ func (b *BuildScript) Platforms(target ...string) ([]strfmt.UUID, error) { return list, nil } -func (b *BuildScript) getPlatformsNode(target ...string) (*Value, error) { - node, err := b.getSolveNode(target...) +func (b *BuildScript) getPlatformsNode(targets ...string) (*Value, error) { + node, err := b.getSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } From 1adb18a83e0bcd92987f4c91223ac1b87f1cccb2 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 9 Oct 2024 11:06:15 -0700 Subject: [PATCH 202/440] Set environment variable on shell detection --- installers/install.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/installers/install.ps1 b/installers/install.ps1 index ae30d9a78a..12d10e20da 100644 --- a/installers/install.ps1 +++ b/installers/install.ps1 @@ -147,6 +147,14 @@ function error([string] $msg) Write-Host $msg -ForegroundColor Red } +function setShellOverride { + $parentProcess = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $PID } + $parentProcessDetails = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $parentProcess.ParentProcessId } + if ($parentProcessDetails.Name -eq "cmd" -or $parentProcessDetails.Name -eq "cmd.exe") { + [System.Environment]::SetEnvironmentVariable("ACTIVESTATE_CLI_SHELL_OVERRIDE", $parentProcessDetails.Name, "Process") + } +} + $version = $script:VERSION if (!$version) { # If the user did not specify a version, formulate a query to fetch the JSON info of the latest @@ -248,6 +256,7 @@ $PSDefaultParameterValues['*:Encoding'] = 'utf8' # Run the installer. $env:ACTIVESTATE_SESSION_TOKEN = $script:SESSION_TOKEN_VALUE +setShellOverride & $exePath $args --source-installer="install.ps1" $success = $? if (Test-Path env:ACTIVESTATE_SESSION_TOKEN) From c4dccde24823f87e9ab1e0f6e1aaced7dc6b6e8d Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 9 Oct 2024 11:49:42 -0700 Subject: [PATCH 203/440] Manually construct table --- internal/runners/config/config.go | 83 +++++++++++++++---------------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index ff68585a4e..6ddf4941a7 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -9,6 +9,7 @@ import ( mediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/table" ) type List struct { @@ -30,17 +31,11 @@ func NewList(prime primeable) (*List, error) { }, nil } -type configData struct { - Key string `locale:"key,Key"` - Value string `locale:"value,Value"` - Default string `locale:"default,Default"` -} - -type configOutput struct { - out output.Outputer - cfg *config.Instance - options []mediator.Option - data []configData +type structuredConfigData struct { + Key string `json:"key"` + Value interface{} `json:"value"` + Default interface{} `json:"default"` + opt mediator.Option } func (c *List) Run(usageFunc func() error) error { @@ -49,22 +44,43 @@ func (c *List) Run(usageFunc func() error) error { return registered[i].Name < registered[j].Name }) - var data []configData + var data []structuredConfigData for _, opt := range registered { configuredValue := c.cfg.Get(opt.Name) - data = append(data, configData{ - Key: formatKey(opt.Name), - Value: formatValue(opt, configuredValue), - Default: formatDefault(mediator.GetDefault(opt)), + data = append(data, structuredConfigData{ + Key: opt.Name, + Value: configuredValue, + Default: mediator.GetDefault(opt), + opt: opt, }) } - c.out.Print(&configOutput{ - out: c.out, - cfg: c.cfg, - options: registered, - data: data, - }) + if c.out.Type().IsStructured() { + c.out.Print(output.Structured(data)) + } else { + if err := c.renderUserFacing(data); err != nil { + return err + } + } + + return nil +} + +func (c *List) renderUserFacing(configData []structuredConfigData) error { + tbl := table.New(locale.Ts("Key", "Value", "Default")) + tbl.HideDash = true + for _, config := range configData { + tbl.AddRow([]string{ + formatKey(config.Key), + formatValue(config.opt, config.Value), + formatDefault(config.Default), + }) + } + + c.out.Print(tbl.Render()) + c.out.Print("") + c.out.Print(locale.T("config_get_help")) + c.out.Print(locale.T("config_set_help")) return nil } @@ -90,36 +106,17 @@ func formatValue(opt mediator.Option, value interface{}) string { v = v[:100] + "..." } - if value == opt.Default { + if value == mediator.GetDefault(opt) { return fmt.Sprintf("[GREEN]%s[/RESET]", v) } return fmt.Sprintf("[BOLD][RED]%s*[/RESET]", v) } -func formatDefault[T any](defaultValue T) string { +func formatDefault(defaultValue interface{}) string { v := fmt.Sprintf("%v", defaultValue) if v == "" { v = "\"\"" } return fmt.Sprintf("[DISABLED]%s[/RESET]", v) } - -func (c *configOutput) MarshalOutput(format output.Format) interface{} { - if format != output.PlainFormatName { - return c.data - } - - c.out.Print(struct { - Data []configData `opts:"table,hideDash,omitKey"` - }{c.data}) - c.out.Print("") - c.out.Print(locale.T("config_get_help")) - c.out.Print(locale.T("config_set_help")) - - return output.Suppress -} - -func (c *configOutput) MarshalStructured(format output.Format) interface{} { - return c.data -} From 129a0365af5ee733d1dfab97ea63efaa1bf54e96 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 9 Oct 2024 13:48:22 -0700 Subject: [PATCH 204/440] Address more PR review comments --- cmd/state/internal/cmdtree/config.go | 2 +- internal/mediators/config/registry.go | 9 ++- internal/runners/config/config.go | 94 +++++++++++++++------------ 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/cmd/state/internal/cmdtree/config.go b/cmd/state/internal/cmdtree/config.go index 1753585602..d09608e4e4 100644 --- a/cmd/state/internal/cmdtree/config.go +++ b/cmd/state/internal/cmdtree/config.go @@ -20,7 +20,7 @@ func newConfigCommand(prime *primer.Values) *captain.Command { if err != nil { return err } - return runner.Run(ccmd.Usage) + return runner.Run() }).SetGroup(UtilsGroup).SetSupportsStructuredOutput() } diff --git a/internal/mediators/config/registry.go b/internal/mediators/config/registry.go index 2d962f4ebe..f6f327f53d 100644 --- a/internal/mediators/config/registry.go +++ b/internal/mediators/config/registry.go @@ -1,5 +1,7 @@ package config +import "sort" + type Type int const ( @@ -85,8 +87,8 @@ func GetDefault(opt Option) interface{} { return opt.Default } -// AllRegistered returns all registered options, excluding hidden ones -func AllRegistered() []Option { +// Registered returns all registered options, excluding hidden ones +func Registered() []Option { var opts []Option for _, opt := range registry { if opt.isHidden { @@ -94,5 +96,8 @@ func AllRegistered() []Option { } opts = append(opts, opt) } + sort.SliceStable(opts, func(i, j int) bool { + return opts[i].Name < opts[j].Name + }) return opts } diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index 6ddf4941a7..f6eb43c464 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -2,7 +2,7 @@ package config import ( "fmt" - "sort" + "strings" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/locale" @@ -13,8 +13,7 @@ import ( ) type List struct { - out output.Outputer - cfg *config.Instance + prime primeable } type primeable interface { @@ -26,8 +25,7 @@ type primeable interface { func NewList(prime primeable) (*List, error) { return &List{ - out: prime.Output(), - cfg: prime.Config(), + prime: prime, }, nil } @@ -38,15 +36,15 @@ type structuredConfigData struct { opt mediator.Option } -func (c *List) Run(usageFunc func() error) error { - registered := mediator.AllRegistered() - sort.SliceStable(registered, func(i, j int) bool { - return registered[i].Name < registered[j].Name - }) +func (c *List) Run() error { + registered := mediator.Registered() + + cfg := c.prime.Config() + out := c.prime.Output() var data []structuredConfigData for _, opt := range registered { - configuredValue := c.cfg.Get(opt.Name) + configuredValue := cfg.Get(opt.Name) data = append(data, structuredConfigData{ Key: opt.Name, Value: configuredValue, @@ -55,8 +53,8 @@ func (c *List) Run(usageFunc func() error) error { }) } - if c.out.Type().IsStructured() { - c.out.Print(output.Structured(data)) + if out.Type().IsStructured() { + out.Print(output.Structured(data)) } else { if err := c.renderUserFacing(data); err != nil { return err @@ -67,56 +65,68 @@ func (c *List) Run(usageFunc func() error) error { } func (c *List) renderUserFacing(configData []structuredConfigData) error { + cfg := c.prime.Config() + out := c.prime.Output() + tbl := table.New(locale.Ts("Key", "Value", "Default")) tbl.HideDash = true for _, config := range configData { tbl.AddRow([]string{ - formatKey(config.Key), - formatValue(config.opt, config.Value), - formatDefault(config.Default), + fmt.Sprintf("[CYAN]%s[/RESET]", config.Key), + colorizeValue(cfg, config.opt, config.Value), + fmt.Sprintf("[DISABLED]%s[/RESET]", formatValue(config.opt, config.Default)), }) } - c.out.Print(tbl.Render()) - c.out.Print("") - c.out.Print(locale.T("config_get_help")) - c.out.Print(locale.T("config_set_help")) + out.Print(tbl.Render()) + out.Print("") + out.Print(locale.T("config_get_help")) + out.Print(locale.T("config_set_help")) return nil } -func formatKey(key string) string { - return fmt.Sprintf("[CYAN]%s[/RESET]", key) +func colorizeValue(cfg *config.Instance, opt mediator.Option, value interface{}) string { + v := formatValue(opt, value) + + var tags []string + if opt.Type == mediator.Bool { + if v == "true" { + tags = append(tags, "[GREEN]") + } else { + tags = append(tags, "[RED]") + } + } + + if cfg.IsSet(opt.Name) { + tags = append(tags, "[BOLD]") + v = v + "*" + } + + if len(tags) > 0 { + return fmt.Sprintf("%s%s[/RESET]", strings.Join(tags, ""), v) + } + + return v } func formatValue(opt mediator.Option, value interface{}) string { - var v string switch opt.Type { - case mediator.Enum: - return fmt.Sprintf("\"%s\"", value) + case mediator.Enum, mediator.String: + return formatString(fmt.Sprintf("%v", value)) default: - v = fmt.Sprintf("%v", value) + return fmt.Sprintf("%v", value) } +} - if v == "" { +func formatString(value string) string { + if value == "" { return "\"\"" } - if len(v) > 100 { - v = v[:100] + "..." - } - - if value == mediator.GetDefault(opt) { - return fmt.Sprintf("[GREEN]%s[/RESET]", v) + if len(value) > 100 { + value = value[:100] + "..." } - return fmt.Sprintf("[BOLD][RED]%s*[/RESET]", v) -} - -func formatDefault(defaultValue interface{}) string { - v := fmt.Sprintf("%v", defaultValue) - if v == "" { - v = "\"\"" - } - return fmt.Sprintf("[DISABLED]%s[/RESET]", v) + return fmt.Sprintf("\"%s\"", value) } From c29e7fa3b18b318676988f45d542f2edda2e0f16 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 9 Oct 2024 15:47:18 -0700 Subject: [PATCH 205/440] Break up value processing --- internal/runners/config/config.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index f6eb43c464..f10959425a 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -71,9 +71,10 @@ func (c *List) renderUserFacing(configData []structuredConfigData) error { tbl := table.New(locale.Ts("Key", "Value", "Default")) tbl.HideDash = true for _, config := range configData { + v := formatValue(config.opt, config.Value) tbl.AddRow([]string{ fmt.Sprintf("[CYAN]%s[/RESET]", config.Key), - colorizeValue(cfg, config.opt, config.Value), + colorizeValue(cfg, config.opt, v), fmt.Sprintf("[DISABLED]%s[/RESET]", formatValue(config.opt, config.Default)), }) } @@ -86,12 +87,10 @@ func (c *List) renderUserFacing(configData []structuredConfigData) error { return nil } -func colorizeValue(cfg *config.Instance, opt mediator.Option, value interface{}) string { - v := formatValue(opt, value) - +func colorizeValue(cfg *config.Instance, opt mediator.Option, value string) string { var tags []string if opt.Type == mediator.Bool { - if v == "true" { + if value == "true" { tags = append(tags, "[GREEN]") } else { tags = append(tags, "[RED]") @@ -100,14 +99,14 @@ func colorizeValue(cfg *config.Instance, opt mediator.Option, value interface{}) if cfg.IsSet(opt.Name) { tags = append(tags, "[BOLD]") - v = v + "*" + value = value + "*" } if len(tags) > 0 { - return fmt.Sprintf("%s%s[/RESET]", strings.Join(tags, ""), v) + return fmt.Sprintf("%s%s[/RESET]", strings.Join(tags, ""), value) } - return v + return value } func formatValue(opt mediator.Option, value interface{}) string { From 979ef137875cd9c655a1f6cb136d955e022909f9 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 10 Oct 2024 11:29:27 -0700 Subject: [PATCH 206/440] fixed version --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index e40bee68a1..f7ccf07ba8 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.46.0-RC1 \ No newline at end of file +0.47.0-RC1 From 49f6ed0ccc0e8cc25fb0f2d14efc9cef18887f80 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 10 Oct 2024 12:37:03 -0700 Subject: [PATCH 207/440] Fix bold output --- internal/colorize/colorize.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/colorize/colorize.go b/internal/colorize/colorize.go index 9fdd9b06c5..c351727f6c 100644 --- a/internal/colorize/colorize.go +++ b/internal/colorize/colorize.go @@ -180,6 +180,8 @@ func colorize(ct ColorTheme, writer io.Writer, arg string) { ct.Warning(writer) case `ERROR`: ct.Error(writer) + case `BOLD`: + ct.Bold(writer) case `DISABLED`: ct.Disabled(writer) case `ACTIONABLE`: From 2fe66b57bfab6a549c887bd962cdc0f389a58875 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 10 Oct 2024 12:58:42 -0700 Subject: [PATCH 208/440] Don't format config value separately --- internal/runners/config/config.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index f10959425a..c3746f8e77 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -71,10 +71,9 @@ func (c *List) renderUserFacing(configData []structuredConfigData) error { tbl := table.New(locale.Ts("Key", "Value", "Default")) tbl.HideDash = true for _, config := range configData { - v := formatValue(config.opt, config.Value) tbl.AddRow([]string{ fmt.Sprintf("[CYAN]%s[/RESET]", config.Key), - colorizeValue(cfg, config.opt, v), + renderConfigValue(cfg, config.opt), fmt.Sprintf("[DISABLED]%s[/RESET]", formatValue(config.opt, config.Default)), }) } @@ -87,16 +86,18 @@ func (c *List) renderUserFacing(configData []structuredConfigData) error { return nil } -func colorizeValue(cfg *config.Instance, opt mediator.Option, value string) string { +func renderConfigValue(cfg *config.Instance, opt mediator.Option) string { + configured := cfg.Get(opt.Name) var tags []string if opt.Type == mediator.Bool { - if value == "true" { + if configured == true { tags = append(tags, "[GREEN]") } else { tags = append(tags, "[RED]") } } + value := formatValue(opt, configured) if cfg.IsSet(opt.Name) { tags = append(tags, "[BOLD]") value = value + "*" From c111f3c99580d595f00d88444c862efead56687b Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 10 Oct 2024 13:03:51 -0700 Subject: [PATCH 209/440] Localize table headers --- internal/locale/locales/en-us.yaml | 6 ++++++ internal/runners/config/config.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 60da73f045..a23b5d66b3 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1518,6 +1518,12 @@ version: other: Version license: other: License +key: + other: Key +value: + other: Value +default: + other: Default vulnerabilities: other: Vulnerabilities (CVEs) dependency_row: diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index c3746f8e77..775a821370 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -68,7 +68,7 @@ func (c *List) renderUserFacing(configData []structuredConfigData) error { cfg := c.prime.Config() out := c.prime.Output() - tbl := table.New(locale.Ts("Key", "Value", "Default")) + tbl := table.New(locale.Ts("key", "value", "default")) tbl.HideDash = true for _, config := range configData { tbl.AddRow([]string{ From f900b877deac2c6b93ac20652d680390147fb0d1 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Thu, 10 Oct 2024 13:04:34 -0700 Subject: [PATCH 210/440] Update internal/runners/config/config.go Co-authored-by: Nathan Rijksen --- internal/runners/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/runners/config/config.go b/internal/runners/config/config.go index 775a821370..689b40e7ef 100644 --- a/internal/runners/config/config.go +++ b/internal/runners/config/config.go @@ -79,9 +79,9 @@ func (c *List) renderUserFacing(configData []structuredConfigData) error { } out.Print(tbl.Render()) - out.Print("") - out.Print(locale.T("config_get_help")) - out.Print(locale.T("config_set_help")) + out.Notice("") + out.Notice(locale.T("config_get_help")) + out.Notice(locale.T("config_set_help")) return nil } From 75accbfc10bcb7ac4f30197418f271a239bf331d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 10 Oct 2024 15:04:30 -0700 Subject: [PATCH 211/440] file hasher now also returns matched files and their individual hash --- cmd/state-svc/internal/hash/file_hasher.go | 90 ++-- cmd/state-svc/internal/resolver/resolver.go | 26 +- .../internal/server/generated/generated.go | 462 +++++++++++++++++- cmd/state-svc/schema/schema.graphqls | 13 +- internal/graph/generated.go | 11 + internal/graph/response.go | 4 + internal/runners/commit/ingredientcall.go | 174 +++++++ .../runners/commit/ingredientcall_test.go | 30 ++ pkg/platform/api/svc/request/hashglobs.go | 11 +- pkg/platform/model/buildplanner/publish.go | 1 + pkg/platform/model/svc.go | 13 +- 11 files changed, 773 insertions(+), 62 deletions(-) create mode 100644 internal/runners/commit/ingredientcall.go create mode 100644 internal/runners/commit/ingredientcall_test.go create mode 100644 pkg/platform/model/buildplanner/publish.go diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 8e99220366..93c4dbe8f2 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -1,7 +1,6 @@ package hash import ( - "encoding/base64" "fmt" "io" "os" @@ -24,58 +23,77 @@ type FileHasher struct { cache fileCache } +type hashedFile struct { + Pattern string + Path string + Hash string +} + func NewFileHasher() *FileHasher { return &FileHasher{ cache: cache.New(24*time.Hour, 24*time.Hour), } } -func (fh *FileHasher) HashFiles(wd string, files []string) (_ string, rerr error) { - sort.Strings(files) - +func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashedFile, rerr error) { + sort.Strings(globs) // ensure consistent ordering + hashedFiles := []hashedFile{} hasher := xxhash.New() - for _, f := range files { - if !filepath.IsAbs(f) { - af, err := filepath.Abs(filepath.Join(wd, f)) - if err != nil { - return "", errs.Wrap(err, "Could not get absolute path for file: %s", f) - } - f = af - } - file, err := os.Open(f) - if err != nil { - return "", errs.Wrap(err, "Could not open file: %s", file.Name()) - } - defer rtutils.Closer(file.Close, &rerr) - - fileInfo, err := file.Stat() + for _, glob := range globs { + files, err := filepath.Glob(glob) if err != nil { - return "", errs.Wrap(err, "Could not stat file: %s", file.Name()) + return "", nil, errs.Wrap(err, "Could not match glob: %s", glob) } + sort.Strings(files) // ensure consistent ordering + for _, f := range files { + if !filepath.IsAbs(f) { + af, err := filepath.Abs(filepath.Join(wd, f)) + if err != nil { + return "", nil, errs.Wrap(err, "Could not get absolute path for file: %s", f) + } + f = af + } + file, err := os.Open(f) + if err != nil { + return "", nil, errs.Wrap(err, "Could not open file: %s", file.Name()) + } + defer rtutils.Closer(file.Close, &rerr) - var hash string - cachedHash, ok := fh.cache.Get(cacheKey(file.Name(), fileInfo.ModTime())) - if ok { - hash, ok = cachedHash.(string) - if !ok { - return "", errs.New("Could not convert cache value to string") + fileInfo, err := file.Stat() + if err != nil { + return "", nil, errs.Wrap(err, "Could not stat file: %s", file.Name()) } - } else { - fileHasher := xxhash.New() - if _, err := io.Copy(fileHasher, file); err != nil { - return "", errs.Wrap(err, "Could not hash file: %s", file.Name()) + + var hash string + cachedHash, ok := fh.cache.Get(cacheKey(file.Name(), fileInfo.ModTime())) + if ok { + hash, ok = cachedHash.(string) + if !ok { + return "", nil, errs.New("Could not convert cache value to string") + } + } else { + fileHasher := xxhash.New() + if _, err := io.Copy(fileHasher, file); err != nil { + return "", nil, errs.Wrap(err, "Could not hash file: %s", file.Name()) + } + + hash = fmt.Sprintf("%016x", fileHasher.Sum64()) } - hash = fmt.Sprintf("%x", fileHasher.Sum(nil)) - } + fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) - fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) + hashedFiles = append(hashedFiles, hashedFile{ + Pattern: glob, + Path: file.Name(), + Hash: hash, + }) - // Incorporate the individual file hash into the overall hash in hex format - fmt.Fprintf(hasher, "%x", hash) + // Incorporate the individual file hash into the overall hash in hex format + fmt.Fprintf(hasher, "%x", hash) + } } - return base64.StdEncoding.EncodeToString(hasher.Sum(nil)), nil + return fmt.Sprintf("%016x", hasher.Sum64()), hashedFiles, nil } func cacheKey(file string, modTime time.Time) string { diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index ed6cae022d..7995751f88 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "os" - "path/filepath" "runtime/debug" "sort" "strconv" @@ -308,19 +307,26 @@ func (r *Resolver) GetJwt(ctx context.Context) (*graph.Jwt, error) { return jwt, nil } -func (r *Resolver) HashGlobs(ctx context.Context, wd string, globs []string) (string, error) { +func (r *Resolver) HashGlobs(ctx context.Context, wd string, globs []string) (*graph.GlobResult, error) { defer func() { handlePanics(recover(), debug.Stack()) }() - var files []string - for _, glob := range globs { - matches, err := filepath.Glob(glob) - if err != nil { - return "", errs.Wrap(err, "Could not match glob: %s", glob) - } - files = append(files, matches...) + hash, files, err := r.fileHasher.HashFiles(wd, globs) + if err != nil { + return nil, errs.Wrap(err, "Could not hash files") + } + + result := &graph.GlobResult{ + Hash: hash, + } + for _, f := range files { + result.Files = append(result.Files, &graph.GlobFileResult{ + Pattern: f.Pattern, + Path: f.Path, + Hash: f.Hash, + }) } - return r.fileHasher.HashFiles(wd, files) + return result, nil } func (r *Resolver) GetCache(ctx context.Context, key string) (string, error) { diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index 43d27bdb9c..16617b4d7b 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -63,6 +63,17 @@ type ComplexityRoot struct { Received func(childComplexity int) int } + GlobFileResult struct { + Hash func(childComplexity int) int + Path func(childComplexity int) int + Pattern func(childComplexity int) int + } + + GlobResult struct { + Files func(childComplexity int) int + Hash func(childComplexity int) int + } + JWT struct { Token func(childComplexity int) int User func(childComplexity int) int @@ -149,7 +160,7 @@ type QueryResolver interface { FetchLogTail(ctx context.Context) (string, error) GetProcessesInUse(ctx context.Context, execDir string) ([]*graph.ProcessInfo, error) GetJwt(ctx context.Context) (*graph.Jwt, error) - HashGlobs(ctx context.Context, wd string, globs []string) (string, error) + HashGlobs(ctx context.Context, wd string, globs []string) (*graph.GlobResult, error) GetCache(ctx context.Context, key string) (string, error) } @@ -221,6 +232,41 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ConfigChangedResponse.Received(childComplexity), true + case "GlobFileResult.hash": + if e.complexity.GlobFileResult.Hash == nil { + break + } + + return e.complexity.GlobFileResult.Hash(childComplexity), true + + case "GlobFileResult.path": + if e.complexity.GlobFileResult.Path == nil { + break + } + + return e.complexity.GlobFileResult.Path(childComplexity), true + + case "GlobFileResult.pattern": + if e.complexity.GlobFileResult.Pattern == nil { + break + } + + return e.complexity.GlobFileResult.Pattern(childComplexity), true + + case "GlobResult.files": + if e.complexity.GlobResult.Files == nil { + break + } + + return e.complexity.GlobResult.Files(childComplexity), true + + case "GlobResult.hash": + if e.complexity.GlobResult.Hash == nil { + break + } + + return e.complexity.GlobResult.Hash(childComplexity), true + case "JWT.token": if e.complexity.JWT.Token == nil { break @@ -715,6 +761,17 @@ type JWT { user: User! } +type GlobFileResult { + pattern: String! + path: String! + hash: String! +} + +type GlobResult { + files: [GlobFileResult!]! + hash: String! +} + type Query { version: Version availableUpdate(desiredChannel: String!, desiredVersion: String!): AvailableUpdate @@ -726,7 +783,7 @@ type Query { fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT - hashGlobs(wd: String!, globs: [String!]!): String! + hashGlobs(wd: String!, globs: [String!]!): GlobResult! getCache(key: String!): String! } @@ -1356,6 +1413,234 @@ func (ec *executionContext) fieldContext_ConfigChangedResponse_received(_ contex return fc, nil } +func (ec *executionContext) _GlobFileResult_pattern(ctx context.Context, field graphql.CollectedField, obj *graph.GlobFileResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobFileResult_pattern(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Pattern, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobFileResult_pattern(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobFileResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GlobFileResult_path(ctx context.Context, field graphql.CollectedField, obj *graph.GlobFileResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobFileResult_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobFileResult_path(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobFileResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GlobFileResult_hash(ctx context.Context, field graphql.CollectedField, obj *graph.GlobFileResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobFileResult_hash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hash, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobFileResult_hash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobFileResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GlobResult_files(ctx context.Context, field graphql.CollectedField, obj *graph.GlobResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobResult_files(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Files, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*graph.GlobFileResult) + fc.Result = res + return ec.marshalNGlobFileResult2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobFileResultᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobResult_files(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "pattern": + return ec.fieldContext_GlobFileResult_pattern(ctx, field) + case "path": + return ec.fieldContext_GlobFileResult_path(ctx, field) + case "hash": + return ec.fieldContext_GlobFileResult_hash(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GlobFileResult", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GlobResult_hash(ctx context.Context, field graphql.CollectedField, obj *graph.GlobResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobResult_hash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hash, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobResult_hash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _JWT_token(ctx context.Context, field graphql.CollectedField, obj *graph.Jwt) (ret graphql.Marshaler) { fc, err := ec.fieldContext_JWT_token(ctx, field) if err != nil { @@ -2608,9 +2893,9 @@ func (ec *executionContext) _Query_hashGlobs(ctx context.Context, field graphql. } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*graph.GlobResult) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNGlobResult2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobResult(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Query_hashGlobs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -2620,7 +2905,13 @@ func (ec *executionContext) fieldContext_Query_hashGlobs(ctx context.Context, fi IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "files": + return ec.fieldContext_GlobResult_files(ctx, field) + case "hash": + return ec.fieldContext_GlobResult_hash(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GlobResult", field.Name) }, } defer func() { @@ -5241,6 +5532,99 @@ func (ec *executionContext) _ConfigChangedResponse(ctx context.Context, sel ast. return out } +var globFileResultImplementors = []string{"GlobFileResult"} + +func (ec *executionContext) _GlobFileResult(ctx context.Context, sel ast.SelectionSet, obj *graph.GlobFileResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, globFileResultImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GlobFileResult") + case "pattern": + out.Values[i] = ec._GlobFileResult_pattern(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "path": + out.Values[i] = ec._GlobFileResult_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hash": + out.Values[i] = ec._GlobFileResult_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var globResultImplementors = []string{"GlobResult"} + +func (ec *executionContext) _GlobResult(ctx context.Context, sel ast.SelectionSet, obj *graph.GlobResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, globResultImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GlobResult") + case "files": + out.Values[i] = ec._GlobResult_files(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hash": + out.Values[i] = ec._GlobResult_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var jWTImplementors = []string{"JWT"} func (ec *executionContext) _JWT(ctx context.Context, sel ast.SelectionSet, obj *graph.Jwt) graphql.Marshaler { @@ -6355,6 +6739,74 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) marshalNGlobFileResult2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobFileResultᚄ(ctx context.Context, sel ast.SelectionSet, v []*graph.GlobFileResult) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGlobFileResult2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobFileResult(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGlobFileResult2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobFileResult(ctx context.Context, sel ast.SelectionSet, v *graph.GlobFileResult) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GlobFileResult(ctx, sel, v) +} + +func (ec *executionContext) marshalNGlobResult2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobResult(ctx context.Context, sel ast.SelectionSet, v graph.GlobResult) graphql.Marshaler { + return ec._GlobResult(ctx, sel, &v) +} + +func (ec *executionContext) marshalNGlobResult2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐGlobResult(ctx context.Context, sel ast.SelectionSet, v *graph.GlobResult) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GlobResult(ctx, sel, v) +} + func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index c689289931..2d0cb8b23d 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -77,6 +77,17 @@ type JWT { user: User! } +type GlobFileResult { + pattern: String! + path: String! + hash: String! +} + +type GlobResult { + files: [GlobFileResult!]! + hash: String! +} + type Query { version: Version availableUpdate(desiredChannel: String!, desiredVersion: String!): AvailableUpdate @@ -88,7 +99,7 @@ type Query { fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! getJWT: JWT - hashGlobs(wd: String!, globs: [String!]!): String! + hashGlobs(wd: String!, globs: [String!]!): GlobResult! getCache(key: String!): String! } diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 9a51e2a224..a9c307c88d 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -24,6 +24,17 @@ type ConfigChangedResponse struct { Received bool `json:"received"` } +type GlobFileResult struct { + Pattern string `json:"pattern"` + Path string `json:"path"` + Hash string `json:"hash"` +} + +type GlobResult struct { + Files []*GlobFileResult `json:"files"` + Hash string `json:"hash"` +} + type Jwt struct { Token string `json:"token"` User *User `json:"user"` diff --git a/internal/graph/response.go b/internal/graph/response.go index c766e4d2a0..52add42637 100644 --- a/internal/graph/response.go +++ b/internal/graph/response.go @@ -27,3 +27,7 @@ type GetProcessesInUseResponse struct { type GetJWTResponse struct { Payload json.RawMessage `json:"getJWT"` } + +type HashGlobsResponse struct { + Response GlobResult `json:"hashGlobs"` +} diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go new file mode 100644 index 0000000000..de0424d58e --- /dev/null +++ b/internal/runners/commit/ingredientcall.go @@ -0,0 +1,174 @@ +package commit + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "time" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" + "github.com/ActiveState/cli/internal/graph" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/platform/api/graphql/request" + "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/platform/model/buildplanner" + "github.com/brunoga/deep" + "github.com/cespare/xxhash" + "github.com/mholt/archiver/v3" +) + +const namespaceSuffixFiles = "files" +const cacheKeyFiles = "buildscript-file-%s" + +func (c *Commit) resolveIngredientCall(script *buildscript.BuildScript, fc *buildscript.FuncCall) error { + hash, hashedFiles, err := c.hashIngredientCall(fc) + if err != nil { + return errs.Wrap(err, "Could not hash ingredient call") + } + + ns := model.NewNamespaceOrg(c.prime.Project().Owner(), namespaceSuffixFiles) + + cached, err := c.isIngredientCallCached(script.AtTime(), ns, hash) + if err != nil { + return errs.Wrap(err, "Could not check if ingredient call is cached") + } + if cached { + return nil + } + + files := []string{} + for _, f := range hashedFiles { + files = append(files, f.Path) + } + tmpFile := fileutils.TempFilePath("", fmt.Sprintf("bs-hash-%s.tar.gz", hash)) + if err := archiver.Archive(files, tmpFile); err != nil { + return errs.Wrap(err, "Could not archive files") + } + defer os.Remove(tmpFile) + + deps, err := c.resolveDependencies(fc) + if err != nil { + return errs.Wrap(err, "Could not resolve dependencies") + } + + // Publish ingredient + bpm := buildplanner.NewBuildPlannerModel(c.prime.Auth(), c.prime.SvcModel()) + _, err = bpm.Publish(request.PublishVariables{ + Name: hash, + Namespace: ns.String(), + Dependencies: deps, + }, tmpFile) + if err != nil { + return errs.Wrap(err, "Could not create publish request") + } + + // Add/update hash argument on the buildscript ingredient function call + fc.SetArgument("hash", buildscript.Value(hash)) + c.setIngredientCallCached(hash) + + return nil +} + +func (c *Commit) hashIngredientCall(fc *buildscript.FuncCall) (string, []*graph.GlobFileResult, error) { + src := fc.Argument("src") + patterns, ok := src.([]string) + if !ok { + return "", nil, errors.New("src argument is not a []string") + } + hashed, err := c.prime.SvcModel().HashGlobs(c.prime.Project().Dir(), patterns) + if err != nil { + return "", nil, errs.Wrap(err, "Could not hash globs") + } + + // Combine file hash with function call hash + fcc, err := deep.Copy(fc) + if err != nil { + return "", nil, errs.Wrap(err, "Could not copy function call") + } + fcc.UnsetArgument("hash") // The (potentially old) hash itself should not be used to calculate the hash + + fcb, err := json.Marshal(fcc) + if err != nil { + return "", nil, errs.Wrap(err, "Could not marshal function call") + } + hasher := xxhash.New() + hasher.Write([]byte(hashed.Hash)) + hasher.Write(fcb) + hash := fmt.Sprintf("%016x", hasher.Sum64()) + + return hash, hashed.Files, nil +} + +type invalidDepsValueType struct { + error +} + +type invalidDepValueType struct { + error +} + +func (c *Commit) resolveDependencies(fc *buildscript.FuncCall) ([]request.PublishVariableDep, error) { + deps := []request.PublishVariableDep{} + bsDeps := fc.Argument("deps") + if bsDeps != nil { + return deps, nil + } + + bsDepSlice, ok := bsDeps.([]any) + if !ok { + return nil, invalidDepsValueType{fmt.Errorf("deps argument is not a []any: %v (%T)", bsDeps, bsDeps)} + } + + for _, dep := range bsDepSlice { + req, ok := dep.(buildscript.DependencyRequirement) + if !ok { + return nil, invalidDepValueType{fmt.Errorf("dep argument is not a Req(): %v (%T)", dep, dep)} + } + deps = append(deps, request.PublishVariableDep{ + request.Dependency{ + Name: req.Name, + Namespace: req.Namespace, + VersionRequirements: model.BuildPlannerVersionConstraintsToString(req.VersionRequirement), + }, + []request.Dependency{}, + }) + } + + return deps, nil +} + +func (c *Commit) isIngredientCallCached(atTime *time.Time, ns model.Namespace, hash string) (bool, error) { + // Check against our local cache to see if we've already handled this file hash + // Technically we don't need this because the SearchIngredients call below already verifies this, but searching + // ingredients is slow, and local cache is FAST. + cacheValue, err := c.prime.SvcModel().GetCache(fmt.Sprintf(cacheKeyFiles, hash)) + if err != nil { + return false, errs.Wrap(err, "Could not get build script cache") + } + if cacheValue != "" { + // Ingredient already exists + return true, nil + } + + // Check against API to see if we've already published this file hash + ingredients, err := model.SearchIngredientsStrict(ns.String(), hash, true, false, atTime, c.prime.Auth()) + if err != nil { + return false, errs.Wrap(err, "Could not search ingredients") + } + if len(ingredients) > 0 { + // Ingredient already exists + return true, nil + } + + return false, nil // If we made it this far it means we did not find any existing cache entry; so it's dirty +} + +func (c *Commit) setIngredientCallCached(hash string) { + err := c.prime.SvcModel().SetCache(fmt.Sprintf(cacheKeyFiles, hash), hash, time.Hour*24*7) + if err != nil { + logging.Warning("Could not set build script cache: %s", errs.JoinMessage(err)) + } +} diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go new file mode 100644 index 0000000000..65431f7155 --- /dev/null +++ b/internal/runners/commit/ingredientcall_test.go @@ -0,0 +1,30 @@ +package commit + +import "testing" + +func Test_hashFuncCall(t *testing.T) { + type args struct { + fc *buildscript.FuncCall + seed string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := hashFuncCall(tt.args.fc, tt.args.seed) + if (err != nil) != tt.wantErr { + t.Errorf("hashFuncCall() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("hashFuncCall() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/platform/api/svc/request/hashglobs.go b/pkg/platform/api/svc/request/hashglobs.go index 41a69a1ee9..cc2cfd5930 100644 --- a/pkg/platform/api/svc/request/hashglobs.go +++ b/pkg/platform/api/svc/request/hashglobs.go @@ -11,8 +11,15 @@ func NewHashGlobs(wd string, globs []string) *HashGlobs { func (c *HashGlobs) Query() string { return `query(wd: String!, $globs: [String!]!) { - hashGlobs(wd: $wd, globs: $globs) - }` + hashGlobs(wd: $wd, globs: $globs) { + hash + files { + pattern + path + hash + } + } +}` } func (c *HashGlobs) Vars() (map[string]interface{}, error) { diff --git a/pkg/platform/model/buildplanner/publish.go b/pkg/platform/model/buildplanner/publish.go new file mode 100644 index 0000000000..5477a32f65 --- /dev/null +++ b/pkg/platform/model/buildplanner/publish.go @@ -0,0 +1 @@ +package buildplanner diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 3b5db843ca..613f952236 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -226,18 +226,15 @@ func (m *SvcModel) SetCache(key, value string, expiry time.Duration) error { return nil } -func (m *SvcModel) HashGlobs(wd string, globs []string) (string, error) { +func (m *SvcModel) HashGlobs(wd string, globs []string) (*graph.GlobResult, error) { defer profile.Measure("svc:HashGlobs", time.Now()) req := request.NewHashGlobs(wd, globs) - response := make(map[string]string) - if err := m.request(context.Background(), req, &response); err != nil { - return "", errs.Wrap(err, "Error sending HashGlobs request to state-svc") - } - if entry, ok := response["hashGlobs"]; ok { - return entry, nil + res := graph.HashGlobsResponse{} + if err := m.request(context.Background(), req, &res); err != nil { + return nil, errs.Wrap(err, "Error sending HashGlobs request to state-svc") } - return "", errs.New("svcModel.HashGlobs() did not return an expected value") + return &res.Response, errs.New("svcModel.HashGlobs() did not return an expected value") } func jsonFromMap(m map[string]interface{}) string { From 4d85dad0224417aadcbbadba5da7b8910c45a391 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 10 Oct 2024 15:08:32 -0700 Subject: [PATCH 212/440] Allow setting and unsetting arguments on function calls external from buildscript pkg --- pkg/buildscript/buildscript.go | 65 +++++++++++++++++++-- pkg/buildscript/queries.go | 102 ++++++++++++++++++++++++--------- pkg/buildscript/raw.go | 23 -------- 3 files changed, 136 insertions(+), 54 deletions(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 7384f378d5..3f1e35d1f0 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -73,21 +73,76 @@ type FuncCall struct { fc *funcCall } -func (f FuncCall) Argument(name string) any { +func (f *FuncCall) MarshalJSON() ([]byte, error) { + return f.fc.MarshalJSON() +} + +func (f *FuncCall) Argument(name string) any { for _, a := range f.fc.Arguments { if a.Assignment == nil || a.Assignment.Key != name { continue } - return a.Assignment.Value.Value() + return exportValue(a.Assignment.Value) } return nil } -func (b *BuildScript) FunctionCalls(name string) []FuncCall { - result := []FuncCall{} +// SetArgument will update the given argument, or add it if it does not exist +func (f *FuncCall) SetArgument(k string, v *value) { + for i, a := range f.fc.Arguments { + if a.Assignment == nil || a.Assignment.Key != k { + continue + } + f.fc.Arguments[i].Assignment.Value = v + return + } + + // Arg doesn't exist; append it instead + f.fc.Arguments = append(f.fc.Arguments, &value{Assignment: &assignment{Key: k, Value: v}}) + + return +} + +func (f *FuncCall) UnsetArgument(k string) { + for i, a := range f.fc.Arguments { + if a.Assignment == nil || a.Assignment.Key != k { + continue + } + f.fc.Arguments = append(f.fc.Arguments[:i], f.fc.Arguments[i+1:]...) + return + } +} + +// Value turns a standard type into a buildscript compatible type +func Value[T string | float64 | []string | []float64](inputv T) *value { + v := &value{} + switch vt := any(inputv).(type) { + case string: + v.Str = &vt + case float64: + v.Number = &vt + case []string: + strValues := make([]*value, len(vt)) + for i, s := range vt { + strValues[i] = &value{Str: &s} + } + v.List = &strValues + case []float64: + numValues := make([]*value, len(vt)) + for i, n := range vt { + numValues[i] = &value{Number: &n} + } + v.List = &numValues + } + + return v +} + +func (b *BuildScript) FunctionCalls(name string) []*FuncCall { + result := []*FuncCall{} for _, f := range b.raw.FuncCalls() { if f.Name == name { - result = append(result, FuncCall{f}) + result = append(result, &FuncCall{f}) } } return result diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 4364c9611f..b5678a6eab 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -1,8 +1,11 @@ package buildscript import ( + "errors" + "fmt" "strings" + "github.com/ActiveState/cli/internal/logging" "github.com/go-openapi/strfmt" "github.com/ActiveState/cli/internal/errs" @@ -49,37 +52,23 @@ func (b *BuildScript) Requirements() ([]Requirement, error) { return nil, errs.Wrap(err, "Could not get requirements node") } + return exportRequirements(requirementsNode), nil +} + +func exportRequirements(v *value) []Requirement { + if v.List == nil { + logging.Error("exportRequirements called with value that does not have a list") + return nil + } var requirements []Requirement - for _, req := range *requirementsNode.List { + for _, req := range *v.List { if req.FuncCall == nil { continue } switch req.FuncCall.Name { - case reqFuncName: - var r DependencyRequirement - for _, arg := range req.FuncCall.Arguments { - switch arg.Assignment.Key { - case requirementNameKey: - r.Name = strValue(arg.Assignment.Value) - case requirementNamespaceKey: - r.Namespace = strValue(arg.Assignment.Value) - case requirementVersionKey: - r.VersionRequirement = getVersionRequirements(arg.Assignment.Value) - } - } - requirements = append(requirements, r) - case revFuncName: - var r RevisionRequirement - for _, arg := range req.FuncCall.Arguments { - switch arg.Assignment.Key { - case requirementNameKey: - r.Name = strValue(arg.Assignment.Value) - case requirementRevisionIDKey: - r.RevisionID = strfmt.UUID(strValue(arg.Assignment.Value)) - } - } - requirements = append(requirements, r) + case reqFuncName, revFuncName: + requirements = append(requirements, parseRequirement(req)) default: requirements = append(requirements, UnknownRequirement{ Name: req.FuncCall.Name, @@ -89,7 +78,68 @@ func (b *BuildScript) Requirements() ([]Requirement, error) { } - return requirements, nil + return requirements +} + +func parseRequirement(req *value) Requirement { + if req.FuncCall == nil { + return nil + } + switch req.FuncCall.Name { + case reqFuncName: + var r DependencyRequirement + for _, arg := range req.FuncCall.Arguments { + switch arg.Assignment.Key { + case requirementNameKey: + r.Name = strValue(arg.Assignment.Value) + case requirementNamespaceKey: + r.Namespace = strValue(arg.Assignment.Value) + case requirementVersionKey: + r.VersionRequirement = getVersionRequirements(arg.Assignment.Value) + } + } + return r + case revFuncName: + var r RevisionRequirement + for _, arg := range req.FuncCall.Arguments { + switch arg.Assignment.Key { + case requirementNameKey: + r.Name = strValue(arg.Assignment.Value) + case requirementRevisionIDKey: + r.RevisionID = strfmt.UUID(strValue(arg.Assignment.Value)) + } + } + return r + default: + return nil + } +} + +func exportValue(v *value) any { + switch { + case v.FuncCall != nil: + if req := parseRequirement(v); req != nil { + return req + } + return &FuncCall{v.FuncCall} + case v.List != nil: + result := []any{} + for _, value := range *v.List { + result = append(result, exportValue(value)) + } + return result + case v.Str != nil: + return strValue(v) + case v.Number != nil: + return *v.Number + case v.Null != nil: + return nil + case v.Assignment != nil: + return v.Assignment + case v.Object != nil: + return v.Object + } + return errors.New(fmt.Sprintf("unknown value type: %#v", v)) } // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index f9749650c8..8a2dbc3149 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -1,8 +1,6 @@ package buildscript import ( - "errors" - "fmt" "strconv" "strings" "time" @@ -73,27 +71,6 @@ type value struct { Ident *string `parser:"| @Ident"` // only in FuncCall or Assignment } -// Value conveniently returns the property that holds the actual value -func (v *value) Value() interface{} { - switch { - case v.FuncCall != nil: - return v.FuncCall - case v.List != nil: - return *v.List - case v.Str != nil: - return strValue(v) - case v.Number != nil: - return *v.Number - case v.Null != nil: - return nil - case v.Assignment != nil: - return v.Assignment - case v.Object != nil: - return v.Object - } - return errors.New(fmt.Sprintf("unknown value type: %#v", v)) -} - type null struct { Null string `parser:"'null'"` } From 5073a4b03b4701e1ce2d33916e67932702958799 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 10 Oct 2024 15:08:45 -0700 Subject: [PATCH 213/440] Fix recursion panics --- pkg/buildscript/buildscript.go | 2 +- pkg/buildscript/marshal_buildexpression.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 3f1e35d1f0..42ce79582c 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -35,7 +35,7 @@ func Create() *BuildScript { } func New() *BuildScript { - bs := Create() + bs := &BuildScript{raw: &rawBuildScript{}} return bs } diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index 513380852f..aa04e38dc8 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -113,9 +113,6 @@ func (f *funcCall) MarshalJSON() ([]byte, error) { // This is needed until buildexpressions support functions as requirements. Once they do, we can // remove this method entirely. func marshalReq(fn *funcCall) ([]byte, error) { - if fn.Name == reqFuncName { - return marshalReq(fn.Arguments[0].FuncCall) - } args := fn.Arguments requirement := make(map[string]interface{}) From da3bba6a1e41e0e808360837a7e3d0ae5451429f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 10 Oct 2024 15:09:04 -0700 Subject: [PATCH 214/440] Fix name collisions --- pkg/buildscript/unmarshal_buildexpression.go | 38 ++++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 6c0fda3656..73ece1e5df 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -146,7 +146,7 @@ func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { } }() - value := &value{} + result := &value{} switch v := valueInterface.(type) { case map[string]interface{}: @@ -168,17 +168,17 @@ func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's value: %v", key, v) } - value.FuncCall = f + result.FuncCall = f } } // It's not a function call, but an object. - if value.FuncCall == nil { + if result.FuncCall == nil { object, err := unmarshalAssignments(path, v) if err != nil { return nil, errs.Wrap(err, "Could not parse object: %v", v) } - value.Object = &object + result.Object = &object } case []interface{}: @@ -190,27 +190,27 @@ func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { } values = append(values, value) } - value.List = &values + result.List = &values case string: if sliceutils.Contains(path, ctxIn) || strings.HasPrefix(v, "$") { - value.Ident = ptr.To(strings.TrimPrefix(v, "$")) + result.Ident = ptr.To(strings.TrimPrefix(v, "$")) } else { - value.Str = ptr.To(strconv.Quote(v)) // quoting is mandatory + result.Str = ptr.To(strconv.Quote(v)) // quoting is mandatory } case float64: - value.Number = ptr.To(v) + result.Number = ptr.To(v) case nil: - value.Null = &null{} + result.Null = &null{} default: logging.Debug("Unknown type: %T at path %s", v, strings.Join(path, ".")) - value.Null = &null{} + result.Null = &null{} } - return value, nil + return result, nil } func isFuncCall(path []string, value map[string]interface{}) bool { @@ -226,7 +226,7 @@ func isFuncCall(path []string, value map[string]interface{}) bool { return !hasIn || sliceutils.Contains(path, ctxAssignments) } -func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*funcCall, error) { +func unmarshalFuncCall(path []string, fc map[string]interface{}) (*funcCall, error) { path = append(path, ctxFuncCall) defer func() { _, _, err := sliceutils.Pop(path) @@ -238,14 +238,14 @@ func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*funcCal // m is a mapping of function name to arguments. There should only be one // set of arguments. Since the arguments are key-value pairs, it should be // a map[string]interface{}. - if len(funcCall) > 1 { + if len(fc) > 1 { return nil, errs.New("Function call has more than one argument mapping") } // Look in the given object for the function's name and argument mapping. var name string var argsInterface interface{} - for key, value := range funcCall { + for key, value := range fc { if _, ok := value.(map[string]interface{}); !ok { return nil, errs.New("Incorrect argument format") } @@ -260,11 +260,11 @@ func unmarshalFuncCall(path []string, funcCall map[string]interface{}) (*funcCal switch v := argsInterface.(type) { case map[string]interface{}: for key, valueInterface := range v { - value, err := unmarshalValue(path, valueInterface) + uv, err := unmarshalValue(path, valueInterface) if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface) } - args = append(args, &value{Assignment: &assignment{key, value}}) + args = append(args, &value{Assignment: &assignment{key, uv}}) } sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Key < args[j].Assignment.Key }) @@ -350,16 +350,16 @@ func transformRequirement(req *value) *value { for _, arg := range *req.Object { key := arg.Key - value := arg.Value + v := arg.Value // Transform the version value from the requirement object. if key == requirementVersionRequirementsKey { key = requirementVersionKey - value = &value{funcCall: transformVersion(arg)} + v = &value{FuncCall: transformVersion(arg)} } // Add the argument to the function transformation. - args = append(args, &value{Assignment: &assignment{key, value}}) + args = append(args, &value{Assignment: &assignment{key, v}}) } return &value{FuncCall: &funcCall{reqFuncName, args}} From c50b3e3fa9daa377bcc8c8defd8ddc024a8d7cc0 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 10 Oct 2024 15:13:49 -0700 Subject: [PATCH 215/440] Turned publish logic into model function for reusability --- internal/runners/publish/publish.go | 49 +++++++++------------- pkg/platform/api/graphql/model/publish.go | 14 ++++--- pkg/platform/model/buildplanner/publish.go | 24 +++++++++++ 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/internal/runners/publish/publish.go b/internal/runners/publish/publish.go index a02ff27620..061b4b71d4 100644 --- a/internal/runners/publish/publish.go +++ b/internal/runners/publish/publish.go @@ -2,7 +2,6 @@ package publish import ( "errors" - "net/http" "path/filepath" "regexp" "strconv" @@ -12,7 +11,6 @@ import ( "github.com/ActiveState/cli/internal/captain" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" - "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/osutils" @@ -20,15 +18,13 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/rtutils/ptr" - "github.com/ActiveState/cli/pkg/platform/api" - graphModel "github.com/ActiveState/cli/pkg/platform/api/graphql/model" "github.com/ActiveState/cli/pkg/platform/api/graphql/request" "github.com/ActiveState/cli/pkg/platform/api/inventory/inventory_client/inventory_operations" "github.com/ActiveState/cli/pkg/platform/api/inventory/inventory_models" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" + "github.com/ActiveState/cli/pkg/platform/model/buildplanner" "github.com/ActiveState/cli/pkg/project" - "github.com/ActiveState/graphql" "github.com/go-openapi/strfmt" "gopkg.in/yaml.v3" ) @@ -56,7 +52,7 @@ type Runner struct { out output.Outputer prompt prompt.Prompter project *project.Project - client *gqlclient.Client + bp *buildplanner.BuildPlanner } type primeable interface { @@ -64,17 +60,17 @@ type primeable interface { primer.Auther primer.Projecter primer.Prompter + primer.SvcModeler } func New(prime primeable) *Runner { - client := gqlclient.NewWithOpts( - api.GetServiceURL(api.ServiceBuildPlanner).String(), 0, - graphql.WithHTTPClient(http.DefaultClient), - graphql.UseMultipartForm(), - ) - client.SetTokenProvider(prime.Auth()) - client.EnableDebugLog() - return &Runner{auth: prime.Auth(), out: prime.Output(), prompt: prime.Prompt(), project: prime.Project(), client: client} + return &Runner{ + auth: prime.Auth(), + out: prime.Output(), + prompt: prime.Prompt(), + project: prime.Project(), + bp: buildplanner.NewBuildPlannerModel(prime.Auth(), prime.SvcModel()), + } } type ParentIngredient struct { @@ -125,7 +121,7 @@ func (r *Runner) Run(params *Params) error { if params.Namespace != "" { reqVars.Namespace = params.Namespace } else if reqVars.Namespace == "" && r.project != nil && r.project.Owner() != "" { - reqVars.Namespace = model.NewNamespaceOrg(r.project.Owner()).String() + reqVars.Namespace = model.NewNamespaceOrg(r.project.Owner(), "").String() } // Name @@ -242,30 +238,25 @@ Do you want to publish this ingredient? r.out.Notice(locale.Tl("uploadingredient_uploading", "Publishing ingredient...")) - pr, err := request.Publish(reqVars, params.Filepath) + publishResult, err := r.bp.Publish(reqVars, params.Filepath) if err != nil { return locale.WrapError(err, "err_uploadingredient_publish", "Could not create publish request") } - result := graphModel.PublishResult{} - - if err := r.client.Run(pr, &result); err != nil { - return locale.WrapError(err, "err_uploadingredient_publish", "", err.Error()) - } - if result.Publish.Error != "" { - return locale.NewError("err_uploadingredient_publish_api", "API responded with error: {{.V0}}", result.Publish.Error) + if publishResult.Error != "" { + return locale.NewError("err_uploadingredient_publish_api", "API responded with error: {{.V0}}", publishResult.Error) } - logging.Debug("Published ingredient ID: %s", result.Publish.IngredientID) - logging.Debug("Published ingredient version ID: %s", result.Publish.IngredientVersionID) - logging.Debug("Published ingredient revision: %d", result.Publish.Revision) + logging.Debug("Published ingredient ID: %s", publishResult.IngredientID) + logging.Debug("Published ingredient version ID: %s", publishResult.IngredientVersionID) + logging.Debug("Published ingredient revision: %d", publishResult.Revision) - ingredientID := strfmt.UUID(result.Publish.IngredientID) + ingredientID := strfmt.UUID(publishResult.IngredientID) publishedIngredient, err := model.FetchIngredient(&ingredientID, r.auth) if err != nil { return locale.WrapError(err, "err_uploadingredient_fetch", "Unable to fetch newly published ingredient") } - versionID := strfmt.UUID(result.Publish.IngredientVersionID) + versionID := strfmt.UUID(publishResult.IngredientVersionID) latestTime, err := model.FetchLatestRevisionTimeStamp(r.auth) if err != nil { @@ -294,7 +285,7 @@ Do you want to publish this ingredient? strconv.Itoa(int(*publishedVersion.Revision)), ingTime.Format(time.RFC3339), ), - result.Publish, + publishResult, )) return nil diff --git a/pkg/platform/api/graphql/model/publish.go b/pkg/platform/api/graphql/model/publish.go index 9599ba2455..f818ef95c4 100644 --- a/pkg/platform/api/graphql/model/publish.go +++ b/pkg/platform/api/graphql/model/publish.go @@ -1,10 +1,12 @@ package model type PublishResult struct { - Publish struct { - ErrorResponse - IngredientID string `json:"ingredientID"` - IngredientVersionID string `json:"ingredientVersionID"` - Revision int `json:"revision"` - } `json:"publish"` + ErrorResponse + IngredientID string `json:"ingredientID"` + IngredientVersionID string `json:"ingredientVersionID"` + Revision int `json:"revision"` +} + +type PublishResponse struct { + Result PublishResult `json:"publish"` } diff --git a/pkg/platform/model/buildplanner/publish.go b/pkg/platform/model/buildplanner/publish.go index 5477a32f65..c680ce6e81 100644 --- a/pkg/platform/model/buildplanner/publish.go +++ b/pkg/platform/model/buildplanner/publish.go @@ -1 +1,25 @@ package buildplanner + +import ( + "github.com/ActiveState/cli/internal/errs" + graphModel "github.com/ActiveState/cli/pkg/platform/api/graphql/model" + "github.com/ActiveState/cli/pkg/platform/api/graphql/request" +) + +func (b *BuildPlanner) Publish(vars request.PublishVariables, filepath string) (*graphModel.PublishResult, error) { + pr, err := request.Publish(vars, filepath) + if err != nil { + return nil, errs.Wrap(err, "Could not create publish request") + } + res := graphModel.PublishResponse{} + + if err := b.client.Run(pr, &res); err != nil { + return nil, errs.Wrap(err, "", err.Error()) + } + + if res.Result.Error != "" { + return nil, errs.New("API responded with error: {{.V0}}", res.Result.Error) + } + + return &res.Result, nil +} From df575414883577a74b7034f931bab8091bb60ad0 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 09:48:18 -0700 Subject: [PATCH 216/440] Made TempFilePath function errorless --- internal/fileutils/fileutils.go | 14 ++++++++++---- test/integration/publish_int_test.go | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/internal/fileutils/fileutils.go b/internal/fileutils/fileutils.go index 411f80a646..6eb835e396 100644 --- a/internal/fileutils/fileutils.go +++ b/internal/fileutils/fileutils.go @@ -17,6 +17,7 @@ import ( "unicode" "github.com/gofrs/flock" + "github.com/labstack/gommon/random" "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/assets" @@ -870,10 +871,15 @@ func TempFileUnsafe(dir, pattern string) *os.File { return f } -func TempFilePathUnsafe(dir, pattern string) string { - f := TempFileUnsafe(dir, pattern) - defer f.Close() - return f.Name() +func TempFilePath(dir, pattern string) string { + if dir == "" { + dir = os.TempDir() + } + fname := random.String(8, random.Alphanumeric) + if pattern != "" { + fname = fmt.Sprintf("%s-%s", fname, pattern) + } + return filepath.Join(dir, fname) } // TempDirUnsafe returns a temp path or panics if it cannot be created diff --git a/test/integration/publish_int_test.go b/test/integration/publish_int_test.go index 709cde17ab..5e1caf8def 100644 --- a/test/integration/publish_int_test.go +++ b/test/integration/publish_int_test.go @@ -52,10 +52,10 @@ func (suite *PublishIntegrationTestSuite) TestPublish() { expect expect } - tempFile := fileutils.TempFilePathUnsafe("", "*.zip") + tempFile := fileutils.TempFilePath("", "*.zip") defer os.Remove(tempFile) - tempFileInvalid := fileutils.TempFilePathUnsafe("", "*.notzip") + tempFileInvalid := fileutils.TempFilePath("", "*.notzip") defer os.Remove(tempFileInvalid) ts := e2e.New(suite.T(), false) From c178ee57956e577c9baca4129bf44e7879a8ac2f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 09:48:40 -0700 Subject: [PATCH 217/440] Allow specifying namespace suffix for org namespace --- pkg/platform/model/vcs.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index 4efaf98749..d29ed72717 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -221,7 +221,12 @@ func NewNamespacePlatform() Namespace { return Namespace{NamespacePlatform, "platform"} } -func NewNamespaceOrg(orgName string) Namespace { +func NewNamespaceOrg(orgName, suffix string) Namespace { + ns := orgName + if suffix != "" { + ns += "/" + suffix + ns = strings.ReplaceAll(ns, "//", "/") + } return Namespace{ nsType: NamespaceOrg, value: NamespaceOrg.prefix + "/" + orgName, From c5211c7e70adb302a97f003dcbea6a5d1d074587 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 09:49:02 -0700 Subject: [PATCH 218/440] Implemented ingredient() function for buildscripts (on commit) --- internal/locale/locales/en-us.yaml | 4 + internal/runners/commit/commit.go | 13 ++ internal/runners/commit/ingredientcall.go | 94 ++++++----- .../runners/commit/ingredientcall_test.go | 146 ++++++++++++++++-- 4 files changed, 211 insertions(+), 46 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 50558aa40b..86d934812f 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1560,3 +1560,7 @@ install_report_updated: other: "Updated: [NOTICE]{{.V0}}[/RESET]" install_report_removed: other: "Removed: [NOTICE]{{.V0}}[/RESET]" +err_commit_invalid_deps_value_type: + other: "Your buildscript contains an ingredient() function call with an invalid value type for its dependencies" +err_commit_invalid_dep_value_type: + other: "Your buildscript contains an ingredient() function call with an invalid value type for one of its dependencies" diff --git a/internal/runners/commit/commit.go b/internal/runners/commit/commit.go index d0edf547d6..1ac478ce50 100644 --- a/internal/runners/commit/commit.go +++ b/internal/runners/commit/commit.go @@ -58,6 +58,13 @@ func rationalizeError(err *error) { *err = errs.WrapUserFacing(*err, buildPlannerErr.LocaleError(), errs.SetIf(buildPlannerErr.InputError(), errs.SetInput())) + + case errors.As(*err, &invalidDepsValueType{}): + *err = errs.WrapUserFacing(*err, locale.T("err_commit_invalid_deps_value_type"), errs.SetInput()) + + case errors.As(*err, &invalidDepValueType{}): + *err = errs.WrapUserFacing(*err, locale.T("err_commit_invalid_dep_value_type"), errs.SetInput()) + } } @@ -78,6 +85,12 @@ func (c *Commit) Run() (rerr error) { return errs.Wrap(err, "Could not get local build script") } + for _, fc := range script.FunctionCalls("ingredient") { + if err := NewIngredientCall(c.prime, script, fc).Resolve(); err != nil { + return errs.Wrap(err, "Could not resolve ingredient") + } + } + // Get equivalent build script for current state of the project localCommitID, err := localcommit.Get(proj.Dir()) if err != nil { diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index de0424d58e..81d12f130e 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -23,15 +23,37 @@ import ( const namespaceSuffixFiles = "files" const cacheKeyFiles = "buildscript-file-%s" -func (c *Commit) resolveIngredientCall(script *buildscript.BuildScript, fc *buildscript.FuncCall) error { - hash, hashedFiles, err := c.hashIngredientCall(fc) +type invalidDepsValueType struct{ error } + +type invalidDepValueType struct{ error } + +type IngredientCall struct { + prime primeable + script *buildscript.BuildScript + funcCall *buildscript.FuncCall + ns model.Namespace +} + +func NewIngredientCall( + prime primeable, + script *buildscript.BuildScript, + funcCall *buildscript.FuncCall, +) *IngredientCall { + return &IngredientCall{ + prime: prime, + script: script, + funcCall: funcCall, + ns: model.NewNamespaceOrg(prime.Project().Owner(), namespaceSuffixFiles), + } +} + +func (i *IngredientCall) Resolve() error { + hash, hashedFiles, err := i.calculateHash() if err != nil { return errs.Wrap(err, "Could not hash ingredient call") } - ns := model.NewNamespaceOrg(c.prime.Project().Owner(), namespaceSuffixFiles) - - cached, err := c.isIngredientCallCached(script.AtTime(), ns, hash) + cached, err := i.isCached(hash) if err != nil { return errs.Wrap(err, "Could not check if ingredient call is cached") } @@ -49,16 +71,16 @@ func (c *Commit) resolveIngredientCall(script *buildscript.BuildScript, fc *buil } defer os.Remove(tmpFile) - deps, err := c.resolveDependencies(fc) + deps, err := i.resolveDependencies() if err != nil { return errs.Wrap(err, "Could not resolve dependencies") } // Publish ingredient - bpm := buildplanner.NewBuildPlannerModel(c.prime.Auth(), c.prime.SvcModel()) + bpm := buildplanner.NewBuildPlannerModel(i.prime.Auth(), i.prime.SvcModel()) _, err = bpm.Publish(request.PublishVariables{ Name: hash, - Namespace: ns.String(), + Namespace: i.ns.String(), Dependencies: deps, }, tmpFile) if err != nil { @@ -66,54 +88,56 @@ func (c *Commit) resolveIngredientCall(script *buildscript.BuildScript, fc *buil } // Add/update hash argument on the buildscript ingredient function call - fc.SetArgument("hash", buildscript.Value(hash)) - c.setIngredientCallCached(hash) + i.funcCall.SetArgument("hash", buildscript.Value(hash)) + i.setCached(hash) return nil } -func (c *Commit) hashIngredientCall(fc *buildscript.FuncCall) (string, []*graph.GlobFileResult, error) { - src := fc.Argument("src") +func (i *IngredientCall) calculateHash() (string, []*graph.GlobFileResult, error) { + src := i.funcCall.Argument("src") patterns, ok := src.([]string) if !ok { return "", nil, errors.New("src argument is not a []string") } - hashed, err := c.prime.SvcModel().HashGlobs(c.prime.Project().Dir(), patterns) + hashed, err := i.prime.SvcModel().HashGlobs(i.prime.Project().Dir(), patterns) if err != nil { return "", nil, errs.Wrap(err, "Could not hash globs") } + hash, err := hashFuncCall(i.funcCall, hashed.Hash) + if err != nil { + return "", nil, errs.Wrap(err, "Could not hash function call") + } + + return hash, hashed.Files, nil +} + +func hashFuncCall(fc *buildscript.FuncCall, seed string) (string, error) { // Combine file hash with function call hash + // We clone the function call here because the (potentially old) hash itself should not be used to calculate the hash + // and unsetting it should not propagate beyond the context of this function. fcc, err := deep.Copy(fc) if err != nil { - return "", nil, errs.Wrap(err, "Could not copy function call") + return "", errs.Wrap(err, "Could not copy function call") } - fcc.UnsetArgument("hash") // The (potentially old) hash itself should not be used to calculate the hash + fcc.UnsetArgument("hash") fcb, err := json.Marshal(fcc) if err != nil { - return "", nil, errs.Wrap(err, "Could not marshal function call") + return "", errs.Wrap(err, "Could not marshal function call") } hasher := xxhash.New() - hasher.Write([]byte(hashed.Hash)) + hasher.Write([]byte(seed)) hasher.Write(fcb) hash := fmt.Sprintf("%016x", hasher.Sum64()) - - return hash, hashed.Files, nil -} - -type invalidDepsValueType struct { - error -} - -type invalidDepValueType struct { - error + return hash, nil } -func (c *Commit) resolveDependencies(fc *buildscript.FuncCall) ([]request.PublishVariableDep, error) { +func (i *IngredientCall) resolveDependencies() ([]request.PublishVariableDep, error) { deps := []request.PublishVariableDep{} - bsDeps := fc.Argument("deps") - if bsDeps != nil { + bsDeps := i.funcCall.Argument("deps") + if bsDeps == nil { return deps, nil } @@ -140,11 +164,11 @@ func (c *Commit) resolveDependencies(fc *buildscript.FuncCall) ([]request.Publis return deps, nil } -func (c *Commit) isIngredientCallCached(atTime *time.Time, ns model.Namespace, hash string) (bool, error) { +func (i *IngredientCall) isCached(hash string) (bool, error) { // Check against our local cache to see if we've already handled this file hash // Technically we don't need this because the SearchIngredients call below already verifies this, but searching // ingredients is slow, and local cache is FAST. - cacheValue, err := c.prime.SvcModel().GetCache(fmt.Sprintf(cacheKeyFiles, hash)) + cacheValue, err := i.prime.SvcModel().GetCache(fmt.Sprintf(cacheKeyFiles, hash)) if err != nil { return false, errs.Wrap(err, "Could not get build script cache") } @@ -154,7 +178,7 @@ func (c *Commit) isIngredientCallCached(atTime *time.Time, ns model.Namespace, h } // Check against API to see if we've already published this file hash - ingredients, err := model.SearchIngredientsStrict(ns.String(), hash, true, false, atTime, c.prime.Auth()) + ingredients, err := model.SearchIngredientsStrict(i.ns.String(), hash, true, false, i.script.AtTime(), i.prime.Auth()) if err != nil { return false, errs.Wrap(err, "Could not search ingredients") } @@ -166,8 +190,8 @@ func (c *Commit) isIngredientCallCached(atTime *time.Time, ns model.Namespace, h return false, nil // If we made it this far it means we did not find any existing cache entry; so it's dirty } -func (c *Commit) setIngredientCallCached(hash string) { - err := c.prime.SvcModel().SetCache(fmt.Sprintf(cacheKeyFiles, hash), hash, time.Hour*24*7) +func (i *IngredientCall) setCached(hash string) { + err := i.prime.SvcModel().SetCache(fmt.Sprintf(cacheKeyFiles, hash), hash, time.Hour*24*7) if err != nil { logging.Warning("Could not set build script cache: %s", errs.JoinMessage(err)) } diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index 65431f7155..819a1d2a31 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -1,30 +1,154 @@ package commit -import "testing" +import ( + "fmt" + "testing" + + "github.com/ActiveState/cli/pkg/buildscript" + "github.com/ActiveState/cli/pkg/platform/api/graphql/request" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const simpleScript = ` +main = ingredient( + src = ["*/**.py", "pyproject.toml"], + hash = "", + deps = [ + Req(name="python", namespace="language", version=Eq(value="3.7.10")) + ] +) +` + +const simpleAlteredScript = ` +main = ingredient( + src = ["*/**.py", "pyproject.toml"], + hash = "", + deps = [ + Req(name="python", namespace="language", version=Eq(value="3.7.10")), + Req(name="python-module-builder", namespace="builder", version=Gt(value="0")), + ] +) +` + +const invalidDepsScript = ` +main = ingredient( + deps = "I should be a slice" +) +` + +const invalidDepScript = ` +main = ingredient( + deps = [ "I should be a Req" ] +) +` func Test_hashFuncCall(t *testing.T) { + type args struct { - fc *buildscript.FuncCall - seed string + script string + seed string } tests := []struct { - name string - args args - want string - wantErr bool + name string + args args + want string }{ - // TODO: Add test cases. + { + "Simple", + args{ + script: simpleScript, + seed: "", + }, + "6fa602bc516a918e", + }, + { + "Simple Altered", + args{ + script: simpleAlteredScript, + seed: "", + }, + "b74d9b5cf2e6b0ee", + }, + { + "Simple With Seed", + args{ + script: simpleScript, + seed: "seed", + }, + "9a9915a8bf84c7ad", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := hashFuncCall(tt.args.fc, tt.args.seed) - if (err != nil) != tt.wantErr { - t.Errorf("hashFuncCall() error = %v, wantErr %v", err, tt.wantErr) + bs, err := buildscript.Unmarshal([]byte(tt.args.script)) + require.NoError(t, err) + fc := bs.FunctionCalls("ingredient")[0] + hashBefore := fc.Argument("hash").(string) + got, err := hashFuncCall(fc, tt.args.seed) + if err != nil { + t.Errorf("hashFuncCall() error = %v", err) return } + hashAfter := fc.Argument("hash").(string) if got != tt.want { t.Errorf("hashFuncCall() got = %v, want %v", got, tt.want) } + assert.Equal(t, hashBefore, hashAfter) // calculating the hash should not affect the hash + }) + } +} + +func TestIngredientCall_resolveDependencies(t *testing.T) { + tests := []struct { + name string + script string + want []request.PublishVariableDep + wantErr assert.ErrorAssertionFunc + }{ + { + "Simple", + simpleScript, + []request.PublishVariableDep{ + { + Dependency: request.Dependency{ + Name: "python", + Namespace: "language", + VersionRequirements: "3.7.10", + }, + Conditions: []request.Dependency{}, + }, + }, + assert.NoError, + }, + { + "Invalid Deps", + invalidDepsScript, + nil, + func(t assert.TestingT, err error, _ ...interface{}) bool { + return assert.ErrorAs(t, err, &invalidDepsValueType{}) + }, + }, + { + "Invalid Dep", + invalidDepScript, + nil, + func(t assert.TestingT, err error, _ ...interface{}) bool { + return assert.ErrorAs(t, err, &invalidDepValueType{}) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bs, err := buildscript.Unmarshal([]byte(tt.script)) + require.NoError(t, err) + fc := bs.FunctionCalls("ingredient")[0] + i := &IngredientCall{script: bs, funcCall: fc} + got, err := i.resolveDependencies() + if !tt.wantErr(t, err, fmt.Sprintf("resolveDependencies()")) { + return + } + assert.Equalf(t, tt.want, got, "resolveDependencies()") }) } } From 252fc24e98cf6c03dd70d6c66fbfb9ac4361fc50 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 10:00:07 -0700 Subject: [PATCH 219/440] Support different dependency types --- internal/runners/commit/ingredientcall.go | 20 ++++++- .../runners/commit/ingredientcall_test.go | 59 +++++++++++++++++-- pkg/platform/api/graphql/request/publish.go | 16 ++--- 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 81d12f130e..6424d82f98 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -135,8 +135,25 @@ func hashFuncCall(fc *buildscript.FuncCall, seed string) (string, error) { } func (i *IngredientCall) resolveDependencies() ([]request.PublishVariableDep, error) { + result := []request.PublishVariableDep{} + for key, typ := range map[string]request.DependencyType{ + "runtime_deps": request.DependencyTypeRuntime, + "build_deps": request.DependencyTypeBuild, + "test_deps": request.DependencyTypeTest, + } { + deps, err := i.resolveDependenciesByKey(key, typ) + if err != nil { + return nil, errs.Wrap(err, "Could not resolve %s", key) + } + result = append(result, deps...) + } + + return result, nil +} + +func (i *IngredientCall) resolveDependenciesByKey(key string, typ request.DependencyType) ([]request.PublishVariableDep, error) { deps := []request.PublishVariableDep{} - bsDeps := i.funcCall.Argument("deps") + bsDeps := i.funcCall.Argument(key) if bsDeps == nil { return deps, nil } @@ -156,6 +173,7 @@ func (i *IngredientCall) resolveDependencies() ([]request.PublishVariableDep, er Name: req.Name, Namespace: req.Namespace, VersionRequirements: model.BuildPlannerVersionConstraintsToString(req.VersionRequirement), + Type: typ, }, []request.Dependency{}, }) diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index 819a1d2a31..482828f893 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -14,7 +14,7 @@ const simpleScript = ` main = ingredient( src = ["*/**.py", "pyproject.toml"], hash = "", - deps = [ + runtime_deps = [ Req(name="python", namespace="language", version=Eq(value="3.7.10")) ] ) @@ -24,22 +24,38 @@ const simpleAlteredScript = ` main = ingredient( src = ["*/**.py", "pyproject.toml"], hash = "", - deps = [ + runtime_deps = [ Req(name="python", namespace="language", version=Eq(value="3.7.10")), Req(name="python-module-builder", namespace="builder", version=Gt(value="0")), ] ) ` +const depTypesScript = ` +main = ingredient( + src = ["*/**.py", "pyproject.toml"], + hash = "", + runtime_deps = [ + Req(name="runtimedep", namespace="language", version=Eq(value="1.0")) + ], + build_deps = [ + Req(name="builddep", namespace="language", version=Eq(value="2.0")) + ], + test_deps = [ + Req(name="testdep", namespace="language", version=Eq(value="3.0")) + ], +) +` + const invalidDepsScript = ` main = ingredient( - deps = "I should be a slice" + runtime_deps = "I should be a slice" ) ` const invalidDepScript = ` main = ingredient( - deps = [ "I should be a Req" ] + runtime_deps = [ "I should be a Req" ] ) ` @@ -115,6 +131,41 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { Name: "python", Namespace: "language", VersionRequirements: "3.7.10", + Type: request.DependencyTypeRuntime, + }, + Conditions: []request.Dependency{}, + }, + }, + assert.NoError, + }, + { + "All Types", + depTypesScript, + []request.PublishVariableDep{ + { + Dependency: request.Dependency{ + Name: "runtimedep", + Namespace: "language", + VersionRequirements: "1.0", + Type: request.DependencyTypeRuntime, + }, + Conditions: []request.Dependency{}, + }, + { + Dependency: request.Dependency{ + Name: "builddep", + Namespace: "language", + VersionRequirements: "2.0", + Type: request.DependencyTypeBuild, + }, + Conditions: []request.Dependency{}, + }, + { + Dependency: request.Dependency{ + Name: "testdep", + Namespace: "language", + VersionRequirements: "3.0", + Type: request.DependencyTypeTest, }, Conditions: []request.Dependency{}, }, diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index 61d6090228..8daaa679d5 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -14,10 +14,12 @@ import ( "gopkg.in/yaml.v3" ) +type DependencyType string + const ( - DependencyTypeRuntime = "runtime" - DependencyTypeBuild = "build" - DependencyTypeTest = "test" + DependencyTypeRuntime DependencyType = "runtime" + DependencyTypeBuild = "build" + DependencyTypeTest = "test" ) func Publish(vars PublishVariables, filepath string) (*PublishInput, error) { @@ -85,10 +87,10 @@ type PublishVariableFeature struct { } type Dependency struct { - Name string `yaml:"name" json:"name"` - Namespace string `yaml:"namespace" json:"namespace"` - VersionRequirements string `yaml:"versionRequirements,omitempty" json:"versionRequirements,omitempty"` - Type string `yaml:"type,omitempty" json:"type,omitempty"` + Name string `yaml:"name" json:"name"` + Namespace string `yaml:"namespace" json:"namespace"` + VersionRequirements string `yaml:"versionRequirements,omitempty" json:"versionRequirements,omitempty"` + Type DependencyType `yaml:"type,omitempty" json:"type,omitempty"` } // ExampleAuthorVariables is used for presenting sample data to the user, it's not used for graphql input From 2a0451e4b104045835699109e20c52e50bda4d8b Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 11:04:14 -0700 Subject: [PATCH 220/440] Update hashes as they changed due to new dependency types --- internal/runners/commit/ingredientcall_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index 482828f893..95c239d987 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -60,7 +60,6 @@ main = ingredient( ` func Test_hashFuncCall(t *testing.T) { - type args struct { script string seed string @@ -76,7 +75,7 @@ func Test_hashFuncCall(t *testing.T) { script: simpleScript, seed: "", }, - "6fa602bc516a918e", + "6a7c7bd03f10e832", }, { "Simple Altered", @@ -84,7 +83,7 @@ func Test_hashFuncCall(t *testing.T) { script: simpleAlteredScript, seed: "", }, - "b74d9b5cf2e6b0ee", + "1471d1796a57e938", }, { "Simple With Seed", @@ -92,7 +91,7 @@ func Test_hashFuncCall(t *testing.T) { script: simpleScript, seed: "seed", }, - "9a9915a8bf84c7ad", + "a9c1a37b5dd6f0d6", }, } for _, tt := range tests { From ab0569e39d00949681ec52787ee7af2be745ea66 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 11:17:48 -0700 Subject: [PATCH 221/440] Fix unit test --- pkg/buildscript/mutations_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/buildscript/mutations_test.go b/pkg/buildscript/mutations_test.go index 948e338bca..6b61057075 100644 --- a/pkg/buildscript/mutations_test.go +++ b/pkg/buildscript/mutations_test.go @@ -344,8 +344,8 @@ func TestUpdatePlatform(t *testing.T) { data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", tt.args.filename)) assert.NoError(t, err) - script, err := UnmarshalBuildExpression(data, nil) - assert.NoError(t, err) + script := New() + assert.NoError(t, script.UnmarshalBuildExpression(data)) if tt.args.operation == types.OperationAdded { err = script.AddPlatform(tt.args.platform) From 28354bcfc2c9022be551c97e948b4d65ccaff96c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 12:51:39 -0700 Subject: [PATCH 222/440] Fix badly formatted errors --- pkg/platform/model/buildplanner/publish.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/platform/model/buildplanner/publish.go b/pkg/platform/model/buildplanner/publish.go index c680ce6e81..0be3e3b74f 100644 --- a/pkg/platform/model/buildplanner/publish.go +++ b/pkg/platform/model/buildplanner/publish.go @@ -14,11 +14,11 @@ func (b *BuildPlanner) Publish(vars request.PublishVariables, filepath string) ( res := graphModel.PublishResponse{} if err := b.client.Run(pr, &res); err != nil { - return nil, errs.Wrap(err, "", err.Error()) + return nil, errs.Wrap(err, "Publish failed") } if res.Result.Error != "" { - return nil, errs.New("API responded with error: {{.V0}}", res.Result.Error) + return nil, errs.New("API responded with error: %s", res.Result.Error) } return &res.Result, nil From 5bfb3d2bf49d6c601564ccbffaa00a2ba3cb473a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 13:10:18 -0700 Subject: [PATCH 223/440] Fix failing test due to formatting change --- pkg/buildscript/marshal_buildexpression.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index aa04e38dc8..cb8a152803 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -49,7 +49,7 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { let[key] = value } m[letKey] = let - return json.Marshal(m) + return json.MarshalIndent(m, "", " ") } // Note: all of the MarshalJSON functions are named the way they are because Go's JSON package From 23e75891c57d691e527770d5cc8b35630cd4cc87 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 13:48:21 -0700 Subject: [PATCH 224/440] Ensure consistent comparison --- internal/runners/commit/ingredientcall_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index 95c239d987..e5f3a1fa52 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -2,6 +2,7 @@ package commit import ( "fmt" + "sort" "testing" "github.com/ActiveState/cli/pkg/buildscript" @@ -198,6 +199,12 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { if !tt.wantErr(t, err, fmt.Sprintf("resolveDependencies()")) { return } + sort.Slice(tt.want, func(i, j int) bool { + return tt.want[i].Name < tt.want[j].Name + }) + sort.Slice(got, func(i, j int) bool { + return got[i].Name < got[j].Name + }) assert.Equalf(t, tt.want, got, "resolveDependencies()") }) } From 167a8a9f405b45b5babb20ef5adfe4f334ac737b Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 14:06:57 -0700 Subject: [PATCH 225/440] Comments and organization --- internal/runners/commit/ingredientcall.go | 21 ++++++++++--- pkg/buildscript/buildscript.go | 38 +++++++++++++++++++++++ pkg/buildscript/queries.go | 29 ----------------- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 6424d82f98..2418b4e6a4 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -27,6 +27,8 @@ type invalidDepsValueType struct{ error } type invalidDepValueType struct{ error } +// IngredientCall is used to evaluate ingredient() function calls and publishes the ingredient in question if it is not +// already published. type IngredientCall struct { prime primeable script *buildscript.BuildScript @@ -47,6 +49,8 @@ func NewIngredientCall( } } +// Resolve will check if the ingredient call refers to an existing ingredient, and if not will create it and update +// the buildscript accordingly. func (i *IngredientCall) Resolve() error { hash, hashedFiles, err := i.calculateHash() if err != nil { @@ -58,9 +62,11 @@ func (i *IngredientCall) Resolve() error { return errs.Wrap(err, "Could not check if ingredient call is cached") } if cached { + // Ingredient already exists, nothing to do return nil } + // Creative tar.gz with all the references files for this ingredient files := []string{} for _, f := range hashedFiles { files = append(files, f.Path) @@ -71,6 +77,7 @@ func (i *IngredientCall) Resolve() error { } defer os.Remove(tmpFile) + // Parse buildscript dependencies deps, err := i.resolveDependencies() if err != nil { return errs.Wrap(err, "Could not resolve dependencies") @@ -94,6 +101,8 @@ func (i *IngredientCall) Resolve() error { return nil } +// calculateHash will calculate a hash based on the files references in the ingredient as well as the ingredient +// rule itself. The ingredient is considered dirty when either the files or the rule itself has changed. func (i *IngredientCall) calculateHash() (string, []*graph.GlobFileResult, error) { src := i.funcCall.Argument("src") patterns, ok := src.([]string) @@ -113,8 +122,9 @@ func (i *IngredientCall) calculateHash() (string, []*graph.GlobFileResult, error return hash, hashed.Files, nil } +// hashFuncCall will calculate the individual hash of the ingredient function call itself. +// The hash argument is excluded from this calculation. func hashFuncCall(fc *buildscript.FuncCall, seed string) (string, error) { - // Combine file hash with function call hash // We clone the function call here because the (potentially old) hash itself should not be used to calculate the hash // and unsetting it should not propagate beyond the context of this function. fcc, err := deep.Copy(fc) @@ -134,6 +144,8 @@ func hashFuncCall(fc *buildscript.FuncCall, seed string) (string, error) { return hash, nil } +// resolveDependencies iterates over the different dependency arguments the ingredient function supports and resolves +// them into the appropriate types used by our models. func (i *IngredientCall) resolveDependencies() ([]request.PublishVariableDep, error) { result := []request.PublishVariableDep{} for key, typ := range map[string]request.DependencyType{ @@ -151,6 +163,7 @@ func (i *IngredientCall) resolveDependencies() ([]request.PublishVariableDep, er return result, nil } +// resolveDependenciesByKey turns ingredient dependencies into the appropriate types used by our models func (i *IngredientCall) resolveDependenciesByKey(key string, typ request.DependencyType) ([]request.PublishVariableDep, error) { deps := []request.PublishVariableDep{} bsDeps := i.funcCall.Argument(key) @@ -182,10 +195,9 @@ func (i *IngredientCall) resolveDependenciesByKey(key string, typ request.Depend return deps, nil } +// isCached checks against our local cache to see if we've already handled this file hash, and if no local cache +// exists checks against the platform ingredient API. func (i *IngredientCall) isCached(hash string) (bool, error) { - // Check against our local cache to see if we've already handled this file hash - // Technically we don't need this because the SearchIngredients call below already verifies this, but searching - // ingredients is slow, and local cache is FAST. cacheValue, err := i.prime.SvcModel().GetCache(fmt.Sprintf(cacheKeyFiles, hash)) if err != nil { return false, errs.Wrap(err, "Could not get build script cache") @@ -208,6 +220,7 @@ func (i *IngredientCall) isCached(hash string) (bool, error) { return false, nil // If we made it this far it means we did not find any existing cache entry; so it's dirty } +// Update our local cache saying we've handled this hash, allowing for faster cache checks than using the platform api func (i *IngredientCall) setCached(hash string) { err := i.prime.SvcModel().SetCache(fmt.Sprintf(cacheKeyFiles, hash), hash, time.Hour*24*7) if err != nil { diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 42ce79582c..fcaf7981c7 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -1,6 +1,8 @@ package buildscript import ( + "errors" + "fmt" "time" "github.com/ActiveState/cli/internal/condition" @@ -77,6 +79,8 @@ func (f *FuncCall) MarshalJSON() ([]byte, error) { return f.fc.MarshalJSON() } +// Argument returns the value of the given argument, or nil if it does not exist +// You will still need to cast the value to the correct type. func (f *FuncCall) Argument(name string) any { for _, a := range f.fc.Arguments { if a.Assignment == nil || a.Assignment.Key != name { @@ -103,6 +107,7 @@ func (f *FuncCall) SetArgument(k string, v *value) { return } +// UnsetArgument will remove the given argument, if it exists func (f *FuncCall) UnsetArgument(k string) { for i, a := range f.fc.Arguments { if a.Assignment == nil || a.Assignment.Key != k { @@ -114,6 +119,7 @@ func (f *FuncCall) UnsetArgument(k string) { } // Value turns a standard type into a buildscript compatible type +// Intended for use with functions like SetArgument. func Value[T string | float64 | []string | []float64](inputv T) *value { v := &value{} switch vt := any(inputv).(type) { @@ -138,6 +144,38 @@ func Value[T string | float64 | []string | []float64](inputv T) *value { return v } +// exportValue takes a raw buildscript value and turns it into an externally consumable one +// Note not all value types are currently fully supported. For example assignments and objects currently are +// passed as the raw type, which can't be cast externally as they are private types. +// We'll want to update these as the use-cases for them become more clear. +func exportValue(v *value) any { + switch { + case v.FuncCall != nil: + if req := parseRequirement(v); req != nil { + return req + } + return &FuncCall{v.FuncCall} + case v.List != nil: + result := []any{} + for _, value := range *v.List { + result = append(result, exportValue(value)) + } + return result + case v.Str != nil: + return strValue(v) + case v.Number != nil: + return *v.Number + case v.Null != nil: + return nil + case v.Assignment != nil: + return v.Assignment + case v.Object != nil: + return v.Object + } + return errors.New(fmt.Sprintf("unknown value type: %#v", v)) +} + +// FunctionCalls will return all function calls that match the given name, regardless of where they occur. func (b *BuildScript) FunctionCalls(name string) []*FuncCall { result := []*FuncCall{} for _, f := range b.raw.FuncCalls() { diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 718e3ffe62..73b5204a40 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -1,8 +1,6 @@ package buildscript import ( - "errors" - "fmt" "strings" "github.com/ActiveState/cli/internal/logging" @@ -119,33 +117,6 @@ func parseRequirement(req *value) Requirement { } } -func exportValue(v *value) any { - switch { - case v.FuncCall != nil: - if req := parseRequirement(v); req != nil { - return req - } - return &FuncCall{v.FuncCall} - case v.List != nil: - result := []any{} - for _, value := range *v.List { - result = append(result, exportValue(value)) - } - return result - case v.Str != nil: - return strValue(v) - case v.Number != nil: - return *v.Number - case v.Null != nil: - return nil - case v.Assignment != nil: - return v.Assignment - case v.Object != nil: - return v.Object - } - return errors.New(fmt.Sprintf("unknown value type: %#v", v)) -} - // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, // which are the most common. // ONLY use this when you know you only need to care about dependencies. From 92aab95fbb214a650136ff940a6ac33ae3b72e00 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 11 Oct 2024 14:40:10 -0700 Subject: [PATCH 226/440] Revert "Merge branch version/0-47-0-RC1 to adopt changes from PR #3531" This reverts commit 28d0d68f0fb20e15c07810aa3153baefc7388a13, reversing changes made to 1827a60e496f3d3a606a09c9d7f40b92cc846b29. --- pkg/buildscript/queries.go | 89 +++----------------- pkg/buildscript/unmarshal.go | 10 --- pkg/buildscript/unmarshal_buildexpression.go | 19 +++-- 3 files changed, 26 insertions(+), 92 deletions(-) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 55e07927dc..2f4a1a6c54 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -4,7 +4,6 @@ import ( "strings" "github.com/go-openapi/strfmt" - "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" @@ -13,7 +12,6 @@ import ( const ( solveFuncName = "solve" solveLegacyFuncName = "solve_legacy" - srcKey = "src" requirementsKey = "requirements" platformsKey = "platforms" ) @@ -45,10 +43,8 @@ type UnknownRequirement struct { func (r UnknownRequirement) IsRequirement() {} -// Returns the requirements for the given target. -// If no target is given, uses the default target (i.e. the name assigned to 'main'). -func (b *BuildScript) Requirements(targets ...string) ([]Requirement, error) { - requirementsNode, err := b.getRequirementsNode(targets...) +func (b *BuildScript) Requirements() ([]Requirement, error) { + requirementsNode, err := b.getRequirementsNode() if err != nil { return nil, errs.Wrap(err, "Could not get requirements node") } @@ -99,8 +95,8 @@ func (b *BuildScript) Requirements(targets ...string) ([]Requirement, error) { // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, // which are the most common. // ONLY use this when you know you only need to care about dependencies. -func (b *BuildScript) DependencyRequirements(targets ...string) ([]types.Requirement, error) { - reqs, err := b.Requirements(targets...) +func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { + reqs, err := b.Requirements() if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } @@ -113,8 +109,8 @@ func (b *BuildScript) DependencyRequirements(targets ...string) ([]types.Require return deps, nil } -func (b *BuildScript) getRequirementsNode(targets ...string) (*Value, error) { - node, err := b.getSolveNode(targets...) +func (b *BuildScript) getRequirementsNode() (*Value, error) { + node, err := b.getSolveNode() if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -151,23 +147,7 @@ func getVersionRequirements(v *Value) []types.VersionRequirement { return reqs } -func isSolveFuncName(name string) bool { - return name == solveFuncName || name == solveLegacyFuncName -} - -func (b *BuildScript) getTargetSolveNode(targets ...string) (*Value, error) { - if len(targets) == 0 { - for _, assignment := range b.raw.Assignments { - if assignment.Key != mainKey { - continue - } - if assignment.Value.Ident != nil && *assignment.Value.Ident != "" { - targets = []string{*assignment.Value.Ident} - break - } - } - } - +func (b *BuildScript) getSolveNode() (*Value, error) { var search func([]*Assignment) *Value search = func(assignments []*Assignment) *Value { var nextLet []*Assignment @@ -177,13 +157,7 @@ func (b *BuildScript) getTargetSolveNode(targets ...string) (*Value, error) { continue } - if funk.Contains(targets, a.Key) && a.Value.FuncCall != nil { - return a.Value - } - - if f := a.Value.FuncCall; len(targets) == 0 && f != nil && isSolveFuncName(f.Name) { - // This is coming from a complex build expression with no straightforward way to determine - // a default target. Fall back on a top-level solve node. + if f := a.Value.FuncCall; f != nil && (f.Name == solveFuncName || f.Name == solveLegacyFuncName) { return a.Value } } @@ -195,50 +169,15 @@ func (b *BuildScript) getTargetSolveNode(targets ...string) (*Value, error) { return nil } - if node := search(b.raw.Assignments); node != nil { return node, nil } - return nil, errNodeNotFound -} - -func (b *BuildScript) getSolveNode(targets ...string) (*Value, error) { - node, err := b.getTargetSolveNode(targets...) - if err != nil { - return nil, errs.Wrap(err, "Could not get target node") - } - - // If the target is the solve function, we're done. - if isSolveFuncName(node.FuncCall.Name) { - return node, nil - } - - // Otherwise, the "src" key contains a reference to the solve node. - // For example: - // - // runtime = state_tool_artifacts_v1(src = sources) - // sources = solve(at_time = ..., platforms = [...], requirements = [...], ...) - // - // Look over the build expression again for that referenced node. - for _, arg := range node.FuncCall.Arguments { - if arg.Assignment == nil { - continue - } - a := arg.Assignment - if a.Key == srcKey && a.Value.Ident != nil { - node, err := b.getSolveNode(*a.Value.Ident) - if err != nil { - return nil, errs.Wrap(err, "Could not get solve node from target") - } - return node, nil - } - } return nil, errNodeNotFound } -func (b *BuildScript) getSolveAtTimeValue(targets ...string) (*Value, error) { - node, err := b.getSolveNode(targets...) +func (b *BuildScript) getSolveAtTimeValue() (*Value, error) { + node, err := b.getSolveNode() if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -252,8 +191,8 @@ func (b *BuildScript) getSolveAtTimeValue(targets ...string) (*Value, error) { return nil, errValueNotFound } -func (b *BuildScript) Platforms(targets ...string) ([]strfmt.UUID, error) { - node, err := b.getPlatformsNode(targets...) +func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { + node, err := b.getPlatformsNode() if err != nil { return nil, errs.Wrap(err, "Could not get platform node") } @@ -265,8 +204,8 @@ func (b *BuildScript) Platforms(targets ...string) ([]strfmt.UUID, error) { return list, nil } -func (b *BuildScript) getPlatformsNode(targets ...string) (*Value, error) { - node, err := b.getSolveNode(targets...) +func (b *BuildScript) getPlatformsNode() (*Value, error) { + node, err := b.getSolveNode() if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index ffbab27f9e..8e3fbe8b03 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -50,15 +50,5 @@ func Unmarshal(data []byte) (*BuildScript, error) { break } - // Verify there are no duplicate key assignments. - // This is primarily to catch duplicate solve nodes for a given target. - seen := make(map[string]bool) - for _, assignment := range raw.Assignments { - if _, exists := seen[assignment.Key]; exists { - return nil, locale.NewInputError(locale.Tl("err_buildscript_duplicate_keys", "Build script has duplicate '{{.V0}}' assignments", assignment.Key)) - } - seen[assignment.Key] = true - } - return &BuildScript{raw}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 31da4efed8..7515205e10 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -102,6 +102,18 @@ func UnmarshalBuildExpression(data []byte, atTime *time.Time) (*BuildScript, err script.raw.AtTime = atTime } + // If the requirements are in legacy object form, e.g. + // requirements = [{"name": "", "namespace": ""}, {...}, ...] + // then transform them into function call form for the AScript format, e.g. + // requirements = [Req(name = "", namespace = ""), Req(...), ...] + requirements, err := script.getRequirementsNode() + if err != nil { + return nil, errs.Wrap(err, "Could not get requirements node") + } + if isLegacyRequirementsList(requirements) { + requirements.List = transformRequirements(requirements).List + } + return script, nil } @@ -258,13 +270,6 @@ func unmarshalFuncCall(path []string, m map[string]interface{}) (*FuncCall, erro if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface) } - if key == requirementsKey && isSolveFuncName(name) && isLegacyRequirementsList(value) { - // If the requirements are in legacy object form, e.g. - // requirements = [{"name": "", "namespace": ""}, {...}, ...] - // then transform them into function call form for the AScript format, e.g. - // requirements = [Req(name = "", namespace = ""), Req(...), ...] - value.List = transformRequirements(value).List - } args = append(args, &Value{Assignment: &Assignment{key, value}}) } sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Key < args[j].Assignment.Key }) From c85719e31e15ec3648a9d609711bb43618fbab91 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 15 Oct 2024 11:37:36 -0700 Subject: [PATCH 227/440] Update command and expect --- test/integration/msg_int_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/msg_int_test.go b/test/integration/msg_int_test.go index 40ce6f867d..ef0870027e 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/msg_int_test.go @@ -78,7 +78,6 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long cp := ts.SpawnWithOpts(e2e.OptArgs("config"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple message`) - cp.Expect("Usage:") cp.ExpectExitCode(0) // Ensure message doesn't stick around when we run another command From 041b4c6e9be114dfd0f0dc5ed73ddcd6bc1d8f5c Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 15 Oct 2024 13:12:48 -0700 Subject: [PATCH 228/440] Use --version --- test/integration/msg_int_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/integration/msg_int_test.go b/test/integration/msg_int_test.go index ef0870027e..9cff086c0e 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/msg_int_test.go @@ -24,8 +24,8 @@ func (suite *MsgIntegrationTestSuite) TestMessage_None() { // We test on config as it just dumps help and has minimal output // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long - cp := ts.Spawn("config") - cp.Expect("Usage:") + cp := ts.Spawn("--version") + cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) // Note: since message failures should fail silently without impacting the user we need to check @@ -76,7 +76,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { // We test on config as it just dumps help and has minimal output // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long - cp := ts.SpawnWithOpts(e2e.OptArgs("config"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple message`) cp.ExpectExitCode(0) @@ -109,8 +109,8 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic_PlacementAfter() { // We test on config as it just dumps help and has minimal output // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long - cp := ts.SpawnWithOpts(e2e.OptArgs("config"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) - cp.Expect("Usage:") + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.Expect(`This is a simple message`) cp.ExpectExitCode(0) } @@ -130,19 +130,19 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic_InterruptPrompt() { ]`, graph.MessageInterruptTypePrompt)), 0755) suite.Require().NoError(err) - cp := ts.SpawnWithOpts(e2e.OptArgs("config"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple message`) cp.Expect("Press ENTER to continue") time.Sleep(time.Millisecond * 100) - suite.Require().NotContains(cp.Output(), "Usage:") + suite.Require().NotContains(cp.Output(), "ActiveState CLI by ActiveState Software Inc.") cp.SendEnter() - cp.Expect("Usage:") + cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) // Test that non-interactive does not prompt - cp = ts.SpawnWithOpts(e2e.OptArgs("config", "-n"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp = ts.SpawnCmdWithOpts("state", e2e.OptArgs("--version", "-n"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple message`) - cp.Expect("Usage:") + cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) suite.Require().NotContains(cp.Output(), "Press ENTER to continue") } @@ -161,10 +161,10 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic_InterruptExit() { ]`, graph.MessageInterruptTypeExit)), 0755) suite.Require().NoError(err) - cp := ts.SpawnWithOpts(e2e.OptArgs("config"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.ExpectExitCode(1) suite.Require().Contains(cp.Snapshot(), "This is a simple message") - suite.Require().NotContains(cp.Output(), "Usage:") + suite.Require().NotContains(cp.Output(), "ActiveState CLI by ActiveState Software Inc.") ts.IgnoreLogErrors() } From c1105c2d9d7a7dbea9fbd5bea9df8eb3225e717f Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 15 Oct 2024 13:45:25 -0700 Subject: [PATCH 229/440] Add back expect --- test/integration/msg_int_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/msg_int_test.go b/test/integration/msg_int_test.go index 9cff086c0e..cb59dff276 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/msg_int_test.go @@ -78,6 +78,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple message`) + cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) // Ensure message doesn't stick around when we run another command From 68c742f50ddcd9fe3e83026865afcbcafe1c0e2f Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 15 Oct 2024 16:03:37 -0700 Subject: [PATCH 230/440] Remove comments --- test/integration/msg_int_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/integration/msg_int_test.go b/test/integration/msg_int_test.go index cb59dff276..0224d3ad3d 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/msg_int_test.go @@ -22,8 +22,6 @@ func (suite *MsgIntegrationTestSuite) TestMessage_None() { ts := e2e.New(suite.T(), false) defer ts.Close() - // We test on config as it just dumps help and has minimal output - // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long cp := ts.Spawn("--version") cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) @@ -74,8 +72,6 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(tt.MessageJson), 0755) suite.Require().NoError(err) - // We test on config as it just dumps help and has minimal output - // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple message`) cp.Expect("ActiveState CLI by ActiveState Software Inc.") @@ -108,8 +104,6 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic_PlacementAfter() { ]`, graph.MessagePlacementTypeAfterCmd)), 0755) suite.Require().NoError(err) - // We test on config as it just dumps help and has minimal output - // The base state command would also work, but it's output is more verbose and termtest likes to cut off content if it's too long cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.Expect(`This is a simple message`) From 288fcb1b484a7097218c7b84e9e10efc3ab3f7a5 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 16 Oct 2024 09:28:22 -0700 Subject: [PATCH 231/440] Consistently handle hash values --- cmd/state-svc/internal/hash/file_hasher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 93c4dbe8f2..94f50b0177 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -89,7 +89,7 @@ func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashed }) // Incorporate the individual file hash into the overall hash in hex format - fmt.Fprintf(hasher, "%x", hash) + fmt.Fprintf(hasher, "%016x", hash) } } From 4754067329f086987d3dd97b6afb0c5253a6b1a4 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 16 Oct 2024 09:32:30 -0700 Subject: [PATCH 232/440] Add comment explaining parseRequirement --- pkg/buildscript/queries.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 77af45b3e5..3a64186fa5 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -79,6 +79,9 @@ func exportRequirements(v *value) []Requirement { return requirements } +// parseRequirement turns a raw *value representing a requirement into an externally consumable requirement type +// It accepts any value as input. If the value does not represent a requirement it simply won't be acted on and a nill +// will be returned. func parseRequirement(req *value) Requirement { if req.FuncCall == nil { return nil From 782b533e76cff77d7cd7076cdde784f13cd684f3 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 16 Oct 2024 09:34:22 -0700 Subject: [PATCH 233/440] Add processBuildPlannerError handling --- pkg/platform/model/buildplanner/publish.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/platform/model/buildplanner/publish.go b/pkg/platform/model/buildplanner/publish.go index 0be3e3b74f..4f23d06802 100644 --- a/pkg/platform/model/buildplanner/publish.go +++ b/pkg/platform/model/buildplanner/publish.go @@ -14,7 +14,7 @@ func (b *BuildPlanner) Publish(vars request.PublishVariables, filepath string) ( res := graphModel.PublishResponse{} if err := b.client.Run(pr, &res); err != nil { - return nil, errs.Wrap(err, "Publish failed") + return nil, processBuildPlannerError(err, "Publish failed") } if res.Result.Error != "" { From bdcc5ab9bbf000a37255472b7351e3c31fa1602b Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 11:32:02 -0700 Subject: [PATCH 234/440] Check for process in use before cleaning cache --- internal/locale/locales/en-us.yaml | 2 ++ internal/runners/clean/cache.go | 43 ++++++++++++++++++++++++++--- internal/runners/clean/uninstall.go | 1 + 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index a23b5d66b3..44da292ab1 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1570,3 +1570,5 @@ config_get_help: other: "To GET the value for a specific config key run '[ACTIONABLE]state config get [/RESET]'" config_set_help: other: "To SET the value for a specific config key run '[ACTIONABLE]state config set [/RESET]'" +err_clean_in_use: + other: "Could not clean your cache as you appear to have a runtime in use. Please stop any running State Tool processes and project runtime processes and try again." \ No newline at end of file diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index b20c6fa716..6e5591fb27 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -1,6 +1,7 @@ package clean import ( + "context" "os" "github.com/ActiveState/cli/internal/constants" @@ -10,11 +11,13 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/svcctl" + "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/projectfile" "github.com/ActiveState/cli/pkg/runtime_helpers" ) type Cache struct { + prime primeable output output.Outputer config configurable confirm promptable @@ -28,11 +31,12 @@ type CacheParams struct { } func NewCache(prime primeable) *Cache { - return newCache(prime.Output(), prime.Config(), prime.Prompt(), prime.IPComm()) + return newCache(prime, prime.Output(), prime.Config(), prime.Prompt(), prime.IPComm()) } -func newCache(output output.Outputer, cfg configurable, confirm promptable, ipComm svcctl.IPCommunicator) *Cache { +func newCache(prime primeable, output output.Outputer, cfg configurable, confirm promptable, ipComm svcctl.IPCommunicator) *Cache { return &Cache{ + prime: prime, output: output, config: cfg, confirm: confirm, @@ -76,9 +80,16 @@ func (c *Cache) removeCache(path string, force bool) error { } } - logging.Debug("Removing cache path: %s", path) - err := removeCache(c.path) + inUse, err := c.checkPathInUse(path) if err != nil { + return errs.Wrap(err, "Failed to check if path is in use") + } + if inUse { + return locale.NewError("err_clean_in_use") + } + + logging.Debug("Removing cache path: %s", path) + if err := removeCache(c.path); err != nil { return errs.Wrap(err, "Failed to remove cache") } @@ -97,6 +108,14 @@ func (c *Cache) removeProjectCache(projectDir, namespace string, force bool) err } } + inUse, err := c.checkPathInUse(projectDir) + if err != nil { + return errs.Wrap(err, "Failed to check if path is in use") + } + if inUse { + return locale.NewError("err_clean_in_use") + } + projectInstallPath, err := runtime_helpers.TargetDirFromProjectDir(projectDir) if err != nil { return errs.Wrap(err, "Failed to determine project install path") @@ -109,3 +128,19 @@ func (c *Cache) removeProjectCache(projectDir, namespace string, force bool) err return nil } + +func (c *Cache) checkPathInUse(path string) (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), model.SvcTimeoutMinimal) + defer cancel() + + procs, err := c.prime.SvcModel().GetProcessesInUse(ctx, path) + if err != nil { + return false, errs.Wrap(err, "Failed to get processes in use") + } + + if len(procs) > 0 { + return true, nil + } + + return false, nil +} diff --git a/internal/runners/clean/uninstall.go b/internal/runners/clean/uninstall.go index 323d89f3aa..8b6071e1a7 100644 --- a/internal/runners/clean/uninstall.go +++ b/internal/runners/clean/uninstall.go @@ -42,6 +42,7 @@ type primeable interface { primer.Configurer primer.IPCommunicator primer.Analyticer + primer.SvcModeler } func NewUninstall(prime primeable) (*Uninstall, error) { From cbc65764ad502f192a8742ffc2670a98794df9ac Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 11:42:01 -0700 Subject: [PATCH 235/440] Add newline --- internal/locale/locales/en-us.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 44da292ab1..0f625d8430 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1571,4 +1571,4 @@ config_get_help: config_set_help: other: "To SET the value for a specific config key run '[ACTIONABLE]state config set [/RESET]'" err_clean_in_use: - other: "Could not clean your cache as you appear to have a runtime in use. Please stop any running State Tool processes and project runtime processes and try again." \ No newline at end of file + other: "Could not clean your cache as you appear to have a runtime in use. Please stop any running State Tool processes and project runtime processes and try again." From c2de1560c1e0b6e063c10b6b25e05cc42565c572 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 12:51:18 -0700 Subject: [PATCH 236/440] Debug integration test --- test/integration/uninstall_int_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/uninstall_int_test.go b/test/integration/uninstall_int_test.go index d6875666cf..97c0cd68e6 100644 --- a/test/integration/uninstall_int_test.go +++ b/test/integration/uninstall_int_test.go @@ -109,6 +109,9 @@ func (suite *UninstallIntegrationTestSuite) testUninstall(all bool) { time.Sleep(2 * time.Second) } + snapshot := cp.Snapshot() + fmt.Println(snapshot) + if all { suite.NoDirExists(ts.Dirs.Cache, "Cache dir should not exist after full uninstall") suite.NoDirExists(ts.Dirs.Config, "Config dir should not exist after full uninstall") From 7d7bb20cfff2f237a79c8843e76cc39a1186f59b Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 13:09:19 -0700 Subject: [PATCH 237/440] Force failure on Windows --- test/integration/uninstall_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/uninstall_int_test.go b/test/integration/uninstall_int_test.go index 97c0cd68e6..46b731f0ce 100644 --- a/test/integration/uninstall_int_test.go +++ b/test/integration/uninstall_int_test.go @@ -98,7 +98,7 @@ func (suite *UninstallIntegrationTestSuite) testUninstall(all bool) { } cp.SendLine("y") if runtime.GOOS == "windows" { - cp.Expect("Deletion of State Tool has been scheduled.") + cp.Expect("Deletion of State Tool has been scheduled. Fail") } else { cp.Expect("Successfully removed State Tool and related files") } From 2c4e8145addb8c83f774b656ba4bc0e137b85633 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 13:21:42 -0700 Subject: [PATCH 238/440] Run only full uninstall --- test/integration/uninstall_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/uninstall_int_test.go b/test/integration/uninstall_int_test.go index 46b731f0ce..e96fff698e 100644 --- a/test/integration/uninstall_int_test.go +++ b/test/integration/uninstall_int_test.go @@ -22,7 +22,7 @@ type UninstallIntegrationTestSuite struct { func (suite *UninstallIntegrationTestSuite) TestUninstall() { suite.OnlyRunForTags(tagsuite.Uninstall, tagsuite.Critical) - suite.T().Run("Partial uninstall", func(t *testing.T) { suite.testUninstall(false) }) + // suite.T().Run("Partial uninstall", func(t *testing.T) { suite.testUninstall(false) }) suite.T().Run("Full uninstall", func(t *testing.T) { suite.testUninstall(true) }) } From d666320bee0d204852695e1ee8f3b00ef34b5db8 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 13:40:30 -0700 Subject: [PATCH 239/440] Print paths to be removed --- internal/runners/clean/run_win.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/runners/clean/run_win.go b/internal/runners/clean/run_win.go index d762dc25b8..f9f61462fb 100644 --- a/internal/runners/clean/run_win.go +++ b/internal/runners/clean/run_win.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "path/filepath" + "strings" svcApp "github.com/ActiveState/cli/cmd/state-svc/app" "github.com/ActiveState/cli/internal/assets" @@ -142,6 +143,7 @@ func removeInstall(logFile string, params *UninstallParams, cfg *config.Instance func removePaths(logFile string, paths ...string) error { logging.Debug("Removing paths: %v", paths) + fmt.Println("Removing paths:", strings.Join(paths, ", ")) scriptName := "removePaths" scriptBlock, err := assets.ReadFileBytes(fmt.Sprintf("scripts/%s.bat", scriptName)) if err != nil { From b1bf0e43a8f8d82a02553e79f3dd672538fbf169 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 13:54:40 -0700 Subject: [PATCH 240/440] Attempt to use longpaths --- internal/runners/clean/run_win.go | 14 +++++++++++++- test/integration/uninstall_int_test.go | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/internal/runners/clean/run_win.go b/internal/runners/clean/run_win.go index f9f61462fb..405bcc447a 100644 --- a/internal/runners/clean/run_win.go +++ b/internal/runners/clean/run_win.go @@ -14,6 +14,7 @@ import ( "github.com/ActiveState/cli/internal/assets" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/installation" "github.com/ActiveState/cli/internal/installation/storage" "github.com/ActiveState/cli/internal/language" @@ -159,8 +160,19 @@ func removePaths(logFile string, paths ...string) error { return locale.WrapError(err, "err_clean_executable", "Could not get executable name") } + var longPaths []string + for _, path := range paths { + longPath, err := fileutils.GetLongPathName(path) + if err != nil { + logging.Error("Could not get long path for %s: %s", path, err) + longPaths = append(longPaths, path) + continue + } + longPaths = append(longPaths, longPath) + } + args := []string{"/C", sf.Filename(), logFile, fmt.Sprintf("%d", os.Getpid()), filepath.Base(exe)} - args = append(args, paths...) + args = append(args, longPaths...) _, err = osutils.ExecuteAndForget("cmd.exe", args) if err != nil { diff --git a/test/integration/uninstall_int_test.go b/test/integration/uninstall_int_test.go index e96fff698e..511db454fe 100644 --- a/test/integration/uninstall_int_test.go +++ b/test/integration/uninstall_int_test.go @@ -98,7 +98,7 @@ func (suite *UninstallIntegrationTestSuite) testUninstall(all bool) { } cp.SendLine("y") if runtime.GOOS == "windows" { - cp.Expect("Deletion of State Tool has been scheduled. Fail") + cp.Expect("Deletion of State Tool has been scheduled") } else { cp.Expect("Successfully removed State Tool and related files") } From 6270c8d4d1b79e2038fe2b55747bd99741dd9c44 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 14:07:34 -0700 Subject: [PATCH 241/440] Revert config changes --- internal/runners/clean/run_win.go | 14 +------------- test/integration/uninstall_int_test.go | 4 ++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/internal/runners/clean/run_win.go b/internal/runners/clean/run_win.go index 405bcc447a..f9f61462fb 100644 --- a/internal/runners/clean/run_win.go +++ b/internal/runners/clean/run_win.go @@ -14,7 +14,6 @@ import ( "github.com/ActiveState/cli/internal/assets" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/installation" "github.com/ActiveState/cli/internal/installation/storage" "github.com/ActiveState/cli/internal/language" @@ -160,19 +159,8 @@ func removePaths(logFile string, paths ...string) error { return locale.WrapError(err, "err_clean_executable", "Could not get executable name") } - var longPaths []string - for _, path := range paths { - longPath, err := fileutils.GetLongPathName(path) - if err != nil { - logging.Error("Could not get long path for %s: %s", path, err) - longPaths = append(longPaths, path) - continue - } - longPaths = append(longPaths, longPath) - } - args := []string{"/C", sf.Filename(), logFile, fmt.Sprintf("%d", os.Getpid()), filepath.Base(exe)} - args = append(args, longPaths...) + args = append(args, paths...) _, err = osutils.ExecuteAndForget("cmd.exe", args) if err != nil { diff --git a/test/integration/uninstall_int_test.go b/test/integration/uninstall_int_test.go index 511db454fe..97c0cd68e6 100644 --- a/test/integration/uninstall_int_test.go +++ b/test/integration/uninstall_int_test.go @@ -22,7 +22,7 @@ type UninstallIntegrationTestSuite struct { func (suite *UninstallIntegrationTestSuite) TestUninstall() { suite.OnlyRunForTags(tagsuite.Uninstall, tagsuite.Critical) - // suite.T().Run("Partial uninstall", func(t *testing.T) { suite.testUninstall(false) }) + suite.T().Run("Partial uninstall", func(t *testing.T) { suite.testUninstall(false) }) suite.T().Run("Full uninstall", func(t *testing.T) { suite.testUninstall(true) }) } @@ -98,7 +98,7 @@ func (suite *UninstallIntegrationTestSuite) testUninstall(all bool) { } cp.SendLine("y") if runtime.GOOS == "windows" { - cp.Expect("Deletion of State Tool has been scheduled") + cp.Expect("Deletion of State Tool has been scheduled.") } else { cp.Expect("Successfully removed State Tool and related files") } From d1677e100caaa9781f92c3d84c5af81c08ef169c Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 14:08:07 -0700 Subject: [PATCH 242/440] Remove debug print --- internal/runners/clean/run_win.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/runners/clean/run_win.go b/internal/runners/clean/run_win.go index f9f61462fb..d762dc25b8 100644 --- a/internal/runners/clean/run_win.go +++ b/internal/runners/clean/run_win.go @@ -8,7 +8,6 @@ import ( "fmt" "os" "path/filepath" - "strings" svcApp "github.com/ActiveState/cli/cmd/state-svc/app" "github.com/ActiveState/cli/internal/assets" @@ -143,7 +142,6 @@ func removeInstall(logFile string, params *UninstallParams, cfg *config.Instance func removePaths(logFile string, paths ...string) error { logging.Debug("Removing paths: %v", paths) - fmt.Println("Removing paths:", strings.Join(paths, ", ")) scriptName := "removePaths" scriptBlock, err := assets.ReadFileBytes(fmt.Sprintf("scripts/%s.bat", scriptName)) if err != nil { From e9f118486eae4078a3dccad7b55316c8ac650525 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 14:08:39 -0700 Subject: [PATCH 243/440] Remove snapshot print --- test/integration/uninstall_int_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/integration/uninstall_int_test.go b/test/integration/uninstall_int_test.go index 97c0cd68e6..d6875666cf 100644 --- a/test/integration/uninstall_int_test.go +++ b/test/integration/uninstall_int_test.go @@ -109,9 +109,6 @@ func (suite *UninstallIntegrationTestSuite) testUninstall(all bool) { time.Sleep(2 * time.Second) } - snapshot := cp.Snapshot() - fmt.Println(snapshot) - if all { suite.NoDirExists(ts.Dirs.Cache, "Cache dir should not exist after full uninstall") suite.NoDirExists(ts.Dirs.Config, "Config dir should not exist after full uninstall") From 7b7f4098ff79f653fe58fcda359bfe25aba7643a Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 15:25:55 -0700 Subject: [PATCH 244/440] Make input error --- internal/runners/clean/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index 6e5591fb27..53bc80e350 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -113,7 +113,7 @@ func (c *Cache) removeProjectCache(projectDir, namespace string, force bool) err return errs.Wrap(err, "Failed to check if path is in use") } if inUse { - return locale.NewError("err_clean_in_use") + return locale.NewInputError("err_clean_in_use") } projectInstallPath, err := runtime_helpers.TargetDirFromProjectDir(projectDir) From 195779cef5f260faca7b40edf7137ef3b0e6c941 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Wed, 16 Oct 2024 15:27:14 -0700 Subject: [PATCH 245/440] Increase timeout --- internal/runners/clean/cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index 53bc80e350..616c3443ff 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -3,6 +3,7 @@ package clean import ( "context" "os" + "time" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" @@ -11,7 +12,6 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/svcctl" - "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/projectfile" "github.com/ActiveState/cli/pkg/runtime_helpers" ) @@ -130,7 +130,7 @@ func (c *Cache) removeProjectCache(projectDir, namespace string, force bool) err } func (c *Cache) checkPathInUse(path string) (bool, error) { - ctx, cancel := context.WithTimeout(context.Background(), model.SvcTimeoutMinimal) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() procs, err := c.prime.SvcModel().GetProcessesInUse(ctx, path) From 27b46d3865ba633ab32658775389b73509f7f1c8 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 17 Oct 2024 08:05:06 -0400 Subject: [PATCH 246/440] Build scripts can distinguish between solve nodes for different targets. --- pkg/buildscript/queries.go | 89 +++++++++++++++++--- pkg/buildscript/unmarshal.go | 10 +++ pkg/buildscript/unmarshal_buildexpression.go | 19 ++--- 3 files changed, 92 insertions(+), 26 deletions(-) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 3a64186fa5..6c9a15901a 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/go-openapi/strfmt" + "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" @@ -13,6 +14,7 @@ import ( const ( solveFuncName = "solve" solveLegacyFuncName = "solve_legacy" + srcKey = "src" requirementsKey = "requirements" platformsKey = "platforms" ) @@ -44,8 +46,10 @@ type UnknownRequirement struct { func (r UnknownRequirement) IsRequirement() {} -func (b *BuildScript) Requirements() ([]Requirement, error) { - requirementsNode, err := b.getRequirementsNode() +// Returns the requirements for the given target. +// If no target is given, uses the default target (i.e. the name assigned to 'main'). +func (b *BuildScript) Requirements(targets ...string) ([]Requirement, error) { + requirementsNode, err := b.getRequirementsNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get requirements node") } @@ -119,8 +123,8 @@ func parseRequirement(req *value) Requirement { // DependencyRequirements is identical to Requirements except that it only considers dependency type requirements, // which are the most common. // ONLY use this when you know you only need to care about dependencies. -func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { - reqs, err := b.Requirements() +func (b *BuildScript) DependencyRequirements(targets ...string) ([]types.Requirement, error) { + reqs, err := b.Requirements(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get requirements") } @@ -133,8 +137,8 @@ func (b *BuildScript) DependencyRequirements() ([]types.Requirement, error) { return deps, nil } -func (b *BuildScript) getRequirementsNode() (*value, error) { - node, err := b.getSolveNode() +func (b *BuildScript) getRequirementsNode(targets ...string) (*value, error) { + node, err := b.getSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -171,7 +175,23 @@ func getVersionRequirements(v *value) []types.VersionRequirement { return reqs } -func (b *BuildScript) getSolveNode() (*value, error) { +func isSolveFuncName(name string) bool { + return name == solveFuncName || name == solveLegacyFuncName +} + +func (b *BuildScript) getTargetSolveNode(targets ...string) (*value, error) { + if len(targets) == 0 { + for _, assignment := range b.raw.Assignments { + if assignment.Key != mainKey { + continue + } + if assignment.Value.Ident != nil && *assignment.Value.Ident != "" { + targets = []string{*assignment.Value.Ident} + break + } + } + } + var search func([]*assignment) *value search = func(assignments []*assignment) *value { var nextLet []*assignment @@ -181,7 +201,13 @@ func (b *BuildScript) getSolveNode() (*value, error) { continue } - if f := a.Value.FuncCall; f != nil && (f.Name == solveFuncName || f.Name == solveLegacyFuncName) { + if funk.Contains(targets, a.Key) && a.Value.FuncCall != nil { + return a.Value + } + + if f := a.Value.FuncCall; len(targets) == 0 && f != nil && isSolveFuncName(f.Name) { + // This is coming from a complex build expression with no straightforward way to determine + // a default target. Fall back on a top-level solve node. return a.Value } } @@ -193,15 +219,50 @@ func (b *BuildScript) getSolveNode() (*value, error) { return nil } + if node := search(b.raw.Assignments); node != nil { return node, nil } + return nil, errNodeNotFound +} + +func (b *BuildScript) getSolveNode(targets ...string) (*value, error) { + node, err := b.getTargetSolveNode(targets...) + if err != nil { + return nil, errs.Wrap(err, "Could not get target node") + } + + // If the target is the solve function, we're done. + if isSolveFuncName(node.FuncCall.Name) { + return node, nil + } + + // Otherwise, the "src" key contains a reference to the solve node. + // For example: + // + // runtime = state_tool_artifacts_v1(src = sources) + // sources = solve(at_time = ..., platforms = [...], requirements = [...], ...) + // + // Look over the build expression again for that referenced node. + for _, arg := range node.FuncCall.Arguments { + if arg.Assignment == nil { + continue + } + a := arg.Assignment + if a.Key == srcKey && a.Value.Ident != nil { + node, err := b.getSolveNode(*a.Value.Ident) + if err != nil { + return nil, errs.Wrap(err, "Could not get solve node from target") + } + return node, nil + } + } return nil, errNodeNotFound } -func (b *BuildScript) getSolveAtTimeValue() (*value, error) { - node, err := b.getSolveNode() +func (b *BuildScript) getSolveAtTimeValue(targets ...string) (*value, error) { + node, err := b.getSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } @@ -215,8 +276,8 @@ func (b *BuildScript) getSolveAtTimeValue() (*value, error) { return nil, errValueNotFound } -func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { - node, err := b.getPlatformsNode() +func (b *BuildScript) Platforms(targets ...string) ([]strfmt.UUID, error) { + node, err := b.getPlatformsNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get platform node") } @@ -228,8 +289,8 @@ func (b *BuildScript) Platforms() ([]strfmt.UUID, error) { return list, nil } -func (b *BuildScript) getPlatformsNode() (*value, error) { - node, err := b.getSolveNode() +func (b *BuildScript) getPlatformsNode(targets ...string) (*value, error) { + node, err := b.getSolveNode(targets...) if err != nil { return nil, errs.Wrap(err, "Could not get solve node") } diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 8e3fbe8b03..ffbab27f9e 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -50,5 +50,15 @@ func Unmarshal(data []byte) (*BuildScript, error) { break } + // Verify there are no duplicate key assignments. + // This is primarily to catch duplicate solve nodes for a given target. + seen := make(map[string]bool) + for _, assignment := range raw.Assignments { + if _, exists := seen[assignment.Key]; exists { + return nil, locale.NewInputError(locale.Tl("err_buildscript_duplicate_keys", "Build script has duplicate '{{.V0}}' assignments", assignment.Key)) + } + seen[assignment.Key] = true + } + return &BuildScript{raw}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index cc625d6ab2..77af730583 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -81,18 +81,6 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { return errs.Wrap(err, "Could not get at_time node") } - // If the requirements are in legacy object form, e.g. - // requirements = [{"name": "", "namespace": ""}, {...}, ...] - // then transform them into function call form for the AScript format, e.g. - // requirements = [Req(name = "", namespace = ""), Req(...), ...] - requirements, err := b.getRequirementsNode() - if err != nil { - return errs.Wrap(err, "Could not get requirements node") - } - if isLegacyRequirementsList(requirements) { - requirements.List = transformRequirements(requirements).List - } - return nil } @@ -239,6 +227,13 @@ func unmarshalFuncCall(path []string, fc map[string]interface{}) (*funcCall, err if err != nil { return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface) } + if key == requirementsKey && isSolveFuncName(name) && isLegacyRequirementsList(uv) { + // If the requirements are in legacy object form, e.g. + // requirements = [{"name": "", "namespace": ""}, {...}, ...] + // then transform them into function call form for the AScript format, e.g. + // requirements = [Req(name = "", namespace = ""), Req(...), ...] + uv.List = transformRequirements(uv).List + } args = append(args, &value{Assignment: &assignment{key, uv}}) } sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Key < args[j].Assignment.Key }) From 9aa7b315e80a1f6e588d6b9245597189e11d01c5 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 17 Oct 2024 09:03:00 -0400 Subject: [PATCH 247/440] Search merge nodes for solve nodes. --- pkg/buildscript/queries.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 6c9a15901a..172f74e0cf 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -15,6 +15,7 @@ const ( solveFuncName = "solve" solveLegacyFuncName = "solve_legacy" srcKey = "src" + mergeKey = "merge" requirementsKey = "requirements" platformsKey = "platforms" ) @@ -237,6 +238,26 @@ func (b *BuildScript) getSolveNode(targets ...string) (*value, error) { return node, nil } + // If the target is a merge call, then look at right and left branches (in reverse order since the + // right branch has precedence). + if node.FuncCall.Name == mergeKey { + for i := len(node.FuncCall.Arguments) - 1; i >= 0; i-- { + arg := node.FuncCall.Arguments[i] + if arg.Assignment == nil { + continue + } + a := arg.Assignment + if a.Value.Ident != nil { + if node, err := b.getSolveNode(*a.Value.Ident); err == nil { + return node, nil + } + // Note: ignore errors because either branch may not contain a solve node. + // We'll return an error if both branches do not contain a solve node. + } + } + return nil, errNodeNotFound + } + // Otherwise, the "src" key contains a reference to the solve node. // For example: // From 6d4951d94e0d7f96f3238bc8ec709a9fc0574530 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 17 Oct 2024 09:24:49 -0700 Subject: [PATCH 248/440] Localize error --- internal/runners/clean/cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index 616c3443ff..e4f48eb14a 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -85,7 +85,7 @@ func (c *Cache) removeCache(path string, force bool) error { return errs.Wrap(err, "Failed to check if path is in use") } if inUse { - return locale.NewError("err_clean_in_use") + return locale.NewInputError("err_clean_in_use") } logging.Debug("Removing cache path: %s", path) From 4fedcbdd86a4c2fb11dc36428dc36b1820114c0a Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 6 Sep 2024 15:09:31 -0400 Subject: [PATCH 249/440] Participle should unquote strings. --- pkg/buildscript/buildscript.go | 2 +- pkg/buildscript/marshal.go | 6 +- pkg/buildscript/marshal_buildexpression.go | 14 ++--- pkg/buildscript/mutations.go | 16 +++--- pkg/buildscript/queries.go | 12 ++-- pkg/buildscript/raw.go | 17 +----- pkg/buildscript/raw_test.go | 60 ++++++++++---------- pkg/buildscript/unmarshal.go | 6 +- pkg/buildscript/unmarshal_buildexpression.go | 11 ++-- 9 files changed, 65 insertions(+), 79 deletions(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index fcaf7981c7..865d182cee 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -162,7 +162,7 @@ func exportValue(v *value) any { } return result case v.Str != nil: - return strValue(v) + return *v.Str case v.Number != nil: return *v.Number case v.Null != nil: diff --git a/pkg/buildscript/marshal.go b/pkg/buildscript/marshal.go index c647616f47..74a11c9a0f 100644 --- a/pkg/buildscript/marshal.go +++ b/pkg/buildscript/marshal.go @@ -8,6 +8,8 @@ import ( "github.com/go-openapi/strfmt" "github.com/thoas/go-funk" + + "github.com/ActiveState/cli/internal/rtutils/ptr" ) const ( @@ -30,7 +32,7 @@ func (b *BuildScript) Marshal() ([]byte, error) { if b.raw.AtTime != nil { buf.WriteString(assignmentString( - &assignment{atTimeKey, newString(b.raw.AtTime.Format(strfmt.RFC3339Millis))})) + &assignment{atTimeKey, &value{Str: ptr.To(b.raw.AtTime.Format(strfmt.RFC3339Millis))}})) buf.WriteString("\n") } @@ -77,7 +79,7 @@ func valueString(v *value) string { return buf.String() case v.Str != nil: - return *v.Str // keep quoted + return strconv.Quote(*v.Str) case v.Number != nil: return strconv.FormatFloat(*v.Number, 'G', -1, 64) // 64-bit float with minimum digits on display diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index cb8a152803..79a371fc7f 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -37,9 +37,9 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { if value.Str == nil { return nil, errs.New("String timestamp expected for '%s'", key) } - atTime, err := strfmt.ParseDateTime(strValue(value)) + atTime, err := strfmt.ParseDateTime(*value.Str) if err != nil { - return nil, errs.Wrap(err, "Invalid timestamp: %s", strValue(value)) + return nil, errs.Wrap(err, "Invalid timestamp: %s", *value.Str) } b.raw.AtTime = ptr.To(time.Time(atTime)) continue // do not include this custom assignment in the let block @@ -68,7 +68,7 @@ func (v *value) MarshalJSON() ([]byte, error) { case v.List != nil: return json.Marshal(v.List) case v.Str != nil: - return json.Marshal(strValue(v)) + return json.Marshal(*v.Str) case v.Number != nil: return json.Marshal(*v.Number) case v.Null != nil: @@ -125,12 +125,12 @@ func marshalReq(fn *funcCall) ([]byte, error) { switch { // Marshal the name argument (e.g. name = "") into {"name": ""} case assignment.Key == requirementNameKey && assignment.Value.Str != nil: - requirement[requirementNameKey] = strValue(assignment.Value) + requirement[requirementNameKey] = *assignment.Value.Str // Marshal the namespace argument (e.g. namespace = "") into // {"namespace": ""} case assignment.Key == requirementNamespaceKey && assignment.Value.Str != nil: - requirement[requirementNamespaceKey] = strValue(assignment.Value) + requirement[requirementNamespaceKey] = *assignment.Value.Str // Marshal the version argument (e.g. version = (value = "")) into // {"version_requirements": [{"comparator": "", "version": ""}]} @@ -143,10 +143,10 @@ func marshalReq(fn *funcCall) ([]byte, error) { req := make(map[string]string) req[requirementComparatorKey] = strings.ToLower(name) if len(funcCall.Arguments) == 0 || funcCall.Arguments[0].Assignment == nil || - funcCall.Arguments[0].Assignment.Value.Str == nil || strValue(funcCall.Arguments[0].Assignment.Value) == "value" { + funcCall.Arguments[0].Assignment.Value.Str == nil || *funcCall.Arguments[0].Assignment.Value.Str == "value" { return errs.New(`Illegal argument for version comparator '%s': 'value = ""' expected`, name) } - req[requirementVersionKey] = strValue(funcCall.Arguments[0].Assignment.Value) + req[requirementVersionKey] = *funcCall.Arguments[0].Assignment.Value.Str requirements = append(requirements, req) case andFuncName: if len(funcCall.Arguments) != 2 { diff --git a/pkg/buildscript/mutations.go b/pkg/buildscript/mutations.go index 5114f1afb0..c974641474 100644 --- a/pkg/buildscript/mutations.go +++ b/pkg/buildscript/mutations.go @@ -42,8 +42,8 @@ func (b *BuildScript) AddRequirement(requirement types.Requirement) error { // Use object form for now, and then transform it into function form later. obj := []*assignment{ - {requirementNameKey, newString(requirement.Name)}, - {requirementNamespaceKey, newString(requirement.Namespace)}, + {requirementNameKey, &value{Str: &requirement.Name}}, + {requirementNamespaceKey, &value{Str: &requirement.Namespace}}, } if requirement.Revision != nil { @@ -54,8 +54,8 @@ func (b *BuildScript) AddRequirement(requirement types.Requirement) error { values := []*value{} for _, req := range requirement.VersionRequirement { values = append(values, &value{Object: &[]*assignment{ - {requirementComparatorKey, newString(req[requirementComparatorKey])}, - {requirementVersionKey, newString(req[requirementVersionKey])}, + {requirementComparatorKey, &value{Str: ptr.To(req[requirementComparatorKey])}}, + {requirementVersionKey, &value{Str: ptr.To(req[requirementVersionKey])}}, }}) } obj = append(obj, &assignment{requirementVersionRequirementsKey, &value{List: &values}}) @@ -94,13 +94,13 @@ func (b *BuildScript) RemoveRequirement(requirement types.Requirement) error { for _, arg := range req.FuncCall.Arguments { if arg.Assignment.Key == requirementNameKey { - match = strValue(arg.Assignment.Value) == requirement.Name + match = *arg.Assignment.Value.Str == requirement.Name if !match || requirement.Namespace == "" { break } } if requirement.Namespace != "" && arg.Assignment.Key == requirementNamespaceKey { - match = strValue(arg.Assignment.Value) == requirement.Namespace + match = *arg.Assignment.Value.Str == requirement.Namespace if !match { break } @@ -132,7 +132,7 @@ func (b *BuildScript) AddPlatform(platformID strfmt.UUID) error { } list := *platformsNode.List - list = append(list, newString(platformID.String())) + list = append(list, &value{Str: ptr.To(platformID.String())}) platformsNode.List = &list return nil @@ -151,7 +151,7 @@ func (b *BuildScript) RemovePlatform(platformID strfmt.UUID) error { var found bool for i, value := range *platformsNode.List { - if value.Str != nil && strValue(value) == platformID.String() { + if value.Str != nil && *value.Str == platformID.String() { list := *platformsNode.List list = append(list[:i], list[i+1:]...) platformsNode.List = &list diff --git a/pkg/buildscript/queries.go b/pkg/buildscript/queries.go index 172f74e0cf..eec73943d0 100644 --- a/pkg/buildscript/queries.go +++ b/pkg/buildscript/queries.go @@ -97,9 +97,9 @@ func parseRequirement(req *value) Requirement { for _, arg := range req.FuncCall.Arguments { switch arg.Assignment.Key { case requirementNameKey: - r.Name = strValue(arg.Assignment.Value) + r.Name = *arg.Assignment.Value.Str case requirementNamespaceKey: - r.Namespace = strValue(arg.Assignment.Value) + r.Namespace = *arg.Assignment.Value.Str case requirementVersionKey: r.VersionRequirement = getVersionRequirements(arg.Assignment.Value) } @@ -110,9 +110,9 @@ func parseRequirement(req *value) Requirement { for _, arg := range req.FuncCall.Arguments { switch arg.Assignment.Key { case requirementNameKey: - r.Name = strValue(arg.Assignment.Value) + r.Name = *arg.Assignment.Value.Str case requirementRevisionIDKey: - r.RevisionID = strfmt.UUID(strValue(arg.Assignment.Value)) + r.RevisionID = strfmt.UUID(*arg.Assignment.Value.Str) } } return r @@ -161,7 +161,7 @@ func getVersionRequirements(v *value) []types.VersionRequirement { case eqFuncName, neFuncName, gtFuncName, gteFuncName, ltFuncName, lteFuncName: reqs = append(reqs, types.VersionRequirement{ requirementComparatorKey: strings.ToLower(v.FuncCall.Name), - requirementVersionKey: strValue(v.FuncCall.Arguments[0].Assignment.Value), + requirementVersionKey: *v.FuncCall.Arguments[0].Assignment.Value.Str, }) // e.g. And(left = Gte(value = "1.0"), right = Lt(value = "2.0")) @@ -305,7 +305,7 @@ func (b *BuildScript) Platforms(targets ...string) ([]strfmt.UUID, error) { list := []strfmt.UUID{} for _, value := range *node.List { - list = append(list, strfmt.UUID(strValue(value))) + list = append(list, strfmt.UUID(*value.Str)) } return list, nil } diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 8a2dbc3149..76fc2916dc 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -1,11 +1,8 @@ package buildscript import ( - "strconv" - "strings" "time" - "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/brunoga/deep" ) @@ -62,7 +59,7 @@ type assignment struct { type value struct { FuncCall *funcCall `parser:"@@"` List *[]*value `parser:"| '[' (@@ (',' @@)* ','?)? ']'"` - Str *string `parser:"| @String"` // note: this value is ALWAYS quoted + Str *string `parser:"| @String"` Number *float64 `parser:"| (@Float | @Int)"` Null *null `parser:"| @@"` @@ -79,15 +76,3 @@ type funcCall struct { Name string `parser:"@Ident"` Arguments []*value `parser:"'(' @@ (',' @@)* ','? ')'"` } - -// newString is a convenience function for constructing a string value from an unquoted string. -// Use this instead of &value{Str: ptr.To(strconv.Quote(s))} -func newString(s string) *value { - return &value{Str: ptr.To(strconv.Quote(s))} -} - -// strValue is a convenience function for retrieving an unquoted string from value. -// Use this instead of strings.Trim(*v.Str, `"`) -func strValue(v *value) string { - return strings.Trim(*v.Str, `"`) -} diff --git a/pkg/buildscript/raw_test.go b/pkg/buildscript/raw_test.go index fb56c6f46c..a3ddc8d503 100644 --- a/pkg/buildscript/raw_test.go +++ b/pkg/buildscript/raw_test.go @@ -32,14 +32,14 @@ main = runtime atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ - []*assignment{ + Assignments: []*assignment{ {"runtime", &value{ FuncCall: &funcCall{"solve", []*value{ {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, {Assignment: &assignment{ "platforms", &value{List: &[]*value{ - {Str: ptr.To(`"linux"`)}, - {Str: ptr.To(`"windows"`)}, + {Str: ptr.To(`linux`)}, + {Str: ptr.To(`windows`)}, }}, }}, {Assignment: &assignment{ @@ -47,19 +47,19 @@ main = runtime {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("python")}}, - {Assignment: &assignment{"namespace", newString("language")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("python")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language")}}}, }}}, {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("requests")}}, - {Assignment: &assignment{"namespace", newString("language/python")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("requests")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language/python")}}}, {Assignment: &assignment{ "version", &value{FuncCall: &funcCall{ Name: "Eq", Arguments: []*value{ - {Assignment: &assignment{"value", newString("3.10.10")}}, + {Assignment: &assignment{"value", &value{Str: ptr.To("3.10.10")}}}, }, }}, }}, @@ -72,7 +72,7 @@ main = runtime }}, {"main", &value{Ident: ptr.To("runtime")}}, }, - &atTime, + AtTime: &atTime, }, script.raw) } @@ -107,7 +107,7 @@ main = merge( atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ - []*assignment{ + Assignments: []*assignment{ {"linux_runtime", &value{ FuncCall: &funcCall{"solve", []*value{ {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, @@ -116,14 +116,14 @@ main = merge( {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("python")}}, - {Assignment: &assignment{"namespace", newString("language")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("python")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language")}}}, }, }}, }}, }}, {Assignment: &assignment{ - "platforms", &value{List: &[]*value{{Str: ptr.To(`"67890"`)}}}, + "platforms", &value{List: &[]*value{{Str: ptr.To(`67890`)}}}, }}, }}, }}, @@ -135,14 +135,14 @@ main = merge( {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("perl")}}, - {Assignment: &assignment{"namespace", newString("language")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("perl")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language")}}}, }, }}, }}, }}, {Assignment: &assignment{ - "platforms", &value{List: &[]*value{{Str: ptr.To(`"12345"`)}}}, + "platforms", &value{List: &[]*value{{Str: ptr.To(`12345`)}}}, }}, }}, }}, @@ -152,7 +152,7 @@ main = merge( {FuncCall: &funcCall{"tar_installer", []*value{{Ident: ptr.To("linux_runtime")}}}}, }}}}, }, - &atTime, + AtTime: &atTime, }, script.raw) } @@ -179,14 +179,14 @@ func TestComplexVersions(t *testing.T) { atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ - []*assignment{ + Assignments: []*assignment{ {"runtime", &value{ FuncCall: &funcCall{"solve", []*value{ {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, {Assignment: &assignment{ "platforms", &value{List: &[]*value{ - {Str: ptr.To(`"96b7e6f2-bebf-564c-bc1c-f04482398f38"`)}, - {Str: ptr.To(`"96b7e6f2-bebf-564c-bc1c-f04482398f38"`)}, + {Str: ptr.To(`96b7e6f2-bebf-564c-bc1c-f04482398f38`)}, + {Str: ptr.To(`96b7e6f2-bebf-564c-bc1c-f04482398f38`)}, }}, }}, {Assignment: &assignment{ @@ -194,20 +194,20 @@ func TestComplexVersions(t *testing.T) { {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("python")}}, - {Assignment: &assignment{"namespace", newString("language")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("python")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language")}}}, }, }}, {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("requests")}}, - {Assignment: &assignment{"namespace", newString("language/python")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("requests")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language/python")}}}, {Assignment: &assignment{ "version", &value{FuncCall: &funcCall{ Name: "Eq", Arguments: []*value{ - {Assignment: &assignment{Key: "value", Value: newString("3.10.10")}}, + {Assignment: &assignment{Key: "value", Value: &value{Str: ptr.To("3.10.10")}}}, }, }}, }}, @@ -216,8 +216,8 @@ func TestComplexVersions(t *testing.T) { {FuncCall: &funcCall{ Name: "Req", Arguments: []*value{ - {Assignment: &assignment{"name", newString("argparse")}}, - {Assignment: &assignment{"namespace", newString("language/python")}}, + {Assignment: &assignment{"name", &value{Str: ptr.To("argparse")}}}, + {Assignment: &assignment{"namespace", &value{Str: ptr.To("language/python")}}}, {Assignment: &assignment{ "version", &value{FuncCall: &funcCall{ Name: "And", @@ -225,13 +225,13 @@ func TestComplexVersions(t *testing.T) { {Assignment: &assignment{Key: "left", Value: &value{FuncCall: &funcCall{ Name: "Gt", Arguments: []*value{ - {Assignment: &assignment{Key: "value", Value: newString("1.0")}}, + {Assignment: &assignment{Key: "value", Value: &value{Str: ptr.To("1.0")}}}, }, }}}}, {Assignment: &assignment{Key: "right", Value: &value{FuncCall: &funcCall{ Name: "Lt", Arguments: []*value{ - {Assignment: &assignment{Key: "value", Value: newString("2.0")}}, + {Assignment: &assignment{Key: "value", Value: &value{Str: ptr.To("2.0")}}}, }, }}}}, }, @@ -246,6 +246,6 @@ func TestComplexVersions(t *testing.T) { }}, {"main", &value{Ident: ptr.To("runtime")}}, }, - &atTime, + AtTime: &atTime, }, script.raw) } diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index ffbab27f9e..3001ad04b4 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -17,7 +17,7 @@ const atTimeKey = "at_time" // Unmarshal returns a structured form of the given AScript (on-disk format). func Unmarshal(data []byte) (*BuildScript, error) { - parser, err := participle.Build[rawBuildScript]() + parser, err := participle.Build[rawBuildScript](participle.Unquote()) if err != nil { return nil, errs.Wrap(err, "Could not create parser for build script") } @@ -42,9 +42,9 @@ func Unmarshal(data []byte) (*BuildScript, error) { if value.Str == nil { break } - atTime, err := strfmt.ParseDateTime(strValue(value)) + atTime, err := strfmt.ParseDateTime(*value.Str) if err != nil { - return nil, errs.Wrap(err, "Invalid timestamp: %s", strValue(value)) + return nil, errs.Wrap(err, "Invalid timestamp: %s", *value.Str) } raw.AtTime = ptr.To(time.Time(atTime)) break diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 77af730583..5efd0229c0 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -3,7 +3,6 @@ package buildscript import ( "encoding/json" "sort" - "strconv" "strings" "time" @@ -69,10 +68,10 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { // Extract the 'at_time' from the solve node, if it exists, and change its value to be a // reference to "$at_time", which is how we want to show it in AScript format. - if atTimeNode, err := b.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(strValue(atTimeNode), `$`) { - atTime, err := strfmt.ParseDateTime(strValue(atTimeNode)) + if atTimeNode, err := b.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(*atTimeNode.Str, `$`) { + atTime, err := strfmt.ParseDateTime(*atTimeNode.Str) if err != nil { - return errs.Wrap(err, "Invalid timestamp: %s", strValue(atTimeNode)) + return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str) } atTimeNode.Str = nil atTimeNode.Ident = ptr.To("at_time") @@ -171,7 +170,7 @@ func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { if sliceutils.Contains(path, ctxIn) || strings.HasPrefix(v, "$") { result.Ident = ptr.To(strings.TrimPrefix(v, "$")) } else { - result.Str = ptr.To(strconv.Quote(v)) // quoting is mandatory + result.Str = ptr.To(v) } case float64: @@ -349,7 +348,7 @@ func transformVersion(requirements *assignment) *funcCall { {Assignment: &assignment{"value", o.Value}}, } case requirementComparatorKey: - f.Name = cases.Title(language.English).String(strValue(o.Value)) + f.Name = cases.Title(language.English).String(*o.Value.Str) } } funcs = append(funcs, f) From 2d3411535825b42ff2c6531cc344ce79ab085c21 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 6 Sep 2024 18:23:09 -0400 Subject: [PATCH 250/440] Initial support for commit info in buildscript. Of the form: ``` Project: URL Time: time ``` --- internal/migrator/migrator.go | 4 +- .../runbits/buildscript/buildscript_test.go | 29 ++++++-- internal/runbits/buildscript/file.go | 70 ++++++++++++++----- .../buildscript/testdata/buildscript1.as | 8 ++- .../buildscript/testdata/buildscript2.as | 8 ++- internal/runbits/checkout/checkout.go | 7 +- internal/runbits/commits_runbit/time.go | 2 +- internal/runners/initialize/init.go | 2 +- internal/runners/reset/reset.go | 2 +- pkg/buildscript/buildscript.go | 36 ++++++++-- pkg/buildscript/buildscript_test.go | 21 ++++-- pkg/buildscript/marshal.go | 11 ++- pkg/buildscript/marshal_buildexpression.go | 13 ---- pkg/buildscript/merge.go | 2 +- pkg/buildscript/merge_test.go | 31 ++++---- pkg/buildscript/raw.go | 5 +- pkg/buildscript/raw_test.go | 46 +++++++++--- pkg/buildscript/unmarshal.go | 51 +++++++++----- pkg/buildscript/unmarshal_buildexpression.go | 2 +- scripts/to-buildscript/main.go | 2 + 20 files changed, 239 insertions(+), 113 deletions(-) diff --git a/internal/migrator/migrator.go b/internal/migrator/migrator.go index 23c7b7fa1f..cfb69eec18 100644 --- a/internal/migrator/migrator.go +++ b/internal/migrator/migrator.go @@ -1,8 +1,6 @@ package migrator import ( - "path/filepath" - "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" @@ -29,7 +27,7 @@ func NewMigrator(auth *authentication.Auth, cfg *config.Instance, svcm *model.Sv case 0: if cfg.GetBool(constants.OptinBuildscriptsConfig) { logging.Debug("Creating buildscript") - if err := buildscript_runbit.Initialize(filepath.Dir(project.Path()), auth, svcm); err != nil { + if err := buildscript_runbit.Initialize(project, auth, svcm); err != nil { return v, errs.Wrap(err, "Failed to initialize buildscript") } } diff --git a/internal/runbits/buildscript/buildscript_test.go b/internal/runbits/buildscript/buildscript_test.go index 5ef9836dde..eadad09d57 100644 --- a/internal/runbits/buildscript/buildscript_test.go +++ b/internal/runbits/buildscript/buildscript_test.go @@ -11,9 +11,19 @@ import ( "github.com/stretchr/testify/require" ) +const testProject = "https://platform.activestate.com/org/project?branch=main&commitID=00000000-0000-0000-0000-000000000000" +const testTime = "2000-01-01T00:00:00.000Z" + +func checkoutInfo(project, time string) string { + return "```\n" + + "Project: " + project + "\n" + + "Time: " + time + "\n" + + "```\n" +} + func TestDiff(t *testing.T) { script, err := buildscript.Unmarshal([]byte( - `at_time = "2000-01-01T00:00:00.000Z" + checkoutInfo(testProject, testTime) + ` runtime = solve( at_time = at_time, platforms = [ @@ -38,7 +48,7 @@ main = runtime`)) // Generate the difference between the modified script and the original expression. result, err := generateDiff(modifiedScript, script) require.NoError(t, err) - assert.Equal(t, `at_time = "2000-01-01T00:00:00.000Z" + assert.Equal(t, checkoutInfo(testProject, testTime)+` runtime = solve( at_time = at_time, platforms = [ @@ -71,11 +81,16 @@ func TestRealWorld(t *testing.T) { require.NoError(t, err) result, err := generateDiff(script1, script2) require.NoError(t, err) - assert.Equal(t, `<<<<<<< local -at_time = "2023-10-16T22:20:29.000Z" -======= -at_time = "2023-08-01T16:20:11.985Z" ->>>>>>> remote + assert.Equal(t, + "```\n"+ + "<<<<<<< local\n"+ + "Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=d908a758-6a81-40d4-b0eb-87069cd7f07d\n"+ + "Time: 2024-05-10T00:00:13.138Z\n"+ + "=======\n"+ + "Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=f3263ee4-ac4c-41ee-b778-2585333f49f7\n"+ + "Time: 2023-08-01T16:20:11.985Z\n"+ + ">>>>>>> remote\n"+ + "```\n"+` runtime = state_tool_artifacts_v1( build_flags = [ ], diff --git a/internal/runbits/buildscript/file.go b/internal/runbits/buildscript/file.go index 72e66d5ff6..90576a5094 100644 --- a/internal/runbits/buildscript/file.go +++ b/internal/runbits/buildscript/file.go @@ -2,6 +2,8 @@ package buildscript_runbit import ( "errors" + "fmt" + "net/url" "os" "path/filepath" @@ -19,15 +21,16 @@ import ( // projecter is a union between project.Project and setup.Targeter type projecter interface { - ProjectDir() string + Dir() string Owner() string Name() string + BranchName() string } var ErrBuildscriptNotExist = errors.New("Build script does not exist") func ScriptFromProject(proj projecter) (*buildscript.BuildScript, error) { - path := filepath.Join(proj.ProjectDir(), constants.BuildScriptFileName) + path := filepath.Join(proj.Dir(), constants.BuildScriptFileName) return ScriptFromFile(path) } @@ -47,33 +50,34 @@ type primeable interface { primer.SvcModeler } -func Initialize(path string, auth *authentication.Auth, svcm *model.SvcModel) error { - scriptPath := filepath.Join(path, constants.BuildScriptFileName) - script, err := ScriptFromFile(scriptPath) - if err == nil { - return nil // nothing to do, buildscript already exists - } - if !errors.Is(err, os.ErrNotExist) { - return errs.Wrap(err, "Could not read build script from file") - } - - logging.Debug("Build script does not exist. Creating one.") - commitId, err := localcommit.Get(path) +// Initialize creates a new build script for the local project. It will overwrite an existing one so +// commands like `state reset` will work. +func Initialize(proj projecter, auth *authentication.Auth, svcm *model.SvcModel) error { + logging.Debug("Initializing build script") + commitId, err := localcommit.Get(proj.Dir()) if err != nil { return errs.Wrap(err, "Unable to get the local commit ID") } buildplanner := buildplanner.NewBuildPlannerModel(auth, svcm) - script, err = buildplanner.GetBuildScript(commitId.String()) + script, err := buildplanner.GetBuildScript(commitId.String()) if err != nil { return errs.Wrap(err, "Unable to get the remote build expression and time") } + if url, err := projectURL(proj, commitId.String()); err == nil { + script.SetProject(url) + } else { + return errs.Wrap(err, "Unable to set project") + } + // Note: script.SetAtTime() was done in GetBuildScript(). + scriptBytes, err := script.Marshal() if err != nil { return errs.Wrap(err, "Unable to marshal build script") } + scriptPath := filepath.Join(proj.Dir(), constants.BuildScriptFileName) logging.Debug("Initializing build script at %s", scriptPath) err = fileutils.WriteFile(scriptPath, scriptBytes) if err != nil { @@ -83,6 +87,23 @@ func Initialize(path string, auth *authentication.Auth, svcm *model.SvcModel) er return nil } +func projectURL(proj projecter, commitID string) (string, error) { + // Note: cannot use api.GetPlatformURL() due to import cycle. + host := constants.DefaultAPIHost + if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" { + host = hostOverride + } + u, err := url.Parse(fmt.Sprintf("https://%s/%s/%s", host, proj.Owner(), proj.Name())) + if err != nil { + return "", errs.Wrap(err, "Unable to parse URL") + } + q := u.Query() + q.Set("branch", proj.BranchName()) + q.Set("commitID", commitID) + u.RawQuery = q.Encode() + return u.String(), nil +} + func Update(proj projecter, newScript *buildscript.BuildScript) error { script, err := ScriptFromProject(proj) if err != nil { @@ -97,13 +118,28 @@ func Update(proj projecter, newScript *buildscript.BuildScript) error { return nil // no changes to write } - sb, err := newScript.Marshal() + // Update the new script's project field to match the current one, except for a new commit ID. + commitID, err := localcommit.Get(proj.Dir()) + if err != nil { + return errs.Wrap(err, "Unable to get the local commit ID") + } + url, err := projectURL(proj, commitID.String()) + if err != nil { + return errs.Wrap(err, "Could not construct project URL") + } + newScript2, err := newScript.Clone() + if err != nil { + return errs.Wrap(err, "Could not clone buildscript") + } + newScript2.SetProject(url) + + sb, err := newScript2.Marshal() if err != nil { return errs.Wrap(err, "Could not marshal build script") } logging.Debug("Writing build script") - if err := fileutils.WriteFile(filepath.Join(proj.ProjectDir(), constants.BuildScriptFileName), sb); err != nil { + if err := fileutils.WriteFile(filepath.Join(proj.Dir(), constants.BuildScriptFileName), sb); err != nil { return errs.Wrap(err, "Could not write build script to file") } return nil diff --git a/internal/runbits/buildscript/testdata/buildscript1.as b/internal/runbits/buildscript/testdata/buildscript1.as index 1bbc21f6b3..3477380cdb 100644 --- a/internal/runbits/buildscript/testdata/buildscript1.as +++ b/internal/runbits/buildscript/testdata/buildscript1.as @@ -1,4 +1,8 @@ -at_time = "2023-10-16T22:20:29.000000Z" +``` +Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=d908a758-6a81-40d4-b0eb-87069cd7f07d +Time: 2024-05-10T00:00:13.138Z +``` + runtime = state_tool_artifacts_v1( build_flags = [ ], @@ -20,4 +24,4 @@ sources = solve( solver_version = null ) -main = runtime \ No newline at end of file +main = runtime diff --git a/internal/runbits/buildscript/testdata/buildscript2.as b/internal/runbits/buildscript/testdata/buildscript2.as index 335b721546..c5f4580161 100644 --- a/internal/runbits/buildscript/testdata/buildscript2.as +++ b/internal/runbits/buildscript/testdata/buildscript2.as @@ -1,4 +1,8 @@ -at_time = "2023-08-01T16:20:11.985000Z" +``` +Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=f3263ee4-ac4c-41ee-b778-2585333f49f7 +Time: 2023-08-01T16:20:11.985000Z +``` + runtime = state_tool_artifacts_v1( build_flags = [ ], @@ -20,4 +24,4 @@ sources = solve( solver_version = null ) -main = runtime \ No newline at end of file +main = runtime diff --git a/internal/runbits/checkout/checkout.go b/internal/runbits/checkout/checkout.go index 5c505f4cc4..74efe258b8 100644 --- a/internal/runbits/checkout/checkout.go +++ b/internal/runbits/checkout/checkout.go @@ -99,7 +99,12 @@ func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath } if r.prime.Config().GetBool(constants.OptinBuildscriptsConfig) { - if err := buildscript_runbit.Initialize(path, r.prime.Auth(), r.prime.SvcModel()); err != nil { + pjf, err := projectfile.FromPath(path) + if err != nil { + return "", errs.Wrap(err, "Unable to load project file") + } + + if err := buildscript_runbit.Initialize(pjf, r.prime.Auth(), r.prime.SvcModel()); err != nil { return "", errs.Wrap(err, "Unable to initialize buildscript") } } diff --git a/internal/runbits/commits_runbit/time.go b/internal/runbits/commits_runbit/time.go index af7f2e33e9..2ee606bd19 100644 --- a/internal/runbits/commits_runbit/time.go +++ b/internal/runbits/commits_runbit/time.go @@ -74,7 +74,7 @@ func ExpandTimeForBuildScript(ts *captain.TimeValue, auth *authentication.Auth, } atTime := script.AtTime() - if atTime.After(timestamp) { + if atTime != nil && atTime.After(timestamp) { return *atTime, nil } diff --git a/internal/runners/initialize/init.go b/internal/runners/initialize/init.go index 85ecd17d1f..607809fd38 100644 --- a/internal/runners/initialize/init.go +++ b/internal/runners/initialize/init.go @@ -281,7 +281,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { } if r.config.GetBool(constants.OptinBuildscriptsConfig) { - if err := buildscript_runbit.Initialize(proj.Dir(), r.auth, r.svcModel); err != nil { + if err := buildscript_runbit.Initialize(proj, r.auth, r.svcModel); err != nil { return errs.Wrap(err, "Unable to initialize buildscript") } } diff --git a/internal/runners/reset/reset.go b/internal/runners/reset/reset.go index 0e2b5c89fb..c046635e63 100644 --- a/internal/runners/reset/reset.go +++ b/internal/runners/reset/reset.go @@ -134,7 +134,7 @@ func (r *Reset) Run(params *Params) error { // Ensure the buildscript exists. Normally we should never do this, but reset is used for resetting from a corrupted // state, so it is appropriate. if r.cfg.GetBool(constants.OptinBuildscriptsConfig) { - if err := buildscript_runbit.Initialize(r.project.Dir(), r.auth, r.svcModel); err != nil { + if err := buildscript_runbit.Initialize(r.project, r.auth, r.svcModel); err != nil { return errs.Wrap(err, "Unable to initialize buildscript") } } diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 865d182cee..739a097c75 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -16,6 +16,9 @@ import ( // methods that are easy to understand and work with. type BuildScript struct { raw *rawBuildScript + + project string + atTime *time.Time } func init() { @@ -41,22 +44,43 @@ func New() *BuildScript { return bs } +func (b *BuildScript) Project() string { + return b.project +} + +func (b *BuildScript) SetProject(url string) { + b.project = url +} + func (b *BuildScript) AtTime() *time.Time { - return b.raw.AtTime + return b.atTime } func (b *BuildScript) SetAtTime(t time.Time) { - b.raw.AtTime = &t + b.atTime = &t } func (b *BuildScript) Equals(other *BuildScript) (bool, error) { - myBytes, err := b.Marshal() + b2, err := b.Clone() + if err != nil { + return false, errs.Wrap(err, "Unable to clone buildscript") + } + other2, err := other.Clone() + if err != nil { + return false, errs.Wrap(err, "Unable to clone other buildscript") + } + + // Do not compare project URLs. + b2.SetProject("") + other2.SetProject("") + + myBytes, err := b2.Marshal() if err != nil { - return false, errs.New("Unable to marshal this buildscript: %s", errs.JoinMessage(err)) + return false, errs.Wrap(err, "Unable to marshal this buildscript") } - otherBytes, err := other.Marshal() + otherBytes, err := other2.Marshal() if err != nil { - return false, errs.New("Unable to marshal other buildscript: %s", errs.JoinMessage(err)) + return false, errs.Wrap(err, "Unable to marshal other buildscript") } return string(myBytes) == string(otherBytes), nil } diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index d4cff2a3b4..42504f1824 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -1,7 +1,6 @@ package buildscript import ( - "fmt" "testing" "time" @@ -10,10 +9,8 @@ import ( "github.com/stretchr/testify/require" ) -var atTime = "2000-01-01T00:00:00.000Z" - -var basicBuildScript = []byte(fmt.Sprintf( - `at_time = "%s" +var basicBuildScript = []byte( + checkoutInfoString(testProject, testTime) + ` runtime = state_tool_artifacts( src = sources ) @@ -29,7 +26,7 @@ sources = solve( solver_version = null ) -main = runtime`, atTime)) +main = runtime`) var basicBuildExpression = []byte(`{ "let": { @@ -100,10 +97,11 @@ func TestRoundTripFromBuildExpression(t *testing.T) { // TestExpressionToScript tests that creating a build script from a given Platform build expression // and at time produces the expected result. func TestExpressionToScript(t *testing.T) { - ts, err := time.Parse(strfmt.RFC3339Millis, atTime) + ts, err := time.Parse(strfmt.RFC3339Millis, testTime) require.NoError(t, err) script := New() + script.SetProject(testProject) script.SetAtTime(ts) require.NoError(t, script.UnmarshalBuildExpression(basicBuildExpression)) @@ -124,3 +122,12 @@ func TestScriptToExpression(t *testing.T) { require.Equal(t, string(basicBuildExpression), string(data)) } + +func TestOutdatedScript(t *testing.T) { + _, err := Unmarshal([]byte( + `at_time = "2000-01-01T00:00:00.000Z" + main = runtime + `)) + assert.Error(t, err) + assert.ErrorIs(t, err, ErrOutdatedAtTime) +} diff --git a/pkg/buildscript/marshal.go b/pkg/buildscript/marshal.go index 74a11c9a0f..6eefd259f8 100644 --- a/pkg/buildscript/marshal.go +++ b/pkg/buildscript/marshal.go @@ -8,8 +8,6 @@ import ( "github.com/go-openapi/strfmt" "github.com/thoas/go-funk" - - "github.com/ActiveState/cli/internal/rtutils/ptr" ) const ( @@ -30,11 +28,12 @@ const ( func (b *BuildScript) Marshal() ([]byte, error) { buf := strings.Builder{} - if b.raw.AtTime != nil { - buf.WriteString(assignmentString( - &assignment{atTimeKey, &value{Str: ptr.To(b.raw.AtTime.Format(strfmt.RFC3339Millis))}})) - buf.WriteString("\n") + buf.WriteString("```\n") + buf.WriteString("Project: " + b.project + "\n") + if b.atTime != nil { + buf.WriteString("Time: " + b.atTime.Format(strfmt.RFC3339Millis) + "\n") } + buf.WriteString("```\n\n") var main *assignment for _, assignment := range b.raw.Assignments { diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index 79a371fc7f..e3a9a13620 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -3,12 +3,9 @@ package buildscript import ( "encoding/json" "strings" - "time" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/rtutils/ptr" - "github.com/go-openapi/strfmt" ) const ( @@ -33,16 +30,6 @@ func (b *BuildScript) MarshalBuildExpression() ([]byte, error) { key := assignment.Key value := assignment.Value switch key { - case atTimeKey: - if value.Str == nil { - return nil, errs.New("String timestamp expected for '%s'", key) - } - atTime, err := strfmt.ParseDateTime(*value.Str) - if err != nil { - return nil, errs.Wrap(err, "Invalid timestamp: %s", *value.Str) - } - b.raw.AtTime = ptr.To(time.Time(atTime)) - continue // do not include this custom assignment in the let block case mainKey: key = inKey // rename } diff --git a/pkg/buildscript/merge.go b/pkg/buildscript/merge.go index 370cc5d8a6..b81e153596 100644 --- a/pkg/buildscript/merge.go +++ b/pkg/buildscript/merge.go @@ -57,7 +57,7 @@ func (b *BuildScript) Merge(other *BuildScript, strategies *mono_models.MergeStr // When merging build scripts we want to use the most recent timestamp atTime := other.AtTime() - if atTime != nil && atTime.After(*b.AtTime()) { + if atTime != nil && b.AtTime() != nil && atTime.After(*b.AtTime()) { b.SetAtTime(*atTime) } diff --git a/pkg/buildscript/merge_test.go b/pkg/buildscript/merge_test.go index 78e058165f..8a11d3a579 100644 --- a/pkg/buildscript/merge_test.go +++ b/pkg/buildscript/merge_test.go @@ -8,9 +8,12 @@ import ( "github.com/stretchr/testify/require" ) +const mergeATime = "2000-01-01T00:00:00.000Z" +const mergeBTime = "2000-01-02T00:00:00.000Z" + func TestMergeAdd(t *testing.T) { - scriptA, err := Unmarshal([]byte(` -at_time = "2000-01-01T00:00:00.000Z" + scriptA, err := Unmarshal([]byte( + checkoutInfoString(testProject, mergeATime) + ` runtime = solve( at_time = at_time, platforms = [ @@ -27,8 +30,8 @@ main = runtime `)) require.NoError(t, err) - scriptB, err := Unmarshal([]byte(` -at_time = "2000-01-02T00:00:00.000Z" + scriptB, err := Unmarshal([]byte( + checkoutInfoString(testProject, mergeBTime) + ` runtime = solve( at_time = at_time, platforms = [ @@ -60,7 +63,7 @@ main = runtime require.NoError(t, err) assert.Equal(t, - `at_time = "2000-01-02T00:00:00.000Z" + checkoutInfoString(testProject, mergeBTime)+` runtime = solve( at_time = at_time, platforms = [ @@ -78,8 +81,8 @@ main = runtime`, string(v)) } func TestMergeRemove(t *testing.T) { - scriptA, err := Unmarshal([]byte(` -at_time = "2000-01-02T00:00:00.000Z" + scriptA, err := Unmarshal([]byte( + checkoutInfoString(testProject, mergeBTime) + ` runtime = solve( at_time = at_time, platforms = [ @@ -97,8 +100,8 @@ main = runtime `)) require.NoError(t, err) - scriptB, err := Unmarshal([]byte(` -at_time = "2000-01-01T00:00:00.000Z" + scriptB, err := Unmarshal([]byte( + checkoutInfoString(testProject, mergeATime) + ` runtime = solve( at_time = at_time, platforms = [ @@ -129,7 +132,7 @@ main = runtime require.NoError(t, err) assert.Equal(t, - `at_time = "2000-01-02T00:00:00.000Z" + checkoutInfoString(testProject, mergeBTime)+` runtime = solve( at_time = at_time, platforms = [ @@ -146,8 +149,8 @@ main = runtime`, string(v)) } func TestMergeConflict(t *testing.T) { - scriptA, err := Unmarshal([]byte(` -at_time = "2000-01-01T00:00:00.000Z" + scriptA, err := Unmarshal([]byte( + checkoutInfoString(testProject, mergeATime) + ` runtime = solve( at_time = at_time, platforms = [ @@ -163,8 +166,8 @@ main = runtime `)) require.NoError(t, err) - scriptB, err := Unmarshal([]byte(` -at_time = "2000-01-01T00:00:00.000Z" + scriptB, err := Unmarshal([]byte( + checkoutInfoString(testProject, mergeATime) + ` runtime = solve( at_time = at_time, platforms = [ diff --git a/pkg/buildscript/raw.go b/pkg/buildscript/raw.go index 76fc2916dc..f60df48453 100644 --- a/pkg/buildscript/raw.go +++ b/pkg/buildscript/raw.go @@ -1,16 +1,13 @@ package buildscript import ( - "time" - "github.com/brunoga/deep" ) // Tagged fields will be filled in by Participle. type rawBuildScript struct { + Info *string `parser:"(RawString @RawString RawString)?"` Assignments []*assignment `parser:"@@+"` - - AtTime *time.Time // set after initial read } // clone is meant to facilitate making modifications to functions at marshal time. The idea is that these modifications diff --git a/pkg/buildscript/raw_test.go b/pkg/buildscript/raw_test.go index a3ddc8d503..d82ae3c112 100644 --- a/pkg/buildscript/raw_test.go +++ b/pkg/buildscript/raw_test.go @@ -10,9 +10,25 @@ import ( "github.com/stretchr/testify/require" ) +const testProject = "https://platform.activestate.com/org/project?branch=main&commitID=00000000-0000-0000-0000-000000000000" +const testTime = "2000-01-01T00:00:00.000Z" + +func checkoutInfoString(project, time string) string { + return "```\n" + + "Project: " + project + "\n" + + "Time: " + time + "\n" + + "```\n" +} + +var testCheckoutInfo string + +func init() { + testCheckoutInfo = checkoutInfoString(testProject, testTime) +} + func TestRawRepresentation(t *testing.T) { script, err := Unmarshal([]byte( - `at_time = "2000-01-01T00:00:00.000Z" + testCheckoutInfo + ` runtime = solve( at_time = at_time, platforms = ["linux", "windows"], @@ -32,6 +48,7 @@ main = runtime atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ + Info: ptr.To(testCheckoutInfo[2 : len(testCheckoutInfo)-3]), Assignments: []*assignment{ {"runtime", &value{ FuncCall: &funcCall{"solve", []*value{ @@ -72,13 +89,15 @@ main = runtime }}, {"main", &value{Ident: ptr.To("runtime")}}, }, - AtTime: &atTime, }, script.raw) + + assert.Equal(t, testProject, script.Project()) + assert.Equal(t, &atTime, script.AtTime()) } func TestComplex(t *testing.T) { script, err := Unmarshal([]byte( - `at_time = "2000-01-01T00:00:00.000Z" + testCheckoutInfo + ` linux_runtime = solve( at_time = at_time, requirements=[ @@ -107,6 +126,7 @@ main = merge( atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ + Info: ptr.To(testCheckoutInfo[2 : len(testCheckoutInfo)-3]), Assignments: []*assignment{ {"linux_runtime", &value{ FuncCall: &funcCall{"solve", []*value{ @@ -152,11 +172,16 @@ main = merge( {FuncCall: &funcCall{"tar_installer", []*value{{Ident: ptr.To("linux_runtime")}}}}, }}}}, }, - AtTime: &atTime, }, script.raw) + + assert.Equal(t, testProject, script.Project()) + assert.Equal(t, &atTime, script.AtTime()) } -const buildscriptWithComplexVersions = `at_time = "2023-04-27T17:30:05.999Z" +func TestComplexVersions(t *testing.T) { + checkoutInfo := checkoutInfoString(testProject, "2023-04-27T17:30:05.999Z") + script, err := Unmarshal([]byte( + checkoutInfo + ` runtime = solve( at_time = at_time, platforms = ["96b7e6f2-bebf-564c-bc1c-f04482398f38", "96b7e6f2-bebf-564c-bc1c-f04482398f38"], @@ -168,10 +193,8 @@ runtime = solve( solver_version = 0 ) -main = runtime` - -func TestComplexVersions(t *testing.T) { - script, err := Unmarshal([]byte(buildscriptWithComplexVersions)) +main = runtime +`)) require.NoError(t, err) atTimeStrfmt, err := strfmt.ParseDateTime("2023-04-27T17:30:05.999Z") @@ -179,6 +202,7 @@ func TestComplexVersions(t *testing.T) { atTime := time.Time(atTimeStrfmt) assert.Equal(t, &rawBuildScript{ + Info: ptr.To(checkoutInfo[2 : len(checkoutInfo)-3]), Assignments: []*assignment{ {"runtime", &value{ FuncCall: &funcCall{"solve", []*value{ @@ -246,6 +270,8 @@ func TestComplexVersions(t *testing.T) { }}, {"main", &value{Ident: ptr.To("runtime")}}, }, - AtTime: &atTime, }, script.raw) + + assert.Equal(t, testProject, script.Project()) + assert.Equal(t, &atTime, script.AtTime()) } diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 3001ad04b4..9602aef76c 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -2,10 +2,12 @@ package buildscript import ( "errors" + "strings" "time" "github.com/alecthomas/participle/v2" "github.com/go-openapi/strfmt" + "gopkg.in/yaml.v2" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" @@ -15,6 +17,13 @@ import ( const atTimeKey = "at_time" +var ErrOutdatedAtTime = errs.New("outdated at_time on top") + +type checkoutInfo struct { + Project string `yaml:"Project"` + Time string `yaml:"Time"` +} + // Unmarshal returns a structured form of the given AScript (on-disk format). func Unmarshal(data []byte) (*BuildScript, error) { parser, err := participle.Build[rawBuildScript](participle.Unquote()) @@ -31,23 +40,12 @@ func Unmarshal(data []byte) (*BuildScript, error) { return nil, locale.WrapError(err, "err_parse_buildscript_bytes", "Could not parse build script: {{.V0}}", err.Error()) } - // Extract 'at_time' value from the list of assignments, if it exists. - for i, assignment := range raw.Assignments { - key := assignment.Key - value := assignment.Value - if key != atTimeKey { + // If 'at_time' is among the list of assignments, this is an outdated build script, so error out. + for _, assignment := range raw.Assignments { + if assignment.Key != atTimeKey { continue } - raw.Assignments = append(raw.Assignments[:i], raw.Assignments[i+1:]...) - if value.Str == nil { - break - } - atTime, err := strfmt.ParseDateTime(*value.Str) - if err != nil { - return nil, errs.Wrap(err, "Invalid timestamp: %s", *value.Str) - } - raw.AtTime = ptr.To(time.Time(atTime)) - break + return nil, ErrOutdatedAtTime } // Verify there are no duplicate key assignments. @@ -60,5 +58,26 @@ func Unmarshal(data []byte) (*BuildScript, error) { seen[assignment.Key] = true } - return &BuildScript{raw}, nil + var project string + var atTime *time.Time + if raw.Info != nil { + info := checkoutInfo{} + + err := yaml.Unmarshal([]byte(strings.Trim(*raw.Info, "`\n")), &info) + if err != nil { + return nil, locale.NewInputError( + "err_buildscript_checkoutinfo", + "Could not parse checkout information in the buildscript. The parser produced the following error: {{.V0}}", err.Error()) + } + + project = info.Project + + atTimeStr, err := strfmt.ParseDateTime(info.Time) + if err != nil { + return nil, errs.Wrap(err, "Invalid timestamp: %s", info.Time) + } + atTime = ptr.To(time.Time(atTimeStr)) + } + + return &BuildScript{raw, project, atTime}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 5efd0229c0..117db2231d 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -75,7 +75,7 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { } atTimeNode.Str = nil atTimeNode.Ident = ptr.To("at_time") - b.raw.AtTime = ptr.To(time.Time(atTime)) + b.atTime = ptr.To(time.Time(atTime)) } else if err != nil { return errs.Wrap(err, "Could not get at_time node") } diff --git a/scripts/to-buildscript/main.go b/scripts/to-buildscript/main.go index 9817cf8d1a..60c7cde520 100644 --- a/scripts/to-buildscript/main.go +++ b/scripts/to-buildscript/main.go @@ -42,6 +42,7 @@ func main() { os.Exit(1) } + project := "https://platform.activestate.com/org/project?branch=main&commitID=00000000-0000-0000-0000-000000000000" var atTime *time.Time if len(os.Args) == (2 + argOffset) { t, err := time.Parse(strfmt.RFC3339Millis, os.Args[1+argOffset]) @@ -52,6 +53,7 @@ func main() { } bs := buildscript.New() + bs.SetProject(project) if atTime != nil { bs.SetAtTime(*atTime) } From ba3aec7f2dfa96d52bcb1d079631920110d92e2d Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 9 Sep 2024 16:58:41 -0400 Subject: [PATCH 251/440] Add warning about using an outdated buildscript. --- internal/captain/rationalize.go | 7 +++++++ internal/locale/locales/en-us.yaml | 2 ++ 2 files changed, 9 insertions(+) diff --git a/internal/captain/rationalize.go b/internal/captain/rationalize.go index 4abbc6fd94..c44d7dbfdf 100644 --- a/internal/captain/rationalize.go +++ b/internal/captain/rationalize.go @@ -6,6 +6,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/runbits/rationalize" + "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" ) @@ -31,5 +32,11 @@ func rationalizeError(err *error) { *err = errs.WrapUserFacing(*err, locale.Tr("err_commit_id_invalid", errInvalidCommitID.CommitID), errs.SetInput()) + + // Outdated build script. + case errors.Is(*err, buildscript.ErrOutdatedAtTime): + *err = errs.WrapUserFacing(*err, + locale.T("err_outdated_buildscript"), + errs.SetInput()) } } diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index bf6e64f9db..6eb637bf9f 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1371,6 +1371,8 @@ err_commit_id_invalid_given: other: "Invalid commit ID: '{{.V0}}'." err_commit_id_not_in_history: other: "The project '[ACTIONABLE]{{.V0}}[/RESET]' does not contain the provided commit: '[ACTIONABLE]{{.V1}}[/RESET]'." +err_outdated_buildscript: + other: "[WARNING]Warning:[/RESET] You are using an outdated version of the buildscript. Please run '[ACTIONABLE]state reset LOCAL[/RESET]' in order to reinitialize your buildscript file." err_shortcutdir_writable: other: Could not continue as we don't have permission to create [ACTIONABLE]{{.V0}}[/RESET]. move_prompt: From 1acb72d92bd8c61855ae48ecd4c00967e6095982 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 30 Sep 2024 13:31:23 -0400 Subject: [PATCH 252/440] Use TIME instead of at_time in build scripts. --- .../runbits/buildscript/buildscript_test.go | 6 +++--- .../buildscript/testdata/buildscript1.as | 2 +- .../buildscript/testdata/buildscript2.as | 2 +- pkg/buildscript/buildscript_test.go | 2 +- pkg/buildscript/marshal_buildexpression.go | 7 ++++++- pkg/buildscript/merge.go | 3 +-- pkg/buildscript/merge_test.go | 16 +++++++-------- pkg/buildscript/raw_test.go | 16 +++++++-------- pkg/buildscript/unmarshal_buildexpression.go | 20 +++++++++++-------- 9 files changed, 41 insertions(+), 33 deletions(-) diff --git a/internal/runbits/buildscript/buildscript_test.go b/internal/runbits/buildscript/buildscript_test.go index eadad09d57..24e47de8ef 100644 --- a/internal/runbits/buildscript/buildscript_test.go +++ b/internal/runbits/buildscript/buildscript_test.go @@ -25,7 +25,7 @@ func TestDiff(t *testing.T) { script, err := buildscript.Unmarshal([]byte( checkoutInfo(testProject, testTime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -50,7 +50,7 @@ main = runtime`)) require.NoError(t, err) assert.Equal(t, checkoutInfo(testProject, testTime)+` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ <<<<<<< local "77777", @@ -99,7 +99,7 @@ runtime = state_tool_artifacts_v1( src = sources ) sources = solve( - at_time = at_time, + at_time = TIME, platforms = [ "78977bc8-0f32-519d-80f3-9043f059398c", "7c998ec2-7491-4e75-be4d-8885800ef5f2", diff --git a/internal/runbits/buildscript/testdata/buildscript1.as b/internal/runbits/buildscript/testdata/buildscript1.as index 3477380cdb..1d33f4566e 100644 --- a/internal/runbits/buildscript/testdata/buildscript1.as +++ b/internal/runbits/buildscript/testdata/buildscript1.as @@ -11,7 +11,7 @@ runtime = state_tool_artifacts_v1( src = sources ) sources = solve( - at_time = at_time, + at_time = TIME, platforms = [ "78977bc8-0f32-519d-80f3-9043f059398c", "7c998ec2-7491-4e75-be4d-8885800ef5f2", diff --git a/internal/runbits/buildscript/testdata/buildscript2.as b/internal/runbits/buildscript/testdata/buildscript2.as index c5f4580161..2fe6441d1d 100644 --- a/internal/runbits/buildscript/testdata/buildscript2.as +++ b/internal/runbits/buildscript/testdata/buildscript2.as @@ -11,7 +11,7 @@ runtime = state_tool_artifacts_v1( src = sources ) sources = solve( - at_time = at_time, + at_time = TIME, platforms = [ "78977bc8-0f32-519d-80f3-9043f059398c", "7c998ec2-7491-4e75-be4d-8885800ef5f2", diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index 42504f1824..4a96fb9766 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -15,7 +15,7 @@ runtime = state_tool_artifacts( src = sources ) sources = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" diff --git a/pkg/buildscript/marshal_buildexpression.go b/pkg/buildscript/marshal_buildexpression.go index e3a9a13620..c161cfad24 100644 --- a/pkg/buildscript/marshal_buildexpression.go +++ b/pkg/buildscript/marshal_buildexpression.go @@ -69,7 +69,12 @@ func (v *value) MarshalJSON() ([]byte, error) { } return json.Marshal(m) case v.Ident != nil: - return json.Marshal("$" + *v.Ident) + name := *v.Ident + switch name { + case "TIME": + name = "at_time" // build expression uses this variable name + } + return json.Marshal("$" + name) } return json.Marshal([]*value{}) // participle does not create v.List if it's empty } diff --git a/pkg/buildscript/merge.go b/pkg/buildscript/merge.go index b81e153596..889cafffb1 100644 --- a/pkg/buildscript/merge.go +++ b/pkg/buildscript/merge.go @@ -85,7 +85,7 @@ func isAutoMergePossible(scriptA *BuildScript, scriptB *BuildScript) bool { } // getComparableJson returns a comparable JSON map[string]interface{} structure for the given build -// script. The map will not have a "requirements" field, nor will it have an "at_time" field. +// script. The map will not have a "requirements" field. func getComparableJson(script *BuildScript) (map[string]interface{}, error) { data, err := script.MarshalBuildExpression() if err != nil { @@ -103,7 +103,6 @@ func getComparableJson(script *BuildScript) (map[string]interface{}, error) { return nil, errs.New("'let' key is not a JSON object") } deleteKey(&letMap, "requirements") - deleteKey(&letMap, "at_time") return m, nil } diff --git a/pkg/buildscript/merge_test.go b/pkg/buildscript/merge_test.go index 8a11d3a579..260bb50867 100644 --- a/pkg/buildscript/merge_test.go +++ b/pkg/buildscript/merge_test.go @@ -15,7 +15,7 @@ func TestMergeAdd(t *testing.T) { scriptA, err := Unmarshal([]byte( checkoutInfoString(testProject, mergeATime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -33,7 +33,7 @@ main = runtime scriptB, err := Unmarshal([]byte( checkoutInfoString(testProject, mergeBTime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -65,7 +65,7 @@ main = runtime assert.Equal(t, checkoutInfoString(testProject, mergeBTime)+` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -84,7 +84,7 @@ func TestMergeRemove(t *testing.T) { scriptA, err := Unmarshal([]byte( checkoutInfoString(testProject, mergeBTime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -103,7 +103,7 @@ main = runtime scriptB, err := Unmarshal([]byte( checkoutInfoString(testProject, mergeATime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -134,7 +134,7 @@ main = runtime assert.Equal(t, checkoutInfoString(testProject, mergeBTime)+` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -152,7 +152,7 @@ func TestMergeConflict(t *testing.T) { scriptA, err := Unmarshal([]byte( checkoutInfoString(testProject, mergeATime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345", "67890" @@ -169,7 +169,7 @@ main = runtime scriptB, err := Unmarshal([]byte( checkoutInfoString(testProject, mergeATime) + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = [ "12345" ], diff --git a/pkg/buildscript/raw_test.go b/pkg/buildscript/raw_test.go index d82ae3c112..306a71dbbe 100644 --- a/pkg/buildscript/raw_test.go +++ b/pkg/buildscript/raw_test.go @@ -30,7 +30,7 @@ func TestRawRepresentation(t *testing.T) { script, err := Unmarshal([]byte( testCheckoutInfo + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = ["linux", "windows"], requirements = [ Req(name = "python", namespace = "language"), @@ -52,7 +52,7 @@ main = runtime Assignments: []*assignment{ {"runtime", &value{ FuncCall: &funcCall{"solve", []*value{ - {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`TIME`)}}}, {Assignment: &assignment{ "platforms", &value{List: &[]*value{ {Str: ptr.To(`linux`)}, @@ -99,7 +99,7 @@ func TestComplex(t *testing.T) { script, err := Unmarshal([]byte( testCheckoutInfo + ` linux_runtime = solve( - at_time = at_time, + at_time = TIME, requirements=[ Req(name = "python", namespace = "language") ], @@ -107,7 +107,7 @@ linux_runtime = solve( ) win_runtime = solve( - at_time = at_time, + at_time = TIME, requirements=[ Req(name = "perl", namespace = "language") ], @@ -130,7 +130,7 @@ main = merge( Assignments: []*assignment{ {"linux_runtime", &value{ FuncCall: &funcCall{"solve", []*value{ - {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`TIME`)}}}, {Assignment: &assignment{ "requirements", &value{List: &[]*value{ {FuncCall: &funcCall{ @@ -149,7 +149,7 @@ main = merge( }}, {"win_runtime", &value{ FuncCall: &funcCall{"solve", []*value{ - {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`TIME`)}}}, {Assignment: &assignment{ "requirements", &value{List: &[]*value{ {FuncCall: &funcCall{ @@ -183,7 +183,7 @@ func TestComplexVersions(t *testing.T) { script, err := Unmarshal([]byte( checkoutInfo + ` runtime = solve( - at_time = at_time, + at_time = TIME, platforms = ["96b7e6f2-bebf-564c-bc1c-f04482398f38", "96b7e6f2-bebf-564c-bc1c-f04482398f38"], requirements = [ Req(name = "python", namespace = "language"), @@ -206,7 +206,7 @@ main = runtime Assignments: []*assignment{ {"runtime", &value{ FuncCall: &funcCall{"solve", []*value{ - {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`at_time`)}}}, + {Assignment: &assignment{"at_time", &value{Ident: ptr.To(`TIME`)}}}, {Assignment: &assignment{ "platforms", &value{List: &[]*value{ {Str: ptr.To(`96b7e6f2-bebf-564c-bc1c-f04482398f38`)}, diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 117db2231d..f3459240f6 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -67,15 +67,19 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { b.raw.Assignments = assignments // Extract the 'at_time' from the solve node, if it exists, and change its value to be a - // reference to "$at_time", which is how we want to show it in AScript format. - if atTimeNode, err := b.getSolveAtTimeValue(); err == nil && atTimeNode.Str != nil && !strings.HasPrefix(*atTimeNode.Str, `$`) { - atTime, err := strfmt.ParseDateTime(*atTimeNode.Str) - if err != nil { - return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str) + // reference to "TIME", which is how we want to show it in AScript format. + if atTimeNode, err := b.getSolveAtTimeValue(); err == nil { + if atTimeNode.Str != nil && !strings.HasPrefix(*atTimeNode.Str, `$`) { + atTime, err := strfmt.ParseDateTime(*atTimeNode.Str) + if err != nil { + return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str) + } + atTimeNode.Str = nil + atTimeNode.Ident = ptr.To("TIME") + b.atTime = ptr.To(time.Time(atTime)) + } else if atTimeNode.Ident != nil && *atTimeNode.Ident == "at_time" { + atTimeNode.Ident = ptr.To("TIME") } - atTimeNode.Str = nil - atTimeNode.Ident = ptr.To("at_time") - b.atTime = ptr.To(time.Time(atTime)) } else if err != nil { return errs.Wrap(err, "Could not get at_time node") } From 6430b8187b58735c3a0767f0601db797f053cc80 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 4 Oct 2024 11:22:36 -0400 Subject: [PATCH 253/440] Attempt to fix failing test. --- test/integration/manifest_int_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/integration/manifest_int_test.go b/test/integration/manifest_int_test.go index 8a8e780100..1928c6986c 100644 --- a/test/integration/manifest_int_test.go +++ b/test/integration/manifest_int_test.go @@ -70,7 +70,7 @@ func (suite *ManifestIntegrationTestSuite) TestManifest_JSON() { } func (suite *ManifestIntegrationTestSuite) TestManifest_Advanced_Reqs() { - suite.OnlyRunForTags(tagsuite.Manifest) + suite.OnlyRunForTags(tagsuite.Manifest, tagsuite.BuildScripts) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -81,8 +81,11 @@ func (suite *ManifestIntegrationTestSuite) TestManifest_Advanced_Reqs() { ts.PrepareProject("ActiveState-CLI-Testing/Python-With-Custom-Reqs", "92ac7df2-0b0c-42f5-9b25-75b0cb4063f7") bsf := filepath.Join(ts.Dirs.Work, constants.BuildScriptFileName) - fileutils.WriteFile(bsf, []byte(fmt.Sprintf(` -at_time = "2022-07-07T19:51:01.140Z" + fileutils.WriteFile(bsf, []byte(fmt.Sprintf( + "```\n"+ + "Project: ActiveState-CLI-Testing/Python-With-Custom-Reqs\n"+ + "Time: 2022-07-07T19:51:01.140Z\n"+ + "```\n"+` runtime = state_tool_artifacts_v1(src = sources) sources = solve( at_time = at_time, From 34781f05abd49449489e2c20aba3b3b0e159c3cb Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 18 Oct 2024 16:37:47 -0400 Subject: [PATCH 254/440] Leverage project's URL instead of constructing a full one. Also, ensure an updated build script with commit ID is always written. --- internal/runbits/buildscript/file.go | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/internal/runbits/buildscript/file.go b/internal/runbits/buildscript/file.go index 90576a5094..e1b30fcc79 100644 --- a/internal/runbits/buildscript/file.go +++ b/internal/runbits/buildscript/file.go @@ -2,7 +2,6 @@ package buildscript_runbit import ( "errors" - "fmt" "net/url" "os" "path/filepath" @@ -22,9 +21,7 @@ import ( // projecter is a union between project.Project and setup.Targeter type projecter interface { Dir() string - Owner() string - Name() string - BranchName() string + URL() string } var ErrBuildscriptNotExist = errors.New("Build script does not exist") @@ -87,37 +84,19 @@ func Initialize(proj projecter, auth *authentication.Auth, svcm *model.SvcModel) return nil } +// projectURL returns proj.URL(), but with the given, updated commitID. func projectURL(proj projecter, commitID string) (string, error) { - // Note: cannot use api.GetPlatformURL() due to import cycle. - host := constants.DefaultAPIHost - if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" { - host = hostOverride - } - u, err := url.Parse(fmt.Sprintf("https://%s/%s/%s", host, proj.Owner(), proj.Name())) + u, err := url.Parse(proj.URL()) if err != nil { return "", errs.Wrap(err, "Unable to parse URL") } q := u.Query() - q.Set("branch", proj.BranchName()) q.Set("commitID", commitID) u.RawQuery = q.Encode() return u.String(), nil } func Update(proj projecter, newScript *buildscript.BuildScript) error { - script, err := ScriptFromProject(proj) - if err != nil { - return errs.Wrap(err, "Could not read build script") - } - - equals, err := script.Equals(newScript) - if err != nil { - return errs.Wrap(err, "Could not compare build script") - } - if script != nil && equals { - return nil // no changes to write - } - // Update the new script's project field to match the current one, except for a new commit ID. commitID, err := localcommit.Get(proj.Dir()) if err != nil { From df6227396577cf17d75af231432814e9b6a2ee9b Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 18 Oct 2024 13:48:06 -0700 Subject: [PATCH 255/440] Update dependecy version --- test/integration/package_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 9660ab1891..b1941977a6 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -662,7 +662,7 @@ func (suite *PackageIntegrationTestSuite) TestChangeSummaryShowsAddedForUpdate() cp = ts.Spawn("install", "jinja2@3.1.4") cp.Expect("Installing jinja2@3.1.4 includes 1 direct dep") - cp.Expect("└─ markupsafe@2.1.5") + cp.Expect("└─ markupsafe@3.0.1") cp.Expect("Updated: language/python/jinja2") cp.ExpectExitCode(0) } From 8737683d8fd39e941d16e44e27752ca47c83ea23 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 21 Oct 2024 11:39:08 -0400 Subject: [PATCH 256/440] Fixed failing integration test. --- test/integration/manifest_int_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/integration/manifest_int_test.go b/test/integration/manifest_int_test.go index 1928c6986c..c567d83ae6 100644 --- a/test/integration/manifest_int_test.go +++ b/test/integration/manifest_int_test.go @@ -79,16 +79,17 @@ func (suite *ManifestIntegrationTestSuite) TestManifest_Advanced_Reqs() { cp := ts.Spawn("config", "set", constants.OptinBuildscriptsConfig, "true") cp.ExpectExitCode(0) - ts.PrepareProject("ActiveState-CLI-Testing/Python-With-Custom-Reqs", "92ac7df2-0b0c-42f5-9b25-75b0cb4063f7") + ts.PrepareActiveStateYAML(`project: https://platform.activestate.com/ActiveState-CLI-Testing/Python-With-Custom-Reqs?branch=main&commitID=92ac7df2-0b0c-42f5-9b25-75b0cb4063f7 +config_version: 1`) // need config_version to be 1 or more so the migrator does not wipe out our build script bsf := filepath.Join(ts.Dirs.Work, constants.BuildScriptFileName) - fileutils.WriteFile(bsf, []byte(fmt.Sprintf( + err := fileutils.WriteFile(bsf, []byte(fmt.Sprintf( "```\n"+ - "Project: ActiveState-CLI-Testing/Python-With-Custom-Reqs\n"+ + "Project: https://platform.activestate.com/ActiveState-CLI-Testing/Python-With-Custom-Reqs?branch=main&commitID=92ac7df2-0b0c-42f5-9b25-75b0cb4063f7\n"+ "Time: 2022-07-07T19:51:01.140Z\n"+ "```\n"+` runtime = state_tool_artifacts_v1(src = sources) sources = solve( - at_time = at_time, + at_time = TIME, requirements = [ Req(name = "python", namespace = "language", version = Eq(value = "3.9.13")), Revision(name = "IngWithRevision", revision_id = "%s"), @@ -97,6 +98,7 @@ sources = solve( ) main = runtime `, e2e.CommitIDNotChecked))) + suite.Require().NoError(err) cp = ts.SpawnWithOpts( e2e.OptArgs("manifest"), From 97359f001e8db30eb8656387bf51485512d8b11c Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 21 Oct 2024 13:36:06 -0400 Subject: [PATCH 257/440] Ensure remote installer can run within restricted powershells. --- test/integration/remote_installer_int_test.go | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/integration/remote_installer_int_test.go b/test/integration/remote_installer_int_test.go index 66891b6645..a0a63cfe2e 100644 --- a/test/integration/remote_installer_int_test.go +++ b/test/integration/remote_installer_int_test.go @@ -2,14 +2,19 @@ package integration import ( "fmt" + "os/exec" "path/filepath" + "runtime" + "strings" "testing" + svcAutostart "github.com/ActiveState/cli/cmd/state-svc/autostart" anaConst "github.com/ActiveState/cli/internal/analytics/constants" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/environment" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/osutils" + "github.com/ActiveState/cli/internal/osutils/autostart" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" @@ -25,6 +30,25 @@ func (suite *RemoteInstallIntegrationTestSuite) TestInstall() { ts := e2e.New(suite.T(), false) defer ts.Close() + // Setup running the remote installer in restricted powershell mode. + if runtime.GOOS == "windows" { + getPolicy := func() string { + policy, err := exec.Command("powershell.exe", "Get-ExecutionPolicy").CombinedOutput() + suite.Require().NoError(err, "error getting policy: "+string(policy)) + return strings.TrimSpace(string(policy)) + } + setPolicy := func(policy string) { + output, err := exec.Command("powershell.exe", "Set-ExecutionPolicy", "-ExecutionPolicy", policy).CombinedOutput() + suite.Require().NoError(err, "error setting policy: "+string(output)) + } + + policy := getPolicy() + defer setPolicy(policy) + + setPolicy("Restricted") + suite.Assert().Equal("Restricted", getPolicy(), "should have set powershell policy to 'Restricted'") + } + tests := []struct { Name string Version string @@ -103,6 +127,13 @@ func (suite *RemoteInstallIntegrationTestSuite) TestInstall() { } } suite.Assert().True(sessionTokenFound, "sessionToken was not found in analytics") + + // Verify a startup shortcut was created (we use powershell to create it). + if runtime.GOOS == "windows" { + shortcut, err := autostart.AutostartPath("", svcAutostart.Options) + suite.Require().NoError(err) + suite.Assert().FileExists(shortcut) + } }) } } From 3f5294cfd5de66729d798ddb35bd29889850e9dd Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 21 Oct 2024 14:57:15 -0400 Subject: [PATCH 258/440] Fixed empty remote installer exe name. --- activestate.yaml | 4 ++-- test/integration/remote_installer_int_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index 88dd7f8c29..0f18c9cfdb 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -181,9 +181,9 @@ scripts: value: | set -e $constants.SET_ENV - TARGET=$BUILD_REMOTE_INSTALLER_TARGET + TARGET=$constants.BUILD_REMOTE_INSTALLER_TARGET if [[ "$GOOS" == "windows" || "$OS" == "Windows_NT" ]]; then - TARGET="${BUILD_REMOTE_INSTALLER_TARGET}.exe" + TARGET="${constants.BUILD_REMOTE_INSTALLER_TARGET}.exe" fi GOFLAGS="" go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@v1.4.0 cd cmd/state-remote-installer diff --git a/test/integration/remote_installer_int_test.go b/test/integration/remote_installer_int_test.go index a0a63cfe2e..1b289788eb 100644 --- a/test/integration/remote_installer_int_test.go +++ b/test/integration/remote_installer_int_test.go @@ -143,7 +143,7 @@ func (s *RemoteInstallIntegrationTestSuite) setupTest(ts *e2e.Session) { buildDir := fileutils.Join(root, "build") installerExe := filepath.Join(buildDir, constants.StateRemoteInstallerCmd+osutils.ExeExtension) if !fileutils.FileExists(installerExe) { - s.T().Fatal("E2E tests require a state-remote-installer binary. Run `state run build-installer`.") + s.T().Fatal("E2E tests require a state-remote-installer binary. Run `state run build-remote-installer`.") } s.remoteInstallerExe = ts.CopyExeToDir(installerExe, filepath.Join(ts.Dirs.Base, "installer")) } From 98ca57f54646451036a1798fcb8de34850b1c103 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 21 Oct 2024 14:35:00 -0700 Subject: [PATCH 259/440] Only set at_time if it is not already set --- pkg/platform/model/buildplanner/build.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index 0b11fc1712..136b382b39 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -110,7 +110,9 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, if err := script.UnmarshalBuildExpression(commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } - script.SetAtTime(time.Time(commit.AtTime)) + if script.AtTime() == nil { + script.SetAtTime(time.Time(commit.AtTime)) + } return &Commit{commit, bp, script}, nil } From 11fb1d5c661f4f94f902f7d17a2a3561592e3d75 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 21 Oct 2024 15:18:53 -0700 Subject: [PATCH 260/440] Set at time consistently --- pkg/platform/model/buildplanner/build.go | 4 +--- pkg/platform/model/buildplanner/buildscript.go | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index 136b382b39..0b11fc1712 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -110,9 +110,7 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, if err := script.UnmarshalBuildExpression(commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } - if script.AtTime() == nil { - script.SetAtTime(time.Time(commit.AtTime)) - } + script.SetAtTime(time.Time(commit.AtTime)) return &Commit{commit, bp, script}, nil } diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index 28bd980ffc..52f038ee5f 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -52,10 +52,10 @@ func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript } script := buildscript.New() - script.SetAtTime(time.Time(resp.Commit.AtTime)) if err := script.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } + script.SetAtTime(time.Time(resp.Commit.AtTime)) return script, nil } From 4bbfd142666ccc943dcb403177194e288738cca7 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 22 Oct 2024 10:58:08 -0400 Subject: [PATCH 261/440] Revert temporary PATH changes. --- internal/runners/exec/exec.go | 4 ---- pkg/runtime/internal/envdef/collection.go | 28 +---------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/internal/runners/exec/exec.go b/internal/runners/exec/exec.go index 3e7ce064a1..e85231d863 100644 --- a/internal/runners/exec/exec.go +++ b/internal/runners/exec/exec.go @@ -5,7 +5,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "strconv" "strings" @@ -155,9 +154,6 @@ func (s *Exec) Run(params *Params, args ...string) (rerr error) { return !v } exesOnPath := osutils.FilterExesOnPATH(exeTarget, env["PATH"], filter) - if runtime.GOOS == "windows" { - exesOnPath = append(exesOnPath, osutils.FilterExesOnPATH(exeTarget, env["Path"], filter)...) - } if len(exesOnPath) > 0 { exeTarget = exesOnPath[0] diff --git a/pkg/runtime/internal/envdef/collection.go b/pkg/runtime/internal/envdef/collection.go index fbb2077b57..3801ff4ba7 100644 --- a/pkg/runtime/internal/envdef/collection.go +++ b/pkg/runtime/internal/envdef/collection.go @@ -1,9 +1,7 @@ package envdef import ( - "os" "path/filepath" - "runtime" "sync" "github.com/ActiveState/cli/internal/errs" @@ -65,29 +63,5 @@ func (c *Collection) Environment(installPath string, inherit bool) (map[string]s } } constants := NewConstants(installPath) - env := result.ExpandVariables(constants).GetEnv(inherit) - promotePath(env) - return env, nil -} - -// promotPath is a temporary fix to ensure that the PATH is interpreted correctly on Windows -// Should be properly addressed by https://activestatef.atlassian.net/browse/DX-3030 -func promotePath(env map[string]string) { - if runtime.GOOS != "windows" { - return - } - - PATH, exists := env["PATH"] - if !exists { - return - } - - // If Path exists, prepend PATH values to it - Path, pathExists := env["Path"] - if !pathExists { - return - } - - env["Path"] = PATH + string(os.PathListSeparator) + Path - delete(env, "PATH") + return result.ExpandVariables(constants).GetEnv(inherit), nil } From bf89295f9ae59b63c5c50c379bcae33c3f490f65 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 22 Oct 2024 10:12:14 -0700 Subject: [PATCH 262/440] Don't set atTime when unmarshalling --- pkg/buildscript/unmarshal_buildexpression.go | 7 ------- pkg/platform/model/buildplanner/buildscript.go | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index f3459240f6..ecae65a5d0 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -4,9 +4,7 @@ import ( "encoding/json" "sort" "strings" - "time" - "github.com/go-openapi/strfmt" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -70,13 +68,8 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { // reference to "TIME", which is how we want to show it in AScript format. if atTimeNode, err := b.getSolveAtTimeValue(); err == nil { if atTimeNode.Str != nil && !strings.HasPrefix(*atTimeNode.Str, `$`) { - atTime, err := strfmt.ParseDateTime(*atTimeNode.Str) - if err != nil { - return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str) - } atTimeNode.Str = nil atTimeNode.Ident = ptr.To("TIME") - b.atTime = ptr.To(time.Time(atTime)) } else if atTimeNode.Ident != nil && *atTimeNode.Ident == "at_time" { atTimeNode.Ident = ptr.To("TIME") } diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index 52f038ee5f..28bd980ffc 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -52,10 +52,10 @@ func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript } script := buildscript.New() + script.SetAtTime(time.Time(resp.Commit.AtTime)) if err := script.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } - script.SetAtTime(time.Time(resp.Commit.AtTime)) return script, nil } From 765332445d7915472a18121da58c5a1d348a428a Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 22 Oct 2024 11:50:50 -0400 Subject: [PATCH 263/440] Handle case-insensitive env var names on Windows. We read the case-insensitive version as needed, but replace it with our case-sensitive version (e.g. "Path" -> "PATH"). --- pkg/runtime/internal/envdef/environment.go | 38 ++++++++++++++--- .../internal/envdef/environment_test.go | 41 +++++++++---------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/pkg/runtime/internal/envdef/environment.go b/pkg/runtime/internal/envdef/environment.go index 9cda4e2cbc..d5742ca2c0 100644 --- a/pkg/runtime/internal/envdef/environment.go +++ b/pkg/runtime/internal/envdef/environment.go @@ -6,6 +6,7 @@ import ( "maps" "os" "path/filepath" + "runtime" "strings" "github.com/ActiveState/cli/internal/constants" @@ -344,19 +345,40 @@ func (ev *EnvironmentVariable) ValueString() string { ev.Separator) } -// GetEnvBasedOn returns the environment variable names and values defined by +// getEnvBasedOn returns the environment variable names and values defined by // the EnvironmentDefinition. // If an environment variable is configured to inherit from the base // environment (`Inherit==true`), the base environment defined by the // `envLookup` method is joined with these environment variables. // This function is mostly used for testing. Use GetEnv() in production. -func (ed *EnvironmentDefinition) GetEnvBasedOn(envLookup map[string]string) (map[string]string, error) { +func (ed *EnvironmentDefinition) getEnvBasedOn(envLookup map[string]string) (map[string]string, error) { res := maps.Clone(envLookup) + // On Windows, environment variable names are case-insensitive. + // For example, it uses "Path", but responds to "PATH" as well. + // This causes trouble with our environment merging, which will end up adding "PATH" (with the + // correct value) alongside "Path" (with the old value). + // In order to remedy this, track the OS-specific environment variable name and if it's + // modified/merged, replace it with our version (e.g. "Path" -> "PATH"). We do not use the OS name + // because we assume ours is the one that's used elsewhere in the codebase, and Windows will + // properly respond to a changed-case name anyway. + osEnvNames := map[string]string{} + if runtime.GOOS == "windows" { + for k := range envLookup { + osEnvNames[strings.ToLower(k)] = k + } + } + for _, ev := range ed.Env { pev := &ev + osName := pev.Name + if runtime.GOOS == "windows" { + if name, ok := osEnvNames[strings.ToLower(osName)]; ok { + osName = name + } + } + osValue, hasOsValue := envLookup[osName] if pev.Inherit { - osValue, hasOsValue := envLookup[pev.Name] if hasOsValue { osEv := ev osEv.Values = []string{osValue} @@ -364,15 +386,19 @@ func (ed *EnvironmentDefinition) GetEnvBasedOn(envLookup map[string]string) (map pev, err = osEv.Merge(ev) if err != nil { return nil, err - } } - } else if _, hasOsValue := envLookup[pev.Name]; hasOsValue { + } else if hasOsValue { res[pev.Name] = "" // unset } // only add environment variable if at least one value is set (This allows us to remove variables from the environment.) if len(ev.Values) > 0 { res[pev.Name] = pev.ValueString() + if pev.Name != osName { + // On Windows, delete the case-insensitive version. Our case-sensitive version has already + // processed the value of the case-insensitive version. + delete(res, osName) + } } } return res, nil @@ -388,7 +414,7 @@ func (ed *EnvironmentDefinition) GetEnv(inherit bool) map[string]string { if inherit { lookupEnv = osutils.EnvSliceToMap(os.Environ()) } - res, err := ed.GetEnvBasedOn(lookupEnv) + res, err := ed.getEnvBasedOn(lookupEnv) if err != nil { panic(fmt.Sprintf("Could not inherit OS environment variable: %v", err)) } diff --git a/pkg/runtime/internal/envdef/environment_test.go b/pkg/runtime/internal/envdef/environment_test.go index 7fd8754f7e..3741d5d393 100644 --- a/pkg/runtime/internal/envdef/environment_test.go +++ b/pkg/runtime/internal/envdef/environment_test.go @@ -1,4 +1,4 @@ -package envdef_test +package envdef import ( "encoding/json" @@ -9,7 +9,6 @@ import ( "github.com/ActiveState/cli/internal/osutils" "github.com/ActiveState/cli/internal/testhelpers/suite" - "github.com/ActiveState/cli/pkg/runtime/internal/envdef" "github.com/stretchr/testify/require" "github.com/ActiveState/cli/internal/fileutils" @@ -21,20 +20,20 @@ type EnvironmentTestSuite struct { func (suite *EnvironmentTestSuite) TestMergeVariables() { - ev1 := envdef.EnvironmentVariable{} + ev1 := EnvironmentVariable{} err := json.Unmarshal([]byte(`{ "env_name": "V", "values": ["a", "b"] }`), &ev1) require.NoError(suite.T(), err) - ev2 := envdef.EnvironmentVariable{} + ev2 := EnvironmentVariable{} err = json.Unmarshal([]byte(`{ "env_name": "V", "values": ["b", "c"] }`), &ev2) require.NoError(suite.T(), err) - expected := &envdef.EnvironmentVariable{} + expected := &EnvironmentVariable{} err = json.Unmarshal([]byte(`{ "env_name": "V", "values": ["b", "c", "a"], @@ -51,7 +50,7 @@ func (suite *EnvironmentTestSuite) TestMergeVariables() { } func (suite *EnvironmentTestSuite) TestMerge() { - ed1 := &envdef.EnvironmentDefinition{} + ed1 := &EnvironmentDefinition{} err := json.Unmarshal([]byte(`{ "env": [{"env_name": "V", "values": ["a", "b"]}], @@ -59,14 +58,14 @@ func (suite *EnvironmentTestSuite) TestMerge() { }`), ed1) require.NoError(suite.T(), err) - ed2 := envdef.EnvironmentDefinition{} + ed2 := EnvironmentDefinition{} err = json.Unmarshal([]byte(`{ "env": [{"env_name": "V", "values": ["c", "d"]}], "installdir": "abc" }`), &ed2) require.NoError(suite.T(), err) - expected := envdef.EnvironmentDefinition{} + expected := EnvironmentDefinition{} err = json.Unmarshal([]byte(`{ "env": [{"env_name": "V", "values": ["c", "d", "a", "b"]}], "installdir": "abc" @@ -80,7 +79,7 @@ func (suite *EnvironmentTestSuite) TestMerge() { } func (suite *EnvironmentTestSuite) TestInheritPath() { - ed1 := &envdef.EnvironmentDefinition{} + ed1 := &EnvironmentDefinition{} err := json.Unmarshal([]byte(`{ "env": [{"env_name": "PATH", "values": ["NEWVALUE"]}], @@ -90,7 +89,7 @@ func (suite *EnvironmentTestSuite) TestInheritPath() { }`), ed1) require.NoError(suite.T(), err) - env, err := ed1.GetEnvBasedOn(map[string]string{"PATH": "OLDVALUE"}) + env, err := ed1.getEnvBasedOn(map[string]string{"PATH": "OLDVALUE"}) require.NoError(suite.T(), err) suite.True(strings.HasPrefix(env["PATH"], "NEWVALUE"), "%s does not start with NEWVALUE", env["PATH"]) suite.True(strings.HasSuffix(env["PATH"], "OLDVALUE"), "%s does not end with OLDVALUE", env["PATH"]) @@ -99,11 +98,11 @@ func (suite *EnvironmentTestSuite) TestInheritPath() { func (suite *EnvironmentTestSuite) TestSharedTests() { type testCase struct { - Name string `json:"name"` - Definitions []envdef.EnvironmentDefinition `json:"definitions"` - BaseEnv map[string]string `json:"base_env"` - Expected map[string]string `json:"result"` - IsError bool `json:"error"` + Name string `json:"name"` + Definitions []EnvironmentDefinition `json:"definitions"` + BaseEnv map[string]string `json:"base_env"` + Expected map[string]string `json:"result"` + IsError bool `json:"error"` } td, err := os.ReadFile("runtime_test_cases.json") @@ -126,7 +125,7 @@ func (suite *EnvironmentTestSuite) TestSharedTests() { suite.Assert().NoError(err, "error merging %d-th definition", i) } - res, err := ed.GetEnvBasedOn(tc.BaseEnv) + res, err := ed.getEnvBasedOn(tc.BaseEnv) if tc.IsError { suite.Assert().Error(err) return @@ -139,7 +138,7 @@ func (suite *EnvironmentTestSuite) TestSharedTests() { } func (suite *EnvironmentTestSuite) TestValueString() { - ev1 := envdef.EnvironmentVariable{} + ev1 := EnvironmentVariable{} err := json.Unmarshal([]byte(`{ "env_name": "V", "values": ["a", "b"] @@ -151,7 +150,7 @@ func (suite *EnvironmentTestSuite) TestValueString() { } func (suite *EnvironmentTestSuite) TestGetEnv() { - ed1 := envdef.EnvironmentDefinition{} + ed1 := EnvironmentDefinition{} err := json.Unmarshal([]byte(`{ "env": [{"env_name": "V", "values": ["a", "b"]}], "installdir": "abc" @@ -177,7 +176,7 @@ func (suite *EnvironmentTestSuite) TestFindBinPathFor() { require.NoError(suite.T(), err, "creating temporary directory") defer os.RemoveAll(tmpDir) - ed1 := envdef.EnvironmentDefinition{} + ed1 := EnvironmentDefinition{} err = json.Unmarshal([]byte(`{ "env": [{"env_name": "PATH", "values": ["${INSTALLDIR}/bin", "${INSTALLDIR}/bin2"]}], "installdir": "abc" @@ -187,7 +186,7 @@ func (suite *EnvironmentTestSuite) TestFindBinPathFor() { tmpDir, err = fileutils.GetLongPathName(tmpDir) require.NoError(suite.T(), err) - constants := envdef.NewConstants(tmpDir) + constants := NewConstants(tmpDir) // expand variables ed1.ExpandVariables(constants) @@ -248,7 +247,7 @@ func TestFilterPATH(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - envdef.FilterPATH(tt.args.env, tt.args.excludes...) + FilterPATH(tt.args.env, tt.args.excludes...) require.Equal(t, tt.want, tt.args.env["PATH"]) }) } From 0f874f8a36d9082b0e8709ff4dee44cc26c8381a Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 23 Oct 2024 14:20:34 -0400 Subject: [PATCH 264/440] Verify execution policy was reset after the fact. --- test/integration/remote_installer_int_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/integration/remote_installer_int_test.go b/test/integration/remote_installer_int_test.go index 1b289788eb..65949c3fb4 100644 --- a/test/integration/remote_installer_int_test.go +++ b/test/integration/remote_installer_int_test.go @@ -43,7 +43,10 @@ func (suite *RemoteInstallIntegrationTestSuite) TestInstall() { } policy := getPolicy() - defer setPolicy(policy) + defer func() { + setPolicy(policy) + suite.Assert().Equal(policy, getPolicy(), "execution policy was not reset to '"+policy+"'; subsequent test results may be invalid") + }() setPolicy("Restricted") suite.Assert().Equal("Restricted", getPolicy(), "should have set powershell policy to 'Restricted'") From 501cb62253b8a88951eb7c838833b5994063c9c8 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 23 Oct 2024 14:07:07 -0400 Subject: [PATCH 265/440] Updated comment for clarity and added integration test. --- pkg/runtime/internal/envdef/environment.go | 6 +++-- test/integration/exec_int_test.go | 27 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pkg/runtime/internal/envdef/environment.go b/pkg/runtime/internal/envdef/environment.go index d5742ca2c0..1a1eb771bb 100644 --- a/pkg/runtime/internal/envdef/environment.go +++ b/pkg/runtime/internal/envdef/environment.go @@ -395,8 +395,10 @@ func (ed *EnvironmentDefinition) getEnvBasedOn(envLookup map[string]string) (map if len(ev.Values) > 0 { res[pev.Name] = pev.ValueString() if pev.Name != osName { - // On Windows, delete the case-insensitive version. Our case-sensitive version has already - // processed the value of the case-insensitive version. + // On Windows, delete the redundant (case-insensitive) version that our case-sensitive + // version could conflict with. (Our version has already processed the value of the + // redundant version.) + // For example, delete "Path" while preserving our "PATH". delete(res, osName) } } diff --git a/test/integration/exec_int_test.go b/test/integration/exec_int_test.go index bb4db87f50..6f4a3de868 100644 --- a/test/integration/exec_int_test.go +++ b/test/integration/exec_int_test.go @@ -178,6 +178,33 @@ func (suite *ExecIntegrationTestSuite) TestExeBatArguments() { cp.ExpectExitCode(0) } +func (suite *ExecIntegrationTestSuite) TestExec_PATH_and_Path_on_Windows() { + suite.OnlyRunForTags(tagsuite.Exec) + + if runtime.GOOS != "windows" { + suite.T().Skip("This test is only for windows") + } + + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("checkout", "ActiveState-CLI/small-python", ".") + cp.Expect("Checked out", e2e.RuntimeSourcingTimeoutOpt) + cp.ExpectExitCode(0) + + cp = ts.Spawn("exec", "where", "python3") + cp.Expect(os.TempDir()) // from runtime's defined PATH + cp.ExpectExitCode(0) + + cp = ts.Spawn("exec", "where", "notepad") + cp.Expect("notepad.exe") // from OS-defined default Path + cp.ExpectExitCode(0) + + cp = ts.Spawn("exec", "does-not-exist") + cp.Expect("not found") // neither on PATH nor Path + cp.ExpectNotExitCode(0) +} + func TestExecIntegrationTestSuite(t *testing.T) { suite.Run(t, new(ExecIntegrationTestSuite)) } From af34e6a05ec52889e69d3c2568845eee2eb4366e Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 23 Oct 2024 16:00:11 -0700 Subject: [PATCH 266/440] Preserve solve at_time --- pkg/buildscript/buildscript.go | 14 +++++++++++++- pkg/buildscript/unmarshal.go | 2 +- pkg/buildscript/unmarshal_buildexpression.go | 8 ++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 739a097c75..1761cef4f5 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -7,6 +7,7 @@ import ( "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/logging" "github.com/brunoga/deep" ) @@ -19,6 +20,11 @@ type BuildScript struct { project string atTime *time.Time + + // solveAtTime is the original at_time found in the solve node. + // This is used to support the legacy use case where the at_time + // is found in the solve node. + solveAtTime *time.Time } func init() { @@ -53,7 +59,12 @@ func (b *BuildScript) SetProject(url string) { } func (b *BuildScript) AtTime() *time.Time { - return b.atTime + // If the atTime is set, prefer that over the + // legacy solveAtTime. + if b.atTime != nil { + return b.atTime + } + return b.solveAtTime } func (b *BuildScript) SetAtTime(t time.Time) { @@ -82,6 +93,7 @@ func (b *BuildScript) Equals(other *BuildScript) (bool, error) { if err != nil { return false, errs.Wrap(err, "Unable to marshal other buildscript") } + logging.Debug("BuildScript.Equals, myBytes: %s, otherBytes: %s", string(myBytes), string(otherBytes)) return string(myBytes) == string(otherBytes), nil } diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 9602aef76c..4719b63460 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -79,5 +79,5 @@ func Unmarshal(data []byte) (*BuildScript, error) { atTime = ptr.To(time.Time(atTimeStr)) } - return &BuildScript{raw, project, atTime}, nil + return &BuildScript{raw, project, atTime, nil}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index ecae65a5d0..959d6303b8 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -4,6 +4,7 @@ import ( "encoding/json" "sort" "strings" + "time" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -12,6 +13,7 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/sliceutils" + "github.com/go-openapi/strfmt" ) // At this time, there is no way to ask the Platform for an empty build expression. @@ -68,8 +70,14 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { // reference to "TIME", which is how we want to show it in AScript format. if atTimeNode, err := b.getSolveAtTimeValue(); err == nil { if atTimeNode.Str != nil && !strings.HasPrefix(*atTimeNode.Str, `$`) { + atTime, err := strfmt.ParseDateTime(*atTimeNode.Str) + if err != nil { + return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str) + } atTimeNode.Str = nil atTimeNode.Ident = ptr.To("TIME") + // Preserve the original at_time found in the solve node. + b.solveAtTime = ptr.To(time.Time(atTime)) } else if atTimeNode.Ident != nil && *atTimeNode.Ident == "at_time" { atTimeNode.Ident = ptr.To("TIME") } From fa3b0b2d695ec20cdb94edda6542729e260871fc Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 24 Oct 2024 13:08:41 -0400 Subject: [PATCH 267/440] Re-enable CheckoutFromArchive test. --- test/integration/checkout_int_test.go | 22 +- .../checkout-from-archive/darwin.tar.gz | Bin 176970 -> 0 bytes ...cfa45d0-ec48-524f-9f46-90c60bfb51ac.tar.gz | Bin 0 -> 316 bytes ...d3cdcd1-7b6c-5e5e-b144-6fa17dc28120.tar.gz | Bin 0 -> 316 bytes .../darwin/buildexpression.json | 36 ++ .../darwin/buildplan.json | 515 ++++++++++++++++++ ...c78166b-9088-5f4b-9ea2-637feba4f51b.tar.gz | Bin 0 -> 316 bytes .../darwin/installer_config.json | 7 + .../checkout-from-archive/linux.tar.gz | Bin 246935 -> 0 bytes ...9ea89fb-1b02-5798-b925-00b6a6297fa7.tar.gz | Bin 0 -> 316 bytes ...6e76974-81c5-5816-bff0-bd91840cf89c.tar.gz | Bin 0 -> 316 bytes .../linux/buildexpression.json | 36 ++ .../linux/buildplan.json | 515 ++++++++++++++++++ ...d77e611-f70b-5aea-a2bb-35a2a21a612f.tar.gz | Bin 0 -> 316 bytes .../linux/installer_config.json | 7 + .../checkout-from-archive/windows.tar.gz | Bin 376673 -> 0 bytes ...42604ea-36aa-5ec4-a0c3-ee160b443ed8.tar.gz | Bin 0 -> 316 bytes ...4a59f2c-c3ec-5586-b389-07149b46ac90.tar.gz | Bin 0 -> 316 bytes ...a2631ef-bcc2-5241-87e0-cbf9ee489bb9.tar.gz | Bin 0 -> 316 bytes .../windows/buildexpression.json | 36 ++ .../windows/buildplan.json | 515 ++++++++++++++++++ .../windows/installer_config.json | 7 + 22 files changed, 1685 insertions(+), 11 deletions(-) delete mode 100644 test/integration/testdata/checkout-from-archive/darwin.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/darwin/4cfa45d0-ec48-524f-9f46-90c60bfb51ac.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/darwin/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/darwin/buildexpression.json create mode 100644 test/integration/testdata/checkout-from-archive/darwin/buildplan.json create mode 100644 test/integration/testdata/checkout-from-archive/darwin/dc78166b-9088-5f4b-9ea2-637feba4f51b.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/darwin/installer_config.json delete mode 100644 test/integration/testdata/checkout-from-archive/linux.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/linux/59ea89fb-1b02-5798-b925-00b6a6297fa7.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/linux/96e76974-81c5-5816-bff0-bd91840cf89c.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/linux/buildexpression.json create mode 100644 test/integration/testdata/checkout-from-archive/linux/buildplan.json create mode 100644 test/integration/testdata/checkout-from-archive/linux/cd77e611-f70b-5aea-a2bb-35a2a21a612f.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/linux/installer_config.json delete mode 100644 test/integration/testdata/checkout-from-archive/windows.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/windows/242604ea-36aa-5ec4-a0c3-ee160b443ed8.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/windows/24a59f2c-c3ec-5586-b389-07149b46ac90.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/windows/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9.tar.gz create mode 100644 test/integration/testdata/checkout-from-archive/windows/buildexpression.json create mode 100644 test/integration/testdata/checkout-from-archive/windows/buildplan.json create mode 100644 test/integration/testdata/checkout-from-archive/windows/installer_config.json diff --git a/test/integration/checkout_int_test.go b/test/integration/checkout_int_test.go index 386786cd68..e73e621326 100644 --- a/test/integration/checkout_int_test.go +++ b/test/integration/checkout_int_test.go @@ -9,6 +9,8 @@ import ( "strings" "testing" + "github.com/mholt/archiver/v3" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/environment" "github.com/ActiveState/cli/internal/fileutils" @@ -365,34 +367,32 @@ func (suite *CheckoutIntegrationTestSuite) TestCveReport() { } func (suite *CheckoutIntegrationTestSuite) TestCheckoutFromArchive() { - suite.T().Skip("Skipping until https://activestatef.atlassian.net/browse/DX-3057 is fixed") suite.OnlyRunForTags(tagsuite.Checkout) ts := e2e.New(suite.T(), false) defer ts.Close() root := environment.GetRootPathUnsafe() - archive := filepath.Join(root, "test", "integration", "testdata", "checkout-from-archive", runtime.GOOS+".tar.gz") + dir := filepath.Join(root, "test", "integration", "testdata", "checkout-from-archive", runtime.GOOS) + tgz := fileutils.TempFilePath("", runtime.GOOS+".tar.gz") + files, err := fileutils.ListDirSimple(dir, false) + suite.Require().NoError(err) + gz := archiver.TarGz{Tar: &archiver.Tar{StripComponents: 1000}} // use a big number to strip all leading dirs + suite.Require().NoError(gz.Archive(files, tgz)) + defer os.Remove(tgz) cp := ts.SpawnWithOpts( - e2e.OptArgs("checkout", archive), + e2e.OptArgs("checkout", tgz), e2e.OptAppendEnv("HTTPS_PROXY=none://"), // simulate lack of network connection ) cp.Expect("Checking out project: ActiveState-CLI/AlmostEmpty") cp.Expect("Setting up the following dependencies:") - cp.Expect("└─ zlib@1.3.1") + cp.Expect("└─ provides_hello@0.0.1") cp.Expect("Sourcing Runtime") cp.Expect("Unpacking") cp.Expect("Installing") cp.Expect("All dependencies have been installed and verified") cp.Expect("Checked out project ActiveState-CLI/AlmostEmpty") cp.ExpectExitCode(0) - - // Verify the zlib runtime files exist. - proj, err := project.FromPath(filepath.Join(ts.Dirs.Work, "AlmostEmpty")) - suite.Require().NoError(err) - cachePath := filepath.Join(ts.Dirs.Cache, runtime_helpers.DirNameFromProjectDir(proj.Dir())) - zlibH := filepath.Join(cachePath, "usr", "include", "zlib.h") - suite.Assert().FileExists(zlibH, "zlib.h does not exist") } func TestCheckoutIntegrationTestSuite(t *testing.T) { diff --git a/test/integration/testdata/checkout-from-archive/darwin.tar.gz b/test/integration/testdata/checkout-from-archive/darwin.tar.gz deleted file mode 100644 index 9138a0f2f60c36710f4a1ba14bcbe28468894a8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176970 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 OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/darwin/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120.tar.gz b/test/integration/testdata/checkout-from-archive/darwin/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/darwin/buildexpression.json b/test/integration/testdata/checkout-from-archive/darwin/buildexpression.json new file mode 100644 index 0000000000..09b5f39d66 --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/darwin/buildexpression.json @@ -0,0 +1,36 @@ +{ + "let": { + "in": "$runtime", + "runtime": { + "state_tool_artifacts_v1": { + "build_flags": [], + "src": "$sources" + } + }, + "sources": { + "solve": { + "at_time": "$at_time", + "platforms": [ + "46a5b48f-226a-4696-9746-ba4d50d661c2", + "78977bc8-0f32-519d-80f3-9043f059398c", + "7c998ec2-7491-4e75-be4d-8885800ef5f2" + ], + "requirements": [ + { + "name": "alternative-build-selector", + "namespace": "internal" + }, + { + "name": "mingw-build-selector", + "namespace": "internal" + }, + { + "name": "provides_hello", + "namespace": "private/ActiveState-CLI" + } + ], + "solver_version": null + } + } + } +} diff --git a/test/integration/testdata/checkout-from-archive/darwin/buildplan.json b/test/integration/testdata/checkout-from-archive/darwin/buildplan.json new file mode 100644 index 0000000000..e3623722a1 --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/darwin/buildplan.json @@ -0,0 +1,515 @@ +{ + "__typename": "BuildCompleted", + "buildPlanID": "", + "status": "COMPLETED", + "terminals": [ + { + "tag": "platform:46a5b48f-226a-4696-9746-ba4d50d661c2", + "nodeIds": [ + "4cfa45d0-ec48-524f-9f46-90c60bfb51ac", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119", + "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120", + "dc78166b-9088-5f4b-9ea2-637feba4f51b" + ] + }, + { + "tag": "platform:78977bc8-0f32-519d-80f3-9043f059398c", + "nodeIds": [ + "242604ea-36aa-5ec4-a0c3-ee160b443ed8", + "24a59f2c-c3ec-5586-b389-07149b46ac90", + "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + }, + { + "tag": "platform:7c998ec2-7491-4e75-be4d-8885800ef5f2", + "nodeIds": [ + "59ea89fb-1b02-5798-b925-00b6a6297fa7", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "96e76974-81c5-5816-bff0-bd91840cf89c", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119", + "cd77e611-f70b-5aea-a2bb-35a2a21a612f" + ] + } + ], + "artifacts": [ + { + "__typename": "ArtifactSucceeded", + "nodeId": "09386295-a8d0-5f5f-a5dc-b22598b3b4c9", + "displayName": "noop-builder", + "mimeType": "application/x-activestate-builder", + "generatedBy": "e4921b27-35eb-5415-87d3-95fd3d5728e5", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"s3://platform-sources/builder/65710b34592066ff70669c67ea0031b138f4249c768c429b74f6f2efe781e077/noop-builder.tar.gz", + "logURL": "", + "errors": null, + "checksum": "65710b34592066ff70669c67ea0031b138f4249c768c429b74f6f2efe781e077" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "242604ea-36aa-5ec4-a0c3-ee160b443ed8", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "0b2a2ff3-2188-54c7-8447-99241326850c", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/242604ea-36aa-5ec4-a0c3-ee160b443ed8/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/242604ea-36aa-5ec4-a0c3-ee160b443ed8/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "24a59f2c-c3ec-5586-b389-07149b46ac90", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "9ec50008-ec06-5950-9212-42f187e5985f", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/24a59f2c-c3ec-5586-b389-07149b46ac90/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/24a59f2c-c3ec-5586-b389-07149b46ac90/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "870f367a-c9f8-5096-9bc3-e0263bf7ca16", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "4cfa45d0-ec48-524f-9f46-90c60bfb51ac", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "7b9048bb-0f78-5b33-bd9d-ebddb96c6ebb", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/4cfa45d0-ec48-524f-9f46-90c60bfb51ac/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/4cfa45d0-ec48-524f-9f46-90c60bfb51ac/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "59ea89fb-1b02-5798-b925-00b6a6297fa7", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "29ab23f6-8da0-5940-94f0-3b80b44959ec", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/59ea89fb-1b02-5798-b925-00b6a6297fa7/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/59ea89fb-1b02-5798-b925-00b6a6297fa7/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "96e76974-81c5-5816-bff0-bd91840cf89c", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "e116aa52-e773-51cf-9b00-a5d8f5983663", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/96e76974-81c5-5816-bff0-bd91840cf89c/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/96e76974-81c5-5816-bff0-bd91840cf89c/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "baf760f5-8ba1-5fba-9514-9e451389b021", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "cd77e611-f70b-5aea-a2bb-35a2a21a612f", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "673345fb-f606-52eb-bc84-f5a88e448f18", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/cd77e611-f70b-5aea-a2bb-35a2a21a612f/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/cd77e611-f70b-5aea-a2bb-35a2a21a612f/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "dc78166b-9088-5f4b-9ea2-637feba4f51b", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "3591500a-67a2-5492-8a16-de272287f804", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/dc78166b-9088-5f4b-9ea2-637feba4f51b/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/dc78166b-9088-5f4b-9ea2-637feba4f51b/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + } + ], + "steps": [ + { + "stepId": "0b2a2ff3-2188-54c7-8447-99241326850c", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "242604ea-36aa-5ec4-a0c3-ee160b443ed8" + ] + }, + { + "stepId": "29ab23f6-8da0-5940-94f0-3b80b44959ec", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "59ea89fb-1b02-5798-b925-00b6a6297fa7" + ] + }, + { + "stepId": "3591500a-67a2-5492-8a16-de272287f804", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "dc78166b-9088-5f4b-9ea2-637feba4f51b" + ] + }, + { + "stepId": "673345fb-f606-52eb-bc84-f5a88e448f18", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "cd77e611-f70b-5aea-a2bb-35a2a21a612f" + ] + }, + { + "stepId": "7b9048bb-0f78-5b33-bd9d-ebddb96c6ebb", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "4cfa45d0-ec48-524f-9f46-90c60bfb51ac" + ] + }, + { + "stepId": "870f367a-c9f8-5096-9bc3-e0263bf7ca16", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9" + ] + }, + { + "stepId": "9ec50008-ec06-5950-9212-42f187e5985f", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "24a59f2c-c3ec-5586-b389-07149b46ac90" + ] + }, + { + "stepId": "baf760f5-8ba1-5fba-9514-9e451389b021", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120" + ] + }, + { + "stepId": "e116aa52-e773-51cf-9b00-a5d8f5983663", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "96e76974-81c5-5816-bff0-bd91840cf89c" + ] + } + ], + "sources": [ + { + "nodeId": "0ad51906-049e-554f-be54-0b7d42c12bde", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 21, + "name": "docker-registry.activestate.build/activestate/windows-mingw-builder", + "namespace": "image", + "version": "sha256:53b503ff3df26cfa40b562fc9c0b01423aa3480599bd16b235bb8093a56ee15c", + "licenses": [] + }, + { + "nodeId": "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "ingredientId": "af6d212c-71a1-5c3c-a335-75a7b22049b6", + "ingredientVersionId": "dbb62101-51ea-5f8d-ac4e-518026b2859f", + "revision": 1, + "name": "provides_hello", + "namespace": "private/ActiveState-CLI", + "version": "0.0.1", + "licenses": [ + "", + "" + ] + }, + { + "nodeId": "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "ingredientId": "99be954b-95b1-53a3-9b6d-eb0c2d75afc1", + "ingredientVersionId": "4442f8b7-8059-5d99-831b-5ae6d866ec97", + "revision": 2, + "name": "alternative-build-selector", + "namespace": "internal", + "version": "10.0.0", + "licenses": [] + }, + { + "nodeId": "b6588d63-067d-501e-84d4-d9a7881c1119", + "ingredientId": "dc83d7cd-50b3-5b7a-ace6-79a9aeba7254", + "ingredientVersionId": "9a824a18-3eff-5ba3-b71c-bfb950fca206", + "revision": 1, + "name": "mingw-build-selector", + "namespace": "internal", + "version": "11.0.0", + "licenses": [] + }, + { + "nodeId": "d4fdf221-c145-525c-9f98-93789680b138", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 4, + "name": "monterey.12.4.x86_64-64gb-with-brew", + "namespace": "image", + "version": "a5b5ff1f9c614d584a99a0da918f52a459a088e043b2fb74f26bd48380b68c40", + "licenses": [] + }, + { + "nodeId": "e311344d-208f-5d1d-a744-32a2a82dbf12", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 21, + "name": "docker-registry.activestate.build/activestate/centos-8-builder", + "namespace": "image", + "version": "sha256:6b227ed35fe1216bac0df1ea503a32bb27d7459f4a07d4d3a705405bfecdf665", + "licenses": [] + }, + { + "nodeId": "e4921b27-35eb-5415-87d3-95fd3d5728e5", + "ingredientId": "888f7a88-fdc8-58f7-8e34-1e28425f3c5a", + "ingredientVersionId": "fcfb451f-d86d-5977-ae48-f27610f7d5ab", + "revision": 3, + "name": "noop-builder", + "namespace": "builder", + "version": "1.0.0", + "licenses": [ + "(MIT-1.0)" + ] + } + ], + "buildLogIds": [ + { + "id": "5839ede4-a43e-54f7-84f9-6af8884e7f47", + "platformID": "" + } + ], + "resolvedRequirements": [ + { + "requirement": { + "name": "provides_hello", + "namespace": "private/ActiveState-CLI" + }, + "resolvedSource": "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + }, + { + "requirement": { + "name": "alternative-build-selector", + "namespace": "internal" + }, + "resolvedSource": "a0018800-f1fb-53dd-8e10-049ba0b9245b" + }, + { + "requirement": { + "name": "mingw-build-selector", + "namespace": "internal" + }, + "resolvedSource": "b6588d63-067d-501e-84d4-d9a7881c1119" + } + ] +} diff --git a/test/integration/testdata/checkout-from-archive/darwin/dc78166b-9088-5f4b-9ea2-637feba4f51b.tar.gz b/test/integration/testdata/checkout-from-archive/darwin/dc78166b-9088-5f4b-9ea2-637feba4f51b.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/darwin/installer_config.json b/test/integration/testdata/checkout-from-archive/darwin/installer_config.json new file mode 100644 index 0000000000..558efceb6f --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/darwin/installer_config.json @@ -0,0 +1,7 @@ +{ + "org_name": "ActiveState-CLI", + "project_name": "AlmostEmpty", + "commit_id": "6cd1cc1f-3886-439b-8373-c24ca06ab150", + "branch": "main", + "platform_id": "46a5b48f-226a-4696-9746-ba4d50d661c2" +} diff --git a/test/integration/testdata/checkout-from-archive/linux.tar.gz b/test/integration/testdata/checkout-from-archive/linux.tar.gz deleted file mode 100644 index d8995fe143c2586cd051ad241e26e1450026a4dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246935 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%0A!|aX!hnh!V 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�*Q_A3_>W1nUv9Fnw6=Fpx3qC^ z{o#fm5@$v~>;k!-^w|&92zGcYqUk4J1aD^bV_QG^*FQdNo%`kf|FIkEKjokDPx+_( PedYfFdTJH;02%}UyXHNK diff --git a/test/integration/testdata/checkout-from-archive/linux/59ea89fb-1b02-5798-b925-00b6a6297fa7.tar.gz b/test/integration/testdata/checkout-from-archive/linux/59ea89fb-1b02-5798-b925-00b6a6297fa7.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/linux/96e76974-81c5-5816-bff0-bd91840cf89c.tar.gz b/test/integration/testdata/checkout-from-archive/linux/96e76974-81c5-5816-bff0-bd91840cf89c.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/linux/buildexpression.json b/test/integration/testdata/checkout-from-archive/linux/buildexpression.json new file mode 100644 index 0000000000..09b5f39d66 --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/linux/buildexpression.json @@ -0,0 +1,36 @@ +{ + "let": { + "in": "$runtime", + "runtime": { + "state_tool_artifacts_v1": { + "build_flags": [], + "src": "$sources" + } + }, + "sources": { + "solve": { + "at_time": "$at_time", + "platforms": [ + "46a5b48f-226a-4696-9746-ba4d50d661c2", + "78977bc8-0f32-519d-80f3-9043f059398c", + "7c998ec2-7491-4e75-be4d-8885800ef5f2" + ], + "requirements": [ + { + "name": "alternative-build-selector", + "namespace": "internal" + }, + { + "name": "mingw-build-selector", + "namespace": "internal" + }, + { + "name": "provides_hello", + "namespace": "private/ActiveState-CLI" + } + ], + "solver_version": null + } + } + } +} diff --git a/test/integration/testdata/checkout-from-archive/linux/buildplan.json b/test/integration/testdata/checkout-from-archive/linux/buildplan.json new file mode 100644 index 0000000000..e3623722a1 --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/linux/buildplan.json @@ -0,0 +1,515 @@ +{ + "__typename": "BuildCompleted", + "buildPlanID": "", + "status": "COMPLETED", + "terminals": [ + { + "tag": "platform:46a5b48f-226a-4696-9746-ba4d50d661c2", + "nodeIds": [ + "4cfa45d0-ec48-524f-9f46-90c60bfb51ac", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119", + "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120", + "dc78166b-9088-5f4b-9ea2-637feba4f51b" + ] + }, + { + "tag": "platform:78977bc8-0f32-519d-80f3-9043f059398c", + "nodeIds": [ + "242604ea-36aa-5ec4-a0c3-ee160b443ed8", + "24a59f2c-c3ec-5586-b389-07149b46ac90", + "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + }, + { + "tag": "platform:7c998ec2-7491-4e75-be4d-8885800ef5f2", + "nodeIds": [ + "59ea89fb-1b02-5798-b925-00b6a6297fa7", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "96e76974-81c5-5816-bff0-bd91840cf89c", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119", + "cd77e611-f70b-5aea-a2bb-35a2a21a612f" + ] + } + ], + "artifacts": [ + { + "__typename": "ArtifactSucceeded", + "nodeId": "09386295-a8d0-5f5f-a5dc-b22598b3b4c9", + "displayName": "noop-builder", + "mimeType": "application/x-activestate-builder", + "generatedBy": "e4921b27-35eb-5415-87d3-95fd3d5728e5", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"s3://platform-sources/builder/65710b34592066ff70669c67ea0031b138f4249c768c429b74f6f2efe781e077/noop-builder.tar.gz", + "logURL": "", + "errors": null, + "checksum": "65710b34592066ff70669c67ea0031b138f4249c768c429b74f6f2efe781e077" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "242604ea-36aa-5ec4-a0c3-ee160b443ed8", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "0b2a2ff3-2188-54c7-8447-99241326850c", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/242604ea-36aa-5ec4-a0c3-ee160b443ed8/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/242604ea-36aa-5ec4-a0c3-ee160b443ed8/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "24a59f2c-c3ec-5586-b389-07149b46ac90", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "9ec50008-ec06-5950-9212-42f187e5985f", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/24a59f2c-c3ec-5586-b389-07149b46ac90/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/24a59f2c-c3ec-5586-b389-07149b46ac90/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "870f367a-c9f8-5096-9bc3-e0263bf7ca16", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "4cfa45d0-ec48-524f-9f46-90c60bfb51ac", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "7b9048bb-0f78-5b33-bd9d-ebddb96c6ebb", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/4cfa45d0-ec48-524f-9f46-90c60bfb51ac/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/4cfa45d0-ec48-524f-9f46-90c60bfb51ac/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "59ea89fb-1b02-5798-b925-00b6a6297fa7", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "29ab23f6-8da0-5940-94f0-3b80b44959ec", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/59ea89fb-1b02-5798-b925-00b6a6297fa7/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/59ea89fb-1b02-5798-b925-00b6a6297fa7/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "96e76974-81c5-5816-bff0-bd91840cf89c", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "e116aa52-e773-51cf-9b00-a5d8f5983663", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/96e76974-81c5-5816-bff0-bd91840cf89c/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/96e76974-81c5-5816-bff0-bd91840cf89c/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "baf760f5-8ba1-5fba-9514-9e451389b021", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "cd77e611-f70b-5aea-a2bb-35a2a21a612f", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "673345fb-f606-52eb-bc84-f5a88e448f18", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/cd77e611-f70b-5aea-a2bb-35a2a21a612f/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/cd77e611-f70b-5aea-a2bb-35a2a21a612f/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "dc78166b-9088-5f4b-9ea2-637feba4f51b", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "3591500a-67a2-5492-8a16-de272287f804", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/dc78166b-9088-5f4b-9ea2-637feba4f51b/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/dc78166b-9088-5f4b-9ea2-637feba4f51b/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + } + ], + "steps": [ + { + "stepId": "0b2a2ff3-2188-54c7-8447-99241326850c", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "242604ea-36aa-5ec4-a0c3-ee160b443ed8" + ] + }, + { + "stepId": "29ab23f6-8da0-5940-94f0-3b80b44959ec", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "59ea89fb-1b02-5798-b925-00b6a6297fa7" + ] + }, + { + "stepId": "3591500a-67a2-5492-8a16-de272287f804", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "dc78166b-9088-5f4b-9ea2-637feba4f51b" + ] + }, + { + "stepId": "673345fb-f606-52eb-bc84-f5a88e448f18", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "cd77e611-f70b-5aea-a2bb-35a2a21a612f" + ] + }, + { + "stepId": "7b9048bb-0f78-5b33-bd9d-ebddb96c6ebb", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "4cfa45d0-ec48-524f-9f46-90c60bfb51ac" + ] + }, + { + "stepId": "870f367a-c9f8-5096-9bc3-e0263bf7ca16", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9" + ] + }, + { + "stepId": "9ec50008-ec06-5950-9212-42f187e5985f", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "24a59f2c-c3ec-5586-b389-07149b46ac90" + ] + }, + { + "stepId": "baf760f5-8ba1-5fba-9514-9e451389b021", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120" + ] + }, + { + "stepId": "e116aa52-e773-51cf-9b00-a5d8f5983663", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "96e76974-81c5-5816-bff0-bd91840cf89c" + ] + } + ], + "sources": [ + { + "nodeId": "0ad51906-049e-554f-be54-0b7d42c12bde", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 21, + "name": "docker-registry.activestate.build/activestate/windows-mingw-builder", + "namespace": "image", + "version": "sha256:53b503ff3df26cfa40b562fc9c0b01423aa3480599bd16b235bb8093a56ee15c", + "licenses": [] + }, + { + "nodeId": "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "ingredientId": "af6d212c-71a1-5c3c-a335-75a7b22049b6", + "ingredientVersionId": "dbb62101-51ea-5f8d-ac4e-518026b2859f", + "revision": 1, + "name": "provides_hello", + "namespace": "private/ActiveState-CLI", + "version": "0.0.1", + "licenses": [ + "", + "" + ] + }, + { + "nodeId": "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "ingredientId": "99be954b-95b1-53a3-9b6d-eb0c2d75afc1", + "ingredientVersionId": "4442f8b7-8059-5d99-831b-5ae6d866ec97", + "revision": 2, + "name": "alternative-build-selector", + "namespace": "internal", + "version": "10.0.0", + "licenses": [] + }, + { + "nodeId": "b6588d63-067d-501e-84d4-d9a7881c1119", + "ingredientId": "dc83d7cd-50b3-5b7a-ace6-79a9aeba7254", + "ingredientVersionId": "9a824a18-3eff-5ba3-b71c-bfb950fca206", + "revision": 1, + "name": "mingw-build-selector", + "namespace": "internal", + "version": "11.0.0", + "licenses": [] + }, + { + "nodeId": "d4fdf221-c145-525c-9f98-93789680b138", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 4, + "name": "monterey.12.4.x86_64-64gb-with-brew", + "namespace": "image", + "version": "a5b5ff1f9c614d584a99a0da918f52a459a088e043b2fb74f26bd48380b68c40", + "licenses": [] + }, + { + "nodeId": "e311344d-208f-5d1d-a744-32a2a82dbf12", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 21, + "name": "docker-registry.activestate.build/activestate/centos-8-builder", + "namespace": "image", + "version": "sha256:6b227ed35fe1216bac0df1ea503a32bb27d7459f4a07d4d3a705405bfecdf665", + "licenses": [] + }, + { + "nodeId": "e4921b27-35eb-5415-87d3-95fd3d5728e5", + "ingredientId": "888f7a88-fdc8-58f7-8e34-1e28425f3c5a", + "ingredientVersionId": "fcfb451f-d86d-5977-ae48-f27610f7d5ab", + "revision": 3, + "name": "noop-builder", + "namespace": "builder", + "version": "1.0.0", + "licenses": [ + "(MIT-1.0)" + ] + } + ], + "buildLogIds": [ + { + "id": "5839ede4-a43e-54f7-84f9-6af8884e7f47", + "platformID": "" + } + ], + "resolvedRequirements": [ + { + "requirement": { + "name": "provides_hello", + "namespace": "private/ActiveState-CLI" + }, + "resolvedSource": "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + }, + { + "requirement": { + "name": "alternative-build-selector", + "namespace": "internal" + }, + "resolvedSource": "a0018800-f1fb-53dd-8e10-049ba0b9245b" + }, + { + "requirement": { + "name": "mingw-build-selector", + "namespace": "internal" + }, + "resolvedSource": "b6588d63-067d-501e-84d4-d9a7881c1119" + } + ] +} diff --git a/test/integration/testdata/checkout-from-archive/linux/cd77e611-f70b-5aea-a2bb-35a2a21a612f.tar.gz b/test/integration/testdata/checkout-from-archive/linux/cd77e611-f70b-5aea-a2bb-35a2a21a612f.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/linux/installer_config.json b/test/integration/testdata/checkout-from-archive/linux/installer_config.json new file mode 100644 index 0000000000..4ce83f80fb --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/linux/installer_config.json @@ -0,0 +1,7 @@ +{ + "org_name": "ActiveState-CLI", + "project_name": "AlmostEmpty", + "commit_id": "6cd1cc1f-3886-439b-8373-c24ca06ab150", + "branch": "main", + "platform_id": "7c998ec2-7491-4e75-be4d-8885800ef5f2" +} \ No newline at end of file diff --git a/test/integration/testdata/checkout-from-archive/windows.tar.gz b/test/integration/testdata/checkout-from-archive/windows.tar.gz deleted file mode 100644 index 70d9ca02283b039e4a39ec30a1cb39f3aaf1d71c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 376673 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

+1s~INt^9S<-GJM-}|mnh9V><9b4=q^%7a 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 OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/windows/24a59f2c-c3ec-5586-b389-07149b46ac90.tar.gz b/test/integration/testdata/checkout-from-archive/windows/24a59f2c-c3ec-5586-b389-07149b46ac90.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/windows/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9.tar.gz b/test/integration/testdata/checkout-from-archive/windows/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab9e4aa458239fa80df49ac2377c9e08bf631ee3 GIT binary patch literal 316 zcmV-C0mJ?uiwFP!000001MSp7OT#b}2k@TvDIPn|HtCuSyb1282k|BzI-Ai**OVk3 zi1^)ob&NSscB(r3|0pEwOVWh!UYgx4udZ*F$%q8gMKQE;p~UC*gVZvS1$X8;&vPCp zi#dl$jJc`3KE%=!CFN7JTN9$`YSVr(?o|9t-Pbk{KP83ExwWaN^wF;Qw6u1fVr{5( z7p|jKZ=1^alx`hyv@e_8C#uUpD`Q&fjOQsEQ_Uzio;j?#UyJ)@)5Vu`^4kZ7BWY$^ z&VZ(=tj|XJ$B^R-rM}I7HsrsYYoSHH;QUv*PzkA#_H*Q)=YN{{zKwQc(&yktI@hWC zFLZvmf2sNYwNN?g|4(?G5St!~!1VIyZMKV>cFTc>$H^NCH~;_u00000000000002M OpYsl|jZsnnC;$M}=AKgk literal 0 HcmV?d00001 diff --git a/test/integration/testdata/checkout-from-archive/windows/buildexpression.json b/test/integration/testdata/checkout-from-archive/windows/buildexpression.json new file mode 100644 index 0000000000..09b5f39d66 --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/windows/buildexpression.json @@ -0,0 +1,36 @@ +{ + "let": { + "in": "$runtime", + "runtime": { + "state_tool_artifacts_v1": { + "build_flags": [], + "src": "$sources" + } + }, + "sources": { + "solve": { + "at_time": "$at_time", + "platforms": [ + "46a5b48f-226a-4696-9746-ba4d50d661c2", + "78977bc8-0f32-519d-80f3-9043f059398c", + "7c998ec2-7491-4e75-be4d-8885800ef5f2" + ], + "requirements": [ + { + "name": "alternative-build-selector", + "namespace": "internal" + }, + { + "name": "mingw-build-selector", + "namespace": "internal" + }, + { + "name": "provides_hello", + "namespace": "private/ActiveState-CLI" + } + ], + "solver_version": null + } + } + } +} diff --git a/test/integration/testdata/checkout-from-archive/windows/buildplan.json b/test/integration/testdata/checkout-from-archive/windows/buildplan.json new file mode 100644 index 0000000000..e3623722a1 --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/windows/buildplan.json @@ -0,0 +1,515 @@ +{ + "__typename": "BuildCompleted", + "buildPlanID": "", + "status": "COMPLETED", + "terminals": [ + { + "tag": "platform:46a5b48f-226a-4696-9746-ba4d50d661c2", + "nodeIds": [ + "4cfa45d0-ec48-524f-9f46-90c60bfb51ac", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119", + "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120", + "dc78166b-9088-5f4b-9ea2-637feba4f51b" + ] + }, + { + "tag": "platform:78977bc8-0f32-519d-80f3-9043f059398c", + "nodeIds": [ + "242604ea-36aa-5ec4-a0c3-ee160b443ed8", + "24a59f2c-c3ec-5586-b389-07149b46ac90", + "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + }, + { + "tag": "platform:7c998ec2-7491-4e75-be4d-8885800ef5f2", + "nodeIds": [ + "59ea89fb-1b02-5798-b925-00b6a6297fa7", + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "96e76974-81c5-5816-bff0-bd91840cf89c", + "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "b6588d63-067d-501e-84d4-d9a7881c1119", + "cd77e611-f70b-5aea-a2bb-35a2a21a612f" + ] + } + ], + "artifacts": [ + { + "__typename": "ArtifactSucceeded", + "nodeId": "09386295-a8d0-5f5f-a5dc-b22598b3b4c9", + "displayName": "noop-builder", + "mimeType": "application/x-activestate-builder", + "generatedBy": "e4921b27-35eb-5415-87d3-95fd3d5728e5", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"s3://platform-sources/builder/65710b34592066ff70669c67ea0031b138f4249c768c429b74f6f2efe781e077/noop-builder.tar.gz", + "logURL": "", + "errors": null, + "checksum": "65710b34592066ff70669c67ea0031b138f4249c768c429b74f6f2efe781e077" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "242604ea-36aa-5ec4-a0c3-ee160b443ed8", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "0b2a2ff3-2188-54c7-8447-99241326850c", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/242604ea-36aa-5ec4-a0c3-ee160b443ed8/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/242604ea-36aa-5ec4-a0c3-ee160b443ed8/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "24a59f2c-c3ec-5586-b389-07149b46ac90", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "9ec50008-ec06-5950-9212-42f187e5985f", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/24a59f2c-c3ec-5586-b389-07149b46ac90/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/24a59f2c-c3ec-5586-b389-07149b46ac90/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "870f367a-c9f8-5096-9bc3-e0263bf7ca16", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/2a2631ef-bcc2-5241-87e0-cbf9ee489bb9/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "4cfa45d0-ec48-524f-9f46-90c60bfb51ac", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "7b9048bb-0f78-5b33-bd9d-ebddb96c6ebb", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/4cfa45d0-ec48-524f-9f46-90c60bfb51ac/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/4cfa45d0-ec48-524f-9f46-90c60bfb51ac/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "59ea89fb-1b02-5798-b925-00b6a6297fa7", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "29ab23f6-8da0-5940-94f0-3b80b44959ec", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/59ea89fb-1b02-5798-b925-00b6a6297fa7/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/59ea89fb-1b02-5798-b925-00b6a6297fa7/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "96e76974-81c5-5816-bff0-bd91840cf89c", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "e116aa52-e773-51cf-9b00-a5d8f5983663", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/96e76974-81c5-5816-bff0-bd91840cf89c/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/96e76974-81c5-5816-bff0-bd91840cf89c/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120", + "displayName": "provides_hello.application/octet-stream", + "mimeType": "application/x.artifact", + "generatedBy": "baf760f5-8ba1-5fba-9514-9e451389b021", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "cd77e611-f70b-5aea-a2bb-35a2a21a612f", + "displayName": "mingw-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "673345fb-f606-52eb-bc84-f5a88e448f18", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/cd77e611-f70b-5aea-a2bb-35a2a21a612f/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/cd77e611-f70b-5aea-a2bb-35a2a21a612f/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + }, + { + "__typename": "ArtifactSucceeded", + "nodeId": "dc78166b-9088-5f4b-9ea2-637feba4f51b", + "displayName": "alternative-build-selector.application/gzip", + "mimeType": "application/x.artifact", + "generatedBy": "3591500a-67a2-5492-8a16-de272287f804", + "runtimeDependencies": [], + "status": "SUCCEEDED", + "url": +"https://dl.activestate.com/artifact/dc78166b-9088-5f4b-9ea2-637feba4f51b/artifact.tar.gz", + "logURL": +"https://dl.activestate.com/organization/9cc783d1-c065-4aea-8a55-3b0dbc77078b/project/c2bd9427-f888-4d53-82df-14c0d612f3bd/commit/4f46dee3-1f8f-475d-af06-9554aed2903e/artifact/dc78166b-9088-5f4b-9ea2-637feba4f51b/logs.jsonl", + "errors": null, + "checksum": "bbcf5b36ff31084f24c3748020768173b17967abcd2437bb93638b9dd6110440" + } + ], + "steps": [ + { + "stepId": "0b2a2ff3-2188-54c7-8447-99241326850c", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "242604ea-36aa-5ec4-a0c3-ee160b443ed8" + ] + }, + { + "stepId": "29ab23f6-8da0-5940-94f0-3b80b44959ec", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "59ea89fb-1b02-5798-b925-00b6a6297fa7" + ] + }, + { + "stepId": "3591500a-67a2-5492-8a16-de272287f804", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "dc78166b-9088-5f4b-9ea2-637feba4f51b" + ] + }, + { + "stepId": "673345fb-f606-52eb-bc84-f5a88e448f18", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "cd77e611-f70b-5aea-a2bb-35a2a21a612f" + ] + }, + { + "stepId": "7b9048bb-0f78-5b33-bd9d-ebddb96c6ebb", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "4cfa45d0-ec48-524f-9f46-90c60bfb51ac" + ] + }, + { + "stepId": "870f367a-c9f8-5096-9bc3-e0263bf7ca16", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "b6588d63-067d-501e-84d4-d9a7881c1119" + ] + } + ], + "outputs": [ + "2a2631ef-bcc2-5241-87e0-cbf9ee489bb9" + ] + }, + { + "stepId": "9ec50008-ec06-5950-9212-42f187e5985f", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "24a59f2c-c3ec-5586-b389-07149b46ac90" + ] + }, + { + "stepId": "baf760f5-8ba1-5fba-9514-9e451389b021", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + ] + } + ], + "outputs": [ + "bd3cdcd1-7b6c-5e5e-b144-6fa17dc28120" + ] + }, + { + "stepId": "e116aa52-e773-51cf-9b00-a5d8f5983663", + "inputs": [ + { + "tag": "builder", + "nodeIds": [ + "09386295-a8d0-5f5f-a5dc-b22598b3b4c9" + ] + }, + { + "tag": "deps", + "nodeIds": [] + }, + { + "tag": "src", + "nodeIds": [ + "a0018800-f1fb-53dd-8e10-049ba0b9245b" + ] + } + ], + "outputs": [ + "96e76974-81c5-5816-bff0-bd91840cf89c" + ] + } + ], + "sources": [ + { + "nodeId": "0ad51906-049e-554f-be54-0b7d42c12bde", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 21, + "name": "docker-registry.activestate.build/activestate/windows-mingw-builder", + "namespace": "image", + "version": "sha256:53b503ff3df26cfa40b562fc9c0b01423aa3480599bd16b235bb8093a56ee15c", + "licenses": [] + }, + { + "nodeId": "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7", + "ingredientId": "af6d212c-71a1-5c3c-a335-75a7b22049b6", + "ingredientVersionId": "dbb62101-51ea-5f8d-ac4e-518026b2859f", + "revision": 1, + "name": "provides_hello", + "namespace": "private/ActiveState-CLI", + "version": "0.0.1", + "licenses": [ + "", + "" + ] + }, + { + "nodeId": "a0018800-f1fb-53dd-8e10-049ba0b9245b", + "ingredientId": "99be954b-95b1-53a3-9b6d-eb0c2d75afc1", + "ingredientVersionId": "4442f8b7-8059-5d99-831b-5ae6d866ec97", + "revision": 2, + "name": "alternative-build-selector", + "namespace": "internal", + "version": "10.0.0", + "licenses": [] + }, + { + "nodeId": "b6588d63-067d-501e-84d4-d9a7881c1119", + "ingredientId": "dc83d7cd-50b3-5b7a-ace6-79a9aeba7254", + "ingredientVersionId": "9a824a18-3eff-5ba3-b71c-bfb950fca206", + "revision": 1, + "name": "mingw-build-selector", + "namespace": "internal", + "version": "11.0.0", + "licenses": [] + }, + { + "nodeId": "d4fdf221-c145-525c-9f98-93789680b138", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 4, + "name": "monterey.12.4.x86_64-64gb-with-brew", + "namespace": "image", + "version": "a5b5ff1f9c614d584a99a0da918f52a459a088e043b2fb74f26bd48380b68c40", + "licenses": [] + }, + { + "nodeId": "e311344d-208f-5d1d-a744-32a2a82dbf12", + "ingredientId": "00000000-0000-1000-8000-000000000000", + "ingredientVersionId": "00000000-0000-1000-8000-000000000000", + "revision": 21, + "name": "docker-registry.activestate.build/activestate/centos-8-builder", + "namespace": "image", + "version": "sha256:6b227ed35fe1216bac0df1ea503a32bb27d7459f4a07d4d3a705405bfecdf665", + "licenses": [] + }, + { + "nodeId": "e4921b27-35eb-5415-87d3-95fd3d5728e5", + "ingredientId": "888f7a88-fdc8-58f7-8e34-1e28425f3c5a", + "ingredientVersionId": "fcfb451f-d86d-5977-ae48-f27610f7d5ab", + "revision": 3, + "name": "noop-builder", + "namespace": "builder", + "version": "1.0.0", + "licenses": [ + "(MIT-1.0)" + ] + } + ], + "buildLogIds": [ + { + "id": "5839ede4-a43e-54f7-84f9-6af8884e7f47", + "platformID": "" + } + ], + "resolvedRequirements": [ + { + "requirement": { + "name": "provides_hello", + "namespace": "private/ActiveState-CLI" + }, + "resolvedSource": "88ece3b1-efd3-52cd-9bac-8a3c5c80e4c7" + }, + { + "requirement": { + "name": "alternative-build-selector", + "namespace": "internal" + }, + "resolvedSource": "a0018800-f1fb-53dd-8e10-049ba0b9245b" + }, + { + "requirement": { + "name": "mingw-build-selector", + "namespace": "internal" + }, + "resolvedSource": "b6588d63-067d-501e-84d4-d9a7881c1119" + } + ] +} diff --git a/test/integration/testdata/checkout-from-archive/windows/installer_config.json b/test/integration/testdata/checkout-from-archive/windows/installer_config.json new file mode 100644 index 0000000000..c0493365cd --- /dev/null +++ b/test/integration/testdata/checkout-from-archive/windows/installer_config.json @@ -0,0 +1,7 @@ +{ + "org_name": "ActiveState-CLI", + "project_name": "AlmostEmpty", + "commit_id": "6cd1cc1f-3886-439b-8373-c24ca06ab150", + "branch": "main", + "platform_id": "78977bc8-0f32-519d-80f3-9043f059398c" +} \ No newline at end of file From 9eab57aecb56c035a97fde9068f97709883b8a47 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 24 Oct 2024 11:15:02 -0700 Subject: [PATCH 268/440] Add unit test --- pkg/buildscript/buildscript.go | 2 +- pkg/buildscript/buildscript_test.go | 56 +++++++++++++++++++ .../buildexpression-roundtrip-legacy.json | 43 ++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 pkg/buildscript/testdata/buildexpression-roundtrip-legacy.json diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 1761cef4f5..01c719780c 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -23,7 +23,7 @@ type BuildScript struct { // solveAtTime is the original at_time found in the solve node. // This is used to support the legacy use case where the at_time - // is found in the solve node. + // is an actual time stamp and not a reference to the $at_time variable. solveAtTime *time.Time } diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index 4a96fb9766..7d2ccbe0fe 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -1,9 +1,13 @@ package buildscript import ( + "fmt" + "path/filepath" "testing" "time" + "github.com/ActiveState/cli/internal/environment" + "github.com/ActiveState/cli/internal/fileutils" "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -94,6 +98,58 @@ func TestRoundTripFromBuildExpression(t *testing.T) { require.Equal(t, string(basicBuildExpression), string(data)) } +func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { + wd, err := environment.GetRootPath() + require.NoError(t, err) + + initialTimeStamp := "2024-10-15T16:37:06.260Z" + updatedTimeStamp := "2024-10-15T16:37:06.261Z" + + data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", "buildexpression-roundtrip-legacy.json")) + require.NoError(t, err) + + // The initial build expression does not use the new at_time format + assert.NotContains(t, string(data), "$at_time") + assert.Contains(t, string(data), initialTimeStamp) + + script := New() + require.NoError(t, script.UnmarshalBuildExpression(data)) + + // Ensure that legacy at_time is preserved in the buildscript. + atTime := script.AtTime() + require.NotNil(t, atTime) + require.Equal(t, initialTimeStamp, atTime.Format(strfmt.RFC3339Millis)) + + data, err = script.MarshalBuildExpression() + require.NoError(t, err) + + // When the build expression is unmarshalled it should now use the new at_time format + assert.Contains(t, string(data), "$at_time") + assert.NotContains(t, string(data), initialTimeStamp) + + // Update the time in the build script + updatedTime, err := time.Parse(strfmt.RFC3339Millis, updatedTimeStamp) + require.NoError(t, err) + script.SetAtTime(updatedTime) + + // The updated time should be reflected in the build script + require.Equal(t, updatedTime, *script.AtTime()) + + data, err = script.Marshal() + require.NoError(t, err) + + // The marshalled build script should now contain the updated time + // in the Time block at the top of the script. + assert.Contains(t, string(data), updatedTimeStamp) + assert.NotContains(t, string(data), fmt.Sprintf("Time: %s", initialTimeStamp)) + + data, err = script.MarshalBuildExpression() + require.NoError(t, err) + + // The build expression representation should now use the new at_time format + assert.Contains(t, string(data), "$at_time") +} + // TestExpressionToScript tests that creating a build script from a given Platform build expression // and at time produces the expected result. func TestExpressionToScript(t *testing.T) { diff --git a/pkg/buildscript/testdata/buildexpression-roundtrip-legacy.json b/pkg/buildscript/testdata/buildexpression-roundtrip-legacy.json new file mode 100644 index 0000000000..08212c58af --- /dev/null +++ b/pkg/buildscript/testdata/buildexpression-roundtrip-legacy.json @@ -0,0 +1,43 @@ +{ + "let": { + "sources": { + "solve": { + "at_time": "2024-10-15T16:37:06.260Z", + "solver_version": null, + "platforms": [ + "46a5b48f-226a-4696-9746-ba4d50d661c2", + "78977bc8-0f32-519d-80f3-9043f059398c", + "7c998ec2-7491-4e75-be4d-8885800ef5f2" + ], + "requirements": [ + { + "name": "community_artifact", + "namespace": "internal" + }, + { + "name": "python", + "namespace": "language", + "version_requirements": [ + { + "comparator": "eq", + "version": "3.11.10" + } + ] + } + ] + } + }, + "artifacts": { + "community_artifacts": { + "src": "$sources" + } + }, + "runtime": { + "state_tool_artifacts": { + "src": "$artifacts", + "build_flags": [] + } + }, + "in": "$runtime" + } +} \ No newline at end of file From a67002f979aedc9490912b009ccdbd6662c166e9 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 24 Oct 2024 11:15:43 -0700 Subject: [PATCH 269/440] Remove debug log --- pkg/buildscript/buildscript.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 01c719780c..30e9b3b264 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -7,7 +7,6 @@ import ( "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/logging" "github.com/brunoga/deep" ) @@ -93,7 +92,6 @@ func (b *BuildScript) Equals(other *BuildScript) (bool, error) { if err != nil { return false, errs.Wrap(err, "Unable to marshal other buildscript") } - logging.Debug("BuildScript.Equals, myBytes: %s, otherBytes: %s", string(myBytes), string(otherBytes)) return string(myBytes) == string(otherBytes), nil } From 8ce192f6512904ac821fcccd2c19a9eee1be2fbf Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 25 Oct 2024 11:20:23 -0400 Subject: [PATCH 270/440] Only assert one of each platform, as the number can change over time. --- test/integration/platforms_int_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/integration/platforms_int_test.go b/test/integration/platforms_int_test.go index 3b21fb75b9..cf87bf57ad 100644 --- a/test/integration/platforms_int_test.go +++ b/test/integration/platforms_int_test.go @@ -24,11 +24,8 @@ func (suite *PlatformsIntegrationTestSuite) TestPlatforms_searchSimple() { cp := ts.Spawn("platforms", "search") expectations := []string{ "Darwin", - "Darwin", - "Linux", "Linux", "Windows", - "Windows", } for _, expectation := range expectations { cp.Expect(expectation) From 0a955947aee86f8821668bef7894562cc5971762 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 25 Oct 2024 11:27:46 -0400 Subject: [PATCH 271/440] Added runtime sourcing timeout opt to fix occasionally failing test. --- cmd/state-installer/test/integration/installer_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/state-installer/test/integration/installer_int_test.go b/cmd/state-installer/test/integration/installer_int_test.go index 1f83eaa242..89b102a305 100644 --- a/cmd/state-installer/test/integration/installer_int_test.go +++ b/cmd/state-installer/test/integration/installer_int_test.go @@ -242,7 +242,7 @@ func (suite *InstallerIntegrationTestSuite) TestInstallWhileInUse() { cp.Expect("successfully installed", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectInput() cp.SendLine("state checkout ActiveState/Perl-5.32") - cp.Expect("Checked out") + cp.Expect("Checked out", e2e.RuntimeSourcingTimeoutOpt) cp.SendLine("state shell Perl-5.32") cp.Expect("Activated") // state.exe remains active From 6c1610f67a061b3d8d479bb31e79763b5f5bbcbe Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 25 Oct 2024 11:18:15 -0400 Subject: [PATCH 272/440] Use fixed timestamp when asserting package versions. --- test/integration/package_int_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index b1941977a6..8ac91a19f2 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -656,13 +656,15 @@ func (suite *PackageIntegrationTestSuite) TestChangeSummaryShowsAddedForUpdate() ts.PrepareProject("ActiveState-CLI/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") - cp = ts.Spawn("install", "jinja2@2.0") + timestamp := "--ts=2024-08-15T20:07:00.000Z" + + cp = ts.Spawn("install", "jinja2@2.0", timestamp) cp.Expect("Added: language/python/jinja2") cp.ExpectExitCode(0) - cp = ts.Spawn("install", "jinja2@3.1.4") + cp = ts.Spawn("install", "jinja2@3.1.4", timestamp) cp.Expect("Installing jinja2@3.1.4 includes 1 direct dep") - cp.Expect("└─ markupsafe@3.0.1") + cp.Expect("└─ markupsafe@2.1.5") cp.Expect("Updated: language/python/jinja2") cp.ExpectExitCode(0) } From 26b2210775c546cdd83c4f5c800cdd12b7ad63ab Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 25 Oct 2024 11:41:23 -0400 Subject: [PATCH 273/440] Use runtime sourcing timeout opt to fix failing test. --- test/integration/activate_int_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index f6690f2dbc..7cb4c87653 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -582,11 +582,8 @@ func (suite *ActivateIntegrationTestSuite) TestActivateBranch() { namespace := "ActiveState-CLI/Branches" - cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") - cp.ExpectExitCode(0) - - cp = ts.Spawn("activate", namespace, "--branch", "firstbranch") - cp.Expect("Activated") + cp := ts.Spawn("activate", namespace, "--branch", "firstbranch") + cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) // note: activate always sources the runtime cp.SendLine("exit") cp.ExpectExitCode(0) } From 33bf8fb3229ee0377e1bd44b290ae38f950577b8 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 25 Oct 2024 11:30:26 -0700 Subject: [PATCH 274/440] Use override when setting atTime --- internal/runners/install/install.go | 8 ++++--- internal/runners/platforms/add.go | 2 +- internal/runners/upgrade/upgrade.go | 2 +- pkg/buildscript/buildscript.go | 17 +++++--------- pkg/buildscript/buildscript_test.go | 22 ++++++++++++++----- pkg/buildscript/merge.go | 2 +- pkg/buildscript/unmarshal.go | 2 +- pkg/buildscript/unmarshal_buildexpression.go | 2 +- pkg/platform/model/buildplanner/build.go | 2 +- .../model/buildplanner/buildscript.go | 2 +- pkg/platform/model/buildplanner/commit.go | 2 +- scripts/to-buildscript/main.go | 2 +- 12 files changed, 36 insertions(+), 29 deletions(-) diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index f0b4ce3b86..e11ca22eed 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -114,6 +114,7 @@ func (i *Install) Run(params Params) (rerr error) { var oldCommit *bpModel.Commit var reqs requirements var ts time.Time + var override bool { pg = output.StartSpinner(out, locale.T("progress_search"), constants.TerminalAnimationInterval) @@ -133,6 +134,7 @@ func (i *Install) Run(params Params) (rerr error) { if err != nil { return errs.Wrap(err, "Unable to get timestamp from params") } + override = params.Timestamp.String() != "" // Get languages used in current project languages, err := model.FetchLanguagesForBuildScript(oldCommit.BuildScript()) @@ -153,7 +155,7 @@ func (i *Install) Run(params Params) (rerr error) { // Prepare updated buildscript script := oldCommit.BuildScript() - if err := prepareBuildScript(script, reqs, ts); err != nil { + if err := prepareBuildScript(script, reqs, ts, override); err != nil { return errs.Wrap(err, "Could not prepare build script") } @@ -340,8 +342,8 @@ func (i *Install) renderUserFacing(reqs requirements) { i.prime.Output().Notice("") } -func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) error { - script.SetAtTime(ts) +func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time, override bool) error { + script.SetAtTime(ts, override) for _, req := range requirements { requirement := types.Requirement{ Namespace: req.Resolved.Namespace, diff --git a/internal/runners/platforms/add.go b/internal/runners/platforms/add.go index b4d8d5c253..5b4d7b4300 100644 --- a/internal/runners/platforms/add.go +++ b/internal/runners/platforms/add.go @@ -98,7 +98,7 @@ func (a *Add) Run(params AddRunParams) (rerr error) { // Prepare updated buildscript script := oldCommit.BuildScript() - script.SetAtTime(ts) + script.SetAtTime(ts, false) script.AddPlatform(*platform.PlatformID) // Update local checkout and source runtime changes diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index 953b20098c..ec1320542d 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -111,7 +111,7 @@ func (u *Upgrade) Run(params *Params) (rerr error) { if err != nil { return errs.Wrap(err, "Failed to fetch latest timestamp") } - bumpedBS.SetAtTime(ts) + bumpedBS.SetAtTime(ts, params.Timestamp.String() != "") // Since our platform is commit based we need to create a commit for the "after" buildplan, even though we may not // end up using it it the user doesn't confirm the upgrade. diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 30e9b3b264..79ad6b7474 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -19,11 +19,6 @@ type BuildScript struct { project string atTime *time.Time - - // solveAtTime is the original at_time found in the solve node. - // This is used to support the legacy use case where the at_time - // is an actual time stamp and not a reference to the $at_time variable. - solveAtTime *time.Time } func init() { @@ -58,15 +53,13 @@ func (b *BuildScript) SetProject(url string) { } func (b *BuildScript) AtTime() *time.Time { - // If the atTime is set, prefer that over the - // legacy solveAtTime. - if b.atTime != nil { - return b.atTime - } - return b.solveAtTime + return b.atTime } -func (b *BuildScript) SetAtTime(t time.Time) { +func (b *BuildScript) SetAtTime(t time.Time, override bool) { + if b.atTime != nil && !override { + return + } b.atTime = &t } diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index 7d2ccbe0fe..4bc18a2d06 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -127,18 +127,30 @@ func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { assert.Contains(t, string(data), "$at_time") assert.NotContains(t, string(data), initialTimeStamp) - // Update the time in the build script + // Update the time in the build script but don't override the existing time updatedTime, err := time.Parse(strfmt.RFC3339Millis, updatedTimeStamp) require.NoError(t, err) - script.SetAtTime(updatedTime) + script.SetAtTime(updatedTime, false) // The updated time should be reflected in the build script - require.Equal(t, updatedTime, *script.AtTime()) + require.Equal(t, initialTimeStamp, script.AtTime().Format(strfmt.RFC3339Millis)) data, err = script.Marshal() require.NoError(t, err) - // The marshalled build script should now contain the updated time + // The marshalled build script should NOT contain the updated time + // in the Time block at the top of the script. + assert.Contains(t, string(data), initialTimeStamp) + assert.NotContains(t, string(data), fmt.Sprintf("Time: %s", updatedTime)) + + // Now override the time in the build script + script.SetAtTime(updatedTime, true) + require.Equal(t, updatedTimeStamp, script.AtTime().Format(strfmt.RFC3339Millis)) + + data, err = script.Marshal() + require.NoError(t, err) + + // The marshalled build script should NOW contain the updated time // in the Time block at the top of the script. assert.Contains(t, string(data), updatedTimeStamp) assert.NotContains(t, string(data), fmt.Sprintf("Time: %s", initialTimeStamp)) @@ -158,7 +170,7 @@ func TestExpressionToScript(t *testing.T) { script := New() script.SetProject(testProject) - script.SetAtTime(ts) + script.SetAtTime(ts, false) require.NoError(t, script.UnmarshalBuildExpression(basicBuildExpression)) data, err := script.Marshal() diff --git a/pkg/buildscript/merge.go b/pkg/buildscript/merge.go index 889cafffb1..32db4da729 100644 --- a/pkg/buildscript/merge.go +++ b/pkg/buildscript/merge.go @@ -58,7 +58,7 @@ func (b *BuildScript) Merge(other *BuildScript, strategies *mono_models.MergeStr // When merging build scripts we want to use the most recent timestamp atTime := other.AtTime() if atTime != nil && b.AtTime() != nil && atTime.After(*b.AtTime()) { - b.SetAtTime(*atTime) + b.SetAtTime(*atTime, true) } return nil diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 4719b63460..9602aef76c 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -79,5 +79,5 @@ func Unmarshal(data []byte) (*BuildScript, error) { atTime = ptr.To(time.Time(atTimeStr)) } - return &BuildScript{raw, project, atTime, nil}, nil + return &BuildScript{raw, project, atTime}, nil } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 959d6303b8..fe7eee2eeb 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -77,7 +77,7 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { atTimeNode.Str = nil atTimeNode.Ident = ptr.To("TIME") // Preserve the original at_time found in the solve node. - b.solveAtTime = ptr.To(time.Time(atTime)) + b.atTime = ptr.To(time.Time(atTime)) } else if atTimeNode.Ident != nil && *atTimeNode.Ident == "at_time" { atTimeNode.Ident = ptr.To("TIME") } diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index 0b11fc1712..feef9b1a28 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -110,7 +110,7 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, if err := script.UnmarshalBuildExpression(commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } - script.SetAtTime(time.Time(commit.AtTime)) + script.SetAtTime(time.Time(commit.AtTime), false) return &Commit{commit, bp, script}, nil } diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index 28bd980ffc..c6fcc6a2ee 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -52,7 +52,7 @@ func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript } script := buildscript.New() - script.SetAtTime(time.Time(resp.Commit.AtTime)) + script.SetAtTime(time.Time(resp.Commit.AtTime), false) if err := script.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } diff --git a/pkg/platform/model/buildplanner/commit.go b/pkg/platform/model/buildplanner/commit.go index 387296b1ff..3086d32930 100644 --- a/pkg/platform/model/buildplanner/commit.go +++ b/pkg/platform/model/buildplanner/commit.go @@ -82,7 +82,7 @@ func (b *BuildPlanner) StageCommit(params StageCommitParams) (*Commit, error) { } stagedScript := buildscript.New() - stagedScript.SetAtTime(time.Time(resp.Commit.AtTime)) + stagedScript.SetAtTime(time.Time(resp.Commit.AtTime), false) if err := stagedScript.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } diff --git a/scripts/to-buildscript/main.go b/scripts/to-buildscript/main.go index 60c7cde520..84a2e4599f 100644 --- a/scripts/to-buildscript/main.go +++ b/scripts/to-buildscript/main.go @@ -55,7 +55,7 @@ func main() { bs := buildscript.New() bs.SetProject(project) if atTime != nil { - bs.SetAtTime(*atTime) + bs.SetAtTime(*atTime, true) } err := bs.UnmarshalBuildExpression([]byte(input)) if err != nil { From bcb3421306f8da9f9b930245fa946f252565970b Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 28 Oct 2024 12:00:23 -0400 Subject: [PATCH 275/440] Do not report config initialization input errors to rollbar. Also, the default logger should not log verbosely by default. This cleans up error output. --- cmd/state/main.go | 10 ++++++++-- internal/logging/logging.go | 7 ++++++- internal/osutils/user/user.go | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cmd/state/main.go b/cmd/state/main.go index 65e11674d2..84ae4a16fb 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -79,8 +79,14 @@ func main() { var err error cfg, err = config.New() if err != nil { - multilog.Critical("Could not initialize config: %v", errs.JoinMessage(err)) - fmt.Fprintf(os.Stderr, "Could not load config, if this problem persists please reinstall the State Tool. Error: %s\n", errs.JoinMessage(err)) + if !locale.IsInputError(err) { + multilog.Critical("Could not initialize config: %v", errs.JoinMessage(err)) + fmt.Fprintf(os.Stderr, "Could not load config, if this problem persists please reinstall the State Tool. Error: %s\n", errs.JoinMessage(err)) + } else { + for _, err2 := range locale.UnpackError(err) { + fmt.Fprintf(os.Stderr, err2.Error()) + } + } exitCode = 1 return } diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 6f0c5e6f73..1ac62a772b 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -128,6 +128,7 @@ type LoggingHandler interface { type standardHandler struct { formatter Formatter + verbose bool } func (l *standardHandler) SetFormatter(f Formatter) { @@ -135,6 +136,7 @@ func (l *standardHandler) SetFormatter(f Formatter) { } func (l *standardHandler) SetVerbose(v bool) { + l.verbose = v } func (l *standardHandler) Output() io.Writer { @@ -143,7 +145,9 @@ func (l *standardHandler) Output() io.Writer { // default handling interface - just func (l *standardHandler) Emit(ctx *MessageContext, message string, args ...interface{}) error { - fmt.Fprintln(os.Stderr, l.formatter.Format(ctx, message, args...)) + if l.verbose { + fmt.Fprintln(os.Stderr, l.formatter.Format(ctx, message, args...)) + } return nil } @@ -158,6 +162,7 @@ func (l *standardHandler) Close() {} var currentHandler LoggingHandler = &standardHandler{ DefaultFormatter, + os.Getenv("VERBOSE") != "", } // Set the current handler of the library. We currently support one handler, but it might be nice to have more diff --git a/internal/osutils/user/user.go b/internal/osutils/user/user.go index 56bacb3791..1b485d0fab 100644 --- a/internal/osutils/user/user.go +++ b/internal/osutils/user/user.go @@ -23,7 +23,7 @@ func (e *HomeDirNotFoundError) Error() string { return homeDirNotFoundErrorMessage } -func (e *HomeDirNotFoundError) LocalizedError() string { +func (e *HomeDirNotFoundError) LocaleError() string { return homeDirNotFoundErrorMessage } From 81cb55f31f70c722dddf04f608cc329471977ed4 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 28 Oct 2024 14:04:39 -0400 Subject: [PATCH 276/440] Do not upload artifacts after deploy. It's superfluous. --- .github/workflows/build.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8169649d28..17f9a22dff 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -509,10 +509,3 @@ jobs: session-build-ubuntu-20.04 session-build-macos-11 session-build-windows-2019 - - - # === Upload Artifacts === - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: build - path: build/ From a160ff7ef8dd6263c444d1a070537608d4a2af95 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 28 Oct 2024 14:17:52 -0400 Subject: [PATCH 277/440] Fixed incorrect commit message operations. --- internal/runners/platforms/remove.go | 2 +- internal/runners/uninstall/uninstall.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/runners/platforms/remove.go b/internal/runners/platforms/remove.go index 85a56d33db..33019cadbf 100644 --- a/internal/runners/platforms/remove.go +++ b/internal/runners/platforms/remove.go @@ -99,7 +99,7 @@ func (a *Remove) Run(params RemoveRunParams) (rerr error) { } // Update local checkout and source runtime changes - if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_added", params.Platform.String()), trigger.TriggerPlatform); err != nil { + if err := reqop_runbit.UpdateAndReload(a.prime, script, oldCommit, locale.Tr("commit_message_removed", params.Platform.String()), trigger.TriggerPlatform); err != nil { return errs.Wrap(err, "Failed to update local checkout") } diff --git a/internal/runners/uninstall/uninstall.go b/internal/runners/uninstall/uninstall.go index be85debf5e..2de3aa2c18 100644 --- a/internal/runners/uninstall/uninstall.go +++ b/internal/runners/uninstall/uninstall.go @@ -137,7 +137,7 @@ func (u *Uninstall) Run(params Params) (rerr error) { pg = nil // Update local checkout and source runtime changes - if err := reqop_runbit.UpdateAndReload(u.prime, script, oldCommit, locale.Tr("commit_message_added", params.Packages.String()), trigger.TriggerUninstall); err != nil { + if err := reqop_runbit.UpdateAndReload(u.prime, script, oldCommit, locale.Tr("commit_message_removed", params.Packages.String()), trigger.TriggerUninstall); err != nil { return errs.Wrap(err, "Failed to update local checkout") } From 4886da99467f994497de07b35d37f9323e6a69af Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 28 Oct 2024 15:35:40 -0400 Subject: [PATCH 278/440] Log %PATH% that did not contain powershell to aid in diagnosing rollbar issue. --- internal/osutils/shortcut/shortcut_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/osutils/shortcut/shortcut_windows.go b/internal/osutils/shortcut/shortcut_windows.go index 30e6b337d3..365b493077 100644 --- a/internal/osutils/shortcut/shortcut_windows.go +++ b/internal/osutils/shortcut/shortcut_windows.go @@ -1,6 +1,7 @@ package shortcut import ( + "errors" "fmt" "os" "os/exec" @@ -74,6 +75,9 @@ func (s *Shortcut) Enable() error { cmd := exec.Command("powershell.exe", args...) out, err := cmd.CombinedOutput() if err != nil { + if errors.Is(err, exec.ErrNotFound) { + return locale.WrapError(err, "err_prepare_shortcut_powershell_not_found", "Could not create shortcut because powershell was not found on your PATH (searched '{{.V0}}')", os.Getenv("PATH")) + } return locale.WrapError(err, "err_clean_start", "Could not create shortcut. Received error: {{.V0}}", string(out)) } From 34d3e1a78ccc83206d0b3146d2b7d36e71787884 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 28 Oct 2024 13:16:41 -0700 Subject: [PATCH 279/440] Address PR review comments --- internal/runners/install/install.go | 8 +++----- internal/runners/platforms/add.go | 2 +- internal/runners/upgrade/upgrade.go | 2 +- pkg/buildscript/buildscript.go | 3 +++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index e11ca22eed..5724c7b580 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -114,7 +114,6 @@ func (i *Install) Run(params Params) (rerr error) { var oldCommit *bpModel.Commit var reqs requirements var ts time.Time - var override bool { pg = output.StartSpinner(out, locale.T("progress_search"), constants.TerminalAnimationInterval) @@ -134,7 +133,6 @@ func (i *Install) Run(params Params) (rerr error) { if err != nil { return errs.Wrap(err, "Unable to get timestamp from params") } - override = params.Timestamp.String() != "" // Get languages used in current project languages, err := model.FetchLanguagesForBuildScript(oldCommit.BuildScript()) @@ -155,7 +153,7 @@ func (i *Install) Run(params Params) (rerr error) { // Prepare updated buildscript script := oldCommit.BuildScript() - if err := prepareBuildScript(script, reqs, ts, override); err != nil { + if err := prepareBuildScript(script, reqs, ts); err != nil { return errs.Wrap(err, "Could not prepare build script") } @@ -342,8 +340,8 @@ func (i *Install) renderUserFacing(reqs requirements) { i.prime.Output().Notice("") } -func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time, override bool) error { - script.SetAtTime(ts, override) +func prepareBuildScript(script *buildscript.BuildScript, requirements requirements, ts time.Time) error { + script.SetAtTime(ts, true) for _, req := range requirements { requirement := types.Requirement{ Namespace: req.Resolved.Namespace, diff --git a/internal/runners/platforms/add.go b/internal/runners/platforms/add.go index 5b4d7b4300..3c194ff96f 100644 --- a/internal/runners/platforms/add.go +++ b/internal/runners/platforms/add.go @@ -98,7 +98,7 @@ func (a *Add) Run(params AddRunParams) (rerr error) { // Prepare updated buildscript script := oldCommit.BuildScript() - script.SetAtTime(ts, false) + script.SetAtTime(ts, true) script.AddPlatform(*platform.PlatformID) // Update local checkout and source runtime changes diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index ec1320542d..eb82b499c2 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -111,7 +111,7 @@ func (u *Upgrade) Run(params *Params) (rerr error) { if err != nil { return errs.Wrap(err, "Failed to fetch latest timestamp") } - bumpedBS.SetAtTime(ts, params.Timestamp.String() != "") + bumpedBS.SetAtTime(ts, true) // Since our platform is commit based we need to create a commit for the "after" buildplan, even though we may not // end up using it it the user doesn't confirm the upgrade. diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index 79ad6b7474..6b118071df 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -56,6 +56,9 @@ func (b *BuildScript) AtTime() *time.Time { return b.atTime } +// SetAtTime sets the AtTime value, if the buildscript already has an AtTime value +// and `override=false` then the value passed here will be discarded. +// Override should in most cases only be true if we are making changes to the buildscript. func (b *BuildScript) SetAtTime(t time.Time, override bool) { if b.atTime != nil && !override { return From d180c70152426cb13606d03d610874a01e6a2c07 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 28 Oct 2024 17:43:39 -0400 Subject: [PATCH 280/440] Localize an access denied error when attempting to migrate an activestate.yaml. --- pkg/projectfile/yamlfield.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/projectfile/yamlfield.go b/pkg/projectfile/yamlfield.go index 2217d37a4b..48d829018f 100644 --- a/pkg/projectfile/yamlfield.go +++ b/pkg/projectfile/yamlfield.go @@ -7,6 +7,8 @@ import ( "regexp" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/osutils" "gopkg.in/yaml.v2" ) @@ -56,6 +58,11 @@ func (y *yamlField) Save(path string) error { } if err := os.WriteFile(path, out, 0664); err != nil { + if osutils.IsAccessDeniedError(err) { + return locale.WrapError(err, "err_migrate_projectfile_access_denied", + "Your project file at '{{.V0}}' is out of date, but State Tool does not have permission to update it. Please make it writeable or re-checkout the project to a writeable location.", + path) + } return errs.Wrap(err, "ioutil.WriteFile %s failed", path) } From f788e57e802d8365deb448fd71cbed7bc6249765 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 29 Oct 2024 10:37:57 -0400 Subject: [PATCH 281/440] Re-enable test. --- .../osutils/autostart/autostart_darwin.go | 4 +++ test/integration/prepare_int_test.go | 25 +++++++------------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/internal/osutils/autostart/autostart_darwin.go b/internal/osutils/autostart/autostart_darwin.go index d5299d1fcd..39cb80e933 100644 --- a/internal/osutils/autostart/autostart_darwin.go +++ b/internal/osutils/autostart/autostart_darwin.go @@ -6,6 +6,7 @@ import ( "path/filepath" "github.com/ActiveState/cli/internal/assets" + "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/logging" @@ -89,6 +90,9 @@ func autostartPath(_ string, opts Options) (string, error) { if err != nil { return "", errs.Wrap(err, "Could not get home directory") } + if testDir, ok := os.LookupEnv(constants.AutostartPathOverrideEnvVarName); ok { + dir = testDir + } path := filepath.Join(dir, "Library/LaunchAgents", fmt.Sprintf(launchFileFormatName, opts.LaunchFileName)) return path, nil } diff --git a/test/integration/prepare_int_test.go b/test/integration/prepare_int_test.go index 3f86d32233..e639f5d5d6 100644 --- a/test/integration/prepare_int_test.go +++ b/test/integration/prepare_int_test.go @@ -8,11 +8,11 @@ import ( "runtime" "testing" - svcApp "github.com/ActiveState/cli/cmd/state-svc/app" svcAutostart "github.com/ActiveState/cli/cmd/state-svc/autostart" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/fileutils" + "github.com/ActiveState/cli/internal/installation/storage" "github.com/ActiveState/cli/internal/osutils" "github.com/ActiveState/cli/internal/osutils/autostart" "github.com/ActiveState/cli/internal/osutils/user" @@ -30,10 +30,6 @@ type PrepareIntegrationTestSuite struct { } func (suite *PrepareIntegrationTestSuite) TestPrepare() { - // Disable test for v0.36: https://activestatef.atlassian.net/browse/DX-1501. - // This test should be re-enabled by https://activestatef.atlassian.net/browse/DX-1435. - suite.T().SkipNow() - suite.OnlyRunForTags(tagsuite.Prepare) if !e2e.RunningOnCI() { suite.T().Skipf("Skipping TestPrepare when not running on CI or on MacOS, as it modifies PATH") @@ -49,7 +45,6 @@ func (suite *PrepareIntegrationTestSuite) TestPrepare() { cp := ts.SpawnWithOpts( e2e.OptArgs("_prepare"), e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.AutostartPathOverrideEnvVarName, autostartDir)), - // e2e.OptAppendEnv(fmt.Sprintf("%s=%s", constants.ConfigEnvVarName, ts.Dirs.Work)), ) cp.ExpectExitCode(0) @@ -59,12 +54,10 @@ func (suite *PrepareIntegrationTestSuite) TestPrepare() { if isAdmin { return } - suite.AssertConfig(filepath.Join(ts.Dirs.Cache, "bin")) + suite.AssertConfig(storage.CachePath()) // Verify autostart was enabled. - app, err := svcApp.New() - suite.Require().NoError(err) - enabled, err := autostart.IsEnabled(app.Path(), svcAutostart.Options) + enabled, err := autostart.IsEnabled(constants.StateSvcCmd, svcAutostart.Options) suite.Require().NoError(err) suite.Assert().True(enabled, "autostart is not enabled") @@ -74,13 +67,13 @@ func (suite *PrepareIntegrationTestSuite) TestPrepare() { suite.Require().NoError(err) profile := filepath.Join(homeDir, ".profile") profileContents := string(fileutils.ReadFileUnsafe(profile)) - suite.Contains(profileContents, app.Path(), "autostart should be configured for Linux server environment") + suite.Contains(profileContents, constants.StateSvcCmd, "autostart should be configured for Linux server environment") } // Verify autostart can be disabled. - err = autostart.Disable(app.Path(), svcAutostart.Options) + err = autostart.Disable(constants.StateSvcCmd, svcAutostart.Options) suite.Require().NoError(err) - enabled, err = autostart.IsEnabled(app.Path(), svcAutostart.Options) + enabled, err = autostart.IsEnabled(constants.StateSvcCmd, svcAutostart.Options) suite.Require().NoError(err) suite.Assert().False(enabled, "autostart is still enabled") @@ -90,7 +83,7 @@ func (suite *PrepareIntegrationTestSuite) TestPrepare() { suite.Require().NoError(err) profile := filepath.Join(homeDir, ".profile") profileContents := fileutils.ReadFileUnsafe(profile) - suite.NotContains(profileContents, app.Exec, "autostart should not be configured for Linux server environment anymore") + suite.NotContains(profileContents, constants.StateSvcCmd, "autostart should not be configured for Linux server environment anymore") } // Verify the Windows shortcuts were installed. @@ -132,8 +125,8 @@ func (suite *PrepareIntegrationTestSuite) TestResetExecutors() { cp := ts.Spawn("activate", "ActiveState-CLI/small-python", "--path", ts.Dirs.Work, "--default") cp.Expect("This project will always be available for use") cp.Expect("Downloading") - cp.Expect("Installing") - cp.Expect("Activated", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Installing", e2e.RuntimeSourcingTimeoutOpt) + cp.Expect("Activated") cp.SendLine("exit") cp.ExpectExitCode(0) From c683f777a3fb33330fb262a89cb3e5bbe8610a85 Mon Sep 17 00:00:00 2001 From: ActiveState CLI Automation Date: Tue, 29 Oct 2024 10:49:49 -0700 Subject: [PATCH 282/440] Update version.txt --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index f7ccf07ba8..ea2909f230 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.47.0-RC1 +0.48.0-RC1 \ No newline at end of file From cff0fe1574b5f865d14ed07c9b5818d835786da1 Mon Sep 17 00:00:00 2001 From: ActiveState CLI Automation Date: Tue, 29 Oct 2024 10:59:59 -0700 Subject: [PATCH 283/440] Update version.txt --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 4c8c50fc8d..582463bf16 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.46.0-RC3 \ No newline at end of file +0.46.1-RC1 \ No newline at end of file From 2dae8ec99f13b5122ae69b8688a6f807616afe34 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 29 Oct 2024 14:03:42 -0400 Subject: [PATCH 284/440] `state refresh` should not error if the runtime is up to date. --- internal/runners/refresh/refresh.go | 3 ++- test/integration/refresh_int_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/runners/refresh/refresh.go b/internal/runners/refresh/refresh.go index 1a25a6b594..41a3e3c352 100644 --- a/internal/runners/refresh/refresh.go +++ b/internal/runners/refresh/refresh.go @@ -92,7 +92,8 @@ func (r *Refresh) Run(params *Params) error { } if !needsUpdate { - return locale.NewInputError("refresh_runtime_uptodate") + r.out.Notice(locale.T("refresh_runtime_uptodate")) + return nil } rti, err := runtime_runbit.Update(r.prime, trigger.TriggerRefresh, runtime_runbit.WithoutHeaders(), runtime_runbit.WithIgnoreAsync()) diff --git a/test/integration/refresh_int_test.go b/test/integration/refresh_int_test.go index 7b2863ba4f..d71dfd1c0b 100644 --- a/test/integration/refresh_int_test.go +++ b/test/integration/refresh_int_test.go @@ -39,7 +39,7 @@ func (suite *RefreshIntegrationTestSuite) TestRefresh() { cp = ts.Spawn("refresh") cp.Expect("already up to date") - cp.ExpectExitCode(1) + cp.ExpectExitCode(0) } func (suite *RefreshIntegrationTestSuite) TestJSON() { From 7bc67060080b451ffda1c68076ea8c476553498c Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 29 Oct 2024 11:18:25 -0700 Subject: [PATCH 285/440] Walk process tree --- installers/install.ps1 | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/installers/install.ps1 b/installers/install.ps1 index 12d10e20da..ac5eb4e97d 100644 --- a/installers/install.ps1 +++ b/installers/install.ps1 @@ -147,11 +147,23 @@ function error([string] $msg) Write-Host $msg -ForegroundColor Red } -function setShellOverride { - $parentProcess = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $PID } - $parentProcessDetails = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $parentProcess.ParentProcessId } - if ($parentProcessDetails.Name -eq "cmd" -or $parentProcessDetails.Name -eq "cmd.exe") { - [System.Environment]::SetEnvironmentVariable("ACTIVESTATE_CLI_SHELL_OVERRIDE", $parentProcessDetails.Name, "Process") +function setShellOverride +{ + # Walk up the process tree to find cmd.exe + # If we encounter it we set the shell override + $currentPid = $PID + while ($currentPid -ne 0) + { + $process = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $currentPid } + if (!$process) { break } + + if ($process.Name -eq "cmd" -or $process.Name -eq "cmd.exe") + { + [System.Environment]::SetEnvironmentVariable("ACTIVESTATE_CLI_SHELL_OVERRIDE", "cmd.exe", "Process") + break + } + + $currentPid = $process.ParentProcessId } } @@ -257,6 +269,7 @@ $PSDefaultParameterValues['*:Encoding'] = 'utf8' # Run the installer. $env:ACTIVESTATE_SESSION_TOKEN = $script:SESSION_TOKEN_VALUE setShellOverride +Write-Host $env:ACTIVESTATE_CLI_SHELL_OVERRIDE & $exePath $args --source-installer="install.ps1" $success = $? if (Test-Path env:ACTIVESTATE_SESSION_TOKEN) From 2bd15f9fc498beaa229bdb7fffe2e84a75963fe1 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 29 Oct 2024 11:30:40 -0700 Subject: [PATCH 286/440] Add v0.46.1 changelog entry --- changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.md b/changelog.md index 10ab393b35..2f4dc4ae66 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.46.1 + +### Fixed + +* Fixed regression in `state refresh` where it would exit with a non-zero code when there were no changes. + ## 0.46.0 ### Added From 31b9182a9bbf07c1b8ab3efdc9cf5999c807760c Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 29 Oct 2024 11:41:21 -0700 Subject: [PATCH 287/440] Debug script --- installers/install.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/installers/install.ps1 b/installers/install.ps1 index ac5eb4e97d..4c75fdb191 100644 --- a/installers/install.ps1 +++ b/installers/install.ps1 @@ -151,15 +151,17 @@ function setShellOverride { # Walk up the process tree to find cmd.exe # If we encounter it we set the shell override + Write-Host "Walking up the process tree to find cmd.exe" $currentPid = $PID while ($currentPid -ne 0) { $process = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $currentPid } if (!$process) { break } + Write-Host "Checking process $($process.Name) with PID $($process.ProcessId)" if ($process.Name -eq "cmd" -or $process.Name -eq "cmd.exe") { - [System.Environment]::SetEnvironmentVariable("ACTIVESTATE_CLI_SHELL_OVERRIDE", "cmd.exe", "Process") + [System.Environment]::SetEnvironmentVariable("ACTIVESTATE_CLI_SHELL_OVERRIDE", $process.Name, "Process") break } From aaf957b3f9c49558b8f14eb3bb857ed143e65ce9 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 29 Oct 2024 11:41:49 -0700 Subject: [PATCH 288/440] More debugging --- installers/install.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/install.ps1 b/installers/install.ps1 index 4c75fdb191..5024d1ee66 100644 --- a/installers/install.ps1 +++ b/installers/install.ps1 @@ -271,7 +271,7 @@ $PSDefaultParameterValues['*:Encoding'] = 'utf8' # Run the installer. $env:ACTIVESTATE_SESSION_TOKEN = $script:SESSION_TOKEN_VALUE setShellOverride -Write-Host $env:ACTIVESTATE_CLI_SHELL_OVERRIDE +Write-Host "Shell override: $env:ACTIVESTATE_CLI_SHELL_OVERRIDE" & $exePath $args --source-installer="install.ps1" $success = $? if (Test-Path env:ACTIVESTATE_SESSION_TOKEN) From 986f378151ec1cccf96fabbe5585e2ec6e40bcce Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 29 Oct 2024 12:48:06 -0700 Subject: [PATCH 289/440] Remove debug prints --- installers/install.ps1 | 3 --- 1 file changed, 3 deletions(-) diff --git a/installers/install.ps1 b/installers/install.ps1 index 5024d1ee66..3c8dceda26 100644 --- a/installers/install.ps1 +++ b/installers/install.ps1 @@ -151,14 +151,12 @@ function setShellOverride { # Walk up the process tree to find cmd.exe # If we encounter it we set the shell override - Write-Host "Walking up the process tree to find cmd.exe" $currentPid = $PID while ($currentPid -ne 0) { $process = Get-WmiObject Win32_Process | Where-Object { $_.ProcessId -eq $currentPid } if (!$process) { break } - Write-Host "Checking process $($process.Name) with PID $($process.ProcessId)" if ($process.Name -eq "cmd" -or $process.Name -eq "cmd.exe") { [System.Environment]::SetEnvironmentVariable("ACTIVESTATE_CLI_SHELL_OVERRIDE", $process.Name, "Process") @@ -271,7 +269,6 @@ $PSDefaultParameterValues['*:Encoding'] = 'utf8' # Run the installer. $env:ACTIVESTATE_SESSION_TOKEN = $script:SESSION_TOKEN_VALUE setShellOverride -Write-Host "Shell override: $env:ACTIVESTATE_CLI_SHELL_OVERRIDE" & $exePath $args --source-installer="install.ps1" $success = $? if (Test-Path env:ACTIVESTATE_SESSION_TOKEN) From 91ae9f7436df7d63d7a448c45dce9fa27bf62d8b Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 29 Oct 2024 13:20:04 -0400 Subject: [PATCH 290/440] Do not log JSON output pipe errors to rollbar. The system is hanging up on us and there is nothing we can do about it. --- internal/output/json.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/internal/output/json.go b/internal/output/json.go index 17aa06fd74..1d572c1a42 100644 --- a/internal/output/json.go +++ b/internal/output/json.go @@ -2,8 +2,11 @@ package output import ( "encoding/json" + "errors" "fmt" "io" + "runtime" + "syscall" "github.com/ActiveState/cli/internal/colorize" "github.com/ActiveState/cli/internal/locale" @@ -63,7 +66,11 @@ func (f *JSON) Fprint(writer io.Writer, value interface{}) { _, err := writer.Write(b) if err != nil { - multilog.Error("Could not write json output, error: %v", err) + if isPipeClosedError(err) { + logging.Error("Could not write json output, error: %v", err) // do not log to rollbar + } else { + multilog.Error("Could not write json output, error: %v", err) + } } } @@ -92,8 +99,22 @@ func (f *JSON) Error(value interface{}) { _, err = f.cfg.OutWriter.Write(b) if err != nil { - multilog.Error("Could not write json output, error: %v", err) + if isPipeClosedError(err) { + logging.Error("Could not write json output, error: %v", err) // do not log to rollbar + } else { + multilog.Error("Could not write json output, error: %v", err) + } + } +} + +func isPipeClosedError(err error) bool { + pipeErr := errors.Is(err, syscall.EPIPE) + if runtime.GOOS == "windows" && errors.Is(err, syscall.Errno(242)) { + // Note: 232 is Windows error code ERROR_NO_DATA, "The pipe is being closed". + // See https://go.dev/src/os/pipe_test.go + pipeErr = true } + return pipeErr } // Notice is ignored by JSON, as they are considered as non-critical output and there's currently no reliable way to From 716f1ee3670638e392010cd65b4c269e2ac2d51a Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 30 Oct 2024 09:47:22 -0400 Subject: [PATCH 291/440] Use the correct error code. --- internal/output/json.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/output/json.go b/internal/output/json.go index 1d572c1a42..ad2b8e2939 100644 --- a/internal/output/json.go +++ b/internal/output/json.go @@ -109,7 +109,7 @@ func (f *JSON) Error(value interface{}) { func isPipeClosedError(err error) bool { pipeErr := errors.Is(err, syscall.EPIPE) - if runtime.GOOS == "windows" && errors.Is(err, syscall.Errno(242)) { + if runtime.GOOS == "windows" && errors.Is(err, syscall.Errno(232)) { // Note: 232 is Windows error code ERROR_NO_DATA, "The pipe is being closed". // See https://go.dev/src/os/pipe_test.go pipeErr = true From b18d59a3764a20b8bedbc91f349817d9767c47fe Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 30 Oct 2024 10:16:03 -0400 Subject: [PATCH 292/440] Do not report user-facing errors to Rollbar. --- internal/runbits/errors/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runbits/errors/errors.go b/internal/runbits/errors/errors.go index eb7cd1c865..f60a8e138a 100644 --- a/internal/runbits/errors/errors.go +++ b/internal/runbits/errors/errors.go @@ -225,5 +225,5 @@ func ReportError(err error, cmd *captain.Command, an analytics.Dispatcher) { } func IsReportableError(err error) bool { - return !locale.IsInputError(err) && !errs.IsExternalError(err) && !errs.IsSilent(err) + return !locale.IsInputError(err) && !errs.IsExternalError(err) && !errs.IsSilent(err) && !errs.IsUserFacing(err) } From 6fba38b4b4705742f2f1d1f0d143fb980c41e646 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 30 Oct 2024 13:03:59 -0400 Subject: [PATCH 293/440] Make access denied error an input error. --- pkg/projectfile/yamlfield.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/projectfile/yamlfield.go b/pkg/projectfile/yamlfield.go index 48d829018f..13b5691530 100644 --- a/pkg/projectfile/yamlfield.go +++ b/pkg/projectfile/yamlfield.go @@ -59,7 +59,7 @@ func (y *yamlField) Save(path string) error { if err := os.WriteFile(path, out, 0664); err != nil { if osutils.IsAccessDeniedError(err) { - return locale.WrapError(err, "err_migrate_projectfile_access_denied", + return locale.WrapInputError(err, "err_migrate_projectfile_access_denied", "Your project file at '{{.V0}}' is out of date, but State Tool does not have permission to update it. Please make it writeable or re-checkout the project to a writeable location.", path) } From 4eb18a87937f099f002c340a8c6dd6cb4a81543f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:21:05 -0700 Subject: [PATCH 294/440] Add doublestar support for globbing and fix inconsistencies in hash calculation --- cmd/state-svc/internal/hash/file_hasher.go | 53 +- .../internal/hash/file_hasher_test.go | 108 ++-- go.mod | 3 +- go.sum | 2 + .../bmatcuk/doublestar/v4/.codecov.yml | 10 + .../bmatcuk/doublestar/v4/.gitignore | 32 ++ .../github.com/bmatcuk/doublestar/v4/LICENSE | 22 + .../bmatcuk/doublestar/v4/README.md | 431 ++++++++++++++++ .../bmatcuk/doublestar/v4/UPGRADING.md | 63 +++ .../bmatcuk/doublestar/v4/doublestar.go | 13 + .../github.com/bmatcuk/doublestar/v4/glob.go | 473 ++++++++++++++++++ .../bmatcuk/doublestar/v4/globoptions.go | 144 ++++++ .../bmatcuk/doublestar/v4/globwalk.go | 414 +++++++++++++++ .../github.com/bmatcuk/doublestar/v4/match.go | 403 +++++++++++++++ .../github.com/bmatcuk/doublestar/v4/utils.go | 157 ++++++ .../bmatcuk/doublestar/v4/validate.go | 82 +++ vendor/modules.txt | 3 + 17 files changed, 2350 insertions(+), 63 deletions(-) create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/.codecov.yml create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/.gitignore create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/LICENSE create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/README.md create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/UPGRADING.md create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/doublestar.go create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/glob.go create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/globoptions.go create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/globwalk.go create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/match.go create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/utils.go create mode 100644 vendor/github.com/bmatcuk/doublestar/v4/validate.go diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 94f50b0177..bda4b4c291 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -6,10 +6,11 @@ import ( "os" "path/filepath" "sort" + "sync" "time" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/rtutils" + "github.com/bmatcuk/doublestar/v4" "github.com/cespare/xxhash" "github.com/patrickmn/go-cache" ) @@ -21,6 +22,7 @@ type fileCache interface { type FileHasher struct { cache fileCache + mutex *sync.Mutex } type hashedFile struct { @@ -32,15 +34,16 @@ type hashedFile struct { func NewFileHasher() *FileHasher { return &FileHasher{ cache: cache.New(24*time.Hour, 24*time.Hour), + mutex: &sync.Mutex{}, } } func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashedFile, rerr error) { - sort.Strings(globs) // ensure consistent ordering + fs := os.DirFS(wd) hashedFiles := []hashedFile{} - hasher := xxhash.New() + hashes := []string{} for _, glob := range globs { - files, err := filepath.Glob(glob) + files, err := doublestar.Glob(fs, glob) if err != nil { return "", nil, errs.Wrap(err, "Could not match glob: %s", glob) } @@ -53,19 +56,17 @@ func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashed } f = af } - file, err := os.Open(f) + fileInfo, err := os.Stat(f) if err != nil { - return "", nil, errs.Wrap(err, "Could not open file: %s", file.Name()) + return "", nil, errs.Wrap(err, "Could not stat file: %s", f) } - defer rtutils.Closer(file.Close, &rerr) - fileInfo, err := file.Stat() - if err != nil { - return "", nil, errs.Wrap(err, "Could not stat file: %s", file.Name()) + if fileInfo.IsDir() { + continue } var hash string - cachedHash, ok := fh.cache.Get(cacheKey(file.Name(), fileInfo.ModTime())) + cachedHash, ok := fh.cache.Get(cacheKey(fileInfo.Name(), fileInfo.ModTime())) if ok { hash, ok = cachedHash.(string) if !ok { @@ -73,27 +74,43 @@ func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashed } } else { fileHasher := xxhash.New() + // include filepath in hash, because moving files should affect the hash + fmt.Fprintf(fileHasher, "%016x", f) + file, err := os.Open(f) + if err != nil { + return "", nil, errs.Wrap(err, "Could not open file: %s", f) + } + defer file.Close() if _, err := io.Copy(fileHasher, file); err != nil { - return "", nil, errs.Wrap(err, "Could not hash file: %s", file.Name()) + return "", nil, errs.Wrap(err, "Could not hash file: %s", fileInfo.Name()) } hash = fmt.Sprintf("%016x", fileHasher.Sum64()) } - fh.cache.Set(cacheKey(file.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) + fh.cache.Set(cacheKey(fileInfo.Name(), fileInfo.ModTime()), hash, cache.NoExpiration) + hashes = append(hashes, hash) hashedFiles = append(hashedFiles, hashedFile{ Pattern: glob, - Path: file.Name(), + Path: f, Hash: hash, }) - - // Incorporate the individual file hash into the overall hash in hex format - fmt.Fprintf(hasher, "%016x", hash) } } - return fmt.Sprintf("%016x", hasher.Sum64()), hashedFiles, nil + if hashedFiles == nil { + return "", nil, nil + } + + // Ensure the overall hash is consistently calculated + sort.Slice(hashedFiles, func(i, j int) bool { return hashedFiles[i].Path < hashedFiles[j].Path }) + h := xxhash.New() + for _, f := range hashedFiles { + fmt.Fprintf(h, "%016x", f.Hash) + } + + return fmt.Sprintf("%016x", h.Sum64()), hashedFiles, nil } func cacheKey(file string, modTime time.Time) string { diff --git a/cmd/state-svc/internal/hash/file_hasher_test.go b/cmd/state-svc/internal/hash/file_hasher_test.go index 421b5dff17..1e5ec7cbba 100644 --- a/cmd/state-svc/internal/hash/file_hasher_test.go +++ b/cmd/state-svc/internal/hash/file_hasher_test.go @@ -2,11 +2,17 @@ package hash import ( "os" + "path/filepath" + "sort" "testing" "time" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" + "github.com/ActiveState/cli/internal/osutils" "github.com/patrickmn/go-cache" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type testCache struct { @@ -31,23 +37,35 @@ func (tc *testCache) Set(key string, value interface{}, expiration time.Duration } func TestFileHasher_HashFiles(t *testing.T) { - file1 := createTempFile(t, "file1") - file2 := createTempFile(t, "file2") + dir := fileutils.TempDirUnsafe() + file1 := createTempFile(t, dir, "file1.txt") + file2 := createTempFile(t, dir, "file2.info") + subfile1 := createTempFile(t, dir, "dir1/subfile1.txt") hasher := NewFileHasher() - hash1, err := hasher.HashFiles([]string{file1, file2}) - assert.NoError(t, err) + hash1, files1, err := hasher.HashFiles(dir, []string{file1, file2, subfile1}) + require.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2}) - assert.NoError(t, err) + hash2, files2, err := hasher.HashFiles(dir, []string{"./**/*"}) + require.NoError(t, err, errs.JoinMessage(err)) + + sort.Slice(files1, func(i, j int) bool { return files1[i].Path < files1[j].Path }) + sort.Slice(files2, func(i, j int) bool { return files2[i].Path < files2[j].Path }) + require.Len(t, files2, 3) + require.Len(t, files2, len(files1)) + + for i, f := range files1 { + assert.Equal(t, f.Path, files2[i].Path) + assert.Equal(t, f.Hash, files2[i].Hash) + } assert.Equal(t, hash1, hash2) } func TestFileHasher_CacheHit(t *testing.T) { - file1 := createTempFile(t, "file1") - file2 := createTempFile(t, "file2") + file1 := createTempFile(t, "", "file1") + file2 := createTempFile(t, "", "file2") tc := &testCache{ cache: cache.New(cache.NoExpiration, cache.NoExpiration), @@ -57,10 +75,10 @@ func TestFileHasher_CacheHit(t *testing.T) { cache: tc, } - hash1, err := hasher.HashFiles([]string{file1, file2}) + hash1, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2}) + hash2, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) assert.Equal(t, hash1, hash2) @@ -69,8 +87,8 @@ func TestFileHasher_CacheHit(t *testing.T) { } func TestFileHasher_CacheMiss(t *testing.T) { - file1 := createTempFile(t, "file1") - file2 := createTempFile(t, "file2") + file1 := createTempFile(t, "", "file1") + file2 := createTempFile(t, "", "file2") tc := &testCache{ cache: cache.New(cache.NoExpiration, cache.NoExpiration), @@ -80,7 +98,7 @@ func TestFileHasher_CacheMiss(t *testing.T) { cache: tc, } - hash1, err := hasher.HashFiles([]string{file1, file2}) + hash1, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) if err := os.Chtimes(file1, time.Now(), time.Now()); err != nil { @@ -92,7 +110,7 @@ func TestFileHasher_CacheMiss(t *testing.T) { err = file.Sync() assert.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2}) + hash2, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) assert.Equal(t, hash1, hash2) @@ -102,11 +120,11 @@ func TestFileHasher_CacheMiss(t *testing.T) { func TestFileHasher_ContentAgnostic(t *testing.T) { // Files have same content but different names and modification times - file1 := createTempFile(t, "file1") + file1 := createTempFile(t, "", "file1") // Ensure mod times are different time.Sleep(1 * time.Millisecond) - file2 := createTempFile(t, "file1") + file2 := createTempFile(t, "", "file1") tc := &testCache{ cache: cache.New(cache.NoExpiration, cache.NoExpiration), @@ -116,10 +134,10 @@ func TestFileHasher_ContentAgnostic(t *testing.T) { cache: tc, } - hash1, err := hasher.HashFiles([]string{file1, file2}) + hash1, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2}) + hash2, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) assert.Equal(t, hash1, hash2) @@ -128,9 +146,9 @@ func TestFileHasher_ContentAgnostic(t *testing.T) { } func TestFileHasher_NotEqualFileAdded(t *testing.T) { - file1 := createTempFile(t, "file1") - file2 := createTempFile(t, "file2") - file3 := createTempFile(t, "file3") + file1 := createTempFile(t, "", "file1") + file2 := createTempFile(t, "", "file2") + file3 := createTempFile(t, "", "file3") tc := &testCache{ cache: cache.New(cache.NoExpiration, cache.NoExpiration), @@ -140,10 +158,10 @@ func TestFileHasher_NotEqualFileAdded(t *testing.T) { cache: tc, } - hash1, err := hasher.HashFiles([]string{file1, file2}) + hash1, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2, file3}) + hash2, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2, file3}) assert.NoError(t, err) assert.NotEqual(t, hash1, hash2) @@ -152,9 +170,9 @@ func TestFileHasher_NotEqualFileAdded(t *testing.T) { } func TestFileHasher_NotEqualFileRemoved(t *testing.T) { - file1 := createTempFile(t, "file1") - file2 := createTempFile(t, "file2") - file3 := createTempFile(t, "file3") + file1 := createTempFile(t, "", "file1") + file2 := createTempFile(t, "", "file2") + file3 := createTempFile(t, "", "file3") tc := &testCache{ cache: cache.New(cache.NoExpiration, cache.NoExpiration), @@ -164,10 +182,10 @@ func TestFileHasher_NotEqualFileRemoved(t *testing.T) { cache: tc, } - hash1, err := hasher.HashFiles([]string{file1, file2, file3}) + hash1, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2, file3}) assert.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2}) + hash2, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) assert.NotEqual(t, hash1, hash2) @@ -176,8 +194,8 @@ func TestFileHasher_NotEqualFileRemoved(t *testing.T) { } func TestFileHasher_NotEqualContentChanged(t *testing.T) { - file1 := createTempFile(t, "file1") - file2 := createTempFile(t, "file2") + file1 := createTempFile(t, "", "file1") + file2 := createTempFile(t, "", "file2") tc := &testCache{ cache: cache.New(cache.NoExpiration, cache.NoExpiration), @@ -187,10 +205,10 @@ func TestFileHasher_NotEqualContentChanged(t *testing.T) { cache: tc, } - hash1, err := hasher.HashFiles([]string{file1, file2}) + hash1, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) - hash2, err := hasher.HashFiles([]string{file1, file2}) + hash2, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) assert.Equal(t, hash1, hash2) @@ -203,7 +221,7 @@ func TestFileHasher_NotEqualContentChanged(t *testing.T) { t.Fatal(err) } - hash2Modified, err := hasher.HashFiles([]string{file1, file2}) + hash2Modified, _, err := hasher.HashFiles(osutils.GetwdUnsafe(), []string{file1, file2}) assert.NoError(t, err) assert.NotEqual(t, hash1, hash2Modified) @@ -211,18 +229,20 @@ func TestFileHasher_NotEqualContentChanged(t *testing.T) { assert.Len(t, tc.misses, 3) } -func createTempFile(t *testing.T, content string) string { - tmpfile, err := os.CreateTemp("", "testfile") - if err != nil { - t.Fatal(err) +func createTempFile(t *testing.T, dir, path string) string { + if dir == "" { + dir = t.TempDir() } - - if _, err := tmpfile.Write([]byte(content)); err != nil { - t.Fatal(err) - } - if err := tmpfile.Close(); err != nil { - t.Fatal(err) + if path == "" { + tmpfile, err := os.CreateTemp(dir, "") + if err != nil { + t.Fatal(err) + } + path = tmpfile.Name() + tmpfile.Close() } + err := fileutils.WriteFile(filepath.Join(dir, path), []byte(path)) // Contents aren't important so long as they're consistent + require.NoError(t, err, errs.JoinMessage(err)) - return tmpfile.Name() + return path } diff --git a/go.mod b/go.mod index b512502678..beaf422d8d 100644 --- a/go.mod +++ b/go.mod @@ -70,6 +70,7 @@ require ( require ( github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 + github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/brunoga/deep v1.2.4 github.com/cespare/xxhash v1.1.0 github.com/charmbracelet/bubbles v0.18.0 @@ -143,7 +144,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/labstack/gommon v0.3.1 // indirect + github.com/labstack/gommon v0.3.1 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matryer/is v1.2.0 // indirect diff --git a/go.sum b/go.sum index f68dcce819..f1008087f9 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/brunoga/deep v1.2.4 h1:Aj9E9oUbE+ccbyh35VC/NHlzzjfIVU69BXu2mt2LmL8= github.com/brunoga/deep v1.2.4/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= diff --git a/vendor/github.com/bmatcuk/doublestar/v4/.codecov.yml b/vendor/github.com/bmatcuk/doublestar/v4/.codecov.yml new file mode 100644 index 0000000000..db6e504a9a --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/.codecov.yml @@ -0,0 +1,10 @@ +coverage: + status: + project: + default: + threshold: 1% + patch: + default: + target: 70% +ignore: + - globoptions.go diff --git a/vendor/github.com/bmatcuk/doublestar/v4/.gitignore b/vendor/github.com/bmatcuk/doublestar/v4/.gitignore new file mode 100644 index 0000000000..af212ecc28 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/.gitignore @@ -0,0 +1,32 @@ +# vi +*~ +*.swp +*.swo + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# test directory +test/ diff --git a/vendor/github.com/bmatcuk/doublestar/v4/LICENSE b/vendor/github.com/bmatcuk/doublestar/v4/LICENSE new file mode 100644 index 0000000000..309c9d1d11 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Bob Matcuk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/bmatcuk/doublestar/v4/README.md b/vendor/github.com/bmatcuk/doublestar/v4/README.md new file mode 100644 index 0000000000..21929a9545 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/README.md @@ -0,0 +1,431 @@ +# doublestar + +Path pattern matching and globbing supporting `doublestar` (`**`) patterns. + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/bmatcuk/doublestar)](https://pkg.go.dev/github.com/bmatcuk/doublestar/v4) +[![Release](https://img.shields.io/github/release/bmatcuk/doublestar.svg?branch=master)](https://github.com/bmatcuk/doublestar/releases) +[![Build Status](https://github.com/bmatcuk/doublestar/actions/workflows/test.yml/badge.svg)](https://github.com/bmatcuk/doublestar/actions) +[![codecov.io](https://img.shields.io/codecov/c/github/bmatcuk/doublestar.svg?branch=master)](https://codecov.io/github/bmatcuk/doublestar?branch=master) +[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/bmatcuk) + +## About + +#### [Upgrading?](UPGRADING.md) + +**doublestar** is a [golang] implementation of path pattern matching and +globbing with support for "doublestar" (aka globstar: `**`) patterns. + +doublestar patterns match files and directories recursively. For example, if +you had the following directory structure: + +```bash +grandparent +`-- parent + |-- child1 + `-- child2 +``` + +You could find the children with patterns such as: `**/child*`, +`grandparent/**/child?`, `**/parent/*`, or even just `**` by itself (which will +return all files and directories recursively). + +Bash's globstar is doublestar's inspiration and, as such, works similarly. +Note that the doublestar must appear as a path component by itself. A pattern +such as `/path**` is invalid and will be treated the same as `/path*`, but +`/path*/**` should achieve the desired result. Additionally, `/path/**` will +match all directories and files under the path directory, but `/path/**/` will +only match directories. + +v4 is a complete rewrite with a focus on performance. Additionally, +[doublestar] has been updated to use the new [io/fs] package for filesystem +access. As a result, it is only supported by [golang] v1.16+. + +## Installation + +**doublestar** can be installed via `go get`: + +```bash +go get github.com/bmatcuk/doublestar/v4 +``` + +To use it in your code, you must import it: + +```go +import "github.com/bmatcuk/doublestar/v4" +``` + +## Usage + +### ErrBadPattern + +```go +doublestar.ErrBadPattern +``` + +Returned by various functions to report that the pattern is malformed. At the +moment, this value is equal to `path.ErrBadPattern`, but, for portability, this +equivalence should probably not be relied upon. + +### Match + +```go +func Match(pattern, name string) (bool, error) +``` + +Match returns true if `name` matches the file name `pattern` ([see +"patterns"]). `name` and `pattern` are split on forward slash (`/`) characters +and may be relative or absolute. + +Match requires pattern to match all of name, not just a substring. The only +possible returned error is `ErrBadPattern`, when pattern is malformed. + +Note: this is meant as a drop-in replacement for `path.Match()` which always +uses `'/'` as the path separator. If you want to support systems which use a +different path separator (such as Windows), what you want is `PathMatch()`. +Alternatively, you can run `filepath.ToSlash()` on both pattern and name and +then use this function. + +Note: users should _not_ count on the returned error, +`doublestar.ErrBadPattern`, being equal to `path.ErrBadPattern`. + + +### MatchUnvalidated + +```go +func MatchUnvalidated(pattern, name string) bool +``` + +MatchUnvalidated can provide a small performance improvement if you don't care +about whether or not the pattern is valid (perhaps because you already ran +`ValidatePattern`). Note that there's really only one case where this +performance improvement is realized: when pattern matching reaches the end of +`name` before reaching the end of `pattern`, such as `Match("a/b/c", "a")`. + + +### PathMatch + +```go +func PathMatch(pattern, name string) (bool, error) +``` + +PathMatch returns true if `name` matches the file name `pattern` ([see +"patterns"]). The difference between Match and PathMatch is that PathMatch will +automatically use your system's path separator to split `name` and `pattern`. +On systems where the path separator is `'\'`, escaping will be disabled. + +Note: this is meant as a drop-in replacement for `filepath.Match()`. It assumes +that both `pattern` and `name` are using the system's path separator. If you +can't be sure of that, use `filepath.ToSlash()` on both `pattern` and `name`, +and then use the `Match()` function instead. + + +### PathMatchUnvalidated + +```go +func PathMatchUnvalidated(pattern, name string) bool +``` + +PathMatchUnvalidated can provide a small performance improvement if you don't +care about whether or not the pattern is valid (perhaps because you already ran +`ValidatePattern`). Note that there's really only one case where this +performance improvement is realized: when pattern matching reaches the end of +`name` before reaching the end of `pattern`, such as `Match("a/b/c", "a")`. + + +### GlobOption + +Options that may be passed to `Glob`, `GlobWalk`, or `FilepathGlob`. Any number +of options may be passed to these functions, and in any order, as the last +argument(s). + +```go +WithFailOnIOErrors() +``` + +If passed, doublestar will abort and return IO errors when encountered. Note +that if the glob pattern references a path that does not exist (such as +`nonexistent/path/*`), this is _not_ considered an IO error: it is considered a +pattern with no matches. + +```go +WithFailOnPatternNotExist() +``` + +If passed, doublestar will abort and return `doublestar.ErrPatternNotExist` if +the pattern references a path that does not exist before any meta characters +such as `nonexistent/path/*`. Note that alts (ie, `{...}`) are expanded before +this check. In other words, a pattern such as `{a,b}/*` may fail if either `a` +or `b` do not exist but `*/{a,b}` will never fail because the star may match +nothing. + +```go +WithFilesOnly() +``` + +If passed, doublestar will only return "files" from `Glob`, `GlobWalk`, or +`FilepathGlob`. In this context, "files" are anything that is not a directory +or a symlink to a directory. + +Note: if combined with the WithNoFollow option, symlinks to directories _will_ +be included in the result since no attempt is made to follow the symlink. + +```go +WithNoFollow() +``` + +If passed, doublestar will not follow symlinks while traversing the filesystem. +However, due to io/fs's _very_ poor support for querying the filesystem about +symlinks, there's a caveat here: if part of the pattern before any meta +characters contains a reference to a symlink, it will be followed. For example, +a pattern such as `path/to/symlink/*` will be followed assuming it is a valid +symlink to a directory. However, from this same example, a pattern such as +`path/to/**` will not traverse the `symlink`, nor would `path/*/symlink/*` + +Note: if combined with the WithFilesOnly option, symlinks to directories _will_ +be included in the result since no attempt is made to follow the symlink. + +### Glob + +```go +func Glob(fsys fs.FS, pattern string, opts ...GlobOption) ([]string, error) +``` + +Glob returns the names of all files matching pattern or nil if there is no +matching file. The syntax of patterns is the same as in `Match()`. The pattern +may describe hierarchical names such as `usr/*/bin/ed`. + +Glob ignores file system errors such as I/O errors reading directories by +default. The only possible returned error is `ErrBadPattern`, reporting that +the pattern is malformed. + +To enable aborting on I/O errors, the `WithFailOnIOErrors` option can be +passed. + +Note: this is meant as a drop-in replacement for `io/fs.Glob()`. Like +`io/fs.Glob()`, this function assumes that your pattern uses `/` as the path +separator even if that's not correct for your OS (like Windows). If you aren't +sure if that's the case, you can use `filepath.ToSlash()` on your pattern +before calling `Glob()`. + +Like `io/fs.Glob()`, patterns containing `/./`, `/../`, or starting with `/` +will return no results and no errors. This seems to be a [conscious +decision](https://github.com/golang/go/issues/44092#issuecomment-774132549), +even if counter-intuitive. You can use [SplitPattern] to divide a pattern into +a base path (to initialize an `FS` object) and pattern. + +Note: users should _not_ count on the returned error, +`doublestar.ErrBadPattern`, being equal to `path.ErrBadPattern`. + +### GlobWalk + +```go +type GlobWalkFunc func(path string, d fs.DirEntry) error + +func GlobWalk(fsys fs.FS, pattern string, fn GlobWalkFunc, opts ...GlobOption) error +``` + +GlobWalk calls the callback function `fn` for every file matching pattern. The +syntax of pattern is the same as in Match() and the behavior is the same as +Glob(), with regard to limitations (such as patterns containing `/./`, `/../`, +or starting with `/`). The pattern may describe hierarchical names such as +usr/*/bin/ed. + +GlobWalk may have a small performance benefit over Glob if you do not need a +slice of matches because it can avoid allocating memory for the matches. +Additionally, GlobWalk gives you access to the `fs.DirEntry` objects for each +match, and lets you quit early by returning a non-nil error from your callback +function. Like `io/fs.WalkDir`, if your callback returns `SkipDir`, GlobWalk +will skip the current directory. This means that if the current path _is_ a +directory, GlobWalk will not recurse into it. If the current path is not a +directory, the rest of the parent directory will be skipped. + +GlobWalk ignores file system errors such as I/O errors reading directories by +default. GlobWalk may return `ErrBadPattern`, reporting that the pattern is +malformed. + +To enable aborting on I/O errors, the `WithFailOnIOErrors` option can be +passed. + +Additionally, if the callback function `fn` returns an error, GlobWalk will +exit immediately and return that error. + +Like Glob(), this function assumes that your pattern uses `/` as the path +separator even if that's not correct for your OS (like Windows). If you aren't +sure if that's the case, you can use filepath.ToSlash() on your pattern before +calling GlobWalk(). + +Note: users should _not_ count on the returned error, +`doublestar.ErrBadPattern`, being equal to `path.ErrBadPattern`. + +### FilepathGlob + +```go +func FilepathGlob(pattern string, opts ...GlobOption) (matches []string, err error) +``` + +FilepathGlob returns the names of all files matching pattern or nil if there is +no matching file. The syntax of pattern is the same as in Match(). The pattern +may describe hierarchical names such as usr/*/bin/ed. + +FilepathGlob ignores file system errors such as I/O errors reading directories +by default. The only possible returned error is `ErrBadPattern`, reporting that +the pattern is malformed. + +To enable aborting on I/O errors, the `WithFailOnIOErrors` option can be +passed. + +Note: FilepathGlob is a convenience function that is meant as a drop-in +replacement for `path/filepath.Glob()` for users who don't need the +complication of io/fs. Basically, it: + +* Runs `filepath.Clean()` and `ToSlash()` on the pattern +* Runs `SplitPattern()` to get a base path and a pattern to Glob +* Creates an FS object from the base path and `Glob()s` on the pattern +* Joins the base path with all of the matches from `Glob()` + +Returned paths will use the system's path separator, just like +`filepath.Glob()`. + +Note: the returned error `doublestar.ErrBadPattern` is not equal to +`filepath.ErrBadPattern`. + +### SplitPattern + +```go +func SplitPattern(p string) (base, pattern string) +``` + +SplitPattern is a utility function. Given a pattern, SplitPattern will return +two strings: the first string is everything up to the last slash (`/`) that +appears _before_ any unescaped "meta" characters (ie, `*?[{`). The second +string is everything after that slash. For example, given the pattern: + +``` +../../path/to/meta*/** + ^----------- split here +``` + +SplitPattern returns "../../path/to" and "meta*/**". This is useful for +initializing os.DirFS() to call Glob() because Glob() will silently fail if +your pattern includes `/./` or `/../`. For example: + +```go +base, pattern := SplitPattern("../../path/to/meta*/**") +fsys := os.DirFS(base) +matches, err := Glob(fsys, pattern) +``` + +If SplitPattern cannot find somewhere to split the pattern (for example, +`meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in this +example). + +Of course, it is your responsibility to decide if the returned base path is +"safe" in the context of your application. Perhaps you could use Match() to +validate against a list of approved base directories? + +### ValidatePattern + +```go +func ValidatePattern(s string) bool +``` + +Validate a pattern. Patterns are validated while they run in Match(), +PathMatch(), and Glob(), so, you normally wouldn't need to call this. However, +there are cases where this might be useful: for example, if your program allows +a user to enter a pattern that you'll run at a later time, you might want to +validate it. + +ValidatePattern assumes your pattern uses '/' as the path separator. + +### ValidatePathPattern + +```go +func ValidatePathPattern(s string) bool +``` + +Like ValidatePattern, only uses your OS path separator. In other words, use +ValidatePattern if you would normally use Match() or Glob(). Use +ValidatePathPattern if you would normally use PathMatch(). Keep in mind, Glob() +requires '/' separators, even if your OS uses something else. + +### Patterns + +**doublestar** supports the following special terms in the patterns: + +Special Terms | Meaning +------------- | ------- +`*` | matches any sequence of non-path-separators +`/**/` | matches zero or more directories +`?` | matches any single non-path-separator character +`[class]` | matches any single non-path-separator character against a class of characters ([see "character classes"]) +`{alt1,...}` | matches a sequence of characters if one of the comma-separated alternatives matches + +Any character with a special meaning can be escaped with a backslash (`\`). + +A doublestar (`**`) should appear surrounded by path separators such as `/**/`. +A mid-pattern doublestar (`**`) behaves like bash's globstar option: a pattern +such as `path/to/**.txt` would return the same results as `path/to/*.txt`. The +pattern you're looking for is `path/to/**/*.txt`. + +#### Character Classes + +Character classes support the following: + +Class | Meaning +---------- | ------- +`[abc]` | matches any single character within the set +`[a-z]` | matches any single character in the range +`[^class]` | matches any single character which does *not* match the class +`[!class]` | same as `^`: negates the class + +## Performance + +``` +goos: darwin +goarch: amd64 +pkg: github.com/bmatcuk/doublestar/v4 +cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz +BenchmarkMatch-8 285639 3868 ns/op 0 B/op 0 allocs/op +BenchmarkGoMatch-8 286945 3726 ns/op 0 B/op 0 allocs/op +BenchmarkPathMatch-8 320511 3493 ns/op 0 B/op 0 allocs/op +BenchmarkGoPathMatch-8 304236 3434 ns/op 0 B/op 0 allocs/op +BenchmarkGlob-8 466 2501123 ns/op 190225 B/op 2849 allocs/op +BenchmarkGlobWalk-8 476 2536293 ns/op 184017 B/op 2750 allocs/op +BenchmarkGoGlob-8 463 2574836 ns/op 194249 B/op 2929 allocs/op +``` + +These benchmarks (in `doublestar_test.go`) compare Match() to path.Match(), +PathMath() to filepath.Match(), and Glob() + GlobWalk() to io/fs.Glob(). They +only run patterns that the standard go packages can understand as well (so, no +`{alts}` or `**`) for a fair comparison. Of course, alts and doublestars will +be less performant than the other pattern meta characters. + +Alts are essentially like running multiple patterns, the number of which can +get large if your pattern has alts nested inside alts. This affects both +matching (ie, Match()) and globbing (Glob()). + +`**` performance in matching is actually pretty similar to a regular `*`, but +can cause a large number of reads when globbing as it will need to recursively +traverse your filesystem. + +## Sponsors +I started this project in 2014 in my spare time and have been maintaining it +ever since. In that time, it has grown into one of the most popular globbing +libraries in the Go ecosystem. So, if **doublestar** is a useful library in +your project, consider [sponsoring] my work! I'd really appreciate it! + +[![MASV](../sponsors/MASV.png?raw=true)](https://massive.io/) + +Thanks for sponsoring me! + +## License + +[MIT License](LICENSE) + +[SplitPattern]: #splitpattern +[doublestar]: https://github.com/bmatcuk/doublestar +[golang]: http://golang.org/ +[io/fs]: https://pkg.go.dev/io/fs +[see "character classes"]: #character-classes +[see "patterns"]: #patterns +[sponsoring]: https://github.com/sponsors/bmatcuk diff --git a/vendor/github.com/bmatcuk/doublestar/v4/UPGRADING.md b/vendor/github.com/bmatcuk/doublestar/v4/UPGRADING.md new file mode 100644 index 0000000000..25aace3db0 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/UPGRADING.md @@ -0,0 +1,63 @@ +# Upgrading from v3 to v4 + +v4 is a complete rewrite with a focus on performance. Additionally, +[doublestar] has been updated to use the new [io/fs] package for filesystem +access. As a result, it is only supported by [golang] v1.16+. + +`Match()` and `PathMatch()` mostly did not change, besides big performance +improvements. Their API is the same. However, note the following corner cases: + +* In previous versions of [doublestar], `PathMatch()` could accept patterns + that used either platform-specific path separators, or `/`. This was + undocumented and didn't match `filepath.Match()`. In v4, both `pattern` and + `name` must be using appropriate path separators for the platform. You can + use `filepath.FromSlash()` to change `/` to platform-specific separators if + you aren't sure. +* In previous versions of [doublestar], a pattern such as `path/to/a/**` would + _not_ match `path/to/a`. In v4, this pattern _will_ match because if `a` was + a directory, `Glob()` would return it. In other words, the following returns + true: `Match("path/to/a/**", "path/to/a")` + +`Glob()` changed from using a [doublestar]-specific filesystem abstraction (the +`OS` interface) to the [io/fs] package. As a result, it now takes a `fs.FS` as +its first argument. This change has a couple ramifications: + +* Like `io/fs.Glob`, `pattern` must use a `/` as path separator, even on + platforms that use something else. You can use `filepath.ToSlash()` on your + patterns if you aren't sure. +* Patterns that contain `/./` or `/../` are invalid. The [io/fs] package + rejects them, returning an IO error. Since `Glob()` ignores IO errors, it'll + end up being silently rejected. You can run `path.Clean()` to ensure they are + removed from the pattern. + +v4 also added a `GlobWalk()` function that is slightly more performant than +`Glob()` if you just need to iterate over the results and don't need a string +slice. You also get `fs.DirEntry` objects for each result, and can quit early +if your callback returns an error. + +# Upgrading from v2 to v3 + +v3 introduced using `!` to negate character classes, in addition to `^`. If any +of your patterns include a character class that starts with an exclamation mark +(ie, `[!...]`), you'll need to update the pattern to escape or move the +exclamation mark. Note that, like the caret (`^`), it only negates the +character class if it is the first character in the character class. + +# Upgrading from v1 to v2 + +The change from v1 to v2 was fairly minor: the return type of the `Open` method +on the `OS` interface was changed from `*os.File` to `File`, a new interface +exported by doublestar. The new `File` interface only defines the functionality +doublestar actually needs (`io.Closer` and `Readdir`), making it easier to use +doublestar with [go-billy], [afero], or something similar. If you were using +this functionality, updating should be as easy as updating `Open's` return +type, since `os.File` already implements `doublestar.File`. + +If you weren't using this functionality, updating should be as easy as changing +your dependencies to point to v2. + +[afero]: https://github.com/spf13/afero +[doublestar]: https://github.com/bmatcuk/doublestar +[go-billy]: https://github.com/src-d/go-billy +[golang]: http://golang.org/ +[io/fs]: https://golang.org/pkg/io/fs/ diff --git a/vendor/github.com/bmatcuk/doublestar/v4/doublestar.go b/vendor/github.com/bmatcuk/doublestar/v4/doublestar.go new file mode 100644 index 0000000000..210fd40ceb --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/doublestar.go @@ -0,0 +1,13 @@ +package doublestar + +import ( + "errors" + "path" +) + +// ErrBadPattern indicates a pattern was malformed. +var ErrBadPattern = path.ErrBadPattern + +// ErrPatternNotExist indicates that the pattern passed to Glob, GlobWalk, or +// FilepathGlob references a path that does not exist. +var ErrPatternNotExist = errors.New("pattern does not exist") diff --git a/vendor/github.com/bmatcuk/doublestar/v4/glob.go b/vendor/github.com/bmatcuk/doublestar/v4/glob.go new file mode 100644 index 0000000000..519601b15c --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/glob.go @@ -0,0 +1,473 @@ +package doublestar + +import ( + "errors" + "io/fs" + "path" +) + +// Glob returns the names of all files matching pattern or nil if there is no +// matching file. The syntax of pattern is the same as in Match(). The pattern +// may describe hierarchical names such as usr/*/bin/ed. +// +// Glob ignores file system errors such as I/O errors reading directories by +// default. The only possible returned error is ErrBadPattern, reporting that +// the pattern is malformed. +// +// To enable aborting on I/O errors, the WithFailOnIOErrors option can be +// passed. +// +// Note: this is meant as a drop-in replacement for io/fs.Glob(). Like +// io/fs.Glob(), this function assumes that your pattern uses `/` as the path +// separator even if that's not correct for your OS (like Windows). If you +// aren't sure if that's the case, you can use filepath.ToSlash() on your +// pattern before calling Glob(). +// +// Like `io/fs.Glob()`, patterns containing `/./`, `/../`, or starting with `/` +// will return no results and no errors. You can use SplitPattern to divide a +// pattern into a base path (to initialize an `FS` object) and pattern. +// +// Note: users should _not_ count on the returned error, +// doublestar.ErrBadPattern, being equal to path.ErrBadPattern. +// +func Glob(fsys fs.FS, pattern string, opts ...GlobOption) ([]string, error) { + if !ValidatePattern(pattern) { + return nil, ErrBadPattern + } + + g := newGlob(opts...) + + if hasMidDoubleStar(pattern) { + // If the pattern has a `**` anywhere but the very end, GlobWalk is more + // performant because it can get away with less allocations. If the pattern + // ends in a `**`, both methods are pretty much the same, but Glob has a + // _very_ slight advantage because of lower function call overhead. + var matches []string + err := g.doGlobWalk(fsys, pattern, true, true, func(p string, d fs.DirEntry) error { + matches = append(matches, p) + return nil + }) + return matches, err + } + return g.doGlob(fsys, pattern, nil, true, true) +} + +// Does the actual globbin' +// - firstSegment is true if we're in the first segment of the pattern, ie, +// the right-most part where we can match files. If it's false, we're +// somewhere in the middle (or at the beginning) and can only match +// directories since there are path segments above us. +// - beforeMeta is true if we're exploring segments before any meta +// characters, ie, in a pattern such as `path/to/file*.txt`, the `path/to/` +// bit does not contain any meta characters. +func (g *glob) doGlob(fsys fs.FS, pattern string, m []string, firstSegment, beforeMeta bool) (matches []string, err error) { + matches = m + patternStart := indexMeta(pattern) + if patternStart == -1 { + // pattern doesn't contain any meta characters - does a file matching the + // pattern exist? + // The pattern may contain escaped wildcard characters for an exact path match. + path := unescapeMeta(pattern) + pathInfo, pathExists, pathErr := g.exists(fsys, path, beforeMeta) + if pathErr != nil { + return nil, pathErr + } + + if pathExists && (!firstSegment || !g.filesOnly || !pathInfo.IsDir()) { + matches = append(matches, path) + } + + return + } + + dir := "." + splitIdx := lastIndexSlashOrAlt(pattern) + if splitIdx != -1 { + if pattern[splitIdx] == '}' { + openingIdx := indexMatchedOpeningAlt(pattern[:splitIdx]) + if openingIdx == -1 { + // if there's no matching opening index, technically Match() will treat + // an unmatched `}` as nothing special, so... we will, too! + splitIdx = lastIndexSlash(pattern[:splitIdx]) + if splitIdx != -1 { + dir = pattern[:splitIdx] + pattern = pattern[splitIdx+1:] + } + } else { + // otherwise, we have to handle the alts: + return g.globAlts(fsys, pattern, openingIdx, splitIdx, matches, firstSegment, beforeMeta) + } + } else { + dir = pattern[:splitIdx] + pattern = pattern[splitIdx+1:] + } + } + + // if `splitIdx` is less than `patternStart`, we know `dir` has no meta + // characters. They would be equal if they are both -1, which means `dir` + // will be ".", and we know that doesn't have meta characters either. + if splitIdx <= patternStart { + return g.globDir(fsys, dir, pattern, matches, firstSegment, beforeMeta) + } + + var dirs []string + dirs, err = g.doGlob(fsys, dir, matches, false, beforeMeta) + if err != nil { + return + } + for _, d := range dirs { + matches, err = g.globDir(fsys, d, pattern, matches, firstSegment, false) + if err != nil { + return + } + } + + return +} + +// handle alts in the glob pattern - `openingIdx` and `closingIdx` are the +// indexes of `{` and `}`, respectively +func (g *glob) globAlts(fsys fs.FS, pattern string, openingIdx, closingIdx int, m []string, firstSegment, beforeMeta bool) (matches []string, err error) { + matches = m + + var dirs []string + startIdx := 0 + afterIdx := closingIdx + 1 + splitIdx := lastIndexSlashOrAlt(pattern[:openingIdx]) + if splitIdx == -1 || pattern[splitIdx] == '}' { + // no common prefix + dirs = []string{""} + } else { + // our alts have a common prefix that we can process first + dirs, err = g.doGlob(fsys, pattern[:splitIdx], matches, false, beforeMeta) + if err != nil { + return + } + + startIdx = splitIdx + 1 + } + + for _, d := range dirs { + patIdx := openingIdx + 1 + altResultsStartIdx := len(matches) + thisResultStartIdx := altResultsStartIdx + for patIdx < closingIdx { + nextIdx := indexNextAlt(pattern[patIdx:closingIdx], true) + if nextIdx == -1 { + nextIdx = closingIdx + } else { + nextIdx += patIdx + } + + alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx) + matches, err = g.doGlob(fsys, alt, matches, firstSegment, beforeMeta) + if err != nil { + return + } + + matchesLen := len(matches) + if altResultsStartIdx != thisResultStartIdx && thisResultStartIdx != matchesLen { + // Alts can result in matches that aren't sorted, or, worse, duplicates + // (consider the trivial pattern `path/to/{a,*}`). Since doGlob returns + // sorted results, we can do a sort of in-place merge and remove + // duplicates. But, we only need to do this if this isn't the first alt + // (ie, `altResultsStartIdx != thisResultsStartIdx`) and if the latest + // alt actually added some matches (`thisResultStartIdx != + // len(matches)`) + matches = sortAndRemoveDups(matches, altResultsStartIdx, thisResultStartIdx, matchesLen) + + // length of matches may have changed + thisResultStartIdx = len(matches) + } else { + thisResultStartIdx = matchesLen + } + + patIdx = nextIdx + 1 + } + } + + return +} + +// find files/subdirectories in the given `dir` that match `pattern` +func (g *glob) globDir(fsys fs.FS, dir, pattern string, matches []string, canMatchFiles, beforeMeta bool) (m []string, e error) { + m = matches + + if pattern == "" { + if !canMatchFiles || !g.filesOnly { + // pattern can be an empty string if the original pattern ended in a + // slash, in which case, we should just return dir, but only if it + // actually exists and it's a directory (or a symlink to a directory) + _, isDir, err := g.isPathDir(fsys, dir, beforeMeta) + if err != nil { + return nil, err + } + if isDir { + m = append(m, dir) + } + } + return + } + + if pattern == "**" { + return g.globDoubleStar(fsys, dir, m, canMatchFiles, beforeMeta) + } + + dirs, err := fs.ReadDir(fsys, dir) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + e = g.handlePatternNotExist(beforeMeta) + } else { + e = g.forwardErrIfFailOnIOErrors(err) + } + return + } + + var matched bool + for _, info := range dirs { + name := info.Name() + matched, e = matchWithSeparator(pattern, name, '/', false) + if e != nil { + return + } + if matched { + matched = canMatchFiles + if !matched || g.filesOnly { + matched, e = g.isDir(fsys, dir, name, info) + if e != nil { + return + } + if canMatchFiles { + // if we're here, it's because g.filesOnly + // is set and we don't want directories + matched = !matched + } + } + if matched { + m = append(m, path.Join(dir, name)) + } + } + } + + return +} + +func (g *glob) globDoubleStar(fsys fs.FS, dir string, matches []string, canMatchFiles, beforeMeta bool) ([]string, error) { + dirs, err := fs.ReadDir(fsys, dir) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return matches, g.handlePatternNotExist(beforeMeta) + } else { + return matches, g.forwardErrIfFailOnIOErrors(err) + } + } + + if !g.filesOnly { + // `**` can match *this* dir, so add it + matches = append(matches, dir) + } + + for _, info := range dirs { + name := info.Name() + isDir, err := g.isDir(fsys, dir, name, info) + if err != nil { + return nil, err + } + if isDir { + matches, err = g.globDoubleStar(fsys, path.Join(dir, name), matches, canMatchFiles, false) + if err != nil { + return nil, err + } + } else if canMatchFiles { + matches = append(matches, path.Join(dir, name)) + } + } + + return matches, nil +} + +// Returns true if the pattern has a doublestar in the middle of the pattern. +// In this case, GlobWalk is faster because it can get away with less +// allocations. However, Glob has a _very_ slight edge if the pattern ends in +// `**`. +func hasMidDoubleStar(p string) bool { + // subtract 3: 2 because we want to return false if the pattern ends in `**` + // (Glob is _very_ slightly faster in that case), and the extra 1 because our + // loop checks p[i] and p[i+1]. + l := len(p) - 3 + for i := 0; i < l; i++ { + if p[i] == '\\' { + // escape next byte + i++ + } else if p[i] == '*' && p[i+1] == '*' { + return true + } + } + return false +} + +// Returns the index of the first unescaped meta character, or negative 1. +func indexMeta(s string) int { + var c byte + l := len(s) + for i := 0; i < l; i++ { + c = s[i] + if c == '*' || c == '?' || c == '[' || c == '{' { + return i + } else if c == '\\' { + // skip next byte + i++ + } + } + return -1 +} + +// Returns the index of the last unescaped slash or closing alt (`}`) in the +// string, or negative 1. +func lastIndexSlashOrAlt(s string) int { + for i := len(s) - 1; i >= 0; i-- { + if (s[i] == '/' || s[i] == '}') && (i == 0 || s[i-1] != '\\') { + return i + } + } + return -1 +} + +// Returns the index of the last unescaped slash in the string, or negative 1. +func lastIndexSlash(s string) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == '/' && (i == 0 || s[i-1] != '\\') { + return i + } + } + return -1 +} + +// Assuming the byte after the end of `s` is a closing `}`, this function will +// find the index of the matching `{`. That is, it'll skip over any nested `{}` +// and account for escaping. +func indexMatchedOpeningAlt(s string) int { + alts := 1 + for i := len(s) - 1; i >= 0; i-- { + if s[i] == '}' && (i == 0 || s[i-1] != '\\') { + alts++ + } else if s[i] == '{' && (i == 0 || s[i-1] != '\\') { + if alts--; alts == 0 { + return i + } + } + } + return -1 +} + +// Returns true if the path exists +func (g *glob) exists(fsys fs.FS, name string, beforeMeta bool) (fs.FileInfo, bool, error) { + // name might end in a slash, but Stat doesn't like that + namelen := len(name) + if namelen > 1 && name[namelen-1] == '/' { + name = name[:namelen-1] + } + + info, err := fs.Stat(fsys, name) + if errors.Is(err, fs.ErrNotExist) { + return nil, false, g.handlePatternNotExist(beforeMeta) + } + return info, err == nil, g.forwardErrIfFailOnIOErrors(err) +} + +// Returns true if the path exists and is a directory or a symlink to a +// directory +func (g *glob) isPathDir(fsys fs.FS, name string, beforeMeta bool) (fs.FileInfo, bool, error) { + info, err := fs.Stat(fsys, name) + if errors.Is(err, fs.ErrNotExist) { + return nil, false, g.handlePatternNotExist(beforeMeta) + } + return info, err == nil && info.IsDir(), g.forwardErrIfFailOnIOErrors(err) +} + +// Returns whether or not the given DirEntry is a directory. If the DirEntry +// represents a symbolic link, the link is followed by running fs.Stat() on +// `path.Join(dir, name)` (if dir is "", name will be used without joining) +func (g *glob) isDir(fsys fs.FS, dir, name string, info fs.DirEntry) (bool, error) { + if !g.noFollow && (info.Type()&fs.ModeSymlink) > 0 { + p := name + if dir != "" { + p = path.Join(dir, name) + } + finfo, err := fs.Stat(fsys, p) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + // this function is only ever called while expanding a glob, so it can + // never return ErrPatternNotExist + return false, nil + } + return false, g.forwardErrIfFailOnIOErrors(err) + } + return finfo.IsDir(), nil + } + return info.IsDir(), nil +} + +// Builds a string from an alt +func buildAlt(prefix, pattern string, startIdx, openingIdx, currentIdx, nextIdx, afterIdx int) string { + // pattern: + // ignored/start{alts,go,here}remaining - len = 36 + // | | | | ^--- afterIdx = 27 + // | | | \--------- nextIdx = 21 + // | | \----------- currentIdx = 19 + // | \----------------- openingIdx = 13 + // \---------------------- startIdx = 8 + // + // result: + // prefix/startgoremaining - len = 7 + 5 + 2 + 9 = 23 + var buf []byte + patLen := len(pattern) + size := (openingIdx - startIdx) + (nextIdx - currentIdx) + (patLen - afterIdx) + if prefix != "" && prefix != "." { + buf = make([]byte, 0, size+len(prefix)+1) + buf = append(buf, prefix...) + buf = append(buf, '/') + } else { + buf = make([]byte, 0, size) + } + buf = append(buf, pattern[startIdx:openingIdx]...) + buf = append(buf, pattern[currentIdx:nextIdx]...) + if afterIdx < patLen { + buf = append(buf, pattern[afterIdx:]...) + } + return string(buf) +} + +// Running alts can produce results that are not sorted, and, worse, can cause +// duplicates (consider the trivial pattern `path/to/{a,*}`). Since we know +// each run of doGlob is sorted, we can basically do the "merge" step of a +// merge sort in-place. +func sortAndRemoveDups(matches []string, idx1, idx2, l int) []string { + var tmp string + for ; idx1 < idx2; idx1++ { + if matches[idx1] < matches[idx2] { + // order is correct + continue + } else if matches[idx1] > matches[idx2] { + // need to swap and then re-sort matches above idx2 + tmp = matches[idx1] + matches[idx1] = matches[idx2] + + shft := idx2 + 1 + for ; shft < l && matches[shft] < tmp; shft++ { + matches[shft-1] = matches[shft] + } + matches[shft-1] = tmp + } else { + // duplicate - shift matches above idx2 down one and decrement l + for shft := idx2 + 1; shft < l; shft++ { + matches[shft-1] = matches[shft] + } + if l--; idx2 == l { + // nothing left to do... matches[idx2:] must have been full of dups + break + } + } + } + return matches[:l] +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go b/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go new file mode 100644 index 0000000000..9483c4bb00 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/globoptions.go @@ -0,0 +1,144 @@ +package doublestar + +import "strings" + +// glob is an internal type to store options during globbing. +type glob struct { + failOnIOErrors bool + failOnPatternNotExist bool + filesOnly bool + noFollow bool +} + +// GlobOption represents a setting that can be passed to Glob, GlobWalk, and +// FilepathGlob. +type GlobOption func(*glob) + +// Construct a new glob object with the given options +func newGlob(opts ...GlobOption) *glob { + g := &glob{} + for _, opt := range opts { + opt(g) + } + return g +} + +// WithFailOnIOErrors is an option that can be passed to Glob, GlobWalk, or +// FilepathGlob. If passed, doublestar will abort and return IO errors when +// encountered. Note that if the glob pattern references a path that does not +// exist (such as `nonexistent/path/*`), this is _not_ considered an IO error: +// it is considered a pattern with no matches. +// +func WithFailOnIOErrors() GlobOption { + return func(g *glob) { + g.failOnIOErrors = true + } +} + +// WithFailOnPatternNotExist is an option that can be passed to Glob, GlobWalk, +// or FilepathGlob. If passed, doublestar will abort and return +// ErrPatternNotExist if the pattern references a path that does not exist +// before any meta charcters such as `nonexistent/path/*`. Note that alts (ie, +// `{...}`) are expanded before this check. In other words, a pattern such as +// `{a,b}/*` may fail if either `a` or `b` do not exist but `*/{a,b}` will +// never fail because the star may match nothing. +// +func WithFailOnPatternNotExist() GlobOption { + return func(g *glob) { + g.failOnPatternNotExist = true + } +} + +// WithFilesOnly is an option that can be passed to Glob, GlobWalk, or +// FilepathGlob. If passed, doublestar will only return files that match the +// pattern, not directories. +// +// Note: if combined with the WithNoFollow option, symlinks to directories +// _will_ be included in the result since no attempt is made to follow the +// symlink. +// +func WithFilesOnly() GlobOption { + return func(g *glob) { + g.filesOnly = true + } +} + +// WithNoFollow is an option that can be passed to Glob, GlobWalk, or +// FilepathGlob. If passed, doublestar will not follow symlinks while +// traversing the filesystem. However, due to io/fs's _very_ poor support for +// querying the filesystem about symlinks, there's a caveat here: if part of +// the pattern before any meta characters contains a reference to a symlink, it +// will be followed. For example, a pattern such as `path/to/symlink/*` will be +// followed assuming it is a valid symlink to a directory. However, from this +// same example, a pattern such as `path/to/**` will not traverse the +// `symlink`, nor would `path/*/symlink/*` +// +// Note: if combined with the WithFilesOnly option, symlinks to directories +// _will_ be included in the result since no attempt is made to follow the +// symlink. +// +func WithNoFollow() GlobOption { + return func(g *glob) { + g.noFollow = true + } +} + +// forwardErrIfFailOnIOErrors is used to wrap the return values of I/O +// functions. When failOnIOErrors is enabled, it will return err; otherwise, it +// always returns nil. +// +func (g *glob) forwardErrIfFailOnIOErrors(err error) error { + if g.failOnIOErrors { + return err + } + return nil +} + +// handleErrNotExist handles fs.ErrNotExist errors. If +// WithFailOnPatternNotExist has been enabled and canFail is true, this will +// return ErrPatternNotExist. Otherwise, it will return nil. +// +func (g *glob) handlePatternNotExist(canFail bool) error { + if canFail && g.failOnPatternNotExist { + return ErrPatternNotExist + } + return nil +} + +// Format options for debugging/testing purposes +func (g *glob) GoString() string { + var b strings.Builder + b.WriteString("opts: ") + + hasOpts := false + if g.failOnIOErrors { + b.WriteString("WithFailOnIOErrors") + hasOpts = true + } + if g.failOnPatternNotExist { + if hasOpts { + b.WriteString(", ") + } + b.WriteString("WithFailOnPatternNotExist") + hasOpts = true + } + if g.filesOnly { + if hasOpts { + b.WriteString(", ") + } + b.WriteString("WithFilesOnly") + hasOpts = true + } + if g.noFollow { + if hasOpts { + b.WriteString(", ") + } + b.WriteString("WithNoFollow") + hasOpts = true + } + + if !hasOpts { + b.WriteString("nil") + } + return b.String() +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go b/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go new file mode 100644 index 0000000000..84e764f0e2 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/globwalk.go @@ -0,0 +1,414 @@ +package doublestar + +import ( + "errors" + "io/fs" + "path" + "path/filepath" + "strings" +) + +// If returned from GlobWalkFunc, will cause GlobWalk to skip the current +// directory. In other words, if the current path is a directory, GlobWalk will +// not recurse into it. Otherwise, GlobWalk will skip the rest of the current +// directory. +var SkipDir = fs.SkipDir + +// Callback function for GlobWalk(). If the function returns an error, GlobWalk +// will end immediately and return the same error. +type GlobWalkFunc func(path string, d fs.DirEntry) error + +// GlobWalk calls the callback function `fn` for every file matching pattern. +// The syntax of pattern is the same as in Match() and the behavior is the same +// as Glob(), with regard to limitations (such as patterns containing `/./`, +// `/../`, or starting with `/`). The pattern may describe hierarchical names +// such as usr/*/bin/ed. +// +// GlobWalk may have a small performance benefit over Glob if you do not need a +// slice of matches because it can avoid allocating memory for the matches. +// Additionally, GlobWalk gives you access to the `fs.DirEntry` objects for +// each match, and lets you quit early by returning a non-nil error from your +// callback function. Like `io/fs.WalkDir`, if your callback returns `SkipDir`, +// GlobWalk will skip the current directory. This means that if the current +// path _is_ a directory, GlobWalk will not recurse into it. If the current +// path is not a directory, the rest of the parent directory will be skipped. +// +// GlobWalk ignores file system errors such as I/O errors reading directories +// by default. GlobWalk may return ErrBadPattern, reporting that the pattern is +// malformed. +// +// To enable aborting on I/O errors, the WithFailOnIOErrors option can be +// passed. +// +// Additionally, if the callback function `fn` returns an error, GlobWalk will +// exit immediately and return that error. +// +// Like Glob(), this function assumes that your pattern uses `/` as the path +// separator even if that's not correct for your OS (like Windows). If you +// aren't sure if that's the case, you can use filepath.ToSlash() on your +// pattern before calling GlobWalk(). +// +// Note: users should _not_ count on the returned error, +// doublestar.ErrBadPattern, being equal to path.ErrBadPattern. +// +func GlobWalk(fsys fs.FS, pattern string, fn GlobWalkFunc, opts ...GlobOption) error { + if !ValidatePattern(pattern) { + return ErrBadPattern + } + + g := newGlob(opts...) + return g.doGlobWalk(fsys, pattern, true, true, fn) +} + +// Actually execute GlobWalk +// - firstSegment is true if we're in the first segment of the pattern, ie, +// the right-most part where we can match files. If it's false, we're +// somewhere in the middle (or at the beginning) and can only match +// directories since there are path segments above us. +// - beforeMeta is true if we're exploring segments before any meta +// characters, ie, in a pattern such as `path/to/file*.txt`, the `path/to/` +// bit does not contain any meta characters. +func (g *glob) doGlobWalk(fsys fs.FS, pattern string, firstSegment, beforeMeta bool, fn GlobWalkFunc) error { + patternStart := indexMeta(pattern) + if patternStart == -1 { + // pattern doesn't contain any meta characters - does a file matching the + // pattern exist? + // The pattern may contain escaped wildcard characters for an exact path match. + path := unescapeMeta(pattern) + info, pathExists, err := g.exists(fsys, path, beforeMeta) + if pathExists && (!firstSegment || !g.filesOnly || !info.IsDir()) { + err = fn(path, dirEntryFromFileInfo(info)) + if err == SkipDir { + err = nil + } + } + return err + } + + dir := "." + splitIdx := lastIndexSlashOrAlt(pattern) + if splitIdx != -1 { + if pattern[splitIdx] == '}' { + openingIdx := indexMatchedOpeningAlt(pattern[:splitIdx]) + if openingIdx == -1 { + // if there's no matching opening index, technically Match() will treat + // an unmatched `}` as nothing special, so... we will, too! + splitIdx = lastIndexSlash(pattern[:splitIdx]) + if splitIdx != -1 { + dir = pattern[:splitIdx] + pattern = pattern[splitIdx+1:] + } + } else { + // otherwise, we have to handle the alts: + return g.globAltsWalk(fsys, pattern, openingIdx, splitIdx, firstSegment, beforeMeta, fn) + } + } else { + dir = pattern[:splitIdx] + pattern = pattern[splitIdx+1:] + } + } + + // if `splitIdx` is less than `patternStart`, we know `dir` has no meta + // characters. They would be equal if they are both -1, which means `dir` + // will be ".", and we know that doesn't have meta characters either. + if splitIdx <= patternStart { + return g.globDirWalk(fsys, dir, pattern, firstSegment, beforeMeta, fn) + } + + return g.doGlobWalk(fsys, dir, false, beforeMeta, func(p string, d fs.DirEntry) error { + if err := g.globDirWalk(fsys, p, pattern, firstSegment, false, fn); err != nil { + return err + } + return nil + }) +} + +// handle alts in the glob pattern - `openingIdx` and `closingIdx` are the +// indexes of `{` and `}`, respectively +func (g *glob) globAltsWalk(fsys fs.FS, pattern string, openingIdx, closingIdx int, firstSegment, beforeMeta bool, fn GlobWalkFunc) (err error) { + var matches []DirEntryWithFullPath + startIdx := 0 + afterIdx := closingIdx + 1 + splitIdx := lastIndexSlashOrAlt(pattern[:openingIdx]) + if splitIdx == -1 || pattern[splitIdx] == '}' { + // no common prefix + matches, err = g.doGlobAltsWalk(fsys, "", pattern, startIdx, openingIdx, closingIdx, afterIdx, firstSegment, beforeMeta, matches) + if err != nil { + return + } + } else { + // our alts have a common prefix that we can process first + startIdx = splitIdx + 1 + innerBeforeMeta := beforeMeta && !hasMetaExceptAlts(pattern[:splitIdx]) + err = g.doGlobWalk(fsys, pattern[:splitIdx], false, beforeMeta, func(p string, d fs.DirEntry) (e error) { + matches, e = g.doGlobAltsWalk(fsys, p, pattern, startIdx, openingIdx, closingIdx, afterIdx, firstSegment, innerBeforeMeta, matches) + return e + }) + if err != nil { + return + } + } + + skip := "" + for _, m := range matches { + if skip != "" { + // Because matches are sorted, we know that descendants of the skipped + // item must come immediately after the skipped item. If we find an item + // that does not have a prefix matching the skipped item, we know we're + // done skipping. I'm using strings.HasPrefix here because + // filepath.HasPrefix has been marked deprecated (and just calls + // strings.HasPrefix anyway). The reason it's deprecated is because it + // doesn't handle case-insensitive paths, nor does it guarantee that the + // prefix is actually a parent directory. Neither is an issue here: the + // paths come from the system so their cases will match, and we guarantee + // a parent directory by appending a slash to the prefix. + // + // NOTE: m.Path will always use slashes as path separators. + if strings.HasPrefix(m.Path, skip) { + continue + } + skip = "" + } + if err = fn(m.Path, m.Entry); err != nil { + if err == SkipDir { + isDir, err := g.isDir(fsys, "", m.Path, m.Entry) + if err != nil { + return err + } + if isDir { + // append a slash to guarantee `skip` will be treated as a parent dir + skip = m.Path + "/" + } else { + // Dir() calls Clean() which calls FromSlash(), so we need to convert + // back to slashes + skip = filepath.ToSlash(filepath.Dir(m.Path)) + "/" + } + err = nil + continue + } + return + } + } + + return +} + +// runs actual matching for alts +func (g *glob) doGlobAltsWalk(fsys fs.FS, d, pattern string, startIdx, openingIdx, closingIdx, afterIdx int, firstSegment, beforeMeta bool, m []DirEntryWithFullPath) (matches []DirEntryWithFullPath, err error) { + matches = m + matchesLen := len(m) + patIdx := openingIdx + 1 + for patIdx < closingIdx { + nextIdx := indexNextAlt(pattern[patIdx:closingIdx], true) + if nextIdx == -1 { + nextIdx = closingIdx + } else { + nextIdx += patIdx + } + + alt := buildAlt(d, pattern, startIdx, openingIdx, patIdx, nextIdx, afterIdx) + err = g.doGlobWalk(fsys, alt, firstSegment, beforeMeta, func(p string, d fs.DirEntry) error { + // insertion sort, ignoring dups + insertIdx := matchesLen + for insertIdx > 0 && matches[insertIdx-1].Path > p { + insertIdx-- + } + if insertIdx > 0 && matches[insertIdx-1].Path == p { + // dup + return nil + } + + // append to grow the slice, then insert + entry := DirEntryWithFullPath{d, p} + matches = append(matches, entry) + for i := matchesLen; i > insertIdx; i-- { + matches[i] = matches[i-1] + } + matches[insertIdx] = entry + matchesLen++ + + return nil + }) + if err != nil { + return + } + + patIdx = nextIdx + 1 + } + + return +} + +func (g *glob) globDirWalk(fsys fs.FS, dir, pattern string, canMatchFiles, beforeMeta bool, fn GlobWalkFunc) (e error) { + if pattern == "" { + if !canMatchFiles || !g.filesOnly { + // pattern can be an empty string if the original pattern ended in a + // slash, in which case, we should just return dir, but only if it + // actually exists and it's a directory (or a symlink to a directory) + info, isDir, err := g.isPathDir(fsys, dir, beforeMeta) + if err != nil { + return err + } + if isDir { + e = fn(dir, dirEntryFromFileInfo(info)) + if e == SkipDir { + e = nil + } + } + } + return + } + + if pattern == "**" { + // `**` can match *this* dir + info, dirExists, err := g.exists(fsys, dir, beforeMeta) + if err != nil { + return err + } + if !dirExists || !info.IsDir() { + return nil + } + if !canMatchFiles || !g.filesOnly { + if e = fn(dir, dirEntryFromFileInfo(info)); e != nil { + if e == SkipDir { + e = nil + } + return + } + } + return g.globDoubleStarWalk(fsys, dir, canMatchFiles, fn) + } + + dirs, err := fs.ReadDir(fsys, dir) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return g.handlePatternNotExist(beforeMeta) + } + return g.forwardErrIfFailOnIOErrors(err) + } + + var matched bool + for _, info := range dirs { + name := info.Name() + matched, e = matchWithSeparator(pattern, name, '/', false) + if e != nil { + return + } + if matched { + matched = canMatchFiles + if !matched || g.filesOnly { + matched, e = g.isDir(fsys, dir, name, info) + if e != nil { + return e + } + if canMatchFiles { + // if we're here, it's because g.filesOnly + // is set and we don't want directories + matched = !matched + } + } + if matched { + if e = fn(path.Join(dir, name), info); e != nil { + if e == SkipDir { + e = nil + } + return + } + } + } + } + + return +} + +// recursively walk files/directories in a directory +func (g *glob) globDoubleStarWalk(fsys fs.FS, dir string, canMatchFiles bool, fn GlobWalkFunc) (e error) { + dirs, err := fs.ReadDir(fsys, dir) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + // This function is only ever called after we know the top-most directory + // exists, so, if we ever get here, we know we'll never return + // ErrPatternNotExist. + return nil + } + return g.forwardErrIfFailOnIOErrors(err) + } + + for _, info := range dirs { + name := info.Name() + isDir, err := g.isDir(fsys, dir, name, info) + if err != nil { + return err + } + + if isDir { + p := path.Join(dir, name) + if !canMatchFiles || !g.filesOnly { + // `**` can match *this* dir, so add it + if e = fn(p, info); e != nil { + if e == SkipDir { + e = nil + continue + } + return + } + } + if e = g.globDoubleStarWalk(fsys, p, canMatchFiles, fn); e != nil { + return + } + } else if canMatchFiles { + if e = fn(path.Join(dir, name), info); e != nil { + if e == SkipDir { + e = nil + } + return + } + } + } + + return +} + +type DirEntryFromFileInfo struct { + fi fs.FileInfo +} + +func (d *DirEntryFromFileInfo) Name() string { + return d.fi.Name() +} + +func (d *DirEntryFromFileInfo) IsDir() bool { + return d.fi.IsDir() +} + +func (d *DirEntryFromFileInfo) Type() fs.FileMode { + return d.fi.Mode().Type() +} + +func (d *DirEntryFromFileInfo) Info() (fs.FileInfo, error) { + return d.fi, nil +} + +func dirEntryFromFileInfo(fi fs.FileInfo) fs.DirEntry { + return &DirEntryFromFileInfo{fi} +} + +type DirEntryWithFullPath struct { + Entry fs.DirEntry + Path string +} + +func hasMetaExceptAlts(s string) bool { + var c byte + l := len(s) + for i := 0; i < l; i++ { + c = s[i] + if c == '*' || c == '?' || c == '[' { + return true + } else if c == '\\' { + // skip next byte + i++ + } + } + return false +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/match.go b/vendor/github.com/bmatcuk/doublestar/v4/match.go new file mode 100644 index 0000000000..c0f20afa43 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/match.go @@ -0,0 +1,403 @@ +package doublestar + +import ( + "path/filepath" + "unicode/utf8" +) + +// Match reports whether name matches the shell pattern. +// The pattern syntax is: +// +// pattern: +// { term } +// term: +// '*' matches any sequence of non-path-separators +// '/**/' matches zero or more directories +// '?' matches any single non-path-separator character +// '[' [ '^' '!' ] { character-range } ']' +// character class (must be non-empty) +// starting with `^` or `!` negates the class +// '{' { term } [ ',' { term } ... ] '}' +// alternatives +// c matches character c (c != '*', '?', '\\', '[') +// '\\' c matches character c +// +// character-range: +// c matches character c (c != '\\', '-', ']') +// '\\' c matches character c +// lo '-' hi matches character c for lo <= c <= hi +// +// Match returns true if `name` matches the file name `pattern`. `name` and +// `pattern` are split on forward slash (`/`) characters and may be relative or +// absolute. +// +// Match requires pattern to match all of name, not just a substring. +// The only possible returned error is ErrBadPattern, when pattern +// is malformed. +// +// A doublestar (`**`) should appear surrounded by path separators such as +// `/**/`. A mid-pattern doublestar (`**`) behaves like bash's globstar +// option: a pattern such as `path/to/**.txt` would return the same results as +// `path/to/*.txt`. The pattern you're looking for is `path/to/**/*.txt`. +// +// Note: this is meant as a drop-in replacement for path.Match() which +// always uses '/' as the path separator. If you want to support systems +// which use a different path separator (such as Windows), what you want +// is PathMatch(). Alternatively, you can run filepath.ToSlash() on both +// pattern and name and then use this function. +// +// Note: users should _not_ count on the returned error, +// doublestar.ErrBadPattern, being equal to path.ErrBadPattern. +// +func Match(pattern, name string) (bool, error) { + return matchWithSeparator(pattern, name, '/', true) +} + +// MatchUnvalidated can provide a small performance improvement if you don't +// care about whether or not the pattern is valid (perhaps because you already +// ran `ValidatePattern`). Note that there's really only one case where this +// performance improvement is realized: when pattern matching reaches the end +// of `name` before reaching the end of `pattern`, such as `Match("a/b/c", +// "a")`. +func MatchUnvalidated(pattern, name string) bool { + matched, _ := matchWithSeparator(pattern, name, '/', false) + return matched +} + +// PathMatch returns true if `name` matches the file name `pattern`. The +// difference between Match and PathMatch is that PathMatch will automatically +// use your system's path separator to split `name` and `pattern`. On systems +// where the path separator is `'\'`, escaping will be disabled. +// +// Note: this is meant as a drop-in replacement for filepath.Match(). It +// assumes that both `pattern` and `name` are using the system's path +// separator. If you can't be sure of that, use filepath.ToSlash() on both +// `pattern` and `name`, and then use the Match() function instead. +// +func PathMatch(pattern, name string) (bool, error) { + return matchWithSeparator(pattern, name, filepath.Separator, true) +} + +// PathMatchUnvalidated can provide a small performance improvement if you +// don't care about whether or not the pattern is valid (perhaps because you +// already ran `ValidatePattern`). Note that there's really only one case where +// this performance improvement is realized: when pattern matching reaches the +// end of `name` before reaching the end of `pattern`, such as `Match("a/b/c", +// "a")`. +func PathMatchUnvalidated(pattern, name string) bool { + matched, _ := matchWithSeparator(pattern, name, filepath.Separator, false) + return matched +} + +func matchWithSeparator(pattern, name string, separator rune, validate bool) (matched bool, err error) { + return doMatchWithSeparator(pattern, name, separator, validate, -1, -1, -1, -1, 0, 0) +} + +func doMatchWithSeparator(pattern, name string, separator rune, validate bool, doublestarPatternBacktrack, doublestarNameBacktrack, starPatternBacktrack, starNameBacktrack, patIdx, nameIdx int) (matched bool, err error) { + patLen := len(pattern) + nameLen := len(name) + startOfSegment := true +MATCH: + for nameIdx < nameLen { + if patIdx < patLen { + switch pattern[patIdx] { + case '*': + if patIdx++; patIdx < patLen && pattern[patIdx] == '*' { + // doublestar - must begin with a path separator, otherwise we'll + // treat it like a single star like bash + patIdx++ + if startOfSegment { + if patIdx >= patLen { + // pattern ends in `/**`: return true + return true, nil + } + + // doublestar must also end with a path separator, otherwise we're + // just going to treat the doublestar as a single star like bash + patRune, patRuneLen := utf8.DecodeRuneInString(pattern[patIdx:]) + if patRune == separator { + patIdx += patRuneLen + + doublestarPatternBacktrack = patIdx + doublestarNameBacktrack = nameIdx + starPatternBacktrack = -1 + starNameBacktrack = -1 + continue + } + } + } + startOfSegment = false + + starPatternBacktrack = patIdx + starNameBacktrack = nameIdx + continue + + case '?': + startOfSegment = false + nameRune, nameRuneLen := utf8.DecodeRuneInString(name[nameIdx:]) + if nameRune == separator { + // `?` cannot match the separator + break + } + + patIdx++ + nameIdx += nameRuneLen + continue + + case '[': + startOfSegment = false + if patIdx++; patIdx >= patLen { + // class didn't end + return false, ErrBadPattern + } + nameRune, nameRuneLen := utf8.DecodeRuneInString(name[nameIdx:]) + + matched := false + negate := pattern[patIdx] == '!' || pattern[patIdx] == '^' + if negate { + patIdx++ + } + + if patIdx >= patLen || pattern[patIdx] == ']' { + // class didn't end or empty character class + return false, ErrBadPattern + } + + last := utf8.MaxRune + for patIdx < patLen && pattern[patIdx] != ']' { + patRune, patRuneLen := utf8.DecodeRuneInString(pattern[patIdx:]) + patIdx += patRuneLen + + // match a range + if last < utf8.MaxRune && patRune == '-' && patIdx < patLen && pattern[patIdx] != ']' { + if pattern[patIdx] == '\\' { + // next character is escaped + patIdx++ + } + patRune, patRuneLen = utf8.DecodeRuneInString(pattern[patIdx:]) + patIdx += patRuneLen + + if last <= nameRune && nameRune <= patRune { + matched = true + break + } + + // didn't match range - reset `last` + last = utf8.MaxRune + continue + } + + // not a range - check if the next rune is escaped + if patRune == '\\' { + patRune, patRuneLen = utf8.DecodeRuneInString(pattern[patIdx:]) + patIdx += patRuneLen + } + + // check if the rune matches + if patRune == nameRune { + matched = true + break + } + + // no matches yet + last = patRune + } + + if matched == negate { + // failed to match - if we reached the end of the pattern, that means + // we never found a closing `]` + if patIdx >= patLen { + return false, ErrBadPattern + } + break + } + + closingIdx := indexUnescapedByte(pattern[patIdx:], ']', true) + if closingIdx == -1 { + // no closing `]` + return false, ErrBadPattern + } + + patIdx += closingIdx + 1 + nameIdx += nameRuneLen + continue + + case '{': + startOfSegment = false + beforeIdx := patIdx + patIdx++ + closingIdx := indexMatchedClosingAlt(pattern[patIdx:], separator != '\\') + if closingIdx == -1 { + // no closing `}` + return false, ErrBadPattern + } + closingIdx += patIdx + + for { + commaIdx := indexNextAlt(pattern[patIdx:closingIdx], separator != '\\') + if commaIdx == -1 { + break + } + commaIdx += patIdx + + result, err := doMatchWithSeparator(pattern[:beforeIdx]+pattern[patIdx:commaIdx]+pattern[closingIdx+1:], name, separator, validate, doublestarPatternBacktrack, doublestarNameBacktrack, starPatternBacktrack, starNameBacktrack, beforeIdx, nameIdx) + if result || err != nil { + return result, err + } + + patIdx = commaIdx + 1 + } + return doMatchWithSeparator(pattern[:beforeIdx]+pattern[patIdx:closingIdx]+pattern[closingIdx+1:], name, separator, validate, doublestarPatternBacktrack, doublestarNameBacktrack, starPatternBacktrack, starNameBacktrack, beforeIdx, nameIdx) + + case '\\': + if separator != '\\' { + // next rune is "escaped" in the pattern - literal match + if patIdx++; patIdx >= patLen { + // pattern ended + return false, ErrBadPattern + } + } + fallthrough + + default: + patRune, patRuneLen := utf8.DecodeRuneInString(pattern[patIdx:]) + nameRune, nameRuneLen := utf8.DecodeRuneInString(name[nameIdx:]) + if patRune != nameRune { + if separator != '\\' && patIdx > 0 && pattern[patIdx-1] == '\\' { + // if this rune was meant to be escaped, we need to move patIdx + // back to the backslash before backtracking or validating below + patIdx-- + } + break + } + + patIdx += patRuneLen + nameIdx += nameRuneLen + startOfSegment = patRune == separator + continue + } + } + + if starPatternBacktrack >= 0 { + // `*` backtrack, but only if the `name` rune isn't the separator + nameRune, nameRuneLen := utf8.DecodeRuneInString(name[starNameBacktrack:]) + if nameRune != separator { + starNameBacktrack += nameRuneLen + patIdx = starPatternBacktrack + nameIdx = starNameBacktrack + startOfSegment = false + continue + } + } + + if doublestarPatternBacktrack >= 0 { + // `**` backtrack, advance `name` past next separator + nameIdx = doublestarNameBacktrack + for nameIdx < nameLen { + nameRune, nameRuneLen := utf8.DecodeRuneInString(name[nameIdx:]) + nameIdx += nameRuneLen + if nameRune == separator { + doublestarNameBacktrack = nameIdx + patIdx = doublestarPatternBacktrack + startOfSegment = true + continue MATCH + } + } + } + + if validate && patIdx < patLen && !doValidatePattern(pattern[patIdx:], separator) { + return false, ErrBadPattern + } + return false, nil + } + + if nameIdx < nameLen { + // we reached the end of `pattern` before the end of `name` + return false, nil + } + + // we've reached the end of `name`; we've successfully matched if we've also + // reached the end of `pattern`, or if the rest of `pattern` can match a + // zero-length string + return isZeroLengthPattern(pattern[patIdx:], separator) +} + +func isZeroLengthPattern(pattern string, separator rune) (ret bool, err error) { + // `/**`, `**/`, and `/**/` are special cases - a pattern such as `path/to/a/**` or `path/to/a/**/` + // *should* match `path/to/a` because `a` might be a directory + if pattern == "" || + pattern == "*" || + pattern == "**" || + pattern == string(separator)+"**" || + pattern == "**"+string(separator) || + pattern == string(separator)+"**"+string(separator) { + return true, nil + } + + if pattern[0] == '{' { + closingIdx := indexMatchedClosingAlt(pattern[1:], separator != '\\') + if closingIdx == -1 { + // no closing '}' + return false, ErrBadPattern + } + closingIdx += 1 + + patIdx := 1 + for { + commaIdx := indexNextAlt(pattern[patIdx:closingIdx], separator != '\\') + if commaIdx == -1 { + break + } + commaIdx += patIdx + + ret, err = isZeroLengthPattern(pattern[patIdx:commaIdx]+pattern[closingIdx+1:], separator) + if ret || err != nil { + return + } + + patIdx = commaIdx + 1 + } + return isZeroLengthPattern(pattern[patIdx:closingIdx]+pattern[closingIdx+1:], separator) + } + + // no luck - validate the rest of the pattern + if !doValidatePattern(pattern, separator) { + return false, ErrBadPattern + } + return false, nil +} + +// Finds the index of the first unescaped byte `c`, or negative 1. +func indexUnescapedByte(s string, c byte, allowEscaping bool) int { + l := len(s) + for i := 0; i < l; i++ { + if allowEscaping && s[i] == '\\' { + // skip next byte + i++ + } else if s[i] == c { + return i + } + } + return -1 +} + +// Assuming the byte before the beginning of `s` is an opening `{`, this +// function will find the index of the matching `}`. That is, it'll skip over +// any nested `{}` and account for escaping +func indexMatchedClosingAlt(s string, allowEscaping bool) int { + alts := 1 + l := len(s) + for i := 0; i < l; i++ { + if allowEscaping && s[i] == '\\' { + // skip next byte + i++ + } else if s[i] == '{' { + alts++ + } else if s[i] == '}' { + if alts--; alts == 0 { + return i + } + } + } + return -1 +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/utils.go b/vendor/github.com/bmatcuk/doublestar/v4/utils.go new file mode 100644 index 0000000000..6b8df9a389 --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/utils.go @@ -0,0 +1,157 @@ +package doublestar + +import ( + "errors" + "os" + "path" + "path/filepath" + "strings" +) + +// SplitPattern is a utility function. Given a pattern, SplitPattern will +// return two strings: the first string is everything up to the last slash +// (`/`) that appears _before_ any unescaped "meta" characters (ie, `*?[{`). +// The second string is everything after that slash. For example, given the +// pattern: +// +// ../../path/to/meta*/** +// ^----------- split here +// +// SplitPattern returns "../../path/to" and "meta*/**". This is useful for +// initializing os.DirFS() to call Glob() because Glob() will silently fail if +// your pattern includes `/./` or `/../`. For example: +// +// base, pattern := SplitPattern("../../path/to/meta*/**") +// fsys := os.DirFS(base) +// matches, err := Glob(fsys, pattern) +// +// If SplitPattern cannot find somewhere to split the pattern (for example, +// `meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in +// this example). +// +// Of course, it is your responsibility to decide if the returned base path is +// "safe" in the context of your application. Perhaps you could use Match() to +// validate against a list of approved base directories? +// +func SplitPattern(p string) (base, pattern string) { + base = "." + pattern = p + + splitIdx := -1 + for i := 0; i < len(p); i++ { + c := p[i] + if c == '\\' { + i++ + } else if c == '/' { + splitIdx = i + } else if c == '*' || c == '?' || c == '[' || c == '{' { + break + } + } + + if splitIdx == 0 { + return "/", p[1:] + } else if splitIdx > 0 { + return p[:splitIdx], p[splitIdx+1:] + } + + return +} + +// FilepathGlob returns the names of all files matching pattern or nil if there +// is no matching file. The syntax of pattern is the same as in Match(). The +// pattern may describe hierarchical names such as usr/*/bin/ed. +// +// FilepathGlob ignores file system errors such as I/O errors reading +// directories by default. The only possible returned error is ErrBadPattern, +// reporting that the pattern is malformed. +// +// To enable aborting on I/O errors, the WithFailOnIOErrors option can be +// passed. +// +// Note: FilepathGlob is a convenience function that is meant as a drop-in +// replacement for `path/filepath.Glob()` for users who don't need the +// complication of io/fs. Basically, it: +// - Runs `filepath.Clean()` and `ToSlash()` on the pattern +// - Runs `SplitPattern()` to get a base path and a pattern to Glob +// - Creates an FS object from the base path and `Glob()s` on the pattern +// - Joins the base path with all of the matches from `Glob()` +// +// Returned paths will use the system's path separator, just like +// `filepath.Glob()`. +// +// Note: the returned error doublestar.ErrBadPattern is not equal to +// filepath.ErrBadPattern. +// +func FilepathGlob(pattern string, opts ...GlobOption) (matches []string, err error) { + if pattern == "" { + // special case to match filepath.Glob behavior + g := newGlob(opts...) + if g.failOnIOErrors { + // match doublestar.Glob behavior here + return nil, os.ErrInvalid + } + return nil, nil + } + + pattern = filepath.Clean(pattern) + pattern = filepath.ToSlash(pattern) + base, f := SplitPattern(pattern) + if f == "" || f == "." || f == ".." { + // some special cases to match filepath.Glob behavior + if !ValidatePathPattern(pattern) { + return nil, ErrBadPattern + } + + if filepath.Separator != '\\' { + pattern = unescapeMeta(pattern) + } + + if _, err = os.Lstat(pattern); err != nil { + g := newGlob(opts...) + if errors.Is(err, os.ErrNotExist) { + return nil, g.handlePatternNotExist(true) + } + return nil, g.forwardErrIfFailOnIOErrors(err) + } + return []string{filepath.FromSlash(pattern)}, nil + } + + fs := os.DirFS(base) + if matches, err = Glob(fs, f, opts...); err != nil { + return nil, err + } + for i := range matches { + // use path.Join because we used ToSlash above to ensure our paths are made + // of forward slashes, no matter what the system uses + matches[i] = filepath.FromSlash(path.Join(base, matches[i])) + } + return +} + +// Finds the next comma, but ignores any commas that appear inside nested `{}`. +// Assumes that each opening bracket has a corresponding closing bracket. +func indexNextAlt(s string, allowEscaping bool) int { + alts := 1 + l := len(s) + for i := 0; i < l; i++ { + if allowEscaping && s[i] == '\\' { + // skip next byte + i++ + } else if s[i] == '{' { + alts++ + } else if s[i] == '}' { + alts-- + } else if s[i] == ',' && alts == 1 { + return i + } + } + return -1 +} + +var metaReplacer = strings.NewReplacer("\\*", "*", "\\?", "?", "\\[", "[", "\\]", "]", "\\{", "{", "\\}", "}") + +// Unescapes meta characters (*?[]{}) +func unescapeMeta(pattern string) string { + return metaReplacer.Replace(pattern) +} diff --git a/vendor/github.com/bmatcuk/doublestar/v4/validate.go b/vendor/github.com/bmatcuk/doublestar/v4/validate.go new file mode 100644 index 0000000000..c689b9ebab --- /dev/null +++ b/vendor/github.com/bmatcuk/doublestar/v4/validate.go @@ -0,0 +1,82 @@ +package doublestar + +import "path/filepath" + +// Validate a pattern. Patterns are validated while they run in Match(), +// PathMatch(), and Glob(), so, you normally wouldn't need to call this. +// However, there are cases where this might be useful: for example, if your +// program allows a user to enter a pattern that you'll run at a later time, +// you might want to validate it. +// +// ValidatePattern assumes your pattern uses '/' as the path separator. +// +func ValidatePattern(s string) bool { + return doValidatePattern(s, '/') +} + +// Like ValidatePattern, only uses your OS path separator. In other words, use +// ValidatePattern if you would normally use Match() or Glob(). Use +// ValidatePathPattern if you would normally use PathMatch(). Keep in mind, +// Glob() requires '/' separators, even if your OS uses something else. +// +func ValidatePathPattern(s string) bool { + return doValidatePattern(s, filepath.Separator) +} + +func doValidatePattern(s string, separator rune) bool { + altDepth := 0 + l := len(s) +VALIDATE: + for i := 0; i < l; i++ { + switch s[i] { + case '\\': + if separator != '\\' { + // skip the next byte - return false if there is no next byte + if i++; i >= l { + return false + } + } + continue + + case '[': + if i++; i >= l { + // class didn't end + return false + } + if s[i] == '^' || s[i] == '!' { + i++ + } + if i >= l || s[i] == ']' { + // class didn't end or empty character class + return false + } + + for ; i < l; i++ { + if separator != '\\' && s[i] == '\\' { + i++ + } else if s[i] == ']' { + // looks good + continue VALIDATE + } + } + + // class didn't end + return false + + case '{': + altDepth++ + continue + + case '}': + if altDepth == 0 { + // alt end without a corresponding start + return false + } + altDepth-- + continue + } + } + + // valid as long as all alts are closed + return altDepth == 0 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c9d9d75f5b..4126f382ca 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -151,6 +151,9 @@ github.com/aymanbagabas/go-osc52/v2 # github.com/blang/semver v3.5.1+incompatible ## explicit github.com/blang/semver +# github.com/bmatcuk/doublestar/v4 v4.7.1 +## explicit; go 1.16 +github.com/bmatcuk/doublestar/v4 # github.com/brunoga/deep v1.2.4 ## explicit; go 1.20.0 github.com/brunoga/deep From b1af420e9dbbb2e2395585bf03cf67c2046d3f3f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:21:56 -0700 Subject: [PATCH 295/440] Add CommonParentPath function --- internal/fileutils/fileutils.go | 34 +++++++++++ internal/fileutils/fileutils_test.go | 90 ++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/internal/fileutils/fileutils.go b/internal/fileutils/fileutils.go index 6eb835e396..8caaa9ed35 100644 --- a/internal/fileutils/fileutils.go +++ b/internal/fileutils/fileutils.go @@ -1248,3 +1248,37 @@ func globPath(path string) string { } return result } + +func CommonParentPath(paths []string) string { + if len(paths) == 0 { + return "" + } + + common := paths[0] + for _, p := range paths[1:] { + common = commonParentPath(common, p) + if common == "" { + return "" + } + } + + return common +} + +func commonParentPath(a, b string) string { + common := "" + a = filepath.ToSlash(a) + b = filepath.ToSlash(b) + as := strings.Split(a, "/") + bs := strings.Split(b, "/") + max := min(len(as), len(bs)) + for x := 1; x <= max; x++ { + ac := strings.Join(as[:x], "/") + bc := strings.Join(bs[:x], "/") + if ac != bc { + return common + } + common = ac + } + return common +} diff --git a/internal/fileutils/fileutils_test.go b/internal/fileutils/fileutils_test.go index 9311d5bea6..a59f3dd5bb 100644 --- a/internal/fileutils/fileutils_test.go +++ b/internal/fileutils/fileutils_test.go @@ -634,3 +634,93 @@ func TestIsWritableDir(t *testing.T) { t.Fatalf("Path should not be writable: %s", pathWithNoPermission) } } + +func TestCommonParentPath(t *testing.T) { + tests := []struct { + paths []string + expected string + }{ + { + paths: []string{"./folder1/file.txt", "./folder1/subfolder/file.txt"}, + expected: "./folder1", + }, + { + paths: []string{"./folder1/file.txt", "./folder2/file.txt"}, + expected: ".", + }, + { + paths: []string{"./folder1/subfolder1/file.txt", "./folder1/subfolder2/file.txt"}, + expected: "./folder1", + }, + { + paths: []string{"./folder1/", "./folder1/subfolder/"}, + expected: "./folder1", + }, + { + paths: []string{"./folder1/file.txt", "./folder1/file.txt"}, + expected: "./folder1/file.txt", + }, + { + paths: []string{"./folder1/file.txt", "./folder1/"}, + expected: "./folder1", + }, + { + paths: []string{"./folder1/file.txt", "./folder2/"}, + expected: ".", + }, + { + paths: []string{"./folder1/file.txt", "./folder1/subfolder/file.txt", "./folder1/subfolder2/file.txt"}, + expected: "./folder1", + }, + { + paths: []string{"./folder1/file.txt", "./folder1/subfolder/file.txt", "./folder2/file.txt"}, + expected: ".", + }, + { + paths: []string{"./folder1/file.txt", "./folder1/subfolder/file.txt", "./folder1/subfolder2/file.txt", "./folder1/subfolder3/file.txt"}, + expected: "./folder1", + }, + { + paths: []string{"./folder1/file.txt", "./folder1/file.txt", "./folder1/file.txt"}, + expected: "./folder1/file.txt", + }, + { + paths: []string{}, + expected: "", + }, + } + + for _, test := range tests { + t.Run(filepath.Join(test.paths...), func(t *testing.T) { + result := CommonParentPath(test.paths) + if result != test.expected { + t.Errorf("CommonParentPath(%v) = %q; expected %q", test.paths, result, test.expected) + } + }) + } +} + +func TestCommonParentPathx(t *testing.T) { + tests := []struct { + a, b string + expected string + }{ + {"./folder1/file.txt", "./folder1/subfolder/file.txt", "./folder1"}, + {"./folder1/file.txt", "./folder2/file.txt", "."}, + {"./folder1/subfolder1/file.txt", "./folder1/subfolder2/file.txt", "./folder1"}, + {"./folder1/", "./folder1/subfolder/", "./folder1"}, + {"./folder1/file.txt", "./folder1/file.txt", "./folder1/file.txt"}, + {"./folder1/file.txt", "./folder1/", "./folder1"}, + {"./folder1/file.txt", "./folder2/", "."}, + {"", "./folder1/file.txt", ""}, + {"./folder1/file.txt", "", ""}, + {"", "", ""}, + } + + for x, test := range tests { + result := commonParentPath(test.a, test.b) + if result != test.expected { + t.Errorf("%d: commonParentPath(%q, %q) = %q; expected %q", x, test.a, test.b, result, test.expected) + } + } +} From 8472bd93a20200784fb5aeef3c65d29858bf99dc Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:22:14 -0700 Subject: [PATCH 296/440] Add GetwdUnsafe --- internal/osutils/osutils.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/osutils/osutils.go b/internal/osutils/osutils.go index 3618d31389..cd436e4bb4 100644 --- a/internal/osutils/osutils.go +++ b/internal/osutils/osutils.go @@ -80,6 +80,14 @@ func Getwd() (string, error) { return r, nil } +func GetwdUnsafe() string { + r, err := Getwd() + if err != nil { + panic(err) + } + return r +} + func EnvSliceToMap(envSlice []string) map[string]string { env := map[string]string{} for _, v := range envSlice { From bb3304b486486c3bf720958322482184ba10f85d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:22:40 -0700 Subject: [PATCH 297/440] `artifacts dl` should fail if artifact status is not successful --- internal/runners/artifacts/download.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/runners/artifacts/download.go b/internal/runners/artifacts/download.go index 22484c9c54..91d5d28ae5 100644 --- a/internal/runners/artifacts/download.go +++ b/internal/runners/artifacts/download.go @@ -20,6 +20,7 @@ import ( buildplanner_runbit "github.com/ActiveState/cli/internal/runbits/buildplanner" "github.com/ActiveState/cli/pkg/buildplan" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/request" + "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/project" @@ -127,6 +128,9 @@ func (d *Download) Run(params *DownloadParams) (rerr error) { } func (d *Download) downloadArtifact(artifact *buildplan.Artifact, targetDir string) (rerr error) { + if artifact.Status != types.ArtifactSucceeded { + return locale.NewInputError("err_artifact_dl_status", "Could not download artifact {{.V0}}, status is {{.V1}}", artifact.Name(), artifact.Status) + } artifactURL, err := url.Parse(artifact.URL) if err != nil { return errs.Wrap(err, "Could not parse artifact URL %s.", artifact.URL) From 40b99efc207e4976659c773bafb724dc774deb7d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:22:56 -0700 Subject: [PATCH 298/440] Add structured output for `artifacts dl` --- internal/runners/artifacts/download.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/runners/artifacts/download.go b/internal/runners/artifacts/download.go index 91d5d28ae5..86b2b4d2bb 100644 --- a/internal/runners/artifacts/download.go +++ b/internal/runners/artifacts/download.go @@ -174,7 +174,11 @@ func (d *Download) downloadArtifact(artifact *buildplan.Artifact, targetDir stri return errs.Wrap(err, "Writing download to target file %s failed", downloadPath) } - d.out.Notice(locale.Tl("msg_download_success", "[SUCCESS]Downloaded {{.V0}} to {{.V1}}[/RESET]", artifact.Name(), downloadPath)) + if d.out.Type().IsStructured() { + d.out.Print(output.Structured(downloadPath)) + } else { + d.out.Notice(locale.Tl("msg_download_success", "[SUCCESS]Downloaded {{.V0}} to {{.V1}}[/RESET]", artifact.Name(), downloadPath)) + } return nil } From 9086810db350fba834c1cc338bff273b8305b692 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:25:36 -0700 Subject: [PATCH 299/440] model.BuildPlannerVersionConstraintsToString > model.VersionRequirementsToString --- internal/runners/commit/ingredientcall.go | 2 +- internal/runners/manifest/version.go | 2 +- internal/runners/packages/list.go | 2 +- internal/runners/publish/publish.go | 2 +- pkg/platform/model/checkpoints.go | 4 ++-- pkg/platform/model/version_constraints.go | 24 +++++++++++++---------- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 2418b4e6a4..49c8b8b953 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -185,7 +185,7 @@ func (i *IngredientCall) resolveDependenciesByKey(key string, typ request.Depend request.Dependency{ Name: req.Name, Namespace: req.Namespace, - VersionRequirements: model.BuildPlannerVersionConstraintsToString(req.VersionRequirement), + VersionRequirements: model.VersionRequirementsToString(req.VersionRequirement, true), Type: typ, }, []request.Dependency{}, diff --git a/internal/runners/manifest/version.go b/internal/runners/manifest/version.go index be81cea882..ee15f52eea 100644 --- a/internal/runners/manifest/version.go +++ b/internal/runners/manifest/version.go @@ -37,7 +37,7 @@ func resolveVersion(req types.Requirement, bpReqs buildplan.Ingredients) *resolv var resolved string if req.VersionRequirement != nil { - requested = platformModel.BuildPlannerVersionConstraintsToString(req.VersionRequirement) + requested = platformModel.VersionRequirementsToString(req.VersionRequirement, true) } else { requested = locale.Tl("manifest_version_auto", "auto") } diff --git a/internal/runners/packages/list.go b/internal/runners/packages/list.go index 4c39904eb9..30aaea006a 100644 --- a/internal/runners/packages/list.go +++ b/internal/runners/packages/list.go @@ -139,7 +139,7 @@ func (l *List) Run(params ListRunParams, nstype model.NamespaceType) error { version := req.VersionConstraint if version == "" { - version = model.GqlReqVersionConstraintsString(req) + version = model.GqlReqVersionConstraintsString(req, false) if version == "" { version = locale.T("constraint_auto") } diff --git a/internal/runners/publish/publish.go b/internal/runners/publish/publish.go index 061b4b71d4..6964d1c98b 100644 --- a/internal/runners/publish/publish.go +++ b/internal/runners/publish/publish.go @@ -418,7 +418,7 @@ func prepareEditRequest(ingredient *ParentIngredient, r *request.PublishVariable request.PublishVariableDep{request.Dependency{ Name: ptr.From(dep.Feature, ""), Namespace: ptr.From(dep.Namespace, ""), - VersionRequirements: model.InventoryRequirementsToString(dep.Requirements), + VersionRequirements: model.InventoryRequirementsToString(dep.Requirements, true), }, []request.Dependency{}}, ) } diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index 87f491a940..0dc6c550a8 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -61,7 +61,7 @@ func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([ languages := []Language{} for _, requirement := range checkpoint { if NamespaceMatch(requirement.Namespace, NamespaceLanguageMatch) { - version := MonoConstraintsToString(requirement.VersionConstraints) + version := MonoConstraintsToString(requirement.VersionConstraints, true) lang := Language{ Name: requirement.Requirement, Version: version, @@ -85,7 +85,7 @@ func FetchLanguagesForBuildScript(script *buildscript.BuildScript) ([]Language, if NamespaceMatch(requirement.Namespace, NamespaceLanguageMatch) { lang := Language{ Name: requirement.Name, - Version: BuildPlannerVersionConstraintsToString(requirement.VersionRequirement), + Version: VersionRequirementsToString(requirement.VersionRequirement, true), } languages = append(languages, lang) } diff --git a/pkg/platform/model/version_constraints.go b/pkg/platform/model/version_constraints.go index ec0c81e645..62fa9ce5b7 100644 --- a/pkg/platform/model/version_constraints.go +++ b/pkg/platform/model/version_constraints.go @@ -16,7 +16,7 @@ type versionConstraints struct { version string } -func InventoryRequirementsToString(requirements inventory_models.Requirements) string { +func InventoryRequirementsToString(requirements inventory_models.Requirements, forAPI bool) string { if requirements == nil { return "" } @@ -25,10 +25,10 @@ func InventoryRequirementsToString(requirements inventory_models.Requirements) s for i, req := range requirements { constraints[i] = &versionConstraints{*req.Comparator, *req.Version} } - return versionConstraintsToString(constraints) + return versionConstraintsToString(constraints, forAPI) } -func GqlReqVersionConstraintsString(requirement *gqlModel.Requirement) string { +func GqlReqVersionConstraintsString(requirement *gqlModel.Requirement, forAPI bool) string { if requirement.VersionConstraints == nil { return "" } @@ -37,10 +37,10 @@ func GqlReqVersionConstraintsString(requirement *gqlModel.Requirement) string { for i, constraint := range requirement.VersionConstraints { constraints[i] = &versionConstraints{constraint.Comparator, constraint.Version} } - return versionConstraintsToString(constraints) + return versionConstraintsToString(constraints, forAPI) } -func BuildPlannerVersionConstraintsToString(requirements []types.VersionRequirement) string { +func VersionRequirementsToString(requirements []types.VersionRequirement, forAPI bool) string { if requirements == nil { return "" } @@ -50,10 +50,10 @@ func BuildPlannerVersionConstraintsToString(requirements []types.VersionRequirem constraints = append(constraints, &versionConstraints{constraint[types.VersionRequirementComparatorKey], constraint[types.VersionRequirementVersionKey]}) } - return versionConstraintsToString(constraints) + return versionConstraintsToString(constraints, forAPI) } -func MonoConstraintsToString(monoConstraints mono_models.Constraints) string { +func MonoConstraintsToString(monoConstraints mono_models.Constraints, forAPI bool) string { if monoConstraints == nil { return "" } @@ -62,10 +62,10 @@ func MonoConstraintsToString(monoConstraints mono_models.Constraints) string { for i, constraint := range monoConstraints { constraints[i] = &versionConstraints{constraint.Comparator, constraint.Version} } - return versionConstraintsToString(constraints) + return versionConstraintsToString(constraints, forAPI) } -func versionConstraintsToString(constraints []*versionConstraints) string { +func versionConstraintsToString(constraints []*versionConstraints, forAPI bool) string { if len(constraints) == 0 { return "" } @@ -78,7 +78,11 @@ func versionConstraintsToString(constraints []*versionConstraints) string { } switch req.comparator { case inventory_models.RequirementComparatorEq: - parts = append(parts, req.version) + if forAPI { + parts = append(parts, fmt.Sprintf("==%s", req.version)) + } else { + parts = append(parts, req.version) + } case inventory_models.RequirementComparatorGt: parts = append(parts, fmt.Sprintf(">%s", req.version)) case inventory_models.RequirementComparatorGte: From ad3aa7b375ae074c177d75b6e7ae8198111d4c97 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:25:56 -0700 Subject: [PATCH 300/440] Added sliceutils.Cast --- internal/sliceutils/sliceutils.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/internal/sliceutils/sliceutils.go b/internal/sliceutils/sliceutils.go index e31ff293d8..6939a60eff 100644 --- a/internal/sliceutils/sliceutils.go +++ b/internal/sliceutils/sliceutils.go @@ -134,3 +134,16 @@ func EqualValues[S ~[]E, E cmp.Ordered](a, b S) bool { return true } + +// Cast allows casting of a slice of any type to a slice of a specific type. +func Cast[T any](slice []any) ([]T, bool) { + result := []T{} + for _, s := range slice { + v, ok := s.(T) + if !ok { + return nil, false + } + result = append(result, v) + } + return result, true +} From ba97d3c612c2fe9dcc5da42032cb67f33a878c3c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:26:46 -0700 Subject: [PATCH 301/440] Automatically set Path according to name/namespace --- pkg/platform/api/graphql/request/publish.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index 8daaa679d5..40fd629bf3 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -103,6 +103,23 @@ type ExampleDepVariables struct { Dependencies []PublishVariableDep `yaml:"dependencies,omitempty"` } +func (p PublishVariables) MarshalJSON() ([]byte, error) { + if p.Path == "" { + if p.Name == "" || p.Namespace == "" { + return nil, errs.New("either Path of Name and Namespace are required") + } + p.Path = fmt.Sprintf("%s/%s", p.Namespace, p.Name) + } + // prevent recursion + type Alias PublishVariables + alias := &struct { + Alias + }{ + Alias: (Alias)(p), + } + return json.Marshal(alias) +} + func (p PublishVariables) MarshalYaml(includeExample bool) ([]byte, error) { v, err := yamlcomment.Marshal(p) if err != nil { From a0f86f541de97cbd2cf4cdaaf512954ab8cd46f6 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:27:00 -0700 Subject: [PATCH 302/440] Fix hashglobs variable notation --- pkg/platform/api/svc/request/hashglobs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/platform/api/svc/request/hashglobs.go b/pkg/platform/api/svc/request/hashglobs.go index cc2cfd5930..29e9077fd1 100644 --- a/pkg/platform/api/svc/request/hashglobs.go +++ b/pkg/platform/api/svc/request/hashglobs.go @@ -10,7 +10,7 @@ func NewHashGlobs(wd string, globs []string) *HashGlobs { } func (c *HashGlobs) Query() string { - return `query(wd: String!, $globs: [String!]!) { + return `query($wd: String!, $globs: [String!]!) { hashGlobs(wd: $wd, globs: $globs) { hash files { From 1cb3d36965100f4b323e49d9866e28f63f6f2dc1 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:27:48 -0700 Subject: [PATCH 303/440] Fix org namespace values --- pkg/platform/model/vcs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/platform/model/vcs.go b/pkg/platform/model/vcs.go index d29ed72717..a964ea207b 100644 --- a/pkg/platform/model/vcs.go +++ b/pkg/platform/model/vcs.go @@ -135,7 +135,7 @@ var ( NamespaceBundle = NamespaceType{"bundle", "bundles", NamespaceBundlesMatch} NamespaceLanguage = NamespaceType{"language", "", NamespaceLanguageMatch} NamespacePlatform = NamespaceType{"platform", "", NamespacePlatformMatch} - NamespaceOrg = NamespaceType{"org", "private/", NamespaceOrgMatch} + NamespaceOrg = NamespaceType{"org", "private", NamespaceOrgMatch} NamespaceRaw = NamespaceType{"raw", "", ""} NamespaceBlank = NamespaceType{"", "", ""} ) @@ -229,7 +229,7 @@ func NewNamespaceOrg(orgName, suffix string) Namespace { } return Namespace{ nsType: NamespaceOrg, - value: NamespaceOrg.prefix + "/" + orgName, + value: NamespaceOrg.prefix + "/" + ns, } } From da63e3df3200038bcd04ef1f7189317646c96d98 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:28:15 -0700 Subject: [PATCH 304/440] Missing import --- pkg/platform/api/graphql/request/publish.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index 40fd629bf3..d7b4ec091d 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "os" "github.com/ActiveState/cli/internal/errs" From 312771ba5b13548882c9447c21d0c644a41023c7 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:28:27 -0700 Subject: [PATCH 305/440] Added archiver package --- internal/archiver/archiver.go | 67 +++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 internal/archiver/archiver.go diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go new file mode 100644 index 0000000000..4b28f36332 --- /dev/null +++ b/internal/archiver/archiver.go @@ -0,0 +1,67 @@ +package archiver + +import ( + "os" + "strings" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" + "github.com/mholt/archiver/v3" +) + +type FileMap struct { + Source string + Target string +} + +func CreateTgz(filepath string, fileMaps []FileMap) error { + f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return errs.Wrap(err, "Could not create temp file") + } + defer f.Close() + tgz := archiver.NewTarGz() + if err := tgz.Create(f); err != nil { + return errs.Wrap(err, "Could not create tar.gz") + } + defer tgz.Close() + + for _, fileMap := range fileMaps { + file, err := os.Open(fileMap.Source) + if err != nil { + return errs.Wrap(err, "Could not open file") + } + + fileInfo, err := file.Stat() + if err != nil { + return errs.Wrap(err, "Could not stat file") + } + + // write it to the archive + err = tgz.Write(archiver.File{ + FileInfo: archiver.FileInfo{ + FileInfo: fileInfo, + CustomName: fileMap.Target, + }, + ReadCloser: file, + }) + file.Close() + if err != nil { + return errs.Wrap(err, "Could not write file to tar.gz") + } + } + + return nil +} + +func FilesWithCommonParent(filepaths ...string) []FileMap { + var fileMaps []FileMap + common := fileutils.CommonParentPath(filepaths) + for _, filepath := range filepaths { + fileMaps = append(fileMaps, FileMap{ + Source: filepath, + Target: strings.TrimPrefix(strings.TrimPrefix(filepath, common), "/"), + }) + } + return fileMaps +} From 908b68510f0d8c9644c16baaba13410b4e905ead Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:29:00 -0700 Subject: [PATCH 306/440] Buildscript external values support ints and slices --- pkg/buildscript/buildscript.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index fcaf7981c7..87ab95c5e8 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -7,6 +7,8 @@ import ( "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/sliceutils" "github.com/brunoga/deep" ) @@ -120,11 +122,13 @@ func (f *FuncCall) UnsetArgument(k string) { // Value turns a standard type into a buildscript compatible type // Intended for use with functions like SetArgument. -func Value[T string | float64 | []string | []float64](inputv T) *value { +func Value[T string | int | float64 | []string | []float64](inputv T) *value { v := &value{} switch vt := any(inputv).(type) { case string: v.Str = &vt + case int: + v.Number = ptr.To(float64(vt)) case float64: v.Number = &vt case []string: @@ -160,6 +164,12 @@ func exportValue(v *value) any { for _, value := range *v.List { result = append(result, exportValue(value)) } + if v, ok := sliceutils.Cast[string](result); ok { + return v + } + if v, ok := sliceutils.Cast[float64](result); ok { + return v + } return result case v.Str != nil: return strValue(v) From 209ced79f954115896be910bfed68912f54ff056 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:30:08 -0700 Subject: [PATCH 307/440] Fix successful request throwing error --- pkg/platform/model/svc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 613f952236..375878b7eb 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -234,7 +234,7 @@ func (m *SvcModel) HashGlobs(wd string, globs []string) (*graph.GlobResult, erro if err := m.request(context.Background(), req, &res); err != nil { return nil, errs.Wrap(err, "Error sending HashGlobs request to state-svc") } - return &res.Response, errs.New("svcModel.HashGlobs() did not return an expected value") + return &res.Response, nil } func jsonFromMap(m map[string]interface{}) string { From 57c7997820fb82da47b7bd4edbdbd324537909f9 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:30:51 -0700 Subject: [PATCH 308/440] Various fixes to ingredientcall during end to end testing --- internal/runners/commit/ingredientcall.go | 160 +++++++++++++++---- pkg/buildscript/unmarshal_buildexpression.go | 68 +++++--- 2 files changed, 177 insertions(+), 51 deletions(-) diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 49c8b8b953..683b127a0c 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -4,20 +4,21 @@ import ( "encoding/json" "errors" "fmt" - "os" + "strings" "time" + "github.com/ActiveState/cli/internal/archiver" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/graph" "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/platform/api/graphql/request" "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/platform/model/buildplanner" "github.com/brunoga/deep" "github.com/cespare/xxhash" - "github.com/mholt/archiver/v3" ) const namespaceSuffixFiles = "files" @@ -27,6 +28,16 @@ type invalidDepsValueType struct{ error } type invalidDepValueType struct{ error } +type invalidFeaturesValueType struct{ error } + +type invalidFeatureValue struct{ error } + +const keyHash = "hash_readonly" +const keyIngredientVersionID = "ingredient_version_id_readonly" +const keyRevision = "revision_readonly" + +var readonlyKeys = []string{keyHash, keyIngredientVersionID, keyRevision} + // IngredientCall is used to evaluate ingredient() function calls and publishes the ingredient in question if it is not // already published. type IngredientCall struct { @@ -57,48 +68,78 @@ func (i *IngredientCall) Resolve() error { return errs.Wrap(err, "Could not hash ingredient call") } - cached, err := i.isCached(hash) + resolvedIngredient, err := i.getCached(hash) if err != nil { return errs.Wrap(err, "Could not check if ingredient call is cached") } - if cached { - // Ingredient already exists, nothing to do - return nil + if resolvedIngredient == nil { + resolvedIngredient, err = i.createIngredient(hash, hashedFiles) + if err != nil { + return errs.Wrap(err, "Could not create ingredient") + } + // Bump timestamp, because otherwise the new ingredient will be unusable + latest, err := model.FetchLatestRevisionTimeStamp(nil) + if err != nil { + return errs.Wrap(err, "Unable to determine latest revision time") + } + i.script.SetAtTime(latest) } - // Creative tar.gz with all the references files for this ingredient + // Add/update arguments on the buildscript ingredient function call + // ingredient_version_id and revision are required for the server to lookup the ingredient without violating + // reproducibility. + // hash is used to uniquely identify the ingredient and is used to cache the ingredient, it is only evaluated on the client. + i.funcCall.SetArgument(keyIngredientVersionID, buildscript.Value(resolvedIngredient.VersionID)) + i.funcCall.SetArgument(keyRevision, buildscript.Value(resolvedIngredient.Revision)) + i.funcCall.SetArgument(keyHash, buildscript.Value(hash)) + i.setCached(hash, resolvedIngredient) + + return nil +} + +func (i *IngredientCall) createIngredient(hash string, hashedFiles []*graph.GlobFileResult) (*resolvedIngredientData, error) { + // Create tar.gz with all the references files for this ingredient files := []string{} for _, f := range hashedFiles { files = append(files, f.Path) } + tmpFile := fileutils.TempFilePath("", fmt.Sprintf("bs-hash-%s.tar.gz", hash)) - if err := archiver.Archive(files, tmpFile); err != nil { - return errs.Wrap(err, "Could not archive files") + if err := archiver.CreateTgz(tmpFile, archiver.FilesWithCommonParent(files...)); err != nil { + return nil, errs.Wrap(err, "Could not create tar.gz") } defer os.Remove(tmpFile) // Parse buildscript dependencies deps, err := i.resolveDependencies() if err != nil { - return errs.Wrap(err, "Could not resolve dependencies") + return nil, errs.Wrap(err, "Could not resolve dependencies") + } + + // Parse buildscript features + features, err := i.resolveFeatures() + if err != nil { + return nil, errs.Wrap(err, "Could not resolve features") } // Publish ingredient bpm := buildplanner.NewBuildPlannerModel(i.prime.Auth(), i.prime.SvcModel()) - _, err = bpm.Publish(request.PublishVariables{ + pr, err := bpm.Publish(request.PublishVariables{ Name: hash, + Description: "buildscript ingredient", Namespace: i.ns.String(), + Version: hash, Dependencies: deps, + Features: features, }, tmpFile) if err != nil { - return errs.Wrap(err, "Could not create publish request") + return nil, errs.Wrap(err, "Could not create publish request") } - // Add/update hash argument on the buildscript ingredient function call - i.funcCall.SetArgument("hash", buildscript.Value(hash)) - i.setCached(hash) - - return nil + return &resolvedIngredientData{ + VersionID: pr.IngredientVersionID, + Revision: pr.Revision, + }, nil } // calculateHash will calculate a hash based on the files references in the ingredient as well as the ingredient @@ -131,7 +172,9 @@ func hashFuncCall(fc *buildscript.FuncCall, seed string) (string, error) { if err != nil { return "", errs.Wrap(err, "Could not copy function call") } - fcc.UnsetArgument("hash") + for _, k := range readonlyKeys { + fcc.UnsetArgument(k) + } fcb, err := json.Marshal(fcc) if err != nil { @@ -195,35 +238,94 @@ func (i *IngredientCall) resolveDependenciesByKey(key string, typ request.Depend return deps, nil } -// isCached checks against our local cache to see if we've already handled this file hash, and if no local cache +func (i *IngredientCall) resolveFeatures() ([]request.PublishVariableFeature, error) { + features := []request.PublishVariableFeature{} + bsFeatures := i.funcCall.Argument("features") + if bsFeatures == nil { + return features, nil + } + + bsFeaturesSlice, ok := bsFeatures.([]string) + if !ok { + return nil, invalidFeaturesValueType{fmt.Errorf("features argument is not an []string: %v (%T)", bsFeatures, bsFeatures)} + } + + for _, feature := range bsFeaturesSlice { + resolvedFeature, err := parseFeature(feature) + if err != nil { + return nil, errs.Wrap(err, "Could not parse feature") + } + features = append(features, resolvedFeature) + } + + return features, nil +} + +// parseFeature will parse a feature string into a request.PublishVariableFeature +// features need to be formatted as `namespace/name@version` +func parseFeature(f string) (request.PublishVariableFeature, error) { + p := request.PublishVariableFeature{} + if !strings.Contains(f, "/") { + return p, &invalidFeatureValue{fmt.Errorf("feature '%s' is missing a namespace, it should be formatted as namespace/name@version", f)} + } + if !strings.Contains(f, "@") { + return p, &invalidFeatureValue{fmt.Errorf("feature '%s' is missing a version, it should be formatted as namespace/name@version", f)} + } + v := strings.Split(f, "@") + p.Version = strings.TrimSpace(v[1]) + v = strings.Split(v[0], "/") + p.Namespace = strings.TrimSpace(strings.Join(v[0:len(v)-1], "/")) + p.Name = strings.TrimSpace(v[len(v)-1]) + return p, nil +} + +type resolvedIngredientData struct { + VersionID string + Revision int +} + +// getCached checks against our local cache to see if we've already handled this file hash, and if no local cache // exists checks against the platform ingredient API. -func (i *IngredientCall) isCached(hash string) (bool, error) { +func (i *IngredientCall) getCached(hash string) (*resolvedIngredientData, error) { cacheValue, err := i.prime.SvcModel().GetCache(fmt.Sprintf(cacheKeyFiles, hash)) if err != nil { - return false, errs.Wrap(err, "Could not get build script cache") + return nil, errs.Wrap(err, "Could not get build script cache") } if cacheValue != "" { + resolvedIngredient := &resolvedIngredientData{} + err := json.Unmarshal([]byte(cacheValue), resolvedIngredient) + if err != nil { + return nil, errs.Wrap(err, "Could not unmarshal cached ingredient") + } // Ingredient already exists - return true, nil + return resolvedIngredient, nil } // Check against API to see if we've already published this file hash ingredients, err := model.SearchIngredientsStrict(i.ns.String(), hash, true, false, i.script.AtTime(), i.prime.Auth()) - if err != nil { - return false, errs.Wrap(err, "Could not search ingredients") + if err != nil && !errors.As(err, ptr.To(&model.ErrSearch404{})) { + return nil, errs.Wrap(err, "Could not search ingredients") } if len(ingredients) > 0 { // Ingredient already exists - return true, nil + return &resolvedIngredientData{ + VersionID: string(*ingredients[0].LatestVersion.IngredientVersionID), + Revision: int(*ingredients[0].LatestVersion.Revision), + }, nil } - return false, nil // If we made it this far it means we did not find any existing cache entry; so it's dirty + return nil, nil // If we made it this far it means we did not find any existing cache entry; so it's dirty } // Update our local cache saying we've handled this hash, allowing for faster cache checks than using the platform api -func (i *IngredientCall) setCached(hash string) { - err := i.prime.SvcModel().SetCache(fmt.Sprintf(cacheKeyFiles, hash), hash, time.Hour*24*7) +func (i *IngredientCall) setCached(hash string, resolvedIngredient *resolvedIngredientData) { + b, err := json.Marshal(resolvedIngredient) + if err != nil { + // Shouldn't happen, but at least we're logging it if it does + logging.Error("Could not marshal cached ingredient: %s", errs.JoinMessage(err)) + } + err = i.prime.SvcModel().SetCache(fmt.Sprintf(cacheKeyFiles, hash), string(b), time.Hour*24*7) if err != nil { - logging.Warning("Could not set build script cache: %s", errs.JoinMessage(err)) + logging.Error("Could not set build script cache: %s", errs.JoinMessage(err)) } } diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index cc625d6ab2..033e5c6f95 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -2,6 +2,7 @@ package buildscript import ( "encoding/json" + "errors" "sort" "strconv" "strings" @@ -77,21 +78,11 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { atTimeNode.Str = nil atTimeNode.Ident = ptr.To("at_time") b.raw.AtTime = ptr.To(time.Time(atTime)) - } else if err != nil { + } else if err != nil && !errors.Is(err, errNodeNotFound) { return errs.Wrap(err, "Could not get at_time node") } - // If the requirements are in legacy object form, e.g. - // requirements = [{"name": "", "namespace": ""}, {...}, ...] - // then transform them into function call form for the AScript format, e.g. - // requirements = [Req(name = "", namespace = ""), Req(...), ...] - requirements, err := b.getRequirementsNode() - if err != nil { - return errs.Wrap(err, "Could not get requirements node") - } - if isLegacyRequirementsList(requirements) { - requirements.List = transformRequirements(requirements).List - } + b.raw.transformToRequirementFuncs() return nil } @@ -180,7 +171,7 @@ func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { result.List = &values case string: - if sliceutils.Contains(path, ctxIn) || strings.HasPrefix(v, "$") { + if len(path) >= 2 && path[len(path)-2] == ctxIn || strings.HasPrefix(v, "$") { result.Ident = ptr.To(strings.TrimPrefix(v, "$")) } else { result.Str = ptr.To(strconv.Quote(v)) // quoting is mandatory @@ -282,15 +273,9 @@ func unmarshalIn(path []string, inValue interface{}) (*value, error) { return in, nil } -// isLegacyRequirementsList returns whether or not the given requirements list is in the legacy -// object format, such as -// -// [ -// {"name": "", "namespace": ""}, -// ..., -// ] -func isLegacyRequirementsList(value *value) bool { - return len(*value.List) > 0 && (*value.List)[0].Object != nil +// isObjectList returns whether or not the given value is a list containing objects +func isObjectList(value *value) bool { + return value.List != nil && len(*value.List) > 0 && (*value.List)[0].Object != nil } // transformRequirements transforms a build expression list of requirements in object form into a @@ -382,3 +367,42 @@ func transformVersion(requirements *assignment) *funcCall { } return f } + +type requirementFunction struct { + FunctionName string + RequirementArguments []string +} + +var requirementFunctions = []requirementFunction{ + {solveFuncName, []string{requirementsKey}}, + {"ingredient", []string{"build_deps", "runtime_deps", "test_deps"}}, +} + +func (r *rawBuildScript) transformToRequirementFuncs() { + // Iterate over the function calls within the buildscript + for _, functionCall := range r.FuncCalls() { + + // Find a matching requirement function + for _, reqFunction := range requirementFunctions { + if functionCall.Name != reqFunction.FunctionName { + continue + } + + // Find a matching requirement argument + for _, arg := range functionCall.Arguments { + if arg.Assignment == nil { + continue + } + for _, reqArgument := range reqFunction.RequirementArguments { + if arg.Assignment.Key == reqArgument { + + // Convert the argument to requirement functions + if isObjectList(arg.Assignment.Value) { + arg.Assignment.Value.List = transformRequirements(arg.Assignment.Value).List + } + } + } + } + } + } +} From 18545b205465b47f762d87b3243fc926c889091a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:31:16 -0700 Subject: [PATCH 309/440] Test ingredientcall --- internal/testhelpers/e2e/session.go | 8 +- test/integration/buildscript_int_test.go | 119 ++++++++++++ test/integration/shared_int_test.go | 26 ++- .../testdata/sample_ingredient/LICENSE.txt | 19 ++ .../testdata/sample_ingredient/PKG-INFO | 70 +++++++ .../testdata/sample_ingredient/README.md | 40 ++++ .../testdata/sample_ingredient/data/data_file | 1 + .../testdata/sample_ingredient/pyproject.toml | 5 + .../testdata/sample_ingredient/setup.cfg | 7 + .../testdata/sample_ingredient/setup.py | 181 ++++++++++++++++++ .../src/sample_activestate.egg-info/PKG-INFO | 70 +++++++ .../sample_activestate.egg-info/SOURCES.txt | 15 ++ .../dependency_links.txt | 1 + .../entry_points.txt | 2 + .../sample_activestate.egg-info/requires.txt | 7 + .../sample_activestate.egg-info/top_level.txt | 1 + .../src/sample_activestate/__init__.py | 3 + .../src/sample_activestate/simple.py | 2 + .../sample_ingredient/tests/test_simple.py | 17 ++ 19 files changed, 583 insertions(+), 11 deletions(-) create mode 100644 test/integration/testdata/sample_ingredient/LICENSE.txt create mode 100644 test/integration/testdata/sample_ingredient/PKG-INFO create mode 100644 test/integration/testdata/sample_ingredient/README.md create mode 100644 test/integration/testdata/sample_ingredient/data/data_file create mode 100644 test/integration/testdata/sample_ingredient/pyproject.toml create mode 100644 test/integration/testdata/sample_ingredient/setup.cfg create mode 100644 test/integration/testdata/sample_ingredient/setup.py create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/PKG-INFO create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/SOURCES.txt create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/dependency_links.txt create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/entry_points.txt create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/requires.txt create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/top_level.txt create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate/__init__.py create mode 100644 test/integration/testdata/sample_ingredient/src/sample_activestate/simple.py create mode 100644 test/integration/testdata/sample_ingredient/tests/test_simple.py diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index e1cb5d2963..a36ad1f57e 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -44,10 +44,12 @@ import ( "github.com/stretchr/testify/require" ) +const RuntimeBuildSourcingTimeout = 6 * time.Minute + var ( RuntimeSolvingTimeoutOpt = termtest.OptExpectTimeout(1 * time.Minute) RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) - RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(6 * time.Minute) + RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(RuntimeBuildSourcingTimeout) ) // Session represents an end-to-end testing session during which several console process can be spawned and tested @@ -370,6 +372,10 @@ func (s *Session) PrepareActiveStateYAML(contents string) { require.NoError(s.T, fileutils.WriteFile(filepath.Join(s.Dirs.Work, constants.ConfigFileName), []byte(contents))) } +func (s *Session) PrepareBuildScript(contents string) { + require.NoError(s.T, fileutils.WriteFile(filepath.Join(s.Dirs.Work, constants.BuildScriptFileName), []byte(contents))) +} + func (s *Session) PrepareCommitIdFile(commitID string) { pjfile, err := projectfile.Parse(filepath.Join(s.Dirs.Work, constants.ConfigFileName)) require.NoError(s.T, err) diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index bd06ea42c4..1bff7cdcc3 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -1,15 +1,24 @@ package integration import ( + "archive/zip" + "encoding/json" "fmt" "path/filepath" + "runtime" "testing" + "time" "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/environment" + "github.com/ActiveState/cli/internal/fileutils" + "github.com/ActiveState/cli/internal/rtutils" + "github.com/ActiveState/cli/internal/runners/artifacts" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" "github.com/ActiveState/cli/pkg/projectfile" + "github.com/ActiveState/termtest" ) type BuildScriptIntegrationTestSuite struct { @@ -39,6 +48,116 @@ func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_NeedsReset() { suite.Require().FileExists(filepath.Join(ts.Dirs.Work, constants.BuildScriptFileName), ts.DebugMessage("")) } +func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_IngredientFunc() { + suite.T().Skip("Since this test creates commits and thus triggers new builds we should avoid running it too often") + + suite.OnlyRunForTags(tagsuite.BuildScripts) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + // Drop the env below once ingredient publishing is live + ts.Env = append(ts.Env, "ACTIVESTATE_API_HOST=pr14847.activestate.build") + + ts.LoginAsPersistentUser() + + // Replace with commented out code when ingredient publishing via buildscripts is live + ts.PrepareActiveStateYAML(fmt.Sprintf("project: %s/%s?commitID=%s\nconfig_version: %d\n", + "https://pr14847.activestate.build", "ActiveState-CLI/Empty", "ab2517ff-c9b4-4ffa-a5d5-58c20557c98e", projectfile.ConfigVersion)) + // ts.PrepareActiveStateYAML(fmt.Sprintf("project: %s/%s?commitID=%s\nconfig_version: %d\n", + // "https://"+constants.DefaultAPIHost, "ActiveState-CLI/Empty", "6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", projectfile.ConfigVersion)) + + var platformID string + switch runtime.GOOS { + case "windows": + platformID = constants.Win10Bit64UUID + case "darwin": + platformID = constants.MacBit64UUID + default: + platformID = constants.LinuxBit64UUID + } + + ts.PrepareBuildScript(fmt.Sprintf(` +at_time = "2024-10-30T21:31:33.000Z" +wheel = make_wheel( + at_time = at_time, + src = tag( + plan = ingredient( + build_deps = [ + Req(name = "python-module-builder", namespace = "builder", version = Gte(value = "0")), + Req(name = "python", namespace = "language", version = Gte(value = "3")), + Req(name = "setuptools", namespace = "language/python", version = Gte(value = "43.0.0")), + Req(name = "wheel", namespace = "language/python", version = Gte(value = "0")) + ], + src = [ + "sample_ingredient/**" + ] + ), + tag = "platform:%s" + ) +) + +main = wheel +`, platformID)) + + root := environment.GetRootPathUnsafe() + sampleSource := filepath.Join(root, "test", "integration", "testdata", "sample_ingredient") + sampleTarget := filepath.Join(ts.Dirs.Work, "sample_ingredient") + suite.Require().NoError(fileutils.Mkdir(sampleTarget)) + suite.Require().NoError(fileutils.CopyFiles(sampleSource, sampleTarget)) + + cp := ts.Spawn("commit") + cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt) + + // Running commit again should say there are no changes + // If this fails then there's likely an issue with calculating the file hash, or checking whether an ingredient + // already exists with the given hash. + cp = ts.Spawn("commit") + cp.Expect("No new changes to commit") + cp.ExpectExit() + + // Commit should've given us the hash + suite.Contains(string(fileutils.ReadFileUnsafe(filepath.Join(ts.Dirs.Work, constants.BuildScriptFileName))), "hash_readonly") + + // Wait for build + var out artifacts.StructuredOutput + suite.Require().NoError(rtutils.Timeout(func() error { + for { + cp = ts.Spawn("artifacts", "--output=json") + if err := cp.ExpectExitCode(0, termtest.OptExpectSilenceErrorHandler()); err != nil { + return err + } + + if err := json.Unmarshal(AssertValidJSON(suite.T(), cp), &out); err != nil { + return err + } + if out.BuildComplete { + break + } + time.Sleep(time.Second * 5) + } + return nil + }, e2e.RuntimeBuildSourcingTimeout)) + + suite.False(out.HasFailedArtifacts) + suite.Empty(out.Platforms[0].Artifacts[0].Errors) + + cp = ts.Spawn("artifacts", "dl", "--output=json", out.Platforms[0].Artifacts[0].ID) + cp.ExpectExitCode(0) + + var path string + suite.Require().NoError(json.Unmarshal(AssertValidJSON(suite.T(), cp), &path)) + + zipReader, err := zip.OpenReader(path) + suite.Require().NoError(err) + defer zipReader.Close() + files := map[string]struct{}{} + for _, f := range zipReader.File { + files[f.Name] = struct{}{} + } + suite.Contains(files, "sample_activestate/__init__.py") + suite.Contains(files, "sample_activestate-1.0.0.dist-info/WHEEL") +} + func TestBuildScriptIntegrationTestSuite(t *testing.T) { suite.Run(t, new(BuildScriptIntegrationTestSuite)) } diff --git a/test/integration/shared_int_test.go b/test/integration/shared_int_test.go index ba214d4ecb..60b3e89385 100644 --- a/test/integration/shared_int_test.go +++ b/test/integration/shared_int_test.go @@ -22,19 +22,25 @@ func init() { // AssertValidJSON asserts that the previous command emitted valid JSON and did not attempt to emit // any non-JSON/structured output. // This should only be called after a command has executed and all output is available. -func AssertValidJSON(t *testing.T, cp *e2e.SpawnedCmd) { +func AssertValidJSON(t *testing.T, cp *e2e.SpawnedCmd) []byte { output := cp.StrippedSnapshot() + if strings.Contains(output, `"errors":[`) { + assert.NotContains(t, output, `output not supported`, "The command attempted to emit non-JSON/structured output:\n"+output) + } if runtime.GOOS != "windows" { assert.True(t, json.Valid([]byte(output)), "The command produced invalid JSON/structured output:\n"+output) + return []byte(output) } else { - // Windows can trim the last byte for some reason. - assert.True( - t, - json.Valid([]byte(output)) || json.Valid([]byte(output+"}")) || json.Valid([]byte(output+"]")), - "The command produced invalid JSON/structured output:\n"+output, - ) - } - if strings.Contains(output, `"errors":[`) { - assert.NotContains(t, output, `output not supported`, "The command attempted to emit non-JSON/structured output:\n"+output) + switch { + case json.Valid([]byte(output)): + return []byte(output) + case json.Valid([]byte(output + "}")): + return []byte(output + "}") + case json.Valid([]byte(output + "]")): + return []byte(output + "]") + } + t.Fatal("The command produced invalid JSON/structured output:\n" + output) } + t.Fatal("Unreachable") + return nil } diff --git a/test/integration/testdata/sample_ingredient/LICENSE.txt b/test/integration/testdata/sample_ingredient/LICENSE.txt new file mode 100644 index 0000000000..c74aceda03 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 The Python Packaging Authority (PyPA) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test/integration/testdata/sample_ingredient/PKG-INFO b/test/integration/testdata/sample_ingredient/PKG-INFO new file mode 100644 index 0000000000..2d57b7f7b5 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/PKG-INFO @@ -0,0 +1,70 @@ +Metadata-Version: 2.1 +Name: sample_activestate +Version: 1.0.0 +Summary: Example Project of Building Wheels using ActiveState Platform +Home-page: https://github.com/pypa/sampleproject +Author: A. Random Developer +Author-email: author@example.com +Project-URL: Bug Reports, https://github.com/pypa/sampleproject/issues +Project-URL: Funding, https://donate.pypi.org +Project-URL: Say Thanks!, http://saythanks.io/to/example +Project-URL: Source, https://github.com/pypa/sampleproject/ +Keywords: sample,setuptools,development +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Build Tools +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.7, <4 +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Requires-Dist: peppercorn +Provides-Extra: dev +Requires-Dist: check-manifest; extra == "dev" +Provides-Extra: test +Requires-Dist: coverage; extra == "test" + +# A sample Python project + +![Python Logo](https://www.python.org/static/community_logos/python-logo.png "Sample inline image") + +A sample project that exists as an aid to the [Python Packaging User +Guide][packaging guide]'s [Tutorial on Packaging and Distributing +Projects][distribution tutorial]. + +This project does not aim to cover best practices for Python project +development as a whole. For example, it does not provide guidance or tool +recommendations for version control, documentation, or testing. + +[The source for this project is available here][src]. + +Most of the configuration for a Python project is done in the `setup.py` file, +an example of which is included in this project. You should edit this file +accordingly to adapt this sample project to your needs. + +---- + +This is the README file for the project. + +The file should use UTF-8 encoding and can be written using +[reStructuredText][rst] or [markdown][md use] with the appropriate [key set][md +use]. It will be used to generate the project webpage on PyPI and will be +displayed as the project homepage on common code-hosting services, and should be +written for that purpose. + +Typical contents for this file would include an overview of the project, basic +usage examples, etc. Generally, including the project changelog in here is not a +good idea, although a simple “What's New” section for the most recent version +may be appropriate. + +[packaging guide]: https://packaging.python.org +[distribution tutorial]: https://packaging.python.org/tutorials/packaging-projects/ +[src]: https://github.com/pypa/sampleproject +[rst]: http://docutils.sourceforge.net/rst.html +[md]: https://tools.ietf.org/html/rfc7764#section-3.5 "CommonMark variant" +[md use]: https://packaging.python.org/specifications/core-metadata/#description-content-type-optional diff --git a/test/integration/testdata/sample_ingredient/README.md b/test/integration/testdata/sample_ingredient/README.md new file mode 100644 index 0000000000..790503e288 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/README.md @@ -0,0 +1,40 @@ +# A sample Python project + +![Python Logo](https://www.python.org/static/community_logos/python-logo.png "Sample inline image") + +A sample project that exists as an aid to the [Python Packaging User +Guide][packaging guide]'s [Tutorial on Packaging and Distributing +Projects][distribution tutorial]. + +This project does not aim to cover best practices for Python project +development as a whole. For example, it does not provide guidance or tool +recommendations for version control, documentation, or testing. + +[The source for this project is available here][src]. + +Most of the configuration for a Python project is done in the `setup.py` file, +an example of which is included in this project. You should edit this file +accordingly to adapt this sample project to your needs. + +---- + +This is the README file for the project.......... + +The file should use UTF-8 encoding and can be written using +[reStructuredText][rst] or [markdown][md use] with the appropriate [key set][md +use]. It will be used to generate the project webpage on PyPI and will be +displayed as the project homepage on common code-hosting services, and should be +written for that purpose. + +Typical contents for this file would include an overview of the project, basic +usage examples, etc. Generally, including the project changelog in here is not a +good idea, although a simple “What's New” section for the most recent version +may be appropriate. + +[packaging guide]: https://packaging.python.org +[distribution tutorial]: https://packaging.python.org/tutorials/packaging-projects/ +[src]: https://github.com/pypa/sampleproject +[rst]: http://docutils.sourceforge.net/rst.html +[md]: https://tools.ietf.org/html/rfc7764#section-3.5 "CommonMark variant" +[md use]: https://packaging.python.org/specifications/core-metadata/#description-content-type-optional + diff --git a/test/integration/testdata/sample_ingredient/data/data_file b/test/integration/testdata/sample_ingredient/data/data_file new file mode 100644 index 0000000000..7c0646bfd5 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/data/data_file @@ -0,0 +1 @@ +some data \ No newline at end of file diff --git a/test/integration/testdata/sample_ingredient/pyproject.toml b/test/integration/testdata/sample_ingredient/pyproject.toml new file mode 100644 index 0000000000..9ebf20604e --- /dev/null +++ b/test/integration/testdata/sample_ingredient/pyproject.toml @@ -0,0 +1,5 @@ +[build-system] +# These are the assumed default build requirements from pip: +# https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support +requires = ["setuptools>=43.0.0", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/test/integration/testdata/sample_ingredient/setup.cfg b/test/integration/testdata/sample_ingredient/setup.cfg new file mode 100644 index 0000000000..10ba068c41 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/setup.cfg @@ -0,0 +1,7 @@ +[metadata] +license_files = LICENSE.txt + +[egg_info] +tag_build = +tag_date = 0 + diff --git a/test/integration/testdata/sample_ingredient/setup.py b/test/integration/testdata/sample_ingredient/setup.py new file mode 100644 index 0000000000..0f54d579f4 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/setup.py @@ -0,0 +1,181 @@ +"""A setuptools based setup module. + +See: +https://packaging.python.org/guides/distributing-packages-using-setuptools/ +https://github.com/pypa/sampleproject +""" + +# Always prefer setuptools over distutils +from setuptools import setup, find_packages +import pathlib + +here = pathlib.Path(__file__).parent.resolve() + +# Get the long description from the README file +long_description = (here / "README.md").read_text(encoding="utf-8") + +# Arguments marked as "Required" below must be included for upload to PyPI. +# Fields marked as "Optional" may be commented out. + +setup( + # This is the name of your project. The first time you publish this + # package, this name will be registered for you. It will determine how + # users can install this project, e.g.: + # + # $ pip install sampleproject + # + # And where it will live on PyPI: https://pypi.org/project/sampleproject/ + # + # There are some restrictions on what makes a valid project name + # specification here: + # https://packaging.python.org/specifications/core-metadata/#name + name="sample_activestate", # Required + # Versions should comply with PEP 440: + # https://www.python.org/dev/peps/pep-0440/ + # + # For a discussion on single-sourcing the version across setup.py and the + # project code, see + # https://packaging.python.org/guides/single-sourcing-package-version/ + version="1.0.0", # Required + # This is a one-line description or tagline of what your project does. This + # corresponds to the "Summary" metadata field: + # https://packaging.python.org/specifications/core-metadata/#summary + description="Example Project of Building Wheels using ActiveState Platform", # Optional + # This is an optional longer description of your project that represents + # the body of text which users will see when they visit PyPI. + # + # Often, this is the same as your README, so you can just read it in from + # that file directly (as we have already done above) + # + # This field corresponds to the "Description" metadata field: + # https://packaging.python.org/specifications/core-metadata/#description-optional + long_description=long_description, # Optional + # Denotes that our long_description is in Markdown; valid values are + # text/plain, text/x-rst, and text/markdown + # + # Optional if long_description is written in reStructuredText (rst) but + # required for plain-text or Markdown; if unspecified, "applications should + # attempt to render [the long_description] as text/x-rst; charset=UTF-8 and + # fall back to text/plain if it is not valid rst" (see link below) + # + # This field corresponds to the "Description-Content-Type" metadata field: + # https://packaging.python.org/specifications/core-metadata/#description-content-type-optional + long_description_content_type="text/markdown", # Optional (see note above) + # This should be a valid link to your project's main homepage. + # + # This field corresponds to the "Home-Page" metadata field: + # https://packaging.python.org/specifications/core-metadata/#home-page-optional + url="https://github.com/pypa/sampleproject", # Optional + # This should be your name or the name of the organization which owns the + # project. + author="A. Random Developer", # Optional + # This should be a valid email address corresponding to the author listed + # above. + author_email="author@example.com", # Optional + # Classifiers help users find your project by categorizing it. + # + # For a list of valid classifiers, see https://pypi.org/classifiers/ + classifiers=[ # Optional + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + "Development Status :: 3 - Alpha", + # Indicate who your project is intended for + "Intended Audience :: Developers", + "Topic :: Software Development :: Build Tools", + # Pick your license as you wish + "License :: OSI Approved :: MIT License", + # Specify the Python versions you support here. In particular, ensure + # that you indicate you support Python 3. These classifiers are *not* + # checked by 'pip install'. See instead 'python_requires' below. + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3 :: Only", + ], + # This field adds keywords for your project which will appear on the + # project page. What does your project relate to? + # + # Note that this is a list of additional keywords, separated + # by commas, to be used to assist searching for the distribution in a + # larger catalog. + keywords="sample, setuptools, development", # Optional + # When your source code is in a subdirectory under the project root, e.g. + # `src/`, it is necessary to specify the `package_dir` argument. + package_dir={"": "src"}, # Optional + # You can just specify package directories manually here if your project is + # simple. Or you can use find_packages(). + # + # Alternatively, if you just want to distribute a single Python file, use + # the `py_modules` argument instead as follows, which will expect a file + # called `my_module.py` to exist: + # + # py_modules=["my_module"], + # + packages=find_packages(where="src"), # Required + # Specify which Python versions you support. In contrast to the + # 'Programming Language' classifiers above, 'pip install' will check this + # and refuse to install the project if the version does not match. See + # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires + python_requires=">=3.7, <4", + # This field lists other packages that your project depends on to run. + # Any package you put here will be installed by pip when your project is + # installed, so they must be valid existing projects. + # + # For an analysis of "install_requires" vs pip's requirements files see: + # https://packaging.python.org/discussions/install-requires-vs-requirements/ + install_requires=["peppercorn"], # Optional + # List additional groups of dependencies here (e.g. development + # dependencies). Users will be able to install these using the "extras" + # syntax, for example: + # + # $ pip install sampleproject[dev] + # + # Similar to `install_requires` above, these must be valid existing + # projects. + extras_require={ # Optional + "dev": ["check-manifest"], + "test": ["coverage"], + }, + # If there are data files included in your packages that need to be + # installed, specify them here. + package_data={ # Optional + "sample": ["package_data.dat"], + }, + # Although 'package_data' is the preferred approach, in some case you may + # need to place data files outside of your packages. See: + # http://docs.python.org/distutils/setupscript.html#installing-additional-files + # + # In this case, 'data_file' will be installed into '/my_data' + data_files=[("my_data", ["data/data_file"])], # Optional + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # `pip` to create the appropriate form of executable for the target + # platform. + # + # For example, the following would provide a command called `sample` which + # executes the function `main` from this package when invoked: + entry_points={ # Optional + "console_scripts": [ + "sample=sample:main", + ], + }, + # List additional URLs that are relevant to your project as a dict. + # + # This field corresponds to the "Project-URL" metadata fields: + # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use + # + # Examples listed include a pattern for specifying where the package tracks + # issues, where the source is hosted, where to say thanks to the package + # maintainers, and where to support the project financially. The key is + # what's used to render the link text on PyPI. + project_urls={ # Optional + "Bug Reports": "https://github.com/pypa/sampleproject/issues", + "Funding": "https://donate.pypi.org", + "Say Thanks!": "http://saythanks.io/to/example", + "Source": "https://github.com/pypa/sampleproject/", + }, +) diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/PKG-INFO b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/PKG-INFO new file mode 100644 index 0000000000..2d57b7f7b5 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/PKG-INFO @@ -0,0 +1,70 @@ +Metadata-Version: 2.1 +Name: sample_activestate +Version: 1.0.0 +Summary: Example Project of Building Wheels using ActiveState Platform +Home-page: https://github.com/pypa/sampleproject +Author: A. Random Developer +Author-email: author@example.com +Project-URL: Bug Reports, https://github.com/pypa/sampleproject/issues +Project-URL: Funding, https://donate.pypi.org +Project-URL: Say Thanks!, http://saythanks.io/to/example +Project-URL: Source, https://github.com/pypa/sampleproject/ +Keywords: sample,setuptools,development +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Build Tools +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.7, <4 +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Requires-Dist: peppercorn +Provides-Extra: dev +Requires-Dist: check-manifest; extra == "dev" +Provides-Extra: test +Requires-Dist: coverage; extra == "test" + +# A sample Python project + +![Python Logo](https://www.python.org/static/community_logos/python-logo.png "Sample inline image") + +A sample project that exists as an aid to the [Python Packaging User +Guide][packaging guide]'s [Tutorial on Packaging and Distributing +Projects][distribution tutorial]. + +This project does not aim to cover best practices for Python project +development as a whole. For example, it does not provide guidance or tool +recommendations for version control, documentation, or testing. + +[The source for this project is available here][src]. + +Most of the configuration for a Python project is done in the `setup.py` file, +an example of which is included in this project. You should edit this file +accordingly to adapt this sample project to your needs. + +---- + +This is the README file for the project. + +The file should use UTF-8 encoding and can be written using +[reStructuredText][rst] or [markdown][md use] with the appropriate [key set][md +use]. It will be used to generate the project webpage on PyPI and will be +displayed as the project homepage on common code-hosting services, and should be +written for that purpose. + +Typical contents for this file would include an overview of the project, basic +usage examples, etc. Generally, including the project changelog in here is not a +good idea, although a simple “What's New” section for the most recent version +may be appropriate. + +[packaging guide]: https://packaging.python.org +[distribution tutorial]: https://packaging.python.org/tutorials/packaging-projects/ +[src]: https://github.com/pypa/sampleproject +[rst]: http://docutils.sourceforge.net/rst.html +[md]: https://tools.ietf.org/html/rfc7764#section-3.5 "CommonMark variant" +[md use]: https://packaging.python.org/specifications/core-metadata/#description-content-type-optional diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/SOURCES.txt b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/SOURCES.txt new file mode 100644 index 0000000000..d71d502dcc --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/SOURCES.txt @@ -0,0 +1,15 @@ +LICENSE.txt +README.md +pyproject.toml +setup.cfg +setup.py +data/data_file +src/sample_activestate/__init__.py +src/sample_activestate/simple.py +src/sample_activestate.egg-info/PKG-INFO +src/sample_activestate.egg-info/SOURCES.txt +src/sample_activestate.egg-info/dependency_links.txt +src/sample_activestate.egg-info/entry_points.txt +src/sample_activestate.egg-info/requires.txt +src/sample_activestate.egg-info/top_level.txt +tests/test_simple.py \ No newline at end of file diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/dependency_links.txt b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/dependency_links.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/entry_points.txt b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/entry_points.txt new file mode 100644 index 0000000000..21b1ba31a1 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +sample = sample:main diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/requires.txt b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/requires.txt new file mode 100644 index 0000000000..d3f379815f --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/requires.txt @@ -0,0 +1,7 @@ +peppercorn + +[dev] +check-manifest + +[test] +coverage diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/top_level.txt b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/top_level.txt new file mode 100644 index 0000000000..ac18024e0b --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate.egg-info/top_level.txt @@ -0,0 +1 @@ +sample_activestate diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate/__init__.py b/test/integration/testdata/sample_ingredient/src/sample_activestate/__init__.py new file mode 100644 index 0000000000..c8f1064994 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate/__init__.py @@ -0,0 +1,3 @@ +def main(): + """Entry point for the application script""" + print("Call your main application code here") diff --git a/test/integration/testdata/sample_ingredient/src/sample_activestate/simple.py b/test/integration/testdata/sample_ingredient/src/sample_activestate/simple.py new file mode 100644 index 0000000000..c929f885b6 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/src/sample_activestate/simple.py @@ -0,0 +1,2 @@ +def add_one(number): + return number + 1 diff --git a/test/integration/testdata/sample_ingredient/tests/test_simple.py b/test/integration/testdata/sample_ingredient/tests/test_simple.py new file mode 100644 index 0000000000..b7a0ee3a65 --- /dev/null +++ b/test/integration/testdata/sample_ingredient/tests/test_simple.py @@ -0,0 +1,17 @@ +# the inclusion of the tests module is not meant to offer best practices for +# testing in general, but rather to support the `find_packages` example in +# setup.py that excludes installing the "tests" package + +import unittest + +from sample.simple import add_one + + +class TestSimple(unittest.TestCase): + + def test_add_one(self): + self.assertEqual(add_one(5), 6) + + +if __name__ == '__main__': + unittest.main() From b810194b88a04e89425ca9473ed5672c3efe7440 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 13:40:55 -0700 Subject: [PATCH 310/440] Add comments --- internal/fileutils/fileutils.go | 2 ++ internal/runners/commit/ingredientcall.go | 2 ++ pkg/buildscript/unmarshal_buildexpression.go | 4 ++++ pkg/platform/api/graphql/request/publish.go | 1 + test/integration/buildscript_int_test.go | 5 +++++ 5 files changed, 14 insertions(+) diff --git a/internal/fileutils/fileutils.go b/internal/fileutils/fileutils.go index 8caaa9ed35..62e8e00f43 100644 --- a/internal/fileutils/fileutils.go +++ b/internal/fileutils/fileutils.go @@ -1249,6 +1249,8 @@ func globPath(path string) string { return result } +// CommonParentPath will return the common parent path of the given paths, provided they share a common path. +// If they do not all share a single common path the result will be empty. func CommonParentPath(paths []string) string { if len(paths) == 0 { return "" diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 683b127a0c..4f6e04d587 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "strings" "time" @@ -238,6 +239,7 @@ func (i *IngredientCall) resolveDependenciesByKey(key string, typ request.Depend return deps, nil } +// resolveFeatures turns ingredient features into the appropriate types used by our models func (i *IngredientCall) resolveFeatures() ([]request.PublishVariableFeature, error) { features := []request.PublishVariableFeature{} bsFeatures := i.funcCall.Argument("features") diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 033e5c6f95..41ff16fdcb 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -373,11 +373,15 @@ type requirementFunction struct { RequirementArguments []string } +// requirementFunctions holds the function names and arguments that hold objects which need to be translated to +// requirement functions (eg. `solve(requirements=..)`). var requirementFunctions = []requirementFunction{ {solveFuncName, []string{requirementsKey}}, {"ingredient", []string{"build_deps", "runtime_deps", "test_deps"}}, } +// transformToRequirementFuncs will look object assignments that need to be converted to requirement functions +// this works off of the defined requirementFunctions above. func (r *rawBuildScript) transformToRequirementFuncs() { // Iterate over the function calls within the buildscript for _, functionCall := range r.FuncCalls() { diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index d7b4ec091d..551bdb9e06 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -104,6 +104,7 @@ type ExampleDepVariables struct { Dependencies []PublishVariableDep `yaml:"dependencies,omitempty"` } +// MarshalJSON automatically takes the name and namespace and turns it into the path argument that the API expects func (p PublishVariables) MarshalJSON() ([]byte, error) { if p.Path == "" { if p.Name == "" || p.Namespace == "" { diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index 1bff7cdcc3..0ab1846ec1 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -99,12 +99,14 @@ wheel = make_wheel( main = wheel `, platformID)) + // Prepare sample ingredient source files root := environment.GetRootPathUnsafe() sampleSource := filepath.Join(root, "test", "integration", "testdata", "sample_ingredient") sampleTarget := filepath.Join(ts.Dirs.Work, "sample_ingredient") suite.Require().NoError(fileutils.Mkdir(sampleTarget)) suite.Require().NoError(fileutils.CopyFiles(sampleSource, sampleTarget)) + // Create a new commit, which will use the source files to create an ingredient if it doesn't already exist cp := ts.Spawn("commit") cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt) @@ -138,15 +140,18 @@ main = wheel return nil }, e2e.RuntimeBuildSourcingTimeout)) + // Ensure build didn't fail suite.False(out.HasFailedArtifacts) suite.Empty(out.Platforms[0].Artifacts[0].Errors) + // Download the wheel artifact that was produced from our source ingredient cp = ts.Spawn("artifacts", "dl", "--output=json", out.Platforms[0].Artifacts[0].ID) cp.ExpectExitCode(0) var path string suite.Require().NoError(json.Unmarshal(AssertValidJSON(suite.T(), cp), &path)) + // Read wheel archive and ensure it contains the expected files zipReader, err := zip.OpenReader(path) suite.Require().NoError(err) defer zipReader.Close() From 104a522fb837e39d3a45fe3ae4d3d2c7bfc31c6f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 14:02:37 -0700 Subject: [PATCH 311/440] Compatibility fixes --- internal/runners/commit/ingredientcall.go | 2 +- test/integration/buildscript_int_test.go | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 4f6e04d587..9b0f7542e3 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -83,7 +83,7 @@ func (i *IngredientCall) Resolve() error { if err != nil { return errs.Wrap(err, "Unable to determine latest revision time") } - i.script.SetAtTime(latest) + i.script.SetAtTime(latest, true) } // Add/update arguments on the buildscript ingredient function call diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index 0ab1846ec1..7eabc67a22 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -49,8 +49,6 @@ func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_NeedsReset() { } func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_IngredientFunc() { - suite.T().Skip("Since this test creates commits and thus triggers new builds we should avoid running it too often") - suite.OnlyRunForTags(tagsuite.BuildScripts) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -61,10 +59,9 @@ func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_IngredientFunc() { ts.LoginAsPersistentUser() // Replace with commented out code when ingredient publishing via buildscripts is live - ts.PrepareActiveStateYAML(fmt.Sprintf("project: %s/%s?commitID=%s\nconfig_version: %d\n", - "https://pr14847.activestate.build", "ActiveState-CLI/Empty", "ab2517ff-c9b4-4ffa-a5d5-58c20557c98e", projectfile.ConfigVersion)) - // ts.PrepareActiveStateYAML(fmt.Sprintf("project: %s/%s?commitID=%s\nconfig_version: %d\n", - // "https://"+constants.DefaultAPIHost, "ActiveState-CLI/Empty", "6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8", projectfile.ConfigVersion)) + projectURL := fmt.Sprintf("https://%s/%s?commitID=%s", "pr14847.activestate.build", "ActiveState-CLI/Empty", "ab2517ff-c9b4-4ffa-a5d5-58c20557c98e") + // projectURL := fmt.Sprintf("https://%s/%s?commitID=%s", constants.DefaultAPIHost, "ActiveState-CLI/Empty", "6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8") + ts.PrepareActiveStateYAML(fmt.Sprintf("project: %s\nconfig_version: %d\n", projectURL, projectfile.ConfigVersion)) var platformID string switch runtime.GOOS { @@ -76,8 +73,12 @@ func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_IngredientFunc() { platformID = constants.LinuxBit64UUID } + denoter := "```" ts.PrepareBuildScript(fmt.Sprintf(` -at_time = "2024-10-30T21:31:33.000Z" +%s +Project: %s +Time: "2024-10-30T21:31:33.000Z" +%s wheel = make_wheel( at_time = at_time, src = tag( @@ -97,7 +98,7 @@ wheel = make_wheel( ) main = wheel -`, platformID)) +`, denoter, projectURL, denoter, platformID)) // Prepare sample ingredient source files root := environment.GetRootPathUnsafe() @@ -114,7 +115,7 @@ main = wheel // If this fails then there's likely an issue with calculating the file hash, or checking whether an ingredient // already exists with the given hash. cp = ts.Spawn("commit") - cp.Expect("No new changes to commit") + cp.Expect("no new changes") cp.ExpectExit() // Commit should've given us the hash From 32bda03deada27dea928164377162dc89938b949 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 14:42:47 -0700 Subject: [PATCH 312/440] Disable test for now --- test/integration/buildscript_int_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index 7eabc67a22..9457158b59 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -49,6 +49,8 @@ func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_NeedsReset() { } func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_IngredientFunc() { + suite.T().Skip("Please enable once ingredient publishing via buildscript on the API is live") + suite.OnlyRunForTags(tagsuite.BuildScripts) ts := e2e.New(suite.T(), false) defer ts.Close() From 69236e0dc1ce8fb5f1a1347381ff245680205fde Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 31 Oct 2024 14:47:12 -0700 Subject: [PATCH 313/440] Drop old test --- internal/fileutils/fileutils_test.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/internal/fileutils/fileutils_test.go b/internal/fileutils/fileutils_test.go index a59f3dd5bb..b3ac45ad17 100644 --- a/internal/fileutils/fileutils_test.go +++ b/internal/fileutils/fileutils_test.go @@ -699,28 +699,3 @@ func TestCommonParentPath(t *testing.T) { }) } } - -func TestCommonParentPathx(t *testing.T) { - tests := []struct { - a, b string - expected string - }{ - {"./folder1/file.txt", "./folder1/subfolder/file.txt", "./folder1"}, - {"./folder1/file.txt", "./folder2/file.txt", "."}, - {"./folder1/subfolder1/file.txt", "./folder1/subfolder2/file.txt", "./folder1"}, - {"./folder1/", "./folder1/subfolder/", "./folder1"}, - {"./folder1/file.txt", "./folder1/file.txt", "./folder1/file.txt"}, - {"./folder1/file.txt", "./folder1/", "./folder1"}, - {"./folder1/file.txt", "./folder2/", "."}, - {"", "./folder1/file.txt", ""}, - {"./folder1/file.txt", "", ""}, - {"", "", ""}, - } - - for x, test := range tests { - result := commonParentPath(test.a, test.b) - if result != test.expected { - t.Errorf("%d: commonParentPath(%q, %q) = %q; expected %q", x, test.a, test.b, result, test.expected) - } - } -} From 48614a3d36add9b799a2efa5d6976817fa2074a2 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 31 Oct 2024 18:47:16 -0400 Subject: [PATCH 314/440] Initial support for rendering wrapped, bulleted lists. Also do not put wrapped links on their own line. --- internal/colorize/{cropped.go => wrap.go} | 23 ++--- .../{cropped_test.go => wrap_test.go} | 36 ++++---- internal/output/plain.go | 10 +-- internal/output/renderers/bulletlist.go | 89 +++++++++++++++++++ .../runbits/dependencies/changesummary.go | 10 +-- internal/runbits/dependencies/summary.go | 12 +-- internal/runners/artifacts/artifacts.go | 21 +++-- internal/runners/cve/cve.go | 9 +- internal/table/table.go | 4 +- 9 files changed, 150 insertions(+), 64 deletions(-) rename internal/colorize/{cropped.go => wrap.go} (79%) rename internal/colorize/{cropped_test.go => wrap_test.go} (62%) create mode 100644 internal/output/renderers/bulletlist.go diff --git a/internal/colorize/cropped.go b/internal/colorize/wrap.go similarity index 79% rename from internal/colorize/cropped.go rename to internal/colorize/wrap.go index 793a139513..74ca14e77b 100644 --- a/internal/colorize/cropped.go +++ b/internal/colorize/wrap.go @@ -5,14 +5,14 @@ import ( "strings" ) -type CroppedLines []CroppedLine +type WrappedLines []WrappedLine -type CroppedLine struct { +type WrappedLine struct { Line string Length int } -func (c CroppedLines) String() string { +func (c WrappedLines) String() string { var result string for _, crop := range c { result = result + crop.Line @@ -22,8 +22,9 @@ func (c CroppedLines) String() string { } var indentRegexp = regexp.MustCompile(`^([ ]+)`) +var isLinkRegexp = regexp.MustCompile(`\s*(\[[^\]]+\])?https?://`) -func GetCroppedText(text string, maxLen int, includeLineEnds bool) CroppedLines { +func Wrap(text string, maxLen int, includeLineEnds bool, continuation string) WrappedLines { indent := "" if indentMatch := indentRegexp.FindStringSubmatch(text); indentMatch != nil { indent = indentMatch[0] @@ -32,11 +33,13 @@ func GetCroppedText(text string, maxLen int, includeLineEnds bool) CroppedLines } } - entries := make([]CroppedLine, 0) + maxLen -= len(continuation) + + entries := make([]WrappedLine, 0) colorCodes := colorRx.FindAllStringSubmatchIndex(text, -1) isLineEnd := false - entry := CroppedLine{} + entry := WrappedLine{} for pos, amend := range text { inColorTag := inRange(pos, colorCodes) @@ -69,8 +72,8 @@ func GetCroppedText(text string, maxLen int, includeLineEnds bool) CroppedLines } } // Extract the word from the current line if it doesn't start the line. - if i > 0 && i < len(entry.Line)-1 { - wrapped = indent + entry.Line[i:] + if i > 0 && i < len(entry.Line)-1 && !isLinkRegexp.MatchString(entry.Line[i:]) { + wrapped = indent + continuation + entry.Line[i:] entry.Line = entry.Line[:i] entry.Length -= wrappedLength isLineEnd = true // emulate for wrapping purposes @@ -79,11 +82,11 @@ func GetCroppedText(text string, maxLen int, includeLineEnds bool) CroppedLines } } entries = append(entries, entry) - entry = CroppedLine{Line: wrapped, Length: wrappedLength} + entry = WrappedLine{Line: wrapped, Length: wrappedLength} } if isLineEnd && includeLineEnds { - entries = append(entries, CroppedLine{"\n", 1}) + entries = append(entries, WrappedLine{"\n", 1}) } } diff --git a/internal/colorize/cropped_test.go b/internal/colorize/wrap_test.go similarity index 62% rename from internal/colorize/cropped_test.go rename to internal/colorize/wrap_test.go index c16dac4544..f4d793c8a0 100644 --- a/internal/colorize/cropped_test.go +++ b/internal/colorize/wrap_test.go @@ -7,7 +7,7 @@ import ( "testing" ) -func Test_GetCroppedText(t *testing.T) { +func Test_Wrap(t *testing.T) { type args struct { text string maxLen int @@ -15,74 +15,74 @@ func Test_GetCroppedText(t *testing.T) { tests := []struct { name string args args - want CroppedLines + want WrappedLines }{ { "No split", args{"[HEADING]Hello[/RESET]", 5}, - []CroppedLine{{"[HEADING]Hello[/RESET]", 5}}, + []WrappedLine{{"[HEADING]Hello[/RESET]", 5}}, }, { "Split", args{"[HEADING]Hello[/RESET]", 3}, - []CroppedLine{{"[HEADING]Hel", 3}, {"lo[/RESET]", 2}}, + []WrappedLine{{"[HEADING]Hel", 3}, {"lo[/RESET]", 2}}, }, { "Split multiple", args{"[HEADING]Hello World[/RESET]", 3}, - []CroppedLine{{"[HEADING]Hel", 3}, {"lo ", 3}, {"Wor", 3}, {"ld[/RESET]", 2}}, + []WrappedLine{{"[HEADING]Hel", 3}, {"lo ", 3}, {"Wor", 3}, {"ld[/RESET]", 2}}, }, { "Split multiple no match", args{"Hello World", 3}, - []CroppedLine{{"Hel", 3}, {"lo ", 3}, {"Wor", 3}, {"ld", 2}}, + []WrappedLine{{"Hel", 3}, {"lo ", 3}, {"Wor", 3}, {"ld", 2}}, }, { "No split no match", args{"Hello", 5}, - []CroppedLine{{"Hello", 5}}, + []WrappedLine{{"Hello", 5}}, }, { "Split multi-byte characters", args{"✔ol1✔ol2✔ol3", 4}, - []CroppedLine{{"✔ol1", 4}, {"✔ol2", 4}, {"✔ol3", 4}}, + []WrappedLine{{"✔ol1", 4}, {"✔ol2", 4}, {"✔ol3", 4}}, }, { "No split multi-byte character with tags", args{"[HEADING]✔ Some Text[/RESET]", 20}, - []CroppedLine{{"[HEADING]✔ Some Text[/RESET]", 11}}, + []WrappedLine{{"[HEADING]✔ Some Text[/RESET]", 11}}, }, { "Split multi-byte character with tags", args{"[HEADING]✔ Some Text[/RESET]", 6}, - []CroppedLine{{"[HEADING]✔ Some", 6}, {" Text[/RESET]", 5}}, + []WrappedLine{{"[HEADING]✔ Some", 6}, {" Text[/RESET]", 5}}, }, { "Split multi-byte character with tags by words", args{"[HEADING]✔ Some Text[/RESET]", 10}, - []CroppedLine{{"[HEADING]✔ Some ", 7}, {"Text[/RESET]", 4}}, + []WrappedLine{{"[HEADING]✔ Some ", 7}, {"Text[/RESET]", 4}}, }, { "Split line break", args{"[HEADING]Hel\nlo[/RESET]", 5}, - []CroppedLine{{"[HEADING]Hel", 3}, {"lo[/RESET]", 2}}, + []WrappedLine{{"[HEADING]Hel", 3}, {"lo[/RESET]", 2}}, }, { "Split nested", args{"[HEADING][NOTICE]Hello[/RESET][/RESET]", 3}, - []CroppedLine{{"[HEADING][NOTICE]Hel", 3}, {"lo[/RESET][/RESET]", 2}}, + []WrappedLine{{"[HEADING][NOTICE]Hel", 3}, {"lo[/RESET][/RESET]", 2}}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := GetCroppedText(tt.args.text, tt.args.maxLen, false); !reflect.DeepEqual(got, tt.want) { - t.Errorf("getCroppedText() = %v, want %v", got, tt.want) + if got := Wrap(tt.args.text, tt.args.maxLen, false, ""); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Wrap() = %v, want %v", got, tt.want) } }) } } -func Test_GetCroppedTextAsString(t *testing.T) { +func Test_WrapAsString(t *testing.T) { tests := []struct { name string text string @@ -119,11 +119,11 @@ func Test_GetCroppedTextAsString(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := GetCroppedText(tt.text, 999, true); !reflect.DeepEqual(got.String(), tt.text) { + if got := Wrap(tt.text, 999, true, ""); !reflect.DeepEqual(got.String(), tt.text) { escape := func(v string) string { return strings.Replace(v, "\n", "\\n", -1) } - t.Errorf("getCroppedText() = %v, want %v (crop data: %s)", escape(got.String()), escape(tt.text), escape(fmt.Sprintf("%#v", got))) + t.Errorf("Wrap() = %v, want %v (crop data: %s)", escape(got.String()), escape(tt.text), escape(fmt.Sprintf("%#v", got))) } }) } diff --git a/internal/output/plain.go b/internal/output/plain.go index 3859303cf9..a927ced6f1 100644 --- a/internal/output/plain.go +++ b/internal/output/plain.go @@ -103,7 +103,7 @@ func (f *Plain) write(writer io.Writer, value interface{}) { // writeNow is a little helper that just writes the given value to the requested writer (no marshalling) func (f *Plain) writeNow(writer io.Writer, value string) { if f.Config().Interactive { - value = wordWrap(value) + value = colorize.Wrap(value, termutils.GetWidth(), true, "").String() } _, err := colorize.Colorize(value, writer, !f.cfg.Colored) if err != nil { @@ -111,14 +111,6 @@ func (f *Plain) writeNow(writer io.Writer, value string) { } } -func wordWrap(text string) string { - return wordWrapWithWidth(text, termutils.GetWidth()) -} - -func wordWrapWithWidth(text string, width int) string { - return colorize.GetCroppedText(text, width, true).String() -} - const nilText = "" // sprint will marshal and return the given value as a string diff --git a/internal/output/renderers/bulletlist.go b/internal/output/renderers/bulletlist.go new file mode 100644 index 0000000000..947013a5d0 --- /dev/null +++ b/internal/output/renderers/bulletlist.go @@ -0,0 +1,89 @@ +package renderers + +import ( + "fmt" + "strings" + + "github.com/ActiveState/cli/internal/colorize" + "github.com/ActiveState/cli/internal/multilog" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/termutils" +) + +type bulletList struct { + prefix string + items []string + bullets []string +} + +// BulletTree outputs a list like: +// +// ├─ one +// ├─ two +// │ wrapped +// └─ three +var BulletTree = []string{output.TreeMid, output.TreeMid, output.TreeLink, output.TreeEnd} + +// HeadedBulletTree outputs a list like: +// +// one +// ├─ two +// │ wrapped +// └─ three +var HeadedBulletTree = []string{"", output.TreeMid, output.TreeLink, output.TreeEnd} + +// NewBulletList returns a printable list of items prefixed with the given set of bullets. +// The set of bullets should contain four items: the bullet for the first item (e.g. ""); the +// bullet for each subsequent item (e.g. "├─"); the bullet for an item's wrapped lines, if any +// (e.g. "│"); and the bullet for the last item (e.g. "└─"). +// The returned list can be passed to a plain printer. It should not be passed to a structured +// printer. +func NewBulletList(prefix string, bullets, items []string) *bulletList { + if len(bullets) != 4 { + multilog.Error("Invalid bullet list: 4 bullets required") + bullets = BulletTree + } + return &bulletList{prefix, items, bullets} +} + +func (b *bulletList) MarshalOutput(format output.Format) interface{} { + out := make([]string, len(b.items)) + + // Determine the indentation of each item. + // If the prefix is pure indentation, then the indent is that prefix. + // If the prefix is not pure indentation, then the indent is the number of characters between + // the first non-space character and the end of the prefix. + // For example, both "* " and " * " have and indent of 2 because items should be indented to + // match the bullet item's left margin (note that wrapping will auto-indent to match the leading + // space in the second example). + indent := b.prefix + if nonIndent := strings.TrimLeft(b.prefix, " "); nonIndent != "" { + indent = strings.Repeat(" ", len(nonIndent)) + } + + for i, item := range b.items { + bullet := b.bullets[0] + if len(b.items) == 1 { + bullet = b.bullets[3] // special case list length of one; use last bullet + } + + if i == 0 { + if bullet != "" { + bullet += " " + } + item = b.prefix + bullet + item + } else { + bullet = b.bullets[1] + continuation := indent + b.bullets[2] + " " + if i == len(b.items)-1 { + bullet = b.bullets[3] // this is the last item + continuation = " " + } + wrapped := colorize.Wrap(item, termutils.GetWidth()-len(indent), true, continuation).String() + item = fmt.Sprintf("%s%s %s", indent, bullet, wrapped) + } + out[i] = item + } + + return strings.Join(out, "\n") +} diff --git a/internal/runbits/dependencies/changesummary.go b/internal/runbits/dependencies/changesummary.go index 38720f40d1..75e9a760f6 100644 --- a/internal/runbits/dependencies/changesummary.go +++ b/internal/runbits/dependencies/changesummary.go @@ -9,6 +9,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/output/renderers" "github.com/ActiveState/cli/internal/sliceutils" "github.com/ActiveState/cli/pkg/buildplan" ) @@ -107,12 +108,8 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, // └─ name@oldVersion → name@newVersion (Updated) // depending on whether or not it has subdependencies, and whether or not showUpdatedPackages is // `true`. + items := make([]string, len(directDependencies)) for i, ingredient := range directDependencies { - prefix := output.TreeMid - if i == len(directDependencies)-1 { - prefix = output.TreeEnd - } - // Retrieve runtime dependencies, and then filter out any dependencies that are common between all added ingredients. runtimeDeps := ingredient.RuntimeDependencies(true) runtimeDeps = runtimeDeps.Filter(func(i *buildplan.Ingredient) bool { _, ok := commonDependencies[i.IngredientID]; return !ok }) @@ -130,8 +127,9 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, item = fmt.Sprintf("[ACTIONABLE]%s@%s[/RESET] → %s (%s)", oldVersion.Name, oldVersion.Version, item, locale.Tl("updated", "updated")) } - out.Notice(fmt.Sprintf(" [DISABLED]%s[/RESET] %s", prefix, item)) + items[i] = item } + out.Notice(renderers.NewBulletList(" ", renderers.BulletTree, items)) out.Notice("") // blank line } diff --git a/internal/runbits/dependencies/summary.go b/internal/runbits/dependencies/summary.go index 33fc2f6a49..a9008ac3a5 100644 --- a/internal/runbits/dependencies/summary.go +++ b/internal/runbits/dependencies/summary.go @@ -7,6 +7,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/output/renderers" "github.com/ActiveState/cli/pkg/buildplan" ) @@ -27,12 +28,8 @@ func OutputSummary(out output.Outputer, directDependencies buildplan.Artifacts) out.Notice("") // blank line out.Notice(locale.Tl("setting_up_dependencies", " Setting up the following dependencies:")) + items := make([]string, len(ingredients)) for i, ingredient := range ingredients { - prefix := " " + output.TreeMid - if i == len(ingredients)-1 { - prefix = " " + output.TreeEnd - } - subDependencies := ingredient.RuntimeDependencies(true) if _, isCommon := commonDependencies[ingredient.IngredientID]; !isCommon { // If the ingredient is itself not a common sub-dependency; filter out any common sub dependencies so we don't @@ -44,10 +41,9 @@ func OutputSummary(out output.Outputer, directDependencies buildplan.Artifacts) subdepLocale = locale.Tl("summary_subdeps", "([ACTIONABLE]{{.V0}}[/RESET] sub-dependencies)", strconv.Itoa(numSubs)) } - item := fmt.Sprintf("[ACTIONABLE]%s@%s[/RESET] %s", ingredient.Name, ingredient.Version, subdepLocale) - - out.Notice(fmt.Sprintf("[DISABLED]%s[/RESET] %s", prefix, item)) + items[i] = fmt.Sprintf("[ACTIONABLE]%s@%s[/RESET] %s", ingredient.Name, ingredient.Version, subdepLocale) } + out.Notice(renderers.NewBulletList(" ", renderers.BulletTree, items)) out.Notice("") // blank line } diff --git a/internal/runners/artifacts/artifacts.go b/internal/runners/artifacts/artifacts.go index 078d2668a1..6d28db8ab0 100644 --- a/internal/runners/artifacts/artifacts.go +++ b/internal/runners/artifacts/artifacts.go @@ -12,6 +12,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/output/renderers" "github.com/ActiveState/cli/internal/primer" buildplanner_runbit "github.com/ActiveState/cli/internal/runbits/buildplanner" "github.com/ActiveState/cli/pkg/buildplan" @@ -204,9 +205,13 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error { for _, artifact := range platform.Artifacts { switch { case len(artifact.Errors) > 0: - b.out.Print(fmt.Sprintf(" • %s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed"))) - b.out.Print(fmt.Sprintf(" %s %s: [ERROR]%s[/RESET]", output.TreeMid, locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": "))) - b.out.Print(fmt.Sprintf(" %s %s: [ACTIONABLE]%s[/RESET]", output.TreeEnd, locale.T("artifact_status_failed_log"), artifact.LogURL)) + b.out.Print(renderers.NewBulletList(" • ", + renderers.HeadedBulletTree, + []string{ + fmt.Sprintf("%s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed")), + fmt.Sprintf("%s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")), + fmt.Sprintf("%s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL), + })) continue case artifact.status == types.ArtifactSkipped: b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped"))) @@ -228,9 +233,13 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error { for _, artifact := range platform.Packages { switch { case len(artifact.Errors) > 0: - b.out.Print(fmt.Sprintf(" • %s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed"))) - b.out.Print(fmt.Sprintf(" %s %s: [ERROR]%s[/RESET]", output.TreeMid, locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": "))) - b.out.Print(fmt.Sprintf(" %s %s: [ACTIONABLE]%s[/RESET]", output.TreeEnd, locale.T("artifact_status_failed_log"), artifact.LogURL)) + b.out.Print(renderers.NewBulletList(" • ", + renderers.HeadedBulletTree, + []string{ + fmt.Sprintf("%s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed")), + fmt.Sprintf("%s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")), + fmt.Sprintf("%s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL), + })) continue case artifact.status == types.ArtifactSkipped: b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped"))) diff --git a/internal/runners/cve/cve.go b/internal/runners/cve/cve.go index 20f6e51116..f1f886307b 100644 --- a/internal/runners/cve/cve.go +++ b/internal/runners/cve/cve.go @@ -10,6 +10,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/output/renderers" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/localcommit" @@ -193,17 +194,15 @@ func (rd *cveOutput) MarshalOutput(format output.Format) interface{} { return false }) + items := make([]string, len(ap.Details)) for i, d := range ap.Details { - bar := output.TreeMid - if i == len(ap.Details)-1 { - bar = output.TreeEnd - } severity := d.Severity if severity == "CRITICAL" { severity = fmt.Sprintf("[ERROR]%-10s[/RESET]", severity) } - rd.output.Print(fmt.Sprintf(" %s %-10s [ACTIONABLE]%s[/RESET]", bar, severity, d.CveID)) + items[i] = fmt.Sprintf("%-10s [ACTIONABLE]%s[/RESET]", severity, d.CveID) } + rd.output.Print(renderers.NewBulletList("", renderers.BulletTree, items)) rd.output.Print("") } diff --git a/internal/table/table.go b/internal/table/table.go index b609daa29f..56924766f1 100644 --- a/internal/table/table.go +++ b/internal/table/table.go @@ -180,9 +180,9 @@ func renderRow(providedColumns []string, colWidths []int) string { widths[len(widths)-1] = mathutils.Total(colWidths[len(widths)-1:]...) } - croppedColumns := []colorize.CroppedLines{} + croppedColumns := []colorize.WrappedLines{} for n, column := range providedColumns { - croppedColumns = append(croppedColumns, colorize.GetCroppedText(column, widths[n]-(padding*2), false)) + croppedColumns = append(croppedColumns, colorize.Wrap(column, widths[n]-(padding*2), false, "")) } var rendered = true From 71d8071f839fd511b2e341bed4422369d4c205a7 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 1 Nov 2024 12:46:06 -0700 Subject: [PATCH 315/440] Remove unused mutex --- cmd/state-svc/internal/hash/file_hasher.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index bda4b4c291..91218e656b 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" "sort" - "sync" "time" "github.com/ActiveState/cli/internal/errs" @@ -22,7 +21,6 @@ type fileCache interface { type FileHasher struct { cache fileCache - mutex *sync.Mutex } type hashedFile struct { @@ -34,7 +32,6 @@ type hashedFile struct { func NewFileHasher() *FileHasher { return &FileHasher{ cache: cache.New(24*time.Hour, 24*time.Hour), - mutex: &sync.Mutex{}, } } From dae0380b0dbaf42656f1d8346a5ff61ec1cce9de Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 1 Nov 2024 12:46:33 -0700 Subject: [PATCH 316/440] Enable ingredientcall integration test --- test/integration/buildscript_int_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index 9457158b59..6e91267f25 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -49,20 +49,13 @@ func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_NeedsReset() { } func (suite *BuildScriptIntegrationTestSuite) TestBuildScript_IngredientFunc() { - suite.T().Skip("Please enable once ingredient publishing via buildscript on the API is live") - suite.OnlyRunForTags(tagsuite.BuildScripts) ts := e2e.New(suite.T(), false) defer ts.Close() - // Drop the env below once ingredient publishing is live - ts.Env = append(ts.Env, "ACTIVESTATE_API_HOST=pr14847.activestate.build") - ts.LoginAsPersistentUser() - // Replace with commented out code when ingredient publishing via buildscripts is live - projectURL := fmt.Sprintf("https://%s/%s?commitID=%s", "pr14847.activestate.build", "ActiveState-CLI/Empty", "ab2517ff-c9b4-4ffa-a5d5-58c20557c98e") - // projectURL := fmt.Sprintf("https://%s/%s?commitID=%s", constants.DefaultAPIHost, "ActiveState-CLI/Empty", "6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8") + projectURL := fmt.Sprintf("https://%s/%s?commitID=%s", constants.DefaultAPIHost, "ActiveState-CLI/Empty", "6d79f2ae-f8b5-46bd-917a-d4b2558ec7b8") ts.PrepareActiveStateYAML(fmt.Sprintf("project: %s\nconfig_version: %d\n", projectURL, projectfile.ConfigVersion)) var platformID string From df7ebad2e11ff5de76b58911def41e3fbbe18277 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 1 Nov 2024 13:00:37 -0700 Subject: [PATCH 317/440] Handle pannics in the poller --- cmd/state-svc/internal/resolver/resolver.go | 33 ++++++++------------- internal/poller/poller.go | 11 ++++++- internal/runbits/panics/panics.go | 7 +++++ 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index 7995751f88..512f5e921d 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -24,9 +24,9 @@ import ( "github.com/ActiveState/cli/internal/graph" "github.com/ActiveState/cli/internal/logging" configMediator "github.com/ActiveState/cli/internal/mediators/config" - "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/poller" "github.com/ActiveState/cli/internal/rtutils/ptr" + "github.com/ActiveState/cli/internal/runbits/panics" "github.com/ActiveState/cli/internal/updater" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/projectfile" @@ -50,6 +50,7 @@ type Resolver struct { // var _ genserver.ResolverRoot = &Resolver{} // Must implement ResolverRoot func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Resolver, error) { + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() msg, err := messages.New(cfg, auth) if err != nil { return nil, errs.Wrap(err, "Could not initialize messages") @@ -110,7 +111,7 @@ func (r *Resolver) Query() genserver.QueryResolver { return r } func (r *Resolver) Mutation() genserver.MutationResolver { return r } func (r *Resolver) Version(ctx context.Context) (*graph.Version, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() r.an.EventWithLabel(anaConsts.CatStateSvc, "endpoint", "Version") logging.Debug("Version resolver") @@ -126,7 +127,7 @@ func (r *Resolver) Version(ctx context.Context) (*graph.Version, error) { } func (r *Resolver) AvailableUpdate(ctx context.Context, desiredChannel, desiredVersion string) (*graph.AvailableUpdate, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() if desiredChannel == "" { desiredChannel = constants.ChannelName @@ -174,7 +175,7 @@ func (r *Resolver) AvailableUpdate(ctx context.Context, desiredChannel, desiredV } func (r *Resolver) Projects(ctx context.Context) ([]*graph.Project, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() r.an.EventWithLabel(anaConsts.CatStateSvc, "endpoint", "Projects") logging.Debug("Projects resolver") @@ -194,7 +195,7 @@ func (r *Resolver) Projects(ctx context.Context) ([]*graph.Project, error) { } func (r *Resolver) AnalyticsEvent(_ context.Context, category, action, source string, _label *string, dimensionsJson string) (*graph.AnalyticsEventResponse, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() logging.Debug("Analytics event resolver: %s - %s: %s (%s)", category, action, ptr.From(_label, "NIL"), source) @@ -228,7 +229,7 @@ func (r *Resolver) AnalyticsEvent(_ context.Context, category, action, source st } func (r *Resolver) ReportRuntimeUsage(_ context.Context, pid int, exec, source string, dimensionsJSON string) (*graph.ReportRuntimeUsageResponse, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() logging.Debug("Runtime usage resolver: %d - %s", pid, exec) var dims *dimensions.Values @@ -242,26 +243,26 @@ func (r *Resolver) ReportRuntimeUsage(_ context.Context, pid int, exec, source s } func (r *Resolver) CheckMessages(ctx context.Context, command string, flags []string) ([]*graph.MessageInfo, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() logging.Debug("Check messages resolver") return r.messages.Check(command, flags) } func (r *Resolver) ConfigChanged(ctx context.Context, key string) (*graph.ConfigChangedResponse, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() go configMediator.NotifyListeners(key) return &graph.ConfigChangedResponse{Received: true}, nil } func (r *Resolver) FetchLogTail(ctx context.Context) (string, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() return logging.ReadTail(), nil } func (r *Resolver) GetProcessesInUse(ctx context.Context, execDir string) ([]*graph.ProcessInfo, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() inUse := r.rtwatch.GetProcessesInUse(execDir) processes := make([]*graph.ProcessInfo, 0, len(inUse)) @@ -272,7 +273,7 @@ func (r *Resolver) GetProcessesInUse(ctx context.Context, execDir string) ([]*gr } func (r *Resolver) GetJwt(ctx context.Context) (*graph.Jwt, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() if err := r.auth.MaybeRenew(); err != nil { return nil, errs.Wrap(err, "Could not renew auth token") @@ -308,7 +309,7 @@ func (r *Resolver) GetJwt(ctx context.Context) (*graph.Jwt, error) { } func (r *Resolver) HashGlobs(ctx context.Context, wd string, globs []string) (*graph.GlobResult, error) { - defer func() { handlePanics(recover(), debug.Stack()) }() + defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() hash, files, err := r.fileHasher.HashFiles(wd, globs) if err != nil { @@ -341,11 +342,3 @@ func (r *Resolver) SetCache(ctx context.Context, key string, value string, expir r.globalCache.Set(key, value, time.Duration(expiry)*time.Second) return &graphqltypes.Void{}, nil } - -func handlePanics(recovered interface{}, stack []byte) { - if recovered != nil { - multilog.Error("Panic: %v", recovered) - logging.Debug("Stack: %s", string(stack)) - panic(recovered) // We're only logging the panic, not interrupting it - } -} diff --git a/internal/poller/poller.go b/internal/poller/poller.go index 56de212f85..563caa9a0b 100644 --- a/internal/poller/poller.go +++ b/internal/poller/poller.go @@ -1,6 +1,7 @@ package poller import ( + "runtime/debug" "sync" "time" @@ -8,6 +9,7 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/runbits/errors" + "github.com/ActiveState/cli/internal/runbits/panics" ) type Poller struct { @@ -19,8 +21,15 @@ type Poller struct { } func New(interval time.Duration, pollFunc func() (interface{}, error)) *Poller { + wrappedFn := func() (interface{}, error) { + defer func() { + panics.LogAndPanic(recover(), debug.Stack()) + }() + return pollFunc() + } + p := &Poller{ - pollFunc: pollFunc, + pollFunc: wrappedFn, done: make(chan struct{}), } go p.start(interval) diff --git a/internal/runbits/panics/panics.go b/internal/runbits/panics/panics.go index 7dd3f3fc5f..ed710415dc 100644 --- a/internal/runbits/panics/panics.go +++ b/internal/runbits/panics/panics.go @@ -34,3 +34,10 @@ func LogPanics(recovered interface{}, stack []byte) bool { } return false } + +// LogAndPanic produces actionable output for panic events (that shouldn't happen) and panics +func LogAndPanic(recovered interface{}, stack []byte) { + multilog.Error("Panic: %v", recovered) + logging.Debug("Stack: %s", string(stack)) + panic(recovered) +} From 675c46e0e710ee2216fe20b1f6743e32c4fecca6 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 1 Nov 2024 13:00:55 -0700 Subject: [PATCH 318/440] Handle potential panics in messages --- cmd/state-svc/internal/messages/messages.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/state-svc/internal/messages/messages.go b/cmd/state-svc/internal/messages/messages.go index 88cdf78f9e..a377b5d911 100644 --- a/cmd/state-svc/internal/messages/messages.go +++ b/cmd/state-svc/internal/messages/messages.go @@ -74,7 +74,11 @@ func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, if cacheValue == nil { return []*graph.MessageInfo{}, nil } - allMessages := cacheValue.([]*graph.MessageInfo) + + allMessages, ok := cacheValue.([]*graph.MessageInfo) + if !ok { + return nil, errs.New("Could not get messages from cache") + } conditionParams := *m.baseParams // copy conditionParams.UserEmail = m.auth.Email() @@ -110,7 +114,11 @@ func check(params *ConditionParams, messages []*graph.MessageInfo, lastReportMap logging.Debug("Checking message %s", message.ID) // Ensure we don't show the same message too often if lastReport, ok := lastReportMap[message.ID]; ok { - lastReportTime, err := time.Parse(time.RFC3339, lastReport.(string)) + lr, ok := lastReport.(string) + if !ok { + return nil, errs.New("Could not get last reported time for message %s as it's not a string", message.ID) + } + lastReportTime, err := time.Parse(time.RFC3339, lr) if err != nil { return nil, errs.New("Could not parse last reported time for message %s as it's not a valid RFC3339 value: %v", message.ID, lastReport) } From 4546624dbe1ad30110c32eddfd46d92117408d90 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 1 Nov 2024 13:03:25 -0700 Subject: [PATCH 319/440] More test cases and support windows paths --- internal/fileutils/fileutils.go | 25 ++++++++++++--- internal/fileutils/fileutils_test.go | 48 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/internal/fileutils/fileutils.go b/internal/fileutils/fileutils.go index 62e8e00f43..114b35652e 100644 --- a/internal/fileutils/fileutils.go +++ b/internal/fileutils/fileutils.go @@ -1267,12 +1267,19 @@ func CommonParentPath(paths []string) string { return common } -func commonParentPath(a, b string) string { +func commonParentPath(a, b string) (result string) { + isWindowsPath := false + defer func() { + if isWindowsPath { + result = posixPathToWindowsPath(result) + } + }() common := "" - a = filepath.ToSlash(a) - b = filepath.ToSlash(b) - as := strings.Split(a, "/") - bs := strings.Split(b, "/") + ab := windowsPathToPosixPath(a) + bb := windowsPathToPosixPath(b) + isWindowsPath = a != ab + as := strings.Split(ab, "/") + bs := strings.Split(bb, "/") max := min(len(as), len(bs)) for x := 1; x <= max; x++ { ac := strings.Join(as[:x], "/") @@ -1284,3 +1291,11 @@ func commonParentPath(a, b string) string { } return common } + +func windowsPathToPosixPath(path string) string { + return strings.ReplaceAll(path, "\\", "/") +} + +func posixPathToWindowsPath(path string) string { + return strings.ReplaceAll(path, "/", "\\") +} diff --git a/internal/fileutils/fileutils_test.go b/internal/fileutils/fileutils_test.go index b3ac45ad17..5aec01ccf6 100644 --- a/internal/fileutils/fileutils_test.go +++ b/internal/fileutils/fileutils_test.go @@ -684,6 +684,54 @@ func TestCommonParentPath(t *testing.T) { paths: []string{"./folder1/file.txt", "./folder1/file.txt", "./folder1/file.txt"}, expected: "./folder1/file.txt", }, + { + paths: []string{"/home/user/folder1/file.txt", "/home/user/folder1/subfolder/file.txt"}, + expected: "/home/user/folder1", + }, + { + paths: []string{"/home/user/folder1/file.txt", "/home/user/folder2/file.txt"}, + expected: "/home/user", + }, + { + paths: []string{"/home/user/folder1/subfolder1/file.txt", "/home/user/folder1/subfolder2/file.txt"}, + expected: "/home/user/folder1", + }, + { + paths: []string{"C:\\Users\\User\\folder1\\file.txt", "C:\\Users\\User\\folder1\\subfolder\\file.txt"}, + expected: "C:\\Users\\User\\folder1", + }, + { + paths: []string{"C:\\Users\\User\\folder1\\file.txt", "C:\\Users\\User\\folder2\\file.txt"}, + expected: "C:\\Users\\User", + }, + { + paths: []string{"C:\\Users\\User\\folder1\\subfolder1\\file.txt", "C:\\Users\\User\\folder1\\subfolder2\\file.txt"}, + expected: "C:\\Users\\User\\folder1", + }, + { + paths: []string{"folder1/file.txt", "folder1/subfolder/file.txt"}, + expected: "folder1", + }, + { + paths: []string{"folder1/file.txt", "folder2/file.txt"}, + expected: "", + }, + { + paths: []string{"folder1/file.txt", "folder1/subfolder/file.txt", "folder1/subfolder2/file.txt"}, + expected: "folder1", + }, + { + paths: []string{"folder1/file.txt", "folder1/file.txt", "folder1/file.txt"}, + expected: "folder1/file.txt", + }, + { + paths: []string{"C:\\Users\\User\\folder1\\file.txt", "C:\\Users\\User\\folder1\\subfolder\\file.txt", "C:\\Users\\User\\folder2\\file.txt"}, + expected: "C:\\Users\\User", + }, + { + paths: []string{"/var/log/syslog", "/var/log/auth.log"}, + expected: "/var/log", + }, { paths: []string{}, expected: "", From 6c49aed4209689bc7e350536a3db45d18997175a Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 1 Nov 2024 13:22:52 -0700 Subject: [PATCH 320/440] Remove panic check on New resolver --- cmd/state-svc/internal/resolver/resolver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index 512f5e921d..ca591fe87d 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -50,7 +50,6 @@ type Resolver struct { // var _ genserver.ResolverRoot = &Resolver{} // Must implement ResolverRoot func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Resolver, error) { - defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() msg, err := messages.New(cfg, auth) if err != nil { return nil, errs.Wrap(err, "Could not initialize messages") From 5e42cff49f5ef762d506aea5bb6443a744ce1984 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 1 Nov 2024 13:23:52 -0700 Subject: [PATCH 321/440] Nil check recovered --- internal/runbits/panics/panics.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/runbits/panics/panics.go b/internal/runbits/panics/panics.go index ed710415dc..d743f7a5e8 100644 --- a/internal/runbits/panics/panics.go +++ b/internal/runbits/panics/panics.go @@ -37,7 +37,9 @@ func LogPanics(recovered interface{}, stack []byte) bool { // LogAndPanic produces actionable output for panic events (that shouldn't happen) and panics func LogAndPanic(recovered interface{}, stack []byte) { - multilog.Error("Panic: %v", recovered) - logging.Debug("Stack: %s", string(stack)) - panic(recovered) + if recovered != nil { + multilog.Error("Panic: %v", recovered) + logging.Debug("Stack: %s", string(stack)) + panic(recovered) // We're only logging the panic, not interrupting it + } } From 7e2ba42dc75a7e6dd5fdc371b2a4b6e214349a95 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 1 Nov 2024 13:32:42 -0700 Subject: [PATCH 322/440] Clean up parent node logic --- internal/sliceutils/sliceutils.go | 10 ++++++++-- pkg/buildscript/unmarshal_buildexpression.go | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/sliceutils/sliceutils.go b/internal/sliceutils/sliceutils.go index 6939a60eff..fb22a6366b 100644 --- a/internal/sliceutils/sliceutils.go +++ b/internal/sliceutils/sliceutils.go @@ -19,14 +19,20 @@ func RemoveFromStrings(slice []string, indexes ...int) []string { } func GetInt(slice []int, index int) (int, bool) { - if index > len(slice)-1 { + if index < 0 { + index = len(slice) + index + } + if index > len(slice)-1 || index < 0 { return -1, false } return slice[index], true } func GetString(slice []string, index int) (string, bool) { - if index > len(slice)-1 { + if index < 0 { + index = len(slice) + index + } + if index > len(slice)-1 || index < 0 { return "", false } // return normalized string diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 1a273473f7..0a57fb306f 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -175,7 +175,8 @@ func unmarshalValue(path []string, valueInterface interface{}) (*value, error) { result.List = &values case string: - if len(path) >= 2 && path[len(path)-2] == ctxIn || strings.HasPrefix(v, "$") { + parentNode, hasParentNode := sliceutils.GetString(path, -2) + if (hasParentNode && parentNode == ctxIn) || strings.HasPrefix(v, "$") { result.Ident = ptr.To(strings.TrimPrefix(v, "$")) } else { result.Str = ptr.To(v) From 55a07c08c1461aedaa8dc9ddd6fc6c30c4ba3516 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 1 Nov 2024 18:07:06 -0400 Subject: [PATCH 323/440] Do not colorize wrapping continuation characters. --- internal/colorize/wrap.go | 27 ++++++++++++++++++++++++- internal/output/renderers/bulletlist.go | 14 ++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/internal/colorize/wrap.go b/internal/colorize/wrap.go index 74ca14e77b..721ec366de 100644 --- a/internal/colorize/wrap.go +++ b/internal/colorize/wrap.go @@ -1,8 +1,11 @@ package colorize import ( + "fmt" "regexp" "strings" + + "github.com/ActiveState/cli/internal/logging" ) type WrappedLines []WrappedLine @@ -37,6 +40,7 @@ func Wrap(text string, maxLen int, includeLineEnds bool, continuation string) Wr entries := make([]WrappedLine, 0) colorCodes := colorRx.FindAllStringSubmatchIndex(text, -1) + colorNames := colorRx.FindAllStringSubmatch(text, -1) isLineEnd := false entry := WrappedLine{} @@ -73,7 +77,15 @@ func Wrap(text string, maxLen int, includeLineEnds bool, continuation string) Wr } // Extract the word from the current line if it doesn't start the line. if i > 0 && i < len(entry.Line)-1 && !isLinkRegexp.MatchString(entry.Line[i:]) { - wrapped = indent + continuation + entry.Line[i:] + tag := colorTag(pos, colorCodes, colorNames) + if continuation != "" && tag != "" { + // Do not colorize the continuation. + wrapped = fmt.Sprintf("%s[/RESET]%s%s%s", indent, continuation, tag, entry.Line[i:]) + } else { + wrapped = indent + continuation + entry.Line[i:] + } + logging.Debug("continuation: '%s'", continuation) + logging.Debug("wrapped: '%s'", wrapped) entry.Line = entry.Line[:i] entry.Length -= wrappedLength isLineEnd = true // emulate for wrapping purposes @@ -103,6 +115,19 @@ func inRange(pos int, ranges [][]int) bool { return false } +// colorTag returns the currently active color tag (if any) at the given position. +func colorTag(pos int, ranges [][]int, names [][]string) string { + for i, intRange := range ranges { + if pos < intRange[0] { + continue // before [COLOR] + } + if i < len(ranges)-1 || pos < ranges[i+1][0] { + return names[i][0] // missing [/RESET] or between [COLOR] and [/RESET] + } + } + return "" +} + func isSpace(b byte) bool { return b == ' ' || b == '\t' } func isUTF8TrailingByte(b byte) bool { diff --git a/internal/output/renderers/bulletlist.go b/internal/output/renderers/bulletlist.go index 947013a5d0..49c98145b3 100644 --- a/internal/output/renderers/bulletlist.go +++ b/internal/output/renderers/bulletlist.go @@ -1,7 +1,6 @@ package renderers import ( - "fmt" "strings" "github.com/ActiveState/cli/internal/colorize" @@ -22,7 +21,7 @@ type bulletList struct { // ├─ two // │ wrapped // └─ three -var BulletTree = []string{output.TreeMid, output.TreeMid, output.TreeLink, output.TreeEnd} +var BulletTree = []string{output.TreeMid, output.TreeMid, output.TreeLink + " ", output.TreeEnd} // HeadedBulletTree outputs a list like: // @@ -30,7 +29,7 @@ var BulletTree = []string{output.TreeMid, output.TreeMid, output.TreeLink, outpu // ├─ two // │ wrapped // └─ three -var HeadedBulletTree = []string{"", output.TreeMid, output.TreeLink, output.TreeEnd} +var HeadedBulletTree = []string{"", output.TreeMid, output.TreeLink + " ", output.TreeEnd} // NewBulletList returns a printable list of items prefixed with the given set of bullets. // The set of bullets should contain four items: the bullet for the first item (e.g. ""); the @@ -73,14 +72,15 @@ func (b *bulletList) MarshalOutput(format output.Format) interface{} { } item = b.prefix + bullet + item } else { - bullet = b.bullets[1] + bullet = b.bullets[1] + " " continuation := indent + b.bullets[2] + " " if i == len(b.items)-1 { - bullet = b.bullets[3] // this is the last item + bullet = b.bullets[3] + " " // this is the last item continuation = " " } - wrapped := colorize.Wrap(item, termutils.GetWidth()-len(indent), true, continuation).String() - item = fmt.Sprintf("%s%s %s", indent, bullet, wrapped) + maxWidth := termutils.GetWidth() - len(indent) - len(bullet) + wrapped := colorize.Wrap(item, maxWidth, true, continuation).String() + item = indent + bullet + wrapped } out[i] = item } From 4779823d085f84b60cfa251d25b3604e04f8e22d Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 4 Nov 2024 10:50:01 -0500 Subject: [PATCH 324/440] Update GitHub Actions macos runner to 13. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 371c9ff600..b2d65094d7 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: - 1.22.x sys: - {os: ubuntu-latest} - - {os: macos-12, shell: zsh} + - {os: macos-13, shell: zsh} - {os: windows-2019} fail-fast: false runs-on: ${{ matrix.sys.os }} From bdd8b3c72e5e477b45994271f1040fe9f8aa8163 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 4 Nov 2024 17:45:56 -0500 Subject: [PATCH 325/440] Implement user-facing errors for environment setup commands. --- internal/locale/locales/en-us.yaml | 4 -- internal/runbits/checkout/checkout.go | 16 ++++---- internal/runners/checkout/checkout.go | 17 ++++++++- internal/runners/initialize/init.go | 20 ++++------ internal/runners/initialize/rationalize.go | 5 +++ internal/runners/swtch/switch.go | 44 ++++++++++++++++++---- 6 files changed, 73 insertions(+), 33 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 6eb637bf9f..9a4deb3a3a 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -646,8 +646,6 @@ err_incompatible_move_file_dir: other: | Could not move [NOTICE]{{.V0}}[/RESET] to [NOTICE]{{.V1}}[/RESET]. One is a file, the other a directory. This indicates that the requested build may be corrupted. -err_init_lang: - other: "Invalid language: {{.V0}}@{{.V1}}" initializing_project: other: | Initializing Project @@ -1217,8 +1215,6 @@ err_findproject_notfound: other: "Could not load project [ACTIONABLE]{{.V0}}[/RESET] from path: [ACTIONABLE]{{.V1}}[/RESET]" arg_state_shell_namespace_description: other: The namespace of the project you wish to start a virtual environment shell/prompt for, or just the project name if previously used -err_language_by_commit: - other: Could not get language from commit ID {{.V0}} err_parse_project: other: Could not parse project file at {{.V0}} err_uninstall_privilege_mismatch: diff --git a/internal/runbits/checkout/checkout.go b/internal/runbits/checkout/checkout.go index 74efe258b8..8e25d88b4e 100644 --- a/internal/runbits/checkout/checkout.go +++ b/internal/runbits/checkout/checkout.go @@ -3,9 +3,6 @@ package checkout import ( "path/filepath" - "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/go-openapi/strfmt" "github.com/ActiveState/cli/internal/constants" @@ -13,6 +10,8 @@ import ( "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/language" "github.com/ActiveState/cli/internal/osutils" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/runbits/git" "github.com/ActiveState/cli/pkg/localcommit" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" @@ -47,6 +46,7 @@ func (e errCommitDoesNotBelong) Error() string { } var errNoCommitID = errs.New("commitID is nil") +var ErrNoOrg = errs.New("unable to get org name") func New(repo git.Repository, prime primeable) *Checkout { return &Checkout{repo, prime} @@ -87,7 +87,7 @@ func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath if !noClone && repoURL != nil && *repoURL != "" { err := r.repo.CloneProject(ns.Owner, ns.Project, path, r.prime.Output(), r.prime.Analytics()) if err != nil { - return "", locale.WrapError(err, "err_clone_project", "Could not clone associated git repository") + return "", errs.Wrap(err, "Could not clone associated git repository") } } } else if commitID == nil { @@ -119,7 +119,7 @@ func (r *Checkout) fetchProject( // the project and create the project file pj, err := model.FetchProjectByName(ns.Owner, ns.Project, r.prime.Auth()) if err != nil { - return "", "", nil, "", "", nil, locale.WrapError(err, "err_fetch_project", "", ns.String()) + return "", "", nil, "", "", nil, errs.Wrap(err, "Unable to fetch project '%s'", ns.String()) } proj := pj.Name @@ -144,7 +144,7 @@ func (r *Checkout) fetchProject( case branchName != "": branch, err = model.BranchForProjectByName(pj, branchName) if err != nil { - return "", "", nil, "", "", nil, locale.WrapError(err, "err_fetch_branch", "", branchName) + return "", "", nil, "", "", nil, errs.Wrap(err, "Could not get branch '%s'", branchName) } commitID = branch.CommitID @@ -175,7 +175,7 @@ func (r *Checkout) fetchProject( return "", "", nil, "", "", nil, errs.Wrap(err, "Unable to get the project's org") } if len(owners) == 0 { - return "", "", nil, "", "", nil, locale.NewInputError("err_no_org_name", "Your project's organization name could not be found") + return "", "", nil, "", "", nil, ErrNoOrg } owner := owners[0].URLName @@ -211,7 +211,7 @@ func CreateProjectFiles(checkoutPath, cachePath, owner, name, branch, commitID, func getLanguage(commitID strfmt.UUID, auth *authentication.Auth) (language.Language, error) { modelLanguage, err := model.LanguageByCommit(commitID, auth) if err != nil { - return language.Unset, locale.WrapError(err, "err_language_by_commit", "", string(commitID)) + return language.Unset, errs.Wrap(err, "Could not get language from commit ID '%s'", string(commitID)) } return language.MakeByNameAndVersion(modelLanguage.Name, modelLanguage.Version), nil diff --git a/internal/runners/checkout/checkout.go b/internal/runners/checkout/checkout.go index 576dcbd453..ea2105031e 100644 --- a/internal/runners/checkout/checkout.go +++ b/internal/runners/checkout/checkout.go @@ -1,6 +1,7 @@ package checkout import ( + "errors" "os" "path/filepath" "strings" @@ -77,6 +78,19 @@ func NewCheckout(prime primeable) *Checkout { } } +func rationalizeError(rerr *error) { + if rerr == nil { + return + } + + switch { + case errors.Is(*rerr, checkout.ErrNoOrg): + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_no_org_name", "Your project's organization name could not be found"), + errs.SetInput()) + } +} + func (u *Checkout) Run(params *Params) (rerr error) { var err error var ns *project.Namespaced @@ -101,6 +115,7 @@ func (u *Checkout) Run(params *Params) (rerr error) { } defer func() { runtime_runbit.RationalizeSolveError(u.prime.Project(), u.auth, &rerr) }() + defer rationalizeError(&rerr) logging.Debug("Checking out %s to %s", ns.String(), params.PreferredPath) @@ -113,7 +128,7 @@ func (u *Checkout) Run(params *Params) (rerr error) { proj, err := project.FromPath(projectDir) if err != nil { - return locale.WrapError(err, "err_project_frompath") + return errs.Wrap(err, "Could not read created project file") } u.prime.SetProject(proj) diff --git a/internal/runners/initialize/init.go b/internal/runners/initialize/init.go index 607809fd38..6707251202 100644 --- a/internal/runners/initialize/init.go +++ b/internal/runners/initialize/init.go @@ -20,7 +20,6 @@ import ( "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/runbits/dependencies" - "github.com/ActiveState/cli/internal/runbits/errors" "github.com/ActiveState/cli/internal/runbits/org" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/runtime" @@ -90,6 +89,8 @@ func (e errUnrecognizedLanguage) Error() string { return fmt.Sprintf("unrecognized language: %s", e.Name) } +var errDeleteProjectAfterError = errs.New("could not delete initialized project") + // New returns a prepared ptr to Initialize instance. func New(prime primeable) *Initialize { return &Initialize{prime, prime.Auth(), prime.Config(), prime.Output(), prime.Analytics(), prime.SvcModel()} @@ -164,16 +165,15 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { err := fileutils.MkdirUnlessExists(path) if err != nil { - return locale.WrapError(err, "err_init_preparedir", "Could not create directory at [NOTICE]{{.V0}}[/RESET]. Error: {{.V1}}", params.Path, err.Error()) + return errs.Wrap(err, "Could not create directory '%s'", params.Path) } path, err = filepath.Abs(params.Path) if err != nil { - return locale.WrapExternalError(err, "err_init_abs_path", "Could not determine absolute path to [NOTICE]{{.V0}}[/RESET]. Error: {{.V1}}", path, err.Error()) + return errs.Wrap(err, "Could not determine absolute path to '%s'", params.Path) } var languageName, languageVersion string - var inferred bool if params.Language != "" { langParts := strings.Split(params.Language, "@") languageName = langParts[0] @@ -181,7 +181,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { languageVersion = langParts[1] } } else { - languageName, languageVersion, inferred = inferLanguage(r.config, r.auth) + languageName, languageVersion, _ = inferLanguage(r.config, r.auth) } if languageName == "" { @@ -200,11 +200,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { version, err := deriveVersion(lang, languageVersion, r.auth) if err != nil { - if inferred || errors.IsReportableError(err) { - return locale.WrapError(err, "err_init_lang", "", languageName, languageVersion) - } else { - return locale.WrapExternalError(err, "err_init_lang", "", languageName, languageVersion) - } + return errs.Wrap(err, "Unable to get language version") } resolvedOwner, err = org.Get(paramOwner, r.auth, r.config) @@ -226,7 +222,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { pjfile, err := projectfile.Create(createParams) if err != nil { - return locale.WrapError(err, "err_init_pjfile", "Could not create project file") + return errs.Wrap(err, "Could not create project file") } // If an error occurs, remove the created activestate.yaml file so the user can try again. @@ -296,7 +292,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { err2 := model.DeleteProject(namespace.Owner, namespace.Project, r.auth) if err2 != nil { multilog.Error("Error deleting remotely created project after runtime setup error: %v", errs.JoinMessage(err2)) - return locale.WrapError(err, "err_init_refresh_delete_project", "Could not setup runtime after init, and could not delete newly created Platform project. Please delete it manually before trying again") + return errDeleteProjectAfterError } return errs.Wrap(err, "Failed to fetch build result") } diff --git a/internal/runners/initialize/rationalize.go b/internal/runners/initialize/rationalize.go index 11c786e6ea..540e350060 100644 --- a/internal/runners/initialize/rationalize.go +++ b/internal/runners/initialize/rationalize.go @@ -83,5 +83,10 @@ func rationalizeError(owner, project string, rerr *error) { errs.SetTips(locale.T("err_init_authenticated"))) } + case errors.Is(*rerr, errDeleteProjectAfterError): + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_init_refresh_delete_project", "Could not setup runtime after init, and could not delete newly created Platform project. Please delete it manually before trying again"), + ) + } } diff --git a/internal/runners/swtch/switch.go b/internal/runners/swtch/switch.go index b0ee096628..c5c997d426 100644 --- a/internal/runners/swtch/switch.go +++ b/internal/runners/swtch/switch.go @@ -1,6 +1,8 @@ package swtch import ( + "errors" + "github.com/ActiveState/cli/internal/analytics" "github.com/ActiveState/cli/internal/config" "github.com/ActiveState/cli/internal/errs" @@ -74,6 +76,15 @@ func (b branchIdentifier) Locale() string { return locale.Tl("branch_identifier_type", "branch") } +type errCommitNotOnBranch struct { + commitID string + branch string +} + +func (e errCommitNotOnBranch) Error() string { + return "commit is not on branch" +} + func New(prime primeable) *Switch { return &Switch{ prime: prime, @@ -86,7 +97,24 @@ func New(prime primeable) *Switch { } } -func (s *Switch) Run(params SwitchParams) error { +func rationalizeError(rerr *error) { + if rerr == nil { + return + } + + var commitNotOnBranchErr *errCommitNotOnBranch + + switch { + case errors.As(*rerr, &commitNotOnBranchErr): + *rerr = errs.WrapUserFacing(*rerr, + locale.Tl("err_identifier_branch_not_on_branch", "Commit does not belong to history for branch [ACTIONABLE]{{.V0}}[/RESET]", commitNotOnBranchErr.branch), + errs.SetInput(), + ) + } +} + +func (s *Switch) Run(params SwitchParams) (rerr error) { + defer rationalizeError(&rerr) logging.Debug("ExecuteSwitch") if s.project == nil { @@ -96,27 +124,27 @@ func (s *Switch) Run(params SwitchParams) error { project, err := model.LegacyFetchProjectByName(s.project.Owner(), s.project.Name()) if err != nil { - return locale.WrapError(err, "err_fetch_project", "", s.project.Namespace().String()) + return errs.Wrap(err, "Could not fetch project '%s'", s.project.Namespace().String()) } identifier, err := resolveIdentifier(project, params.Identifier) if err != nil { - return locale.WrapError(err, "err_resolve_identifier", "Could not resolve identifier '{{.V0}}'", params.Identifier) + return errs.Wrap(err, "Could not resolve identifier '%s'", params.Identifier) } if id, ok := identifier.(branchIdentifier); ok { err = s.project.Source().SetBranch(id.branch.Label) if err != nil { - return locale.WrapError(err, "err_switch_set_branch", "Could not update branch") + return errs.Wrap(err, "Could not update branch") } } belongs, err := model.CommitBelongsToBranch(s.project.Owner(), s.project.Name(), s.project.BranchName(), identifier.CommitID(), s.auth) if err != nil { - return locale.WrapError(err, "err_identifier_branch", "Could not determine if commit belongs to branch") + return errs.Wrap(err, "Could not determine if commit belongs to branch") } if !belongs { - return locale.NewInputError("err_identifier_branch_not_on_branch", "Commit does not belong to history for branch [ACTIONABLE]{{.V0}}[/RESET]", s.project.BranchName()) + return &errCommitNotOnBranch{identifier.CommitID().String(), s.project.BranchName()} } err = localcommit.Set(s.project.Dir(), identifier.CommitID().String()) @@ -126,7 +154,7 @@ func (s *Switch) Run(params SwitchParams) error { _, err = runtime_runbit.Update(s.prime, trigger.TriggerSwitch) if err != nil { - return locale.WrapError(err, "err_refresh_runtime") + return errs.Wrap(err, "Could not setup runtime") } s.out.Print(output.Prepare( @@ -148,7 +176,7 @@ func resolveIdentifier(project *mono_models.Project, idParam string) (identifier branch, err := model.BranchForProjectByName(project, idParam) if err != nil { - return nil, locale.WrapError(err, "err_identifier_branch", "Could not get branch '{{.V0}}' for current project", idParam) + return nil, errs.Wrap(err, "Could not get branch '%s'", idParam) } From 406f7904345e6d14f6104f43c244de71da1f0dca Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 13:49:25 -0800 Subject: [PATCH 326/440] Update graphQL response handling --- go.mod | 5 +- go.sum | 6 +- internal/gqlclient/gqlclient.go | 7 +- .../graphql/graphql.go | 128 +++++- internal/graphql/graphql_json_test.go | 107 +++++ internal/graphql/graphql_multipart_test.go | 188 ++++++++ internal/graphql/graphql_test.go | 376 ++++++++++++++++ internal/runners/packages/list.go | 2 +- .../buildplanner/response/buildexpression.go | 7 - .../api/buildplanner/response/buildtarget.go | 7 - .../api/buildplanner/response/commit.go | 41 -- .../api/buildplanner/response/commiterror.go | 4 +- .../buildplanner/response/createproject.go | 8 +- .../api/buildplanner/response/mergecommit.go | 12 +- .../api/buildplanner/response/project.go | 39 ++ .../api/buildplanner/response/revertcommit.go | 6 +- .../api/buildplanner/response/stagecommit.go | 8 - pkg/platform/api/graphql/model/publish.go | 4 - pkg/platform/api/graphql/model/vcs.go | 6 - pkg/platform/api/graphql/request/vcs.go | 25 +- .../api/hasura_inventory/model/inventory.go | 4 - pkg/platform/api/mediator/model/cve.go | 8 - .../api/mediator/model/supportedlangs.go | 6 +- .../vulnerabilities/model/vulnerabilities.go | 4 - pkg/platform/model/buildplanner/build.go | 28 +- .../model/buildplanner/buildplanner.go | 2 +- .../model/buildplanner/buildscript.go | 14 +- pkg/platform/model/buildplanner/commit.go | 60 +-- pkg/platform/model/buildplanner/project.go | 12 +- pkg/platform/model/buildplanner/publish.go | 8 +- pkg/platform/model/checkpoints.go | 32 +- pkg/platform/model/cve.go | 12 +- pkg/platform/model/inventory.go | 6 +- pkg/platform/model/organizations.go | 8 +- pkg/platform/model/projects.go | 8 +- pkg/platform/model/supportedlangs.go | 4 +- pkg/platform/model/svc.go | 18 +- pkg/platform/model/vulnerabilities.go | 4 +- .../github.com/ActiveState/graphql/.gitignore | 14 - .../ActiveState/graphql/.travis.yml | 16 - vendor/github.com/ActiveState/graphql/LICENSE | 201 --------- .../github.com/ActiveState/graphql/README.md | 67 --- vendor/github.com/matryer/is/.gitignore | 24 ++ vendor/github.com/matryer/is/.travis.yml | 40 ++ vendor/github.com/matryer/is/LICENSE | 21 + vendor/github.com/matryer/is/README.md | 43 ++ vendor/github.com/matryer/is/is-1.7.go | 64 +++ vendor/github.com/matryer/is/is-before-1.7.go | 23 + vendor/github.com/matryer/is/is.go | 403 ++++++++++++++++++ vendor/modules.txt | 8 +- 50 files changed, 1607 insertions(+), 541 deletions(-) rename {vendor/github.com/ActiveState => internal}/graphql/graphql.go (71%) create mode 100644 internal/graphql/graphql_json_test.go create mode 100644 internal/graphql/graphql_multipart_test.go create mode 100644 internal/graphql/graphql_test.go delete mode 100644 pkg/platform/api/buildplanner/response/buildexpression.go delete mode 100644 pkg/platform/api/buildplanner/response/buildtarget.go delete mode 100644 pkg/platform/api/buildplanner/response/stagecommit.go delete mode 100644 vendor/github.com/ActiveState/graphql/.gitignore delete mode 100644 vendor/github.com/ActiveState/graphql/.travis.yml delete mode 100644 vendor/github.com/ActiveState/graphql/LICENSE delete mode 100644 vendor/github.com/ActiveState/graphql/README.md create mode 100644 vendor/github.com/matryer/is/.gitignore create mode 100644 vendor/github.com/matryer/is/.travis.yml create mode 100644 vendor/github.com/matryer/is/LICENSE create mode 100644 vendor/github.com/matryer/is/README.md create mode 100644 vendor/github.com/matryer/is/is-1.7.go create mode 100644 vendor/github.com/matryer/is/is-before-1.7.go create mode 100644 vendor/github.com/matryer/is/is.go diff --git a/go.mod b/go.mod index b512502678..d98d578d24 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,6 @@ require ( ) require ( - github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 github.com/brunoga/deep v1.2.4 github.com/cespare/xxhash v1.1.0 github.com/charmbracelet/bubbles v0.18.0 @@ -143,10 +142,10 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/labstack/gommon v0.3.1 // indirect + github.com/labstack/gommon v0.3.1 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matryer/is v1.2.0 // indirect + github.com/matryer/is v1.4.1 github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.7 // indirect diff --git a/go.sum b/go.sum index f68dcce819..73a9c3d8a7 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,6 @@ github.com/99designs/gqlgen v0.17.54 h1:AsF49k/7RJlwA00RQYsYN0T8cQuaosnV/7G1dHC3 github.com/99designs/gqlgen v0.17.54/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc= github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527 h1:lW+qgVXf/iAnSs8SgagWxh8d6nsbpmwyhmeg9/fp0Os= github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527/go.mod h1:/9SyzKLlJSuIa7WAsLUUhHqTK9+PtZD8cKF8G4SLpa0= -github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 h1:UCx/ObpVRgC4fp2OlJM2iNdMMu+K87/aPxKrQ1WRLj4= -github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48/go.mod h1:NhUbNQ8UpfnC6nZvZ8oThqYSCE/G8FQp9JUrK9jXJs0= github.com/ActiveState/pty v0.0.0-20230628221854-6fb90eb08a14 h1:RdhhSiwmgyUaaF2GBNrbqTwE5SM+MaVjwf91Ua+CK8c= github.com/ActiveState/pty v0.0.0-20230628221854-6fb90eb08a14/go.mod h1:5mM6vNRQwshCjlkOnVpwC//4ZpkiC6nmZr8lPOxJdXs= github.com/ActiveState/termtest v0.7.3-0.20240703202616-34f7899287a4 h1:aYm+l6fT6sg+xEfO2+uSt0UJqw7WECwwqvSR7zaL/yI= @@ -477,8 +475,8 @@ github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ= github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ= github.com/mash/go-tempfile-suffix v0.0.0-20150731093933-48f0f8a3a5ab h1:2lWb5W+cZTlTIrJUuzVMrs6vcEvY0ydkkMH66EdPaqc= github.com/mash/go-tempfile-suffix v0.0.0-20150731093933-48f0f8a3a5ab/go.mod h1:oBJAJTenVXse2frk3J4anZ7funxla2ZdJHFQShkD43k= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= +github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= diff --git a/internal/gqlclient/gqlclient.go b/internal/gqlclient/gqlclient.go index bb6f23be9b..5580fd0ac6 100644 --- a/internal/gqlclient/gqlclient.go +++ b/internal/gqlclient/gqlclient.go @@ -14,12 +14,12 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/profile" "github.com/ActiveState/cli/internal/singleton/uniqid" "github.com/ActiveState/cli/internal/strutils" "github.com/ActiveState/cli/pkg/platform/api" - "github.com/ActiveState/graphql" "github.com/pkg/errors" ) @@ -44,6 +44,11 @@ type RequestWithFiles interface { Files() []File } +type RequestWithMultiQuery interface { + Request + IsMultiQuery() bool +} + type Header map[string][]string type graphqlClient = graphql.Client diff --git a/vendor/github.com/ActiveState/graphql/graphql.go b/internal/graphql/graphql.go similarity index 71% rename from vendor/github.com/ActiveState/graphql/graphql.go rename to internal/graphql/graphql.go index 452ad84baa..7085f0448c 100644 --- a/vendor/github.com/ActiveState/graphql/graphql.go +++ b/internal/graphql/graphql.go @@ -39,6 +39,8 @@ import ( "io" "mime/multipart" "net/http" + "strings" + "unicode" "github.com/pkg/errors" ) @@ -108,8 +110,10 @@ func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{} } c.logf(">> variables: %v", req.vars) c.logf(">> query: %s", req.q) + + intermediateResp := make(map[string]interface{}) gr := &graphResponse{ - Data: resp, + Data: &intermediateResp, } r, err := http.NewRequest(http.MethodPost, c.endpoint, &requestBody) if err != nil { @@ -141,7 +145,31 @@ func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{} // return first error return gr.Errors[0] } - return nil + + if req.dataPath != "" { + val, err := findValueByPath(intermediateResp, req.dataPath) + if err != nil { + // If the response is empty, return nil instead of an error + if len(intermediateResp) == 0 { + return nil + } + return err + } + data, err := json.Marshal(val) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, resp) + } + + data, err := json.Marshal(intermediateResp) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + if resp == nil { + return nil + } + return json.Unmarshal(data, resp) } func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp interface{}) error { @@ -175,8 +203,9 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter c.logf(">> variables: %s", variablesBuf.String()) c.logf(">> files: %d", len(req.files)) c.logf(">> query: %s", req.q) + intermediateResp := make(map[string]interface{}) gr := &graphResponse{ - Data: resp, + Data: &intermediateResp, } r, err := http.NewRequest(http.MethodPost, c.endpoint, &requestBody) if err != nil { @@ -208,7 +237,27 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter // return first error return gr.Errors[0] } - return nil + + if req.dataPath != "" { + val, err := findValueByPath(intermediateResp, req.dataPath) + if err != nil { + return errors.Wrap(err, "finding value by path") + } + data, err := json.Marshal(val) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, resp) + } + + data, err := json.Marshal(intermediateResp) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + if resp == nil { + return nil + } + return json.Unmarshal(data, resp) } // WithHTTPClient specifies the underlying http.Client to use when @@ -249,9 +298,10 @@ type graphResponse struct { // Request is a GraphQL request. type Request struct { - q string - vars map[string]interface{} - files []file + q string + vars map[string]interface{} + files []file + dataPath string // Header represent any request headers that will be set // when the request is made. @@ -261,12 +311,48 @@ type Request struct { // NewRequest makes a new Request with the specified string. func NewRequest(q string) *Request { req := &Request{ - q: q, - Header: make(map[string][]string), + q: q, + Header: make(map[string][]string), + dataPath: inferDataPath(q), } return req } +// inferDataPath attempts to extract the first field name after the operation type +// as the data path. Returns empty string if unable to infer. +// For example, given the query: +// +// query { user { name } } +// +// it will return "user". +// The dataPath is used to signal to the client where it should start unmarshaling the response. +func inferDataPath(query string) string { + query = strings.TrimSpace(query) + if query == "" { + return "" + } + + startIdx := strings.Index(query, "{") + if startIdx == -1 { + return "" + } + query = query[startIdx+1:] + query = strings.TrimSpace(query) + if query == "" || query == "}" { + return "" + } + + var result strings.Builder + for _, ch := range query { + if ch == '(' || ch == '{' || unicode.IsSpace(ch) || ch == ':' { + break + } + result.WriteRune(ch) + } + + return strings.TrimSpace(result.String()) +} + // Var sets a variable. func (req *Request) Var(key string, value interface{}) { if req.vars == nil { @@ -286,9 +372,33 @@ func (req *Request) File(fieldname, filename string, r io.Reader) { }) } +// DataPath sets the path to the data field in the response. +// This is useful if you want to unmarshal a nested object. +// If not set, it will use the automatically inferred path. +func (req *Request) DataPath(path string) { + req.dataPath = path +} + // file represents a file to upload. type file struct { Field string Name string R io.Reader } + +func findValueByPath(data map[string]interface{}, path string) (interface{}, error) { + if val, ok := data[path]; ok { + return val, nil + } + + // Recursively search through nested maps + for _, val := range data { + if nestedMap, ok := val.(map[string]interface{}); ok { + if found, err := findValueByPath(nestedMap, path); err == nil { + return found, nil + } + } + } + + return nil, fmt.Errorf("path %q not found in response", path) +} diff --git a/internal/graphql/graphql_json_test.go b/internal/graphql/graphql_json_test.go new file mode 100644 index 0000000000..71f7f56d0e --- /dev/null +++ b/internal/graphql/graphql_json_test.go @@ -0,0 +1,107 @@ +package graphql + +import ( + "context" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/matryer/is" +) + +func TestDoJSON(t *testing.T) { + is := is.New(t) + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + is.Equal(r.Method, http.MethodPost) + b, err := ioutil.ReadAll(r.Body) + is.NoErr(err) + is.Equal(string(b), `{"query":"query {}","variables":null}`+"\n") + io.WriteString(w, `{ + "data": { + "something": "yes" + } + }`) + })) + defer srv.Close() + + ctx := context.Background() + client := NewClient(srv.URL) + + ctx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + var responseData map[string]interface{} + err := client.Run(ctx, &Request{q: "query {}"}, &responseData) + is.NoErr(err) + is.Equal(calls, 1) // calls + is.Equal(responseData["something"], "yes") +} + +func TestQueryJSON(t *testing.T) { + is := is.New(t) + + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + b, err := ioutil.ReadAll(r.Body) + is.NoErr(err) + is.Equal(string(b), `{"query":"query {}","variables":{"username":"matryer"}}`+"\n") + _, err = io.WriteString(w, `{"data":{"value":"some data"}}`) + is.NoErr(err) + })) + defer srv.Close() + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + client := NewClient(srv.URL) + + req := NewRequest("query {}") + req.Var("username", "matryer") + + // check variables + is.True(req != nil) + is.Equal(req.vars["username"], "matryer") + + var resp struct { + Value string + } + err := client.Run(ctx, req, &resp) + is.NoErr(err) + is.Equal(calls, 1) + + is.Equal(resp.Value, "some data") +} + +func TestHeader(t *testing.T) { + is := is.New(t) + + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + is.Equal(r.Header.Get("X-Custom-Header"), "123") + + _, err := io.WriteString(w, `{"data":{"value":"some data"}}`) + is.NoErr(err) + })) + defer srv.Close() + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + client := NewClient(srv.URL) + + req := NewRequest("query {}") + req.Header.Set("X-Custom-Header", "123") + + var resp struct { + Value string + } + err := client.Run(ctx, req, &resp) + is.NoErr(err) + is.Equal(calls, 1) + + is.Equal(resp.Value, "some data") +} diff --git a/internal/graphql/graphql_multipart_test.go b/internal/graphql/graphql_multipart_test.go new file mode 100644 index 0000000000..1756d95a39 --- /dev/null +++ b/internal/graphql/graphql_multipart_test.go @@ -0,0 +1,188 @@ +package graphql + +import ( + "context" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/matryer/is" +) + +func TestWithClient(t *testing.T) { + is := is.New(t) + var calls int + testClient := &http.Client{ + Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) { + calls++ + resp := &http.Response{ + Body: ioutil.NopCloser(strings.NewReader(`{"data":{"key":"value"}}`)), + } + return resp, nil + }), + } + + ctx := context.Background() + client := NewClient("", WithHTTPClient(testClient), UseMultipartForm()) + + req := NewRequest(``) + client.Run(ctx, req, nil) + + is.Equal(calls, 1) // calls +} + +func TestDoUseMultipartForm(t *testing.T) { + is := is.New(t) + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + is.Equal(r.Method, http.MethodPost) + query := r.FormValue("query") + is.Equal(query, `query {}`) + io.WriteString(w, `{ + "data": { + "something": "yes" + } + }`) + })) + defer srv.Close() + + ctx := context.Background() + client := NewClient(srv.URL, UseMultipartForm()) + + ctx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + var responseData map[string]interface{} + err := client.Run(ctx, &Request{q: "query {}"}, &responseData) + is.NoErr(err) + is.Equal(calls, 1) // calls + is.Equal(responseData["something"], "yes") +} + +func TestDoErr(t *testing.T) { + is := is.New(t) + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + is.Equal(r.Method, http.MethodPost) + query := r.FormValue("query") + is.Equal(query, `query {}`) + io.WriteString(w, `{ + "errors": [{ + "message": "Something went wrong" + }] + }`) + })) + defer srv.Close() + + ctx := context.Background() + client := NewClient(srv.URL, UseMultipartForm()) + + ctx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + var responseData map[string]interface{} + err := client.Run(ctx, &Request{q: "query {}"}, &responseData) + is.True(err != nil) + is.Equal(err.Error(), "graphql: Something went wrong") +} + +func TestDoNoResponse(t *testing.T) { + is := is.New(t) + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + is.Equal(r.Method, http.MethodPost) + query := r.FormValue("query") + is.Equal(query, `query {}`) + io.WriteString(w, `{ + "data": { + "something": "yes" + } + }`) + })) + defer srv.Close() + + ctx := context.Background() + client := NewClient(srv.URL, UseMultipartForm()) + + ctx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + err := client.Run(ctx, &Request{q: "query {}"}, nil) + is.NoErr(err) + is.Equal(calls, 1) // calls +} + +func TestQuery(t *testing.T) { + is := is.New(t) + + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + query := r.FormValue("query") + is.Equal(query, "query {}") + is.Equal(r.FormValue("variables"), `{"username":"matryer"}`+"\n") + _, err := io.WriteString(w, `{"data":{"value":"some data"}}`) + is.NoErr(err) + })) + defer srv.Close() + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + client := NewClient(srv.URL, UseMultipartForm()) + + req := NewRequest("query {}") + req.Var("username", "matryer") + + // check variables + is.True(req != nil) + is.Equal(req.vars["username"], "matryer") + + var resp struct { + Value string + } + err := client.Run(ctx, req, &resp) + is.NoErr(err) + is.Equal(calls, 1) + + is.Equal(resp.Value, "some data") + +} + +func TestFile(t *testing.T) { + is := is.New(t) + + var calls int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + file, header, err := r.FormFile("file") + is.NoErr(err) + defer file.Close() + is.Equal(header.Filename, "filename.txt") + + b, err := ioutil.ReadAll(file) + is.NoErr(err) + is.Equal(string(b), `This is a file`) + + _, err = io.WriteString(w, `{"data":{"value":"some data"}}`) + is.NoErr(err) + })) + defer srv.Close() + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + client := NewClient(srv.URL, UseMultipartForm()) + f := strings.NewReader(`This is a file`) + req := NewRequest("query {}") + req.File("file", "filename.txt", f) + err := client.Run(ctx, req, nil) + is.NoErr(err) +} + +type roundTripperFunc func(req *http.Request) (*http.Response, error) + +func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return fn(req) +} diff --git a/internal/graphql/graphql_test.go b/internal/graphql/graphql_test.go new file mode 100644 index 0000000000..0911274e0a --- /dev/null +++ b/internal/graphql/graphql_test.go @@ -0,0 +1,376 @@ +package graphql + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +func TestClient_DataPath(t *testing.T) { + mockResponse := map[string]interface{}{ + "data": map[string]interface{}{ + "users": map[string]interface{}{ + "items": []map[string]interface{}{ + {"id": "1", "name": "Alice"}, + {"id": "2", "name": "Bob"}, + }, + }, + }, + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(mockResponse) + })) + defer server.Close() + + client := NewClient(server.URL) + + tests := []struct { + name string + query string + dataPath string + want interface{} + }{ + { + name: "no data path", + query: `{ + users { + items { + id + name + } + } + }`, + dataPath: "", + want: map[string]interface{}{ + "users": map[string]interface{}{ + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + }, + }, + { + name: "with data path to users", + query: `{ + users { + items { + id + name + } + } + }`, + dataPath: "users", + want: map[string]interface{}{ + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + }, + { + name: "named query without data path", + query: `query GetUsers { + users { + items { + id + name + } + } + }`, + dataPath: "", + want: map[string]interface{}{ + "users": map[string]interface{}{ + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + }, + }, + { + name: "named query with data path", + query: `query GetUsers { + users { + items { + id + name + } + } + }`, + dataPath: "users", + want: map[string]interface{}{ + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + }, + { + name: "named mutation", + query: `mutation CreateUser { + users { + items { + id + name + } + } + }`, + dataPath: "users", + want: map[string]interface{}{ + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + }, + { + name: "anonymous query with items data path", + query: `{ + users { + items { + id + name + } + } + }`, + dataPath: "items", + want: []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + { + name: "named query with items data path", + query: `query GetUsers { + users { + items { + id + name + } + } + }`, + dataPath: "items", + want: []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + { + name: "named mutation with items data path", + query: `mutation CreateUser { + users { + items { + id + name + } + } + }`, + dataPath: "items", + want: []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := NewRequest(tt.query) + req.DataPath(tt.dataPath) + + var resp interface{} + err := client.Run(context.Background(), req, &resp) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + got, err := json.Marshal(resp) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } + + want, err := json.Marshal(tt.want) + if err != nil { + t.Fatalf("failed to marshal expected result: %v", err) + } + + if string(got) != string(want) { + t.Errorf("got %s, want %s", got, want) + } + }) + } +} + +func Test_inferDataPath(t *testing.T) { + tests := []struct { + name string + query string + want string + }{ + { + name: "simple query", + query: `{ + users { + id + name + } + }`, + want: "users", + }, + { + name: "query with variables", + query: `query ($id: ID!) { + user(id: $id) { + name + email + } + }`, + want: "user", + }, + { + name: "query with operation name", + query: `query GetUser { + user { + name + } + }`, + want: "user", + }, + { + name: "query with multiple fields", + query: `{ + user { + name + } + posts { + title + } + }`, + want: "user", + }, + { + name: "empty query", + query: "", + want: "", + }, + { + name: "alternate empty query", + query: `query { + query {} + }`, + want: "query", + }, + { + name: "malformed query", + query: `query { + badly formatted* + }`, + want: "badly", + }, + { + name: "named mutation", + query: `mutation CreateUser($input: UserInput!) { + createUser(input: $input) { + id + name + } + }`, + want: "createUser", + }, + { + name: "named query with aliases", + query: `query GetUserDetails { + userInfo: user { + profile { + firstName + lastName + } + settings { + theme + } + } + }`, + want: "userInfo", + }, + { + name: "complex nested query", + query: `query FetchDashboard { + dashboard { + widgets { + id + data { + chart { + points + } + } + } + } + }`, + want: "dashboard", + }, + { + name: "query with fragments", + query: `query GetUserWithPosts { + user { + ...UserFields + posts { + ...PostFields + } + } + } + + fragment UserFields on User { + id + name + } + + fragment PostFields on Post { + title + content + }`, + want: "user", + }, + { + name: "subscription", + query: `subscription OnUserUpdate { + userUpdated { + id + status + } + }`, + want: "userUpdated", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := inferDataPath(tt.query); got != tt.want { + t.Errorf("inferDataPath() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNewRequest_DataPathInference(t *testing.T) { + query := `{ + users { + id + name + } + }` + + req := NewRequest(query) + if req.dataPath != "users" { + t.Errorf("NewRequest() dataPath = %v, want %v", req.dataPath, "users") + } + + // Test that manual override works + req.DataPath("override") + if req.dataPath != "override" { + t.Errorf("DataPath() override failed, got = %v, want %v", req.dataPath, "override") + } +} diff --git a/internal/runners/packages/list.go b/internal/runners/packages/list.go index 4c39904eb9..efd27d0707 100644 --- a/internal/runners/packages/list.go +++ b/internal/runners/packages/list.go @@ -242,7 +242,7 @@ func fetchCheckpoint(commit *strfmt.UUID, auth *authentication.Auth) ([]*gqlMode return nil, nil } - checkpoint, _, err := model.FetchCheckpointForCommit(*commit, auth) + checkpoint, err := model.FetchCheckpointForCommit(*commit, auth) if err != nil && errors.Is(err, model.ErrNoData) { return nil, locale.WrapExternalError(err, "package_no_data") } diff --git a/pkg/platform/api/buildplanner/response/buildexpression.go b/pkg/platform/api/buildplanner/response/buildexpression.go deleted file mode 100644 index f8e9fefc2c..0000000000 --- a/pkg/platform/api/buildplanner/response/buildexpression.go +++ /dev/null @@ -1,7 +0,0 @@ -package response - -type BuildExpressionResponse struct { - Type string `json:"__typename"` - Commit *Commit `json:"commit"` - *Error -} diff --git a/pkg/platform/api/buildplanner/response/buildtarget.go b/pkg/platform/api/buildplanner/response/buildtarget.go deleted file mode 100644 index 2154156480..0000000000 --- a/pkg/platform/api/buildplanner/response/buildtarget.go +++ /dev/null @@ -1,7 +0,0 @@ -package response - -type BuildTargetResult struct { - Build *BuildResponse `json:"buildCommitTarget"` - *Error - *NotFoundError -} diff --git a/pkg/platform/api/buildplanner/response/commit.go b/pkg/platform/api/buildplanner/response/commit.go index 127613883b..0ae4103535 100644 --- a/pkg/platform/api/buildplanner/response/commit.go +++ b/pkg/platform/api/buildplanner/response/commit.go @@ -10,47 +10,6 @@ import ( "github.com/go-openapi/strfmt" ) -type ProjectCommitResponse struct { - Project *ProjectResponse `json:"project"` -} - -// PostProcess must satisfy gqlclient.PostProcessor interface -func (c *ProjectCommitResponse) PostProcess() error { - if c.Project == nil { - return errs.New("Project is nil") - } - - if IsErrorResponse(c.Project.Type) { - return ProcessProjectError(c.Project, "Could not get build from project response") - } - - if c.Project.Commit == nil { - return errs.New("Commit is nil") - } - - if IsErrorResponse(c.Project.Type) { - return ProcessProjectError(c.Project, "Could not get build from project response") - } - - if c.Project.Commit == nil { - return errs.New("Commit is nil") - } - - if IsErrorResponse(c.Project.Commit.Type) { - return ProcessCommitError(c.Project.Commit, "Could not get build from commit from project response") - } - - if c.Project.Commit.Build == nil { - return errs.New("Commit does not contain build") - } - - if IsErrorResponse(c.Project.Commit.Build.Type) { - return ProcessBuildError(c.Project.Commit.Build, "Could not get build from project commit response") - } - - return nil -} - func ProcessBuildError(build *BuildResponse, fallbackMessage string) error { logging.Debug("ProcessBuildError: build.Type=%s", build.Type) if build.Type == types.PlanningErrorType { diff --git a/pkg/platform/api/buildplanner/response/commiterror.go b/pkg/platform/api/buildplanner/response/commiterror.go index e5efef0793..f517f1fba4 100644 --- a/pkg/platform/api/buildplanner/response/commiterror.go +++ b/pkg/platform/api/buildplanner/response/commiterror.go @@ -73,7 +73,7 @@ type RevertCommitError struct { func (m *RevertCommitError) Error() string { return m.Message } -func ProcessRevertCommitError(rcErr *revertedCommit, fallbackMessage string) error { +func ProcessRevertCommitError(rcErr *RevertedCommit, fallbackMessage string) error { if rcErr.Type != "" { return &RevertCommitError{rcErr.Type, rcErr.Message} } @@ -87,7 +87,7 @@ type MergedCommitError struct { func (m *MergedCommitError) Error() string { return m.Message } -func ProcessMergedCommitError(mcErr *mergedCommit, fallbackMessage string) error { +func ProcessMergedCommitError(mcErr *MergedCommit, fallbackMessage string) error { if mcErr.Type != "" { return &MergedCommitError{mcErr.Type, mcErr.Message} } diff --git a/pkg/platform/api/buildplanner/response/createproject.go b/pkg/platform/api/buildplanner/response/createproject.go index ba8984a1e5..1db41851a9 100644 --- a/pkg/platform/api/buildplanner/response/createproject.go +++ b/pkg/platform/api/buildplanner/response/createproject.go @@ -4,7 +4,7 @@ import ( "github.com/ActiveState/cli/internal/errs" ) -type projectCreated struct { +type ProjectCreated struct { Type string `json:"__typename"` Commit *Commit `json:"commit"` *Error @@ -13,10 +13,6 @@ type projectCreated struct { *ForbiddenError } -type CreateProjectResult struct { - ProjectCreated *projectCreated `json:"createProject"` -} - type ProjectCreatedError struct { Type string Message string @@ -24,7 +20,7 @@ type ProjectCreatedError struct { func (p *ProjectCreatedError) Error() string { return p.Message } -func ProcessProjectCreatedError(pcErr *projectCreated, fallbackMessage string) error { +func ProcessProjectCreatedError(pcErr *ProjectCreated, fallbackMessage string) error { if pcErr.Error == nil { return errs.New(fallbackMessage) } diff --git a/pkg/platform/api/buildplanner/response/mergecommit.go b/pkg/platform/api/buildplanner/response/mergecommit.go index c112011421..aec9a7de68 100644 --- a/pkg/platform/api/buildplanner/response/mergecommit.go +++ b/pkg/platform/api/buildplanner/response/mergecommit.go @@ -1,6 +1,9 @@ package response -type mergedCommit struct { +// MergedCommit is the result of a merge commit mutation. +// The resulting commit is only pushed to the platform automatically if the target ref was a named +// branch and the merge strategy was FastForward. +type MergedCommit struct { Type string `json:"__typename"` Commit *Commit `json:"commit"` *Error @@ -12,10 +15,3 @@ type mergedCommit struct { *HeadOnBranchMovedError *NoChangeSinceLastCommitError } - -// MergeCommitResult is the result of a merge commit mutation. -// The resulting commit is only pushed to the platform automatically if the target ref was a named -// branch and the merge strategy was FastForward. -type MergeCommitResult struct { - MergedCommit *mergedCommit `json:"mergeCommit"` -} diff --git a/pkg/platform/api/buildplanner/response/project.go b/pkg/platform/api/buildplanner/response/project.go index 4fb2efefc5..356ef619ed 100644 --- a/pkg/platform/api/buildplanner/response/project.go +++ b/pkg/platform/api/buildplanner/response/project.go @@ -1,8 +1,47 @@ package response +import "github.com/ActiveState/cli/internal/errs" + // ProjectResponse contains the commit and any errors. type ProjectResponse struct { Type string `json:"__typename"` Commit *Commit `json:"commit"` *Error } + +// PostProcess must satisfy gqlclient.PostProcessor interface +func (pr *ProjectResponse) PostProcess() error { + if pr == nil { + return errs.New("Project is nil") + } + + if IsErrorResponse(pr.Type) { + return ProcessProjectError(pr, "Could not get build from project response") + } + + if pr.Commit == nil { + return errs.New("Commit is nil") + } + + if IsErrorResponse(pr.Type) { + return ProcessProjectError(pr, "Could not get build from project response") + } + + if pr.Commit == nil { + return errs.New("Commit is nil") + } + + if IsErrorResponse(pr.Commit.Type) { + return ProcessCommitError(pr.Commit, "Could not get build from commit from project response") + } + + if pr.Commit.Build == nil { + return errs.New("Commit does not contain build") + } + + if IsErrorResponse(pr.Commit.Build.Type) { + return ProcessBuildError(pr.Commit.Build, "Could not get build from project commit response") + } + + return nil +} diff --git a/pkg/platform/api/buildplanner/response/revertcommit.go b/pkg/platform/api/buildplanner/response/revertcommit.go index 525c380a65..10237ad224 100644 --- a/pkg/platform/api/buildplanner/response/revertcommit.go +++ b/pkg/platform/api/buildplanner/response/revertcommit.go @@ -4,14 +4,10 @@ import ( "github.com/go-openapi/strfmt" ) -type revertedCommit struct { +type RevertedCommit struct { Type string `json:"__typename"` Commit *Commit `json:"commit"` CommonAncestor strfmt.UUID `json:"commonAncestorID"` ConflictPaths []string `json:"conflictPaths"` *Error } - -type RevertCommitResult struct { - RevertedCommit *revertedCommit `json:"revertCommit"` -} diff --git a/pkg/platform/api/buildplanner/response/stagecommit.go b/pkg/platform/api/buildplanner/response/stagecommit.go deleted file mode 100644 index f64312f56d..0000000000 --- a/pkg/platform/api/buildplanner/response/stagecommit.go +++ /dev/null @@ -1,8 +0,0 @@ -package response - -// StageCommitResult is the result of a stage commit mutation. -// It contains the resulting commit from the operation and any errors. -// The resulting commit is NOT pushed to the platform automatically. -type StageCommitResult struct { - Commit *Commit `json:"stageCommit"` -} diff --git a/pkg/platform/api/graphql/model/publish.go b/pkg/platform/api/graphql/model/publish.go index f818ef95c4..0a5479661f 100644 --- a/pkg/platform/api/graphql/model/publish.go +++ b/pkg/platform/api/graphql/model/publish.go @@ -6,7 +6,3 @@ type PublishResult struct { IngredientVersionID string `json:"ingredientVersionID"` Revision int `json:"revision"` } - -type PublishResponse struct { - Result PublishResult `json:"publish"` -} diff --git a/pkg/platform/api/graphql/model/vcs.go b/pkg/platform/api/graphql/model/vcs.go index bd9d87b891..b32732873e 100644 --- a/pkg/platform/api/graphql/model/vcs.go +++ b/pkg/platform/api/graphql/model/vcs.go @@ -14,9 +14,3 @@ type Requirement struct { type Commit struct { AtTime strfmt.DateTime `json:"at_time"` } - -type Checkpoint struct { - Requirements []*Requirement `json:"vcs_checkpoints"` - Commit *Commit `json:"vcs_commits_by_pk"` -} - diff --git a/pkg/platform/api/graphql/request/vcs.go b/pkg/platform/api/graphql/request/vcs.go index 70dec20ce0..2dfc59d578 100644 --- a/pkg/platform/api/graphql/request/vcs.go +++ b/pkg/platform/api/graphql/request/vcs.go @@ -23,13 +23,32 @@ func (p *checkpointByCommit) Query() string { version_constraint constraint_json } + }` +} + +func (p *checkpointByCommit) Vars() (map[string]interface{}, error) { + return p.vars, nil +} + +// New request type for commit details +func CommitByID(commitID strfmt.UUID) *commitByID { + return &commitByID{vars: map[string]interface{}{ + "commit_id": commitID, + }} +} + +type commitByID struct { + vars map[string]interface{} +} + +func (p *commitByID) Query() string { + return `query ($commit_id: uuid!) { vcs_commits_by_pk(commit_id: $commit_id) { at_time } - } - ` + }` } -func (p *checkpointByCommit) Vars() (map[string]interface{}, error) { +func (p *commitByID) Vars() (map[string]interface{}, error) { return p.vars, nil } diff --git a/pkg/platform/api/hasura_inventory/model/inventory.go b/pkg/platform/api/hasura_inventory/model/inventory.go index 689cc89ddf..5c03030c92 100644 --- a/pkg/platform/api/hasura_inventory/model/inventory.go +++ b/pkg/platform/api/hasura_inventory/model/inventory.go @@ -7,7 +7,3 @@ import ( type LastIngredientRevisionTime struct { RevisionTime strfmt.DateTime `json:"revision_time"` } - -type LatestRevisionResponse struct { - RevisionTimes []LastIngredientRevisionTime `json:"last_ingredient_revision_time"` -} diff --git a/pkg/platform/api/mediator/model/cve.go b/pkg/platform/api/mediator/model/cve.go index 1422a5af2f..1f62cddf9d 100644 --- a/pkg/platform/api/mediator/model/cve.go +++ b/pkg/platform/api/mediator/model/cve.go @@ -41,10 +41,6 @@ type ProjectVulnerabilities struct { Message *string `json:"message,omitempty"` } -type ProjectResponse struct { - ProjectVulnerabilities `json:"project"` -} - type CommitVulnerabilities struct { CommitID string `json:"commit_id"` VulnerabilityHistogram []SeverityCount `json:"vulnerability_histogram"` @@ -53,10 +49,6 @@ type CommitVulnerabilities struct { Message *string `json:"message,omitempty"` } -type CommitResponse struct { - CommitVulnerabilities `json:"commit"` -} - type SeverityCount struct { Severity string `json:"severity"` Count int `json:"count"` diff --git a/pkg/platform/api/mediator/model/supportedlangs.go b/pkg/platform/api/mediator/model/supportedlangs.go index 9d656799f0..a1044efe23 100644 --- a/pkg/platform/api/mediator/model/supportedlangs.go +++ b/pkg/platform/api/mediator/model/supportedlangs.go @@ -1,10 +1,6 @@ package model -// SupportedLanguagesResponse is a struct for the payload of the supported languages mediator endpoint -type SupportedLanguagesResponse struct { - Languages []SupportedLanguage `json:"unstableSupportedLanguages"` -} - +// SupportedLanguage is a struct for the payload of the supported languages mediator endpoint type SupportedLanguage struct { Name string `json:"name"` DefaultVersion string `json:"default_version"` diff --git a/pkg/platform/api/vulnerabilities/model/vulnerabilities.go b/pkg/platform/api/vulnerabilities/model/vulnerabilities.go index dd37854d05..f6e3a63c93 100644 --- a/pkg/platform/api/vulnerabilities/model/vulnerabilities.go +++ b/pkg/platform/api/vulnerabilities/model/vulnerabilities.go @@ -7,10 +7,6 @@ const ( SeverityLow = "low" ) -type VulnerabilitiesResponse struct { - Vulnerabilities []VulnerableIngredientsFilter `json:"vulnerabilities"` -} - type VulnerableIngredientsFilter struct { Name string `json:"name"` PrimaryNamespace string `json:"primary_namespace"` diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index feef9b1a28..ea5196ab2f 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -10,6 +10,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" @@ -20,7 +21,6 @@ import ( "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/api/reqsimport" - "github.com/ActiveState/graphql" "github.com/go-openapi/strfmt" ) @@ -58,7 +58,7 @@ const fetchCommitCacheExpiry = time.Hour * 12 func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, target *string) (*Commit, error) { logging.Debug("FetchCommit, commitID: %s, owner: %s, project: %s", commitID, owner, project) - resp := &response.ProjectCommitResponse{} + resp := &response.ProjectResponse{} cacheKey := strings.Join([]string{"FetchCommit", commitID.String(), owner, project, ptr.From(target, "")}, "-") respRaw, err := b.cache.GetCache(cacheKey) @@ -78,7 +78,7 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, } return nil, err } - if resp.Project.Commit.Build.Status == raw.Completed { + if resp.Commit.Build.Status == raw.Completed { respBytes, err := json.Marshal(resp) if err != nil { return nil, errs.Wrap(err, "failed to marshal cache") @@ -92,14 +92,14 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, // The BuildPlanner will return a build plan with a status of // "planning" if the build plan is not ready yet. We need to // poll the BuildPlanner until the build is ready. - if resp.Project.Commit.Build.Status == raw.Planning { - resp.Project.Commit.Build, err = b.pollBuildPlanned(commitID.String(), owner, project, target) + if resp.Commit.Build.Status == raw.Planning { + resp.Commit.Build, err = b.pollBuildPlanned(commitID.String(), owner, project, target) if err != nil { return nil, errs.Wrap(err, "failed to poll build plan") } } - commit := resp.Project.Commit + commit := resp.Commit bp, err := buildplan.Unmarshal(commit.Build.RawMessage) if err != nil { @@ -230,7 +230,7 @@ func VersionStringToRequirements(version string) ([]types.VersionRequirement, er // pollBuildPlanned polls the buildplan until it has passed the planning stage (ie. it's either planned or further along). func (b *BuildPlanner) pollBuildPlanned(commitID, owner, project string, target *string) (*response.BuildResponse, error) { - resp := &response.ProjectCommitResponse{} + resp := &response.ProjectResponse{} ticker := time.NewTicker(pollInterval) for { select { @@ -244,7 +244,7 @@ func (b *BuildPlanner) pollBuildPlanned(commitID, owner, project string, target return nil, errs.New("Build plan response is nil") } - build := resp.Project.Commit.Build + build := resp.Commit.Build if build.Status != raw.Planning { return build, nil @@ -265,18 +265,18 @@ func (e ErrFailedArtifacts) Error() string { func (bp *BuildPlanner) BuildTarget(owner, project, commitID, target string) error { logging.Debug("BuildTarget, owner: %s, project: %s, commitID: %s, target: %s", owner, project, commitID, target) - resp := &response.BuildTargetResult{} + resp := &response.BuildResponse{} err := bp.client.Run(request.Evaluate(owner, project, commitID, target), resp) if err != nil { return processBuildPlannerError(err, "Failed to evaluate target") } - if resp.Build == nil { + if resp == nil { return errs.New("Build is nil") } - if response.IsErrorResponse(resp.Build.Type) { - return response.ProcessBuildError(resp.Build, "Could not process error response from evaluate target") + if response.IsErrorResponse(resp.Type) { + return response.ProcessBuildError(resp, "Could not process error response from evaluate target") } return nil @@ -285,7 +285,7 @@ func (bp *BuildPlanner) BuildTarget(owner, project, commitID, target string) err // WaitForBuild polls the build until it has passed the completed stage (ie. it's either successful or failed). func (b *BuildPlanner) WaitForBuild(commitID strfmt.UUID, owner, project string, target *string) error { failedArtifacts := map[strfmt.UUID]*response.ArtifactResponse{} - resp := &response.ProjectCommitResponse{} + resp := &response.ProjectResponse{} ticker := time.NewTicker(pollInterval) for { select { @@ -299,7 +299,7 @@ func (b *BuildPlanner) WaitForBuild(commitID strfmt.UUID, owner, project string, return errs.New("Build plan response is nil") } - build := resp.Project.Commit.Build + build := resp.Commit.Build // If the build status is planning it may not have any artifacts yet. if build.Status == raw.Planning { diff --git a/pkg/platform/model/buildplanner/buildplanner.go b/pkg/platform/model/buildplanner/buildplanner.go index 72468b839b..dd566307ba 100644 --- a/pkg/platform/model/buildplanner/buildplanner.go +++ b/pkg/platform/model/buildplanner/buildplanner.go @@ -4,10 +4,10 @@ import ( "time" "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" - "github.com/ActiveState/graphql" ) const clientDeprecationErrorKey = "CLIENT_DEPRECATION_ERROR" diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index c6fcc6a2ee..3826a220f3 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -14,7 +14,7 @@ import ( func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript, error) { logging.Debug("GetBuildScript, commitID: %s", commitID) - resp := &bpResp.BuildExpressionResponse{} + resp := &bpResp.Commit{} cacheKey := strings.Join([]string{"GetBuildScript", commitID}, "-") respRaw, err := b.cache.GetCache(cacheKey) @@ -39,21 +39,21 @@ func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript } } - if resp.Commit == nil { + if resp == nil { return nil, errs.New("Commit is nil") } - if bpResp.IsErrorResponse(resp.Commit.Type) { - return nil, bpResp.ProcessCommitError(resp.Commit, "Could not get build expression from commit") + if bpResp.IsErrorResponse(resp.Type) { + return nil, bpResp.ProcessCommitError(resp, "Could not get build expression from commit") } - if resp.Commit.Expression == nil { + if resp.Expression == nil { return nil, errs.New("Commit does not contain expression") } script := buildscript.New() - script.SetAtTime(time.Time(resp.Commit.AtTime), false) - if err := script.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { + script.SetAtTime(time.Time(resp.AtTime), false) + if err := script.UnmarshalBuildExpression(resp.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } diff --git a/pkg/platform/model/buildplanner/commit.go b/pkg/platform/model/buildplanner/commit.go index 3086d32930..10d2bef641 100644 --- a/pkg/platform/model/buildplanner/commit.go +++ b/pkg/platform/model/buildplanner/commit.go @@ -45,80 +45,80 @@ func (b *BuildPlanner) StageCommit(params StageCommitParams) (*Commit, error) { // With the updated build expression call the stage commit mutation request := request.StageCommit(params.Owner, params.Project, params.ParentCommit, params.Description, script.AtTime(), expression) - resp := &response.StageCommitResult{} + resp := &response.Commit{} if err := b.client.Run(request, resp); err != nil { return nil, processBuildPlannerError(err, "failed to stage commit") } - if resp.Commit == nil { + if resp == nil { return nil, errs.New("Staged commit is nil") } - if response.IsErrorResponse(resp.Commit.Type) { - return nil, response.ProcessCommitError(resp.Commit, "Could not process error response from stage commit") + if response.IsErrorResponse(resp.Type) { + return nil, response.ProcessCommitError(resp, "Could not process error response from stage commit") } - if resp.Commit.CommitID == "" { + if resp.CommitID == "" { return nil, errs.New("Staged commit does not contain commitID") } - if response.IsErrorResponse(resp.Commit.Build.Type) { - return &Commit{resp.Commit, nil, nil}, response.ProcessBuildError(resp.Commit.Build, "Could not process error response from stage commit") + if response.IsErrorResponse(resp.Build.Type) { + return &Commit{resp, nil, nil}, response.ProcessBuildError(resp.Build, "Could not process error response from stage commit") } // The BuildPlanner will return a build plan with a status of // "planning" if the build plan is not ready yet. We need to // poll the BuildPlanner until the build is ready. - if resp.Commit.Build.Status == raw.Planning { - resp.Commit.Build, err = b.pollBuildPlanned(resp.Commit.CommitID.String(), params.Owner, params.Project, nil) + if resp.Build.Status == raw.Planning { + resp.Build, err = b.pollBuildPlanned(resp.CommitID.String(), params.Owner, params.Project, nil) if err != nil { return nil, errs.Wrap(err, "failed to poll build plan") } } - bp, err := buildplan.Unmarshal(resp.Commit.Build.RawMessage) + bp, err := buildplan.Unmarshal(resp.Build.RawMessage) if err != nil { return nil, errs.Wrap(err, "failed to unmarshal build plan") } stagedScript := buildscript.New() - stagedScript.SetAtTime(time.Time(resp.Commit.AtTime), false) - if err := stagedScript.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { + stagedScript.SetAtTime(time.Time(resp.AtTime), false) + if err := stagedScript.UnmarshalBuildExpression(resp.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } - return &Commit{resp.Commit, bp, stagedScript}, nil + return &Commit{resp, bp, stagedScript}, nil } func (b *BuildPlanner) RevertCommit(organization, project, parentCommitID, commitID string) (strfmt.UUID, error) { logging.Debug("RevertCommit, organization: %s, project: %s, commitID: %s", organization, project, commitID) - resp := &response.RevertCommitResult{} + resp := &response.RevertedCommit{} err := b.client.Run(request.RevertCommit(organization, project, parentCommitID, commitID), resp) if err != nil { return "", processBuildPlannerError(err, "Failed to revert commit") } - if resp.RevertedCommit == nil { + if resp == nil { return "", errs.New("Revert commit response is nil") } - if response.IsErrorResponse(resp.RevertedCommit.Type) { - return "", response.ProcessRevertCommitError(resp.RevertedCommit, "Could not revert commit") + if response.IsErrorResponse(resp.Type) { + return "", response.ProcessRevertCommitError(resp, "Could not revert commit") } - if resp.RevertedCommit.Commit == nil { + if resp.Commit == nil { return "", errs.New("Revert commit's commit is nil'") } - if response.IsErrorResponse(resp.RevertedCommit.Commit.Type) { - return "", response.ProcessCommitError(resp.RevertedCommit.Commit, "Could not process error response from revert commit") + if response.IsErrorResponse(resp.Commit.Type) { + return "", response.ProcessCommitError(resp.Commit, "Could not process error response from revert commit") } - if resp.RevertedCommit.Commit.CommitID == "" { + if resp.Commit.CommitID == "" { return "", errs.New("Commit does not contain commitID") } - return resp.RevertedCommit.Commit.CommitID, nil + return resp.Commit.CommitID, nil } type MergeCommitParams struct { @@ -132,27 +132,27 @@ type MergeCommitParams struct { func (b *BuildPlanner) MergeCommit(params *MergeCommitParams) (strfmt.UUID, error) { logging.Debug("MergeCommit, owner: %s, project: %s", params.Owner, params.Project) request := request.MergeCommit(params.Owner, params.Project, params.TargetRef, params.OtherRef, params.Strategy) - resp := &response.MergeCommitResult{} + resp := &response.MergedCommit{} err := b.client.Run(request, resp) if err != nil { return "", processBuildPlannerError(err, "Failed to merge commit") } - if resp.MergedCommit == nil { + if resp == nil { return "", errs.New("MergedCommit is nil") } - if response.IsErrorResponse(resp.MergedCommit.Type) { - return "", response.ProcessMergedCommitError(resp.MergedCommit, "Could not merge commit") + if response.IsErrorResponse(resp.Type) { + return "", response.ProcessMergedCommitError(resp, "Could not merge commit") } - if resp.MergedCommit.Commit == nil { + if resp.Commit == nil { return "", errs.New("Merge commit's commit is nil'") } - if response.IsErrorResponse(resp.MergedCommit.Commit.Type) { - return "", response.ProcessCommitError(resp.MergedCommit.Commit, "Could not process error response from merge commit") + if response.IsErrorResponse(resp.Commit.Type) { + return "", response.ProcessCommitError(resp.Commit, "Could not process error response from merge commit") } - return resp.MergedCommit.Commit.CommitID, nil + return resp.Commit.CommitID, nil } diff --git a/pkg/platform/model/buildplanner/project.go b/pkg/platform/model/buildplanner/project.go index 9eb7dcb34d..fbb501e786 100644 --- a/pkg/platform/model/buildplanner/project.go +++ b/pkg/platform/model/buildplanner/project.go @@ -60,22 +60,22 @@ func (b *BuildPlanner) CreateProject(params *CreateProjectParams) (strfmt.UUID, // Create the project. request := request.CreateProject(params.Owner, params.Project, params.Private, expression, params.Description) - resp := &response.CreateProjectResult{} + resp := &response.ProjectCreated{} if err := b.client.Run(request, resp); err != nil { return "", processBuildPlannerError(err, "Failed to create project") } - if resp.ProjectCreated == nil { + if resp == nil { return "", errs.New("ProjectCreated is nil") } - if response.IsErrorResponse(resp.ProjectCreated.Type) { - return "", response.ProcessProjectCreatedError(resp.ProjectCreated, "Could not create project") + if response.IsErrorResponse(resp.Type) { + return "", response.ProcessProjectCreatedError(resp, "Could not create project") } - if resp.ProjectCreated.Commit == nil { + if resp.Commit == nil { return "", errs.New("ProjectCreated.Commit is nil") } - return resp.ProjectCreated.Commit.CommitID, nil + return resp.Commit.CommitID, nil } diff --git a/pkg/platform/model/buildplanner/publish.go b/pkg/platform/model/buildplanner/publish.go index 4f23d06802..3b6ca644f9 100644 --- a/pkg/platform/model/buildplanner/publish.go +++ b/pkg/platform/model/buildplanner/publish.go @@ -11,15 +11,15 @@ func (b *BuildPlanner) Publish(vars request.PublishVariables, filepath string) ( if err != nil { return nil, errs.Wrap(err, "Could not create publish request") } - res := graphModel.PublishResponse{} + res := graphModel.PublishResult{} if err := b.client.Run(pr, &res); err != nil { return nil, processBuildPlannerError(err, "Publish failed") } - if res.Result.Error != "" { - return nil, errs.New("API responded with error: %s", res.Result.Error) + if res.Error != "" { + return nil, errs.New("API responded with error: %s", res.Error) } - return &res.Result, nil + return &res, nil } diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index 87f491a940..3ca70b00d2 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -11,7 +11,6 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/pkg/platform/api/graphql" gqlModel "github.com/ActiveState/cli/pkg/platform/api/graphql/model" @@ -35,7 +34,7 @@ type Language struct { // GetRequirement searches a commit for a requirement by name. func GetRequirement(commitID strfmt.UUID, namespace Namespace, requirement string, auth *authentication.Auth) (*gqlModel.Requirement, error) { - chkPt, _, err := FetchCheckpointForCommit(commitID, auth) + chkPt, err := FetchCheckpointForCommit(commitID, auth) if err != nil { return nil, err } @@ -53,7 +52,7 @@ func GetRequirement(commitID strfmt.UUID, namespace Namespace, requirement strin // FetchLanguagesForCommit fetches a list of language names for the given commit func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]Language, error) { - checkpoint, _, err := FetchCheckpointForCommit(commitID, auth) + checkpoint, err := FetchCheckpointForCommit(commitID, auth) if err != nil { return nil, err } @@ -95,25 +94,38 @@ func FetchLanguagesForBuildScript(script *buildscript.BuildScript) ([]Language, } // FetchCheckpointForCommit fetches the checkpoint for the given commit -func FetchCheckpointForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]*gqlModel.Requirement, strfmt.DateTime, error) { +func FetchCheckpointForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]*gqlModel.Requirement, error) { logging.Debug("fetching checkpoint (%s)", commitID.String()) request := request.CheckpointByCommit(commitID) gql := graphql.New(auth) - response := gqlModel.Checkpoint{} + response := []*gqlModel.Requirement{} err := gql.Run(request, &response) if err != nil { - return nil, strfmt.DateTime{}, errs.Wrap(err, "gql.Run failed") + return nil, errs.Wrap(err, "gql.Run failed") } - logging.Debug("Returning %d requirements", len(response.Requirements)) + logging.Debug("Returning %d requirements", len(response)) - if response.Commit == nil { - return nil, strfmt.DateTime{}, locale.WrapError(ErrNoData, "err_no_data_found") + return response, nil +} + +func FetchAtTimeForCommit(commitID strfmt.UUID, auth *authentication.Auth) (strfmt.DateTime, error) { + logging.Debug("fetching atTime for commit (%s)", commitID.String()) + + request := request.CommitByID(commitID) + + gql := graphql.New(auth) + response := gqlModel.Commit{} + err := gql.Run(request, &response) + if err != nil { + return strfmt.DateTime{}, errs.Wrap(err, "gql.Run failed") } - return response.Requirements, response.Commit.AtTime, nil + logging.Debug("Returning %s", response.AtTime) + + return response.AtTime, nil } func GqlReqsToMonoCheckpoint(requirements []*gqlModel.Requirement) []*mono_models.Checkpoint { diff --git a/pkg/platform/model/cve.go b/pkg/platform/model/cve.go index 040086e2df..97a9696026 100644 --- a/pkg/platform/model/cve.go +++ b/pkg/platform/model/cve.go @@ -27,22 +27,22 @@ func FetchProjectVulnerabilities(auth *authentication.Auth, org, project string) ) } req := request.VulnerabilitiesByProject(org, project) - var resp model.ProjectResponse + var resp model.ProjectVulnerabilities med := mediator.New(auth) err := med.Run(req, &resp) if err != nil { return nil, errs.Wrap(err, "Failed to run mediator request.") } - msg := resp.ProjectVulnerabilities.Message + msg := resp.Message if msg != nil { - if resp.ProjectVulnerabilities.TypeName == "NotFound" { + if resp.TypeName == "NotFound" { return nil, &ErrProjectNotFound{org, project} } return nil, locale.NewError("project_vulnerability_err", "Request to retrieve vulnerability information failed with error: {{.V0}}", *msg) } - return &resp.ProjectVulnerabilities, nil + return &resp, nil } // FetchCommitVulnerabilities returns the vulnerability information of a project @@ -55,7 +55,7 @@ func FetchCommitVulnerabilities(auth *authentication.Auth, commitID string) (*mo ) } req := request.VulnerabilitiesByCommit(commitID) - var resp model.CommitResponse + var resp model.CommitVulnerabilities med := mediator.New(auth) err := med.Run(req, &resp) if err != nil { @@ -67,7 +67,7 @@ func FetchCommitVulnerabilities(auth *authentication.Auth, commitID string) (*mo return nil, locale.NewError("commit_vulnerability_err", "Request to retrieve vulnerability information for commit {{.V0}} failed with error: {{.V1}}", commitID, *msg) } - return removeModerateSeverity(resp.CommitVulnerabilities), nil + return removeModerateSeverity(resp), nil } func removeModerateSeverity(cv model.CommitVulnerabilities) *model.CommitVulnerabilities { diff --git a/pkg/platform/model/inventory.go b/pkg/platform/model/inventory.go index aa4176e151..ab719b75f6 100644 --- a/pkg/platform/model/inventory.go +++ b/pkg/platform/model/inventory.go @@ -300,7 +300,7 @@ func FetchPlatformsMap() (map[strfmt.UUID]*Platform, error) { } func FetchPlatformsForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]*Platform, error) { - checkpt, _, err := FetchCheckpointForCommit(commitID, auth) + checkpt, err := FetchCheckpointForCommit(commitID, auth) if err != nil { return nil, err } @@ -636,14 +636,14 @@ func FetchLatestTimeStamp(auth *authentication.Auth) (time.Time, error) { func FetchLatestRevisionTimeStamp(auth *authentication.Auth) (time.Time, error) { client := hsInventory.New(auth) request := hsInventoryRequest.NewLatestRevision() - response := hsInventoryModel.LatestRevisionResponse{} + response := []hsInventoryModel.LastIngredientRevisionTime{} err := client.Run(request, &response) if err != nil { return time.Now(), errs.Wrap(err, "Failed to get latest change time") } // Increment time by 1 second to work around API precision issue where same second comparisons can fall on either side - t := time.Time(response.RevisionTimes[0].RevisionTime) + t := time.Time(response[0].RevisionTime) t = t.Add(time.Second) return t, nil diff --git a/pkg/platform/model/organizations.go b/pkg/platform/model/organizations.go index 6afba14bed..dd5ff56dfb 100644 --- a/pkg/platform/model/organizations.go +++ b/pkg/platform/model/organizations.go @@ -125,17 +125,17 @@ func FetchOrganizationsByIDs(ids []strfmt.UUID, auth *authentication.Auth) ([]mo request := request.OrganizationsByIDs(ids) gql := graphql.New(auth) - response := model.Organizations{} + response := []model.Organization{} err := gql.Run(request, &response) if err != nil { return nil, errs.Wrap(err, "gql.Run failed") } - if len(response.Organizations) != len(ids) { - logging.Debug("Organization membership mismatch: %d members returned for %d members requested. Caller must account for this.", len(response.Organizations), len(ids)) + if len(response) != len(ids) { + logging.Debug("Organization membership mismatch: %d members returned for %d members requested. Caller must account for this.", len(response), len(ids)) } - return response.Organizations, nil + return response, nil } func processOrgErrorResponse(err error) error { diff --git a/pkg/platform/model/projects.go b/pkg/platform/model/projects.go index 9adb161b63..4a31c69333 100644 --- a/pkg/platform/model/projects.go +++ b/pkg/platform/model/projects.go @@ -55,17 +55,17 @@ func FetchProjectByName(orgName string, projectName string, auth *authentication request := request.ProjectByOrgAndName(orgName, projectName) gql := graphql.New(auth) - response := model.Projects{} + response := []model.Project{} err := gql.Run(request, &response) if err != nil { return nil, errs.Wrap(err, "GraphQL request failed") } - if len(response.Projects) == 0 { + if len(response) == 0 { return nil, &ErrProjectNotFound{orgName, projectName} } - return response.Projects[0].ToMonoProject() + return response[0].ToMonoProject() } // FetchOrganizationProjects fetches the projects for an organization @@ -105,7 +105,7 @@ func LanguageByCommit(commitID strfmt.UUID, auth *authentication.Auth) (Language } func FetchTimeStampForCommit(commitID strfmt.UUID, auth *authentication.Auth) (*time.Time, error) { - _, atTime, err := FetchCheckpointForCommit(commitID, auth) + atTime, err := FetchAtTimeForCommit(commitID, auth) if err != nil { return nil, errs.Wrap(err, "Unable to fetch checkpoint for commit ID") } diff --git a/pkg/platform/model/supportedlangs.go b/pkg/platform/model/supportedlangs.go index 3241f133dc..85ee940485 100644 --- a/pkg/platform/model/supportedlangs.go +++ b/pkg/platform/model/supportedlangs.go @@ -11,11 +11,11 @@ import ( func FetchSupportedLanguages(osPlatformName string) ([]model.SupportedLanguage, error) { kernelName := HostPlatformToKernelName(osPlatformName) req := request.SupportedLanguages(kernelName) - var resp model.SupportedLanguagesResponse + var resp []model.SupportedLanguage med := mediator.New(nil) err := med.Run(req, &resp) if err != nil { return nil, errs.Wrap(err, "Failed to run mediator request.") } - return resp.Languages, nil + return resp, nil } diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 613f952236..1505040a8c 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -12,12 +12,12 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/graph" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/profile" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" "github.com/ActiveState/cli/pkg/platform/api/svc/request" - "github.com/ActiveState/graphql" ) var SvcTimeoutMinimal = time.Millisecond * 500 @@ -130,12 +130,12 @@ func (m *SvcModel) CheckMessages(ctx context.Context, command string, flags []st defer profile.Measure("svc:CheckMessages", time.Now()) r := request.NewMessagingRequest(command, flags) - resp := graph.CheckMessagesResponse{} + resp := []*graph.MessageInfo{} if err := m.request(ctx, r, &resp); err != nil { return nil, errs.Wrap(err, "Error sending messages request") } - return resp.Messages, nil + return resp, nil } func (m *SvcModel) ConfigChanged(ctx context.Context, key string) error { @@ -170,12 +170,12 @@ func (m *SvcModel) GetProcessesInUse(ctx context.Context, execDir string) ([]*gr defer profile.Measure("svc:GetProcessesInUse", time.Now()) req := request.NewGetProcessesInUse(execDir) - response := graph.GetProcessesInUseResponse{} + response := []*graph.ProcessInfo{} if err := m.request(ctx, req, &response); err != nil { return nil, errs.Wrap(err, "Error sending GetProcessesInUse request to state-svc") } - return response.Processes, nil + return response, nil } // GetJWT grabs the JWT from the svc, if it exists. @@ -205,14 +205,14 @@ func (m *SvcModel) GetCache(key string) (result string, _ error) { defer profile.Measure("svc:GetCache", time.Now()) req := request.NewGetCache(key) - response := make(map[string]string) + var response string if err := m.request(context.Background(), req, &response); err != nil { return "", errs.Wrap(err, "Error sending GetCache request to state-svc") } - if entry, ok := response["getCache"]; ok { - return entry, nil + if response != "" { + return response, nil } - return "", errs.New("svcModel.GetCache() did not return an expected value") + return "", nil } func (m *SvcModel) SetCache(key, value string, expiry time.Duration) error { diff --git a/pkg/platform/model/vulnerabilities.go b/pkg/platform/model/vulnerabilities.go index f8e57aeffa..d50f57e409 100644 --- a/pkg/platform/model/vulnerabilities.go +++ b/pkg/platform/model/vulnerabilities.go @@ -69,14 +69,14 @@ func FetchVulnerabilitiesForIngredients(auth *authentication.Auth, ingredients [ med := vulnerabilities.New(auth) req := request.VulnerabilitiesByIngredients(requestIngredients) - var resp model.VulnerabilitiesResponse + var resp []model.VulnerableIngredientsFilter err := med.Run(req, &resp) if err != nil { return nil, errs.Wrap(err, "Failed to run vulnerabilities request") } vulnerabilities := make(map[string]*VulnerabilityIngredient) - for _, v := range resp.Vulnerabilities { + for _, v := range resp { key := fmt.Sprintf("%s/%s/%s", v.PrimaryNamespace, v.Name, v.Version) if _, ok := vulnerabilities[key]; !ok { vulnerabilities[key] = &VulnerabilityIngredient{ diff --git a/vendor/github.com/ActiveState/graphql/.gitignore b/vendor/github.com/ActiveState/graphql/.gitignore deleted file mode 100644 index a1338d6851..0000000000 --- a/vendor/github.com/ActiveState/graphql/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 -.glide/ diff --git a/vendor/github.com/ActiveState/graphql/.travis.yml b/vendor/github.com/ActiveState/graphql/.travis.yml deleted file mode 100644 index 715ae6a2ca..0000000000 --- a/vendor/github.com/ActiveState/graphql/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go - -go: - - 1.7.x - - 1.8.x - - 1.9.x - -before_install: - - go get github.com/golang/lint/golint - -before_script: - - go vet ./... - - golint ./... - -script: - - go test -v ./... diff --git a/vendor/github.com/ActiveState/graphql/LICENSE b/vendor/github.com/ActiveState/graphql/LICENSE deleted file mode 100644 index 9b0dfaa85e..0000000000 --- a/vendor/github.com/ActiveState/graphql/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2017 Machine Box, Ltd. - - 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. diff --git a/vendor/github.com/ActiveState/graphql/README.md b/vendor/github.com/ActiveState/graphql/README.md deleted file mode 100644 index 2836c7eee2..0000000000 --- a/vendor/github.com/ActiveState/graphql/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# graphql [![GoDoc](https://godoc.org/github.com/machinebox/graphql?status.png)](http://godoc.org/github.com/machinebox/graphql) [![Build Status](https://travis-ci.org/machinebox/graphql.svg?branch=master)](https://travis-ci.org/machinebox/graphql) [![Go Report Card](https://goreportcard.com/badge/github.com/machinebox/graphql)](https://goreportcard.com/report/github.com/machinebox/graphql) - -Low-level GraphQL client for Go. - -* Simple, familiar API -* Respects `context.Context` timeouts and cancellation -* Build and execute any kind of GraphQL request -* Use strong Go types for response data -* Use variables and upload files -* Simple error handling - -## Installation -Make sure you have a working Go environment. To install graphql, simply run: - -``` -$ go get github.com/machinebox/graphql -``` - -## Usage - -```go -import "context" - -// create a client (safe to share across requests) -client := graphql.NewClient("https://machinebox.io/graphql") - -// make a request -req := graphql.NewRequest(` - query ($key: String!) { - items (id:$key) { - field1 - field2 - field3 - } - } -`) - -// set any variables -req.Var("key", "value") - -// set header fields -req.Header.Set("Cache-Control", "no-cache") - -// define a Context for the request -ctx := context.Background() - -// run it and capture the response -var respData ResponseStruct -if err := client.Run(ctx, req, &respData); err != nil { - log.Fatal(err) -} -``` - -### File support via multipart form data - -By default, the package will send a JSON body. To enable the sending of files, you can opt to -use multipart form data instead using the `UseMultipartForm` option when you create your `Client`: - -``` -client := graphql.NewClient("https://machinebox.io/graphql", graphql.UseMultipartForm()) -``` - -For more information, [read the godoc package documentation](http://godoc.org/github.com/machinebox/graphql) or the [blog post](https://blog.machinebox.io/a-graphql-client-library-for-go-5bffd0455878). - -## Thanks - -Thanks to [Chris Broadfoot](https://github.com/broady) for design help. diff --git a/vendor/github.com/matryer/is/.gitignore b/vendor/github.com/matryer/is/.gitignore new file mode 100644 index 0000000000..daf913b1b3 --- /dev/null +++ b/vendor/github.com/matryer/is/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/matryer/is/.travis.yml b/vendor/github.com/matryer/is/.travis.yml new file mode 100644 index 0000000000..ff6379e03e --- /dev/null +++ b/vendor/github.com/matryer/is/.travis.yml @@ -0,0 +1,40 @@ +os: + - linux + - osx + - windows + +language: go + +sudo: required + +go: + # "1.x" always refers to the latest Go version, inc. the patch release. + # e.g. "1.x" is 1.13 until 1.13.1 is available. + - 1.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - 1.12.x + - 1.13.x + - 1.14.x + - tip + +matrix: + allow_failures: + - os: windows + go: tip + exclude: + # OSX 1.6.4 is not present in travis. + # https://github.com/travis-ci/travis-ci/issues/10309 + - go: 1.6.x + os: osx + +install: + - go get -d -v ./... + +script: + - go build -v ./... + - go test ./... diff --git a/vendor/github.com/matryer/is/LICENSE b/vendor/github.com/matryer/is/LICENSE new file mode 100644 index 0000000000..3154c1540d --- /dev/null +++ b/vendor/github.com/matryer/is/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017-2018 Mat Ryer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/matryer/is/README.md b/vendor/github.com/matryer/is/README.md new file mode 100644 index 0000000000..1ea022febb --- /dev/null +++ b/vendor/github.com/matryer/is/README.md @@ -0,0 +1,43 @@ +# is [![GoDoc](https://godoc.org/github.com/matryer/is?status.png)](http://godoc.org/github.com/matryer/is) [![Go Report Card](https://goreportcard.com/badge/github.com/matryer/is)](https://goreportcard.com/report/github.com/matryer/is) [![Build Status](https://travis-ci.org/matryer/is.svg?branch=master)](https://travis-ci.org/matryer/is) +Professional lightweight testing mini-framework for Go. + +* Easy to write and read +* [Beautifully simple API](https://pkg.go.dev/github.com/matryer/is) with everything you need: `is.Equal`, `is.True`, `is.NoErr`, and `is.Fail` +* Use comments to add descriptions (which show up when tests fail) + +Failures are very easy to read: + +![Examples of failures](https://github.com/matryer/is/raw/master/misc/delicious-failures.png) + +### Usage + +The following code shows a range of useful ways you can use +the helper methods: + +```go +func Test(t *testing.T) { + + is := is.New(t) + + signedin, err := isSignedIn(ctx) + is.NoErr(err) // isSignedIn error + is.Equal(signedin, true) // must be signed in + + body := readBody(r) + is.True(strings.Contains(body, "Hi there")) + +} +``` + +## Color + +To turn off the colors, run `go test` with the `-nocolor` flag, +or with the env var [`NO_COLOR` (with any value)](https://no-color.org). + +``` +go test -nocolor +``` + +``` +NO_COLOR=1 go test +``` diff --git a/vendor/github.com/matryer/is/is-1.7.go b/vendor/github.com/matryer/is/is-1.7.go new file mode 100644 index 0000000000..c75e473fe2 --- /dev/null +++ b/vendor/github.com/matryer/is/is-1.7.go @@ -0,0 +1,64 @@ +// +build go1.7 + +package is + +import ( + "regexp" + "runtime" +) + +// Helper marks the calling function as a test helper function. +// When printing file and line information, that function will be skipped. +// +// Available with Go 1.7 and later. +func (is *I) Helper() { + is.helpers[callerName(1)] = struct{}{} +} + +// callerName gives the function name (qualified with a package path) +// for the caller after skip frames (where 0 means the current function). +func callerName(skip int) string { + // Make room for the skip PC. + var pc [1]uintptr + n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName + if n == 0 { + panic("is: zero callers found") + } + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + return frame.Function +} + +// The maximum number of stack frames to go through when skipping helper functions for +// the purpose of decorating log messages. +const maxStackLen = 50 + +var reIsSourceFile = regexp.MustCompile(`is(-1.7)?\.go$`) + +func (is *I) callerinfo() (path string, line int, ok bool) { + var pc [maxStackLen]uintptr + // Skip two extra frames to account for this function + // and runtime.Callers itself. + n := runtime.Callers(2, pc[:]) + if n == 0 { + panic("is: zero callers found") + } + frames := runtime.CallersFrames(pc[:n]) + var firstFrame, frame runtime.Frame + for more := true; more; { + frame, more = frames.Next() + if reIsSourceFile.MatchString(frame.File) { + continue + } + if firstFrame.PC == 0 { + firstFrame = frame + } + if _, ok := is.helpers[frame.Function]; ok { + // Frame is inside a helper function. + continue + } + return frame.File, frame.Line, true + } + // If no "non-helper" frame is found, the first non is frame is returned. + return firstFrame.File, firstFrame.Line, true +} diff --git a/vendor/github.com/matryer/is/is-before-1.7.go b/vendor/github.com/matryer/is/is-before-1.7.go new file mode 100644 index 0000000000..c62f87208c --- /dev/null +++ b/vendor/github.com/matryer/is/is-before-1.7.go @@ -0,0 +1,23 @@ +// +build !go1.7 + +package is + +import ( + "regexp" + "runtime" +) + +var reIsSourceFile = regexp.MustCompile("is(-before-1.7)?\\.go$") + +func (is *I) callerinfo() (path string, line int, ok bool) { + for i := 0; ; i++ { + _, path, line, ok = runtime.Caller(i) + if !ok { + return + } + if reIsSourceFile.MatchString(path) { + continue + } + return path, line, true + } +} diff --git a/vendor/github.com/matryer/is/is.go b/vendor/github.com/matryer/is/is.go new file mode 100644 index 0000000000..305c1537db --- /dev/null +++ b/vendor/github.com/matryer/is/is.go @@ -0,0 +1,403 @@ +// Package is provides a lightweight extension to the +// standard library's testing capabilities. +// +// Comments on the assertion lines are used to add +// a description. +// +// The following failing test: +// +// func Test(t *testing.T) { +// is := is.New(t) +// a, b := 1, 2 +// is.Equal(a, b) // expect to be the same +// } +// +// Will output: +// +// your_test.go:123: 1 != 2 // expect to be the same +// +// Usage +// +// The following code shows a range of useful ways you can use +// the helper methods: +// +// func Test(t *testing.T) { +// // always start tests with this +// is := is.New(t) +// +// signedin, err := isSignedIn(ctx) +// is.NoErr(err) // isSignedIn error +// is.Equal(signedin, true) // must be signed in +// +// body := readBody(r) +// is.True(strings.Contains(body, "Hi there")) +// } +package is + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "strconv" + "strings" +) + +// T reports when failures occur. +// testing.T implements this interface. +type T interface { + // Fail indicates that the test has failed but + // allowed execution to continue. + // Fail is called in relaxed mode (via NewRelaxed). + Fail() + // FailNow indicates that the test has failed and + // aborts the test. + // FailNow is called in strict mode (via New). + FailNow() +} + +// I is the test helper harness. +type I struct { + t T + fail func() + out io.Writer + colorful bool + + helpers map[string]struct{} // functions to be skipped when writing file/line info +} + +var noColorFlag bool + +func init() { + var envNoColor bool + + // prefer https://no-color.org (with any value) + if _, ok := os.LookupEnv("NO_COLOR"); ok { + envNoColor = true + } + + if v, ok := os.LookupEnv("IS_NO_COLOR"); ok { + if b, err := strconv.ParseBool(v); err == nil { + envNoColor = b + } + } + + flag.BoolVar(&noColorFlag, "nocolor", envNoColor, "turns off colors") +} + +// New makes a new testing helper using the specified +// T through which failures will be reported. +// In strict mode, failures call T.FailNow causing the test +// to be aborted. See NewRelaxed for alternative behavior. +func New(t T) *I { + return &I{t, t.FailNow, os.Stdout, !noColorFlag, map[string]struct{}{}} +} + +// NewRelaxed makes a new testing helper using the specified +// T through which failures will be reported. +// In relaxed mode, failures call T.Fail allowing +// multiple failures per test. +func NewRelaxed(t T) *I { + return &I{t, t.Fail, os.Stdout, !noColorFlag, map[string]struct{}{}} +} + +func (is *I) log(args ...interface{}) { + s := is.decorate(fmt.Sprint(args...)) + fmt.Fprintf(is.out, s) + is.fail() +} + +func (is *I) logf(format string, args ...interface{}) { + is.log(fmt.Sprintf(format, args...)) +} + +// Fail immediately fails the test. +// +// func Test(t *testing.T) { +// is := is.New(t) +// is.Fail() // TODO: write this test +// } +// +// In relaxed mode, execution will continue after a call to +// Fail, but that test will still fail. +func (is *I) Fail() { + is.log("failed") +} + +// True asserts that the expression is true. The expression +// code itself will be reported if the assertion fails. +// +// func Test(t *testing.T) { +// is := is.New(t) +// val := method() +// is.True(val != nil) // val should never be nil +// } +// +// Will output: +// +// your_test.go:123: not true: val != nil +func (is *I) True(expression bool) { + if !expression { + is.log("not true: $ARGS") + } +} + +// Equal asserts that a and b are equal. +// +// func Test(t *testing.T) { +// is := is.New(t) +// a := greet("Mat") +// is.Equal(a, "Hi Mat") // greeting +// } +// +// Will output: +// +// your_test.go:123: Hey Mat != Hi Mat // greeting +func (is *I) Equal(a, b interface{}) { + if areEqual(a, b) { + return + } + if isNil(a) || isNil(b) { + is.logf("%s != %s", is.valWithType(a), is.valWithType(b)) + } else if reflect.ValueOf(a).Type() == reflect.ValueOf(b).Type() { + is.logf("%v != %v", a, b) + } else { + is.logf("%s != %s", is.valWithType(a), is.valWithType(b)) + } +} + +// New is a method wrapper around the New function. +// It allows you to write subtests using a similar +// pattern: +// +// func Test(t *testing.T) { +// is := is.New(t) +// t.Run("sub", func(t *testing.T) { +// is := is.New(t) +// // TODO: test +// }) +// } +func (is *I) New(t T) *I { + return New(t) +} + +// NewRelaxed is a method wrapper around the NewRelaxed +// method. It allows you to write subtests using a similar +// pattern: +// +// func Test(t *testing.T) { +// is := is.NewRelaxed(t) +// t.Run("sub", func(t *testing.T) { +// is := is.NewRelaxed(t) +// // TODO: test +// }) +// } +func (is *I) NewRelaxed(t T) *I { + return NewRelaxed(t) +} + +func (is *I) valWithType(v interface{}) string { + if isNil(v) { + return "" + } + if is.colorful { + return fmt.Sprintf("%[1]s%[3]T(%[2]s%[3]v%[1]s)%[2]s", colorType, colorNormal, v) + } + return fmt.Sprintf("%[1]T(%[1]v)", v) +} + +// NoErr asserts that err is nil. +// +// func Test(t *testing.T) { +// is := is.New(t) +// val, err := getVal() +// is.NoErr(err) // getVal error +// is.True(len(val) > 10) // val cannot be short +// } +// +// Will output: +// +// your_test.go:123: err: not found // getVal error +func (is *I) NoErr(err error) { + if err != nil { + is.logf("err: %s", err.Error()) + } +} + +// isNil gets whether the object is nil or not. +func isNil(object interface{}) bool { + if object == nil { + return true + } + value := reflect.ValueOf(object) + kind := value.Kind() + if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + return true + } + return false +} + +// areEqual gets whether a equals b or not. +func areEqual(a, b interface{}) bool { + if isNil(a) && isNil(b) { + return true + } + if isNil(a) || isNil(b) { + return false + } + if reflect.DeepEqual(a, b) { + return true + } + aValue := reflect.ValueOf(a) + bValue := reflect.ValueOf(b) + return aValue == bValue +} + +// loadComment gets the Go comment from the specified line +// in the specified file. +func loadComment(path string, line int) (string, bool) { + f, err := os.Open(path) + if err != nil { + return "", false + } + defer f.Close() + s := bufio.NewScanner(f) + i := 1 + for s.Scan() { + if i != line { + i++ + continue + } + + text := s.Text() + commentI := strings.Index(text, "// ") + if commentI == -1 { + return "", false // no comment + } + text = text[commentI+2:] + text = strings.TrimSpace(text) + return text, true + } + return "", false +} + +// loadArguments gets the arguments from the function call +// on the specified line of the file. +func loadArguments(path string, line int) (string, bool) { + f, err := os.Open(path) + if err != nil { + return "", false + } + defer f.Close() + s := bufio.NewScanner(f) + i := 1 + for s.Scan() { + if i != line { + i++ + continue + } + text := s.Text() + braceI := strings.Index(text, "(") + if braceI == -1 { + return "", false + } + text = text[braceI+1:] + cs := bufio.NewScanner(strings.NewReader(text)) + cs.Split(bufio.ScanBytes) + j := 0 + c := 1 + for cs.Scan() { + switch cs.Text() { + case ")": + c-- + case "(": + c++ + } + if c == 0 { + break + } + j++ + } + text = text[:j] + return text, true + } + return "", false +} + +// decorate prefixes the string with the file and line of the call site +// and inserts the final newline if needed and indentation tabs for formatting. +// this function was copied from the testing framework and modified. +func (is *I) decorate(s string) string { + path, lineNumber, ok := is.callerinfo() // decorate + log + public function. + file := filepath.Base(path) + if ok { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + lineNumber = 1 + } + buf := new(bytes.Buffer) + // Every line is indented at least one tab. + buf.WriteByte('\t') + if is.colorful { + buf.WriteString(colorFile) + } + fmt.Fprintf(buf, "%s:%d: ", file, lineNumber) + if is.colorful { + buf.WriteString(colorNormal) + } + + s = escapeFormatString(s) + + lines := strings.Split(s, "\n") + if l := len(lines); l > 1 && lines[l-1] == "" { + lines = lines[:l-1] + } + for i, line := range lines { + if i > 0 { + // Second and subsequent lines are indented an extra tab. + buf.WriteString("\n\t\t") + } + // expand arguments (if $ARGS is present) + if strings.Contains(line, "$ARGS") { + args, _ := loadArguments(path, lineNumber) + line = strings.Replace(line, "$ARGS", args, -1) + } + buf.WriteString(line) + } + comment, ok := loadComment(path, lineNumber) + if ok { + if is.colorful { + buf.WriteString(colorComment) + } + buf.WriteString(" // ") + comment = escapeFormatString(comment) + buf.WriteString(comment) + if is.colorful { + buf.WriteString(colorNormal) + } + } + buf.WriteString("\n") + return buf.String() +} + +// escapeFormatString escapes strings for use in formatted functions like Sprintf. +func escapeFormatString(fmt string) string { + return strings.Replace(fmt, "%", "%%", -1) +} + +const ( + colorNormal = "\u001b[39m" + colorComment = "\u001b[31m" + colorFile = "\u001b[90m" + colorType = "\u001b[90m" +) diff --git a/vendor/modules.txt b/vendor/modules.txt index c9d9d75f5b..aa4ea8fdd8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -16,9 +16,6 @@ github.com/99designs/gqlgen/graphql/playground # github.com/ActiveState/go-ogle-analytics v0.0.0-20170510030904-9b3f14901527 ## explicit github.com/ActiveState/go-ogle-analytics -# github.com/ActiveState/graphql v0.0.0-20230719154233-6949037a6e48 -## explicit -github.com/ActiveState/graphql # github.com/ActiveState/pty v0.0.0-20230628221854-6fb90eb08a14 ## explicit; go 1.13 github.com/ActiveState/pty @@ -464,8 +461,9 @@ github.com/maruel/natural # github.com/mash/go-tempfile-suffix v0.0.0-20150731093933-48f0f8a3a5ab ## explicit github.com/mash/go-tempfile-suffix -# github.com/matryer/is v1.2.0 -## explicit +# github.com/matryer/is v1.4.1 +## explicit; go 1.14 +github.com/matryer/is # github.com/mattn/go-colorable v0.1.13 ## explicit; go 1.15 github.com/mattn/go-colorable From bbfb2faf87c5d20fb31a04fd0e4e7c6ab7979a41 Mon Sep 17 00:00:00 2001 From: Mike Drakos Date: Tue, 5 Nov 2024 13:54:43 -0800 Subject: [PATCH 327/440] Apply suggestions from code review Co-authored-by: Nathan Rijksen --- cmd/state-svc/internal/messages/messages.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/state-svc/internal/messages/messages.go b/cmd/state-svc/internal/messages/messages.go index a377b5d911..cecd8a9471 100644 --- a/cmd/state-svc/internal/messages/messages.go +++ b/cmd/state-svc/internal/messages/messages.go @@ -77,7 +77,7 @@ func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, allMessages, ok := cacheValue.([]*graph.MessageInfo) if !ok { - return nil, errs.New("Could not get messages from cache") + return nil, errs.New("cacheValue has unexpected type: %T", cacheValue) } conditionParams := *m.baseParams // copy @@ -116,7 +116,7 @@ func check(params *ConditionParams, messages []*graph.MessageInfo, lastReportMap if lastReport, ok := lastReportMap[message.ID]; ok { lr, ok := lastReport.(string) if !ok { - return nil, errs.New("Could not get last reported time for message %s as it's not a string", message.ID) + return nil, errs.New("Could not get last reported time for message %s as it's not a string: %T", message.ID, lastReport) } lastReportTime, err := time.Parse(time.RFC3339, lr) if err != nil { From fe8713a087070c2cd54dcc34ea9dea2ac912374e Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 14:02:05 -0800 Subject: [PATCH 328/440] Move panic handling --- cmd/state-svc/internal/messages/messages.go | 5 +++++ cmd/state-svc/internal/resolver/resolver.go | 6 ++++++ internal/poller/poller.go | 11 +---------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cmd/state-svc/internal/messages/messages.go b/cmd/state-svc/internal/messages/messages.go index cecd8a9471..1495a8a1b2 100644 --- a/cmd/state-svc/internal/messages/messages.go +++ b/cmd/state-svc/internal/messages/messages.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "runtime/debug" "sync" "time" @@ -15,6 +16,7 @@ import ( "github.com/ActiveState/cli/internal/httputil" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/poller" + "github.com/ActiveState/cli/internal/runbits/panics" "github.com/ActiveState/cli/internal/strutils" auth "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/sysinfo" @@ -43,6 +45,9 @@ func New(cfg *config.Instance, auth *auth.Auth) (*Messages, error) { } poll := poller.New(1*time.Hour, func() (interface{}, error) { + defer func() { + panics.LogAndPanic(recover(), debug.Stack()) + }() resp, err := fetch() return resp, err }) diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index ca591fe87d..ba91441e7f 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -57,6 +57,9 @@ func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Res upchecker := updater.NewDefaultChecker(cfg, an) pollUpdate := poller.New(1*time.Hour, func() (interface{}, error) { + defer func() { + panics.LogAndPanic(recover(), debug.Stack()) + }() logging.Debug("Poller checking for update info") return upchecker.CheckFor(constants.ChannelName, "") }) @@ -71,6 +74,9 @@ func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Res } pollAuth := poller.New(time.Duration(int64(time.Millisecond)*pollRate), func() (interface{}, error) { + defer func() { + panics.LogAndPanic(recover(), debug.Stack()) + }() if auth.SyncRequired() { return nil, auth.Sync() } diff --git a/internal/poller/poller.go b/internal/poller/poller.go index 563caa9a0b..56de212f85 100644 --- a/internal/poller/poller.go +++ b/internal/poller/poller.go @@ -1,7 +1,6 @@ package poller import ( - "runtime/debug" "sync" "time" @@ -9,7 +8,6 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/runbits/errors" - "github.com/ActiveState/cli/internal/runbits/panics" ) type Poller struct { @@ -21,15 +19,8 @@ type Poller struct { } func New(interval time.Duration, pollFunc func() (interface{}, error)) *Poller { - wrappedFn := func() (interface{}, error) { - defer func() { - panics.LogAndPanic(recover(), debug.Stack()) - }() - return pollFunc() - } - p := &Poller{ - pollFunc: wrappedFn, + pollFunc: pollFunc, done: make(chan struct{}), } go p.start(interval) From e190d885db28d2e8a3be0ff8364e6cf87119b187 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 14:06:59 -0800 Subject: [PATCH 329/440] Fix JWT response --- internal/graph/response.go | 8 -------- pkg/platform/model/svc.go | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/internal/graph/response.go b/internal/graph/response.go index 52add42637..fb8e33bbde 100644 --- a/internal/graph/response.go +++ b/internal/graph/response.go @@ -1,9 +1,5 @@ package graph -import ( - "encoding/json" -) - type VersionResponse struct { Version Version `json:"version"` } @@ -24,10 +20,6 @@ type GetProcessesInUseResponse struct { Processes []*ProcessInfo `json:"getProcessesInUse"` } -type GetJWTResponse struct { - Payload json.RawMessage `json:"getJWT"` -} - type HashGlobsResponse struct { Response GlobResult `json:"hashGlobs"` } diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 1505040a8c..da1ae109b0 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -186,13 +186,13 @@ func (m *SvcModel) GetJWT(ctx context.Context) (*mono_models.JWT, error) { defer profile.Measure("svc:GetJWT", time.Now()) r := request.NewJWTRequest() - resp := graph.GetJWTResponse{} + var resp json.RawMessage if err := m.request(ctx, r, &resp); err != nil { return nil, errs.Wrap(err, "Error sending messages request") } jwt := &mono_models.JWT{} - err := json.Unmarshal(resp.Payload, &jwt) + err := json.Unmarshal(resp, &jwt) if err != nil { return nil, errs.Wrap(err, "Error unmarshaling JWT") } From e30cf4374366bac08a3161a1988a063ad9b7688a Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 14:23:41 -0800 Subject: [PATCH 330/440] Update remaining service gql responses --- internal/graph/response.go | 25 ------------------------- pkg/platform/model/svc.go | 16 ++++++++-------- 2 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 internal/graph/response.go diff --git a/internal/graph/response.go b/internal/graph/response.go deleted file mode 100644 index fb8e33bbde..0000000000 --- a/internal/graph/response.go +++ /dev/null @@ -1,25 +0,0 @@ -package graph - -type VersionResponse struct { - Version Version `json:"version"` -} - -type ProjectsResponse struct { - Projects []*Project `json:"projects"` -} - -type AvailableUpdateResponse struct { - AvailableUpdate AvailableUpdate `json:"availableUpdate"` -} - -type CheckMessagesResponse struct { - Messages []*MessageInfo `json:"checkMessages"` -} - -type GetProcessesInUseResponse struct { - Processes []*ProcessInfo `json:"getProcessesInUse"` -} - -type HashGlobsResponse struct { - Response GlobResult `json:"hashGlobs"` -} diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index da1ae109b0..f121af7eee 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -65,20 +65,20 @@ func (m *SvcModel) request(ctx context.Context, request gqlclient.Request, resp func (m *SvcModel) StateVersion(ctx context.Context) (*graph.Version, error) { r := request.NewVersionRequest() - resp := graph.VersionResponse{} + resp := graph.Version{} if err := m.request(ctx, r, &resp); err != nil { return nil, err } - return &resp.Version, nil + return &resp, nil } func (m *SvcModel) LocalProjects(ctx context.Context) ([]*graph.Project, error) { r := request.NewLocalProjectsRequest() - response := graph.ProjectsResponse{Projects: []*graph.Project{}} + response := []*graph.Project{} if err := m.request(ctx, r, &response); err != nil { return nil, err } - return response.Projects, nil + return response, nil } // CheckUpdate returns cached update information. There is no guarantee that @@ -88,12 +88,12 @@ func (m *SvcModel) LocalProjects(ctx context.Context) ([]*graph.Project, error) func (m *SvcModel) CheckUpdate(ctx context.Context, desiredChannel, desiredVersion string) (*graph.AvailableUpdate, error) { defer profile.Measure("svc:CheckUpdate", time.Now()) r := request.NewAvailableUpdate(desiredChannel, desiredVersion) - u := graph.AvailableUpdateResponse{} + u := graph.AvailableUpdate{} if err := m.request(ctx, r, &u); err != nil { return nil, errs.Wrap(err, "Error checking if update is available.") } - return &u.AvailableUpdate, nil + return &u, nil } func (m *SvcModel) Ping() error { @@ -230,11 +230,11 @@ func (m *SvcModel) HashGlobs(wd string, globs []string) (*graph.GlobResult, erro defer profile.Measure("svc:HashGlobs", time.Now()) req := request.NewHashGlobs(wd, globs) - res := graph.HashGlobsResponse{} + res := graph.GlobResult{} if err := m.request(context.Background(), req, &res); err != nil { return nil, errs.Wrap(err, "Error sending HashGlobs request to state-svc") } - return &res.Response, errs.New("svcModel.HashGlobs() did not return an expected value") + return &res, errs.New("svcModel.HashGlobs() did not return an expected value") } func jsonFromMap(m map[string]interface{}) string { From f4b31703c9a878ff5d24da7cf6defeee0cb58edd Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 5 Nov 2024 14:54:09 -0800 Subject: [PATCH 331/440] Fix unit test failures --- internal/runners/commit/ingredientcall_test.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index e5f3a1fa52..18877ed3fe 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -1,7 +1,6 @@ package commit import ( - "fmt" "sort" "testing" @@ -56,7 +55,10 @@ main = ingredient( const invalidDepScript = ` main = ingredient( - runtime_deps = [ "I should be a Req" ] + runtime_deps = [ + Req(name="runtimedep", namespace="language", version=Eq(value="1.0")), + "I should be a Req" + ] ) ` @@ -130,7 +132,7 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { Dependency: request.Dependency{ Name: "python", Namespace: "language", - VersionRequirements: "3.7.10", + VersionRequirements: "==3.7.10", Type: request.DependencyTypeRuntime, }, Conditions: []request.Dependency{}, @@ -146,7 +148,7 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { Dependency: request.Dependency{ Name: "runtimedep", Namespace: "language", - VersionRequirements: "1.0", + VersionRequirements: "==1.0", Type: request.DependencyTypeRuntime, }, Conditions: []request.Dependency{}, @@ -155,7 +157,7 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { Dependency: request.Dependency{ Name: "builddep", Namespace: "language", - VersionRequirements: "2.0", + VersionRequirements: "==2.0", Type: request.DependencyTypeBuild, }, Conditions: []request.Dependency{}, @@ -164,7 +166,7 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { Dependency: request.Dependency{ Name: "testdep", Namespace: "language", - VersionRequirements: "3.0", + VersionRequirements: "==3.0", Type: request.DependencyTypeTest, }, Conditions: []request.Dependency{}, @@ -177,6 +179,7 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { invalidDepsScript, nil, func(t assert.TestingT, err error, _ ...interface{}) bool { + assert.Error(t, err) return assert.ErrorAs(t, err, &invalidDepsValueType{}) }, }, @@ -196,7 +199,7 @@ func TestIngredientCall_resolveDependencies(t *testing.T) { fc := bs.FunctionCalls("ingredient")[0] i := &IngredientCall{script: bs, funcCall: fc} got, err := i.resolveDependencies() - if !tt.wantErr(t, err, fmt.Sprintf("resolveDependencies()")) { + if !tt.wantErr(t, err, "") { return } sort.Slice(tt.want, func(i, j int) bool { From fa24657df89083ecbec78a1b26e81467bf171008 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 5 Nov 2024 14:55:41 -0800 Subject: [PATCH 332/440] Update hashes in test --- internal/runners/commit/ingredientcall_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index 18877ed3fe..ab773dff91 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -78,6 +78,7 @@ func Test_hashFuncCall(t *testing.T) { script: simpleScript, seed: "", }, + // If this fails you can update it to actual if you are sure your logic changes would result in the hash being different "6a7c7bd03f10e832", }, { @@ -86,7 +87,8 @@ func Test_hashFuncCall(t *testing.T) { script: simpleAlteredScript, seed: "", }, - "1471d1796a57e938", + // If this fails you can update it to actual if you are sure your logic changes would result in the hash being different + "6003277e065496c1", }, { "Simple With Seed", @@ -94,7 +96,8 @@ func Test_hashFuncCall(t *testing.T) { script: simpleScript, seed: "seed", }, - "a9c1a37b5dd6f0d6", + // If this fails you can update it to actual if you are sure your logic changes would result in the hash being different + "ee89d52f355f9985", }, } for _, tt := range tests { From d0d975c16b621e828793ed9874c32650933a3d7c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 5 Nov 2024 15:00:32 -0800 Subject: [PATCH 333/440] Add log info --- test/integration/buildscript_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index 6e91267f25..068dae4fc8 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -138,7 +138,7 @@ main = wheel // Ensure build didn't fail suite.False(out.HasFailedArtifacts) - suite.Empty(out.Platforms[0].Artifacts[0].Errors) + suite.Empty(out.Platforms[0].Artifacts[0].Errors, "Log: %s", out.Platforms[0].Artifacts[0].LogURL) // Download the wheel artifact that was produced from our source ingredient cp = ts.Spawn("artifacts", "dl", "--output=json", out.Platforms[0].Artifacts[0].ID) From 7a8ba61b96e0d99f23cdc2057083286dc72046a8 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 15:50:45 -0800 Subject: [PATCH 334/440] Do not depend on environment variables --- activestate.yaml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index 0f18c9cfdb..8a50fe411d 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -391,14 +391,10 @@ scripts: value: | set -e - TARGET_BRANCH=$GITHUB_BASE_REF - if [ "$GITHUB_BASE_REF" == "" ]; then - TARGET_BRANCH="master" - fi - - git fetch --quiet origin $TARGET_BRANCH:refs/remotes/origin/$TARGET_BRANCH + git fetch --quiet origin master - CHANGED=$(git diff --name-only origin/$TARGET_BRANCH | grep -v testdata | grep -v vendor) + MERGE_BASE=$(git merge-base HEAD origin/master) + CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) NO_NEWLINE=0 for FILE in $CHANGED; do if file "$FILE" | grep -q -E 'text|ASCII'; then From 7088da3b0b3c4354a09e7d232705de2eff81eeed Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 15:58:58 -0800 Subject: [PATCH 335/440] Debug script --- activestate.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activestate.yaml b/activestate.yaml index 8a50fe411d..fecc086cf9 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -392,9 +392,12 @@ scripts: set -e git fetch --quiet origin master + echo "Checking format for changes since master" MERGE_BASE=$(git merge-base HEAD origin/master) + echo "Merge base: $MERGE_BASE" CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) + echo "Changed files: $CHANGED" NO_NEWLINE=0 for FILE in $CHANGED; do if file "$FILE" | grep -q -E 'text|ASCII'; then From 9f7a82e000b68b2eaecd8b9a5abd35c22529fa97 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 16:03:54 -0800 Subject: [PATCH 336/440] More debugging --- activestate.yaml | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index fecc086cf9..09bdeb5923 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -390,30 +390,46 @@ scripts: description: "Checks if the code is formatted correctly" value: | set -e + set -x - git fetch --quiet origin master + echo "Starting check-format script" + + if ! git fetch --quiet origin master; then + echo "Error: Failed to fetch from origin" + exit 1 + fi echo "Checking format for changes since master" - MERGE_BASE=$(git merge-base HEAD origin/master) + MERGE_BASE=$(git merge-base HEAD origin/master) || { + echo "Error: Could not find merge base with origin/master" + exit 1 + } echo "Merge base: $MERGE_BASE" - CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) + + CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) || true + if [ -z "$CHANGED" ]; then + echo "No relevant changed files found" + exit 0 + } echo "Changed files: $CHANGED" + NO_NEWLINE=0 for FILE in $CHANGED; do + echo "Checking file: $FILE" + if [ ! -f "$FILE" ]; then + echo "Warning: File no longer exists: $FILE" + continue + } if file "$FILE" | grep -q -E 'text|ASCII'; then + echo "File is text: $FILE" if [ $(tail -c 1 "$FILE" | wc -l) -eq 0 ]; then echo "Missing newline at end of file: $FILE" NO_NEWLINE=1 fi + else + echo "Skipping non-text file: $FILE" fi done - - if [ "$NO_NEWLINE" -ne 0 ]; then - echo "Error: Some text files are missing a newline at the end." - exit 1 - else - echo "Success: All modified text files end with a newline." - fi - name: grab-mergecommits language: bash standalone: true From 837c4b305e43b7dac1a5ceab40cd2c5620260815 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 16:10:01 -0800 Subject: [PATCH 337/440] More debugging --- activestate.yaml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/activestate.yaml b/activestate.yaml index 09bdeb5923..eab74a3ea2 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -393,15 +393,39 @@ scripts: set -x echo "Starting check-format script" + + # Check if origin exists + if ! git remote get-url origin; then + echo "Error: 'origin' remote is not configured" + exit 1 + fi - if ! git fetch --quiet origin master; then + # Show current git status + echo "Current branch: $(git branch --show-current)" + echo "Git remotes:" + git remote -v + + # Fetch with more verbose output + echo "Fetching from origin..." + if ! git fetch --verbose origin master; then echo "Error: Failed to fetch from origin" exit 1 fi + + # Verify master branch exists + if ! git show-ref --verify --quiet refs/remotes/origin/master; then + echo "Error: origin/master branch does not exist" + echo "Current HEAD: $(git rev-parse HEAD)" + echo "Origin master: $(git rev-parse origin/master)" + exit 1 + fi + echo "Checking format for changes since master" MERGE_BASE=$(git merge-base HEAD origin/master) || { echo "Error: Could not find merge base with origin/master" + echo "Current HEAD: $(git rev-parse HEAD)" + echo "Origin master: $(git rev-parse origin/master)" exit 1 } echo "Merge base: $MERGE_BASE" From 5b391b6a8d5c91c1f2602645c1e33edcabccaa63 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 16:17:20 -0800 Subject: [PATCH 338/440] Try to reconcile shallow clone --- activestate.yaml | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index eab74a3ea2..ba8535ce1f 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -394,42 +394,38 @@ scripts: echo "Starting check-format script" - # Check if origin exists - if ! git remote get-url origin; then - echo "Error: 'origin' remote is not configured" - exit 1 + if git rev-parse --is-shallow-repository; then + echo "Detected shallow clone, fetching complete history..." + git fetch --unshallow origin master || { + echo "Failed to unshallow repository" + exit 1 + } fi - # Show current git status - echo "Current branch: $(git branch --show-current)" - echo "Git remotes:" - git remote -v - - # Fetch with more verbose output - echo "Fetching from origin..." - if ! git fetch --verbose origin master; then - echo "Error: Failed to fetch from origin" + echo "Fetching master branch..." + git fetch origin master:refs/remotes/origin/master || { + echo "Failed to fetch master branch" exit 1 - fi + } - # Verify master branch exists - if ! git show-ref --verify --quiet refs/remotes/origin/master; then - echo "Error: origin/master branch does not exist" - echo "Current HEAD: $(git rev-parse HEAD)" - echo "Origin master: $(git rev-parse origin/master)" - exit 1 + # If we're in detached HEAD state, create a temporary branch + if [ -z "$(git branch --show-current)" ]; then + echo "Detected detached HEAD state, creating temporary branch..." + git branch temp_format_check HEAD || { + echo "Failed to create temporary branch" + exit 1 + } + git checkout temp_format_check fi echo "Checking format for changes since master" - MERGE_BASE=$(git merge-base HEAD origin/master) || { echo "Error: Could not find merge base with origin/master" echo "Current HEAD: $(git rev-parse HEAD)" echo "Origin master: $(git rev-parse origin/master)" exit 1 } - echo "Merge base: $MERGE_BASE" - + CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) || true if [ -z "$CHANGED" ]; then echo "No relevant changed files found" From cdef753795f3152cb508e9a44ff7c842db845c25 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 16:22:45 -0800 Subject: [PATCH 339/440] Fix syntax error --- activestate.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index ba8535ce1f..e1308e03f4 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -430,7 +430,7 @@ scripts: if [ -z "$CHANGED" ]; then echo "No relevant changed files found" exit 0 - } + fi echo "Changed files: $CHANGED" NO_NEWLINE=0 @@ -439,7 +439,7 @@ scripts: if [ ! -f "$FILE" ]; then echo "Warning: File no longer exists: $FILE" continue - } + fi if file "$FILE" | grep -q -E 'text|ASCII'; then echo "File is text: $FILE" if [ $(tail -c 1 "$FILE" | wc -l) -eq 0 ]; then From 416aa32be27794276ea1c98a6e59fad3f3eded39 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 5 Nov 2024 16:29:03 -0800 Subject: [PATCH 340/440] Clean up script --- activestate.yaml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index e1308e03f4..e80c19130c 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -390,10 +390,7 @@ scripts: description: "Checks if the code is formatted correctly" value: | set -e - set -x - echo "Starting check-format script" - if git rev-parse --is-shallow-repository; then echo "Detected shallow clone, fetching complete history..." git fetch --unshallow origin master || { @@ -402,27 +399,13 @@ scripts: } fi - echo "Fetching master branch..." git fetch origin master:refs/remotes/origin/master || { echo "Failed to fetch master branch" exit 1 } - # If we're in detached HEAD state, create a temporary branch - if [ -z "$(git branch --show-current)" ]; then - echo "Detected detached HEAD state, creating temporary branch..." - git branch temp_format_check HEAD || { - echo "Failed to create temporary branch" - exit 1 - } - git checkout temp_format_check - fi - - echo "Checking format for changes since master" MERGE_BASE=$(git merge-base HEAD origin/master) || { echo "Error: Could not find merge base with origin/master" - echo "Current HEAD: $(git rev-parse HEAD)" - echo "Origin master: $(git rev-parse origin/master)" exit 1 } From 3db6eb745ecb48e696abee993a5e79aaadd98b9f Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 5 Nov 2024 17:48:11 -0500 Subject: [PATCH 341/440] Refactor wrapping, support multiple tags after continuations, and added unit test. --- internal/colorize/wrap.go | 100 +++++++++++++++++++++------------ internal/colorize/wrap_test.go | 31 +++++++++- 2 files changed, 93 insertions(+), 38 deletions(-) diff --git a/internal/colorize/wrap.go b/internal/colorize/wrap.go index 721ec366de..d40234b0be 100644 --- a/internal/colorize/wrap.go +++ b/internal/colorize/wrap.go @@ -4,8 +4,7 @@ import ( "fmt" "regexp" "strings" - - "github.com/ActiveState/cli/internal/logging" + "unicode/utf8" ) type WrappedLines []WrappedLine @@ -17,8 +16,8 @@ type WrappedLine struct { func (c WrappedLines) String() string { var result string - for _, crop := range c { - result = result + crop.Line + for _, wrapped := range c { + result = result + wrapped.Line } return result @@ -28,43 +27,56 @@ var indentRegexp = regexp.MustCompile(`^([ ]+)`) var isLinkRegexp = regexp.MustCompile(`\s*(\[[^\]]+\])?https?://`) func Wrap(text string, maxLen int, includeLineEnds bool, continuation string) WrappedLines { + // Determine indentation of wrapped lines based on any leading indentation. indent := "" if indentMatch := indentRegexp.FindStringSubmatch(text); indentMatch != nil { indent = indentMatch[0] if len(text) > len(indent) && strings.HasPrefix(text[len(indent):], "• ") { + // If text to wrap is a bullet item, indent extra to be flush after bullet. indent += " " } } - maxLen -= len(continuation) + // If wrapping includes continuation text, reduce maximum wrapping length accordingly. + maxLen -= utf8.RuneCountInString(StripColorCodes(continuation)) entries := make([]WrappedLine, 0) colorCodes := colorRx.FindAllStringSubmatchIndex(text, -1) colorNames := colorRx.FindAllStringSubmatch(text, -1) - isLineEnd := false entry := WrappedLine{} + // Iterate over the text, one character at a time, and construct wrapped lines while doing so. for pos, amend := range text { - inColorTag := inRange(pos, colorCodes) - - isLineEnd = amend == '\n' + isLineEnd := amend == '\n' + isTextEnd := pos == len(text)-1 + // Add the current character to the wrapped line. + // Update the wrapped line's length as long as the added character is not part of a tag like + // [ERROR] or [/RESET]. if !isLineEnd { entry.Line += string(amend) - if !inColorTag { + if !inRange(pos, colorCodes) { entry.Length++ } } - - // Ensure the next position is not within a color tag and check conditions that would end this entry - if isLineEnd || (!inRange(pos+1, colorCodes) && (entry.Length == maxLen || pos == len(text)-1)) { - wrapped := "" - wrappedLength := len(indent) - nextCharIsSpace := pos+1 < len(text) && isSpace(text[pos+1]) - if !isLineEnd && entry.Length == maxLen && !nextCharIsSpace && pos < len(text)-1 { - // Put the current word on the next line, if possible. - // Find the start of the current word and its printed length, taking color ranges and - // multi-byte characters into account. + atWrapPosition := entry.Length == maxLen + + // When we've reached the end of the line, either naturally (line or text end), or when we've + // reached the wrap position (maximum length), we need to wrap the current word (if any) and + // set up the next (wrapped) line. + // Note that if we've reached the wrap position but there's a tag immediately after it, we want + // to include the tag, so do not wrap in that case. + if isLineEnd || isTextEnd || (atWrapPosition && !inRange(pos+1, colorCodes)) { + wrapped := "" // the start of the next (wrapped) line + wrappedLength := len(indent) // the current length of the next (wrapped) line + + // We need to prepare the next (wrapped) line unless we're at line end, text end, not at wrap + // position, or the next character is a space (i.e. no wrapping needed). + nextCharIsSpace := !isTextEnd && isSpace(text[pos+1]) + if !isLineEnd && !isTextEnd && atWrapPosition && !nextCharIsSpace { + // Determine the start of the current word along with its printed length (taking color + // ranges and multi-byte characters into account). + // We need to know these things in order to put it on the next line (if possible). i := len(entry.Line) - 1 for ; i > 0; i-- { if isSpace(entry.Line[i]) { @@ -75,24 +87,33 @@ func Wrap(text string, maxLen int, includeLineEnds bool, continuation string) Wr wrappedLength++ } } - // Extract the word from the current line if it doesn't start the line. - if i > 0 && i < len(entry.Line)-1 && !isLinkRegexp.MatchString(entry.Line[i:]) { - tag := colorTag(pos, colorCodes, colorNames) - if continuation != "" && tag != "" { - // Do not colorize the continuation. - wrapped = fmt.Sprintf("%s[/RESET]%s%s%s", indent, continuation, tag, entry.Line[i:]) - } else { - wrapped = indent + continuation + entry.Line[i:] - } - logging.Debug("continuation: '%s'", continuation) - logging.Debug("wrapped: '%s'", wrapped) + + // We can wrap this word on the next line as long as it's not at the beginning of the + // current line and it's not part of a hyperlink. + canWrap := i > 0 && !isLinkRegexp.MatchString(entry.Line[i:]) + if canWrap { + wrapped = entry.Line[i:] entry.Line = entry.Line[:i] entry.Length -= wrappedLength isLineEnd = true // emulate for wrapping purposes + + // Prepend the continuation string to the wrapped line, and indent it as necessary. + // The continuation itself should not be tagged with anything like [ERROR], but any text + // after the continuation should be tagged. + if continuation != "" { + if tags := colorTags(pos, colorCodes, colorNames); len(tags) > 0 { + wrapped = fmt.Sprintf("%s[/RESET]%s%s%s", indent, continuation, strings.Join(tags, ""), wrapped) + } else { + wrapped = indent + continuation + wrapped + } + } else { + wrapped = indent + wrapped + } } else { wrappedLength = len(indent) // reset } } + entries = append(entries, entry) entry = WrappedLine{Line: wrapped, Length: wrappedLength} } @@ -115,17 +136,22 @@ func inRange(pos int, ranges [][]int) bool { return false } -// colorTag returns the currently active color tag (if any) at the given position. -func colorTag(pos int, ranges [][]int, names [][]string) string { +// colorTags returns the currently active color tags (if any) at the given position. +func colorTags(pos int, ranges [][]int, names [][]string) []string { + tags := make([]string, 0) for i, intRange := range ranges { if pos < intRange[0] { - continue // before [COLOR] + break // before [COLOR] } - if i < len(ranges)-1 || pos < ranges[i+1][0] { - return names[i][0] // missing [/RESET] or between [COLOR] and [/RESET] + if pos > intRange[0] { + if names[i][1] == "/RESET" { + tags = make([]string, 0) // clear + } else { + tags = append(tags, names[i][0]) + } } } - return "" + return tags } func isSpace(b byte) bool { return b == ' ' || b == '\t' } diff --git a/internal/colorize/wrap_test.go b/internal/colorize/wrap_test.go index f4d793c8a0..2c5d61dfda 100644 --- a/internal/colorize/wrap_test.go +++ b/internal/colorize/wrap_test.go @@ -5,6 +5,8 @@ import ( "reflect" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func Test_Wrap(t *testing.T) { @@ -123,8 +125,35 @@ func Test_WrapAsString(t *testing.T) { escape := func(v string) string { return strings.Replace(v, "\n", "\\n", -1) } - t.Errorf("Wrap() = %v, want %v (crop data: %s)", escape(got.String()), escape(tt.text), escape(fmt.Sprintf("%#v", got))) + t.Errorf("Wrap() = %v, want %v (wrap data: %s)", escape(got.String()), escape(tt.text), escape(fmt.Sprintf("%#v", got))) } }) } } + +func TestWrapBullet(t *testing.T) { + lines := Wrap(" • This is a bullet", 15, true, "") + assert.Equal(t, " • This is a \n bullet", lines.String()) +} + +func TestWrapContinuation(t *testing.T) { + // Test normal wrapping with no continuation. + lines := Wrap("This is an error", 9, true, "") + assert.Equal(t, "This is \nan error", lines.String()) + + // Verify continuations are not tagged. + lines = Wrap("[ERROR]This is an error[/RESET]", 10, true, "|") + assert.Equal(t, "[ERROR]This is \n[/RESET]|[ERROR]an error[/RESET]", lines.String()) + + // Verify only active tags come after continuations. + lines = Wrap("[BOLD]This is not[/RESET] an error", 10, true, "|") + assert.Equal(t, "[BOLD]This is \n[/RESET]|[BOLD]not[/RESET] an \n|error", lines.String()) + + // Verify continuations are not tagged, even if [/RESET] is omitted. + lines = Wrap("[ERROR]This is an error", 10, true, "|") + assert.Equal(t, "[ERROR]This is \n[/RESET]|[ERROR]an error", lines.String()) + + // Verify multiple tags are restored after continuations. + lines = Wrap("[BOLD][RED]This is a bold, red message[/RESET]", 11, true, "|") + assert.Equal(t, "[BOLD][RED]This is a \n[/RESET]|[BOLD][RED]bold, red \n[/RESET]|[BOLD][RED]message[/RESET]", lines.String()) +} From fe25e87c3385a56c9f664e848e3634d1c4c752d2 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 6 Nov 2024 11:56:21 -0500 Subject: [PATCH 342/440] Use struct for bullet list bullets, added tests, and use String() method instead of output marshaler method. --- internal/output/renderers/bulletlist.go | 73 ++++++++++++------- internal/output/renderers/bulletlist_test.go | 33 +++++++++ .../runbits/dependencies/changesummary.go | 2 +- internal/runbits/dependencies/summary.go | 2 +- internal/runners/artifacts/artifacts.go | 4 +- internal/runners/cve/cve.go | 2 +- 6 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 internal/output/renderers/bulletlist_test.go diff --git a/internal/output/renderers/bulletlist.go b/internal/output/renderers/bulletlist.go index 49c98145b3..ca760beed4 100644 --- a/internal/output/renderers/bulletlist.go +++ b/internal/output/renderers/bulletlist.go @@ -2,50 +2,58 @@ package renderers import ( "strings" + "unicode/utf8" "github.com/ActiveState/cli/internal/colorize" - "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/termutils" ) +type Bullets struct { + Start string + Mid string + Link string + End string +} + type bulletList struct { prefix string items []string - bullets []string + bullets Bullets } // BulletTree outputs a list like: // // ├─ one // ├─ two -// │ wrapped +// │ wrapped // └─ three -var BulletTree = []string{output.TreeMid, output.TreeMid, output.TreeLink + " ", output.TreeEnd} +var BulletTree = Bullets{output.TreeMid, output.TreeMid, output.TreeLink + " ", output.TreeEnd} + +// BulletTreeDisabled is like BulletTree, but tags the tree glyphs with [DISABLED]. +var BulletTreeDisabled = Bullets{ + "[DISABLED]" + output.TreeMid + "[/RESET]", + "[DISABLED]" + output.TreeMid + "[/RESET]", + "[DISABLED]" + output.TreeLink + " [/RESET]", + "[DISABLED]" + output.TreeEnd + "[/RESET]", +} // HeadedBulletTree outputs a list like: // // one // ├─ two -// │ wrapped +// │ wrapped // └─ three -var HeadedBulletTree = []string{"", output.TreeMid, output.TreeLink + " ", output.TreeEnd} +var HeadedBulletTree = Bullets{"", output.TreeMid, output.TreeLink + " ", output.TreeEnd} -// NewBulletList returns a printable list of items prefixed with the given set of bullets. -// The set of bullets should contain four items: the bullet for the first item (e.g. ""); the -// bullet for each subsequent item (e.g. "├─"); the bullet for an item's wrapped lines, if any -// (e.g. "│"); and the bullet for the last item (e.g. "└─"). -// The returned list can be passed to a plain printer. It should not be passed to a structured -// printer. -func NewBulletList(prefix string, bullets, items []string) *bulletList { - if len(bullets) != 4 { - multilog.Error("Invalid bullet list: 4 bullets required") - bullets = BulletTree - } +func NewBulletList(prefix string, bullets Bullets, items []string) *bulletList { return &bulletList{prefix, items, bullets} } -func (b *bulletList) MarshalOutput(format output.Format) interface{} { +// str is the business logic for returning a bullet list's string representation for a given +// maximum width. Clients should call String() instead. Only tests should directly call this +// function. +func (b *bulletList) str(maxWidth int) string { out := make([]string, len(b.items)) // Determine the indentation of each item. @@ -61,29 +69,38 @@ func (b *bulletList) MarshalOutput(format output.Format) interface{} { } for i, item := range b.items { - bullet := b.bullets[0] + bullet := b.bullets.Start if len(b.items) == 1 { - bullet = b.bullets[3] // special case list length of one; use last bullet + bullet = b.bullets.End // special case list length of one; use last bullet } + prefix := "" + continuation := "" if i == 0 { if bullet != "" { bullet += " " } - item = b.prefix + bullet + item + prefix = b.prefix + bullet } else { - bullet = b.bullets[1] + " " - continuation := indent + b.bullets[2] + " " + bullet = b.bullets.Mid + " " + continuation = indent + b.bullets.Link + " " if i == len(b.items)-1 { - bullet = b.bullets[3] + " " // this is the last item + bullet = b.bullets.End + " " // this is the last item continuation = " " } - maxWidth := termutils.GetWidth() - len(indent) - len(bullet) - wrapped := colorize.Wrap(item, maxWidth, true, continuation).String() - item = indent + bullet + wrapped + prefix = indent + bullet } - out[i] = item + wrapped := colorize.Wrap(item, maxWidth-len(indent)-bulletLength(bullet), true, continuation).String() + out[i] = prefix + wrapped } return strings.Join(out, "\n") } + +func (b *bulletList) String() string { + return b.str(termutils.GetWidth()) +} + +func bulletLength(bullet string) int { + return utf8.RuneCountInString(colorize.StripColorCodes(bullet)) +} diff --git a/internal/output/renderers/bulletlist_test.go b/internal/output/renderers/bulletlist_test.go new file mode 100644 index 0000000000..ad8f85102b --- /dev/null +++ b/internal/output/renderers/bulletlist_test.go @@ -0,0 +1,33 @@ +package renderers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBulletTree(t *testing.T) { + assert.Equal(t, "├─ one\n├─ two \n│ wrapped\n└─ three", + NewBulletList("", BulletTree, []string{"one", "two wrapped", "three"}).str(13)) +} + +func TestBulletTreeDisabled(t *testing.T) { + assert.Equal(t, "[DISABLED]├─[/RESET] one\n[DISABLED]├─[/RESET] two \n[DISABLED]│ [/RESET] wrapped\n[DISABLED]└─[/RESET] three", + NewBulletList("", BulletTreeDisabled, []string{"one", "two wrapped", "three"}).str(13)) +} + +func TestHeadedBulletTree(t *testing.T) { + assert.Equal(t, "one\n├─ two \n│ wrapped\n└─ three", + NewBulletList("", HeadedBulletTree, []string{"one", "two wrapped", "three"}).str(13)) +} + +func TestUnwrappedLink(t *testing.T) { + assert.Equal(t, "├─ one\n└─ https://host:port/path#anchor", + NewBulletList("", BulletTree, []string{"one", "https://host:port/path#anchor"}).str(10)) +} + +func TestIndented(t *testing.T) { + // Note: use max width of 17 instead of 15 to account for extra continuation indent (2+2). + assert.Equal(t, " ├─ one\n ├─ two \n │ wrapped\n └─ three", + NewBulletList(" ", BulletTree, []string{"one", "two wrapped", "three"}).str(17)) +} diff --git a/internal/runbits/dependencies/changesummary.go b/internal/runbits/dependencies/changesummary.go index 75e9a760f6..a1c94ea6b4 100644 --- a/internal/runbits/dependencies/changesummary.go +++ b/internal/runbits/dependencies/changesummary.go @@ -130,6 +130,6 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, items[i] = item } - out.Notice(renderers.NewBulletList(" ", renderers.BulletTree, items)) + out.Notice(renderers.NewBulletList(" ", renderers.BulletTreeDisabled, items).String()) out.Notice("") // blank line } diff --git a/internal/runbits/dependencies/summary.go b/internal/runbits/dependencies/summary.go index a9008ac3a5..ac14f260df 100644 --- a/internal/runbits/dependencies/summary.go +++ b/internal/runbits/dependencies/summary.go @@ -44,6 +44,6 @@ func OutputSummary(out output.Outputer, directDependencies buildplan.Artifacts) items[i] = fmt.Sprintf("[ACTIONABLE]%s@%s[/RESET] %s", ingredient.Name, ingredient.Version, subdepLocale) } - out.Notice(renderers.NewBulletList(" ", renderers.BulletTree, items)) + out.Notice(renderers.NewBulletList(" ", renderers.BulletTreeDisabled, items).String()) out.Notice("") // blank line } diff --git a/internal/runners/artifacts/artifacts.go b/internal/runners/artifacts/artifacts.go index 6d28db8ab0..22734c783d 100644 --- a/internal/runners/artifacts/artifacts.go +++ b/internal/runners/artifacts/artifacts.go @@ -211,7 +211,7 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error { fmt.Sprintf("%s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed")), fmt.Sprintf("%s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")), fmt.Sprintf("%s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL), - })) + }).String()) continue case artifact.status == types.ArtifactSkipped: b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped"))) @@ -239,7 +239,7 @@ func (b *Artifacts) outputPlain(out *StructuredOutput, fullID bool) error { fmt.Sprintf("%s ([ERROR]%s[/RESET])", artifact.Name, locale.T("artifact_status_failed")), fmt.Sprintf("%s: [ERROR]%s[/RESET]", locale.T("artifact_status_failed_message"), strings.Join(artifact.Errors, ": ")), fmt.Sprintf("%s: [ACTIONABLE]%s[/RESET]", locale.T("artifact_status_failed_log"), artifact.LogURL), - })) + }).String()) continue case artifact.status == types.ArtifactSkipped: b.out.Print(fmt.Sprintf(" • %s ([NOTICE]%s[/RESET])", artifact.Name, locale.T("artifact_status_skipped"))) diff --git a/internal/runners/cve/cve.go b/internal/runners/cve/cve.go index f1f886307b..96c24a24f4 100644 --- a/internal/runners/cve/cve.go +++ b/internal/runners/cve/cve.go @@ -202,7 +202,7 @@ func (rd *cveOutput) MarshalOutput(format output.Format) interface{} { } items[i] = fmt.Sprintf("%-10s [ACTIONABLE]%s[/RESET]", severity, d.CveID) } - rd.output.Print(renderers.NewBulletList("", renderers.BulletTree, items)) + rd.output.Print(renderers.NewBulletList("", renderers.BulletTree, items).String()) rd.output.Print("") } From 7ff9681a6bcde56a8fecd5bc4062c06210d41f21 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 6 Nov 2024 09:27:16 -0800 Subject: [PATCH 343/440] Cleanup script --- activestate.yaml | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index e80c19130c..91f5a44eb9 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -391,24 +391,14 @@ scripts: value: | set -e - if git rev-parse --is-shallow-repository; then + if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then echo "Detected shallow clone, fetching complete history..." - git fetch --unshallow origin master || { - echo "Failed to unshallow repository" - exit 1 - } + git fetch --unshallow origin master fi - git fetch origin master:refs/remotes/origin/master || { - echo "Failed to fetch master branch" - exit 1 - } + git fetch origin master:refs/remotes/origin/master - MERGE_BASE=$(git merge-base HEAD origin/master) || { - echo "Error: Could not find merge base with origin/master" - exit 1 - } - + MERGE_BASE=$(git merge-base HEAD origin/master) CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) || true if [ -z "$CHANGED" ]; then echo "No relevant changed files found" @@ -418,19 +408,14 @@ scripts: NO_NEWLINE=0 for FILE in $CHANGED; do - echo "Checking file: $FILE" if [ ! -f "$FILE" ]; then - echo "Warning: File no longer exists: $FILE" continue fi if file "$FILE" | grep -q -E 'text|ASCII'; then - echo "File is text: $FILE" if [ $(tail -c 1 "$FILE" | wc -l) -eq 0 ]; then echo "Missing newline at end of file: $FILE" NO_NEWLINE=1 fi - else - echo "Skipping non-text file: $FILE" fi done - name: grab-mergecommits From 85a8031419e0faea4bdaffb6e0978181d35ca7f1 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 6 Nov 2024 09:53:41 -0800 Subject: [PATCH 344/440] Don't check if target branch is master --- activestate.yaml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/activestate.yaml b/activestate.yaml index 91f5a44eb9..e3cd66155a 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -389,28 +389,19 @@ scripts: standalone: true description: "Checks if the code is formatted correctly" value: | - set -e - - if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then - echo "Detected shallow clone, fetching complete history..." - git fetch --unshallow origin master + TARGET_BRANCH=$GITHUB_BASE_REF + if [ "$GITHUB_BASE_REF" == "" ]; then + TARGET_BRANCH="master" fi - git fetch origin master:refs/remotes/origin/master - - MERGE_BASE=$(git merge-base HEAD origin/master) - CHANGED=$(git diff --name-only $MERGE_BASE...HEAD | grep -v testdata | grep -v vendor) || true - if [ -z "$CHANGED" ]; then - echo "No relevant changed files found" + if [ "$TARGET_BRANCH" == "master" ]; then + echo "Target branch is master, not checking for newlines" exit 0 fi - echo "Changed files: $CHANGED" + CHANGED=$(git diff --name-only origin/$TARGET_BRANCH | grep -v testdata | grep -v vendor) NO_NEWLINE=0 for FILE in $CHANGED; do - if [ ! -f "$FILE" ]; then - continue - fi if file "$FILE" | grep -q -E 'text|ASCII'; then if [ $(tail -c 1 "$FILE" | wc -l) -eq 0 ]; then echo "Missing newline at end of file: $FILE" @@ -418,6 +409,13 @@ scripts: fi fi done + + if [ "$NO_NEWLINE" -ne 0 ]; then + echo "Error: Some text files are missing a newline at the end." + exit 1 + else + echo "Success: All modified text files end with a newline." + fi - name: grab-mergecommits language: bash standalone: true From 5112a8c4dcb7fd9530afc60282b1e1da97cd583b Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 6 Nov 2024 09:59:29 -0800 Subject: [PATCH 345/440] Put back --- activestate.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activestate.yaml b/activestate.yaml index e3cd66155a..ef353eebef 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -389,6 +389,8 @@ scripts: standalone: true description: "Checks if the code is formatted correctly" value: | + set -e + TARGET_BRANCH=$GITHUB_BASE_REF if [ "$GITHUB_BASE_REF" == "" ]; then TARGET_BRANCH="master" From 329c41200a8f4afa22743e55bd757d72e2325c6d Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 6 Nov 2024 10:14:21 -0800 Subject: [PATCH 346/440] Add back git fetch --- activestate.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activestate.yaml b/activestate.yaml index ef353eebef..8d021d9ce1 100644 --- a/activestate.yaml +++ b/activestate.yaml @@ -401,6 +401,8 @@ scripts: exit 0 fi + git fetch --quiet origin $TARGET_BRANCH:refs/remotes/origin/$TARGET_BRANCH + CHANGED=$(git diff --name-only origin/$TARGET_BRANCH | grep -v testdata | grep -v vendor) NO_NEWLINE=0 for FILE in $CHANGED; do From 96baec2114fe33899a4f9c505abd33374d6ff61d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 6 Nov 2024 10:29:37 -0800 Subject: [PATCH 347/440] Also handle requirements in solve_legacy --- pkg/buildscript/unmarshal_buildexpression.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 0a57fb306f..8561deb229 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -382,6 +382,7 @@ type requirementFunction struct { // requirement functions (eg. `solve(requirements=..)`). var requirementFunctions = []requirementFunction{ {solveFuncName, []string{requirementsKey}}, + {solveLegacyFuncName, []string{requirementsKey}}, {"ingredient", []string{"build_deps", "runtime_deps", "test_deps"}}, } From d2a7df34902c384fc0af7059f3e4a5d58ead15bc Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 6 Nov 2024 11:23:32 -0800 Subject: [PATCH 348/440] Add back empty response check --- pkg/platform/model/checkpoints.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/platform/model/checkpoints.go b/pkg/platform/model/checkpoints.go index 3ca70b00d2..2cc16dddb6 100644 --- a/pkg/platform/model/checkpoints.go +++ b/pkg/platform/model/checkpoints.go @@ -11,6 +11,7 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/pkg/platform/api/graphql" gqlModel "github.com/ActiveState/cli/pkg/platform/api/graphql/model" @@ -108,6 +109,10 @@ func FetchCheckpointForCommit(commitID strfmt.UUID, auth *authentication.Auth) ( logging.Debug("Returning %d requirements", len(response)) + if len(response) == 0 { + return nil, locale.WrapError(ErrNoData, "err_no_data_found") + } + return response, nil } From 332823c63aa556f3501216b26dc0db03c75700b4 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 6 Nov 2024 12:58:05 -0800 Subject: [PATCH 349/440] Add debug info --- test/integration/buildscript_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index 068dae4fc8..d600c9ab69 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -134,7 +134,7 @@ main = wheel time.Sleep(time.Second * 5) } return nil - }, e2e.RuntimeBuildSourcingTimeout)) + }, e2e.RuntimeBuildSourcingTimeout), ts.DebugMessage("")) // Ensure build didn't fail suite.False(out.HasFailedArtifacts) From ba3e9a53581bd0b968ee6eaaddd00f3e4b7e872f Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 6 Nov 2024 13:55:19 -0800 Subject: [PATCH 350/440] Ensure function call hash is consistently calculated --- go.mod | 1 + go.sum | 2 + internal/runners/commit/ingredientcall.go | 6 + .../runners/commit/ingredientcall_test.go | 6 +- vendor/github.com/gowebpki/jcs/.gitignore | 34 ++ vendor/github.com/gowebpki/jcs/LICENSE | 202 ++++++++ vendor/github.com/gowebpki/jcs/README.md | 72 +++ vendor/github.com/gowebpki/jcs/es6numfmt.go | 59 +++ vendor/github.com/gowebpki/jcs/jcs.go | 489 ++++++++++++++++++ vendor/modules.txt | 3 + 10 files changed, 871 insertions(+), 3 deletions(-) create mode 100644 vendor/github.com/gowebpki/jcs/.gitignore create mode 100644 vendor/github.com/gowebpki/jcs/LICENSE create mode 100644 vendor/github.com/gowebpki/jcs/README.md create mode 100644 vendor/github.com/gowebpki/jcs/es6numfmt.go create mode 100644 vendor/github.com/gowebpki/jcs/jcs.go diff --git a/go.mod b/go.mod index beaf422d8d..fbe81cfbf2 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,7 @@ require ( github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/lipgloss v0.9.1 github.com/go-git/go-git/v5 v5.12.0 + github.com/gowebpki/jcs v1.0.1 github.com/klauspost/compress v1.11.4 github.com/mholt/archiver/v3 v3.5.1 github.com/zijiren233/yaml-comment v0.2.1 diff --git a/go.sum b/go.sum index f1008087f9..b53eb36707 100644 --- a/go.sum +++ b/go.sum @@ -361,6 +361,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gowebpki/jcs v1.0.1 h1:Qjzg8EOkrOTuWP7DqQ1FbYtcpEbeTzUoTN9bptp8FOU= +github.com/gowebpki/jcs v1.0.1/go.mod h1:CID1cNZ+sHp1CCpAR8mPf6QRtagFBgPJE0FCUQ6+BrI= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 9b0f7542e3..1eaae8cd2a 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -20,6 +20,7 @@ import ( "github.com/ActiveState/cli/pkg/platform/model/buildplanner" "github.com/brunoga/deep" "github.com/cespare/xxhash" + "github.com/gowebpki/jcs" ) const namespaceSuffixFiles = "files" @@ -181,6 +182,11 @@ func hashFuncCall(fc *buildscript.FuncCall, seed string) (string, error) { if err != nil { return "", errs.Wrap(err, "Could not marshal function call") } + // Go's JSON implementation does not produce canonical output, so we need to utilize additional tooling to ensure + // the hash we create is based on canonical data. + if fcb, err = jcs.Transform(fcb); err != nil { + return "", errs.Wrap(err, "Could not transform json blob to canonical json") + } hasher := xxhash.New() hasher.Write([]byte(seed)) hasher.Write(fcb) diff --git a/internal/runners/commit/ingredientcall_test.go b/internal/runners/commit/ingredientcall_test.go index ab773dff91..bf4d70bf19 100644 --- a/internal/runners/commit/ingredientcall_test.go +++ b/internal/runners/commit/ingredientcall_test.go @@ -79,7 +79,7 @@ func Test_hashFuncCall(t *testing.T) { seed: "", }, // If this fails you can update it to actual if you are sure your logic changes would result in the hash being different - "6a7c7bd03f10e832", + "6395ae666f293cd2", }, { "Simple Altered", @@ -88,7 +88,7 @@ func Test_hashFuncCall(t *testing.T) { seed: "", }, // If this fails you can update it to actual if you are sure your logic changes would result in the hash being different - "6003277e065496c1", + "b9a486d874a27bd5", }, { "Simple With Seed", @@ -97,7 +97,7 @@ func Test_hashFuncCall(t *testing.T) { seed: "seed", }, // If this fails you can update it to actual if you are sure your logic changes would result in the hash being different - "ee89d52f355f9985", + "5cca922938bd9261", }, } for _, tt := range tests { diff --git a/vendor/github.com/gowebpki/jcs/.gitignore b/vendor/github.com/gowebpki/jcs/.gitignore new file mode 100644 index 0000000000..a692565e11 --- /dev/null +++ b/vendor/github.com/gowebpki/jcs/.gitignore @@ -0,0 +1,34 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +coverageout +coverage.html +goreportcard.db + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# vscode +*/.vscode +*.code-workspace + +# sublime text +*.sublime-workspace +*.sublime-project + +# Mac files +.DS_Store diff --git a/vendor/github.com/gowebpki/jcs/LICENSE b/vendor/github.com/gowebpki/jcs/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/gowebpki/jcs/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/gowebpki/jcs/README.md b/vendor/github.com/gowebpki/jcs/README.md new file mode 100644 index 0000000000..c2c976784e --- /dev/null +++ b/vendor/github.com/gowebpki/jcs/README.md @@ -0,0 +1,72 @@ +![JCS](https://cyberphone.github.io/doc/security/jcs.svg) + +# JSON Canonicalization + +[![Go Report Card](https://goreportcard.com/badge/github.com/gowebpki/jcs)](https://goreportcard.com/report/github.com/gowebpki/jcs) +[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://pkg.go.dev/github.com/gowebpki/jcs) +[![GitHub license](https://img.shields.io/github/license/gowebpki/jcs.svg?style=flat)](https://github.com/gowebpki/jcs/blob/master/LICENSE) +[![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/gowebpki/jcs.svg?style=flat)](https://github.com/gowebpki/jcs) + +Cryptographic operations like hashing and signing depend on that the target +data does not change during serialization, transport, or parsing. +By applying the rules defined by JCS (JSON Canonicalization Scheme), +data provided in the JSON [[RFC8259](https://tools.ietf.org/html/rfc8259)] +format can be exchanged "as is", while still being subject to secure cryptographic operations. +JCS achieves this by building on the serialization formats for JSON +primitives as defined by ECMAScript [[ES](https://ecma-international.org/ecma-262/)], +constraining JSON data to the I-JSON [[RFC7493](https://tools.ietf.org/html//rfc7493)] subset, +and through a platform independent property sorting scheme. + +Public RFC: https://tools.ietf.org/html/rfc8785 + +The JSON Canonicalization Scheme concept in a nutshell: +- Serialization of primitive JSON data types using methods compatible with ECMAScript's `JSON.stringify()` +- Lexicographic sorting of JSON `Object` properties in a *recursive* process +- JSON `Array` data is also subject to canonicalization, *but element order remains untouched* + +### Original Work + +This code was originally created by Anders Rundgren aka cyberphone and can be found here: +https://github.com/cyberphone/json-canonicalization. This fork and work is done with Anders' +permission and is an attempt to clean up the Golang version. + + +### Sample Input +```code +{ + "numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001], + "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", + "literals": [null, true, false] +} +``` +### Expected Output +```code +{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"} +``` + +Note: for platform interoperable canonicalization, the output must be converted to UTF-8 +as well, here shown in hexadecimal notation: + +```code +7b 22 6c 69 74 65 72 61 6c 73 22 3a 5b 6e 75 6c 6c 2c 74 72 75 65 2c 66 61 6c 73 65 5d 2c 22 6e +75 6d 62 65 72 73 22 3a 5b 33 33 33 33 33 33 33 33 33 2e 33 33 33 33 33 33 33 2c 31 65 2b 33 30 +2c 34 2e 35 2c 30 2e 30 30 32 2c 31 65 2d 32 37 5d 2c 22 73 74 72 69 6e 67 22 3a 22 e2 82 ac 24 +5c 75 30 30 30 66 5c 6e 41 27 42 5c 22 5c 5c 5c 5c 5c 22 2f 22 7d +``` +### Combining JCS and JWS (RFC7515) +[JWS-JCS](https://github.com/cyberphone/jws-jcs#combining-detached-jws-with-jcs-json-canonicalization-scheme) + +### On-line Browser JCS Test +https://cyberphone.github.io/doc/security/browser-json-canonicalization.html + +### ECMAScript Proposal: JSON.canonify() +[JSON.canonify()](https://github.com/cyberphone/json-canonicalization/blob/master/JSON.canonify.md) + +### Other Canonicalization Efforts +https://tools.ietf.org/html/draft-staykov-hu-json-canonical-form-00 + +http://wiki.laptop.org/go/Canonical_JSON + +https://gibson042.github.io/canonicaljson-spec/ + +https://gist.github.com/mikesamuel/20710f94a53e440691f04bf79bc3d756 diff --git a/vendor/github.com/gowebpki/jcs/es6numfmt.go b/vendor/github.com/gowebpki/jcs/es6numfmt.go new file mode 100644 index 0000000000..011e1df554 --- /dev/null +++ b/vendor/github.com/gowebpki/jcs/es6numfmt.go @@ -0,0 +1,59 @@ +// Copyright 2021 Bret Jordan & Benedikt Thoma, All rights reserved. +// Copyright 2006-2019 WebPKI.org (http://webpki.org). +// +// Use of this source code is governed by an Apache 2.0 license that can be +// found in the LICENSE file in the root of the source tree. + +package jcs + +import ( + "errors" + "math" + "strconv" + "strings" +) + +const invalidPattern uint64 = 0x7ff0000000000000 + +// NumberToJSON converts numbers in IEEE-754 double precision into the +// format specified for JSON in EcmaScript Version 6 and forward. +// The core application for this is canonicalization per RFC 8785: +func NumberToJSON(ieeeF64 float64) (res string, err error) { + ieeeU64 := math.Float64bits(ieeeF64) + + // Special case: NaN and Infinity are invalid in JSON + if (ieeeU64 & invalidPattern) == invalidPattern { + return "null", errors.New("Invalid JSON number: " + strconv.FormatUint(ieeeU64, 16)) + } + + // Special case: eliminate "-0" as mandated by the ES6-JSON/JCS specifications + if ieeeF64 == 0 { // Right, this line takes both -0 and 0 + return "0", nil + } + + // Deal with the sign separately + var sign string = "" + if ieeeF64 < 0 { + ieeeF64 = -ieeeF64 + sign = "-" + } + + // ES6 has a unique "g" format + var format byte = 'e' + if ieeeF64 < 1e+21 && ieeeF64 >= 1e-6 { + format = 'f' + } + + // The following should (in "theory") do the trick: + es6Formatted := strconv.FormatFloat(ieeeF64, format, -1, 64) + + // Ryu version + exponent := strings.IndexByte(es6Formatted, 'e') + if exponent > 0 { + // Go outputs "1e+09" which must be rewritten as "1e+9" + if es6Formatted[exponent+2] == '0' { + es6Formatted = es6Formatted[:exponent+2] + es6Formatted[exponent+3:] + } + } + return sign + es6Formatted, nil +} diff --git a/vendor/github.com/gowebpki/jcs/jcs.go b/vendor/github.com/gowebpki/jcs/jcs.go new file mode 100644 index 0000000000..f67524c544 --- /dev/null +++ b/vendor/github.com/gowebpki/jcs/jcs.go @@ -0,0 +1,489 @@ +// Copyright 2021 Bret Jordan & Benedikt Thoma, All rights reserved. +// Copyright 2006-2019 WebPKI.org (http://webpki.org). +// +// Use of this source code is governed by an Apache 2.0 license that can be +// found in the LICENSE file in the root of the source tree. + +// Package jcs transforms UTF-8 JSON data into a canonicalized version according RFC 8785 +package jcs + +import ( + "container/list" + "errors" + "fmt" + "strconv" + "strings" + "unicode/utf16" +) + +type nameValueType struct { + name string + sortKey []uint16 + value string +} + +type jcsData struct { + // JSON data MUST be UTF-8 encoded + jsonData []byte + // Current pointer in jsonData + index int +} + +// JSON standard escapes (modulo \u) +var ( + asciiEscapes = []byte{'\\', '"', 'b', 'f', 'n', 'r', 't'} + binaryEscapes = []byte{'\\', '"', '\b', '\f', '\n', '\r', '\t'} +) + +// JSON literals +var literals = []string{"true", "false", "null"} + +// Transform converts raw JSON data from a []byte array into a canonicalized version according RFC 8785 +func Transform(jsonData []byte) ([]byte, error) { + if jsonData == nil { + return nil, errors.New("No JSON data provided") + } + + // Create a JCS Data struct to store the JSON Data and the index. + var jd jcsData + jd.jsonData = jsonData + j := &jd + + transformed, err := j.parseEntry() + if err != nil { + return nil, err + } + + for j.index < len(j.jsonData) { + if !j.isWhiteSpace(j.jsonData[j.index]) { + return nil, errors.New("Improperly terminated JSON object") + } + j.index++ + } + return []byte(transformed), err +} + +func (j *jcsData) isWhiteSpace(c byte) bool { + return c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09 +} + +func (j *jcsData) nextChar() (byte, error) { + if j.index < len(j.jsonData) { + c := j.jsonData[j.index] + if c > 0x7f { + return 0, errors.New("Unexpected non-ASCII character") + } + j.index++ + return c, nil + } + return 0, errors.New("Unexpected EOF reached") +} + +// scan advances index on jsonData to the first non whitespace character and returns it. +func (j *jcsData) scan() (byte, error) { + for { + c, err := j.nextChar() + if err != nil { + return 0, err + } + + if j.isWhiteSpace(c) { + continue + } + + return c, nil + } +} + +func (j *jcsData) scanFor(expected byte) error { + c, err := j.scan() + if err != nil { + return err + } + if c != expected { + return fmt.Errorf("Expected %s but got %s", string(expected), string(c)) + } + return nil +} + +func (j *jcsData) getUEscape() (rune, error) { + start := j.index + for i := 0; i < 4; i++ { + _, err := j.nextChar() + if err != nil { + return 0, err + } + } + + u16, err := strconv.ParseUint(string(j.jsonData[start:j.index]), 16, 64) + if err != nil { + return 0, err + } + return rune(u16), nil +} + +func (j *jcsData) decorateString(rawUTF8 string) string { + var quotedString strings.Builder + quotedString.WriteByte('"') + +CoreLoop: + for _, c := range []byte(rawUTF8) { + // Is this within the JSON standard escapes? + for i, esc := range binaryEscapes { + if esc == c { + quotedString.WriteByte('\\') + quotedString.WriteByte(asciiEscapes[i]) + + continue CoreLoop + } + } + if c < 0x20 { + // Other ASCII control characters must be escaped with \uhhhh + quotedString.WriteString(fmt.Sprintf("\\u%04x", c)) + } else { + quotedString.WriteByte(c) + } + } + quotedString.WriteByte('"') + + return quotedString.String() +} + +// parseEntry is the entrypoint into the parsing control flow +func (j *jcsData) parseEntry() (string, error) { + c, err := j.scan() + if err != nil { + return "", err + } + j.index-- + + switch c { + case '{', '"', '[': + return j.parseElement() + default: + value, err := parseLiteral(string(j.jsonData)) + if err != nil { + return "", err + } + + j.index = len(j.jsonData) + return value, nil + } +} + +func (j *jcsData) parseQuotedString() (string, error) { + var rawString strings.Builder + +CoreLoop: + for { + var c byte + if j.index < len(j.jsonData) { + c = j.jsonData[j.index] + j.index++ + } else { + return "", errors.New("Unexpected EOF reached") + } + + if c == '"' { + break + } + + if c < ' ' { + return "", errors.New("Unterminated string literal") + } else if c == '\\' { + // Escape sequence + c, err := j.nextChar() + if err != nil { + return "", err + } + + if c == 'u' { + // The \u escape + firstUTF16, err := j.getUEscape() + if err != nil { + return "", err + } + + if utf16.IsSurrogate(firstUTF16) { + // If the first UTF-16 code unit has a certain value there must be + // another succeeding UTF-16 code unit as well + backslash, err := j.nextChar() + if err != nil { + return "", err + } + u, err := j.nextChar() + if err != nil { + return "", err + } + + if backslash != '\\' || u != 'u' { + return "", errors.New("Missing surrogate") + } + + // Output the UTF-32 code point as UTF-8 + uEscape, err := j.getUEscape() + if err != nil { + return "", err + } + rawString.WriteRune(utf16.DecodeRune(firstUTF16, uEscape)) + + } else { + // Single UTF-16 code identical to UTF-32. Output as UTF-8 + rawString.WriteRune(firstUTF16) + } + } else if c == '/' { + // Benign but useless escape + rawString.WriteByte('/') + } else { + // The JSON standard escapes + for i, esc := range asciiEscapes { + if esc == c { + rawString.WriteByte(binaryEscapes[i]) + continue CoreLoop + } + } + return "", fmt.Errorf("Unexpected escape: \\%s", string(c)) + } + } else { + // Just an ordinary ASCII character alternatively a UTF-8 byte + // outside of ASCII. + // Note that properly formatted UTF-8 never clashes with ASCII + // making byte per byte search for ASCII break characters work + // as expected. + rawString.WriteByte(c) + } + } + + return rawString.String(), nil +} + +func (j *jcsData) parseSimpleType() (string, error) { + var token strings.Builder + + j.index-- + + // no condition is needed here. + // if the buffer reaches EOF scan returns an error, or we terminate because the + // json simple type terminates + for { + c, err := j.scan() + if err != nil { + return "", err + } + + if c == ',' || c == ']' || c == '}' { + j.index-- + break + } + + token.WriteByte(c) + } + + if token.Len() == 0 { + return "", errors.New("Missing argument") + } + + return parseLiteral(token.String()) +} + +func parseLiteral(value string) (string, error) { + // Is it a JSON literal? + for _, literal := range literals { + if literal == value { + return literal, nil + } + } + + // Apparently not so we assume that it is a I-JSON number + ieeeF64, err := strconv.ParseFloat(value, 64) + if err != nil { + return "", err + } + + value, err = NumberToJSON(ieeeF64) + if err != nil { + return "", err + } + + return value, nil +} + +func (j *jcsData) parseElement() (string, error) { + c, err := j.scan() + if err != nil { + return "", err + } + + switch c { + case '{': + return j.parseObject() + case '"': + str, err := j.parseQuotedString() + if err != nil { + return "", err + } + return j.decorateString(str), nil + case '[': + return j.parseArray() + default: + return j.parseSimpleType() + } +} + +func (j *jcsData) peek() (byte, error) { + c, err := j.scan() + if err != nil { + return 0, err + } + + j.index-- + return c, nil +} + +func (j *jcsData) parseArray() (string, error) { + var arrayData strings.Builder + var next bool + + arrayData.WriteByte('[') + + for { + c, err := j.peek() + if err != nil { + return "", err + } + + if c == ']' { + j.index++ + break + } + + if next { + err = j.scanFor(',') + if err != nil { + return "", err + } + arrayData.WriteByte(',') + } else { + next = true + } + + element, err := j.parseElement() + if err != nil { + return "", err + } + arrayData.WriteString(element) + } + + arrayData.WriteByte(']') + return arrayData.String(), nil +} + +func (j *jcsData) lexicographicallyPrecedes(sortKey []uint16, e *list.Element) (bool, error) { + // Find the minimum length of the sortKeys + oldSortKey := e.Value.(nameValueType).sortKey + minLength := len(oldSortKey) + if minLength > len(sortKey) { + minLength = len(sortKey) + } + for q := 0; q < minLength; q++ { + diff := int(sortKey[q]) - int(oldSortKey[q]) + if diff < 0 { + // Smaller => Precedes + return true, nil + } else if diff > 0 { + // Bigger => No match + return false, nil + } + // Still equal => Continue + } + // The sortKeys compared equal up to minLength + if len(sortKey) < len(oldSortKey) { + // Shorter => Precedes + return true, nil + } + if len(sortKey) == len(oldSortKey) { + return false, fmt.Errorf("Duplicate key: %s", e.Value.(nameValueType).name) + } + // Longer => No match + return false, nil +} + +func (j *jcsData) parseObject() (string, error) { + nameValueList := list.New() + var next bool = false +CoreLoop: + for { + c, err := j.peek() + if err != nil { + return "", err + } + + if c == '}' { + // advance index because of peeked '}' + j.index++ + break + } + + if next { + err = j.scanFor(',') + if err != nil { + return "", err + } + } + next = true + + err = j.scanFor('"') + if err != nil { + return "", err + } + rawUTF8, err := j.parseQuotedString() + if err != nil { + break + } + // Sort keys on UTF-16 code units + // Since UTF-8 doesn't have endianess this is just a value transformation + // In the Go case the transformation is UTF-8 => UTF-32 => UTF-16 + sortKey := utf16.Encode([]rune(rawUTF8)) + err = j.scanFor(':') + if err != nil { + return "", err + } + + element, err := j.parseElement() + if err != nil { + return "", err + } + nameValue := nameValueType{rawUTF8, sortKey, element} + for e := nameValueList.Front(); e != nil; e = e.Next() { + // Check if the key is smaller than a previous key + if precedes, err := j.lexicographicallyPrecedes(sortKey, e); err != nil { + return "", err + } else if precedes { + // Precedes => Insert before and exit sorting + nameValueList.InsertBefore(nameValue, e) + continue CoreLoop + } + // Continue searching for a possibly succeeding sortKey + // (which is straightforward since the list is ordered) + } + // The sortKey is either the first or is succeeding all previous sortKeys + nameValueList.PushBack(nameValue) + } + + // Now everything is sorted so we can properly serialize the object + var objectData strings.Builder + objectData.WriteByte('{') + next = false + for e := nameValueList.Front(); e != nil; e = e.Next() { + if next { + objectData.WriteByte(',') + } + next = true + nameValue := e.Value.(nameValueType) + objectData.WriteString(j.decorateString(nameValue.name)) + objectData.WriteByte(':') + objectData.WriteString(nameValue.value) + } + objectData.WriteByte('}') + return objectData.String(), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4126f382ca..f5aa52ed7c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -371,6 +371,9 @@ github.com/google/uuid # github.com/gorilla/websocket v1.5.0 ## explicit; go 1.12 github.com/gorilla/websocket +# github.com/gowebpki/jcs v1.0.1 +## explicit; go 1.15 +github.com/gowebpki/jcs # github.com/hashicorp/go-cleanhttp v0.5.2 ## explicit; go 1.13 github.com/hashicorp/go-cleanhttp From 774120b9f8a61caa29bd196a34e879ce8796e7d3 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 6 Nov 2024 14:40:55 -0800 Subject: [PATCH 351/440] Increase solving timeout --- internal/testhelpers/e2e/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index a36ad1f57e..1446a21b6e 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -47,7 +47,7 @@ import ( const RuntimeBuildSourcingTimeout = 6 * time.Minute var ( - RuntimeSolvingTimeoutOpt = termtest.OptExpectTimeout(1 * time.Minute) + RuntimeSolvingTimeoutOpt = termtest.OptExpectTimeout(90 * time.Second) RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) RuntimeBuildSourcingTimeoutOpt = termtest.OptExpectTimeout(RuntimeBuildSourcingTimeout) ) From 864af32995e479be89dd4ee7e1ed209dd314aa71 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 6 Nov 2024 14:42:07 -0800 Subject: [PATCH 352/440] Add debug info --- test/integration/buildscript_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/buildscript_int_test.go b/test/integration/buildscript_int_test.go index d600c9ab69..adaf3e9ae6 100644 --- a/test/integration/buildscript_int_test.go +++ b/test/integration/buildscript_int_test.go @@ -104,7 +104,7 @@ main = wheel // Create a new commit, which will use the source files to create an ingredient if it doesn't already exist cp := ts.Spawn("commit") - cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt) + cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt, termtest.OptExpectErrorMessage(ts.DebugMessage(""))) // Running commit again should say there are no changes // If this fails then there's likely an issue with calculating the file hash, or checking whether an ingredient From 445dbe41d8726d4ce832f0f4ce9d23ed729b014b Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Wed, 6 Nov 2024 14:45:38 -0800 Subject: [PATCH 353/440] Give windows more time --- internal/testhelpers/e2e/session.go | 2 -- internal/testhelpers/e2e/session_unix.go | 8 ++++++++ internal/testhelpers/e2e/session_windows.go | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 internal/testhelpers/e2e/session_unix.go create mode 100644 internal/testhelpers/e2e/session_windows.go diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 1446a21b6e..9655aae58f 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -44,8 +44,6 @@ import ( "github.com/stretchr/testify/require" ) -const RuntimeBuildSourcingTimeout = 6 * time.Minute - var ( RuntimeSolvingTimeoutOpt = termtest.OptExpectTimeout(90 * time.Second) RuntimeSourcingTimeoutOpt = termtest.OptExpectTimeout(3 * time.Minute) diff --git a/internal/testhelpers/e2e/session_unix.go b/internal/testhelpers/e2e/session_unix.go new file mode 100644 index 0000000000..c9f594ded2 --- /dev/null +++ b/internal/testhelpers/e2e/session_unix.go @@ -0,0 +1,8 @@ +//go:build !windows +// +build !windows + +package e2e + +import "time" + +const RuntimeBuildSourcingTimeout = 6 * time.Minute diff --git a/internal/testhelpers/e2e/session_windows.go b/internal/testhelpers/e2e/session_windows.go new file mode 100644 index 0000000000..4c6d8fbf62 --- /dev/null +++ b/internal/testhelpers/e2e/session_windows.go @@ -0,0 +1,6 @@ +package e2e + +import "time" + +// RuntimeBuildSourcingTimeout uses a higher timeout on Windows cause Windows +const RuntimeBuildSourcingTimeout = 12 * time.Minute From 18ce970353919ff545ad66df13c17d408ca01dd2 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 6 Nov 2024 15:59:21 -0800 Subject: [PATCH 354/440] Remove unused interface --- internal/gqlclient/gqlclient.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/gqlclient/gqlclient.go b/internal/gqlclient/gqlclient.go index 5580fd0ac6..273a6e7b5a 100644 --- a/internal/gqlclient/gqlclient.go +++ b/internal/gqlclient/gqlclient.go @@ -44,11 +44,6 @@ type RequestWithFiles interface { Files() []File } -type RequestWithMultiQuery interface { - Request - IsMultiQuery() bool -} - type Header map[string][]string type graphqlClient = graphql.Client From 110c0db277e18ba873406ab0bb3630e00db892ed Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 7 Nov 2024 10:54:25 -0500 Subject: [PATCH 355/440] CVE report should include changed requirements. Previously it would only show for added requirements. If a requirement changes versions, we should include it in the CVE report. --- internal/runbits/cves/cves.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index a810fae39b..b9b598482b 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -77,7 +77,7 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } } - names := addedRequirements(oldBuildPlan, newBuildPlan) + names := changedRequirements(oldBuildPlan, newBuildPlan) pg := output.StartSpinner(c.prime.Output(), locale.Tr("progress_cve_search", strings.Join(names, ", ")), constants.TerminalAnimationInterval) ingredientVulnerabilities, err := model.FetchVulnerabilitiesForIngredients(c.prime.Auth(), ingredients) @@ -235,7 +235,7 @@ func (c *CveReport) promptForSecurity() (bool, error) { return confirm, nil } -func addedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildplan.BuildPlan) []string { +func changedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildplan.BuildPlan) []string { var names []string var oldRequirements buildplan.Requirements if oldBuildPlan != nil { @@ -243,13 +243,16 @@ func addedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildpla } newRequirements := newBuildPlan.Requirements() - oldReqs := make(map[string]bool) + oldReqs := make(map[string]string) for _, req := range oldRequirements { - oldReqs[qualifiedName(req)] = true + oldReqs[qualifiedName(req)] = req.Ingredient.Version } for _, req := range newRequirements { - if oldReqs[qualifiedName(req)] || req.Namespace == buildplan.NamespaceInternal { + if req.Namespace == buildplan.NamespaceInternal { + continue + } + if version, exists := oldReqs[qualifiedName(req)]; exists && version == req.Ingredient.Version { continue } names = append(names, req.Name) From 3c0d5fcfd39b31acd18ad76f114894d6d89adfec Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 7 Nov 2024 16:08:41 -0500 Subject: [PATCH 356/440] `state commit` should not poll for build result. --- internal/runners/commit/commit.go | 2 +- pkg/platform/model/buildplanner/build.go | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/runners/commit/commit.go b/internal/runners/commit/commit.go index 1ac478ce50..4b7b208ccc 100644 --- a/internal/runners/commit/commit.go +++ b/internal/runners/commit/commit.go @@ -160,7 +160,7 @@ func (c *Commit) Run() (rerr error) { } // Get old buildplan. - oldCommit, err := bp.FetchCommit(localCommitID, proj.Owner(), proj.Name(), nil) + oldCommit, err := bp.FetchCommitNoPoll(localCommitID, proj.Owner(), proj.Name(), nil) if err != nil { return errs.Wrap(err, "Failed to fetch old commit") } diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index feef9b1a28..867e056396 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -57,6 +57,14 @@ func (c *client) Run(req gqlclient.Request, resp interface{}) error { const fetchCommitCacheExpiry = time.Hour * 12 func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, target *string) (*Commit, error) { + return b.fetchCommit(commitID, owner, project, target, true) +} + +func (b *BuildPlanner) FetchCommitNoPoll(commitID strfmt.UUID, owner, project string, target *string) (*Commit, error) { + return b.fetchCommit(commitID, owner, project, target, false) +} + +func (b *BuildPlanner) fetchCommit(commitID strfmt.UUID, owner, project string, target *string, poll bool) (*Commit, error) { logging.Debug("FetchCommit, commitID: %s, owner: %s, project: %s", commitID, owner, project) resp := &response.ProjectCommitResponse{} @@ -92,7 +100,7 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, // The BuildPlanner will return a build plan with a status of // "planning" if the build plan is not ready yet. We need to // poll the BuildPlanner until the build is ready. - if resp.Project.Commit.Build.Status == raw.Planning { + if poll && resp.Project.Commit.Build.Status == raw.Planning { resp.Project.Commit.Build, err = b.pollBuildPlanned(commitID.String(), owner, project, target) if err != nil { return nil, errs.Wrap(err, "failed to poll build plan") From 9132b74b0feec29238830811dfca46052a1a9478 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 7 Nov 2024 12:30:12 -0500 Subject: [PATCH 357/440] Merge pull request #3582 from ActiveState/mitchell/dx-3150 CVE report should include changed requirements. --- internal/runbits/cves/cves.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index a810fae39b..b9b598482b 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -77,7 +77,7 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } } - names := addedRequirements(oldBuildPlan, newBuildPlan) + names := changedRequirements(oldBuildPlan, newBuildPlan) pg := output.StartSpinner(c.prime.Output(), locale.Tr("progress_cve_search", strings.Join(names, ", ")), constants.TerminalAnimationInterval) ingredientVulnerabilities, err := model.FetchVulnerabilitiesForIngredients(c.prime.Auth(), ingredients) @@ -235,7 +235,7 @@ func (c *CveReport) promptForSecurity() (bool, error) { return confirm, nil } -func addedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildplan.BuildPlan) []string { +func changedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildplan.BuildPlan) []string { var names []string var oldRequirements buildplan.Requirements if oldBuildPlan != nil { @@ -243,13 +243,16 @@ func addedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildpla } newRequirements := newBuildPlan.Requirements() - oldReqs := make(map[string]bool) + oldReqs := make(map[string]string) for _, req := range oldRequirements { - oldReqs[qualifiedName(req)] = true + oldReqs[qualifiedName(req)] = req.Ingredient.Version } for _, req := range newRequirements { - if oldReqs[qualifiedName(req)] || req.Namespace == buildplan.NamespaceInternal { + if req.Namespace == buildplan.NamespaceInternal { + continue + } + if version, exists := oldReqs[qualifiedName(req)]; exists && version == req.Ingredient.Version { continue } names = append(names, req.Name) From 67c2110e862f411c862d8ee0e9c636b54d2bff5c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 7 Nov 2024 13:33:49 -0800 Subject: [PATCH 358/440] Ensure filepaths are using forward slash --- internal/archiver/archiver.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index 4b28f36332..550ef792a3 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -2,6 +2,7 @@ package archiver import ( "os" + "path/filepath" "strings" "github.com/ActiveState/cli/internal/errs" @@ -57,10 +58,11 @@ func CreateTgz(filepath string, fileMaps []FileMap) error { func FilesWithCommonParent(filepaths ...string) []FileMap { var fileMaps []FileMap common := fileutils.CommonParentPath(filepaths) - for _, filepath := range filepaths { + for _, path := range filepaths { + path = filepath.ToSlash(path) fileMaps = append(fileMaps, FileMap{ - Source: filepath, - Target: strings.TrimPrefix(strings.TrimPrefix(filepath, common), "/"), + Source: filepath.ToSlash(path), + Target: strings.TrimPrefix(filepath.ToSlash(strings.TrimPrefix(path, common)), "/"), }) } return fileMaps From 6c38c9e75cba68cac7146e416c4079dfae8e605d Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 7 Nov 2024 13:34:19 -0800 Subject: [PATCH 359/440] Drop redundant calls --- internal/archiver/archiver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index 550ef792a3..90799136ec 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -61,8 +61,8 @@ func FilesWithCommonParent(filepaths ...string) []FileMap { for _, path := range filepaths { path = filepath.ToSlash(path) fileMaps = append(fileMaps, FileMap{ - Source: filepath.ToSlash(path), - Target: strings.TrimPrefix(filepath.ToSlash(strings.TrimPrefix(path, common)), "/"), + Source: path, + Target: strings.TrimPrefix(strings.TrimPrefix(path, common), "/"), }) } return fileMaps From d4caca7d365309e48db4e82c6b7bc1506ef99e00 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 7 Nov 2024 16:35:17 -0500 Subject: [PATCH 360/440] Fix failing tests. --- test/integration/publish_int_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/integration/publish_int_test.go b/test/integration/publish_int_test.go index 5e1caf8def..a8963c8724 100644 --- a/test/integration/publish_int_test.go +++ b/test/integration/publish_int_test.go @@ -52,10 +52,12 @@ func (suite *PublishIntegrationTestSuite) TestPublish() { expect expect } - tempFile := fileutils.TempFilePath("", "*.zip") + tempFile := fileutils.TempFilePath("", ".zip") + suite.Require().NoError(fileutils.Touch(tempFile)) defer os.Remove(tempFile) - tempFileInvalid := fileutils.TempFilePath("", "*.notzip") + tempFileInvalid := fileutils.TempFilePath("", ".notzip") + suite.Require().NoError(fileutils.Touch(tempFileInvalid)) defer os.Remove(tempFileInvalid) ts := e2e.New(suite.T(), false) From f380d3dacb325ec415a7190fad498fd1c60a8731 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Thu, 7 Nov 2024 14:07:06 -0800 Subject: [PATCH 361/440] Use relative paths for archives --- cmd/state-svc/internal/hash/file_hasher.go | 23 ++++++++----------- .../internal/hash/file_hasher_test.go | 6 +++++ internal/archiver/archiver.go | 10 +++++--- internal/runners/commit/ingredientcall.go | 2 +- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/cmd/state-svc/internal/hash/file_hasher.go b/cmd/state-svc/internal/hash/file_hasher.go index 91218e656b..505b1e3637 100644 --- a/cmd/state-svc/internal/hash/file_hasher.go +++ b/cmd/state-svc/internal/hash/file_hasher.go @@ -45,17 +45,14 @@ func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashed return "", nil, errs.Wrap(err, "Could not match glob: %s", glob) } sort.Strings(files) // ensure consistent ordering - for _, f := range files { - if !filepath.IsAbs(f) { - af, err := filepath.Abs(filepath.Join(wd, f)) - if err != nil { - return "", nil, errs.Wrap(err, "Could not get absolute path for file: %s", f) - } - f = af + for _, relativePath := range files { + absolutePath, err := filepath.Abs(filepath.Join(wd, relativePath)) + if err != nil { + return "", nil, errs.Wrap(err, "Could not get absolute path for file: %s", relativePath) } - fileInfo, err := os.Stat(f) + fileInfo, err := os.Stat(absolutePath) if err != nil { - return "", nil, errs.Wrap(err, "Could not stat file: %s", f) + return "", nil, errs.Wrap(err, "Could not stat file: %s", absolutePath) } if fileInfo.IsDir() { @@ -72,10 +69,10 @@ func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashed } else { fileHasher := xxhash.New() // include filepath in hash, because moving files should affect the hash - fmt.Fprintf(fileHasher, "%016x", f) - file, err := os.Open(f) + fmt.Fprintf(fileHasher, "%016x", relativePath) + file, err := os.Open(absolutePath) if err != nil { - return "", nil, errs.Wrap(err, "Could not open file: %s", f) + return "", nil, errs.Wrap(err, "Could not open file: %s", absolutePath) } defer file.Close() if _, err := io.Copy(fileHasher, file); err != nil { @@ -90,7 +87,7 @@ func (fh *FileHasher) HashFiles(wd string, globs []string) (_ string, _ []hashed hashes = append(hashes, hash) hashedFiles = append(hashedFiles, hashedFile{ Pattern: glob, - Path: f, + Path: relativePath, Hash: hash, }) } diff --git a/cmd/state-svc/internal/hash/file_hasher_test.go b/cmd/state-svc/internal/hash/file_hasher_test.go index 1e5ec7cbba..2873e53ded 100644 --- a/cmd/state-svc/internal/hash/file_hasher_test.go +++ b/cmd/state-svc/internal/hash/file_hasher_test.go @@ -1,9 +1,11 @@ package hash import ( + "fmt" "os" "path/filepath" "sort" + "strings" "testing" "time" @@ -50,6 +52,10 @@ func TestFileHasher_HashFiles(t *testing.T) { hash2, files2, err := hasher.HashFiles(dir, []string{"./**/*"}) require.NoError(t, err, errs.JoinMessage(err)) + for _, f := range files1 { + assert.False(t, strings.HasPrefix(f.Path, dir), fmt.Sprintf("'%s' should not be prefixed with '%s'", f.Path, dir)) + } + sort.Slice(files1, func(i, j int) bool { return files1[i].Path < files1[j].Path }) sort.Slice(files2, func(i, j int) bool { return files2[i].Path < files2[j].Path }) require.Len(t, files2, 3) diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index 90799136ec..ada7438fec 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -15,8 +15,8 @@ type FileMap struct { Target string } -func CreateTgz(filepath string, fileMaps []FileMap) error { - f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644) +func CreateTgz(archivePath string, workDir string, fileMaps []FileMap) error { + f, err := os.OpenFile(archivePath, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return errs.Wrap(err, "Could not create temp file") } @@ -28,7 +28,11 @@ func CreateTgz(filepath string, fileMaps []FileMap) error { defer tgz.Close() for _, fileMap := range fileMaps { - file, err := os.Open(fileMap.Source) + source := fileMap.Source + if !filepath.IsAbs(source) { + source = filepath.Join(workDir, source) + } + file, err := os.Open(source) if err != nil { return errs.Wrap(err, "Could not open file") } diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 1eaae8cd2a..654d340756 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -107,7 +107,7 @@ func (i *IngredientCall) createIngredient(hash string, hashedFiles []*graph.Glob } tmpFile := fileutils.TempFilePath("", fmt.Sprintf("bs-hash-%s.tar.gz", hash)) - if err := archiver.CreateTgz(tmpFile, archiver.FilesWithCommonParent(files...)); err != nil { + if err := archiver.CreateTgz(tmpFile, i.prime.Project().Dir(), archiver.FilesWithCommonParent(files...)); err != nil { return nil, errs.Wrap(err, "Could not create tar.gz") } defer os.Remove(tmpFile) From b1c60863e9a7d3fff7e3c91e20d53104d34f7a50 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 7 Nov 2024 17:37:54 -0500 Subject: [PATCH 362/440] `state commit`, `state push`, and `state reset` should not fail if there is no work to be don --- internal/runners/commit/commit.go | 11 ++--------- internal/runners/push/push.go | 12 +++++++++--- internal/runners/push/rationalize.go | 15 --------------- internal/runners/reset/reset.go | 3 ++- test/integration/commit_int_test.go | 2 +- test/integration/push_int_test.go | 7 +------ test/integration/reset_int_test.go | 2 +- 7 files changed, 16 insertions(+), 36 deletions(-) diff --git a/internal/runners/commit/commit.go b/internal/runners/commit/commit.go index 4b7b208ccc..08a7f2f12b 100644 --- a/internal/runners/commit/commit.go +++ b/internal/runners/commit/commit.go @@ -35,8 +35,6 @@ func New(p primeable) *Commit { return &Commit{p} } -var ErrNoChanges = errors.New("buildscript has no changes") - func rationalizeError(err *error) { var buildPlannerErr *bpResp.BuildPlannerError @@ -44,12 +42,6 @@ func rationalizeError(err *error) { case err == nil: return - case errors.Is(*err, ErrNoChanges): - *err = errs.WrapUserFacing(*err, locale.Tl( - "commit_notice_no_change", - "Your buildscript contains no new changes. No commit necessary.", - ), errs.SetInput()) - case errors.Is(*err, buildscript_runbit.ErrBuildscriptNotExist): *err = errs.WrapUserFacing(*err, locale.T("err_buildscript_not_exist")) @@ -109,7 +101,8 @@ func (c *Commit) Run() (rerr error) { // Check if there is anything to commit if equals { - return ErrNoChanges + out.Notice(locale.Tl("commit_notice_no_change", "Your buildscript contains no new changes. No commit necessary.")) + return nil } pg := output.StartSpinner(out, locale.T("progress_commit"), constants.TerminalAnimationInterval) diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index df61f99e2f..ca1c774161 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -13,6 +13,7 @@ import ( "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/localcommit" + bpResp "github.com/ActiveState/cli/pkg/platform/api/buildplanner/response" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" "github.com/ActiveState/cli/pkg/platform/authentication" @@ -65,8 +66,7 @@ const ( ) var ( - errNoChanges = errors.New("no changes") - errNoCommit = errors.New("no commit") + errNoCommit = errors.New("no commit") ) type errProjectNameInUse struct { @@ -234,7 +234,8 @@ func (r *Push) Run(params PushParams) (rerr error) { // Check if branch is already up to date if branch.CommitID != nil && branch.CommitID.String() == commitID.String() { - return errNoChanges + r.out.Notice(locale.T("push_no_changes")) + return nil } // Perform the (fast-forward) push. @@ -246,6 +247,11 @@ func (r *Push) Run(params PushParams) (rerr error) { Strategy: types.MergeCommitStrategyFastForward, }) if err != nil { + var mergeCommitErr *bpResp.MergedCommitError + if errors.As(err, &mergeCommitErr) && mergeCommitErr.Type == types.NoChangeSinceLastCommitErrorType { + r.out.Notice(locale.T("push_no_changes")) + return nil + } return errs.Wrap(err, "Could not push") } } diff --git a/internal/runners/push/rationalize.go b/internal/runners/push/rationalize.go index 90af8e6774..762ed5c55a 100644 --- a/internal/runners/push/rationalize.go +++ b/internal/runners/push/rationalize.go @@ -52,13 +52,6 @@ func rationalizeError(err *error) { errs.SetInput(), ) - // No changes made - case errors.Is(*err, errNoChanges): - *err = errs.WrapUserFacing(*err, - locale.T("push_no_changes"), - errs.SetInput(), - ) - // Project name is already in use case errors.As(*err, &projectNameInUseErr): *err = errs.WrapUserFacing(*err, @@ -86,14 +79,6 @@ func rationalizeError(err *error) { *err = errs.WrapUserFacing(*err, locale.T("err_push_target_invalid_history"), errs.SetInput()) - - // No changes made - case types.NoChangeSinceLastCommitErrorType: - *err = errs.WrapUserFacing(*err, - locale.T("push_no_changes"), - errs.SetInput(), - ) - } } } diff --git a/internal/runners/reset/reset.go b/internal/runners/reset/reset.go index c046635e63..45e042e725 100644 --- a/internal/runners/reset/reset.go +++ b/internal/runners/reset/reset.go @@ -86,7 +86,8 @@ func (r *Reset) Run(params *Params) error { return errs.Wrap(err, "Unable to get local commit") } if *latestCommit == localCommitID { - return locale.NewInputError("err_reset_latest", "You are already on the latest commit") + r.out.Notice(locale.Tl("err_reset_latest", "You are already on the latest commit")) + return nil } commitID = *latestCommit diff --git a/test/integration/commit_int_test.go b/test/integration/commit_int_test.go index 9dbe1c22b4..77e549d7ea 100644 --- a/test/integration/commit_int_test.go +++ b/test/integration/commit_int_test.go @@ -35,7 +35,7 @@ func (suite *CommitIntegrationTestSuite) TestCommitManualBuildScriptMod() { cp := ts.Spawn("commit") cp.Expect("no new changes") - cp.ExpectExitCode(1) + cp.ExpectExitCode(0) _, err = buildscript_runbit.ScriptFromProject(proj) suite.Require().NoError(err) // verify validity diff --git a/test/integration/push_int_test.go b/test/integration/push_int_test.go index a19dd5323b..6a6e03514f 100644 --- a/test/integration/push_int_test.go +++ b/test/integration/push_int_test.go @@ -259,12 +259,7 @@ func (suite *PushIntegrationTestSuite) TestPush_NoChanges() { ts.LoginAsPersistentUser() cp := ts.Spawn("push") cp.Expect("no local changes to push") - cp.ExpectExitCode(1) - ts.IgnoreLogErrors() - - if strings.Count(cp.Snapshot(), " x ") != 1 { - suite.Fail("Expected exactly ONE error message, got: ", cp.Snapshot()) - } + cp.ExpectExitCode(0) } func (suite *PushIntegrationTestSuite) TestPush_NameInUse() { diff --git a/test/integration/reset_int_test.go b/test/integration/reset_int_test.go index e7cc862ea9..9b3199ee60 100644 --- a/test/integration/reset_int_test.go +++ b/test/integration/reset_int_test.go @@ -51,7 +51,7 @@ func (suite *ResetIntegrationTestSuite) TestReset() { cp = ts.Spawn("reset") cp.Expect("You are already on the latest commit") - cp.ExpectNotExitCode(0) + cp.ExpectExitCode(0) cp = ts.Spawn("reset", "00000000-0000-0000-0000-000000000000") cp.Expect("The given commit ID does not exist") From 3b1998bf755a4113ef4f27f59dc746f777d85219 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 7 Nov 2024 15:17:21 -0800 Subject: [PATCH 363/440] Remove path inference and use length --- internal/graphql/graphql.go | 112 ++------ internal/graphql/graphql_json_test.go | 14 +- internal/graphql/graphql_multipart_test.go | 6 +- internal/graphql/graphql_test.go | 293 +++++---------------- 4 files changed, 101 insertions(+), 324 deletions(-) diff --git a/internal/graphql/graphql.go b/internal/graphql/graphql.go index 7085f0448c..cfd400b710 100644 --- a/internal/graphql/graphql.go +++ b/internal/graphql/graphql.go @@ -39,8 +39,6 @@ import ( "io" "mime/multipart" "net/http" - "strings" - "unicode" "github.com/pkg/errors" ) @@ -146,30 +144,7 @@ func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{} return gr.Errors[0] } - if req.dataPath != "" { - val, err := findValueByPath(intermediateResp, req.dataPath) - if err != nil { - // If the response is empty, return nil instead of an error - if len(intermediateResp) == 0 { - return nil - } - return err - } - data, err := json.Marshal(val) - if err != nil { - return errors.Wrap(err, "remarshaling response") - } - return json.Unmarshal(data, resp) - } - - data, err := json.Marshal(intermediateResp) - if err != nil { - return errors.Wrap(err, "remarshaling response") - } - if resp == nil { - return nil - } - return json.Unmarshal(data, resp) + return c.marshalResponse(intermediateResp, resp) } func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp interface{}) error { @@ -238,25 +213,32 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter return gr.Errors[0] } - if req.dataPath != "" { - val, err := findValueByPath(intermediateResp, req.dataPath) - if err != nil { - return errors.Wrap(err, "finding value by path") - } - data, err := json.Marshal(val) - if err != nil { - return errors.Wrap(err, "remarshaling response") + return c.marshalResponse(intermediateResp, resp) +} + +// marshalResponse handles marshaling the intermediate response and unmarshaling it into the final response object +func (c *Client) marshalResponse(intermediateResp map[string]interface{}, resp interface{}) error { + // If resp is nil, no need to process further + if resp == nil { + return nil + } + + // Handle single-value response case + if len(intermediateResp) == 1 { + for _, val := range intermediateResp { + data, err := json.Marshal(val) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, resp) } - return json.Unmarshal(data, resp) } + // Handle multi-value response case data, err := json.Marshal(intermediateResp) if err != nil { return errors.Wrap(err, "remarshaling response") } - if resp == nil { - return nil - } return json.Unmarshal(data, resp) } @@ -298,10 +280,9 @@ type graphResponse struct { // Request is a GraphQL request. type Request struct { - q string - vars map[string]interface{} - files []file - dataPath string + q string + vars map[string]interface{} + files []file // Header represent any request headers that will be set // when the request is made. @@ -311,48 +292,12 @@ type Request struct { // NewRequest makes a new Request with the specified string. func NewRequest(q string) *Request { req := &Request{ - q: q, - Header: make(map[string][]string), - dataPath: inferDataPath(q), + q: q, + Header: make(map[string][]string), } return req } -// inferDataPath attempts to extract the first field name after the operation type -// as the data path. Returns empty string if unable to infer. -// For example, given the query: -// -// query { user { name } } -// -// it will return "user". -// The dataPath is used to signal to the client where it should start unmarshaling the response. -func inferDataPath(query string) string { - query = strings.TrimSpace(query) - if query == "" { - return "" - } - - startIdx := strings.Index(query, "{") - if startIdx == -1 { - return "" - } - query = query[startIdx+1:] - query = strings.TrimSpace(query) - if query == "" || query == "}" { - return "" - } - - var result strings.Builder - for _, ch := range query { - if ch == '(' || ch == '{' || unicode.IsSpace(ch) || ch == ':' { - break - } - result.WriteRune(ch) - } - - return strings.TrimSpace(result.String()) -} - // Var sets a variable. func (req *Request) Var(key string, value interface{}) { if req.vars == nil { @@ -372,13 +317,6 @@ func (req *Request) File(fieldname, filename string, r io.Reader) { }) } -// DataPath sets the path to the data field in the response. -// This is useful if you want to unmarshal a nested object. -// If not set, it will use the automatically inferred path. -func (req *Request) DataPath(path string) { - req.dataPath = path -} - // file represents a file to upload. type file struct { Field string diff --git a/internal/graphql/graphql_json_test.go b/internal/graphql/graphql_json_test.go index 71f7f56d0e..3cd6dde068 100644 --- a/internal/graphql/graphql_json_test.go +++ b/internal/graphql/graphql_json_test.go @@ -23,7 +23,9 @@ func TestDoJSON(t *testing.T) { is.Equal(string(b), `{"query":"query {}","variables":null}`+"\n") io.WriteString(w, `{ "data": { - "something": "yes" + "query": { + "something": "yes" + } } }`) })) @@ -50,7 +52,7 @@ func TestQueryJSON(t *testing.T) { b, err := ioutil.ReadAll(r.Body) is.NoErr(err) is.Equal(string(b), `{"query":"query {}","variables":{"username":"matryer"}}`+"\n") - _, err = io.WriteString(w, `{"data":{"value":"some data"}}`) + _, err = io.WriteString(w, `{"data":{"query":{"value":"some data"}}}`) is.NoErr(err) })) defer srv.Close() @@ -84,7 +86,13 @@ func TestHeader(t *testing.T) { calls++ is.Equal(r.Header.Get("X-Custom-Header"), "123") - _, err := io.WriteString(w, `{"data":{"value":"some data"}}`) + _, err := io.WriteString(w, `{ + "data": { + "query": { + "value": "some data" + } + } + }`) is.NoErr(err) })) defer srv.Close() diff --git a/internal/graphql/graphql_multipart_test.go b/internal/graphql/graphql_multipart_test.go index 1756d95a39..7ae97b5a9f 100644 --- a/internal/graphql/graphql_multipart_test.go +++ b/internal/graphql/graphql_multipart_test.go @@ -45,7 +45,9 @@ func TestDoUseMultipartForm(t *testing.T) { is.Equal(query, `query {}`) io.WriteString(w, `{ "data": { - "something": "yes" + "query": { + "something": "yes" + } } }`) })) @@ -125,7 +127,7 @@ func TestQuery(t *testing.T) { query := r.FormValue("query") is.Equal(query, "query {}") is.Equal(r.FormValue("variables"), `{"username":"matryer"}`+"\n") - _, err := io.WriteString(w, `{"data":{"value":"some data"}}`) + _, err := io.WriteString(w, `{"data":{"query":{"value":"some data"}}}`) is.NoErr(err) })) defer srv.Close() diff --git a/internal/graphql/graphql_test.go b/internal/graphql/graphql_test.go index 0911274e0a..1a24c533f3 100644 --- a/internal/graphql/graphql_test.go +++ b/internal/graphql/graphql_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func TestClient_DataPath(t *testing.T) { +func TestClient_SingleField(t *testing.T) { mockResponse := map[string]interface{}{ "data": map[string]interface{}{ "users": map[string]interface{}{ @@ -28,33 +28,12 @@ func TestClient_DataPath(t *testing.T) { client := NewClient(server.URL) tests := []struct { - name string - query string - dataPath string - want interface{} + name string + query string + want interface{} }{ { - name: "no data path", - query: `{ - users { - items { - id - name - } - } - }`, - dataPath: "", - want: map[string]interface{}{ - "users": map[string]interface{}{ - "items": []interface{}{ - map[string]interface{}{"id": "1", "name": "Alice"}, - map[string]interface{}{"id": "2", "name": "Bob"}, - }, - }, - }, - }, - { - name: "with data path to users", + name: "basic query", query: `{ users { items { @@ -63,7 +42,6 @@ func TestClient_DataPath(t *testing.T) { } } }`, - dataPath: "users", want: map[string]interface{}{ "items": []interface{}{ map[string]interface{}{"id": "1", "name": "Alice"}, @@ -72,7 +50,7 @@ func TestClient_DataPath(t *testing.T) { }, }, { - name: "named query without data path", + name: "named query", query: `query GetUsers { users { items { @@ -81,27 +59,6 @@ func TestClient_DataPath(t *testing.T) { } } }`, - dataPath: "", - want: map[string]interface{}{ - "users": map[string]interface{}{ - "items": []interface{}{ - map[string]interface{}{"id": "1", "name": "Alice"}, - map[string]interface{}{"id": "2", "name": "Bob"}, - }, - }, - }, - }, - { - name: "named query with data path", - query: `query GetUsers { - users { - items { - id - name - } - } - }`, - dataPath: "users", want: map[string]interface{}{ "items": []interface{}{ map[string]interface{}{"id": "1", "name": "Alice"}, @@ -119,7 +76,6 @@ func TestClient_DataPath(t *testing.T) { } } }`, - dataPath: "users", want: map[string]interface{}{ "items": []interface{}{ map[string]interface{}{"id": "1", "name": "Alice"}, @@ -128,7 +84,7 @@ func TestClient_DataPath(t *testing.T) { }, }, { - name: "anonymous query with items data path", + name: "anonymous query", query: `{ users { items { @@ -137,42 +93,11 @@ func TestClient_DataPath(t *testing.T) { } } }`, - dataPath: "items", - want: []interface{}{ - map[string]interface{}{"id": "1", "name": "Alice"}, - map[string]interface{}{"id": "2", "name": "Bob"}, - }, - }, - { - name: "named query with items data path", - query: `query GetUsers { - users { - items { - id - name - } - } - }`, - dataPath: "items", - want: []interface{}{ - map[string]interface{}{"id": "1", "name": "Alice"}, - map[string]interface{}{"id": "2", "name": "Bob"}, - }, - }, - { - name: "named mutation with items data path", - query: `mutation CreateUser { - users { - items { - id - name - } - } - }`, - dataPath: "items", - want: []interface{}{ - map[string]interface{}{"id": "1", "name": "Alice"}, - map[string]interface{}{"id": "2", "name": "Bob"}, + want: map[string]interface{}{ + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, }, }, } @@ -180,7 +105,6 @@ func TestClient_DataPath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := NewRequest(tt.query) - req.DataPath(tt.dataPath) var resp interface{} err := client.Run(context.Background(), req, &resp) @@ -205,172 +129,77 @@ func TestClient_DataPath(t *testing.T) { } } -func Test_inferDataPath(t *testing.T) { +func TestClient_MultipleFields(t *testing.T) { + mockResponse := map[string]interface{}{ + "data": map[string]interface{}{ + "users": map[string]interface{}{"id": "1", "name": "Alice"}, + "items": []map[string]interface{}{ + {"id": "1", "name": "Alice"}, + {"id": "2", "name": "Bob"}, + }, + }, + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(mockResponse) + })) + defer server.Close() + + client := NewClient(server.URL) + tests := []struct { name string query string - want string + want interface{} }{ { - name: "simple query", + name: "basic query", query: `{ users { id name } - }`, - want: "users", - }, - { - name: "query with variables", - query: `query ($id: ID!) { - user(id: $id) { - name - email - } - }`, - want: "user", - }, - { - name: "query with operation name", - query: `query GetUser { - user { - name - } - }`, - want: "user", - }, - { - name: "query with multiple fields", - query: `{ - user { - name - } - posts { - title - } - }`, - want: "user", - }, - { - name: "empty query", - query: "", - want: "", - }, - { - name: "alternate empty query", - query: `query { - query {} - }`, - want: "query", - }, - { - name: "malformed query", - query: `query { - badly formatted* - }`, - want: "badly", - }, - { - name: "named mutation", - query: `mutation CreateUser($input: UserInput!) { - createUser(input: $input) { + items { id name } }`, - want: "createUser", - }, - { - name: "named query with aliases", - query: `query GetUserDetails { - userInfo: user { - profile { - firstName - lastName - } - settings { - theme - } - } - }`, - want: "userInfo", - }, - { - name: "complex nested query", - query: `query FetchDashboard { - dashboard { - widgets { - id - data { - chart { - points - } - } - } - } - }`, - want: "dashboard", - }, - { - name: "query with fragments", - query: `query GetUserWithPosts { - user { - ...UserFields - posts { - ...PostFields - } - } - } - - fragment UserFields on User { - id - name - } - - fragment PostFields on Post { - title - content - }`, - want: "user", - }, - { - name: "subscription", - query: `subscription OnUserUpdate { - userUpdated { - id - status - } - }`, - want: "userUpdated", + want: map[string]interface{}{ + "users": map[string]interface{}{ + "id": "1", + "name": "Alice", + }, + "items": []interface{}{ + map[string]interface{}{"id": "1", "name": "Alice"}, + map[string]interface{}{"id": "2", "name": "Bob"}, + }, + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := inferDataPath(tt.query); got != tt.want { - t.Errorf("inferDataPath() = %v, want %v", got, tt.want) + req := NewRequest(tt.query) + + var resp interface{} + err := client.Run(context.Background(), req, &resp) + if err != nil { + t.Fatalf("unexpected error: %v", err) } - }) - } -} -func TestNewRequest_DataPathInference(t *testing.T) { - query := `{ - users { - id - name - } - }` + got, err := json.Marshal(resp) + if err != nil { + t.Fatalf("failed to marshal response: %v", err) + } - req := NewRequest(query) - if req.dataPath != "users" { - t.Errorf("NewRequest() dataPath = %v, want %v", req.dataPath, "users") - } + want, err := json.Marshal(tt.want) + if err != nil { + t.Fatalf("failed to marshal expected result: %v", err) + } - // Test that manual override works - req.DataPath("override") - if req.dataPath != "override" { - t.Errorf("DataPath() override failed, got = %v, want %v", req.dataPath, "override") + if string(got) != string(want) { + t.Errorf("got %s, want %s", got, want) + } + }) } } From 4a9f51cf868b54a557fc948801d37d2555a56d88 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 7 Nov 2024 16:29:42 -0500 Subject: [PATCH 364/440] Merge pull request #3584 from ActiveState/mitchell/dx-3144 `state commit` should not poll for build result. --- internal/runners/commit/commit.go | 2 +- pkg/platform/model/buildplanner/build.go | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/runners/commit/commit.go b/internal/runners/commit/commit.go index 1ac478ce50..4b7b208ccc 100644 --- a/internal/runners/commit/commit.go +++ b/internal/runners/commit/commit.go @@ -160,7 +160,7 @@ func (c *Commit) Run() (rerr error) { } // Get old buildplan. - oldCommit, err := bp.FetchCommit(localCommitID, proj.Owner(), proj.Name(), nil) + oldCommit, err := bp.FetchCommitNoPoll(localCommitID, proj.Owner(), proj.Name(), nil) if err != nil { return errs.Wrap(err, "Failed to fetch old commit") } diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index feef9b1a28..867e056396 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -57,6 +57,14 @@ func (c *client) Run(req gqlclient.Request, resp interface{}) error { const fetchCommitCacheExpiry = time.Hour * 12 func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, target *string) (*Commit, error) { + return b.fetchCommit(commitID, owner, project, target, true) +} + +func (b *BuildPlanner) FetchCommitNoPoll(commitID strfmt.UUID, owner, project string, target *string) (*Commit, error) { + return b.fetchCommit(commitID, owner, project, target, false) +} + +func (b *BuildPlanner) fetchCommit(commitID strfmt.UUID, owner, project string, target *string, poll bool) (*Commit, error) { logging.Debug("FetchCommit, commitID: %s, owner: %s, project: %s", commitID, owner, project) resp := &response.ProjectCommitResponse{} @@ -92,7 +100,7 @@ func (b *BuildPlanner) FetchCommit(commitID strfmt.UUID, owner, project string, // The BuildPlanner will return a build plan with a status of // "planning" if the build plan is not ready yet. We need to // poll the BuildPlanner until the build is ready. - if resp.Project.Commit.Build.Status == raw.Planning { + if poll && resp.Project.Commit.Build.Status == raw.Planning { resp.Project.Commit.Build, err = b.pollBuildPlanned(commitID.String(), owner, project, target) if err != nil { return nil, errs.Wrap(err, "failed to poll build plan") From 04f13089d15ab8c09b950732e382612842cc5175 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 7 Nov 2024 16:46:50 -0800 Subject: [PATCH 365/440] Cleanup --- internal/graphql/graphql.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/internal/graphql/graphql.go b/internal/graphql/graphql.go index cfd400b710..780a2c6d46 100644 --- a/internal/graphql/graphql.go +++ b/internal/graphql/graphql.go @@ -216,14 +216,11 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter return c.marshalResponse(intermediateResp, resp) } -// marshalResponse handles marshaling the intermediate response and unmarshaling it into the final response object func (c *Client) marshalResponse(intermediateResp map[string]interface{}, resp interface{}) error { - // If resp is nil, no need to process further if resp == nil { return nil } - // Handle single-value response case if len(intermediateResp) == 1 { for _, val := range intermediateResp { data, err := json.Marshal(val) @@ -234,7 +231,6 @@ func (c *Client) marshalResponse(intermediateResp map[string]interface{}, resp i } } - // Handle multi-value response case data, err := json.Marshal(intermediateResp) if err != nil { return errors.Wrap(err, "remarshaling response") @@ -323,20 +319,3 @@ type file struct { Name string R io.Reader } - -func findValueByPath(data map[string]interface{}, path string) (interface{}, error) { - if val, ok := data[path]; ok { - return val, nil - } - - // Recursively search through nested maps - for _, val := range data { - if nestedMap, ok := val.(map[string]interface{}); ok { - if found, err := findValueByPath(nestedMap, path); err == nil { - return found, nil - } - } - } - - return nil, fmt.Errorf("path %q not found in response", path) -} From c1fc008b95f79efa8051ad157a3844d8d79366a3 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 8 Nov 2024 09:36:04 -0800 Subject: [PATCH 366/440] Add comment around absolute paths --- internal/archiver/archiver.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index ada7438fec..e8ece29276 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -12,7 +12,7 @@ import ( type FileMap struct { Source string - Target string + Target string // Note: Target paths should always be relative to the archive root, do not use absolute paths } func CreateTgz(archivePath string, workDir string, fileMaps []FileMap) error { @@ -30,6 +30,8 @@ func CreateTgz(archivePath string, workDir string, fileMaps []FileMap) error { for _, fileMap := range fileMaps { source := fileMap.Source if !filepath.IsAbs(source) { + // Ensure the source path is absolute, because otherwise it will use the global working directory which + // we're not interested in. source = filepath.Join(workDir, source) } file, err := os.Open(source) From be89fa82963c6f17eaafcc88ba1081e32e9065ad Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 8 Nov 2024 14:29:15 -0500 Subject: [PATCH 367/440] Declining the security prompt should cancel `state commit`. --- internal/runners/commit/commit.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/runners/commit/commit.go b/internal/runners/commit/commit.go index 08a7f2f12b..cdf503db46 100644 --- a/internal/runners/commit/commit.go +++ b/internal/runners/commit/commit.go @@ -122,20 +122,6 @@ func (c *Commit) Run() (rerr error) { return errs.Wrap(err, "Could not update project to reflect build script changes.") } - // Update local commit ID - if err := localcommit.Set(proj.Dir(), stagedCommit.CommitID.String()); err != nil { - return errs.Wrap(err, "Could not set local commit ID") - } - - // Update our local build expression to match the committed one. This allows our API a way to ensure forward compatibility. - newScript, err := bp.GetBuildScript(stagedCommit.CommitID.String()) - if err != nil { - return errs.Wrap(err, "Unable to get the remote build script") - } - if err := buildscript_runbit.Update(proj, newScript); err != nil { - return errs.Wrap(err, "Could not update local build script") - } - pg.Stop(locale.T("progress_success")) pg = nil @@ -169,6 +155,20 @@ func (c *Commit) Run() (rerr error) { return errs.Wrap(err, "Could not report CVEs") } + // Update local commit ID + if err := localcommit.Set(proj.Dir(), stagedCommit.CommitID.String()); err != nil { + return errs.Wrap(err, "Could not set local commit ID") + } + + // Update our local build expression to match the committed one. This allows our API a way to ensure forward compatibility. + newScript, err := bp.GetBuildScript(stagedCommit.CommitID.String()) + if err != nil { + return errs.Wrap(err, "Unable to get the remote build script") + } + if err := buildscript_runbit.Update(proj, newScript); err != nil { + return errs.Wrap(err, "Could not update local build script") + } + out.Notice("") // blank line out.Print(output.Prepare( locale.Tl( From 09dcb48e54cff87e3397cf2de7a1c48c680a4326 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 8 Nov 2024 15:22:43 -0500 Subject: [PATCH 368/440] Target & Verify action should append to Jira labels, not overwrite. --- scripts/internal/workflow-helpers/jira.go | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/internal/workflow-helpers/jira.go b/scripts/internal/workflow-helpers/jira.go index a250ea64a4..77699eaa7a 100644 --- a/scripts/internal/workflow-helpers/jira.go +++ b/scripts/internal/workflow-helpers/jira.go @@ -181,6 +181,7 @@ func UpdateJiraFixVersion(client *jira.Client, issue *jira.Issue, versionID stri Key: issue.Key, Fields: &jira.IssueFields{ FixVersions: []*jira.FixVersion{{ID: versionID}}, + Labels: issue.Fields.Labels, }, } From d092e16a2f8129ef621e88c8bcb211b0c42e6f9c Mon Sep 17 00:00:00 2001 From: mdrakos Date: Fri, 8 Nov 2024 13:37:51 -0800 Subject: [PATCH 369/440] Log retries --- internal/retryhttp/client.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/retryhttp/client.go b/internal/retryhttp/client.go index a26478119d..9e99c29169 100644 --- a/internal/retryhttp/client.go +++ b/internal/retryhttp/client.go @@ -12,6 +12,7 @@ import ( "time" "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/logging" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-retryablehttp" "github.com/thoas/go-funk" @@ -139,6 +140,7 @@ func normalizeResponse(res *http.Response, err error) (*http.Response, error) { } func normalizeRetryResponse(res *http.Response, err error, numTries int) (*http.Response, error) { + logging.Debug("Retry failed with error: %v, after %d tries", err, numTries) if err2, ok := err.(net.Error); ok && err2.Timeout() { return res, locale.WrapExternalError(&UserNetworkError{-1}, "err_user_network_timeout", "", locale.Tr("err_user_network_solution", constants.ForumsURL)) } From c20a2fb41fbb2687bc291ee3b3e54043ea1fb9a1 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 8 Nov 2024 14:59:52 -0800 Subject: [PATCH 370/440] Ensure we use the correct atTime when ingredient was cached at a newer timestamp --- internal/runners/commit/ingredientcall.go | 52 ++++++++++++++++--- .../model/buildplanner/buildscript.go | 2 +- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/internal/runners/commit/ingredientcall.go b/internal/runners/commit/ingredientcall.go index 654d340756..c9eb4a6b37 100644 --- a/internal/runners/commit/ingredientcall.go +++ b/internal/runners/commit/ingredientcall.go @@ -70,7 +70,11 @@ func (i *IngredientCall) Resolve() error { return errs.Wrap(err, "Could not hash ingredient call") } - resolvedIngredient, err := i.getCached(hash) + latest, err := model.FetchLatestRevisionTimeStamp(nil) + if err != nil { + return errs.Wrap(err, "Unable to determine latest revision time") + } + resolvedIngredient, err := i.getCached(hash, latest) if err != nil { return errs.Wrap(err, "Could not check if ingredient call is cached") } @@ -80,11 +84,11 @@ func (i *IngredientCall) Resolve() error { return errs.Wrap(err, "Could not create ingredient") } // Bump timestamp, because otherwise the new ingredient will be unusable - latest, err := model.FetchLatestRevisionTimeStamp(nil) - if err != nil { - return errs.Wrap(err, "Unable to determine latest revision time") - } i.script.SetAtTime(latest, true) + } else { + // Update atTime according to ingredient. This MAY not actually be an update if the resolvedIngredient has + // the same atTime as the stored buildscript. + i.script.SetAtTime(resolvedIngredient.AtTime, true) } // Add/update arguments on the buildscript ingredient function call @@ -138,9 +142,15 @@ func (i *IngredientCall) createIngredient(hash string, hashedFiles []*graph.Glob return nil, errs.Wrap(err, "Could not create publish request") } + latest, err := model.FetchLatestRevisionTimeStamp(nil) + if err != nil { + return nil, errs.Wrap(err, "Unable to determine latest revision time") + } + return &resolvedIngredientData{ VersionID: pr.IngredientVersionID, Revision: pr.Revision, + AtTime: latest, }, nil } @@ -290,11 +300,24 @@ func parseFeature(f string) (request.PublishVariableFeature, error) { type resolvedIngredientData struct { VersionID string Revision int + AtTime time.Time +} + +// MarshalJSON works around go's json.Marshal not allowing you to specify the format of time fields +func (r resolvedIngredientData) MarshalJSON() ([]byte, error) { + type Alias resolvedIngredientData + return json.Marshal(&struct { + Alias + AtTime string + }{ + Alias: Alias(r), + AtTime: r.AtTime.Format(time.RFC3339), + }) } // getCached checks against our local cache to see if we've already handled this file hash, and if no local cache // exists checks against the platform ingredient API. -func (i *IngredientCall) getCached(hash string) (*resolvedIngredientData, error) { +func (i *IngredientCall) getCached(hash string, latest time.Time) (*resolvedIngredientData, error) { cacheValue, err := i.prime.SvcModel().GetCache(fmt.Sprintf(cacheKeyFiles, hash)) if err != nil { return nil, errs.Wrap(err, "Could not get build script cache") @@ -319,6 +342,23 @@ func (i *IngredientCall) getCached(hash string) (*resolvedIngredientData, error) return &resolvedIngredientData{ VersionID: string(*ingredients[0].LatestVersion.IngredientVersionID), Revision: int(*ingredients[0].LatestVersion.Revision), + AtTime: ptr.From(i.script.AtTime(), latest), // Fall back to latest if script has no AtTime + }, nil + } + + // Check again if the hash exists at the latest timestamp, because even if the ingredient exists, it might not exist + // at the atTime of the buildscript. In which case the buildscript will need to bump its timestamp without touching + // the ingredient. + ingredients, err = model.SearchIngredientsStrict(i.ns.String(), hash, true, false, &latest, i.prime.Auth()) + if err != nil && !errors.As(err, ptr.To(&model.ErrSearch404{})) { + return nil, errs.Wrap(err, "Could not search ingredients") + } + if len(ingredients) > 0 { + // Ingredient already exists + return &resolvedIngredientData{ + VersionID: string(*ingredients[0].LatestVersion.IngredientVersionID), + Revision: int(*ingredients[0].LatestVersion.Revision), + AtTime: latest, }, nil } diff --git a/pkg/platform/model/buildplanner/buildscript.go b/pkg/platform/model/buildplanner/buildscript.go index c6fcc6a2ee..e733a58481 100644 --- a/pkg/platform/model/buildplanner/buildscript.go +++ b/pkg/platform/model/buildplanner/buildscript.go @@ -52,7 +52,7 @@ func (b *BuildPlanner) GetBuildScript(commitID string) (*buildscript.BuildScript } script := buildscript.New() - script.SetAtTime(time.Time(resp.Commit.AtTime), false) + script.SetAtTime(time.Time(resp.Commit.AtTime), true) if err := script.UnmarshalBuildExpression(resp.Commit.Expression); err != nil { return nil, errs.Wrap(err, "failed to parse build expression") } From 543089cde3aa102487cd0926b68dff44aa5267af Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Fri, 8 Nov 2024 15:00:09 -0800 Subject: [PATCH 371/440] Fix time comparison issues with buildscripts due to inconsistent time formatting --- internal/runbits/buildscript/buildscript_test.go | 2 +- pkg/buildscript/buildscript.go | 12 +++++++++++- pkg/buildscript/buildscript_test.go | 13 ++++++------- pkg/buildscript/marshal.go | 4 ++-- pkg/buildscript/merge_test.go | 4 ++-- pkg/buildscript/raw_test.go | 6 +++--- pkg/buildscript/unmarshal.go | 12 ++++++++---- pkg/buildscript/unmarshal_buildexpression.go | 2 +- scripts/to-buildscript/main.go | 3 +-- 9 files changed, 35 insertions(+), 23 deletions(-) diff --git a/internal/runbits/buildscript/buildscript_test.go b/internal/runbits/buildscript/buildscript_test.go index 24e47de8ef..60190339ee 100644 --- a/internal/runbits/buildscript/buildscript_test.go +++ b/internal/runbits/buildscript/buildscript_test.go @@ -12,7 +12,7 @@ import ( ) const testProject = "https://platform.activestate.com/org/project?branch=main&commitID=00000000-0000-0000-0000-000000000000" -const testTime = "2000-01-01T00:00:00.000Z" +const testTime = "2000-01-01T00:00:00Z" func checkoutInfo(project, time string) string { return "```\n" + diff --git a/pkg/buildscript/buildscript.go b/pkg/buildscript/buildscript.go index e3a8ed3d6b..9909f80527 100644 --- a/pkg/buildscript/buildscript.go +++ b/pkg/buildscript/buildscript.go @@ -7,6 +7,7 @@ import ( "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/sliceutils" "github.com/brunoga/deep" @@ -65,7 +66,15 @@ func (b *BuildScript) SetAtTime(t time.Time, override bool) { if b.atTime != nil && !override { return } - b.atTime = &t + // Ensure time is RFC3339 formatted, even though it's not actually stored at that format, it does still + // affect the specificity of the data stored and can ultimately lead to inconsistencies if not explicitly handled. + t2, err := time.Parse(time.RFC3339, t.Format(time.RFC3339)) + if err != nil { + // Pointless error check as this should never happen, but you know what they say about assumptions + logging.Error("Error parsing time: %s", err) + } + b.atTime = ptr.To(t2) + _ = b.atTime } func (b *BuildScript) Equals(other *BuildScript) (bool, error) { @@ -90,6 +99,7 @@ func (b *BuildScript) Equals(other *BuildScript) (bool, error) { if err != nil { return false, errs.Wrap(err, "Unable to marshal other buildscript") } + return string(myBytes) == string(otherBytes), nil } diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index 4bc18a2d06..2d0406833c 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -8,7 +8,6 @@ import ( "github.com/ActiveState/cli/internal/environment" "github.com/ActiveState/cli/internal/fileutils" - "github.com/go-openapi/strfmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -118,7 +117,7 @@ func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { // Ensure that legacy at_time is preserved in the buildscript. atTime := script.AtTime() require.NotNil(t, atTime) - require.Equal(t, initialTimeStamp, atTime.Format(strfmt.RFC3339Millis)) + require.Equal(t, initialTimeStamp, atTime.Format(time.RFC3339)) data, err = script.MarshalBuildExpression() require.NoError(t, err) @@ -128,12 +127,12 @@ func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { assert.NotContains(t, string(data), initialTimeStamp) // Update the time in the build script but don't override the existing time - updatedTime, err := time.Parse(strfmt.RFC3339Millis, updatedTimeStamp) + updatedTime, err := time.Parse(time.RFC3339, updatedTimeStamp) require.NoError(t, err) script.SetAtTime(updatedTime, false) // The updated time should be reflected in the build script - require.Equal(t, initialTimeStamp, script.AtTime().Format(strfmt.RFC3339Millis)) + require.Equal(t, initialTimeStamp, script.AtTime().Format(time.RFC3339)) data, err = script.Marshal() require.NoError(t, err) @@ -145,7 +144,7 @@ func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { // Now override the time in the build script script.SetAtTime(updatedTime, true) - require.Equal(t, updatedTimeStamp, script.AtTime().Format(strfmt.RFC3339Millis)) + require.Equal(t, updatedTimeStamp, script.AtTime().Format(time.RFC3339)) data, err = script.Marshal() require.NoError(t, err) @@ -165,7 +164,7 @@ func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { // TestExpressionToScript tests that creating a build script from a given Platform build expression // and at time produces the expected result. func TestExpressionToScript(t *testing.T) { - ts, err := time.Parse(strfmt.RFC3339Millis, testTime) + ts, err := time.Parse(time.RFC3339, testTime) require.NoError(t, err) script := New() @@ -193,7 +192,7 @@ func TestScriptToExpression(t *testing.T) { func TestOutdatedScript(t *testing.T) { _, err := Unmarshal([]byte( - `at_time = "2000-01-01T00:00:00.000Z" + `at_time = "2000-01-01T00:00:00Z" main = runtime `)) assert.Error(t, err) diff --git a/pkg/buildscript/marshal.go b/pkg/buildscript/marshal.go index 6eefd259f8..4a006a5f55 100644 --- a/pkg/buildscript/marshal.go +++ b/pkg/buildscript/marshal.go @@ -5,8 +5,8 @@ import ( "fmt" "strconv" "strings" + "time" - "github.com/go-openapi/strfmt" "github.com/thoas/go-funk" ) @@ -31,7 +31,7 @@ func (b *BuildScript) Marshal() ([]byte, error) { buf.WriteString("```\n") buf.WriteString("Project: " + b.project + "\n") if b.atTime != nil { - buf.WriteString("Time: " + b.atTime.Format(strfmt.RFC3339Millis) + "\n") + buf.WriteString("Time: " + b.atTime.Format(time.RFC3339) + "\n") } buf.WriteString("```\n\n") diff --git a/pkg/buildscript/merge_test.go b/pkg/buildscript/merge_test.go index 260bb50867..5dddb14fd8 100644 --- a/pkg/buildscript/merge_test.go +++ b/pkg/buildscript/merge_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/require" ) -const mergeATime = "2000-01-01T00:00:00.000Z" -const mergeBTime = "2000-01-02T00:00:00.000Z" +const mergeATime = "2000-01-01T00:00:00Z" +const mergeBTime = "2000-01-02T00:00:00Z" func TestMergeAdd(t *testing.T) { scriptA, err := Unmarshal([]byte( diff --git a/pkg/buildscript/raw_test.go b/pkg/buildscript/raw_test.go index 306a71dbbe..a19a7e3f55 100644 --- a/pkg/buildscript/raw_test.go +++ b/pkg/buildscript/raw_test.go @@ -11,7 +11,7 @@ import ( ) const testProject = "https://platform.activestate.com/org/project?branch=main&commitID=00000000-0000-0000-0000-000000000000" -const testTime = "2000-01-01T00:00:00.000Z" +const testTime = "2000-01-01T00:00:00Z" func checkoutInfoString(project, time string) string { return "```\n" + @@ -43,7 +43,7 @@ main = runtime `)) require.NoError(t, err) - atTimeStrfmt, err := strfmt.ParseDateTime("2000-01-01T00:00:00.000Z") + atTimeStrfmt, err := strfmt.ParseDateTime("2000-01-01T00:00:00Z") require.NoError(t, err) atTime := time.Time(atTimeStrfmt) @@ -121,7 +121,7 @@ main = merge( `)) require.NoError(t, err) - atTimeStrfmt, err := strfmt.ParseDateTime("2000-01-01T00:00:00.000Z") + atTimeStrfmt, err := strfmt.ParseDateTime("2000-01-01T00:00:00Z") require.NoError(t, err) atTime := time.Time(atTimeStrfmt) diff --git a/pkg/buildscript/unmarshal.go b/pkg/buildscript/unmarshal.go index 9602aef76c..5382e135e5 100644 --- a/pkg/buildscript/unmarshal.go +++ b/pkg/buildscript/unmarshal.go @@ -12,7 +12,6 @@ import ( "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/rtutils/ptr" ) const atTimeKey = "at_time" @@ -72,11 +71,16 @@ func Unmarshal(data []byte) (*BuildScript, error) { project = info.Project - atTimeStr, err := strfmt.ParseDateTime(info.Time) + atTimeVal, err := time.Parse(time.RFC3339, info.Time) if err != nil { - return nil, errs.Wrap(err, "Invalid timestamp: %s", info.Time) + // Older buildscripts used microsecond specificity + atDateTime, err := strfmt.ParseDateTime(info.Time) + if err != nil { + return nil, errs.Wrap(err, "Invalid timestamp: %s", info.Time) + } + atTimeVal = time.Time(atDateTime) } - atTime = ptr.To(time.Time(atTimeStr)) + atTime = &atTimeVal } return &BuildScript{raw, project, atTime}, nil diff --git a/pkg/buildscript/unmarshal_buildexpression.go b/pkg/buildscript/unmarshal_buildexpression.go index 8561deb229..0323da3514 100644 --- a/pkg/buildscript/unmarshal_buildexpression.go +++ b/pkg/buildscript/unmarshal_buildexpression.go @@ -78,7 +78,7 @@ func (b *BuildScript) UnmarshalBuildExpression(data []byte) error { atTimeNode.Str = nil atTimeNode.Ident = ptr.To("TIME") // Preserve the original at_time found in the solve node. - b.atTime = ptr.To(time.Time(atTime)) + b.SetAtTime(time.Time(atTime), true) } else if atTimeNode.Ident != nil && *atTimeNode.Ident == "at_time" { atTimeNode.Ident = ptr.To("TIME") } diff --git a/scripts/to-buildscript/main.go b/scripts/to-buildscript/main.go index 84a2e4599f..6800ddd78a 100644 --- a/scripts/to-buildscript/main.go +++ b/scripts/to-buildscript/main.go @@ -10,7 +10,6 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/pkg/buildscript" - "github.com/go-openapi/strfmt" ) func main() { @@ -45,7 +44,7 @@ func main() { project := "https://platform.activestate.com/org/project?branch=main&commitID=00000000-0000-0000-0000-000000000000" var atTime *time.Time if len(os.Args) == (2 + argOffset) { - t, err := time.Parse(strfmt.RFC3339Millis, os.Args[1+argOffset]) + t, err := time.Parse(time.RFC3339, os.Args[1+argOffset]) if err != nil { panic(errs.JoinMessage(err)) } From bceec933ccd74e3295d072dac5bf39eb5969934c Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 12 Nov 2024 10:57:16 -0800 Subject: [PATCH 372/440] Fix test --- test/integration/commit_int_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/commit_int_test.go b/test/integration/commit_int_test.go index 9dbe1c22b4..3904e72793 100644 --- a/test/integration/commit_int_test.go +++ b/test/integration/commit_int_test.go @@ -75,11 +75,11 @@ func (suite *CommitIntegrationTestSuite) TestCommitAtTimeChange() { suite.Require().NoError(err) // verify validity // Update top-level at_time variable. - dateTime := "2023-06-21T12:34:56.789Z" + dateTime := "2023-06-21T12:34:56Z" buildScriptFile := filepath.Join(proj.Dir(), constants.BuildScriptFileName) contents, err := fileutils.ReadFile(buildScriptFile) suite.Require().NoError(err) - contents = bytes.Replace(contents, []byte("2023-06-22T21:56:10.504Z"), []byte(dateTime), 1) + contents = bytes.Replace(contents, []byte("2023-06-22T21:56:10Z"), []byte(dateTime), 1) suite.Require().NoError(fileutils.WriteFile(buildScriptFile, contents)) suite.Require().Contains(string(fileutils.ReadFileUnsafe(filepath.Join(proj.Dir(), constants.BuildScriptFileName))), dateTime) From e1a5947706ebbc2e00614b0631ba90f5aa481b63 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 12 Nov 2024 14:42:30 -0500 Subject: [PATCH 373/440] Added dashboard build progress details URL. --- internal/constants/constants.go | 3 +++ internal/locale/locales/en-us.yaml | 4 +++- internal/runbits/runtime/progress/progress.go | 1 + internal/runbits/runtime/runtime.go | 19 +++++++++++++++++++ pkg/runtime/events/events.go | 1 + pkg/runtime/options.go | 4 ++++ pkg/runtime/setup.go | 2 ++ test/integration/runtime_int_test.go | 3 ++- 8 files changed, 35 insertions(+), 2 deletions(-) diff --git a/internal/constants/constants.go b/internal/constants/constants.go index b8f054e96e..f64eb5650d 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -493,3 +493,6 @@ const OverrideShellEnvVarName = "ACTIVESTATE_CLI_SHELL_OVERRIDE" // IgnoreEnvEnvVarName is the environment variable to set for skipping specific environment variables during runtime setup. const IgnoreEnvEnvVarName = "ACTIVESTATE_CLI_IGNORE_ENV" + +// ProgressUrlPathName is the trailing path for a project's build progress. +const BuildProgressUrlPathName = "distributions" diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 9a4deb3a3a..7030506100 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1127,7 +1127,9 @@ progress_solve_preruntime: progress_pkg_nolang: other: "• Searching for [ACTIONABLE]{{.V0}}[/RESET] across all languages in the ActiveState Catalog" progress_build_log: - other: "Build Log [ACTIONABLE]{{.V0}}[/RESET]" + other: "Build Log: [ACTIONABLE]{{.V0}}[/RESET]" +progress_build_url: + other: "Detailed Progress: [ACTIONABLE]{{.V0}}[/RESET]" progress_completed: other: "[SUCCESS]✔ All dependencies have been installed and verified[/RESET]" progress_failed: diff --git a/internal/runbits/runtime/progress/progress.go b/internal/runbits/runtime/progress/progress.go index a071d5774e..2d902f0a42 100644 --- a/internal/runbits/runtime/progress/progress.go +++ b/internal/runbits/runtime/progress/progress.go @@ -147,6 +147,7 @@ func (p *ProgressDigester) Handle(ev events.Event) error { // already progressbars being displayed which won't play nice with newly printed output. if v.RequiresBuild { p.out.Notice(locale.Tr("progress_build_log", v.LogFilePath)) + p.out.Notice(locale.Tr("progress_build_url", v.ProgressUrl)) } p.recipeID = v.RecipeID diff --git a/internal/runbits/runtime/runtime.go b/internal/runbits/runtime/runtime.go index f4b72ba59b..5cd987d89d 100644 --- a/internal/runbits/runtime/runtime.go +++ b/internal/runbits/runtime/runtime.go @@ -1,6 +1,7 @@ package runtime_runbit import ( + "net/url" "os" anaConsts "github.com/ActiveState/cli/internal/analytics/constants" @@ -252,6 +253,24 @@ func Update( if opts.Archive != nil { rtOpts = append(rtOpts, runtime.WithArchive(opts.Archive.Dir, opts.Archive.PlatformID, checkout.ArtifactExt)) } + if commit.BuildPlan().IsBuildInProgress() { + // Build progress URL is of the form + // https://///distributions?branch=&commitID= + host := constants.DefaultAPIHost + if hostOverride := os.Getenv(constants.APIHostEnvVarName); hostOverride != "" { + host = hostOverride + } + path, err := url.JoinPath(proj.Owner(), proj.Name(), constants.BuildProgressUrlPathName) + if err != nil { + return nil, errs.Wrap(err, "Could not construct progress url path") + } + u := &url.URL{Scheme: "https", Host: host, Path: path} + q := u.Query() + q.Set("branch", proj.BranchName()) + q.Set("commitID", commitID.String()) + u.RawQuery = q.Encode() + rtOpts = append(rtOpts, runtime.WithBuildProgressUrl(u.String())) + } if err := rt.Update(buildPlan, rtHash, rtOpts...); err != nil { return nil, locale.WrapError(err, "err_packages_update_runtime_install") diff --git a/pkg/runtime/events/events.go b/pkg/runtime/events/events.go index 9585c182b3..1f9ffebfcf 100644 --- a/pkg/runtime/events/events.go +++ b/pkg/runtime/events/events.go @@ -37,6 +37,7 @@ type Start struct { RequiresBuild bool LogFilePath string + ProgressUrl string ArtifactsToBuild buildplan.ArtifactIDMap ArtifactsToDownload buildplan.ArtifactIDMap diff --git a/pkg/runtime/options.go b/pkg/runtime/options.go index dd150afaef..19492243cf 100644 --- a/pkg/runtime/options.go +++ b/pkg/runtime/options.go @@ -13,6 +13,10 @@ func WithBuildlogFilePath(path string) SetOpt { return func(opts *Opts) { opts.BuildlogFilePath = path } } +func WithBuildProgressUrl(url string) SetOpt { + return func(opts *Opts) { opts.BuildProgressUrl = url } +} + func WithPreferredLibcVersion(version string) SetOpt { return func(opts *Opts) { opts.PreferredLibcVersion = version } } diff --git a/pkg/runtime/setup.go b/pkg/runtime/setup.go index b59229af79..d35ca25805 100644 --- a/pkg/runtime/setup.go +++ b/pkg/runtime/setup.go @@ -46,6 +46,7 @@ type Opts struct { PreferredLibcVersion string EventHandlers []events.HandlerFunc BuildlogFilePath string + BuildProgressUrl string FromArchive *fromArchive @@ -200,6 +201,7 @@ func (s *setup) RunAndWait() (rerr error) { RecipeID: s.buildplan.LegacyRecipeID(), RequiresBuild: s.buildplan.IsBuildInProgress() && len(s.toDownload) > 0, LogFilePath: s.opts.BuildlogFilePath, + ProgressUrl: s.opts.BuildProgressUrl, ArtifactsToBuild: s.toBuild, ArtifactsToDownload: s.toDownload, ArtifactsToUnpack: s.toUnpack, diff --git a/test/integration/runtime_int_test.go b/test/integration/runtime_int_test.go index 55b440c199..cb22734c18 100644 --- a/test/integration/runtime_int_test.go +++ b/test/integration/runtime_int_test.go @@ -169,7 +169,8 @@ func (suite *RuntimeIntegrationTestSuite) TestBuildInProgress() { ts.PrepareEmptyProject() cp = ts.Spawn("install", "private/"+e2e.PersistentUsername+"/hello-world", "--ts", "now") - cp.Expect("Build Log") + cp.Expect("Build Log:") + cp.Expect("Detailed Progress:") cp.Expect("Building") cp.Expect("All dependencies have been installed and verified", e2e.RuntimeBuildSourcingTimeoutOpt) cp.Expect("Added: private/" + e2e.PersistentUsername + "/hello-world") From 5d70309aac8a11263d5ce1b879820b15fb9c3c4a Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 12 Nov 2024 14:23:13 -0800 Subject: [PATCH 374/440] Fix unit test --- pkg/buildscript/buildscript_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/buildscript/buildscript_test.go b/pkg/buildscript/buildscript_test.go index 2d0406833c..a5fed3d3b9 100644 --- a/pkg/buildscript/buildscript_test.go +++ b/pkg/buildscript/buildscript_test.go @@ -101,15 +101,14 @@ func TestRoundTripFromBuildExpressionWithLegacyAtTime(t *testing.T) { wd, err := environment.GetRootPath() require.NoError(t, err) - initialTimeStamp := "2024-10-15T16:37:06.260Z" - updatedTimeStamp := "2024-10-15T16:37:06.261Z" + initialTimeStamp := "2024-10-15T16:37:06Z" + updatedTimeStamp := "2024-10-15T16:37:07Z" data, err := fileutils.ReadFile(filepath.Join(wd, "pkg", "buildscript", "testdata", "buildexpression-roundtrip-legacy.json")) require.NoError(t, err) // The initial build expression does not use the new at_time format assert.NotContains(t, string(data), "$at_time") - assert.Contains(t, string(data), initialTimeStamp) script := New() require.NoError(t, script.UnmarshalBuildExpression(data)) From 41062d2fcc93db60e65381a6492fb98f3978f0d8 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 12 Nov 2024 14:24:57 -0800 Subject: [PATCH 375/440] Fix TestRealWorld --- internal/runbits/buildscript/buildscript_test.go | 4 ++-- internal/runbits/buildscript/testdata/buildscript1.as | 2 +- internal/runbits/buildscript/testdata/buildscript2.as | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/runbits/buildscript/buildscript_test.go b/internal/runbits/buildscript/buildscript_test.go index 60190339ee..8f201e2814 100644 --- a/internal/runbits/buildscript/buildscript_test.go +++ b/internal/runbits/buildscript/buildscript_test.go @@ -85,10 +85,10 @@ func TestRealWorld(t *testing.T) { "```\n"+ "<<<<<<< local\n"+ "Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=d908a758-6a81-40d4-b0eb-87069cd7f07d\n"+ - "Time: 2024-05-10T00:00:13.138Z\n"+ + "Time: 2024-05-10T00:00:13Z\n"+ "=======\n"+ "Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=f3263ee4-ac4c-41ee-b778-2585333f49f7\n"+ - "Time: 2023-08-01T16:20:11.985Z\n"+ + "Time: 2023-08-01T16:20:11Z\n"+ ">>>>>>> remote\n"+ "```\n"+` runtime = state_tool_artifacts_v1( diff --git a/internal/runbits/buildscript/testdata/buildscript1.as b/internal/runbits/buildscript/testdata/buildscript1.as index 1d33f4566e..9ed3f42adc 100644 --- a/internal/runbits/buildscript/testdata/buildscript1.as +++ b/internal/runbits/buildscript/testdata/buildscript1.as @@ -1,6 +1,6 @@ ``` Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=d908a758-6a81-40d4-b0eb-87069cd7f07d -Time: 2024-05-10T00:00:13.138Z +Time: 2024-05-10T00:00:13Z ``` runtime = state_tool_artifacts_v1( diff --git a/internal/runbits/buildscript/testdata/buildscript2.as b/internal/runbits/buildscript/testdata/buildscript2.as index 2fe6441d1d..8e1458918c 100644 --- a/internal/runbits/buildscript/testdata/buildscript2.as +++ b/internal/runbits/buildscript/testdata/buildscript2.as @@ -1,6 +1,6 @@ ``` Project: https://platform.activestate.com/ActiveState-CLI/Merge?branch=main&commitID=f3263ee4-ac4c-41ee-b778-2585333f49f7 -Time: 2023-08-01T16:20:11.985000Z +Time: 2023-08-01T16:20:11Z ``` runtime = state_tool_artifacts_v1( From 2bf0030e9269e605296b89833d9832e2bf0de1cc Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 13 Nov 2024 11:43:14 -0800 Subject: [PATCH 376/440] Debug test --- internal/testhelpers/e2e/env.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/testhelpers/e2e/env.go b/internal/testhelpers/e2e/env.go index 38cb7cb14b..7270368d84 100644 --- a/internal/testhelpers/e2e/env.go +++ b/internal/testhelpers/e2e/env.go @@ -40,6 +40,7 @@ func sandboxedTestEnvironment(t *testing.T, dirs *Dirs, updatePath bool, extraEn constants.ServiceSockDir + "=" + dirs.SockRoot, constants.HomeEnvVarName + "=" + dirs.HomeDir, systemHomeEnvVarName + "=" + dirs.HomeDir, + constants.DebugServiceRequestsEnvVarName + "=true", "NO_COLOR=true", "CI=true", }...) From 6a243fa7a4b59819b4c550120d278f7684935867 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 13 Nov 2024 15:11:45 -0500 Subject: [PATCH 377/440] Add a bit of lag to the JWT renewal time. It was possible to land within the interval between actual expired time and expected expired time. --- pkg/platform/authentication/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/platform/authentication/auth.go b/pkg/platform/authentication/auth.go index 2b0c3a7936..6bd4b9c2cc 100644 --- a/pkg/platform/authentication/auth.go +++ b/pkg/platform/authentication/auth.go @@ -317,7 +317,7 @@ func (s *Auth) UpdateSession(accessToken *mono_models.JWT) { s.bearerToken = accessToken.Token clientAuth := httptransport.BearerToken(s.bearerToken) s.clientAuth = &clientAuth - s.lastRenewal = ptr.To(time.Now()) + s.lastRenewal = ptr.To(time.Now().Add(-1 * time.Minute)) // the renewal happened up to a few seconds ago, not now persist = s } From 8ef7da780754695e985177a3fb1c7e538940eea4 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 13 Nov 2024 12:58:51 -0800 Subject: [PATCH 378/440] Apply same unmarshalling to gqlclient --- internal/gqlclient/gqlclient.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/internal/gqlclient/gqlclient.go b/internal/gqlclient/gqlclient.go index 273a6e7b5a..a9589bc51f 100644 --- a/internal/gqlclient/gqlclient.go +++ b/internal/gqlclient/gqlclient.go @@ -312,8 +312,9 @@ func (c *Client) runWithFiles(ctx context.Context, gqlReq RequestWithFiles, resp logging.Debug("gqlclient: response: %s", responseData) } + intermediateResp := make(map[string]interface{}) gr := &graphResponse{ - Data: response, + Data: &intermediateResp, } req = req.WithContext(ctx) c.Log(fmt.Sprintf(">> Raw Request: %s\n", req.URL.String())) @@ -371,5 +372,20 @@ func (c *Client) runWithFiles(ctx context.Context, gqlReq RequestWithFiles, resp if err := json.Unmarshal(resp, &gr); err != nil { return errors.Wrap(err, "decoding response") } - return nil + + if len(intermediateResp) == 1 { + for _, val := range intermediateResp { + data, err := json.Marshal(val) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, response) + } + } + + data, err := json.Marshal(intermediateResp) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, response) } From 9f1af8ed3e65f195dc5c4bfbe6dc7e85e3b2403e Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 13 Nov 2024 13:11:13 -0800 Subject: [PATCH 379/440] Remove debug code --- internal/testhelpers/e2e/env.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/testhelpers/e2e/env.go b/internal/testhelpers/e2e/env.go index 7270368d84..38cb7cb14b 100644 --- a/internal/testhelpers/e2e/env.go +++ b/internal/testhelpers/e2e/env.go @@ -40,7 +40,6 @@ func sandboxedTestEnvironment(t *testing.T, dirs *Dirs, updatePath bool, extraEn constants.ServiceSockDir + "=" + dirs.SockRoot, constants.HomeEnvVarName + "=" + dirs.HomeDir, systemHomeEnvVarName + "=" + dirs.HomeDir, - constants.DebugServiceRequestsEnvVarName + "=true", "NO_COLOR=true", "CI=true", }...) From 2ecd5c7ace6628e7f2e94d3266fbf32793ef3ce7 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 14 Nov 2024 10:02:10 -0800 Subject: [PATCH 380/440] Add comment --- internal/gqlclient/gqlclient.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/gqlclient/gqlclient.go b/internal/gqlclient/gqlclient.go index a9589bc51f..d9e4c1ae60 100644 --- a/internal/gqlclient/gqlclient.go +++ b/internal/gqlclient/gqlclient.go @@ -373,6 +373,8 @@ func (c *Client) runWithFiles(ctx context.Context, gqlReq RequestWithFiles, resp return errors.Wrap(err, "decoding response") } + // If the response is a single object, meaning we only have a single query in the request, we can unmarshal the + // response directly to the response type. Otherwise, we need to marshal the response as we normally would. if len(intermediateResp) == 1 { for _, val := range intermediateResp { data, err := json.Marshal(val) From e862c58b097d0a50b2ed9950492c351378aa7e4f Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 14 Nov 2024 15:28:58 -0500 Subject: [PATCH 381/440] Reduce JWT lifetime instead. --- pkg/platform/authentication/auth.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/platform/authentication/auth.go b/pkg/platform/authentication/auth.go index 6bd4b9c2cc..8e1a1f8db3 100644 --- a/pkg/platform/authentication/auth.go +++ b/pkg/platform/authentication/auth.go @@ -37,8 +37,8 @@ type ErrTokenRequired struct{ *locale.LocalizedError } var errNotYetGranted = locale.NewInputError("err_auth_device_noauth") // jwtLifetime is the lifetime of the JWT. This is defined by the API, but the API doesn't communicate this. -// We drop a minute from this to avoid race conditions with the API. -const jwtLifetime = (1 * time.Hour) - (1 * time.Minute) +// We drop 10 minutes from this to be on the safe side and avoid race conditions with the API. +const jwtLifetime = (1 * time.Hour) - (10 * time.Minute) // Auth is the base structure used to record the authenticated state type Auth struct { @@ -317,7 +317,7 @@ func (s *Auth) UpdateSession(accessToken *mono_models.JWT) { s.bearerToken = accessToken.Token clientAuth := httptransport.BearerToken(s.bearerToken) s.clientAuth = &clientAuth - s.lastRenewal = ptr.To(time.Now().Add(-1 * time.Minute)) // the renewal happened up to a few seconds ago, not now + s.lastRenewal = ptr.To(time.Now()) persist = s } From b5451fce73adb7ce5e620c7ea47f39e885a0d9c2 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 15 Nov 2024 15:44:35 -0500 Subject: [PATCH 382/440] Initial support for enabling prompts to react to `--force` flag. --- cmd/state/internal/cmdtree/clean.go | 14 ++++++-- cmd/state/internal/cmdtree/packages.go | 9 +++++ cmd/state/internal/cmdtree/reset.go | 4 ++- cmd/state/internal/cmdtree/revert.go | 4 ++- cmd/state/internal/cmdtree/update.go | 8 +++-- cmd/state/internal/cmdtree/use.go | 4 ++- internal/locale/locales/en-us.yaml | 6 ++++ internal/prompt/prompt.go | 49 ++++++++++++++++++++----- internal/runbits/auth/keypair.go | 20 ++++++----- internal/runbits/auth/login.go | 2 +- internal/runbits/cves/cves.go | 29 +++++++-------- internal/runners/clean/cache.go | 50 ++++++++++++++------------ internal/runners/clean/config.go | 26 ++++++++------ internal/runners/clean/uninstall.go | 32 +++++++++-------- internal/runners/projects/delete.go | 10 ++++-- internal/runners/projects/edit.go | 13 +++---- internal/runners/projects/move.go | 13 +++---- internal/runners/publish/publish.go | 12 +++---- internal/runners/push/push.go | 20 +++++++---- internal/runners/reset/reset.go | 10 +++--- internal/runners/revert/revert.go | 10 +++--- internal/runners/scripts/edit.go | 4 +-- internal/runners/update/lock.go | 18 +++++----- internal/runners/update/unlock.go | 22 ++++++------ internal/runners/upgrade/upgrade.go | 6 +++- internal/runners/use/reset.go | 13 ++++--- test/integration/projects_int_test.go | 2 +- 27 files changed, 257 insertions(+), 153 deletions(-) diff --git a/cmd/state/internal/cmdtree/clean.go b/cmd/state/internal/cmdtree/clean.go index 758f6bb4a1..37ca6cfbe8 100644 --- a/cmd/state/internal/cmdtree/clean.go +++ b/cmd/state/internal/cmdtree/clean.go @@ -59,7 +59,12 @@ func newCleanUninstallCommand(prime *primer.Values, globals *globalOptions) *cap return err } - params.NonInteractive = globals.NonInteractive // distinct from --force + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } + if params.Force { + prime.Prompt().EnableForce() + } return runner.Run(¶ms) }, ) @@ -83,7 +88,9 @@ func newCleanCacheCommand(prime *primer.Values, globals *globalOptions) *captain }, }, func(ccmd *captain.Command, _ []string) error { - params.Force = globals.NonInteractive + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } return runner.Run(¶ms) }, ) @@ -107,6 +114,9 @@ func newCleanConfigCommand(prime *primer.Values) *captain.Command { }, []*captain.Argument{}, func(ccmd *captain.Command, _ []string) error { + if params.Force { + prime.Prompt().EnableForce() + } return runner.Run(¶ms) }, ) diff --git a/cmd/state/internal/cmdtree/packages.go b/cmd/state/internal/cmdtree/packages.go index 636dc02dc3..820b189927 100644 --- a/cmd/state/internal/cmdtree/packages.go +++ b/cmd/state/internal/cmdtree/packages.go @@ -54,6 +54,7 @@ func newInstallCommand(prime *primer.Values) *captain.Command { runner := install.New(prime, model.NamespacePackage) params := install.Params{} + force := false var packagesRaw string cmd := captain.NewCommand( @@ -67,6 +68,11 @@ func newInstallCommand(prime *primer.Values) *captain.Command { Description: locale.T("package_flag_ts_description"), Value: ¶ms.Timestamp, }, + { + Name: "force", + Description: locale.Tl("package_flag_force_description", "Ignore security policy preventing packages with CVEs from being installed (not recommended)"), + Value: &force, + }, }, []*captain.Argument{ { @@ -82,6 +88,9 @@ func newInstallCommand(prime *primer.Values) *captain.Command { return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") } } + if force { + prime.Prompt().EnableForce() + } return runner.Run(params) }, ) diff --git a/cmd/state/internal/cmdtree/reset.go b/cmd/state/internal/cmdtree/reset.go index 44f550cc09..2a86485ab7 100644 --- a/cmd/state/internal/cmdtree/reset.go +++ b/cmd/state/internal/cmdtree/reset.go @@ -25,7 +25,9 @@ func newResetCommand(prime *primer.Values, globals *globalOptions) *captain.Comm }, }, func(ccmd *captain.Command, args []string) error { - params.Force = globals.NonInteractive + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } return runner.Run(params) }, ).SetGroup(VCSGroup).SetSupportsStructuredOutput() diff --git a/cmd/state/internal/cmdtree/revert.go b/cmd/state/internal/cmdtree/revert.go index 8d01f8b34c..6150d53bf1 100644 --- a/cmd/state/internal/cmdtree/revert.go +++ b/cmd/state/internal/cmdtree/revert.go @@ -32,7 +32,9 @@ func newRevertCommand(prime *primer.Values, globals *globalOptions) *captain.Com }, }, func(ccmd *captain.Command, args []string) error { - params.Force = globals.NonInteractive + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } return runner.Run(params) }, ).SetGroup(VCSGroup).SetSupportsStructuredOutput() diff --git a/cmd/state/internal/cmdtree/update.go b/cmd/state/internal/cmdtree/update.go index a027d6dbe6..45f20cab21 100644 --- a/cmd/state/internal/cmdtree/update.go +++ b/cmd/state/internal/cmdtree/update.go @@ -52,7 +52,9 @@ func newUpdateLockCommand(prime *primer.Values, globals *globalOptions) *captain }, []*captain.Argument{}, func(cmd *captain.Command, args []string) error { - params.NonInteractive = globals.NonInteractive + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } return runner.Run(¶ms) }, ) @@ -73,7 +75,9 @@ func newUpdateUnlockCommand(prime *primer.Values, globals *globalOptions) *capta []*captain.Flag{}, []*captain.Argument{}, func(cmd *captain.Command, args []string) error { - params.NonInteractive = globals.NonInteractive + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } return runner.Run(¶ms) }, ) diff --git a/cmd/state/internal/cmdtree/use.go b/cmd/state/internal/cmdtree/use.go index 2971339b51..5c4bd8d9bf 100644 --- a/cmd/state/internal/cmdtree/use.go +++ b/cmd/state/internal/cmdtree/use.go @@ -44,7 +44,9 @@ func newUseResetCommand(prime *primer.Values, globals *globalOptions) *captain.C []*captain.Flag{}, []*captain.Argument{}, func(_ *captain.Command, _ []string) error { - params.Force = globals.NonInteractive + if globals.NonInteractive { + prime.Prompt().SetInteractive(false) + } return use.NewReset(prime).Run(params) }, ) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 7030506100..add273ea81 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1165,6 +1165,12 @@ warning_vulnerable_short: prompt_continue_pkg_operation: other: | Do you want to continue installing this dependency despite its vulnerabilities? +prompt_continue_force: + other: "Continuing because the '[ACTIONABLE]--force[/RESET]' flag is set." +prompt_continue_non_interactive: + other: "Continuting because State Tool is running in non-interactive mode." +prompt_abort_non_interactive: + other: "Aborting because State Tool is running in non-interactive mode. To bypass you can use the '[ACTIONABLE]--force[/RESET]' flag." unstable_command_warning: other: | This command is still in beta. If you want to opt-in to unstable features, run the following command: diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index be6def602c..4735947305 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -9,23 +9,37 @@ import ( "gopkg.in/AlecAivazis/survey.v1/terminal" "github.com/ActiveState/cli/internal/analytics/constants" + "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" ) +type ConfirmKind int + +const ( + // A confirm prompt was completed by the user. + User ConfirmKind = iota + // A confirm prompt was completed in non-interactive mode. + NonInteractive + // A confirm prompt was completed via --force. + Force +) + type EventDispatcher interface { EventWithLabel(category, action string, label string, dim ...*dimensions.Values) } -// Prompter is the interface used to run our prompt from, useful for mocking in tests +// Prompter is the interface used to run our prompt from type Prompter interface { Input(title, message string, defaultResponse *string, flags ...ValidatorFlag) (string, error) InputAndValidate(title, message string, defaultResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) Select(title, message string, choices []string, defaultResponse *string) (string, error) - Confirm(title, message string, defaultChoice *bool) (bool, error) + Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, ConfirmKind, error) InputSecret(title, message string, flags ...ValidatorFlag) (string, error) IsInteractive() bool + SetInteractive(bool) + EnableForce() } // ValidatorFunc is a function pass to the Prompter to perform validation @@ -39,11 +53,12 @@ type Prompt struct { out output.Outputer analytics EventDispatcher isInteractive bool + isForced bool } // New creates a new prompter func New(isInteractive bool, an EventDispatcher) Prompter { - return &Prompt{output.Get(), an, isInteractive} + return &Prompt{output.Get(), an, isInteractive, false} } // IsInteractive checks if the prompts can be interactive or should just return default values @@ -51,6 +66,16 @@ func (p *Prompt) IsInteractive() bool { return p.isInteractive } +func (p *Prompt) SetInteractive(interactive bool) { + p.isInteractive = interactive +} + +// EnableForce forces confirm prompts to return the force value (which is often different from the +// non-interactive value). +func (p *Prompt) EnableForce() { + p.isForced = true +} + // ValidatorFlag represents flags for prompt functions to change their behavior on. type ValidatorFlag int @@ -158,13 +183,21 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * } // Confirm prompts user for yes or no response. -func (p *Prompt) Confirm(title, message string, defaultChoice *bool) (bool, error) { +func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, ConfirmKind, error) { + if p.isForced { + if forcedChoice == nil { + return false, Force, errs.New("No force option given for force-enabled prompt") + } + logging.Debug("Prompt %s confirmed with choice %v in force mode", title, forcedChoice) + return *forcedChoice, Force, nil + } + if !p.isInteractive { if defaultChoice != nil { logging.Debug("Prompt %s confirmed with default choice %v in non-interactive mode", title, defaultChoice) - return *defaultChoice, nil + return *defaultChoice, NonInteractive, nil } - return false, interactiveInputError(message) + return false, NonInteractive, interactiveInputError(message) } if title != "" { p.out.Notice(output.Emphasize(title)) @@ -186,11 +219,11 @@ func (p *Prompt) Confirm(title, message string, defaultChoice *bool) (bool, erro if err == terminal.InterruptErr { p.analytics.EventWithLabel(constants.CatPrompt, title, "interrupt") } - return false, locale.NewInputError(err.Error()) + return false, User, locale.NewInputError(err.Error()) } p.analytics.EventWithLabel(constants.CatPrompt, title, translateConfirm(resp)) - return resp, nil + return resp, User, nil } func translateConfirm(confirm bool) string { diff --git a/internal/runbits/auth/keypair.go b/internal/runbits/auth/keypair.go index 611060376e..c92ee7974b 100644 --- a/internal/runbits/auth/keypair.go +++ b/internal/runbits/auth/keypair.go @@ -9,6 +9,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" secretsapi "github.com/ActiveState/cli/pkg/platform/api/secrets" secretsModels "github.com/ActiveState/cli/pkg/platform/api/secrets/secrets_models" "github.com/ActiveState/cli/pkg/platform/authentication" @@ -112,19 +113,20 @@ func promptForPreviousPassphrase(prompt prompt.Prompter) (string, error) { return passphrase, nil } -func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prompt prompt.Prompter, auth *authentication.Auth) error { - var err error +func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prmpt prompt.Prompter, auth *authentication.Auth) error { // previous passphrase is invalid, inform user and ask if they want to generate a new keypair out.Notice(locale.T("auth_generate_new_keypair_message")) - yes, err := prompt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), new(bool)) + yes, kind, err := prmpt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil) if err != nil { - return err + return errs.Wrap(err, "Unable to confirm") } - if yes { - _, err = keypairs.GenerateAndSaveEncodedKeypair(cfg, secretsapi.Get(auth), passphrase, constants.DefaultRSABitLength, auth) - // TODO delete user's secrets - } else { - err = locale.NewError("auth_err_unrecoverable_keypair") + if !yes { + if kind == prompt.NonInteractive { + return locale.NewInputError("prompt_abort_non_interactive") + } + return locale.NewInputError("auth_err_unrecoverable_keypair") } + _, err = keypairs.GenerateAndSaveEncodedKeypair(cfg, secretsapi.Get(auth), passphrase, constants.DefaultRSABitLength, auth) + // TODO delete user's secrets return err } diff --git a/internal/runbits/auth/login.go b/internal/runbits/auth/login.go index 1159613f9a..785a759f02 100644 --- a/internal/runbits/auth/login.go +++ b/internal/runbits/auth/login.go @@ -266,7 +266,7 @@ func authenticateWithBrowser(out output.Outputer, auth *authentication.Auth, pro var cont bool var err error for !cont { - cont, err = prompt.Confirm(locale.Tl("continue", "Continue?"), locale.T("auth_press_enter"), ptr.To(false)) + cont, _, err = prompt.Confirm(locale.Tl("continue", "Continue?"), locale.T("auth_press_enter"), ptr.To(false), nil) if err != nil { return errs.Wrap(err, "Prompt failed") } diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index b9b598482b..0ecb893c8f 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -12,6 +12,7 @@ import ( configMediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/buildplan" vulnModel "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/model" @@ -104,20 +105,24 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } c.summarizeCVEs(vulnerabilities) - cont, err := c.promptForSecurity() + + confirm, kind, err := c.prime.Prompt().Confirm("", locale.Tr("prompt_continue_pkg_operation"), ptr.To(false), ptr.To(true)) if err != nil { - return errs.Wrap(err, "Failed to prompt for security") + return errs.Wrap(err, "Unable to confirm") } - - if !cont { - if !c.prime.Prompt().IsInteractive() { + if !confirm { + if kind == prompt.NonInteractive { return errs.AddTips( - locale.NewInputError("err_pkgop_security_prompt", "Operation aborted due to security prompt"), + locale.NewInputError("prompt_abort_non_interactive"), locale.Tl("more_info_prompt", "To disable security prompting run: [ACTIONABLE]state config set security.prompt.enabled false[/RESET]"), ) } - return locale.NewInputError("err_pkgop_security_prompt", "Operation aborted due to security prompt") + return locale.NewInputError("err_pkgop_security_prompt", "Operation aborted by user") + } + if kind == prompt.Force { + c.prime.Output().Notice(locale.T("prompt_continue_force")) } + c.prime.Output().Notice("") // Empty line return nil } @@ -225,16 +230,6 @@ func (c *CveReport) summarizeCVEs(vulnerabilities model.VulnerableIngredientsByL out.Print("") } -func (c *CveReport) promptForSecurity() (bool, error) { - confirm, err := c.prime.Prompt().Confirm("", locale.Tr("prompt_continue_pkg_operation"), ptr.To(false)) - if err != nil { - return false, locale.WrapError(err, "err_pkgop_confirm", "Need a confirmation.") - } - c.prime.Output().Notice("") // Empty line - - return confirm, nil -} - func changedRequirements(oldBuildPlan *buildplan.BuildPlan, newBuildPlan *buildplan.BuildPlan) []string { var names []string var oldRequirements buildplan.Requirements diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index e4f48eb14a..e8ca73adf4 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -11,6 +11,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/svcctl" "github.com/ActiveState/cli/pkg/projectfile" "github.com/ActiveState/cli/pkg/runtime_helpers" @@ -20,13 +21,12 @@ type Cache struct { prime primeable output output.Outputer config configurable - confirm promptable + confirm prompt.Prompter path string ipComm svcctl.IPCommunicator } type CacheParams struct { - Force bool Project string } @@ -34,7 +34,7 @@ func NewCache(prime primeable) *Cache { return newCache(prime, prime.Output(), prime.Config(), prime.Prompt(), prime.IPComm()) } -func newCache(prime primeable, output output.Outputer, cfg configurable, confirm promptable, ipComm svcctl.IPCommunicator) *Cache { +func newCache(prime primeable, output output.Outputer, cfg configurable, confirm prompt.Prompter, ipComm svcctl.IPCommunicator) *Cache { return &Cache{ prime: prime, output: output, @@ -58,7 +58,7 @@ func (c *Cache) Run(params *CacheParams) error { } for _, projectPath := range paths { - err := c.removeProjectCache(projectPath, params.Project, params.Force) + err := c.removeProjectCache(projectPath, params.Project) if err != nil { return err } @@ -66,18 +66,20 @@ func (c *Cache) Run(params *CacheParams) error { return nil } - return c.removeCache(c.path, params.Force) + return c.removeCache(c.path) } -func (c *Cache) removeCache(path string, force bool) error { - if !force { - ok, err := c.confirm.Confirm(locale.T("confirm"), locale.T("clean_cache_confirm"), new(bool)) - if err != nil { - return err - } - if !ok { - return locale.NewInputError("err_clean_cache_not_confirmed", "Cleaning of cache aborted by user") - } +func (c *Cache) removeCache(path string) error { + defaultValue := !c.prime.Prompt().IsInteractive() + ok, kind, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.T("clean_cache_confirm"), &defaultValue, nil) + if err != nil { + return errs.Wrap(err, "Could not confirm") + } + if !ok { + return locale.NewInputError("err_clean_cache_not_confirmed", "Cleaning of cache aborted by user") + } + if kind == prompt.NonInteractive { + c.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } inUse, err := c.checkPathInUse(path) @@ -97,15 +99,17 @@ func (c *Cache) removeCache(path string, force bool) error { return nil } -func (c *Cache) removeProjectCache(projectDir, namespace string, force bool) error { - if !force { - ok, err := c.confirm.Confirm(locale.T("confirm"), locale.Tr("clean_cache_artifact_confirm", namespace), new(bool)) - if err != nil { - return err - } - if !ok { - return locale.NewInputError("err_clean_cache_artifact_not_confirmed", "Cleaning of cached runtime aborted by user") - } +func (c *Cache) removeProjectCache(projectDir, namespace string) error { + defaultValue := !c.prime.Prompt().IsInteractive() + ok, kind, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.Tr("clean_cache_artifact_confirm", namespace), &defaultValue, nil) + if err != nil { + return errs.Wrap(err, "Could not confirm") + } + if !ok { + return locale.NewInputError("err_clean_cache_artifact_not_confirmed", "Cleaning of cached runtime aborted by user") + } + if kind == prompt.NonInteractive { + c.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } inUse, err := c.checkPathInUse(projectDir) diff --git a/internal/runners/clean/config.go b/internal/runners/clean/config.go index 333b2b0375..6524b86371 100644 --- a/internal/runners/clean/config.go +++ b/internal/runners/clean/config.go @@ -8,6 +8,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/svcctl" "github.com/ActiveState/cli/pkg/project" @@ -25,7 +26,7 @@ type configurable interface { type Config struct { output output.Outputer - confirm promptable + confirm prompt.Prompter cfg configurable ipComm svcctl.IPCommunicator } @@ -38,7 +39,7 @@ func NewConfig(prime primeable) *Config { return newConfig(prime.Output(), prime.Prompt(), prime.Config(), prime.IPComm()) } -func newConfig(out output.Outputer, confirm promptable, cfg configurable, ipComm svcctl.IPCommunicator) *Config { +func newConfig(out output.Outputer, confirm prompt.Prompter, cfg configurable, ipComm svcctl.IPCommunicator) *Config { return &Config{ output: out, confirm: confirm, @@ -52,14 +53,19 @@ func (c *Config) Run(params *ConfigParams) error { return locale.NewError("err_clean_cache_activated") } - if !params.Force { - ok, err := c.confirm.Confirm(locale.T("confirm"), locale.T("clean_config_confirm"), ptr.To(true)) - if err != nil { - return locale.WrapError(err, "err_clean_config_confirm", "Could not confirm clean config choice") - } - if !ok { - return locale.NewInputError("err_clean_config_aborted", "Cleaning of config aborted by user") - } + defaultChoice := !c.confirm.IsInteractive() + ok, kind, err := c.confirm.Confirm(locale.T("confirm"), locale.T("clean_config_confirm"), &defaultChoice, ptr.To(true)) + if err != nil { + return errs.Wrap(err, "Unable to confirm") + } + if !ok { + return locale.NewInputError("err_clean_config_aborted", "Cleaning of config aborted by user") + } + switch kind { + case prompt.NonInteractive: + c.output.Notice(locale.T("prompt_continue_non_interactive")) + case prompt.Force: + c.output.Notice(locale.T("prompt_continue_force")) } if err := stopServices(c.cfg, c.output, c.ipComm, params.Force); err != nil { diff --git a/internal/runners/clean/uninstall.go b/internal/runners/clean/uninstall.go index 8b6071e1a7..1b888697c1 100644 --- a/internal/runners/clean/uninstall.go +++ b/internal/runners/clean/uninstall.go @@ -13,27 +13,23 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/svcctl" ) -type promptable interface { - Confirm(title, message string, defaultChoice *bool) (bool, error) - Select(title, message string, choices []string, defaultChoice *string) (string, error) -} - type Uninstall struct { out output.Outputer - prompt promptable + prompt prompt.Prompter cfg *config.Instance ipComm svcctl.IPCommunicator an analytics.Dispatcher } type UninstallParams struct { - Force bool - NonInteractive bool - All bool - Prompt bool + Force bool + All bool + Prompt bool } type primeable interface { @@ -49,7 +45,7 @@ func NewUninstall(prime primeable) (*Uninstall, error) { return newUninstall(prime.Output(), prime.Prompt(), prime.Config(), prime.IPComm(), prime.Analytics()) } -func newUninstall(out output.Outputer, prompt promptable, cfg *config.Instance, ipComm svcctl.IPCommunicator, an analytics.Dispatcher) (*Uninstall, error) { +func newUninstall(out output.Outputer, prompt prompt.Prompter, cfg *config.Instance, ipComm svcctl.IPCommunicator, an analytics.Dispatcher) (*Uninstall, error) { return &Uninstall{ out: out, prompt: prompt, @@ -81,19 +77,25 @@ func (u *Uninstall) Run(params *UninstallParams) error { if selection == choices[1] { params.All = true } - } else if !params.Force { - defaultChoice := params.NonInteractive + } else { + defaultChoice := !u.prompt.IsInteractive() confirmMessage := locale.T("uninstall_confirm") if params.All { confirmMessage = locale.T("uninstall_confirm_all") } - ok, err := u.prompt.Confirm(locale.T("confirm"), confirmMessage, &defaultChoice) + ok, kind, err := u.prompt.Confirm(locale.T("confirm"), confirmMessage, &defaultChoice, ptr.To(true)) if err != nil { - return locale.WrapError(err, "err_uninstall_confirm", "Could not confirm uninstall choice") + return errs.Wrap(err, "Unable to confirm") } if !ok { return locale.NewInputError("err_uninstall_aborted", "Uninstall aborted by user") } + switch kind { + case prompt.NonInteractive: + u.out.Notice(locale.T("prompt_continue_non_interactive")) + case prompt.Force: + u.out.Notice(locale.T("prompt_continue_force")) + } } if err := stopServices(u.cfg, u.out, u.ipComm, params.Force); err != nil { diff --git a/internal/runners/projects/delete.go b/internal/runners/projects/delete.go index 2cae69944a..20912814eb 100644 --- a/internal/runners/projects/delete.go +++ b/internal/runners/projects/delete.go @@ -1,6 +1,7 @@ package projects import ( + "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" @@ -36,14 +37,17 @@ func (d *Delete) Run(params *DeleteParams) error { return locale.NewInputError("err_projects_delete_authenticated", "You need to be authenticated to delete a project.") } - defaultChoice := !d.out.Config().Interactive - confirm, err := d.prompt.Confirm("", locale.Tl("project_delete_confim", "Are you sure you want to delete the project {{.V0}}?", params.Project.String()), &defaultChoice) + defaultChoice := !d.prompt.IsInteractive() + confirm, kind, err := d.prompt.Confirm("", locale.Tl("project_delete_confim", "Are you sure you want to delete the project {{.V0}}?", params.Project.String()), &defaultChoice, nil) if err != nil { - return locale.WrapError(err, "err_project_delete_confirm", "Could not confirm delete choice") + return errs.Wrap(err, "Unable to confirm") } if !confirm { return locale.NewInputError("err_project_delete_aborted", "Delete aborted by user") } + if kind == prompt.NonInteractive { + d.out.Notice(locale.T("prompt_continue_non_interactive")) + } err = model.DeleteProject(params.Project.Owner, params.Project.Project, d.auth) if err != nil { diff --git a/internal/runners/projects/edit.go b/internal/runners/projects/edit.go index 1bf55860de..8919008058 100644 --- a/internal/runners/projects/edit.go +++ b/internal/runners/projects/edit.go @@ -90,15 +90,16 @@ func (e *Edit) Run(params *EditParams) error { editMsg += locale.Tl("edit_prompt_confirm", "Continue?") - defaultChoice := !e.out.Config().Interactive - edit, err := e.prompt.Confirm("", editMsg, &defaultChoice) + defaultChoice := !e.prompt.IsInteractive() + edit, kind, err := e.prompt.Confirm("", editMsg, &defaultChoice, nil) if err != nil { - return locale.WrapError(err, "err_edit_prompt", "Could not prompt for edit confirmation") + return errs.Wrap(err, "Unable to confirm") } - if !edit { - e.out.Print(locale.Tl("edit_cancelled", "Project edit cancelled")) - return nil + return locale.NewInputError("edit_cancelled", "Project edit cancelled") + } + if kind == prompt.NonInteractive { + e.out.Notice(locale.T("prompt_continue_non_interactive")) } if err = model.EditProject(params.Namespace.Owner, params.Namespace.Project, editable, e.auth); err != nil { diff --git a/internal/runners/projects/move.go b/internal/runners/projects/move.go index c3d4b40c31..fa9e7ed2a1 100644 --- a/internal/runners/projects/move.go +++ b/internal/runners/projects/move.go @@ -41,15 +41,16 @@ func (m *Move) Run(params *MoveParams) error { return locale.NewInputError("err_project_move_auth", "In order to move your project you need to be authenticated. Please run '[ACTIONABLE]state auth[/RESET]' to authenticate.") } - defaultChoice := !m.out.Config().Interactive - move, err := m.prompt.Confirm("", locale.Tr("move_prompt", params.Namespace.String(), params.NewOwner, params.Namespace.Project), &defaultChoice) + defaultChoice := !m.prompt.IsInteractive() + move, kind, err := m.prompt.Confirm("", locale.Tr("move_prompt", params.Namespace.String(), params.NewOwner, params.Namespace.Project), &defaultChoice, nil) if err != nil { - return locale.WrapError(err, "err_move_prompt", "Could not prompt for move confirmation") + return errs.Wrap(err, "Unable to confirm") } - if !move { - m.out.Print(locale.Tl("move_cancelled", "Project move aborted by user")) - return nil + return locale.NewInputError("move_cancelled", "Project move aborted by user") + } + if kind == prompt.NonInteractive { + m.out.Notice(locale.T("prompt_continue_non_interactive")) } if err = model.MoveProject(params.Namespace.Owner, params.Namespace.Project, params.NewOwner, m.auth); err != nil { diff --git a/internal/runners/publish/publish.go b/internal/runners/publish/publish.go index 6964d1c98b..2b8af90997 100644 --- a/internal/runners/publish/publish.go +++ b/internal/runners/publish/publish.go @@ -218,22 +218,22 @@ func (r *Runner) Run(params *Params) error { return errs.Wrap(err, "Could not marshal publish variables") } - cont, err := r.prompt.Confirm( + cont, kind, err := r.prompt.Confirm( "", locale.Tl("uploadingredient_confirm", `Prepared the following ingredient: {{.V0}} Do you want to publish this ingredient? -`, string(b)), - ptr.To(true), - ) +`, string(b)), ptr.To(true), nil) if err != nil { return errs.Wrap(err, "Confirmation failed") } if !cont { - r.out.Print(locale.Tl("uploadingredient_cancel", "Publish cancelled")) - return nil + return locale.NewInputError("uploadingredient_cancel", "Publish cancelled") + } + if kind == prompt.NonInteractive { + r.out.Notice(locale.T("prompt_continue_non_interactive")) } r.out.Notice(locale.Tl("uploadingredient_uploading", "Publishing ingredient...")) diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index ca1c774161..10f935dfe8 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -128,10 +128,15 @@ func (r *Push) Run(params PushParams) (rerr error) { // Ask to create a copy if the user does not have org permissions if intend&pushFromNoPermission > 0 && !params.Namespace.IsValid() { - var err error - createCopy, err := r.prompt.Confirm("", locale.T("push_prompt_not_authorized"), ptr.To(true)) - if err != nil || !createCopy { - return err + createCopy, kind, err := r.prompt.Confirm("", locale.T("push_prompt_not_authorized"), ptr.To(true), nil) + if err != nil { + return errs.Wrap(err, "Unable to confirm") + } + if !createCopy { + return nil + } + if kind == prompt.NonInteractive { + r.out.Notice(locale.T("prompt_continue_non_interactive")) } } @@ -167,16 +172,19 @@ func (r *Push) Run(params PushParams) (rerr error) { // If the user didn't necessarily intend to create the project we should ask them for confirmation if intend&intendCreateProject == 0 { - createProject, err := r.prompt.Confirm( + createProject, kind, err := r.prompt.Confirm( locale.Tl("create_project", "Create Project"), locale.Tl("push_confirm_create_project", "You are about to create the project [NOTICE]{{.V0}}[/RESET]. Continue?", targetNamespace.String()), - ptr.To(true)) + ptr.To(true), nil) if err != nil { return errs.Wrap(err, "Confirmation failed") } if !createProject { return rationalize.ErrActionAborted } + if kind == prompt.NonInteractive { + r.out.Notice(locale.T("prompt_continue_non_interactive")) + } } r.out.Notice(locale.Tl("push_creating_project", "Creating project [NOTICE]{{.V1}}[/RESET] under [NOTICE]{{.V0}}[/RESET] on the ActiveState Platform", targetNamespace.Owner, targetNamespace.Project)) diff --git a/internal/runners/reset/reset.go b/internal/runners/reset/reset.go index 45e042e725..944842bf67 100644 --- a/internal/runners/reset/reset.go +++ b/internal/runners/reset/reset.go @@ -26,7 +26,6 @@ import ( const local = "LOCAL" type Params struct { - Force bool CommitID string } @@ -117,14 +116,17 @@ func (r *Reset) Run(params *Params) error { } r.out.Notice(locale.Tl("reset_commit", "Your project will be reset to [ACTIONABLE]{{.V0}}[/RESET]\n", commitID.String())) if commitID != localCommitID { - defaultChoice := params.Force || !r.out.Config().Interactive - confirm, err := r.prompt.Confirm("", locale.Tl("reset_confim", "Resetting is destructive. You will lose any changes that were not pushed. Are you sure you want to do this?"), &defaultChoice) + defaultChoice := !r.prime.Prompt().IsInteractive() + confirm, kind, err := r.prime.Prompt().Confirm("", locale.Tl("reset_confim", "Resetting is destructive. You will lose any changes that were not pushed. Are you sure you want to do this?"), &defaultChoice, nil) if err != nil { - return locale.WrapError(err, "err_reset_confirm", "Could not confirm reset choice") + return errs.Wrap(err, "Unable to confirm") } if !confirm { return locale.NewInputError("err_reset_aborted", "Reset aborted by user") } + if kind == prompt.NonInteractive { + r.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) + } } err = localcommit.Set(r.project.Dir(), commitID.String()) diff --git a/internal/runners/revert/revert.go b/internal/runners/revert/revert.go index 60da9a2441..335bab3af4 100644 --- a/internal/runners/revert/revert.go +++ b/internal/runners/revert/revert.go @@ -40,7 +40,6 @@ type Revert struct { type Params struct { CommitID string To bool - Force bool } type primeable interface { @@ -139,14 +138,17 @@ func (r *Revert) Run(params *Params) (rerr error) { } } - defaultChoice := params.Force || !r.out.Config().Interactive - revert, err := r.prompt.Confirm("", locale.Tl("revert_confirm", "Continue?"), &defaultChoice) + defaultChoice := !r.prime.Prompt().IsInteractive() + revert, kind, err := r.prime.Prompt().Confirm("", locale.Tl("revert_confirm", "Continue?"), &defaultChoice, nil) if err != nil { - return locale.WrapError(err, "err_revert_confirm", "Could not confirm revert choice") + return errs.Wrap(err, "Unable to confirm") } if !revert { return locale.NewInputError("err_revert_aborted", "Revert aborted by user") } + if kind == prompt.NonInteractive { + r.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) + } revertCommit, err := revertFunc(revertParams, bp) if err != nil { diff --git a/internal/runners/scripts/edit.go b/internal/runners/scripts/edit.go index fa34bc2ce6..3f3e04be19 100644 --- a/internal/runners/scripts/edit.go +++ b/internal/runners/scripts/edit.go @@ -15,6 +15,7 @@ import ( "github.com/ActiveState/cli/internal/osutils" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/scriptfile" "github.com/ActiveState/cli/pkg/project" @@ -236,8 +237,7 @@ func startInteractive(sw *scriptWatcher, scriptName string, output output.Output go sw.run(scriptName, output, cfg, proj) for { - doneConfirmDefault := true - doneEditing, err := prompt.Confirm("", locale.T("prompt_done_editing"), &doneConfirmDefault) + doneEditing, _, err := prompt.Confirm("", locale.T("prompt_done_editing"), ptr.To(true), nil) if err != nil { return errs.Wrap(err, "Prompter returned with failure.") } diff --git a/internal/runners/update/lock.go b/internal/runners/update/lock.go index 4be61c0b72..707d01b8a7 100644 --- a/internal/runners/update/lock.go +++ b/internal/runners/update/lock.go @@ -41,8 +41,7 @@ func (stv *StateToolChannelVersion) Type() string { } type LockParams struct { - Channel StateToolChannelVersion - NonInteractive bool + Channel StateToolChannelVersion } type Lock struct { @@ -72,8 +71,8 @@ func (l *Lock) Run(params *LockParams) error { l.out.Notice(locale.Tl("locking_version", "Locking State Tool version for current project.")) - if l.project.IsLocked() && !params.NonInteractive { - if err := confirmLock(l.prompt); err != nil { + if l.project.IsLocked() { + if err := confirmLock(l.prompt, l.out); err != nil { return locale.WrapError(err, "err_update_lock_confirm", "Could not confirm whether to lock update.") } } @@ -128,17 +127,20 @@ func (l *Lock) Run(params *LockParams) error { return nil } -func confirmLock(prom prompt.Prompter) error { +func confirmLock(prom prompt.Prompter, out output.Outputer) error { + defaultChoice := !prom.IsInteractive() msg := locale.T("confirm_update_locked_version_prompt") - confirmed, err := prom.Confirm(locale.T("confirm"), msg, new(bool)) + confirmed, kind, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) if err != nil { - return err + return errs.Wrap(err, "Unable to confirm") } - if !confirmed { return locale.NewInputError("err_update_lock_noconfirm", "Cancelling by your request.") } + if kind == prompt.NonInteractive { + out.Notice(locale.T("prompt_continue_non_interactive")) + } return nil } diff --git a/internal/runners/update/unlock.go b/internal/runners/update/unlock.go index 91021941d7..8eae02f42d 100644 --- a/internal/runners/update/unlock.go +++ b/internal/runners/update/unlock.go @@ -2,6 +2,7 @@ package update import ( "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/output" @@ -13,7 +14,6 @@ import ( ) type UnlockParams struct { - NonInteractive bool } type Unlock struct { @@ -44,15 +44,13 @@ func (u *Unlock) Run(params *UnlockParams) error { u.out.Notice(locale.Tl("unlocking_version", "Unlocking State Tool version for current project.")) - if !params.NonInteractive { - err := confirmUnlock(u.prompt) - if err != nil { - return locale.WrapError(err, "err_update_unlock_confirm", "Unlock cancelled by user.") - } + err := confirmUnlock(u.prompt, u.out) + if err != nil { + return locale.WrapError(err, "err_update_unlock_confirm", "Unlock cancelled by user.") } // Invalidate the installer version lock. - err := u.cfg.Set(updater.CfgKeyInstallVersion, "") + err = u.cfg.Set(updater.CfgKeyInstallVersion, "") if err != nil { multilog.Error("Failed to invalidate installer version lock on `state update lock` invocation: %v", err) } @@ -71,18 +69,20 @@ func (u *Unlock) Run(params *UnlockParams) error { return nil } -func confirmUnlock(prom prompt.Prompter) error { +func confirmUnlock(prom prompt.Prompter, out output.Outputer) error { msg := locale.T("confirm_update_unlocked_version_prompt") defaultChoice := !prom.IsInteractive() - confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice) + confirmed, kind, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) if err != nil { - return err + return errs.Wrap(err, "Unable to confirm") } - if !confirmed { return locale.NewInputError("err_update_lock_noconfirm", "Cancelling by your request.") } + if kind == prompt.NonInteractive { + out.Notice(locale.T("prompt_continue_non_interactive")) + } return nil } diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index eb82b499c2..22d8caeb2f 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -12,6 +12,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/rationalize" @@ -283,13 +284,16 @@ func (u *Upgrade) renderUserFacing(changes []structuredChange, expand bool) erro out.Print(tbl.Render()) out.Notice(" ") // Empty line (prompts use Notice) - confirm, err := u.prime.Prompt().Confirm("", locale.Tr("upgrade_confirm"), ptr.To(true)) + confirm, kind, err := u.prime.Prompt().Confirm("", locale.Tr("upgrade_confirm"), ptr.To(true), nil) if err != nil { return errs.Wrap(err, "confirmation failed") } if !confirm { return ErrAbort } + if kind == prompt.NonInteractive { + u.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) + } return nil } diff --git a/internal/runners/use/reset.go b/internal/runners/use/reset.go index c8a570423e..85e5b0283e 100644 --- a/internal/runners/use/reset.go +++ b/internal/runners/use/reset.go @@ -4,6 +4,7 @@ import ( "runtime" "github.com/ActiveState/cli/internal/config" + "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/globaldefault" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" @@ -20,7 +21,6 @@ type Reset struct { } type ResetParams struct { - Force bool } func NewReset(prime primeable) *Reset { @@ -39,15 +39,18 @@ func (u *Reset) Run(params *ResetParams) error { return locale.NewInputError(locale.T("use_reset_notice_not_reset")) } - defaultChoice := params.Force || !u.out.Config().Interactive - ok, err := u.prompt.Confirm(locale.T("confirm"), - locale.Tl("use_reset_confirm", "You are about to stop using your project runtime. Continue?"), &defaultChoice) + defaultChoice := !u.prompt.IsInteractive() + ok, kind, err := u.prompt.Confirm(locale.T("confirm"), + locale.Tl("use_reset_confirm", "You are about to stop using your project runtime. Continue?"), &defaultChoice, nil) if err != nil { - return err + return errs.Wrap(err, "Unable to confirm") } if !ok { return locale.NewInputError("err_reset_aborted", "Reset aborted by user") } + if kind == prompt.NonInteractive { + u.out.Notice(locale.T("prompt_continue_non_interactive")) + } reset, err := globaldefault.ResetDefaultActivation(u.subshell, u.config) if err != nil { diff --git a/test/integration/projects_int_test.go b/test/integration/projects_int_test.go index d60c8f335a..7316466515 100644 --- a/test/integration/projects_int_test.go +++ b/test/integration/projects_int_test.go @@ -161,7 +161,7 @@ func (suite *ProjectsIntegrationTestSuite) TestMove() { cp.Expect("Continue? (y/N)") cp.SendLine("n") cp.Expect("aborted") - cp.ExpectExitCode(0) + cp.ExpectNotExitCode(0) } func TestProjectsIntegrationTestSuite(t *testing.T) { From 1fdc129a9b5312b4fe660536ba4d5b9799c31791 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 15 Nov 2024 15:56:30 -0500 Subject: [PATCH 383/440] Use prompt accessors instead of Confirm return values. --- internal/prompt/prompt.go | 32 ++++++++++++----------------- internal/runbits/auth/keypair.go | 5 +---- internal/runbits/auth/login.go | 2 +- internal/runbits/cves/cves.go | 7 +++---- internal/runners/clean/cache.go | 8 ++++---- internal/runners/clean/config.go | 8 ++++---- internal/runners/clean/uninstall.go | 8 ++++---- internal/runners/projects/delete.go | 4 ++-- internal/runners/projects/edit.go | 4 ++-- internal/runners/projects/move.go | 4 ++-- internal/runners/publish/publish.go | 4 ++-- internal/runners/push/push.go | 8 ++++---- internal/runners/reset/reset.go | 4 ++-- internal/runners/revert/revert.go | 4 ++-- internal/runners/scripts/edit.go | 2 +- internal/runners/update/lock.go | 4 ++-- internal/runners/update/unlock.go | 4 ++-- internal/runners/upgrade/upgrade.go | 5 ++--- internal/runners/use/reset.go | 4 ++-- 19 files changed, 55 insertions(+), 66 deletions(-) diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 4735947305..07c004c9d4 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -15,17 +15,6 @@ import ( "github.com/ActiveState/cli/internal/output" ) -type ConfirmKind int - -const ( - // A confirm prompt was completed by the user. - User ConfirmKind = iota - // A confirm prompt was completed in non-interactive mode. - NonInteractive - // A confirm prompt was completed via --force. - Force -) - type EventDispatcher interface { EventWithLabel(category, action string, label string, dim ...*dimensions.Values) } @@ -35,11 +24,12 @@ type Prompter interface { Input(title, message string, defaultResponse *string, flags ...ValidatorFlag) (string, error) InputAndValidate(title, message string, defaultResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) Select(title, message string, choices []string, defaultResponse *string) (string, error) - Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, ConfirmKind, error) + Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) InputSecret(title, message string, flags ...ValidatorFlag) (string, error) IsInteractive() bool SetInteractive(bool) EnableForce() + IsForceEnabled() bool } // ValidatorFunc is a function pass to the Prompter to perform validation @@ -76,6 +66,10 @@ func (p *Prompt) EnableForce() { p.isForced = true } +func (p *Prompt) IsForceEnabled() bool { + return p.isForced +} + // ValidatorFlag represents flags for prompt functions to change their behavior on. type ValidatorFlag int @@ -183,21 +177,21 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * } // Confirm prompts user for yes or no response. -func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, ConfirmKind, error) { +func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) { if p.isForced { if forcedChoice == nil { - return false, Force, errs.New("No force option given for force-enabled prompt") + return false, errs.New("No force option given for force-enabled prompt") } logging.Debug("Prompt %s confirmed with choice %v in force mode", title, forcedChoice) - return *forcedChoice, Force, nil + return *forcedChoice, nil } if !p.isInteractive { if defaultChoice != nil { logging.Debug("Prompt %s confirmed with default choice %v in non-interactive mode", title, defaultChoice) - return *defaultChoice, NonInteractive, nil + return *defaultChoice, nil } - return false, NonInteractive, interactiveInputError(message) + return false, interactiveInputError(message) } if title != "" { p.out.Notice(output.Emphasize(title)) @@ -219,11 +213,11 @@ func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoic if err == terminal.InterruptErr { p.analytics.EventWithLabel(constants.CatPrompt, title, "interrupt") } - return false, User, locale.NewInputError(err.Error()) + return false, locale.NewInputError(err.Error()) } p.analytics.EventWithLabel(constants.CatPrompt, title, translateConfirm(resp)) - return resp, User, nil + return resp, nil } func translateConfirm(confirm bool) string { diff --git a/internal/runbits/auth/keypair.go b/internal/runbits/auth/keypair.go index c92ee7974b..b91c44671b 100644 --- a/internal/runbits/auth/keypair.go +++ b/internal/runbits/auth/keypair.go @@ -116,14 +116,11 @@ func promptForPreviousPassphrase(prompt prompt.Prompter) (string, error) { func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prmpt prompt.Prompter, auth *authentication.Auth) error { // previous passphrase is invalid, inform user and ask if they want to generate a new keypair out.Notice(locale.T("auth_generate_new_keypair_message")) - yes, kind, err := prmpt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil) + yes, err := prmpt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !yes { - if kind == prompt.NonInteractive { - return locale.NewInputError("prompt_abort_non_interactive") - } return locale.NewInputError("auth_err_unrecoverable_keypair") } _, err = keypairs.GenerateAndSaveEncodedKeypair(cfg, secretsapi.Get(auth), passphrase, constants.DefaultRSABitLength, auth) diff --git a/internal/runbits/auth/login.go b/internal/runbits/auth/login.go index 785a759f02..ac902ef2aa 100644 --- a/internal/runbits/auth/login.go +++ b/internal/runbits/auth/login.go @@ -266,7 +266,7 @@ func authenticateWithBrowser(out output.Outputer, auth *authentication.Auth, pro var cont bool var err error for !cont { - cont, _, err = prompt.Confirm(locale.Tl("continue", "Continue?"), locale.T("auth_press_enter"), ptr.To(false), nil) + cont, err = prompt.Confirm(locale.Tl("continue", "Continue?"), locale.T("auth_press_enter"), ptr.To(false), nil) if err != nil { return errs.Wrap(err, "Prompt failed") } diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index 0ecb893c8f..a82649c489 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -12,7 +12,6 @@ import ( configMediator "github.com/ActiveState/cli/internal/mediators/config" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/buildplan" vulnModel "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/model" @@ -106,12 +105,12 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil c.summarizeCVEs(vulnerabilities) - confirm, kind, err := c.prime.Prompt().Confirm("", locale.Tr("prompt_continue_pkg_operation"), ptr.To(false), ptr.To(true)) + confirm, err := c.prime.Prompt().Confirm("", locale.Tr("prompt_continue_pkg_operation"), ptr.To(false), ptr.To(true)) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !confirm { - if kind == prompt.NonInteractive { + if !c.prime.Prompt().IsInteractive() { return errs.AddTips( locale.NewInputError("prompt_abort_non_interactive"), locale.Tl("more_info_prompt", "To disable security prompting run: [ACTIONABLE]state config set security.prompt.enabled false[/RESET]"), @@ -119,7 +118,7 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } return locale.NewInputError("err_pkgop_security_prompt", "Operation aborted by user") } - if kind == prompt.Force { + if c.prime.Prompt().IsForceEnabled() { c.prime.Output().Notice(locale.T("prompt_continue_force")) } c.prime.Output().Notice("") // Empty line diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index e8ca73adf4..bbb87ede43 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -71,14 +71,14 @@ func (c *Cache) Run(params *CacheParams) error { func (c *Cache) removeCache(path string) error { defaultValue := !c.prime.Prompt().IsInteractive() - ok, kind, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.T("clean_cache_confirm"), &defaultValue, nil) + ok, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.T("clean_cache_confirm"), &defaultValue, nil) if err != nil { return errs.Wrap(err, "Could not confirm") } if !ok { return locale.NewInputError("err_clean_cache_not_confirmed", "Cleaning of cache aborted by user") } - if kind == prompt.NonInteractive { + if !c.prime.Prompt().IsInteractive() { c.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } @@ -101,14 +101,14 @@ func (c *Cache) removeCache(path string) error { func (c *Cache) removeProjectCache(projectDir, namespace string) error { defaultValue := !c.prime.Prompt().IsInteractive() - ok, kind, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.Tr("clean_cache_artifact_confirm", namespace), &defaultValue, nil) + ok, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.Tr("clean_cache_artifact_confirm", namespace), &defaultValue, nil) if err != nil { return errs.Wrap(err, "Could not confirm") } if !ok { return locale.NewInputError("err_clean_cache_artifact_not_confirmed", "Cleaning of cached runtime aborted by user") } - if kind == prompt.NonInteractive { + if !c.prime.Prompt().IsInteractive() { c.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/clean/config.go b/internal/runners/clean/config.go index 6524b86371..c0aeebc2dd 100644 --- a/internal/runners/clean/config.go +++ b/internal/runners/clean/config.go @@ -54,17 +54,17 @@ func (c *Config) Run(params *ConfigParams) error { } defaultChoice := !c.confirm.IsInteractive() - ok, kind, err := c.confirm.Confirm(locale.T("confirm"), locale.T("clean_config_confirm"), &defaultChoice, ptr.To(true)) + ok, err := c.confirm.Confirm(locale.T("confirm"), locale.T("clean_config_confirm"), &defaultChoice, ptr.To(true)) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !ok { return locale.NewInputError("err_clean_config_aborted", "Cleaning of config aborted by user") } - switch kind { - case prompt.NonInteractive: + switch { + case !c.confirm.IsInteractive(): c.output.Notice(locale.T("prompt_continue_non_interactive")) - case prompt.Force: + case c.confirm.IsForceEnabled(): c.output.Notice(locale.T("prompt_continue_force")) } diff --git a/internal/runners/clean/uninstall.go b/internal/runners/clean/uninstall.go index 1b888697c1..d8c671cc44 100644 --- a/internal/runners/clean/uninstall.go +++ b/internal/runners/clean/uninstall.go @@ -83,17 +83,17 @@ func (u *Uninstall) Run(params *UninstallParams) error { if params.All { confirmMessage = locale.T("uninstall_confirm_all") } - ok, kind, err := u.prompt.Confirm(locale.T("confirm"), confirmMessage, &defaultChoice, ptr.To(true)) + ok, err := u.prompt.Confirm(locale.T("confirm"), confirmMessage, &defaultChoice, ptr.To(true)) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !ok { return locale.NewInputError("err_uninstall_aborted", "Uninstall aborted by user") } - switch kind { - case prompt.NonInteractive: + switch { + case !u.prompt.IsInteractive(): u.out.Notice(locale.T("prompt_continue_non_interactive")) - case prompt.Force: + case u.prompt.IsForceEnabled(): u.out.Notice(locale.T("prompt_continue_force")) } } diff --git a/internal/runners/projects/delete.go b/internal/runners/projects/delete.go index 20912814eb..6c1aae3a7e 100644 --- a/internal/runners/projects/delete.go +++ b/internal/runners/projects/delete.go @@ -38,14 +38,14 @@ func (d *Delete) Run(params *DeleteParams) error { } defaultChoice := !d.prompt.IsInteractive() - confirm, kind, err := d.prompt.Confirm("", locale.Tl("project_delete_confim", "Are you sure you want to delete the project {{.V0}}?", params.Project.String()), &defaultChoice, nil) + confirm, err := d.prompt.Confirm("", locale.Tl("project_delete_confim", "Are you sure you want to delete the project {{.V0}}?", params.Project.String()), &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !confirm { return locale.NewInputError("err_project_delete_aborted", "Delete aborted by user") } - if kind == prompt.NonInteractive { + if !d.prompt.IsInteractive() { d.out.Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/projects/edit.go b/internal/runners/projects/edit.go index 8919008058..741d588f92 100644 --- a/internal/runners/projects/edit.go +++ b/internal/runners/projects/edit.go @@ -91,14 +91,14 @@ func (e *Edit) Run(params *EditParams) error { editMsg += locale.Tl("edit_prompt_confirm", "Continue?") defaultChoice := !e.prompt.IsInteractive() - edit, kind, err := e.prompt.Confirm("", editMsg, &defaultChoice, nil) + edit, err := e.prompt.Confirm("", editMsg, &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !edit { return locale.NewInputError("edit_cancelled", "Project edit cancelled") } - if kind == prompt.NonInteractive { + if !e.prompt.IsInteractive() { e.out.Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/projects/move.go b/internal/runners/projects/move.go index fa9e7ed2a1..858dbead14 100644 --- a/internal/runners/projects/move.go +++ b/internal/runners/projects/move.go @@ -42,14 +42,14 @@ func (m *Move) Run(params *MoveParams) error { } defaultChoice := !m.prompt.IsInteractive() - move, kind, err := m.prompt.Confirm("", locale.Tr("move_prompt", params.Namespace.String(), params.NewOwner, params.Namespace.Project), &defaultChoice, nil) + move, err := m.prompt.Confirm("", locale.Tr("move_prompt", params.Namespace.String(), params.NewOwner, params.Namespace.Project), &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !move { return locale.NewInputError("move_cancelled", "Project move aborted by user") } - if kind == prompt.NonInteractive { + if !m.prompt.IsInteractive() { m.out.Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/publish/publish.go b/internal/runners/publish/publish.go index 2b8af90997..239a5233bb 100644 --- a/internal/runners/publish/publish.go +++ b/internal/runners/publish/publish.go @@ -218,7 +218,7 @@ func (r *Runner) Run(params *Params) error { return errs.Wrap(err, "Could not marshal publish variables") } - cont, kind, err := r.prompt.Confirm( + cont, err := r.prompt.Confirm( "", locale.Tl("uploadingredient_confirm", `Prepared the following ingredient: @@ -232,7 +232,7 @@ Do you want to publish this ingredient? if !cont { return locale.NewInputError("uploadingredient_cancel", "Publish cancelled") } - if kind == prompt.NonInteractive { + if !r.prompt.IsInteractive() { r.out.Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index 10f935dfe8..d4db851d73 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -128,14 +128,14 @@ func (r *Push) Run(params PushParams) (rerr error) { // Ask to create a copy if the user does not have org permissions if intend&pushFromNoPermission > 0 && !params.Namespace.IsValid() { - createCopy, kind, err := r.prompt.Confirm("", locale.T("push_prompt_not_authorized"), ptr.To(true), nil) + createCopy, err := r.prompt.Confirm("", locale.T("push_prompt_not_authorized"), ptr.To(true), nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !createCopy { return nil } - if kind == prompt.NonInteractive { + if !r.prompt.IsInteractive() { r.out.Notice(locale.T("prompt_continue_non_interactive")) } } @@ -172,7 +172,7 @@ func (r *Push) Run(params PushParams) (rerr error) { // If the user didn't necessarily intend to create the project we should ask them for confirmation if intend&intendCreateProject == 0 { - createProject, kind, err := r.prompt.Confirm( + createProject, err := r.prompt.Confirm( locale.Tl("create_project", "Create Project"), locale.Tl("push_confirm_create_project", "You are about to create the project [NOTICE]{{.V0}}[/RESET]. Continue?", targetNamespace.String()), ptr.To(true), nil) @@ -182,7 +182,7 @@ func (r *Push) Run(params PushParams) (rerr error) { if !createProject { return rationalize.ErrActionAborted } - if kind == prompt.NonInteractive { + if !r.prompt.IsInteractive() { r.out.Notice(locale.T("prompt_continue_non_interactive")) } } diff --git a/internal/runners/reset/reset.go b/internal/runners/reset/reset.go index 944842bf67..2f27cd1931 100644 --- a/internal/runners/reset/reset.go +++ b/internal/runners/reset/reset.go @@ -117,14 +117,14 @@ func (r *Reset) Run(params *Params) error { r.out.Notice(locale.Tl("reset_commit", "Your project will be reset to [ACTIONABLE]{{.V0}}[/RESET]\n", commitID.String())) if commitID != localCommitID { defaultChoice := !r.prime.Prompt().IsInteractive() - confirm, kind, err := r.prime.Prompt().Confirm("", locale.Tl("reset_confim", "Resetting is destructive. You will lose any changes that were not pushed. Are you sure you want to do this?"), &defaultChoice, nil) + confirm, err := r.prime.Prompt().Confirm("", locale.Tl("reset_confim", "Resetting is destructive. You will lose any changes that were not pushed. Are you sure you want to do this?"), &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !confirm { return locale.NewInputError("err_reset_aborted", "Reset aborted by user") } - if kind == prompt.NonInteractive { + if !r.prime.Prompt().IsInteractive() { r.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } } diff --git a/internal/runners/revert/revert.go b/internal/runners/revert/revert.go index 335bab3af4..0618528826 100644 --- a/internal/runners/revert/revert.go +++ b/internal/runners/revert/revert.go @@ -139,14 +139,14 @@ func (r *Revert) Run(params *Params) (rerr error) { } defaultChoice := !r.prime.Prompt().IsInteractive() - revert, kind, err := r.prime.Prompt().Confirm("", locale.Tl("revert_confirm", "Continue?"), &defaultChoice, nil) + revert, err := r.prime.Prompt().Confirm("", locale.Tl("revert_confirm", "Continue?"), &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !revert { return locale.NewInputError("err_revert_aborted", "Revert aborted by user") } - if kind == prompt.NonInteractive { + if !r.prime.Prompt().IsInteractive() { r.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/scripts/edit.go b/internal/runners/scripts/edit.go index 3f3e04be19..59d74333c9 100644 --- a/internal/runners/scripts/edit.go +++ b/internal/runners/scripts/edit.go @@ -237,7 +237,7 @@ func startInteractive(sw *scriptWatcher, scriptName string, output output.Output go sw.run(scriptName, output, cfg, proj) for { - doneEditing, _, err := prompt.Confirm("", locale.T("prompt_done_editing"), ptr.To(true), nil) + doneEditing, err := prompt.Confirm("", locale.T("prompt_done_editing"), ptr.To(true), nil) if err != nil { return errs.Wrap(err, "Prompter returned with failure.") } diff --git a/internal/runners/update/lock.go b/internal/runners/update/lock.go index 707d01b8a7..9ad15b52b9 100644 --- a/internal/runners/update/lock.go +++ b/internal/runners/update/lock.go @@ -131,14 +131,14 @@ func confirmLock(prom prompt.Prompter, out output.Outputer) error { defaultChoice := !prom.IsInteractive() msg := locale.T("confirm_update_locked_version_prompt") - confirmed, kind, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) + confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !confirmed { return locale.NewInputError("err_update_lock_noconfirm", "Cancelling by your request.") } - if kind == prompt.NonInteractive { + if !prom.IsInteractive() { out.Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/update/unlock.go b/internal/runners/update/unlock.go index 8eae02f42d..d3d5e99921 100644 --- a/internal/runners/update/unlock.go +++ b/internal/runners/update/unlock.go @@ -73,14 +73,14 @@ func confirmUnlock(prom prompt.Prompter, out output.Outputer) error { msg := locale.T("confirm_update_unlocked_version_prompt") defaultChoice := !prom.IsInteractive() - confirmed, kind, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) + confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } if !confirmed { return locale.NewInputError("err_update_lock_noconfirm", "Cancelling by your request.") } - if kind == prompt.NonInteractive { + if !prom.IsInteractive() { out.Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index 22d8caeb2f..41f2207cf6 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -12,7 +12,6 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" - "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/commits_runbit" "github.com/ActiveState/cli/internal/runbits/rationalize" @@ -284,14 +283,14 @@ func (u *Upgrade) renderUserFacing(changes []structuredChange, expand bool) erro out.Print(tbl.Render()) out.Notice(" ") // Empty line (prompts use Notice) - confirm, kind, err := u.prime.Prompt().Confirm("", locale.Tr("upgrade_confirm"), ptr.To(true), nil) + confirm, err := u.prime.Prompt().Confirm("", locale.Tr("upgrade_confirm"), ptr.To(true), nil) if err != nil { return errs.Wrap(err, "confirmation failed") } if !confirm { return ErrAbort } - if kind == prompt.NonInteractive { + if !u.prime.Prompt().IsInteractive() { u.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) } diff --git a/internal/runners/use/reset.go b/internal/runners/use/reset.go index 85e5b0283e..9fa5aa26e8 100644 --- a/internal/runners/use/reset.go +++ b/internal/runners/use/reset.go @@ -40,7 +40,7 @@ func (u *Reset) Run(params *ResetParams) error { } defaultChoice := !u.prompt.IsInteractive() - ok, kind, err := u.prompt.Confirm(locale.T("confirm"), + ok, err := u.prompt.Confirm(locale.T("confirm"), locale.Tl("use_reset_confirm", "You are about to stop using your project runtime. Continue?"), &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Unable to confirm") @@ -48,7 +48,7 @@ func (u *Reset) Run(params *ResetParams) error { if !ok { return locale.NewInputError("err_reset_aborted", "Reset aborted by user") } - if kind == prompt.NonInteractive { + if !u.prompt.IsInteractive() { u.out.Notice(locale.T("prompt_continue_non_interactive")) } From 918a41c94670469f5827c03353dae4a9b449dd49 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 15 Nov 2024 16:18:11 -0500 Subject: [PATCH 384/440] Updated prompt.Confirm callers outside state tool. --- cmd/state-installer/installer.go | 2 +- cmd/state-remote-installer/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/state-installer/installer.go b/cmd/state-installer/installer.go index 65ce101cb5..b5fef2a594 100644 --- a/cmd/state-installer/installer.go +++ b/cmd/state-installer/installer.go @@ -55,7 +55,7 @@ func (i *Installer) Install() (rerr error) { } if isAdmin && !i.Params.force && !i.Params.isUpdate && !i.Params.nonInteractive { prompter := prompt.New(true, i.an) - confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false)) + confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false), nil) if err != nil { return errs.Wrap(err, "Unable to confirm") } diff --git a/cmd/state-remote-installer/main.go b/cmd/state-remote-installer/main.go index c5c08b4fb5..2ca3c36f75 100644 --- a/cmd/state-remote-installer/main.go +++ b/cmd/state-remote-installer/main.go @@ -174,7 +174,7 @@ func main() { func execute(out output.Outputer, prompt prompt.Prompter, cfg *config.Instance, an analytics.Dispatcher, args []string, params *Params) error { msg := locale.Tr("tos_disclaimer", constants.TermsOfServiceURLLatest) msg += locale.Tr("tos_disclaimer_prompt", constants.TermsOfServiceURLLatest) - cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, ptr.To(true)) + cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, ptr.To(true), nil) if err != nil { return errs.Wrap(err, "Could not prompt for confirmation") } From c795a06dbca97a0dbfeac2939d2b0fc60497852e Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 18 Nov 2024 10:57:47 -0500 Subject: [PATCH 385/440] Fixed failing integration test. --- internal/runbits/runtime/runtime.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runbits/runtime/runtime.go b/internal/runbits/runtime/runtime.go index 5cd987d89d..83b995ff75 100644 --- a/internal/runbits/runtime/runtime.go +++ b/internal/runbits/runtime/runtime.go @@ -253,7 +253,7 @@ func Update( if opts.Archive != nil { rtOpts = append(rtOpts, runtime.WithArchive(opts.Archive.Dir, opts.Archive.PlatformID, checkout.ArtifactExt)) } - if commit.BuildPlan().IsBuildInProgress() { + if buildPlan.IsBuildInProgress() { // Build progress URL is of the form // https://///distributions?branch=&commitID= host := constants.DefaultAPIHost From 568a074d2188abd1b5d276a88064052580edf0b7 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 18 Nov 2024 11:17:42 -0500 Subject: [PATCH 386/440] Remove mock. --- internal/prompt/mock/mock.go | 77 ------------------------------------ 1 file changed, 77 deletions(-) delete mode 100644 internal/prompt/mock/mock.go diff --git a/internal/prompt/mock/mock.go b/internal/prompt/mock/mock.go deleted file mode 100644 index e32a2b08c1..0000000000 --- a/internal/prompt/mock/mock.go +++ /dev/null @@ -1,77 +0,0 @@ -package mock - -import ( - "reflect" - - "github.com/ActiveState/cli/internal/prompt" - - tmock "github.com/stretchr/testify/mock" -) - -var _ prompt.Prompter = &Mock{} - -// Mock the struct to mock the Prompt struct -type Mock struct { - tmock.Mock -} - -// Init an object -func Init() *Mock { - return &Mock{} -} - -// Close it -func (m *Mock) Close() { -} - -// Input prompts the user for input -func (m *Mock) Input(title, message string, defaultResponse *string, flags ...prompt.ValidatorFlag) (string, error) { - args := m.Called(title, message, defaultResponse, flags) - return args.String(0), failure(args.Get(1)) -} - -// InputAndValidate prompts the user for input witha customer validator and validation flags -func (m *Mock) InputAndValidate(title, message string, defaultResponse *string, validator prompt.ValidatorFunc, flags ...prompt.ValidatorFlag) (response string, err error) { - args := m.Called(message, message, defaultResponse, validator) - return args.String(0), failure(args.Get(1)) -} - -// Select prompts the user to select one entry from multiple choices -func (m *Mock) Select(title, message string, choices []string, defaultChoice *string) (string, error) { - args := m.Called(title, message, choices, defaultChoice) - return args.String(0), failure(args.Get(1)) -} - -// Confirm prompts user for yes or no response. -func (m *Mock) Confirm(title, message string, defaultChoice *bool) (bool, error) { - args := m.Called(title, message, defaultChoice) - return args.Bool(0), failure(args.Get(1)) -} - -// InputSecret prompts the user for input and obfuscates the text in stdout. -// Will fail if empty. -func (m *Mock) InputSecret(title, message string, flags ...prompt.ValidatorFlag) (response string, err error) { - args := m.Called(title, message) - return args.String(0), failure(args.Get(1)) -} - -// OnMethod behaves like mock.On but disregards whether arguments match or not -func (m *Mock) OnMethod(methodName string) *tmock.Call { - methodType := reflect.ValueOf(m).MethodByName(methodName).Type() - anyArgs := []interface{}{} - for i := 0; i < methodType.NumIn(); i++ { - anyArgs = append(anyArgs, tmock.Anything) - } - return m.On(methodName, anyArgs...) -} - -func (m *Mock) IsInteractive() bool { - return true -} - -func failure(arg interface{}) error { - if arg == nil { - return nil - } - return arg.(error) -} From 4113b3e0cdc322959f476ba01c67693b386b1000 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 18 Nov 2024 12:32:48 -0500 Subject: [PATCH 387/440] Updated integration tests with non-interactive and force notices. --- internal/locale/locales/en-us.yaml | 2 +- internal/runners/push/push.go | 2 +- test/integration/package_int_test.go | 48 ++++++++++++++++++++++++ test/integration/reset_int_test.go | 1 + test/integration/update_lock_int_test.go | 1 + 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index add273ea81..b5a42a0105 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1168,7 +1168,7 @@ prompt_continue_pkg_operation: prompt_continue_force: other: "Continuing because the '[ACTIONABLE]--force[/RESET]' flag is set." prompt_continue_non_interactive: - other: "Continuting because State Tool is running in non-interactive mode." + other: "Continuing because State Tool is running in non-interactive mode." prompt_abort_non_interactive: other: "Aborting because State Tool is running in non-interactive mode. To bypass you can use the '[ACTIONABLE]--force[/RESET]' flag." unstable_command_warning: diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index d4db851d73..8c828f2f13 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -333,7 +333,7 @@ func (r *Push) promptNamespace() (*project.Namespaced, error) { owner := r.auth.WhoAmI() owner, err := r.prompt.Input("", locale.T("push_prompt_owner"), &owner) if err != nil { - return nil, locale.WrapError(err, "err_push_get_owner", "Could not deterimine project owner") + return nil, locale.WrapError(err, "err_push_get_owner", "Could not determine project owner") } var name string diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 8ac91a19f2..60983fc0bd 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -600,6 +600,54 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Prompt() { cp.ExpectExitCode(0) } +func (suite *PackageIntegrationTestSuite) TestCVE_NonInteractive() { + suite.OnlyRunForTags(tagsuite.Package) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + ts.LoginAsPersistentUser() + + ts.PrepareProject("ActiveState-CLi/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") + + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "set", "security.prompt.level", "high") + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "set", constants.SecurityPromptConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z", "--non-interactive") + cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.Expect("Aborting because State Tool is running in non-interactive mode") + cp.ExpectNotExitCode(0) +} + +func (suite *PackageIntegrationTestSuite) TestCVE_Force() { + suite.OnlyRunForTags(tagsuite.Package) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + ts.LoginAsPersistentUser() + + ts.PrepareProject("ActiveState-CLi/small-python", "5a1e49e5-8ceb-4a09-b605-ed334474855b") + + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "set", "security.prompt.level", "high") + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "set", constants.SecurityPromptConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z", "--force") + cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.Expect("Continuing because the '--force' flag is set") + cp.ExpectExitCode(0) +} + func (suite *PackageIntegrationTestSuite) TestCVE_Indirect() { suite.OnlyRunForTags(tagsuite.Package) ts := e2e.New(suite.T(), false) diff --git a/test/integration/reset_int_test.go b/test/integration/reset_int_test.go index 9b3199ee60..98ae43997f 100644 --- a/test/integration/reset_int_test.go +++ b/test/integration/reset_int_test.go @@ -90,6 +90,7 @@ func (suite *ResetIntegrationTestSuite) TestRevertInvalidURL() { cp.ExpectNotExitCode(0) cp = ts.Spawn("reset", "-n") + cp.Expect("Continuing because State Tool is running in non-interactive mode") cp.Expect("Successfully reset to commit: " + commitID.String()) cp.ExpectExitCode(0) } diff --git a/test/integration/update_lock_int_test.go b/test/integration/update_lock_int_test.go index 6e9f9add34..471c4b8527 100644 --- a/test/integration/update_lock_int_test.go +++ b/test/integration/update_lock_int_test.go @@ -203,6 +203,7 @@ func (suite *UpdateIntegrationTestSuite) TestLockUnlock() { e2e.OptArgs("update", "unlock", "-n"), e2e.OptAppendEnv(suite.env(false, false)...), ) + cp.Expect("Continuing because State Tool is running in non-interactive mode") cp.Expect("unlocked") data, err = os.ReadFile(pjfile.Path()) From 3eb0e78c456d1450d31c5b3003acf71097c655fc Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 18 Nov 2024 15:26:35 -0500 Subject: [PATCH 388/440] Fixed interline diffs. Do not try to clean up the line diff, as it may end up inadvertently combining lines. --- internal/runbits/buildscript/buildscript.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/runbits/buildscript/buildscript.go b/internal/runbits/buildscript/buildscript.go index 4972405f75..b8443447f7 100644 --- a/internal/runbits/buildscript/buildscript.go +++ b/internal/runbits/buildscript/buildscript.go @@ -33,7 +33,6 @@ func generateDiff(script *buildscript.BuildScript, otherScript *buildscript.Buil scriptLines, newScriptLines, lines := diff.DiffLinesToChars(string(sb1), string(sb2)) hunks := diff.DiffMain(scriptLines, newScriptLines, false) hunks = diff.DiffCharsToLines(hunks, lines) - hunks = diff.DiffCleanupSemantic(hunks) for i := 0; i < len(hunks); i++ { switch hunk := hunks[i]; hunk.Type { case diffmatchpatch.DiffEqual: From 8321e42d804b7bfc76ca3fff4ca962505de1ad96 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 18 Nov 2024 14:32:59 -0800 Subject: [PATCH 389/440] Reorganize error handling --- .../buildplanner/request/buildexpression.go | 6 +- .../api/buildplanner/request/commit.go | 32 +++---- .../api/buildplanner/request/createproject.go | 85 ++++++++++++------- .../api/buildplanner/request/evaluate.go | 30 +++---- .../api/buildplanner/request/mergecommit.go | 85 ++++++++++--------- .../api/buildplanner/request/revertcommit.go | 71 +++++++++------- .../api/buildplanner/request/stagecommit.go | 79 ++++++++--------- .../api/buildplanner/response/build.go | 4 +- .../api/buildplanner/response/commit.go | 34 +------- .../api/buildplanner/response/commiterror.go | 31 +------ .../buildplanner/response/createproject.go | 3 - .../api/buildplanner/response/mergecommit.go | 8 +- .../api/buildplanner/response/pushcommit.go | 10 --- .../api/buildplanner/response/revertcommit.go | 1 + .../api/buildplanner/response/shared.go | 29 +++---- .../api/buildplanner/response/solveerror.go | 41 ++++++--- pkg/platform/api/buildplanner/types/errors.go | 1 + pkg/platform/api/graphql/request/publish.go | 26 +++--- 18 files changed, 268 insertions(+), 308 deletions(-) delete mode 100644 pkg/platform/api/buildplanner/response/pushcommit.go diff --git a/pkg/platform/api/buildplanner/request/buildexpression.go b/pkg/platform/api/buildplanner/request/buildexpression.go index f07f097086..b98ae68672 100644 --- a/pkg/platform/api/buildplanner/request/buildexpression.go +++ b/pkg/platform/api/buildplanner/request/buildexpression.go @@ -19,11 +19,7 @@ query ($commitID: ID!) { atTime expr } - ... on Error{ - __typename - message - } - ... on NotFound { + ... on Error { __typename message } diff --git a/pkg/platform/api/buildplanner/request/commit.go b/pkg/platform/api/buildplanner/request/commit.go index 366a157cac..d479ac79de 100644 --- a/pkg/platform/api/buildplanner/request/commit.go +++ b/pkg/platform/api/buildplanner/request/commit.go @@ -147,7 +147,7 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S name namespace version_requirements: versionRequirements { - comparator + comparator version } } @@ -155,12 +155,20 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S } } ... on Error { + __typename message } - ... on PlanningError { - message + ... on ErrorWithSubErrors { + __typename subErrors { __typename + buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } ... on GenericSolveError { path message @@ -185,23 +193,12 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S parameters } } - ... on TargetNotFound { - message - requestedTarget - possibleTargets - } } } } } ... on Error { - message - } - ... on NotFound { __typename - type - resource - mayNeedAuthentication message } } @@ -210,13 +207,6 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S __typename message } - ... on NotFound { - __typename - type - resource - mayNeedAuthentication - message - } } } ` diff --git a/pkg/platform/api/buildplanner/request/createproject.go b/pkg/platform/api/buildplanner/request/createproject.go index 8f3c52aca0..f24959d2a8 100644 --- a/pkg/platform/api/buildplanner/request/createproject.go +++ b/pkg/platform/api/buildplanner/request/createproject.go @@ -18,37 +18,60 @@ type createProject struct { func (c *createProject) Query() string { return ` mutation ($organization: String!, $project: String!, $private: Boolean!, $expr: BuildExpr!, $description: String!) { - createProject(input:{organization:$organization, project:$project, private:$private, expr:$expr, description:$description}) { - ... on ProjectCreated { - __typename - commit { - __typename - commitId - } - } - ... on AlreadyExists { - __typename - message - } - ... on NotFound { - __typename - message - } - ... on ParseError { - __typename - message - path - } - ... on ValidationError { - __typename - message - } - ... on Forbidden { - __typename - message - } - } -}` + createProject( + input: {organization: $organization, project: $project, private: $private, expr: $expr, description: $description} + ) { + ... on ProjectCreated { + __typename + commit { + __typename + commitId + } + } + ... on Error { + __typename + message + } + ... on ErrorWithSubErrors { + __typename + subErrors { + __typename + buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } + ... on GenericSolveError { + path + message + isTransient + validationErrors { + error + jsonPath + } + } + ... on RemediableSolveError { + path + message + isTransient + errorType + validationErrors { + error + jsonPath + } + suggestedRemediations { + remediationType + command + parameters + } + } + } + } + } +} +` } func (c *createProject) Vars() (map[string]interface{}, error) { diff --git a/pkg/platform/api/buildplanner/request/evaluate.go b/pkg/platform/api/buildplanner/request/evaluate.go index acabfdc7cf..6ddbd26876 100644 --- a/pkg/platform/api/buildplanner/request/evaluate.go +++ b/pkg/platform/api/buildplanner/request/evaluate.go @@ -23,13 +23,23 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target __typename status } - ... on PlanningError { + ... on Error { __typename message + } + ... on ErrorWithSubErrors { + __typename subErrors { __typename + buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } ... on GenericSolveError { - buildExprPath + path message isTransient validationErrors { @@ -38,7 +48,7 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target } } ... on RemediableSolveError { - buildExprPath + path message isTransient errorType @@ -52,21 +62,11 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target parameters } } - ... on TargetNotFound { - message - requestedTarget - possibleTargets - } } } - ... on NotFound { - type - message - resource - mayNeedAuthentication - } } -}` +} +` } func (b *evaluate) Vars() (map[string]interface{}, error) { diff --git a/pkg/platform/api/buildplanner/request/mergecommit.go b/pkg/platform/api/buildplanner/request/mergecommit.go index b1868d5f2d..f9423fae36 100644 --- a/pkg/platform/api/buildplanner/request/mergecommit.go +++ b/pkg/platform/api/buildplanner/request/mergecommit.go @@ -21,52 +21,55 @@ type mergeCommit struct { func (b *mergeCommit) Query() string { return ` mutation ($organization: String!, $project: String!, $targetRef: String!, $otherRef: String!, $strategy: MergeStrategy) { - mergeCommit(input:{organization:$organization, project:$project, targetVcsRef:$targetRef, otherVcsRef:$otherRef, strategy:$strategy}) { - ... on MergedCommit { - commit { - __typename - commitId - } - } - ... on MergeConflict { - __typename - message - } - ... on FastForwardError { - __typename - message - } - ... on NoCommonBaseFound { - __typename - message - } - ... on NotFound { - __typename - message - mayNeedAuthentication + mergeCommit( + input: {organization: $organization, project: $project, targetVcsRef: $targetRef, otherVcsRef: $otherRef, strategy: $strategy} + ) { + ... on MergedCommit { + commit { + __typename + commitId + } } - ... on ParseError { + ... on Error { __typename message } - ... on ValidationError { + ... on ErrorWithSubErrors { __typename - message - } - ... on Forbidden { - __typename - message - } - ... on HeadOnBranchMoved { - __typename - message - commitId - branchId - } - ... on NoChangeSinceLastCommit { - __typename - message - commitId + subErrors { + __typename + buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } + ... on GenericSolveError { + path + message + isTransient + validationErrors { + error + jsonPath + } + } + ... on RemediableSolveError { + path + message + isTransient + errorType + validationErrors { + error + jsonPath + } + suggestedRemediations { + remediationType + command + parameters + } + } + } } } } diff --git a/pkg/platform/api/buildplanner/request/revertcommit.go b/pkg/platform/api/buildplanner/request/revertcommit.go index 1a2e4fbe5a..5cbf609ba2 100644 --- a/pkg/platform/api/buildplanner/request/revertcommit.go +++ b/pkg/platform/api/buildplanner/request/revertcommit.go @@ -35,45 +35,50 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target commitId } } - ... on RevertConflict { + ... on Error { __typename message - commitId - targetCommitId - conflictPaths } - ... on CommitHasNoParent { + ... on ErrorWithSubErrors { __typename - message - } - ... on NotFound { - __typename - message - mayNeedAuthentication - } - ... on ParseError { - __typename - message - path - } - ... on ValidationError { - __typename - message - } - ... on Forbidden { - __typename - message - } - ... on HeadOnBranchMoved { - __typename - message - } - ... on NoChangeSinceLastCommit { - message - commitId + subErrors { + __typename + buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } + ... on GenericSolveError { + path + message + isTransient + validationErrors { + error + jsonPath + } + } + ... on RemediableSolveError { + path + message + isTransient + errorType + validationErrors { + error + jsonPath + } + suggestedRemediations { + remediationType + command + parameters + } + } + } } } -}` +} +` } func (r *revertCommit) Vars() (map[string]interface{}, error) { diff --git a/pkg/platform/api/buildplanner/request/stagecommit.go b/pkg/platform/api/buildplanner/request/stagecommit.go index 16b5f9d30c..7eb6788fc1 100644 --- a/pkg/platform/api/buildplanner/request/stagecommit.go +++ b/pkg/platform/api/buildplanner/request/stagecommit.go @@ -159,12 +159,20 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri } } ... on Error { + __typename message } - ... on PlanningError { - message + ... on ErrorWithSubErrors { + __typename subErrors { __typename + buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } ... on GenericSolveError { path message @@ -189,11 +197,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri parameters } } - ... on TargetNotFound { - message - requestedTarget - possibleTargets - } } } } @@ -202,41 +205,41 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri __typename message } - ... on NotFound { - __typename - message - type - resource - mayNeedAuthentication - } - ... on ParseError { - __typename - message - path - } - ... on Forbidden { - __typename - operation - message - resource - } - ... on HeadOnBranchMoved { - __typename - commitId - branchId - message - } - ... on NoChangeSinceLastCommit { - __typename - commitId - message - } - ... on ValidationError { + ... on ErrorWithSubErrors { __typename subErrors { __typename - message buildExprPath + ... on RemediableError { + possibleRemediations { + description + suggestedPriority + } + } + ... on GenericSolveError { + path + message + isTransient + validationErrors { + error + jsonPath + } + } + ... on RemediableSolveError { + path + message + isTransient + errorType + validationErrors { + error + jsonPath + } + suggestedRemediations { + remediationType + command + parameters + } + } } } } diff --git a/pkg/platform/api/buildplanner/response/build.go b/pkg/platform/api/buildplanner/response/build.go index 063cdaf7c3..af823e811d 100644 --- a/pkg/platform/api/buildplanner/response/build.go +++ b/pkg/platform/api/buildplanner/response/build.go @@ -15,12 +15,12 @@ type ArtifactResponse struct { } type BuildResponse struct { - *Error - *PlanningError Type string `json:"__typename"` Artifacts []ArtifactResponse `json:"artifacts"` Status string `json:"status"` RawMessage json.RawMessage `json:"rawMessage"` + *Error + *ErrorWithSubErrors } func (b *BuildResponse) MarshalJSON() ([]byte, error) { diff --git a/pkg/platform/api/buildplanner/response/commit.go b/pkg/platform/api/buildplanner/response/commit.go index 0ae4103535..b8923ed37f 100644 --- a/pkg/platform/api/buildplanner/response/commit.go +++ b/pkg/platform/api/buildplanner/response/commit.go @@ -38,7 +38,7 @@ func processPlanningError(message string, subErrors []*BuildExprError) error { } } - if se.Type != types.RemediableSolveErrorType && se.Type != types.GenericSolveErrorType { + if se.Type != types.RemediableSolveErrorType && se.Type != types.GenericSolveErrorType && se.Type != types.RemediableError { continue } @@ -70,32 +70,6 @@ func ProcessProjectError(project *ProjectResponse, fallbackMessage string) error return errs.New(fallbackMessage) } -// PlanningError represents an error that occurred during planning. -type PlanningError struct { - SubErrors []*BuildExprError `json:"subErrors"` -} - -// BuildExprError represents a location in the build script where an error occurred. -type BuildExprError struct { - Type string `json:"__typename"` - BuildExprPath string `json:"buildExprPath"` - Message string `json:"message"` - IsTransient bool `json:"isTransient"` - ValidationErrors []*SolverErrorValidationError `json:"validationErrors"` - *TargetNotFound - *RemediableSolveError -} - -type TargetNotFound struct { - Type string `json:"__typename"` - RequestedTarget string `json:"requestedTarget"` - PossibleTargets []string `json:"possibleTargets"` -} - -type ValidationError struct { - SubErrors []*BuildExprError `json:"subErrors"` -} - // Commit contains the build and any errors. type Commit struct { Type string `json:"__typename"` @@ -105,9 +79,5 @@ type Commit struct { ParentID strfmt.UUID `json:"parentId"` Build *BuildResponse `json:"build"` *Error - *ParseError - *ValidationError - *ForbiddenError - *HeadOnBranchMovedError - *NoChangeSinceLastCommitError + *ErrorWithSubErrors } diff --git a/pkg/platform/api/buildplanner/response/commiterror.go b/pkg/platform/api/buildplanner/response/commiterror.go index f517f1fba4..02d50dc558 100644 --- a/pkg/platform/api/buildplanner/response/commiterror.go +++ b/pkg/platform/api/buildplanner/response/commiterror.go @@ -6,7 +6,6 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - "github.com/go-openapi/strfmt" ) type CommitError struct { @@ -29,7 +28,7 @@ func ProcessCommitError(commit *Commit, fallbackMessage string) error { case types.ParseErrorType: return &CommitError{ commit.Type, commit.Message, - locale.NewInputError("err_buildplanner_parse_error", "The platform failed to parse the build expression. Received message: {{.V0}}. Path: {{.V1}}", commit.Message, commit.ParseError.Path), + locale.NewInputError("err_buildplanner_parse_error", "The platform failed to parse the build expression. Received message: {{.V0}}.", commit.Message), } case types.ValidationErrorType: var subErrorMessages []string @@ -49,7 +48,7 @@ func ProcessCommitError(commit *Commit, fallbackMessage string) error { case types.ForbiddenErrorType: return &CommitError{ commit.Type, commit.Message, - locale.NewInputError("err_buildplanner_forbidden", "Operation forbidden: {{.V0}}. Received message: {{.V1}}", commit.Operation, commit.Message), + locale.NewInputError("err_buildplanner_forbidden", "Operation forbidden, Received message: {{.V1}}", commit.Message), } case types.HeadOnBranchMovedErrorType: return errs.Wrap(&CommitError{ @@ -93,29 +92,3 @@ func ProcessMergedCommitError(mcErr *MergedCommit, fallbackMessage string) error } return errs.New(fallbackMessage) } - -// HeadOnBranchMovedError represents an error that occurred because the head on -// a remote branch has moved. -type HeadOnBranchMovedError struct { - HeadBranchID strfmt.UUID `json:"branchId"` -} - -// NoChangeSinceLastCommitError represents an error that occurred because there -// were no changes since the last commit. -type NoChangeSinceLastCommitError struct { - NoChangeCommitID strfmt.UUID `json:"commitId"` -} - -// MergeConflictError represents an error that occurred because of a merge conflict. -type MergeConflictError struct { - CommonAncestorID strfmt.UUID `json:"commonAncestorId"` - ConflictPaths []string `json:"conflictPaths"` -} - -// MergeError represents two different errors in the BuildPlanner's graphQL -// schema with the same fields. Those errors being: FastForwardError and -// NoCommonBaseFound. Inspect the Type field to determine which error it is. -type MergeError struct { - TargetVCSRef strfmt.UUID `json:"targetVcsRef"` - OtherVCSRef strfmt.UUID `json:"otherVcsRef"` -} diff --git a/pkg/platform/api/buildplanner/response/createproject.go b/pkg/platform/api/buildplanner/response/createproject.go index 1db41851a9..9531aa2efc 100644 --- a/pkg/platform/api/buildplanner/response/createproject.go +++ b/pkg/platform/api/buildplanner/response/createproject.go @@ -8,9 +8,6 @@ type ProjectCreated struct { Type string `json:"__typename"` Commit *Commit `json:"commit"` *Error - *NotFoundError - *ParseError - *ForbiddenError } type ProjectCreatedError struct { diff --git a/pkg/platform/api/buildplanner/response/mergecommit.go b/pkg/platform/api/buildplanner/response/mergecommit.go index aec9a7de68..1eb73019a0 100644 --- a/pkg/platform/api/buildplanner/response/mergecommit.go +++ b/pkg/platform/api/buildplanner/response/mergecommit.go @@ -7,11 +7,5 @@ type MergedCommit struct { Type string `json:"__typename"` Commit *Commit `json:"commit"` *Error - *MergeConflictError - *MergeError - *NotFoundError - *ParseError - *ForbiddenError - *HeadOnBranchMovedError - *NoChangeSinceLastCommitError + *ErrorWithSubErrors } diff --git a/pkg/platform/api/buildplanner/response/pushcommit.go b/pkg/platform/api/buildplanner/response/pushcommit.go deleted file mode 100644 index 4be1dd71a7..0000000000 --- a/pkg/platform/api/buildplanner/response/pushcommit.go +++ /dev/null @@ -1,10 +0,0 @@ -package response - -// PushCommitResult is the result of a push commit mutation. -// It contains the resulting commit from the operation and any errors. -// The resulting commit is pushed to the platform automatically. -type PushCommitResult struct { - Type string `json:"__typename"` - Commit *Commit `json:"pushCommit"` - *Error -} diff --git a/pkg/platform/api/buildplanner/response/revertcommit.go b/pkg/platform/api/buildplanner/response/revertcommit.go index 10237ad224..b624a7b85f 100644 --- a/pkg/platform/api/buildplanner/response/revertcommit.go +++ b/pkg/platform/api/buildplanner/response/revertcommit.go @@ -10,4 +10,5 @@ type RevertedCommit struct { CommonAncestor strfmt.UUID `json:"commonAncestorID"` ConflictPaths []string `json:"conflictPaths"` *Error + *ErrorWithSubErrors } diff --git a/pkg/platform/api/buildplanner/response/shared.go b/pkg/platform/api/buildplanner/response/shared.go index 627bfcb949..3b54830c32 100644 --- a/pkg/platform/api/buildplanner/response/shared.go +++ b/pkg/platform/api/buildplanner/response/shared.go @@ -93,25 +93,24 @@ func IsErrorResponse(errorType string) bool { errorType == types.ComitHasNoParentErrorType } -// NotFoundError represents an error that occurred because a resource was not found. -type NotFoundError struct { - Type string `json:"type"` - Resource string `json:"resource"` - MayNeedAuthentication bool `json:"mayNeedAuthentication"` -} - -// ParseError is an error that occurred while parsing the build expression. -type ParseError struct { - Path string `json:"path"` +// Error contains an error message. +type Error struct { + Message string `json:"message"` } -type ForbiddenError struct { - Operation string `json:"operation"` +type ErrorWithSubErrors struct { + SubErrors []*BuildExprError `json:"subErrors"` } -// Error contains an error message. -type Error struct { - Message string `json:"message"` +// BuildExprError represents a location in the build script where an error occurred. +type BuildExprError struct { + Type string `json:"__typename"` + Message string `json:"message"` + BuildExprPath []string `json:"buildExprPath"` + *TargetNotFoundError + *RemediableError + *GenericSolveError + *RemediableSolveError } type TargetNotFoundError struct { diff --git a/pkg/platform/api/buildplanner/response/solveerror.go b/pkg/platform/api/buildplanner/response/solveerror.go index 79bb58253d..09543a9a53 100644 --- a/pkg/platform/api/buildplanner/response/solveerror.go +++ b/pkg/platform/api/buildplanner/response/solveerror.go @@ -1,8 +1,16 @@ package response -import ( - "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" -) +import "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" + +// RemediableSolveError represents a solver error that can be remediated. +type RemediableError struct { + Remediations []*SolverErrorRemediation `json:"possibleRemediations"` +} + +type GenericSolveError struct { + IsTransient bool `json:"isTransient"` + ValidationErrors []*SolverErrorValidationError `json:"validationErrors"` +} // SolverErrorValidationError represents a validation error that occurred during planning. type SolverErrorValidationError struct { @@ -10,18 +18,17 @@ type SolverErrorValidationError struct { Error string `json:"error"` } -// RemediableSolveError represents a solver error that can be remediated. -type RemediableSolveError struct { - ErrorType string `json:"errorType"` - Remediations []*SolverErrorRemediation `json:"suggestedRemediations"` - Requirements []*types.Requirement `json:"requirements"` - Incompatibilities []*SolveErrorIncompatibility `json:"incompatibilities"` -} - // SolverErrorRemediation contains the recommeneded remediation for remediable error. type SolverErrorRemediation struct { - RemediationType string `json:"solveErrorRemediationType"` - Command string `json:"command"` + Description string `json:"description"` + SuggestedPriority string `json:"suggestedPriority"` +} + +type RemediableSolveError struct { + ErrorType string `json:"errorType"` + Incompatibilities []*SolveErrorIncompatibility `json:"incompatibilities"` + Requirements []*types.Requirement `json:"requirements"` + SuggestedRemediations []*SolverErrorRemediation `json:"suggestedRemediations"` } // SolverErrorIncompatibility represents a solver incompatibility error. @@ -33,12 +40,20 @@ type SolveErrorIncompatibility struct { // SolveErrorPackageIncompatibility represents a package incompatibility error. type SolveErrorPackageIncompatibility struct { + Type string `json:"type"` Feature string `json:"feature"` Namespace string `json:"namespace"` } // SolveErrorPlatformIncompatibility represents a platform incompatibility error. type SolveErrorPlatformIncompatibility struct { + Type string `json:"type"` PlatformID string `json:"platformID"` PlatformKernel string `json:"platformKernel"` } + +const ( + SolveErrorIncompatibilityTypeDependency = "DEPENDENCY" + SolveErrorIncompatibilityTypePlatform = "PLATFORM" + SolveErrorIncompatibilityTypeRequirement = "REQUIREMENT" +) diff --git a/pkg/platform/api/buildplanner/types/errors.go b/pkg/platform/api/buildplanner/types/errors.go index e3f234d30a..e4dd6d894a 100644 --- a/pkg/platform/api/buildplanner/types/errors.go +++ b/pkg/platform/api/buildplanner/types/errors.go @@ -10,6 +10,7 @@ const ( HeadOnBranchMovedErrorType = "HeadOnBranchMoved" ForbiddenErrorType = "Forbidden" GenericSolveErrorType = "GenericSolveError" + RemediableError = "RemediableError" RemediableSolveErrorType = "RemediableSolveError" PlanningErrorType = "PlanningError" MergeConflictType = "MergeConflict" diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index 551bdb9e06..45b7313dd2 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -202,19 +202,19 @@ func (p *PublishInput) Files() []gqlclient.File { func (p *PublishInput) Query() string { return ` - mutation ($input: PublishInput!) { - publish(input: $input) { - ... on CreatedIngredientVersionRevision { - ingredientID - ingredientVersionID - revision - } - ... on Error{ - __typename - error: message - } - } - } +mutation ($input: PublishInput!) { + publish(input: $input) { + ... on CreatedIngredientVersionRevision { + ingredientID + ingredientVersionID + revision + } + ... on Error { + __typename + error: message + } + } +} ` } From 2ccc89d0db20a9a3d2109d3d92f9379c51b7e122 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 18 Nov 2024 15:55:25 -0800 Subject: [PATCH 390/440] Simplify sub errors --- .../api/buildplanner/request/commit.go | 13 ---------- .../api/buildplanner/request/createproject.go | 13 ---------- .../api/buildplanner/request/evaluate.go | 13 ---------- .../api/buildplanner/request/mergecommit.go | 13 ---------- .../api/buildplanner/request/revertcommit.go | 13 ---------- .../api/buildplanner/request/stagecommit.go | 26 ------------------- 6 files changed, 91 deletions(-) diff --git a/pkg/platform/api/buildplanner/request/commit.go b/pkg/platform/api/buildplanner/request/commit.go index d479ac79de..052ab021c8 100644 --- a/pkg/platform/api/buildplanner/request/commit.go +++ b/pkg/platform/api/buildplanner/request/commit.go @@ -163,14 +163,7 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -179,7 +172,6 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S } } ... on RemediableSolveError { - path message isTransient errorType @@ -187,11 +179,6 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } diff --git a/pkg/platform/api/buildplanner/request/createproject.go b/pkg/platform/api/buildplanner/request/createproject.go index f24959d2a8..84f978346b 100644 --- a/pkg/platform/api/buildplanner/request/createproject.go +++ b/pkg/platform/api/buildplanner/request/createproject.go @@ -37,14 +37,7 @@ mutation ($organization: String!, $project: String!, $private: Boolean!, $expr: subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -53,7 +46,6 @@ mutation ($organization: String!, $project: String!, $private: Boolean!, $expr: } } ... on RemediableSolveError { - path message isTransient errorType @@ -61,11 +53,6 @@ mutation ($organization: String!, $project: String!, $private: Boolean!, $expr: error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } diff --git a/pkg/platform/api/buildplanner/request/evaluate.go b/pkg/platform/api/buildplanner/request/evaluate.go index 6ddbd26876..e9a4f40a08 100644 --- a/pkg/platform/api/buildplanner/request/evaluate.go +++ b/pkg/platform/api/buildplanner/request/evaluate.go @@ -32,14 +32,7 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -48,7 +41,6 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target } } ... on RemediableSolveError { - path message isTransient errorType @@ -56,11 +48,6 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } diff --git a/pkg/platform/api/buildplanner/request/mergecommit.go b/pkg/platform/api/buildplanner/request/mergecommit.go index f9423fae36..4f360b71f1 100644 --- a/pkg/platform/api/buildplanner/request/mergecommit.go +++ b/pkg/platform/api/buildplanner/request/mergecommit.go @@ -39,14 +39,7 @@ mutation ($organization: String!, $project: String!, $targetRef: String!, $other subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -55,7 +48,6 @@ mutation ($organization: String!, $project: String!, $targetRef: String!, $other } } ... on RemediableSolveError { - path message isTransient errorType @@ -63,11 +55,6 @@ mutation ($organization: String!, $project: String!, $targetRef: String!, $other error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } diff --git a/pkg/platform/api/buildplanner/request/revertcommit.go b/pkg/platform/api/buildplanner/request/revertcommit.go index 5cbf609ba2..30aff4dc07 100644 --- a/pkg/platform/api/buildplanner/request/revertcommit.go +++ b/pkg/platform/api/buildplanner/request/revertcommit.go @@ -44,14 +44,7 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -60,7 +53,6 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target } } ... on RemediableSolveError { - path message isTransient errorType @@ -68,11 +60,6 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } diff --git a/pkg/platform/api/buildplanner/request/stagecommit.go b/pkg/platform/api/buildplanner/request/stagecommit.go index 7eb6788fc1..e58a218966 100644 --- a/pkg/platform/api/buildplanner/request/stagecommit.go +++ b/pkg/platform/api/buildplanner/request/stagecommit.go @@ -167,14 +167,7 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -183,7 +176,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri } } ... on RemediableSolveError { - path message isTransient errorType @@ -191,11 +183,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } @@ -210,14 +197,7 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri subErrors { __typename buildExprPath - ... on RemediableError { - possibleRemediations { - description - suggestedPriority - } - } ... on GenericSolveError { - path message isTransient validationErrors { @@ -226,7 +206,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri } } ... on RemediableSolveError { - path message isTransient errorType @@ -234,11 +213,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri error jsonPath } - suggestedRemediations { - remediationType - command - parameters - } } } } From 759d1a9e077415ebd22d289ff42e342fff9aef75 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Mon, 18 Nov 2024 16:14:36 -0800 Subject: [PATCH 391/440] More error cleanup --- .../api/buildplanner/request/commit.go | 1 - .../api/buildplanner/request/createproject.go | 1 - .../api/buildplanner/request/evaluate.go | 1 - .../api/buildplanner/request/mergecommit.go | 1 - .../api/buildplanner/request/revertcommit.go | 1 - .../api/buildplanner/request/stagecommit.go | 2 - .../api/buildplanner/response/shared.go | 7 +-- .../api/buildplanner/response/solveerror.go | 47 ------------------- 8 files changed, 2 insertions(+), 59 deletions(-) diff --git a/pkg/platform/api/buildplanner/request/commit.go b/pkg/platform/api/buildplanner/request/commit.go index 052ab021c8..23893de175 100644 --- a/pkg/platform/api/buildplanner/request/commit.go +++ b/pkg/platform/api/buildplanner/request/commit.go @@ -162,7 +162,6 @@ query ($commitID: String!, $organization: String!, $project: String!, $target: S __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient diff --git a/pkg/platform/api/buildplanner/request/createproject.go b/pkg/platform/api/buildplanner/request/createproject.go index 84f978346b..bd9fb6cc03 100644 --- a/pkg/platform/api/buildplanner/request/createproject.go +++ b/pkg/platform/api/buildplanner/request/createproject.go @@ -36,7 +36,6 @@ mutation ($organization: String!, $project: String!, $private: Boolean!, $expr: __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient diff --git a/pkg/platform/api/buildplanner/request/evaluate.go b/pkg/platform/api/buildplanner/request/evaluate.go index e9a4f40a08..1c1b3be910 100644 --- a/pkg/platform/api/buildplanner/request/evaluate.go +++ b/pkg/platform/api/buildplanner/request/evaluate.go @@ -31,7 +31,6 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient diff --git a/pkg/platform/api/buildplanner/request/mergecommit.go b/pkg/platform/api/buildplanner/request/mergecommit.go index 4f360b71f1..ed10ee4893 100644 --- a/pkg/platform/api/buildplanner/request/mergecommit.go +++ b/pkg/platform/api/buildplanner/request/mergecommit.go @@ -38,7 +38,6 @@ mutation ($organization: String!, $project: String!, $targetRef: String!, $other __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient diff --git a/pkg/platform/api/buildplanner/request/revertcommit.go b/pkg/platform/api/buildplanner/request/revertcommit.go index 30aff4dc07..9c88b8e399 100644 --- a/pkg/platform/api/buildplanner/request/revertcommit.go +++ b/pkg/platform/api/buildplanner/request/revertcommit.go @@ -43,7 +43,6 @@ mutation ($organization: String!, $project: String!, $commitId: String!, $target __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient diff --git a/pkg/platform/api/buildplanner/request/stagecommit.go b/pkg/platform/api/buildplanner/request/stagecommit.go index e58a218966..b1ec1166e6 100644 --- a/pkg/platform/api/buildplanner/request/stagecommit.go +++ b/pkg/platform/api/buildplanner/request/stagecommit.go @@ -166,7 +166,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient @@ -196,7 +195,6 @@ mutation ($organization: String!, $project: String!, $parentCommit: ID!, $descri __typename subErrors { __typename - buildExprPath ... on GenericSolveError { message isTransient diff --git a/pkg/platform/api/buildplanner/response/shared.go b/pkg/platform/api/buildplanner/response/shared.go index 3b54830c32..b714d06b4a 100644 --- a/pkg/platform/api/buildplanner/response/shared.go +++ b/pkg/platform/api/buildplanner/response/shared.go @@ -104,13 +104,10 @@ type ErrorWithSubErrors struct { // BuildExprError represents a location in the build script where an error occurred. type BuildExprError struct { - Type string `json:"__typename"` - Message string `json:"message"` - BuildExprPath []string `json:"buildExprPath"` + Type string `json:"__typename"` + Message string `json:"message"` *TargetNotFoundError - *RemediableError *GenericSolveError - *RemediableSolveError } type TargetNotFoundError struct { diff --git a/pkg/platform/api/buildplanner/response/solveerror.go b/pkg/platform/api/buildplanner/response/solveerror.go index 09543a9a53..d662d8d1fa 100644 --- a/pkg/platform/api/buildplanner/response/solveerror.go +++ b/pkg/platform/api/buildplanner/response/solveerror.go @@ -1,12 +1,5 @@ package response -import "github.com/ActiveState/cli/pkg/platform/api/buildplanner/types" - -// RemediableSolveError represents a solver error that can be remediated. -type RemediableError struct { - Remediations []*SolverErrorRemediation `json:"possibleRemediations"` -} - type GenericSolveError struct { IsTransient bool `json:"isTransient"` ValidationErrors []*SolverErrorValidationError `json:"validationErrors"` @@ -17,43 +10,3 @@ type SolverErrorValidationError struct { JSONPath string `json:"jsonPath"` Error string `json:"error"` } - -// SolverErrorRemediation contains the recommeneded remediation for remediable error. -type SolverErrorRemediation struct { - Description string `json:"description"` - SuggestedPriority string `json:"suggestedPriority"` -} - -type RemediableSolveError struct { - ErrorType string `json:"errorType"` - Incompatibilities []*SolveErrorIncompatibility `json:"incompatibilities"` - Requirements []*types.Requirement `json:"requirements"` - SuggestedRemediations []*SolverErrorRemediation `json:"suggestedRemediations"` -} - -// SolverErrorIncompatibility represents a solver incompatibility error. -type SolveErrorIncompatibility struct { - Type string `json:"type"` - *SolveErrorPackageIncompatibility - *SolveErrorPlatformIncompatibility -} - -// SolveErrorPackageIncompatibility represents a package incompatibility error. -type SolveErrorPackageIncompatibility struct { - Type string `json:"type"` - Feature string `json:"feature"` - Namespace string `json:"namespace"` -} - -// SolveErrorPlatformIncompatibility represents a platform incompatibility error. -type SolveErrorPlatformIncompatibility struct { - Type string `json:"type"` - PlatformID string `json:"platformID"` - PlatformKernel string `json:"platformKernel"` -} - -const ( - SolveErrorIncompatibilityTypeDependency = "DEPENDENCY" - SolveErrorIncompatibilityTypePlatform = "PLATFORM" - SolveErrorIncompatibilityTypeRequirement = "REQUIREMENT" -) From d33a4d07fd54a36f5c83c136496db308482ec57a Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 18 Nov 2024 17:24:47 -0500 Subject: [PATCH 392/440] Handle recursive links in runtime sources. --- internal/smartlink/smartlink.go | 30 ++++++++-- internal/smartlink/smartlink_lin_mac.go | 6 -- internal/smartlink/smartlink_test.go | 75 +++++++++++++++++++++++++ pkg/runtime/depot.go | 4 +- pkg/runtime/links_windows.go | 2 +- 5 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 internal/smartlink/smartlink_test.go diff --git a/internal/smartlink/smartlink.go b/internal/smartlink/smartlink.go index feb5134fa5..49fbe2d01d 100644 --- a/internal/smartlink/smartlink.go +++ b/internal/smartlink/smartlink.go @@ -10,7 +10,7 @@ import ( ) // LinkContents will link the contents of src to desc -func LinkContents(src, dest string) error { +func LinkContents(src, dest string, visited map[string]bool) error { if !fileutils.DirExists(src) { return errs.New("src dir does not exist: %s", src) } @@ -24,12 +24,23 @@ func LinkContents(src, dest string) error { return errs.Wrap(err, "Could not resolve src and dest paths") } + if visited == nil { + visited = make(map[string]bool) + } + if _, exists := visited[src]; exists { + // We've encountered a recursive link. This is most often the case when the resolved src has + // already been visited. In that case, just link the dest to the src (which may be a directory; + // this is fine). + return linkFile(src, dest) + } + visited[src] = true + entries, err := os.ReadDir(src) if err != nil { return errs.Wrap(err, "Reading dir %s failed", src) } for _, entry := range entries { - if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name())); err != nil { + if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil { return errs.Wrap(err, "Link failed") } } @@ -39,13 +50,24 @@ func LinkContents(src, dest string) error { // Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure), // which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks. -func Link(src, dest string) error { +func Link(src, dest string, visited map[string]bool) error { var err error src, dest, err = resolvePaths(src, dest) if err != nil { return errs.Wrap(err, "Could not resolve src and dest paths") } + if visited == nil { + visited = make(map[string]bool) + } + if _, exists := visited[src]; exists { + // We've encountered a recursive link. This is most often the case when the resolved src has + // already been visited. In that case, just link the dest to the src (which may be a directory; + // this is fine). + return linkFile(src, dest) + } + visited[src] = true + if fileutils.IsDir(src) { if err := fileutils.Mkdir(dest); err != nil { return errs.Wrap(err, "could not create directory %s", dest) @@ -55,7 +77,7 @@ func Link(src, dest string) error { return errs.Wrap(err, "could not read directory %s", src) } for _, entry := range entries { - if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name())); err != nil { + if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil { return errs.Wrap(err, "sub link failed") } } diff --git a/internal/smartlink/smartlink_lin_mac.go b/internal/smartlink/smartlink_lin_mac.go index b19f45d411..edc065f280 100644 --- a/internal/smartlink/smartlink_lin_mac.go +++ b/internal/smartlink/smartlink_lin_mac.go @@ -5,16 +5,10 @@ package smartlink import ( "os" - - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/fileutils" ) // file will create a symlink from src to dest, and falls back on a hardlink if no symlink is available. // This is a workaround for the fact that Windows does not support symlinks without admin privileges. func linkFile(src, dest string) error { - if fileutils.IsDir(src) { - return errs.New("src is a directory, not a file: %s", src) - } return os.Symlink(src, dest) } diff --git a/internal/smartlink/smartlink_test.go b/internal/smartlink/smartlink_test.go new file mode 100644 index 0000000000..5b52fbb0d3 --- /dev/null +++ b/internal/smartlink/smartlink_test.go @@ -0,0 +1,75 @@ +package smartlink + +import ( + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/ActiveState/cli/internal/fileutils" + "github.com/stretchr/testify/require" +) + +func TestLinkContentsWithCircularLink(t *testing.T) { + srcDir, err := os.MkdirTemp("", "src") + require.NoError(t, err) + defer os.RemoveAll(srcDir) + + destDir, err := os.MkdirTemp("", "dest") + require.NoError(t, err) + defer os.RemoveAll(destDir) + + // Create test file structure: + // src/ + // ├── regular.txt + // └── subdir/ + // ├── circle -> subdir (circular link) + // └── subfile.txt + + testFile := filepath.Join(srcDir, "regular.txt") + err = os.WriteFile(testFile, []byte("test content"), 0644) + require.NoError(t, err) + + subDir := filepath.Join(srcDir, "subdir") + err = os.Mkdir(subDir, 0755) + require.NoError(t, err) + + subFile := filepath.Join(subDir, "subfile.txt") + err = os.WriteFile(subFile, []byte("sub content"), 0644) + require.NoError(t, err) + + circularLink := filepath.Join(subDir, "circle") + err = os.Symlink(subDir, circularLink) + require.NoError(t, err) + + err = LinkContents(srcDir, destDir, nil) + if runtime.GOOS == "windows" { + require.Error(t, err) + return // hard links to directories are not allowed on Windows + } + require.NoError(t, err) + + // Verify file structure. + destFile := filepath.Join(destDir, "regular.txt") + require.FileExists(t, destFile) + content, err := os.ReadFile(destFile) + require.NoError(t, err) + require.Equal(t, "test content", string(content)) + + destSubFile := filepath.Join(destDir, "subdir", "subfile.txt") + require.FileExists(t, destSubFile) + subContent, err := os.ReadFile(destSubFile) + require.NoError(t, err) + require.Equal(t, "sub content", string(subContent)) + + destCircular := filepath.Join(destDir, "subdir", "circle") + require.FileExists(t, destCircular) + target, err := fileutils.ResolveUniquePath(destCircular) + require.NoError(t, err) + srcCircular := filepath.Join(srcDir, "subdir") + if runtime.GOOS == "darwin" { + srcCircular, err = fileutils.ResolveUniquePath(srcCircular) // needed for full $TMPDIR resolution + require.NoError(t, err) + } + require.Equal(t, target, srcCircular) +} diff --git a/pkg/runtime/depot.go b/pkg/runtime/depot.go index 610418e952..c53a1ac1e6 100644 --- a/pkg/runtime/depot.go +++ b/pkg/runtime/depot.go @@ -177,7 +177,7 @@ func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string) } // Copy or link the artifact files, depending on whether the artifact in question relies on file transformations - if err := smartlink.LinkContents(absoluteSrc, absoluteDest); err != nil { + if err := smartlink.LinkContents(absoluteSrc, absoluteDest, nil); err != nil { return errs.Wrap(err, "failed to link artifact") } @@ -295,7 +295,7 @@ func (d *depot) Undeploy(id strfmt.UUID, relativeSrc, path string) error { for sharedFile, relinkSrc := range redeploys { switch deploy.Type { case deploymentTypeLink: - if err := smartlink.Link(relinkSrc, sharedFile); err != nil { + if err := smartlink.Link(relinkSrc, sharedFile, nil); err != nil { return errs.Wrap(err, "failed to relink file") } case deploymentTypeCopy: diff --git a/pkg/runtime/links_windows.go b/pkg/runtime/links_windows.go index 0bdbbe79e8..907882a66f 100644 --- a/pkg/runtime/links_windows.go +++ b/pkg/runtime/links_windows.go @@ -43,7 +43,7 @@ func supportsHardLinks(path string) (supported bool) { } logging.Debug("Attempting to link '%s' to '%s'", lnk, target) - err = smartlink.Link(target, lnk) + err = smartlink.Link(target, lnk, nil) if err != nil { logging.Debug("Test link creation failed: %v", err) return false From 84b42c223a47666c79fd3d1996e9bdabd53710fa Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 19 Nov 2024 13:28:04 -0800 Subject: [PATCH 393/440] Add date recognition to messages --- cmd/state-svc/internal/messages/messages.go | 44 +- .../internal/messages/messages_test.go | 132 ++- .../internal/server/generated/generated.go | 876 ++++++++++++++---- cmd/state-svc/schema/schema.graphqls | 2 + internal/graph/generated.go | 2 + test/integration/msg_int_test.go | 108 ++- 6 files changed, 963 insertions(+), 201 deletions(-) diff --git a/cmd/state-svc/internal/messages/messages.go b/cmd/state-svc/internal/messages/messages.go index 1495a8a1b2..1717053654 100644 --- a/cmd/state-svc/internal/messages/messages.go +++ b/cmd/state-svc/internal/messages/messages.go @@ -44,7 +44,7 @@ func New(cfg *config.Instance, auth *auth.Auth) (*Messages, error) { return nil, errs.Wrap(err, "Could not parse state version") } - poll := poller.New(1*time.Hour, func() (interface{}, error) { + poll := poller.New(10*time.Minute, func() (interface{}, error) { defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() @@ -112,11 +112,39 @@ func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, return msgs, nil } +func messageInDateRange(message *graph.MessageInfo, baseTime time.Time) (bool, error) { + logging.Debug("Checking message %s in date range with base time %s", message.ID, baseTime.Format(time.RFC3339)) + if message.StartDate != "" { + startDate, err := time.Parse(time.RFC3339, message.StartDate) + if err != nil { + return false, errs.Wrap(err, "Could not parse start date for message %s", message.ID) + } + if baseTime.Before(startDate) { + logging.Debug("Skipping message %s as it is before start date %s", message.ID, startDate.Format(time.RFC3339)) + return false, nil + } + } + + if message.EndDate != "" { + endDate, err := time.Parse(time.RFC3339, message.EndDate) + if err != nil { + return false, errs.Wrap(err, "Could not parse end date for message %s", message.ID) + } + if baseTime.After(endDate) { + logging.Debug("Skipping message %s as it is after end date %s", message.ID, endDate.Format(time.RFC3339)) + return false, nil + } + } + + return true, nil +} + func check(params *ConditionParams, messages []*graph.MessageInfo, lastReportMap map[string]interface{}, baseTime time.Time) ([]*graph.MessageInfo, error) { funcMap := conditionFuncMap() filteredMessages := []*graph.MessageInfo{} for _, message := range messages { logging.Debug("Checking message %s", message.ID) + // Ensure we don't show the same message too often if lastReport, ok := lastReportMap[message.ID]; ok { lr, ok := lastReport.(string) @@ -140,11 +168,23 @@ func check(params *ConditionParams, messages []*graph.MessageInfo, lastReportMap } } + // Check if message is within date range + inRange, err := messageInDateRange(message, baseTime) + if err != nil { + logging.Warning("Could not check if message %s is in date range: %v", message.ID, err) + continue + } + if !inRange { + logging.Debug("Skipping message %s as it is outside of its date range", message.ID) + continue + } + // Validate the conditional if message.Condition != "" { result, err := strutils.ParseTemplate(fmt.Sprintf(`{{%s}}`, message.Condition), params, funcMap) if err != nil { - return nil, errs.Wrap(err, "Could not parse condition template for message %s", message.ID) + logging.Warning("Could not parse condition template for message %s: %v", message.ID, err) + continue } if result == "true" { logging.Debug("Including message %s as condition %s evaluated to %s", message.ID, message.Condition, result) diff --git a/cmd/state-svc/internal/messages/messages_test.go b/cmd/state-svc/internal/messages/messages_test.go index ffaf24f92a..186bc6719e 100644 --- a/cmd/state-svc/internal/messages/messages_test.go +++ b/cmd/state-svc/internal/messages/messages_test.go @@ -11,6 +11,7 @@ import ( ) func Test_check(t *testing.T) { + baseTime := time.Now() type args struct { params *ConditionParams messages []*graph.MessageInfo @@ -31,7 +32,7 @@ func Test_check(t *testing.T) { {ID: "A"}, {ID: "B"}, {ID: "C"}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A", "B", "C"}, false, @@ -48,7 +49,7 @@ func Test_check(t *testing.T) { {ID: "C", Condition: `eq .Command "foobar"`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, @@ -64,7 +65,7 @@ func Test_check(t *testing.T) { {ID: "B", Condition: `contains .UserEmail "fred"`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A"}, false, @@ -80,7 +81,7 @@ func Test_check(t *testing.T) { {ID: "B", Condition: `hasPrefix .UserEmail "org"`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A"}, false, @@ -96,7 +97,7 @@ func Test_check(t *testing.T) { {ID: "B", Condition: `hasSuffix .UserEmail "org"`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, @@ -112,7 +113,7 @@ func Test_check(t *testing.T) { {ID: "B", Condition: `regexMatch .UserEmail "^doe.org$"`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A"}, false, @@ -128,7 +129,7 @@ func Test_check(t *testing.T) { {ID: "B", Condition: `regexMatch .UserEmail ".*("`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A"}, false, @@ -150,7 +151,7 @@ func Test_check(t *testing.T) { {ID: "H", Condition: `eq .StateVersion.Build "foo"`}, }, lastReportMap: map[string]interface{}{}, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A", "B", "C", "D"}, false, @@ -165,10 +166,10 @@ func Test_check(t *testing.T) { {ID: "C", Repeat: graph.MessageRepeatTypeDisabled}, }, lastReportMap: map[string]interface{}{ - "A": time.Now(), - "C": time.Now(), + "A": baseTime.Format(time.RFC3339), + "C": baseTime.Format(time.RFC3339), }, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, @@ -183,10 +184,10 @@ func Test_check(t *testing.T) { {ID: "C", Repeat: graph.MessageRepeatTypeConstantly}, }, lastReportMap: map[string]interface{}{ - "A": time.Now(), - "C": time.Now().Add(-time.Hour * 24 * 30), + "A": baseTime.Format(time.RFC3339), + "C": baseTime.Add(-time.Hour * 24 * 30).Format(time.RFC3339), }, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"A", "B", "C"}, false, @@ -201,11 +202,11 @@ func Test_check(t *testing.T) { {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ - "A": time.Now(), - "B": time.Now().Add(-time.Hour), - "C": time.Now(), + "A": baseTime.Format(time.RFC3339), + "B": baseTime.Add(-time.Hour).Format(time.RFC3339), + "C": baseTime.Format(time.RFC3339), }, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, @@ -220,11 +221,11 @@ func Test_check(t *testing.T) { {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ - "A": time.Now(), - "B": time.Now().Add(-time.Hour * 24), - "C": time.Now(), + "A": baseTime.Format(time.RFC3339), + "B": baseTime.Add(-time.Hour * 24).Format(time.RFC3339), + "C": baseTime.Format(time.RFC3339), }, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, @@ -239,11 +240,11 @@ func Test_check(t *testing.T) { {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ - "A": time.Now(), - "B": time.Now().Add(-time.Hour * 24 * 7), - "C": time.Now(), + "A": baseTime.Format(time.RFC3339), + "B": baseTime.Add(-time.Hour * 24 * 7).Format(time.RFC3339), + "C": baseTime.Format(time.RFC3339), }, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, @@ -258,15 +259,86 @@ func Test_check(t *testing.T) { {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ - "A": time.Now(), - "B": time.Now().Add(-time.Hour * 24 * 7 * 30), - "C": time.Now(), + "A": baseTime.Format(time.RFC3339), + "B": baseTime.Add(-time.Hour * 24 * 7 * 30).Format(time.RFC3339), + "C": baseTime.Format(time.RFC3339), }, - baseTime: time.Now(), + baseTime: baseTime, }, []string{"B"}, false, }, + { + "Date Range - Within Range", + args{ + params: &ConditionParams{}, + messages: []*graph.MessageInfo{ + {ID: "A", StartDate: baseTime.Add(-24 * time.Hour).Format(time.RFC3339), EndDate: baseTime.Add(24 * time.Hour).Format(time.RFC3339)}, + {ID: "B", StartDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339), EndDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, + {ID: "C", StartDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339), EndDate: baseTime.Add(24 * time.Hour).Format(time.RFC3339)}, + }, + lastReportMap: map[string]interface{}{}, + baseTime: baseTime, + }, + []string{"A", "B"}, + false, + }, + { + "Date Range - No Dates Specified", + args{ + params: &ConditionParams{}, + messages: []*graph.MessageInfo{ + {ID: "A"}, + {ID: "B", StartDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339)}, + {ID: "C", EndDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, + }, + lastReportMap: map[string]interface{}{}, + baseTime: baseTime, + }, + []string{"A", "B", "C"}, + false, + }, + { + "Date Range - Invalid Date Format", + args{ + params: &ConditionParams{}, + messages: []*graph.MessageInfo{ + {ID: "A", StartDate: "invalid-date"}, + }, + lastReportMap: map[string]interface{}{}, + baseTime: baseTime, + }, + []string{}, + true, + }, + { + "Date Range - Only Start Date", + args{ + params: &ConditionParams{}, + messages: []*graph.MessageInfo{ + {ID: "A", StartDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339)}, + {ID: "B", StartDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, + }, + lastReportMap: map[string]interface{}{}, + baseTime: baseTime, + }, + []string{"A"}, + false, + }, + { + "Date Range - Only End Date", + args{ + params: &ConditionParams{}, + messages: []*graph.MessageInfo{ + {ID: "A", EndDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, + {ID: "B", EndDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339)}, + }, + lastReportMap: map[string]interface{}{}, + baseTime: baseTime, + }, + []string{"A"}, + false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index 16617b4d7b..cfb794c9ee 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -81,11 +81,13 @@ type ComplexityRoot struct { MessageInfo struct { Condition func(childComplexity int) int + EndDate func(childComplexity int) int ID func(childComplexity int) int Interrupt func(childComplexity int) int Message func(childComplexity int) int Placement func(childComplexity int) int Repeat func(childComplexity int) int + StartDate func(childComplexity int) int } Mutation struct { @@ -288,6 +290,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.MessageInfo.Condition(childComplexity), true + case "MessageInfo.endDate": + if e.complexity.MessageInfo.EndDate == nil { + break + } + + return e.complexity.MessageInfo.EndDate(childComplexity), true + case "MessageInfo.id": if e.complexity.MessageInfo.ID == nil { break @@ -323,6 +332,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.MessageInfo.Repeat(childComplexity), true + case "MessageInfo.startDate": + if e.complexity.MessageInfo.StartDate == nil { + break + } + + return e.complexity.MessageInfo.StartDate(childComplexity), true + case "Mutation.setCache": if e.complexity.Mutation.SetCache == nil { break @@ -739,6 +755,8 @@ type MessageInfo { id: String! message: String! condition: String! + startDate: String! + endDate: String! repeat: MessageRepeatType! interrupt: MessageInterruptType! placement: MessagePlacementType! @@ -812,290 +830,710 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) func (ec *executionContext) field_Mutation_setCache_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["key"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Mutation_setCache_argsKey(ctx, rawArgs) + if err != nil { + return nil, err } args["key"] = arg0 - var arg1 string - if tmp, ok := rawArgs["value"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) - arg1, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Mutation_setCache_argsValue(ctx, rawArgs) + if err != nil { + return nil, err } args["value"] = arg1 - var arg2 int - if tmp, ok := rawArgs["expiry"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expiry")) - arg2, err = ec.unmarshalNInt2int(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Mutation_setCache_argsExpiry(ctx, rawArgs) + if err != nil { + return nil, err } args["expiry"] = arg2 return args, nil } +func (ec *executionContext) field_Mutation_setCache_argsKey( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["key"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + if tmp, ok := rawArgs["key"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_setCache_argsValue( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["value"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) + if tmp, ok := rawArgs["value"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_setCache_argsExpiry( + ctx context.Context, + rawArgs map[string]interface{}, +) (int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["expiry"] + if !ok { + var zeroVal int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("expiry")) + if tmp, ok := rawArgs["expiry"]; ok { + return ec.unmarshalNInt2int(ctx, tmp) + } + + var zeroVal int + return zeroVal, nil +} func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query___type_argsName(ctx, rawArgs) + if err != nil { + return nil, err } args["name"] = arg0 return args, nil } +func (ec *executionContext) field_Query___type_argsName( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["name"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + if tmp, ok := rawArgs["name"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_analyticsEvent_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["category"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("category")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_analyticsEvent_argsCategory(ctx, rawArgs) + if err != nil { + return nil, err } args["category"] = arg0 - var arg1 string - if tmp, ok := rawArgs["action"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("action")) - arg1, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_analyticsEvent_argsAction(ctx, rawArgs) + if err != nil { + return nil, err } args["action"] = arg1 - var arg2 string - if tmp, ok := rawArgs["source"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) - arg2, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_analyticsEvent_argsSource(ctx, rawArgs) + if err != nil { + return nil, err } args["source"] = arg2 - var arg3 *string - if tmp, ok := rawArgs["label"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("label")) - arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) - if err != nil { - return nil, err - } + arg3, err := ec.field_Query_analyticsEvent_argsLabel(ctx, rawArgs) + if err != nil { + return nil, err } args["label"] = arg3 - var arg4 string - if tmp, ok := rawArgs["dimensionsJson"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) - arg4, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg4, err := ec.field_Query_analyticsEvent_argsDimensionsJSON(ctx, rawArgs) + if err != nil { + return nil, err } args["dimensionsJson"] = arg4 return args, nil } +func (ec *executionContext) field_Query_analyticsEvent_argsCategory( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["category"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("category")) + if tmp, ok := rawArgs["category"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_analyticsEvent_argsAction( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["action"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("action")) + if tmp, ok := rawArgs["action"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_analyticsEvent_argsSource( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["source"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) + if tmp, ok := rawArgs["source"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_analyticsEvent_argsLabel( + ctx context.Context, + rawArgs map[string]interface{}, +) (*string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["label"] + if !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("label")) + if tmp, ok := rawArgs["label"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_analyticsEvent_argsDimensionsJSON( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["dimensionsJson"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) + if tmp, ok := rawArgs["dimensionsJson"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_availableUpdate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["desiredChannel"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredChannel")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_availableUpdate_argsDesiredChannel(ctx, rawArgs) + if err != nil { + return nil, err } args["desiredChannel"] = arg0 - var arg1 string - if tmp, ok := rawArgs["desiredVersion"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredVersion")) - arg1, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_availableUpdate_argsDesiredVersion(ctx, rawArgs) + if err != nil { + return nil, err } args["desiredVersion"] = arg1 return args, nil } +func (ec *executionContext) field_Query_availableUpdate_argsDesiredChannel( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["desiredChannel"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredChannel")) + if tmp, ok := rawArgs["desiredChannel"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_availableUpdate_argsDesiredVersion( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["desiredVersion"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredVersion")) + if tmp, ok := rawArgs["desiredVersion"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_checkMessages_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["command"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("command")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_checkMessages_argsCommand(ctx, rawArgs) + if err != nil { + return nil, err } args["command"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["flags"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("flags")) - arg1, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_checkMessages_argsFlags(ctx, rawArgs) + if err != nil { + return nil, err } args["flags"] = arg1 return args, nil } +func (ec *executionContext) field_Query_checkMessages_argsCommand( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["command"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("command")) + if tmp, ok := rawArgs["command"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_checkMessages_argsFlags( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["flags"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("flags")) + if tmp, ok := rawArgs["flags"]; ok { + return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} func (ec *executionContext) field_Query_configChanged_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["key"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_configChanged_argsKey(ctx, rawArgs) + if err != nil { + return nil, err } args["key"] = arg0 return args, nil } +func (ec *executionContext) field_Query_configChanged_argsKey( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["key"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + if tmp, ok := rawArgs["key"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_getCache_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["key"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_getCache_argsKey(ctx, rawArgs) + if err != nil { + return nil, err } args["key"] = arg0 return args, nil } +func (ec *executionContext) field_Query_getCache_argsKey( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["key"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + if tmp, ok := rawArgs["key"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_getProcessesInUse_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["execDir"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("execDir")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_getProcessesInUse_argsExecDir(ctx, rawArgs) + if err != nil { + return nil, err } args["execDir"] = arg0 return args, nil } +func (ec *executionContext) field_Query_getProcessesInUse_argsExecDir( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["execDir"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("execDir")) + if tmp, ok := rawArgs["execDir"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_hashGlobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["wd"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("wd")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_hashGlobs_argsWd(ctx, rawArgs) + if err != nil { + return nil, err } args["wd"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["globs"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("globs")) - arg1, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_hashGlobs_argsGlobs(ctx, rawArgs) + if err != nil { + return nil, err } args["globs"] = arg1 return args, nil } +func (ec *executionContext) field_Query_hashGlobs_argsWd( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["wd"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("wd")) + if tmp, ok := rawArgs["wd"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_hashGlobs_argsGlobs( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["globs"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("globs")) + if tmp, ok := rawArgs["globs"]; ok { + return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} func (ec *executionContext) field_Query_reportRuntimeUsage_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 int - if tmp, ok := rawArgs["pid"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pid")) - arg0, err = ec.unmarshalNInt2int(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_reportRuntimeUsage_argsPid(ctx, rawArgs) + if err != nil { + return nil, err } args["pid"] = arg0 - var arg1 string - if tmp, ok := rawArgs["exec"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("exec")) - arg1, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_reportRuntimeUsage_argsExec(ctx, rawArgs) + if err != nil { + return nil, err } args["exec"] = arg1 - var arg2 string - if tmp, ok := rawArgs["source"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) - arg2, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_reportRuntimeUsage_argsSource(ctx, rawArgs) + if err != nil { + return nil, err } args["source"] = arg2 - var arg3 string - if tmp, ok := rawArgs["dimensionsJson"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) - arg3, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg3, err := ec.field_Query_reportRuntimeUsage_argsDimensionsJSON(ctx, rawArgs) + if err != nil { + return nil, err } args["dimensionsJson"] = arg3 return args, nil } +func (ec *executionContext) field_Query_reportRuntimeUsage_argsPid( + ctx context.Context, + rawArgs map[string]interface{}, +) (int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["pid"] + if !ok { + var zeroVal int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("pid")) + if tmp, ok := rawArgs["pid"]; ok { + return ec.unmarshalNInt2int(ctx, tmp) + } + + var zeroVal int + return zeroVal, nil +} + +func (ec *executionContext) field_Query_reportRuntimeUsage_argsExec( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["exec"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("exec")) + if tmp, ok := rawArgs["exec"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_reportRuntimeUsage_argsSource( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["source"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) + if tmp, ok := rawArgs["source"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_reportRuntimeUsage_argsDimensionsJSON( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["dimensionsJson"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) + if tmp, ok := rawArgs["dimensionsJson"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field___Type_enumValues_argsIncludeDeprecated(ctx, rawArgs) + if err != nil { + return nil, err } args["includeDeprecated"] = arg0 return args, nil } +func (ec *executionContext) field___Type_enumValues_argsIncludeDeprecated( + ctx context.Context, + rawArgs map[string]interface{}, +) (bool, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["includeDeprecated"] + if !ok { + var zeroVal bool + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + if tmp, ok := rawArgs["includeDeprecated"]; ok { + return ec.unmarshalOBoolean2bool(ctx, tmp) + } + + var zeroVal bool + return zeroVal, nil +} func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field___Type_fields_argsIncludeDeprecated(ctx, rawArgs) + if err != nil { + return nil, err } args["includeDeprecated"] = arg0 return args, nil } +func (ec *executionContext) field___Type_fields_argsIncludeDeprecated( + ctx context.Context, + rawArgs map[string]interface{}, +) (bool, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["includeDeprecated"] + if !ok { + var zeroVal bool + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + if tmp, ok := rawArgs["includeDeprecated"]; ok { + return ec.unmarshalOBoolean2bool(ctx, tmp) + } + + var zeroVal bool + return zeroVal, nil +} // endregion ***************************** args.gotpl ***************************** @@ -1871,6 +2309,94 @@ func (ec *executionContext) fieldContext_MessageInfo_condition(_ context.Context return fc, nil } +func (ec *executionContext) _MessageInfo_startDate(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MessageInfo_startDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.StartDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MessageInfo_startDate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MessageInfo", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _MessageInfo_endDate(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MessageInfo_endDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EndDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MessageInfo_endDate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MessageInfo", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _MessageInfo_repeat(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { fc, err := ec.fieldContext_MessageInfo_repeat(ctx, field) if err != nil { @@ -2635,6 +3161,10 @@ func (ec *executionContext) fieldContext_Query_checkMessages(ctx context.Context return ec.fieldContext_MessageInfo_message(ctx, field) case "condition": return ec.fieldContext_MessageInfo_condition(ctx, field) + case "startDate": + return ec.fieldContext_MessageInfo_startDate(ctx, field) + case "endDate": + return ec.fieldContext_MessageInfo_endDate(ctx, field) case "repeat": return ec.fieldContext_MessageInfo_repeat(ctx, field) case "interrupt": @@ -5695,6 +6225,16 @@ func (ec *executionContext) _MessageInfo(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } + case "startDate": + out.Values[i] = ec._MessageInfo_startDate(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "endDate": + out.Values[i] = ec._MessageInfo_endDate(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "repeat": out.Values[i] = ec._MessageInfo_repeat(ctx, field, obj) if out.Values[i] == graphql.Null { diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index 2d0cb8b23d..9f71c26909 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -55,6 +55,8 @@ type MessageInfo { id: String! message: String! condition: String! + startDate: String! + endDate: String! repeat: MessageRepeatType! interrupt: MessageInterruptType! placement: MessagePlacementType! diff --git a/internal/graph/generated.go b/internal/graph/generated.go index a9c307c88d..61f7178059 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -44,6 +44,8 @@ type MessageInfo struct { ID string `json:"id"` Message string `json:"message"` Condition string `json:"condition"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` Repeat MessageRepeatType `json:"repeat"` Interrupt MessageInterruptType `json:"interrupt"` Placement MessagePlacementType `json:"placement"` diff --git a/test/integration/msg_int_test.go b/test/integration/msg_int_test.go index 0224d3ad3d..08a983cd63 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/msg_int_test.go @@ -36,6 +36,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { Name string MessageJson string ExpectRepeat bool + ExpectShown bool }{ { "Defaults", @@ -44,6 +45,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Message": "This is a [NOTICE]simple[/RESET] message" }]`, false, + true, }, { "Repeat Hourly", @@ -53,6 +55,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Repeat": "Hourly" }]`, false, + true, }, { "Repeat Constantly", @@ -62,6 +65,103 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Repeat": "Constantly" }]`, true, + true, + }, + { + "Within Date Range", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "StartDate": "%s", + "EndDate": "%s" + }]`, + time.Now().Add(-1*time.Hour).Format(time.RFC3339), + time.Now().Add(1*time.Hour).Format(time.RFC3339)), + false, + true, + }, + { + "Outside Date Range", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "StartDate": "%s", + "EndDate": "%s" + }]`, + time.Now().Add(1*time.Hour).Format(time.RFC3339), + time.Now().Add(2*time.Hour).Format(time.RFC3339)), + false, + false, + }, + { + "Only Start Date - Valid", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "StartDate": "%s" + }]`, + time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + false, + true, + }, + { + "Only End Date - Valid", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "EndDate": "%s" + }]`, + time.Now().Add(1*time.Hour).Format(time.RFC3339)), + false, + true, + }, + { + "Outside Date Range - Future", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "StartDate": "%s", + "EndDate": "%s" + }]`, + time.Now().Add(1*time.Hour).Format(time.RFC3339), + time.Now().Add(2*time.Hour).Format(time.RFC3339)), + false, + false, + }, + { + "Outside Date Range - Past", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "StartDate": "%s", + "EndDate": "%s" + }]`, + time.Now().Add(-2*time.Hour).Format(time.RFC3339), + time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + false, + false, + }, + { + "Only Start Date - Invalid", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "StartDate": "%s" + }]`, + time.Now().Add(1*time.Hour).Format(time.RFC3339)), + false, + false, + }, + { + "Only End Date - Invalid", + fmt.Sprintf(`[{ + "ID": "simple", + "Message": "This is a [NOTICE]simple[/RESET] message", + "EndDate": "%s" + }]`, + time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + false, + false, }, } for _, tt := range tests { @@ -73,7 +173,13 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { suite.Require().NoError(err) cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) - cp.Expect(`This is a simple message`) + + if !tt.ExpectShown { + suite.Require().NotContains(cp.Output(), "This is a simple message", "Message should not appear when outside date range") + } else { + cp.Expect(`This is a simple message`) + } + cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) From 975776b3d4b34030d30a8541b3add6d377c2e521 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 19 Nov 2024 13:56:59 -0800 Subject: [PATCH 394/440] Remove debug logs --- cmd/state-svc/internal/messages/messages.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/state-svc/internal/messages/messages.go b/cmd/state-svc/internal/messages/messages.go index 1717053654..37581d067b 100644 --- a/cmd/state-svc/internal/messages/messages.go +++ b/cmd/state-svc/internal/messages/messages.go @@ -113,14 +113,12 @@ func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, } func messageInDateRange(message *graph.MessageInfo, baseTime time.Time) (bool, error) { - logging.Debug("Checking message %s in date range with base time %s", message.ID, baseTime.Format(time.RFC3339)) if message.StartDate != "" { startDate, err := time.Parse(time.RFC3339, message.StartDate) if err != nil { return false, errs.Wrap(err, "Could not parse start date for message %s", message.ID) } if baseTime.Before(startDate) { - logging.Debug("Skipping message %s as it is before start date %s", message.ID, startDate.Format(time.RFC3339)) return false, nil } } @@ -131,7 +129,6 @@ func messageInDateRange(message *graph.MessageInfo, baseTime time.Time) (bool, e return false, errs.Wrap(err, "Could not parse end date for message %s", message.ID) } if baseTime.After(endDate) { - logging.Debug("Skipping message %s as it is after end date %s", message.ID, endDate.Format(time.RFC3339)) return false, nil } } From 1412ea34cbf62860cbc21b6c5c8782da1bdd5b6b Mon Sep 17 00:00:00 2001 From: mdrakos Date: Tue, 19 Nov 2024 13:57:07 -0800 Subject: [PATCH 395/440] Rename tests --- test/integration/msg_int_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/msg_int_test.go b/test/integration/msg_int_test.go index 08a983cd63..fbf16e3ab9 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/msg_int_test.go @@ -94,7 +94,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { false, }, { - "Only Start Date - Valid", + "Only Start Date - Inside Range", fmt.Sprintf(`[{ "ID": "simple", "Message": "This is a [NOTICE]simple[/RESET] message", @@ -105,7 +105,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { true, }, { - "Only End Date - Valid", + "Only End Date - Inside Range", fmt.Sprintf(`[{ "ID": "simple", "Message": "This is a [NOTICE]simple[/RESET] message", @@ -142,7 +142,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { false, }, { - "Only Start Date - Invalid", + "Only Start Date - Outside Range", fmt.Sprintf(`[{ "ID": "simple", "Message": "This is a [NOTICE]simple[/RESET] message", @@ -153,7 +153,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { false, }, { - "Only End Date - Invalid", + "Only End Date - Outside Range", fmt.Sprintf(`[{ "ID": "simple", "Message": "This is a [NOTICE]simple[/RESET] message", From 574ff6b99f2eeca354a04f2b2b3ab3a2a16ec556 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 19 Nov 2024 17:55:59 -0500 Subject: [PATCH 396/440] Added "--portable" flag to `state checkout` for copying runtime files instead of linking them. Also make `state deploy` runtimes portable by default. --- cmd/state/internal/cmdtree/checkout.go | 5 +++++ internal/assets/contents/activestate.yaml.cache.tpl | 1 + internal/runbits/checkout/checkout.go | 7 ++++--- internal/runbits/runtime/runtime.go | 3 +++ internal/runners/activate/activate.go | 2 +- internal/runners/checkout/checkout.go | 3 ++- internal/runners/deploy/deploy.go | 2 +- pkg/project/project.go | 2 ++ pkg/projectfile/projectfile.go | 9 ++++++--- pkg/runtime/options.go | 4 ++++ pkg/runtime/setup.go | 3 ++- 11 files changed, 31 insertions(+), 10 deletions(-) diff --git a/cmd/state/internal/cmdtree/checkout.go b/cmd/state/internal/cmdtree/checkout.go index e83fc6ee1c..0e1c91ab23 100644 --- a/cmd/state/internal/cmdtree/checkout.go +++ b/cmd/state/internal/cmdtree/checkout.go @@ -26,6 +26,11 @@ func newCheckoutCommand(prime *primer.Values) *captain.Command { Description: locale.Tl("flag_state_checkout_runtime-path_description", "Path to store the runtime files"), Value: ¶ms.RuntimePath, }, + { + Name: "portable", + Description: locale.Tl("flag_state_checkout_portable_description", "Copy files to runtime-path instead of linking to them"), + Value: ¶ms.Portable, + }, { Name: "no-clone", Description: locale.Tl("flag_state_checkout_no_clone_description", "Do not clone the github repository associated with this project (if any)"), diff --git a/internal/assets/contents/activestate.yaml.cache.tpl b/internal/assets/contents/activestate.yaml.cache.tpl index fcf8468204..7adaa70bc0 100644 --- a/internal/assets/contents/activestate.yaml.cache.tpl +++ b/internal/assets/contents/activestate.yaml.cache.tpl @@ -1,2 +1,3 @@ # It is recommended that you do not commit this file as it contains configuration that is specific to your machine cache: {{ .Cache }} +portable: {{ .Portable }} diff --git a/internal/runbits/checkout/checkout.go b/internal/runbits/checkout/checkout.go index 8e25d88b4e..fccb4ec9a8 100644 --- a/internal/runbits/checkout/checkout.go +++ b/internal/runbits/checkout/checkout.go @@ -52,7 +52,7 @@ func New(repo git.Repository, prime primeable) *Checkout { return &Checkout{repo, prime} } -func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone, bareCheckout bool) (_ string, rerr error) { +func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone, bareCheckout, portable bool) (_ string, rerr error) { defer r.rationalizeError(&rerr) path, err := r.pathToUse(ns, targetPath) @@ -94,7 +94,7 @@ func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath return "", errNoCommitID } - if err := CreateProjectFiles(path, cachePath, owner, proj, branchName, commitID.String(), language); err != nil { + if err := CreateProjectFiles(path, cachePath, owner, proj, branchName, commitID.String(), language, portable); err != nil { return "", errs.Wrap(err, "Could not create project files") } @@ -182,7 +182,7 @@ func (r *Checkout) fetchProject( return owner, proj, commitID, branchName, language, pj.RepoURL, nil } -func CreateProjectFiles(checkoutPath, cachePath, owner, name, branch, commitID, language string) error { +func CreateProjectFiles(checkoutPath, cachePath, owner, name, branch, commitID, language string, portable bool) error { configFile := filepath.Join(checkoutPath, constants.ConfigFileName) if !fileutils.FileExists(configFile) { _, err := projectfile.Create(&projectfile.CreateParams{ @@ -192,6 +192,7 @@ func CreateProjectFiles(checkoutPath, cachePath, owner, name, branch, commitID, Directory: checkoutPath, Language: language, Cache: cachePath, + Portable: portable, }) if err != nil { if osutils.IsAccessDeniedError(err) { diff --git a/internal/runbits/runtime/runtime.go b/internal/runbits/runtime/runtime.go index 83b995ff75..88305f47a6 100644 --- a/internal/runbits/runtime/runtime.go +++ b/internal/runbits/runtime/runtime.go @@ -271,6 +271,9 @@ func Update( u.RawQuery = q.Encode() rtOpts = append(rtOpts, runtime.WithBuildProgressUrl(u.String())) } + if proj.IsPortable() { + rtOpts = append(rtOpts, runtime.WithPortable()) + } if err := rt.Update(buildPlan, rtHash, rtOpts...); err != nil { return nil, locale.WrapError(err, "err_packages_update_runtime_install") diff --git a/internal/runners/activate/activate.go b/internal/runners/activate/activate.go index 1cf3c2f389..1edb95cb37 100644 --- a/internal/runners/activate/activate.go +++ b/internal/runners/activate/activate.go @@ -95,7 +95,7 @@ func (r *Activate) Run(params *ActivateParams) (rerr error) { } // Perform fresh checkout - pathToUse, err := r.activateCheckout.Run(params.Namespace, params.Branch, "", params.PreferredPath, false, false) + pathToUse, err := r.activateCheckout.Run(params.Namespace, params.Branch, "", params.PreferredPath, false, false, false) if err != nil { return locale.WrapError(err, "err_activate_pathtouse", "Could not figure out what path to use.") } diff --git a/internal/runners/checkout/checkout.go b/internal/runners/checkout/checkout.go index ea2105031e..5d4025d4d9 100644 --- a/internal/runners/checkout/checkout.go +++ b/internal/runners/checkout/checkout.go @@ -38,6 +38,7 @@ type Params struct { RuntimePath string NoClone bool Force bool + Portable bool } type primeable interface { @@ -121,7 +122,7 @@ func (u *Checkout) Run(params *Params) (rerr error) { u.out.Notice(locale.Tr("checking_out", ns.String())) - projectDir, err := u.checkout.Run(ns, params.Branch, params.RuntimePath, params.PreferredPath, params.NoClone, archive != nil) + projectDir, err := u.checkout.Run(ns, params.Branch, params.RuntimePath, params.PreferredPath, params.NoClone, archive != nil, params.Portable) if err != nil { return errs.Wrap(err, "Checkout failed") } diff --git a/internal/runners/deploy/deploy.go b/internal/runners/deploy/deploy.go index b69ac6587b..472c8ecffd 100644 --- a/internal/runners/deploy/deploy.go +++ b/internal/runners/deploy/deploy.go @@ -168,7 +168,7 @@ func (d *Deploy) install(params *Params, commitID strfmt.UUID) (rerr error) { if err := checkout.CreateProjectFiles( params.Path, params.Path, params.Namespace.Owner, params.Namespace.Project, - constants.DefaultBranchName, commitID.String(), "", + constants.DefaultBranchName, commitID.String(), "", true, ); err != nil { return errs.Wrap(err, "Could not create project files") } diff --git a/pkg/project/project.go b/pkg/project/project.go index 22c3d46c60..06ea03ccbe 100644 --- a/pkg/project/project.go +++ b/pkg/project/project.go @@ -255,6 +255,8 @@ func (p *Project) Lock() string { return p.projectfile.Lock } // Cache returns the cache information for this project func (p *Project) Cache() string { return p.projectfile.Cache } +func (p *Project) IsPortable() bool { return p.projectfile.Portable && p.projectfile.Cache != "" } + // Namespace returns project namespace func (p *Project) Namespace() *Namespaced { return &Namespaced{Owner: p.projectfile.Owner(), Project: p.projectfile.Name()} diff --git a/pkg/projectfile/projectfile.go b/pkg/projectfile/projectfile.go index 9761cbb17b..e3ebcc5c62 100644 --- a/pkg/projectfile/projectfile.go +++ b/pkg/projectfile/projectfile.go @@ -108,6 +108,7 @@ type Project struct { Jobs Jobs `yaml:"jobs,omitempty"` Private bool `yaml:"private,omitempty"` Cache string `yaml:"cache,omitempty"` + Portable bool `yaml:"portable,omitempty"` path string // "private" parsedURL projectURL // parsed url data parsedChannel string @@ -920,6 +921,7 @@ type CreateParams struct { path string ProjectURL string Cache string + Portable bool } // Create will create a new activestate.yaml with a projectURL for the given details @@ -1019,7 +1021,7 @@ func createCustom(params *CreateParams, lang language.Language) (*Project, error } if params.Cache != "" { - createErr := createHostFile(params.Directory, params.Cache) + createErr := createHostFile(params.Directory, params.Cache, params.Portable) if createErr != nil { return nil, errs.Wrap(createErr, "Could not create cache file") } @@ -1028,14 +1030,15 @@ func createCustom(params *CreateParams, lang language.Language) (*Project, error return Parse(params.path) } -func createHostFile(filePath, cachePath string) error { +func createHostFile(filePath, cachePath string, portable bool) error { user, err := user.Current() if err != nil { return errs.Wrap(err, "Could not get current user") } data := map[string]interface{}{ - "Cache": cachePath, + "Cache": cachePath, + "Portable": portable, } tplName := "activestate.yaml.cache.tpl" diff --git a/pkg/runtime/options.go b/pkg/runtime/options.go index 19492243cf..d5d57ceb49 100644 --- a/pkg/runtime/options.go +++ b/pkg/runtime/options.go @@ -21,6 +21,10 @@ func WithPreferredLibcVersion(version string) SetOpt { return func(opts *Opts) { opts.PreferredLibcVersion = version } } +func WithPortable() SetOpt { + return func(opts *Opts) { opts.Portable = true } +} + func WithArchive(dir string, platformID strfmt.UUID, ext string) SetOpt { return func(opts *Opts) { opts.FromArchive = &fromArchive{dir, platformID, ext} diff --git a/pkg/runtime/setup.go b/pkg/runtime/setup.go index d35ca25805..a1159454a2 100644 --- a/pkg/runtime/setup.go +++ b/pkg/runtime/setup.go @@ -47,6 +47,7 @@ type Opts struct { EventHandlers []events.HandlerFunc BuildlogFilePath string BuildProgressUrl string + Portable bool FromArchive *fromArchive @@ -467,7 +468,7 @@ func (s *setup) install(id strfmt.UUID) (rerr error) { return errs.Wrap(err, "Could not get env") } - if envDef.NeedsTransforms() || !s.supportsHardLinks { + if envDef.NeedsTransforms() || !s.supportsHardLinks || s.opts.Portable { if err := s.depot.DeployViaCopy(id, envDef.InstallDir, s.path); err != nil { return errs.Wrap(err, "Could not deploy artifact via copy") } From 7ba048cb3a57eb58c1f790dca1db81f58d9f3a8c Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 20 Nov 2024 13:48:18 -0800 Subject: [PATCH 397/440] Merge graphQL client code --- internal/gqlclient/gqlclient.go | 393 ----------------- internal/{gqlclient => graphql}/error.go | 2 +- internal/graphql/graphql.go | 395 ++++++++++++++++-- internal/graphql/graphql_json_test.go | 21 +- internal/graphql/graphql_multipart_test.go | 36 +- internal/graphql/graphql_shared_test.go | 43 ++ internal/graphql/graphql_test.go | 12 +- pkg/platform/api/graphql/graphql.go | 6 +- pkg/platform/api/graphql/request/publish.go | 8 +- .../api/hasura_inventory/inventory.go | 6 +- pkg/platform/api/mediator/mediator.go | 6 +- .../api/vulnerabilities/vulnerabilities.go | 6 +- pkg/platform/model/buildplanner/build.go | 3 +- .../model/buildplanner/buildplanner.go | 5 +- pkg/platform/model/svc.go | 9 +- 15 files changed, 470 insertions(+), 481 deletions(-) delete mode 100644 internal/gqlclient/gqlclient.go rename internal/{gqlclient => graphql}/error.go (95%) create mode 100644 internal/graphql/graphql_shared_test.go diff --git a/internal/gqlclient/gqlclient.go b/internal/gqlclient/gqlclient.go deleted file mode 100644 index d9e4c1ae60..0000000000 --- a/internal/gqlclient/gqlclient.go +++ /dev/null @@ -1,393 +0,0 @@ -package gqlclient - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "mime/multipart" - "net/http" - "os" - "strings" - "time" - - "github.com/ActiveState/cli/internal/constants" - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/graphql" - "github.com/ActiveState/cli/internal/logging" - "github.com/ActiveState/cli/internal/profile" - "github.com/ActiveState/cli/internal/singleton/uniqid" - "github.com/ActiveState/cli/internal/strutils" - "github.com/ActiveState/cli/pkg/platform/api" - "github.com/pkg/errors" -) - -type File struct { - Field string - Name string - R io.Reader -} - -type Request0 interface { - Query() string - Vars() map[string]interface{} -} - -type Request interface { - Query() string - Vars() (map[string]interface{}, error) -} - -type RequestWithFiles interface { - Request - Files() []File -} - -type Header map[string][]string - -type graphqlClient = graphql.Client - -// StandardizedErrors works around API's that don't follow the graphql standard -// It looks redundant because it needs to address two different API responses. -// https://activestatef.atlassian.net/browse/PB-4291 -type StandardizedErrors struct { - Message string - Error string - Errors []graphErr -} - -func (e StandardizedErrors) HasErrors() bool { - return len(e.Errors) > 0 || e.Error != "" -} - -// Values tells us all the relevant error messages returned. -// We don't include e.Error because it's an unhelpful generic error code redundant with the message. -func (e StandardizedErrors) Values() []string { - var errs []string - for _, err := range e.Errors { - errs = append(errs, err.Message) - } - if e.Message != "" { - errs = append(errs, e.Message) - } - return errs -} - -type graphResponse struct { - Data interface{} - Error string - Message string - Errors []graphErr -} - -type graphErr struct { - Message string -} - -func (e graphErr) Error() string { - return "graphql: " + e.Message -} - -type BearerTokenProvider interface { - BearerToken() string -} - -type Client struct { - *graphqlClient - url string - tokenProvider BearerTokenProvider - timeout time.Duration -} - -func NewWithOpts(url string, timeout time.Duration, opts ...graphql.ClientOption) *Client { - if timeout == 0 { - timeout = time.Second * 60 - } - - client := &Client{ - graphqlClient: graphql.NewClient(url, opts...), - timeout: timeout, - url: url, - } - if os.Getenv(constants.DebugServiceRequestsEnvVarName) == "true" { - client.EnableDebugLog() - } - return client -} - -func New(url string, timeout time.Duration) *Client { - return NewWithOpts(url, timeout, graphql.WithHTTPClient(api.NewHTTPClient())) -} - -// EnableDebugLog turns on debug logging -func (c *Client) EnableDebugLog() { - c.graphqlClient.Log = func(s string) { logging.Debug("graphqlClient log message: %s", s) } -} - -func (c *Client) SetTokenProvider(tokenProvider BearerTokenProvider) { - c.tokenProvider = tokenProvider -} - -func (c *Client) SetDebug(b bool) { - c.graphqlClient.Log = func(string) {} - if b { - c.graphqlClient.Log = func(s string) { - fmt.Fprintln(os.Stderr, s) - } - } -} - -func (c *Client) Run(request Request, response interface{}) error { - ctx := context.Background() - if c.timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, c.timeout) - defer cancel() - } - err := c.RunWithContext(ctx, request, response) - return err // Needs var so the cancel defer triggers at the right time -} - -type PostProcessor interface { - PostProcess() error -} - -func (c *Client) RunWithContext(ctx context.Context, request Request, response interface{}) (rerr error) { - defer func() { - if rerr != nil { - return - } - if postProcessor, ok := response.(PostProcessor); ok { - rerr = postProcessor.PostProcess() - } - }() - name := strutils.Summarize(request.Query(), 25) - defer profile.Measure(fmt.Sprintf("gqlclient:RunWithContext:(%s)", name), time.Now()) - - if fileRequest, ok := request.(RequestWithFiles); ok { - return c.runWithFiles(ctx, fileRequest, response) - } - - vars, err := request.Vars() - if err != nil { - return errs.Wrap(err, "Could not get variables") - } - - graphRequest := graphql.NewRequest(request.Query()) - for key, value := range vars { - graphRequest.Var(key, value) - } - - if fileRequest, ok := request.(RequestWithFiles); ok { - for _, file := range fileRequest.Files() { - graphRequest.File(file.Field, file.Name, file.R) - } - } - - var bearerToken string - if c.tokenProvider != nil { - bearerToken = c.tokenProvider.BearerToken() - if bearerToken != "" { - graphRequest.Header.Set("Authorization", "Bearer "+bearerToken) - } - } - - graphRequest.Header.Set("X-Requestor", uniqid.Text()) - - if err := c.graphqlClient.Run(ctx, graphRequest, &response); err != nil { - return NewRequestError(err, request) - } - - return nil -} - -type JsonRequest struct { - Query string `json:"query"` - Variables map[string]interface{} `json:"variables"` -} - -func (c *Client) runWithFiles(ctx context.Context, gqlReq RequestWithFiles, response interface{}) error { - // Construct the multi-part request. - bodyReader, bodyWriter := io.Pipe() - - req, err := http.NewRequest("POST", c.url, bodyReader) - if err != nil { - return errs.Wrap(err, "Could not create http request") - } - - req.Body = bodyReader - - mw := multipart.NewWriter(bodyWriter) - req.Header.Set("Content-Type", "multipart/form-data; boundary="+mw.Boundary()) - - vars, err := gqlReq.Vars() - if err != nil { - return errs.Wrap(err, "Could not get variables") - } - - varJson, err := json.Marshal(vars) - if err != nil { - return errs.Wrap(err, "Could not marshal vars") - } - - reqErrChan := make(chan error) - go func() { - defer bodyWriter.Close() - defer mw.Close() - defer close(reqErrChan) - - // Operations - operations, err := mw.CreateFormField("operations") - if err != nil { - reqErrChan <- errs.Wrap(err, "Could not create form field operations") - return - } - - jsonReq := JsonRequest{ - Query: gqlReq.Query(), - Variables: vars, - } - jsonReqV, err := json.Marshal(jsonReq) - if err != nil { - reqErrChan <- errs.Wrap(err, "Could not marshal json request") - return - } - if _, err := operations.Write(jsonReqV); err != nil { - reqErrChan <- errs.Wrap(err, "Could not write json request") - return - } - - // Map - if len(gqlReq.Files()) > 0 { - mapField, err := mw.CreateFormField("map") - if err != nil { - reqErrChan <- errs.Wrap(err, "Could not create form field map") - return - } - for n, f := range gqlReq.Files() { - if _, err := mapField.Write([]byte(fmt.Sprintf(`{"%d": ["%s"]}`, n, f.Field))); err != nil { - reqErrChan <- errs.Wrap(err, "Could not write map field") - return - } - } - // File upload - for n, file := range gqlReq.Files() { - part, err := mw.CreateFormFile(fmt.Sprintf("%d", n), file.Name) - if err != nil { - reqErrChan <- errs.Wrap(err, "Could not create form file") - return - } - - _, err = io.Copy(part, file.R) - if err != nil { - reqErrChan <- errs.Wrap(err, "Could not read file") - return - } - } - } - }() - - c.Log(fmt.Sprintf(">> query: %s", gqlReq.Query())) - c.Log(fmt.Sprintf(">> variables: %s", string(varJson))) - fnames := []string{} - for _, file := range gqlReq.Files() { - fnames = append(fnames, file.Name) - } - c.Log(fmt.Sprintf(">> files: %v", fnames)) - - // Run the request. - var bearerToken string - if c.tokenProvider != nil { - bearerToken = c.tokenProvider.BearerToken() - if bearerToken != "" { - req.Header.Set("Authorization", "Bearer "+bearerToken) - } - } - if os.Getenv(constants.DebugServiceRequestsEnvVarName) == "true" { - responseData, err := json.MarshalIndent(response, "", " ") - if err != nil { - return errs.Wrap(err, "failed to marshal response") - } - logging.Debug("gqlclient: response: %s", responseData) - } - - intermediateResp := make(map[string]interface{}) - gr := &graphResponse{ - Data: &intermediateResp, - } - req = req.WithContext(ctx) - c.Log(fmt.Sprintf(">> Raw Request: %s\n", req.URL.String())) - - var res *http.Response - resErrChan := make(chan error) - go func() { - var err error - res, err = http.DefaultClient.Do(req) - resErrChan <- err - }() - - // Due to the streaming uploads the request error can happen both before and after the http request itself, hence - // the creative select case you see before you. - wait := true - for wait { - select { - case err := <-reqErrChan: - if err != nil { - c.Log(fmt.Sprintf("Request Error: %s", err)) - return err - } - case err := <-resErrChan: - wait = false - if err != nil { - c.Log(fmt.Sprintf("Response Error: %s", err)) - return err - } - } - } - - if res == nil { - return errs.New("Received empty response") - } - - defer res.Body.Close() - var buf bytes.Buffer - if _, err := io.Copy(&buf, res.Body); err != nil { - c.Log(fmt.Sprintf("Read Error: %s", err)) - return errors.Wrap(err, "reading body") - } - resp := buf.Bytes() - c.Log(fmt.Sprintf("<< Response code: %d, body: %s\n", res.StatusCode, string(resp))) - - // Work around API's that don't follow the graphql standard - // https://activestatef.atlassian.net/browse/PB-4291 - standardizedErrors := StandardizedErrors{} - if err := json.Unmarshal(resp, &standardizedErrors); err != nil { - return errors.Wrap(err, "decoding error response") - } - if standardizedErrors.HasErrors() { - return errs.New(strings.Join(standardizedErrors.Values(), "\n")) - } - - if err := json.Unmarshal(resp, &gr); err != nil { - return errors.Wrap(err, "decoding response") - } - - // If the response is a single object, meaning we only have a single query in the request, we can unmarshal the - // response directly to the response type. Otherwise, we need to marshal the response as we normally would. - if len(intermediateResp) == 1 { - for _, val := range intermediateResp { - data, err := json.Marshal(val) - if err != nil { - return errors.Wrap(err, "remarshaling response") - } - return json.Unmarshal(data, response) - } - } - - data, err := json.Marshal(intermediateResp) - if err != nil { - return errors.Wrap(err, "remarshaling response") - } - return json.Unmarshal(data, response) -} diff --git a/internal/gqlclient/error.go b/internal/graphql/error.go similarity index 95% rename from internal/gqlclient/error.go rename to internal/graphql/error.go index f5c9e66a17..b8031b24c8 100644 --- a/internal/gqlclient/error.go +++ b/internal/graphql/error.go @@ -1,4 +1,4 @@ -package gqlclient +package graphql import "fmt" diff --git a/internal/graphql/graphql.go b/internal/graphql/graphql.go index 780a2c6d46..86278ae0bb 100644 --- a/internal/graphql/graphql.go +++ b/internal/graphql/graphql.go @@ -39,15 +39,84 @@ import ( "io" "mime/multipart" "net/http" + "os" + "strings" + "time" + "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/profile" + "github.com/ActiveState/cli/internal/singleton/uniqid" + "github.com/ActiveState/cli/internal/strutils" + "github.com/ActiveState/cli/pkg/platform/api" "github.com/pkg/errors" ) +type Request interface { + Query() string + Vars() (map[string]interface{}, error) +} + +type RequestWithFiles interface { + Request + Files() []File +} + +type RequestWithHeaders interface { + Request + Headers() map[string][]string +} + +// StandardizedErrors works around API's that don't follow the graphql standard +// It looks redundant because it needs to address two different API responses. +// https://activestatef.atlassian.net/browse/PB-4291 +type StandardizedErrors struct { + Message string + Error string + Errors []graphErr +} + +func (e StandardizedErrors) HasErrors() bool { + return len(e.Errors) > 0 || e.Error != "" +} + +// Values tells us all the relevant error messages returned. +// We don't include e.Error because it's an unhelpful generic error code redundant with the message. +func (e StandardizedErrors) Values() []string { + var errs []string + for _, err := range e.Errors { + errs = append(errs, err.Message) + } + if e.Message != "" { + errs = append(errs, e.Message) + } + return errs +} + +type graphErr struct { + Message string +} + +func (e graphErr) Error() string { + return "graphql: " + e.Message +} + +type BearerTokenProvider interface { + BearerToken() string +} + +type PostProcessor interface { + PostProcess() error +} + // Client is a client for interacting with a GraphQL API. type Client struct { - endpoint string + url string httpClient *http.Client useMultipartForm bool + tokenProvider BearerTokenProvider + timeout time.Duration // Log is called with various debug information. // To log to standard out, use: @@ -55,11 +124,29 @@ type Client struct { Log func(s string) } -// NewClient makes a new Client capable of making GraphQL requests. -func NewClient(endpoint string, opts ...ClientOption) *Client { +func NewWithOpts(url string, timeout time.Duration, opts ...ClientOption) *Client { + if timeout == 0 { + timeout = time.Second * 60 + } + + c := newClient(url, opts...) + if os.Getenv(constants.DebugServiceRequestsEnvVarName) == "true" { + c.EnableDebugLog() + } + c.timeout = timeout + + return c +} + +func New(url string, timeout time.Duration) *Client { + return NewWithOpts(url, timeout, WithHTTPClient(api.NewHTTPClient())) +} + +// newClient makes a new Client capable of making GraphQL requests. +func newClient(endpoint string, opts ...ClientOption) *Client { c := &Client{ - endpoint: endpoint, - Log: func(string) {}, + url: endpoint, + Log: func(string) {}, } for _, optionFunc := range opts { optionFunc(c) @@ -74,27 +161,90 @@ func (c *Client) logf(format string, args ...interface{}) { c.Log(fmt.Sprintf(format, args...)) } -// Run executes the query and unmarshals the response from the data field +func (c *Client) EnableDebugLog() { + c.Log = func(s string) { + logging.Debug("graphqlClient log message: %s", s) + } +} + +func (c *Client) SetTokenProvider(tokenProvider BearerTokenProvider) { + c.tokenProvider = tokenProvider +} + +func (c *Client) Run(request Request, response interface{}) error { + ctx := context.Background() + if c.timeout != 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, c.timeout) + defer cancel() + } + if err := c.RunWithContext(ctx, request, response); err != nil { + return NewRequestError(err, request) + } + return nil +} + +// RunWithContext executes the query and unmarshals the response from the data field // into the response object. // Pass in a nil response object to skip response parsing. // If the request fails or the server returns an error, the first error // will be returned. -func (c *Client) Run(ctx context.Context, req *Request, resp interface{}) error { +func (c *Client) RunWithContext(ctx context.Context, req Request, resp interface{}) (rerr error) { + defer func() { + if rerr != nil { + return + } + if postProcessor, ok := resp.(PostProcessor); ok { + rerr = postProcessor.PostProcess() + } + }() + name := strutils.Summarize(req.Query(), 25) + defer profile.Measure(fmt.Sprintf("gqlclient:RunWithContext:(%s)", name), time.Now()) + select { case <-ctx.Done(): return ctx.Err() default: } - if len(req.files) > 0 && !c.useMultipartForm { - return errors.New("cannot send files with PostFields option") + + gqlRequest := newRequest(req.Query()) + vars, err := req.Vars() + if err != nil { + return errs.Wrap(err, "Could not get vars") + } + gqlRequest.vars = vars + + var bearerToken string + if c.tokenProvider != nil { + bearerToken = c.tokenProvider.BearerToken() + if bearerToken != "" { + gqlRequest.Header.Set("Authorization", "Bearer "+bearerToken) + } + } + + gqlRequest.Header.Set("X-Requestor", uniqid.Text()) + + if header, ok := req.(RequestWithHeaders); ok { + for key, values := range header.Headers() { + for _, value := range values { + gqlRequest.Header.Set(key, value) + } + } } + + if fileRequest, ok := req.(RequestWithFiles); ok { + gqlRequest.files = fileRequest.Files() + return c.runWithFiles(ctx, gqlRequest, resp) + } + if c.useMultipartForm { - return c.runWithPostFields(ctx, req, resp) + return c.runWithPostFields(ctx, gqlRequest, resp) } - return c.runWithJSON(ctx, req, resp) + + return c.runWithJSON(ctx, gqlRequest, resp) } -func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{}) error { +func (c *Client) runWithJSON(ctx context.Context, req *GQLRequest, resp interface{}) error { var requestBody bytes.Buffer requestBodyObj := struct { Query string `json:"query"` @@ -113,7 +263,7 @@ func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{} gr := &graphResponse{ Data: &intermediateResp, } - r, err := http.NewRequest(http.MethodPost, c.endpoint, &requestBody) + r, err := http.NewRequest(http.MethodPost, c.url, &requestBody) if err != nil { return err } @@ -147,7 +297,7 @@ func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{} return c.marshalResponse(intermediateResp, resp) } -func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp interface{}) error { +func (c *Client) runWithPostFields(ctx context.Context, req *GQLRequest, resp interface{}) error { var requestBody bytes.Buffer writer := multipart.NewWriter(&requestBody) if err := writer.WriteField("query", req.q); err != nil { @@ -163,6 +313,7 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter return errors.Wrap(err, "encode variables") } } + for i := range req.files { part, err := writer.CreateFormFile(req.files[i].Field, req.files[i].Name) if err != nil { @@ -172,17 +323,19 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter return errors.Wrap(err, "preparing file") } } + if err := writer.Close(); err != nil { return errors.Wrap(err, "close writer") } + c.logf(">> variables: %s", variablesBuf.String()) - c.logf(">> files: %d", len(req.files)) c.logf(">> query: %s", req.q) + c.logf(">> files: %d", len(req.files)) intermediateResp := make(map[string]interface{}) gr := &graphResponse{ Data: &intermediateResp, } - r, err := http.NewRequest(http.MethodPost, c.endpoint, &requestBody) + r, err := http.NewRequest(http.MethodPost, c.url, &requestBody) if err != nil { return err } @@ -216,6 +369,192 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter return c.marshalResponse(intermediateResp, resp) } +type JsonRequest struct { + Query string `json:"query"` + Variables map[string]interface{} `json:"variables"` +} + +func (c *Client) runWithFiles(ctx context.Context, request *GQLRequest, response interface{}) error { + // Construct the multi-part request. + bodyReader, bodyWriter := io.Pipe() + + req, err := http.NewRequest("POST", c.url, bodyReader) + if err != nil { + return errs.Wrap(err, "Could not create http request") + } + + req.Body = bodyReader + + mw := multipart.NewWriter(bodyWriter) + req.Header.Set("Content-Type", "multipart/form-data; boundary="+mw.Boundary()) + + vars := request.vars + varJson, err := json.Marshal(vars) + if err != nil { + return errs.Wrap(err, "Could not marshal vars") + } + + reqErrChan := make(chan error) + go func() { + defer bodyWriter.Close() + defer mw.Close() + defer close(reqErrChan) + + // Operations + operations, err := mw.CreateFormField("operations") + if err != nil { + reqErrChan <- errs.Wrap(err, "Could not create form field operations") + return + } + + jsonReq := JsonRequest{ + Query: request.q, + Variables: vars, + } + jsonReqV, err := json.Marshal(jsonReq) + if err != nil { + reqErrChan <- errs.Wrap(err, "Could not marshal json request") + return + } + if _, err := operations.Write(jsonReqV); err != nil { + reqErrChan <- errs.Wrap(err, "Could not write json request") + return + } + + // Map + if len(request.files) > 0 { + mapField, err := mw.CreateFormField("map") + if err != nil { + reqErrChan <- errs.Wrap(err, "Could not create form field map") + return + } + for n, f := range request.files { + if _, err := mapField.Write([]byte(fmt.Sprintf(`{"%d": ["%s"]}`, n, f.Field))); err != nil { + reqErrChan <- errs.Wrap(err, "Could not write map field") + return + } + } + // File upload + for n, file := range request.files { + part, err := mw.CreateFormFile(fmt.Sprintf("%d", n), file.Name) + if err != nil { + reqErrChan <- errs.Wrap(err, "Could not create form file") + return + } + + _, err = io.Copy(part, file.R) + if err != nil { + reqErrChan <- errs.Wrap(err, "Could not read file") + return + } + } + } + }() + + c.Log(fmt.Sprintf(">> query: %s", request.q)) + c.Log(fmt.Sprintf(">> variables: %s", string(varJson))) + fnames := []string{} + for _, file := range request.files { + fnames = append(fnames, file.Name) + } + c.Log(fmt.Sprintf(">> files: %v", fnames)) + + // Run the request. + var bearerToken string + if c.tokenProvider != nil { + bearerToken = c.tokenProvider.BearerToken() + if bearerToken != "" { + req.Header.Set("Authorization", "Bearer "+bearerToken) + } + } + if os.Getenv(constants.DebugServiceRequestsEnvVarName) == "true" { + responseData, err := json.MarshalIndent(response, "", " ") + if err != nil { + return errs.Wrap(err, "failed to marshal response") + } + logging.Debug("gqlclient: response: %s", responseData) + } + + intermediateResp := make(map[string]interface{}) + gr := &graphResponse{ + Data: &intermediateResp, + } + req = req.WithContext(ctx) + c.Log(fmt.Sprintf(">> Raw Request: %s\n", req.URL.String())) + + var res *http.Response + resErrChan := make(chan error) + go func() { + var err error + res, err = http.DefaultClient.Do(req) + resErrChan <- err + }() + + // Due to the streaming uploads the request error can happen both before and after the http request itself, hence + // the creative select case you see before you. + wait := true + for wait { + select { + case err := <-reqErrChan: + if err != nil { + c.Log(fmt.Sprintf("Request Error: %s", err)) + return err + } + case err := <-resErrChan: + wait = false + if err != nil { + c.Log(fmt.Sprintf("Response Error: %s", err)) + return err + } + } + } + + if res == nil { + return errs.New("Received empty response") + } + + defer res.Body.Close() + var buf bytes.Buffer + if _, err := io.Copy(&buf, res.Body); err != nil { + c.Log(fmt.Sprintf("Read Error: %s", err)) + return errors.Wrap(err, "reading body") + } + resp := buf.Bytes() + c.Log(fmt.Sprintf("<< Response code: %d, body: %s\n", res.StatusCode, string(resp))) + + // Work around API's that don't follow the graphql standard + // https://activestatef.atlassian.net/browse/PB-4291 + standardizedErrors := StandardizedErrors{} + if err := json.Unmarshal(resp, &standardizedErrors); err != nil { + return errors.Wrap(err, "decoding error response") + } + if standardizedErrors.HasErrors() { + return errs.New(strings.Join(standardizedErrors.Values(), "\n")) + } + + if err := json.Unmarshal(resp, &gr); err != nil { + return errors.Wrap(err, "decoding response") + } + + // If the response is a single object, meaning we only have a single query in the request, we can unmarshal the + // response directly to the response type. Otherwise, we need to marshal the response as we normally would. + if len(intermediateResp) == 1 { + for _, val := range intermediateResp { + data, err := json.Marshal(val) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, response) + } + } + + data, err := json.Marshal(intermediateResp) + if err != nil { + return errors.Wrap(err, "remarshaling response") + } + return json.Unmarshal(data, response) +} + func (c *Client) marshalResponse(intermediateResp map[string]interface{}, resp interface{}) error { if resp == nil { return nil @@ -275,19 +614,19 @@ type graphResponse struct { } // Request is a GraphQL request. -type Request struct { +type GQLRequest struct { q string vars map[string]interface{} - files []file + files []File // Header represent any request headers that will be set // when the request is made. Header http.Header } -// NewRequest makes a new Request with the specified string. -func NewRequest(q string) *Request { - req := &Request{ +// newRequest makes a new Request with the specified string. +func newRequest(q string) *GQLRequest { + req := &GQLRequest{ q: q, Header: make(map[string][]string), } @@ -295,7 +634,7 @@ func NewRequest(q string) *Request { } // Var sets a variable. -func (req *Request) Var(key string, value interface{}) { +func (req *GQLRequest) Var(key string, value interface{}) { if req.vars == nil { req.vars = make(map[string]interface{}) } @@ -305,16 +644,20 @@ func (req *Request) Var(key string, value interface{}) { // File sets a file to upload. // Files are only supported with a Client that was created with // the UseMultipartForm option. -func (req *Request) File(fieldname, filename string, r io.Reader) { - req.files = append(req.files, file{ +func (req *GQLRequest) File(fieldname, filename string, r io.Reader) { + req.files = append(req.files, File{ Field: fieldname, Name: filename, R: r, }) } -// file represents a file to upload. -type file struct { +func (req *GQLRequest) Files() []File { + return req.files +} + +// File represents a File to upload. +type File struct { Field string Name string R io.Reader diff --git a/internal/graphql/graphql_json_test.go b/internal/graphql/graphql_json_test.go index 3cd6dde068..b2fbdda289 100644 --- a/internal/graphql/graphql_json_test.go +++ b/internal/graphql/graphql_json_test.go @@ -32,12 +32,12 @@ func TestDoJSON(t *testing.T) { defer srv.Close() ctx := context.Background() - client := NewClient(srv.URL) + client := newClient(srv.URL) ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() var responseData map[string]interface{} - err := client.Run(ctx, &Request{q: "query {}"}, &responseData) + err := client.RunWithContext(ctx, &TestRequest{"query {}", nil, nil}, &responseData) is.NoErr(err) is.Equal(calls, 1) // calls is.Equal(responseData["something"], "yes") @@ -59,10 +59,10 @@ func TestQueryJSON(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - client := NewClient(srv.URL) + client := newClient(srv.URL) - req := NewRequest("query {}") - req.Var("username", "matryer") + req := NewTestRequest("query {}") + req.vars["username"] = "matryer" // check variables is.True(req != nil) @@ -71,7 +71,7 @@ func TestQueryJSON(t *testing.T) { var resp struct { Value string } - err := client.Run(ctx, req, &resp) + err := client.RunWithContext(ctx, req, &resp) is.NoErr(err) is.Equal(calls, 1) @@ -99,15 +99,14 @@ func TestHeader(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - client := NewClient(srv.URL) - - req := NewRequest("query {}") - req.Header.Set("X-Custom-Header", "123") + client := newClient(srv.URL) + req := NewTestRequest("query {}") + req.headers["X-Custom-Header"] = []string{"123"} var resp struct { Value string } - err := client.Run(ctx, req, &resp) + err := client.RunWithContext(ctx, req, &resp) is.NoErr(err) is.Equal(calls, 1) diff --git a/internal/graphql/graphql_multipart_test.go b/internal/graphql/graphql_multipart_test.go index 7ae97b5a9f..a6734748ad 100644 --- a/internal/graphql/graphql_multipart_test.go +++ b/internal/graphql/graphql_multipart_test.go @@ -27,10 +27,9 @@ func TestWithClient(t *testing.T) { } ctx := context.Background() - client := NewClient("", WithHTTPClient(testClient), UseMultipartForm()) + client := newClient("", WithHTTPClient(testClient), UseMultipartForm()) - req := NewRequest(``) - client.Run(ctx, req, nil) + client.RunWithContext(ctx, NewTestRequest("query {}"), nil) is.Equal(calls, 1) // calls } @@ -54,12 +53,12 @@ func TestDoUseMultipartForm(t *testing.T) { defer srv.Close() ctx := context.Background() - client := NewClient(srv.URL, UseMultipartForm()) + client := newClient(srv.URL, UseMultipartForm()) ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() var responseData map[string]interface{} - err := client.Run(ctx, &Request{q: "query {}"}, &responseData) + err := client.RunWithContext(ctx, NewTestRequest("query {}"), &responseData) is.NoErr(err) is.Equal(calls, 1) // calls is.Equal(responseData["something"], "yes") @@ -82,12 +81,12 @@ func TestDoErr(t *testing.T) { defer srv.Close() ctx := context.Background() - client := NewClient(srv.URL, UseMultipartForm()) + client := newClient(srv.URL, UseMultipartForm()) ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() var responseData map[string]interface{} - err := client.Run(ctx, &Request{q: "query {}"}, &responseData) + err := client.RunWithContext(ctx, NewTestRequest("query {}"), &responseData) is.True(err != nil) is.Equal(err.Error(), "graphql: Something went wrong") } @@ -109,11 +108,11 @@ func TestDoNoResponse(t *testing.T) { defer srv.Close() ctx := context.Background() - client := NewClient(srv.URL, UseMultipartForm()) + client := newClient(srv.URL, UseMultipartForm()) ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() - err := client.Run(ctx, &Request{q: "query {}"}, nil) + err := client.RunWithContext(ctx, NewTestRequest("query {}"), nil) is.NoErr(err) is.Equal(calls, 1) // calls } @@ -134,10 +133,10 @@ func TestQuery(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - client := NewClient(srv.URL, UseMultipartForm()) + client := newClient(srv.URL, UseMultipartForm()) - req := NewRequest("query {}") - req.Var("username", "matryer") + req := NewTestRequest("query {}") + req.vars["username"] = "matryer" // check variables is.True(req != nil) @@ -146,7 +145,7 @@ func TestQuery(t *testing.T) { var resp struct { Value string } - err := client.Run(ctx, req, &resp) + err := client.RunWithContext(ctx, req, &resp) is.NoErr(err) is.Equal(calls, 1) @@ -160,7 +159,7 @@ func TestFile(t *testing.T) { var calls int srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls++ - file, header, err := r.FormFile("file") + file, header, err := r.FormFile("0") is.NoErr(err) defer file.Close() is.Equal(header.Filename, "filename.txt") @@ -175,11 +174,12 @@ func TestFile(t *testing.T) { defer srv.Close() ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() - client := NewClient(srv.URL, UseMultipartForm()) + client := newClient(srv.URL, UseMultipartForm()) f := strings.NewReader(`This is a file`) - req := NewRequest("query {}") - req.File("file", "filename.txt", f) - err := client.Run(ctx, req, nil) + req := NewTestRequestWithFiles("query {}") + req.files = append(req.files, File{Field: "file", Name: "filename.txt", R: f}) + var resp string + err := client.RunWithContext(ctx, req, &resp) is.NoErr(err) } diff --git a/internal/graphql/graphql_shared_test.go b/internal/graphql/graphql_shared_test.go new file mode 100644 index 0000000000..e20d924709 --- /dev/null +++ b/internal/graphql/graphql_shared_test.go @@ -0,0 +1,43 @@ +package graphql + +type TestRequest struct { + q string + vars map[string]interface{} + headers map[string][]string +} + +func NewTestRequest(q string) *TestRequest { + return &TestRequest{ + q: q, + vars: make(map[string]interface{}), + headers: make(map[string][]string), + } +} + +func (r *TestRequest) Query() string { + return r.q +} + +func (r *TestRequest) Vars() (map[string]interface{}, error) { + return r.vars, nil +} + +func (r *TestRequest) Headers() map[string][]string { + return r.headers +} + +type TestRequestWithFiles struct { + *TestRequest + files []File +} + +func NewTestRequestWithFiles(q string) *TestRequestWithFiles { + return &TestRequestWithFiles{ + TestRequest: NewTestRequest(q), + files: make([]File, 0), + } +} + +func (r *TestRequestWithFiles) Files() []File { + return r.files +} diff --git a/internal/graphql/graphql_test.go b/internal/graphql/graphql_test.go index 1a24c533f3..0bc1875361 100644 --- a/internal/graphql/graphql_test.go +++ b/internal/graphql/graphql_test.go @@ -25,7 +25,7 @@ func TestClient_SingleField(t *testing.T) { })) defer server.Close() - client := NewClient(server.URL) + client := newClient(server.URL) tests := []struct { name string @@ -104,10 +104,10 @@ func TestClient_SingleField(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - req := NewRequest(tt.query) + req := NewTestRequest(tt.query) var resp interface{} - err := client.Run(context.Background(), req, &resp) + err := client.RunWithContext(context.Background(), req, &resp) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -145,7 +145,7 @@ func TestClient_MultipleFields(t *testing.T) { })) defer server.Close() - client := NewClient(server.URL) + client := newClient(server.URL) tests := []struct { name string @@ -179,10 +179,10 @@ func TestClient_MultipleFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - req := NewRequest(tt.query) + req := NewTestRequest(tt.query) var resp interface{} - err := client.Run(context.Background(), req, &resp) + err := client.RunWithContext(context.Background(), req, &resp) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/platform/api/graphql/graphql.go b/pkg/platform/api/graphql/graphql.go index f0e01183cb..878a9d6653 100644 --- a/pkg/platform/api/graphql/graphql.go +++ b/pkg/platform/api/graphql/graphql.go @@ -1,14 +1,14 @@ package graphql import ( - "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *gqlclient.Client { +func New(auth *authentication.Auth) *graphql.Client { url := api.GetServiceURL(api.ServiceGraphQL) - c := gqlclient.New(url.String(), 0) + c := graphql.New(url.String(), 0) c.SetTokenProvider(auth) return c } diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index 551bdb9e06..966d210ce8 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -9,7 +9,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" - "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/locale" yamlcomment "github.com/zijiren233/yaml-comment" "gopkg.in/yaml.v3" @@ -187,11 +187,11 @@ func (p *PublishInput) Close() error { return p.file.Close() } -func (p *PublishInput) Files() []gqlclient.File { +func (p *PublishInput) Files() []graphql.File { if p.file == nil { - return []gqlclient.File{} + return []graphql.File{} } - return []gqlclient.File{ + return []graphql.File{ { Field: "variables.input.file", // this needs to map to the graphql input, eg. variables.input.file Name: p.Variables.Name, diff --git a/pkg/platform/api/hasura_inventory/inventory.go b/pkg/platform/api/hasura_inventory/inventory.go index b4ddd8dc71..dbca50e093 100644 --- a/pkg/platform/api/hasura_inventory/inventory.go +++ b/pkg/platform/api/hasura_inventory/inventory.go @@ -1,13 +1,13 @@ package inventory import ( - "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *gqlclient.Client { - client := gqlclient.New(api.GetServiceURL(api.ServiceHasuraInventory).String(), 0) +func New(auth *authentication.Auth) *graphql.Client { + client := graphql.New(api.GetServiceURL(api.ServiceHasuraInventory).String(), 0) if auth != nil && auth.Authenticated() { client.SetTokenProvider(auth) diff --git a/pkg/platform/api/mediator/mediator.go b/pkg/platform/api/mediator/mediator.go index 193f43a45b..67d744fdec 100644 --- a/pkg/platform/api/mediator/mediator.go +++ b/pkg/platform/api/mediator/mediator.go @@ -1,14 +1,14 @@ package mediator import ( - "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *gqlclient.Client { +func New(auth *authentication.Auth) *graphql.Client { url := api.GetServiceURL(api.ServiceMediator) - c := gqlclient.New(url.String(), 0) + c := graphql.New(url.String(), 0) if auth != nil { c.SetTokenProvider(auth) } diff --git a/pkg/platform/api/vulnerabilities/vulnerabilities.go b/pkg/platform/api/vulnerabilities/vulnerabilities.go index 7e49978dc2..9861444cba 100644 --- a/pkg/platform/api/vulnerabilities/vulnerabilities.go +++ b/pkg/platform/api/vulnerabilities/vulnerabilities.go @@ -1,13 +1,13 @@ package vulnerabilities import ( - "github.com/ActiveState/cli/internal/gqlclient" + "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *gqlclient.Client { - client := gqlclient.New(api.GetServiceURL(api.ServiceVulnerabilities).String(), 0) +func New(auth *authentication.Auth) *graphql.Client { + client := graphql.New(api.GetServiceURL(api.ServiceVulnerabilities).String(), 0) // Most requests to this service require authentication if auth != nil && auth.Authenticated() { diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index 812d40385a..a9d6c9ad3b 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -9,7 +9,6 @@ import ( "time" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" @@ -50,7 +49,7 @@ func (c *Commit) BuildScript() *buildscript.BuildScript { return c.buildscript } -func (c *client) Run(req gqlclient.Request, resp interface{}) error { +func (c *client) Run(req graphql.Request, resp interface{}) error { return c.gqlClient.Run(req, resp) } diff --git a/pkg/platform/model/buildplanner/buildplanner.go b/pkg/platform/model/buildplanner/buildplanner.go index dd566307ba..04533bbe6a 100644 --- a/pkg/platform/model/buildplanner/buildplanner.go +++ b/pkg/platform/model/buildplanner/buildplanner.go @@ -3,7 +3,6 @@ package buildplanner import ( "time" - "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/pkg/platform/api" @@ -13,7 +12,7 @@ import ( const clientDeprecationErrorKey = "CLIENT_DEPRECATION_ERROR" type client struct { - gqlClient *gqlclient.Client + gqlClient *graphql.Client } type BuildPlanner struct { @@ -41,7 +40,7 @@ func NewBuildPlannerModel(auth *authentication.Auth, cache cacher) *BuildPlanner bpURL := api.GetServiceURL(api.ServiceBuildPlanner).String() logging.Debug("Using build planner at: %s", bpURL) - gqlClient := gqlclient.NewWithOpts(bpURL, 0, graphql.WithHTTPClient(api.NewHTTPClient())) + gqlClient := graphql.NewWithOpts(bpURL, 0, graphql.WithHTTPClient(api.NewHTTPClient())) if auth != nil && auth.Authenticated() { gqlClient.SetTokenProvider(auth) diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index c61a660b6a..1fde4a4576 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -10,7 +10,6 @@ import ( "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/graph" "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/logging" @@ -23,7 +22,7 @@ import ( var SvcTimeoutMinimal = time.Millisecond * 500 type SvcModel struct { - client *gqlclient.Client + client *graphql.Client } // NewSvcModel returns a model for all client connections to a State Svc. This function returns an error if the State service is not yet ready to communicate. @@ -31,7 +30,7 @@ func NewSvcModel(port string) *SvcModel { localURL := "http://127.0.0.1" + port + "/query" return &SvcModel{ - client: gqlclient.NewWithOpts(localURL, 0, graphql.WithHTTPClient(&http.Client{})), + client: graphql.NewWithOpts(localURL, 0, graphql.WithHTTPClient(&http.Client{})), } } @@ -40,12 +39,12 @@ func (m *SvcModel) EnableDebugLog() { m.client.EnableDebugLog() } -func (m *SvcModel) request(ctx context.Context, request gqlclient.Request, resp interface{}) error { +func (m *SvcModel) request(ctx context.Context, request graphql.Request, resp interface{}) error { defer profile.Measure("SvcModel:request", time.Now()) err := m.client.RunWithContext(ctx, request, resp) if err != nil { - reqError := &gqlclient.RequestError{} + reqError := &graphql.RequestError{} if errors.As(err, &reqError) && (!condition.BuiltViaCI() || condition.InTest()) { vars, err := request.Vars() if err != nil { From b3a22a5ebff0bde5aa11ef3a267fb4351b870df1 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 20 Nov 2024 14:11:33 -0800 Subject: [PATCH 398/440] Remove RemediableError type --- pkg/platform/api/buildplanner/response/commit.go | 2 +- pkg/platform/api/buildplanner/types/errors.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/platform/api/buildplanner/response/commit.go b/pkg/platform/api/buildplanner/response/commit.go index b8923ed37f..74e4ec204c 100644 --- a/pkg/platform/api/buildplanner/response/commit.go +++ b/pkg/platform/api/buildplanner/response/commit.go @@ -38,7 +38,7 @@ func processPlanningError(message string, subErrors []*BuildExprError) error { } } - if se.Type != types.RemediableSolveErrorType && se.Type != types.GenericSolveErrorType && se.Type != types.RemediableError { + if se.Type != types.RemediableSolveErrorType && se.Type != types.GenericSolveErrorType { continue } diff --git a/pkg/platform/api/buildplanner/types/errors.go b/pkg/platform/api/buildplanner/types/errors.go index e4dd6d894a..e3f234d30a 100644 --- a/pkg/platform/api/buildplanner/types/errors.go +++ b/pkg/platform/api/buildplanner/types/errors.go @@ -10,7 +10,6 @@ const ( HeadOnBranchMovedErrorType = "HeadOnBranchMoved" ForbiddenErrorType = "Forbidden" GenericSolveErrorType = "GenericSolveError" - RemediableError = "RemediableError" RemediableSolveErrorType = "RemediableSolveError" PlanningErrorType = "PlanningError" MergeConflictType = "MergeConflict" From 5f2e02e9e9258cb3e0df1e626af076f8bc3b78bd Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 20 Nov 2024 15:04:06 -0800 Subject: [PATCH 399/440] Minor cleanup --- internal/graphql/graphql.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/internal/graphql/graphql.go b/internal/graphql/graphql.go index 86278ae0bb..283e6c0b1e 100644 --- a/internal/graphql/graphql.go +++ b/internal/graphql/graphql.go @@ -227,7 +227,7 @@ func (c *Client) RunWithContext(ctx context.Context, req Request, resp interface if header, ok := req.(RequestWithHeaders); ok { for key, values := range header.Headers() { for _, value := range values { - gqlRequest.Header.Set(key, value) + gqlRequest.Header.Add(key, value) } } } @@ -244,7 +244,7 @@ func (c *Client) RunWithContext(ctx context.Context, req Request, resp interface return c.runWithJSON(ctx, gqlRequest, resp) } -func (c *Client) runWithJSON(ctx context.Context, req *GQLRequest, resp interface{}) error { +func (c *Client) runWithJSON(ctx context.Context, req *gqlRequest, resp interface{}) error { var requestBody bytes.Buffer requestBodyObj := struct { Query string `json:"query"` @@ -297,7 +297,7 @@ func (c *Client) runWithJSON(ctx context.Context, req *GQLRequest, resp interfac return c.marshalResponse(intermediateResp, resp) } -func (c *Client) runWithPostFields(ctx context.Context, req *GQLRequest, resp interface{}) error { +func (c *Client) runWithPostFields(ctx context.Context, req *gqlRequest, resp interface{}) error { var requestBody bytes.Buffer writer := multipart.NewWriter(&requestBody) if err := writer.WriteField("query", req.q); err != nil { @@ -369,12 +369,12 @@ func (c *Client) runWithPostFields(ctx context.Context, req *GQLRequest, resp in return c.marshalResponse(intermediateResp, resp) } -type JsonRequest struct { +type jsonRequest struct { Query string `json:"query"` Variables map[string]interface{} `json:"variables"` } -func (c *Client) runWithFiles(ctx context.Context, request *GQLRequest, response interface{}) error { +func (c *Client) runWithFiles(ctx context.Context, request *gqlRequest, response interface{}) error { // Construct the multi-part request. bodyReader, bodyWriter := io.Pipe() @@ -407,7 +407,7 @@ func (c *Client) runWithFiles(ctx context.Context, request *GQLRequest, response return } - jsonReq := JsonRequest{ + jsonReq := jsonRequest{ Query: request.q, Variables: vars, } @@ -614,7 +614,7 @@ type graphResponse struct { } // Request is a GraphQL request. -type GQLRequest struct { +type gqlRequest struct { q string vars map[string]interface{} files []File @@ -625,8 +625,8 @@ type GQLRequest struct { } // newRequest makes a new Request with the specified string. -func newRequest(q string) *GQLRequest { - req := &GQLRequest{ +func newRequest(q string) *gqlRequest { + req := &gqlRequest{ q: q, Header: make(map[string][]string), } @@ -634,7 +634,7 @@ func newRequest(q string) *GQLRequest { } // Var sets a variable. -func (req *GQLRequest) Var(key string, value interface{}) { +func (req *gqlRequest) Var(key string, value interface{}) { if req.vars == nil { req.vars = make(map[string]interface{}) } @@ -644,7 +644,7 @@ func (req *GQLRequest) Var(key string, value interface{}) { // File sets a file to upload. // Files are only supported with a Client that was created with // the UseMultipartForm option. -func (req *GQLRequest) File(fieldname, filename string, r io.Reader) { +func (req *gqlRequest) File(fieldname, filename string, r io.Reader) { req.files = append(req.files, File{ Field: fieldname, Name: filename, @@ -652,10 +652,6 @@ func (req *GQLRequest) File(fieldname, filename string, r io.Reader) { }) } -func (req *GQLRequest) Files() []File { - return req.files -} - // File represents a File to upload. type File struct { Field string From fbec1ec1cf0259dbf18674113e241d6fbc88c3c6 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 20 Nov 2024 16:00:47 -0800 Subject: [PATCH 400/440] Rename messages to notifications --- cmd/state-svc/gqlgen.yml | 4 +- .../{messages => notifications}/condition.go | 2 +- .../notifications.go} | 122 +- .../notifications_test.go} | 80 +- cmd/state-svc/internal/resolver/resolver.go | 10 +- .../internal/server/generated/generated.go | 1212 ++++++----------- cmd/state-svc/schema/schema.graphqls | 18 +- .../messenger.go => notifier/notifier.go} | 58 +- cmd/state/main.go | 8 +- internal/constants/constants.go | 4 +- internal/graph/generated.go | 118 +- pkg/platform/api/svc/request/messaging.go | 31 - pkg/platform/api/svc/request/notification.go | 31 + pkg/platform/model/svc.go | 12 +- ...g_int_test.go => notification_int_test.go} | 84 +- 15 files changed, 687 insertions(+), 1107 deletions(-) rename cmd/state-svc/internal/{messages => notifications}/condition.go (98%) rename cmd/state-svc/internal/{messages/messages.go => notifications/notifications.go} (50%) rename cmd/state-svc/internal/{messages/messages_test.go => notifications/notifications_test.go} (78%) rename cmd/state/internal/cmdtree/exechandlers/{messenger/messenger.go => notifier/notifier.go} (53%) delete mode 100644 pkg/platform/api/svc/request/messaging.go create mode 100644 pkg/platform/api/svc/request/notification.go rename test/integration/{msg_int_test.go => notification_int_test.go} (62%) diff --git a/cmd/state-svc/gqlgen.yml b/cmd/state-svc/gqlgen.yml index 0c6a28fb70..476087864a 100644 --- a/cmd/state-svc/gqlgen.yml +++ b/cmd/state-svc/gqlgen.yml @@ -39,8 +39,8 @@ model: # gqlgen will search for any type names in the schema in these go packages # if they match it will use them, otherwise it will generate them. -autobind: - - "github.com/ActiveState/cli/internal/graph" +# autobind: +# - "github.com/ActiveState/cli/internal/graph" # This section declares type mapping between the GraphQL and go type systems # diff --git a/cmd/state-svc/internal/messages/condition.go b/cmd/state-svc/internal/notifications/condition.go similarity index 98% rename from cmd/state-svc/internal/messages/condition.go rename to cmd/state-svc/internal/notifications/condition.go index 5e39e8326e..2645dd98ea 100644 --- a/cmd/state-svc/internal/messages/condition.go +++ b/cmd/state-svc/internal/notifications/condition.go @@ -1,4 +1,4 @@ -package messages +package notifications import ( "regexp" diff --git a/cmd/state-svc/internal/messages/messages.go b/cmd/state-svc/internal/notifications/notifications.go similarity index 50% rename from cmd/state-svc/internal/messages/messages.go rename to cmd/state-svc/internal/notifications/notifications.go index 37581d067b..6d65b35393 100644 --- a/cmd/state-svc/internal/messages/messages.go +++ b/cmd/state-svc/internal/notifications/notifications.go @@ -1,4 +1,4 @@ -package messages +package notifications import ( "encoding/json" @@ -25,7 +25,7 @@ import ( const ConfigKeyLastReport = "messages.last_reported" -type Messages struct { +type Notifications struct { cfg *config.Instance auth *auth.Auth baseParams *ConditionParams @@ -33,7 +33,7 @@ type Messages struct { checkMutex sync.Mutex } -func New(cfg *config.Instance, auth *auth.Auth) (*Messages, error) { +func New(cfg *config.Instance, auth *auth.Auth) (*Notifications, error) { osVersion, err := sysinfo.OSVersion() if err != nil { return nil, errs.Wrap(err, "Could not get OS version") @@ -52,7 +52,7 @@ func New(cfg *config.Instance, auth *auth.Auth) (*Messages, error) { return resp, err }) - return &Messages{ + return &Notifications{ baseParams: &ConditionParams{ OS: sysinfo.OS().String(), OSVersion: NewVersionFromSysinfo(osVersion), @@ -65,22 +65,22 @@ func New(cfg *config.Instance, auth *auth.Auth) (*Messages, error) { }, nil } -func (m *Messages) Close() error { +func (m *Notifications) Close() error { m.poll.Close() return nil } -func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, error) { - // Prevent multiple checks at the same time, which could lead to the same message showing multiple times +func (m *Notifications) Check(command string, flags []string) ([]*graph.NotificationInfo, error) { + // Prevent multiple checks at the same time, which could lead to the same notification showing multiple times m.checkMutex.Lock() defer m.checkMutex.Unlock() cacheValue := m.poll.ValueFromCache() if cacheValue == nil { - return []*graph.MessageInfo{}, nil + return []*graph.NotificationInfo{}, nil } - allMessages, ok := cacheValue.([]*graph.MessageInfo) + allNotifications, ok := cacheValue.([]*graph.NotificationInfo) if !ok { return nil, errs.New("cacheValue has unexpected type: %T", cacheValue) } @@ -95,10 +95,10 @@ func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, conditionParams.UserID = id.String() } - logging.Debug("Checking %d messages with params: %#v", len(allMessages), conditionParams) + logging.Debug("Checking %d notifications with params: %#v", len(allNotifications), conditionParams) lastReportMap := m.cfg.GetStringMap(ConfigKeyLastReport) - msgs, err := check(&conditionParams, allMessages, lastReportMap, time.Now()) + msgs, err := check(&conditionParams, allNotifications, lastReportMap, time.Now()) if err != nil { return nil, errs.Wrap(err, "Could not check messages") } @@ -106,27 +106,27 @@ func (m *Messages) Check(command string, flags []string) ([]*graph.MessageInfo, lastReportMap[msg.ID] = time.Now().Format(time.RFC3339) } if err := m.cfg.Set(ConfigKeyLastReport, lastReportMap); err != nil { - return nil, errs.Wrap(err, "Could not save last reported messages") + return nil, errs.Wrap(err, "Could not save last reported notifications") } return msgs, nil } -func messageInDateRange(message *graph.MessageInfo, baseTime time.Time) (bool, error) { - if message.StartDate != "" { - startDate, err := time.Parse(time.RFC3339, message.StartDate) +func notificationInDateRange(notification *graph.NotificationInfo, baseTime time.Time) (bool, error) { + if notification.StartDate != "" { + startDate, err := time.Parse(time.RFC3339, notification.StartDate) if err != nil { - return false, errs.Wrap(err, "Could not parse start date for message %s", message.ID) + return false, errs.Wrap(err, "Could not parse start date for notification %s", notification.ID) } if baseTime.Before(startDate) { return false, nil } } - if message.EndDate != "" { - endDate, err := time.Parse(time.RFC3339, message.EndDate) + if notification.EndDate != "" { + endDate, err := time.Parse(time.RFC3339, notification.EndDate) if err != nil { - return false, errs.Wrap(err, "Could not parse end date for message %s", message.ID) + return false, errs.Wrap(err, "Could not parse end date for notification %s", notification.ID) } if baseTime.After(endDate) { return false, nil @@ -136,73 +136,73 @@ func messageInDateRange(message *graph.MessageInfo, baseTime time.Time) (bool, e return true, nil } -func check(params *ConditionParams, messages []*graph.MessageInfo, lastReportMap map[string]interface{}, baseTime time.Time) ([]*graph.MessageInfo, error) { +func check(params *ConditionParams, notifications []*graph.NotificationInfo, lastReportMap map[string]interface{}, baseTime time.Time) ([]*graph.NotificationInfo, error) { funcMap := conditionFuncMap() - filteredMessages := []*graph.MessageInfo{} - for _, message := range messages { - logging.Debug("Checking message %s", message.ID) + filteredNotifications := []*graph.NotificationInfo{} + for _, notification := range notifications { + logging.Debug("Checking notification %s", notification.ID) // Ensure we don't show the same message too often - if lastReport, ok := lastReportMap[message.ID]; ok { + if lastReport, ok := lastReportMap[notification.ID]; ok { lr, ok := lastReport.(string) if !ok { - return nil, errs.New("Could not get last reported time for message %s as it's not a string: %T", message.ID, lastReport) + return nil, errs.New("Could not get last reported time for notification %s as it's not a string: %T", notification.ID, lastReport) } lastReportTime, err := time.Parse(time.RFC3339, lr) if err != nil { - return nil, errs.New("Could not parse last reported time for message %s as it's not a valid RFC3339 value: %v", message.ID, lastReport) + return nil, errs.New("Could not parse last reported time for notification %s as it's not a valid RFC3339 value: %v", notification.ID, lastReport) } lastReportTimeAgo := baseTime.Sub(lastReportTime) - showMessage, err := repeatValid(message.Repeat, lastReportTimeAgo) + showNotification, err := repeatValid(notification.Repeat, lastReportTimeAgo) if err != nil { - return nil, errs.Wrap(err, "Could not validate repeat for message %s", message.ID) + return nil, errs.Wrap(err, "Could not validate repeat for notification %s", notification.ID) } - if !showMessage { - logging.Debug("Skipping message %s as it was shown %s ago", message.ID, lastReportTimeAgo) + if !showNotification { + logging.Debug("Skipping notification %s as it was shown %s ago", notification.ID, lastReportTimeAgo) continue } } // Check if message is within date range - inRange, err := messageInDateRange(message, baseTime) + inRange, err := notificationInDateRange(notification, baseTime) if err != nil { - logging.Warning("Could not check if message %s is in date range: %v", message.ID, err) + logging.Warning("Could not check if notification %s is in date range: %v", notification.ID, err) continue } if !inRange { - logging.Debug("Skipping message %s as it is outside of its date range", message.ID) + logging.Debug("Skipping notification %s as it is outside of its date range", notification.ID) continue } // Validate the conditional - if message.Condition != "" { - result, err := strutils.ParseTemplate(fmt.Sprintf(`{{%s}}`, message.Condition), params, funcMap) + if notification.Condition != "" { + result, err := strutils.ParseTemplate(fmt.Sprintf(`{{%s}}`, notification.Condition), params, funcMap) if err != nil { - logging.Warning("Could not parse condition template for message %s: %v", message.ID, err) + logging.Warning("Could not parse condition template for notification %s: %v", notification.ID, err) continue } if result == "true" { - logging.Debug("Including message %s as condition %s evaluated to %s", message.ID, message.Condition, result) - filteredMessages = append(filteredMessages, message) + logging.Debug("Including notification %s as condition %s evaluated to %s", notification.ID, notification.Condition, result) + filteredNotifications = append(filteredNotifications, notification) } else { - logging.Debug("Skipping message %s as condition %s evaluated to %s", message.ID, message.Condition, result) + logging.Debug("Skipping notification %s as condition %s evaluated to %s", notification.ID, notification.Condition, result) } } else { - logging.Debug("Including message %s as it has no condition", message.ID) - filteredMessages = append(filteredMessages, message) + logging.Debug("Including notification %s as it has no condition", notification.ID) + filteredNotifications = append(filteredNotifications, notification) } } - return filteredMessages, nil + return filteredNotifications, nil } -func fetch() ([]*graph.MessageInfo, error) { +func fetch() ([]*graph.NotificationInfo, error) { var body []byte var err error - if v := os.Getenv(constants.MessagesOverrideEnvVarName); v != "" { + if v := os.Getenv(constants.NotificationsOverrideEnvVarName); v != "" { body, err = fileutils.ReadFile(v) if err != nil { return nil, errs.Wrap(err, "Could not read messages override file") @@ -214,40 +214,40 @@ func fetch() ([]*graph.MessageInfo, error) { } } - var messages []*graph.MessageInfo - if err := json.Unmarshal(body, &messages); err != nil { + var notifications []*graph.NotificationInfo + if err := json.Unmarshal(body, ¬ifications); err != nil { return nil, errs.Wrap(err, "Could not unmarshall messages information") } // Set defaults - for _, message := range messages { - if message.Placement == "" { - message.Placement = graph.MessagePlacementTypeBeforeCmd + for _, notification := range notifications { + if notification.Placement == "" { + notification.Placement = graph.NotificationPlacementTypeBeforeCmd } - if message.Interrupt == "" { - message.Interrupt = graph.MessageInterruptTypeDisabled + if notification.Interrupt == "" { + notification.Interrupt = graph.NotificationInterruptTypeDisabled } - if message.Repeat == "" { - message.Repeat = graph.MessageRepeatTypeDisabled + if notification.Repeat == "" { + notification.Repeat = graph.NotificationRepeatTypeDisabled } } - return messages, nil + return notifications, nil } -func repeatValid(repeatType graph.MessageRepeatType, lastReportTimeAgo time.Duration) (bool, error) { +func repeatValid(repeatType graph.NotificationRepeatType, lastReportTimeAgo time.Duration) (bool, error) { switch repeatType { - case graph.MessageRepeatTypeConstantly: + case graph.NotificationRepeatTypeConstantly: return true, nil - case graph.MessageRepeatTypeDisabled: + case graph.NotificationRepeatTypeDisabled: return false, nil - case graph.MessageRepeatTypeHourly: + case graph.NotificationRepeatTypeHourly: return lastReportTimeAgo >= time.Hour, nil - case graph.MessageRepeatTypeDaily: + case graph.NotificationRepeatTypeDaily: return lastReportTimeAgo >= 24*time.Hour, nil - case graph.MessageRepeatTypeWeekly: + case graph.NotificationRepeatTypeWeekly: return lastReportTimeAgo >= 7*24*time.Hour, nil - case graph.MessageRepeatTypeMonthly: + case graph.NotificationRepeatTypeMonthly: return lastReportTimeAgo >= 30*24*time.Hour, nil default: return false, errs.New("Unknown repeat type: %s", repeatType) diff --git a/cmd/state-svc/internal/messages/messages_test.go b/cmd/state-svc/internal/notifications/notifications_test.go similarity index 78% rename from cmd/state-svc/internal/messages/messages_test.go rename to cmd/state-svc/internal/notifications/notifications_test.go index 186bc6719e..fae1dd5e3b 100644 --- a/cmd/state-svc/internal/messages/messages_test.go +++ b/cmd/state-svc/internal/notifications/notifications_test.go @@ -1,4 +1,4 @@ -package messages +package notifications import ( "reflect" @@ -14,7 +14,7 @@ func Test_check(t *testing.T) { baseTime := time.Now() type args struct { params *ConditionParams - messages []*graph.MessageInfo + notifications []*graph.NotificationInfo lastReportMap map[string]interface{} baseTime time.Time } @@ -28,7 +28,7 @@ func Test_check(t *testing.T) { "No special conditions", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A"}, {ID: "B"}, {ID: "C"}, }, lastReportMap: map[string]interface{}{}, @@ -43,7 +43,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ Command: "foo", }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `eq .Command "bar"`}, {ID: "B", Condition: `eq .Command "foo"`}, {ID: "C", Condition: `eq .Command "foobar"`}, @@ -60,7 +60,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ UserEmail: "john@doe.org", }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `contains .UserEmail "john"`}, {ID: "B", Condition: `contains .UserEmail "fred"`}, }, @@ -76,7 +76,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ UserEmail: "john@doe.org", }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `hasPrefix .UserEmail "john"`}, {ID: "B", Condition: `hasPrefix .UserEmail "org"`}, }, @@ -92,7 +92,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ UserEmail: "john@doe.org", }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `hasSuffix .UserEmail "john"`}, {ID: "B", Condition: `hasSuffix .UserEmail "org"`}, }, @@ -108,7 +108,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ UserEmail: "john@doe.org", }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `regexMatch .UserEmail ".*@doe.org$"`}, {ID: "B", Condition: `regexMatch .UserEmail "^doe.org$"`}, }, @@ -124,7 +124,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ UserEmail: "john@doe.org", }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `regexMatch .UserEmail ".*@doe.org$"`}, {ID: "B", Condition: `regexMatch .UserEmail ".*("`}, }, @@ -140,7 +140,7 @@ func Test_check(t *testing.T) { params: &ConditionParams{ StateVersion: NewVersionFromSemver(semver.MustParse("7.8.9-SHA123456a7b")), }, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", Condition: `eq .StateVersion.Major 7`}, {ID: "B", Condition: `eq .StateVersion.Minor 8`}, {ID: "C", Condition: `eq .StateVersion.Patch 9`}, @@ -160,10 +160,10 @@ func Test_check(t *testing.T) { "Repeat Disabled", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ - {ID: "A", Repeat: graph.MessageRepeatTypeDisabled}, - {ID: "B", Repeat: graph.MessageRepeatTypeDisabled}, - {ID: "C", Repeat: graph.MessageRepeatTypeDisabled}, + notifications: []*graph.NotificationInfo{ + {ID: "A", Repeat: graph.NotificationRepeatTypeDisabled}, + {ID: "B", Repeat: graph.NotificationRepeatTypeDisabled}, + {ID: "C", Repeat: graph.NotificationRepeatTypeDisabled}, }, lastReportMap: map[string]interface{}{ "A": baseTime.Format(time.RFC3339), @@ -178,10 +178,10 @@ func Test_check(t *testing.T) { "Repeat Constantly", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ - {ID: "A", Repeat: graph.MessageRepeatTypeConstantly}, - {ID: "B", Repeat: graph.MessageRepeatTypeConstantly}, - {ID: "C", Repeat: graph.MessageRepeatTypeConstantly}, + notifications: []*graph.NotificationInfo{ + {ID: "A", Repeat: graph.NotificationRepeatTypeConstantly}, + {ID: "B", Repeat: graph.NotificationRepeatTypeConstantly}, + {ID: "C", Repeat: graph.NotificationRepeatTypeConstantly}, }, lastReportMap: map[string]interface{}{ "A": baseTime.Format(time.RFC3339), @@ -196,10 +196,10 @@ func Test_check(t *testing.T) { "Repeat Hourly", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ - {ID: "A", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "B", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, + notifications: []*graph.NotificationInfo{ + {ID: "A", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "B", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "C", Repeat: graph.NotificationRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ "A": baseTime.Format(time.RFC3339), @@ -215,10 +215,10 @@ func Test_check(t *testing.T) { "Repeat Daily", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ - {ID: "A", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "B", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, + notifications: []*graph.NotificationInfo{ + {ID: "A", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "B", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "C", Repeat: graph.NotificationRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ "A": baseTime.Format(time.RFC3339), @@ -234,10 +234,10 @@ func Test_check(t *testing.T) { "Repeat Weekly", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ - {ID: "A", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "B", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, + notifications: []*graph.NotificationInfo{ + {ID: "A", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "B", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "C", Repeat: graph.NotificationRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ "A": baseTime.Format(time.RFC3339), @@ -253,10 +253,10 @@ func Test_check(t *testing.T) { "Repeat Monthly", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ - {ID: "A", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "B", Repeat: graph.MessageRepeatTypeHourly}, - {ID: "C", Repeat: graph.MessageRepeatTypeHourly}, + notifications: []*graph.NotificationInfo{ + {ID: "A", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "B", Repeat: graph.NotificationRepeatTypeHourly}, + {ID: "C", Repeat: graph.NotificationRepeatTypeHourly}, }, lastReportMap: map[string]interface{}{ "A": baseTime.Format(time.RFC3339), @@ -272,7 +272,7 @@ func Test_check(t *testing.T) { "Date Range - Within Range", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", StartDate: baseTime.Add(-24 * time.Hour).Format(time.RFC3339), EndDate: baseTime.Add(24 * time.Hour).Format(time.RFC3339)}, {ID: "B", StartDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339), EndDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, {ID: "C", StartDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339), EndDate: baseTime.Add(24 * time.Hour).Format(time.RFC3339)}, @@ -287,7 +287,7 @@ func Test_check(t *testing.T) { "Date Range - No Dates Specified", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A"}, {ID: "B", StartDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339)}, {ID: "C", EndDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, @@ -302,7 +302,7 @@ func Test_check(t *testing.T) { "Date Range - Invalid Date Format", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", StartDate: "invalid-date"}, }, lastReportMap: map[string]interface{}{}, @@ -315,7 +315,7 @@ func Test_check(t *testing.T) { "Date Range - Only Start Date", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", StartDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339)}, {ID: "B", StartDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, }, @@ -329,7 +329,7 @@ func Test_check(t *testing.T) { "Date Range - Only End Date", args{ params: &ConditionParams{}, - messages: []*graph.MessageInfo{ + notifications: []*graph.NotificationInfo{ {ID: "A", EndDate: baseTime.Add(1 * time.Hour).Format(time.RFC3339)}, {ID: "B", EndDate: baseTime.Add(-1 * time.Hour).Format(time.RFC3339)}, }, @@ -342,7 +342,7 @@ func Test_check(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := check(tt.args.params, tt.args.messages, tt.args.lastReportMap, tt.args.baseTime) + got, err := check(tt.args.params, tt.args.notifications, tt.args.lastReportMap, tt.args.baseTime) if (err != nil) != tt.wantErr { t.Errorf("check() error = %v, wantErr %v", errs.JoinMessage(err), tt.wantErr) return diff --git a/cmd/state-svc/internal/resolver/resolver.go b/cmd/state-svc/internal/resolver/resolver.go index ba91441e7f..4f218fefb8 100644 --- a/cmd/state-svc/internal/resolver/resolver.go +++ b/cmd/state-svc/internal/resolver/resolver.go @@ -11,7 +11,7 @@ import ( "github.com/ActiveState/cli/cmd/state-svc/internal/graphqltypes" "github.com/ActiveState/cli/cmd/state-svc/internal/hash" - "github.com/ActiveState/cli/cmd/state-svc/internal/messages" + "github.com/ActiveState/cli/cmd/state-svc/internal/notifications" "github.com/ActiveState/cli/cmd/state-svc/internal/rtwatcher" genserver "github.com/ActiveState/cli/cmd/state-svc/internal/server/generated" "github.com/ActiveState/cli/internal/analytics/client/sync" @@ -35,7 +35,7 @@ import ( type Resolver struct { cfg *config.Instance - messages *messages.Messages + messages *notifications.Notifications updatePoller *poller.Poller authPoller *poller.Poller projectIDCache *projectcache.ID @@ -50,7 +50,7 @@ type Resolver struct { // var _ genserver.ResolverRoot = &Resolver{} // Must implement ResolverRoot func New(cfg *config.Instance, an *sync.Client, auth *authentication.Auth) (*Resolver, error) { - msg, err := messages.New(cfg, auth) + msg, err := notifications.New(cfg, auth) if err != nil { return nil, errs.Wrap(err, "Could not initialize messages") } @@ -247,9 +247,9 @@ func (r *Resolver) ReportRuntimeUsage(_ context.Context, pid int, exec, source s return &graph.ReportRuntimeUsageResponse{Received: true}, nil } -func (r *Resolver) CheckMessages(ctx context.Context, command string, flags []string) ([]*graph.MessageInfo, error) { +func (r *Resolver) CheckNotifications(ctx context.Context, command string, flags []string) ([]*graph.NotificationInfo, error) { defer func() { panics.LogAndPanic(recover(), debug.Stack()) }() - logging.Debug("Check messages resolver") + logging.Debug("Check notifications resolver") return r.messages.Check(command, flags) } diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index cfb794c9ee..b290494161 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -79,21 +79,21 @@ type ComplexityRoot struct { User func(childComplexity int) int } - MessageInfo struct { - Condition func(childComplexity int) int - EndDate func(childComplexity int) int - ID func(childComplexity int) int - Interrupt func(childComplexity int) int - Message func(childComplexity int) int - Placement func(childComplexity int) int - Repeat func(childComplexity int) int - StartDate func(childComplexity int) int - } - Mutation struct { SetCache func(childComplexity int, key string, value string, expiry int) int } + NotificationInfo struct { + Condition func(childComplexity int) int + EndDate func(childComplexity int) int + ID func(childComplexity int) int + Interrupt func(childComplexity int) int + Notification func(childComplexity int) int + Placement func(childComplexity int) int + Repeat func(childComplexity int) int + StartDate func(childComplexity int) int + } + Organization struct { Role func(childComplexity int) int URLname func(childComplexity int) int @@ -112,7 +112,7 @@ type ComplexityRoot struct { Query struct { AnalyticsEvent func(childComplexity int, category string, action string, source string, label *string, dimensionsJSON string) int AvailableUpdate func(childComplexity int, desiredChannel string, desiredVersion string) int - CheckMessages func(childComplexity int, command string, flags []string) int + CheckNotifications func(childComplexity int, command string, flags []string) int ConfigChanged func(childComplexity int, key string) int FetchLogTail func(childComplexity int) int GetCache func(childComplexity int, key string) int @@ -157,7 +157,7 @@ type QueryResolver interface { Projects(ctx context.Context) ([]*graph.Project, error) AnalyticsEvent(ctx context.Context, category string, action string, source string, label *string, dimensionsJSON string) (*graph.AnalyticsEventResponse, error) ReportRuntimeUsage(ctx context.Context, pid int, exec string, source string, dimensionsJSON string) (*graph.ReportRuntimeUsageResponse, error) - CheckMessages(ctx context.Context, command string, flags []string) ([]*graph.MessageInfo, error) + CheckNotifications(ctx context.Context, command string, flags []string) ([]*graph.NotificationInfo, error) ConfigChanged(ctx context.Context, key string) (*graph.ConfigChangedResponse, error) FetchLogTail(ctx context.Context) (string, error) GetProcessesInUse(ctx context.Context, execDir string) ([]*graph.ProcessInfo, error) @@ -283,73 +283,73 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.JWT.User(childComplexity), true - case "MessageInfo.condition": - if e.complexity.MessageInfo.Condition == nil { + case "Mutation.setCache": + if e.complexity.Mutation.SetCache == nil { break } - return e.complexity.MessageInfo.Condition(childComplexity), true - - case "MessageInfo.endDate": - if e.complexity.MessageInfo.EndDate == nil { - break + args, err := ec.field_Mutation_setCache_args(context.TODO(), rawArgs) + if err != nil { + return 0, false } - return e.complexity.MessageInfo.EndDate(childComplexity), true + return e.complexity.Mutation.SetCache(childComplexity, args["key"].(string), args["value"].(string), args["expiry"].(int)), true - case "MessageInfo.id": - if e.complexity.MessageInfo.ID == nil { + case "NotificationInfo.condition": + if e.complexity.NotificationInfo.Condition == nil { break } - return e.complexity.MessageInfo.ID(childComplexity), true + return e.complexity.NotificationInfo.Condition(childComplexity), true - case "MessageInfo.interrupt": - if e.complexity.MessageInfo.Interrupt == nil { + case "NotificationInfo.endDate": + if e.complexity.NotificationInfo.EndDate == nil { break } - return e.complexity.MessageInfo.Interrupt(childComplexity), true + return e.complexity.NotificationInfo.EndDate(childComplexity), true - case "MessageInfo.message": - if e.complexity.MessageInfo.Message == nil { + case "NotificationInfo.id": + if e.complexity.NotificationInfo.ID == nil { break } - return e.complexity.MessageInfo.Message(childComplexity), true + return e.complexity.NotificationInfo.ID(childComplexity), true - case "MessageInfo.placement": - if e.complexity.MessageInfo.Placement == nil { + case "NotificationInfo.interrupt": + if e.complexity.NotificationInfo.Interrupt == nil { break } - return e.complexity.MessageInfo.Placement(childComplexity), true + return e.complexity.NotificationInfo.Interrupt(childComplexity), true - case "MessageInfo.repeat": - if e.complexity.MessageInfo.Repeat == nil { + case "NotificationInfo.notification": + if e.complexity.NotificationInfo.Notification == nil { break } - return e.complexity.MessageInfo.Repeat(childComplexity), true + return e.complexity.NotificationInfo.Notification(childComplexity), true - case "MessageInfo.startDate": - if e.complexity.MessageInfo.StartDate == nil { + case "NotificationInfo.placement": + if e.complexity.NotificationInfo.Placement == nil { break } - return e.complexity.MessageInfo.StartDate(childComplexity), true + return e.complexity.NotificationInfo.Placement(childComplexity), true - case "Mutation.setCache": - if e.complexity.Mutation.SetCache == nil { + case "NotificationInfo.repeat": + if e.complexity.NotificationInfo.Repeat == nil { break } - args, err := ec.field_Mutation_setCache_args(context.TODO(), rawArgs) - if err != nil { - return 0, false + return e.complexity.NotificationInfo.Repeat(childComplexity), true + + case "NotificationInfo.startDate": + if e.complexity.NotificationInfo.StartDate == nil { + break } - return e.complexity.Mutation.SetCache(childComplexity, args["key"].(string), args["value"].(string), args["expiry"].(int)), true + return e.complexity.NotificationInfo.StartDate(childComplexity), true case "Organization.role": if e.complexity.Organization.Role == nil { @@ -417,17 +417,17 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.AvailableUpdate(childComplexity, args["desiredChannel"].(string), args["desiredVersion"].(string)), true - case "Query.checkMessages": - if e.complexity.Query.CheckMessages == nil { + case "Query.checkNotifications": + if e.complexity.Query.CheckNotifications == nil { break } - args, err := ec.field_Query_checkMessages_args(context.TODO(), rawArgs) + args, err := ec.field_Query_checkNotifications_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Query.CheckMessages(childComplexity, args["command"].(string), args["flags"].([]string)), true + return e.complexity.Query.CheckNotifications(childComplexity, args["command"].(string), args["flags"].([]string)), true case "Query.configChanged": if e.complexity.Query.ConfigChanged == nil { @@ -731,7 +731,7 @@ type ReportRuntimeUsageResponse { received: Boolean! } -enum MessageRepeatType { +enum NotificationRepeatType { Disabled Constantly Hourly @@ -740,26 +740,26 @@ enum MessageRepeatType { Monthly } -enum MessageInterruptType { +enum NotificationInterruptType { Disabled Prompt Exit } -enum MessagePlacementType { +enum NotificationPlacementType { BeforeCmd AfterCmd } -type MessageInfo { +type NotificationInfo { id: String! - message: String! + notification: String! condition: String! startDate: String! endDate: String! - repeat: MessageRepeatType! - interrupt: MessageInterruptType! - placement: MessagePlacementType! + repeat: NotificationRepeatType! + interrupt: NotificationInterruptType! + placement: NotificationPlacementType! } type Organization { @@ -796,7 +796,7 @@ type Query { projects: [Project]! analyticsEvent(category: String!, action: String!, source: String!, label: String, dimensionsJson: String!): AnalyticsEventResponse reportRuntimeUsage(pid: Int!, exec: String!, source: String!, dimensionsJson: String!): ReportRuntimeUsageResponse - checkMessages(command: String!, flags: [String!]!): [MessageInfo!]! + checkNotifications(command: String!, flags: [String!]!): [NotificationInfo!]! configChanged(key: String!): ConfigChangedResponse fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! @@ -830,710 +830,290 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) func (ec *executionContext) field_Mutation_setCache_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Mutation_setCache_argsKey(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["key"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["key"] = arg0 - arg1, err := ec.field_Mutation_setCache_argsValue(ctx, rawArgs) - if err != nil { - return nil, err + var arg1 string + if tmp, ok := rawArgs["value"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["value"] = arg1 - arg2, err := ec.field_Mutation_setCache_argsExpiry(ctx, rawArgs) - if err != nil { - return nil, err + var arg2 int + if tmp, ok := rawArgs["expiry"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expiry")) + arg2, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } } args["expiry"] = arg2 return args, nil } -func (ec *executionContext) field_Mutation_setCache_argsKey( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["key"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) - if tmp, ok := rawArgs["key"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_setCache_argsValue( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["value"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) - if tmp, ok := rawArgs["value"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Mutation_setCache_argsExpiry( - ctx context.Context, - rawArgs map[string]interface{}, -) (int, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["expiry"] - if !ok { - var zeroVal int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("expiry")) - if tmp, ok := rawArgs["expiry"]; ok { - return ec.unmarshalNInt2int(ctx, tmp) - } - - var zeroVal int - return zeroVal, nil -} func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query___type_argsName(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["name"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["name"] = arg0 return args, nil } -func (ec *executionContext) field_Query___type_argsName( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["name"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - if tmp, ok := rawArgs["name"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_analyticsEvent_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_analyticsEvent_argsCategory(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["category"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("category")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["category"] = arg0 - arg1, err := ec.field_Query_analyticsEvent_argsAction(ctx, rawArgs) - if err != nil { - return nil, err + var arg1 string + if tmp, ok := rawArgs["action"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("action")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["action"] = arg1 - arg2, err := ec.field_Query_analyticsEvent_argsSource(ctx, rawArgs) - if err != nil { - return nil, err + var arg2 string + if tmp, ok := rawArgs["source"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) + arg2, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["source"] = arg2 - arg3, err := ec.field_Query_analyticsEvent_argsLabel(ctx, rawArgs) - if err != nil { - return nil, err + var arg3 *string + if tmp, ok := rawArgs["label"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("label")) + arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } } args["label"] = arg3 - arg4, err := ec.field_Query_analyticsEvent_argsDimensionsJSON(ctx, rawArgs) - if err != nil { - return nil, err + var arg4 string + if tmp, ok := rawArgs["dimensionsJson"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) + arg4, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["dimensionsJson"] = arg4 return args, nil } -func (ec *executionContext) field_Query_analyticsEvent_argsCategory( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["category"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("category")) - if tmp, ok := rawArgs["category"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_analyticsEvent_argsAction( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["action"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("action")) - if tmp, ok := rawArgs["action"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_analyticsEvent_argsSource( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["source"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) - if tmp, ok := rawArgs["source"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_analyticsEvent_argsLabel( - ctx context.Context, - rawArgs map[string]interface{}, -) (*string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["label"] - if !ok { - var zeroVal *string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("label")) - if tmp, ok := rawArgs["label"]; ok { - return ec.unmarshalOString2ᚖstring(ctx, tmp) - } - - var zeroVal *string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_analyticsEvent_argsDimensionsJSON( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["dimensionsJson"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) - if tmp, ok := rawArgs["dimensionsJson"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_availableUpdate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_availableUpdate_argsDesiredChannel(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["desiredChannel"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredChannel")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["desiredChannel"] = arg0 - arg1, err := ec.field_Query_availableUpdate_argsDesiredVersion(ctx, rawArgs) - if err != nil { - return nil, err + var arg1 string + if tmp, ok := rawArgs["desiredVersion"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredVersion")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["desiredVersion"] = arg1 return args, nil } -func (ec *executionContext) field_Query_availableUpdate_argsDesiredChannel( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["desiredChannel"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredChannel")) - if tmp, ok := rawArgs["desiredChannel"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_availableUpdate_argsDesiredVersion( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["desiredVersion"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("desiredVersion")) - if tmp, ok := rawArgs["desiredVersion"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} -func (ec *executionContext) field_Query_checkMessages_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_checkNotifications_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_checkMessages_argsCommand(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["command"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("command")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["command"] = arg0 - arg1, err := ec.field_Query_checkMessages_argsFlags(ctx, rawArgs) - if err != nil { - return nil, err + var arg1 []string + if tmp, ok := rawArgs["flags"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("flags")) + arg1, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + if err != nil { + return nil, err + } } args["flags"] = arg1 return args, nil } -func (ec *executionContext) field_Query_checkMessages_argsCommand( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["command"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("command")) - if tmp, ok := rawArgs["command"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_checkMessages_argsFlags( - ctx context.Context, - rawArgs map[string]interface{}, -) ([]string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["flags"] - if !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("flags")) - if tmp, ok := rawArgs["flags"]; ok { - return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Query_configChanged_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_configChanged_argsKey(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["key"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["key"] = arg0 return args, nil } -func (ec *executionContext) field_Query_configChanged_argsKey( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["key"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) - if tmp, ok := rawArgs["key"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_getCache_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_getCache_argsKey(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["key"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["key"] = arg0 return args, nil } -func (ec *executionContext) field_Query_getCache_argsKey( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["key"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("key")) - if tmp, ok := rawArgs["key"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_getProcessesInUse_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_getProcessesInUse_argsExecDir(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["execDir"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("execDir")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["execDir"] = arg0 return args, nil } -func (ec *executionContext) field_Query_getProcessesInUse_argsExecDir( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["execDir"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("execDir")) - if tmp, ok := rawArgs["execDir"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field_Query_hashGlobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_hashGlobs_argsWd(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 string + if tmp, ok := rawArgs["wd"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("wd")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["wd"] = arg0 - arg1, err := ec.field_Query_hashGlobs_argsGlobs(ctx, rawArgs) - if err != nil { - return nil, err + var arg1 []string + if tmp, ok := rawArgs["globs"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("globs")) + arg1, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + if err != nil { + return nil, err + } } args["globs"] = arg1 return args, nil } -func (ec *executionContext) field_Query_hashGlobs_argsWd( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["wd"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("wd")) - if tmp, ok := rawArgs["wd"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_hashGlobs_argsGlobs( - ctx context.Context, - rawArgs map[string]interface{}, -) ([]string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["globs"] - if !ok { - var zeroVal []string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("globs")) - if tmp, ok := rawArgs["globs"]; ok { - return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) - } - - var zeroVal []string - return zeroVal, nil -} func (ec *executionContext) field_Query_reportRuntimeUsage_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field_Query_reportRuntimeUsage_argsPid(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 int + if tmp, ok := rawArgs["pid"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pid")) + arg0, err = ec.unmarshalNInt2int(ctx, tmp) + if err != nil { + return nil, err + } } args["pid"] = arg0 - arg1, err := ec.field_Query_reportRuntimeUsage_argsExec(ctx, rawArgs) - if err != nil { - return nil, err + var arg1 string + if tmp, ok := rawArgs["exec"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("exec")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["exec"] = arg1 - arg2, err := ec.field_Query_reportRuntimeUsage_argsSource(ctx, rawArgs) - if err != nil { - return nil, err + var arg2 string + if tmp, ok := rawArgs["source"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) + arg2, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["source"] = arg2 - arg3, err := ec.field_Query_reportRuntimeUsage_argsDimensionsJSON(ctx, rawArgs) - if err != nil { - return nil, err + var arg3 string + if tmp, ok := rawArgs["dimensionsJson"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) + arg3, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } args["dimensionsJson"] = arg3 return args, nil } -func (ec *executionContext) field_Query_reportRuntimeUsage_argsPid( - ctx context.Context, - rawArgs map[string]interface{}, -) (int, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["pid"] - if !ok { - var zeroVal int - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("pid")) - if tmp, ok := rawArgs["pid"]; ok { - return ec.unmarshalNInt2int(ctx, tmp) - } - - var zeroVal int - return zeroVal, nil -} - -func (ec *executionContext) field_Query_reportRuntimeUsage_argsExec( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["exec"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("exec")) - if tmp, ok := rawArgs["exec"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_reportRuntimeUsage_argsSource( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["source"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("source")) - if tmp, ok := rawArgs["source"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} - -func (ec *executionContext) field_Query_reportRuntimeUsage_argsDimensionsJSON( - ctx context.Context, - rawArgs map[string]interface{}, -) (string, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["dimensionsJson"] - if !ok { - var zeroVal string - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("dimensionsJson")) - if tmp, ok := rawArgs["dimensionsJson"]; ok { - return ec.unmarshalNString2string(ctx, tmp) - } - - var zeroVal string - return zeroVal, nil -} func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field___Type_enumValues_argsIncludeDeprecated(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } } args["includeDeprecated"] = arg0 return args, nil } -func (ec *executionContext) field___Type_enumValues_argsIncludeDeprecated( - ctx context.Context, - rawArgs map[string]interface{}, -) (bool, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["includeDeprecated"] - if !ok { - var zeroVal bool - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - if tmp, ok := rawArgs["includeDeprecated"]; ok { - return ec.unmarshalOBoolean2bool(ctx, tmp) - } - - var zeroVal bool - return zeroVal, nil -} func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - arg0, err := ec.field___Type_fields_argsIncludeDeprecated(ctx, rawArgs) - if err != nil { - return nil, err + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } } args["includeDeprecated"] = arg0 return args, nil } -func (ec *executionContext) field___Type_fields_argsIncludeDeprecated( - ctx context.Context, - rawArgs map[string]interface{}, -) (bool, error) { - // We won't call the directive if the argument is null. - // Set call_argument_directives_with_null to true to call directives - // even if the argument is null. - _, ok := rawArgs["includeDeprecated"] - if !ok { - var zeroVal bool - return zeroVal, nil - } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - if tmp, ok := rawArgs["includeDeprecated"]; ok { - return ec.unmarshalOBoolean2bool(ctx, tmp) - } - - var zeroVal bool - return zeroVal, nil -} // endregion ***************************** args.gotpl ***************************** @@ -2177,8 +1757,8 @@ func (ec *executionContext) fieldContext_JWT_user(_ context.Context, field graph return fc, nil } -func (ec *executionContext) _MessageInfo_id(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_id(ctx, field) +func (ec *executionContext) _Mutation_setCache(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_setCache(ctx, field) if err != nil { return graphql.Null } @@ -2191,38 +1771,46 @@ func (ec *executionContext) _MessageInfo_id(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return ec.resolvers.Mutation().SetCache(rctx, fc.Args["key"].(string), fc.Args["value"].(string), fc.Args["expiry"].(int)) }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*graphqltypes.Void) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqltypesᚐVoid(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_setCache(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "Mutation", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Void does not have child fields") }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_setCache_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } -func (ec *executionContext) _MessageInfo_message(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_message(ctx, field) +func (ec *executionContext) _NotificationInfo_id(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_id(ctx, field) if err != nil { return graphql.Null } @@ -2235,7 +1823,7 @@ func (ec *executionContext) _MessageInfo_message(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Message, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -2252,9 +1840,9 @@ func (ec *executionContext) _MessageInfo_message(ctx context.Context, field grap return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, @@ -2265,8 +1853,8 @@ func (ec *executionContext) fieldContext_MessageInfo_message(_ context.Context, return fc, nil } -func (ec *executionContext) _MessageInfo_condition(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_condition(ctx, field) +func (ec *executionContext) _NotificationInfo_notification(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_notification(ctx, field) if err != nil { return graphql.Null } @@ -2279,7 +1867,7 @@ func (ec *executionContext) _MessageInfo_condition(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Condition, nil + return obj.Notification, nil }) if err != nil { ec.Error(ctx, err) @@ -2296,9 +1884,9 @@ func (ec *executionContext) _MessageInfo_condition(ctx context.Context, field gr return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_condition(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_notification(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, @@ -2309,8 +1897,8 @@ func (ec *executionContext) fieldContext_MessageInfo_condition(_ context.Context return fc, nil } -func (ec *executionContext) _MessageInfo_startDate(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_startDate(ctx, field) +func (ec *executionContext) _NotificationInfo_condition(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_condition(ctx, field) if err != nil { return graphql.Null } @@ -2323,7 +1911,7 @@ func (ec *executionContext) _MessageInfo_startDate(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.StartDate, nil + return obj.Condition, nil }) if err != nil { ec.Error(ctx, err) @@ -2340,9 +1928,9 @@ func (ec *executionContext) _MessageInfo_startDate(ctx context.Context, field gr return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_startDate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_condition(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, @@ -2353,8 +1941,8 @@ func (ec *executionContext) fieldContext_MessageInfo_startDate(_ context.Context return fc, nil } -func (ec *executionContext) _MessageInfo_endDate(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_endDate(ctx, field) +func (ec *executionContext) _NotificationInfo_startDate(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_startDate(ctx, field) if err != nil { return graphql.Null } @@ -2367,7 +1955,7 @@ func (ec *executionContext) _MessageInfo_endDate(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EndDate, nil + return obj.StartDate, nil }) if err != nil { ec.Error(ctx, err) @@ -2384,9 +1972,9 @@ func (ec *executionContext) _MessageInfo_endDate(ctx context.Context, field grap return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_endDate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_startDate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, @@ -2397,8 +1985,8 @@ func (ec *executionContext) fieldContext_MessageInfo_endDate(_ context.Context, return fc, nil } -func (ec *executionContext) _MessageInfo_repeat(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_repeat(ctx, field) +func (ec *executionContext) _NotificationInfo_endDate(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_endDate(ctx, field) if err != nil { return graphql.Null } @@ -2411,7 +1999,7 @@ func (ec *executionContext) _MessageInfo_repeat(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Repeat, nil + return obj.EndDate, nil }) if err != nil { ec.Error(ctx, err) @@ -2423,26 +2011,26 @@ func (ec *executionContext) _MessageInfo_repeat(ctx context.Context, field graph } return graphql.Null } - res := resTmp.(graph.MessageRepeatType) + res := resTmp.(string) fc.Result = res - return ec.marshalNMessageRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageRepeatType(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_repeat(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_endDate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type MessageRepeatType does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _MessageInfo_interrupt(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_interrupt(ctx, field) +func (ec *executionContext) _NotificationInfo_repeat(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_repeat(ctx, field) if err != nil { return graphql.Null } @@ -2455,7 +2043,7 @@ func (ec *executionContext) _MessageInfo_interrupt(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Interrupt, nil + return obj.Repeat, nil }) if err != nil { ec.Error(ctx, err) @@ -2467,26 +2055,26 @@ func (ec *executionContext) _MessageInfo_interrupt(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(graph.MessageInterruptType) + res := resTmp.(graph.NotificationRepeatType) fc.Result = res - return ec.marshalNMessageInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInterruptType(ctx, field.Selections, res) + return ec.marshalNNotificationRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationRepeatType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_interrupt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_repeat(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type MessageInterruptType does not have child fields") + return nil, errors.New("field of type NotificationRepeatType does not have child fields") }, } return fc, nil } -func (ec *executionContext) _MessageInfo_placement(ctx context.Context, field graphql.CollectedField, obj *graph.MessageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MessageInfo_placement(ctx, field) +func (ec *executionContext) _NotificationInfo_interrupt(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_interrupt(ctx, field) if err != nil { return graphql.Null } @@ -2499,7 +2087,7 @@ func (ec *executionContext) _MessageInfo_placement(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Placement, nil + return obj.Interrupt, nil }) if err != nil { ec.Error(ctx, err) @@ -2511,26 +2099,26 @@ func (ec *executionContext) _MessageInfo_placement(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(graph.MessagePlacementType) + res := resTmp.(graph.NotificationInterruptType) fc.Result = res - return ec.marshalNMessagePlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessagePlacementType(ctx, field.Selections, res) + return ec.marshalNNotificationInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInterruptType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MessageInfo_placement(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_interrupt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "MessageInfo", + Object: "NotificationInfo", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type MessagePlacementType does not have child fields") + return nil, errors.New("field of type NotificationInterruptType does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Mutation_setCache(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_setCache(ctx, field) +func (ec *executionContext) _NotificationInfo_placement(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_placement(ctx, field) if err != nil { return graphql.Null } @@ -2543,41 +2131,33 @@ func (ec *executionContext) _Mutation_setCache(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().SetCache(rctx, fc.Args["key"].(string), fc.Args["value"].(string), fc.Args["expiry"].(int)) + return obj.Placement, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*graphqltypes.Void) + res := resTmp.(graph.NotificationPlacementType) fc.Result = res - return ec.marshalOVoid2ᚖgithubᚗcomᚋActiveStateᚋcliᚋcmdᚋstateᚑsvcᚋinternalᚋgraphqltypesᚐVoid(ctx, field.Selections, res) + return ec.marshalNNotificationPlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationPlacementType(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Mutation_setCache(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_placement(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Mutation", + Object: "NotificationInfo", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Void does not have child fields") + return nil, errors.New("field of type NotificationPlacementType does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_setCache_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } @@ -3116,8 +2696,8 @@ func (ec *executionContext) fieldContext_Query_reportRuntimeUsage(ctx context.Co return fc, nil } -func (ec *executionContext) _Query_checkMessages(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_checkMessages(ctx, field) +func (ec *executionContext) _Query_checkNotifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_checkNotifications(ctx, field) if err != nil { return graphql.Null } @@ -3130,7 +2710,7 @@ func (ec *executionContext) _Query_checkMessages(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().CheckMessages(rctx, fc.Args["command"].(string), fc.Args["flags"].([]string)) + return ec.resolvers.Query().CheckNotifications(rctx, fc.Args["command"].(string), fc.Args["flags"].([]string)) }) if err != nil { ec.Error(ctx, err) @@ -3142,12 +2722,12 @@ func (ec *executionContext) _Query_checkMessages(ctx context.Context, field grap } return graphql.Null } - res := resTmp.([]*graph.MessageInfo) + res := resTmp.([]*graph.NotificationInfo) fc.Result = res - return ec.marshalNMessageInfo2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInfoᚄ(ctx, field.Selections, res) + return ec.marshalNNotificationInfo2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInfoᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_checkMessages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_checkNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -3156,23 +2736,23 @@ func (ec *executionContext) fieldContext_Query_checkMessages(ctx context.Context Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": - return ec.fieldContext_MessageInfo_id(ctx, field) - case "message": - return ec.fieldContext_MessageInfo_message(ctx, field) + return ec.fieldContext_NotificationInfo_id(ctx, field) + case "notification": + return ec.fieldContext_NotificationInfo_notification(ctx, field) case "condition": - return ec.fieldContext_MessageInfo_condition(ctx, field) + return ec.fieldContext_NotificationInfo_condition(ctx, field) case "startDate": - return ec.fieldContext_MessageInfo_startDate(ctx, field) + return ec.fieldContext_NotificationInfo_startDate(ctx, field) case "endDate": - return ec.fieldContext_MessageInfo_endDate(ctx, field) + return ec.fieldContext_NotificationInfo_endDate(ctx, field) case "repeat": - return ec.fieldContext_MessageInfo_repeat(ctx, field) + return ec.fieldContext_NotificationInfo_repeat(ctx, field) case "interrupt": - return ec.fieldContext_MessageInfo_interrupt(ctx, field) + return ec.fieldContext_NotificationInfo_interrupt(ctx, field) case "placement": - return ec.fieldContext_MessageInfo_placement(ctx, field) + return ec.fieldContext_NotificationInfo_placement(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type MessageInfo", field.Name) + return nil, fmt.Errorf("no field named %q was found under type NotificationInfo", field.Name) }, } defer func() { @@ -3182,7 +2762,7 @@ func (ec *executionContext) fieldContext_Query_checkMessages(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_checkMessages_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Query_checkNotifications_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -6199,54 +5779,100 @@ func (ec *executionContext) _JWT(ctx context.Context, sel ast.SelectionSet, obj return out } -var messageInfoImplementors = []string{"MessageInfo"} +var mutationImplementors = []string{"Mutation"} -func (ec *executionContext) _MessageInfo(ctx context.Context, sel ast.SelectionSet, obj *graph.MessageInfo) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, messageInfoImplementors) +func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Mutation", + }) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { + innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ + Object: field.Name, + Field: field, + }) + switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("MessageInfo") + out.Values[i] = graphql.MarshalString("Mutation") + case "setCache": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_setCache(ctx, field) + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var notificationInfoImplementors = []string{"NotificationInfo"} + +func (ec *executionContext) _NotificationInfo(ctx context.Context, sel ast.SelectionSet, obj *graph.NotificationInfo) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, notificationInfoImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("NotificationInfo") case "id": - out.Values[i] = ec._MessageInfo_id(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_id(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "message": - out.Values[i] = ec._MessageInfo_message(ctx, field, obj) + case "notification": + out.Values[i] = ec._NotificationInfo_notification(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "condition": - out.Values[i] = ec._MessageInfo_condition(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_condition(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "startDate": - out.Values[i] = ec._MessageInfo_startDate(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_startDate(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "endDate": - out.Values[i] = ec._MessageInfo_endDate(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_endDate(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "repeat": - out.Values[i] = ec._MessageInfo_repeat(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_repeat(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "interrupt": - out.Values[i] = ec._MessageInfo_interrupt(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_interrupt(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "placement": - out.Values[i] = ec._MessageInfo_placement(ctx, field, obj) + out.Values[i] = ec._NotificationInfo_placement(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -6273,52 +5899,6 @@ func (ec *executionContext) _MessageInfo(ctx context.Context, sel ast.SelectionS return out } -var mutationImplementors = []string{"Mutation"} - -func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors) - ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ - Object: "Mutation", - }) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ - Object: field.Name, - Field: field, - }) - - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Mutation") - case "setCache": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_setCache(ctx, field) - }) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var organizationImplementors = []string{"Organization"} func (ec *executionContext) _Organization(ctx context.Context, sel ast.SelectionSet, obj *graph.Organization) graphql.Marshaler { @@ -6568,7 +6148,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "checkMessages": + case "checkNotifications": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -6577,7 +6157,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_checkMessages(ctx, field) + res = ec._Query_checkNotifications(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -7362,7 +6942,7 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti return res } -func (ec *executionContext) marshalNMessageInfo2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInfoᚄ(ctx context.Context, sel ast.SelectionSet, v []*graph.MessageInfo) graphql.Marshaler { +func (ec *executionContext) marshalNNotificationInfo2ᚕᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInfoᚄ(ctx context.Context, sel ast.SelectionSet, v []*graph.NotificationInfo) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -7386,7 +6966,7 @@ func (ec *executionContext) marshalNMessageInfo2ᚕᚖgithubᚗcomᚋActiveState if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNMessageInfo2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInfo(ctx, sel, v[i]) + ret[i] = ec.marshalNNotificationInfo2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInfo(ctx, sel, v[i]) } if isLen1 { f(i) @@ -7406,43 +6986,43 @@ func (ec *executionContext) marshalNMessageInfo2ᚕᚖgithubᚗcomᚋActiveState return ret } -func (ec *executionContext) marshalNMessageInfo2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInfo(ctx context.Context, sel ast.SelectionSet, v *graph.MessageInfo) graphql.Marshaler { +func (ec *executionContext) marshalNNotificationInfo2ᚖgithubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInfo(ctx context.Context, sel ast.SelectionSet, v *graph.NotificationInfo) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._MessageInfo(ctx, sel, v) + return ec._NotificationInfo(ctx, sel, v) } -func (ec *executionContext) unmarshalNMessageInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInterruptType(ctx context.Context, v interface{}) (graph.MessageInterruptType, error) { - var res graph.MessageInterruptType +func (ec *executionContext) unmarshalNNotificationInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInterruptType(ctx context.Context, v interface{}) (graph.NotificationInterruptType, error) { + var res graph.NotificationInterruptType err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNMessageInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageInterruptType(ctx context.Context, sel ast.SelectionSet, v graph.MessageInterruptType) graphql.Marshaler { +func (ec *executionContext) marshalNNotificationInterruptType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationInterruptType(ctx context.Context, sel ast.SelectionSet, v graph.NotificationInterruptType) graphql.Marshaler { return v } -func (ec *executionContext) unmarshalNMessagePlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessagePlacementType(ctx context.Context, v interface{}) (graph.MessagePlacementType, error) { - var res graph.MessagePlacementType +func (ec *executionContext) unmarshalNNotificationPlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationPlacementType(ctx context.Context, v interface{}) (graph.NotificationPlacementType, error) { + var res graph.NotificationPlacementType err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNMessagePlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessagePlacementType(ctx context.Context, sel ast.SelectionSet, v graph.MessagePlacementType) graphql.Marshaler { +func (ec *executionContext) marshalNNotificationPlacementType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationPlacementType(ctx context.Context, sel ast.SelectionSet, v graph.NotificationPlacementType) graphql.Marshaler { return v } -func (ec *executionContext) unmarshalNMessageRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageRepeatType(ctx context.Context, v interface{}) (graph.MessageRepeatType, error) { - var res graph.MessageRepeatType +func (ec *executionContext) unmarshalNNotificationRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationRepeatType(ctx context.Context, v interface{}) (graph.NotificationRepeatType, error) { + var res graph.NotificationRepeatType err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNMessageRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐMessageRepeatType(ctx context.Context, sel ast.SelectionSet, v graph.MessageRepeatType) graphql.Marshaler { +func (ec *executionContext) marshalNNotificationRepeatType2githubᚗcomᚋActiveStateᚋcliᚋinternalᚋgraphᚐNotificationRepeatType(ctx context.Context, sel ast.SelectionSet, v graph.NotificationRepeatType) graphql.Marshaler { return v } diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index 9f71c26909..094c3b829d 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -31,7 +31,7 @@ type ReportRuntimeUsageResponse { received: Boolean! } -enum MessageRepeatType { +enum NotificationRepeatType { Disabled Constantly Hourly @@ -40,26 +40,26 @@ enum MessageRepeatType { Monthly } -enum MessageInterruptType { +enum NotificationInterruptType { Disabled Prompt Exit } -enum MessagePlacementType { +enum NotificationPlacementType { BeforeCmd AfterCmd } -type MessageInfo { +type NotificationInfo { id: String! - message: String! + notification: String! condition: String! startDate: String! endDate: String! - repeat: MessageRepeatType! - interrupt: MessageInterruptType! - placement: MessagePlacementType! + repeat: NotificationRepeatType! + interrupt: NotificationInterruptType! + placement: NotificationPlacementType! } type Organization { @@ -96,7 +96,7 @@ type Query { projects: [Project]! analyticsEvent(category: String!, action: String!, source: String!, label: String, dimensionsJson: String!): AnalyticsEventResponse reportRuntimeUsage(pid: Int!, exec: String!, source: String!, dimensionsJson: String!): ReportRuntimeUsageResponse - checkMessages(command: String!, flags: [String!]!): [MessageInfo!]! + checkNotifications(command: String!, flags: [String!]!): [NotificationInfo!]! configChanged(key: String!): ConfigChangedResponse fetchLogTail: String! getProcessesInUse(execDir: String!): [ProcessInfo!]! diff --git a/cmd/state/internal/cmdtree/exechandlers/messenger/messenger.go b/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go similarity index 53% rename from cmd/state/internal/cmdtree/exechandlers/messenger/messenger.go rename to cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go index efbbb825ed..4c670a612a 100644 --- a/cmd/state/internal/cmdtree/exechandlers/messenger/messenger.go +++ b/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go @@ -1,4 +1,4 @@ -package messenger +package notifier import ( "fmt" @@ -16,20 +16,20 @@ import ( "golang.org/x/net/context" ) -type Messenger struct { - out output.Outputer - svcModel *model.SvcModel - messages []*graph.MessageInfo +type Notifier struct { + out output.Outputer + svcModel *model.SvcModel + notifications []*graph.NotificationInfo } -func New(out output.Outputer, svcModel *model.SvcModel) *Messenger { - return &Messenger{ +func New(out output.Outputer, svcModel *model.SvcModel) *Notifier { + return &Notifier{ out: out, svcModel: svcModel, } } -func (m *Messenger) OnExecStart(cmd *captain.Command, _ []string) error { +func (m *Notifier) OnExecStart(cmd *captain.Command, _ []string) error { if m.out.Type().IsStructured() { // No point showing messaging on non-plain output (eg. json) return nil @@ -42,23 +42,23 @@ func (m *Messenger) OnExecStart(cmd *captain.Command, _ []string) error { cmds := cmd.JoinedCommandNames() flags := cmd.ActiveFlagNames() - messages, err := m.svcModel.CheckMessages(context.Background(), cmds, flags) + notifications, err := m.svcModel.CheckNotifications(context.Background(), cmds, flags) if err != nil { - multilog.Error("Could not report messages as CheckMessages return an error: %s", errs.JoinMessage(err)) + multilog.Error("Could not report notifications as CheckNotifications return an error: %s", errs.JoinMessage(err)) } - m.messages = messages + m.notifications = notifications - logging.Debug("Received %d messages to print", len(messages)) + logging.Debug("Received %d notifications to print", len(notifications)) - if err := m.PrintByPlacement(graph.MessagePlacementTypeBeforeCmd); err != nil { + if err := m.PrintByPlacement(graph.NotificationPlacementTypeBeforeCmd); err != nil { return errs.Wrap(err, "message error occurred before cmd") } return nil } -func (m *Messenger) OnExecStop(cmd *captain.Command, _ []string) error { +func (m *Notifier) OnExecStop(cmd *captain.Command, _ []string) error { if m.out.Type().IsStructured() { // No point showing messaging on non-plain output (eg. json) return nil @@ -68,36 +68,36 @@ func (m *Messenger) OnExecStop(cmd *captain.Command, _ []string) error { return nil // do not print update/deprecation warnings/messages when running `state update` } - if err := m.PrintByPlacement(graph.MessagePlacementTypeAfterCmd); err != nil { + if err := m.PrintByPlacement(graph.NotificationPlacementTypeAfterCmd); err != nil { return errs.Wrap(err, "message error occurred before cmd") } return nil } -func (m *Messenger) PrintByPlacement(placement graph.MessagePlacementType) error { +func (m *Notifier) PrintByPlacement(placement graph.NotificationPlacementType) error { exit := []string{} - messages := []*graph.MessageInfo{} - for _, message := range m.messages { - if message.Placement != placement { - logging.Debug("Skipping message %s as it's placement (%s) does not match %s", message.ID, message.Placement, placement) - messages = append(messages, message) + notifications := []*graph.NotificationInfo{} + for _, notification := range m.notifications { + if notification.Placement != placement { + logging.Debug("Skipping notification %s as it's placement (%s) does not match %s", notification.ID, notification.Placement, placement) + notifications = append(notifications, notification) continue } - if placement == graph.MessagePlacementTypeAfterCmd { + if placement == graph.NotificationPlacementTypeAfterCmd { m.out.Notice("") // Line break after } - logging.Debug("Printing message: %s", message.ID) - m.out.Notice(message.Message) + logging.Debug("Printing notification: %s, %s", notification.ID, notification.Notification) + m.out.Notice(notification.Notification) - if placement == graph.MessagePlacementTypeBeforeCmd { + if placement == graph.NotificationPlacementTypeBeforeCmd { m.out.Notice("") // Line break before } - if message.Interrupt == graph.MessageInterruptTypePrompt { + if notification.Interrupt == graph.NotificationInterruptTypePrompt { if m.out.Config().Interactive { m.out.Print(locale.Tl("messenger_prompt_continue", "Press ENTER to continue.")) fmt.Scanln(ptr.To("")) // Wait for input from user @@ -106,12 +106,12 @@ func (m *Messenger) PrintByPlacement(placement graph.MessagePlacementType) error } } - if message.Interrupt == graph.MessageInterruptTypeExit { - exit = append(exit, message.ID) + if notification.Interrupt == graph.NotificationInterruptTypeExit { + exit = append(exit, notification.ID) } } - m.messages = messages + m.notifications = notifications if len(exit) > 0 { // It's the responsibility of the message to give the user context as to why this exit happened. diff --git a/cmd/state/main.go b/cmd/state/main.go index 84ae4a16fb..2788e8d1b9 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ActiveState/cli/cmd/state/internal/cmdtree" - "github.com/ActiveState/cli/cmd/state/internal/cmdtree/exechandlers/messenger" + "github.com/ActiveState/cli/cmd/state/internal/cmdtree/exechandlers/notifier" anAsync "github.com/ActiveState/cli/internal/analytics/client/async" anaConst "github.com/ActiveState/cli/internal/analytics/constants" "github.com/ActiveState/cli/internal/captain" @@ -247,9 +247,9 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out logging.Debug("Could not find child command, error: %v", err) } - msger := messenger.New(out, svcmodel) - cmds.OnExecStart(msger.OnExecStart) - cmds.OnExecStop(msger.OnExecStop) + notifier := notifier.New(out, svcmodel) + cmds.OnExecStart(notifier.OnExecStart) + cmds.OnExecStop(notifier.OnExecStop) // Auto update to latest state tool version if possible. if updated, err := autoUpdate(svcmodel, args, childCmd, cfg, an, out); err == nil && updated { diff --git a/internal/constants/constants.go b/internal/constants/constants.go index f64eb5650d..56bd48c1a3 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -154,8 +154,8 @@ const OptinUnstableEnvVarName = "ACTIVESTATE_OPTIN_UNSTABLE" // ServiceSockDir overrides the default socket path root diriectory used by the state service const ServiceSockDir = "ACTIVESTATE_SVC_SOCK" -// MessagesOverrideEnvVarName is used to override the location of the messages file (for testing purposes - should hold local filepath) -const MessagesOverrideEnvVarName = "ACTIVESTATE_MESSAGES_OVERRIDE" +// NotificationsOverrideEnvVarName is used to override the location of the notifications file (for testing purposes - should hold local filepath) +const NotificationsOverrideEnvVarName = "ACTIVESTATE_NOTIFICATIONS_OVERRIDE" // DisableErrorTipsEnvVarName disables the display of tips in error messages. // This should only be used by the installer so-as not to pollute error message output. diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 61f7178059..482806f028 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -40,18 +40,18 @@ type Jwt struct { User *User `json:"user"` } -type MessageInfo struct { - ID string `json:"id"` - Message string `json:"message"` - Condition string `json:"condition"` - StartDate string `json:"startDate"` - EndDate string `json:"endDate"` - Repeat MessageRepeatType `json:"repeat"` - Interrupt MessageInterruptType `json:"interrupt"` - Placement MessagePlacementType `json:"placement"` +type Mutation struct { } -type Mutation struct { +type NotificationInfo struct { + ID string `json:"id"` + Notification string `json:"notification"` + Condition string `json:"condition"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + Repeat NotificationRepeatType `json:"repeat"` + Interrupt NotificationInterruptType `json:"interrupt"` + Placement NotificationPlacementType `json:"placement"` } type Organization struct { @@ -95,135 +95,135 @@ type Version struct { State *StateVersion `json:"state"` } -type MessageInterruptType string +type NotificationInterruptType string const ( - MessageInterruptTypeDisabled MessageInterruptType = "Disabled" - MessageInterruptTypePrompt MessageInterruptType = "Prompt" - MessageInterruptTypeExit MessageInterruptType = "Exit" + NotificationInterruptTypeDisabled NotificationInterruptType = "Disabled" + NotificationInterruptTypePrompt NotificationInterruptType = "Prompt" + NotificationInterruptTypeExit NotificationInterruptType = "Exit" ) -var AllMessageInterruptType = []MessageInterruptType{ - MessageInterruptTypeDisabled, - MessageInterruptTypePrompt, - MessageInterruptTypeExit, +var AllNotificationInterruptType = []NotificationInterruptType{ + NotificationInterruptTypeDisabled, + NotificationInterruptTypePrompt, + NotificationInterruptTypeExit, } -func (e MessageInterruptType) IsValid() bool { +func (e NotificationInterruptType) IsValid() bool { switch e { - case MessageInterruptTypeDisabled, MessageInterruptTypePrompt, MessageInterruptTypeExit: + case NotificationInterruptTypeDisabled, NotificationInterruptTypePrompt, NotificationInterruptTypeExit: return true } return false } -func (e MessageInterruptType) String() string { +func (e NotificationInterruptType) String() string { return string(e) } -func (e *MessageInterruptType) UnmarshalGQL(v interface{}) error { +func (e *NotificationInterruptType) UnmarshalGQL(v interface{}) error { str, ok := v.(string) if !ok { return fmt.Errorf("enums must be strings") } - *e = MessageInterruptType(str) + *e = NotificationInterruptType(str) if !e.IsValid() { - return fmt.Errorf("%s is not a valid MessageInterruptType", str) + return fmt.Errorf("%s is not a valid NotificationInterruptType", str) } return nil } -func (e MessageInterruptType) MarshalGQL(w io.Writer) { +func (e NotificationInterruptType) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -type MessagePlacementType string +type NotificationPlacementType string const ( - MessagePlacementTypeBeforeCmd MessagePlacementType = "BeforeCmd" - MessagePlacementTypeAfterCmd MessagePlacementType = "AfterCmd" + NotificationPlacementTypeBeforeCmd NotificationPlacementType = "BeforeCmd" + NotificationPlacementTypeAfterCmd NotificationPlacementType = "AfterCmd" ) -var AllMessagePlacementType = []MessagePlacementType{ - MessagePlacementTypeBeforeCmd, - MessagePlacementTypeAfterCmd, +var AllNotificationPlacementType = []NotificationPlacementType{ + NotificationPlacementTypeBeforeCmd, + NotificationPlacementTypeAfterCmd, } -func (e MessagePlacementType) IsValid() bool { +func (e NotificationPlacementType) IsValid() bool { switch e { - case MessagePlacementTypeBeforeCmd, MessagePlacementTypeAfterCmd: + case NotificationPlacementTypeBeforeCmd, NotificationPlacementTypeAfterCmd: return true } return false } -func (e MessagePlacementType) String() string { +func (e NotificationPlacementType) String() string { return string(e) } -func (e *MessagePlacementType) UnmarshalGQL(v interface{}) error { +func (e *NotificationPlacementType) UnmarshalGQL(v interface{}) error { str, ok := v.(string) if !ok { return fmt.Errorf("enums must be strings") } - *e = MessagePlacementType(str) + *e = NotificationPlacementType(str) if !e.IsValid() { - return fmt.Errorf("%s is not a valid MessagePlacementType", str) + return fmt.Errorf("%s is not a valid NotificationPlacementType", str) } return nil } -func (e MessagePlacementType) MarshalGQL(w io.Writer) { +func (e NotificationPlacementType) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -type MessageRepeatType string +type NotificationRepeatType string const ( - MessageRepeatTypeDisabled MessageRepeatType = "Disabled" - MessageRepeatTypeConstantly MessageRepeatType = "Constantly" - MessageRepeatTypeHourly MessageRepeatType = "Hourly" - MessageRepeatTypeDaily MessageRepeatType = "Daily" - MessageRepeatTypeWeekly MessageRepeatType = "Weekly" - MessageRepeatTypeMonthly MessageRepeatType = "Monthly" + NotificationRepeatTypeDisabled NotificationRepeatType = "Disabled" + NotificationRepeatTypeConstantly NotificationRepeatType = "Constantly" + NotificationRepeatTypeHourly NotificationRepeatType = "Hourly" + NotificationRepeatTypeDaily NotificationRepeatType = "Daily" + NotificationRepeatTypeWeekly NotificationRepeatType = "Weekly" + NotificationRepeatTypeMonthly NotificationRepeatType = "Monthly" ) -var AllMessageRepeatType = []MessageRepeatType{ - MessageRepeatTypeDisabled, - MessageRepeatTypeConstantly, - MessageRepeatTypeHourly, - MessageRepeatTypeDaily, - MessageRepeatTypeWeekly, - MessageRepeatTypeMonthly, +var AllNotificationRepeatType = []NotificationRepeatType{ + NotificationRepeatTypeDisabled, + NotificationRepeatTypeConstantly, + NotificationRepeatTypeHourly, + NotificationRepeatTypeDaily, + NotificationRepeatTypeWeekly, + NotificationRepeatTypeMonthly, } -func (e MessageRepeatType) IsValid() bool { +func (e NotificationRepeatType) IsValid() bool { switch e { - case MessageRepeatTypeDisabled, MessageRepeatTypeConstantly, MessageRepeatTypeHourly, MessageRepeatTypeDaily, MessageRepeatTypeWeekly, MessageRepeatTypeMonthly: + case NotificationRepeatTypeDisabled, NotificationRepeatTypeConstantly, NotificationRepeatTypeHourly, NotificationRepeatTypeDaily, NotificationRepeatTypeWeekly, NotificationRepeatTypeMonthly: return true } return false } -func (e MessageRepeatType) String() string { +func (e NotificationRepeatType) String() string { return string(e) } -func (e *MessageRepeatType) UnmarshalGQL(v interface{}) error { +func (e *NotificationRepeatType) UnmarshalGQL(v interface{}) error { str, ok := v.(string) if !ok { return fmt.Errorf("enums must be strings") } - *e = MessageRepeatType(str) + *e = NotificationRepeatType(str) if !e.IsValid() { - return fmt.Errorf("%s is not a valid MessageRepeatType", str) + return fmt.Errorf("%s is not a valid NotificationRepeatType", str) } return nil } -func (e MessageRepeatType) MarshalGQL(w io.Writer) { +func (e NotificationRepeatType) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } diff --git a/pkg/platform/api/svc/request/messaging.go b/pkg/platform/api/svc/request/messaging.go deleted file mode 100644 index a762e924ee..0000000000 --- a/pkg/platform/api/svc/request/messaging.go +++ /dev/null @@ -1,31 +0,0 @@ -package request - -type MessagingRequest struct { - command string - flags []string -} - -func NewMessagingRequest(command string, flags []string) *MessagingRequest { - return &MessagingRequest{ - command: command, - flags: flags, - } -} - -func (m *MessagingRequest) Query() string { - return `query($command: String!, $flags: [String!]!) { - checkMessages(command: $command, flags: $flags) { - id - message - interrupt - placement - } - }` -} - -func (m *MessagingRequest) Vars() (map[string]interface{}, error) { - return map[string]interface{}{ - "command": m.command, - "flags": m.flags, - }, nil -} diff --git a/pkg/platform/api/svc/request/notification.go b/pkg/platform/api/svc/request/notification.go new file mode 100644 index 0000000000..a4983960ac --- /dev/null +++ b/pkg/platform/api/svc/request/notification.go @@ -0,0 +1,31 @@ +package request + +type NotificationRequest struct { + command string + flags []string +} + +func NewNotificationRequest(command string, flags []string) *NotificationRequest { + return &NotificationRequest{ + command: command, + flags: flags, + } +} + +func (m *NotificationRequest) Query() string { + return `query($command: String!, $flags: [String!]!) { + checkNotifications(command: $command, flags: $flags) { + id + notification + interrupt + placement + } + }` +} + +func (m *NotificationRequest) Vars() (map[string]interface{}, error) { + return map[string]interface{}{ + "command": m.command, + "flags": m.flags, + }, nil +} diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index c61a660b6a..5cfca41cfa 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -125,14 +125,14 @@ func (m *SvcModel) ReportRuntimeUsage(ctx context.Context, pid int, exec, source return nil } -func (m *SvcModel) CheckMessages(ctx context.Context, command string, flags []string) ([]*graph.MessageInfo, error) { - logging.Debug("Checking for messages") - defer profile.Measure("svc:CheckMessages", time.Now()) +func (m *SvcModel) CheckNotifications(ctx context.Context, command string, flags []string) ([]*graph.NotificationInfo, error) { + logging.Debug("Checking for notifications") + defer profile.Measure("svc:CheckNotifications", time.Now()) - r := request.NewMessagingRequest(command, flags) - resp := []*graph.MessageInfo{} + r := request.NewNotificationRequest(command, flags) + resp := []*graph.NotificationInfo{} if err := m.request(ctx, r, &resp); err != nil { - return nil, errs.Wrap(err, "Error sending messages request") + return nil, errs.Wrap(err, "Error sending notifications request") } return resp, nil diff --git a/test/integration/msg_int_test.go b/test/integration/notification_int_test.go similarity index 62% rename from test/integration/msg_int_test.go rename to test/integration/notification_int_test.go index fbf16e3ab9..77a48e999b 100644 --- a/test/integration/msg_int_test.go +++ b/test/integration/notification_int_test.go @@ -13,11 +13,11 @@ import ( "github.com/ActiveState/cli/internal/testhelpers/tagsuite" ) -type MsgIntegrationTestSuite struct { +type NotificationIntegrationTestSuite struct { tagsuite.Suite } -func (suite *MsgIntegrationTestSuite) TestMessage_None() { +func (suite *NotificationIntegrationTestSuite) TestNotification_None() { suite.OnlyRunForTags(tagsuite.Messaging, tagsuite.Critical) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -30,7 +30,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_None() { // the logs for any potential issues. This is done automatically by ts.Close(). } -func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { +func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { suite.OnlyRunForTags(tagsuite.Messaging, tagsuite.Critical) tests := []struct { Name string @@ -42,7 +42,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Defaults", `[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message" + "Notification": "This is a [NOTICE]simple[/RESET] notification" }]`, false, true, @@ -51,7 +51,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Repeat Hourly", `[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "Repeat": "Hourly" }]`, false, @@ -61,7 +61,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Repeat Constantly", `[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "Repeat": "Constantly" }]`, true, @@ -71,7 +71,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Within Date Range", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -84,7 +84,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Outside Date Range", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -97,7 +97,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Only Start Date - Inside Range", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s" }]`, time.Now().Add(-1*time.Hour).Format(time.RFC3339)), @@ -108,7 +108,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Only End Date - Inside Range", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "EndDate": "%s" }]`, time.Now().Add(1*time.Hour).Format(time.RFC3339)), @@ -119,7 +119,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Outside Date Range - Future", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -132,7 +132,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Outside Date Range - Past", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -145,7 +145,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Only Start Date - Outside Range", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s" }]`, time.Now().Add(1*time.Hour).Format(time.RFC3339)), @@ -156,7 +156,7 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { "Only End Date - Outside Range", fmt.Sprintf(`[{ "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "EndDate": "%s" }]`, time.Now().Add(-1*time.Hour).Format(time.RFC3339)), @@ -169,15 +169,15 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(tt.MessageJson), 0755) + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(tt.MessageJson), 0755) suite.Require().NoError(err) - cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) if !tt.ExpectShown { - suite.Require().NotContains(cp.Output(), "This is a simple message", "Message should not appear when outside date range") + suite.Require().NotContains(cp.Output(), "This is a simple notification", "Notification should not appear when outside date range") } else { - cp.Expect(`This is a simple message`) + cp.Expect(`This is a simple notification`) } cp.Expect("ActiveState CLI by ActiveState Software Inc.") @@ -186,53 +186,53 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic() { // Ensure message doesn't stick around when we run another command cp = ts.Spawn("--version") if tt.ExpectRepeat { - cp.Expect(`This is a simple message`) + cp.Expect(`This is a simple notification`) } cp.ExpectExitCode(0) if !tt.ExpectRepeat { - suite.Require().NotContains(cp.Output(), "This is a simple message", "Should not repeat as that's the default behavior") + suite.Require().NotContains(cp.Output(), "This is a simple notification", "Should not repeat as that's the default behavior") } }) } } -func (suite *MsgIntegrationTestSuite) TestMessage_Basic_PlacementAfter() { +func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_PlacementAfter() { suite.OnlyRunForTags(tagsuite.Messaging) ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(fmt.Sprintf(`[ + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(fmt.Sprintf(`[ { "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "Placement": "%s" } -]`, graph.MessagePlacementTypeAfterCmd)), 0755) +]`, graph.NotificationPlacementTypeAfterCmd)), 0755) suite.Require().NoError(err) - cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) cp.Expect("ActiveState CLI by ActiveState Software Inc.") - cp.Expect(`This is a simple message`) + cp.Expect(`This is a simple notification`) cp.ExpectExitCode(0) } -func (suite *MsgIntegrationTestSuite) TestMessage_Basic_InterruptPrompt() { +func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptPrompt() { suite.OnlyRunForTags(tagsuite.Messaging) ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(fmt.Sprintf(`[ + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(fmt.Sprintf(`[ { "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "Repeat": "Constantly", "Interrupt": "%s" } -]`, graph.MessageInterruptTypePrompt)), 0755) +]`, graph.NotificationInterruptTypePrompt)), 0755) suite.Require().NoError(err) - cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) - cp.Expect(`This is a simple message`) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) + cp.Expect(`This is a simple notification`) cp.Expect("Press ENTER to continue") time.Sleep(time.Millisecond * 100) suite.Require().NotContains(cp.Output(), "ActiveState CLI by ActiveState Software Inc.") @@ -241,34 +241,34 @@ func (suite *MsgIntegrationTestSuite) TestMessage_Basic_InterruptPrompt() { cp.ExpectExitCode(0) // Test that non-interactive does not prompt - cp = ts.SpawnCmdWithOpts("state", e2e.OptArgs("--version", "-n"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) - cp.Expect(`This is a simple message`) + cp = ts.SpawnCmdWithOpts("state", e2e.OptArgs("--version", "-n"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) + cp.Expect(`This is a simple notification`) cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) suite.Require().NotContains(cp.Output(), "Press ENTER to continue") } -func (suite *MsgIntegrationTestSuite) TestMessage_Basic_InterruptExit() { +func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptExit() { suite.OnlyRunForTags(tagsuite.Messaging) ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(fmt.Sprintf(`[ + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(fmt.Sprintf(`[ { "ID": "simple", - "Message": "This is a [NOTICE]simple[/RESET] message", + "Notification": "This is a [NOTICE]simple[/RESET] notification", "Interrupt": "%s" } -]`, graph.MessageInterruptTypeExit)), 0755) +]`, graph.NotificationInterruptTypeExit)), 0755) suite.Require().NoError(err) - cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.MessagesOverrideEnvVarName+"="+msgFile)) + cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) cp.ExpectExitCode(1) - suite.Require().Contains(cp.Snapshot(), "This is a simple message") + suite.Require().Contains(cp.Snapshot(), "This is a simple notification") suite.Require().NotContains(cp.Output(), "ActiveState CLI by ActiveState Software Inc.") ts.IgnoreLogErrors() } -func TestMsgIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(MsgIntegrationTestSuite)) +func TestNotificationIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(NotificationIntegrationTestSuite)) } From 3dfbe589f8d7c42a6d1f1dff728830f6c9e4a218 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 20 Nov 2024 16:16:14 -0800 Subject: [PATCH 401/440] Update tag --- internal/testhelpers/tagsuite/tagsuite.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testhelpers/tagsuite/tagsuite.go b/internal/testhelpers/tagsuite/tagsuite.go index 3066469670..f735e815b4 100644 --- a/internal/testhelpers/tagsuite/tagsuite.go +++ b/internal/testhelpers/tagsuite/tagsuite.go @@ -54,8 +54,8 @@ const ( JSON = "json" Languages = "languages" Manifest = "manifest" - Messaging = "messaging" Migrations = "migrations" + Notifications = "notifications" Organizations = "organizations" Output = "output" Package = "package" From 2d3f53a288bc96008071ef91c653133f2edefc47 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Wed, 20 Nov 2024 16:23:27 -0800 Subject: [PATCH 402/440] Fix tag --- test/integration/notification_int_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/integration/notification_int_test.go b/test/integration/notification_int_test.go index 77a48e999b..7dc288984e 100644 --- a/test/integration/notification_int_test.go +++ b/test/integration/notification_int_test.go @@ -18,7 +18,7 @@ type NotificationIntegrationTestSuite struct { } func (suite *NotificationIntegrationTestSuite) TestNotification_None() { - suite.OnlyRunForTags(tagsuite.Messaging, tagsuite.Critical) + suite.OnlyRunForTags(tagsuite.Notifications, tagsuite.Critical) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -31,7 +31,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_None() { } func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { - suite.OnlyRunForTags(tagsuite.Messaging, tagsuite.Critical) + suite.OnlyRunForTags(tagsuite.Notifications, tagsuite.Critical) tests := []struct { Name string MessageJson string @@ -197,7 +197,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { } func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_PlacementAfter() { - suite.OnlyRunForTags(tagsuite.Messaging) + suite.OnlyRunForTags(tagsuite.Notifications) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -217,7 +217,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_PlacementA } func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptPrompt() { - suite.OnlyRunForTags(tagsuite.Messaging) + suite.OnlyRunForTags(tagsuite.Notifications) ts := e2e.New(suite.T(), false) defer ts.Close() @@ -249,7 +249,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptP } func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptExit() { - suite.OnlyRunForTags(tagsuite.Messaging) + suite.OnlyRunForTags(tagsuite.Notifications) ts := e2e.New(suite.T(), false) defer ts.Close() From ee56104ad6dc219cade81e1697a70ffbe4f811db Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 20 Nov 2024 17:22:04 -0500 Subject: [PATCH 403/440] Added force options for other prompt methods and made `--force` a global flag. --- cmd/state-installer/installer.go | 14 +++-- cmd/state-remote-installer/main.go | 4 +- cmd/state/internal/cmdtree/clean.go | 27 +++------ cmd/state/internal/cmdtree/cmdtree.go | 11 +++- cmd/state/internal/cmdtree/packages.go | 12 +--- cmd/state/main.go | 5 +- internal/locale/locales/en-us.yaml | 10 ++-- internal/prompt/prompt.go | 79 +++++++++++++++++-------- internal/runbits/auth/keypair.go | 6 +- internal/runbits/auth/login.go | 8 +-- internal/runbits/cves/cves.go | 19 ++---- internal/runbits/findproject/project.go | 6 +- internal/runners/clean/cache.go | 15 ++--- internal/runners/clean/config.go | 8 +-- internal/runners/clean/uninstall.go | 10 +--- internal/runners/fork/fork.go | 3 +- internal/runners/install/install.go | 2 +- internal/runners/invite/invite.go | 3 +- internal/runners/projects/delete.go | 8 +-- internal/runners/projects/edit.go | 7 +-- internal/runners/projects/move.go | 8 +-- internal/runners/publish/publish.go | 7 +-- internal/runners/push/push.go | 14 ++--- internal/runners/reset/reset.go | 8 +-- internal/runners/revert/revert.go | 8 +-- internal/runners/scripts/edit.go | 2 +- internal/runners/update/lock.go | 12 ++-- internal/runners/update/unlock.go | 12 ++-- internal/runners/upgrade/upgrade.go | 5 +- internal/runners/use/reset.go | 8 +-- 30 files changed, 161 insertions(+), 180 deletions(-) diff --git a/cmd/state-installer/installer.go b/cmd/state-installer/installer.go index b5fef2a594..6deaadad41 100644 --- a/cmd/state-installer/installer.go +++ b/cmd/state-installer/installer.go @@ -53,11 +53,17 @@ func (i *Installer) Install() (rerr error) { if err != nil { return errs.Wrap(err, "Could not determine if running as Windows administrator") } - if isAdmin && !i.Params.force && !i.Params.isUpdate && !i.Params.nonInteractive { - prompter := prompt.New(true, i.an) - confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false), nil) + if isAdmin && !i.Params.isUpdate { + prompter := prompt.New(i.an) + if i.Params.nonInteractive { + prompter.SetInteractive(false) + } + if i.Params.force { + prompter.SetForce(true) + } + confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false), ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !confirm { return locale.NewInputError("installer_aborted", "Installation aborted by the user") diff --git a/cmd/state-remote-installer/main.go b/cmd/state-remote-installer/main.go index 2ca3c36f75..29b1b6cfc1 100644 --- a/cmd/state-remote-installer/main.go +++ b/cmd/state-remote-installer/main.go @@ -116,7 +116,7 @@ func main() { an = sync.New(anaConst.SrcStateRemoteInstaller, cfg, nil, out) // Set up prompter - prompter := prompt.New(true, an) + prompter := prompt.New(an) params := newParams() cmd := captain.NewCommand( @@ -176,7 +176,7 @@ func execute(out output.Outputer, prompt prompt.Prompter, cfg *config.Instance, msg += locale.Tr("tos_disclaimer_prompt", constants.TermsOfServiceURLLatest) cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, ptr.To(true), nil) if err != nil { - return errs.Wrap(err, "Could not prompt for confirmation") + return errs.Wrap(err, "Not confirmed") } if !cont { diff --git a/cmd/state/internal/cmdtree/clean.go b/cmd/state/internal/cmdtree/clean.go index 37ca6cfbe8..988ef3cd6c 100644 --- a/cmd/state/internal/cmdtree/clean.go +++ b/cmd/state/internal/cmdtree/clean.go @@ -36,12 +36,6 @@ func newCleanUninstallCommand(prime *primer.Values, globals *globalOptions) *cap Description: locale.Tl("flag_state_clean_uninstall_all", "Also delete all associated config and cache files"), Value: ¶ms.All, }, - { - Name: "force", - Shorthand: "f", - Description: locale.T("flag_state_clean_uninstall_force_description"), - Value: ¶ms.Force, - }, { // This option is only used by the Windows uninstall shortcut to ask the user if they wish // to delete everything or keep cache and config. The user is also asked to press Enter @@ -62,8 +56,9 @@ func newCleanUninstallCommand(prime *primer.Values, globals *globalOptions) *cap if globals.NonInteractive { prime.Prompt().SetInteractive(false) } - if params.Force { - prime.Prompt().EnableForce() + if globals.Force { + prime.Prompt().SetForce(true) + params.Force = true } return runner.Run(¶ms) }, @@ -96,7 +91,7 @@ func newCleanCacheCommand(prime *primer.Values, globals *globalOptions) *captain ) } -func newCleanConfigCommand(prime *primer.Values) *captain.Command { +func newCleanConfigCommand(prime *primer.Values, globals *globalOptions) *captain.Command { runner := clean.NewConfig(prime) params := clean.ConfigParams{} return captain.NewCommand( @@ -104,18 +99,12 @@ func newCleanConfigCommand(prime *primer.Values) *captain.Command { locale.Tl("clean_config_title", "Cleaning Configuration"), locale.T("clean_config_description"), prime, - []*captain.Flag{ - { - Name: "force", - Shorthand: "f", - Description: locale.T("flag_state_clean_config_force_description"), - Value: ¶ms.Force, - }, - }, + []*captain.Flag{}, []*captain.Argument{}, func(ccmd *captain.Command, _ []string) error { - if params.Force { - prime.Prompt().EnableForce() + if globals.Force { + prime.Prompt().SetForce(true) + params.Force = true } return runner.Run(¶ms) }, diff --git a/cmd/state/internal/cmdtree/cmdtree.go b/cmd/state/internal/cmdtree/cmdtree.go index db47ba83c7..730fc65f3c 100644 --- a/cmd/state/internal/cmdtree/cmdtree.go +++ b/cmd/state/internal/cmdtree/cmdtree.go @@ -80,7 +80,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { cleanCmd.AddChildren( newCleanUninstallCommand(prime, globals), newCleanCacheCommand(prime, globals), - newCleanConfigCommand(prime), + newCleanConfigCommand(prime, globals), ) deployCmd := newDeployCommand(prime) @@ -95,7 +95,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { eventsCmd := newEventsCommand(prime) eventsCmd.AddChildren(newEventsLogCommand(prime)) - installCmd := newInstallCommand(prime) + installCmd := newInstallCommand(prime, globals) uninstallCmd := newUninstallCommand(prime) importCmd := newImportCommand(prime, globals) searchCmd := newSearchCommand(prime) @@ -234,6 +234,7 @@ type globalOptions struct { Output string Monochrome bool NonInteractive bool + Force bool } // Group instances are used to group command help output. @@ -303,6 +304,12 @@ func newStateCommand(globals *globalOptions, prime *primer.Values) *captain.Comm Persist: true, Value: &globals.NonInteractive, }, + { + Name: "force", + Description: locale.T("flag_state_force_description"), + Persist: true, + Value: &globals.Force, + }, { Name: "version", Description: locale.T("flag_state_version_description"), diff --git a/cmd/state/internal/cmdtree/packages.go b/cmd/state/internal/cmdtree/packages.go index 820b189927..a0d2d55174 100644 --- a/cmd/state/internal/cmdtree/packages.go +++ b/cmd/state/internal/cmdtree/packages.go @@ -50,11 +50,10 @@ func newPackagesCommand(prime *primer.Values) *captain.Command { return cmd } -func newInstallCommand(prime *primer.Values) *captain.Command { +func newInstallCommand(prime *primer.Values, globals *globalOptions) *captain.Command { runner := install.New(prime, model.NamespacePackage) params := install.Params{} - force := false var packagesRaw string cmd := captain.NewCommand( @@ -68,11 +67,6 @@ func newInstallCommand(prime *primer.Values) *captain.Command { Description: locale.T("package_flag_ts_description"), Value: ¶ms.Timestamp, }, - { - Name: "force", - Description: locale.Tl("package_flag_force_description", "Ignore security policy preventing packages with CVEs from being installed (not recommended)"), - Value: &force, - }, }, []*captain.Argument{ { @@ -88,8 +82,8 @@ func newInstallCommand(prime *primer.Values) *captain.Command { return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") } } - if force { - prime.Prompt().EnableForce() + if globals.Force { + prime.Prompt().SetForce(true) } return runner.Run(params) }, diff --git a/cmd/state/main.go b/cmd/state/main.go index 84ae4a16fb..78daa39eb7 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -224,7 +224,10 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out }() // Set up prompter - prompter := prompt.New(isInteractive, an) + prompter := prompt.New(an) + if !isInteractive { + prompter.SetInteractive(false) + } // Set up conditional, which accesses a lot of primer data sshell := subshell.New(cfg) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index b5a42a0105..eb4923fdf8 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -23,6 +23,8 @@ flag_state_output_description: other: "Set the output method. Possible values: plain, simple, json, editor" flag_state_non_interactive_description: other: Run the State Tool without any prompts +flag_state_force_description: + other: Run the State Tool without any prompts, overriding any safe defaults flag_state_version_description: other: Show the version of our state executable flag_state_activate_path_description: @@ -755,12 +757,8 @@ cache_description: other: Removes cached Runtime Environments clean_config_description: other: Removes global State Tool configuration. Project configuration will not be affected. -flag_state_clean_uninstall_force_description: - other: Run uninstall operation without prompts and ignoring any errors stopping running services arg_state_clean_cache_project_description: other: The project to be removed from the local cache. -flag_state_clean_config_force_description: - other: Run clean config operation without prompts and ignoring any errors stopping running services err_uninstall_activated: other: Cannot uninstall the State Tool while in an activated state. Please deactivate by entering [ACTIONABLE]exit[/RESET] or pressing [ACTIONABLE]Ctrl+D[/RESET] and then try again. err_remove_cache: @@ -1171,6 +1169,10 @@ prompt_continue_non_interactive: other: "Continuing because State Tool is running in non-interactive mode." prompt_abort_non_interactive: other: "Aborting because State Tool is running in non-interactive mode. To bypass you can use the '[ACTIONABLE]--force[/RESET]' flag." +prompt_using_force: + other: "Using '[ACTIONABLE]{{.V0}}[/RESET]' because the '[ACTIONABLE]--force[/RESET]' flag is set." +prompt_using_non_interactive: + other: "Using '[ACTIONABLE]{{.V0}}[/RESET]' because State Tool is running in non-interactive mode." unstable_command_warning: other: | This command is still in beta. If you want to opt-in to unstable features, run the following command: diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 07c004c9d4..9a591a9ef2 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -11,7 +11,6 @@ import ( "github.com/ActiveState/cli/internal/analytics/constants" "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" - "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" ) @@ -21,15 +20,14 @@ type EventDispatcher interface { // Prompter is the interface used to run our prompt from type Prompter interface { - Input(title, message string, defaultResponse *string, flags ...ValidatorFlag) (string, error) - InputAndValidate(title, message string, defaultResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) - Select(title, message string, choices []string, defaultResponse *string) (string, error) + Input(title, message string, defaultResponse *string, forcedResponse *string, flags ...ValidatorFlag) (string, error) + InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) + Select(title, message string, choices []string, defaultResponse *string, forcedResponse *string) (string, error) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) InputSecret(title, message string, flags ...ValidatorFlag) (string, error) IsInteractive() bool SetInteractive(bool) - EnableForce() - IsForceEnabled() bool + SetForce(bool) } // ValidatorFunc is a function pass to the Prompter to perform validation @@ -47,8 +45,8 @@ type Prompt struct { } // New creates a new prompter -func New(isInteractive bool, an EventDispatcher) Prompter { - return &Prompt{output.Get(), an, isInteractive, false} +func New(an EventDispatcher) Prompter { + return &Prompt{output.Get(), an, true, false} } // IsInteractive checks if the prompts can be interactive or should just return default values @@ -60,13 +58,13 @@ func (p *Prompt) SetInteractive(interactive bool) { p.isInteractive = interactive } -// EnableForce forces confirm prompts to return the force value (which is often different from the +// SetForce enables prompts to return the force value (which is often different from the // non-interactive value). -func (p *Prompt) EnableForce() { - p.isForced = true +func (p *Prompt) SetForce(force bool) { + p.isForced = force } -func (p *Prompt) IsForceEnabled() bool { +func (p *Prompt) IsForced() bool { return p.isForced } @@ -82,8 +80,8 @@ const ( ) // Input prompts the user for input. The user can specify available validation flags to trigger validation of responses -func (p *Prompt) Input(title, message string, defaultResponse *string, flags ...ValidatorFlag) (string, error) { - return p.InputAndValidate(title, message, defaultResponse, func(val interface{}) error { +func (p *Prompt) Input(title, message string, defaultResponse *string, forcedResponse *string, flags ...ValidatorFlag) (string, error) { + return p.InputAndValidate(title, message, defaultResponse, forcedResponse, func(val interface{}) error { return nil }, flags...) } @@ -100,10 +98,22 @@ func interactiveInputError(message string) error { } // InputAndValidate prompts an input field and allows you to specfiy a custom validation function as well as the built in flags -func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) { +func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) { + if p.isForced { + response := forcedResponse + if response == nil { + response = defaultResponse + } + if response != nil { + p.out.Notice(locale.Tr("prompt_using_force", *response)) + return *response, nil + } + return "", errs.New("No force option given for forced prompt") + } + if !p.isInteractive { if defaultResponse != nil { - logging.Debug("Selecting default choice %s for Input prompt %s in non-interactive mode", *defaultResponse, title) + p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultResponse)) return *defaultResponse, nil } return "", interactiveInputError(message) @@ -124,7 +134,7 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string // We handle defaults more clearly than the survey package can if defaultResponse != nil && *defaultResponse != "" { - v, err := p.Select("", formatMessage(message, !p.out.Config().Colored), []string{*defaultResponse, locale.Tl("prompt_custom", "Other ..")}, defaultResponse) + v, err := p.Select("", formatMessage(message, !p.out.Config().Colored), []string{*defaultResponse, locale.Tl("prompt_custom", "Other ..")}, defaultResponse, forcedResponse) if err != nil { return "", err } @@ -145,10 +155,22 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string } // Select prompts the user to select one entry from multiple choices -func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string) (string, error) { +func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string, forcedChoice *string) (string, error) { + if p.isForced { + choice := forcedChoice + if choice == nil { + choice = defaultChoice + } + if choice != nil { + p.out.Notice(locale.Tr("prompt_using_force", *choice)) + return *choice, nil + } + return "", errs.New("No force option given for forced prompt") + } + if !p.isInteractive { if defaultChoice != nil { - logging.Debug("Selecting default choice %s for Select prompt %s in non-interactive mode", *defaultChoice, title) + p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultChoice)) return *defaultChoice, nil } return "", interactiveInputError(message) @@ -179,17 +201,24 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * // Confirm prompts user for yes or no response. func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) { if p.isForced { - if forcedChoice == nil { - return false, errs.New("No force option given for force-enabled prompt") + choice := forcedChoice + if choice == nil { + choice = defaultChoice } - logging.Debug("Prompt %s confirmed with choice %v in force mode", title, forcedChoice) - return *forcedChoice, nil + if choice != nil { + p.out.Notice(locale.T("prompt_continue_force")) + return *choice, nil + } + return false, errs.New("No force option given for forced prompt") } if !p.isInteractive { if defaultChoice != nil { - logging.Debug("Prompt %s confirmed with default choice %v in non-interactive mode", title, defaultChoice) - return *defaultChoice, nil + if *defaultChoice { + p.out.Notice(locale.T("prompt_continue_non_interactive")) + return true, nil + } + return false, locale.NewInputError("prompt_abort_non_interactive") } return false, interactiveInputError(message) } diff --git a/internal/runbits/auth/keypair.go b/internal/runbits/auth/keypair.go index b91c44671b..ab94c1b12f 100644 --- a/internal/runbits/auth/keypair.go +++ b/internal/runbits/auth/keypair.go @@ -113,12 +113,12 @@ func promptForPreviousPassphrase(prompt prompt.Prompter) (string, error) { return passphrase, nil } -func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prmpt prompt.Prompter, auth *authentication.Auth) error { +func promptUserToRegenerateKeypair(passphrase string, cfg keypairs.Configurable, out output.Outputer, prompt prompt.Prompter, auth *authentication.Auth) error { // previous passphrase is invalid, inform user and ask if they want to generate a new keypair out.Notice(locale.T("auth_generate_new_keypair_message")) - yes, err := prmpt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil) + yes, err := prompt.Confirm("", locale.T("auth_confirm_generate_new_keypair_prompt"), ptr.To(false), nil) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !yes { return locale.NewInputError("auth_err_unrecoverable_keypair") diff --git a/internal/runbits/auth/login.go b/internal/runbits/auth/login.go index ac902ef2aa..5c55e2fed9 100644 --- a/internal/runbits/auth/login.go +++ b/internal/runbits/auth/login.go @@ -106,7 +106,7 @@ func RequireAuthentication(message string, cfg keypairs.Configurable, out output locale.T("prompt_login_action"), locale.T("prompt_signup_browser_action"), } - choice, err := prompt.Select(locale.Tl("login_signup", "Login or Signup"), locale.T("prompt_login_or_signup"), choices, new(string)) + choice, err := prompt.Select(locale.Tl("login_signup", "Login or Signup"), locale.T("prompt_login_or_signup"), choices, ptr.To(""), nil) if err != nil { return errs.Wrap(err, "Prompt cancelled") } @@ -139,7 +139,7 @@ func ensureCredentials(credentials *mono_models.Credentials, prompter prompt.Pro if nonInteractive { return locale.NewInputError("err_auth_needinput") } - credentials.Username, err = prompter.Input("", locale.T("username_prompt"), new(string), prompt.InputRequired) + credentials.Username, err = prompter.Input("", locale.T("username_prompt"), ptr.To(""), nil, prompt.InputRequired) if err != nil { return errs.Wrap(err, "Input cancelled") } @@ -175,7 +175,7 @@ func AuthenticateWithCredentials(credentials *mono_models.Credentials, auth *aut func promptToken(credentials *mono_models.Credentials, out output.Outputer, prompt prompt.Prompter, auth *authentication.Auth) error { var err error - credentials.Totp, err = prompt.Input("", locale.T("totp_prompt"), new(string)) + credentials.Totp, err = prompt.Input("", locale.T("totp_prompt"), ptr.To(""), nil) if err != nil { return err } @@ -268,7 +268,7 @@ func authenticateWithBrowser(out output.Outputer, auth *authentication.Auth, pro for !cont { cont, err = prompt.Confirm(locale.Tl("continue", "Continue?"), locale.T("auth_press_enter"), ptr.To(false), nil) if err != nil { - return errs.Wrap(err, "Prompt failed") + return errs.Wrap(err, "Not confirmed") } } apiKey, err = auth.AuthenticateWithDevice(strfmt.UUID(*response.DeviceCode)) diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index a82649c489..ad7d19ff86 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -106,20 +106,13 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil c.summarizeCVEs(vulnerabilities) confirm, err := c.prime.Prompt().Confirm("", locale.Tr("prompt_continue_pkg_operation"), ptr.To(false), ptr.To(true)) - if err != nil { - return errs.Wrap(err, "Unable to confirm") - } - if !confirm { - if !c.prime.Prompt().IsInteractive() { - return errs.AddTips( - locale.NewInputError("prompt_abort_non_interactive"), - locale.Tl("more_info_prompt", "To disable security prompting run: [ACTIONABLE]state config set security.prompt.enabled false[/RESET]"), - ) - } - return locale.NewInputError("err_pkgop_security_prompt", "Operation aborted by user") + if err == nil && !confirm { + err = locale.NewInputError("err_pkgop_security_prompt", "Operation aborted by user") } - if c.prime.Prompt().IsForceEnabled() { - c.prime.Output().Notice(locale.T("prompt_continue_force")) + if err != nil { + return errs.AddTips(err, + locale.Tl("more_info_prompt", "To disable security prompting run: [ACTIONABLE]state config set security.prompt.enabled false[/RESET]"), + ) } c.prime.Output().Notice("") // Empty line diff --git a/internal/runbits/findproject/project.go b/internal/runbits/findproject/project.go index b488365884..02c8ea0bba 100644 --- a/internal/runbits/findproject/project.go +++ b/internal/runbits/findproject/project.go @@ -99,7 +99,8 @@ func FromNamespaceLocal(ns *project.Namespaced, cfg projectfile.ConfigGetter, pr "", locale.Tl("project_select_namespace", "Multiple projects with that name were found. Please select one."), matchingNamespaces, - &namespace) + &namespace, + nil) if err != nil { return nil, locale.WrapError(err, "err_project_select_namespace", "Error selecting project") } @@ -117,7 +118,8 @@ func FromNamespaceLocal(ns *project.Namespaced, cfg projectfile.ConfigGetter, pr "", locale.Tl("project_select_path", "Multiple project paths for the selected project were found. Please select one."), paths, - &path) + &path, + nil) if err != nil { return nil, locale.WrapError(err, "err_project_select_path", "Error selecting project path") } diff --git a/internal/runners/clean/cache.go b/internal/runners/clean/cache.go index bbb87ede43..542a608869 100644 --- a/internal/runners/clean/cache.go +++ b/internal/runners/clean/cache.go @@ -12,6 +12,7 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/svcctl" "github.com/ActiveState/cli/pkg/projectfile" "github.com/ActiveState/cli/pkg/runtime_helpers" @@ -71,16 +72,13 @@ func (c *Cache) Run(params *CacheParams) error { func (c *Cache) removeCache(path string) error { defaultValue := !c.prime.Prompt().IsInteractive() - ok, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.T("clean_cache_confirm"), &defaultValue, nil) + ok, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.T("clean_cache_confirm"), &defaultValue, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Could not confirm") + return errs.Wrap(err, "Not confirmed") } if !ok { return locale.NewInputError("err_clean_cache_not_confirmed", "Cleaning of cache aborted by user") } - if !c.prime.Prompt().IsInteractive() { - c.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) - } inUse, err := c.checkPathInUse(path) if err != nil { @@ -101,16 +99,13 @@ func (c *Cache) removeCache(path string) error { func (c *Cache) removeProjectCache(projectDir, namespace string) error { defaultValue := !c.prime.Prompt().IsInteractive() - ok, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.Tr("clean_cache_artifact_confirm", namespace), &defaultValue, nil) + ok, err := c.prime.Prompt().Confirm(locale.T("confirm"), locale.Tr("clean_cache_artifact_confirm", namespace), &defaultValue, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Could not confirm") + return errs.Wrap(err, "Not confirmed") } if !ok { return locale.NewInputError("err_clean_cache_artifact_not_confirmed", "Cleaning of cached runtime aborted by user") } - if !c.prime.Prompt().IsInteractive() { - c.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) - } inUse, err := c.checkPathInUse(projectDir) if err != nil { diff --git a/internal/runners/clean/config.go b/internal/runners/clean/config.go index c0aeebc2dd..63252ed24d 100644 --- a/internal/runners/clean/config.go +++ b/internal/runners/clean/config.go @@ -56,17 +56,11 @@ func (c *Config) Run(params *ConfigParams) error { defaultChoice := !c.confirm.IsInteractive() ok, err := c.confirm.Confirm(locale.T("confirm"), locale.T("clean_config_confirm"), &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !ok { return locale.NewInputError("err_clean_config_aborted", "Cleaning of config aborted by user") } - switch { - case !c.confirm.IsInteractive(): - c.output.Notice(locale.T("prompt_continue_non_interactive")) - case c.confirm.IsForceEnabled(): - c.output.Notice(locale.T("prompt_continue_force")) - } if err := stopServices(c.cfg, c.output, c.ipComm, params.Force); err != nil { return errs.Wrap(err, "Failed to stop services.") diff --git a/internal/runners/clean/uninstall.go b/internal/runners/clean/uninstall.go index d8c671cc44..4aead3614c 100644 --- a/internal/runners/clean/uninstall.go +++ b/internal/runners/clean/uninstall.go @@ -70,7 +70,7 @@ func (u *Uninstall) Run(params *UninstallParams) error { locale.Tl("uninstall_prompt", "Uninstall the State Tool, but keep runtime cache and configuration files"), locale.Tl("uninstall_prompt_all", "Completely uninstall the State Tool, including runtime cache and configuration files"), } - selection, err := u.prompt.Select("", "", choices, new(string)) + selection, err := u.prompt.Select("", "", choices, ptr.To(""), nil) if err != nil { return locale.WrapError(err, "err_uninstall_prompt", "Could not read uninstall option") } @@ -85,17 +85,11 @@ func (u *Uninstall) Run(params *UninstallParams) error { } ok, err := u.prompt.Confirm(locale.T("confirm"), confirmMessage, &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !ok { return locale.NewInputError("err_uninstall_aborted", "Uninstall aborted by user") } - switch { - case !u.prompt.IsInteractive(): - u.out.Notice(locale.T("prompt_continue_non_interactive")) - case u.prompt.IsForceEnabled(): - u.out.Notice(locale.T("prompt_continue_force")) - } } if err := stopServices(u.cfg, u.out, u.ipComm, params.Force); err != nil { diff --git a/internal/runners/fork/fork.go b/internal/runners/fork/fork.go index f751900fa0..744a557d1c 100644 --- a/internal/runners/fork/fork.go +++ b/internal/runners/fork/fork.go @@ -6,6 +6,7 @@ import ( "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" @@ -104,7 +105,7 @@ func determineOwner(username string, prompter prompt.Prompter, auth *authenticat } options = append([]string{username}, options...) - r, err := prompter.Select(locale.Tl("fork_owner_title", "Owner"), locale.Tl("fork_select_org", "Who should the new project belong to?"), options, new(string)) + r, err := prompter.Select(locale.Tl("fork_owner_title", "Owner"), locale.Tl("fork_select_org", "Who should the new project belong to?"), options, ptr.To(""), nil) owner, exists := displayNameToURLNameMap[r] if !exists { return "", errs.New("Selected organization does not have a URL name") diff --git a/internal/runners/install/install.go b/internal/runners/install/install.go index 5724c7b580..28482abd36 100644 --- a/internal/runners/install/install.go +++ b/internal/runners/install/install.go @@ -319,7 +319,7 @@ func (i *Install) promptForMatchingIngredient(req *requirement, ingredients []*m choice, err := i.prime.Prompt().Select( locale.T("prompt_pkgop_ingredient"), locale.Tr("prompt_pkgop_ingredient_msg", req.Requested.String()), - choices, &choices[0], + choices, &choices[0], nil, ) if err != nil { return nil, errs.Wrap(err, "prompting failed") diff --git a/internal/runners/invite/invite.go b/internal/runners/invite/invite.go index c93aa1e6b7..ea9fd36249 100644 --- a/internal/runners/invite/invite.go +++ b/internal/runners/invite/invite.go @@ -11,6 +11,7 @@ import ( "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" @@ -106,7 +107,7 @@ func (i *invite) Run(params *Params, args []string) error { func (i *invite) promptForRole() (Role, error) { choices := roleNames() - selection, err := i.prompt.Select(locale.Tl("invite_role", "Role"), locale.Tl("invite_select_org_role", "What role should the user(s) be given?"), choices, new(string)) + selection, err := i.prompt.Select(locale.Tl("invite_role", "Role"), locale.Tl("invite_select_org_role", "What role should the user(s) be given?"), choices, ptr.To(""), nil) if err != nil { return -1, err } diff --git a/internal/runners/projects/delete.go b/internal/runners/projects/delete.go index 6c1aae3a7e..822a4a3977 100644 --- a/internal/runners/projects/delete.go +++ b/internal/runners/projects/delete.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/project" @@ -38,16 +39,13 @@ func (d *Delete) Run(params *DeleteParams) error { } defaultChoice := !d.prompt.IsInteractive() - confirm, err := d.prompt.Confirm("", locale.Tl("project_delete_confim", "Are you sure you want to delete the project {{.V0}}?", params.Project.String()), &defaultChoice, nil) + confirm, err := d.prompt.Confirm("", locale.Tl("project_delete_confim", "Are you sure you want to delete the project {{.V0}}?", params.Project.String()), &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !confirm { return locale.NewInputError("err_project_delete_aborted", "Delete aborted by user") } - if !d.prompt.IsInteractive() { - d.out.Notice(locale.T("prompt_continue_non_interactive")) - } err = model.DeleteProject(params.Project.Owner, params.Project.Project, d.auth) if err != nil { diff --git a/internal/runners/projects/edit.go b/internal/runners/projects/edit.go index 741d588f92..13b7cc1538 100644 --- a/internal/runners/projects/edit.go +++ b/internal/runners/projects/edit.go @@ -91,16 +91,13 @@ func (e *Edit) Run(params *EditParams) error { editMsg += locale.Tl("edit_prompt_confirm", "Continue?") defaultChoice := !e.prompt.IsInteractive() - edit, err := e.prompt.Confirm("", editMsg, &defaultChoice, nil) + edit, err := e.prompt.Confirm("", editMsg, &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !edit { return locale.NewInputError("edit_cancelled", "Project edit cancelled") } - if !e.prompt.IsInteractive() { - e.out.Notice(locale.T("prompt_continue_non_interactive")) - } if err = model.EditProject(params.Namespace.Owner, params.Namespace.Project, editable, e.auth); err != nil { return locale.WrapError(err, "err_edit_project", "Could not edit project") diff --git a/internal/runners/projects/move.go b/internal/runners/projects/move.go index 858dbead14..c67a7e06f2 100644 --- a/internal/runners/projects/move.go +++ b/internal/runners/projects/move.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" "github.com/ActiveState/cli/pkg/project" @@ -42,16 +43,13 @@ func (m *Move) Run(params *MoveParams) error { } defaultChoice := !m.prompt.IsInteractive() - move, err := m.prompt.Confirm("", locale.Tr("move_prompt", params.Namespace.String(), params.NewOwner, params.Namespace.Project), &defaultChoice, nil) + move, err := m.prompt.Confirm("", locale.Tr("move_prompt", params.Namespace.String(), params.NewOwner, params.Namespace.Project), &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !move { return locale.NewInputError("move_cancelled", "Project move aborted by user") } - if !m.prompt.IsInteractive() { - m.out.Notice(locale.T("prompt_continue_non_interactive")) - } if err = model.MoveProject(params.Namespace.Owner, params.Namespace.Project, params.NewOwner, m.auth); err != nil { return locale.WrapError(err, "err_move_project", "Could not move project") diff --git a/internal/runners/publish/publish.go b/internal/runners/publish/publish.go index 239a5233bb..a26797a8b7 100644 --- a/internal/runners/publish/publish.go +++ b/internal/runners/publish/publish.go @@ -227,14 +227,11 @@ func (r *Runner) Run(params *Params) error { Do you want to publish this ingredient? `, string(b)), ptr.To(true), nil) if err != nil { - return errs.Wrap(err, "Confirmation failed") + return errs.Wrap(err, "Not confirmed") } if !cont { return locale.NewInputError("uploadingredient_cancel", "Publish cancelled") } - if !r.prompt.IsInteractive() { - r.out.Notice(locale.T("prompt_continue_non_interactive")) - } r.out.Notice(locale.Tl("uploadingredient_uploading", "Publishing ingredient...")) @@ -447,7 +444,7 @@ func (r *Runner) OpenInEditor(pr *request.PublishVariables) error { } // Wait for confirmation - if _, err := r.prompt.Input("", locale.Tl("uploadingredient_edit_confirm", "Press enter when done editing"), ptr.To("")); err != nil { + if _, err := r.prompt.Input("", locale.Tl("uploadingredient_edit_confirm", "Press enter when done editing"), ptr.To(""), nil); err != nil { return errs.Wrap(err, "Confirmation failed") } diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index 8c828f2f13..6a2c069fa0 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -130,14 +130,11 @@ func (r *Push) Run(params PushParams) (rerr error) { if intend&pushFromNoPermission > 0 && !params.Namespace.IsValid() { createCopy, err := r.prompt.Confirm("", locale.T("push_prompt_not_authorized"), ptr.To(true), nil) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !createCopy { return nil } - if !r.prompt.IsInteractive() { - r.out.Notice(locale.T("prompt_continue_non_interactive")) - } } // Prompt for namespace IF: @@ -177,14 +174,11 @@ func (r *Push) Run(params PushParams) (rerr error) { locale.Tl("push_confirm_create_project", "You are about to create the project [NOTICE]{{.V0}}[/RESET]. Continue?", targetNamespace.String()), ptr.To(true), nil) if err != nil { - return errs.Wrap(err, "Confirmation failed") + return errs.Wrap(err, "Not confirmed") } if !createProject { return rationalize.ErrActionAborted } - if !r.prompt.IsInteractive() { - r.out.Notice(locale.T("prompt_continue_non_interactive")) - } } r.out.Notice(locale.Tl("push_creating_project", "Creating project [NOTICE]{{.V1}}[/RESET] under [NOTICE]{{.V0}}[/RESET] on the ActiveState Platform", targetNamespace.Owner, targetNamespace.Project)) @@ -331,7 +325,7 @@ func (r *Push) namespaceFromProject() (*project.Namespaced, error) { func (r *Push) promptNamespace() (*project.Namespaced, error) { owner := r.auth.WhoAmI() - owner, err := r.prompt.Input("", locale.T("push_prompt_owner"), &owner) + owner, err := r.prompt.Input("", locale.T("push_prompt_owner"), &owner, nil) if err != nil { return nil, locale.WrapError(err, "err_push_get_owner", "Could not determine project owner") } @@ -347,7 +341,7 @@ func (r *Push) promptNamespace() (*project.Namespaced, error) { logging.Debug("Error fetching language for commit: %v", err) } - name, err = r.prompt.Input("", locale.Tl("push_prompt_name", "What would you like the name of this project to be?"), &name) + name, err = r.prompt.Input("", locale.Tl("push_prompt_name", "What would you like the name of this project to be?"), &name, nil) if err != nil { return nil, locale.WrapError(err, "err_push_get_name", "Could not determine project name") } diff --git a/internal/runners/reset/reset.go b/internal/runners/reset/reset.go index 2f27cd1931..b1ada068f1 100644 --- a/internal/runners/reset/reset.go +++ b/internal/runners/reset/reset.go @@ -12,6 +12,7 @@ import ( "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/buildscript" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/runbits/runtime" @@ -117,16 +118,13 @@ func (r *Reset) Run(params *Params) error { r.out.Notice(locale.Tl("reset_commit", "Your project will be reset to [ACTIONABLE]{{.V0}}[/RESET]\n", commitID.String())) if commitID != localCommitID { defaultChoice := !r.prime.Prompt().IsInteractive() - confirm, err := r.prime.Prompt().Confirm("", locale.Tl("reset_confim", "Resetting is destructive. You will lose any changes that were not pushed. Are you sure you want to do this?"), &defaultChoice, nil) + confirm, err := r.prime.Prompt().Confirm("", locale.Tl("reset_confim", "Resetting is destructive. You will lose any changes that were not pushed. Are you sure you want to do this?"), &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !confirm { return locale.NewInputError("err_reset_aborted", "Reset aborted by user") } - if !r.prime.Prompt().IsInteractive() { - r.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) - } } err = localcommit.Set(r.project.Dir(), commitID.String()) diff --git a/internal/runners/revert/revert.go b/internal/runners/revert/revert.go index 0618528826..e5860c8a2d 100644 --- a/internal/runners/revert/revert.go +++ b/internal/runners/revert/revert.go @@ -10,6 +10,7 @@ import ( "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/primer" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/commit" "github.com/ActiveState/cli/internal/runbits/rationalize" runtime_runbit "github.com/ActiveState/cli/internal/runbits/runtime" @@ -139,16 +140,13 @@ func (r *Revert) Run(params *Params) (rerr error) { } defaultChoice := !r.prime.Prompt().IsInteractive() - revert, err := r.prime.Prompt().Confirm("", locale.Tl("revert_confirm", "Continue?"), &defaultChoice, nil) + revert, err := r.prime.Prompt().Confirm("", locale.Tl("revert_confirm", "Continue?"), &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !revert { return locale.NewInputError("err_revert_aborted", "Revert aborted by user") } - if !r.prime.Prompt().IsInteractive() { - r.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) - } revertCommit, err := revertFunc(revertParams, bp) if err != nil { diff --git a/internal/runners/scripts/edit.go b/internal/runners/scripts/edit.go index 59d74333c9..c33d4eb8e7 100644 --- a/internal/runners/scripts/edit.go +++ b/internal/runners/scripts/edit.go @@ -239,7 +239,7 @@ func startInteractive(sw *scriptWatcher, scriptName string, output output.Output for { doneEditing, err := prompt.Confirm("", locale.T("prompt_done_editing"), ptr.To(true), nil) if err != nil { - return errs.Wrap(err, "Prompter returned with failure.") + return errs.Wrap(err, "Not confirmed") } if doneEditing { sw.done <- true diff --git a/internal/runners/update/lock.go b/internal/runners/update/lock.go index 9ad15b52b9..1a6095dee0 100644 --- a/internal/runners/update/lock.go +++ b/internal/runners/update/lock.go @@ -11,6 +11,7 @@ import ( "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/updater" "github.com/ActiveState/cli/pkg/platform/model" @@ -72,7 +73,7 @@ func (l *Lock) Run(params *LockParams) error { l.out.Notice(locale.Tl("locking_version", "Locking State Tool version for current project.")) if l.project.IsLocked() { - if err := confirmLock(l.prompt, l.out); err != nil { + if err := confirmLock(l.prompt); err != nil { return locale.WrapError(err, "err_update_lock_confirm", "Could not confirm whether to lock update.") } } @@ -127,20 +128,17 @@ func (l *Lock) Run(params *LockParams) error { return nil } -func confirmLock(prom prompt.Prompter, out output.Outputer) error { +func confirmLock(prom prompt.Prompter) error { defaultChoice := !prom.IsInteractive() msg := locale.T("confirm_update_locked_version_prompt") - confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) + confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !confirmed { return locale.NewInputError("err_update_lock_noconfirm", "Cancelling by your request.") } - if !prom.IsInteractive() { - out.Notice(locale.T("prompt_continue_non_interactive")) - } return nil } diff --git a/internal/runners/update/unlock.go b/internal/runners/update/unlock.go index d3d5e99921..8bf427fe5c 100644 --- a/internal/runners/update/unlock.go +++ b/internal/runners/update/unlock.go @@ -7,6 +7,7 @@ import ( "github.com/ActiveState/cli/internal/multilog" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/internal/updater" "github.com/ActiveState/cli/pkg/project" @@ -44,7 +45,7 @@ func (u *Unlock) Run(params *UnlockParams) error { u.out.Notice(locale.Tl("unlocking_version", "Unlocking State Tool version for current project.")) - err := confirmUnlock(u.prompt, u.out) + err := confirmUnlock(u.prompt) if err != nil { return locale.WrapError(err, "err_update_unlock_confirm", "Unlock cancelled by user.") } @@ -69,20 +70,17 @@ func (u *Unlock) Run(params *UnlockParams) error { return nil } -func confirmUnlock(prom prompt.Prompter, out output.Outputer) error { +func confirmUnlock(prom prompt.Prompter) error { msg := locale.T("confirm_update_unlocked_version_prompt") defaultChoice := !prom.IsInteractive() - confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, nil) + confirmed, err := prom.Confirm(locale.T("confirm"), msg, &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !confirmed { return locale.NewInputError("err_update_lock_noconfirm", "Cancelling by your request.") } - if !prom.IsInteractive() { - out.Notice(locale.T("prompt_continue_non_interactive")) - } return nil } diff --git a/internal/runners/upgrade/upgrade.go b/internal/runners/upgrade/upgrade.go index 41f2207cf6..ed15303caf 100644 --- a/internal/runners/upgrade/upgrade.go +++ b/internal/runners/upgrade/upgrade.go @@ -285,14 +285,11 @@ func (u *Upgrade) renderUserFacing(changes []structuredChange, expand bool) erro out.Notice(" ") // Empty line (prompts use Notice) confirm, err := u.prime.Prompt().Confirm("", locale.Tr("upgrade_confirm"), ptr.To(true), nil) if err != nil { - return errs.Wrap(err, "confirmation failed") + return errs.Wrap(err, "Not confirmed") } if !confirm { return ErrAbort } - if !u.prime.Prompt().IsInteractive() { - u.prime.Output().Notice(locale.T("prompt_continue_non_interactive")) - } return nil } diff --git a/internal/runners/use/reset.go b/internal/runners/use/reset.go index 9fa5aa26e8..eed07e12e1 100644 --- a/internal/runners/use/reset.go +++ b/internal/runners/use/reset.go @@ -10,6 +10,7 @@ import ( "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/output" "github.com/ActiveState/cli/internal/prompt" + "github.com/ActiveState/cli/internal/rtutils/ptr" "github.com/ActiveState/cli/internal/subshell" ) @@ -41,16 +42,13 @@ func (u *Reset) Run(params *ResetParams) error { defaultChoice := !u.prompt.IsInteractive() ok, err := u.prompt.Confirm(locale.T("confirm"), - locale.Tl("use_reset_confirm", "You are about to stop using your project runtime. Continue?"), &defaultChoice, nil) + locale.Tl("use_reset_confirm", "You are about to stop using your project runtime. Continue?"), &defaultChoice, ptr.To(true)) if err != nil { - return errs.Wrap(err, "Unable to confirm") + return errs.Wrap(err, "Not confirmed") } if !ok { return locale.NewInputError("err_reset_aborted", "Reset aborted by user") } - if !u.prompt.IsInteractive() { - u.out.Notice(locale.T("prompt_continue_non_interactive")) - } reset, err := globaldefault.ResetDefaultActivation(u.subshell, u.config) if err != nil { From ecffc293409de1b9df0a4a64da7f4891587db845 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 10:31:06 -0500 Subject: [PATCH 404/440] Set prompt interactive and force modes immediately upon parsing flags. Commands should not be responsible for setting these modes before invoking runners. --- cmd/state/internal/cmdtree/clean.go | 18 +++--------------- cmd/state/internal/cmdtree/cmdtree.go | 20 +++++++++----------- cmd/state/internal/cmdtree/packages.go | 5 +---- cmd/state/internal/cmdtree/reset.go | 5 +---- cmd/state/internal/cmdtree/revert.go | 5 +---- cmd/state/internal/cmdtree/update.go | 10 ++-------- cmd/state/internal/cmdtree/use.go | 5 +---- internal/captain/command.go | 3 +++ internal/prompt/prompt.go | 13 +++++++++++-- internal/runbits/auth/login.go | 11 +++++------ internal/runners/auth/auth.go | 2 +- 11 files changed, 38 insertions(+), 59 deletions(-) diff --git a/cmd/state/internal/cmdtree/clean.go b/cmd/state/internal/cmdtree/clean.go index 988ef3cd6c..4f25dd7570 100644 --- a/cmd/state/internal/cmdtree/clean.go +++ b/cmd/state/internal/cmdtree/clean.go @@ -53,19 +53,13 @@ func newCleanUninstallCommand(prime *primer.Values, globals *globalOptions) *cap return err } - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } - if globals.Force { - prime.Prompt().SetForce(true) - params.Force = true - } + params.Force = globals.Force return runner.Run(¶ms) }, ) } -func newCleanCacheCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newCleanCacheCommand(prime *primer.Values) *captain.Command { runner := clean.NewCache(prime) params := clean.CacheParams{} return captain.NewCommand( @@ -83,9 +77,6 @@ func newCleanCacheCommand(prime *primer.Values, globals *globalOptions) *captain }, }, func(ccmd *captain.Command, _ []string) error { - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } return runner.Run(¶ms) }, ) @@ -102,10 +93,7 @@ func newCleanConfigCommand(prime *primer.Values, globals *globalOptions) *captai []*captain.Flag{}, []*captain.Argument{}, func(ccmd *captain.Command, _ []string) error { - if globals.Force { - prime.Prompt().SetForce(true) - params.Force = true - } + params.Force = globals.Force return runner.Run(¶ms) }, ) diff --git a/cmd/state/internal/cmdtree/cmdtree.go b/cmd/state/internal/cmdtree/cmdtree.go index 730fc65f3c..739a3cd727 100644 --- a/cmd/state/internal/cmdtree/cmdtree.go +++ b/cmd/state/internal/cmdtree/cmdtree.go @@ -79,7 +79,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { cleanCmd := newCleanCommand(prime) cleanCmd.AddChildren( newCleanUninstallCommand(prime, globals), - newCleanCacheCommand(prime, globals), + newCleanCacheCommand(prime), newCleanConfigCommand(prime, globals), ) @@ -95,7 +95,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { eventsCmd := newEventsCommand(prime) eventsCmd.AddChildren(newEventsLogCommand(prime)) - installCmd := newInstallCommand(prime, globals) + installCmd := newInstallCommand(prime) uninstallCmd := newUninstallCommand(prime) importCmd := newImportCommand(prime, globals) searchCmd := newSearchCommand(prime) @@ -137,8 +137,8 @@ func New(prime *primer.Values, args ...string) *CmdTree { updateCmd := newUpdateCommand(prime) updateCmd.AddChildren( - newUpdateLockCommand(prime, globals), - newUpdateUnlockCommand(prime, globals)) + newUpdateLockCommand(prime), + newUpdateUnlockCommand(prime)) branchCmd := newBranchCommand(prime) branchCmd.AddChildren( @@ -157,7 +157,7 @@ func New(prime *primer.Values, args ...string) *CmdTree { useCmd := newUseCommand(prime) useCmd.AddChildren( - newUseResetCommand(prime, globals), + newUseResetCommand(prime), newUseShowCommand(prime), ) @@ -204,8 +204,8 @@ func New(prime *primer.Values, args ...string) *CmdTree { prepareCmd, newProtocolCommand(prime), newExecCommand(prime, args...), - newRevertCommand(prime, globals), - newResetCommand(prime, globals), + newRevertCommand(prime), + newResetCommand(prime), secretsCmd, branchCmd, newLearnCommand(prime), @@ -302,12 +302,14 @@ func newStateCommand(globals *globalOptions, prime *primer.Values) *captain.Comm Description: locale.T("flag_state_non_interactive_description"), Shorthand: "n", Persist: true, + OnUse: func() { prime.Prompt().SetInteractive(false) }, Value: &globals.NonInteractive, }, { Name: "force", Description: locale.T("flag_state_force_description"), Persist: true, + OnUse: func() { prime.Prompt().SetForce(true) }, Value: &globals.Force, }, { @@ -325,10 +327,6 @@ func newStateCommand(globals *globalOptions, prime *primer.Values) *captain.Comm }, []*captain.Argument{}, func(ccmd *captain.Command, args []string) error { - if globals.Verbose { - logging.CurrentHandler().SetVerbose(true) - } - return runner.Run(ccmd.Usage) }, ) diff --git a/cmd/state/internal/cmdtree/packages.go b/cmd/state/internal/cmdtree/packages.go index a0d2d55174..636dc02dc3 100644 --- a/cmd/state/internal/cmdtree/packages.go +++ b/cmd/state/internal/cmdtree/packages.go @@ -50,7 +50,7 @@ func newPackagesCommand(prime *primer.Values) *captain.Command { return cmd } -func newInstallCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newInstallCommand(prime *primer.Values) *captain.Command { runner := install.New(prime, model.NamespacePackage) params := install.Params{} @@ -82,9 +82,6 @@ func newInstallCommand(prime *primer.Values, globals *globalOptions) *captain.Co return locale.WrapInputError(err, "err_install_packages_args", "Invalid install arguments") } } - if globals.Force { - prime.Prompt().SetForce(true) - } return runner.Run(params) }, ) diff --git a/cmd/state/internal/cmdtree/reset.go b/cmd/state/internal/cmdtree/reset.go index 2a86485ab7..b27f63b9d6 100644 --- a/cmd/state/internal/cmdtree/reset.go +++ b/cmd/state/internal/cmdtree/reset.go @@ -7,7 +7,7 @@ import ( "github.com/ActiveState/cli/internal/runners/reset" ) -func newResetCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newResetCommand(prime *primer.Values) *captain.Command { runner := reset.New(prime) params := &reset.Params{} @@ -25,9 +25,6 @@ func newResetCommand(prime *primer.Values, globals *globalOptions) *captain.Comm }, }, func(ccmd *captain.Command, args []string) error { - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } return runner.Run(params) }, ).SetGroup(VCSGroup).SetSupportsStructuredOutput() diff --git a/cmd/state/internal/cmdtree/revert.go b/cmd/state/internal/cmdtree/revert.go index 6150d53bf1..4df0315a21 100644 --- a/cmd/state/internal/cmdtree/revert.go +++ b/cmd/state/internal/cmdtree/revert.go @@ -7,7 +7,7 @@ import ( "github.com/ActiveState/cli/internal/runners/revert" ) -func newRevertCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newRevertCommand(prime *primer.Values) *captain.Command { runner := revert.New(prime) params := &revert.Params{} @@ -32,9 +32,6 @@ func newRevertCommand(prime *primer.Values, globals *globalOptions) *captain.Com }, }, func(ccmd *captain.Command, args []string) error { - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } return runner.Run(params) }, ).SetGroup(VCSGroup).SetSupportsStructuredOutput() diff --git a/cmd/state/internal/cmdtree/update.go b/cmd/state/internal/cmdtree/update.go index 45f20cab21..89ccef4a73 100644 --- a/cmd/state/internal/cmdtree/update.go +++ b/cmd/state/internal/cmdtree/update.go @@ -34,7 +34,7 @@ func newUpdateCommand(prime *primer.Values) *captain.Command { return cmd } -func newUpdateLockCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newUpdateLockCommand(prime *primer.Values) *captain.Command { runner := update.NewLock(prime) params := update.LockParams{} @@ -52,9 +52,6 @@ func newUpdateLockCommand(prime *primer.Values, globals *globalOptions) *captain }, []*captain.Argument{}, func(cmd *captain.Command, args []string) error { - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } return runner.Run(¶ms) }, ) @@ -63,7 +60,7 @@ func newUpdateLockCommand(prime *primer.Values, globals *globalOptions) *captain return cmd } -func newUpdateUnlockCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newUpdateUnlockCommand(prime *primer.Values) *captain.Command { runner := update.NewUnlock(prime) params := update.UnlockParams{} @@ -75,9 +72,6 @@ func newUpdateUnlockCommand(prime *primer.Values, globals *globalOptions) *capta []*captain.Flag{}, []*captain.Argument{}, func(cmd *captain.Command, args []string) error { - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } return runner.Run(¶ms) }, ) diff --git a/cmd/state/internal/cmdtree/use.go b/cmd/state/internal/cmdtree/use.go index 5c4bd8d9bf..b6cfe24aa2 100644 --- a/cmd/state/internal/cmdtree/use.go +++ b/cmd/state/internal/cmdtree/use.go @@ -33,7 +33,7 @@ func newUseCommand(prime *primer.Values) *captain.Command { return cmd } -func newUseResetCommand(prime *primer.Values, globals *globalOptions) *captain.Command { +func newUseResetCommand(prime *primer.Values) *captain.Command { params := &use.ResetParams{} return captain.NewCommand( @@ -44,9 +44,6 @@ func newUseResetCommand(prime *primer.Values, globals *globalOptions) *captain.C []*captain.Flag{}, []*captain.Argument{}, func(_ *captain.Command, _ []string) error { - if globals.NonInteractive { - prime.Prompt().SetInteractive(false) - } return use.NewReset(prime).Run(params) }, ) diff --git a/internal/captain/command.go b/internal/captain/command.go index aa5fd383f3..16a25bc252 100644 --- a/internal/captain/command.go +++ b/internal/captain/command.go @@ -521,6 +521,9 @@ func (c *Command) flagByName(name string, persistOnly bool) *Flag { return flag } } + if c.parent != nil { + return c.parent.flagByName(name, persistOnly) + } return nil } diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 9a591a9ef2..5ec5bb709f 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -28,6 +28,7 @@ type Prompter interface { IsInteractive() bool SetInteractive(bool) SetForce(bool) + IsForced() bool } // ValidatorFunc is a function pass to the Prompter to perform validation @@ -80,6 +81,8 @@ const ( ) // Input prompts the user for input. The user can specify available validation flags to trigger validation of responses +// If the prompt is non-interactive, it returns defaultResponse. +// If the prompt is forced, it returns forcedResponse if not nil, or defaultResponse. func (p *Prompt) Input(title, message string, defaultResponse *string, forcedResponse *string, flags ...ValidatorFlag) (string, error) { return p.InputAndValidate(title, message, defaultResponse, forcedResponse, func(val interface{}) error { return nil @@ -98,6 +101,8 @@ func interactiveInputError(message string) error { } // InputAndValidate prompts an input field and allows you to specfiy a custom validation function as well as the built in flags +// If the prompt is non-interactive, it returns defaultResponse. +// If the prompt is forced, it returns forcedResponse if not nil, or defaultResponse. func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) { if p.isForced { response := forcedResponse @@ -154,7 +159,9 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string return response, nil } -// Select prompts the user to select one entry from multiple choices +// Select prompts the user to select one entry from multiple choices. +// If the prompt is non-interactive, it returns defaultChoice. +// If the prompt is forced, it returns forcedChoice if not nil, or defaultChoice. func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string, forcedChoice *string) (string, error) { if p.isForced { choice := forcedChoice @@ -199,6 +206,8 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * } // Confirm prompts user for yes or no response. +// If the prompt is non-interactive, it returns defaultChoice. +// If the prompt is forced, it returns forcedChoice if not nil, or defaultChoice. func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) { if p.isForced { choice := forcedChoice @@ -259,7 +268,7 @@ func translateConfirm(confirm bool) string { // InputSecret prompts the user for input and obfuscates the text in stdout. // Will fail if empty. func (p *Prompt) InputSecret(title, message string, flags ...ValidatorFlag) (string, error) { - if !p.isInteractive { + if !p.isInteractive || p.isForced { return "", interactiveInputError(message) } var response string diff --git a/internal/runbits/auth/login.go b/internal/runbits/auth/login.go index 5c55e2fed9..aa7cb9ad4a 100644 --- a/internal/runbits/auth/login.go +++ b/internal/runbits/auth/login.go @@ -29,13 +29,12 @@ var OpenURI = osutils.OpenURI // Authenticate will prompt the user for authentication func Authenticate(cfg keypairs.Configurable, out output.Outputer, prompt prompt.Prompter, auth *authentication.Auth) error { - return AuthenticateWithInput("", "", "", false, cfg, out, prompt, auth) + return AuthenticateWithInput("", "", "", cfg, out, prompt, auth) } // AuthenticateWithInput will prompt the user for authentication if the input doesn't already provide it func AuthenticateWithInput( username, password, totp string, - nonInteractive bool, cfg keypairs.Configurable, out output.Outputer, prompt prompt.Prompter, @@ -44,7 +43,7 @@ func AuthenticateWithInput( logging.Debug("Authenticating with input") credentials := &mono_models.Credentials{Username: username, Password: password, Totp: totp} - if err := ensureCredentials(credentials, prompt, nonInteractive); err != nil { + if err := ensureCredentials(credentials, prompt); err != nil { return locale.WrapInputError(err, "login_cancelled") } @@ -133,10 +132,10 @@ func RequireAuthentication(message string, cfg keypairs.Configurable, out output return nil } -func ensureCredentials(credentials *mono_models.Credentials, prompter prompt.Prompter, nonInteractive bool) error { +func ensureCredentials(credentials *mono_models.Credentials, prompter prompt.Prompter) error { var err error if credentials.Username == "" { - if nonInteractive { + if !prompter.IsInteractive() || prompter.IsForced() { return locale.NewInputError("err_auth_needinput") } credentials.Username, err = prompter.Input("", locale.T("username_prompt"), ptr.To(""), nil, prompt.InputRequired) @@ -146,7 +145,7 @@ func ensureCredentials(credentials *mono_models.Credentials, prompter prompt.Pro } if credentials.Password == "" { - if nonInteractive { + if !prompter.IsInteractive() || prompter.IsForced() { return locale.NewInputError("err_auth_needinput") } credentials.Password, err = prompter.InputSecret("", locale.T("password_prompt"), prompt.InputRequired) diff --git a/internal/runners/auth/auth.go b/internal/runners/auth/auth.go index 6edac9b48e..47555bdae5 100644 --- a/internal/runners/auth/auth.go +++ b/internal/runners/auth/auth.go @@ -84,7 +84,7 @@ func (a *Auth) Run(params *AuthParams) error { func (a *Auth) authenticate(params *AuthParams) error { if params.Prompt || params.Username != "" { - return auth.AuthenticateWithInput(params.Username, params.Password, params.Totp, params.NonInteractive, a.Cfg, a.Outputer, a.Prompter, a.Auth) + return auth.AuthenticateWithInput(params.Username, params.Password, params.Totp, a.Cfg, a.Outputer, a.Prompter, a.Auth) } if params.Token != "" { From a6b2d52f6a5573f229f8e88cf36240d0d83c200c Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 11:41:07 -0500 Subject: [PATCH 405/440] Consolidate non-interactive mode setting and detection. --- cmd/state-installer/installer.go | 2 +- cmd/state-remote-installer/main.go | 2 +- cmd/state/main.go | 10 +++------- internal/constants/constants.go | 3 --- internal/prompt/prompt.go | 4 ++-- test/integration/edit_int_test.go | 7 ++----- 6 files changed, 9 insertions(+), 19 deletions(-) diff --git a/cmd/state-installer/installer.go b/cmd/state-installer/installer.go index 6deaadad41..7d168697e5 100644 --- a/cmd/state-installer/installer.go +++ b/cmd/state-installer/installer.go @@ -54,7 +54,7 @@ func (i *Installer) Install() (rerr error) { return errs.Wrap(err, "Could not determine if running as Windows administrator") } if isAdmin && !i.Params.isUpdate { - prompter := prompt.New(i.an) + prompter := prompt.New(i.out, i.an) if i.Params.nonInteractive { prompter.SetInteractive(false) } diff --git a/cmd/state-remote-installer/main.go b/cmd/state-remote-installer/main.go index 29b1b6cfc1..7aa6a5653f 100644 --- a/cmd/state-remote-installer/main.go +++ b/cmd/state-remote-installer/main.go @@ -116,7 +116,7 @@ func main() { an = sync.New(anaConst.SrcStateRemoteInstaller, cfg, nil, out) // Set up prompter - prompter := prompt.New(an) + prompter := prompt.New(out, an) params := newParams() cmd := captain.NewCommand( diff --git a/cmd/state/main.go b/cmd/state/main.go index 78daa39eb7..b507525845 100644 --- a/cmd/state/main.go +++ b/cmd/state/main.go @@ -110,9 +110,8 @@ func main() { // Set up our legacy outputer setPrinterColors(outFlags) - isInteractive := strings.ToLower(os.Getenv(constants.NonInteractiveEnvVarName)) != "true" && out.Config().Interactive // Run our main command logic, which is logic that defers to the error handling logic below - err = run(os.Args, isInteractive, cfg, out) + err = run(os.Args, cfg, out) if err != nil { exitCode, err = runbits_errors.ParseUserFacing(err) if err != nil { @@ -121,7 +120,7 @@ func main() { } } -func run(args []string, isInteractive bool, cfg *config.Instance, out output.Outputer) (rerr error) { +func run(args []string, cfg *config.Instance, out output.Outputer) (rerr error) { defer profile.Measure("main:run", time.Now()) // Set up profiling @@ -224,10 +223,7 @@ func run(args []string, isInteractive bool, cfg *config.Instance, out output.Out }() // Set up prompter - prompter := prompt.New(an) - if !isInteractive { - prompter.SetInteractive(false) - } + prompter := prompt.New(out, an) // Set up conditional, which accesses a lot of primer data sshell := subshell.New(cfg) diff --git a/internal/constants/constants.go b/internal/constants/constants.go index f64eb5650d..61b3adf8ef 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -118,9 +118,6 @@ const OverrideSessionTokenEnvVarName = "ACTIVESTATE_OVERRIDE_SESSION_TOKEN" // UpdateTagEnvVarName const UpdateTagEnvVarName = "ACTIVESTATE_UPDATE_TAG" -// NonInteractiveEnvVarName is the name of the environment variable that specifies whether to run the State Tool without prompts -const NonInteractiveEnvVarName = "ACTIVESTATE_NONINTERACTIVE" - // E2ETestEnvVarName is the name of the environment variable that specifies that we are running under E2E tests const E2ETestEnvVarName = "ACTIVESTATE_E2E_TEST" diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 5ec5bb709f..61b67cc0b8 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -46,8 +46,8 @@ type Prompt struct { } // New creates a new prompter -func New(an EventDispatcher) Prompter { - return &Prompt{output.Get(), an, true, false} +func New(out output.Outputer, an EventDispatcher) Prompter { + return &Prompt{out, an, out.Config().Interactive, false} } // IsInteractive checks if the prompts can be interactive or should just return default values diff --git a/test/integration/edit_int_test.go b/test/integration/edit_int_test.go index 1b144d618f..3e154428ed 100644 --- a/test/integration/edit_int_test.go +++ b/test/integration/edit_int_test.go @@ -8,12 +8,10 @@ import ( "testing" "time" - "github.com/ActiveState/cli/internal/testhelpers/suite" - - "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/environment" "github.com/ActiveState/cli/internal/fileutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" + "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" "github.com/ActiveState/cli/pkg/project" ) @@ -76,9 +74,8 @@ func (suite *EditIntegrationTestSuite) TestEdit_NonInteractive() { } ts, env := suite.setup() defer ts.Close() - extraEnv := e2e.OptAppendEnv(constants.NonInteractiveEnvVarName + "=true") - cp := ts.SpawnWithOpts(e2e.OptArgs("scripts", "edit", "test-script"), env, extraEnv) + cp := ts.SpawnWithOpts(e2e.OptArgs("scripts", "edit", "test-script", "-n"), env) cp.Expect("Watching file changes") // Can't consistently get this line detected on CI cp.Expect("Script changes detected") From bf58cd5640b1edb0748252c833d6dfe197f935a7 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 09:31:17 -0800 Subject: [PATCH 406/440] Use correct state tool --- test/integration/notification_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/notification_int_test.go b/test/integration/notification_int_test.go index 7dc288984e..4e9f2f8f4c 100644 --- a/test/integration/notification_int_test.go +++ b/test/integration/notification_int_test.go @@ -241,7 +241,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptP cp.ExpectExitCode(0) // Test that non-interactive does not prompt - cp = ts.SpawnCmdWithOpts("state", e2e.OptArgs("--version", "-n"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) + cp = ts.SpawnWithOpts(e2e.OptArgs("--version", "-n"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) cp.Expect(`This is a simple notification`) cp.Expect("ActiveState CLI by ActiveState Software Inc.") cp.ExpectExitCode(0) From 06643afcb93a1a4dbe686afacb5033d1b3691850 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 10:02:09 -0800 Subject: [PATCH 407/440] Revert config change --- cmd/state-svc/gqlgen.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/state-svc/gqlgen.yml b/cmd/state-svc/gqlgen.yml index 476087864a..0c6a28fb70 100644 --- a/cmd/state-svc/gqlgen.yml +++ b/cmd/state-svc/gqlgen.yml @@ -39,8 +39,8 @@ model: # gqlgen will search for any type names in the schema in these go packages # if they match it will use them, otherwise it will generate them. -# autobind: -# - "github.com/ActiveState/cli/internal/graph" +autobind: + - "github.com/ActiveState/cli/internal/graph" # This section declares type mapping between the GraphQL and go type systems # From 6a801e888e4052a295f2780ecb9535ce2671bcb9 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 10:03:24 -0800 Subject: [PATCH 408/440] Change constant --- cmd/state-svc/internal/notifications/notifications.go | 2 +- internal/constants/constants.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/state-svc/internal/notifications/notifications.go b/cmd/state-svc/internal/notifications/notifications.go index 6d65b35393..7b9174d98c 100644 --- a/cmd/state-svc/internal/notifications/notifications.go +++ b/cmd/state-svc/internal/notifications/notifications.go @@ -208,7 +208,7 @@ func fetch() ([]*graph.NotificationInfo, error) { return nil, errs.Wrap(err, "Could not read messages override file") } } else { - body, err = httputil.Get(constants.MessagesInfoURL) + body, err = httputil.Get(constants.NotificationsInfoURL) if err != nil { return nil, errs.Wrap(err, "Could not fetch messages information") } diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 56bd48c1a3..09e979157d 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -264,8 +264,8 @@ const VulnerabilitiesAPIPath = "/v13s/v1/graphql" // HasuraInventoryAPIPath is the path used for the hasura inventory api const HasuraInventoryAPIPath = "/sv/hasura-inventory/v1/graphql" -// MessagesInfoURL is the URL we check against to see what versions are deprecated -const MessagesInfoURL = "https://state-tool.s3.amazonaws.com/messages.json" +// NotificationsInfoURL is the URL we check against to see what versions are deprecated +const NotificationsInfoURL = "https://state-tool.s3.amazonaws.com/notifications.json" // DateFormatUser is the date format we use when communicating with the end-user const DateFormatUser = "January 02, 2006" From e091792665c2dc623a7e29603fb5d5b5434c6191 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 10:08:11 -0800 Subject: [PATCH 409/440] Convert remaining --- .../internal/notifications/condition.go | 2 +- .../internal/notifications/notifications.go | 10 +++++----- .../cmdtree/exechandlers/notifier/notifier.go | 20 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cmd/state-svc/internal/notifications/condition.go b/cmd/state-svc/internal/notifications/condition.go index 2645dd98ea..3e3aae510f 100644 --- a/cmd/state-svc/internal/notifications/condition.go +++ b/cmd/state-svc/internal/notifications/condition.go @@ -57,7 +57,7 @@ func conditionFuncMap() template.FuncMap { "regexMatch": func(str, pattern string) bool { rx, err := regexp.Compile(pattern) if err != nil { - multilog.Error("Messages: Could not compile regex pattern: %s", err) + multilog.Error("Notifications: Could not compile regex pattern: %s", err) return false } return rx.MatchString(str) diff --git a/cmd/state-svc/internal/notifications/notifications.go b/cmd/state-svc/internal/notifications/notifications.go index 7b9174d98c..59fd16fb58 100644 --- a/cmd/state-svc/internal/notifications/notifications.go +++ b/cmd/state-svc/internal/notifications/notifications.go @@ -23,7 +23,7 @@ import ( "github.com/blang/semver" ) -const ConfigKeyLastReport = "messages.last_reported" +const ConfigKeyLastReport = "notifications.last_reported" type Notifications struct { cfg *config.Instance @@ -100,7 +100,7 @@ func (m *Notifications) Check(command string, flags []string) ([]*graph.Notifica lastReportMap := m.cfg.GetStringMap(ConfigKeyLastReport) msgs, err := check(&conditionParams, allNotifications, lastReportMap, time.Now()) if err != nil { - return nil, errs.Wrap(err, "Could not check messages") + return nil, errs.Wrap(err, "Could not check notifications") } for _, msg := range msgs { lastReportMap[msg.ID] = time.Now().Format(time.RFC3339) @@ -205,18 +205,18 @@ func fetch() ([]*graph.NotificationInfo, error) { if v := os.Getenv(constants.NotificationsOverrideEnvVarName); v != "" { body, err = fileutils.ReadFile(v) if err != nil { - return nil, errs.Wrap(err, "Could not read messages override file") + return nil, errs.Wrap(err, "Could not read notifications override file") } } else { body, err = httputil.Get(constants.NotificationsInfoURL) if err != nil { - return nil, errs.Wrap(err, "Could not fetch messages information") + return nil, errs.Wrap(err, "Could not fetch notifications information") } } var notifications []*graph.NotificationInfo if err := json.Unmarshal(body, ¬ifications); err != nil { - return nil, errs.Wrap(err, "Could not unmarshall messages information") + return nil, errs.Wrap(err, "Could not unmarshall notifications information") } // Set defaults diff --git a/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go b/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go index 4c670a612a..30927dc40c 100644 --- a/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go +++ b/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go @@ -31,12 +31,12 @@ func New(out output.Outputer, svcModel *model.SvcModel) *Notifier { func (m *Notifier) OnExecStart(cmd *captain.Command, _ []string) error { if m.out.Type().IsStructured() { - // No point showing messaging on non-plain output (eg. json) + // No point showing notifications on non-plain output (eg. json) return nil } if cmd.Name() == "update" { - return nil // do not print update/deprecation warnings/messages when running `state update` + return nil // do not print update/deprecation warnings/notifications when running `state update` } cmds := cmd.JoinedCommandNames() @@ -52,7 +52,7 @@ func (m *Notifier) OnExecStart(cmd *captain.Command, _ []string) error { logging.Debug("Received %d notifications to print", len(notifications)) if err := m.PrintByPlacement(graph.NotificationPlacementTypeBeforeCmd); err != nil { - return errs.Wrap(err, "message error occurred before cmd") + return errs.Wrap(err, "notification error occurred before cmd") } return nil @@ -60,16 +60,16 @@ func (m *Notifier) OnExecStart(cmd *captain.Command, _ []string) error { func (m *Notifier) OnExecStop(cmd *captain.Command, _ []string) error { if m.out.Type().IsStructured() { - // No point showing messaging on non-plain output (eg. json) + // No point showing notification on non-plain output (eg. json) return nil } if cmd.Name() == "update" { - return nil // do not print update/deprecation warnings/messages when running `state update` + return nil // do not print update/deprecation warnings/notifications when running `state update` } if err := m.PrintByPlacement(graph.NotificationPlacementTypeAfterCmd); err != nil { - return errs.Wrap(err, "message error occurred before cmd") + return errs.Wrap(err, "notification error occurred before cmd") } return nil @@ -99,10 +99,10 @@ func (m *Notifier) PrintByPlacement(placement graph.NotificationPlacementType) e if notification.Interrupt == graph.NotificationInterruptTypePrompt { if m.out.Config().Interactive { - m.out.Print(locale.Tl("messenger_prompt_continue", "Press ENTER to continue.")) + m.out.Print(locale.Tl("notifier_prompt_continue", "Press ENTER to continue.")) fmt.Scanln(ptr.To("")) // Wait for input from user } else { - logging.Debug("Skipping message prompt as we're not in interactive mode") + logging.Debug("Skipping notification prompt as we're not in interactive mode") } } @@ -114,9 +114,9 @@ func (m *Notifier) PrintByPlacement(placement graph.NotificationPlacementType) e m.notifications = notifications if len(exit) > 0 { - // It's the responsibility of the message to give the user context as to why this exit happened. + // It's the responsibility of the notification to give the user context as to why this exit happened. // We pass an input error here to ensure this doesn't get logged. - return errs.Silence(errs.WrapExitCode(errs.New("Following messages triggered exit: %s", strings.Join(exit, ", ")), 1)) + return errs.Silence(errs.WrapExitCode(errs.New("Following notifications triggered exit: %s", strings.Join(exit, ", ")), 1)) } return nil From da5fee5f07321b6400f2d1e89eec3831a64376aa Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 13:47:57 -0500 Subject: [PATCH 410/440] Recurse into recursively linked directories not more than once. This results in a destination without directory links, which are not supported on Windows. --- internal/smartlink/smartlink.go | 33 ++++++++++--------------- internal/smartlink/smartlink_lin_mac.go | 6 +++++ internal/smartlink/smartlink_test.go | 30 +++++++++++----------- pkg/runtime/depot.go | 2 +- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/internal/smartlink/smartlink.go b/internal/smartlink/smartlink.go index 49fbe2d01d..3df90034dd 100644 --- a/internal/smartlink/smartlink.go +++ b/internal/smartlink/smartlink.go @@ -10,7 +10,7 @@ import ( ) // LinkContents will link the contents of src to desc -func LinkContents(src, dest string, visited map[string]bool) error { +func LinkContents(src, dest string) error { if !fileutils.DirExists(src) { return errs.New("src dir does not exist: %s", src) } @@ -24,23 +24,12 @@ func LinkContents(src, dest string, visited map[string]bool) error { return errs.Wrap(err, "Could not resolve src and dest paths") } - if visited == nil { - visited = make(map[string]bool) - } - if _, exists := visited[src]; exists { - // We've encountered a recursive link. This is most often the case when the resolved src has - // already been visited. In that case, just link the dest to the src (which may be a directory; - // this is fine). - return linkFile(src, dest) - } - visited[src] = true - entries, err := os.ReadDir(src) if err != nil { return errs.Wrap(err, "Reading dir %s failed", src) } for _, entry := range entries { - if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil { + if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), nil); err != nil { return errs.Wrap(err, "Link failed") } } @@ -50,7 +39,7 @@ func LinkContents(src, dest string, visited map[string]bool) error { // Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure), // which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks. -func Link(src, dest string, visited map[string]bool) error { +func Link(src, dest string, visited map[string]int) error { var err error src, dest, err = resolvePaths(src, dest) if err != nil { @@ -58,15 +47,19 @@ func Link(src, dest string, visited map[string]bool) error { } if visited == nil { - visited = make(map[string]bool) + visited = make(map[string]int) } - if _, exists := visited[src]; exists { + if count, exists := visited[src]; exists { // We've encountered a recursive link. This is most often the case when the resolved src has - // already been visited. In that case, just link the dest to the src (which may be a directory; - // this is fine). - return linkFile(src, dest) + // already been visited. We will recurse into the directory no more than once, so that any + // runtime paths that reference the link will not silently fail. + if count > 1 { + return nil + } + visited[src]++ + } else { + visited[src] = 1 } - visited[src] = true if fileutils.IsDir(src) { if err := fileutils.Mkdir(dest); err != nil { diff --git a/internal/smartlink/smartlink_lin_mac.go b/internal/smartlink/smartlink_lin_mac.go index edc065f280..b19f45d411 100644 --- a/internal/smartlink/smartlink_lin_mac.go +++ b/internal/smartlink/smartlink_lin_mac.go @@ -5,10 +5,16 @@ package smartlink import ( "os" + + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/fileutils" ) // file will create a symlink from src to dest, and falls back on a hardlink if no symlink is available. // This is a workaround for the fact that Windows does not support symlinks without admin privileges. func linkFile(src, dest string) error { + if fileutils.IsDir(src) { + return errs.New("src is a directory, not a file: %s", src) + } return os.Symlink(src, dest) } diff --git a/internal/smartlink/smartlink_test.go b/internal/smartlink/smartlink_test.go index 5b52fbb0d3..1222a0413d 100644 --- a/internal/smartlink/smartlink_test.go +++ b/internal/smartlink/smartlink_test.go @@ -3,10 +3,8 @@ package smartlink import ( "os" "path/filepath" - "runtime" "testing" - "github.com/ActiveState/cli/internal/fileutils" "github.com/stretchr/testify/require" ) @@ -42,14 +40,17 @@ func TestLinkContentsWithCircularLink(t *testing.T) { err = os.Symlink(subDir, circularLink) require.NoError(t, err) - err = LinkContents(srcDir, destDir, nil) - if runtime.GOOS == "windows" { - require.Error(t, err) - return // hard links to directories are not allowed on Windows - } + err = LinkContents(srcDir, destDir) require.NoError(t, err) // Verify file structure. + // src/ + // ├── regular.txt + // └── subdir/ + // ├── circle + // │ │ (no subdir/) + // │ └── subfile.txt + // └── subfile.txt destFile := filepath.Join(destDir, "regular.txt") require.FileExists(t, destFile) content, err := os.ReadFile(destFile) @@ -62,14 +63,11 @@ func TestLinkContentsWithCircularLink(t *testing.T) { require.NoError(t, err) require.Equal(t, "sub content", string(subContent)) - destCircular := filepath.Join(destDir, "subdir", "circle") - require.FileExists(t, destCircular) - target, err := fileutils.ResolveUniquePath(destCircular) + require.NoDirExists(t, filepath.Join(destDir, "subdir", "circle", "circle")) + + destCircularSubFile := filepath.Join(destDir, "subdir", "circle", "subfile.txt") + require.FileExists(t, destCircularSubFile) + subContent, err = os.ReadFile(destCircularSubFile) require.NoError(t, err) - srcCircular := filepath.Join(srcDir, "subdir") - if runtime.GOOS == "darwin" { - srcCircular, err = fileutils.ResolveUniquePath(srcCircular) // needed for full $TMPDIR resolution - require.NoError(t, err) - } - require.Equal(t, target, srcCircular) + require.Equal(t, "sub content", string(subContent)) } diff --git a/pkg/runtime/depot.go b/pkg/runtime/depot.go index c53a1ac1e6..8443fcb3ee 100644 --- a/pkg/runtime/depot.go +++ b/pkg/runtime/depot.go @@ -177,7 +177,7 @@ func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string) } // Copy or link the artifact files, depending on whether the artifact in question relies on file transformations - if err := smartlink.LinkContents(absoluteSrc, absoluteDest, nil); err != nil { + if err := smartlink.LinkContents(absoluteSrc, absoluteDest); err != nil { return errs.Wrap(err, "failed to link artifact") } From acaa7e16fac5ea2cd5330c1f1e3c6127c0b40214 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 11:03:56 -0800 Subject: [PATCH 411/440] Make changes backwards compatible --- .../internal/server/generated/generated.go | 40 +++++++++---------- cmd/state-svc/schema/schema.graphqls | 2 +- .../cmdtree/exechandlers/notifier/notifier.go | 4 +- internal/constants/constants.go | 2 +- internal/graph/generated.go | 16 ++++---- pkg/platform/api/svc/request/notification.go | 2 +- test/integration/notification_int_test.go | 36 ++++++++--------- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/cmd/state-svc/internal/server/generated/generated.go b/cmd/state-svc/internal/server/generated/generated.go index b290494161..9e2bbb7947 100644 --- a/cmd/state-svc/internal/server/generated/generated.go +++ b/cmd/state-svc/internal/server/generated/generated.go @@ -84,14 +84,14 @@ type ComplexityRoot struct { } NotificationInfo struct { - Condition func(childComplexity int) int - EndDate func(childComplexity int) int - ID func(childComplexity int) int - Interrupt func(childComplexity int) int - Notification func(childComplexity int) int - Placement func(childComplexity int) int - Repeat func(childComplexity int) int - StartDate func(childComplexity int) int + Condition func(childComplexity int) int + EndDate func(childComplexity int) int + ID func(childComplexity int) int + Interrupt func(childComplexity int) int + Message func(childComplexity int) int + Placement func(childComplexity int) int + Repeat func(childComplexity int) int + StartDate func(childComplexity int) int } Organization struct { @@ -323,12 +323,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.NotificationInfo.Interrupt(childComplexity), true - case "NotificationInfo.notification": - if e.complexity.NotificationInfo.Notification == nil { + case "NotificationInfo.message": + if e.complexity.NotificationInfo.Message == nil { break } - return e.complexity.NotificationInfo.Notification(childComplexity), true + return e.complexity.NotificationInfo.Message(childComplexity), true case "NotificationInfo.placement": if e.complexity.NotificationInfo.Placement == nil { @@ -753,7 +753,7 @@ enum NotificationPlacementType { type NotificationInfo { id: String! - notification: String! + message: String! condition: String! startDate: String! endDate: String! @@ -1853,8 +1853,8 @@ func (ec *executionContext) fieldContext_NotificationInfo_id(_ context.Context, return fc, nil } -func (ec *executionContext) _NotificationInfo_notification(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NotificationInfo_notification(ctx, field) +func (ec *executionContext) _NotificationInfo_message(ctx context.Context, field graphql.CollectedField, obj *graph.NotificationInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NotificationInfo_message(ctx, field) if err != nil { return graphql.Null } @@ -1867,7 +1867,7 @@ func (ec *executionContext) _NotificationInfo_notification(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Notification, nil + return obj.Message, nil }) if err != nil { ec.Error(ctx, err) @@ -1884,7 +1884,7 @@ func (ec *executionContext) _NotificationInfo_notification(ctx context.Context, return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_NotificationInfo_notification(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NotificationInfo_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "NotificationInfo", Field: field, @@ -2737,8 +2737,8 @@ func (ec *executionContext) fieldContext_Query_checkNotifications(ctx context.Co switch field.Name { case "id": return ec.fieldContext_NotificationInfo_id(ctx, field) - case "notification": - return ec.fieldContext_NotificationInfo_notification(ctx, field) + case "message": + return ec.fieldContext_NotificationInfo_message(ctx, field) case "condition": return ec.fieldContext_NotificationInfo_condition(ctx, field) case "startDate": @@ -5841,8 +5841,8 @@ func (ec *executionContext) _NotificationInfo(ctx context.Context, sel ast.Selec if out.Values[i] == graphql.Null { out.Invalids++ } - case "notification": - out.Values[i] = ec._NotificationInfo_notification(ctx, field, obj) + case "message": + out.Values[i] = ec._NotificationInfo_message(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } diff --git a/cmd/state-svc/schema/schema.graphqls b/cmd/state-svc/schema/schema.graphqls index 094c3b829d..606eb5cfd3 100644 --- a/cmd/state-svc/schema/schema.graphqls +++ b/cmd/state-svc/schema/schema.graphqls @@ -53,7 +53,7 @@ enum NotificationPlacementType { type NotificationInfo { id: String! - notification: String! + message: String! condition: String! startDate: String! endDate: String! diff --git a/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go b/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go index 30927dc40c..63a8083864 100644 --- a/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go +++ b/cmd/state/internal/cmdtree/exechandlers/notifier/notifier.go @@ -90,8 +90,8 @@ func (m *Notifier) PrintByPlacement(placement graph.NotificationPlacementType) e m.out.Notice("") // Line break after } - logging.Debug("Printing notification: %s, %s", notification.ID, notification.Notification) - m.out.Notice(notification.Notification) + logging.Debug("Printing notification: %s, %s", notification.ID, notification.Message) + m.out.Notice(notification.Message) if placement == graph.NotificationPlacementTypeBeforeCmd { m.out.Notice("") // Line break before diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 09e979157d..c5130ab6c7 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -265,7 +265,7 @@ const VulnerabilitiesAPIPath = "/v13s/v1/graphql" const HasuraInventoryAPIPath = "/sv/hasura-inventory/v1/graphql" // NotificationsInfoURL is the URL we check against to see what versions are deprecated -const NotificationsInfoURL = "https://state-tool.s3.amazonaws.com/notifications.json" +const NotificationsInfoURL = "https://state-tool.s3.amazonaws.com/messages.json" // DateFormatUser is the date format we use when communicating with the end-user const DateFormatUser = "January 02, 2006" diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 482806f028..f61461ffa3 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -44,14 +44,14 @@ type Mutation struct { } type NotificationInfo struct { - ID string `json:"id"` - Notification string `json:"notification"` - Condition string `json:"condition"` - StartDate string `json:"startDate"` - EndDate string `json:"endDate"` - Repeat NotificationRepeatType `json:"repeat"` - Interrupt NotificationInterruptType `json:"interrupt"` - Placement NotificationPlacementType `json:"placement"` + ID string `json:"id"` + Message string `json:"message"` + Condition string `json:"condition"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + Repeat NotificationRepeatType `json:"repeat"` + Interrupt NotificationInterruptType `json:"interrupt"` + Placement NotificationPlacementType `json:"placement"` } type Organization struct { diff --git a/pkg/platform/api/svc/request/notification.go b/pkg/platform/api/svc/request/notification.go index a4983960ac..bc5cd097f0 100644 --- a/pkg/platform/api/svc/request/notification.go +++ b/pkg/platform/api/svc/request/notification.go @@ -16,7 +16,7 @@ func (m *NotificationRequest) Query() string { return `query($command: String!, $flags: [String!]!) { checkNotifications(command: $command, flags: $flags) { id - notification + message interrupt placement } diff --git a/test/integration/notification_int_test.go b/test/integration/notification_int_test.go index 4e9f2f8f4c..9889932cdc 100644 --- a/test/integration/notification_int_test.go +++ b/test/integration/notification_int_test.go @@ -42,7 +42,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Defaults", `[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification" + "Message": "This is a [NOTICE]simple[/RESET] notification" }]`, false, true, @@ -51,7 +51,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Repeat Hourly", `[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "Repeat": "Hourly" }]`, false, @@ -61,7 +61,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Repeat Constantly", `[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "Repeat": "Constantly" }]`, true, @@ -71,7 +71,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Within Date Range", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -84,7 +84,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Outside Date Range", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -97,7 +97,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Only Start Date - Inside Range", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s" }]`, time.Now().Add(-1*time.Hour).Format(time.RFC3339)), @@ -108,7 +108,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Only End Date - Inside Range", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "EndDate": "%s" }]`, time.Now().Add(1*time.Hour).Format(time.RFC3339)), @@ -119,7 +119,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Outside Date Range - Future", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -132,7 +132,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Outside Date Range - Past", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s", "EndDate": "%s" }]`, @@ -145,7 +145,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Only Start Date - Outside Range", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "StartDate": "%s" }]`, time.Now().Add(1*time.Hour).Format(time.RFC3339)), @@ -156,7 +156,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { "Only End Date - Outside Range", fmt.Sprintf(`[{ "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "EndDate": "%s" }]`, time.Now().Add(-1*time.Hour).Format(time.RFC3339)), @@ -169,7 +169,7 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic() { ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(tt.MessageJson), 0755) + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(tt.MessageJson), 0755) suite.Require().NoError(err) cp := ts.SpawnWithOpts(e2e.OptArgs("--version"), e2e.OptAppendEnv(constants.NotificationsOverrideEnvVarName+"="+msgFile)) @@ -201,10 +201,10 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_PlacementA ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(fmt.Sprintf(`[ + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(fmt.Sprintf(`[ { "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "Placement": "%s" } ]`, graph.NotificationPlacementTypeAfterCmd)), 0755) @@ -221,10 +221,10 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptP ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(fmt.Sprintf(`[ + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(fmt.Sprintf(`[ { "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "Repeat": "Constantly", "Interrupt": "%s" } @@ -253,10 +253,10 @@ func (suite *NotificationIntegrationTestSuite) TestNotification_Basic_InterruptE ts := e2e.New(suite.T(), false) defer ts.Close() - msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "notifications.json", []byte(fmt.Sprintf(`[ + msgFile, err := fileutils.WriteTempFileToDir(ts.Dirs.Work, "messages.json", []byte(fmt.Sprintf(`[ { "ID": "simple", - "Notification": "This is a [NOTICE]simple[/RESET] notification", + "Message": "This is a [NOTICE]simple[/RESET] notification", "Interrupt": "%s" } ]`, graph.NotificationInterruptTypeExit)), 0755) From 254108a7e45fa0c5ab729bd7a91f49493d861dfa Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 11:25:09 -0800 Subject: [PATCH 412/440] Rename to gqlclient --- internal/{graphql => gqlclient}/error.go | 2 +- internal/{graphql => gqlclient}/graphql.go | 2 +- internal/{graphql => gqlclient}/graphql_json_test.go | 2 +- .../{graphql => gqlclient}/graphql_multipart_test.go | 2 +- .../{graphql => gqlclient}/graphql_shared_test.go | 2 +- internal/{graphql => gqlclient}/graphql_test.go | 2 +- pkg/platform/api/graphql/graphql.go | 6 +++--- pkg/platform/api/graphql/request/publish.go | 8 ++++---- pkg/platform/api/hasura_inventory/inventory.go | 6 +++--- pkg/platform/api/mediator/mediator.go | 6 +++--- pkg/platform/api/vulnerabilities/vulnerabilities.go | 6 +++--- pkg/platform/model/buildplanner/build.go | 12 ++++++------ pkg/platform/model/buildplanner/buildplanner.go | 6 +++--- pkg/platform/model/svc.go | 10 +++++----- 14 files changed, 36 insertions(+), 36 deletions(-) rename internal/{graphql => gqlclient}/error.go (95%) rename internal/{graphql => gqlclient}/graphql.go (99%) rename internal/{graphql => gqlclient}/graphql_json_test.go (99%) rename internal/{graphql => gqlclient}/graphql_multipart_test.go (99%) rename internal/{graphql => gqlclient}/graphql_shared_test.go (97%) rename internal/{graphql => gqlclient}/graphql_test.go (99%) diff --git a/internal/graphql/error.go b/internal/gqlclient/error.go similarity index 95% rename from internal/graphql/error.go rename to internal/gqlclient/error.go index b8031b24c8..f5c9e66a17 100644 --- a/internal/graphql/error.go +++ b/internal/gqlclient/error.go @@ -1,4 +1,4 @@ -package graphql +package gqlclient import "fmt" diff --git a/internal/graphql/graphql.go b/internal/gqlclient/graphql.go similarity index 99% rename from internal/graphql/graphql.go rename to internal/gqlclient/graphql.go index 283e6c0b1e..bb1003465a 100644 --- a/internal/graphql/graphql.go +++ b/internal/gqlclient/graphql.go @@ -29,7 +29,7 @@ // // httpclient := &http.Client{} // client := graphql.NewClient("https://machinebox.io/graphql", graphql.WithHTTPClient(httpclient)) -package graphql +package gqlclient import ( "bytes" diff --git a/internal/graphql/graphql_json_test.go b/internal/gqlclient/graphql_json_test.go similarity index 99% rename from internal/graphql/graphql_json_test.go rename to internal/gqlclient/graphql_json_test.go index b2fbdda289..4dce0b22c2 100644 --- a/internal/graphql/graphql_json_test.go +++ b/internal/gqlclient/graphql_json_test.go @@ -1,4 +1,4 @@ -package graphql +package gqlclient import ( "context" diff --git a/internal/graphql/graphql_multipart_test.go b/internal/gqlclient/graphql_multipart_test.go similarity index 99% rename from internal/graphql/graphql_multipart_test.go rename to internal/gqlclient/graphql_multipart_test.go index a6734748ad..bf05030ecd 100644 --- a/internal/graphql/graphql_multipart_test.go +++ b/internal/gqlclient/graphql_multipart_test.go @@ -1,4 +1,4 @@ -package graphql +package gqlclient import ( "context" diff --git a/internal/graphql/graphql_shared_test.go b/internal/gqlclient/graphql_shared_test.go similarity index 97% rename from internal/graphql/graphql_shared_test.go rename to internal/gqlclient/graphql_shared_test.go index e20d924709..da62126795 100644 --- a/internal/graphql/graphql_shared_test.go +++ b/internal/gqlclient/graphql_shared_test.go @@ -1,4 +1,4 @@ -package graphql +package gqlclient type TestRequest struct { q string diff --git a/internal/graphql/graphql_test.go b/internal/gqlclient/graphql_test.go similarity index 99% rename from internal/graphql/graphql_test.go rename to internal/gqlclient/graphql_test.go index 0bc1875361..9cde0c9abf 100644 --- a/internal/graphql/graphql_test.go +++ b/internal/gqlclient/graphql_test.go @@ -1,4 +1,4 @@ -package graphql +package gqlclient import ( "context" diff --git a/pkg/platform/api/graphql/graphql.go b/pkg/platform/api/graphql/graphql.go index 878a9d6653..f0e01183cb 100644 --- a/pkg/platform/api/graphql/graphql.go +++ b/pkg/platform/api/graphql/graphql.go @@ -1,14 +1,14 @@ package graphql import ( - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *graphql.Client { +func New(auth *authentication.Auth) *gqlclient.Client { url := api.GetServiceURL(api.ServiceGraphQL) - c := graphql.New(url.String(), 0) + c := gqlclient.New(url.String(), 0) c.SetTokenProvider(auth) return c } diff --git a/pkg/platform/api/graphql/request/publish.go b/pkg/platform/api/graphql/request/publish.go index 966d210ce8..551bdb9e06 100644 --- a/pkg/platform/api/graphql/request/publish.go +++ b/pkg/platform/api/graphql/request/publish.go @@ -9,7 +9,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/fileutils" - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/locale" yamlcomment "github.com/zijiren233/yaml-comment" "gopkg.in/yaml.v3" @@ -187,11 +187,11 @@ func (p *PublishInput) Close() error { return p.file.Close() } -func (p *PublishInput) Files() []graphql.File { +func (p *PublishInput) Files() []gqlclient.File { if p.file == nil { - return []graphql.File{} + return []gqlclient.File{} } - return []graphql.File{ + return []gqlclient.File{ { Field: "variables.input.file", // this needs to map to the graphql input, eg. variables.input.file Name: p.Variables.Name, diff --git a/pkg/platform/api/hasura_inventory/inventory.go b/pkg/platform/api/hasura_inventory/inventory.go index dbca50e093..b4ddd8dc71 100644 --- a/pkg/platform/api/hasura_inventory/inventory.go +++ b/pkg/platform/api/hasura_inventory/inventory.go @@ -1,13 +1,13 @@ package inventory import ( - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *graphql.Client { - client := graphql.New(api.GetServiceURL(api.ServiceHasuraInventory).String(), 0) +func New(auth *authentication.Auth) *gqlclient.Client { + client := gqlclient.New(api.GetServiceURL(api.ServiceHasuraInventory).String(), 0) if auth != nil && auth.Authenticated() { client.SetTokenProvider(auth) diff --git a/pkg/platform/api/mediator/mediator.go b/pkg/platform/api/mediator/mediator.go index 67d744fdec..193f43a45b 100644 --- a/pkg/platform/api/mediator/mediator.go +++ b/pkg/platform/api/mediator/mediator.go @@ -1,14 +1,14 @@ package mediator import ( - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *graphql.Client { +func New(auth *authentication.Auth) *gqlclient.Client { url := api.GetServiceURL(api.ServiceMediator) - c := graphql.New(url.String(), 0) + c := gqlclient.New(url.String(), 0) if auth != nil { c.SetTokenProvider(auth) } diff --git a/pkg/platform/api/vulnerabilities/vulnerabilities.go b/pkg/platform/api/vulnerabilities/vulnerabilities.go index 9861444cba..7e49978dc2 100644 --- a/pkg/platform/api/vulnerabilities/vulnerabilities.go +++ b/pkg/platform/api/vulnerabilities/vulnerabilities.go @@ -1,13 +1,13 @@ package vulnerabilities import ( - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" ) -func New(auth *authentication.Auth) *graphql.Client { - client := graphql.New(api.GetServiceURL(api.ServiceVulnerabilities).String(), 0) +func New(auth *authentication.Auth) *gqlclient.Client { + client := gqlclient.New(api.GetServiceURL(api.ServiceVulnerabilities).String(), 0) // Most requests to this service require authentication if auth != nil && auth.Authenticated() { diff --git a/pkg/platform/model/buildplanner/build.go b/pkg/platform/model/buildplanner/build.go index a9d6c9ad3b..9abd93156d 100644 --- a/pkg/platform/model/buildplanner/build.go +++ b/pkg/platform/model/buildplanner/build.go @@ -9,7 +9,7 @@ import ( "time" "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/locale" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/rtutils/ptr" @@ -49,7 +49,7 @@ func (c *Commit) BuildScript() *buildscript.BuildScript { return c.buildscript } -func (c *client) Run(req graphql.Request, resp interface{}) error { +func (c *client) Run(req gqlclient.Request, resp interface{}) error { return c.gqlClient.Run(req, resp) } @@ -152,11 +152,11 @@ func (b *BuildPlanner) fetchCommit(commitID strfmt.UUID, owner, project string, // "data": null // } func processBuildPlannerError(bpErr error, fallbackMessage string) error { - graphqlErr := &graphql.GraphErr{} - if errors.As(bpErr, graphqlErr) { - code, ok := graphqlErr.Extensions[codeExtensionKey].(string) + gqlclientErr := &gqlclient.GraphErr{} + if errors.As(bpErr, gqlclientErr) { + code, ok := gqlclientErr.Extensions[codeExtensionKey].(string) if ok && code == clientDeprecationErrorKey { - return &response.BuildPlannerError{Err: locale.NewExternalError("err_buildplanner_deprecated", "Encountered deprecation error: {{.V0}}", graphqlErr.Message)} + return &response.BuildPlannerError{Err: locale.NewExternalError("err_buildplanner_deprecated", "Encountered deprecation error: {{.V0}}", gqlclientErr.Message)} } } if locale.IsInputError(bpErr) { diff --git a/pkg/platform/model/buildplanner/buildplanner.go b/pkg/platform/model/buildplanner/buildplanner.go index 04533bbe6a..6233807447 100644 --- a/pkg/platform/model/buildplanner/buildplanner.go +++ b/pkg/platform/model/buildplanner/buildplanner.go @@ -3,7 +3,7 @@ package buildplanner import ( "time" - "github.com/ActiveState/cli/internal/graphql" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/pkg/platform/api" "github.com/ActiveState/cli/pkg/platform/authentication" @@ -12,7 +12,7 @@ import ( const clientDeprecationErrorKey = "CLIENT_DEPRECATION_ERROR" type client struct { - gqlClient *graphql.Client + gqlClient *gqlclient.Client } type BuildPlanner struct { @@ -40,7 +40,7 @@ func NewBuildPlannerModel(auth *authentication.Auth, cache cacher) *BuildPlanner bpURL := api.GetServiceURL(api.ServiceBuildPlanner).String() logging.Debug("Using build planner at: %s", bpURL) - gqlClient := graphql.NewWithOpts(bpURL, 0, graphql.WithHTTPClient(api.NewHTTPClient())) + gqlClient := gqlclient.NewWithOpts(bpURL, 0, gqlclient.WithHTTPClient(api.NewHTTPClient())) if auth != nil && auth.Authenticated() { gqlClient.SetTokenProvider(auth) diff --git a/pkg/platform/model/svc.go b/pkg/platform/model/svc.go index 1fde4a4576..c0ed10b850 100644 --- a/pkg/platform/model/svc.go +++ b/pkg/platform/model/svc.go @@ -10,8 +10,8 @@ import ( "github.com/ActiveState/cli/internal/condition" "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/gqlclient" "github.com/ActiveState/cli/internal/graph" - "github.com/ActiveState/cli/internal/graphql" "github.com/ActiveState/cli/internal/logging" "github.com/ActiveState/cli/internal/profile" "github.com/ActiveState/cli/internal/rtutils/ptr" @@ -22,7 +22,7 @@ import ( var SvcTimeoutMinimal = time.Millisecond * 500 type SvcModel struct { - client *graphql.Client + client *gqlclient.Client } // NewSvcModel returns a model for all client connections to a State Svc. This function returns an error if the State service is not yet ready to communicate. @@ -30,7 +30,7 @@ func NewSvcModel(port string) *SvcModel { localURL := "http://127.0.0.1" + port + "/query" return &SvcModel{ - client: graphql.NewWithOpts(localURL, 0, graphql.WithHTTPClient(&http.Client{})), + client: gqlclient.NewWithOpts(localURL, 0, gqlclient.WithHTTPClient(&http.Client{})), } } @@ -39,12 +39,12 @@ func (m *SvcModel) EnableDebugLog() { m.client.EnableDebugLog() } -func (m *SvcModel) request(ctx context.Context, request graphql.Request, resp interface{}) error { +func (m *SvcModel) request(ctx context.Context, request gqlclient.Request, resp interface{}) error { defer profile.Measure("SvcModel:request", time.Now()) err := m.client.RunWithContext(ctx, request, resp) if err != nil { - reqError := &graphql.RequestError{} + reqError := &gqlclient.RequestError{} if errors.As(err, &reqError) && (!condition.BuiltViaCI() || condition.InTest()) { vars, err := request.Vars() if err != nil { From f50e832a7165088e4a2cf3ca340e1441b299cc92 Mon Sep 17 00:00:00 2001 From: mdrakos Date: Thu, 21 Nov 2024 11:31:05 -0800 Subject: [PATCH 413/440] Rename files --- internal/gqlclient/{graphql.go => gqlclient.go} | 0 .../gqlclient/{graphql_json_test.go => gqlclient_json_test.go} | 0 .../{graphql_multipart_test.go => gqlclient_multipart_test.go} | 0 .../{graphql_shared_test.go => gqlclient_shared_test.go} | 0 internal/gqlclient/{graphql_test.go => gqlclient_test.go} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename internal/gqlclient/{graphql.go => gqlclient.go} (100%) rename internal/gqlclient/{graphql_json_test.go => gqlclient_json_test.go} (100%) rename internal/gqlclient/{graphql_multipart_test.go => gqlclient_multipart_test.go} (100%) rename internal/gqlclient/{graphql_shared_test.go => gqlclient_shared_test.go} (100%) rename internal/gqlclient/{graphql_test.go => gqlclient_test.go} (100%) diff --git a/internal/gqlclient/graphql.go b/internal/gqlclient/gqlclient.go similarity index 100% rename from internal/gqlclient/graphql.go rename to internal/gqlclient/gqlclient.go diff --git a/internal/gqlclient/graphql_json_test.go b/internal/gqlclient/gqlclient_json_test.go similarity index 100% rename from internal/gqlclient/graphql_json_test.go rename to internal/gqlclient/gqlclient_json_test.go diff --git a/internal/gqlclient/graphql_multipart_test.go b/internal/gqlclient/gqlclient_multipart_test.go similarity index 100% rename from internal/gqlclient/graphql_multipart_test.go rename to internal/gqlclient/gqlclient_multipart_test.go diff --git a/internal/gqlclient/graphql_shared_test.go b/internal/gqlclient/gqlclient_shared_test.go similarity index 100% rename from internal/gqlclient/graphql_shared_test.go rename to internal/gqlclient/gqlclient_shared_test.go diff --git a/internal/gqlclient/graphql_test.go b/internal/gqlclient/gqlclient_test.go similarity index 100% rename from internal/gqlclient/graphql_test.go rename to internal/gqlclient/gqlclient_test.go From fa5bc026f4d67f3448f679659bd9f050077fa165 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 15:05:21 -0500 Subject: [PATCH 414/440] Update localizations for non-interactive and force flags. --- internal/locale/locales/en-us.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index eb4923fdf8..2ee84b1c67 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -22,9 +22,9 @@ flag_state_monochrome_output_description: flag_state_output_description: other: "Set the output method. Possible values: plain, simple, json, editor" flag_state_non_interactive_description: - other: Run the State Tool without any prompts + other: Assume default values for any prompts flag_state_force_description: - other: Run the State Tool without any prompts, overriding any safe defaults + other: Disable prompts and perform the requested action regardless of defaults values (this can result in destructive or unsafe actions, such as installing packages with critical CVEs) flag_state_version_description: other: Show the version of our state executable flag_state_activate_path_description: From 40b37ba2895f39d6cba7e1780ee2799185edd4ae Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 15:06:50 -0500 Subject: [PATCH 415/440] Use a constant error for prompts that do not have forced values. --- internal/captain/rationalize.go | 6 ++++++ internal/prompt/prompt.go | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/captain/rationalize.go b/internal/captain/rationalize.go index c44d7dbfdf..feba9c19dd 100644 --- a/internal/captain/rationalize.go +++ b/internal/captain/rationalize.go @@ -5,6 +5,7 @@ import ( "github.com/ActiveState/cli/internal/errs" "github.com/ActiveState/cli/internal/locale" + "github.com/ActiveState/cli/internal/prompt" "github.com/ActiveState/cli/internal/runbits/rationalize" "github.com/ActiveState/cli/pkg/buildscript" "github.com/ActiveState/cli/pkg/localcommit" @@ -38,5 +39,10 @@ func rationalizeError(err *error) { *err = errs.WrapUserFacing(*err, locale.T("err_outdated_buildscript"), errs.SetInput()) + + case errors.Is(*err, prompt.ErrNoForceOption): + *err = errs.WrapUserFacing(*err, + locale.T("err_prompt_no_force_option", + "This command has a prompt that does not support the '[ACTIONABLE]--force[/RESET]' flag.")) } } diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 61b67cc0b8..ae18a55cfe 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -45,6 +45,8 @@ type Prompt struct { isForced bool } +var ErrNoForceOption = errs.New("No force option given for forced prompt") + // New creates a new prompter func New(out output.Outputer, an EventDispatcher) Prompter { return &Prompt{out, an, out.Config().Interactive, false} @@ -113,7 +115,7 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string p.out.Notice(locale.Tr("prompt_using_force", *response)) return *response, nil } - return "", errs.New("No force option given for forced prompt") + return "", ErrNoForceOption } if !p.isInteractive { @@ -172,7 +174,7 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * p.out.Notice(locale.Tr("prompt_using_force", *choice)) return *choice, nil } - return "", errs.New("No force option given for forced prompt") + return "", ErrNoForceOption } if !p.isInteractive { @@ -218,7 +220,7 @@ func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoic p.out.Notice(locale.T("prompt_continue_force")) return *choice, nil } - return false, errs.New("No force option given for forced prompt") + return false, ErrNoForceOption } if !p.isInteractive { From 8b4a5afabc184e0de6c44fb85a52f4988547ab72 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 19:05:36 -0500 Subject: [PATCH 416/440] Do not require custom runtime path for portable projects. --- cmd/state/internal/cmdtree/checkout.go | 2 +- pkg/project/project.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/state/internal/cmdtree/checkout.go b/cmd/state/internal/cmdtree/checkout.go index 0e1c91ab23..05ee821101 100644 --- a/cmd/state/internal/cmdtree/checkout.go +++ b/cmd/state/internal/cmdtree/checkout.go @@ -28,7 +28,7 @@ func newCheckoutCommand(prime *primer.Values) *captain.Command { }, { Name: "portable", - Description: locale.Tl("flag_state_checkout_portable_description", "Copy files to runtime-path instead of linking to them"), + Description: locale.Tl("flag_state_checkout_portable_description", "Copy files to their runtime path instead of linking to them"), Value: ¶ms.Portable, }, { diff --git a/pkg/project/project.go b/pkg/project/project.go index 06ea03ccbe..ad911807fd 100644 --- a/pkg/project/project.go +++ b/pkg/project/project.go @@ -255,7 +255,7 @@ func (p *Project) Lock() string { return p.projectfile.Lock } // Cache returns the cache information for this project func (p *Project) Cache() string { return p.projectfile.Cache } -func (p *Project) IsPortable() bool { return p.projectfile.Portable && p.projectfile.Cache != "" } +func (p *Project) IsPortable() bool { return p.projectfile.Portable } // Namespace returns project namespace func (p *Project) Namespace() *Namespaced { From 9dd2ff9dab7d8102282f4a67950516a9fec05067 Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 19:07:41 -0500 Subject: [PATCH 417/440] Revert "Recurse into recursively linked directories not more than once." This reverts commit da5fee5f07321b6400f2d1e89eec3831a64376aa. --- internal/smartlink/smartlink.go | 33 +++++++++++++++---------- internal/smartlink/smartlink_lin_mac.go | 6 ----- internal/smartlink/smartlink_test.go | 30 +++++++++++----------- pkg/runtime/depot.go | 2 +- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/internal/smartlink/smartlink.go b/internal/smartlink/smartlink.go index 3df90034dd..49fbe2d01d 100644 --- a/internal/smartlink/smartlink.go +++ b/internal/smartlink/smartlink.go @@ -10,7 +10,7 @@ import ( ) // LinkContents will link the contents of src to desc -func LinkContents(src, dest string) error { +func LinkContents(src, dest string, visited map[string]bool) error { if !fileutils.DirExists(src) { return errs.New("src dir does not exist: %s", src) } @@ -24,12 +24,23 @@ func LinkContents(src, dest string) error { return errs.Wrap(err, "Could not resolve src and dest paths") } + if visited == nil { + visited = make(map[string]bool) + } + if _, exists := visited[src]; exists { + // We've encountered a recursive link. This is most often the case when the resolved src has + // already been visited. In that case, just link the dest to the src (which may be a directory; + // this is fine). + return linkFile(src, dest) + } + visited[src] = true + entries, err := os.ReadDir(src) if err != nil { return errs.Wrap(err, "Reading dir %s failed", src) } for _, entry := range entries { - if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), nil); err != nil { + if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil { return errs.Wrap(err, "Link failed") } } @@ -39,7 +50,7 @@ func LinkContents(src, dest string) error { // Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure), // which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks. -func Link(src, dest string, visited map[string]int) error { +func Link(src, dest string, visited map[string]bool) error { var err error src, dest, err = resolvePaths(src, dest) if err != nil { @@ -47,19 +58,15 @@ func Link(src, dest string, visited map[string]int) error { } if visited == nil { - visited = make(map[string]int) + visited = make(map[string]bool) } - if count, exists := visited[src]; exists { + if _, exists := visited[src]; exists { // We've encountered a recursive link. This is most often the case when the resolved src has - // already been visited. We will recurse into the directory no more than once, so that any - // runtime paths that reference the link will not silently fail. - if count > 1 { - return nil - } - visited[src]++ - } else { - visited[src] = 1 + // already been visited. In that case, just link the dest to the src (which may be a directory; + // this is fine). + return linkFile(src, dest) } + visited[src] = true if fileutils.IsDir(src) { if err := fileutils.Mkdir(dest); err != nil { diff --git a/internal/smartlink/smartlink_lin_mac.go b/internal/smartlink/smartlink_lin_mac.go index b19f45d411..edc065f280 100644 --- a/internal/smartlink/smartlink_lin_mac.go +++ b/internal/smartlink/smartlink_lin_mac.go @@ -5,16 +5,10 @@ package smartlink import ( "os" - - "github.com/ActiveState/cli/internal/errs" - "github.com/ActiveState/cli/internal/fileutils" ) // file will create a symlink from src to dest, and falls back on a hardlink if no symlink is available. // This is a workaround for the fact that Windows does not support symlinks without admin privileges. func linkFile(src, dest string) error { - if fileutils.IsDir(src) { - return errs.New("src is a directory, not a file: %s", src) - } return os.Symlink(src, dest) } diff --git a/internal/smartlink/smartlink_test.go b/internal/smartlink/smartlink_test.go index 1222a0413d..5b52fbb0d3 100644 --- a/internal/smartlink/smartlink_test.go +++ b/internal/smartlink/smartlink_test.go @@ -3,8 +3,10 @@ package smartlink import ( "os" "path/filepath" + "runtime" "testing" + "github.com/ActiveState/cli/internal/fileutils" "github.com/stretchr/testify/require" ) @@ -40,17 +42,14 @@ func TestLinkContentsWithCircularLink(t *testing.T) { err = os.Symlink(subDir, circularLink) require.NoError(t, err) - err = LinkContents(srcDir, destDir) + err = LinkContents(srcDir, destDir, nil) + if runtime.GOOS == "windows" { + require.Error(t, err) + return // hard links to directories are not allowed on Windows + } require.NoError(t, err) // Verify file structure. - // src/ - // ├── regular.txt - // └── subdir/ - // ├── circle - // │ │ (no subdir/) - // │ └── subfile.txt - // └── subfile.txt destFile := filepath.Join(destDir, "regular.txt") require.FileExists(t, destFile) content, err := os.ReadFile(destFile) @@ -63,11 +62,14 @@ func TestLinkContentsWithCircularLink(t *testing.T) { require.NoError(t, err) require.Equal(t, "sub content", string(subContent)) - require.NoDirExists(t, filepath.Join(destDir, "subdir", "circle", "circle")) - - destCircularSubFile := filepath.Join(destDir, "subdir", "circle", "subfile.txt") - require.FileExists(t, destCircularSubFile) - subContent, err = os.ReadFile(destCircularSubFile) + destCircular := filepath.Join(destDir, "subdir", "circle") + require.FileExists(t, destCircular) + target, err := fileutils.ResolveUniquePath(destCircular) require.NoError(t, err) - require.Equal(t, "sub content", string(subContent)) + srcCircular := filepath.Join(srcDir, "subdir") + if runtime.GOOS == "darwin" { + srcCircular, err = fileutils.ResolveUniquePath(srcCircular) // needed for full $TMPDIR resolution + require.NoError(t, err) + } + require.Equal(t, target, srcCircular) } diff --git a/pkg/runtime/depot.go b/pkg/runtime/depot.go index 8443fcb3ee..c53a1ac1e6 100644 --- a/pkg/runtime/depot.go +++ b/pkg/runtime/depot.go @@ -177,7 +177,7 @@ func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string) } // Copy or link the artifact files, depending on whether the artifact in question relies on file transformations - if err := smartlink.LinkContents(absoluteSrc, absoluteDest); err != nil { + if err := smartlink.LinkContents(absoluteSrc, absoluteDest, nil); err != nil { return errs.Wrap(err, "failed to link artifact") } From f0c778435c92d4a97717775f18abc52c7f61b86a Mon Sep 17 00:00:00 2001 From: mitchell Date: Thu, 21 Nov 2024 19:20:38 -0500 Subject: [PATCH 418/440] Make it explicit that re-encountering file involves a symlink source. --- internal/smartlink/smartlink.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/internal/smartlink/smartlink.go b/internal/smartlink/smartlink.go index 49fbe2d01d..2c62efd01f 100644 --- a/internal/smartlink/smartlink.go +++ b/internal/smartlink/smartlink.go @@ -24,17 +24,6 @@ func LinkContents(src, dest string, visited map[string]bool) error { return errs.Wrap(err, "Could not resolve src and dest paths") } - if visited == nil { - visited = make(map[string]bool) - } - if _, exists := visited[src]; exists { - // We've encountered a recursive link. This is most often the case when the resolved src has - // already been visited. In that case, just link the dest to the src (which may be a directory; - // this is fine). - return linkFile(src, dest) - } - visited[src] = true - entries, err := os.ReadDir(src) if err != nil { return errs.Wrap(err, "Reading dir %s failed", src) @@ -51,6 +40,8 @@ func LinkContents(src, dest string, visited map[string]bool) error { // Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure), // which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks. func Link(src, dest string, visited map[string]bool) error { + srcWasSymlink := isSymlink(src) + var err error src, dest, err = resolvePaths(src, dest) if err != nil { @@ -60,7 +51,7 @@ func Link(src, dest string, visited map[string]bool) error { if visited == nil { visited = make(map[string]bool) } - if _, exists := visited[src]; exists { + if _, exists := visited[src]; exists && srcWasSymlink { // We've encountered a recursive link. This is most often the case when the resolved src has // already been visited. In that case, just link the dest to the src (which may be a directory; // this is fine). @@ -153,6 +144,11 @@ func UnlinkContents(src, dest string) error { return nil } +func isSymlink(src string) bool { + target, err := fileutils.SymlinkTarget(src) + return err == nil && src != target +} + // resolvePaths will resolve src and dest to absolute paths and return them. // This is to ensure that we're always comparing apples to apples when doing string comparisons on paths. func resolvePaths(src, dest string) (string, string, error) { From 97d2beb536d5c5297507a81e87d2b3aa90394308 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 22 Nov 2024 10:52:10 -0500 Subject: [PATCH 419/440] Installing in non-interactive mode on Windows with admin should bypass the prompt. --- cmd/state-installer/installer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/state-installer/installer.go b/cmd/state-installer/installer.go index 7d168697e5..3a4bc67049 100644 --- a/cmd/state-installer/installer.go +++ b/cmd/state-installer/installer.go @@ -61,7 +61,8 @@ func (i *Installer) Install() (rerr error) { if i.Params.force { prompter.SetForce(true) } - confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), ptr.To(false), ptr.To(true)) + defaultChoice := i.Params.nonInteractive + confirm, err := prompter.Confirm("", locale.T("installer_prompt_is_admin"), &defaultChoice, ptr.To(true)) if err != nil { return errs.Wrap(err, "Not confirmed") } From 878565bdeb01aa9e952edd6bf67039001ac4b3d6 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 22 Nov 2024 15:16:26 -0500 Subject: [PATCH 420/440] Show prompts, even if running in non-interactive or force mode. --- internal/prompt/overrides.go | 60 +++++++++++++ internal/prompt/prompt.go | 157 +++++++++++++++++++++-------------- 2 files changed, 154 insertions(+), 63 deletions(-) diff --git a/internal/prompt/overrides.go b/internal/prompt/overrides.go index d1d94c4a32..230f7b86c0 100644 --- a/internal/prompt/overrides.go +++ b/internal/prompt/overrides.go @@ -6,6 +6,34 @@ import ( type Select struct { *survey.Select + nonInteractiveChoice *string +} + +func (s *Select) Prompt() (interface{}, error) { + if s.nonInteractiveChoice == nil { + return s.Select.Prompt() + } + + idx := 0 + for i, choice := range s.Select.Options { + if choice == *s.nonInteractiveChoice { + idx = i + break + } + } + + err := s.Select.Render( + survey.SelectQuestionTemplate, + survey.SelectTemplateData{ + Select: *s.Select, + PageEntries: s.Select.Options, + SelectedIndex: idx, + }) + if err != nil { + return nil, err + } + + return *s.nonInteractiveChoice, nil } func (s *Select) Cleanup(interface{}) error { @@ -15,6 +43,22 @@ func (s *Select) Cleanup(interface{}) error { type Input struct { *survey.Input + nonInteractiveResponse *string +} + +func (i *Input) Prompt() (interface{}, error) { + if i.nonInteractiveResponse == nil { + return i.Input.Prompt() + } + + err := i.Input.Render( + survey.InputQuestionTemplate, + survey.InputTemplateData{Input: *i.Input}) + if err != nil { + return nil, err + } + + return *i.nonInteractiveResponse, nil } func (i *Input) Cleanup(val interface{}) error { @@ -33,6 +77,22 @@ func (i *Password) Cleanup(val interface{}) error { type Confirm struct { *survey.Confirm + nonInteractiveChoice *bool +} + +func (s *Confirm) Prompt() (interface{}, error) { + if s.nonInteractiveChoice == nil { + return s.Confirm.Prompt() + } + + err := s.Confirm.Render( + survey.ConfirmQuestionTemplate, + survey.ConfirmTemplateData{Confirm: *s.Confirm}) + if err != nil { + return nil, err + } + + return *s.nonInteractiveChoice, nil } func (s *Confirm) Cleanup(interface{}) error { diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index ae18a55cfe..d1db14cb01 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -106,26 +106,6 @@ func interactiveInputError(message string) error { // If the prompt is non-interactive, it returns defaultResponse. // If the prompt is forced, it returns forcedResponse if not nil, or defaultResponse. func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string, forcedResponse *string, validator ValidatorFunc, flags ...ValidatorFlag) (string, error) { - if p.isForced { - response := forcedResponse - if response == nil { - response = defaultResponse - } - if response != nil { - p.out.Notice(locale.Tr("prompt_using_force", *response)) - return *response, nil - } - return "", ErrNoForceOption - } - - if !p.isInteractive { - if defaultResponse != nil { - p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultResponse)) - return *defaultResponse, nil - } - return "", interactiveInputError(message) - } - var response string flagValidators, err := processValidators(flags) if err != nil { @@ -139,6 +119,29 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string p.out.Notice(output.Emphasize(title)) } + var nonInteractiveResponse *string + + if p.isForced { + nonInteractiveResponse = forcedResponse + if nonInteractiveResponse == nil { + nonInteractiveResponse = defaultResponse + } + if nonInteractiveResponse == nil { + return "", ErrNoForceOption + } + } + + if !p.isInteractive { + nonInteractiveResponse = defaultResponse + if nonInteractiveResponse == nil { + return "", interactiveInputError(message) + } + } + + if p.out.Type().IsStructured() { + return *nonInteractiveResponse, nil + } + // We handle defaults more clearly than the survey package can if defaultResponse != nil && *defaultResponse != "" { v, err := p.Select("", formatMessage(message, !p.out.Config().Colored), []string{*defaultResponse, locale.Tl("prompt_custom", "Other ..")}, defaultResponse, forcedResponse) @@ -153,11 +156,18 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string err = survey.AskOne(&Input{&survey.Input{ Message: formatMessage(message, !p.out.Config().Colored), - }}, &response, validator) + }, nonInteractiveResponse}, &response, validator) if err != nil { return "", locale.NewInputError(err.Error()) } + switch { + case p.isForced: + p.out.Notice(locale.Tr("prompt_using_force", response)) + case !p.isInteractive: + p.out.Notice(locale.Tr("prompt_using_non_interactive", response)) + } + return response, nil } @@ -165,33 +175,36 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string // If the prompt is non-interactive, it returns defaultChoice. // If the prompt is forced, it returns forcedChoice if not nil, or defaultChoice. func (p *Prompt) Select(title, message string, choices []string, defaultChoice *string, forcedChoice *string) (string, error) { + if title != "" { + p.out.Notice(output.Emphasize(title)) + } + + var defChoice string + if defaultChoice != nil { + defChoice = *defaultChoice + } + + var nonInteractiveChoice *string + if p.isForced { - choice := forcedChoice - if choice == nil { - choice = defaultChoice + nonInteractiveChoice = forcedChoice + if nonInteractiveChoice == nil { + nonInteractiveChoice = defaultChoice } - if choice != nil { - p.out.Notice(locale.Tr("prompt_using_force", *choice)) - return *choice, nil + if nonInteractiveChoice == nil { + return "", ErrNoForceOption } - return "", ErrNoForceOption } if !p.isInteractive { - if defaultChoice != nil { - p.out.Notice(locale.Tr("prompt_using_non_interactive", *defaultChoice)) - return *defaultChoice, nil + nonInteractiveChoice = defaultChoice + if nonInteractiveChoice == nil { + return "", interactiveInputError(message) } - return "", interactiveInputError(message) } - if title != "" { - p.out.Notice(output.Emphasize(title)) - } - - var defChoice string - if defaultChoice != nil { - defChoice = *defaultChoice + if p.out.Type().IsStructured() { + return *nonInteractiveChoice, nil } var response string @@ -200,10 +213,18 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * Options: choices, Default: defChoice, FilterFn: func(input string, choices []string) []string { return choices }, // no filter - }}, &response, nil) + }, nonInteractiveChoice}, &response, nil) if err != nil { return "", locale.NewInputError(err.Error()) } + + switch { + case p.isForced: + p.out.Notice(locale.Tr("prompt_using_force", response)) + case !p.isInteractive: + p.out.Notice(locale.Tr("prompt_using_non_interactive", response)) + } + return response, nil } @@ -211,44 +232,45 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * // If the prompt is non-interactive, it returns defaultChoice. // If the prompt is forced, it returns forcedChoice if not nil, or defaultChoice. func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoice *bool) (bool, error) { + p.analytics.EventWithLabel(constants.CatPrompt, title, "present") + + if title != "" { + p.out.Notice(output.Emphasize(title)) + } + + var defChoice bool + if defaultChoice != nil { + defChoice = *defaultChoice + } + + var nonInteractiveChoice *bool + if p.isForced { - choice := forcedChoice - if choice == nil { - choice = defaultChoice + nonInteractiveChoice = forcedChoice + if nonInteractiveChoice == nil { + nonInteractiveChoice = defaultChoice } - if choice != nil { - p.out.Notice(locale.T("prompt_continue_force")) - return *choice, nil + if nonInteractiveChoice == nil { + return false, ErrNoForceOption } - return false, ErrNoForceOption } if !p.isInteractive { - if defaultChoice != nil { - if *defaultChoice { - p.out.Notice(locale.T("prompt_continue_non_interactive")) - return true, nil - } - return false, locale.NewInputError("prompt_abort_non_interactive") + nonInteractiveChoice = defaultChoice + if nonInteractiveChoice == nil { + return false, interactiveInputError(message) } - return false, interactiveInputError(message) } - if title != "" { - p.out.Notice(output.Emphasize(title)) - } - - p.analytics.EventWithLabel(constants.CatPrompt, title, "present") - var defChoice bool - if defaultChoice != nil { - defChoice = *defaultChoice + if p.out.Type().IsStructured() { + return *nonInteractiveChoice, nil } var resp bool err := survey.AskOne(&Confirm{&survey.Confirm{ Message: formatMessage(strings.TrimSuffix(message, "\n"), !p.out.Config().Colored), Default: defChoice, - }}, &resp, nil) + }, nonInteractiveChoice}, &resp, nil) if err != nil { if err == terminal.InterruptErr { p.analytics.EventWithLabel(constants.CatPrompt, title, "interrupt") @@ -257,6 +279,15 @@ func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoic } p.analytics.EventWithLabel(constants.CatPrompt, title, translateConfirm(resp)) + switch { + case p.isForced: + p.out.Notice(locale.T("prompt_continue_force")) + case !p.isInteractive && resp: + p.out.Notice(locale.T("prompt_continue_non_interactive")) + case !p.isInteractive && !resp: + return false, locale.NewInputError("prompt_abort_non_interactive") + } + return resp, nil } From b75482b5310cdb82448e8869fecee90ada254e39 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 22 Nov 2024 17:18:13 -0500 Subject: [PATCH 421/440] When deploying via link, deploy symlinks directly instead of resolving them first. --- internal/smartlink/smartlink.go | 32 +++++++++++----------------- internal/smartlink/smartlink_test.go | 2 +- pkg/runtime/depot.go | 4 ++-- pkg/runtime/links_windows.go | 2 +- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/internal/smartlink/smartlink.go b/internal/smartlink/smartlink.go index 2c62efd01f..f32c806a6b 100644 --- a/internal/smartlink/smartlink.go +++ b/internal/smartlink/smartlink.go @@ -10,7 +10,7 @@ import ( ) // LinkContents will link the contents of src to desc -func LinkContents(src, dest string, visited map[string]bool) error { +func LinkContents(src, dest string) error { if !fileutils.DirExists(src) { return errs.New("src dir does not exist: %s", src) } @@ -29,7 +29,7 @@ func LinkContents(src, dest string, visited map[string]bool) error { return errs.Wrap(err, "Reading dir %s failed", src) } for _, entry := range entries { - if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil { + if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name())); err != nil { return errs.Wrap(err, "Link failed") } } @@ -39,27 +39,21 @@ func LinkContents(src, dest string, visited map[string]bool) error { // Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure), // which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks. -func Link(src, dest string, visited map[string]bool) error { - srcWasSymlink := isSymlink(src) - +func Link(src, dest string) error { var err error src, dest, err = resolvePaths(src, dest) if err != nil { return errs.Wrap(err, "Could not resolve src and dest paths") } - if visited == nil { - visited = make(map[string]bool) - } - if _, exists := visited[src]; exists && srcWasSymlink { - // We've encountered a recursive link. This is most often the case when the resolved src has - // already been visited. In that case, just link the dest to the src (which may be a directory; - // this is fine). - return linkFile(src, dest) - } - visited[src] = true - if fileutils.IsDir(src) { + if isSymlink(src) { + // Links to directories are okay on Linux and macOS, but will fail on Windows. + // If we ever get here on Windows, the artifact being deployed is bad and there's nothing we + // can do about it except receive the report from Rollbar and report it internally. + return linkFile(src, dest) + } + if err := fileutils.Mkdir(dest); err != nil { return errs.Wrap(err, "could not create directory %s", dest) } @@ -68,7 +62,7 @@ func Link(src, dest string, visited map[string]bool) error { return errs.Wrap(err, "could not read directory %s", src) } for _, entry := range entries { - if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil { + if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name())); err != nil { return errs.Wrap(err, "sub link failed") } } @@ -153,11 +147,11 @@ func isSymlink(src string) bool { // This is to ensure that we're always comparing apples to apples when doing string comparisons on paths. func resolvePaths(src, dest string) (string, string, error) { var err error - src, err = fileutils.ResolveUniquePath(src) + src, err = filepath.Abs(filepath.Clean(src)) if err != nil { return "", "", errs.Wrap(err, "Could not resolve src path") } - dest, err = fileutils.ResolveUniquePath(dest) + dest, err = filepath.Abs(filepath.Clean(dest)) if err != nil { return "", "", errs.Wrap(err, "Could not resolve dest path") } diff --git a/internal/smartlink/smartlink_test.go b/internal/smartlink/smartlink_test.go index 5b52fbb0d3..50a772254f 100644 --- a/internal/smartlink/smartlink_test.go +++ b/internal/smartlink/smartlink_test.go @@ -42,7 +42,7 @@ func TestLinkContentsWithCircularLink(t *testing.T) { err = os.Symlink(subDir, circularLink) require.NoError(t, err) - err = LinkContents(srcDir, destDir, nil) + err = LinkContents(srcDir, destDir) if runtime.GOOS == "windows" { require.Error(t, err) return // hard links to directories are not allowed on Windows diff --git a/pkg/runtime/depot.go b/pkg/runtime/depot.go index c53a1ac1e6..610418e952 100644 --- a/pkg/runtime/depot.go +++ b/pkg/runtime/depot.go @@ -177,7 +177,7 @@ func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string) } // Copy or link the artifact files, depending on whether the artifact in question relies on file transformations - if err := smartlink.LinkContents(absoluteSrc, absoluteDest, nil); err != nil { + if err := smartlink.LinkContents(absoluteSrc, absoluteDest); err != nil { return errs.Wrap(err, "failed to link artifact") } @@ -295,7 +295,7 @@ func (d *depot) Undeploy(id strfmt.UUID, relativeSrc, path string) error { for sharedFile, relinkSrc := range redeploys { switch deploy.Type { case deploymentTypeLink: - if err := smartlink.Link(relinkSrc, sharedFile, nil); err != nil { + if err := smartlink.Link(relinkSrc, sharedFile); err != nil { return errs.Wrap(err, "failed to relink file") } case deploymentTypeCopy: diff --git a/pkg/runtime/links_windows.go b/pkg/runtime/links_windows.go index 907882a66f..0bdbbe79e8 100644 --- a/pkg/runtime/links_windows.go +++ b/pkg/runtime/links_windows.go @@ -43,7 +43,7 @@ func supportsHardLinks(path string) (supported bool) { } logging.Debug("Attempting to link '%s' to '%s'", lnk, target) - err = smartlink.Link(target, lnk, nil) + err = smartlink.Link(target, lnk) if err != nil { logging.Debug("Test link creation failed: %v", err) return false From cef990a7680976fbd46509485f089c54a176a42c Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 26 Nov 2024 10:51:00 -0500 Subject: [PATCH 422/440] Update remote installer expectations. --- test/integration/remote_installer_int_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/integration/remote_installer_int_test.go b/test/integration/remote_installer_int_test.go index 65949c3fb4..96c4ef50b9 100644 --- a/test/integration/remote_installer_int_test.go +++ b/test/integration/remote_installer_int_test.go @@ -74,7 +74,7 @@ func (suite *RemoteInstallIntegrationTestSuite) TestInstall() { installPath := filepath.Join(ts.Dirs.Work, "install") stateExePath := filepath.Join(installPath, "bin", constants.StateCmd+osutils.ExeExtension) - args := []string{"-n"} + args := []string{"--non-interactive"} if tt.Version != "" { args = append(args, "--version", tt.Version) } @@ -93,10 +93,15 @@ func (suite *RemoteInstallIntegrationTestSuite) TestInstall() { ) cp.Expect("Terms of Service") - cp.SendLine("Y") + cp.Expect("Continuing because State Tool is running in non-interactive mode") cp.Expect("Downloading") cp.Expect("Running Installer...") cp.Expect("Installing") + if runtime.GOOS == "windows" { + cp.Expect("You are installing the State Tool as an admin") + cp.Expect("Are you sure") + cp.Expect("Continuing because State Tool is running in non-interactive mode") + } cp.Expect("Installation Complete") cp.Expect("Press ENTER to exit") cp.SendEnter() From fdb87b037d41a4f60e69c39ba10d252c4084ba25 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 26 Nov 2024 15:13:09 -0500 Subject: [PATCH 423/440] `state pull` should use the already-merged commit's build script instead of attempting a merge. There will be no strategy, and lead to a nil-pointer exception. --- internal/runners/pull/pull.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/runners/pull/pull.go b/internal/runners/pull/pull.go index 0e9ea15c92..4048e4097d 100644 --- a/internal/runners/pull/pull.go +++ b/internal/runners/pull/pull.go @@ -278,12 +278,10 @@ func (p *Pull) mergeBuildScript(remoteCommit, localCommit strfmt.UUID) error { // Compute the merge strategy. strategies, err := model.MergeCommit(remoteCommit, localCommit) if err != nil { - switch { - case errors.Is(err, model.ErrMergeFastForward): + if errors.Is(err, model.ErrMergeFastForward) || errors.Is(err, model.ErrMergeCommitInHistory) { return buildscript_runbit.Update(p.project, scriptB) - case !errors.Is(err, model.ErrMergeCommitInHistory): - return locale.WrapError(err, "err_mergecommit", "Could not detect if merge is necessary.") } + return locale.WrapError(err, "err_mergecommit", "Could not detect if merge is necessary.") } // Attempt the merge. From d1a7e18160cf6c9badad22e50292c3a85111d210 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 27 Nov 2024 11:08:08 -0500 Subject: [PATCH 424/440] Minimize changes from previous commit. --- internal/smartlink/smartlink.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/smartlink/smartlink.go b/internal/smartlink/smartlink.go index f32c806a6b..c101abde5e 100644 --- a/internal/smartlink/smartlink.go +++ b/internal/smartlink/smartlink.go @@ -40,6 +40,8 @@ func LinkContents(src, dest string) error { // Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure), // which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks. func Link(src, dest string) error { + originalSrc := src + var err error src, dest, err = resolvePaths(src, dest) if err != nil { @@ -47,11 +49,14 @@ func Link(src, dest string) error { } if fileutils.IsDir(src) { - if isSymlink(src) { + if fileutils.IsSymlink(originalSrc) { + // If the original src is a symlink, the resolved src is no longer a symlink and could point + // to a parent directory, resulting in a recursive directory structure. + // Avoid any potential problems by simply linking the original link to the target. // Links to directories are okay on Linux and macOS, but will fail on Windows. // If we ever get here on Windows, the artifact being deployed is bad and there's nothing we // can do about it except receive the report from Rollbar and report it internally. - return linkFile(src, dest) + return linkFile(originalSrc, dest) } if err := fileutils.Mkdir(dest); err != nil { @@ -138,20 +143,15 @@ func UnlinkContents(src, dest string) error { return nil } -func isSymlink(src string) bool { - target, err := fileutils.SymlinkTarget(src) - return err == nil && src != target -} - // resolvePaths will resolve src and dest to absolute paths and return them. // This is to ensure that we're always comparing apples to apples when doing string comparisons on paths. func resolvePaths(src, dest string) (string, string, error) { var err error - src, err = filepath.Abs(filepath.Clean(src)) + src, err = fileutils.ResolveUniquePath(src) if err != nil { return "", "", errs.Wrap(err, "Could not resolve src path") } - dest, err = filepath.Abs(filepath.Clean(dest)) + dest, err = fileutils.ResolveUniquePath(dest) if err != nil { return "", "", errs.Wrap(err, "Could not resolve dest path") } From 4d46d28c9bacacc06fac456b2c6e674db496cab7 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 13 Dec 2024 13:28:52 -0500 Subject: [PATCH 425/440] Stop creating new users. The Dashboard requires e-mail confirmation which we cannot do. --- internal/testhelpers/e2e/session.go | 44 ----------------------------- test/integration/fork_int_test.go | 20 +++++-------- test/integration/import_int_test.go | 7 +++-- test/integration/push_int_test.go | 7 ++--- 4 files changed, 15 insertions(+), 63 deletions(-) diff --git a/internal/testhelpers/e2e/session.go b/internal/testhelpers/e2e/session.go index 9655aae58f..17d993cfea 100644 --- a/internal/testhelpers/e2e/session.go +++ b/internal/testhelpers/e2e/session.go @@ -28,9 +28,6 @@ import ( "github.com/ActiveState/cli/internal/subshell/bash" "github.com/ActiveState/cli/internal/subshell/sscommon" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" - "github.com/ActiveState/cli/pkg/platform/api" - "github.com/ActiveState/cli/pkg/platform/api/mono" - "github.com/ActiveState/cli/pkg/platform/api/mono/mono_client/users" "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" "github.com/ActiveState/cli/pkg/platform/authentication" "github.com/ActiveState/cli/pkg/platform/model" @@ -39,7 +36,6 @@ import ( "github.com/ActiveState/cli/pkg/projectfile" "github.com/ActiveState/termtest" "github.com/go-openapi/strfmt" - "github.com/google/uuid" "github.com/phayes/permbits" "github.com/stretchr/testify/require" ) @@ -448,48 +444,8 @@ func (s *Session) LogoutUser() { p.ExpectExitCode(0) } -func (s *Session) CreateNewUser() *mono_models.UserEditable { - uid, err := uuid.NewRandom() - require.NoError(s.T, err) - - username := fmt.Sprintf("user-%s", uid.String()[0:8]) - password := uid.String()[8:] - email := fmt.Sprintf("%s@test.tld", username) - user := &mono_models.UserEditable{ - Username: username, - Password: password, - Name: username, - Email: email, - } - - params := users.NewAddUserParams() - params.SetUser(user) - - // The default mono API client host is "testing.tld" inside unit tests. - // Since we actually want to create production users, we need to manually instantiate a mono API - // client with the right host. - serviceURL := api.GetServiceURL(api.ServiceMono) - host := os.Getenv(constants.APIHostEnvVarName) - if host == "" { - host = constants.DefaultAPIHost - } - serviceURL.Host = strings.Replace(serviceURL.Host, string(api.ServiceMono)+api.TestingPlatform, host, 1) - _, err = mono.Init(serviceURL, nil).Users.AddUser(params) - require.NoError(s.T, err, "Error creating new user") - - p := s.Spawn(tagsuite.Auth, "--username", username, "--password", password) - p.Expect("logged in") - p.ExpectExitCode(0) - - s.users = append(s.users, username) - - return user -} - // NotifyProjectCreated indicates that the given project was created on the Platform and needs to // be deleted when the session is closed. -// This only needs to be called for projects created by PersistentUsername, not projects created by -// users created with CreateNewUser(). Created users' projects are auto-deleted. func (s *Session) NotifyProjectCreated(org, name string) { s.createdProjects = append(s.createdProjects, project.NewNamespace(org, name, "")) } diff --git a/test/integration/fork_int_test.go b/test/integration/fork_int_test.go index f26df5e682..31cdf1bc75 100644 --- a/test/integration/fork_int_test.go +++ b/test/integration/fork_int_test.go @@ -2,12 +2,10 @@ package integration import ( "testing" - "time" - - "github.com/ActiveState/cli/internal/testhelpers/suite" - "github.com/ActiveState/termtest" + "github.com/ActiveState/cli/internal/strutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" + "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" ) @@ -26,17 +24,13 @@ func (suite *ForkIntegrationTestSuite) TestFork() { ts := e2e.New(suite.T(), false) defer suite.cleanup(ts) - user := ts.CreateNewUser() + ts.LoginAsPersistentUser() + pname := strutils.UUID() - cp := ts.Spawn("fork", "ActiveState-CLI/Python3", "--name", "Test-Python3", "--org", user.Username) + cp := ts.Spawn("fork", "ActiveState-CLI/Python3", "--name", pname.String(), "--org", e2e.PersistentUsername) cp.Expect("fork has been successfully created") cp.ExpectExitCode(0) - - // Check if we error out on conflicts properly - cp = ts.Spawn("fork", "ActiveState-CLI/Python3", "--name", "Test-Python3", "--org", user.Username) - cp.Expect(`You already have a project with the name`) - cp.ExpectExitCode(1) - ts.IgnoreLogErrors() + ts.NotifyProjectCreated(e2e.PersistentUsername, pname.String()) } func (suite *ForkIntegrationTestSuite) TestFork_FailNameExists() { @@ -46,7 +40,7 @@ func (suite *ForkIntegrationTestSuite) TestFork_FailNameExists() { ts.LoginAsPersistentUser() cp := ts.Spawn("fork", "ActiveState-CLI/Python3", "--org", e2e.PersistentUsername) - cp.Expect("You already have a project with the name 'Python3'", termtest.OptExpectTimeout(30*time.Second)) + cp.Expect("You already have a project with the name 'Python3'") cp.ExpectNotExitCode(0) ts.IgnoreLogErrors() } diff --git a/test/integration/import_int_test.go b/test/integration/import_int_test.go index 47c9c9cd7b..8b4b5eba5f 100644 --- a/test/integration/import_int_test.go +++ b/test/integration/import_int_test.go @@ -11,6 +11,7 @@ import ( "github.com/ActiveState/termtest" "github.com/ActiveState/cli/internal/constants" + "github.com/ActiveState/cli/internal/strutils" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/osutil" "github.com/ActiveState/cli/internal/testhelpers/suite" @@ -85,12 +86,14 @@ func (suite *ImportIntegrationTestSuite) TestImport() { ts := e2e.New(suite.T(), false) defer ts.Close() - user := ts.CreateNewUser() - namespace := fmt.Sprintf("%s/%s", user.Username, "Python3") + ts.LoginAsPersistentUser() + pname := strutils.UUID() + namespace := fmt.Sprintf("%s/%s", e2e.PersistentUsername, pname.String()) cp := ts.Spawn("init", "--language", "python", namespace, ts.Dirs.Work) cp.Expect("successfully initialized", e2e.RuntimeSourcingTimeoutOpt) cp.ExpectExitCode(0) + ts.NotifyProjectCreated(e2e.PersistentUsername, pname.String()) reqsFilePath := filepath.Join(cp.WorkDirectory(), reqsFileName) diff --git a/test/integration/push_int_test.go b/test/integration/push_int_test.go index 6a6e03514f..4f3276cd9c 100644 --- a/test/integration/push_int_test.go +++ b/test/integration/push_int_test.go @@ -116,7 +116,7 @@ func (suite *PushIntegrationTestSuite) TestPush_NoPermission_NewProject() { suite.OnlyRunForTags(tagsuite.Push) ts := e2e.New(suite.T(), false) defer ts.Close() - user := ts.CreateNewUser() + ts.LoginAsPersistentUser() pname := strutils.UUID() cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") @@ -155,12 +155,11 @@ func (suite *PushIntegrationTestSuite) TestPush_NoPermission_NewProject() { cp.SendLine(pname.String()) cp.Expect("Project created") cp.ExpectExitCode(0) - // Note: no need for ts.NotifyProjectCreated because newly created users and their projects are - // auto-cleaned by e2e. + ts.NotifyProjectCreated(e2e.PersistentUsername, pname.String()) pjfile, err = projectfile.Parse(pjfilepath) suite.Require().NoError(err) - suite.Require().Contains(pjfile.Project, user.Username) + suite.Require().Contains(pjfile.Project, e2e.PersistentUsername) suite.Require().Contains(pjfile.Project, pname.String()) } From 5f636e5249c31e9eb42dbbad969fa8fb6a4a4a8a Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 13 Dec 2024 13:55:28 -0500 Subject: [PATCH 426/440] Disable test. --- test/integration/push_int_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/push_int_test.go b/test/integration/push_int_test.go index 4f3276cd9c..7c17d65aa8 100644 --- a/test/integration/push_int_test.go +++ b/test/integration/push_int_test.go @@ -109,6 +109,7 @@ func (suite *PushIntegrationTestSuite) TestInitAndPush() { // Test pushing without permission, and choosing to create a new project func (suite *PushIntegrationTestSuite) TestPush_NoPermission_NewProject() { + suite.T().Skip("Cannot create new unprivileged users") // DX-3190 if runtime.GOOS == "windows" { suite.T().Skip("Skipped on Windows for now because SendKeyDown() doesnt work (regardless of bash/cmd)") } From 65777bc32858baf6f0757d84244b5183a09c7148 Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 13 Dec 2024 15:32:21 -0500 Subject: [PATCH 427/440] Added tests for projects that utilize solver V2 and solver V3. --- internal/testhelpers/tagsuite/tagsuite.go | 2 + test/integration/install_int_test.go | 127 ++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/internal/testhelpers/tagsuite/tagsuite.go b/internal/testhelpers/tagsuite/tagsuite.go index f735e815b4..2dc587c8cf 100644 --- a/internal/testhelpers/tagsuite/tagsuite.go +++ b/internal/testhelpers/tagsuite/tagsuite.go @@ -79,6 +79,8 @@ const ( Service = "service" Shell = "shell" Show = "show" + SolverV2 = "solver-v2" + SolverV3 = "solver-v3" Switch = "switch" Uninstall = "uninstall" Upgrade = "upgrade" diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index 4f50a4ced8..f7c0a2d3c5 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -1,6 +1,7 @@ package integration import ( + "path/filepath" "strings" "testing" @@ -131,6 +132,132 @@ func (suite *InstallIntegrationTestSuite) TestInstall_Resolved() { cp.ExpectExitCode(0) } +func (suite *InstallIntegrationTestSuite) TestInstall_SolverV2() { + suite.OnlyRunForTags(tagsuite.Install, tagsuite.SolverV2) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + tests := []struct { + Name string + Namespace string + Package string + ExpectedFail bool + }{ + { + "Python-Camel", + "ActiveState-CLI/Python3#971e48e4-7f9b-44e6-ad48-86cd03ffc12d", + "requests", + false, + }, + { + "Python-Alternative", + "ActiveState-CLI/Python3-Alternative#c2b3f176-4788-479c-aad3-8359d28ba3ce", + "requests", + false, + }, + { + "Perl-Camel", + "ActiveState-CLI/Perl#a0a1692e-d999-4763-b933-2d0d5758bf12", + "JSON", + false, + }, + { + "Perl-Alternative", + "ActiveState-CLI/Perl-Alternative#ccc57e0b-fccf-41c1-8e1c-24f4de2e55fa", + "JSON", + false, + }, + { + "Ruby-Alternative", + "ActiveState-CLI/ruby#b6540776-7f2c-461b-8924-77fe46669209", + "base64", + false, + }, + } + + for _, tt := range tests { + suite.Run(tt.Name, func() { + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("checkout", tt.Namespace, tt.Name) + if !tt.ExpectedFail { + cp.ExpectExitCode(0) + } else { + cp.ExpectNotExitCode(0) + return + } + + cp = ts.SpawnWithOpts( + e2e.OptArgs("install", tt.Package), + e2e.OptWD(filepath.Join(ts.Dirs.Work, tt.Name)), + ) + cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt) + }) + } + +} + +func (suite *InstallIntegrationTestSuite) TestInstall_SolverV3() { + suite.OnlyRunForTags(tagsuite.Install, tagsuite.SolverV3) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + tests := []struct { + Name string + Namespace string + Package string + ExpectedFail bool + }{ + { + "Python", + "ActiveState-CLI/Python3-Alternative-V3#354efec1-eaa3-4f41-bc50-08fdbf076628", + "requests", + false, + }, + { + "Perl", + "ActiveState-CLI/Perl-Alternative-V3#3d66ff94-72be-43ce-b3d8-897bb6758cf0", + "JSON", + false, + }, + { + "Ruby", + "ActiveState-CLI/ruby-V3#6db5b307-d63a-45e2-9d3b-70a1a1f6c10a", + "base64", + true, + }, + } + + for _, tt := range tests { + suite.Run(tt.Name, func() { + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("config", "set", constants.AsyncRuntimeConfig, "true") + cp.ExpectExitCode(0) + + cp = ts.Spawn("checkout", tt.Namespace, tt.Name) + if !tt.ExpectedFail { + cp.ExpectExitCode(0) + } else { + cp.ExpectNotExitCode(0) + return + } + + cp = ts.SpawnWithOpts( + e2e.OptArgs("install", tt.Package), + e2e.OptWD(filepath.Join(ts.Dirs.Work, tt.Name)), + ) + cp.ExpectExitCode(0, e2e.RuntimeSolvingTimeoutOpt) + }) + } + +} + func TestInstallIntegrationTestSuite(t *testing.T) { suite.Run(t, new(InstallIntegrationTestSuite)) } From a83eff3696c89bfd2efef417989ccbb19f80e7a4 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 16 Dec 2024 10:27:30 -0500 Subject: [PATCH 428/440] Remediate CVE in golang.org/x/crypto. --- go.mod | 10 +- go.sum | 10 + .../github.com/stretchr/objx/.codeclimate.yml | 21 - vendor/github.com/stretchr/objx/.gitignore | 11 - vendor/github.com/stretchr/objx/LICENSE | 22 - vendor/github.com/stretchr/objx/README.md | 80 - vendor/github.com/stretchr/objx/Taskfile.yml | 27 - vendor/github.com/stretchr/objx/accessors.go | 197 -- .../github.com/stretchr/objx/conversions.go | 280 -- vendor/github.com/stretchr/objx/doc.go | 66 - vendor/github.com/stretchr/objx/map.go | 214 -- vendor/github.com/stretchr/objx/mutations.go | 77 - vendor/github.com/stretchr/objx/security.go | 12 - vendor/github.com/stretchr/objx/tests.go | 17 - .../github.com/stretchr/objx/type_specific.go | 346 --- .../stretchr/objx/type_specific_codegen.go | 2261 ----------------- vendor/github.com/stretchr/objx/value.go | 159 -- .../github.com/stretchr/testify/mock/doc.go | 44 - .../github.com/stretchr/testify/mock/mock.go | 1241 --------- .../x/crypto/chacha20/chacha_noasm.go | 2 +- .../{chacha_ppc64le.go => chacha_ppc64x.go} | 2 +- .../{chacha_ppc64le.s => chacha_ppc64x.s} | 114 +- .../x/crypto/internal/poly1305/mac_noasm.go | 2 +- .../{sum_ppc64le.go => sum_ppc64x.go} | 2 +- .../poly1305/{sum_ppc64le.s => sum_ppc64x.s} | 30 +- vendor/golang.org/x/crypto/sha3/doc.go | 4 + vendor/golang.org/x/crypto/sha3/hashes.go | 31 +- vendor/golang.org/x/crypto/sha3/sha3.go | 187 +- vendor/golang.org/x/crypto/sha3/shake.go | 89 +- vendor/golang.org/x/crypto/sha3/xor.go | 40 - vendor/golang.org/x/crypto/ssh/client_auth.go | 5 + vendor/golang.org/x/crypto/ssh/server.go | 19 +- .../golang.org/x/sys/cpu/asm_darwin_x86_gc.s | 17 + vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go | 61 + vendor/golang.org/x/sys/cpu/cpu_gc_x86.go | 4 +- .../x/sys/cpu/{cpu_x86.s => cpu_gc_x86.s} | 2 +- vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go | 6 - .../golang.org/x/sys/cpu/cpu_linux_arm64.go | 1 - vendor/golang.org/x/sys/cpu/cpu_other_x86.go | 11 + vendor/golang.org/x/sys/cpu/cpu_x86.go | 6 +- .../x/sys/cpu/syscall_darwin_x86_gc.go | 98 + vendor/golang.org/x/sys/unix/README.md | 2 +- vendor/golang.org/x/sys/unix/ioctl_linux.go | 96 + vendor/golang.org/x/sys/unix/mkerrors.sh | 16 +- vendor/golang.org/x/sys/unix/syscall_aix.go | 2 +- vendor/golang.org/x/sys/unix/syscall_linux.go | 64 +- .../x/sys/unix/syscall_linux_arm64.go | 2 + .../x/sys/unix/syscall_linux_loong64.go | 2 + .../x/sys/unix/syscall_linux_riscv64.go | 2 + .../x/sys/unix/syscall_zos_s390x.go | 104 +- .../golang.org/x/sys/unix/vgetrandom_linux.go | 13 + .../x/sys/unix/vgetrandom_unsupported.go | 11 + vendor/golang.org/x/sys/unix/zerrors_linux.go | 44 +- .../x/sys/unix/zerrors_linux_386.go | 25 + .../x/sys/unix/zerrors_linux_amd64.go | 25 + .../x/sys/unix/zerrors_linux_arm.go | 25 + .../x/sys/unix/zerrors_linux_arm64.go | 26 + .../x/sys/unix/zerrors_linux_loong64.go | 25 + .../x/sys/unix/zerrors_linux_mips.go | 25 + .../x/sys/unix/zerrors_linux_mips64.go | 25 + .../x/sys/unix/zerrors_linux_mips64le.go | 25 + .../x/sys/unix/zerrors_linux_mipsle.go | 25 + .../x/sys/unix/zerrors_linux_ppc.go | 25 + .../x/sys/unix/zerrors_linux_ppc64.go | 25 + .../x/sys/unix/zerrors_linux_ppc64le.go | 25 + .../x/sys/unix/zerrors_linux_riscv64.go | 25 + .../x/sys/unix/zerrors_linux_s390x.go | 25 + .../x/sys/unix/zerrors_linux_sparc64.go | 25 + .../golang.org/x/sys/unix/zsyscall_linux.go | 27 +- .../x/sys/unix/zsysnum_linux_amd64.go | 1 + .../x/sys/unix/zsysnum_linux_arm64.go | 2 +- .../x/sys/unix/zsysnum_linux_loong64.go | 2 + .../x/sys/unix/zsysnum_linux_riscv64.go | 2 +- .../x/sys/unix/ztypes_darwin_amd64.go | 60 + .../x/sys/unix/ztypes_darwin_arm64.go | 60 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 222 +- .../golang.org/x/sys/unix/ztypes_zos_s390x.go | 6 + .../golang.org/x/sys/windows/dll_windows.go | 2 +- .../x/sys/windows/syscall_windows.go | 36 +- .../golang.org/x/sys/windows/types_windows.go | 127 + .../x/sys/windows/zsyscall_windows.go | 71 + vendor/golang.org/x/term/README.md | 11 +- vendor/modules.txt | 12 +- 83 files changed, 1811 insertions(+), 5392 deletions(-) delete mode 100644 vendor/github.com/stretchr/objx/.codeclimate.yml delete mode 100644 vendor/github.com/stretchr/objx/.gitignore delete mode 100644 vendor/github.com/stretchr/objx/LICENSE delete mode 100644 vendor/github.com/stretchr/objx/README.md delete mode 100644 vendor/github.com/stretchr/objx/Taskfile.yml delete mode 100644 vendor/github.com/stretchr/objx/accessors.go delete mode 100644 vendor/github.com/stretchr/objx/conversions.go delete mode 100644 vendor/github.com/stretchr/objx/doc.go delete mode 100644 vendor/github.com/stretchr/objx/map.go delete mode 100644 vendor/github.com/stretchr/objx/mutations.go delete mode 100644 vendor/github.com/stretchr/objx/security.go delete mode 100644 vendor/github.com/stretchr/objx/tests.go delete mode 100644 vendor/github.com/stretchr/objx/type_specific.go delete mode 100644 vendor/github.com/stretchr/objx/type_specific_codegen.go delete mode 100644 vendor/github.com/stretchr/objx/value.go delete mode 100644 vendor/github.com/stretchr/testify/mock/doc.go delete mode 100644 vendor/github.com/stretchr/testify/mock/mock.go rename vendor/golang.org/x/crypto/chacha20/{chacha_ppc64le.go => chacha_ppc64x.go} (89%) rename vendor/golang.org/x/crypto/chacha20/{chacha_ppc64le.s => chacha_ppc64x.s} (76%) rename vendor/golang.org/x/crypto/internal/poly1305/{sum_ppc64le.go => sum_ppc64x.go} (95%) rename vendor/golang.org/x/crypto/internal/poly1305/{sum_ppc64le.s => sum_ppc64x.s} (89%) delete mode 100644 vendor/golang.org/x/crypto/sha3/xor.go create mode 100644 vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s create mode 100644 vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go rename vendor/golang.org/x/sys/cpu/{cpu_x86.s => cpu_gc_x86.s} (94%) create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_x86.go create mode 100644 vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go create mode 100644 vendor/golang.org/x/sys/unix/vgetrandom_linux.go create mode 100644 vendor/golang.org/x/sys/unix/vgetrandom_unsupported.go diff --git a/go.mod b/go.mod index ce4d3982ba..12567127cc 100644 --- a/go.mod +++ b/go.mod @@ -57,11 +57,11 @@ require ( github.com/thoas/go-funk v0.8.0 github.com/vbauerster/mpb/v7 v7.1.5 github.com/vektah/gqlparser/v2 v2.5.16 - golang.org/x/crypto v0.27.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.29.0 - golang.org/x/sys v0.25.0 - golang.org/x/term v0.24.0 - golang.org/x/text v0.18.0 + golang.org/x/sys v0.28.0 + golang.org/x/term v0.27.0 + golang.org/x/text v0.21.0 gopkg.in/AlecAivazis/survey.v1 v1.8.8 gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 gopkg.in/yaml.v2 v2.4.0 @@ -111,7 +111,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/skeema/knownhosts v1.2.2 // indirect github.com/sosodev/duration v1.3.1 // indirect - golang.org/x/sync v0.8.0 // indirect + golang.org/x/sync v0.10.0 // indirect ) require ( diff --git a/go.sum b/go.sum index 77b18a745a..9e9fc36e64 100644 --- a/go.sum +++ b/go.sum @@ -731,6 +731,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -805,6 +807,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -857,6 +861,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -866,6 +872,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -879,6 +887,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= diff --git a/vendor/github.com/stretchr/objx/.codeclimate.yml b/vendor/github.com/stretchr/objx/.codeclimate.yml deleted file mode 100644 index 559fa399c1..0000000000 --- a/vendor/github.com/stretchr/objx/.codeclimate.yml +++ /dev/null @@ -1,21 +0,0 @@ -engines: - gofmt: - enabled: true - golint: - enabled: true - govet: - enabled: true - -exclude_patterns: -- ".github/" -- "vendor/" -- "codegen/" -- "*.yml" -- ".*.yml" -- "*.md" -- "Gopkg.*" -- "doc.go" -- "type_specific_codegen_test.go" -- "type_specific_codegen.go" -- ".gitignore" -- "LICENSE" diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore deleted file mode 100644 index ea58090bd2..0000000000 --- a/vendor/github.com/stretchr/objx/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/github.com/stretchr/objx/LICENSE b/vendor/github.com/stretchr/objx/LICENSE deleted file mode 100644 index 44d4d9d5a7..0000000000 --- a/vendor/github.com/stretchr/objx/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2014 Stretchr, Inc. -Copyright (c) 2017-2018 objx contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md deleted file mode 100644 index 78dc1f8b03..0000000000 --- a/vendor/github.com/stretchr/objx/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Objx -[![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx) -[![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx) -[![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability) -[![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage) -[![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) -[![GoDoc](https://pkg.go.dev/badge/github.com/stretchr/objx?utm_source=godoc)](https://pkg.go.dev/github.com/stretchr/objx) - -Objx - Go package for dealing with maps, slices, JSON and other data. - -Get started: - -- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) -- Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx - -## Overview -Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. - -### Pattern -Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: - - m, err := objx.FromJSON(json) - -NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. - -Use `Get` to access the value you're interested in. You can use dot and array -notation too: - - m.Get("places[0].latlng") - -Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - - if m.Get("code").IsStr() { // Your code... } - -Or you can just assume the type, and use one of the strong type methods to extract the real value: - - m.Get("code").Int() - -If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. - - Get("code").Int(-1) - -If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. - -### Reading data -A simple example of how to use Objx: - - // Use MustFromJSON to make an objx.Map from some JSON - m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - - // Get the details - name := m.Get("name").Str() - age := m.Get("age").Int() - - // Get their nickname (or use their name if they don't have one) - nickname := m.Get("nickname").Str(name) - -### Ranging -Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: - - m := objx.MustFromJSON(json) - for key, value := range m { - // Your code... - } - -## Installation -To install Objx, use go get: - - go get github.com/stretchr/objx - -### Staying up to date -To update Objx to the latest version, run: - - go get -u github.com/stretchr/objx - -### Supported go versions -We currently support the three recent major Go versions. - -## Contributing -Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml deleted file mode 100644 index 8a79e8d674..0000000000 --- a/vendor/github.com/stretchr/objx/Taskfile.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: '3' - -tasks: - default: - deps: [test] - - lint: - desc: Checks code style - cmds: - - gofmt -d -s *.go - - go vet ./... - silent: true - - lint-fix: - desc: Fixes code style - cmds: - - gofmt -w -s *.go - - test: - desc: Runs go tests - cmds: - - go test -race ./... - - test-coverage: - desc: Runs go tests and calculates test coverage - cmds: - - go test -race -coverprofile=c.out ./... diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go deleted file mode 100644 index 72f1d1c1ce..0000000000 --- a/vendor/github.com/stretchr/objx/accessors.go +++ /dev/null @@ -1,197 +0,0 @@ -package objx - -import ( - "reflect" - "regexp" - "strconv" - "strings" -) - -const ( - // PathSeparator is the character used to separate the elements - // of the keypath. - // - // For example, `location.address.city` - PathSeparator string = "." - - // arrayAccessRegexString is the regex used to extract the array number - // from the access path - arrayAccessRegexString = `^(.+)\[([0-9]+)\]$` - - // mapAccessRegexString is the regex used to extract the map key - // from the access path - mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$` -) - -// arrayAccessRegex is the compiled arrayAccessRegexString -var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString) - -// mapAccessRegex is the compiled mapAccessRegexString -var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) - -// Get gets the value using the specified selector and -// returns it inside a new Obj object. -// -// If it cannot find the value, Get will return a nil -// value inside an instance of Obj. -// -// Get can only operate directly on map[string]interface{} and []interface. -// -// # Example -// -// To access the title of the third chapter of the second book, do: -// -// o.Get("books[1].chapters[2].title") -func (m Map) Get(selector string) *Value { - rawObj := access(m, selector, nil, false) - return &Value{data: rawObj} -} - -// Set sets the value using the specified selector and -// returns the object on which Set was called. -// -// Set can only operate directly on map[string]interface{} and []interface -// -// # Example -// -// To set the title of the third chapter of the second book, do: -// -// o.Set("books[1].chapters[2].title","Time to Go") -func (m Map) Set(selector string, value interface{}) Map { - access(m, selector, value, true) - return m -} - -// getIndex returns the index, which is hold in s by two branches. -// It also returns s without the index part, e.g. name[1] will return (1, name). -// If no index is found, -1 is returned -func getIndex(s string) (int, string) { - arrayMatches := arrayAccessRegex.FindStringSubmatch(s) - if len(arrayMatches) > 0 { - // Get the key into the map - selector := arrayMatches[1] - // Get the index into the array at the key - // We know this can't fail because arrayMatches[2] is an int for sure - index, _ := strconv.Atoi(arrayMatches[2]) - return index, selector - } - return -1, s -} - -// getKey returns the key which is held in s by two brackets. -// It also returns the next selector. -func getKey(s string) (string, string) { - selSegs := strings.SplitN(s, PathSeparator, 2) - thisSel := selSegs[0] - nextSel := "" - - if len(selSegs) > 1 { - nextSel = selSegs[1] - } - - mapMatches := mapAccessRegex.FindStringSubmatch(s) - if len(mapMatches) > 0 { - if _, err := strconv.Atoi(mapMatches[2]); err != nil { - thisSel = mapMatches[1] - nextSel = "[" + mapMatches[2] + "]" + mapMatches[3] - - if thisSel == "" { - thisSel = mapMatches[2] - nextSel = mapMatches[3] - } - - if nextSel == "" { - selSegs = []string{"", ""} - } else if nextSel[0] == '.' { - nextSel = nextSel[1:] - } - } - } - - return thisSel, nextSel -} - -// access accesses the object using the selector and performs the -// appropriate action. -func access(current interface{}, selector string, value interface{}, isSet bool) interface{} { - thisSel, nextSel := getKey(selector) - - indexes := []int{} - for strings.Contains(thisSel, "[") { - prevSel := thisSel - index := -1 - index, thisSel = getIndex(thisSel) - indexes = append(indexes, index) - if prevSel == thisSel { - break - } - } - - if curMap, ok := current.(Map); ok { - current = map[string]interface{}(curMap) - } - // get the object in question - switch current.(type) { - case map[string]interface{}: - curMSI := current.(map[string]interface{}) - if nextSel == "" && isSet { - curMSI[thisSel] = value - return nil - } - - _, ok := curMSI[thisSel].(map[string]interface{}) - if !ok { - _, ok = curMSI[thisSel].(Map) - } - - if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet { - curMSI[thisSel] = map[string]interface{}{} - } - - current = curMSI[thisSel] - default: - current = nil - } - - // do we need to access the item of an array? - if len(indexes) > 0 { - num := len(indexes) - for num > 0 { - num-- - index := indexes[num] - indexes = indexes[:num] - if array, ok := interSlice(current); ok { - if index < len(array) { - current = array[index] - } else { - current = nil - break - } - } - } - } - - if nextSel != "" { - current = access(current, nextSel, value, isSet) - } - return current -} - -func interSlice(slice interface{}) ([]interface{}, bool) { - if array, ok := slice.([]interface{}); ok { - return array, ok - } - - s := reflect.ValueOf(slice) - if s.Kind() != reflect.Slice { - return nil, false - } - - ret := make([]interface{}, s.Len()) - - for i := 0; i < s.Len(); i++ { - ret[i] = s.Index(i).Interface() - } - - return ret, true -} diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go deleted file mode 100644 index 01c63d7d3b..0000000000 --- a/vendor/github.com/stretchr/objx/conversions.go +++ /dev/null @@ -1,280 +0,0 @@ -package objx - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "net/url" - "strconv" -) - -// SignatureSeparator is the character that is used to -// separate the Base64 string from the security signature. -const SignatureSeparator = "_" - -// URLValuesSliceKeySuffix is the character that is used to -// specify a suffix for slices parsed by URLValues. -// If the suffix is set to "[i]", then the index of the slice -// is used in place of i -// Ex: Suffix "[]" would have the form a[]=b&a[]=c -// OR Suffix "[i]" would have the form a[0]=b&a[1]=c -// OR Suffix "" would have the form a=b&a=c -var urlValuesSliceKeySuffix = "[]" - -const ( - URLValuesSliceKeySuffixEmpty = "" - URLValuesSliceKeySuffixArray = "[]" - URLValuesSliceKeySuffixIndex = "[i]" -) - -// SetURLValuesSliceKeySuffix sets the character that is used to -// specify a suffix for slices parsed by URLValues. -// If the suffix is set to "[i]", then the index of the slice -// is used in place of i -// Ex: Suffix "[]" would have the form a[]=b&a[]=c -// OR Suffix "[i]" would have the form a[0]=b&a[1]=c -// OR Suffix "" would have the form a=b&a=c -func SetURLValuesSliceKeySuffix(s string) error { - if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex { - urlValuesSliceKeySuffix = s - return nil - } - - return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.") -} - -// JSON converts the contained object to a JSON string -// representation -func (m Map) JSON() (string, error) { - for k, v := range m { - m[k] = cleanUp(v) - } - - result, err := json.Marshal(m) - if err != nil { - err = errors.New("objx: JSON encode failed with: " + err.Error()) - } - return string(result), err -} - -func cleanUpInterfaceArray(in []interface{}) []interface{} { - result := make([]interface{}, len(in)) - for i, v := range in { - result[i] = cleanUp(v) - } - return result -} - -func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { - result := Map{} - for k, v := range in { - result[fmt.Sprintf("%v", k)] = cleanUp(v) - } - return result -} - -func cleanUpStringMap(in map[string]interface{}) Map { - result := Map{} - for k, v := range in { - result[k] = cleanUp(v) - } - return result -} - -func cleanUpMSIArray(in []map[string]interface{}) []Map { - result := make([]Map, len(in)) - for i, v := range in { - result[i] = cleanUpStringMap(v) - } - return result -} - -func cleanUpMapArray(in []Map) []Map { - result := make([]Map, len(in)) - for i, v := range in { - result[i] = cleanUpStringMap(v) - } - return result -} - -func cleanUp(v interface{}) interface{} { - switch v := v.(type) { - case []interface{}: - return cleanUpInterfaceArray(v) - case []map[string]interface{}: - return cleanUpMSIArray(v) - case map[interface{}]interface{}: - return cleanUpInterfaceMap(v) - case Map: - return cleanUpStringMap(v) - case []Map: - return cleanUpMapArray(v) - default: - return v - } -} - -// MustJSON converts the contained object to a JSON string -// representation and panics if there is an error -func (m Map) MustJSON() string { - result, err := m.JSON() - if err != nil { - panic(err.Error()) - } - return result -} - -// Base64 converts the contained object to a Base64 string -// representation of the JSON string representation -func (m Map) Base64() (string, error) { - var buf bytes.Buffer - - jsonData, err := m.JSON() - if err != nil { - return "", err - } - - encoder := base64.NewEncoder(base64.StdEncoding, &buf) - _, _ = encoder.Write([]byte(jsonData)) - _ = encoder.Close() - - return buf.String(), nil -} - -// MustBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and panics -// if there is an error -func (m Map) MustBase64() string { - result, err := m.Base64() - if err != nil { - panic(err.Error()) - } - return result -} - -// SignedBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and signs it -// using the provided key. -func (m Map) SignedBase64(key string) (string, error) { - base64, err := m.Base64() - if err != nil { - return "", err - } - - sig := HashWithKey(base64, key) - return base64 + SignatureSeparator + sig, nil -} - -// MustSignedBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and signs it -// using the provided key and panics if there is an error -func (m Map) MustSignedBase64(key string) string { - result, err := m.SignedBase64(key) - if err != nil { - panic(err.Error()) - } - return result -} - -/* - URL Query - ------------------------------------------------ -*/ - -// URLValues creates a url.Values object from an Obj. This -// function requires that the wrapped object be a map[string]interface{} -func (m Map) URLValues() url.Values { - vals := make(url.Values) - - m.parseURLValues(m, vals, "") - - return vals -} - -func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) { - useSliceIndex := false - if urlValuesSliceKeySuffix == "[i]" { - useSliceIndex = true - } - - for k, v := range queryMap { - val := &Value{data: v} - switch { - case val.IsObjxMap(): - if key == "" { - m.parseURLValues(val.ObjxMap(), vals, k) - } else { - m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]") - } - case val.IsObjxMapSlice(): - sliceKey := k - if key != "" { - sliceKey = key + "[" + k + "]" - } - - if useSliceIndex { - for i, sv := range val.MustObjxMapSlice() { - sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" - m.parseURLValues(sv, vals, sk) - } - } else { - sliceKey = sliceKey + urlValuesSliceKeySuffix - for _, sv := range val.MustObjxMapSlice() { - m.parseURLValues(sv, vals, sliceKey) - } - } - case val.IsMSISlice(): - sliceKey := k - if key != "" { - sliceKey = key + "[" + k + "]" - } - - if useSliceIndex { - for i, sv := range val.MustMSISlice() { - sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" - m.parseURLValues(New(sv), vals, sk) - } - } else { - sliceKey = sliceKey + urlValuesSliceKeySuffix - for _, sv := range val.MustMSISlice() { - m.parseURLValues(New(sv), vals, sliceKey) - } - } - case val.IsStrSlice(), val.IsBoolSlice(), - val.IsFloat32Slice(), val.IsFloat64Slice(), - val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(), - val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice(): - - sliceKey := k - if key != "" { - sliceKey = key + "[" + k + "]" - } - - if useSliceIndex { - for i, sv := range val.StringSlice() { - sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" - vals.Set(sk, sv) - } - } else { - sliceKey = sliceKey + urlValuesSliceKeySuffix - vals[sliceKey] = val.StringSlice() - } - - default: - if key == "" { - vals.Set(k, val.String()) - } else { - vals.Set(key+"["+k+"]", val.String()) - } - } - } -} - -// URLQuery gets an encoded URL query representing the given -// Obj. This function requires that the wrapped object be a -// map[string]interface{} -func (m Map) URLQuery() (string, error) { - return m.URLValues().Encode(), nil -} diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go deleted file mode 100644 index b170af74b3..0000000000 --- a/vendor/github.com/stretchr/objx/doc.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Package objx provides utilities for dealing with maps, slices, JSON and other data. - -# Overview - -Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes -a powerful `Get` method (among others) that allows you to easily and quickly get -access to data within the map, without having to worry too much about type assertions, -missing data, default values etc. - -# Pattern - -Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. -Call one of the `objx.` functions to create your `objx.Map` to get going: - - m, err := objx.FromJSON(json) - -NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, -the rest will be optimistic and try to figure things out without panicking. - -Use `Get` to access the value you're interested in. You can use dot and array -notation too: - - m.Get("places[0].latlng") - -Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - - if m.Get("code").IsStr() { // Your code... } - -Or you can just assume the type, and use one of the strong type methods to extract the real value: - - m.Get("code").Int() - -If there's no value there (or if it's the wrong type) then a default value will be returned, -or you can be explicit about the default value. - - Get("code").Int(-1) - -If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, -manipulating and selecting that data. You can find out more by exploring the index below. - -# Reading data - -A simple example of how to use Objx: - - // Use MustFromJSON to make an objx.Map from some JSON - m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - - // Get the details - name := m.Get("name").Str() - age := m.Get("age").Int() - - // Get their nickname (or use their name if they don't have one) - nickname := m.Get("nickname").Str(name) - -# Ranging - -Since `objx.Map` is a `map[string]interface{}` you can treat it as such. -For example, to `range` the data, do what you would expect: - - m := objx.MustFromJSON(json) - for key, value := range m { - // Your code... - } -*/ -package objx diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go deleted file mode 100644 index ab9f9ae67c..0000000000 --- a/vendor/github.com/stretchr/objx/map.go +++ /dev/null @@ -1,214 +0,0 @@ -package objx - -import ( - "encoding/base64" - "encoding/json" - "errors" - "io/ioutil" - "net/url" - "strings" -) - -// MSIConvertable is an interface that defines methods for converting your -// custom types to a map[string]interface{} representation. -type MSIConvertable interface { - // MSI gets a map[string]interface{} (msi) representing the - // object. - MSI() map[string]interface{} -} - -// Map provides extended functionality for working with -// untyped data, in particular map[string]interface (msi). -type Map map[string]interface{} - -// Value returns the internal value instance -func (m Map) Value() *Value { - return &Value{data: m} -} - -// Nil represents a nil Map. -var Nil = New(nil) - -// New creates a new Map containing the map[string]interface{} in the data argument. -// If the data argument is not a map[string]interface, New attempts to call the -// MSI() method on the MSIConvertable interface to create one. -func New(data interface{}) Map { - if _, ok := data.(map[string]interface{}); !ok { - if converter, ok := data.(MSIConvertable); ok { - data = converter.MSI() - } else { - return nil - } - } - return Map(data.(map[string]interface{})) -} - -// MSI creates a map[string]interface{} and puts it inside a new Map. -// -// The arguments follow a key, value pattern. -// -// Returns nil if any key argument is non-string or if there are an odd number of arguments. -// -// # Example -// -// To easily create Maps: -// -// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) -// -// // creates an Map equivalent to -// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} -func MSI(keyAndValuePairs ...interface{}) Map { - newMap := Map{} - keyAndValuePairsLen := len(keyAndValuePairs) - if keyAndValuePairsLen%2 != 0 { - return nil - } - for i := 0; i < keyAndValuePairsLen; i = i + 2 { - key := keyAndValuePairs[i] - value := keyAndValuePairs[i+1] - - // make sure the key is a string - keyString, keyStringOK := key.(string) - if !keyStringOK { - return nil - } - newMap[keyString] = value - } - return newMap -} - -// ****** Conversion Constructors - -// MustFromJSON creates a new Map containing the data specified in the -// jsonString. -// -// Panics if the JSON is invalid. -func MustFromJSON(jsonString string) Map { - o, err := FromJSON(jsonString) - if err != nil { - panic("objx: MustFromJSON failed with error: " + err.Error()) - } - return o -} - -// MustFromJSONSlice creates a new slice of Map containing the data specified in the -// jsonString. Works with jsons with a top level array -// -// Panics if the JSON is invalid. -func MustFromJSONSlice(jsonString string) []Map { - slice, err := FromJSONSlice(jsonString) - if err != nil { - panic("objx: MustFromJSONSlice failed with error: " + err.Error()) - } - return slice -} - -// FromJSON creates a new Map containing the data specified in the -// jsonString. -// -// Returns an error if the JSON is invalid. -func FromJSON(jsonString string) (Map, error) { - var m Map - err := json.Unmarshal([]byte(jsonString), &m) - if err != nil { - return Nil, err - } - return m, nil -} - -// FromJSONSlice creates a new slice of Map containing the data specified in the -// jsonString. Works with jsons with a top level array -// -// Returns an error if the JSON is invalid. -func FromJSONSlice(jsonString string) ([]Map, error) { - var slice []Map - err := json.Unmarshal([]byte(jsonString), &slice) - if err != nil { - return nil, err - } - return slice, nil -} - -// FromBase64 creates a new Obj containing the data specified -// in the Base64 string. -// -// The string is an encoded JSON string returned by Base64 -func FromBase64(base64String string) (Map, error) { - decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) - decoded, err := ioutil.ReadAll(decoder) - if err != nil { - return nil, err - } - return FromJSON(string(decoded)) -} - -// MustFromBase64 creates a new Obj containing the data specified -// in the Base64 string and panics if there is an error. -// -// The string is an encoded JSON string returned by Base64 -func MustFromBase64(base64String string) Map { - result, err := FromBase64(base64String) - if err != nil { - panic("objx: MustFromBase64 failed with error: " + err.Error()) - } - return result -} - -// FromSignedBase64 creates a new Obj containing the data specified -// in the Base64 string. -// -// The string is an encoded JSON string returned by SignedBase64 -func FromSignedBase64(base64String, key string) (Map, error) { - parts := strings.Split(base64String, SignatureSeparator) - if len(parts) != 2 { - return nil, errors.New("objx: Signed base64 string is malformed") - } - - sig := HashWithKey(parts[0], key) - if parts[1] != sig { - return nil, errors.New("objx: Signature for base64 data does not match") - } - return FromBase64(parts[0]) -} - -// MustFromSignedBase64 creates a new Obj containing the data specified -// in the Base64 string and panics if there is an error. -// -// The string is an encoded JSON string returned by Base64 -func MustFromSignedBase64(base64String, key string) Map { - result, err := FromSignedBase64(base64String, key) - if err != nil { - panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) - } - return result -} - -// FromURLQuery generates a new Obj by parsing the specified -// query. -// -// For queries with multiple values, the first value is selected. -func FromURLQuery(query string) (Map, error) { - vals, err := url.ParseQuery(query) - if err != nil { - return nil, err - } - m := Map{} - for k, vals := range vals { - m[k] = vals[0] - } - return m, nil -} - -// MustFromURLQuery generates a new Obj by parsing the specified -// query. -// -// For queries with multiple values, the first value is selected. -// -// Panics if it encounters an error -func MustFromURLQuery(query string) Map { - o, err := FromURLQuery(query) - if err != nil { - panic("objx: MustFromURLQuery failed with error: " + err.Error()) - } - return o -} diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go deleted file mode 100644 index c3400a3f70..0000000000 --- a/vendor/github.com/stretchr/objx/mutations.go +++ /dev/null @@ -1,77 +0,0 @@ -package objx - -// Exclude returns a new Map with the keys in the specified []string -// excluded. -func (m Map) Exclude(exclude []string) Map { - excluded := make(Map) - for k, v := range m { - if !contains(exclude, k) { - excluded[k] = v - } - } - return excluded -} - -// Copy creates a shallow copy of the Obj. -func (m Map) Copy() Map { - copied := Map{} - for k, v := range m { - copied[k] = v - } - return copied -} - -// Merge blends the specified map with a copy of this map and returns the result. -// -// Keys that appear in both will be selected from the specified map. -// This method requires that the wrapped object be a map[string]interface{} -func (m Map) Merge(merge Map) Map { - return m.Copy().MergeHere(merge) -} - -// MergeHere blends the specified map with this map and returns the current map. -// -// Keys that appear in both will be selected from the specified map. The original map -// will be modified. This method requires that -// the wrapped object be a map[string]interface{} -func (m Map) MergeHere(merge Map) Map { - for k, v := range merge { - m[k] = v - } - return m -} - -// Transform builds a new Obj giving the transformer a chance -// to change the keys and values as it goes. This method requires that -// the wrapped object be a map[string]interface{} -func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { - newMap := Map{} - for k, v := range m { - modifiedKey, modifiedVal := transformer(k, v) - newMap[modifiedKey] = modifiedVal - } - return newMap -} - -// TransformKeys builds a new map using the specified key mapping. -// -// Unspecified keys will be unaltered. -// This method requires that the wrapped object be a map[string]interface{} -func (m Map) TransformKeys(mapping map[string]string) Map { - return m.Transform(func(key string, value interface{}) (string, interface{}) { - if newKey, ok := mapping[key]; ok { - return newKey, value - } - return key, value - }) -} - -// Checks if a string slice contains a string -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go deleted file mode 100644 index 692be8e2a9..0000000000 --- a/vendor/github.com/stretchr/objx/security.go +++ /dev/null @@ -1,12 +0,0 @@ -package objx - -import ( - "crypto/sha1" - "encoding/hex" -) - -// HashWithKey hashes the specified string using the security key -func HashWithKey(data, key string) string { - d := sha1.Sum([]byte(data + ":" + key)) - return hex.EncodeToString(d[:]) -} diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go deleted file mode 100644 index d9e0b479a4..0000000000 --- a/vendor/github.com/stretchr/objx/tests.go +++ /dev/null @@ -1,17 +0,0 @@ -package objx - -// Has gets whether there is something at the specified selector -// or not. -// -// If m is nil, Has will always return false. -func (m Map) Has(selector string) bool { - if m == nil { - return false - } - return !m.Get(selector).IsNil() -} - -// IsNil gets whether the data is nil or not. -func (v *Value) IsNil() bool { - return v == nil || v.data == nil -} diff --git a/vendor/github.com/stretchr/objx/type_specific.go b/vendor/github.com/stretchr/objx/type_specific.go deleted file mode 100644 index 80f88d9fa2..0000000000 --- a/vendor/github.com/stretchr/objx/type_specific.go +++ /dev/null @@ -1,346 +0,0 @@ -package objx - -/* - MSI (map[string]interface{} and []map[string]interface{}) -*/ - -// MSI gets the value as a map[string]interface{}, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - if s, ok := v.data.(Map); ok { - return map[string]interface{}(s) - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustMSI gets the value as a map[string]interface{}. -// -// Panics if the object is not a map[string]interface{}. -func (v *Value) MustMSI() map[string]interface{} { - if s, ok := v.data.(Map); ok { - return map[string]interface{}(s) - } - return v.data.(map[string]interface{}) -} - -// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault -// value or nil if the value is not a []map[string]interface{}. -func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { - if s, ok := v.data.([]map[string]interface{}); ok { - return s - } - - s := v.ObjxMapSlice() - if s == nil { - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil - } - - result := make([]map[string]interface{}, len(s)) - for i := range s { - result[i] = s[i].Value().MSI() - } - return result -} - -// MustMSISlice gets the value as a []map[string]interface{}. -// -// Panics if the object is not a []map[string]interface{}. -func (v *Value) MustMSISlice() []map[string]interface{} { - if s := v.MSISlice(); s != nil { - return s - } - - return v.data.([]map[string]interface{}) -} - -// IsMSI gets whether the object contained is a map[string]interface{} or not. -func (v *Value) IsMSI() bool { - _, ok := v.data.(map[string]interface{}) - if !ok { - _, ok = v.data.(Map) - } - return ok -} - -// IsMSISlice gets whether the object contained is a []map[string]interface{} or not. -func (v *Value) IsMSISlice() bool { - _, ok := v.data.([]map[string]interface{}) - if !ok { - _, ok = v.data.([]Map) - if !ok { - s, ok := v.data.([]interface{}) - if ok { - for i := range s { - switch s[i].(type) { - case Map: - case map[string]interface{}: - default: - return false - } - } - return true - } - } - } - return ok -} - -// EachMSI calls the specified callback for each object -// in the []map[string]interface{}. -// -// Panics if the object is the wrong type. -func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { - for index, val := range v.MustMSISlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereMSI uses the specified decider function to select items -// from the []map[string]interface{}. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { - var selected []map[string]interface{} - v.EachMSI(func(index int, val map[string]interface{}) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupMSI uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]map[string]interface{}. -func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { - groups := make(map[string][]map[string]interface{}) - v.EachMSI(func(index int, val map[string]interface{}) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]map[string]interface{}, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceMSI uses the specified function to replace each map[string]interface{}s -// by iterating each item. The data in the returned result will be a -// []map[string]interface{} containing the replaced items. -func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { - arr := v.MustMSISlice() - replaced := make([]map[string]interface{}, len(arr)) - v.EachMSI(func(index int, val map[string]interface{}) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectMSI uses the specified collector function to collect a value -// for each of the map[string]interface{}s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { - arr := v.MustMSISlice() - collected := make([]interface{}, len(arr)) - v.EachMSI(func(index int, val map[string]interface{}) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - ObjxMap ((Map) and [](Map)) -*/ - -// ObjxMap gets the value as a (Map), returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { - if s, ok := v.data.((Map)); ok { - return s - } - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return New(nil) -} - -// MustObjxMap gets the value as a (Map). -// -// Panics if the object is not a (Map). -func (v *Value) MustObjxMap() Map { - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - return v.data.((Map)) -} - -// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault -// value or nil if the value is not a [](Map). -func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { - if s, ok := v.data.([]Map); ok { - return s - } - - if s, ok := v.data.([]map[string]interface{}); ok { - result := make([]Map, len(s)) - for i := range s { - result[i] = s[i] - } - return result - } - - s, ok := v.data.([]interface{}) - if !ok { - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil - } - - result := make([]Map, len(s)) - for i := range s { - switch s[i].(type) { - case Map: - result[i] = s[i].(Map) - case map[string]interface{}: - result[i] = New(s[i]) - default: - return nil - } - } - return result -} - -// MustObjxMapSlice gets the value as a [](Map). -// -// Panics if the object is not a [](Map). -func (v *Value) MustObjxMapSlice() [](Map) { - if s := v.ObjxMapSlice(); s != nil { - return s - } - return v.data.([](Map)) -} - -// IsObjxMap gets whether the object contained is a (Map) or not. -func (v *Value) IsObjxMap() bool { - _, ok := v.data.((Map)) - if !ok { - _, ok = v.data.(map[string]interface{}) - } - return ok -} - -// IsObjxMapSlice gets whether the object contained is a [](Map) or not. -func (v *Value) IsObjxMapSlice() bool { - _, ok := v.data.([](Map)) - if !ok { - _, ok = v.data.([]map[string]interface{}) - if !ok { - s, ok := v.data.([]interface{}) - if ok { - for i := range s { - switch s[i].(type) { - case Map: - case map[string]interface{}: - default: - return false - } - } - return true - } - } - } - - return ok -} - -// EachObjxMap calls the specified callback for each object -// in the [](Map). -// -// Panics if the object is the wrong type. -func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { - for index, val := range v.MustObjxMapSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereObjxMap uses the specified decider function to select items -// from the [](Map). The object contained in the result will contain -// only the selected items. -func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { - var selected [](Map) - v.EachObjxMap(func(index int, val Map) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupObjxMap uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][](Map). -func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { - groups := make(map[string][](Map)) - v.EachObjxMap(func(index int, val Map) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([](Map), 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceObjxMap uses the specified function to replace each (Map)s -// by iterating each item. The data in the returned result will be a -// [](Map) containing the replaced items. -func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { - arr := v.MustObjxMapSlice() - replaced := make([](Map), len(arr)) - v.EachObjxMap(func(index int, val Map) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectObjxMap uses the specified collector function to collect a value -// for each of the (Map)s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { - arr := v.MustObjxMapSlice() - collected := make([]interface{}, len(arr)) - v.EachObjxMap(func(index int, val Map) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go deleted file mode 100644 index 45850456e1..0000000000 --- a/vendor/github.com/stretchr/objx/type_specific_codegen.go +++ /dev/null @@ -1,2261 +0,0 @@ -package objx - -/* - Inter (interface{} and []interface{}) -*/ - -// Inter gets the value as a interface{}, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Inter(optionalDefault ...interface{}) interface{} { - if s, ok := v.data.(interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInter gets the value as a interface{}. -// -// Panics if the object is not a interface{}. -func (v *Value) MustInter() interface{} { - return v.data.(interface{}) -} - -// InterSlice gets the value as a []interface{}, returns the optionalDefault -// value or nil if the value is not a []interface{}. -func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { - if s, ok := v.data.([]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInterSlice gets the value as a []interface{}. -// -// Panics if the object is not a []interface{}. -func (v *Value) MustInterSlice() []interface{} { - return v.data.([]interface{}) -} - -// IsInter gets whether the object contained is a interface{} or not. -func (v *Value) IsInter() bool { - _, ok := v.data.(interface{}) - return ok -} - -// IsInterSlice gets whether the object contained is a []interface{} or not. -func (v *Value) IsInterSlice() bool { - _, ok := v.data.([]interface{}) - return ok -} - -// EachInter calls the specified callback for each object -// in the []interface{}. -// -// Panics if the object is the wrong type. -func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { - for index, val := range v.MustInterSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInter uses the specified decider function to select items -// from the []interface{}. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { - var selected []interface{} - v.EachInter(func(index int, val interface{}) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInter uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]interface{}. -func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { - groups := make(map[string][]interface{}) - v.EachInter(func(index int, val interface{}) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]interface{}, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInter uses the specified function to replace each interface{}s -// by iterating each item. The data in the returned result will be a -// []interface{} containing the replaced items. -func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { - arr := v.MustInterSlice() - replaced := make([]interface{}, len(arr)) - v.EachInter(func(index int, val interface{}) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInter uses the specified collector function to collect a value -// for each of the interface{}s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { - arr := v.MustInterSlice() - collected := make([]interface{}, len(arr)) - v.EachInter(func(index int, val interface{}) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Bool (bool and []bool) -*/ - -// Bool gets the value as a bool, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Bool(optionalDefault ...bool) bool { - if s, ok := v.data.(bool); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return false -} - -// MustBool gets the value as a bool. -// -// Panics if the object is not a bool. -func (v *Value) MustBool() bool { - return v.data.(bool) -} - -// BoolSlice gets the value as a []bool, returns the optionalDefault -// value or nil if the value is not a []bool. -func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { - if s, ok := v.data.([]bool); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustBoolSlice gets the value as a []bool. -// -// Panics if the object is not a []bool. -func (v *Value) MustBoolSlice() []bool { - return v.data.([]bool) -} - -// IsBool gets whether the object contained is a bool or not. -func (v *Value) IsBool() bool { - _, ok := v.data.(bool) - return ok -} - -// IsBoolSlice gets whether the object contained is a []bool or not. -func (v *Value) IsBoolSlice() bool { - _, ok := v.data.([]bool) - return ok -} - -// EachBool calls the specified callback for each object -// in the []bool. -// -// Panics if the object is the wrong type. -func (v *Value) EachBool(callback func(int, bool) bool) *Value { - for index, val := range v.MustBoolSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereBool uses the specified decider function to select items -// from the []bool. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereBool(decider func(int, bool) bool) *Value { - var selected []bool - v.EachBool(func(index int, val bool) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupBool uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]bool. -func (v *Value) GroupBool(grouper func(int, bool) string) *Value { - groups := make(map[string][]bool) - v.EachBool(func(index int, val bool) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]bool, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceBool uses the specified function to replace each bools -// by iterating each item. The data in the returned result will be a -// []bool containing the replaced items. -func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { - arr := v.MustBoolSlice() - replaced := make([]bool, len(arr)) - v.EachBool(func(index int, val bool) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectBool uses the specified collector function to collect a value -// for each of the bools in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { - arr := v.MustBoolSlice() - collected := make([]interface{}, len(arr)) - v.EachBool(func(index int, val bool) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Str (string and []string) -*/ - -// Str gets the value as a string, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Str(optionalDefault ...string) string { - if s, ok := v.data.(string); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return "" -} - -// MustStr gets the value as a string. -// -// Panics if the object is not a string. -func (v *Value) MustStr() string { - return v.data.(string) -} - -// StrSlice gets the value as a []string, returns the optionalDefault -// value or nil if the value is not a []string. -func (v *Value) StrSlice(optionalDefault ...[]string) []string { - if s, ok := v.data.([]string); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustStrSlice gets the value as a []string. -// -// Panics if the object is not a []string. -func (v *Value) MustStrSlice() []string { - return v.data.([]string) -} - -// IsStr gets whether the object contained is a string or not. -func (v *Value) IsStr() bool { - _, ok := v.data.(string) - return ok -} - -// IsStrSlice gets whether the object contained is a []string or not. -func (v *Value) IsStrSlice() bool { - _, ok := v.data.([]string) - return ok -} - -// EachStr calls the specified callback for each object -// in the []string. -// -// Panics if the object is the wrong type. -func (v *Value) EachStr(callback func(int, string) bool) *Value { - for index, val := range v.MustStrSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereStr uses the specified decider function to select items -// from the []string. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereStr(decider func(int, string) bool) *Value { - var selected []string - v.EachStr(func(index int, val string) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupStr uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]string. -func (v *Value) GroupStr(grouper func(int, string) string) *Value { - groups := make(map[string][]string) - v.EachStr(func(index int, val string) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]string, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceStr uses the specified function to replace each strings -// by iterating each item. The data in the returned result will be a -// []string containing the replaced items. -func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { - arr := v.MustStrSlice() - replaced := make([]string, len(arr)) - v.EachStr(func(index int, val string) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectStr uses the specified collector function to collect a value -// for each of the strings in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { - arr := v.MustStrSlice() - collected := make([]interface{}, len(arr)) - v.EachStr(func(index int, val string) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int (int and []int) -*/ - -// Int gets the value as a int, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int(optionalDefault ...int) int { - if s, ok := v.data.(int); ok { - return s - } - if s, ok := v.data.(float64); ok { - if float64(int(s)) == s { - return int(s) - } - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt gets the value as a int. -// -// Panics if the object is not a int. -func (v *Value) MustInt() int { - if s, ok := v.data.(float64); ok { - if float64(int(s)) == s { - return int(s) - } - } - return v.data.(int) -} - -// IntSlice gets the value as a []int, returns the optionalDefault -// value or nil if the value is not a []int. -func (v *Value) IntSlice(optionalDefault ...[]int) []int { - if s, ok := v.data.([]int); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustIntSlice gets the value as a []int. -// -// Panics if the object is not a []int. -func (v *Value) MustIntSlice() []int { - return v.data.([]int) -} - -// IsInt gets whether the object contained is a int or not. -func (v *Value) IsInt() bool { - _, ok := v.data.(int) - return ok -} - -// IsIntSlice gets whether the object contained is a []int or not. -func (v *Value) IsIntSlice() bool { - _, ok := v.data.([]int) - return ok -} - -// EachInt calls the specified callback for each object -// in the []int. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt(callback func(int, int) bool) *Value { - for index, val := range v.MustIntSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt uses the specified decider function to select items -// from the []int. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt(decider func(int, int) bool) *Value { - var selected []int - v.EachInt(func(index int, val int) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int. -func (v *Value) GroupInt(grouper func(int, int) string) *Value { - groups := make(map[string][]int) - v.EachInt(func(index int, val int) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt uses the specified function to replace each ints -// by iterating each item. The data in the returned result will be a -// []int containing the replaced items. -func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { - arr := v.MustIntSlice() - replaced := make([]int, len(arr)) - v.EachInt(func(index int, val int) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt uses the specified collector function to collect a value -// for each of the ints in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { - arr := v.MustIntSlice() - collected := make([]interface{}, len(arr)) - v.EachInt(func(index int, val int) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int8 (int8 and []int8) -*/ - -// Int8 gets the value as a int8, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int8(optionalDefault ...int8) int8 { - if s, ok := v.data.(int8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt8 gets the value as a int8. -// -// Panics if the object is not a int8. -func (v *Value) MustInt8() int8 { - return v.data.(int8) -} - -// Int8Slice gets the value as a []int8, returns the optionalDefault -// value or nil if the value is not a []int8. -func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { - if s, ok := v.data.([]int8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt8Slice gets the value as a []int8. -// -// Panics if the object is not a []int8. -func (v *Value) MustInt8Slice() []int8 { - return v.data.([]int8) -} - -// IsInt8 gets whether the object contained is a int8 or not. -func (v *Value) IsInt8() bool { - _, ok := v.data.(int8) - return ok -} - -// IsInt8Slice gets whether the object contained is a []int8 or not. -func (v *Value) IsInt8Slice() bool { - _, ok := v.data.([]int8) - return ok -} - -// EachInt8 calls the specified callback for each object -// in the []int8. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt8(callback func(int, int8) bool) *Value { - for index, val := range v.MustInt8Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt8 uses the specified decider function to select items -// from the []int8. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { - var selected []int8 - v.EachInt8(func(index int, val int8) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt8 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int8. -func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { - groups := make(map[string][]int8) - v.EachInt8(func(index int, val int8) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int8, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt8 uses the specified function to replace each int8s -// by iterating each item. The data in the returned result will be a -// []int8 containing the replaced items. -func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { - arr := v.MustInt8Slice() - replaced := make([]int8, len(arr)) - v.EachInt8(func(index int, val int8) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt8 uses the specified collector function to collect a value -// for each of the int8s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { - arr := v.MustInt8Slice() - collected := make([]interface{}, len(arr)) - v.EachInt8(func(index int, val int8) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int16 (int16 and []int16) -*/ - -// Int16 gets the value as a int16, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int16(optionalDefault ...int16) int16 { - if s, ok := v.data.(int16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt16 gets the value as a int16. -// -// Panics if the object is not a int16. -func (v *Value) MustInt16() int16 { - return v.data.(int16) -} - -// Int16Slice gets the value as a []int16, returns the optionalDefault -// value or nil if the value is not a []int16. -func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { - if s, ok := v.data.([]int16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt16Slice gets the value as a []int16. -// -// Panics if the object is not a []int16. -func (v *Value) MustInt16Slice() []int16 { - return v.data.([]int16) -} - -// IsInt16 gets whether the object contained is a int16 or not. -func (v *Value) IsInt16() bool { - _, ok := v.data.(int16) - return ok -} - -// IsInt16Slice gets whether the object contained is a []int16 or not. -func (v *Value) IsInt16Slice() bool { - _, ok := v.data.([]int16) - return ok -} - -// EachInt16 calls the specified callback for each object -// in the []int16. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt16(callback func(int, int16) bool) *Value { - for index, val := range v.MustInt16Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt16 uses the specified decider function to select items -// from the []int16. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { - var selected []int16 - v.EachInt16(func(index int, val int16) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt16 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int16. -func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { - groups := make(map[string][]int16) - v.EachInt16(func(index int, val int16) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int16, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt16 uses the specified function to replace each int16s -// by iterating each item. The data in the returned result will be a -// []int16 containing the replaced items. -func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { - arr := v.MustInt16Slice() - replaced := make([]int16, len(arr)) - v.EachInt16(func(index int, val int16) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt16 uses the specified collector function to collect a value -// for each of the int16s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { - arr := v.MustInt16Slice() - collected := make([]interface{}, len(arr)) - v.EachInt16(func(index int, val int16) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int32 (int32 and []int32) -*/ - -// Int32 gets the value as a int32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int32(optionalDefault ...int32) int32 { - if s, ok := v.data.(int32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt32 gets the value as a int32. -// -// Panics if the object is not a int32. -func (v *Value) MustInt32() int32 { - return v.data.(int32) -} - -// Int32Slice gets the value as a []int32, returns the optionalDefault -// value or nil if the value is not a []int32. -func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { - if s, ok := v.data.([]int32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt32Slice gets the value as a []int32. -// -// Panics if the object is not a []int32. -func (v *Value) MustInt32Slice() []int32 { - return v.data.([]int32) -} - -// IsInt32 gets whether the object contained is a int32 or not. -func (v *Value) IsInt32() bool { - _, ok := v.data.(int32) - return ok -} - -// IsInt32Slice gets whether the object contained is a []int32 or not. -func (v *Value) IsInt32Slice() bool { - _, ok := v.data.([]int32) - return ok -} - -// EachInt32 calls the specified callback for each object -// in the []int32. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt32(callback func(int, int32) bool) *Value { - for index, val := range v.MustInt32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt32 uses the specified decider function to select items -// from the []int32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { - var selected []int32 - v.EachInt32(func(index int, val int32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int32. -func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { - groups := make(map[string][]int32) - v.EachInt32(func(index int, val int32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt32 uses the specified function to replace each int32s -// by iterating each item. The data in the returned result will be a -// []int32 containing the replaced items. -func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { - arr := v.MustInt32Slice() - replaced := make([]int32, len(arr)) - v.EachInt32(func(index int, val int32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt32 uses the specified collector function to collect a value -// for each of the int32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { - arr := v.MustInt32Slice() - collected := make([]interface{}, len(arr)) - v.EachInt32(func(index int, val int32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int64 (int64 and []int64) -*/ - -// Int64 gets the value as a int64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int64(optionalDefault ...int64) int64 { - if s, ok := v.data.(int64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt64 gets the value as a int64. -// -// Panics if the object is not a int64. -func (v *Value) MustInt64() int64 { - return v.data.(int64) -} - -// Int64Slice gets the value as a []int64, returns the optionalDefault -// value or nil if the value is not a []int64. -func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { - if s, ok := v.data.([]int64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt64Slice gets the value as a []int64. -// -// Panics if the object is not a []int64. -func (v *Value) MustInt64Slice() []int64 { - return v.data.([]int64) -} - -// IsInt64 gets whether the object contained is a int64 or not. -func (v *Value) IsInt64() bool { - _, ok := v.data.(int64) - return ok -} - -// IsInt64Slice gets whether the object contained is a []int64 or not. -func (v *Value) IsInt64Slice() bool { - _, ok := v.data.([]int64) - return ok -} - -// EachInt64 calls the specified callback for each object -// in the []int64. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt64(callback func(int, int64) bool) *Value { - for index, val := range v.MustInt64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt64 uses the specified decider function to select items -// from the []int64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { - var selected []int64 - v.EachInt64(func(index int, val int64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int64. -func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { - groups := make(map[string][]int64) - v.EachInt64(func(index int, val int64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt64 uses the specified function to replace each int64s -// by iterating each item. The data in the returned result will be a -// []int64 containing the replaced items. -func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { - arr := v.MustInt64Slice() - replaced := make([]int64, len(arr)) - v.EachInt64(func(index int, val int64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt64 uses the specified collector function to collect a value -// for each of the int64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { - arr := v.MustInt64Slice() - collected := make([]interface{}, len(arr)) - v.EachInt64(func(index int, val int64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint (uint and []uint) -*/ - -// Uint gets the value as a uint, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint(optionalDefault ...uint) uint { - if s, ok := v.data.(uint); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint gets the value as a uint. -// -// Panics if the object is not a uint. -func (v *Value) MustUint() uint { - return v.data.(uint) -} - -// UintSlice gets the value as a []uint, returns the optionalDefault -// value or nil if the value is not a []uint. -func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { - if s, ok := v.data.([]uint); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUintSlice gets the value as a []uint. -// -// Panics if the object is not a []uint. -func (v *Value) MustUintSlice() []uint { - return v.data.([]uint) -} - -// IsUint gets whether the object contained is a uint or not. -func (v *Value) IsUint() bool { - _, ok := v.data.(uint) - return ok -} - -// IsUintSlice gets whether the object contained is a []uint or not. -func (v *Value) IsUintSlice() bool { - _, ok := v.data.([]uint) - return ok -} - -// EachUint calls the specified callback for each object -// in the []uint. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint(callback func(int, uint) bool) *Value { - for index, val := range v.MustUintSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint uses the specified decider function to select items -// from the []uint. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint(decider func(int, uint) bool) *Value { - var selected []uint - v.EachUint(func(index int, val uint) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint. -func (v *Value) GroupUint(grouper func(int, uint) string) *Value { - groups := make(map[string][]uint) - v.EachUint(func(index int, val uint) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint uses the specified function to replace each uints -// by iterating each item. The data in the returned result will be a -// []uint containing the replaced items. -func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { - arr := v.MustUintSlice() - replaced := make([]uint, len(arr)) - v.EachUint(func(index int, val uint) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint uses the specified collector function to collect a value -// for each of the uints in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { - arr := v.MustUintSlice() - collected := make([]interface{}, len(arr)) - v.EachUint(func(index int, val uint) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint8 (uint8 and []uint8) -*/ - -// Uint8 gets the value as a uint8, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint8(optionalDefault ...uint8) uint8 { - if s, ok := v.data.(uint8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint8 gets the value as a uint8. -// -// Panics if the object is not a uint8. -func (v *Value) MustUint8() uint8 { - return v.data.(uint8) -} - -// Uint8Slice gets the value as a []uint8, returns the optionalDefault -// value or nil if the value is not a []uint8. -func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { - if s, ok := v.data.([]uint8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint8Slice gets the value as a []uint8. -// -// Panics if the object is not a []uint8. -func (v *Value) MustUint8Slice() []uint8 { - return v.data.([]uint8) -} - -// IsUint8 gets whether the object contained is a uint8 or not. -func (v *Value) IsUint8() bool { - _, ok := v.data.(uint8) - return ok -} - -// IsUint8Slice gets whether the object contained is a []uint8 or not. -func (v *Value) IsUint8Slice() bool { - _, ok := v.data.([]uint8) - return ok -} - -// EachUint8 calls the specified callback for each object -// in the []uint8. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { - for index, val := range v.MustUint8Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint8 uses the specified decider function to select items -// from the []uint8. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { - var selected []uint8 - v.EachUint8(func(index int, val uint8) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint8 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint8. -func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { - groups := make(map[string][]uint8) - v.EachUint8(func(index int, val uint8) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint8, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint8 uses the specified function to replace each uint8s -// by iterating each item. The data in the returned result will be a -// []uint8 containing the replaced items. -func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { - arr := v.MustUint8Slice() - replaced := make([]uint8, len(arr)) - v.EachUint8(func(index int, val uint8) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint8 uses the specified collector function to collect a value -// for each of the uint8s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { - arr := v.MustUint8Slice() - collected := make([]interface{}, len(arr)) - v.EachUint8(func(index int, val uint8) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint16 (uint16 and []uint16) -*/ - -// Uint16 gets the value as a uint16, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint16(optionalDefault ...uint16) uint16 { - if s, ok := v.data.(uint16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint16 gets the value as a uint16. -// -// Panics if the object is not a uint16. -func (v *Value) MustUint16() uint16 { - return v.data.(uint16) -} - -// Uint16Slice gets the value as a []uint16, returns the optionalDefault -// value or nil if the value is not a []uint16. -func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { - if s, ok := v.data.([]uint16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint16Slice gets the value as a []uint16. -// -// Panics if the object is not a []uint16. -func (v *Value) MustUint16Slice() []uint16 { - return v.data.([]uint16) -} - -// IsUint16 gets whether the object contained is a uint16 or not. -func (v *Value) IsUint16() bool { - _, ok := v.data.(uint16) - return ok -} - -// IsUint16Slice gets whether the object contained is a []uint16 or not. -func (v *Value) IsUint16Slice() bool { - _, ok := v.data.([]uint16) - return ok -} - -// EachUint16 calls the specified callback for each object -// in the []uint16. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { - for index, val := range v.MustUint16Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint16 uses the specified decider function to select items -// from the []uint16. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { - var selected []uint16 - v.EachUint16(func(index int, val uint16) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint16 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint16. -func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { - groups := make(map[string][]uint16) - v.EachUint16(func(index int, val uint16) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint16, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint16 uses the specified function to replace each uint16s -// by iterating each item. The data in the returned result will be a -// []uint16 containing the replaced items. -func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { - arr := v.MustUint16Slice() - replaced := make([]uint16, len(arr)) - v.EachUint16(func(index int, val uint16) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint16 uses the specified collector function to collect a value -// for each of the uint16s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { - arr := v.MustUint16Slice() - collected := make([]interface{}, len(arr)) - v.EachUint16(func(index int, val uint16) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint32 (uint32 and []uint32) -*/ - -// Uint32 gets the value as a uint32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint32(optionalDefault ...uint32) uint32 { - if s, ok := v.data.(uint32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint32 gets the value as a uint32. -// -// Panics if the object is not a uint32. -func (v *Value) MustUint32() uint32 { - return v.data.(uint32) -} - -// Uint32Slice gets the value as a []uint32, returns the optionalDefault -// value or nil if the value is not a []uint32. -func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { - if s, ok := v.data.([]uint32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint32Slice gets the value as a []uint32. -// -// Panics if the object is not a []uint32. -func (v *Value) MustUint32Slice() []uint32 { - return v.data.([]uint32) -} - -// IsUint32 gets whether the object contained is a uint32 or not. -func (v *Value) IsUint32() bool { - _, ok := v.data.(uint32) - return ok -} - -// IsUint32Slice gets whether the object contained is a []uint32 or not. -func (v *Value) IsUint32Slice() bool { - _, ok := v.data.([]uint32) - return ok -} - -// EachUint32 calls the specified callback for each object -// in the []uint32. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { - for index, val := range v.MustUint32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint32 uses the specified decider function to select items -// from the []uint32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { - var selected []uint32 - v.EachUint32(func(index int, val uint32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint32. -func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { - groups := make(map[string][]uint32) - v.EachUint32(func(index int, val uint32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint32 uses the specified function to replace each uint32s -// by iterating each item. The data in the returned result will be a -// []uint32 containing the replaced items. -func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { - arr := v.MustUint32Slice() - replaced := make([]uint32, len(arr)) - v.EachUint32(func(index int, val uint32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint32 uses the specified collector function to collect a value -// for each of the uint32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { - arr := v.MustUint32Slice() - collected := make([]interface{}, len(arr)) - v.EachUint32(func(index int, val uint32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint64 (uint64 and []uint64) -*/ - -// Uint64 gets the value as a uint64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint64(optionalDefault ...uint64) uint64 { - if s, ok := v.data.(uint64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint64 gets the value as a uint64. -// -// Panics if the object is not a uint64. -func (v *Value) MustUint64() uint64 { - return v.data.(uint64) -} - -// Uint64Slice gets the value as a []uint64, returns the optionalDefault -// value or nil if the value is not a []uint64. -func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { - if s, ok := v.data.([]uint64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint64Slice gets the value as a []uint64. -// -// Panics if the object is not a []uint64. -func (v *Value) MustUint64Slice() []uint64 { - return v.data.([]uint64) -} - -// IsUint64 gets whether the object contained is a uint64 or not. -func (v *Value) IsUint64() bool { - _, ok := v.data.(uint64) - return ok -} - -// IsUint64Slice gets whether the object contained is a []uint64 or not. -func (v *Value) IsUint64Slice() bool { - _, ok := v.data.([]uint64) - return ok -} - -// EachUint64 calls the specified callback for each object -// in the []uint64. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { - for index, val := range v.MustUint64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint64 uses the specified decider function to select items -// from the []uint64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { - var selected []uint64 - v.EachUint64(func(index int, val uint64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint64. -func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { - groups := make(map[string][]uint64) - v.EachUint64(func(index int, val uint64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint64 uses the specified function to replace each uint64s -// by iterating each item. The data in the returned result will be a -// []uint64 containing the replaced items. -func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { - arr := v.MustUint64Slice() - replaced := make([]uint64, len(arr)) - v.EachUint64(func(index int, val uint64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint64 uses the specified collector function to collect a value -// for each of the uint64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { - arr := v.MustUint64Slice() - collected := make([]interface{}, len(arr)) - v.EachUint64(func(index int, val uint64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uintptr (uintptr and []uintptr) -*/ - -// Uintptr gets the value as a uintptr, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { - if s, ok := v.data.(uintptr); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUintptr gets the value as a uintptr. -// -// Panics if the object is not a uintptr. -func (v *Value) MustUintptr() uintptr { - return v.data.(uintptr) -} - -// UintptrSlice gets the value as a []uintptr, returns the optionalDefault -// value or nil if the value is not a []uintptr. -func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { - if s, ok := v.data.([]uintptr); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUintptrSlice gets the value as a []uintptr. -// -// Panics if the object is not a []uintptr. -func (v *Value) MustUintptrSlice() []uintptr { - return v.data.([]uintptr) -} - -// IsUintptr gets whether the object contained is a uintptr or not. -func (v *Value) IsUintptr() bool { - _, ok := v.data.(uintptr) - return ok -} - -// IsUintptrSlice gets whether the object contained is a []uintptr or not. -func (v *Value) IsUintptrSlice() bool { - _, ok := v.data.([]uintptr) - return ok -} - -// EachUintptr calls the specified callback for each object -// in the []uintptr. -// -// Panics if the object is the wrong type. -func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { - for index, val := range v.MustUintptrSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUintptr uses the specified decider function to select items -// from the []uintptr. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { - var selected []uintptr - v.EachUintptr(func(index int, val uintptr) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUintptr uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uintptr. -func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { - groups := make(map[string][]uintptr) - v.EachUintptr(func(index int, val uintptr) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uintptr, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUintptr uses the specified function to replace each uintptrs -// by iterating each item. The data in the returned result will be a -// []uintptr containing the replaced items. -func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { - arr := v.MustUintptrSlice() - replaced := make([]uintptr, len(arr)) - v.EachUintptr(func(index int, val uintptr) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUintptr uses the specified collector function to collect a value -// for each of the uintptrs in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { - arr := v.MustUintptrSlice() - collected := make([]interface{}, len(arr)) - v.EachUintptr(func(index int, val uintptr) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Float32 (float32 and []float32) -*/ - -// Float32 gets the value as a float32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Float32(optionalDefault ...float32) float32 { - if s, ok := v.data.(float32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustFloat32 gets the value as a float32. -// -// Panics if the object is not a float32. -func (v *Value) MustFloat32() float32 { - return v.data.(float32) -} - -// Float32Slice gets the value as a []float32, returns the optionalDefault -// value or nil if the value is not a []float32. -func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { - if s, ok := v.data.([]float32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustFloat32Slice gets the value as a []float32. -// -// Panics if the object is not a []float32. -func (v *Value) MustFloat32Slice() []float32 { - return v.data.([]float32) -} - -// IsFloat32 gets whether the object contained is a float32 or not. -func (v *Value) IsFloat32() bool { - _, ok := v.data.(float32) - return ok -} - -// IsFloat32Slice gets whether the object contained is a []float32 or not. -func (v *Value) IsFloat32Slice() bool { - _, ok := v.data.([]float32) - return ok -} - -// EachFloat32 calls the specified callback for each object -// in the []float32. -// -// Panics if the object is the wrong type. -func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { - for index, val := range v.MustFloat32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereFloat32 uses the specified decider function to select items -// from the []float32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { - var selected []float32 - v.EachFloat32(func(index int, val float32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupFloat32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]float32. -func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { - groups := make(map[string][]float32) - v.EachFloat32(func(index int, val float32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]float32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceFloat32 uses the specified function to replace each float32s -// by iterating each item. The data in the returned result will be a -// []float32 containing the replaced items. -func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { - arr := v.MustFloat32Slice() - replaced := make([]float32, len(arr)) - v.EachFloat32(func(index int, val float32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectFloat32 uses the specified collector function to collect a value -// for each of the float32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { - arr := v.MustFloat32Slice() - collected := make([]interface{}, len(arr)) - v.EachFloat32(func(index int, val float32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Float64 (float64 and []float64) -*/ - -// Float64 gets the value as a float64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Float64(optionalDefault ...float64) float64 { - if s, ok := v.data.(float64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustFloat64 gets the value as a float64. -// -// Panics if the object is not a float64. -func (v *Value) MustFloat64() float64 { - return v.data.(float64) -} - -// Float64Slice gets the value as a []float64, returns the optionalDefault -// value or nil if the value is not a []float64. -func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { - if s, ok := v.data.([]float64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustFloat64Slice gets the value as a []float64. -// -// Panics if the object is not a []float64. -func (v *Value) MustFloat64Slice() []float64 { - return v.data.([]float64) -} - -// IsFloat64 gets whether the object contained is a float64 or not. -func (v *Value) IsFloat64() bool { - _, ok := v.data.(float64) - return ok -} - -// IsFloat64Slice gets whether the object contained is a []float64 or not. -func (v *Value) IsFloat64Slice() bool { - _, ok := v.data.([]float64) - return ok -} - -// EachFloat64 calls the specified callback for each object -// in the []float64. -// -// Panics if the object is the wrong type. -func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { - for index, val := range v.MustFloat64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereFloat64 uses the specified decider function to select items -// from the []float64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { - var selected []float64 - v.EachFloat64(func(index int, val float64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupFloat64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]float64. -func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { - groups := make(map[string][]float64) - v.EachFloat64(func(index int, val float64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]float64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceFloat64 uses the specified function to replace each float64s -// by iterating each item. The data in the returned result will be a -// []float64 containing the replaced items. -func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { - arr := v.MustFloat64Slice() - replaced := make([]float64, len(arr)) - v.EachFloat64(func(index int, val float64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectFloat64 uses the specified collector function to collect a value -// for each of the float64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { - arr := v.MustFloat64Slice() - collected := make([]interface{}, len(arr)) - v.EachFloat64(func(index int, val float64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Complex64 (complex64 and []complex64) -*/ - -// Complex64 gets the value as a complex64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Complex64(optionalDefault ...complex64) complex64 { - if s, ok := v.data.(complex64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustComplex64 gets the value as a complex64. -// -// Panics if the object is not a complex64. -func (v *Value) MustComplex64() complex64 { - return v.data.(complex64) -} - -// Complex64Slice gets the value as a []complex64, returns the optionalDefault -// value or nil if the value is not a []complex64. -func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { - if s, ok := v.data.([]complex64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustComplex64Slice gets the value as a []complex64. -// -// Panics if the object is not a []complex64. -func (v *Value) MustComplex64Slice() []complex64 { - return v.data.([]complex64) -} - -// IsComplex64 gets whether the object contained is a complex64 or not. -func (v *Value) IsComplex64() bool { - _, ok := v.data.(complex64) - return ok -} - -// IsComplex64Slice gets whether the object contained is a []complex64 or not. -func (v *Value) IsComplex64Slice() bool { - _, ok := v.data.([]complex64) - return ok -} - -// EachComplex64 calls the specified callback for each object -// in the []complex64. -// -// Panics if the object is the wrong type. -func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { - for index, val := range v.MustComplex64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereComplex64 uses the specified decider function to select items -// from the []complex64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { - var selected []complex64 - v.EachComplex64(func(index int, val complex64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupComplex64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]complex64. -func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { - groups := make(map[string][]complex64) - v.EachComplex64(func(index int, val complex64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]complex64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceComplex64 uses the specified function to replace each complex64s -// by iterating each item. The data in the returned result will be a -// []complex64 containing the replaced items. -func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { - arr := v.MustComplex64Slice() - replaced := make([]complex64, len(arr)) - v.EachComplex64(func(index int, val complex64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectComplex64 uses the specified collector function to collect a value -// for each of the complex64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { - arr := v.MustComplex64Slice() - collected := make([]interface{}, len(arr)) - v.EachComplex64(func(index int, val complex64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Complex128 (complex128 and []complex128) -*/ - -// Complex128 gets the value as a complex128, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Complex128(optionalDefault ...complex128) complex128 { - if s, ok := v.data.(complex128); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustComplex128 gets the value as a complex128. -// -// Panics if the object is not a complex128. -func (v *Value) MustComplex128() complex128 { - return v.data.(complex128) -} - -// Complex128Slice gets the value as a []complex128, returns the optionalDefault -// value or nil if the value is not a []complex128. -func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { - if s, ok := v.data.([]complex128); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustComplex128Slice gets the value as a []complex128. -// -// Panics if the object is not a []complex128. -func (v *Value) MustComplex128Slice() []complex128 { - return v.data.([]complex128) -} - -// IsComplex128 gets whether the object contained is a complex128 or not. -func (v *Value) IsComplex128() bool { - _, ok := v.data.(complex128) - return ok -} - -// IsComplex128Slice gets whether the object contained is a []complex128 or not. -func (v *Value) IsComplex128Slice() bool { - _, ok := v.data.([]complex128) - return ok -} - -// EachComplex128 calls the specified callback for each object -// in the []complex128. -// -// Panics if the object is the wrong type. -func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { - for index, val := range v.MustComplex128Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereComplex128 uses the specified decider function to select items -// from the []complex128. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { - var selected []complex128 - v.EachComplex128(func(index int, val complex128) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupComplex128 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]complex128. -func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { - groups := make(map[string][]complex128) - v.EachComplex128(func(index int, val complex128) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]complex128, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceComplex128 uses the specified function to replace each complex128s -// by iterating each item. The data in the returned result will be a -// []complex128 containing the replaced items. -func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { - arr := v.MustComplex128Slice() - replaced := make([]complex128, len(arr)) - v.EachComplex128(func(index int, val complex128) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectComplex128 uses the specified collector function to collect a value -// for each of the complex128s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { - arr := v.MustComplex128Slice() - collected := make([]interface{}, len(arr)) - v.EachComplex128(func(index int, val complex128) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go deleted file mode 100644 index 4e5f9b77e6..0000000000 --- a/vendor/github.com/stretchr/objx/value.go +++ /dev/null @@ -1,159 +0,0 @@ -package objx - -import ( - "fmt" - "strconv" -) - -// Value provides methods for extracting interface{} data in various -// types. -type Value struct { - // data contains the raw data being managed by this Value - data interface{} -} - -// Data returns the raw data contained by this Value -func (v *Value) Data() interface{} { - return v.data -} - -// String returns the value always as a string -func (v *Value) String() string { - switch { - case v.IsNil(): - return "" - case v.IsStr(): - return v.Str() - case v.IsBool(): - return strconv.FormatBool(v.Bool()) - case v.IsFloat32(): - return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) - case v.IsFloat64(): - return strconv.FormatFloat(v.Float64(), 'f', -1, 64) - case v.IsInt(): - return strconv.FormatInt(int64(v.Int()), 10) - case v.IsInt8(): - return strconv.FormatInt(int64(v.Int8()), 10) - case v.IsInt16(): - return strconv.FormatInt(int64(v.Int16()), 10) - case v.IsInt32(): - return strconv.FormatInt(int64(v.Int32()), 10) - case v.IsInt64(): - return strconv.FormatInt(v.Int64(), 10) - case v.IsUint(): - return strconv.FormatUint(uint64(v.Uint()), 10) - case v.IsUint8(): - return strconv.FormatUint(uint64(v.Uint8()), 10) - case v.IsUint16(): - return strconv.FormatUint(uint64(v.Uint16()), 10) - case v.IsUint32(): - return strconv.FormatUint(uint64(v.Uint32()), 10) - case v.IsUint64(): - return strconv.FormatUint(v.Uint64(), 10) - } - return fmt.Sprintf("%#v", v.Data()) -} - -// StringSlice returns the value always as a []string -func (v *Value) StringSlice(optionalDefault ...[]string) []string { - switch { - case v.IsStrSlice(): - return v.MustStrSlice() - case v.IsBoolSlice(): - slice := v.MustBoolSlice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatBool(iv) - } - return vals - case v.IsFloat32Slice(): - slice := v.MustFloat32Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatFloat(float64(iv), 'f', -1, 32) - } - return vals - case v.IsFloat64Slice(): - slice := v.MustFloat64Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatFloat(iv, 'f', -1, 64) - } - return vals - case v.IsIntSlice(): - slice := v.MustIntSlice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt8Slice(): - slice := v.MustInt8Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt16Slice(): - slice := v.MustInt16Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt32Slice(): - slice := v.MustInt32Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt64Slice(): - slice := v.MustInt64Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(iv, 10) - } - return vals - case v.IsUintSlice(): - slice := v.MustUintSlice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint8Slice(): - slice := v.MustUint8Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint16Slice(): - slice := v.MustUint16Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint32Slice(): - slice := v.MustUint32Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint64Slice(): - slice := v.MustUint64Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(iv, 10) - } - return vals - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - - return []string{} -} diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go deleted file mode 100644 index d6b3c844cc..0000000000 --- a/vendor/github.com/stretchr/testify/mock/doc.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package mock provides a system by which it is possible to mock your objects -// and verify calls are happening as expected. -// -// # Example Usage -// -// The mock package provides an object, Mock, that tracks activity on another object. It is usually -// embedded into a test object as shown below: -// -// type MyTestObject struct { -// // add a Mock object instance -// mock.Mock -// -// // other fields go here as normal -// } -// -// When implementing the methods of an interface, you wire your functions up -// to call the Mock.Called(args...) method, and return the appropriate values. -// -// For example, to mock a method that saves the name and age of a person and returns -// the year of their birth or an error, you might write this: -// -// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { -// args := o.Called(firstname, lastname, age) -// return args.Int(0), args.Error(1) -// } -// -// The Int, Error and Bool methods are examples of strongly typed getters that take the argument -// index position. Given this argument list: -// -// (12, true, "Something") -// -// You could read them out strongly typed like this: -// -// args.Int(0) -// args.Bool(1) -// args.String(2) -// -// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: -// -// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) -// -// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those -// cases you should check for nil first. -package mock diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go deleted file mode 100644 index 213bde2ea6..0000000000 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ /dev/null @@ -1,1241 +0,0 @@ -package mock - -import ( - "errors" - "fmt" - "path" - "reflect" - "regexp" - "runtime" - "strings" - "sync" - "time" - - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" - "github.com/stretchr/objx" - - "github.com/stretchr/testify/assert" -) - -// regex for GCCGO functions -var gccgoRE = regexp.MustCompile(`\.pN\d+_`) - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Logf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - FailNow() -} - -/* - Call -*/ - -// Call represents a method call and is used for setting expectations, -// as well as recording activity. -type Call struct { - Parent *Mock - - // The name of the method that was or will be called. - Method string - - // Holds the arguments of the method. - Arguments Arguments - - // Holds the arguments that should be returned when - // this method is called. - ReturnArguments Arguments - - // Holds the caller info for the On() call - callerInfo []string - - // The number of times to return the return arguments when setting - // expectations. 0 means to always return the value. - Repeatability int - - // Amount of times this call has been called - totalCalls int - - // Call to this method can be optional - optional bool - - // Holds a channel that will be used to block the Return until it either - // receives a message or is closed. nil means it returns immediately. - WaitFor <-chan time.Time - - waitTime time.Duration - - // Holds a handler used to manipulate arguments content that are passed by - // reference. It's useful when mocking methods such as unmarshalers or - // decoders. - RunFn func(Arguments) - - // PanicMsg holds msg to be used to mock panic on the function call - // if the PanicMsg is set to a non nil string the function call will panic - // irrespective of other settings - PanicMsg *string - - // Calls which must be satisfied before this call can be - requires []*Call -} - -func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { - return &Call{ - Parent: parent, - Method: methodName, - Arguments: methodArguments, - ReturnArguments: make([]interface{}, 0), - callerInfo: callerInfo, - Repeatability: 0, - WaitFor: nil, - RunFn: nil, - PanicMsg: nil, - } -} - -func (c *Call) lock() { - c.Parent.mutex.Lock() -} - -func (c *Call) unlock() { - c.Parent.mutex.Unlock() -} - -// Return specifies the return arguments for the expectation. -// -// Mock.On("DoSomething").Return(errors.New("failed")) -func (c *Call) Return(returnArguments ...interface{}) *Call { - c.lock() - defer c.unlock() - - c.ReturnArguments = returnArguments - - return c -} - -// Panic specifies if the function call should fail and the panic message -// -// Mock.On("DoSomething").Panic("test panic") -func (c *Call) Panic(msg string) *Call { - c.lock() - defer c.unlock() - - c.PanicMsg = &msg - - return c -} - -// Once indicates that the mock should only return the value once. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() -func (c *Call) Once() *Call { - return c.Times(1) -} - -// Twice indicates that the mock should only return the value twice. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() -func (c *Call) Twice() *Call { - return c.Times(2) -} - -// Times indicates that the mock should only return the indicated number -// of times. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) -func (c *Call) Times(i int) *Call { - c.lock() - defer c.unlock() - c.Repeatability = i - return c -} - -// WaitUntil sets the channel that will block the mock's return until its closed -// or a message is received. -// -// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) -func (c *Call) WaitUntil(w <-chan time.Time) *Call { - c.lock() - defer c.unlock() - c.WaitFor = w - return c -} - -// After sets how long to block until the call returns -// -// Mock.On("MyMethod", arg1, arg2).After(time.Second) -func (c *Call) After(d time.Duration) *Call { - c.lock() - defer c.unlock() - c.waitTime = d - return c -} - -// Run sets a handler to be called before returning. It can be used when -// mocking a method (such as an unmarshaler) that takes a pointer to a struct and -// sets properties in such struct -// -// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { -// arg := args.Get(0).(*map[string]interface{}) -// arg["foo"] = "bar" -// }) -func (c *Call) Run(fn func(args Arguments)) *Call { - c.lock() - defer c.unlock() - c.RunFn = fn - return c -} - -// Maybe allows the method call to be optional. Not calling an optional method -// will not cause an error while asserting expectations -func (c *Call) Maybe() *Call { - c.lock() - defer c.unlock() - c.optional = true - return c -} - -// On chains a new expectation description onto the mocked interface. This -// allows syntax like. -// -// Mock. -// On("MyMethod", 1).Return(nil). -// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) -// -//go:noinline -func (c *Call) On(methodName string, arguments ...interface{}) *Call { - return c.Parent.On(methodName, arguments...) -} - -// Unset removes a mock handler from being called. -// -// test.On("func", mock.Anything).Unset() -func (c *Call) Unset() *Call { - var unlockOnce sync.Once - - for _, arg := range c.Arguments { - if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { - panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) - } - } - - c.lock() - defer unlockOnce.Do(c.unlock) - - foundMatchingCall := false - - // in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones - var index int // write index - for _, call := range c.Parent.ExpectedCalls { - if call.Method == c.Method { - _, diffCount := call.Arguments.Diff(c.Arguments) - if diffCount == 0 { - foundMatchingCall = true - // Remove from ExpectedCalls - just skip it - continue - } - } - c.Parent.ExpectedCalls[index] = call - index++ - } - // trim slice up to last copied index - c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index] - - if !foundMatchingCall { - unlockOnce.Do(c.unlock) - c.Parent.fail("\n\nmock: Could not find expected call\n-----------------------------\n\n%s\n\n", - callString(c.Method, c.Arguments, true), - ) - } - - return c -} - -// NotBefore indicates that the mock should only be called after the referenced -// calls have been called as expected. The referenced calls may be from the -// same mock instance and/or other mock instances. -// -// Mock.On("Do").Return(nil).Notbefore( -// Mock.On("Init").Return(nil) -// ) -func (c *Call) NotBefore(calls ...*Call) *Call { - c.lock() - defer c.unlock() - - for _, call := range calls { - if call.Parent == nil { - panic("not before calls must be created with Mock.On()") - } - } - - c.requires = append(c.requires, calls...) - return c -} - -// Mock is the workhorse used to track activity on another object. -// For an example of its usage, refer to the "Example Usage" section at the top -// of this document. -type Mock struct { - // Represents the calls that are expected of - // an object. - ExpectedCalls []*Call - - // Holds the calls that were made to this mocked object. - Calls []Call - - // test is An optional variable that holds the test struct, to be used when an - // invalid mock call was made. - test TestingT - - // TestData holds any data that might be useful for testing. Testify ignores - // this data completely allowing you to do whatever you like with it. - testData objx.Map - - mutex sync.Mutex -} - -// String provides a %v format string for Mock. -// Note: this is used implicitly by Arguments.Diff if a Mock is passed. -// It exists because go's default %v formatting traverses the struct -// without acquiring the mutex, which is detected by go test -race. -func (m *Mock) String() string { - return fmt.Sprintf("%[1]T<%[1]p>", m) -} - -// TestData holds any data that might be useful for testing. Testify ignores -// this data completely allowing you to do whatever you like with it. -func (m *Mock) TestData() objx.Map { - if m.testData == nil { - m.testData = make(objx.Map) - } - - return m.testData -} - -/* - Setting expectations -*/ - -// Test sets the test struct variable of the mock object -func (m *Mock) Test(t TestingT) { - m.mutex.Lock() - defer m.mutex.Unlock() - m.test = t -} - -// fail fails the current test with the given formatted format and args. -// In case that a test was defined, it uses the test APIs for failing a test, -// otherwise it uses panic. -func (m *Mock) fail(format string, args ...interface{}) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.test == nil { - panic(fmt.Sprintf(format, args...)) - } - m.test.Errorf(format, args...) - m.test.FailNow() -} - -// On starts a description of an expectation of the specified method -// being called. -// -// Mock.On("MyMethod", arg1, arg2) -func (m *Mock) On(methodName string, arguments ...interface{}) *Call { - for _, arg := range arguments { - if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { - panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) - } - } - - m.mutex.Lock() - defer m.mutex.Unlock() - c := newCall(m, methodName, assert.CallerInfo(), arguments...) - m.ExpectedCalls = append(m.ExpectedCalls, c) - return c -} - -// /* -// Recording and responding to activity -// */ - -func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { - var expectedCall *Call - - for i, call := range m.ExpectedCalls { - if call.Method == method { - _, diffCount := call.Arguments.Diff(arguments) - if diffCount == 0 { - expectedCall = call - if call.Repeatability > -1 { - return i, call - } - } - } - } - - return -1, expectedCall -} - -type matchCandidate struct { - call *Call - mismatch string - diffCount int -} - -func (c matchCandidate) isBetterMatchThan(other matchCandidate) bool { - if c.call == nil { - return false - } - if other.call == nil { - return true - } - - if c.diffCount > other.diffCount { - return false - } - if c.diffCount < other.diffCount { - return true - } - - if c.call.Repeatability > 0 && other.call.Repeatability <= 0 { - return true - } - return false -} - -func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { - var bestMatch matchCandidate - - for _, call := range m.expectedCalls() { - if call.Method == method { - - errInfo, tempDiffCount := call.Arguments.Diff(arguments) - tempCandidate := matchCandidate{ - call: call, - mismatch: errInfo, - diffCount: tempDiffCount, - } - if tempCandidate.isBetterMatchThan(bestMatch) { - bestMatch = tempCandidate - } - } - } - - return bestMatch.call, bestMatch.mismatch -} - -func callString(method string, arguments Arguments, includeArgumentValues bool) string { - var argValsString string - if includeArgumentValues { - var argVals []string - for argIndex, arg := range arguments { - if _, ok := arg.(*FunctionalOptionsArgument); ok { - argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg)) - continue - } - argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) - } - argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) - } - - return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) -} - -// Called tells the mock object that a method has been called, and gets an array -// of arguments to return. Panics if the call is unexpected (i.e. not preceded by -// appropriate .On .Return() calls) -// If Call.WaitFor is set, blocks until the channel is closed or receives a message. -func (m *Mock) Called(arguments ...interface{}) Arguments { - // get the calling function's name - pc, _, _, ok := runtime.Caller(1) - if !ok { - panic("Couldn't get the caller information") - } - functionPath := runtime.FuncForPC(pc).Name() - // Next four lines are required to use GCCGO function naming conventions. - // For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock - // uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree - // With GCCGO we need to remove interface information starting from pN

. - if gccgoRE.MatchString(functionPath) { - functionPath = gccgoRE.Split(functionPath, -1)[0] - } - parts := strings.Split(functionPath, ".") - functionName := parts[len(parts)-1] - return m.MethodCalled(functionName, arguments...) -} - -// MethodCalled tells the mock object that the given method has been called, and gets -// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded -// by appropriate .On .Return() calls) -// If Call.WaitFor is set, blocks until the channel is closed or receives a message. -func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { - m.mutex.Lock() - // TODO: could combine expected and closes in single loop - found, call := m.findExpectedCall(methodName, arguments...) - - if found < 0 { - // expected call found, but it has already been called with repeatable times - if call != nil { - m.mutex.Unlock() - m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) - } - // we have to fail here - because we don't know what to do - // as the return arguments. This is because: - // - // a) this is a totally unexpected call to this method, - // b) the arguments are not what was expected, or - // c) the developer has forgotten to add an accompanying On...Return pair. - closestCall, mismatch := m.findClosestCall(methodName, arguments...) - m.mutex.Unlock() - - if closestCall != nil { - m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", - callString(methodName, arguments, true), - callString(methodName, closestCall.Arguments, true), - diffArguments(closestCall.Arguments, arguments), - strings.TrimSpace(mismatch), - ) - } else { - m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) - } - } - - for _, requirement := range call.requires { - if satisfied, _ := requirement.Parent.checkExpectation(requirement); !satisfied { - m.mutex.Unlock() - m.fail("mock: Unexpected Method Call\n-----------------------------\n\n%s\n\nMust not be called before%s:\n\n%s", - callString(call.Method, call.Arguments, true), - func() (s string) { - if requirement.totalCalls > 0 { - s = " another call of" - } - if call.Parent != requirement.Parent { - s += " method from another mock instance" - } - return - }(), - callString(requirement.Method, requirement.Arguments, true), - ) - } - } - - if call.Repeatability == 1 { - call.Repeatability = -1 - } else if call.Repeatability > 1 { - call.Repeatability-- - } - call.totalCalls++ - - // add the call - m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...)) - m.mutex.Unlock() - - // block if specified - if call.WaitFor != nil { - <-call.WaitFor - } else { - time.Sleep(call.waitTime) - } - - m.mutex.Lock() - panicMsg := call.PanicMsg - m.mutex.Unlock() - if panicMsg != nil { - panic(*panicMsg) - } - - m.mutex.Lock() - runFn := call.RunFn - m.mutex.Unlock() - - if runFn != nil { - runFn(arguments) - } - - m.mutex.Lock() - returnArgs := call.ReturnArguments - m.mutex.Unlock() - - return returnArgs -} - -/* - Assertions -*/ - -type assertExpectationiser interface { - AssertExpectations(TestingT) bool -} - -// AssertExpectationsForObjects asserts that everything specified with On and Return -// of the specified objects was in fact called as expected. -// -// Calls may have occurred in any order. -func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - for _, obj := range testObjects { - if m, ok := obj.(*Mock); ok { - t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") - obj = m - } - m := obj.(assertExpectationiser) - if !m.AssertExpectations(t) { - t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) - return false - } - } - return true -} - -// AssertExpectations asserts that everything specified with On and Return was -// in fact called as expected. Calls may have occurred in any order. -func (m *Mock) AssertExpectations(t TestingT) bool { - if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() { - return true - } - if h, ok := t.(tHelper); ok { - h.Helper() - } - - m.mutex.Lock() - defer m.mutex.Unlock() - var failedExpectations int - - // iterate through each expectation - expectedCalls := m.expectedCalls() - for _, expectedCall := range expectedCalls { - satisfied, reason := m.checkExpectation(expectedCall) - if !satisfied { - failedExpectations++ - t.Logf(reason) - } - } - - if failedExpectations != 0 { - t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) - } - - return failedExpectations == 0 -} - -func (m *Mock) checkExpectation(call *Call) (bool, string) { - if !call.optional && !m.methodWasCalled(call.Method, call.Arguments) && call.totalCalls == 0 { - return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) - } - if call.Repeatability > 0 { - return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) - } - return true, fmt.Sprintf("PASS:\t%s(%s)", call.Method, call.Arguments.String()) -} - -// AssertNumberOfCalls asserts that the method was called expectedCalls times. -func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - var actualCalls int - for _, call := range m.calls() { - if call.Method == methodName { - actualCalls++ - } - } - return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls)) -} - -// AssertCalled asserts that the method was called. -// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. -func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - if !m.methodWasCalled(methodName, arguments) { - var calledWithArgs []string - for _, call := range m.calls() { - calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments)) - } - if len(calledWithArgs) == 0 { - return assert.Fail(t, "Should have called with given arguments", - fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments)) - } - return assert.Fail(t, "Should have called with given arguments", - fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n"))) - } - return true -} - -// AssertNotCalled asserts that the method was not called. -// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. -func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - if m.methodWasCalled(methodName, arguments) { - return assert.Fail(t, "Should not have called with given arguments", - fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments)) - } - return true -} - -// IsMethodCallable checking that the method can be called -// If the method was called more than `Repeatability` return false -func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - - for _, v := range m.ExpectedCalls { - if v.Method != methodName { - continue - } - if len(arguments) != len(v.Arguments) { - continue - } - if v.Repeatability < v.totalCalls { - continue - } - if isArgsEqual(v.Arguments, arguments) { - return true - } - } - return false -} - -// isArgsEqual compares arguments -func isArgsEqual(expected Arguments, args []interface{}) bool { - if len(expected) != len(args) { - return false - } - for i, v := range args { - if !reflect.DeepEqual(expected[i], v) { - return false - } - } - return true -} - -func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool { - for _, call := range m.calls() { - if call.Method == methodName { - - _, differences := Arguments(expected).Diff(call.Arguments) - - if differences == 0 { - // found the expected call - return true - } - - } - } - // we didn't find the expected call - return false -} - -func (m *Mock) expectedCalls() []*Call { - return append([]*Call{}, m.ExpectedCalls...) -} - -func (m *Mock) calls() []Call { - return append([]Call{}, m.Calls...) -} - -/* - Arguments -*/ - -// Arguments holds an array of method arguments or return values. -type Arguments []interface{} - -const ( - // Anything is used in Diff and Assert when the argument being tested - // shouldn't be taken into consideration. - Anything = "mock.Anything" -) - -// AnythingOfTypeArgument contains the type of an argument -// for use when type checking. Used in Diff and Assert. -// -// Deprecated: this is an implementation detail that must not be used. Use [AnythingOfType] instead. -type AnythingOfTypeArgument = anythingOfTypeArgument - -// anythingOfTypeArgument is a string that contains the type of an argument -// for use when type checking. Used in Diff and Assert. -type anythingOfTypeArgument string - -// AnythingOfType returns a special value containing the -// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String]. -// -// Used in Diff and Assert. -// -// For example: -// -// Assert(t, AnythingOfType("string"), AnythingOfType("int")) -func AnythingOfType(t string) AnythingOfTypeArgument { - return anythingOfTypeArgument(t) -} - -// IsTypeArgument is a struct that contains the type of an argument -// for use when type checking. This is an alternative to AnythingOfType. -// Used in Diff and Assert. -type IsTypeArgument struct { - t reflect.Type -} - -// IsType returns an IsTypeArgument object containing the type to check for. -// You can provide a zero-value of the type to check. This is an -// alternative to AnythingOfType. Used in Diff and Assert. -// -// For example: -// Assert(t, IsType(""), IsType(0)) -func IsType(t interface{}) *IsTypeArgument { - return &IsTypeArgument{t: reflect.TypeOf(t)} -} - -// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument -// for use when type checking. -type FunctionalOptionsArgument struct { - value interface{} -} - -// String returns the string representation of FunctionalOptionsArgument -func (f *FunctionalOptionsArgument) String() string { - var name string - tValue := reflect.ValueOf(f.value) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() - } - - return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1) -} - -// FunctionalOptions returns an FunctionalOptionsArgument object containing the functional option type -// and the values to check of -// -// For example: -// Assert(t, FunctionalOptions("[]foo.FunctionalOption", foo.Opt1(), foo.Opt2())) -func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument { - return &FunctionalOptionsArgument{ - value: value, - } -} - -// argumentMatcher performs custom argument matching, returning whether or -// not the argument is matched by the expectation fixture function. -type argumentMatcher struct { - // fn is a function which accepts one argument, and returns a bool. - fn reflect.Value -} - -func (f argumentMatcher) Matches(argument interface{}) bool { - expectType := f.fn.Type().In(0) - expectTypeNilSupported := false - switch expectType.Kind() { - case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr: - expectTypeNilSupported = true - } - - argType := reflect.TypeOf(argument) - var arg reflect.Value - if argType == nil { - arg = reflect.New(expectType).Elem() - } else { - arg = reflect.ValueOf(argument) - } - - if argType == nil && !expectTypeNilSupported { - panic(errors.New("attempting to call matcher with nil for non-nil expected type")) - } - if argType == nil || argType.AssignableTo(expectType) { - result := f.fn.Call([]reflect.Value{arg}) - return result[0].Bool() - } - return false -} - -func (f argumentMatcher) String() string { - return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String()) -} - -// MatchedBy can be used to match a mock call based on only certain properties -// from a complex struct or some calculation. It takes a function that will be -// evaluated with the called argument and will return true when there's a match -// and false otherwise. -// -// Example: -// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" })) -// -// |fn|, must be a function accepting a single argument (of the expected type) -// which returns a bool. If |fn| doesn't match the required signature, -// MatchedBy() panics. -func MatchedBy(fn interface{}) argumentMatcher { - fnType := reflect.TypeOf(fn) - - if fnType.Kind() != reflect.Func { - panic(fmt.Sprintf("assert: arguments: %s is not a func", fn)) - } - if fnType.NumIn() != 1 { - panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn)) - } - if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool { - panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn)) - } - - return argumentMatcher{fn: reflect.ValueOf(fn)} -} - -// Get Returns the argument at the specified index. -func (args Arguments) Get(index int) interface{} { - if index+1 > len(args) { - panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args))) - } - return args[index] -} - -// Is gets whether the objects match the arguments specified. -func (args Arguments) Is(objects ...interface{}) bool { - for i, obj := range args { - if obj != objects[i] { - return false - } - } - return true -} - -// Diff gets a string describing the differences between the arguments -// and the specified objects. -// -// Returns the diff string and number of differences found. -func (args Arguments) Diff(objects []interface{}) (string, int) { - // TODO: could return string as error and nil for No difference - - output := "\n" - var differences int - - maxArgCount := len(args) - if len(objects) > maxArgCount { - maxArgCount = len(objects) - } - - for i := 0; i < maxArgCount; i++ { - var actual, expected interface{} - var actualFmt, expectedFmt string - - if len(objects) <= i { - actual = "(Missing)" - actualFmt = "(Missing)" - } else { - actual = objects[i] - actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual) - } - - if len(args) <= i { - expected = "(Missing)" - expectedFmt = "(Missing)" - } else { - expected = args[i] - expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected) - } - - if matcher, ok := expected.(argumentMatcher); ok { - var matches bool - func() { - defer func() { - if r := recover(); r != nil { - actualFmt = fmt.Sprintf("panic in argument matcher: %v", r) - } - }() - matches = matcher.Matches(actual) - }() - if matches { - output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) - } else { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) - } - } else { - switch expected := expected.(type) { - case anythingOfTypeArgument: - // type checking - if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) - } - case *IsTypeArgument: - actualT := reflect.TypeOf(actual) - if actualT != expected.t { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) - } - case *FunctionalOptionsArgument: - t := expected.value - - var name string - tValue := reflect.ValueOf(t) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() - } - - tName := reflect.TypeOf(t).Name() - if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) - } else { - if ef, af := assertOpts(t, actual); ef == "" && af == "" { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) - } - } - - default: - if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) - } - } - } - - } - - if differences == 0 { - return "No differences.", differences - } - - return output, differences -} - -// Assert compares the arguments with the specified objects and fails if -// they do not exactly match. -func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - // get the differences - diff, diffCount := args.Diff(objects) - - if diffCount == 0 { - return true - } - - // there are differences... report them... - t.Logf(diff) - t.Errorf("%sArguments do not match.", assert.CallerInfo()) - - return false -} - -// String gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -// -// If no index is provided, String() returns a complete string representation -// of the arguments. -func (args Arguments) String(indexOrNil ...int) string { - if len(indexOrNil) == 0 { - // normal String() method - return a string representation of the args - var argsStr []string - for _, arg := range args { - argsStr = append(argsStr, fmt.Sprintf("%T", arg)) // handles nil nicely - } - return strings.Join(argsStr, ",") - } else if len(indexOrNil) == 1 { - // Index has been specified - get the argument at that index - index := indexOrNil[0] - var s string - var ok bool - if s, ok = args.Get(index).(string); !ok { - panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index))) - } - return s - } - - panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) -} - -// Int gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Int(index int) int { - var s int - var ok bool - if s, ok = args.Get(index).(int); !ok { - panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -// Error gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Error(index int) error { - obj := args.Get(index) - var s error - var ok bool - if obj == nil { - return nil - } - if s, ok = obj.(error); !ok { - panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -// Bool gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Bool(index int) bool { - var s bool - var ok bool - if s, ok = args.Get(index).(bool); !ok { - panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { - t := reflect.TypeOf(v) - k := t.Kind() - - if k == reflect.Ptr { - t = t.Elem() - k = t.Kind() - } - return t, k -} - -func diffArguments(expected Arguments, actual Arguments) string { - if len(expected) != len(actual) { - return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual)) - } - - for x := range expected { - if diffString := diff(expected[x], actual[x]); diffString != "" { - return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString) - } - } - - return "" -} - -// diff returns a diff of both values as long as both are of the same type and -// are a struct, map, slice or array. Otherwise it returns an empty string. -func diff(expected interface{}, actual interface{}) string { - if expected == nil || actual == nil { - return "" - } - - et, ek := typeAndKind(expected) - at, _ := typeAndKind(actual) - - if et != at { - return "" - } - - if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { - return "" - } - - e := spewConfig.Sdump(expected) - a := spewConfig.Sdump(actual) - - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(e), - B: difflib.SplitLines(a), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - - return diff -} - -var spewConfig = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, -} - -type tHelper interface { - Helper() -} - -func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { - expectedOpts := reflect.ValueOf(expected) - actualOpts := reflect.ValueOf(actual) - var expectedNames []string - for i := 0; i < expectedOpts.Len(); i++ { - expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface())) - } - var actualNames []string - for i := 0; i < actualOpts.Len(); i++ { - actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface())) - } - if !assert.ObjectsAreEqual(expectedNames, actualNames) { - expectedFmt = fmt.Sprintf("%v", expectedNames) - actualFmt = fmt.Sprintf("%v", actualNames) - return - } - - for i := 0; i < expectedOpts.Len(); i++ { - expectedOpt := expectedOpts.Index(i).Interface() - actualOpt := actualOpts.Index(i).Interface() - - expectedFunc := expectedNames[i] - actualFunc := actualNames[i] - if expectedFunc != actualFunc { - expectedFmt = expectedFunc - actualFmt = actualFunc - return - } - - ot := reflect.TypeOf(expectedOpt) - var expectedValues []reflect.Value - var actualValues []reflect.Value - if ot.NumIn() == 0 { - return - } - - for i := 0; i < ot.NumIn(); i++ { - vt := ot.In(i).Elem() - expectedValues = append(expectedValues, reflect.New(vt)) - actualValues = append(actualValues, reflect.New(vt)) - } - - reflect.ValueOf(expectedOpt).Call(expectedValues) - reflect.ValueOf(actualOpt).Call(actualValues) - - for i := 0; i < ot.NumIn(); i++ { - if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) { - expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface()) - actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface()) - return - } - } - } - - return "", "" -} - -func funcName(opt interface{}) string { - n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name() - return strings.TrimSuffix(path.Base(n), path.Ext(n)) -} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go index db42e6676a..c709b72847 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego +//go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego package chacha20 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.go similarity index 89% rename from vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go rename to vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.go index 3a4287f990..bd183d9ba1 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego +//go:build gc && !purego && (ppc64 || ppc64le) package chacha20 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.s similarity index 76% rename from vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s rename to vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.s index c672ccf698..a660b4112f 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64x.s @@ -19,7 +19,7 @@ // The differences in this and the original implementation are // due to the calling conventions and initialization of constants. -//go:build gc && !purego +//go:build gc && !purego && (ppc64 || ppc64le) #include "textflag.h" @@ -36,32 +36,68 @@ // for VPERMXOR #define MASK R18 -DATA consts<>+0x00(SB)/8, $0x3320646e61707865 -DATA consts<>+0x08(SB)/8, $0x6b20657479622d32 -DATA consts<>+0x10(SB)/8, $0x0000000000000001 -DATA consts<>+0x18(SB)/8, $0x0000000000000000 -DATA consts<>+0x20(SB)/8, $0x0000000000000004 -DATA consts<>+0x28(SB)/8, $0x0000000000000000 -DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d -DATA consts<>+0x38(SB)/8, $0x0203000106070405 -DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c -DATA consts<>+0x48(SB)/8, $0x0102030005060704 -DATA consts<>+0x50(SB)/8, $0x6170786561707865 -DATA consts<>+0x58(SB)/8, $0x6170786561707865 -DATA consts<>+0x60(SB)/8, $0x3320646e3320646e -DATA consts<>+0x68(SB)/8, $0x3320646e3320646e -DATA consts<>+0x70(SB)/8, $0x79622d3279622d32 -DATA consts<>+0x78(SB)/8, $0x79622d3279622d32 -DATA consts<>+0x80(SB)/8, $0x6b2065746b206574 -DATA consts<>+0x88(SB)/8, $0x6b2065746b206574 -DATA consts<>+0x90(SB)/8, $0x0000000100000000 -DATA consts<>+0x98(SB)/8, $0x0000000300000002 -DATA consts<>+0xa0(SB)/8, $0x5566774411223300 -DATA consts<>+0xa8(SB)/8, $0xddeeffcc99aabb88 -DATA consts<>+0xb0(SB)/8, $0x6677445522330011 -DATA consts<>+0xb8(SB)/8, $0xeeffccddaabb8899 +DATA consts<>+0x00(SB)/4, $0x61707865 +DATA consts<>+0x04(SB)/4, $0x3320646e +DATA consts<>+0x08(SB)/4, $0x79622d32 +DATA consts<>+0x0c(SB)/4, $0x6b206574 +DATA consts<>+0x10(SB)/4, $0x00000001 +DATA consts<>+0x14(SB)/4, $0x00000000 +DATA consts<>+0x18(SB)/4, $0x00000000 +DATA consts<>+0x1c(SB)/4, $0x00000000 +DATA consts<>+0x20(SB)/4, $0x00000004 +DATA consts<>+0x24(SB)/4, $0x00000000 +DATA consts<>+0x28(SB)/4, $0x00000000 +DATA consts<>+0x2c(SB)/4, $0x00000000 +DATA consts<>+0x30(SB)/4, $0x0e0f0c0d +DATA consts<>+0x34(SB)/4, $0x0a0b0809 +DATA consts<>+0x38(SB)/4, $0x06070405 +DATA consts<>+0x3c(SB)/4, $0x02030001 +DATA consts<>+0x40(SB)/4, $0x0d0e0f0c +DATA consts<>+0x44(SB)/4, $0x090a0b08 +DATA consts<>+0x48(SB)/4, $0x05060704 +DATA consts<>+0x4c(SB)/4, $0x01020300 +DATA consts<>+0x50(SB)/4, $0x61707865 +DATA consts<>+0x54(SB)/4, $0x61707865 +DATA consts<>+0x58(SB)/4, $0x61707865 +DATA consts<>+0x5c(SB)/4, $0x61707865 +DATA consts<>+0x60(SB)/4, $0x3320646e +DATA consts<>+0x64(SB)/4, $0x3320646e +DATA consts<>+0x68(SB)/4, $0x3320646e +DATA consts<>+0x6c(SB)/4, $0x3320646e +DATA consts<>+0x70(SB)/4, $0x79622d32 +DATA consts<>+0x74(SB)/4, $0x79622d32 +DATA consts<>+0x78(SB)/4, $0x79622d32 +DATA consts<>+0x7c(SB)/4, $0x79622d32 +DATA consts<>+0x80(SB)/4, $0x6b206574 +DATA consts<>+0x84(SB)/4, $0x6b206574 +DATA consts<>+0x88(SB)/4, $0x6b206574 +DATA consts<>+0x8c(SB)/4, $0x6b206574 +DATA consts<>+0x90(SB)/4, $0x00000000 +DATA consts<>+0x94(SB)/4, $0x00000001 +DATA consts<>+0x98(SB)/4, $0x00000002 +DATA consts<>+0x9c(SB)/4, $0x00000003 +DATA consts<>+0xa0(SB)/4, $0x11223300 +DATA consts<>+0xa4(SB)/4, $0x55667744 +DATA consts<>+0xa8(SB)/4, $0x99aabb88 +DATA consts<>+0xac(SB)/4, $0xddeeffcc +DATA consts<>+0xb0(SB)/4, $0x22330011 +DATA consts<>+0xb4(SB)/4, $0x66774455 +DATA consts<>+0xb8(SB)/4, $0xaabb8899 +DATA consts<>+0xbc(SB)/4, $0xeeffccdd GLOBL consts<>(SB), RODATA, $0xc0 +#ifdef GOARCH_ppc64 +#define BE_XXBRW_INIT() \ + LVSL (R0)(R0), V24 \ + VSPLTISB $3, V25 \ + VXOR V24, V25, V24 \ + +#define BE_XXBRW(vr) VPERM vr, vr, V24, vr +#else +#define BE_XXBRW_INIT() +#define BE_XXBRW(vr) +#endif + //func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32) TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40 MOVD out+0(FP), OUT @@ -94,6 +130,8 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40 // Clear V27 VXOR V27, V27, V27 + BE_XXBRW_INIT() + // V28 LXVW4X (CONSTBASE)(R11), VS60 @@ -299,6 +337,11 @@ loop_vsx: VADDUWM V8, V18, V8 VADDUWM V12, V19, V12 + BE_XXBRW(V0) + BE_XXBRW(V4) + BE_XXBRW(V8) + BE_XXBRW(V12) + CMPU LEN, $64 BLT tail_vsx @@ -327,6 +370,11 @@ loop_vsx: VADDUWM V9, V18, V8 VADDUWM V13, V19, V12 + BE_XXBRW(V0) + BE_XXBRW(V4) + BE_XXBRW(V8) + BE_XXBRW(V12) + CMPU LEN, $64 BLT tail_vsx @@ -334,8 +382,8 @@ loop_vsx: LXVW4X (INP)(R8), VS60 LXVW4X (INP)(R9), VS61 LXVW4X (INP)(R10), VS62 - VXOR V27, V0, V27 + VXOR V27, V0, V27 VXOR V28, V4, V28 VXOR V29, V8, V29 VXOR V30, V12, V30 @@ -354,6 +402,11 @@ loop_vsx: VADDUWM V10, V18, V8 VADDUWM V14, V19, V12 + BE_XXBRW(V0) + BE_XXBRW(V4) + BE_XXBRW(V8) + BE_XXBRW(V12) + CMPU LEN, $64 BLT tail_vsx @@ -381,6 +434,11 @@ loop_vsx: VADDUWM V11, V18, V8 VADDUWM V15, V19, V12 + BE_XXBRW(V0) + BE_XXBRW(V4) + BE_XXBRW(V8) + BE_XXBRW(V12) + CMPU LEN, $64 BLT tail_vsx @@ -408,9 +466,9 @@ loop_vsx: done_vsx: // Increment counter by number of 64 byte blocks - MOVD (CNT), R14 + MOVWZ (CNT), R14 ADD BLOCKS, R14 - MOVD R14, (CNT) + MOVWZ R14, (CNT) RET tail_vsx: diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go index 333da285b3..bd896bdc76 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego +//go:build (!amd64 && !ppc64le && !ppc64 && !s390x) || !gc || purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go similarity index 95% rename from vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go rename to vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go index 4aec4874b5..1a1679aaad 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego +//go:build gc && !purego && (ppc64 || ppc64le) package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s similarity index 89% rename from vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s rename to vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s index b3c1699bff..6899a1dabc 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64x.s @@ -2,15 +2,25 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego +//go:build gc && !purego && (ppc64 || ppc64le) #include "textflag.h" // This was ported from the amd64 implementation. +#ifdef GOARCH_ppc64le +#define LE_MOVD MOVD +#define LE_MOVWZ MOVWZ +#define LE_MOVHZ MOVHZ +#else +#define LE_MOVD MOVDBR +#define LE_MOVWZ MOVWBR +#define LE_MOVHZ MOVHBR +#endif + #define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \ - MOVD (msg), t0; \ - MOVD 8(msg), t1; \ + LE_MOVD (msg)( R0), t0; \ + LE_MOVD (msg)(R24), t1; \ MOVD $1, t2; \ ADDC t0, h0, h0; \ ADDE t1, h1, h1; \ @@ -50,10 +60,6 @@ ADDE t3, h1, h1; \ ADDZE h2 -DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF -DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC -GLOBL ·poly1305Mask<>(SB), RODATA, $16 - // func update(state *[7]uint64, msg []byte) TEXT ·update(SB), $0-32 MOVD state+0(FP), R3 @@ -66,6 +72,8 @@ TEXT ·update(SB), $0-32 MOVD 24(R3), R11 // r0 MOVD 32(R3), R12 // r1 + MOVD $8, R24 + CMP R5, $16 BLT bytes_between_0_and_15 @@ -94,7 +102,7 @@ flush_buffer: // Greater than 8 -- load the rightmost remaining bytes in msg // and put into R17 (h1) - MOVD (R4)(R21), R17 + LE_MOVD (R4)(R21), R17 MOVD $16, R22 // Find the offset to those bytes @@ -118,7 +126,7 @@ just1: BLT less8 // Exactly 8 - MOVD (R4), R16 + LE_MOVD (R4), R16 CMP R17, $0 @@ -133,7 +141,7 @@ less8: MOVD $0, R22 // shift count CMP R5, $4 BLT less4 - MOVWZ (R4), R16 + LE_MOVWZ (R4), R16 ADD $4, R4 ADD $-4, R5 MOVD $32, R22 @@ -141,7 +149,7 @@ less8: less4: CMP R5, $2 BLT less2 - MOVHZ (R4), R21 + LE_MOVHZ (R4), R21 SLD R22, R21, R21 OR R16, R21, R16 ADD $16, R22 diff --git a/vendor/golang.org/x/crypto/sha3/doc.go b/vendor/golang.org/x/crypto/sha3/doc.go index 7e02309070..bbf391fe6e 100644 --- a/vendor/golang.org/x/crypto/sha3/doc.go +++ b/vendor/golang.org/x/crypto/sha3/doc.go @@ -5,6 +5,10 @@ // Package sha3 implements the SHA-3 fixed-output-length hash functions and // the SHAKE variable-output-length hash functions defined by FIPS-202. // +// All types in this package also implement [encoding.BinaryMarshaler], +// [encoding.BinaryAppender] and [encoding.BinaryUnmarshaler] to marshal and +// unmarshal the internal state of the hash. +// // Both types of hash function use the "sponge" construction and the Keccak // permutation. For a detailed specification see http://keccak.noekeon.org/ // diff --git a/vendor/golang.org/x/crypto/sha3/hashes.go b/vendor/golang.org/x/crypto/sha3/hashes.go index c544b29e5f..31fffbe044 100644 --- a/vendor/golang.org/x/crypto/sha3/hashes.go +++ b/vendor/golang.org/x/crypto/sha3/hashes.go @@ -48,33 +48,52 @@ func init() { crypto.RegisterHash(crypto.SHA3_512, New512) } +const ( + dsbyteSHA3 = 0b00000110 + dsbyteKeccak = 0b00000001 + dsbyteShake = 0b00011111 + dsbyteCShake = 0b00000100 + + // rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in + // bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits. + rateK256 = (1600 - 256) / 8 + rateK448 = (1600 - 448) / 8 + rateK512 = (1600 - 512) / 8 + rateK768 = (1600 - 768) / 8 + rateK1024 = (1600 - 1024) / 8 +) + func new224Generic() *state { - return &state{rate: 144, outputLen: 28, dsbyte: 0x06} + return &state{rate: rateK448, outputLen: 28, dsbyte: dsbyteSHA3} } func new256Generic() *state { - return &state{rate: 136, outputLen: 32, dsbyte: 0x06} + return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteSHA3} } func new384Generic() *state { - return &state{rate: 104, outputLen: 48, dsbyte: 0x06} + return &state{rate: rateK768, outputLen: 48, dsbyte: dsbyteSHA3} } func new512Generic() *state { - return &state{rate: 72, outputLen: 64, dsbyte: 0x06} + return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteSHA3} } // NewLegacyKeccak256 creates a new Keccak-256 hash. // // Only use this function if you require compatibility with an existing cryptosystem // that uses non-standard padding. All other users should use New256 instead. -func NewLegacyKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} } +func NewLegacyKeccak256() hash.Hash { + return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak} +} // NewLegacyKeccak512 creates a new Keccak-512 hash. // // Only use this function if you require compatibility with an existing cryptosystem // that uses non-standard padding. All other users should use New512 instead. -func NewLegacyKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} } +func NewLegacyKeccak512() hash.Hash { + return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak} +} // Sum224 returns the SHA3-224 digest of the data. func Sum224(data []byte) (digest [28]byte) { diff --git a/vendor/golang.org/x/crypto/sha3/sha3.go b/vendor/golang.org/x/crypto/sha3/sha3.go index afedde5abf..6658c44479 100644 --- a/vendor/golang.org/x/crypto/sha3/sha3.go +++ b/vendor/golang.org/x/crypto/sha3/sha3.go @@ -4,6 +4,15 @@ package sha3 +import ( + "crypto/subtle" + "encoding/binary" + "errors" + "unsafe" + + "golang.org/x/sys/cpu" +) + // spongeDirection indicates the direction bytes are flowing through the sponge. type spongeDirection int @@ -14,16 +23,13 @@ const ( spongeSqueezing ) -const ( - // maxRate is the maximum size of the internal buffer. SHAKE-256 - // currently needs the largest buffer. - maxRate = 168 -) - type state struct { - // Generic sponge components. - a [25]uint64 // main state of the hash - rate int // the number of bytes of state to use + a [1600 / 8]byte // main state of the hash + + // a[n:rate] is the buffer. If absorbing, it's the remaining space to XOR + // into before running the permutation. If squeezing, it's the remaining + // output to produce before running the permutation. + n, rate int // dsbyte contains the "domain separation" bits and the first bit of // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the @@ -39,10 +45,6 @@ type state struct { // Extendable-Output Functions (May 2014)" dsbyte byte - i, n int // storage[i:n] is the buffer, i is only used while squeezing - storage [maxRate]byte - - // Specific to SHA-3 and SHAKE. outputLen int // the default output size in bytes state spongeDirection // whether the sponge is absorbing or squeezing } @@ -61,7 +63,7 @@ func (d *state) Reset() { d.a[i] = 0 } d.state = spongeAbsorbing - d.i, d.n = 0, 0 + d.n = 0 } func (d *state) clone() *state { @@ -69,22 +71,25 @@ func (d *state) clone() *state { return &ret } -// permute applies the KeccakF-1600 permutation. It handles -// any input-output buffering. +// permute applies the KeccakF-1600 permutation. func (d *state) permute() { - switch d.state { - case spongeAbsorbing: - // If we're absorbing, we need to xor the input into the state - // before applying the permutation. - xorIn(d, d.storage[:d.rate]) - d.n = 0 - keccakF1600(&d.a) - case spongeSqueezing: - // If we're squeezing, we need to apply the permutation before - // copying more output. - keccakF1600(&d.a) - d.i = 0 - copyOut(d, d.storage[:d.rate]) + var a *[25]uint64 + if cpu.IsBigEndian { + a = new([25]uint64) + for i := range a { + a[i] = binary.LittleEndian.Uint64(d.a[i*8:]) + } + } else { + a = (*[25]uint64)(unsafe.Pointer(&d.a)) + } + + keccakF1600(a) + d.n = 0 + + if cpu.IsBigEndian { + for i := range a { + binary.LittleEndian.PutUint64(d.a[i*8:], a[i]) + } } } @@ -92,53 +97,36 @@ func (d *state) permute() { // the multi-bitrate 10..1 padding rule, and permutes the state. func (d *state) padAndPermute() { // Pad with this instance's domain-separator bits. We know that there's - // at least one byte of space in d.buf because, if it were full, + // at least one byte of space in the sponge because, if it were full, // permute would have been called to empty it. dsbyte also contains the // first one bit for the padding. See the comment in the state struct. - d.storage[d.n] = d.dsbyte - d.n++ - for d.n < d.rate { - d.storage[d.n] = 0 - d.n++ - } + d.a[d.n] ^= d.dsbyte // This adds the final one bit for the padding. Because of the way that // bits are numbered from the LSB upwards, the final bit is the MSB of // the last byte. - d.storage[d.rate-1] ^= 0x80 + d.a[d.rate-1] ^= 0x80 // Apply the permutation d.permute() d.state = spongeSqueezing - d.n = d.rate - copyOut(d, d.storage[:d.rate]) } // Write absorbs more data into the hash's state. It panics if any // output has already been read. -func (d *state) Write(p []byte) (written int, err error) { +func (d *state) Write(p []byte) (n int, err error) { if d.state != spongeAbsorbing { panic("sha3: Write after Read") } - written = len(p) + + n = len(p) for len(p) > 0 { - if d.n == 0 && len(p) >= d.rate { - // The fast path; absorb a full "rate" bytes of input and apply the permutation. - xorIn(d, p[:d.rate]) - p = p[d.rate:] - keccakF1600(&d.a) - } else { - // The slow path; buffer the input until we can fill the sponge, and then xor it in. - todo := d.rate - d.n - if todo > len(p) { - todo = len(p) - } - d.n += copy(d.storage[d.n:], p[:todo]) - p = p[todo:] - - // If the sponge is full, apply the permutation. - if d.n == d.rate { - d.permute() - } + x := subtle.XORBytes(d.a[d.n:d.rate], d.a[d.n:d.rate], p) + d.n += x + p = p[x:] + + // If the sponge is full, apply the permutation. + if d.n == d.rate { + d.permute() } } @@ -156,14 +144,14 @@ func (d *state) Read(out []byte) (n int, err error) { // Now, do the squeezing. for len(out) > 0 { - n := copy(out, d.storage[d.i:d.n]) - d.i += n - out = out[n:] - // Apply the permutation if we've squeezed the sponge dry. - if d.i == d.rate { + if d.n == d.rate { d.permute() } + + x := copy(out, d.a[d.n:d.rate]) + d.n += x + out = out[x:] } return @@ -183,3 +171,74 @@ func (d *state) Sum(in []byte) []byte { dup.Read(hash) return append(in, hash...) } + +const ( + magicSHA3 = "sha\x08" + magicShake = "sha\x09" + magicCShake = "sha\x0a" + magicKeccak = "sha\x0b" + // magic || rate || main state || n || sponge direction + marshaledSize = len(magicSHA3) + 1 + 200 + 1 + 1 +) + +func (d *state) MarshalBinary() ([]byte, error) { + return d.AppendBinary(make([]byte, 0, marshaledSize)) +} + +func (d *state) AppendBinary(b []byte) ([]byte, error) { + switch d.dsbyte { + case dsbyteSHA3: + b = append(b, magicSHA3...) + case dsbyteShake: + b = append(b, magicShake...) + case dsbyteCShake: + b = append(b, magicCShake...) + case dsbyteKeccak: + b = append(b, magicKeccak...) + default: + panic("unknown dsbyte") + } + // rate is at most 168, and n is at most rate. + b = append(b, byte(d.rate)) + b = append(b, d.a[:]...) + b = append(b, byte(d.n), byte(d.state)) + return b, nil +} + +func (d *state) UnmarshalBinary(b []byte) error { + if len(b) != marshaledSize { + return errors.New("sha3: invalid hash state") + } + + magic := string(b[:len(magicSHA3)]) + b = b[len(magicSHA3):] + switch { + case magic == magicSHA3 && d.dsbyte == dsbyteSHA3: + case magic == magicShake && d.dsbyte == dsbyteShake: + case magic == magicCShake && d.dsbyte == dsbyteCShake: + case magic == magicKeccak && d.dsbyte == dsbyteKeccak: + default: + return errors.New("sha3: invalid hash state identifier") + } + + rate := int(b[0]) + b = b[1:] + if rate != d.rate { + return errors.New("sha3: invalid hash state function") + } + + copy(d.a[:], b) + b = b[len(d.a):] + + n, state := int(b[0]), spongeDirection(b[1]) + if n > d.rate { + return errors.New("sha3: invalid hash state") + } + d.n = n + if state != spongeAbsorbing && state != spongeSqueezing { + return errors.New("sha3: invalid hash state") + } + d.state = state + + return nil +} diff --git a/vendor/golang.org/x/crypto/sha3/shake.go b/vendor/golang.org/x/crypto/sha3/shake.go index 1ea9275b8b..a6b3a4281f 100644 --- a/vendor/golang.org/x/crypto/sha3/shake.go +++ b/vendor/golang.org/x/crypto/sha3/shake.go @@ -16,9 +16,12 @@ package sha3 // [2] https://doi.org/10.6028/NIST.SP.800-185 import ( + "bytes" "encoding/binary" + "errors" "hash" "io" + "math/bits" ) // ShakeHash defines the interface to hash functions that support @@ -50,44 +53,36 @@ type cshakeState struct { initBlock []byte } -// Consts for configuring initial SHA-3 state -const ( - dsbyteShake = 0x1f - dsbyteCShake = 0x04 - rate128 = 168 - rate256 = 136 -) +func bytepad(data []byte, rate int) []byte { + out := make([]byte, 0, 9+len(data)+rate-1) + out = append(out, leftEncode(uint64(rate))...) + out = append(out, data...) + if padlen := rate - len(out)%rate; padlen < rate { + out = append(out, make([]byte, padlen)...) + } + return out +} -func bytepad(input []byte, w int) []byte { - // leftEncode always returns max 9 bytes - buf := make([]byte, 0, 9+len(input)+w) - buf = append(buf, leftEncode(uint64(w))...) - buf = append(buf, input...) - padlen := w - (len(buf) % w) - return append(buf, make([]byte, padlen)...) -} - -func leftEncode(value uint64) []byte { - var b [9]byte - binary.BigEndian.PutUint64(b[1:], value) - // Trim all but last leading zero bytes - i := byte(1) - for i < 8 && b[i] == 0 { - i++ +func leftEncode(x uint64) []byte { + // Let n be the smallest positive integer for which 2^(8n) > x. + n := (bits.Len64(x) + 7) / 8 + if n == 0 { + n = 1 } - // Prepend number of encoded bytes - b[i-1] = 9 - i - return b[i-1:] + // Return n || x with n as a byte and x an n bytes in big-endian order. + b := make([]byte, 9) + binary.BigEndian.PutUint64(b[1:], x) + b = b[9-n-1:] + b[0] = byte(n) + return b } func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash { c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}} - - // leftEncode returns max 9 bytes - c.initBlock = make([]byte, 0, 9*2+len(N)+len(S)) - c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...) + c.initBlock = make([]byte, 0, 9+len(N)+9+len(S)) // leftEncode returns max 9 bytes + c.initBlock = append(c.initBlock, leftEncode(uint64(len(N))*8)...) c.initBlock = append(c.initBlock, N...) - c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...) + c.initBlock = append(c.initBlock, leftEncode(uint64(len(S))*8)...) c.initBlock = append(c.initBlock, S...) c.Write(bytepad(c.initBlock, c.rate)) return &c @@ -111,6 +106,30 @@ func (c *state) Clone() ShakeHash { return c.clone() } +func (c *cshakeState) MarshalBinary() ([]byte, error) { + return c.AppendBinary(make([]byte, 0, marshaledSize+len(c.initBlock))) +} + +func (c *cshakeState) AppendBinary(b []byte) ([]byte, error) { + b, err := c.state.AppendBinary(b) + if err != nil { + return nil, err + } + b = append(b, c.initBlock...) + return b, nil +} + +func (c *cshakeState) UnmarshalBinary(b []byte) error { + if len(b) <= marshaledSize { + return errors.New("sha3: invalid hash state") + } + if err := c.state.UnmarshalBinary(b[:marshaledSize]); err != nil { + return err + } + c.initBlock = bytes.Clone(b[marshaledSize:]) + return nil +} + // NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. // Its generic security strength is 128 bits against all attacks if at // least 32 bytes of its output are used. @@ -126,11 +145,11 @@ func NewShake256() ShakeHash { } func newShake128Generic() *state { - return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake} + return &state{rate: rateK256, outputLen: 32, dsbyte: dsbyteShake} } func newShake256Generic() *state { - return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake} + return &state{rate: rateK512, outputLen: 64, dsbyte: dsbyteShake} } // NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash, @@ -143,7 +162,7 @@ func NewCShake128(N, S []byte) ShakeHash { if len(N) == 0 && len(S) == 0 { return NewShake128() } - return newCShake(N, S, rate128, 32, dsbyteCShake) + return newCShake(N, S, rateK256, 32, dsbyteCShake) } // NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash, @@ -156,7 +175,7 @@ func NewCShake256(N, S []byte) ShakeHash { if len(N) == 0 && len(S) == 0 { return NewShake256() } - return newCShake(N, S, rate256, 64, dsbyteCShake) + return newCShake(N, S, rateK512, 64, dsbyteCShake) } // ShakeSum128 writes an arbitrary-length digest of data into hash. diff --git a/vendor/golang.org/x/crypto/sha3/xor.go b/vendor/golang.org/x/crypto/sha3/xor.go deleted file mode 100644 index 6ada5c9574..0000000000 --- a/vendor/golang.org/x/crypto/sha3/xor.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015 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 sha3 - -import ( - "crypto/subtle" - "encoding/binary" - "unsafe" - - "golang.org/x/sys/cpu" -) - -// xorIn xors the bytes in buf into the state. -func xorIn(d *state, buf []byte) { - if cpu.IsBigEndian { - for i := 0; len(buf) >= 8; i++ { - a := binary.LittleEndian.Uint64(buf) - d.a[i] ^= a - buf = buf[8:] - } - } else { - ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a)) - subtle.XORBytes(ab[:], ab[:], buf) - } -} - -// copyOut copies uint64s to a byte buffer. -func copyOut(d *state, b []byte) { - if cpu.IsBigEndian { - for i := 0; len(b) >= 8; i++ { - binary.LittleEndian.PutUint64(b, d.a[i]) - b = b[8:] - } - } else { - ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a)) - copy(b, ab[:]) - } -} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index b93961010d..b86dde151d 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -555,6 +555,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe } gotMsgExtInfo := false + gotUserAuthInfoRequest := false for { packet, err := c.readPacket() if err != nil { @@ -585,6 +586,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe if msg.PartialSuccess { return authPartialSuccess, msg.Methods, nil } + if !gotUserAuthInfoRequest { + return authFailure, msg.Methods, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) + } return authFailure, msg.Methods, nil case msgUserAuthSuccess: return authSuccess, nil, nil @@ -596,6 +600,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe if err := Unmarshal(packet, &msg); err != nil { return authFailure, nil, err } + gotUserAuthInfoRequest = true // Manually unpack the prompt/echo pairs. rest := msg.Prompts diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 3ca9e89e22..5b5ccd96f4 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -149,7 +149,7 @@ func (s *ServerConfig) AddHostKey(key Signer) { } // cachedPubKey contains the results of querying whether a public key is -// acceptable for a user. +// acceptable for a user. This is a FIFO cache. type cachedPubKey struct { user string pubKeyData []byte @@ -157,7 +157,13 @@ type cachedPubKey struct { perms *Permissions } -const maxCachedPubKeys = 16 +// maxCachedPubKeys is the number of cache entries we store. +// +// Due to consistent misuse of the PublicKeyCallback API, we have reduced this +// to 1, such that the only key in the cache is the most recently seen one. This +// forces the behavior that the last call to PublicKeyCallback will always be +// with the key that is used for authentication. +const maxCachedPubKeys = 1 // pubKeyCache caches tests for public keys. Since SSH clients // will query whether a public key is acceptable before attempting to @@ -179,9 +185,10 @@ func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) { // add adds the given tuple to the cache. func (c *pubKeyCache) add(candidate cachedPubKey) { - if len(c.keys) < maxCachedPubKeys { - c.keys = append(c.keys, candidate) + if len(c.keys) >= maxCachedPubKeys { + c.keys = c.keys[1:] } + c.keys = append(c.keys, candidate) } // ServerConn is an authenticated SSH connection, as seen from the @@ -510,8 +517,8 @@ userAuthLoop: if err := s.transport.writePacket(Marshal(discMsg)); err != nil { return nil, err } - - return nil, discMsg + authErrs = append(authErrs, discMsg) + return nil, &ServerAuthError{Errors: authErrs} } var userAuthReq userAuthRequestMsg diff --git a/vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s b/vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s new file mode 100644 index 0000000000..ec2acfe540 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/asm_darwin_x86_gc.s @@ -0,0 +1,17 @@ +// 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. + +//go:build darwin && amd64 && gc + +#include "textflag.h" + +TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctl(SB) +GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) + +TEXT libc_sysctlbyname_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctlbyname(SB) +GLOBL ·libc_sysctlbyname_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctlbyname_trampoline_addr(SB)/8, $libc_sysctlbyname_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go b/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go new file mode 100644 index 0000000000..b838cb9e95 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_darwin_x86.go @@ -0,0 +1,61 @@ +// 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. + +//go:build darwin && amd64 && gc + +package cpu + +// darwinSupportsAVX512 checks Darwin kernel for AVX512 support via sysctl +// call (see issue 43089). It also restricts AVX512 support for Darwin to +// kernel version 21.3.0 (MacOS 12.2.0) or later (see issue 49233). +// +// Background: +// Darwin implements a special mechanism to economize on thread state when +// AVX512 specific registers are not in use. This scheme minimizes state when +// preempting threads that haven't yet used any AVX512 instructions, but adds +// special requirements to check for AVX512 hardware support at runtime (e.g. +// via sysctl call or commpage inspection). See issue 43089 and link below for +// full background: +// https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.1.10/osfmk/i386/fpu.c#L214-L240 +// +// Additionally, all versions of the Darwin kernel from 19.6.0 through 21.2.0 +// (corresponding to MacOS 10.15.6 - 12.1) have a bug that can cause corruption +// of the AVX512 mask registers (K0-K7) upon signal return. For this reason +// AVX512 is considered unsafe to use on Darwin for kernel versions prior to +// 21.3.0, where a fix has been confirmed. See issue 49233 for full background. +func darwinSupportsAVX512() bool { + return darwinSysctlEnabled([]byte("hw.optional.avx512f\x00")) && darwinKernelVersionCheck(21, 3, 0) +} + +// Ensure Darwin kernel version is at least major.minor.patch, avoiding dependencies +func darwinKernelVersionCheck(major, minor, patch int) bool { + var release [256]byte + err := darwinOSRelease(&release) + if err != nil { + return false + } + + var mmp [3]int + c := 0 +Loop: + for _, b := range release[:] { + switch { + case b >= '0' && b <= '9': + mmp[c] = 10*mmp[c] + int(b-'0') + case b == '.': + c++ + if c > 2 { + return false + } + case b == 0: + break Loop + default: + return false + } + } + if c != 2 { + return false + } + return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch) +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go index 910728fb16..32a44514e2 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.go @@ -6,10 +6,10 @@ package cpu -// cpuid is implemented in cpu_x86.s for gc compiler +// cpuid is implemented in cpu_gc_x86.s for gc compiler // and in cpu_gccgo.c for gccgo. func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) -// xgetbv with ecx = 0 is implemented in cpu_x86.s for gc compiler +// xgetbv with ecx = 0 is implemented in cpu_gc_x86.s for gc compiler // and in cpu_gccgo.c for gccgo. func xgetbv() (eax, edx uint32) diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.s b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s similarity index 94% rename from vendor/golang.org/x/sys/cpu/cpu_x86.s rename to vendor/golang.org/x/sys/cpu/cpu_gc_x86.s index 7d7ba33efb..ce208ce6d6 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_x86.s +++ b/vendor/golang.org/x/sys/cpu/cpu_gc_x86.s @@ -18,7 +18,7 @@ TEXT ·cpuid(SB), NOSPLIT, $0-24 RET // func xgetbv() (eax, edx uint32) -TEXT ·xgetbv(SB),NOSPLIT,$0-8 +TEXT ·xgetbv(SB), NOSPLIT, $0-8 MOVL $0, CX XGETBV MOVL AX, eax+0(FP) diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go index 99c60fe9f9..170d21ddfd 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.go @@ -23,9 +23,3 @@ func xgetbv() (eax, edx uint32) { gccgoXgetbv(&a, &d) return a, d } - -// gccgo doesn't build on Darwin, per: -// https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/gcc.rb#L76 -func darwinSupportsAVX512() bool { - return false -} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go index 08f35ea177..f1caf0f78e 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_arm64.go @@ -110,7 +110,6 @@ func doinit() { ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM) ARM64.HasDIT = isSet(hwCap, hwcap_DIT) - // HWCAP2 feature bits ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2) ARM64.HasI8MM = isSet(hwCap2, hwcap2_I8MM) diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_x86.go b/vendor/golang.org/x/sys/cpu/cpu_other_x86.go new file mode 100644 index 0000000000..a0fd7e2f75 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_x86.go @@ -0,0 +1,11 @@ +// 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. + +//go:build 386 || amd64p32 || (amd64 && (!darwin || !gc)) + +package cpu + +func darwinSupportsAVX512() bool { + panic("only implemented for gc && amd64 && darwin") +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_x86.go b/vendor/golang.org/x/sys/cpu/cpu_x86.go index c29f5e4c5a..600a680786 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_x86.go +++ b/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -92,10 +92,8 @@ func archInit() { osSupportsAVX = isSet(1, eax) && isSet(2, eax) if runtime.GOOS == "darwin" { - // Darwin doesn't save/restore AVX-512 mask registers correctly across signal handlers. - // Since users can't rely on mask register contents, let's not advertise AVX-512 support. - // See issue 49233. - osSupportsAVX512 = false + // Darwin requires special AVX512 checks, see cpu_darwin_x86.go + osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512() } else { // Check if OPMASK and ZMM registers have OS support. osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax) diff --git a/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go b/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go new file mode 100644 index 0000000000..4d0888b0c0 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/syscall_darwin_x86_gc.go @@ -0,0 +1,98 @@ +// 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. + +// Minimal copy of x/sys/unix so the cpu package can make a +// system call on Darwin without depending on x/sys/unix. + +//go:build darwin && amd64 && gc + +package cpu + +import ( + "syscall" + "unsafe" +) + +type _C_int int32 + +// adapted from unix.Uname() at x/sys/unix/syscall_darwin.go L419 +func darwinOSRelease(release *[256]byte) error { + // from x/sys/unix/zerrors_openbsd_amd64.go + const ( + CTL_KERN = 0x1 + KERN_OSRELEASE = 0x2 + ) + + mib := []_C_int{CTL_KERN, KERN_OSRELEASE} + n := unsafe.Sizeof(*release) + + return sysctl(mib, &release[0], &n, nil, 0) +} + +type Errno = syscall.Errno + +var _zero uintptr // Single-word zero for use when we need a valid pointer to 0 bytes. + +// from x/sys/unix/zsyscall_darwin_amd64.go L791-807 +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + if _, _, err := syscall_syscall6( + libc_sysctl_trampoline_addr, + uintptr(_p0), + uintptr(len(mib)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + ); err != 0 { + return err + } + + return nil +} + +var libc_sysctl_trampoline_addr uintptr + +// adapted from internal/cpu/cpu_arm64_darwin.go +func darwinSysctlEnabled(name []byte) bool { + out := int32(0) + nout := unsafe.Sizeof(out) + if ret := sysctlbyname(&name[0], (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); ret != nil { + return false + } + return out > 0 +} + +//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib" + +var libc_sysctlbyname_trampoline_addr uintptr + +// adapted from runtime/sys_darwin.go in the pattern of sysctl() above, as defined in x/sys/unix +func sysctlbyname(name *byte, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error { + if _, _, err := syscall_syscall6( + libc_sysctlbyname_trampoline_addr, + uintptr(unsafe.Pointer(name)), + uintptr(unsafe.Pointer(old)), + uintptr(unsafe.Pointer(oldlen)), + uintptr(unsafe.Pointer(new)), + uintptr(newlen), + 0, + ); err != 0 { + return err + } + + return nil +} + +//go:cgo_import_dynamic libc_sysctlbyname sysctlbyname "/usr/lib/libSystem.B.dylib" + +// Implemented in the runtime package (runtime/sys_darwin.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 diff --git a/vendor/golang.org/x/sys/unix/README.md b/vendor/golang.org/x/sys/unix/README.md index 7d3c060e12..6e08a76a71 100644 --- a/vendor/golang.org/x/sys/unix/README.md +++ b/vendor/golang.org/x/sys/unix/README.md @@ -156,7 +156,7 @@ from the generated architecture-specific files listed below, and merge these into a common file for each OS. The merge is performed in the following steps: -1. Construct the set of common code that is idential in all architecture-specific files. +1. Construct the set of common code that is identical in all architecture-specific files. 2. Write this common code to the merged file. 3. Remove the common code from all architecture-specific files. diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go index dbe680eab8..7ca4fa12aa 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -58,6 +58,102 @@ func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) { return &value, err } +// IoctlGetEthtoolTsInfo fetches ethtool timestamping and PHC +// association for the network device specified by ifname. +func IoctlGetEthtoolTsInfo(fd int, ifname string) (*EthtoolTsInfo, error) { + ifr, err := NewIfreq(ifname) + if err != nil { + return nil, err + } + + value := EthtoolTsInfo{Cmd: ETHTOOL_GET_TS_INFO} + ifrd := ifr.withData(unsafe.Pointer(&value)) + + err = ioctlIfreqData(fd, SIOCETHTOOL, &ifrd) + return &value, err +} + +// IoctlGetHwTstamp retrieves the hardware timestamping configuration +// for the network device specified by ifname. +func IoctlGetHwTstamp(fd int, ifname string) (*HwTstampConfig, error) { + ifr, err := NewIfreq(ifname) + if err != nil { + return nil, err + } + + value := HwTstampConfig{} + ifrd := ifr.withData(unsafe.Pointer(&value)) + + err = ioctlIfreqData(fd, SIOCGHWTSTAMP, &ifrd) + return &value, err +} + +// IoctlSetHwTstamp updates the hardware timestamping configuration for +// the network device specified by ifname. +func IoctlSetHwTstamp(fd int, ifname string, cfg *HwTstampConfig) error { + ifr, err := NewIfreq(ifname) + if err != nil { + return err + } + ifrd := ifr.withData(unsafe.Pointer(cfg)) + return ioctlIfreqData(fd, SIOCSHWTSTAMP, &ifrd) +} + +// FdToClockID derives the clock ID from the file descriptor number +// - see clock_gettime(3), FD_TO_CLOCKID macros. The resulting ID is +// suitable for system calls like ClockGettime. +func FdToClockID(fd int) int32 { return int32((int(^fd) << 3) | 3) } + +// IoctlPtpClockGetcaps returns the description of a given PTP device. +func IoctlPtpClockGetcaps(fd int) (*PtpClockCaps, error) { + var value PtpClockCaps + err := ioctlPtr(fd, PTP_CLOCK_GETCAPS2, unsafe.Pointer(&value)) + return &value, err +} + +// IoctlPtpSysOffsetPrecise returns a description of the clock +// offset compared to the system clock. +func IoctlPtpSysOffsetPrecise(fd int) (*PtpSysOffsetPrecise, error) { + var value PtpSysOffsetPrecise + err := ioctlPtr(fd, PTP_SYS_OFFSET_PRECISE2, unsafe.Pointer(&value)) + return &value, err +} + +// IoctlPtpSysOffsetExtended returns an extended description of the +// clock offset compared to the system clock. The samples parameter +// specifies the desired number of measurements. +func IoctlPtpSysOffsetExtended(fd int, samples uint) (*PtpSysOffsetExtended, error) { + value := PtpSysOffsetExtended{Samples: uint32(samples)} + err := ioctlPtr(fd, PTP_SYS_OFFSET_EXTENDED2, unsafe.Pointer(&value)) + return &value, err +} + +// IoctlPtpPinGetfunc returns the configuration of the specified +// I/O pin on given PTP device. +func IoctlPtpPinGetfunc(fd int, index uint) (*PtpPinDesc, error) { + value := PtpPinDesc{Index: uint32(index)} + err := ioctlPtr(fd, PTP_PIN_GETFUNC2, unsafe.Pointer(&value)) + return &value, err +} + +// IoctlPtpPinSetfunc updates configuration of the specified PTP +// I/O pin. +func IoctlPtpPinSetfunc(fd int, pd *PtpPinDesc) error { + return ioctlPtr(fd, PTP_PIN_SETFUNC2, unsafe.Pointer(pd)) +} + +// IoctlPtpPeroutRequest configures the periodic output mode of the +// PTP I/O pins. +func IoctlPtpPeroutRequest(fd int, r *PtpPeroutRequest) error { + return ioctlPtr(fd, PTP_PEROUT_REQUEST2, unsafe.Pointer(r)) +} + +// IoctlPtpExttsRequest configures the external timestamping mode +// of the PTP I/O pins. +func IoctlPtpExttsRequest(fd int, r *PtpExttsRequest) error { + return ioctlPtr(fd, PTP_EXTTS_REQUEST2, unsafe.Pointer(r)) +} + // IoctlGetWatchdogInfo fetches information about a watchdog device from the // Linux watchdog API. For more information, see: // https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index e14b766a32..6ab02b6c31 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -158,6 +158,16 @@ includes_Linux=' #endif #define _GNU_SOURCE +// See the description in unix/linux/types.go +#if defined(__ARM_EABI__) || \ + (defined(__mips__) && (_MIPS_SIM == _ABIO32)) || \ + (defined(__powerpc__) && (!defined(__powerpc64__))) +# ifdef _TIME_BITS +# undef _TIME_BITS +# endif +# define _TIME_BITS 32 +#endif + // is broken on powerpc64, as it fails to include definitions of // these structures. We just include them copied from . #if defined(__powerpc__) @@ -256,6 +266,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -527,6 +538,7 @@ ccflags="$@" $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ || + $2 ~ /^PTP_/ || $2 ~ /^RAW_PAYLOAD_/ || $2 ~ /^[US]F_/ || $2 ~ /^TP_STATUS_/ || @@ -656,7 +668,7 @@ errors=$( signals=$( echo '#include ' | $CC -x c - -E -dM $ccflags | awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' | - grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' | + grep -E -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' | sort ) @@ -666,7 +678,7 @@ echo '#include ' | $CC -x c - -E -dM $ccflags | sort >_error.grep echo '#include ' | $CC -x c - -E -dM $ccflags | awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' | - grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' | + grep -E -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' | sort >_signal.grep echo '// mkerrors.sh' "$@" diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index 67ce6cef2d..6f15ba1eaf 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -360,7 +360,7 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, var status _C_int var r Pid_t err = ERESTART - // AIX wait4 may return with ERESTART errno, while the processus is still + // AIX wait4 may return with ERESTART errno, while the process is still // active. for err == ERESTART { r, err = wait4(Pid_t(pid), &status, options, rusage) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 3f1d3d4cb2..230a94549a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1295,6 +1295,48 @@ func GetsockoptTCPInfo(fd, level, opt int) (*TCPInfo, error) { return &value, err } +// GetsockoptTCPCCVegasInfo returns algorithm specific congestion control information for a socket using the "vegas" +// algorithm. +// +// The socket's congestion control algorighm can be retrieved via [GetsockoptString] with the [TCP_CONGESTION] option: +// +// algo, err := unix.GetsockoptString(fd, unix.IPPROTO_TCP, unix.TCP_CONGESTION) +func GetsockoptTCPCCVegasInfo(fd, level, opt int) (*TCPVegasInfo, error) { + var value [SizeofTCPCCInfo / 4]uint32 // ensure proper alignment + vallen := _Socklen(SizeofTCPCCInfo) + err := getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen) + out := (*TCPVegasInfo)(unsafe.Pointer(&value[0])) + return out, err +} + +// GetsockoptTCPCCDCTCPInfo returns algorithm specific congestion control information for a socket using the "dctp" +// algorithm. +// +// The socket's congestion control algorighm can be retrieved via [GetsockoptString] with the [TCP_CONGESTION] option: +// +// algo, err := unix.GetsockoptString(fd, unix.IPPROTO_TCP, unix.TCP_CONGESTION) +func GetsockoptTCPCCDCTCPInfo(fd, level, opt int) (*TCPDCTCPInfo, error) { + var value [SizeofTCPCCInfo / 4]uint32 // ensure proper alignment + vallen := _Socklen(SizeofTCPCCInfo) + err := getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen) + out := (*TCPDCTCPInfo)(unsafe.Pointer(&value[0])) + return out, err +} + +// GetsockoptTCPCCBBRInfo returns algorithm specific congestion control information for a socket using the "bbr" +// algorithm. +// +// The socket's congestion control algorighm can be retrieved via [GetsockoptString] with the [TCP_CONGESTION] option: +// +// algo, err := unix.GetsockoptString(fd, unix.IPPROTO_TCP, unix.TCP_CONGESTION) +func GetsockoptTCPCCBBRInfo(fd, level, opt int) (*TCPBBRInfo, error) { + var value [SizeofTCPCCInfo / 4]uint32 // ensure proper alignment + vallen := _Socklen(SizeofTCPCCInfo) + err := getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen) + out := (*TCPBBRInfo)(unsafe.Pointer(&value[0])) + return out, err +} + // GetsockoptString returns the string value of the socket option opt for the // socket associated with fd at the given socket level. func GetsockoptString(fd, level, opt int) (string, error) { @@ -1818,6 +1860,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys ClockAdjtime(clockid int32, buf *Timex) (state int, err error) //sys ClockGetres(clockid int32, res *Timespec) (err error) //sys ClockGettime(clockid int32, time *Timespec) (err error) +//sys ClockSettime(clockid int32, time *Timespec) (err error) //sys ClockNanosleep(clockid int32, flags int, request *Timespec, remain *Timespec) (err error) //sys Close(fd int) (err error) //sys CloseRange(first uint, last uint, flags uint) (err error) @@ -1959,7 +2002,26 @@ func Getpgrp() (pid int) { //sysnb Getpid() (pid int) //sysnb Getppid() (ppid int) //sys Getpriority(which int, who int) (prio int, err error) -//sys Getrandom(buf []byte, flags int) (n int, err error) + +func Getrandom(buf []byte, flags int) (n int, err error) { + vdsoRet, supported := vgetrandom(buf, uint32(flags)) + if supported { + if vdsoRet < 0 { + return 0, errnoErr(syscall.Errno(-vdsoRet)) + } + return vdsoRet, nil + } + var p *byte + if len(buf) > 0 { + p = &buf[0] + } + r, _, e := Syscall(SYS_GETRANDOM, uintptr(unsafe.Pointer(p)), uintptr(len(buf)), uintptr(flags)) + if e != 0 { + return 0, errnoErr(e) + } + return int(r), nil +} + //sysnb Getrusage(who int, rusage *Rusage) (err error) //sysnb Getsid(pid int) (sid int, err error) //sysnb Gettid() (tid int) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index cf2ee6c75e..745e5c7e6c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -182,3 +182,5 @@ func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error } return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags) } + +const SYS_FSTATAT = SYS_NEWFSTATAT diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go index 3d0e98451f..dd2262a407 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -214,3 +214,5 @@ func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error } return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags) } + +const SYS_FSTATAT = SYS_NEWFSTATAT diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index 6f5a288944..8cf3670bda 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -187,3 +187,5 @@ func RISCVHWProbe(pairs []RISCVHWProbePairs, set *CPUSet, flags uint) (err error } return riscvHWProbe(pairs, setSize, set, flags) } + +const SYS_FSTATAT = SYS_NEWFSTATAT diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go index 312ae6ac1d..7bf5c04bb0 100644 --- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go @@ -768,6 +768,15 @@ func Munmap(b []byte) (err error) { return mapper.Munmap(b) } +func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mmap(uintptr(addr), length, prot, flags, fd, offset) + return unsafe.Pointer(xaddr), err +} + +func MunmapPtr(addr unsafe.Pointer, length uintptr) (err error) { + return mapper.munmap(uintptr(addr), length) +} + //sys Gethostname(buf []byte) (err error) = SYS___GETHOSTNAME_A //sysnb Getgid() (gid int) //sysnb Getpid() (pid int) @@ -816,10 +825,10 @@ func Lstat(path string, stat *Stat_t) (err error) { // for checking symlinks begins with $VERSION/ $SYSNAME/ $SYSSYMR/ $SYSSYMA/ func isSpecialPath(path []byte) (v bool) { var special = [4][8]byte{ - [8]byte{'V', 'E', 'R', 'S', 'I', 'O', 'N', '/'}, - [8]byte{'S', 'Y', 'S', 'N', 'A', 'M', 'E', '/'}, - [8]byte{'S', 'Y', 'S', 'S', 'Y', 'M', 'R', '/'}, - [8]byte{'S', 'Y', 'S', 'S', 'Y', 'M', 'A', '/'}} + {'V', 'E', 'R', 'S', 'I', 'O', 'N', '/'}, + {'S', 'Y', 'S', 'N', 'A', 'M', 'E', '/'}, + {'S', 'Y', 'S', 'S', 'Y', 'M', 'R', '/'}, + {'S', 'Y', 'S', 'S', 'Y', 'M', 'A', '/'}} var i, j int for i = 0; i < len(special); i++ { @@ -3115,3 +3124,90 @@ func legacy_Mkfifoat(dirfd int, path string, mode uint32) (err error) { //sys Posix_openpt(oflag int) (fd int, err error) = SYS_POSIX_OPENPT //sys Grantpt(fildes int) (rc int, err error) = SYS_GRANTPT //sys Unlockpt(fildes int) (rc int, err error) = SYS_UNLOCKPT + +func fcntlAsIs(fd uintptr, cmd int, arg uintptr) (val int, err error) { + runtime.EnterSyscall() + r0, e2, e1 := CallLeFuncWithErr(GetZosLibVec()+SYS_FCNTL<<4, uintptr(fd), uintptr(cmd), arg) + runtime.ExitSyscall() + val = int(r0) + if int64(r0) == -1 { + err = errnoErr2(e1, e2) + } + return +} + +func Fcntl(fd uintptr, cmd int, op interface{}) (ret int, err error) { + switch op.(type) { + case *Flock_t: + err = FcntlFlock(fd, cmd, op.(*Flock_t)) + if err != nil { + ret = -1 + } + return + case int: + return FcntlInt(fd, cmd, op.(int)) + case *F_cnvrt: + return fcntlAsIs(fd, cmd, uintptr(unsafe.Pointer(op.(*F_cnvrt)))) + case unsafe.Pointer: + return fcntlAsIs(fd, cmd, uintptr(op.(unsafe.Pointer))) + default: + return -1, EINVAL + } + return +} + +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + return sendfile(outfd, infd, offset, count) +} + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + // TODO: use LE call instead if the call is implemented + originalOffset, err := Seek(infd, 0, SEEK_CUR) + if err != nil { + return -1, err + } + //start reading data from in_fd + if offset != nil { + _, err := Seek(infd, *offset, SEEK_SET) + if err != nil { + return -1, err + } + } + + buf := make([]byte, count) + readBuf := make([]byte, 0) + var n int = 0 + for i := 0; i < count; i += n { + n, err := Read(infd, buf) + if n == 0 { + if err != nil { + return -1, err + } else { // EOF + break + } + } + readBuf = append(readBuf, buf...) + buf = buf[0:0] + } + + n2, err := Write(outfd, readBuf) + if err != nil { + return -1, err + } + + //When sendfile() returns, this variable will be set to the + // offset of the byte following the last byte that was read. + if offset != nil { + *offset = *offset + int64(n) + // If offset is not NULL, then sendfile() does not modify the file + // offset of in_fd + _, err := Seek(infd, originalOffset, SEEK_SET) + if err != nil { + return -1, err + } + } + return n2, nil +} diff --git a/vendor/golang.org/x/sys/unix/vgetrandom_linux.go b/vendor/golang.org/x/sys/unix/vgetrandom_linux.go new file mode 100644 index 0000000000..07ac8e09d1 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/vgetrandom_linux.go @@ -0,0 +1,13 @@ +// 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. + +//go:build linux && go1.24 + +package unix + +import _ "unsafe" + +//go:linkname vgetrandom runtime.vgetrandom +//go:noescape +func vgetrandom(p []byte, flags uint32) (ret int, supported bool) diff --git a/vendor/golang.org/x/sys/unix/vgetrandom_unsupported.go b/vendor/golang.org/x/sys/unix/vgetrandom_unsupported.go new file mode 100644 index 0000000000..297e97bce9 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/vgetrandom_unsupported.go @@ -0,0 +1,11 @@ +// 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. + +//go:build !linux || !go1.24 + +package unix + +func vgetrandom(p []byte, flags uint32) (ret int, supported bool) { + return -1, false +} diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 01a70b2463..6ebc48b3fe 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -321,6 +321,9 @@ const ( AUDIT_INTEGRITY_STATUS = 0x70a AUDIT_IPC = 0x517 AUDIT_IPC_SET_PERM = 0x51f + AUDIT_IPE_ACCESS = 0x58c + AUDIT_IPE_CONFIG_CHANGE = 0x58d + AUDIT_IPE_POLICY_LOAD = 0x58e AUDIT_KERNEL = 0x7d0 AUDIT_KERNEL_OTHER = 0x524 AUDIT_KERN_MODULE = 0x532 @@ -489,12 +492,14 @@ const ( BPF_F_ID = 0x20 BPF_F_NETFILTER_IP_DEFRAG = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 + BPF_F_REDIRECT_FLAGS = 0x19 BPF_F_REPLACE = 0x4 BPF_F_SLEEPABLE = 0x10 BPF_F_STRICT_ALIGNMENT = 0x1 BPF_F_TEST_REG_INVARIANTS = 0x80 BPF_F_TEST_RND_HI32 = 0x4 BPF_F_TEST_RUN_ON_CPU = 0x1 + BPF_F_TEST_SKB_CHECKSUM_COMPLETE = 0x4 BPF_F_TEST_STATE_FREQ = 0x8 BPF_F_TEST_XDP_LIVE_FRAMES = 0x2 BPF_F_XDP_DEV_BOUND_ONLY = 0x40 @@ -1165,6 +1170,7 @@ const ( EXTA = 0xe EXTB = 0xf F2FS_SUPER_MAGIC = 0xf2f52010 + FALLOC_FL_ALLOCATE_RANGE = 0x0 FALLOC_FL_COLLAPSE_RANGE = 0x8 FALLOC_FL_INSERT_RANGE = 0x20 FALLOC_FL_KEEP_SIZE = 0x1 @@ -1798,6 +1804,8 @@ const ( LANDLOCK_ACCESS_NET_BIND_TCP = 0x1 LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2 LANDLOCK_CREATE_RULESET_VERSION = 0x1 + LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET = 0x1 + LANDLOCK_SCOPE_SIGNAL = 0x2 LINUX_REBOOT_CMD_CAD_OFF = 0x0 LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef LINUX_REBOOT_CMD_HALT = 0xcdef0123 @@ -1922,6 +1930,8 @@ const ( MNT_EXPIRE = 0x4 MNT_FORCE = 0x1 MNT_ID_REQ_SIZE_VER0 = 0x18 + MNT_ID_REQ_SIZE_VER1 = 0x20 + MNT_NS_INFO_SIZE_VER0 = 0x10 MODULE_INIT_COMPRESSED_FILE = 0x4 MODULE_INIT_IGNORE_MODVERSIONS = 0x1 MODULE_INIT_IGNORE_VERMAGIC = 0x2 @@ -2187,7 +2197,7 @@ const ( NFT_REG_SIZE = 0x10 NFT_REJECT_ICMPX_MAX = 0x3 NFT_RT_MAX = 0x4 - NFT_SECMARK_CTX_MAXLEN = 0x100 + NFT_SECMARK_CTX_MAXLEN = 0x1000 NFT_SET_MAXNAMELEN = 0x100 NFT_SOCKET_MAX = 0x3 NFT_TABLE_F_MASK = 0x7 @@ -2356,9 +2366,11 @@ const ( PERF_MEM_LVLNUM_IO = 0xa PERF_MEM_LVLNUM_L1 = 0x1 PERF_MEM_LVLNUM_L2 = 0x2 + PERF_MEM_LVLNUM_L2_MHB = 0x5 PERF_MEM_LVLNUM_L3 = 0x3 PERF_MEM_LVLNUM_L4 = 0x4 PERF_MEM_LVLNUM_LFB = 0xc + PERF_MEM_LVLNUM_MSC = 0x6 PERF_MEM_LVLNUM_NA = 0xf PERF_MEM_LVLNUM_PMEM = 0xe PERF_MEM_LVLNUM_RAM = 0xd @@ -2431,6 +2443,7 @@ const ( PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 + PROCFS_IOCTL_MAGIC = 'f' PROC_SUPER_MAGIC = 0x9fa0 PROT_EXEC = 0x4 PROT_GROWSDOWN = 0x1000000 @@ -2620,6 +2633,28 @@ const ( PR_UNALIGN_NOPRINT = 0x1 PR_UNALIGN_SIGBUS = 0x2 PSTOREFS_MAGIC = 0x6165676c + PTP_CLK_MAGIC = '=' + PTP_ENABLE_FEATURE = 0x1 + PTP_EXTTS_EDGES = 0x6 + PTP_EXTTS_EVENT_VALID = 0x1 + PTP_EXTTS_V1_VALID_FLAGS = 0x7 + PTP_EXTTS_VALID_FLAGS = 0x1f + PTP_EXT_OFFSET = 0x10 + PTP_FALLING_EDGE = 0x4 + PTP_MAX_SAMPLES = 0x19 + PTP_PEROUT_DUTY_CYCLE = 0x2 + PTP_PEROUT_ONE_SHOT = 0x1 + PTP_PEROUT_PHASE = 0x4 + PTP_PEROUT_V1_VALID_FLAGS = 0x0 + PTP_PEROUT_VALID_FLAGS = 0x7 + PTP_PIN_GETFUNC = 0xc0603d06 + PTP_PIN_GETFUNC2 = 0xc0603d0f + PTP_RISING_EDGE = 0x2 + PTP_STRICT_FLAGS = 0x8 + PTP_SYS_OFFSET_EXTENDED = 0xc4c03d09 + PTP_SYS_OFFSET_EXTENDED2 = 0xc4c03d12 + PTP_SYS_OFFSET_PRECISE = 0xc0403d08 + PTP_SYS_OFFSET_PRECISE2 = 0xc0403d11 PTRACE_ATTACH = 0x10 PTRACE_CONT = 0x7 PTRACE_DETACH = 0x11 @@ -2933,15 +2968,17 @@ const ( RUSAGE_SELF = 0x0 RUSAGE_THREAD = 0x1 RWF_APPEND = 0x10 + RWF_ATOMIC = 0x40 RWF_DSYNC = 0x2 RWF_HIPRI = 0x1 RWF_NOAPPEND = 0x20 RWF_NOWAIT = 0x8 - RWF_SUPPORTED = 0x3f + RWF_SUPPORTED = 0x7f RWF_SYNC = 0x4 RWF_WRITE_LIFE_NOT_SET = 0x0 SCHED_BATCH = 0x3 SCHED_DEADLINE = 0x6 + SCHED_EXT = 0x7 SCHED_FIFO = 0x1 SCHED_FLAG_ALL = 0x7f SCHED_FLAG_DL_OVERRUN = 0x4 @@ -3210,6 +3247,7 @@ const ( STATX_ATTR_MOUNT_ROOT = 0x2000 STATX_ATTR_NODUMP = 0x40 STATX_ATTR_VERITY = 0x100000 + STATX_ATTR_WRITE_ATOMIC = 0x400000 STATX_BASIC_STATS = 0x7ff STATX_BLOCKS = 0x400 STATX_BTIME = 0x800 @@ -3226,6 +3264,7 @@ const ( STATX_SUBVOL = 0x8000 STATX_TYPE = 0x1 STATX_UID = 0x8 + STATX_WRITE_ATOMIC = 0x10000 STATX__RESERVED = 0x80000000 SYNC_FILE_RANGE_WAIT_AFTER = 0x4 SYNC_FILE_RANGE_WAIT_BEFORE = 0x1 @@ -3624,6 +3663,7 @@ const ( XDP_UMEM_PGOFF_COMPLETION_RING = 0x180000000 XDP_UMEM_PGOFF_FILL_RING = 0x100000000 XDP_UMEM_REG = 0x4 + XDP_UMEM_TX_METADATA_LEN = 0x4 XDP_UMEM_TX_SW_CSUM = 0x2 XDP_UMEM_UNALIGNED_CHUNK_FLAG = 0x1 XDP_USE_NEED_WAKEUP = 0x8 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 684a5168da..c0d45e3205 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -109,6 +109,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -153,9 +154,14 @@ const ( NFDBITS = 0x20 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -232,6 +238,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_GETFPREGS = 0xe PTRACE_GETFPXREGS = 0x12 PTRACE_GET_THREAD_AREA = 0x19 @@ -278,6 +298,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -316,6 +338,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 61d74b592d..c731d24f02 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -109,6 +109,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -153,9 +154,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -232,6 +238,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_ARCH_PRCTL = 0x1e PTRACE_GETFPREGS = 0xe PTRACE_GETFPXREGS = 0x12 @@ -279,6 +299,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -317,6 +339,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index a28c9e3e89..680018a4a7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x20 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_GETCRUNCHREGS = 0x19 PTRACE_GETFDPIC = 0x1f PTRACE_GETFDPIC_EXEC = 0x0 @@ -284,6 +304,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -322,6 +344,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index ab5d1fe8ea..a63909f308 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -112,6 +112,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -154,9 +155,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -200,6 +206,7 @@ const ( PERF_EVENT_IOC_SET_BPF = 0x40042408 PERF_EVENT_IOC_SET_FILTER = 0x40082406 PERF_EVENT_IOC_SET_OUTPUT = 0x2405 + POE_MAGIC = 0x504f4530 PPPIOCATTACH = 0x4004743d PPPIOCATTCHAN = 0x40047438 PPPIOCBRIDGECHAN = 0x40047435 @@ -235,6 +242,20 @@ const ( PROT_BTI = 0x10 PROT_MTE = 0x20 PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_PEEKMTETAGS = 0x21 PTRACE_POKEMTETAGS = 0x22 PTRACE_SYSEMU = 0x1f @@ -275,6 +296,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -313,6 +336,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index c523090e7c..9b0a2573fe 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -109,6 +109,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -154,9 +155,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -233,6 +239,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_SYSEMU = 0x1f PTRACE_SYSEMU_SINGLESTEP = 0x20 RLIMIT_AS = 0x9 @@ -271,6 +291,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -309,6 +331,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 01e6ea7804..958e6e0645 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x100 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x20 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x20007434 PPPIOCXFERUNIT = 0x2000744e PR_SET_PTRACER_ANY = 0xffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETFPREGS = 0xe PTRACE_GET_THREAD_AREA = 0x19 PTRACE_GET_THREAD_AREA_3264 = 0xc4 @@ -277,6 +297,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -315,6 +337,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x1029 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 7aa610b1e7..50c7f25bd1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x100 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x20007434 PPPIOCXFERUNIT = 0x2000744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETFPREGS = 0xe PTRACE_GET_THREAD_AREA = 0x19 PTRACE_GET_THREAD_AREA_3264 = 0xc4 @@ -277,6 +297,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -315,6 +337,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x1029 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 92af771b44..ced21d66d9 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x100 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x20007434 PPPIOCXFERUNIT = 0x2000744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETFPREGS = 0xe PTRACE_GET_THREAD_AREA = 0x19 PTRACE_GET_THREAD_AREA_3264 = 0xc4 @@ -277,6 +297,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -315,6 +337,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x1029 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index b27ef5e6f1..226c044190 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x100 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x20 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x20007434 PPPIOCXFERUNIT = 0x2000744e PR_SET_PTRACER_ANY = 0xffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETFPREGS = 0xe PTRACE_GET_THREAD_AREA = 0x19 PTRACE_GET_THREAD_AREA_3264 = 0xc4 @@ -277,6 +297,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -315,6 +337,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x1029 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 237a2cefb3..3122737cd4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x4000 ICANON = 0x100 IEXTEN = 0x400 @@ -152,9 +153,14 @@ const ( NL3 = 0x300 NLDLY = 0x300 NOFLSH = 0x80000000 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x4 ONLCR = 0x2 @@ -232,6 +238,20 @@ const ( PPPIOCXFERUNIT = 0x2000744e PROT_SAO = 0x10 PR_SET_PTRACER_ANY = 0xffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETEVRREGS = 0x14 PTRACE_GETFPREGS = 0xe PTRACE_GETREGS64 = 0x16 @@ -332,6 +352,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -370,6 +392,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 4a5c555a36..eb5d3467ed 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x4000 ICANON = 0x100 IEXTEN = 0x400 @@ -152,9 +153,14 @@ const ( NL3 = 0x300 NLDLY = 0x300 NOFLSH = 0x80000000 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x4 ONLCR = 0x2 @@ -232,6 +238,20 @@ const ( PPPIOCXFERUNIT = 0x2000744e PROT_SAO = 0x10 PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETEVRREGS = 0x14 PTRACE_GETFPREGS = 0xe PTRACE_GETREGS64 = 0x16 @@ -336,6 +356,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -374,6 +396,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index a02fb49a5f..e921ebc60b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x4000 ICANON = 0x100 IEXTEN = 0x400 @@ -152,9 +153,14 @@ const ( NL3 = 0x300 NLDLY = 0x300 NOFLSH = 0x80000000 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x4 ONLCR = 0x2 @@ -232,6 +238,20 @@ const ( PPPIOCXFERUNIT = 0x2000744e PROT_SAO = 0x10 PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETEVRREGS = 0x14 PTRACE_GETFPREGS = 0xe PTRACE_GETREGS64 = 0x16 @@ -336,6 +356,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -374,6 +396,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index e26a7c61b2..38ba81c55c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_GETFDPIC = 0x21 PTRACE_GETFDPIC_EXEC = 0x0 PTRACE_GETFDPIC_INTERP = 0x1 @@ -268,6 +288,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -306,6 +328,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index c48f7c2103..71f0400977 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -108,6 +108,7 @@ const ( HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 + HIDIOCREVOKE = 0x4004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -150,9 +151,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x8008b705 NS_GET_NSTYPE = 0xb703 NS_GET_OWNER_UID = 0xb704 NS_GET_PARENT = 0xb702 + NS_GET_PID_FROM_PIDNS = 0x8004b706 + NS_GET_PID_IN_PIDNS = 0x8004b708 + NS_GET_TGID_FROM_PIDNS = 0x8004b707 + NS_GET_TGID_IN_PIDNS = 0x8004b709 NS_GET_USERNS = 0xb701 OLCUC = 0x2 ONLCR = 0x4 @@ -229,6 +235,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x80503d01 + PTP_CLOCK_GETCAPS2 = 0x80503d0a + PTP_ENABLE_PPS = 0x40043d04 + PTP_ENABLE_PPS2 = 0x40043d0d + PTP_EXTTS_REQUEST = 0x40103d02 + PTP_EXTTS_REQUEST2 = 0x40103d0b + PTP_MASK_CLEAR_ALL = 0x3d13 + PTP_MASK_EN_SINGLE = 0x40043d14 + PTP_PEROUT_REQUEST = 0x40383d03 + PTP_PEROUT_REQUEST2 = 0x40383d0c + PTP_PIN_SETFUNC = 0x40603d07 + PTP_PIN_SETFUNC2 = 0x40603d10 + PTP_SYS_OFFSET = 0x43403d05 + PTP_SYS_OFFSET2 = 0x43403d0e PTRACE_DISABLE_TE = 0x5010 PTRACE_ENABLE_TE = 0x5009 PTRACE_GET_LAST_BREAK = 0x5006 @@ -340,6 +360,8 @@ const ( RTC_WIE_ON = 0x700f RTC_WKALM_RD = 0x80287010 RTC_WKALM_SET = 0x4028700f + SCM_DEVMEM_DMABUF = 0x4f + SCM_DEVMEM_LINEAR = 0x4e SCM_TIMESTAMPING = 0x25 SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a @@ -378,6 +400,9 @@ const ( SO_CNX_ADVICE = 0x35 SO_COOKIE = 0x39 SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DEVMEM_DMABUF = 0x4f + SO_DEVMEM_DONTNEED = 0x50 + SO_DEVMEM_LINEAR = 0x4e SO_DOMAIN = 0x27 SO_DONTROUTE = 0x5 SO_ERROR = 0x4 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index ad4b9aace7..c44a313322 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -112,6 +112,7 @@ const ( HIDIOCGRAWINFO = 0x40084803 HIDIOCGRDESC = 0x50044802 HIDIOCGRDESCSIZE = 0x40044801 + HIDIOCREVOKE = 0x8004480d HUPCL = 0x400 ICANON = 0x2 IEXTEN = 0x8000 @@ -155,9 +156,14 @@ const ( NFDBITS = 0x40 NLDLY = 0x100 NOFLSH = 0x80 + NS_GET_MNTNS_ID = 0x4008b705 NS_GET_NSTYPE = 0x2000b703 NS_GET_OWNER_UID = 0x2000b704 NS_GET_PARENT = 0x2000b702 + NS_GET_PID_FROM_PIDNS = 0x4004b706 + NS_GET_PID_IN_PIDNS = 0x4004b708 + NS_GET_TGID_FROM_PIDNS = 0x4004b707 + NS_GET_TGID_IN_PIDNS = 0x4004b709 NS_GET_USERNS = 0x2000b701 OLCUC = 0x2 ONLCR = 0x4 @@ -234,6 +240,20 @@ const ( PPPIOCUNBRIDGECHAN = 0x20007434 PPPIOCXFERUNIT = 0x2000744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTP_CLOCK_GETCAPS = 0x40503d01 + PTP_CLOCK_GETCAPS2 = 0x40503d0a + PTP_ENABLE_PPS = 0x80043d04 + PTP_ENABLE_PPS2 = 0x80043d0d + PTP_EXTTS_REQUEST = 0x80103d02 + PTP_EXTTS_REQUEST2 = 0x80103d0b + PTP_MASK_CLEAR_ALL = 0x20003d13 + PTP_MASK_EN_SINGLE = 0x80043d14 + PTP_PEROUT_REQUEST = 0x80383d03 + PTP_PEROUT_REQUEST2 = 0x80383d0c + PTP_PIN_SETFUNC = 0x80603d07 + PTP_PIN_SETFUNC2 = 0x80603d10 + PTP_SYS_OFFSET = 0x83403d05 + PTP_SYS_OFFSET2 = 0x83403d0e PTRACE_GETFPAREGS = 0x14 PTRACE_GETFPREGS = 0xe PTRACE_GETFPREGS64 = 0x19 @@ -331,6 +351,8 @@ const ( RTC_WIE_ON = 0x2000700f RTC_WKALM_RD = 0x40287010 RTC_WKALM_SET = 0x8028700f + SCM_DEVMEM_DMABUF = 0x58 + SCM_DEVMEM_LINEAR = 0x57 SCM_TIMESTAMPING = 0x23 SCM_TIMESTAMPING_OPT_STATS = 0x38 SCM_TIMESTAMPING_PKTINFO = 0x3c @@ -417,6 +439,9 @@ const ( SO_CNX_ADVICE = 0x37 SO_COOKIE = 0x3b SO_DETACH_REUSEPORT_BPF = 0x47 + SO_DEVMEM_DMABUF = 0x58 + SO_DEVMEM_DONTNEED = 0x59 + SO_DEVMEM_LINEAR = 0x57 SO_DOMAIN = 0x1029 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 1bc1a5adb2..5cc1e8eb2f 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -592,6 +592,16 @@ func ClockGettime(clockid int32, time *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ClockSettime(clockid int32, time *Timespec) (err error) { + _, _, e1 := Syscall(SYS_CLOCK_SETTIME, uintptr(clockid), uintptr(unsafe.Pointer(time)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ClockNanosleep(clockid int32, flags int, request *Timespec, remain *Timespec) (err error) { _, _, e1 := Syscall6(SYS_CLOCK_NANOSLEEP, uintptr(clockid), uintptr(flags), uintptr(unsafe.Pointer(request)), uintptr(unsafe.Pointer(remain)), 0, 0) if e1 != 0 { @@ -971,23 +981,6 @@ func Getpriority(which int, who int) (prio int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Getrandom(buf []byte, flags int) (n int, err error) { - var _p0 unsafe.Pointer - if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall(SYS_GETRANDOM, uintptr(_p0), uintptr(len(buf)), uintptr(flags)) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getrusage(who int, rusage *Rusage) (err error) { _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index d3e38f681a..f485dbf456 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -341,6 +341,7 @@ const ( SYS_STATX = 332 SYS_IO_PGETEVENTS = 333 SYS_RSEQ = 334 + SYS_URETPROBE = 335 SYS_PIDFD_SEND_SIGNAL = 424 SYS_IO_URING_SETUP = 425 SYS_IO_URING_ENTER = 426 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 6c778c2327..1893e2fe88 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -85,7 +85,7 @@ const ( SYS_SPLICE = 76 SYS_TEE = 77 SYS_READLINKAT = 78 - SYS_FSTATAT = 79 + SYS_NEWFSTATAT = 79 SYS_FSTAT = 80 SYS_SYNC = 81 SYS_FSYNC = 82 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index 37281cf51a..16a4017da0 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -84,6 +84,8 @@ const ( SYS_SPLICE = 76 SYS_TEE = 77 SYS_READLINKAT = 78 + SYS_NEWFSTATAT = 79 + SYS_FSTAT = 80 SYS_SYNC = 81 SYS_FSYNC = 82 SYS_FDATASYNC = 83 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 9889f6a559..a5459e766f 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -84,7 +84,7 @@ const ( SYS_SPLICE = 76 SYS_TEE = 77 SYS_READLINKAT = 78 - SYS_FSTATAT = 79 + SYS_NEWFSTATAT = 79 SYS_FSTAT = 80 SYS_SYNC = 81 SYS_FSYNC = 82 diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index d003c3d437..17c53bd9b3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -462,11 +462,14 @@ type FdSet struct { const ( SizeofIfMsghdr = 0x70 + SizeofIfMsghdr2 = 0xa0 SizeofIfData = 0x60 + SizeofIfData64 = 0x80 SizeofIfaMsghdr = 0x14 SizeofIfmaMsghdr = 0x10 SizeofIfmaMsghdr2 = 0x14 SizeofRtMsghdr = 0x5c + SizeofRtMsghdr2 = 0x5c SizeofRtMetrics = 0x38 ) @@ -480,6 +483,20 @@ type IfMsghdr struct { Data IfData } +type IfMsghdr2 struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Snd_len int32 + Snd_maxlen int32 + Snd_drops int32 + Timer int32 + Data IfData64 +} + type IfData struct { Type uint8 Typelen uint8 @@ -512,6 +529,34 @@ type IfData struct { Reserved2 uint32 } +type IfData64 struct { + Type uint8 + Typelen uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Recvquota uint8 + Xmitquota uint8 + Unused1 uint8 + Mtu uint32 + Metric uint32 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Recvtiming uint32 + Xmittiming uint32 + Lastchange Timeval32 +} + type IfaMsghdr struct { Msglen uint16 Version uint8 @@ -557,6 +602,21 @@ type RtMsghdr struct { Rmx RtMetrics } +type RtMsghdr2 struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Flags int32 + Addrs int32 + Refcnt int32 + Parentflags int32 + Reserved int32 + Use int32 + Inits uint32 + Rmx RtMetrics +} + type RtMetrics struct { Locks uint32 Mtu uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index 0d45a941aa..2392226a74 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -462,11 +462,14 @@ type FdSet struct { const ( SizeofIfMsghdr = 0x70 + SizeofIfMsghdr2 = 0xa0 SizeofIfData = 0x60 + SizeofIfData64 = 0x80 SizeofIfaMsghdr = 0x14 SizeofIfmaMsghdr = 0x10 SizeofIfmaMsghdr2 = 0x14 SizeofRtMsghdr = 0x5c + SizeofRtMsghdr2 = 0x5c SizeofRtMetrics = 0x38 ) @@ -480,6 +483,20 @@ type IfMsghdr struct { Data IfData } +type IfMsghdr2 struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Snd_len int32 + Snd_maxlen int32 + Snd_drops int32 + Timer int32 + Data IfData64 +} + type IfData struct { Type uint8 Typelen uint8 @@ -512,6 +529,34 @@ type IfData struct { Reserved2 uint32 } +type IfData64 struct { + Type uint8 + Typelen uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Recvquota uint8 + Xmitquota uint8 + Unused1 uint8 + Mtu uint32 + Metric uint32 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Recvtiming uint32 + Xmittiming uint32 + Lastchange Timeval32 +} + type IfaMsghdr struct { Msglen uint16 Version uint8 @@ -557,6 +602,21 @@ type RtMsghdr struct { Rmx RtMetrics } +type RtMsghdr2 struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Flags int32 + Addrs int32 + Refcnt int32 + Parentflags int32 + Reserved int32 + Use int32 + Inits uint32 + Rmx RtMetrics +} + type RtMetrics struct { Locks uint32 Mtu uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 9f2550dc31..5537148dcb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -87,31 +87,35 @@ type StatxTimestamp struct { } type Statx_t struct { - Mask uint32 - Blksize uint32 - Attributes uint64 - Nlink uint32 - Uid uint32 - Gid uint32 - Mode uint16 - _ [1]uint16 - Ino uint64 - Size uint64 - Blocks uint64 - Attributes_mask uint64 - Atime StatxTimestamp - Btime StatxTimestamp - Ctime StatxTimestamp - Mtime StatxTimestamp - Rdev_major uint32 - Rdev_minor uint32 - Dev_major uint32 - Dev_minor uint32 - Mnt_id uint64 - Dio_mem_align uint32 - Dio_offset_align uint32 - Subvol uint64 - _ [11]uint64 + Mask uint32 + Blksize uint32 + Attributes uint64 + Nlink uint32 + Uid uint32 + Gid uint32 + Mode uint16 + _ [1]uint16 + Ino uint64 + Size uint64 + Blocks uint64 + Attributes_mask uint64 + Atime StatxTimestamp + Btime StatxTimestamp + Ctime StatxTimestamp + Mtime StatxTimestamp + Rdev_major uint32 + Rdev_minor uint32 + Dev_major uint32 + Dev_minor uint32 + Mnt_id uint64 + Dio_mem_align uint32 + Dio_offset_align uint32 + Subvol uint64 + Atomic_write_unit_min uint32 + Atomic_write_unit_max uint32 + Atomic_write_segments_max uint32 + _ [1]uint32 + _ [9]uint64 } type Fsid struct { @@ -516,6 +520,29 @@ type TCPInfo struct { Total_rto_time uint32 } +type TCPVegasInfo struct { + Enabled uint32 + Rttcnt uint32 + Rtt uint32 + Minrtt uint32 +} + +type TCPDCTCPInfo struct { + Enabled uint16 + Ce_state uint16 + Alpha uint32 + Ab_ecn uint32 + Ab_tot uint32 +} + +type TCPBBRInfo struct { + Bw_lo uint32 + Bw_hi uint32 + Min_rtt uint32 + Pacing_gain uint32 + Cwnd_gain uint32 +} + type CanFilter struct { Id uint32 Mask uint32 @@ -557,6 +584,7 @@ const ( SizeofICMPv6Filter = 0x20 SizeofUcred = 0xc SizeofTCPInfo = 0xf8 + SizeofTCPCCInfo = 0x14 SizeofCanFilter = 0x8 SizeofTCPRepairOpt = 0x8 ) @@ -1724,12 +1752,6 @@ const ( IFLA_IPVLAN_UNSPEC = 0x0 IFLA_IPVLAN_MODE = 0x1 IFLA_IPVLAN_FLAGS = 0x2 - NETKIT_NEXT = -0x1 - NETKIT_PASS = 0x0 - NETKIT_DROP = 0x2 - NETKIT_REDIRECT = 0x7 - NETKIT_L2 = 0x0 - NETKIT_L3 = 0x1 IFLA_NETKIT_UNSPEC = 0x0 IFLA_NETKIT_PEER_INFO = 0x1 IFLA_NETKIT_PRIMARY = 0x2 @@ -1768,6 +1790,7 @@ const ( IFLA_VXLAN_DF = 0x1d IFLA_VXLAN_VNIFILTER = 0x1e IFLA_VXLAN_LOCALBYPASS = 0x1f + IFLA_VXLAN_LABEL_POLICY = 0x20 IFLA_GENEVE_UNSPEC = 0x0 IFLA_GENEVE_ID = 0x1 IFLA_GENEVE_REMOTE = 0x2 @@ -1797,6 +1820,8 @@ const ( IFLA_GTP_ROLE = 0x4 IFLA_GTP_CREATE_SOCKETS = 0x5 IFLA_GTP_RESTART_COUNT = 0x6 + IFLA_GTP_LOCAL = 0x7 + IFLA_GTP_LOCAL6 = 0x8 IFLA_BOND_UNSPEC = 0x0 IFLA_BOND_MODE = 0x1 IFLA_BOND_ACTIVE_SLAVE = 0x2 @@ -1829,6 +1854,7 @@ const ( IFLA_BOND_AD_LACP_ACTIVE = 0x1d IFLA_BOND_MISSED_MAX = 0x1e IFLA_BOND_NS_IP6_TARGET = 0x1f + IFLA_BOND_COUPLED_CONTROL = 0x20 IFLA_BOND_AD_INFO_UNSPEC = 0x0 IFLA_BOND_AD_INFO_AGGREGATOR = 0x1 IFLA_BOND_AD_INFO_NUM_PORTS = 0x2 @@ -1897,6 +1923,7 @@ const ( IFLA_HSR_SEQ_NR = 0x5 IFLA_HSR_VERSION = 0x6 IFLA_HSR_PROTOCOL = 0x7 + IFLA_HSR_INTERLINK = 0x8 IFLA_STATS_UNSPEC = 0x0 IFLA_STATS_LINK_64 = 0x1 IFLA_STATS_LINK_XSTATS = 0x2 @@ -1949,6 +1976,15 @@ const ( IFLA_DSA_MASTER = 0x1 ) +const ( + NETKIT_NEXT = -0x1 + NETKIT_PASS = 0x0 + NETKIT_DROP = 0x2 + NETKIT_REDIRECT = 0x7 + NETKIT_L2 = 0x0 + NETKIT_L3 = 0x1 +) + const ( NF_INET_PRE_ROUTING = 0x0 NF_INET_LOCAL_IN = 0x1 @@ -2558,8 +2594,8 @@ const ( SOF_TIMESTAMPING_BIND_PHC = 0x8000 SOF_TIMESTAMPING_OPT_ID_TCP = 0x10000 - SOF_TIMESTAMPING_LAST = 0x10000 - SOF_TIMESTAMPING_MASK = 0x1ffff + SOF_TIMESTAMPING_LAST = 0x20000 + SOF_TIMESTAMPING_MASK = 0x3ffff SCM_TSTAMP_SND = 0x0 SCM_TSTAMP_SCHED = 0x1 @@ -3505,7 +3541,7 @@ type Nhmsg struct { type NexthopGrp struct { Id uint32 Weight uint8 - Resvd1 uint8 + High uint8 Resvd2 uint16 } @@ -3766,7 +3802,7 @@ const ( ETHTOOL_MSG_PSE_GET = 0x24 ETHTOOL_MSG_PSE_SET = 0x25 ETHTOOL_MSG_RSS_GET = 0x26 - ETHTOOL_MSG_USER_MAX = 0x2b + ETHTOOL_MSG_USER_MAX = 0x2d ETHTOOL_MSG_KERNEL_NONE = 0x0 ETHTOOL_MSG_STRSET_GET_REPLY = 0x1 ETHTOOL_MSG_LINKINFO_GET_REPLY = 0x2 @@ -3806,7 +3842,7 @@ const ( ETHTOOL_MSG_MODULE_NTF = 0x24 ETHTOOL_MSG_PSE_GET_REPLY = 0x25 ETHTOOL_MSG_RSS_GET_REPLY = 0x26 - ETHTOOL_MSG_KERNEL_MAX = 0x2b + ETHTOOL_MSG_KERNEL_MAX = 0x2e ETHTOOL_FLAG_COMPACT_BITSETS = 0x1 ETHTOOL_FLAG_OMIT_REPLY = 0x2 ETHTOOL_FLAG_STATS = 0x4 @@ -3814,7 +3850,7 @@ const ( ETHTOOL_A_HEADER_DEV_INDEX = 0x1 ETHTOOL_A_HEADER_DEV_NAME = 0x2 ETHTOOL_A_HEADER_FLAGS = 0x3 - ETHTOOL_A_HEADER_MAX = 0x3 + ETHTOOL_A_HEADER_MAX = 0x4 ETHTOOL_A_BITSET_BIT_UNSPEC = 0x0 ETHTOOL_A_BITSET_BIT_INDEX = 0x1 ETHTOOL_A_BITSET_BIT_NAME = 0x2 @@ -3951,7 +3987,7 @@ const ( ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL = 0x17 ETHTOOL_A_COALESCE_USE_CQE_MODE_TX = 0x18 ETHTOOL_A_COALESCE_USE_CQE_MODE_RX = 0x19 - ETHTOOL_A_COALESCE_MAX = 0x1c + ETHTOOL_A_COALESCE_MAX = 0x1e ETHTOOL_A_PAUSE_UNSPEC = 0x0 ETHTOOL_A_PAUSE_HEADER = 0x1 ETHTOOL_A_PAUSE_AUTONEG = 0x2 @@ -3995,11 +4031,11 @@ const ( ETHTOOL_A_CABLE_RESULT_UNSPEC = 0x0 ETHTOOL_A_CABLE_RESULT_PAIR = 0x1 ETHTOOL_A_CABLE_RESULT_CODE = 0x2 - ETHTOOL_A_CABLE_RESULT_MAX = 0x2 + ETHTOOL_A_CABLE_RESULT_MAX = 0x3 ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC = 0x0 ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR = 0x1 ETHTOOL_A_CABLE_FAULT_LENGTH_CM = 0x2 - ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = 0x2 + ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = 0x3 ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC = 0x0 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED = 0x1 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED = 0x2 @@ -4082,6 +4118,107 @@ type EthtoolDrvinfo struct { Regdump_len uint32 } +type EthtoolTsInfo struct { + Cmd uint32 + So_timestamping uint32 + Phc_index int32 + Tx_types uint32 + Tx_reserved [3]uint32 + Rx_filters uint32 + Rx_reserved [3]uint32 +} + +type HwTstampConfig struct { + Flags int32 + Tx_type int32 + Rx_filter int32 +} + +const ( + HWTSTAMP_FILTER_NONE = 0x0 + HWTSTAMP_FILTER_ALL = 0x1 + HWTSTAMP_FILTER_SOME = 0x2 + HWTSTAMP_FILTER_PTP_V1_L4_EVENT = 0x3 + HWTSTAMP_FILTER_PTP_V2_L4_EVENT = 0x6 + HWTSTAMP_FILTER_PTP_V2_L2_EVENT = 0x9 + HWTSTAMP_FILTER_PTP_V2_EVENT = 0xc +) + +const ( + HWTSTAMP_TX_OFF = 0x0 + HWTSTAMP_TX_ON = 0x1 + HWTSTAMP_TX_ONESTEP_SYNC = 0x2 +) + +type ( + PtpClockCaps struct { + Max_adj int32 + N_alarm int32 + N_ext_ts int32 + N_per_out int32 + Pps int32 + N_pins int32 + Cross_timestamping int32 + Adjust_phase int32 + Max_phase_adj int32 + Rsv [11]int32 + } + PtpClockTime struct { + Sec int64 + Nsec uint32 + Reserved uint32 + } + PtpExttsEvent struct { + T PtpClockTime + Index uint32 + Flags uint32 + Rsv [2]uint32 + } + PtpExttsRequest struct { + Index uint32 + Flags uint32 + Rsv [2]uint32 + } + PtpPeroutRequest struct { + StartOrPhase PtpClockTime + Period PtpClockTime + Index uint32 + Flags uint32 + On PtpClockTime + } + PtpPinDesc struct { + Name [64]byte + Index uint32 + Func uint32 + Chan uint32 + Rsv [5]uint32 + } + PtpSysOffset struct { + Samples uint32 + Rsv [3]uint32 + Ts [51]PtpClockTime + } + PtpSysOffsetExtended struct { + Samples uint32 + Clockid int32 + Rsv [2]uint32 + Ts [25][3]PtpClockTime + } + PtpSysOffsetPrecise struct { + Device PtpClockTime + Realtime PtpClockTime + Monoraw PtpClockTime + Rsv [4]uint32 + } +) + +const ( + PTP_PF_NONE = 0x0 + PTP_PF_EXTTS = 0x1 + PTP_PF_PEROUT = 0x2 + PTP_PF_PHYSYNC = 0x3 +) + type ( HIDRawReportDescriptor struct { Size uint32 @@ -4263,6 +4400,7 @@ const ( type LandlockRulesetAttr struct { Access_fs uint64 Access_net uint64 + Scoped uint64 } type LandlockPathBeneathAttr struct { @@ -4609,7 +4747,7 @@ const ( NL80211_ATTR_MAC_HINT = 0xc8 NL80211_ATTR_MAC_MASK = 0xd7 NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca - NL80211_ATTR_MAX = 0x14a + NL80211_ATTR_MAX = 0x14c NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 NL80211_ATTR_MAX_CSA_COUNTERS = 0xce NL80211_ATTR_MAX_MATCH_SETS = 0x85 @@ -5213,7 +5351,7 @@ const ( NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf - NL80211_FREQUENCY_ATTR_MAX = 0x20 + NL80211_FREQUENCY_ATTR_MAX = 0x21 NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc diff --git a/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go index d9a13af468..2e5d5a4435 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go @@ -377,6 +377,12 @@ type Flock_t struct { Pid int32 } +type F_cnvrt struct { + Cvtcmd int32 + Pccsid int16 + Fccsid int16 +} + type Termios struct { Cflag uint32 Iflag uint32 diff --git a/vendor/golang.org/x/sys/windows/dll_windows.go b/vendor/golang.org/x/sys/windows/dll_windows.go index 115341fba6..4e613cf633 100644 --- a/vendor/golang.org/x/sys/windows/dll_windows.go +++ b/vendor/golang.org/x/sys/windows/dll_windows.go @@ -65,7 +65,7 @@ func LoadDLL(name string) (dll *DLL, err error) { return d, nil } -// MustLoadDLL is like LoadDLL but panics if load operation failes. +// MustLoadDLL is like LoadDLL but panics if load operation fails. func MustLoadDLL(name string) *DLL { d, e := LoadDLL(name) if e != nil { diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 5cee9a3143..4a32543868 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -168,6 +168,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) [failretval==InvalidHandle] = CreateNamedPipeW //sys ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error) //sys DisconnectNamedPipe(pipe Handle) (err error) +//sys GetNamedPipeClientProcessId(pipe Handle, clientProcessID *uint32) (err error) +//sys GetNamedPipeServerProcessId(pipe Handle, serverProcessID *uint32) (err error) //sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) //sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState @@ -725,20 +727,12 @@ func DurationSinceBoot() time.Duration { } func Ftruncate(fd Handle, length int64) (err error) { - curoffset, e := Seek(fd, 0, 1) - if e != nil { - return e - } - defer Seek(fd, curoffset, 0) - _, e = Seek(fd, length, 0) - if e != nil { - return e + type _FILE_END_OF_FILE_INFO struct { + EndOfFile int64 } - e = SetEndOfFile(fd) - if e != nil { - return e - } - return nil + var info _FILE_END_OF_FILE_INFO + info.EndOfFile = length + return SetFileInformationByHandle(fd, FileEndOfFileInfo, (*byte)(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info))) } func Gettimeofday(tv *Timeval) (err error) { @@ -894,6 +888,11 @@ const socket_error = uintptr(^uint32(0)) //sys GetACP() (acp uint32) = kernel32.GetACP //sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar //sys getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) = iphlpapi.GetBestInterfaceEx +//sys GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) = iphlpapi.GetIfEntry2Ex +//sys GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) = iphlpapi.GetUnicastIpAddressEntry +//sys NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyIpInterfaceChange +//sys NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) = iphlpapi.NotifyUnicastIpAddressChange +//sys CancelMibChangeNotify2(notificationHandle Handle) (errcode error) = iphlpapi.CancelMibChangeNotify2 // For testing: clients can set this flag to force // creation of IPv6 sockets to return EAFNOSUPPORT. @@ -1685,13 +1684,16 @@ func (s NTStatus) Error() string { // do not use NTUnicodeString, and instead UTF16PtrFromString should be used for // the more common *uint16 string type. func NewNTUnicodeString(s string) (*NTUnicodeString, error) { - var u NTUnicodeString - s16, err := UTF16PtrFromString(s) + s16, err := UTF16FromString(s) if err != nil { return nil, err } - RtlInitUnicodeString(&u, s16) - return &u, nil + n := uint16(len(s16) * 2) + return &NTUnicodeString{ + Length: n - 2, // subtract 2 bytes for the NULL terminator + MaximumLength: n, + Buffer: &s16[0], + }, nil } // Slice returns a uint16 slice that aliases the data in the NTUnicodeString. diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 7b97a154c9..9d138de5fe 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -176,6 +176,7 @@ const ( WAIT_FAILED = 0xFFFFFFFF // Access rights for process. + PROCESS_ALL_ACCESS = 0xFFFF PROCESS_CREATE_PROCESS = 0x0080 PROCESS_CREATE_THREAD = 0x0002 PROCESS_DUP_HANDLE = 0x0040 @@ -2203,6 +2204,132 @@ const ( IfOperStatusLowerLayerDown = 7 ) +const ( + IF_MAX_PHYS_ADDRESS_LENGTH = 32 + IF_MAX_STRING_SIZE = 256 +) + +// MIB_IF_ENTRY_LEVEL enumeration from netioapi.h or +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getifentry2ex. +const ( + MibIfEntryNormal = 0 + MibIfEntryNormalWithoutStatistics = 2 +) + +// MIB_NOTIFICATION_TYPE enumeration from netioapi.h or +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ne-netioapi-mib_notification_type. +const ( + MibParameterNotification = 0 + MibAddInstance = 1 + MibDeleteInstance = 2 + MibInitialNotification = 3 +) + +// MibIfRow2 stores information about a particular interface. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_if_row2. +type MibIfRow2 struct { + InterfaceLuid uint64 + InterfaceIndex uint32 + InterfaceGuid GUID + Alias [IF_MAX_STRING_SIZE + 1]uint16 + Description [IF_MAX_STRING_SIZE + 1]uint16 + PhysicalAddressLength uint32 + PhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]uint8 + PermanentPhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]uint8 + Mtu uint32 + Type uint32 + TunnelType uint32 + MediaType uint32 + PhysicalMediumType uint32 + AccessType uint32 + DirectionType uint32 + InterfaceAndOperStatusFlags uint8 + OperStatus uint32 + AdminStatus uint32 + MediaConnectState uint32 + NetworkGuid GUID + ConnectionType uint32 + TransmitLinkSpeed uint64 + ReceiveLinkSpeed uint64 + InOctets uint64 + InUcastPkts uint64 + InNUcastPkts uint64 + InDiscards uint64 + InErrors uint64 + InUnknownProtos uint64 + InUcastOctets uint64 + InMulticastOctets uint64 + InBroadcastOctets uint64 + OutOctets uint64 + OutUcastPkts uint64 + OutNUcastPkts uint64 + OutDiscards uint64 + OutErrors uint64 + OutUcastOctets uint64 + OutMulticastOctets uint64 + OutBroadcastOctets uint64 + OutQLen uint64 +} + +// MIB_UNICASTIPADDRESS_ROW stores information about a unicast IP address. See +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_row. +type MibUnicastIpAddressRow struct { + Address RawSockaddrInet6 // SOCKADDR_INET union + InterfaceLuid uint64 + InterfaceIndex uint32 + PrefixOrigin uint32 + SuffixOrigin uint32 + ValidLifetime uint32 + PreferredLifetime uint32 + OnLinkPrefixLength uint8 + SkipAsSource uint8 + DadState uint32 + ScopeId uint32 + CreationTimeStamp Filetime +} + +const ScopeLevelCount = 16 + +// MIB_IPINTERFACE_ROW stores interface management information for a particular IP address family on a network interface. +// See https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_ipinterface_row. +type MibIpInterfaceRow struct { + Family uint16 + InterfaceLuid uint64 + InterfaceIndex uint32 + MaxReassemblySize uint32 + InterfaceIdentifier uint64 + MinRouterAdvertisementInterval uint32 + MaxRouterAdvertisementInterval uint32 + AdvertisingEnabled uint8 + ForwardingEnabled uint8 + WeakHostSend uint8 + WeakHostReceive uint8 + UseAutomaticMetric uint8 + UseNeighborUnreachabilityDetection uint8 + ManagedAddressConfigurationSupported uint8 + OtherStatefulConfigurationSupported uint8 + AdvertiseDefaultRoute uint8 + RouterDiscoveryBehavior uint32 + DadTransmits uint32 + BaseReachableTime uint32 + RetransmitTime uint32 + PathMtuDiscoveryTimeout uint32 + LinkLocalAddressBehavior uint32 + LinkLocalAddressTimeout uint32 + ZoneIndices [ScopeLevelCount]uint32 + SitePrefixLength uint32 + Metric uint32 + NlMtu uint32 + Connected uint8 + SupportsWakeUpPatterns uint8 + SupportsNeighborDiscovery uint8 + SupportsRouterDiscovery uint8 + ReachableTime uint32 + TransmitOffload uint32 + ReceiveOffload uint32 + DisableDefaultRoutes uint8 +} + // Console related constants used for the mode parameter to SetConsoleMode. See // https://docs.microsoft.com/en-us/windows/console/setconsolemode for details. diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 4c2e1bdc01..01c0716c2c 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -181,10 +181,15 @@ var ( procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") procDwmGetWindowAttribute = moddwmapi.NewProc("DwmGetWindowAttribute") procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute") + procCancelMibChangeNotify2 = modiphlpapi.NewProc("CancelMibChangeNotify2") procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") + procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex") + procGetUnicastIpAddressEntry = modiphlpapi.NewProc("GetUnicastIpAddressEntry") + procNotifyIpInterfaceChange = modiphlpapi.NewProc("NotifyIpInterfaceChange") + procNotifyUnicastIpAddressChange = modiphlpapi.NewProc("NotifyUnicastIpAddressChange") procAddDllDirectory = modkernel32.NewProc("AddDllDirectory") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") procCancelIo = modkernel32.NewProc("CancelIo") @@ -275,8 +280,10 @@ var ( procGetMaximumProcessorCount = modkernel32.NewProc("GetMaximumProcessorCount") procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") procGetModuleHandleExW = modkernel32.NewProc("GetModuleHandleExW") + procGetNamedPipeClientProcessId = modkernel32.NewProc("GetNamedPipeClientProcessId") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") + procGetNamedPipeServerProcessId = modkernel32.NewProc("GetNamedPipeServerProcessId") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procGetPriorityClass = modkernel32.NewProc("GetPriorityClass") procGetProcAddress = modkernel32.NewProc("GetProcAddress") @@ -1606,6 +1613,14 @@ func DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, si return } +func CancelMibChangeNotify2(notificationHandle Handle) (errcode error) { + r0, _, _ := syscall.Syscall(procCancelMibChangeNotify2.Addr(), 1, uintptr(notificationHandle), 0, 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0) if r0 != 0 { @@ -1638,6 +1653,46 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) { return } +func GetIfEntry2Ex(level uint32, row *MibIfRow2) (errcode error) { + r0, _, _ := syscall.Syscall(procGetIfEntry2Ex.Addr(), 2, uintptr(level), uintptr(unsafe.Pointer(row)), 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func GetUnicastIpAddressEntry(row *MibUnicastIpAddressRow) (errcode error) { + r0, _, _ := syscall.Syscall(procGetUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func NotifyIpInterfaceChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) { + var _p0 uint32 + if initialNotification { + _p0 = 1 + } + r0, _, _ := syscall.Syscall6(procNotifyIpInterfaceChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + +func NotifyUnicastIpAddressChange(family uint16, callback uintptr, callerContext unsafe.Pointer, initialNotification bool, notificationHandle *Handle) (errcode error) { + var _p0 uint32 + if initialNotification { + _p0 = 1 + } + r0, _, _ := syscall.Syscall6(procNotifyUnicastIpAddressChange.Addr(), 5, uintptr(family), uintptr(callback), uintptr(callerContext), uintptr(_p0), uintptr(unsafe.Pointer(notificationHandle)), 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + func AddDllDirectory(path *uint16) (cookie uintptr, err error) { r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) cookie = uintptr(r0) @@ -2393,6 +2448,14 @@ func GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err er return } +func GetNamedPipeClientProcessId(pipe Handle, clientProcessID *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetNamedPipeClientProcessId.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(clientProcessID)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) if r1 == 0 { @@ -2409,6 +2472,14 @@ func GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint3 return } +func GetNamedPipeServerProcessId(pipe Handle, serverProcessID *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetNamedPipeServerProcessId.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(serverProcessID)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetOverlappedResult(handle Handle, overlapped *Overlapped, done *uint32, wait bool) (err error) { var _p0 uint32 if wait { diff --git a/vendor/golang.org/x/term/README.md b/vendor/golang.org/x/term/README.md index d03d0aefef..05ff623f94 100644 --- a/vendor/golang.org/x/term/README.md +++ b/vendor/golang.org/x/term/README.md @@ -4,16 +4,13 @@ This repository provides Go terminal and console support packages. -## Download/Install - -The easiest way to install is to run `go get -u golang.org/x/term`. You can -also manually git clone the repository to `$GOPATH/src/golang.org/x/term`. - ## Report Issues / Send Patches This repository uses Gerrit for code changes. To learn how to submit changes to -this repository, see https://golang.org/doc/contribute.html. +this repository, see https://go.dev/doc/contribute. + +The git repository is https://go.googlesource.com/term. The main issue tracker for the term repository is located at -https://github.com/golang/go/issues. Prefix your issue with "x/term:" in the +https://go.dev/issues. Prefix your issue with "x/term:" in the subject line, so it is easy to find. diff --git a/vendor/modules.txt b/vendor/modules.txt index c3a959dedc..26ebe57b82 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -608,11 +608,9 @@ github.com/spf13/cobra github.com/spf13/pflag # github.com/stretchr/objx v0.5.2 ## explicit; go 1.20 -github.com/stretchr/objx # github.com/stretchr/testify v1.9.0 ## explicit; go 1.17 github.com/stretchr/testify/assert -github.com/stretchr/testify/mock github.com/stretchr/testify/require github.com/stretchr/testify/suite # github.com/thoas/go-funk v0.8.0 @@ -676,7 +674,7 @@ go.mongodb.org/mongo-driver/bson/bsonrw go.mongodb.org/mongo-driver/bson/bsontype go.mongodb.org/mongo-driver/bson/primitive go.mongodb.org/mongo-driver/x/bsonx/bsoncore -# golang.org/x/crypto v0.27.0 +# golang.org/x/crypto v0.31.0 ## explicit; go 1.20 golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert @@ -713,10 +711,10 @@ golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/socks golang.org/x/net/proxy -# golang.org/x/sync v0.8.0 +# golang.org/x/sync v0.10.0 ## explicit; go 1.18 golang.org/x/sync/errgroup -# golang.org/x/sys v0.25.0 +# golang.org/x/sys v0.28.0 ## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/execabs @@ -724,10 +722,10 @@ golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows golang.org/x/sys/windows/registry -# golang.org/x/term v0.24.0 +# golang.org/x/term v0.27.0 ## explicit; go 1.18 golang.org/x/term -# golang.org/x/text v0.18.0 +# golang.org/x/text v0.21.0 ## explicit; go 1.18 golang.org/x/text/cases golang.org/x/text/internal From 63973c69120170a0e45c64517a1fbee1649e0b73 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 16 Dec 2024 14:51:51 -0500 Subject: [PATCH 429/440] Assert installing as admin prompt happens on Windows. --- cmd/state-installer/test/integration/installer_int_test.go | 3 +++ test/integration/install_scripts_int_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/cmd/state-installer/test/integration/installer_int_test.go b/cmd/state-installer/test/integration/installer_int_test.go index 89b102a305..d55adf8869 100644 --- a/cmd/state-installer/test/integration/installer_int_test.go +++ b/cmd/state-installer/test/integration/installer_int_test.go @@ -48,6 +48,9 @@ func (suite *InstallerIntegrationTestSuite) TestInstallFromLocalSource() { // Assert output cp.Expect("Installing State Tool") + if runtime.GOOS == "windows" { + cp.Expect("Continuing because State Tool is running in non-interactive mode") // admin prompt + } cp.Expect("Done") cp.Expect("successfully installed") suite.NotContains(cp.Output(), "Downloading State Tool") diff --git a/test/integration/install_scripts_int_test.go b/test/integration/install_scripts_int_test.go index 87e8d5e424..7c27fd4cd6 100644 --- a/test/integration/install_scripts_int_test.go +++ b/test/integration/install_scripts_int_test.go @@ -114,6 +114,9 @@ func (suite *InstallScriptsIntegrationTestSuite) TestInstall() { } cp := ts.SpawnCmdWithOpts(cmd, opts...) cp.Expect("Preparing Installer for State Tool Package Manager") + if runtime.GOOS == "windows" { + cp.Expect("Continuing because the '--force' flag is set") // admin prompt + } cp.Expect("Installation Complete", e2e.RuntimeSourcingTimeoutOpt) if tt.Activate != "" || tt.ActivateByCommand != "" { From f70957bfaaf4cbdd462b8f722066fa0ac31f5e1a Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 16 Dec 2024 15:07:50 -0500 Subject: [PATCH 430/440] Force prompt option should override non-interactive option. --- internal/prompt/prompt.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index d1db14cb01..4ae16f9ef5 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -129,9 +129,7 @@ func (p *Prompt) InputAndValidate(title, message string, defaultResponse *string if nonInteractiveResponse == nil { return "", ErrNoForceOption } - } - - if !p.isInteractive { + } else if !p.isInteractive { nonInteractiveResponse = defaultResponse if nonInteractiveResponse == nil { return "", interactiveInputError(message) @@ -194,9 +192,7 @@ func (p *Prompt) Select(title, message string, choices []string, defaultChoice * if nonInteractiveChoice == nil { return "", ErrNoForceOption } - } - - if !p.isInteractive { + } else if !p.isInteractive { nonInteractiveChoice = defaultChoice if nonInteractiveChoice == nil { return "", interactiveInputError(message) @@ -253,9 +249,7 @@ func (p *Prompt) Confirm(title, message string, defaultChoice *bool, forcedChoic if nonInteractiveChoice == nil { return false, ErrNoForceOption } - } - - if !p.isInteractive { + } else if !p.isInteractive { nonInteractiveChoice = defaultChoice if nonInteractiveChoice == nil { return false, interactiveInputError(message) From 86fb843bd886d6b738ae67645227e412ae4ce0cd Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 18 Dec 2024 16:32:11 -0500 Subject: [PATCH 431/440] Installers should not default to non-interactive. --- cmd/state-installer/cmd.go | 2 +- cmd/state-remote-installer/main.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/state-installer/cmd.go b/cmd/state-installer/cmd.go index dc9f1deb72..89a5450319 100644 --- a/cmd/state-installer/cmd.go +++ b/cmd/state-installer/cmd.go @@ -105,7 +105,7 @@ func main() { OutWriter: os.Stdout, ErrWriter: os.Stderr, Colored: true, - Interactive: false, + Interactive: term.IsTerminal(int(os.Stdin.Fd())), }) if err != nil { multilog.Critical("Could not set up output handler: " + errs.JoinMessage(err)) diff --git a/cmd/state-remote-installer/main.go b/cmd/state-remote-installer/main.go index 7aa6a5653f..af903268b6 100644 --- a/cmd/state-remote-installer/main.go +++ b/cmd/state-remote-installer/main.go @@ -29,6 +29,7 @@ import ( "github.com/ActiveState/cli/internal/runbits/errors" "github.com/ActiveState/cli/internal/runbits/panics" "github.com/ActiveState/cli/internal/updater" + "golang.org/x/term" ) type Params struct { @@ -90,7 +91,7 @@ func main() { OutWriter: os.Stdout, ErrWriter: os.Stderr, Colored: true, - Interactive: false, + Interactive: term.IsTerminal(int(os.Stdin.Fd())), }) if err != nil { logging.Error("Could not set up output handler: " + errs.JoinMessage(err)) From 9d57da838d311ce3308526e5f13044c2c8c39278 Mon Sep 17 00:00:00 2001 From: mitchell Date: Wed, 18 Dec 2024 17:01:35 -0500 Subject: [PATCH 432/440] Fixed remote installer accepting TOS in non-interactive mode. --- cmd/state-remote-installer/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/state-remote-installer/main.go b/cmd/state-remote-installer/main.go index af903268b6..8d2c3c6116 100644 --- a/cmd/state-remote-installer/main.go +++ b/cmd/state-remote-installer/main.go @@ -173,9 +173,13 @@ func main() { } func execute(out output.Outputer, prompt prompt.Prompter, cfg *config.Instance, an analytics.Dispatcher, args []string, params *Params) error { + if params.nonInteractive { + prompt.SetInteractive(false) + } + defaultChoice := params.nonInteractive msg := locale.Tr("tos_disclaimer", constants.TermsOfServiceURLLatest) msg += locale.Tr("tos_disclaimer_prompt", constants.TermsOfServiceURLLatest) - cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, ptr.To(true), nil) + cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, &defaultChoice, nil) if err != nil { return errs.Wrap(err, "Not confirmed") } From edfa0c128586fb5d659d2e5600cc0b90d02765f0 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Dec 2024 12:25:46 -0500 Subject: [PATCH 433/440] Bump golang-jwt version to address CVE. --- go.mod | 2 +- go.sum | 2 + vendor/github.com/golang-jwt/jwt/v4/README.md | 47 ++++++++++++------- .../github.com/golang-jwt/jwt/v4/SECURITY.md | 19 ++++++++ vendor/github.com/golang-jwt/jwt/v4/claims.go | 10 ++-- vendor/github.com/golang-jwt/jwt/v4/parser.go | 36 ++++++++------ .../github.com/golang-jwt/jwt/v4/rsa_pss.go | 1 + vendor/github.com/golang-jwt/jwt/v4/token.go | 21 +++++++-- vendor/github.com/golang-jwt/jwt/v4/types.go | 20 ++++++-- vendor/modules.txt | 4 +- 10 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 vendor/github.com/golang-jwt/jwt/v4/SECURITY.md diff --git a/go.mod b/go.mod index 12567127cc..55a15ab997 100644 --- a/go.mod +++ b/go.mod @@ -135,7 +135,7 @@ require ( github.com/go-openapi/spec v0.20.3 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.3.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 // indirect diff --git a/go.sum b/go.sum index 9e9fc36e64..b5fa1de3be 100644 --- a/go.sum +++ b/go.sum @@ -313,6 +313,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= diff --git a/vendor/github.com/golang-jwt/jwt/v4/README.md b/vendor/github.com/golang-jwt/jwt/v4/README.md index 01b21646e0..30f2f2a6f7 100644 --- a/vendor/github.com/golang-jwt/jwt/v4/README.md +++ b/vendor/github.com/golang-jwt/jwt/v4/README.md @@ -36,24 +36,39 @@ The part in the middle is the interesting bit. It's called the Claims and conta This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. +## Installation Guidelines + +1. To install the jwt package, you first need to have [Go](https://go.dev/doc/install) installed, then you can use the command below to add `jwt-go` as a dependency in your Go program. + +```sh +go get -u github.com/golang-jwt/jwt/v4 +``` + +2. Import it in your code: + +```go +import "github.com/golang-jwt/jwt/v4" +``` + ## Examples -See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage: +See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v4) for examples of usage: -* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac) -* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac) -* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt#pkg-examples) +* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#example-Parse-Hmac) +* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#example-New-Hmac) +* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#pkg-examples) ## Extensions -This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. +This library publishes all the necessary components for adding your own signing methods or key functions. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod` or provide a `jwt.Keyfunc`. -A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs). +A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs) or to implement additional standards. -| Extension | Purpose | Repo | -|-----------|----------------------------------------------------------------------------------------------|--------------------------------------------| -| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | -| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| Extension | Purpose | Repo | +| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------ | +| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | +| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | *Disclaimer*: Unless otherwise specified, these integrations are maintained by third parties and should not be considered as a primary offer by any of the mentioned cloud providers @@ -81,7 +96,7 @@ A token is simply a JSON object that is signed by its author. this tells you exa * The author of the token was in the possession of the signing secret * The data has not been modified since it was signed -It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. +It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. The companion project https://github.com/golang-jwt/jwe aims at a (very) experimental implementation of the JWE standard. ### Choosing a Signing Method @@ -95,10 +110,10 @@ Asymmetric signing methods, such as RSA, use different keys for signing and veri Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: -* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation -* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation -* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation -* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation +* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation +* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation +* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation +* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation ### JWT and OAuth @@ -116,7 +131,7 @@ This library uses descriptive error messages whenever possible. If you are not g ## More -Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt). +Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt/v4). The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. diff --git a/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md b/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md new file mode 100644 index 0000000000..b08402c342 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +As of February 2022 (and until this document is updated), the latest version `v4` is supported. + +## Reporting a Vulnerability + +If you think you found a vulnerability, and even if you are not sure, please report it to jwt-go-security@googlegroups.com or one of the other [golang-jwt maintainers](https://github.com/orgs/golang-jwt/people). Please try be explicit, describe steps to reproduce the security issue with code example(s). + +You will receive a response within a timely manner. If the issue is confirmed, we will do our best to release a patch as soon as possible given the complexity of the problem. + +## Public Discussions + +Please avoid publicly discussing a potential security vulnerability. + +Let's take this offline and find a solution first, this limits the potential impact as much as possible. + +We appreciate your help! diff --git a/vendor/github.com/golang-jwt/jwt/v4/claims.go b/vendor/github.com/golang-jwt/jwt/v4/claims.go index 4f00db2fb8..364cec8773 100644 --- a/vendor/github.com/golang-jwt/jwt/v4/claims.go +++ b/vendor/github.com/golang-jwt/jwt/v4/claims.go @@ -56,7 +56,7 @@ func (c RegisteredClaims) Valid() error { // default value in Go, let's not fail the verification for them. if !c.VerifyExpiresAt(now, false) { delta := now.Sub(c.ExpiresAt.Time) - vErr.Inner = fmt.Errorf("%s by %v", delta, ErrTokenExpired) + vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) vErr.Errors |= ValidationErrorExpired } @@ -149,7 +149,7 @@ func (c StandardClaims) Valid() error { // default value in Go, let's not fail the verification for them. if !c.VerifyExpiresAt(now, false) { delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) - vErr.Inner = fmt.Errorf("%s by %v", delta, ErrTokenExpired) + vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) vErr.Errors |= ValidationErrorExpired } @@ -265,9 +265,5 @@ func verifyIss(iss string, cmp string, required bool) bool { if iss == "" { return !required } - if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { - return true - } else { - return false - } + return subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 } diff --git a/vendor/github.com/golang-jwt/jwt/v4/parser.go b/vendor/github.com/golang-jwt/jwt/v4/parser.go index 2f61a69d7f..9dd36e5a5a 100644 --- a/vendor/github.com/golang-jwt/jwt/v4/parser.go +++ b/vendor/github.com/golang-jwt/jwt/v4/parser.go @@ -36,12 +36,21 @@ func NewParser(options ...ParserOption) *Parser { return p } -// Parse parses, validates, verifies the signature and returns the parsed token. -// keyFunc will receive the parsed token and should return the key for validating. +// Parse parses, validates, verifies the signature and returns the parsed token. keyFunc will +// receive the parsed token and should return the key for validating. func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) } +// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object +// implementing the Claims interface. This provides default values which can be overridden and +// allows a caller to use their own type, rather than the default MapClaims implementation of +// Claims. +// +// Note: If you provide a custom claim implementation that embeds one of the standard claims (such +// as RegisteredClaims), make sure that a) you either embed a non-pointer version of the claims or +// b) if you are using a pointer, allocate the proper memory for it before passing in the overall +// claims, otherwise you might run into a panic. func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { token, parts, err := p.ParseUnverified(tokenString, claims) if err != nil { @@ -78,12 +87,17 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} } + // Perform validation + token.Signature = parts[2] + if err := token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorSignatureInvalid} + } + vErr := &ValidationError{} // Validate Claims if !p.SkipClaimsValidation { if err := token.Claims.Valid(); err != nil { - // If the Claims Valid returned an error, check if it is a validation error, // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set if e, ok := err.(*ValidationError); !ok { @@ -91,22 +105,14 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf } else { vErr = e } + return token, vErr } } - // Perform validation - token.Signature = parts[2] - if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { - vErr.Inner = err - vErr.Errors |= ValidationErrorSignatureInvalid - } - - if vErr.valid() { - token.Valid = true - return token, nil - } + // No errors so far, token is valid. + token.Valid = true - return token, vErr + return token, nil } // ParseUnverified parses the token but doesn't validate the signature. diff --git a/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go b/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go index 5a8502feb3..4fd6f9e610 100644 --- a/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go +++ b/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go @@ -1,3 +1,4 @@ +//go:build go1.4 // +build go1.4 package jwt diff --git a/vendor/github.com/golang-jwt/jwt/v4/token.go b/vendor/github.com/golang-jwt/jwt/v4/token.go index 09b4cde5ae..786b275ce0 100644 --- a/vendor/github.com/golang-jwt/jwt/v4/token.go +++ b/vendor/github.com/golang-jwt/jwt/v4/token.go @@ -7,7 +7,6 @@ import ( "time" ) - // DecodePaddingAllowed will switch the codec used for decoding JWTs respectively. Note that the JWS RFC7515 // states that the tokens will utilize a Base64url encoding with no padding. Unfortunately, some implementations // of JWT are producing non-standard tokens, and thus require support for decoding. Note that this is a global @@ -15,6 +14,12 @@ import ( // To use the non-recommended decoding, set this boolean to `true` prior to using this package. var DecodePaddingAllowed bool +// DecodeStrict will switch the codec used for decoding JWTs into strict mode. +// In this mode, the decoder requires that trailing padding bits are zero, as described in RFC 4648 section 3.5. +// Note that this is a global variable, and updating it will change the behavior on a package level, and is also NOT go-routine safe. +// To use strict decoding, set this boolean to `true` prior to using this package. +var DecodeStrict bool + // TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). // You can override it to use another time value. This is useful for testing or if your // server uses a different time zone than your tokens. @@ -100,6 +105,11 @@ func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token return NewParser(options...).Parse(tokenString, keyFunc) } +// ParseWithClaims is a shortcut for NewParser().ParseWithClaims(). +// +// Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims), +// make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the +// proper memory for it before passing in the overall claims, otherwise you might run into a panic. func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc) } @@ -117,12 +127,17 @@ func EncodeSegment(seg []byte) string { // Deprecated: In a future release, we will demote this function to a non-exported function, since it // should only be used internally func DecodeSegment(seg string) ([]byte, error) { + encoding := base64.RawURLEncoding + if DecodePaddingAllowed { if l := len(seg) % 4; l > 0 { seg += strings.Repeat("=", 4-l) } - return base64.URLEncoding.DecodeString(seg) + encoding = base64.URLEncoding } - return base64.RawURLEncoding.DecodeString(seg) + if DecodeStrict { + encoding = encoding.Strict() + } + return encoding.DecodeString(seg) } diff --git a/vendor/github.com/golang-jwt/jwt/v4/types.go b/vendor/github.com/golang-jwt/jwt/v4/types.go index 2c647fd2e6..ac8e140eb1 100644 --- a/vendor/github.com/golang-jwt/jwt/v4/types.go +++ b/vendor/github.com/golang-jwt/jwt/v4/types.go @@ -53,9 +53,23 @@ func (date NumericDate) MarshalJSON() (b []byte, err error) { if TimePrecision < time.Second { prec = int(math.Log10(float64(time.Second) / float64(TimePrecision))) } - f := float64(date.Truncate(TimePrecision).UnixNano()) / float64(time.Second) - - return []byte(strconv.FormatFloat(f, 'f', prec, 64)), nil + truncatedDate := date.Truncate(TimePrecision) + + // For very large timestamps, UnixNano would overflow an int64, but this + // function requires nanosecond level precision, so we have to use the + // following technique to get round the issue: + // 1. Take the normal unix timestamp to form the whole number part of the + // output, + // 2. Take the result of the Nanosecond function, which retuns the offset + // within the second of the particular unix time instance, to form the + // decimal part of the output + // 3. Concatenate them to produce the final result + seconds := strconv.FormatInt(truncatedDate.Unix(), 10) + nanosecondsOffset := strconv.FormatFloat(float64(truncatedDate.Nanosecond())/float64(time.Second), 'f', prec, 64) + + output := append([]byte(seconds), []byte(nanosecondsOffset)[1:]...) + + return output, nil } // UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a diff --git a/vendor/modules.txt b/vendor/modules.txt index 26ebe57b82..6e80f5c6cb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -344,8 +344,8 @@ github.com/gofrs/flock # github.com/golang-jwt/jwt v3.2.2+incompatible ## explicit github.com/golang-jwt/jwt -# github.com/golang-jwt/jwt/v4 v4.3.0 -## explicit; go 1.15 +# github.com/golang-jwt/jwt/v4 v4.5.1 +## explicit; go 1.16 github.com/golang-jwt/jwt/v4 # github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da ## explicit From 9d8eed8b961c5a7b754bd6a42f0633738c3c8130 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Dec 2024 12:43:56 -0500 Subject: [PATCH 434/440] Bump golang.org/net/x to fix CVE. --- go.mod | 2 +- go.sum | 2 + .../x/net/http2/client_conn_pool.go | 8 +- vendor/golang.org/x/net/http2/config.go | 122 +++++ vendor/golang.org/x/net/http2/config_go124.go | 61 +++ .../x/net/http2/config_pre_go124.go | 16 + vendor/golang.org/x/net/http2/frame.go | 4 +- vendor/golang.org/x/net/http2/http2.go | 95 +++- vendor/golang.org/x/net/http2/server.go | 244 ++++++--- vendor/golang.org/x/net/http2/transport.go | 516 ++++++++++++------ vendor/golang.org/x/net/http2/unencrypted.go | 32 ++ vendor/golang.org/x/net/http2/write.go | 10 + vendor/modules.txt | 2 +- 13 files changed, 840 insertions(+), 274 deletions(-) create mode 100644 vendor/golang.org/x/net/http2/config.go create mode 100644 vendor/golang.org/x/net/http2/config_go124.go create mode 100644 vendor/golang.org/x/net/http2/config_pre_go124.go create mode 100644 vendor/golang.org/x/net/http2/unencrypted.go diff --git a/go.mod b/go.mod index 12567127cc..8fbcaa881d 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,7 @@ require ( github.com/vbauerster/mpb/v7 v7.1.5 github.com/vektah/gqlparser/v2 v2.5.16 golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.29.0 + golang.org/x/net v0.33.0 golang.org/x/sys v0.28.0 golang.org/x/term v0.27.0 golang.org/x/text v0.21.0 diff --git a/go.sum b/go.sum index 9e9fc36e64..50ce8f319e 100644 --- a/go.sum +++ b/go.sum @@ -792,6 +792,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/vendor/golang.org/x/net/http2/client_conn_pool.go b/vendor/golang.org/x/net/http2/client_conn_pool.go index 780968d6c1..e81b73e6a7 100644 --- a/vendor/golang.org/x/net/http2/client_conn_pool.go +++ b/vendor/golang.org/x/net/http2/client_conn_pool.go @@ -8,8 +8,8 @@ package http2 import ( "context" - "crypto/tls" "errors" + "net" "net/http" "sync" ) @@ -158,7 +158,7 @@ func (c *dialCall) dial(ctx context.Context, addr string) { // This code decides which ones live or die. // The return value used is whether c was used. // c is never closed. -func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) { +func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c net.Conn) (used bool, err error) { p.mu.Lock() for _, cc := range p.conns[key] { if cc.CanTakeNewRequest() { @@ -194,8 +194,8 @@ type addConnCall struct { err error } -func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) { - cc, err := t.NewClientConn(tc) +func (c *addConnCall) run(t *Transport, key string, nc net.Conn) { + cc, err := t.NewClientConn(nc) p := c.p p.mu.Lock() diff --git a/vendor/golang.org/x/net/http2/config.go b/vendor/golang.org/x/net/http2/config.go new file mode 100644 index 0000000000..de58dfb8dc --- /dev/null +++ b/vendor/golang.org/x/net/http2/config.go @@ -0,0 +1,122 @@ +// 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 http2 + +import ( + "math" + "net/http" + "time" +) + +// http2Config is a package-internal version of net/http.HTTP2Config. +// +// http.HTTP2Config was added in Go 1.24. +// When running with a version of net/http that includes HTTP2Config, +// we merge the configuration with the fields in Transport or Server +// to produce an http2Config. +// +// Zero valued fields in http2Config are interpreted as in the +// net/http.HTTPConfig documentation. +// +// Precedence order for reconciling configurations is: +// +// - Use the net/http.{Server,Transport}.HTTP2Config value, when non-zero. +// - Otherwise use the http2.{Server.Transport} value. +// - If the resulting value is zero or out of range, use a default. +type http2Config struct { + MaxConcurrentStreams uint32 + MaxDecoderHeaderTableSize uint32 + MaxEncoderHeaderTableSize uint32 + MaxReadFrameSize uint32 + MaxUploadBufferPerConnection int32 + MaxUploadBufferPerStream int32 + SendPingTimeout time.Duration + PingTimeout time.Duration + WriteByteTimeout time.Duration + PermitProhibitedCipherSuites bool + CountError func(errType string) +} + +// configFromServer merges configuration settings from +// net/http.Server.HTTP2Config and http2.Server. +func configFromServer(h1 *http.Server, h2 *Server) http2Config { + conf := http2Config{ + MaxConcurrentStreams: h2.MaxConcurrentStreams, + MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, + MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, + MaxReadFrameSize: h2.MaxReadFrameSize, + MaxUploadBufferPerConnection: h2.MaxUploadBufferPerConnection, + MaxUploadBufferPerStream: h2.MaxUploadBufferPerStream, + SendPingTimeout: h2.ReadIdleTimeout, + PingTimeout: h2.PingTimeout, + WriteByteTimeout: h2.WriteByteTimeout, + PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites, + CountError: h2.CountError, + } + fillNetHTTPServerConfig(&conf, h1) + setConfigDefaults(&conf, true) + return conf +} + +// configFromServer merges configuration settings from h2 and h2.t1.HTTP2 +// (the net/http Transport). +func configFromTransport(h2 *Transport) http2Config { + conf := http2Config{ + MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, + MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, + MaxReadFrameSize: h2.MaxReadFrameSize, + SendPingTimeout: h2.ReadIdleTimeout, + PingTimeout: h2.PingTimeout, + WriteByteTimeout: h2.WriteByteTimeout, + } + + // Unlike most config fields, where out-of-range values revert to the default, + // Transport.MaxReadFrameSize clips. + if conf.MaxReadFrameSize < minMaxFrameSize { + conf.MaxReadFrameSize = minMaxFrameSize + } else if conf.MaxReadFrameSize > maxFrameSize { + conf.MaxReadFrameSize = maxFrameSize + } + + if h2.t1 != nil { + fillNetHTTPTransportConfig(&conf, h2.t1) + } + setConfigDefaults(&conf, false) + return conf +} + +func setDefault[T ~int | ~int32 | ~uint32 | ~int64](v *T, minval, maxval, defval T) { + if *v < minval || *v > maxval { + *v = defval + } +} + +func setConfigDefaults(conf *http2Config, server bool) { + setDefault(&conf.MaxConcurrentStreams, 1, math.MaxUint32, defaultMaxStreams) + setDefault(&conf.MaxEncoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) + setDefault(&conf.MaxDecoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) + if server { + setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, 1<<20) + } else { + setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, transportDefaultConnFlow) + } + if server { + setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, 1<<20) + } else { + setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, transportDefaultStreamFlow) + } + setDefault(&conf.MaxReadFrameSize, minMaxFrameSize, maxFrameSize, defaultMaxReadFrameSize) + setDefault(&conf.PingTimeout, 1, math.MaxInt64, 15*time.Second) +} + +// adjustHTTP1MaxHeaderSize converts a limit in bytes on the size of an HTTP/1 header +// to an HTTP/2 MAX_HEADER_LIST_SIZE value. +func adjustHTTP1MaxHeaderSize(n int64) int64 { + // http2's count is in a slightly different unit and includes 32 bytes per pair. + // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. + const perFieldOverhead = 32 // per http2 spec + const typicalHeaders = 10 // conservative + return n + typicalHeaders*perFieldOverhead +} diff --git a/vendor/golang.org/x/net/http2/config_go124.go b/vendor/golang.org/x/net/http2/config_go124.go new file mode 100644 index 0000000000..e3784123c8 --- /dev/null +++ b/vendor/golang.org/x/net/http2/config_go124.go @@ -0,0 +1,61 @@ +// 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. + +//go:build go1.24 + +package http2 + +import "net/http" + +// fillNetHTTPServerConfig sets fields in conf from srv.HTTP2. +func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) { + fillNetHTTPConfig(conf, srv.HTTP2) +} + +// fillNetHTTPServerConfig sets fields in conf from tr.HTTP2. +func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) { + fillNetHTTPConfig(conf, tr.HTTP2) +} + +func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) { + if h2 == nil { + return + } + if h2.MaxConcurrentStreams != 0 { + conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) + } + if h2.MaxEncoderHeaderTableSize != 0 { + conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize) + } + if h2.MaxDecoderHeaderTableSize != 0 { + conf.MaxDecoderHeaderTableSize = uint32(h2.MaxDecoderHeaderTableSize) + } + if h2.MaxConcurrentStreams != 0 { + conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) + } + if h2.MaxReadFrameSize != 0 { + conf.MaxReadFrameSize = uint32(h2.MaxReadFrameSize) + } + if h2.MaxReceiveBufferPerConnection != 0 { + conf.MaxUploadBufferPerConnection = int32(h2.MaxReceiveBufferPerConnection) + } + if h2.MaxReceiveBufferPerStream != 0 { + conf.MaxUploadBufferPerStream = int32(h2.MaxReceiveBufferPerStream) + } + if h2.SendPingTimeout != 0 { + conf.SendPingTimeout = h2.SendPingTimeout + } + if h2.PingTimeout != 0 { + conf.PingTimeout = h2.PingTimeout + } + if h2.WriteByteTimeout != 0 { + conf.WriteByteTimeout = h2.WriteByteTimeout + } + if h2.PermitProhibitedCipherSuites { + conf.PermitProhibitedCipherSuites = true + } + if h2.CountError != nil { + conf.CountError = h2.CountError + } +} diff --git a/vendor/golang.org/x/net/http2/config_pre_go124.go b/vendor/golang.org/x/net/http2/config_pre_go124.go new file mode 100644 index 0000000000..060fd6c64c --- /dev/null +++ b/vendor/golang.org/x/net/http2/config_pre_go124.go @@ -0,0 +1,16 @@ +// 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. + +//go:build !go1.24 + +package http2 + +import "net/http" + +// Pre-Go 1.24 fallback. +// The Server.HTTP2 and Transport.HTTP2 config fields were added in Go 1.24. + +func fillNetHTTPServerConfig(conf *http2Config, srv *http.Server) {} + +func fillNetHTTPTransportConfig(conf *http2Config, tr *http.Transport) {} diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go index 105c3b279c..81faec7e75 100644 --- a/vendor/golang.org/x/net/http2/frame.go +++ b/vendor/golang.org/x/net/http2/frame.go @@ -1490,7 +1490,7 @@ func (mh *MetaHeadersFrame) checkPseudos() error { pf := mh.PseudoFields() for i, hf := range pf { switch hf.Name { - case ":method", ":path", ":scheme", ":authority": + case ":method", ":path", ":scheme", ":authority", ":protocol": isRequest = true case ":status": isResponse = true @@ -1498,7 +1498,7 @@ func (mh *MetaHeadersFrame) checkPseudos() error { return pseudoHeaderError(hf.Name) } // Check for duplicates. - // This would be a bad algorithm, but N is 4. + // This would be a bad algorithm, but N is 5. // And this doesn't allocate. for _, hf2 := range pf[:i] { if hf.Name == hf2.Name { diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go index 003e649f30..c7601c909f 100644 --- a/vendor/golang.org/x/net/http2/http2.go +++ b/vendor/golang.org/x/net/http2/http2.go @@ -19,8 +19,9 @@ import ( "bufio" "context" "crypto/tls" + "errors" "fmt" - "io" + "net" "net/http" "os" "sort" @@ -33,10 +34,11 @@ import ( ) var ( - VerboseLogs bool - logFrameWrites bool - logFrameReads bool - inTests bool + VerboseLogs bool + logFrameWrites bool + logFrameReads bool + inTests bool + disableExtendedConnectProtocol bool ) func init() { @@ -49,6 +51,9 @@ func init() { logFrameWrites = true logFrameReads = true } + if strings.Contains(e, "http2xconnect=0") { + disableExtendedConnectProtocol = true + } } const ( @@ -140,6 +145,10 @@ func (s Setting) Valid() error { if s.Val < 16384 || s.Val > 1<<24-1 { return ConnectionError(ErrCodeProtocol) } + case SettingEnableConnectProtocol: + if s.Val != 1 && s.Val != 0 { + return ConnectionError(ErrCodeProtocol) + } } return nil } @@ -149,21 +158,23 @@ func (s Setting) Valid() error { type SettingID uint16 const ( - SettingHeaderTableSize SettingID = 0x1 - SettingEnablePush SettingID = 0x2 - SettingMaxConcurrentStreams SettingID = 0x3 - SettingInitialWindowSize SettingID = 0x4 - SettingMaxFrameSize SettingID = 0x5 - SettingMaxHeaderListSize SettingID = 0x6 + SettingHeaderTableSize SettingID = 0x1 + SettingEnablePush SettingID = 0x2 + SettingMaxConcurrentStreams SettingID = 0x3 + SettingInitialWindowSize SettingID = 0x4 + SettingMaxFrameSize SettingID = 0x5 + SettingMaxHeaderListSize SettingID = 0x6 + SettingEnableConnectProtocol SettingID = 0x8 ) var settingName = map[SettingID]string{ - SettingHeaderTableSize: "HEADER_TABLE_SIZE", - SettingEnablePush: "ENABLE_PUSH", - SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", - SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", - SettingMaxFrameSize: "MAX_FRAME_SIZE", - SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", + SettingHeaderTableSize: "HEADER_TABLE_SIZE", + SettingEnablePush: "ENABLE_PUSH", + SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", + SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", + SettingMaxFrameSize: "MAX_FRAME_SIZE", + SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", + SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL", } func (s SettingID) String() string { @@ -237,13 +248,19 @@ func (cw closeWaiter) Wait() { // Its buffered writer is lazily allocated as needed, to minimize // idle memory usage with many connections. type bufferedWriter struct { - _ incomparable - w io.Writer // immutable - bw *bufio.Writer // non-nil when data is buffered + _ incomparable + group synctestGroupInterface // immutable + conn net.Conn // immutable + bw *bufio.Writer // non-nil when data is buffered + byteTimeout time.Duration // immutable, WriteByteTimeout } -func newBufferedWriter(w io.Writer) *bufferedWriter { - return &bufferedWriter{w: w} +func newBufferedWriter(group synctestGroupInterface, conn net.Conn, timeout time.Duration) *bufferedWriter { + return &bufferedWriter{ + group: group, + conn: conn, + byteTimeout: timeout, + } } // bufWriterPoolBufferSize is the size of bufio.Writer's @@ -270,7 +287,7 @@ func (w *bufferedWriter) Available() int { func (w *bufferedWriter) Write(p []byte) (n int, err error) { if w.bw == nil { bw := bufWriterPool.Get().(*bufio.Writer) - bw.Reset(w.w) + bw.Reset((*bufferedWriterTimeoutWriter)(w)) w.bw = bw } return w.bw.Write(p) @@ -288,6 +305,38 @@ func (w *bufferedWriter) Flush() error { return err } +type bufferedWriterTimeoutWriter bufferedWriter + +func (w *bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) { + return writeWithByteTimeout(w.group, w.conn, w.byteTimeout, p) +} + +// writeWithByteTimeout writes to conn. +// If more than timeout passes without any bytes being written to the connection, +// the write fails. +func writeWithByteTimeout(group synctestGroupInterface, conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { + if timeout <= 0 { + return conn.Write(p) + } + for { + var now time.Time + if group == nil { + now = time.Now() + } else { + now = group.Now() + } + conn.SetWriteDeadline(now.Add(timeout)) + nn, err := conn.Write(p[n:]) + n += nn + if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) { + // Either we finished the write, made no progress, or hit the deadline. + // Whichever it is, we're done now. + conn.SetWriteDeadline(time.Time{}) + return n, err + } + } +} + func mustUint31(v int32) uint32 { if v < 0 || v > 2147483647 { panic("out of range") diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 6c349f3ec6..b55547aec6 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -29,6 +29,7 @@ import ( "bufio" "bytes" "context" + "crypto/rand" "crypto/tls" "errors" "fmt" @@ -52,10 +53,14 @@ import ( ) const ( - prefaceTimeout = 10 * time.Second - firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway - handlerChunkWriteSize = 4 << 10 - defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to? + prefaceTimeout = 10 * time.Second + firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway + handlerChunkWriteSize = 4 << 10 + defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to? + + // maxQueuedControlFrames is the maximum number of control frames like + // SETTINGS, PING and RST_STREAM that will be queued for writing before + // the connection is closed to prevent memory exhaustion attacks. maxQueuedControlFrames = 10000 ) @@ -127,6 +132,22 @@ type Server struct { // If zero or negative, there is no timeout. IdleTimeout time.Duration + // ReadIdleTimeout is the timeout after which a health check using a ping + // frame will be carried out if no frame is received on the connection. + // If zero, no health check is performed. + ReadIdleTimeout time.Duration + + // PingTimeout is the timeout after which the connection will be closed + // if a response to a ping is not received. + // If zero, a default of 15 seconds is used. + PingTimeout time.Duration + + // WriteByteTimeout is the timeout after which a connection will be + // closed if no data can be written to it. The timeout begins when data is + // available to write, and is extended whenever any bytes are written. + // If zero or negative, there is no timeout. + WriteByteTimeout time.Duration + // MaxUploadBufferPerConnection is the size of the initial flow // control window for each connections. The HTTP/2 spec does not // allow this to be smaller than 65535 or larger than 2^32-1. @@ -189,57 +210,6 @@ func (s *Server) afterFunc(d time.Duration, f func()) timer { return timeTimer{time.AfterFunc(d, f)} } -func (s *Server) initialConnRecvWindowSize() int32 { - if s.MaxUploadBufferPerConnection >= initialWindowSize { - return s.MaxUploadBufferPerConnection - } - return 1 << 20 -} - -func (s *Server) initialStreamRecvWindowSize() int32 { - if s.MaxUploadBufferPerStream > 0 { - return s.MaxUploadBufferPerStream - } - return 1 << 20 -} - -func (s *Server) maxReadFrameSize() uint32 { - if v := s.MaxReadFrameSize; v >= minMaxFrameSize && v <= maxFrameSize { - return v - } - return defaultMaxReadFrameSize -} - -func (s *Server) maxConcurrentStreams() uint32 { - if v := s.MaxConcurrentStreams; v > 0 { - return v - } - return defaultMaxStreams -} - -func (s *Server) maxDecoderHeaderTableSize() uint32 { - if v := s.MaxDecoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - -func (s *Server) maxEncoderHeaderTableSize() uint32 { - if v := s.MaxEncoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - -// maxQueuedControlFrames is the maximum number of control frames like -// SETTINGS, PING and RST_STREAM that will be queued for writing before -// the connection is closed to prevent memory exhaustion attacks. -func (s *Server) maxQueuedControlFrames() int { - // TODO: if anybody asks, add a Server field, and remember to define the - // behavior of negative values. - return maxQueuedControlFrames -} - type serverInternalState struct { mu sync.Mutex activeConns map[*serverConn]struct{} @@ -336,7 +306,7 @@ func ConfigureServer(s *http.Server, conf *Server) error { if s.TLSNextProto == nil { s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){} } - protoHandler := func(hs *http.Server, c *tls.Conn, h http.Handler) { + protoHandler := func(hs *http.Server, c net.Conn, h http.Handler, sawClientPreface bool) { if testHookOnConn != nil { testHookOnConn() } @@ -353,12 +323,31 @@ func ConfigureServer(s *http.Server, conf *Server) error { ctx = bc.BaseContext() } conf.ServeConn(c, &ServeConnOpts{ - Context: ctx, - Handler: h, - BaseConfig: hs, + Context: ctx, + Handler: h, + BaseConfig: hs, + SawClientPreface: sawClientPreface, }) } - s.TLSNextProto[NextProtoTLS] = protoHandler + s.TLSNextProto[NextProtoTLS] = func(hs *http.Server, c *tls.Conn, h http.Handler) { + protoHandler(hs, c, h, false) + } + // The "unencrypted_http2" TLSNextProto key is used to pass off non-TLS HTTP/2 conns. + // + // A connection passed in this method has already had the HTTP/2 preface read from it. + s.TLSNextProto[nextProtoUnencryptedHTTP2] = func(hs *http.Server, c *tls.Conn, h http.Handler) { + nc, err := unencryptedNetConnFromTLSConn(c) + if err != nil { + if lg := hs.ErrorLog; lg != nil { + lg.Print(err) + } else { + log.Print(err) + } + go c.Close() + return + } + protoHandler(hs, nc, h, true) + } return nil } @@ -440,13 +429,15 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon baseCtx, cancel := serverConnBaseContext(c, opts) defer cancel() + http1srv := opts.baseConfig() + conf := configFromServer(http1srv, s) sc := &serverConn{ srv: s, - hs: opts.baseConfig(), + hs: http1srv, conn: c, baseCtx: baseCtx, remoteAddrStr: c.RemoteAddr().String(), - bw: newBufferedWriter(c), + bw: newBufferedWriter(s.group, c, conf.WriteByteTimeout), handler: opts.handler(), streams: make(map[uint32]*stream), readFrameCh: make(chan readFrameResult), @@ -456,9 +447,12 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way doneServing: make(chan struct{}), clientMaxStreams: math.MaxUint32, // Section 6.5.2: "Initially, there is no limit to this value" - advMaxStreams: s.maxConcurrentStreams(), + advMaxStreams: conf.MaxConcurrentStreams, initialStreamSendWindowSize: initialWindowSize, + initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream, maxFrameSize: initialMaxFrameSize, + pingTimeout: conf.PingTimeout, + countErrorFunc: conf.CountError, serveG: newGoroutineLock(), pushEnabled: true, sawClientPreface: opts.SawClientPreface, @@ -491,15 +485,15 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon sc.flow.add(initialWindowSize) sc.inflow.init(initialWindowSize) sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf) - sc.hpackEncoder.SetMaxDynamicTableSizeLimit(s.maxEncoderHeaderTableSize()) + sc.hpackEncoder.SetMaxDynamicTableSizeLimit(conf.MaxEncoderHeaderTableSize) fr := NewFramer(sc.bw, c) - if s.CountError != nil { - fr.countError = s.CountError + if conf.CountError != nil { + fr.countError = conf.CountError } - fr.ReadMetaHeaders = hpack.NewDecoder(s.maxDecoderHeaderTableSize(), nil) + fr.ReadMetaHeaders = hpack.NewDecoder(conf.MaxDecoderHeaderTableSize, nil) fr.MaxHeaderListSize = sc.maxHeaderListSize() - fr.SetMaxReadFrameSize(s.maxReadFrameSize()) + fr.SetMaxReadFrameSize(conf.MaxReadFrameSize) sc.framer = fr if tc, ok := c.(connectionStater); ok { @@ -532,7 +526,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon // So for now, do nothing here again. } - if !s.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) { + if !conf.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) { // "Endpoints MAY choose to generate a connection error // (Section 5.4.1) of type INADEQUATE_SECURITY if one of // the prohibited cipher suites are negotiated." @@ -569,7 +563,7 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon opts.UpgradeRequest = nil } - sc.serve() + sc.serve(conf) } func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) { @@ -609,6 +603,7 @@ type serverConn struct { tlsState *tls.ConnectionState // shared by all handlers, like net/http remoteAddrStr string writeSched WriteScheduler + countErrorFunc func(errType string) // Everything following is owned by the serve loop; use serveG.check(): serveG goroutineLock // used to verify funcs are on serve() @@ -628,6 +623,7 @@ type serverConn struct { streams map[uint32]*stream unstartedHandlers []unstartedHandler initialStreamSendWindowSize int32 + initialStreamRecvWindowSize int32 maxFrameSize int32 peerMaxHeaderListSize uint32 // zero means unknown (default) canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case @@ -638,9 +634,14 @@ type serverConn struct { inGoAway bool // we've started to or sent GOAWAY inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop needToSendGoAway bool // we need to schedule a GOAWAY frame write + pingSent bool + sentPingData [8]byte goAwayCode ErrCode shutdownTimer timer // nil until used idleTimer timer // nil if unused + readIdleTimeout time.Duration + pingTimeout time.Duration + readIdleTimer timer // nil if unused // Owned by the writeFrameAsync goroutine: headerWriteBuf bytes.Buffer @@ -655,11 +656,7 @@ func (sc *serverConn) maxHeaderListSize() uint32 { if n <= 0 { n = http.DefaultMaxHeaderBytes } - // http2's count is in a slightly different unit and includes 32 bytes per pair. - // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. - const perFieldOverhead = 32 // per http2 spec - const typicalHeaders = 10 // conservative - return uint32(n + typicalHeaders*perFieldOverhead) + return uint32(adjustHTTP1MaxHeaderSize(int64(n))) } func (sc *serverConn) curOpenStreams() uint32 { @@ -923,7 +920,7 @@ func (sc *serverConn) notePanic() { } } -func (sc *serverConn) serve() { +func (sc *serverConn) serve(conf http2Config) { sc.serveG.check() defer sc.notePanic() defer sc.conn.Close() @@ -935,20 +932,24 @@ func (sc *serverConn) serve() { sc.vlogf("http2: server connection from %v on %p", sc.conn.RemoteAddr(), sc.hs) } + settings := writeSettings{ + {SettingMaxFrameSize, conf.MaxReadFrameSize}, + {SettingMaxConcurrentStreams, sc.advMaxStreams}, + {SettingMaxHeaderListSize, sc.maxHeaderListSize()}, + {SettingHeaderTableSize, conf.MaxDecoderHeaderTableSize}, + {SettingInitialWindowSize, uint32(sc.initialStreamRecvWindowSize)}, + } + if !disableExtendedConnectProtocol { + settings = append(settings, Setting{SettingEnableConnectProtocol, 1}) + } sc.writeFrame(FrameWriteRequest{ - write: writeSettings{ - {SettingMaxFrameSize, sc.srv.maxReadFrameSize()}, - {SettingMaxConcurrentStreams, sc.advMaxStreams}, - {SettingMaxHeaderListSize, sc.maxHeaderListSize()}, - {SettingHeaderTableSize, sc.srv.maxDecoderHeaderTableSize()}, - {SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())}, - }, + write: settings, }) sc.unackedSettings++ // Each connection starts with initialWindowSize inflow tokens. // If a higher value is configured, we add more tokens. - if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff > 0 { + if diff := conf.MaxUploadBufferPerConnection - initialWindowSize; diff > 0 { sc.sendWindowUpdate(nil, int(diff)) } @@ -968,11 +969,18 @@ func (sc *serverConn) serve() { defer sc.idleTimer.Stop() } + if conf.SendPingTimeout > 0 { + sc.readIdleTimeout = conf.SendPingTimeout + sc.readIdleTimer = sc.srv.afterFunc(conf.SendPingTimeout, sc.onReadIdleTimer) + defer sc.readIdleTimer.Stop() + } + go sc.readFrames() // closed by defer sc.conn.Close above settingsTimer := sc.srv.afterFunc(firstSettingsTimeout, sc.onSettingsTimer) defer settingsTimer.Stop() + lastFrameTime := sc.srv.now() loopNum := 0 for { loopNum++ @@ -986,6 +994,7 @@ func (sc *serverConn) serve() { case res := <-sc.wroteFrameCh: sc.wroteFrame(res) case res := <-sc.readFrameCh: + lastFrameTime = sc.srv.now() // Process any written frames before reading new frames from the client since a // written frame could have triggered a new stream to be started. if sc.writingFrameAsync { @@ -1017,6 +1026,8 @@ func (sc *serverConn) serve() { case idleTimerMsg: sc.vlogf("connection is idle") sc.goAway(ErrCodeNo) + case readIdleTimerMsg: + sc.handlePingTimer(lastFrameTime) case shutdownTimerMsg: sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr()) return @@ -1039,7 +1050,7 @@ func (sc *serverConn) serve() { // If the peer is causing us to generate a lot of control frames, // but not reading them from us, assume they are trying to make us // run out of memory. - if sc.queuedControlFrames > sc.srv.maxQueuedControlFrames() { + if sc.queuedControlFrames > maxQueuedControlFrames { sc.vlogf("http2: too many control frames in send queue, closing connection") return } @@ -1055,12 +1066,39 @@ func (sc *serverConn) serve() { } } +func (sc *serverConn) handlePingTimer(lastFrameReadTime time.Time) { + if sc.pingSent { + sc.vlogf("timeout waiting for PING response") + sc.conn.Close() + return + } + + pingAt := lastFrameReadTime.Add(sc.readIdleTimeout) + now := sc.srv.now() + if pingAt.After(now) { + // We received frames since arming the ping timer. + // Reset it for the next possible timeout. + sc.readIdleTimer.Reset(pingAt.Sub(now)) + return + } + + sc.pingSent = true + // Ignore crypto/rand.Read errors: It generally can't fail, and worse case if it does + // is we send a PING frame containing 0s. + _, _ = rand.Read(sc.sentPingData[:]) + sc.writeFrame(FrameWriteRequest{ + write: &writePing{data: sc.sentPingData}, + }) + sc.readIdleTimer.Reset(sc.pingTimeout) +} + type serverMessage int // Message values sent to serveMsgCh. var ( settingsTimerMsg = new(serverMessage) idleTimerMsg = new(serverMessage) + readIdleTimerMsg = new(serverMessage) shutdownTimerMsg = new(serverMessage) gracefulShutdownMsg = new(serverMessage) handlerDoneMsg = new(serverMessage) @@ -1068,6 +1106,7 @@ var ( func (sc *serverConn) onSettingsTimer() { sc.sendServeMsg(settingsTimerMsg) } func (sc *serverConn) onIdleTimer() { sc.sendServeMsg(idleTimerMsg) } +func (sc *serverConn) onReadIdleTimer() { sc.sendServeMsg(readIdleTimerMsg) } func (sc *serverConn) onShutdownTimer() { sc.sendServeMsg(shutdownTimerMsg) } func (sc *serverConn) sendServeMsg(msg interface{}) { @@ -1320,6 +1359,10 @@ func (sc *serverConn) wroteFrame(res frameWriteResult) { sc.writingFrame = false sc.writingFrameAsync = false + if res.err != nil { + sc.conn.Close() + } + wr := res.wr if writeEndsStream(wr.write) { @@ -1594,6 +1637,11 @@ func (sc *serverConn) processFrame(f Frame) error { func (sc *serverConn) processPing(f *PingFrame) error { sc.serveG.check() if f.IsAck() { + if sc.pingSent && sc.sentPingData == f.Data { + // This is a response to a PING we sent. + sc.pingSent = false + sc.readIdleTimer.Reset(sc.readIdleTimeout) + } // 6.7 PING: " An endpoint MUST NOT respond to PING frames // containing this flag." return nil @@ -1757,6 +1805,9 @@ func (sc *serverConn) processSetting(s Setting) error { sc.maxFrameSize = int32(s.Val) // the maximum valid s.Val is < 2^31 case SettingMaxHeaderListSize: sc.peerMaxHeaderListSize = s.Val + case SettingEnableConnectProtocol: + // Receipt of this parameter by a server does not + // have any impact default: // Unknown setting: "An endpoint that receives a SETTINGS // frame with any unknown or unsupported identifier MUST @@ -2160,7 +2211,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream st.cw.Init() st.flow.conn = &sc.flow // link to conn-level counter st.flow.add(sc.initialStreamSendWindowSize) - st.inflow.init(sc.srv.initialStreamRecvWindowSize()) + st.inflow.init(sc.initialStreamRecvWindowSize) if sc.hs.WriteTimeout > 0 { st.writeDeadline = sc.srv.afterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) } @@ -2187,11 +2238,17 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res scheme: f.PseudoValue("scheme"), authority: f.PseudoValue("authority"), path: f.PseudoValue("path"), + protocol: f.PseudoValue("protocol"), + } + + // extended connect is disabled, so we should not see :protocol + if disableExtendedConnectProtocol && rp.protocol != "" { + return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol)) } isConnect := rp.method == "CONNECT" if isConnect { - if rp.path != "" || rp.scheme != "" || rp.authority == "" { + if rp.protocol == "" && (rp.path != "" || rp.scheme != "" || rp.authority == "") { return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol)) } } else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") { @@ -2215,6 +2272,9 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res if rp.authority == "" { rp.authority = rp.header.Get("Host") } + if rp.protocol != "" { + rp.header.Set(":protocol", rp.protocol) + } rw, req, err := sc.newWriterAndRequestNoBody(st, rp) if err != nil { @@ -2241,6 +2301,7 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res type requestParam struct { method string scheme, authority, path string + protocol string header http.Header } @@ -2282,7 +2343,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r var url_ *url.URL var requestURI string - if rp.method == "CONNECT" { + if rp.method == "CONNECT" && rp.protocol == "" { url_ = &url.URL{Host: rp.authority} requestURI = rp.authority // mimic HTTP/1 server behavior } else { @@ -2855,6 +2916,11 @@ func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { return nil } +func (w *responseWriter) EnableFullDuplex() error { + // We always support full duplex responses, so this is a no-op. + return nil +} + func (w *responseWriter) Flush() { w.FlushError() } @@ -3301,7 +3367,7 @@ func (sc *serverConn) countError(name string, err error) error { if sc == nil || sc.srv == nil { return err } - f := sc.srv.CountError + f := sc.countErrorFunc if f == nil { return err } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 61f511f97a..090d0e1bdb 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -25,7 +25,6 @@ import ( "net/http" "net/http/httptrace" "net/textproto" - "os" "sort" "strconv" "strings" @@ -203,6 +202,20 @@ func (t *Transport) markNewGoroutine() { } } +func (t *Transport) now() time.Time { + if t != nil && t.transportTestHooks != nil { + return t.transportTestHooks.group.Now() + } + return time.Now() +} + +func (t *Transport) timeSince(when time.Time) time.Duration { + if t != nil && t.transportTestHooks != nil { + return t.now().Sub(when) + } + return time.Since(when) +} + // newTimer creates a new time.Timer, or a synthetic timer in tests. func (t *Transport) newTimer(d time.Duration) timer { if t.transportTestHooks != nil { @@ -227,40 +240,26 @@ func (t *Transport) contextWithTimeout(ctx context.Context, d time.Duration) (co } func (t *Transport) maxHeaderListSize() uint32 { - if t.MaxHeaderListSize == 0 { + n := int64(t.MaxHeaderListSize) + if t.t1 != nil && t.t1.MaxResponseHeaderBytes != 0 { + n = t.t1.MaxResponseHeaderBytes + if n > 0 { + n = adjustHTTP1MaxHeaderSize(n) + } + } + if n <= 0 { return 10 << 20 } - if t.MaxHeaderListSize == 0xffffffff { + if n >= 0xffffffff { return 0 } - return t.MaxHeaderListSize -} - -func (t *Transport) maxFrameReadSize() uint32 { - if t.MaxReadFrameSize == 0 { - return 0 // use the default provided by the peer - } - if t.MaxReadFrameSize < minMaxFrameSize { - return minMaxFrameSize - } - if t.MaxReadFrameSize > maxFrameSize { - return maxFrameSize - } - return t.MaxReadFrameSize + return uint32(n) } func (t *Transport) disableCompression() bool { return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) } -func (t *Transport) pingTimeout() time.Duration { - if t.PingTimeout == 0 { - return 15 * time.Second - } - return t.PingTimeout - -} - // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. // It returns an error if t1 has already been HTTP/2-enabled. // @@ -296,8 +295,8 @@ func configureTransports(t1 *http.Transport) (*Transport, error) { if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") { t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1") } - upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper { - addr := authorityAddr("https", authority) + upgradeFn := func(scheme, authority string, c net.Conn) http.RoundTripper { + addr := authorityAddr(scheme, authority) if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil { go c.Close() return erringRoundTripper{err} @@ -308,18 +307,37 @@ func configureTransports(t1 *http.Transport) (*Transport, error) { // was unknown) go c.Close() } + if scheme == "http" { + return (*unencryptedTransport)(t2) + } return t2 } - if m := t1.TLSNextProto; len(m) == 0 { - t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{ - "h2": upgradeFn, + if t1.TLSNextProto == nil { + t1.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper) + } + t1.TLSNextProto[NextProtoTLS] = func(authority string, c *tls.Conn) http.RoundTripper { + return upgradeFn("https", authority, c) + } + // The "unencrypted_http2" TLSNextProto key is used to pass off non-TLS HTTP/2 conns. + t1.TLSNextProto[nextProtoUnencryptedHTTP2] = func(authority string, c *tls.Conn) http.RoundTripper { + nc, err := unencryptedNetConnFromTLSConn(c) + if err != nil { + go c.Close() + return erringRoundTripper{err} } - } else { - m["h2"] = upgradeFn + return upgradeFn("http", authority, nc) } return t2, nil } +// unencryptedTransport is a Transport with a RoundTrip method that +// always permits http:// URLs. +type unencryptedTransport Transport + +func (t *unencryptedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + return (*Transport)(t).RoundTripOpt(req, RoundTripOpt{allowHTTP: true}) +} + func (t *Transport) connPool() ClientConnPool { t.connPoolOnce.Do(t.initConnPool) return t.connPoolOrDef @@ -339,7 +357,7 @@ type ClientConn struct { t *Transport tconn net.Conn // usually *tls.Conn, except specialized impls tlsState *tls.ConnectionState // nil only for specialized impls - reused uint32 // whether conn is being reused; atomic + atomicReused uint32 // whether conn is being reused; atomic singleUse bool // whether being used for a single http.Request getConnCalled bool // used by clientConnPool @@ -350,31 +368,54 @@ type ClientConn struct { idleTimeout time.Duration // or 0 for never idleTimer timer - mu sync.Mutex // guards following - cond *sync.Cond // hold mu; broadcast on flow/closed changes - flow outflow // our conn-level flow control quota (cs.outflow is per stream) - inflow inflow // peer's conn-level flow control - doNotReuse bool // whether conn is marked to not be reused for any future requests - closing bool - closed bool - seenSettings bool // true if we've seen a settings frame, false otherwise - wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back - goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received - goAwayDebug string // goAway frame's debug data, retained as a string - streams map[uint32]*clientStream // client-initiated - streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip - nextStreamID uint32 - pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams - pings map[[8]byte]chan struct{} // in flight ping data to notification channel - br *bufio.Reader - lastActive time.Time - lastIdle time.Time // time last idle + mu sync.Mutex // guards following + cond *sync.Cond // hold mu; broadcast on flow/closed changes + flow outflow // our conn-level flow control quota (cs.outflow is per stream) + inflow inflow // peer's conn-level flow control + doNotReuse bool // whether conn is marked to not be reused for any future requests + closing bool + closed bool + seenSettings bool // true if we've seen a settings frame, false otherwise + seenSettingsChan chan struct{} // closed when seenSettings is true or frame reading fails + wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back + goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received + goAwayDebug string // goAway frame's debug data, retained as a string + streams map[uint32]*clientStream // client-initiated + streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip + nextStreamID uint32 + pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams + pings map[[8]byte]chan struct{} // in flight ping data to notification channel + br *bufio.Reader + lastActive time.Time + lastIdle time.Time // time last idle // Settings from peer: (also guarded by wmu) - maxFrameSize uint32 - maxConcurrentStreams uint32 - peerMaxHeaderListSize uint64 - peerMaxHeaderTableSize uint32 - initialWindowSize uint32 + maxFrameSize uint32 + maxConcurrentStreams uint32 + peerMaxHeaderListSize uint64 + peerMaxHeaderTableSize uint32 + initialWindowSize uint32 + initialStreamRecvWindowSize int32 + readIdleTimeout time.Duration + pingTimeout time.Duration + extendedConnectAllowed bool + + // rstStreamPingsBlocked works around an unfortunate gRPC behavior. + // gRPC strictly limits the number of PING frames that it will receive. + // The default is two pings per two hours, but the limit resets every time + // the gRPC endpoint sends a HEADERS or DATA frame. See golang/go#70575. + // + // rstStreamPingsBlocked is set after receiving a response to a PING frame + // bundled with an RST_STREAM (see pendingResets below), and cleared after + // receiving a HEADERS or DATA frame. + rstStreamPingsBlocked bool + + // pendingResets is the number of RST_STREAM frames we have sent to the peer, + // without confirming that the peer has received them. When we send a RST_STREAM, + // we bundle it with a PING frame, unless a PING is already in flight. We count + // the reset stream against the connection's concurrency limit until we get + // a PING response. This limits the number of requests we'll try to send to a + // completely unresponsive connection. + pendingResets int // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. // Write to reqHeaderMu to lock it, read from it to unlock. @@ -432,12 +473,12 @@ type clientStream struct { sentHeaders bool // owned by clientConnReadLoop: - firstByte bool // got the first response byte - pastHeaders bool // got first MetaHeadersFrame (actual headers) - pastTrailers bool // got optional second MetaHeadersFrame (trailers) - num1xx uint8 // number of 1xx responses seen - readClosed bool // peer sent an END_STREAM flag - readAborted bool // read loop reset the stream + firstByte bool // got the first response byte + pastHeaders bool // got first MetaHeadersFrame (actual headers) + pastTrailers bool // got optional second MetaHeadersFrame (trailers) + readClosed bool // peer sent an END_STREAM flag + readAborted bool // read loop reset the stream + totalHeaderSize int64 // total size of 1xx headers seen trailer http.Header // accumulated trailers resTrailer *http.Header // client's Response.Trailer @@ -499,6 +540,7 @@ func (cs *clientStream) closeReqBodyLocked() { } type stickyErrWriter struct { + group synctestGroupInterface conn net.Conn timeout time.Duration err *error @@ -508,22 +550,9 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) { if *sew.err != nil { return 0, *sew.err } - for { - if sew.timeout != 0 { - sew.conn.SetWriteDeadline(time.Now().Add(sew.timeout)) - } - nn, err := sew.conn.Write(p[n:]) - n += nn - if n < len(p) && nn > 0 && errors.Is(err, os.ErrDeadlineExceeded) { - // Keep extending the deadline so long as we're making progress. - continue - } - if sew.timeout != 0 { - sew.conn.SetWriteDeadline(time.Time{}) - } - *sew.err = err - return n, err - } + n, err = writeWithByteTimeout(sew.group, sew.conn, sew.timeout, p) + *sew.err = err + return n, err } // noCachedConnError is the concrete type of ErrNoCachedConn, which @@ -554,6 +583,8 @@ type RoundTripOpt struct { // no cached connection is available, RoundTripOpt // will return ErrNoCachedConn. OnlyCachedConn bool + + allowHTTP bool // allow http:// URLs } func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { @@ -586,7 +617,14 @@ func authorityAddr(scheme string, authority string) (addr string) { // RoundTripOpt is like RoundTrip, but takes options. func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { - if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { + switch req.URL.Scheme { + case "https": + // Always okay. + case "http": + if !t.AllowHTTP && !opt.allowHTTP { + return nil, errors.New("http2: unencrypted HTTP/2 not enabled") + } + default: return nil, errors.New("http2: unsupported scheme") } @@ -597,7 +635,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) return nil, err } - reused := !atomic.CompareAndSwapUint32(&cc.reused, 0, 1) + reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1) traceGotConn(req, cc, reused) res, err := cc.RoundTrip(req) if err != nil && retry <= 6 { @@ -622,6 +660,22 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res } } } + if err == errClientConnNotEstablished { + // This ClientConn was created recently, + // this is the first request to use it, + // and the connection is closed and not usable. + // + // In this state, cc.idleTimer will remove the conn from the pool + // when it fires. Stop the timer and remove it here so future requests + // won't try to use this connection. + // + // If the timer has already fired and we're racing it, the redundant + // call to MarkDead is harmless. + if cc.idleTimer != nil { + cc.idleTimer.Stop() + } + t.connPool().MarkDead(cc) + } if err != nil { t.vlogf("RoundTrip failure: %v", err) return nil, err @@ -640,9 +694,10 @@ func (t *Transport) CloseIdleConnections() { } var ( - errClientConnClosed = errors.New("http2: client conn is closed") - errClientConnUnusable = errors.New("http2: client conn not usable") - errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") + errClientConnClosed = errors.New("http2: client conn is closed") + errClientConnUnusable = errors.New("http2: client conn not usable") + errClientConnNotEstablished = errors.New("http2: client conn could not be established") + errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") ) // shouldRetryRequest is called by RoundTrip when a request fails to get @@ -758,44 +813,38 @@ func (t *Transport) expectContinueTimeout() time.Duration { return t.t1.ExpectContinueTimeout } -func (t *Transport) maxDecoderHeaderTableSize() uint32 { - if v := t.MaxDecoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - -func (t *Transport) maxEncoderHeaderTableSize() uint32 { - if v := t.MaxEncoderHeaderTableSize; v > 0 { - return v - } - return initialHeaderTableSize -} - func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { return t.newClientConn(c, t.disableKeepAlives()) } func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { + conf := configFromTransport(t) cc := &ClientConn{ - t: t, - tconn: c, - readerDone: make(chan struct{}), - nextStreamID: 1, - maxFrameSize: 16 << 10, // spec default - initialWindowSize: 65535, // spec default - maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. - peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. - streams: make(map[uint32]*clientStream), - singleUse: singleUse, - wantSettingsAck: true, - pings: make(map[[8]byte]chan struct{}), - reqHeaderMu: make(chan struct{}, 1), - } + t: t, + tconn: c, + readerDone: make(chan struct{}), + nextStreamID: 1, + maxFrameSize: 16 << 10, // spec default + initialWindowSize: 65535, // spec default + initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream, + maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. + peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. + streams: make(map[uint32]*clientStream), + singleUse: singleUse, + seenSettingsChan: make(chan struct{}), + wantSettingsAck: true, + readIdleTimeout: conf.SendPingTimeout, + pingTimeout: conf.PingTimeout, + pings: make(map[[8]byte]chan struct{}), + reqHeaderMu: make(chan struct{}, 1), + lastActive: t.now(), + } + var group synctestGroupInterface if t.transportTestHooks != nil { t.markNewGoroutine() t.transportTestHooks.newclientconn(cc) c = cc.tconn + group = t.group } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) @@ -807,24 +856,23 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro // TODO: adjust this writer size to account for frame size + // MTU + crypto/tls record padding. cc.bw = bufio.NewWriter(stickyErrWriter{ + group: group, conn: c, - timeout: t.WriteByteTimeout, + timeout: conf.WriteByteTimeout, err: &cc.werr, }) cc.br = bufio.NewReader(c) cc.fr = NewFramer(cc.bw, cc.br) - if t.maxFrameReadSize() != 0 { - cc.fr.SetMaxReadFrameSize(t.maxFrameReadSize()) - } + cc.fr.SetMaxReadFrameSize(conf.MaxReadFrameSize) if t.CountError != nil { cc.fr.countError = t.CountError } - maxHeaderTableSize := t.maxDecoderHeaderTableSize() + maxHeaderTableSize := conf.MaxDecoderHeaderTableSize cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil) cc.fr.MaxHeaderListSize = t.maxHeaderListSize() cc.henc = hpack.NewEncoder(&cc.hbuf) - cc.henc.SetMaxDynamicTableSizeLimit(t.maxEncoderHeaderTableSize()) + cc.henc.SetMaxDynamicTableSizeLimit(conf.MaxEncoderHeaderTableSize) cc.peerMaxHeaderTableSize = initialHeaderTableSize if cs, ok := c.(connectionStater); ok { @@ -834,11 +882,9 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro initialSettings := []Setting{ {ID: SettingEnablePush, Val: 0}, - {ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow}, - } - if max := t.maxFrameReadSize(); max != 0 { - initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: max}) + {ID: SettingInitialWindowSize, Val: uint32(cc.initialStreamRecvWindowSize)}, } + initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: conf.MaxReadFrameSize}) if max := t.maxHeaderListSize(); max != 0 { initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max}) } @@ -848,8 +894,8 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro cc.bw.Write(clientPreface) cc.fr.WriteSettings(initialSettings...) - cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow) - cc.inflow.init(transportDefaultConnFlow + initialWindowSize) + cc.fr.WriteWindowUpdate(0, uint32(conf.MaxUploadBufferPerConnection)) + cc.inflow.init(conf.MaxUploadBufferPerConnection + initialWindowSize) cc.bw.Flush() if cc.werr != nil { cc.Close() @@ -867,7 +913,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro } func (cc *ClientConn) healthCheck() { - pingTimeout := cc.t.pingTimeout() + pingTimeout := cc.pingTimeout // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. ctx, cancel := cc.t.contextWithTimeout(context.Background(), pingTimeout) @@ -995,7 +1041,7 @@ func (cc *ClientConn) State() ClientConnState { return ClientConnState{ Closed: cc.closed, Closing: cc.closing || cc.singleUse || cc.doNotReuse || cc.goAway != nil, - StreamsActive: len(cc.streams), + StreamsActive: len(cc.streams) + cc.pendingResets, StreamsReserved: cc.streamsReserved, StreamsPending: cc.pendingRequests, LastIdle: cc.lastIdle, @@ -1027,16 +1073,38 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { // writing it. maxConcurrentOkay = true } else { - maxConcurrentOkay = int64(len(cc.streams)+cc.streamsReserved+1) <= int64(cc.maxConcurrentStreams) + // We can take a new request if the total of + // - active streams; + // - reservation slots for new streams; and + // - streams for which we have sent a RST_STREAM and a PING, + // but received no subsequent frame + // is less than the concurrency limit. + maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) } st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay && !cc.doNotReuse && int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && !cc.tooIdleLocked() + + // If this connection has never been used for a request and is closed, + // then let it take a request (which will fail). + // + // This avoids a situation where an error early in a connection's lifetime + // goes unreported. + if cc.nextStreamID == 1 && cc.streamsReserved == 0 && cc.closed { + st.canTakeNewRequest = true + } + return } +// currentRequestCountLocked reports the number of concurrency slots currently in use, +// including active streams, reserved slots, and reset streams waiting for acknowledgement. +func (cc *ClientConn) currentRequestCountLocked() int { + return len(cc.streams) + cc.streamsReserved + cc.pendingResets +} + func (cc *ClientConn) canTakeNewRequestLocked() bool { st := cc.idleStateLocked() return st.canTakeNewRequest @@ -1049,7 +1117,7 @@ func (cc *ClientConn) tooIdleLocked() bool { // times are compared based on their wall time. We don't want // to reuse a connection that's been sitting idle during // VM/laptop suspend if monotonic time was also frozen. - return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout + return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && cc.t.timeSince(cc.lastIdle.Round(0)) > cc.idleTimeout } // onIdleTimeout is called from a time.AfterFunc goroutine. It will @@ -1411,6 +1479,8 @@ func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream) cs.cleanupWriteRequest(err) } +var errExtendedConnectNotSupported = errors.New("net/http: extended connect not supported by peer") + // writeRequest sends a request. // // It returns nil after the request is written, the response read, @@ -1426,12 +1496,31 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre return err } + // wait for setting frames to be received, a server can change this value later, + // but we just wait for the first settings frame + var isExtendedConnect bool + if req.Method == "CONNECT" && req.Header.Get(":protocol") != "" { + isExtendedConnect = true + } + // Acquire the new-request lock by writing to reqHeaderMu. // This lock guards the critical section covering allocating a new stream ID // (requires mu) and creating the stream (requires wmu). if cc.reqHeaderMu == nil { panic("RoundTrip on uninitialized ClientConn") // for tests } + if isExtendedConnect { + select { + case <-cs.reqCancel: + return errRequestCanceled + case <-ctx.Done(): + return ctx.Err() + case <-cc.seenSettingsChan: + if !cc.extendedConnectAllowed { + return errExtendedConnectNotSupported + } + } + } select { case cc.reqHeaderMu <- struct{}{}: case <-cs.reqCancel: @@ -1613,6 +1702,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) { cs.reqBodyClosed = make(chan struct{}) } bodyClosed := cs.reqBodyClosed + closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil cc.mu.Unlock() if mustCloseBody { cs.reqBody.Close() @@ -1637,16 +1727,44 @@ func (cs *clientStream) cleanupWriteRequest(err error) { if cs.sentHeaders { if se, ok := err.(StreamError); ok { if se.Cause != errFromPeer { - cc.writeStreamReset(cs.ID, se.Code, err) + cc.writeStreamReset(cs.ID, se.Code, false, err) } } else { - cc.writeStreamReset(cs.ID, ErrCodeCancel, err) + // We're cancelling an in-flight request. + // + // This could be due to the server becoming unresponsive. + // To avoid sending too many requests on a dead connection, + // we let the request continue to consume a concurrency slot + // until we can confirm the server is still responding. + // We do this by sending a PING frame along with the RST_STREAM + // (unless a ping is already in flight). + // + // For simplicity, we don't bother tracking the PING payload: + // We reset cc.pendingResets any time we receive a PING ACK. + // + // We skip this if the conn is going to be closed on idle, + // because it's short lived and will probably be closed before + // we get the ping response. + ping := false + if !closeOnIdle { + cc.mu.Lock() + // rstStreamPingsBlocked works around a gRPC behavior: + // see comment on the field for details. + if !cc.rstStreamPingsBlocked { + if cc.pendingResets == 0 { + ping = true + } + cc.pendingResets++ + } + cc.mu.Unlock() + } + cc.writeStreamReset(cs.ID, ErrCodeCancel, ping, err) } } cs.bufPipe.CloseWithError(err) // no-op if already closed } else { if cs.sentHeaders && !cs.sentEndStream { - cc.writeStreamReset(cs.ID, ErrCodeNo, nil) + cc.writeStreamReset(cs.ID, ErrCodeNo, false, nil) } cs.bufPipe.CloseWithError(errRequestCanceled) } @@ -1668,12 +1786,17 @@ func (cs *clientStream) cleanupWriteRequest(err error) { // Must hold cc.mu. func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { for { - cc.lastActive = time.Now() + if cc.closed && cc.nextStreamID == 1 && cc.streamsReserved == 0 { + // This is the very first request sent to this connection. + // Return a fatal error which aborts the retry loop. + return errClientConnNotEstablished + } + cc.lastActive = cc.t.now() if cc.closed || !cc.canTakeNewRequestLocked() { return errClientConnUnusable } cc.lastIdle = time.Time{} - if int64(len(cc.streams)) < int64(cc.maxConcurrentStreams) { + if cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) { return nil } cc.pendingRequests++ @@ -1945,7 +2068,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) func validateHeaders(hdrs http.Header) string { for k, vv := range hdrs { - if !httpguts.ValidHeaderFieldName(k) { + if !httpguts.ValidHeaderFieldName(k) && k != ":protocol" { return fmt.Sprintf("name %q", k) } for _, v := range vv { @@ -1961,6 +2084,10 @@ func validateHeaders(hdrs http.Header) string { var errNilRequestURL = errors.New("http2: Request.URI is nil") +func isNormalConnect(req *http.Request) bool { + return req.Method == "CONNECT" && req.Header.Get(":protocol") == "" +} + // requires cc.wmu be held. func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { cc.hbuf.Reset() @@ -1981,7 +2108,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail } var path string - if req.Method != "CONNECT" { + if !isNormalConnect(req) { path = req.URL.RequestURI() if !validPseudoPath(path) { orig := path @@ -2018,7 +2145,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail m = http.MethodGet } f(":method", m) - if req.Method != "CONNECT" { + if !isNormalConnect(req) { f(":path", path) f(":scheme", req.URL.Scheme) } @@ -2199,7 +2326,7 @@ type resAndError struct { func (cc *ClientConn) addStreamLocked(cs *clientStream) { cs.flow.add(int32(cc.initialWindowSize)) cs.flow.setConnFlow(&cc.flow) - cs.inflow.init(transportDefaultStreamFlow) + cs.inflow.init(cc.initialStreamRecvWindowSize) cs.ID = cc.nextStreamID cc.nextStreamID += 2 cc.streams[cs.ID] = cs @@ -2215,10 +2342,10 @@ func (cc *ClientConn) forgetStreamID(id uint32) { if len(cc.streams) != slen-1 { panic("forgetting unknown stream id") } - cc.lastActive = time.Now() + cc.lastActive = cc.t.now() if len(cc.streams) == 0 && cc.idleTimer != nil { cc.idleTimer.Reset(cc.idleTimeout) - cc.lastIdle = time.Now() + cc.lastIdle = cc.t.now() } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. @@ -2278,7 +2405,6 @@ func isEOFOrNetReadError(err error) bool { func (rl *clientConnReadLoop) cleanup() { cc := rl.cc - cc.t.connPool().MarkDead(cc) defer cc.closeConn() defer close(cc.readerDone) @@ -2302,6 +2428,24 @@ func (rl *clientConnReadLoop) cleanup() { } cc.closed = true + // If the connection has never been used, and has been open for only a short time, + // leave it in the connection pool for a little while. + // + // This avoids a situation where new connections are constantly created, + // added to the pool, fail, and are removed from the pool, without any error + // being surfaced to the user. + const unusedWaitTime = 5 * time.Second + idleTime := cc.t.now().Sub(cc.lastActive) + if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime { + cc.idleTimer = cc.t.afterFunc(unusedWaitTime-idleTime, func() { + cc.t.connPool().MarkDead(cc) + }) + } else { + cc.mu.Unlock() // avoid any deadlocks in MarkDead + cc.t.connPool().MarkDead(cc) + cc.mu.Lock() + } + for _, cs := range cc.streams { select { case <-cs.peerClosed: @@ -2345,7 +2489,7 @@ func (cc *ClientConn) countReadFrameError(err error) { func (rl *clientConnReadLoop) run() error { cc := rl.cc gotSettings := false - readIdleTimeout := cc.t.ReadIdleTimeout + readIdleTimeout := cc.readIdleTimeout var t timer if readIdleTimeout != 0 { t = cc.t.afterFunc(readIdleTimeout, cc.healthCheck) @@ -2359,7 +2503,7 @@ func (rl *clientConnReadLoop) run() error { cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) } if se, ok := err.(StreamError); ok { - if cs := rl.streamByID(se.StreamID); cs != nil { + if cs := rl.streamByID(se.StreamID, notHeaderOrDataFrame); cs != nil { if se.Cause == nil { se.Cause = cc.fr.errDetail } @@ -2405,13 +2549,16 @@ func (rl *clientConnReadLoop) run() error { if VerboseLogs { cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, summarizeFrame(f), err) } + if !cc.seenSettings { + close(cc.seenSettingsChan) + } return err } } } func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error { - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, headerOrDataFrame) if cs == nil { // We'd get here if we canceled a request while the // server had its response still in flight. So if this @@ -2529,15 +2676,34 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra if f.StreamEnded() { return nil, errors.New("1xx informational response with END_STREAM flag") } - cs.num1xx++ - const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http - if cs.num1xx > max1xxResponses { - return nil, errors.New("http2: too many 1xx informational responses") - } if fn := cs.get1xxTraceFunc(); fn != nil { + // If the 1xx response is being delivered to the user, + // then they're responsible for limiting the number + // of responses. if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil { return nil, err } + } else { + // If the user didn't examine the 1xx response, then we + // limit the size of all 1xx headers. + // + // This differs a bit from the HTTP/1 implementation, which + // limits the size of all 1xx headers plus the final response. + // Use the larger limit of MaxHeaderListSize and + // net/http.Transport.MaxResponseHeaderBytes. + limit := int64(cs.cc.t.maxHeaderListSize()) + if t1 := cs.cc.t.t1; t1 != nil && t1.MaxResponseHeaderBytes > limit { + limit = t1.MaxResponseHeaderBytes + } + for _, h := range f.Fields { + cs.totalHeaderSize += int64(h.Size()) + } + if cs.totalHeaderSize > limit { + if VerboseLogs { + log.Printf("http2: 1xx informational responses too large") + } + return nil, errors.New("header list too large") + } } if statusCode == 100 { traceGot100Continue(cs.trace) @@ -2721,7 +2887,7 @@ func (b transportResponseBody) Close() error { func (rl *clientConnReadLoop) processData(f *DataFrame) error { cc := rl.cc - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, headerOrDataFrame) data := f.Data() if cs == nil { cc.mu.Lock() @@ -2856,9 +3022,22 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) { cs.abortStream(err) } -func (rl *clientConnReadLoop) streamByID(id uint32) *clientStream { +// Constants passed to streamByID for documentation purposes. +const ( + headerOrDataFrame = true + notHeaderOrDataFrame = false +) + +// streamByID returns the stream with the given id, or nil if no stream has that id. +// If headerOrData is true, it clears rst.StreamPingsBlocked. +func (rl *clientConnReadLoop) streamByID(id uint32, headerOrData bool) *clientStream { rl.cc.mu.Lock() defer rl.cc.mu.Unlock() + if headerOrData { + // Work around an unfortunate gRPC behavior. + // See comment on ClientConn.rstStreamPingsBlocked for details. + rl.cc.rstStreamPingsBlocked = false + } cs := rl.cc.streams[id] if cs != nil && !cs.readAborted { return cs @@ -2952,6 +3131,21 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { case SettingHeaderTableSize: cc.henc.SetMaxDynamicTableSize(s.Val) cc.peerMaxHeaderTableSize = s.Val + case SettingEnableConnectProtocol: + if err := s.Valid(); err != nil { + return err + } + // If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL, + // we require that it do so in the first SETTINGS frame. + // + // When we attempt to use extended CONNECT, we wait for the first + // SETTINGS frame to see if the server supports it. If we let the + // server enable the feature with a later SETTINGS frame, then + // users will see inconsistent results depending on whether we've + // seen that frame or not. + if !cc.seenSettings { + cc.extendedConnectAllowed = s.Val == 1 + } default: cc.vlogf("Unhandled Setting: %v", s) } @@ -2969,6 +3163,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { // connection can establish to our default. cc.maxConcurrentStreams = defaultMaxConcurrentStreams } + close(cc.seenSettingsChan) cc.seenSettings = true } @@ -2977,7 +3172,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { cc := rl.cc - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, notHeaderOrDataFrame) if f.StreamID != 0 && cs == nil { return nil } @@ -3006,7 +3201,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { } func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error { - cs := rl.streamByID(f.StreamID) + cs := rl.streamByID(f.StreamID, notHeaderOrDataFrame) if cs == nil { // TODO: return error if server tries to RST_STREAM an idle stream return nil @@ -3081,6 +3276,12 @@ func (rl *clientConnReadLoop) processPing(f *PingFrame) error { close(c) delete(cc.pings, f.Data) } + if cc.pendingResets > 0 { + // See clientStream.cleanupWriteRequest. + cc.pendingResets = 0 + cc.rstStreamPingsBlocked = true + cc.cond.Broadcast() + } return nil } cc := rl.cc @@ -3103,13 +3304,20 @@ func (rl *clientConnReadLoop) processPushPromise(f *PushPromiseFrame) error { return ConnectionError(ErrCodeProtocol) } -func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error) { +// writeStreamReset sends a RST_STREAM frame. +// When ping is true, it also sends a PING frame with a random payload. +func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, ping bool, err error) { // TODO: map err to more interesting error codes, once the // HTTP community comes up with some. But currently for // RST_STREAM there's no equivalent to GOAWAY frame's debug // data, and the error codes are all pretty vague ("cancel"). cc.wmu.Lock() cc.fr.WriteRSTStream(streamID, code) + if ping { + var payload [8]byte + rand.Read(payload[:]) + cc.fr.WritePing(false, payload) + } cc.bw.Flush() cc.wmu.Unlock() } @@ -3263,7 +3471,7 @@ func traceGotConn(req *http.Request, cc *ClientConn, reused bool) { cc.mu.Lock() ci.WasIdle = len(cc.streams) == 0 && reused if ci.WasIdle && !cc.lastActive.IsZero() { - ci.IdleTime = time.Since(cc.lastActive) + ci.IdleTime = cc.t.timeSince(cc.lastActive) } cc.mu.Unlock() diff --git a/vendor/golang.org/x/net/http2/unencrypted.go b/vendor/golang.org/x/net/http2/unencrypted.go new file mode 100644 index 0000000000..b2de211613 --- /dev/null +++ b/vendor/golang.org/x/net/http2/unencrypted.go @@ -0,0 +1,32 @@ +// 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 http2 + +import ( + "crypto/tls" + "errors" + "net" +) + +const nextProtoUnencryptedHTTP2 = "unencrypted_http2" + +// unencryptedNetConnFromTLSConn retrieves a net.Conn wrapped in a *tls.Conn. +// +// TLSNextProto functions accept a *tls.Conn. +// +// When passing an unencrypted HTTP/2 connection to a TLSNextProto function, +// we pass a *tls.Conn with an underlying net.Conn containing the unencrypted connection. +// To be extra careful about mistakes (accidentally dropping TLS encryption in a place +// where we want it), the tls.Conn contains a net.Conn with an UnencryptedNetConn method +// that returns the actual connection we want to use. +func unencryptedNetConnFromTLSConn(tc *tls.Conn) (net.Conn, error) { + conner, ok := tc.NetConn().(interface { + UnencryptedNetConn() net.Conn + }) + if !ok { + return nil, errors.New("http2: TLS conn unexpectedly found in unencrypted handoff") + } + return conner.UnencryptedNetConn(), nil +} diff --git a/vendor/golang.org/x/net/http2/write.go b/vendor/golang.org/x/net/http2/write.go index 33f61398a1..6ff6bee7e9 100644 --- a/vendor/golang.org/x/net/http2/write.go +++ b/vendor/golang.org/x/net/http2/write.go @@ -131,6 +131,16 @@ func (se StreamError) writeFrame(ctx writeContext) error { func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max } +type writePing struct { + data [8]byte +} + +func (w writePing) writeFrame(ctx writeContext) error { + return ctx.Framer().WritePing(false, w.data) +} + +func (w writePing) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.data) <= max } + type writePingAck struct{ pf *PingFrame } func (w writePingAck) writeFrame(ctx writeContext) error { diff --git a/vendor/modules.txt b/vendor/modules.txt index 26ebe57b82..de145bc8b6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -701,7 +701,7 @@ golang.org/x/crypto/ssh/knownhosts # golang.org/x/mod v0.21.0 ## explicit; go 1.22.0 golang.org/x/mod/semver -# golang.org/x/net v0.29.0 +# golang.org/x/net v0.33.0 ## explicit; go 1.18 golang.org/x/net/context golang.org/x/net/http/httpguts From ae09c37240a729e29c3e25626baccee86457280c Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Dec 2024 13:34:10 -0500 Subject: [PATCH 435/440] Added missing localization. --- internal/locale/locales/en-us.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 2ee84b1c67..364d1e22dc 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -610,6 +610,8 @@ err_language_updated: other: Failed to update language bundle_err_cannot_fetch_checkpoint: other: Cannot fetch checkpoint for bundle listing +package_err_cannot_fetch_checkpoint: + other: Cannot fetch checkpoint for package listing package_err_cannot_obtain_commit: other: Cannot obtain commit for package listing bundle_err_cannot_obtain_commit: From 058c68c151f6e28578e96ed060b0c75668d42fc6 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Dec 2024 14:52:15 -0500 Subject: [PATCH 436/440] Warn when writing colored output fails. Errors are sporadic and benign, but they affect automation tests, so drop the log level to warning. --- internal/output/plain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/output/plain.go b/internal/output/plain.go index a927ced6f1..14e8088bbb 100644 --- a/internal/output/plain.go +++ b/internal/output/plain.go @@ -107,7 +107,7 @@ func (f *Plain) writeNow(writer io.Writer, value string) { } _, err := colorize.Colorize(value, writer, !f.cfg.Colored) if err != nil { - logging.ErrorNoStacktrace("Writing colored output failed: %v", err) + logging.Warning("Writing colored output failed: %v", err) } } From df6a059d476c5552e49a53422e027e57d1ab3496 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 24 Dec 2024 10:21:44 -0500 Subject: [PATCH 437/440] Generalized `state install` CVE report. Accurately listing all package names being checked would often result in a horrendously long notice. Also, now that `state install` allows multiple arguments, singular "Dependency" is not good grammar. --- internal/locale/locales/en-us.yaml | 10 +++++----- internal/runbits/cves/cves.go | 4 ++-- test/integration/package_int_test.go | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 2ee84b1c67..c304e1303c 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1099,7 +1099,7 @@ progress_search: progress_platform_search: other: "• Searching for platform in the ActiveState Catalog" progress_cve_search: - other: "• Checking for vulnerabilities (CVEs) on [ACTIONABLE]{{.V0}}[/RESET] and its dependencies" + other: "• Checking for vulnerabilities (CVEs)" setup_runtime: other: "Setting Up Runtime" progress_solve: @@ -1138,13 +1138,13 @@ unstable_feature_banner: other: "[NOTICE]Beta Feature: This feature is still in beta and may be unstable.[/RESET]\n" warning_vulnerable: other: | - [ERROR]Warning: Dependency has {{.V0}} direct and {{.V1}} indirect known vulnerabilities (CVEs)[/RESET] + [ERROR]Warning: Found {{.V0}} direct and {{.V1}} indirect known vulnerabilities (CVEs)[/RESET] warning_vulnerable_indirectonly: other: | - [ERROR]Warning: Dependency has {{.V0}} indirect known vulnerabilities (CVEs)[/RESET] + [ERROR]Warning: Found {{.V0}} indirect known vulnerabilities (CVEs)[/RESET] warning_vulnerable_directonly: other: | - [ERROR]Warning: Dependency has {{.V0}} known vulnerabilities (CVEs)[/RESET] + [ERROR]Warning: Found {{.V0}} known vulnerabilities (CVEs)[/RESET] cve_critical: other: Critical cve_high: @@ -1159,7 +1159,7 @@ disable_prompting_vulnerabilities: other: To disable prompting for vulnerabilities run '[ACTIONABLE]state config set security.prompt.enabled false[/RESET]'. warning_vulnerable_short: other: | - [ERROR]Warning:[/RESET] Dependency has [ERROR]{{.V0}} known vulnerabilities (CVEs)[/RESET]. Severity: {{.V1}}. Run '[ACTIONABLE]state security[/RESET]' for more info. + [ERROR]Warning:[/RESET] Found [ERROR]{{.V0}} known vulnerabilities (CVEs)[/RESET]. Severity: {{.V1}}. Run '[ACTIONABLE]state security[/RESET]' for more info. prompt_continue_pkg_operation: other: | Do you want to continue installing this dependency despite its vulnerabilities? diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index ad7d19ff86..7275c7b3f2 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -77,8 +77,7 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil } } - names := changedRequirements(oldBuildPlan, newBuildPlan) - pg := output.StartSpinner(c.prime.Output(), locale.Tr("progress_cve_search", strings.Join(names, ", ")), constants.TerminalAnimationInterval) + pg := output.StartSpinner(c.prime.Output(), locale.T("progress_cve_search"), constants.TerminalAnimationInterval) ingredientVulnerabilities, err := model.FetchVulnerabilitiesForIngredients(c.prime.Auth(), ingredients) if err != nil { @@ -96,6 +95,7 @@ func (c *CveReport) Report(newBuildPlan *buildplan.BuildPlan, oldBuildPlan *buil pg.Stop(locale.T("progress_unsafe")) pg = nil + names := changedRequirements(oldBuildPlan, newBuildPlan) vulnerabilities := model.CombineVulnerabilities(ingredientVulnerabilities, names...) if c.prime.Prompt() == nil || !c.shouldPromptForSecurity(vulnerabilities) { diff --git a/test/integration/package_int_test.go b/test/integration/package_int_test.go index 60983fc0bd..bb3cb388e6 100644 --- a/test/integration/package_int_test.go +++ b/test/integration/package_int_test.go @@ -571,7 +571,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_NoPrompt() { // Note: this version has 2 direct vulnerabilities, and 3 indirect vulnerabilities, but since // we're not prompting, we're only showing a single count. cp = ts.Spawn("install", "urllib3@2.0.2") - cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.ExpectRe(`Warning: Found .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.ExpectExitCode(0) } @@ -594,7 +594,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Prompt() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z") - cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.ExpectRe(`Warning: Found .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("y") cp.ExpectExitCode(0) @@ -619,7 +619,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_NonInteractive() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z", "--non-interactive") - cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.ExpectRe(`Warning: Found .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Aborting because State Tool is running in non-interactive mode") cp.ExpectNotExitCode(0) } @@ -643,7 +643,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Force() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "urllib3@2.0.2", "--ts=2024-09-10T16:36:34.393Z", "--force") - cp.ExpectRe(`Warning: Dependency has .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.ExpectRe(`Warning: Found .* vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Continuing because the '--force' flag is set") cp.ExpectExitCode(0) } @@ -664,7 +664,7 @@ func (suite *PackageIntegrationTestSuite) TestCVE_Indirect() { cp.ExpectExitCode(0) cp = ts.Spawn("install", "private/ActiveState-CLI-Testing/language/python/django_dep", "--ts=2024-09-10T16:36:34.393Z") - cp.ExpectRe(`Warning: Dependency has \d+ indirect known vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) + cp.ExpectRe(`Warning: Found \d+ indirect known vulnerabilities`, e2e.RuntimeSolvingTimeoutOpt) cp.Expect("Do you want to continue") cp.SendLine("n") cp.ExpectExitCode(1) From 65599ef1ee342f0aedc2524ccc931f7ce036e326 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 24 Dec 2024 12:59:11 -0500 Subject: [PATCH 438/440] Turn on non-interactive mode for structured output. --- cmd/state/internal/cmdtree/cmdtree.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/state/internal/cmdtree/cmdtree.go b/cmd/state/internal/cmdtree/cmdtree.go index 739a3cd727..54e0fe0208 100644 --- a/cmd/state/internal/cmdtree/cmdtree.go +++ b/cmd/state/internal/cmdtree/cmdtree.go @@ -295,7 +295,12 @@ func newStateCommand(globals *globalOptions, prime *primer.Values) *captain.Comm Shorthand: "o", Description: locale.T("flag_state_output_description"), Persist: true, - Value: &globals.Output, + OnUse: func() { + if prime.Output().Type().IsStructured() { + globals.NonInteractive = true + } + }, + Value: &globals.Output, }, { Name: "non-interactive", // Name and Shorthand should be kept in sync with cmd/state/output.go From deecfc5a58732b0d4a87be8d74796ae3b30fa724 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 6 Jan 2025 11:42:01 -0500 Subject: [PATCH 439/440] Remediate CVE in go-git. --- go.mod | 12 +- go.sum | 16 + .../ProtonMail/go-crypto/ocb/ocb.go | 5 +- .../go-crypto/openpgp/armor/armor.go | 66 +- .../go-crypto/openpgp/armor/encode.go | 77 +- .../go-crypto/openpgp/canonical_text.go | 12 +- .../ProtonMail/go-crypto/openpgp/ecdh/ecdh.go | 6 +- .../go-crypto/openpgp/ed25519/ed25519.go | 115 +++ .../go-crypto/openpgp/ed448/ed448.go | 119 +++ .../go-crypto/openpgp/errors/errors.go | 70 +- .../openpgp/internal/algorithm/cipher.go | 12 +- .../openpgp/internal/ecc/curve_info.go | 9 +- .../go-crypto/openpgp/internal/ecc/ed25519.go | 10 +- .../go-crypto/openpgp/internal/ecc/ed448.go | 10 +- .../go-crypto/openpgp/key_generation.go | 133 +++- .../ProtonMail/go-crypto/openpgp/keys.go | 125 ++- .../go-crypto/openpgp/packet/aead_crypter.go | 36 +- .../go-crypto/openpgp/packet/compressed.go | 44 +- .../go-crypto/openpgp/packet/config.go | 170 +++- .../go-crypto/openpgp/packet/config_v5.go | 7 + .../go-crypto/openpgp/packet/encrypted_key.go | 424 ++++++++-- .../go-crypto/openpgp/packet/literal.go | 6 +- .../go-crypto/openpgp/packet/marker.go | 33 + .../openpgp/packet/one_pass_signature.go | 132 +++- .../go-crypto/openpgp/packet/opaque.go | 3 +- .../go-crypto/openpgp/packet/packet.go | 152 +++- .../openpgp/packet/packet_sequence.go | 222 ++++++ .../openpgp/packet/packet_unsupported.go | 24 + .../go-crypto/openpgp/packet/padding.go | 26 + .../go-crypto/openpgp/packet/private_key.go | 540 ++++++++++--- .../go-crypto/openpgp/packet/public_key.go | 482 ++++++++++-- .../go-crypto/openpgp/packet/reader.go | 159 +++- .../go-crypto/openpgp/packet/recipient.go | 15 + .../go-crypto/openpgp/packet/signature.go | 737 ++++++++++++++---- .../openpgp/packet/symmetric_key_encrypted.go | 89 ++- .../openpgp/packet/symmetrically_encrypted.go | 4 + .../packet/symmetrically_encrypted_aead.go | 15 +- .../packet/symmetrically_encrypted_mdc.go | 10 +- .../go-crypto/openpgp/packet/userattribute.go | 3 +- .../go-crypto/openpgp/packet/userid.go | 3 +- .../ProtonMail/go-crypto/openpgp/read.go | 115 +-- .../go-crypto/openpgp/read_write_test_data.go | 201 ++++- .../ProtonMail/go-crypto/openpgp/s2k/s2k.go | 45 +- .../go-crypto/openpgp/s2k/s2k_cache.go | 2 +- .../go-crypto/openpgp/s2k/s2k_config.go | 6 +- .../ProtonMail/go-crypto/openpgp/write.go | 65 +- .../go-crypto/openpgp/x25519/x25519.go | 221 ++++++ .../ProtonMail/go-crypto/openpgp/x448/x448.go | 229 ++++++ .../cyphar/filepath-securejoin/CHANGELOG.md | 209 +++++ .../cyphar/filepath-securejoin/LICENSE | 2 +- .../cyphar/filepath-securejoin/README.md | 140 +++- .../cyphar/filepath-securejoin/VERSION | 2 +- .../cyphar/filepath-securejoin/doc.go | 39 + .../gocompat_errors_go120.go | 18 + .../gocompat_errors_unsupported.go | 38 + .../gocompat_generics_go121.go | 32 + .../gocompat_generics_unsupported.go | 124 +++ .../cyphar/filepath-securejoin/join.go | 104 +-- .../filepath-securejoin/lookup_linux.go | 388 +++++++++ .../cyphar/filepath-securejoin/mkdir_linux.go | 215 +++++ .../cyphar/filepath-securejoin/open_linux.go | 103 +++ .../filepath-securejoin/openat2_linux.go | 127 +++ .../filepath-securejoin/openat_linux.go | 59 ++ .../filepath-securejoin/procfs_linux.go | 452 +++++++++++ .../cyphar/filepath-securejoin/vfs.go | 26 +- vendor/github.com/go-git/go-billy/v5/Makefile | 7 + vendor/github.com/go-git/go-billy/v5/fs.go | 2 + .../go-git/go-billy/v5/memfs/memory.go | 36 +- .../go-git/go-billy/v5/memfs/storage.go | 3 +- .../go-git/go-billy/v5/osfs/os_bound.go | 6 +- .../go-git/go-billy/v5/osfs/os_posix.go | 4 +- .../go-git/go-billy/v5/osfs/os_wasip1.go | 34 + .../go-git/go-billy/v5/util/util.go | 35 +- .../go-git/go-git/v5/COMPATIBILITY.md | 3 +- .../go-git/go-git/v5/CONTRIBUTING.md | 7 + vendor/github.com/go-git/go-git/v5/blame.go | 13 +- .../go-git/go-git/v5/config/config.go | 2 + .../go-git/v5/internal/revision/scanner.go | 7 +- vendor/github.com/go-git/go-git/v5/options.go | 26 + .../v5/plumbing/format/gitignore/dir.go | 4 + .../v5/plumbing/format/index/decoder.go | 103 ++- .../v5/plumbing/format/index/encoder.go | 94 ++- .../plumbing/format/packfile/delta_index.go | 20 +- .../plumbing/format/packfile/patch_delta.go | 21 +- .../v5/plumbing/format/pktline/scanner.go | 2 + .../go-git/v5/plumbing/object/signature.go | 1 + .../go-git/go-git/v5/plumbing/object/tree.go | 1 + .../v5/plumbing/protocol/packp/filter.go | 76 ++ .../plumbing/protocol/packp/sideband/demux.go | 2 +- .../v5/plumbing/protocol/packp/srvresp.go | 3 + .../v5/plumbing/protocol/packp/ulreq.go | 1 + .../plumbing/protocol/packp/ulreq_encode.go | 11 + .../go-git/go-git/v5/plumbing/reference.go | 4 +- .../go-git/v5/plumbing/transport/common.go | 7 +- .../v5/plumbing/transport/file/client.go | 19 +- .../v5/plumbing/transport/http/common.go | 6 +- .../v5/plumbing/transport/server/loader.go | 12 +- vendor/github.com/go-git/go-git/v5/remote.go | 33 +- .../github.com/go-git/go-git/v5/repository.go | 4 +- vendor/github.com/go-git/go-git/v5/status.go | 69 ++ .../v5/storage/filesystem/dotgit/dotgit.go | 33 +- .../go-git/v5/storage/filesystem/index.go | 2 +- .../go-git/v5/storage/filesystem/object.go | 4 +- .../github.com/go-git/go-git/v5/submodule.go | 6 +- .../go-git/v5/utils/merkletrie/change.go | 9 + .../go-git/v5/utils/merkletrie/difftree.go | 2 +- .../go-git/go-git/v5/utils/sync/bufio.go | 2 +- .../go-git/go-git/v5/utils/sync/bytes.go | 2 +- .../go-git/go-git/v5/utils/sync/zlib.go | 4 +- .../github.com/go-git/go-git/v5/worktree.go | 117 ++- .../go-git/go-git/v5/worktree_commit.go | 43 +- .../go-git/go-git/v5/worktree_linux.go | 3 +- .../go-git/go-git/v5/worktree_status.go | 34 +- .../skeema/knownhosts/CONTRIBUTING.md | 36 + vendor/github.com/skeema/knownhosts/README.md | 36 +- .../skeema/knownhosts/knownhosts.go | 317 +++++++- .../testify/assert/assertion_compare.go | 35 +- .../testify/assert/assertion_format.go | 34 +- .../testify/assert/assertion_forward.go | 68 +- .../testify/assert/assertion_order.go | 10 +- .../stretchr/testify/assert/assertions.go | 157 +++- .../testify/assert/yaml/yaml_custom.go | 25 + .../testify/assert/yaml/yaml_default.go | 37 + .../stretchr/testify/assert/yaml/yaml_fail.go | 18 + .../stretchr/testify/require/require.go | 432 +++++----- .../stretchr/testify/require/require.go.tmpl | 2 +- .../testify/require/require_forward.go | 68 +- .../stretchr/testify/require/requirements.go | 2 +- .../github.com/stretchr/testify/suite/doc.go | 4 + vendor/modules.txt | 25 +- 130 files changed, 7977 insertions(+), 1363 deletions(-) create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go create mode 100644 vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md create mode 100644 vendor/github.com/cyphar/filepath-securejoin/doc.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/open_linux.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/openat_linux.go create mode 100644 vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go create mode 100644 vendor/github.com/go-git/go-billy/v5/osfs/os_wasip1.go create mode 100644 vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go create mode 100644 vendor/github.com/skeema/knownhosts/CONTRIBUTING.md create mode 100644 vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go create mode 100644 vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go create mode 100644 vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go diff --git a/go.mod b/go.mod index 55ece8c997..df547a4c1e 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/spf13/cast v1.3.0 github.com/spf13/cobra v1.1.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/thoas/go-funk v0.8.0 github.com/vbauerster/mpb/v7 v7.1.5 github.com/vektah/gqlparser/v2 v2.5.16 @@ -75,7 +75,7 @@ require ( github.com/charmbracelet/bubbles v0.18.0 github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/lipgloss v0.9.1 - github.com/go-git/go-git/v5 v5.12.0 + github.com/go-git/go-git/v5 v5.13.1 github.com/gowebpki/jcs v1.0.1 github.com/klauspost/compress v1.11.4 github.com/mholt/archiver/v3 v3.5.1 @@ -86,14 +86,14 @@ require ( dario.cat/mergo v1.0.0 // indirect github.com/ActiveState/pty v0.0.0-20230628221854-6fb90eb08a14 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v1.0.0 // indirect + github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/andybalholm/brotli v1.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-git/go-billy/v5 v5.6.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // indirect @@ -109,7 +109,7 @@ require ( github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/skeema/knownhosts v1.2.2 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/sosodev/duration v1.3.1 // indirect golang.org/x/sync v0.10.0 // indirect ) diff --git a/go.sum b/go.sum index fbcccef181..ca20c3449c 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0= github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -131,6 +133,8 @@ github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/dave/jennifer v0.18.0 h1:fhwWYwRltL8wW567TWRHCstLaBCEsk5M5DE4rrMsi94= github.com/dave/jennifer v0.18.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -150,6 +154,7 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -168,16 +173,21 @@ github.com/gammazero/workerpool v1.1.1/go.mod h1:5BN0IJVRjSFAypo9QTJCaWdijjNz9Jj github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= +github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M= +github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -549,6 +559,7 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -597,6 +608,7 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rollbar/rollbar-go v1.1.0 h1:3ysiHp3ep8W50ykgBMCKXJGaK2Jdivru7SW9EYfAo+M= github.com/rollbar/rollbar-go v1.1.0/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -621,6 +633,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skratchdot/open-golang v0.0.0-20190104022628-a2dfa6d0dab6 h1:cGT4dcuEyBwwu/v6tosyqcDp2yoIo/LwjMGixUvg3nU= github.com/skratchdot/open-golang v0.0.0-20190104022628-a2dfa6d0dab6/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -654,6 +668,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/thoas/go-funk v0.8.0 h1:JP9tKSvnpFVclYgDM0Is7FD9M4fhPvqA0s0BsXmzSRQ= github.com/thoas/go-funk v0.8.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= diff --git a/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go b/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go index 1a6f73502e..5022285b44 100644 --- a/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go +++ b/vendor/github.com/ProtonMail/go-crypto/ocb/ocb.go @@ -18,8 +18,9 @@ import ( "crypto/cipher" "crypto/subtle" "errors" - "github.com/ProtonMail/go-crypto/internal/byteutil" "math/bits" + + "github.com/ProtonMail/go-crypto/internal/byteutil" ) type ocb struct { @@ -153,7 +154,7 @@ func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte { truncatedNonce := make([]byte, len(nonce)) copy(truncatedNonce, nonce) truncatedNonce[len(truncatedNonce)-1] &= 192 - Ktop := make([]byte, blockSize) + var Ktop []byte if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) { Ktop = o.reusableKtop.Ktop } else { diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go index d7af9141e3..e0a677f284 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.go @@ -23,7 +23,7 @@ import ( // Headers // // base64-encoded Bytes -// '=' base64 encoded checksum +// '=' base64 encoded checksum (optional) not checked anymore // -----END Type----- // // where Headers is a possibly empty sequence of Key: Value lines. @@ -40,36 +40,15 @@ type Block struct { var ArmorCorrupt error = errors.StructuralError("armor invalid") -const crc24Init = 0xb704ce -const crc24Poly = 0x1864cfb -const crc24Mask = 0xffffff - -// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 -func crc24(crc uint32, d []byte) uint32 { - for _, b := range d { - crc ^= uint32(b) << 16 - for i := 0; i < 8; i++ { - crc <<= 1 - if crc&0x1000000 != 0 { - crc ^= crc24Poly - } - } - } - return crc -} - var armorStart = []byte("-----BEGIN ") var armorEnd = []byte("-----END ") var armorEndOfLine = []byte("-----") -// lineReader wraps a line based reader. It watches for the end of an armor -// block and records the expected CRC value. +// lineReader wraps a line based reader. It watches for the end of an armor block type lineReader struct { - in *bufio.Reader - buf []byte - eof bool - crc uint32 - crcSet bool + in *bufio.Reader + buf []byte + eof bool } func (l *lineReader) Read(p []byte) (n int, err error) { @@ -98,26 +77,9 @@ func (l *lineReader) Read(p []byte) (n int, err error) { if len(line) == 5 && line[0] == '=' { // This is the checksum line - var expectedBytes [3]byte - var m int - m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) - if m != 3 || err != nil { - return - } - l.crc = uint32(expectedBytes[0])<<16 | - uint32(expectedBytes[1])<<8 | - uint32(expectedBytes[2]) - - line, _, err = l.in.ReadLine() - if err != nil && err != io.EOF { - return - } - if !bytes.HasPrefix(line, armorEnd) { - return 0, ArmorCorrupt - } + // Don't check the checksum l.eof = true - l.crcSet = true return 0, io.EOF } @@ -138,23 +100,14 @@ func (l *lineReader) Read(p []byte) (n int, err error) { return } -// openpgpReader passes Read calls to the underlying base64 decoder, but keeps -// a running CRC of the resulting data and checks the CRC against the value -// found by the lineReader at EOF. +// openpgpReader passes Read calls to the underlying base64 decoder. type openpgpReader struct { - lReader *lineReader - b64Reader io.Reader - currentCRC uint32 + lReader *lineReader + b64Reader io.Reader } func (r *openpgpReader) Read(p []byte) (n int, err error) { n, err = r.b64Reader.Read(p) - r.currentCRC = crc24(r.currentCRC, p[:n]) - - if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { - return 0, ArmorCorrupt - } - return } @@ -222,7 +175,6 @@ TryNextBlock: } p.lReader.in = r - p.oReader.currentCRC = crc24Init p.oReader.lReader = &p.lReader p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) p.Body = &p.oReader diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go index 5b6e16c19d..112f98b835 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.go @@ -14,6 +14,23 @@ var blockEnd = []byte("\n=") var newline = []byte("\n") var armorEndOfLineOut = []byte("-----\n") +const crc24Init = 0xb704ce +const crc24Poly = 0x1864cfb + +// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 +func crc24(crc uint32, d []byte) uint32 { + for _, b := range d { + crc ^= uint32(b) << 16 + for i := 0; i < 8; i++ { + crc <<= 1 + if crc&0x1000000 != 0 { + crc ^= crc24Poly + } + } + } + return crc +} + // writeSlices writes its arguments to the given Writer. func writeSlices(out io.Writer, slices ...[]byte) (err error) { for _, s := range slices { @@ -99,15 +116,18 @@ func (l *lineBreaker) Close() (err error) { // // encoding -> base64 encoder -> lineBreaker -> out type encoding struct { - out io.Writer - breaker *lineBreaker - b64 io.WriteCloser - crc uint32 - blockType []byte + out io.Writer + breaker *lineBreaker + b64 io.WriteCloser + crc uint32 + crcEnabled bool + blockType []byte } func (e *encoding) Write(data []byte) (n int, err error) { - e.crc = crc24(e.crc, data) + if e.crcEnabled { + e.crc = crc24(e.crc, data) + } return e.b64.Write(data) } @@ -118,20 +138,21 @@ func (e *encoding) Close() (err error) { } e.breaker.Close() - var checksumBytes [3]byte - checksumBytes[0] = byte(e.crc >> 16) - checksumBytes[1] = byte(e.crc >> 8) - checksumBytes[2] = byte(e.crc) + if e.crcEnabled { + var checksumBytes [3]byte + checksumBytes[0] = byte(e.crc >> 16) + checksumBytes[1] = byte(e.crc >> 8) + checksumBytes[2] = byte(e.crc) - var b64ChecksumBytes [4]byte - base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) + var b64ChecksumBytes [4]byte + base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) - return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) + return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) + } + return writeSlices(e.out, newline, armorEnd, e.blockType, armorEndOfLine) } -// Encode returns a WriteCloser which will encode the data written to it in -// OpenPGP armor. -func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { +func encode(out io.Writer, blockType string, headers map[string]string, checksum bool) (w io.WriteCloser, err error) { bType := []byte(blockType) err = writeSlices(out, armorStart, bType, armorEndOfLineOut) if err != nil { @@ -151,11 +172,27 @@ func Encode(out io.Writer, blockType string, headers map[string]string) (w io.Wr } e := &encoding{ - out: out, - breaker: newLineBreaker(out, 64), - crc: crc24Init, - blockType: bType, + out: out, + breaker: newLineBreaker(out, 64), + blockType: bType, + crc: crc24Init, + crcEnabled: checksum, } e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) return e, nil } + +// Encode returns a WriteCloser which will encode the data written to it in +// OpenPGP armor. +func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { + return encode(out, blockType, headers, true) +} + +// EncodeWithChecksumOption returns a WriteCloser which will encode the data written to it in +// OpenPGP armor and provides the option to include a checksum. +// When forming ASCII Armor, the CRC24 footer SHOULD NOT be generated, +// unless interoperability with implementations that require the CRC24 footer +// to be present is a concern. +func EncodeWithChecksumOption(out io.Writer, blockType string, headers map[string]string, doChecksum bool) (w io.WriteCloser, err error) { + return encode(out, blockType, headers, doChecksum) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go index a94f6150c4..5b40e1375d 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.go @@ -30,8 +30,12 @@ func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) { if c == '\r' { *s = 1 } else if c == '\n' { - cw.Write(buf[start:i]) - cw.Write(newline) + if _, err := cw.Write(buf[start:i]); err != nil { + return 0, err + } + if _, err := cw.Write(newline); err != nil { + return 0, err + } start = i + 1 } case 1: @@ -39,7 +43,9 @@ func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) { } } - cw.Write(buf[start:]) + if _, err := cw.Write(buf[start:]); err != nil { + return 0, err + } return len(buf), nil } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go index c895bad6bb..db8fb163b6 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.go @@ -163,13 +163,9 @@ func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLead if _, err := param.Write([]byte("Anonymous Sender ")); err != nil { return nil, err } - // For v5 keys, the 20 leftmost octets of the fingerprint are used. - if _, err := param.Write(fingerprint[:20]); err != nil { + if _, err := param.Write(fingerprint[:]); err != nil { return nil, err } - if param.Len()-len(curveOID) != 45 { - return nil, errors.New("ecdh: malformed KDF Param") - } // MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param ); h := pub.KDF.Hash.New() diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go new file mode 100644 index 0000000000..6abdf7c446 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed25519/ed25519.go @@ -0,0 +1,115 @@ +// Package ed25519 implements the ed25519 signature algorithm for OpenPGP +// as defined in the Open PGP crypto refresh. +package ed25519 + +import ( + "crypto/subtle" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + ed25519lib "github.com/cloudflare/circl/sign/ed25519" +) + +const ( + // PublicKeySize is the size, in bytes, of public keys in this package. + PublicKeySize = ed25519lib.PublicKeySize + // SeedSize is the size, in bytes, of private key seeds. + // The private key representation used by RFC 8032. + SeedSize = ed25519lib.SeedSize + // SignatureSize is the size, in bytes, of signatures generated and verified by this package. + SignatureSize = ed25519lib.SignatureSize +) + +type PublicKey struct { + // Point represents the elliptic curve point of the public key. + Point []byte +} + +type PrivateKey struct { + PublicKey + // Key the private key representation by RFC 8032, + // encoded as seed | pub key point. + Key []byte +} + +// NewPublicKey creates a new empty ed25519 public key. +func NewPublicKey() *PublicKey { + return &PublicKey{} +} + +// NewPrivateKey creates a new empty private key referencing the public key. +func NewPrivateKey(key PublicKey) *PrivateKey { + return &PrivateKey{ + PublicKey: key, + } +} + +// Seed returns the ed25519 private key secret seed. +// The private key representation by RFC 8032. +func (pk *PrivateKey) Seed() []byte { + return pk.Key[:SeedSize] +} + +// MarshalByteSecret returns the underlying 32 byte seed of the private key. +func (pk *PrivateKey) MarshalByteSecret() []byte { + return pk.Seed() +} + +// UnmarshalByteSecret computes the private key from the secret seed +// and stores it in the private key object. +func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error { + sk.Key = ed25519lib.NewKeyFromSeed(seed) + return nil +} + +// GenerateKey generates a fresh private key with the provided randomness source. +func GenerateKey(rand io.Reader) (*PrivateKey, error) { + publicKey, privateKey, err := ed25519lib.GenerateKey(rand) + if err != nil { + return nil, err + } + privateKeyOut := new(PrivateKey) + privateKeyOut.PublicKey.Point = publicKey[:] + privateKeyOut.Key = privateKey[:] + return privateKeyOut, nil +} + +// Sign signs a message with the ed25519 algorithm. +// priv MUST be a valid key! Check this with Validate() before use. +func Sign(priv *PrivateKey, message []byte) ([]byte, error) { + return ed25519lib.Sign(priv.Key, message), nil +} + +// Verify verifies an ed25519 signature. +func Verify(pub *PublicKey, message []byte, signature []byte) bool { + return ed25519lib.Verify(pub.Point, message, signature) +} + +// Validate checks if the ed25519 private key is valid. +func Validate(priv *PrivateKey) error { + expectedPrivateKey := ed25519lib.NewKeyFromSeed(priv.Seed()) + if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 { + return errors.KeyInvalidError("ed25519: invalid ed25519 secret") + } + if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 { + return errors.KeyInvalidError("ed25519: invalid ed25519 public key") + } + return nil +} + +// ENCODING/DECODING signature: + +// WriteSignature encodes and writes an ed25519 signature to writer. +func WriteSignature(writer io.Writer, signature []byte) error { + _, err := writer.Write(signature) + return err +} + +// ReadSignature decodes an ed25519 signature from a reader. +func ReadSignature(reader io.Reader) ([]byte, error) { + signature := make([]byte, SignatureSize) + if _, err := io.ReadFull(reader, signature); err != nil { + return nil, err + } + return signature, nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go new file mode 100644 index 0000000000..b11fb4fb17 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/ed448/ed448.go @@ -0,0 +1,119 @@ +// Package ed448 implements the ed448 signature algorithm for OpenPGP +// as defined in the Open PGP crypto refresh. +package ed448 + +import ( + "crypto/subtle" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + ed448lib "github.com/cloudflare/circl/sign/ed448" +) + +const ( + // PublicKeySize is the size, in bytes, of public keys in this package. + PublicKeySize = ed448lib.PublicKeySize + // SeedSize is the size, in bytes, of private key seeds. + // The private key representation used by RFC 8032. + SeedSize = ed448lib.SeedSize + // SignatureSize is the size, in bytes, of signatures generated and verified by this package. + SignatureSize = ed448lib.SignatureSize +) + +type PublicKey struct { + // Point represents the elliptic curve point of the public key. + Point []byte +} + +type PrivateKey struct { + PublicKey + // Key the private key representation by RFC 8032, + // encoded as seed | public key point. + Key []byte +} + +// NewPublicKey creates a new empty ed448 public key. +func NewPublicKey() *PublicKey { + return &PublicKey{} +} + +// NewPrivateKey creates a new empty private key referencing the public key. +func NewPrivateKey(key PublicKey) *PrivateKey { + return &PrivateKey{ + PublicKey: key, + } +} + +// Seed returns the ed448 private key secret seed. +// The private key representation by RFC 8032. +func (pk *PrivateKey) Seed() []byte { + return pk.Key[:SeedSize] +} + +// MarshalByteSecret returns the underlying seed of the private key. +func (pk *PrivateKey) MarshalByteSecret() []byte { + return pk.Seed() +} + +// UnmarshalByteSecret computes the private key from the secret seed +// and stores it in the private key object. +func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error { + sk.Key = ed448lib.NewKeyFromSeed(seed) + return nil +} + +// GenerateKey generates a fresh private key with the provided randomness source. +func GenerateKey(rand io.Reader) (*PrivateKey, error) { + publicKey, privateKey, err := ed448lib.GenerateKey(rand) + if err != nil { + return nil, err + } + privateKeyOut := new(PrivateKey) + privateKeyOut.PublicKey.Point = publicKey[:] + privateKeyOut.Key = privateKey[:] + return privateKeyOut, nil +} + +// Sign signs a message with the ed448 algorithm. +// priv MUST be a valid key! Check this with Validate() before use. +func Sign(priv *PrivateKey, message []byte) ([]byte, error) { + // Ed448 is used with the empty string as a context string. + // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7 + return ed448lib.Sign(priv.Key, message, ""), nil +} + +// Verify verifies a ed448 signature +func Verify(pub *PublicKey, message []byte, signature []byte) bool { + // Ed448 is used with the empty string as a context string. + // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7 + return ed448lib.Verify(pub.Point, message, signature, "") +} + +// Validate checks if the ed448 private key is valid +func Validate(priv *PrivateKey) error { + expectedPrivateKey := ed448lib.NewKeyFromSeed(priv.Seed()) + if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 { + return errors.KeyInvalidError("ed448: invalid ed448 secret") + } + if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 { + return errors.KeyInvalidError("ed448: invalid ed448 public key") + } + return nil +} + +// ENCODING/DECODING signature: + +// WriteSignature encodes and writes an ed448 signature to writer. +func WriteSignature(writer io.Writer, signature []byte) error { + _, err := writer.Write(signature) + return err +} + +// ReadSignature decodes an ed448 signature from a reader. +func ReadSignature(reader io.Reader) ([]byte, error) { + signature := make([]byte, SignatureSize) + if _, err := io.ReadFull(reader, signature); err != nil { + return nil, err + } + return signature, nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go index 17e2bcfed2..0eb3937b39 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go @@ -9,6 +9,18 @@ import ( "strconv" ) +var ( + // ErrDecryptSessionKeyParsing is a generic error message for parsing errors in decrypted data + // to reduce the risk of oracle attacks. + ErrDecryptSessionKeyParsing = DecryptWithSessionKeyError("parsing error") + // ErrAEADTagVerification is returned if one of the tag verifications in SEIPDv2 fails + ErrAEADTagVerification error = DecryptWithSessionKeyError("AEAD tag verification failed") + // ErrMDCHashMismatch + ErrMDCHashMismatch error = SignatureError("MDC hash mismatch") + // ErrMDCMissing + ErrMDCMissing error = SignatureError("MDC packet not found") +) + // A StructuralError is returned when OpenPGP data is found to be syntactically // invalid. type StructuralError string @@ -17,6 +29,34 @@ func (s StructuralError) Error() string { return "openpgp: invalid data: " + string(s) } +// A DecryptWithSessionKeyError is returned when a failure occurs when reading from symmetrically decrypted data or +// an authentication tag verification fails. +// Such an error indicates that the supplied session key is likely wrong or the data got corrupted. +type DecryptWithSessionKeyError string + +func (s DecryptWithSessionKeyError) Error() string { + return "openpgp: decryption with session key failed: " + string(s) +} + +// HandleSensitiveParsingError handles parsing errors when reading data from potentially decrypted data. +// The function makes parsing errors generic to reduce the risk of oracle attacks in SEIPDv1. +func HandleSensitiveParsingError(err error, decrypted bool) error { + if !decrypted { + // Data was not encrypted so we return the inner error. + return err + } + // The data is read from a stream that decrypts using a session key; + // therefore, we need to handle parsing errors appropriately. + // This is essential to mitigate the risk of oracle attacks. + if decError, ok := err.(*DecryptWithSessionKeyError); ok { + return decError + } + if decError, ok := err.(DecryptWithSessionKeyError); ok { + return decError + } + return ErrDecryptSessionKeyParsing +} + // UnsupportedError indicates that, although the OpenPGP data is valid, it // makes use of currently unimplemented features. type UnsupportedError string @@ -41,9 +81,6 @@ func (b SignatureError) Error() string { return "openpgp: invalid signature: " + string(b) } -var ErrMDCHashMismatch error = SignatureError("MDC hash mismatch") -var ErrMDCMissing error = SignatureError("MDC packet not found") - type signatureExpiredError int func (se signatureExpiredError) Error() string { @@ -58,6 +95,14 @@ func (ke keyExpiredError) Error() string { return "openpgp: key expired" } +var ErrSignatureOlderThanKey error = signatureOlderThanKeyError(0) + +type signatureOlderThanKeyError int + +func (ske signatureOlderThanKeyError) Error() string { + return "openpgp: signature is older than the key" +} + var ErrKeyExpired error = keyExpiredError(0) type keyIncorrectError int @@ -92,12 +137,24 @@ func (keyRevokedError) Error() string { var ErrKeyRevoked error = keyRevokedError(0) +type WeakAlgorithmError string + +func (e WeakAlgorithmError) Error() string { + return "openpgp: weak algorithms are rejected: " + string(e) +} + type UnknownPacketTypeError uint8 func (upte UnknownPacketTypeError) Error() string { return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) } +type CriticalUnknownPacketTypeError uint8 + +func (upte CriticalUnknownPacketTypeError) Error() string { + return "openpgp: unknown critical packet type: " + strconv.Itoa(int(upte)) +} + // AEADError indicates that there is a problem when initializing or using a // AEAD instance, configuration struct, nonces or index values. type AEADError string @@ -114,3 +171,10 @@ type ErrDummyPrivateKey string func (dke ErrDummyPrivateKey) Error() string { return "openpgp: s2k GNU dummy key: " + string(dke) } + +// ErrMalformedMessage results when the packet sequence is incorrect +type ErrMalformedMessage string + +func (dke ErrMalformedMessage) Error() string { + return "openpgp: malformed message " + string(dke) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go index 5760cff80e..c76a75bcda 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.go @@ -51,24 +51,14 @@ func (sk CipherFunction) Id() uint8 { return uint8(sk) } -var keySizeByID = map[uint8]int{ - TripleDES.Id(): 24, - CAST5.Id(): cast5.KeySize, - AES128.Id(): 16, - AES192.Id(): 24, - AES256.Id(): 32, -} - // KeySize returns the key size, in bytes, of cipher. func (cipher CipherFunction) KeySize() int { switch cipher { - case TripleDES: - return 24 case CAST5: return cast5.KeySize case AES128: return 16 - case AES192: + case AES192, TripleDES: return 24 case AES256: return 32 diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go index 35751034dd..0da2d0d852 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.go @@ -4,11 +4,14 @@ package ecc import ( "bytes" "crypto/elliptic" + "github.com/ProtonMail/go-crypto/bitcurves" "github.com/ProtonMail/go-crypto/brainpool" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" ) +const Curve25519GenName = "Curve25519" + type CurveInfo struct { GenName string Oid *encoding.OID @@ -42,19 +45,19 @@ var Curves = []CurveInfo{ }, { // Curve25519 - GenName: "Curve25519", + GenName: Curve25519GenName, Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}), Curve: NewCurve25519(), }, { - // X448 + // x448 GenName: "Curve448", Oid: encoding.NewOID([]byte{0x2B, 0x65, 0x6F}), Curve: NewX448(), }, { // Ed25519 - GenName: "Curve25519", + GenName: Curve25519GenName, Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}), Curve: NewEd25519(), }, diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go index 54a08a8a38..5a4c3a8596 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.go @@ -2,6 +2,7 @@ package ecc import ( + "bytes" "crypto/subtle" "io" @@ -90,7 +91,14 @@ func (c *ed25519) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) { } func getEd25519Sk(publicKey, privateKey []byte) ed25519lib.PrivateKey { - return append(privateKey, publicKey...) + privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey) + + if privateKeyCap >= privateKeyLen+publicKeyLen && + bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) { + return privateKey[:privateKeyLen+publicKeyLen] + } + + return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...) } func (c *ed25519) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) { diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go index 18cd80434b..b6edda7480 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.go @@ -2,6 +2,7 @@ package ecc import ( + "bytes" "crypto/subtle" "io" @@ -84,7 +85,14 @@ func (c *ed448) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) { } func getEd448Sk(publicKey, privateKey []byte) ed448lib.PrivateKey { - return append(privateKey, publicKey...) + privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey) + + if privateKeyCap >= privateKeyLen+publicKeyLen && + bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) { + return privateKey[:privateKeyLen+publicKeyLen] + } + + return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...) } func (c *ed448) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) { diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go index 0e71934cd9..77213f66be 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.go @@ -15,11 +15,15 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/ecdh" "github.com/ProtonMail/go-crypto/openpgp/ecdsa" + "github.com/ProtonMail/go-crypto/openpgp/ed25519" + "github.com/ProtonMail/go-crypto/openpgp/ed448" "github.com/ProtonMail/go-crypto/openpgp/eddsa" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" "github.com/ProtonMail/go-crypto/openpgp/packet" + "github.com/ProtonMail/go-crypto/openpgp/x25519" + "github.com/ProtonMail/go-crypto/openpgp/x448" ) // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a @@ -36,8 +40,10 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err return nil, err } primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw) - if config != nil && config.V5Keys { - primary.UpgradeToV5() + if config.V6() { + if err := primary.UpgradeToV6(); err != nil { + return nil, err + } } e := &Entity{ @@ -45,9 +51,25 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err PrivateKey: primary, Identities: make(map[string]*Identity), Subkeys: []Subkey{}, + Signatures: []*packet.Signature{}, + } + + if config.V6() { + // In v6 keys algorithm preferences should be stored in direct key signatures + selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeDirectSignature, config) + err = writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config) + if err != nil { + return nil, err + } + err = selfSignature.SignDirectKeyBinding(&primary.PublicKey, primary, config) + if err != nil { + return nil, err + } + e.Signatures = append(e.Signatures, selfSignature) + e.SelfSignature = selfSignature } - err = e.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs) + err = e.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs, !config.V6()) if err != nil { return nil, err } @@ -65,32 +87,19 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err func (t *Entity) AddUserId(name, comment, email string, config *packet.Config) error { creationTime := config.Now() keyLifetimeSecs := config.KeyLifetime() - return t.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs) + return t.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs, !config.V6()) } -func (t *Entity) addUserId(name, comment, email string, config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32) error { - uid := packet.NewUserId(name, comment, email) - if uid == nil { - return errors.InvalidArgumentError("user id field contained invalid characters") - } - - if _, ok := t.Identities[uid.Id]; ok { - return errors.InvalidArgumentError("user id exist") - } - - primary := t.PrivateKey - - isPrimaryId := len(t.Identities) == 0 +func writeKeyProperties(selfSignature *packet.Signature, creationTime time.Time, keyLifetimeSecs uint32, config *packet.Config) error { + advertiseAead := config.AEAD() != nil - selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config) selfSignature.CreationTime = creationTime selfSignature.KeyLifetimeSecs = &keyLifetimeSecs - selfSignature.IsPrimaryId = &isPrimaryId selfSignature.FlagsValid = true selfSignature.FlagSign = true selfSignature.FlagCertify = true selfSignature.SEIPDv1 = true // true by default, see 5.8 vs. 5.14 - selfSignature.SEIPDv2 = config.AEAD() != nil + selfSignature.SEIPDv2 = advertiseAead // Set the PreferredHash for the SelfSignature from the packet.Config. // If it is not the must-implement algorithm from rfc4880bis, append that. @@ -119,18 +128,44 @@ func (t *Entity) addUserId(name, comment, email string, config *packet.Config, c selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression())) } - // And for DefaultMode. - modes := []uint8{uint8(config.AEAD().Mode())} - if config.AEAD().Mode() != packet.AEADModeOCB { - modes = append(modes, uint8(packet.AEADModeOCB)) + if advertiseAead { + // Get the preferred AEAD mode from the packet.Config. + // If it is not the must-implement algorithm from rfc9580, append that. + modes := []uint8{uint8(config.AEAD().Mode())} + if config.AEAD().Mode() != packet.AEADModeOCB { + modes = append(modes, uint8(packet.AEADModeOCB)) + } + + // For preferred (AES256, GCM), we'll generate (AES256, GCM), (AES256, OCB), (AES128, GCM), (AES128, OCB) + for _, cipher := range selfSignature.PreferredSymmetric { + for _, mode := range modes { + selfSignature.PreferredCipherSuites = append(selfSignature.PreferredCipherSuites, [2]uint8{cipher, mode}) + } + } + } + return nil +} + +func (t *Entity) addUserId(name, comment, email string, config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32, writeProperties bool) error { + uid := packet.NewUserId(name, comment, email) + if uid == nil { + return errors.InvalidArgumentError("user id field contained invalid characters") + } + + if _, ok := t.Identities[uid.Id]; ok { + return errors.InvalidArgumentError("user id exist") } - // For preferred (AES256, GCM), we'll generate (AES256, GCM), (AES256, OCB), (AES128, GCM), (AES128, OCB) - for _, cipher := range selfSignature.PreferredSymmetric { - for _, mode := range modes { - selfSignature.PreferredCipherSuites = append(selfSignature.PreferredCipherSuites, [2]uint8{cipher, mode}) + primary := t.PrivateKey + isPrimaryId := len(t.Identities) == 0 + selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config) + if writeProperties { + err := writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config) + if err != nil { + return err } } + selfSignature.IsPrimaryId = &isPrimaryId // User ID binding signature err := selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config) @@ -158,8 +193,10 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error { } sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw) sub.IsSubkey = true - if config != nil && config.V5Keys { - sub.UpgradeToV5() + if config.V6() { + if err := sub.UpgradeToV6(); err != nil { + return err + } } subkey := Subkey{ @@ -203,8 +240,10 @@ func (e *Entity) addEncryptionSubkey(config *packet.Config, creationTime time.Ti } sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw) sub.IsSubkey = true - if config != nil && config.V5Keys { - sub.UpgradeToV5() + if config.V6() { + if err := sub.UpgradeToV6(); err != nil { + return err + } } subkey := Subkey{ @@ -242,6 +281,11 @@ func newSigner(config *packet.Config) (signer interface{}, err error) { } return rsa.GenerateKey(config.Random(), bits) case packet.PubKeyAlgoEdDSA: + if config.V6() { + // Implementations MUST NOT accept or generate v6 key material + // using the deprecated OIDs. + return nil, errors.InvalidArgumentError("EdDSALegacy cannot be used for v6 keys") + } curve := ecc.FindEdDSAByGenName(string(config.CurveName())) if curve == nil { return nil, errors.InvalidArgumentError("unsupported curve") @@ -263,6 +307,18 @@ func newSigner(config *packet.Config) (signer interface{}, err error) { return nil, err } return priv, nil + case packet.PubKeyAlgoEd25519: + priv, err := ed25519.GenerateKey(config.Random()) + if err != nil { + return nil, err + } + return priv, nil + case packet.PubKeyAlgoEd448: + priv, err := ed448.GenerateKey(config.Random()) + if err != nil { + return nil, err + } + return priv, nil default: return nil, errors.InvalidArgumentError("unsupported public key algorithm") } @@ -285,6 +341,13 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { case packet.PubKeyAlgoEdDSA, packet.PubKeyAlgoECDSA: fallthrough // When passing EdDSA or ECDSA, we generate an ECDH subkey case packet.PubKeyAlgoECDH: + if config.V6() && + (config.CurveName() == packet.Curve25519 || + config.CurveName() == packet.Curve448) { + // Implementations MUST NOT accept or generate v6 key material + // using the deprecated OIDs. + return nil, errors.InvalidArgumentError("ECDH with Curve25519/448 legacy cannot be used for v6 keys") + } var kdf = ecdh.KDF{ Hash: algorithm.SHA512, Cipher: algorithm.AES256, @@ -294,6 +357,10 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { return nil, errors.InvalidArgumentError("unsupported curve") } return ecdh.GenerateKey(config.Random(), curve, kdf) + case packet.PubKeyAlgoEd25519, packet.PubKeyAlgoX25519: // When passing Ed25519, we generate an x25519 subkey + return x25519.GenerateKey(config.Random()) + case packet.PubKeyAlgoEd448, packet.PubKeyAlgoX448: // When passing Ed448, we generate an x448 subkey + return x448.GenerateKey(config.Random()) default: return nil, errors.InvalidArgumentError("unsupported public key algorithm") } @@ -302,7 +369,7 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { var bigOne = big.NewInt(1) // generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the -// given bit size, using the given random source and prepopulated primes. +// given bit size, using the given random source and pre-populated primes. func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) { priv := new(rsa.PrivateKey) priv.E = 65537 diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go index 2d7b0cf373..a071353e2e 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/keys.go @@ -6,6 +6,7 @@ package openpgp import ( goerrors "errors" + "fmt" "io" "time" @@ -24,11 +25,13 @@ var PrivateKeyType = "PGP PRIVATE KEY BLOCK" // (which must be a signing key), one or more identities claimed by that key, // and zero or more subkeys, which may be encryption keys. type Entity struct { - PrimaryKey *packet.PublicKey - PrivateKey *packet.PrivateKey - Identities map[string]*Identity // indexed by Identity.Name - Revocations []*packet.Signature - Subkeys []Subkey + PrimaryKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Identities map[string]*Identity // indexed by Identity.Name + Revocations []*packet.Signature + Subkeys []Subkey + SelfSignature *packet.Signature // Direct-key self signature of the PrimaryKey (contains primary key properties in v6) + Signatures []*packet.Signature // all (potentially unverified) self-signatures, revocations, and third-party signatures } // An Identity represents an identity claimed by an Entity and zero or more @@ -120,12 +123,12 @@ func shouldPreferIdentity(existingId, potentialNewId *Identity) bool { // given Entity. func (e *Entity) EncryptionKey(now time.Time) (Key, bool) { // Fail to find any encryption key if the... - i := e.PrimaryIdentity() - if e.PrimaryKey.KeyExpired(i.SelfSignature, now) || // primary key has expired - i.SelfSignature == nil || // user ID has no self-signature - i.SelfSignature.SigExpired(now) || // user ID self-signature has expired + primarySelfSignature, primaryIdentity := e.PrimarySelfSignature() + if primarySelfSignature == nil || // no self-signature found + e.PrimaryKey.KeyExpired(primarySelfSignature, now) || // primary key has expired e.Revoked(now) || // primary key has been revoked - i.Revoked(now) { // user ID has been revoked + primarySelfSignature.SigExpired(now) || // user ID or or direct self-signature has expired + (primaryIdentity != nil && primaryIdentity.Revoked(now)) { // user ID has been revoked (for v4 keys) return Key{}, false } @@ -152,9 +155,9 @@ func (e *Entity) EncryptionKey(now time.Time) (Key, bool) { // If we don't have any subkeys for encryption and the primary key // is marked as OK to encrypt with, then we can use it. - if i.SelfSignature.FlagsValid && i.SelfSignature.FlagEncryptCommunications && + if primarySelfSignature.FlagsValid && primarySelfSignature.FlagEncryptCommunications && e.PrimaryKey.PubKeyAlgo.CanEncrypt() { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature, e.Revocations}, true + return Key{e, e.PrimaryKey, e.PrivateKey, primarySelfSignature, e.Revocations}, true } return Key{}, false @@ -186,12 +189,12 @@ func (e *Entity) SigningKeyById(now time.Time, id uint64) (Key, bool) { func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key, bool) { // Fail to find any signing key if the... - i := e.PrimaryIdentity() - if e.PrimaryKey.KeyExpired(i.SelfSignature, now) || // primary key has expired - i.SelfSignature == nil || // user ID has no self-signature - i.SelfSignature.SigExpired(now) || // user ID self-signature has expired + primarySelfSignature, primaryIdentity := e.PrimarySelfSignature() + if primarySelfSignature == nil || // no self-signature found + e.PrimaryKey.KeyExpired(primarySelfSignature, now) || // primary key has expired e.Revoked(now) || // primary key has been revoked - i.Revoked(now) { // user ID has been revoked + primarySelfSignature.SigExpired(now) || // user ID or direct self-signature has expired + (primaryIdentity != nil && primaryIdentity.Revoked(now)) { // user ID has been revoked (for v4 keys) return Key{}, false } @@ -220,12 +223,12 @@ func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key, // If we don't have any subkeys for signing and the primary key // is marked as OK to sign with, then we can use it. - if i.SelfSignature.FlagsValid && - (flags&packet.KeyFlagCertify == 0 || i.SelfSignature.FlagCertify) && - (flags&packet.KeyFlagSign == 0 || i.SelfSignature.FlagSign) && + if primarySelfSignature.FlagsValid && + (flags&packet.KeyFlagCertify == 0 || primarySelfSignature.FlagCertify) && + (flags&packet.KeyFlagSign == 0 || primarySelfSignature.FlagSign) && e.PrimaryKey.PubKeyAlgo.CanSign() && (id == 0 || e.PrimaryKey.KeyId == id) { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature, e.Revocations}, true + return Key{e, e.PrimaryKey, e.PrivateKey, primarySelfSignature, e.Revocations}, true } // No keys with a valid Signing Flag or no keys matched the id passed in @@ -259,7 +262,7 @@ func (e *Entity) EncryptPrivateKeys(passphrase []byte, config *packet.Config) er var keysToEncrypt []*packet.PrivateKey // Add entity private key to encrypt. if e.PrivateKey != nil && !e.PrivateKey.Dummy() && !e.PrivateKey.Encrypted { - keysToEncrypt = append(keysToEncrypt, e.PrivateKey) + keysToEncrypt = append(keysToEncrypt, e.PrivateKey) } // Add subkeys to encrypt. @@ -271,7 +274,7 @@ func (e *Entity) EncryptPrivateKeys(passphrase []byte, config *packet.Config) er return packet.EncryptPrivateKeys(keysToEncrypt, passphrase, config) } -// DecryptPrivateKeys decrypts all encrypted keys in the entitiy with the given passphrase. +// DecryptPrivateKeys decrypts all encrypted keys in the entity with the given passphrase. // Avoids recomputation of similar s2k key derivations. Public keys and dummy keys are ignored, // and don't cause an error to be returned. func (e *Entity) DecryptPrivateKeys(passphrase []byte) error { @@ -284,7 +287,7 @@ func (e *Entity) DecryptPrivateKeys(passphrase []byte) error { // Add subkeys to decrypt. for _, sub := range e.Subkeys { if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() && sub.PrivateKey.Encrypted { - keysToDecrypt = append(keysToDecrypt, sub.PrivateKey) + keysToDecrypt = append(keysToDecrypt, sub.PrivateKey) } } return packet.DecryptPrivateKeys(keysToDecrypt, passphrase) @@ -318,8 +321,7 @@ type EntityList []*Entity func (el EntityList) KeysById(id uint64) (keys []Key) { for _, e := range el { if e.PrimaryKey.KeyId == id { - ident := e.PrimaryIdentity() - selfSig := ident.SelfSignature + selfSig, _ := e.PrimarySelfSignature() keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig, e.Revocations}) } @@ -441,7 +443,6 @@ func readToNextPublicKey(packets *packet.Reader) (err error) { return } else if err != nil { if _, ok := err.(errors.UnsupportedError); ok { - err = nil continue } return @@ -479,6 +480,7 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) { } var revocations []*packet.Signature + var directSignatures []*packet.Signature EachPacket: for { p, err := packets.Next() @@ -497,9 +499,7 @@ EachPacket: if pkt.SigType == packet.SigTypeKeyRevocation { revocations = append(revocations, pkt) } else if pkt.SigType == packet.SigTypeDirectSignature { - // TODO: RFC4880 5.2.1 permits signatures - // directly on keys (eg. to bind additional - // revocation keys). + directSignatures = append(directSignatures, pkt) } // Else, ignoring the signature as it does not follow anything // we would know to attach it to. @@ -522,12 +522,39 @@ EachPacket: return nil, err } default: - // we ignore unknown packets + // we ignore unknown packets. } } - if len(e.Identities) == 0 { - return nil, errors.StructuralError("entity without any identities") + if len(e.Identities) == 0 && e.PrimaryKey.Version < 6 { + return nil, errors.StructuralError(fmt.Sprintf("v%d entity without any identities", e.PrimaryKey.Version)) + } + + // An implementation MUST ensure that a valid direct-key signature is present before using a v6 key. + if e.PrimaryKey.Version == 6 { + if len(directSignatures) == 0 { + return nil, errors.StructuralError("v6 entity without a valid direct-key signature") + } + // Select main direct key signature. + var mainDirectKeySelfSignature *packet.Signature + for _, directSignature := range directSignatures { + if directSignature.SigType == packet.SigTypeDirectSignature && + directSignature.CheckKeyIdOrFingerprint(e.PrimaryKey) && + (mainDirectKeySelfSignature == nil || + directSignature.CreationTime.After(mainDirectKeySelfSignature.CreationTime)) { + mainDirectKeySelfSignature = directSignature + } + } + if mainDirectKeySelfSignature == nil { + return nil, errors.StructuralError("no valid direct-key self-signature for v6 primary key found") + } + // Check that the main self-signature is valid. + err = e.PrimaryKey.VerifyDirectKeySignature(mainDirectKeySelfSignature) + if err != nil { + return nil, errors.StructuralError("invalid direct-key self-signature for v6 primary key") + } + e.SelfSignature = mainDirectKeySelfSignature + e.Signatures = directSignatures } for _, revocation := range revocations { @@ -672,6 +699,12 @@ func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign boo return err } } + for _, directSignature := range e.Signatures { + err := directSignature.Serialize(w) + if err != nil { + return err + } + } for _, ident := range e.Identities { err = ident.UserId.Serialize(w) if err != nil { @@ -738,6 +771,12 @@ func (e *Entity) Serialize(w io.Writer) error { return err } } + for _, directSignature := range e.Signatures { + err := directSignature.Serialize(w) + if err != nil { + return err + } + } for _, ident := range e.Identities { err = ident.UserId.Serialize(w) if err != nil { @@ -840,3 +879,23 @@ func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, rea sk.Revocations = append(sk.Revocations, revSig) return nil } + +func (e *Entity) primaryDirectSignature() *packet.Signature { + return e.SelfSignature +} + +// PrimarySelfSignature searches the entity for the self-signature that stores key preferences. +// For V4 keys, returns the self-signature of the primary identity, and the identity. +// For V6 keys, returns the latest valid direct-key self-signature, and no identity (nil). +// This self-signature is to be used to check the key expiration, +// algorithm preferences, and so on. +func (e *Entity) PrimarySelfSignature() (*packet.Signature, *Identity) { + if e.PrimaryKey.Version == 6 { + return e.primaryDirectSignature(), nil + } + primaryIdentity := e.PrimaryIdentity() + if primaryIdentity == nil { + return nil, nil + } + return primaryIdentity.SelfSignature, primaryIdentity +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go index cee83bdc7a..2eecd062f5 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go @@ -88,17 +88,20 @@ func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) { if errRead != nil && errRead != io.EOF { return 0, errRead } - decrypted, errChunk := ar.openChunk(cipherChunk) - if errChunk != nil { - return 0, errChunk - } - // Return decrypted bytes, buffering if necessary - if len(dst) < len(decrypted) { - n = copy(dst, decrypted[:len(dst)]) - ar.buffer.Write(decrypted[len(dst):]) - } else { - n = copy(dst, decrypted) + if len(cipherChunk) > 0 { + decrypted, errChunk := ar.openChunk(cipherChunk) + if errChunk != nil { + return 0, errChunk + } + + // Return decrypted bytes, buffering if necessary + if len(dst) < len(decrypted) { + n = copy(dst, decrypted[:len(dst)]) + ar.buffer.Write(decrypted[len(dst):]) + } else { + n = copy(dst, decrypted) + } } // Check final authentication tag @@ -116,6 +119,12 @@ func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) { // checked in the last Read call. In the future, this function could be used to // wipe the reader and peeked, decrypted bytes, if necessary. func (ar *aeadDecrypter) Close() (err error) { + if !ar.eof { + errChunk := ar.validateFinalTag(ar.peekedBytes) + if errChunk != nil { + return errChunk + } + } return nil } @@ -138,7 +147,7 @@ func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) { nonce := ar.computeNextNonce() plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata) if err != nil { - return nil, err + return nil, errors.ErrAEADTagVerification } ar.bytesProcessed += len(plainChunk) if err = ar.aeadCrypter.incrementIndex(); err != nil { @@ -163,9 +172,8 @@ func (ar *aeadDecrypter) validateFinalTag(tag []byte) error { // ... and total number of encrypted octets adata = append(adata, amountBytes...) nonce := ar.computeNextNonce() - _, err := ar.aead.Open(nil, nonce, tag, adata) - if err != nil { - return err + if _, err := ar.aead.Open(nil, nonce, tag, adata); err != nil { + return errors.ErrAEADTagVerification } return nil } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go index 2f5cad71da..0bcb38caca 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go @@ -8,9 +8,10 @@ import ( "compress/bzip2" "compress/flate" "compress/zlib" - "github.com/ProtonMail/go-crypto/openpgp/errors" "io" "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/errors" ) // Compressed represents a compressed OpenPGP packet. The decompressed contents @@ -39,6 +40,37 @@ type CompressionConfig struct { Level int } +// decompressionReader ensures that the whole compression packet is read. +type decompressionReader struct { + compressed io.Reader + decompressed io.ReadCloser + readAll bool +} + +func newDecompressionReader(r io.Reader, decompressor io.ReadCloser) *decompressionReader { + return &decompressionReader{ + compressed: r, + decompressed: decompressor, + } +} + +func (dr *decompressionReader) Read(data []byte) (n int, err error) { + if dr.readAll { + return 0, io.EOF + } + n, err = dr.decompressed.Read(data) + if err == io.EOF { + dr.readAll = true + // Close the decompressor. + if errDec := dr.decompressed.Close(); errDec != nil { + return n, errDec + } + // Consume all remaining data from the compressed packet. + consumeAll(dr.compressed) + } + return n, err +} + func (c *Compressed) parse(r io.Reader) error { var buf [1]byte _, err := readFull(r, buf[:]) @@ -50,11 +82,15 @@ func (c *Compressed) parse(r io.Reader) error { case 0: c.Body = r case 1: - c.Body = flate.NewReader(r) + c.Body = newDecompressionReader(r, flate.NewReader(r)) case 2: - c.Body, err = zlib.NewReader(r) + decompressor, err := zlib.NewReader(r) + if err != nil { + return err + } + c.Body = newDecompressionReader(r, decompressor) case 3: - c.Body = bzip2.NewReader(r) + c.Body = newDecompressionReader(r, io.NopCloser(bzip2.NewReader(r))) default: err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go index 04994bec97..8bf8e6e51f 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go @@ -14,6 +14,34 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/s2k" ) +var ( + defaultRejectPublicKeyAlgorithms = map[PublicKeyAlgorithm]bool{ + PubKeyAlgoElGamal: true, + PubKeyAlgoDSA: true, + } + defaultRejectHashAlgorithms = map[crypto.Hash]bool{ + crypto.MD5: true, + crypto.RIPEMD160: true, + } + defaultRejectMessageHashAlgorithms = map[crypto.Hash]bool{ + crypto.SHA1: true, + crypto.MD5: true, + crypto.RIPEMD160: true, + } + defaultRejectCurves = map[Curve]bool{ + CurveSecP256k1: true, + } +) + +// A global feature flag to indicate v5 support. +// Can be set via a build tag, e.g.: `go build -tags v5 ./...` +// If the build tag is missing config_v5.go will set it to true. +// +// Disables parsing of v5 keys and v5 signatures. +// These are non-standard entities, which in the crypto-refresh have been superseded +// by v6 keys, v6 signatures and SEIPDv2 encrypted data, respectively. +var V5Disabled = false + // Config collects a number of parameters along with sensible defaults. // A nil *Config is valid and results in all default values. type Config struct { @@ -73,9 +101,16 @@ type Config struct { // **Note: using this option may break compatibility with other OpenPGP // implementations, as well as future versions of this library.** AEADConfig *AEADConfig - // V5Keys configures version 5 key generation. If false, this package still - // supports version 5 keys, but produces version 4 keys. - V5Keys bool + // V6Keys configures version 6 key generation. If false, this package still + // supports version 6 keys, but produces version 4 keys. + V6Keys bool + // Minimum RSA key size allowed for key generation and message signing, verification and encryption. + MinRSABits uint16 + // Reject insecure algorithms, only works with v2 api + RejectPublicKeyAlgorithms map[PublicKeyAlgorithm]bool + RejectHashAlgorithms map[crypto.Hash]bool + RejectMessageHashAlgorithms map[crypto.Hash]bool + RejectCurves map[Curve]bool // "The validity period of the key. This is the number of seconds after // the key creation time that the key expires. If this is not present // or has a value of zero, the key never expires. This is found only on @@ -104,12 +139,40 @@ type Config struct { // might be no other way than to tolerate the missing MDC. Setting this flag, allows this // mode of operation. It should be considered a measure of last resort. InsecureAllowUnauthenticatedMessages bool + // InsecureAllowDecryptionWithSigningKeys allows decryption with keys marked as signing keys in the v2 API. + // This setting is potentially insecure, but it is needed as some libraries + // ignored key flags when selecting a key for encryption. + // Not relevant for the v1 API, as all keys were allowed in decryption. + InsecureAllowDecryptionWithSigningKeys bool // KnownNotations is a map of Notation Data names to bools, which controls // the notation names that are allowed to be present in critical Notation Data // signature subpackets. KnownNotations map[string]bool // SignatureNotations is a list of Notations to be added to any signatures. SignatureNotations []*Notation + // CheckIntendedRecipients controls, whether the OpenPGP Intended Recipient Fingerprint feature + // should be enabled for encryption and decryption. + // (See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-intended-recipient-fingerpr). + // When the flag is set, encryption produces Intended Recipient Fingerprint signature sub-packets and decryption + // checks whether the key it was encrypted to is one of the included fingerprints in the signature. + // If the flag is disabled, no Intended Recipient Fingerprint sub-packets are created or checked. + // The default behavior, when the config or flag is nil, is to enable the feature. + CheckIntendedRecipients *bool + // CacheSessionKey controls if decryption should return the session key used for decryption. + // If the flag is set, the session key is cached in the message details struct. + CacheSessionKey bool + // CheckPacketSequence is a flag that controls if the pgp message reader should strictly check + // that the packet sequence conforms with the grammar mandated by rfc4880. + // The default behavior, when the config or flag is nil, is to check the packet sequence. + CheckPacketSequence *bool + // NonDeterministicSignaturesViaNotation is a flag to enable randomization of signatures. + // If true, a salt notation is used to randomize signatures generated by v4 and v5 keys + // (v6 signatures are always non-deterministic, by design). + // This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur + // during the signing computation. It is added to signatures of any algo for simplicity, and as it may also serve as protection in case of + // weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks. + // The default behavior, when the config or flag is nil, is to enable the feature. + NonDeterministicSignaturesViaNotation *bool } func (c *Config) Random() io.Reader { @@ -197,7 +260,7 @@ func (c *Config) S2K() *s2k.Config { return nil } // for backwards compatibility - if c != nil && c.S2KCount > 0 && c.S2KConfig == nil { + if c.S2KCount > 0 && c.S2KConfig == nil { return &s2k.Config{ S2KCount: c.S2KCount, } @@ -233,6 +296,13 @@ func (c *Config) AllowUnauthenticatedMessages() bool { return c.InsecureAllowUnauthenticatedMessages } +func (c *Config) AllowDecryptionWithSigningKeys() bool { + if c == nil { + return false + } + return c.InsecureAllowDecryptionWithSigningKeys +} + func (c *Config) KnownNotation(notationName string) bool { if c == nil { return false @@ -246,3 +316,95 @@ func (c *Config) Notations() []*Notation { } return c.SignatureNotations } + +func (c *Config) V6() bool { + if c == nil { + return false + } + return c.V6Keys +} + +func (c *Config) IntendedRecipients() bool { + if c == nil || c.CheckIntendedRecipients == nil { + return true + } + return *c.CheckIntendedRecipients +} + +func (c *Config) RetrieveSessionKey() bool { + if c == nil { + return false + } + return c.CacheSessionKey +} + +func (c *Config) MinimumRSABits() uint16 { + if c == nil || c.MinRSABits == 0 { + return 2047 + } + return c.MinRSABits +} + +func (c *Config) RejectPublicKeyAlgorithm(alg PublicKeyAlgorithm) bool { + var rejectedAlgorithms map[PublicKeyAlgorithm]bool + if c == nil || c.RejectPublicKeyAlgorithms == nil { + // Default + rejectedAlgorithms = defaultRejectPublicKeyAlgorithms + } else { + rejectedAlgorithms = c.RejectPublicKeyAlgorithms + } + return rejectedAlgorithms[alg] +} + +func (c *Config) RejectHashAlgorithm(hash crypto.Hash) bool { + var rejectedAlgorithms map[crypto.Hash]bool + if c == nil || c.RejectHashAlgorithms == nil { + // Default + rejectedAlgorithms = defaultRejectHashAlgorithms + } else { + rejectedAlgorithms = c.RejectHashAlgorithms + } + return rejectedAlgorithms[hash] +} + +func (c *Config) RejectMessageHashAlgorithm(hash crypto.Hash) bool { + var rejectedAlgorithms map[crypto.Hash]bool + if c == nil || c.RejectMessageHashAlgorithms == nil { + // Default + rejectedAlgorithms = defaultRejectMessageHashAlgorithms + } else { + rejectedAlgorithms = c.RejectMessageHashAlgorithms + } + return rejectedAlgorithms[hash] +} + +func (c *Config) RejectCurve(curve Curve) bool { + var rejectedCurve map[Curve]bool + if c == nil || c.RejectCurves == nil { + // Default + rejectedCurve = defaultRejectCurves + } else { + rejectedCurve = c.RejectCurves + } + return rejectedCurve[curve] +} + +func (c *Config) StrictPacketSequence() bool { + if c == nil || c.CheckPacketSequence == nil { + return true + } + return *c.CheckPacketSequence +} + +func (c *Config) RandomizeSignaturesViaNotation() bool { + if c == nil || c.NonDeterministicSignaturesViaNotation == nil { + return true + } + return *c.NonDeterministicSignaturesViaNotation +} + +// BoolPointer is a helper function to set a boolean pointer in the Config. +// e.g., config.CheckPacketSequence = BoolPointer(true) +func BoolPointer(value bool) *bool { + return &value +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go new file mode 100644 index 0000000000..f2415906b9 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go @@ -0,0 +1,7 @@ +//go:build !v5 + +package packet + +func init() { + V5Disabled = true +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go index eeff2902c1..b90bb28911 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go @@ -5,9 +5,11 @@ package packet import ( + "bytes" "crypto" "crypto/rsa" "encoding/binary" + "encoding/hex" "io" "math/big" "strconv" @@ -16,32 +18,85 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/elgamal" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" + "github.com/ProtonMail/go-crypto/openpgp/x25519" + "github.com/ProtonMail/go-crypto/openpgp/x448" ) -const encryptedKeyVersion = 3 - // EncryptedKey represents a public-key encrypted session key. See RFC 4880, // section 5.1. type EncryptedKey struct { - KeyId uint64 - Algo PublicKeyAlgorithm - CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet - Key []byte // only valid after a successful Decrypt + Version int + KeyId uint64 + KeyVersion int // v6 + KeyFingerprint []byte // v6 + Algo PublicKeyAlgorithm + CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet + Key []byte // only valid after a successful Decrypt encryptedMPI1, encryptedMPI2 encoding.Field + ephemeralPublicX25519 *x25519.PublicKey // used for x25519 + ephemeralPublicX448 *x448.PublicKey // used for x448 + encryptedSession []byte // used for x25519 and x448 } func (e *EncryptedKey) parse(r io.Reader) (err error) { - var buf [10]byte - _, err = readFull(r, buf[:]) + var buf [8]byte + _, err = readFull(r, buf[:versionSize]) if err != nil { return } - if buf[0] != encryptedKeyVersion { + e.Version = int(buf[0]) + if e.Version != 3 && e.Version != 6 { return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) } - e.KeyId = binary.BigEndian.Uint64(buf[1:9]) - e.Algo = PublicKeyAlgorithm(buf[9]) + if e.Version == 6 { + //Read a one-octet size of the following two fields. + if _, err = readFull(r, buf[:1]); err != nil { + return + } + // The size may also be zero, and the key version and + // fingerprint omitted for an "anonymous recipient" + if buf[0] != 0 { + // non-anonymous case + _, err = readFull(r, buf[:versionSize]) + if err != nil { + return + } + e.KeyVersion = int(buf[0]) + if e.KeyVersion != 4 && e.KeyVersion != 6 { + return errors.UnsupportedError("unknown public key version " + strconv.Itoa(e.KeyVersion)) + } + var fingerprint []byte + if e.KeyVersion == 6 { + fingerprint = make([]byte, fingerprintSizeV6) + } else if e.KeyVersion == 4 { + fingerprint = make([]byte, fingerprintSize) + } + _, err = readFull(r, fingerprint) + if err != nil { + return + } + e.KeyFingerprint = fingerprint + if e.KeyVersion == 6 { + e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[:keyIdSize]) + } else if e.KeyVersion == 4 { + e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[fingerprintSize-keyIdSize : fingerprintSize]) + } + } + } else { + _, err = readFull(r, buf[:8]) + if err != nil { + return + } + e.KeyId = binary.BigEndian.Uint64(buf[:keyIdSize]) + } + + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + e.Algo = PublicKeyAlgorithm(buf[0]) + var cipherFunction byte switch e.Algo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: e.encryptedMPI1 = new(encoding.MPI) @@ -68,26 +123,39 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) { if _, err = e.encryptedMPI2.ReadFrom(r); err != nil { return } + case PubKeyAlgoX25519: + e.ephemeralPublicX25519, e.encryptedSession, cipherFunction, err = x25519.DecodeFields(r, e.Version == 6) + if err != nil { + return + } + case PubKeyAlgoX448: + e.ephemeralPublicX448, e.encryptedSession, cipherFunction, err = x448.DecodeFields(r, e.Version == 6) + if err != nil { + return + } + } + if e.Version < 6 { + switch e.Algo { + case PubKeyAlgoX25519, PubKeyAlgoX448: + e.CipherFunc = CipherFunction(cipherFunction) + // Check for validiy is in the Decrypt method + } } + _, err = consumeAll(r) return } -func checksumKeyMaterial(key []byte) uint16 { - var checksum uint16 - for _, v := range key { - checksum += uint16(v) - } - return checksum -} - // Decrypt decrypts an encrypted session key with the given private key. The // private key must have been decrypted first. // If config is nil, sensible defaults will be used. func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { - if e.KeyId != 0 && e.KeyId != priv.KeyId { + if e.Version < 6 && e.KeyId != 0 && e.KeyId != priv.KeyId { return errors.InvalidArgumentError("cannot decrypt encrypted session key for key id " + strconv.FormatUint(e.KeyId, 16) + " with private key id " + strconv.FormatUint(priv.KeyId, 16)) } + if e.Version == 6 && e.KeyVersion != 0 && !bytes.Equal(e.KeyFingerprint, priv.Fingerprint) { + return errors.InvalidArgumentError("cannot decrypt encrypted session key for key fingerprint " + hex.EncodeToString(e.KeyFingerprint) + " with private key fingerprint " + hex.EncodeToString(priv.Fingerprint)) + } if e.Algo != priv.PubKeyAlgo { return errors.InvalidArgumentError("cannot decrypt encrypted session key of type " + strconv.Itoa(int(e.Algo)) + " with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) } @@ -113,52 +181,116 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { vsG := e.encryptedMPI1.Bytes() m := e.encryptedMPI2.Bytes() oid := priv.PublicKey.oid.EncodedBytes() - b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, priv.PublicKey.Fingerprint[:]) + fp := priv.PublicKey.Fingerprint[:] + if priv.PublicKey.Version == 5 { + // For v5 the, the fingerprint must be restricted to 20 bytes + fp = fp[:20] + } + b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, fp) + case PubKeyAlgoX25519: + b, err = x25519.Decrypt(priv.PrivateKey.(*x25519.PrivateKey), e.ephemeralPublicX25519, e.encryptedSession) + case PubKeyAlgoX448: + b, err = x448.Decrypt(priv.PrivateKey.(*x448.PrivateKey), e.ephemeralPublicX448, e.encryptedSession) default: err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) } - if err != nil { return err } - e.CipherFunc = CipherFunction(b[0]) - if !e.CipherFunc.IsSupported() { - return errors.UnsupportedError("unsupported encryption function") - } - - e.Key = b[1 : len(b)-2] - expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) - checksum := checksumKeyMaterial(e.Key) - if checksum != expectedChecksum { - return errors.StructuralError("EncryptedKey checksum incorrect") + var key []byte + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH: + keyOffset := 0 + if e.Version < 6 { + e.CipherFunc = CipherFunction(b[0]) + keyOffset = 1 + if !e.CipherFunc.IsSupported() { + return errors.UnsupportedError("unsupported encryption function") + } + } + key, err = decodeChecksumKey(b[keyOffset:]) + if err != nil { + return err + } + case PubKeyAlgoX25519, PubKeyAlgoX448: + if e.Version < 6 { + switch e.CipherFunc { + case CipherAES128, CipherAES192, CipherAES256: + break + default: + return errors.StructuralError("v3 PKESK mandates AES as cipher function for x25519 and x448") + } + } + key = b[:] + default: + return errors.UnsupportedError("unsupported algorithm for decryption") } - + e.Key = key return nil } // Serialize writes the encrypted key packet, e, to w. func (e *EncryptedKey) Serialize(w io.Writer) error { - var mpiLen int + var encodedLength int switch e.Algo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - mpiLen = int(e.encryptedMPI1.EncodedLength()) + encodedLength = int(e.encryptedMPI1.EncodedLength()) case PubKeyAlgoElGamal: - mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) + encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) case PubKeyAlgoECDH: - mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) + encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) + case PubKeyAlgoX25519: + encodedLength = x25519.EncodedFieldsLength(e.encryptedSession, e.Version == 6) + case PubKeyAlgoX448: + encodedLength = x448.EncodedFieldsLength(e.encryptedSession, e.Version == 6) default: return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo))) } - err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen) + packetLen := versionSize /* version */ + keyIdSize /* key id */ + algorithmSize /* algo */ + encodedLength + if e.Version == 6 { + packetLen = versionSize /* version */ + algorithmSize /* algo */ + encodedLength + keyVersionSize /* key version */ + if e.KeyVersion == 6 { + packetLen += fingerprintSizeV6 + } else if e.KeyVersion == 4 { + packetLen += fingerprintSize + } + } + + err := serializeHeader(w, packetTypeEncryptedKey, packetLen) if err != nil { return err } - w.Write([]byte{encryptedKeyVersion}) - binary.Write(w, binary.BigEndian, e.KeyId) - w.Write([]byte{byte(e.Algo)}) + _, err = w.Write([]byte{byte(e.Version)}) + if err != nil { + return err + } + if e.Version == 6 { + _, err = w.Write([]byte{byte(e.KeyVersion)}) + if err != nil { + return err + } + // The key version number may also be zero, + // and the fingerprint omitted + if e.KeyVersion != 0 { + _, err = w.Write(e.KeyFingerprint) + if err != nil { + return err + } + } + } else { + // Write KeyID + err = binary.Write(w, binary.BigEndian, e.KeyId) + if err != nil { + return err + } + } + _, err = w.Write([]byte{byte(e.Algo)}) + if err != nil { + return err + } switch e.Algo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: @@ -176,34 +308,115 @@ func (e *EncryptedKey) Serialize(w io.Writer) error { } _, err := w.Write(e.encryptedMPI2.EncodedBytes()) return err + case PubKeyAlgoX25519: + err := x25519.EncodeFields(w, e.ephemeralPublicX25519, e.encryptedSession, byte(e.CipherFunc), e.Version == 6) + return err + case PubKeyAlgoX448: + err := x448.EncodeFields(w, e.ephemeralPublicX448, e.encryptedSession, byte(e.CipherFunc), e.Version == 6) + return err default: panic("internal error") } } -// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// SerializeEncryptedKeyAEAD serializes an encrypted key packet to w that contains // key, encrypted to pub. +// If aeadSupported is set, PKESK v6 is used, otherwise v3. +// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted. // If config is nil, sensible defaults will be used. -func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { - var buf [10]byte - buf[0] = encryptedKeyVersion - binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) - buf[9] = byte(pub.PubKeyAlgo) - - keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) - keyBlock[0] = byte(cipherFunc) - copy(keyBlock[1:], key) - checksum := checksumKeyMaterial(key) - keyBlock[1+len(key)] = byte(checksum >> 8) - keyBlock[1+len(key)+1] = byte(checksum) +func SerializeEncryptedKeyAEAD(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, config *Config) error { + return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, aeadSupported, key, false, config) +} + +// SerializeEncryptedKeyAEADwithHiddenOption serializes an encrypted key packet to w that contains +// key, encrypted to pub. +// Offers the hidden flag option to indicated if the PKESK packet should include a wildcard KeyID. +// If aeadSupported is set, PKESK v6 is used, otherwise v3. +// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted. +// If config is nil, sensible defaults will be used. +func SerializeEncryptedKeyAEADwithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, hidden bool, config *Config) error { + var buf [36]byte // max possible header size is v6 + lenHeaderWritten := versionSize + version := 3 + + if aeadSupported { + version = 6 + } + // An implementation MUST NOT generate ElGamal v6 PKESKs. + if version == 6 && pub.PubKeyAlgo == PubKeyAlgoElGamal { + return errors.InvalidArgumentError("ElGamal v6 PKESK are not allowed") + } + // In v3 PKESKs, for x25519 and x448, mandate using AES + if version == 3 && (pub.PubKeyAlgo == PubKeyAlgoX25519 || pub.PubKeyAlgo == PubKeyAlgoX448) { + switch cipherFunc { + case CipherAES128, CipherAES192, CipherAES256: + break + default: + return errors.InvalidArgumentError("v3 PKESK mandates AES for x25519 and x448") + } + } + + buf[0] = byte(version) + + // If hidden is set, the key should be hidden + // An implementation MAY accept or use a Key ID of all zeros, + // or a key version of zero and no key fingerprint, to hide the intended decryption key. + // See Section 5.1.8. in the open pgp crypto refresh + if version == 6 { + if !hidden { + // A one-octet size of the following two fields. + buf[1] = byte(keyVersionSize + len(pub.Fingerprint)) + // A one octet key version number. + buf[2] = byte(pub.Version) + lenHeaderWritten += keyVersionSize + 1 + // The fingerprint of the public key + copy(buf[lenHeaderWritten:lenHeaderWritten+len(pub.Fingerprint)], pub.Fingerprint) + lenHeaderWritten += len(pub.Fingerprint) + } else { + // The size may also be zero, and the key version + // and fingerprint omitted for an "anonymous recipient" + buf[1] = 0 + lenHeaderWritten += 1 + } + } else { + if !hidden { + binary.BigEndian.PutUint64(buf[versionSize:(versionSize+keyIdSize)], pub.KeyId) + } + lenHeaderWritten += keyIdSize + } + buf[lenHeaderWritten] = byte(pub.PubKeyAlgo) + lenHeaderWritten += algorithmSize + + var keyBlock []byte + switch pub.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH: + lenKeyBlock := len(key) + 2 + if version < 6 { + lenKeyBlock += 1 // cipher type included + } + keyBlock = make([]byte, lenKeyBlock) + keyOffset := 0 + if version < 6 { + keyBlock[0] = byte(cipherFunc) + keyOffset = 1 + } + encodeChecksumKey(keyBlock[keyOffset:], key) + case PubKeyAlgoX25519, PubKeyAlgoX448: + // algorithm is added in plaintext below + keyBlock = key + } switch pub.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + return serializeEncryptedKeyRSA(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*rsa.PublicKey), keyBlock) case PubKeyAlgoElGamal: - return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + return serializeEncryptedKeyElGamal(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*elgamal.PublicKey), keyBlock) case PubKeyAlgoECDH: - return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint) + return serializeEncryptedKeyECDH(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint) + case PubKeyAlgoX25519: + return serializeEncryptedKeyX25519(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x25519.PublicKey), keyBlock, byte(cipherFunc), version) + case PubKeyAlgoX448: + return serializeEncryptedKeyX448(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x448.PublicKey), keyBlock, byte(cipherFunc), version) case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } @@ -211,14 +424,32 @@ func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunctio return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } -func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error { +// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// key, encrypted to pub. +// PKESKv6 is used if config.AEAD() is not nil. +// If config is nil, sensible defaults will be used. +// Deprecated: Use SerializeEncryptedKeyAEAD instead. +func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { + return SerializeEncryptedKeyAEAD(w, pub, cipherFunc, config.AEAD() != nil, key, config) +} + +// SerializeEncryptedKeyWithHiddenOption serializes an encrypted key packet to w that contains +// key, encrypted to pub. PKESKv6 is used if config.AEAD() is not nil. +// The hidden option controls if the packet should be anonymous, i.e., omit key metadata. +// If config is nil, sensible defaults will be used. +// Deprecated: Use SerializeEncryptedKeyAEADwithHiddenOption instead. +func SerializeEncryptedKeyWithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, hidden bool, config *Config) error { + return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, config.AEAD() != nil, key, hidden, config) +} + +func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header []byte, pub *rsa.PublicKey, keyBlock []byte) error { cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) if err != nil { return errors.InvalidArgumentError("RSA encryption failed: " + err.Error()) } cipherMPI := encoding.NewMPI(cipherText) - packetLen := 10 /* header length */ + int(cipherMPI.EncodedLength()) + packetLen := len(header) /* header length */ + int(cipherMPI.EncodedLength()) err = serializeHeader(w, packetTypeEncryptedKey, packetLen) if err != nil { @@ -232,13 +463,13 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub return err } -func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error { +func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header []byte, pub *elgamal.PublicKey, keyBlock []byte) error { c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) if err != nil { return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error()) } - packetLen := 10 /* header length */ + packetLen := len(header) /* header length */ packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 @@ -257,7 +488,7 @@ func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, return err } -func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error { +func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header []byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error { vsG, c, err := ecdh.Encrypt(rand, pub, keyBlock, oid.EncodedBytes(), fingerprint) if err != nil { return errors.InvalidArgumentError("ECDH encryption failed: " + err.Error()) @@ -266,7 +497,7 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub g := encoding.NewMPI(vsG) m := encoding.NewOID(c) - packetLen := 10 /* header length */ + packetLen := len(header) /* header length */ packetLen += int(g.EncodedLength()) + int(m.EncodedLength()) err = serializeHeader(w, packetTypeEncryptedKey, packetLen) @@ -284,3 +515,70 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub _, err = w.Write(m.EncodedBytes()) return err } + +func serializeEncryptedKeyX25519(w io.Writer, rand io.Reader, header []byte, pub *x25519.PublicKey, keyBlock []byte, cipherFunc byte, version int) error { + ephemeralPublicX25519, ciphertext, err := x25519.Encrypt(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("x25519 encryption failed: " + err.Error()) + } + + packetLen := len(header) /* header length */ + packetLen += x25519.EncodedFieldsLength(ciphertext, version == 6) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + + _, err = w.Write(header[:]) + if err != nil { + return err + } + return x25519.EncodeFields(w, ephemeralPublicX25519, ciphertext, cipherFunc, version == 6) +} + +func serializeEncryptedKeyX448(w io.Writer, rand io.Reader, header []byte, pub *x448.PublicKey, keyBlock []byte, cipherFunc byte, version int) error { + ephemeralPublicX448, ciphertext, err := x448.Encrypt(rand, pub, keyBlock) + if err != nil { + return errors.InvalidArgumentError("x448 encryption failed: " + err.Error()) + } + + packetLen := len(header) /* header length */ + packetLen += x448.EncodedFieldsLength(ciphertext, version == 6) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + + _, err = w.Write(header[:]) + if err != nil { + return err + } + return x448.EncodeFields(w, ephemeralPublicX448, ciphertext, cipherFunc, version == 6) +} + +func checksumKeyMaterial(key []byte) uint16 { + var checksum uint16 + for _, v := range key { + checksum += uint16(v) + } + return checksum +} + +func decodeChecksumKey(msg []byte) (key []byte, err error) { + key = msg[:len(msg)-2] + expectedChecksum := uint16(msg[len(msg)-2])<<8 | uint16(msg[len(msg)-1]) + checksum := checksumKeyMaterial(key) + if checksum != expectedChecksum { + err = errors.StructuralError("session key checksum is incorrect") + } + return +} + +func encodeChecksumKey(buffer []byte, key []byte) { + copy(buffer, key) + checksum := checksumKeyMaterial(key) + buffer[len(key)] = byte(checksum >> 8) + buffer[len(key)+1] = byte(checksum) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go index 4be987609b..8a028c8a17 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go @@ -58,9 +58,9 @@ func (l *LiteralData) parse(r io.Reader) (err error) { // on completion. The fileName is truncated to 255 bytes. func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) { var buf [4]byte - buf[0] = 't' - if isBinary { - buf[0] = 'b' + buf[0] = 'b' + if !isBinary { + buf[0] = 'u' } if len(fileName) > 255 { fileName = fileName[:255] diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go new file mode 100644 index 0000000000..1ee378ba3c --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go @@ -0,0 +1,33 @@ +package packet + +import ( + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +type Marker struct{} + +const markerString = "PGP" + +// parse just checks if the packet contains "PGP". +func (m *Marker) parse(reader io.Reader) error { + var buffer [3]byte + if _, err := io.ReadFull(reader, buffer[:]); err != nil { + return err + } + if string(buffer[:]) != markerString { + return errors.StructuralError("invalid marker packet") + } + return nil +} + +// SerializeMarker writes a marker packet to writer. +func SerializeMarker(writer io.Writer) error { + err := serializeHeader(writer, packetTypeMarker, len(markerString)) + if err != nil { + return err + } + _, err = writer.Write([]byte(markerString)) + return err +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go index 033fb2d7e8..f393c4063b 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go @@ -7,34 +7,37 @@ package packet import ( "crypto" "encoding/binary" - "github.com/ProtonMail/go-crypto/openpgp/errors" - "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" "io" "strconv" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" ) // OnePassSignature represents a one-pass signature packet. See RFC 4880, // section 5.4. type OnePassSignature struct { - SigType SignatureType - Hash crypto.Hash - PubKeyAlgo PublicKeyAlgorithm - KeyId uint64 - IsLast bool + Version int + SigType SignatureType + Hash crypto.Hash + PubKeyAlgo PublicKeyAlgorithm + KeyId uint64 + IsLast bool + Salt []byte // v6 only + KeyFingerprint []byte // v6 only } -const onePassSignatureVersion = 3 - func (ops *OnePassSignature) parse(r io.Reader) (err error) { - var buf [13]byte - - _, err = readFull(r, buf[:]) + var buf [8]byte + // Read: version | signature type | hash algorithm | public-key algorithm + _, err = readFull(r, buf[:4]) if err != nil { return } - if buf[0] != onePassSignatureVersion { - err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) + if buf[0] != 3 && buf[0] != 6 { + return errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) } + ops.Version = int(buf[0]) var ok bool ops.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2]) @@ -44,15 +47,69 @@ func (ops *OnePassSignature) parse(r io.Reader) (err error) { ops.SigType = SignatureType(buf[1]) ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) - ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) - ops.IsLast = buf[12] != 0 + + if ops.Version == 6 { + // Only for v6, a variable-length field containing the salt + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + saltLength := int(buf[0]) + var expectedSaltLength int + expectedSaltLength, err = SaltLengthForHash(ops.Hash) + if err != nil { + return + } + if saltLength != expectedSaltLength { + err = errors.StructuralError("unexpected salt size for the given hash algorithm") + return + } + salt := make([]byte, expectedSaltLength) + _, err = readFull(r, salt) + if err != nil { + return + } + ops.Salt = salt + + // Only for v6 packets, 32 octets of the fingerprint of the signing key. + fingerprint := make([]byte, 32) + _, err = readFull(r, fingerprint) + if err != nil { + return + } + ops.KeyFingerprint = fingerprint + ops.KeyId = binary.BigEndian.Uint64(ops.KeyFingerprint[:8]) + } else { + _, err = readFull(r, buf[:8]) + if err != nil { + return + } + ops.KeyId = binary.BigEndian.Uint64(buf[:8]) + } + + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + ops.IsLast = buf[0] != 0 return } // Serialize marshals the given OnePassSignature to w. func (ops *OnePassSignature) Serialize(w io.Writer) error { - var buf [13]byte - buf[0] = onePassSignatureVersion + //v3 length 1+1+1+1+8+1 = + packetLength := 13 + if ops.Version == 6 { + // v6 length 1+1+1+1+1+len(salt)+32+1 = + packetLength = 38 + len(ops.Salt) + } + + if err := serializeHeader(w, packetTypeOnePassSignature, packetLength); err != nil { + return err + } + + var buf [8]byte + buf[0] = byte(ops.Version) buf[1] = uint8(ops.SigType) var ok bool buf[2], ok = algorithm.HashToHashIdWithSha1(ops.Hash) @@ -60,14 +117,41 @@ func (ops *OnePassSignature) Serialize(w io.Writer) error { return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) } buf[3] = uint8(ops.PubKeyAlgo) - binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) - if ops.IsLast { - buf[12] = 1 - } - if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { + _, err := w.Write(buf[:4]) + if err != nil { return err } - _, err := w.Write(buf[:]) + + if ops.Version == 6 { + // write salt for v6 signatures + _, err := w.Write([]byte{uint8(len(ops.Salt))}) + if err != nil { + return err + } + _, err = w.Write(ops.Salt) + if err != nil { + return err + } + + // write fingerprint v6 signatures + _, err = w.Write(ops.KeyFingerprint) + if err != nil { + return err + } + } else { + binary.BigEndian.PutUint64(buf[:8], ops.KeyId) + _, err := w.Write(buf[:8]) + if err != nil { + return err + } + } + + isLast := []byte{byte(0)} + if ops.IsLast { + isLast[0] = 1 + } + + _, err = w.Write(isLast) return err } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go index 4f8204079f..cef7c661d3 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go @@ -7,7 +7,6 @@ package packet import ( "bytes" "io" - "io/ioutil" "github.com/ProtonMail/go-crypto/openpgp/errors" ) @@ -26,7 +25,7 @@ type OpaquePacket struct { } func (op *OpaquePacket) parse(r io.Reader) (err error) { - op.Contents, err = ioutil.ReadAll(r) + op.Contents, err = io.ReadAll(r) return } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go index 4d86a7da82..1e92e22c97 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go @@ -311,12 +311,15 @@ const ( packetTypePrivateSubkey packetType = 7 packetTypeCompressed packetType = 8 packetTypeSymmetricallyEncrypted packetType = 9 + packetTypeMarker packetType = 10 packetTypeLiteralData packetType = 11 + packetTypeTrust packetType = 12 packetTypeUserId packetType = 13 packetTypePublicSubkey packetType = 14 packetTypeUserAttribute packetType = 17 packetTypeSymmetricallyEncryptedIntegrityProtected packetType = 18 packetTypeAEADEncrypted packetType = 20 + packetPadding packetType = 21 ) // EncryptedDataPacket holds encrypted data. It is currently implemented by @@ -328,7 +331,7 @@ type EncryptedDataPacket interface { // Read reads a single OpenPGP packet from the given io.Reader. If there is an // error parsing a packet, the whole packet is consumed from the input. func Read(r io.Reader) (p Packet, err error) { - tag, _, contents, err := readHeader(r) + tag, len, contents, err := readHeader(r) if err != nil { return } @@ -367,8 +370,93 @@ func Read(r io.Reader) (p Packet, err error) { p = se case packetTypeAEADEncrypted: p = new(AEADEncrypted) + case packetPadding: + p = Padding(len) + case packetTypeMarker: + p = new(Marker) + case packetTypeTrust: + // Not implemented, just consume + err = errors.UnknownPacketTypeError(tag) default: + // Packet Tags from 0 to 39 are critical. + // Packet Tags from 40 to 63 are non-critical. + if tag < 40 { + err = errors.CriticalUnknownPacketTypeError(tag) + } else { + err = errors.UnknownPacketTypeError(tag) + } + } + if p != nil { + err = p.parse(contents) + } + if err != nil { + consumeAll(contents) + } + return +} + +// ReadWithCheck reads a single OpenPGP message packet from the given io.Reader. If there is an +// error parsing a packet, the whole packet is consumed from the input. +// ReadWithCheck additionally checks if the OpenPGP message packet sequence adheres +// to the packet composition rules in rfc4880, if not throws an error. +func ReadWithCheck(r io.Reader, sequence *SequenceVerifier) (p Packet, msgErr error, err error) { + tag, len, contents, err := readHeader(r) + if err != nil { + return + } + switch tag { + case packetTypeEncryptedKey: + msgErr = sequence.Next(ESKSymbol) + p = new(EncryptedKey) + case packetTypeSignature: + msgErr = sequence.Next(SigSymbol) + p = new(Signature) + case packetTypeSymmetricKeyEncrypted: + msgErr = sequence.Next(ESKSymbol) + p = new(SymmetricKeyEncrypted) + case packetTypeOnePassSignature: + msgErr = sequence.Next(OPSSymbol) + p = new(OnePassSignature) + case packetTypeCompressed: + msgErr = sequence.Next(CompSymbol) + p = new(Compressed) + case packetTypeSymmetricallyEncrypted: + msgErr = sequence.Next(EncSymbol) + p = new(SymmetricallyEncrypted) + case packetTypeLiteralData: + msgErr = sequence.Next(LDSymbol) + p = new(LiteralData) + case packetTypeSymmetricallyEncryptedIntegrityProtected: + msgErr = sequence.Next(EncSymbol) + se := new(SymmetricallyEncrypted) + se.IntegrityProtected = true + p = se + case packetTypeAEADEncrypted: + msgErr = sequence.Next(EncSymbol) + p = new(AEADEncrypted) + case packetPadding: + p = Padding(len) + case packetTypeMarker: + p = new(Marker) + case packetTypeTrust: + // Not implemented, just consume err = errors.UnknownPacketTypeError(tag) + case packetTypePrivateKey, + packetTypePrivateSubkey, + packetTypePublicKey, + packetTypePublicSubkey, + packetTypeUserId, + packetTypeUserAttribute: + msgErr = sequence.Next(UnknownSymbol) + consumeAll(contents) + default: + // Packet Tags from 0 to 39 are critical. + // Packet Tags from 40 to 63 are non-critical. + if tag < 40 { + err = errors.CriticalUnknownPacketTypeError(tag) + } else { + err = errors.UnknownPacketTypeError(tag) + } } if p != nil { err = p.parse(contents) @@ -385,17 +473,17 @@ type SignatureType uint8 const ( SigTypeBinary SignatureType = 0x00 - SigTypeText = 0x01 - SigTypeGenericCert = 0x10 - SigTypePersonaCert = 0x11 - SigTypeCasualCert = 0x12 - SigTypePositiveCert = 0x13 - SigTypeSubkeyBinding = 0x18 - SigTypePrimaryKeyBinding = 0x19 - SigTypeDirectSignature = 0x1F - SigTypeKeyRevocation = 0x20 - SigTypeSubkeyRevocation = 0x28 - SigTypeCertificationRevocation = 0x30 + SigTypeText SignatureType = 0x01 + SigTypeGenericCert SignatureType = 0x10 + SigTypePersonaCert SignatureType = 0x11 + SigTypeCasualCert SignatureType = 0x12 + SigTypePositiveCert SignatureType = 0x13 + SigTypeSubkeyBinding SignatureType = 0x18 + SigTypePrimaryKeyBinding SignatureType = 0x19 + SigTypeDirectSignature SignatureType = 0x1F + SigTypeKeyRevocation SignatureType = 0x20 + SigTypeSubkeyRevocation SignatureType = 0x28 + SigTypeCertificationRevocation SignatureType = 0x30 ) // PublicKeyAlgorithm represents the different public key system specified for @@ -412,6 +500,11 @@ const ( PubKeyAlgoECDSA PublicKeyAlgorithm = 19 // https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt PubKeyAlgoEdDSA PublicKeyAlgorithm = 22 + // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh + PubKeyAlgoX25519 PublicKeyAlgorithm = 25 + PubKeyAlgoX448 PublicKeyAlgorithm = 26 + PubKeyAlgoEd25519 PublicKeyAlgorithm = 27 + PubKeyAlgoEd448 PublicKeyAlgorithm = 28 // Deprecated in RFC 4880, Section 13.5. Use key flags instead. PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 @@ -422,7 +515,7 @@ const ( // key of the given type. func (pka PublicKeyAlgorithm) CanEncrypt() bool { switch pka { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH: + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH, PubKeyAlgoX25519, PubKeyAlgoX448: return true } return false @@ -432,7 +525,7 @@ func (pka PublicKeyAlgorithm) CanEncrypt() bool { // sign a message. func (pka PublicKeyAlgorithm) CanSign() bool { switch pka { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA: + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA, PubKeyAlgoEd25519, PubKeyAlgoEd448: return true } return false @@ -512,6 +605,11 @@ func (mode AEADMode) TagLength() int { return algorithm.AEADMode(mode).TagLength() } +// IsSupported returns true if the aead mode is supported from the library +func (mode AEADMode) IsSupported() bool { + return algorithm.AEADMode(mode).TagLength() > 0 +} + // new returns a fresh instance of the given mode. func (mode AEADMode) new(block cipher.Block) cipher.AEAD { return algorithm.AEADMode(mode).New(block) @@ -526,8 +624,17 @@ const ( KeySuperseded ReasonForRevocation = 1 KeyCompromised ReasonForRevocation = 2 KeyRetired ReasonForRevocation = 3 + UserIDNotValid ReasonForRevocation = 32 + Unknown ReasonForRevocation = 200 ) +func NewReasonForRevocation(value byte) ReasonForRevocation { + if value < 4 || value == 32 { + return ReasonForRevocation(value) + } + return Unknown +} + // Curve is a mapping to supported ECC curves for key generation. // See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-06.html#name-curve-specific-wire-formats type Curve string @@ -549,3 +656,20 @@ type TrustLevel uint8 // TrustAmount represents a trust amount per RFC4880 5.2.3.13 type TrustAmount uint8 + +const ( + // versionSize is the length in bytes of the version value. + versionSize = 1 + // algorithmSize is the length in bytes of the key algorithm value. + algorithmSize = 1 + // keyVersionSize is the length in bytes of the key version value + keyVersionSize = 1 + // keyIdSize is the length in bytes of the key identifier value. + keyIdSize = 8 + // timestampSize is the length in bytes of encoded timestamps. + timestampSize = 4 + // fingerprintSizeV6 is the length in bytes of the key fingerprint in v6. + fingerprintSizeV6 = 32 + // fingerprintSize is the length in bytes of the key fingerprint. + fingerprintSize = 20 +) diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go new file mode 100644 index 0000000000..55a8a56c2d --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go @@ -0,0 +1,222 @@ +package packet + +// This file implements the pushdown automata (PDA) from PGPainless (Paul Schaub) +// to verify pgp packet sequences. See Paul's blogpost for more details: +// https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/ +import ( + "fmt" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +func NewErrMalformedMessage(from State, input InputSymbol, stackSymbol StackSymbol) errors.ErrMalformedMessage { + return errors.ErrMalformedMessage(fmt.Sprintf("state %d, input symbol %d, stack symbol %d ", from, input, stackSymbol)) +} + +// InputSymbol defines the input alphabet of the PDA +type InputSymbol uint8 + +const ( + LDSymbol InputSymbol = iota + SigSymbol + OPSSymbol + CompSymbol + ESKSymbol + EncSymbol + EOSSymbol + UnknownSymbol +) + +// StackSymbol defines the stack alphabet of the PDA +type StackSymbol int8 + +const ( + MsgStackSymbol StackSymbol = iota + OpsStackSymbol + KeyStackSymbol + EndStackSymbol + EmptyStackSymbol +) + +// State defines the states of the PDA +type State int8 + +const ( + OpenPGPMessage State = iota + ESKMessage + LiteralMessage + CompressedMessage + EncryptedMessage + ValidMessage +) + +// transition represents a state transition in the PDA +type transition func(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) + +// SequenceVerifier is a pushdown automata to verify +// PGP messages packet sequences according to rfc4880. +type SequenceVerifier struct { + stack []StackSymbol + state State +} + +// Next performs a state transition with the given input symbol. +// If the transition fails a ErrMalformedMessage is returned. +func (sv *SequenceVerifier) Next(input InputSymbol) error { + for { + stackSymbol := sv.popStack() + transitionFunc := getTransition(sv.state) + nextState, newStackSymbols, redo, err := transitionFunc(input, stackSymbol) + if err != nil { + return err + } + if redo { + sv.pushStack(stackSymbol) + } + for _, newStackSymbol := range newStackSymbols { + sv.pushStack(newStackSymbol) + } + sv.state = nextState + if !redo { + break + } + } + return nil +} + +// Valid returns true if RDA is in a valid state. +func (sv *SequenceVerifier) Valid() bool { + return sv.state == ValidMessage && len(sv.stack) == 0 +} + +func (sv *SequenceVerifier) AssertValid() error { + if !sv.Valid() { + return errors.ErrMalformedMessage("invalid message") + } + return nil +} + +func NewSequenceVerifier() *SequenceVerifier { + return &SequenceVerifier{ + stack: []StackSymbol{EndStackSymbol, MsgStackSymbol}, + state: OpenPGPMessage, + } +} + +func (sv *SequenceVerifier) popStack() StackSymbol { + if len(sv.stack) == 0 { + return EmptyStackSymbol + } + elemIndex := len(sv.stack) - 1 + stackSymbol := sv.stack[elemIndex] + sv.stack = sv.stack[:elemIndex] + return stackSymbol +} + +func (sv *SequenceVerifier) pushStack(stackSymbol StackSymbol) { + sv.stack = append(sv.stack, stackSymbol) +} + +func getTransition(from State) transition { + switch from { + case OpenPGPMessage: + return fromOpenPGPMessage + case LiteralMessage: + return fromLiteralMessage + case CompressedMessage: + return fromCompressedMessage + case EncryptedMessage: + return fromEncryptedMessage + case ESKMessage: + return fromESKMessage + case ValidMessage: + return fromValidMessage + } + return nil +} + +// fromOpenPGPMessage is the transition for the state OpenPGPMessage. +func fromOpenPGPMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) { + if stackSymbol != MsgStackSymbol { + return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol) + } + switch input { + case LDSymbol: + return LiteralMessage, nil, false, nil + case SigSymbol: + return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, false, nil + case OPSSymbol: + return OpenPGPMessage, []StackSymbol{OpsStackSymbol, MsgStackSymbol}, false, nil + case CompSymbol: + return CompressedMessage, nil, false, nil + case ESKSymbol: + return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil + case EncSymbol: + return EncryptedMessage, nil, false, nil + } + return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol) +} + +// fromESKMessage is the transition for the state ESKMessage. +func fromESKMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) { + if stackSymbol != KeyStackSymbol { + return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol) + } + switch input { + case ESKSymbol: + return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil + case EncSymbol: + return EncryptedMessage, nil, false, nil + } + return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol) +} + +// fromLiteralMessage is the transition for the state LiteralMessage. +func fromLiteralMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) { + switch input { + case SigSymbol: + if stackSymbol == OpsStackSymbol { + return LiteralMessage, nil, false, nil + } + case EOSSymbol: + if stackSymbol == EndStackSymbol { + return ValidMessage, nil, false, nil + } + } + return 0, nil, false, NewErrMalformedMessage(LiteralMessage, input, stackSymbol) +} + +// fromLiteralMessage is the transition for the state CompressedMessage. +func fromCompressedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) { + switch input { + case SigSymbol: + if stackSymbol == OpsStackSymbol { + return CompressedMessage, nil, false, nil + } + case EOSSymbol: + if stackSymbol == EndStackSymbol { + return ValidMessage, nil, false, nil + } + } + return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil +} + +// fromEncryptedMessage is the transition for the state EncryptedMessage. +func fromEncryptedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) { + switch input { + case SigSymbol: + if stackSymbol == OpsStackSymbol { + return EncryptedMessage, nil, false, nil + } + case EOSSymbol: + if stackSymbol == EndStackSymbol { + return ValidMessage, nil, false, nil + } + } + return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil +} + +// fromValidMessage is the transition for the state ValidMessage. +func fromValidMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) { + return 0, nil, false, NewErrMalformedMessage(ValidMessage, input, stackSymbol) +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go new file mode 100644 index 0000000000..2d714723cf --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go @@ -0,0 +1,24 @@ +package packet + +import ( + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" +) + +// UnsupportedPackage represents a OpenPGP packet with a known packet type +// but with unsupported content. +type UnsupportedPacket struct { + IncompletePacket Packet + Error errors.UnsupportedError +} + +// Implements the Packet interface +func (up *UnsupportedPacket) parse(read io.Reader) error { + err := up.IncompletePacket.parse(read) + if castedErr, ok := err.(errors.UnsupportedError); ok { + up.Error = castedErr + return nil + } + return err +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go new file mode 100644 index 0000000000..3b6a7045d1 --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go @@ -0,0 +1,26 @@ +package packet + +import ( + "io" +) + +// Padding type represents a Padding Packet (Tag 21). +// The padding type is represented by the length of its padding. +// see https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21 +type Padding int + +// parse just ignores the padding content. +func (pad Padding) parse(reader io.Reader) error { + _, err := io.CopyN(io.Discard, reader, int64(pad)) + return err +} + +// SerializePadding writes the padding to writer. +func (pad Padding) SerializePadding(writer io.Writer, rand io.Reader) error { + err := serializeHeader(writer, packetPadding, int(pad)) + if err != nil { + return err + } + _, err = io.CopyN(writer, rand, int64(pad)) + return err +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go index 2fc4386437..f04e6c6b87 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go @@ -9,22 +9,28 @@ import ( "crypto" "crypto/cipher" "crypto/dsa" - "crypto/rand" "crypto/rsa" "crypto/sha1" + "crypto/sha256" + "crypto/subtle" + "fmt" "io" - "io/ioutil" "math/big" "strconv" "time" "github.com/ProtonMail/go-crypto/openpgp/ecdh" "github.com/ProtonMail/go-crypto/openpgp/ecdsa" + "github.com/ProtonMail/go-crypto/openpgp/ed25519" + "github.com/ProtonMail/go-crypto/openpgp/ed448" "github.com/ProtonMail/go-crypto/openpgp/eddsa" "github.com/ProtonMail/go-crypto/openpgp/elgamal" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" "github.com/ProtonMail/go-crypto/openpgp/s2k" + "github.com/ProtonMail/go-crypto/openpgp/x25519" + "github.com/ProtonMail/go-crypto/openpgp/x448" + "golang.org/x/crypto/hkdf" ) // PrivateKey represents a possibly encrypted private key. See RFC 4880, @@ -35,14 +41,14 @@ type PrivateKey struct { encryptedData []byte cipher CipherFunction s2k func(out, in []byte) - // An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or + aead AEADMode // only relevant if S2KAEAD is enabled + // An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519|ed448}.PrivateKey or // crypto.Signer/crypto.Decrypter (Decryptor RSA only). - PrivateKey interface{} - sha1Checksum bool - iv []byte + PrivateKey interface{} + iv []byte // Type of encryption of the S2K packet - // Allowed values are 0 (Not encrypted), 254 (SHA1), or + // Allowed values are 0 (Not encrypted), 253 (AEAD), 254 (SHA1), or // 255 (2-byte checksum) s2kType S2KType // Full parameters of the S2K packet @@ -55,6 +61,8 @@ type S2KType uint8 const ( // S2KNON unencrypt S2KNON S2KType = 0 + // S2KAEAD use authenticated encryption + S2KAEAD S2KType = 253 // S2KSHA1 sha1 sum check S2KSHA1 S2KType = 254 // S2KCHECKSUM sum check @@ -103,6 +111,34 @@ func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKe return pk } +func NewX25519PrivateKey(creationTime time.Time, priv *x25519.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewX25519PublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewX448PrivateKey(creationTime time.Time, priv *x448.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewX448PublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewEd25519PrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewEd25519PublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + +func NewEd448PrivateKey(creationTime time.Time, priv *ed448.PrivateKey) *PrivateKey { + pk := new(PrivateKey) + pk.PublicKey = *NewEd448PublicKey(creationTime, &priv.PublicKey) + pk.PrivateKey = priv + return pk +} + // NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that // implements RSA, ECDSA or EdDSA. func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey { @@ -122,6 +158,14 @@ func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey) case eddsa.PrivateKey: pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey) + case *ed25519.PrivateKey: + pk.PublicKey = *NewEd25519PublicKey(creationTime, &pubkey.PublicKey) + case ed25519.PrivateKey: + pk.PublicKey = *NewEd25519PublicKey(creationTime, &pubkey.PublicKey) + case *ed448.PrivateKey: + pk.PublicKey = *NewEd448PublicKey(creationTime, &pubkey.PublicKey) + case ed448.PrivateKey: + pk.PublicKey = *NewEd448PublicKey(creationTime, &pubkey.PublicKey) default: panic("openpgp: unknown signer type in NewSignerPrivateKey") } @@ -129,7 +173,7 @@ func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey return pk } -// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey. +// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh|x25519|x448}.PrivateKey. func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey { pk := new(PrivateKey) switch priv := decrypter.(type) { @@ -139,6 +183,10 @@ func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *Priv pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey) case *ecdh.PrivateKey: pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey) + case *x25519.PrivateKey: + pk.PublicKey = *NewX25519PublicKey(creationTime, &priv.PublicKey) + case *x448.PrivateKey: + pk.PublicKey = *NewX448PublicKey(creationTime, &priv.PublicKey) default: panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey") } @@ -152,6 +200,11 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { return } v5 := pk.PublicKey.Version == 5 + v6 := pk.PublicKey.Version == 6 + + if V5Disabled && v5 { + return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed") + } var buf [1]byte _, err = readFull(r, buf[:]) @@ -160,7 +213,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { } pk.s2kType = S2KType(buf[0]) var optCount [1]byte - if v5 { + if v5 || (v6 && pk.s2kType != S2KNON) { if _, err = readFull(r, optCount[:]); err != nil { return } @@ -170,9 +223,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { case S2KNON: pk.s2k = nil pk.Encrypted = false - case S2KSHA1, S2KCHECKSUM: - if v5 && pk.s2kType == S2KCHECKSUM { - return errors.StructuralError("wrong s2k identifier for version 5") + case S2KSHA1, S2KCHECKSUM, S2KAEAD: + if (v5 || v6) && pk.s2kType == S2KCHECKSUM { + return errors.StructuralError(fmt.Sprintf("wrong s2k identifier for version %d", pk.Version)) } _, err = readFull(r, buf[:]) if err != nil { @@ -182,6 +235,29 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { if pk.cipher != 0 && !pk.cipher.IsSupported() { return errors.UnsupportedError("unsupported cipher function in private key") } + // [Optional] If string-to-key usage octet was 253, + // a one-octet AEAD algorithm. + if pk.s2kType == S2KAEAD { + _, err = readFull(r, buf[:]) + if err != nil { + return + } + pk.aead = AEADMode(buf[0]) + if !pk.aead.IsSupported() { + return errors.UnsupportedError("unsupported aead mode in private key") + } + } + + // [Optional] Only for a version 6 packet, + // and if string-to-key usage octet was 255, 254, or 253, + // an one-octet count of the following field. + if v6 { + _, err = readFull(r, buf[:]) + if err != nil { + return + } + } + pk.s2kParams, err = s2k.ParseIntoParams(r) if err != nil { return @@ -189,28 +265,43 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { if pk.s2kParams.Dummy() { return } + if pk.s2kParams.Mode() == s2k.Argon2S2K && pk.s2kType != S2KAEAD { + return errors.StructuralError("using Argon2 S2K without AEAD is not allowed") + } + if pk.s2kParams.Mode() == s2k.SimpleS2K && pk.Version == 6 { + return errors.StructuralError("using Simple S2K with version 6 keys is not allowed") + } pk.s2k, err = pk.s2kParams.Function() if err != nil { return } pk.Encrypted = true - if pk.s2kType == S2KSHA1 { - pk.sha1Checksum = true - } default: return errors.UnsupportedError("deprecated s2k function in private key") } if pk.Encrypted { - blockSize := pk.cipher.blockSize() - if blockSize == 0 { + var ivSize int + // If the S2K usage octet was 253, the IV is of the size expected by the AEAD mode, + // unless it's a version 5 key, in which case it's the size of the symmetric cipher's block size. + // For all other S2K modes, it's always the block size. + if !v5 && pk.s2kType == S2KAEAD { + ivSize = pk.aead.IvLength() + } else { + ivSize = pk.cipher.blockSize() + } + + if ivSize == 0 { return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) } - pk.iv = make([]byte, blockSize) + pk.iv = make([]byte, ivSize) _, err = readFull(r, pk.iv) if err != nil { return } + if v5 && pk.s2kType == S2KAEAD { + pk.iv = pk.iv[:pk.aead.IvLength()] + } } var privateKeyData []byte @@ -230,7 +321,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { return } } else { - privateKeyData, err = ioutil.ReadAll(r) + privateKeyData, err = io.ReadAll(r) if err != nil { return } @@ -239,16 +330,22 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) { if len(privateKeyData) < 2 { return errors.StructuralError("truncated private key data") } - var sum uint16 - for i := 0; i < len(privateKeyData)-2; i++ { - sum += uint16(privateKeyData[i]) - } - if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) || - privateKeyData[len(privateKeyData)-1] != uint8(sum) { - return errors.StructuralError("private key checksum failure") + if pk.Version != 6 { + // checksum + var sum uint16 + for i := 0; i < len(privateKeyData)-2; i++ { + sum += uint16(privateKeyData[i]) + } + if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) || + privateKeyData[len(privateKeyData)-1] != uint8(sum) { + return errors.StructuralError("private key checksum failure") + } + privateKeyData = privateKeyData[:len(privateKeyData)-2] + return pk.parsePrivateKey(privateKeyData) + } else { + // No checksum + return pk.parsePrivateKey(privateKeyData) } - privateKeyData = privateKeyData[:len(privateKeyData)-2] - return pk.parsePrivateKey(privateKeyData) } pk.encryptedData = privateKeyData @@ -280,18 +377,59 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) { optional := bytes.NewBuffer(nil) if pk.Encrypted || pk.Dummy() { - optional.Write([]byte{uint8(pk.cipher)}) - if err := pk.s2kParams.Serialize(optional); err != nil { + // [Optional] If string-to-key usage octet was 255, 254, or 253, + // a one-octet symmetric encryption algorithm. + if _, err = optional.Write([]byte{uint8(pk.cipher)}); err != nil { + return + } + // [Optional] If string-to-key usage octet was 253, + // a one-octet AEAD algorithm. + if pk.s2kType == S2KAEAD { + if _, err = optional.Write([]byte{uint8(pk.aead)}); err != nil { + return + } + } + + s2kBuffer := bytes.NewBuffer(nil) + if err := pk.s2kParams.Serialize(s2kBuffer); err != nil { return err } + // [Optional] Only for a version 6 packet, and if string-to-key + // usage octet was 255, 254, or 253, an one-octet + // count of the following field. + if pk.Version == 6 { + if _, err = optional.Write([]byte{uint8(s2kBuffer.Len())}); err != nil { + return + } + } + // [Optional] If string-to-key usage octet was 255, 254, or 253, + // a string-to-key (S2K) specifier. The length of the string-to-key specifier + // depends on its type + if _, err = io.Copy(optional, s2kBuffer); err != nil { + return + } + + // IV if pk.Encrypted { - optional.Write(pk.iv) + if _, err = optional.Write(pk.iv); err != nil { + return + } + if pk.Version == 5 && pk.s2kType == S2KAEAD { + // Add padding for version 5 + padding := make([]byte, pk.cipher.blockSize()-len(pk.iv)) + if _, err = optional.Write(padding); err != nil { + return + } + } } } - if pk.Version == 5 { + if pk.Version == 5 || (pk.Version == 6 && pk.s2kType != S2KNON) { contents.Write([]byte{uint8(optional.Len())}) } - io.Copy(contents, optional) + + if _, err := io.Copy(contents, optional); err != nil { + return err + } if !pk.Dummy() { l := 0 @@ -303,8 +441,10 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) { return err } l = buf.Len() - checksum := mod64kHash(buf.Bytes()) - buf.Write([]byte{byte(checksum >> 8), byte(checksum)}) + if pk.Version != 6 { + checksum := mod64kHash(buf.Bytes()) + buf.Write([]byte{byte(checksum >> 8), byte(checksum)}) + } priv = buf.Bytes() } else { priv, l = pk.encryptedData, len(pk.encryptedData) @@ -370,6 +510,26 @@ func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error { return err } +func serializeX25519PrivateKey(w io.Writer, priv *x25519.PrivateKey) error { + _, err := w.Write(priv.Secret) + return err +} + +func serializeX448PrivateKey(w io.Writer, priv *x448.PrivateKey) error { + _, err := w.Write(priv.Secret) + return err +} + +func serializeEd25519PrivateKey(w io.Writer, priv *ed25519.PrivateKey) error { + _, err := w.Write(priv.MarshalByteSecret()) + return err +} + +func serializeEd448PrivateKey(w io.Writer, priv *ed448.PrivateKey) error { + _, err := w.Write(priv.MarshalByteSecret()) + return err +} + // decrypt decrypts an encrypted private key using a decryption key. func (pk *PrivateKey) decrypt(decryptionKey []byte) error { if pk.Dummy() { @@ -378,37 +538,51 @@ func (pk *PrivateKey) decrypt(decryptionKey []byte) error { if !pk.Encrypted { return nil } - block := pk.cipher.new(decryptionKey) - cfb := cipher.NewCFBDecrypter(block, pk.iv) - - data := make([]byte, len(pk.encryptedData)) - cfb.XORKeyStream(data, pk.encryptedData) - - if pk.sha1Checksum { - if len(data) < sha1.Size { - return errors.StructuralError("truncated private key data") - } - h := sha1.New() - h.Write(data[:len(data)-sha1.Size]) - sum := h.Sum(nil) - if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { - return errors.StructuralError("private key checksum failure") - } - data = data[:len(data)-sha1.Size] - } else { - if len(data) < 2 { - return errors.StructuralError("truncated private key data") + var data []byte + switch pk.s2kType { + case S2KAEAD: + aead := pk.aead.new(block) + additionalData, err := pk.additionalData() + if err != nil { + return err } - var sum uint16 - for i := 0; i < len(data)-2; i++ { - sum += uint16(data[i]) + // Decrypt the encrypted key material with aead + data, err = aead.Open(nil, pk.iv, pk.encryptedData, additionalData) + if err != nil { + return err } - if data[len(data)-2] != uint8(sum>>8) || - data[len(data)-1] != uint8(sum) { - return errors.StructuralError("private key checksum failure") + case S2KSHA1, S2KCHECKSUM: + cfb := cipher.NewCFBDecrypter(block, pk.iv) + data = make([]byte, len(pk.encryptedData)) + cfb.XORKeyStream(data, pk.encryptedData) + if pk.s2kType == S2KSHA1 { + if len(data) < sha1.Size { + return errors.StructuralError("truncated private key data") + } + h := sha1.New() + h.Write(data[:len(data)-sha1.Size]) + sum := h.Sum(nil) + if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-sha1.Size] + } else { + if len(data) < 2 { + return errors.StructuralError("truncated private key data") + } + var sum uint16 + for i := 0; i < len(data)-2; i++ { + sum += uint16(data[i]) + } + if data[len(data)-2] != uint8(sum>>8) || + data[len(data)-1] != uint8(sum) { + return errors.StructuralError("private key checksum failure") + } + data = data[:len(data)-2] } - data = data[:len(data)-2] + default: + return errors.InvalidArgumentError("invalid s2k type") } err := pk.parsePrivateKey(data) @@ -424,7 +598,6 @@ func (pk *PrivateKey) decrypt(decryptionKey []byte) error { pk.s2k = nil pk.Encrypted = false pk.encryptedData = nil - return nil } @@ -440,6 +613,9 @@ func (pk *PrivateKey) decryptWithCache(passphrase []byte, keyCache *s2k.Cache) e if err != nil { return err } + if pk.s2kType == S2KAEAD { + key = pk.applyHKDF(key) + } return pk.decrypt(key) } @@ -454,11 +630,14 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error { key := make([]byte, pk.cipher.KeySize()) pk.s2k(key, passphrase) + if pk.s2kType == S2KAEAD { + key = pk.applyHKDF(key) + } return pk.decrypt(key) } // DecryptPrivateKeys decrypts all encrypted keys with the given config and passphrase. -// Avoids recomputation of similar s2k key derivations. +// Avoids recomputation of similar s2k key derivations. func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error { // Create a cache to avoid recomputation of key derviations for the same passphrase. s2kCache := &s2k.Cache{} @@ -474,7 +653,7 @@ func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error { } // encrypt encrypts an unencrypted private key. -func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction CipherFunction) error { +func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, s2kType S2KType, cipherFunction CipherFunction, rand io.Reader) error { if pk.Dummy() { return errors.ErrDummyPrivateKey("dummy key found") } @@ -485,7 +664,15 @@ func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction Cip if len(key) != cipherFunction.KeySize() { return errors.InvalidArgumentError("supplied encryption key has the wrong size") } - + + if params.Mode() == s2k.Argon2S2K && s2kType != S2KAEAD { + return errors.InvalidArgumentError("using Argon2 S2K without AEAD is not allowed") + } + if params.Mode() != s2k.Argon2S2K && params.Mode() != s2k.IteratedSaltedS2K && + params.Mode() != s2k.SaltedS2K { // only allowed for high-entropy passphrases + return errors.InvalidArgumentError("insecure S2K mode") + } + priv := bytes.NewBuffer(nil) err := pk.serializePrivateKey(priv) if err != nil { @@ -497,35 +684,53 @@ func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction Cip pk.s2k, err = pk.s2kParams.Function() if err != nil { return err - } + } privateKeyBytes := priv.Bytes() - pk.sha1Checksum = true + pk.s2kType = s2kType block := pk.cipher.new(key) - pk.iv = make([]byte, pk.cipher.blockSize()) - _, err = rand.Read(pk.iv) - if err != nil { - return err - } - cfb := cipher.NewCFBEncrypter(block, pk.iv) - - if pk.sha1Checksum { - pk.s2kType = S2KSHA1 - h := sha1.New() - h.Write(privateKeyBytes) - sum := h.Sum(nil) - privateKeyBytes = append(privateKeyBytes, sum...) - } else { - pk.s2kType = S2KCHECKSUM - var sum uint16 - for _, b := range privateKeyBytes { - sum += uint16(b) + switch s2kType { + case S2KAEAD: + if pk.aead == 0 { + return errors.StructuralError("aead mode is not set on key") } - priv.Write([]byte{uint8(sum >> 8), uint8(sum)}) + aead := pk.aead.new(block) + additionalData, err := pk.additionalData() + if err != nil { + return err + } + pk.iv = make([]byte, aead.NonceSize()) + _, err = io.ReadFull(rand, pk.iv) + if err != nil { + return err + } + // Decrypt the encrypted key material with aead + pk.encryptedData = aead.Seal(nil, pk.iv, privateKeyBytes, additionalData) + case S2KSHA1, S2KCHECKSUM: + pk.iv = make([]byte, pk.cipher.blockSize()) + _, err = io.ReadFull(rand, pk.iv) + if err != nil { + return err + } + cfb := cipher.NewCFBEncrypter(block, pk.iv) + if s2kType == S2KSHA1 { + h := sha1.New() + h.Write(privateKeyBytes) + sum := h.Sum(nil) + privateKeyBytes = append(privateKeyBytes, sum...) + } else { + var sum uint16 + for _, b := range privateKeyBytes { + sum += uint16(b) + } + privateKeyBytes = append(privateKeyBytes, []byte{uint8(sum >> 8), uint8(sum)}...) + } + pk.encryptedData = make([]byte, len(privateKeyBytes)) + cfb.XORKeyStream(pk.encryptedData, privateKeyBytes) + default: + return errors.InvalidArgumentError("invalid s2k type for encryption") } - pk.encryptedData = make([]byte, len(privateKeyBytes)) - cfb.XORKeyStream(pk.encryptedData, privateKeyBytes) pk.Encrypted = true pk.PrivateKey = nil return err @@ -544,8 +749,15 @@ func (pk *PrivateKey) EncryptWithConfig(passphrase []byte, config *Config) error return err } s2k(key, passphrase) + s2kType := S2KSHA1 + if config.AEAD() != nil { + s2kType = S2KAEAD + pk.aead = config.AEAD().Mode() + pk.cipher = config.Cipher() + key = pk.applyHKDF(key) + } // Encrypt the private key with the derived encryption key. - return pk.encrypt(key, params, config.Cipher()) + return pk.encrypt(key, params, s2kType, config.Cipher(), config.Random()) } // EncryptPrivateKeys encrypts all unencrypted keys with the given config and passphrase. @@ -564,7 +776,16 @@ func EncryptPrivateKeys(keys []*PrivateKey, passphrase []byte, config *Config) e s2k(encryptionKey, passphrase) for _, key := range keys { if key != nil && !key.Dummy() && !key.Encrypted { - err = key.encrypt(encryptionKey, params, config.Cipher()) + s2kType := S2KSHA1 + if config.AEAD() != nil { + s2kType = S2KAEAD + key.aead = config.AEAD().Mode() + key.cipher = config.Cipher() + derivedKey := key.applyHKDF(encryptionKey) + err = key.encrypt(derivedKey, params, s2kType, config.Cipher(), config.Random()) + } else { + err = key.encrypt(encryptionKey, params, s2kType, config.Cipher(), config.Random()) + } if err != nil { return err } @@ -581,7 +802,7 @@ func (pk *PrivateKey) Encrypt(passphrase []byte) error { S2KMode: s2k.IteratedSaltedS2K, S2KCount: 65536, Hash: crypto.SHA256, - } , + }, DefaultCipher: CipherAES256, } return pk.EncryptWithConfig(passphrase, config) @@ -601,6 +822,14 @@ func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) { err = serializeEdDSAPrivateKey(w, priv) case *ecdh.PrivateKey: err = serializeECDHPrivateKey(w, priv) + case *x25519.PrivateKey: + err = serializeX25519PrivateKey(w, priv) + case *x448.PrivateKey: + err = serializeX448PrivateKey(w, priv) + case *ed25519.PrivateKey: + err = serializeEd25519PrivateKey(w, priv) + case *ed448.PrivateKey: + err = serializeEd448PrivateKey(w, priv) default: err = errors.InvalidArgumentError("unknown private key type") } @@ -621,8 +850,18 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) { return pk.parseECDHPrivateKey(data) case PubKeyAlgoEdDSA: return pk.parseEdDSAPrivateKey(data) + case PubKeyAlgoX25519: + return pk.parseX25519PrivateKey(data) + case PubKeyAlgoX448: + return pk.parseX448PrivateKey(data) + case PubKeyAlgoEd25519: + return pk.parseEd25519PrivateKey(data) + case PubKeyAlgoEd448: + return pk.parseEd448PrivateKey(data) + default: + err = errors.StructuralError("unknown private key type") + return } - panic("impossible") } func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { @@ -743,6 +982,86 @@ func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) { return nil } +func (pk *PrivateKey) parseX25519PrivateKey(data []byte) (err error) { + publicKey := pk.PublicKey.PublicKey.(*x25519.PublicKey) + privateKey := x25519.NewPrivateKey(*publicKey) + privateKey.PublicKey = *publicKey + + privateKey.Secret = make([]byte, x25519.KeySize) + + if len(data) != x25519.KeySize { + err = errors.StructuralError("wrong x25519 key size") + return err + } + subtle.ConstantTimeCopy(1, privateKey.Secret, data) + if err = x25519.Validate(privateKey); err != nil { + return err + } + pk.PrivateKey = privateKey + return nil +} + +func (pk *PrivateKey) parseX448PrivateKey(data []byte) (err error) { + publicKey := pk.PublicKey.PublicKey.(*x448.PublicKey) + privateKey := x448.NewPrivateKey(*publicKey) + privateKey.PublicKey = *publicKey + + privateKey.Secret = make([]byte, x448.KeySize) + + if len(data) != x448.KeySize { + err = errors.StructuralError("wrong x448 key size") + return err + } + subtle.ConstantTimeCopy(1, privateKey.Secret, data) + if err = x448.Validate(privateKey); err != nil { + return err + } + pk.PrivateKey = privateKey + return nil +} + +func (pk *PrivateKey) parseEd25519PrivateKey(data []byte) (err error) { + publicKey := pk.PublicKey.PublicKey.(*ed25519.PublicKey) + privateKey := ed25519.NewPrivateKey(*publicKey) + privateKey.PublicKey = *publicKey + + if len(data) != ed25519.SeedSize { + err = errors.StructuralError("wrong ed25519 key size") + return err + } + err = privateKey.UnmarshalByteSecret(data) + if err != nil { + return err + } + err = ed25519.Validate(privateKey) + if err != nil { + return err + } + pk.PrivateKey = privateKey + return nil +} + +func (pk *PrivateKey) parseEd448PrivateKey(data []byte) (err error) { + publicKey := pk.PublicKey.PublicKey.(*ed448.PublicKey) + privateKey := ed448.NewPrivateKey(*publicKey) + privateKey.PublicKey = *publicKey + + if len(data) != ed448.SeedSize { + err = errors.StructuralError("wrong ed448 key size") + return err + } + err = privateKey.UnmarshalByteSecret(data) + if err != nil { + return err + } + err = ed448.Validate(privateKey) + if err != nil { + return err + } + pk.PrivateKey = privateKey + return nil +} + func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) { eddsaPub := pk.PublicKey.PublicKey.(*eddsa.PublicKey) eddsaPriv := eddsa.NewPrivateKey(*eddsaPub) @@ -767,6 +1086,41 @@ func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) { return nil } +func (pk *PrivateKey) additionalData() ([]byte, error) { + additionalData := bytes.NewBuffer(nil) + // Write additional data prefix based on packet type + var packetByte byte + if pk.PublicKey.IsSubkey { + packetByte = 0xc7 + } else { + packetByte = 0xc5 + } + // Write public key to additional data + _, err := additionalData.Write([]byte{packetByte}) + if err != nil { + return nil, err + } + err = pk.PublicKey.serializeWithoutHeaders(additionalData) + if err != nil { + return nil, err + } + return additionalData.Bytes(), nil +} + +func (pk *PrivateKey) applyHKDF(inputKey []byte) []byte { + var packetByte byte + if pk.PublicKey.IsSubkey { + packetByte = 0xc7 + } else { + packetByte = 0xc5 + } + associatedData := []byte{packetByte, byte(pk.Version), byte(pk.cipher), byte(pk.aead)} + hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData) + encryptionKey := make([]byte, pk.cipher.KeySize()) + _, _ = readFull(hkdfReader, encryptionKey) + return encryptionKey +} + func validateDSAParameters(priv *dsa.PrivateKey) error { p := priv.P // group prime q := priv.Q // subgroup order diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go index 3402b8c140..f8da781bbe 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go @@ -5,7 +5,6 @@ package packet import ( - "crypto" "crypto/dsa" "crypto/rsa" "crypto/sha1" @@ -21,23 +20,24 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/ecdh" "github.com/ProtonMail/go-crypto/openpgp/ecdsa" + "github.com/ProtonMail/go-crypto/openpgp/ed25519" + "github.com/ProtonMail/go-crypto/openpgp/ed448" "github.com/ProtonMail/go-crypto/openpgp/eddsa" "github.com/ProtonMail/go-crypto/openpgp/elgamal" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" + "github.com/ProtonMail/go-crypto/openpgp/x25519" + "github.com/ProtonMail/go-crypto/openpgp/x448" ) -type kdfHashFunction byte -type kdfAlgorithm byte - // PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. type PublicKey struct { Version int CreationTime time.Time PubKeyAlgo PublicKeyAlgorithm - PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey + PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey, *x25519.PublicKey, *x448.PublicKey, *ed25519.PublicKey, *ed448.PublicKey Fingerprint []byte KeyId uint64 IsSubkey bool @@ -61,11 +61,19 @@ func (pk *PublicKey) UpgradeToV5() { pk.setFingerprintAndKeyId() } +// UpgradeToV6 updates the version of the key to v6, and updates all necessary +// fields. +func (pk *PublicKey) UpgradeToV6() error { + pk.Version = 6 + pk.setFingerprintAndKeyId() + return pk.checkV6Compatibility() +} + // signingKey provides a convenient abstraction over signature verification // for v3 and v4 public keys. type signingKey interface { SerializeForHash(io.Writer) error - SerializeSignaturePrefix(io.Writer) + SerializeSignaturePrefix(io.Writer) error serializeWithoutHeaders(io.Writer) error } @@ -174,6 +182,54 @@ func NewEdDSAPublicKey(creationTime time.Time, pub *eddsa.PublicKey) *PublicKey return pk } +func NewX25519PublicKey(creationTime time.Time, pub *x25519.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoX25519, + PublicKey: pub, + } + + pk.setFingerprintAndKeyId() + return pk +} + +func NewX448PublicKey(creationTime time.Time, pub *x448.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoX448, + PublicKey: pub, + } + + pk.setFingerprintAndKeyId() + return pk +} + +func NewEd25519PublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoEd25519, + PublicKey: pub, + } + + pk.setFingerprintAndKeyId() + return pk +} + +func NewEd448PublicKey(creationTime time.Time, pub *ed448.PublicKey) *PublicKey { + pk := &PublicKey{ + Version: 4, + CreationTime: creationTime, + PubKeyAlgo: PubKeyAlgoEd448, + PublicKey: pub, + } + + pk.setFingerprintAndKeyId() + return pk +} + func (pk *PublicKey) parse(r io.Reader) (err error) { // RFC 4880, section 5.5.2 var buf [6]byte @@ -181,12 +237,19 @@ func (pk *PublicKey) parse(r io.Reader) (err error) { if err != nil { return } - if buf[0] != 4 && buf[0] != 5 { + + pk.Version = int(buf[0]) + if pk.Version != 4 && pk.Version != 5 && pk.Version != 6 { return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0]))) } - pk.Version = int(buf[0]) - if pk.Version == 5 { + if V5Disabled && pk.Version == 5 { + return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed") + } + + if pk.Version >= 5 { + // Read the four-octet scalar octet count + // The count is not used in this implementation var n [4]byte _, err = readFull(r, n[:]) if err != nil { @@ -195,6 +258,7 @@ func (pk *PublicKey) parse(r io.Reader) (err error) { } pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) + // Ignore four-ocet length switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: err = pk.parseRSA(r) @@ -208,6 +272,14 @@ func (pk *PublicKey) parse(r io.Reader) (err error) { err = pk.parseECDH(r) case PubKeyAlgoEdDSA: err = pk.parseEdDSA(r) + case PubKeyAlgoX25519: + err = pk.parseX25519(r) + case PubKeyAlgoX448: + err = pk.parseX448(r) + case PubKeyAlgoEd25519: + err = pk.parseEd25519(r) + case PubKeyAlgoEd448: + err = pk.parseEd448(r) default: err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } @@ -221,21 +293,44 @@ func (pk *PublicKey) parse(r io.Reader) (err error) { func (pk *PublicKey) setFingerprintAndKeyId() { // RFC 4880, section 12.2 - if pk.Version == 5 { + if pk.Version >= 5 { fingerprint := sha256.New() - pk.SerializeForHash(fingerprint) + if err := pk.SerializeForHash(fingerprint); err != nil { + // Should not happen for a hash. + panic(err) + } pk.Fingerprint = make([]byte, 32) copy(pk.Fingerprint, fingerprint.Sum(nil)) pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8]) } else { fingerprint := sha1.New() - pk.SerializeForHash(fingerprint) + if err := pk.SerializeForHash(fingerprint); err != nil { + // Should not happen for a hash. + panic(err) + } pk.Fingerprint = make([]byte, 20) copy(pk.Fingerprint, fingerprint.Sum(nil)) pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) } } +func (pk *PublicKey) checkV6Compatibility() error { + // Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs. + switch pk.PubKeyAlgo { + case PubKeyAlgoECDH: + curveInfo := ecc.FindByOid(pk.oid) + if curveInfo == nil { + return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid)) + } + if curveInfo.GenName == ecc.Curve25519GenName { + return errors.StructuralError("cannot generate v6 key with deprecated OID: Curve25519Legacy") + } + case PubKeyAlgoEdDSA: + return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy") + } + return nil +} + // parseRSA parses RSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKey) parseRSA(r io.Reader) (err error) { @@ -324,16 +419,17 @@ func (pk *PublicKey) parseECDSA(r io.Reader) (err error) { if _, err = pk.oid.ReadFrom(r); err != nil { return } - pk.p = new(encoding.MPI) - if _, err = pk.p.ReadFrom(r); err != nil { - return - } curveInfo := ecc.FindByOid(pk.oid) if curveInfo == nil { return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid)) } + pk.p = new(encoding.MPI) + if _, err = pk.p.ReadFrom(r); err != nil { + return + } + c, ok := curveInfo.Curve.(ecc.ECDSACurve) if !ok { return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) @@ -353,6 +449,17 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) { if _, err = pk.oid.ReadFrom(r); err != nil { return } + + curveInfo := ecc.FindByOid(pk.oid) + if curveInfo == nil { + return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid)) + } + + if pk.Version == 6 && curveInfo.GenName == ecc.Curve25519GenName { + // Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs. + return errors.StructuralError("cannot read v6 key with deprecated OID: Curve25519Legacy") + } + pk.p = new(encoding.MPI) if _, err = pk.p.ReadFrom(r); err != nil { return @@ -362,12 +469,6 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) { return } - curveInfo := ecc.FindByOid(pk.oid) - - if curveInfo == nil { - return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid)) - } - c, ok := curveInfo.Curve.(ecc.ECDHCurve) if !ok { return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid)) @@ -396,10 +497,16 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) { } func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) { + if pk.Version == 6 { + // Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs. + return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy") + } + pk.oid = new(encoding.OID) if _, err = pk.oid.ReadFrom(r); err != nil { return } + curveInfo := ecc.FindByOid(pk.oid) if curveInfo == nil { return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid)) @@ -435,75 +542,145 @@ func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) { return } +func (pk *PublicKey) parseX25519(r io.Reader) (err error) { + point := make([]byte, x25519.KeySize) + _, err = io.ReadFull(r, point) + if err != nil { + return + } + pub := &x25519.PublicKey{ + Point: point, + } + pk.PublicKey = pub + return +} + +func (pk *PublicKey) parseX448(r io.Reader) (err error) { + point := make([]byte, x448.KeySize) + _, err = io.ReadFull(r, point) + if err != nil { + return + } + pub := &x448.PublicKey{ + Point: point, + } + pk.PublicKey = pub + return +} + +func (pk *PublicKey) parseEd25519(r io.Reader) (err error) { + point := make([]byte, ed25519.PublicKeySize) + _, err = io.ReadFull(r, point) + if err != nil { + return + } + pub := &ed25519.PublicKey{ + Point: point, + } + pk.PublicKey = pub + return +} + +func (pk *PublicKey) parseEd448(r io.Reader) (err error) { + point := make([]byte, ed448.PublicKeySize) + _, err = io.ReadFull(r, point) + if err != nil { + return + } + pub := &ed448.PublicKey{ + Point: point, + } + pk.PublicKey = pub + return +} + // SerializeForHash serializes the PublicKey to w with the special packet // header format needed for hashing. func (pk *PublicKey) SerializeForHash(w io.Writer) error { - pk.SerializeSignaturePrefix(w) + if err := pk.SerializeSignaturePrefix(w); err != nil { + return err + } return pk.serializeWithoutHeaders(w) } // SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // The prefix is used when calculating a signature over this public key. See // RFC 4880, section 5.2.4. -func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) { +func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) error { var pLength = pk.algorithmSpecificByteCount() - if pk.Version == 5 { - pLength += 10 // version, timestamp (4), algorithm, key octet count (4). - w.Write([]byte{ - 0x9A, + // version, timestamp, algorithm + pLength += versionSize + timestampSize + algorithmSize + if pk.Version >= 5 { + // key octet count (4). + pLength += 4 + _, err := w.Write([]byte{ + // When a v4 signature is made over a key, the hash data starts with the octet 0x99, followed by a two-octet length + // of the key, and then the body of the key packet. When a v6 signature is made over a key, the hash data starts + // with the salt, then octet 0x9B, followed by a four-octet length of the key, and then the body of the key packet. + 0x95 + byte(pk.Version), byte(pLength >> 24), byte(pLength >> 16), byte(pLength >> 8), byte(pLength), }) - return + return err } - pLength += 6 - w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) + if _, err := w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}); err != nil { + return err + } + return nil } func (pk *PublicKey) Serialize(w io.Writer) (err error) { - length := 6 // 6 byte header + length := uint32(versionSize + timestampSize + algorithmSize) // 6 byte header length += pk.algorithmSpecificByteCount() - if pk.Version == 5 { + if pk.Version >= 5 { length += 4 // octet key count } packetType := packetTypePublicKey if pk.IsSubkey { packetType = packetTypePublicSubkey } - err = serializeHeader(w, packetType, length) + err = serializeHeader(w, packetType, int(length)) if err != nil { return } return pk.serializeWithoutHeaders(w) } -func (pk *PublicKey) algorithmSpecificByteCount() int { - length := 0 +func (pk *PublicKey) algorithmSpecificByteCount() uint32 { + length := uint32(0) switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - length += int(pk.n.EncodedLength()) - length += int(pk.e.EncodedLength()) + length += uint32(pk.n.EncodedLength()) + length += uint32(pk.e.EncodedLength()) case PubKeyAlgoDSA: - length += int(pk.p.EncodedLength()) - length += int(pk.q.EncodedLength()) - length += int(pk.g.EncodedLength()) - length += int(pk.y.EncodedLength()) + length += uint32(pk.p.EncodedLength()) + length += uint32(pk.q.EncodedLength()) + length += uint32(pk.g.EncodedLength()) + length += uint32(pk.y.EncodedLength()) case PubKeyAlgoElGamal: - length += int(pk.p.EncodedLength()) - length += int(pk.g.EncodedLength()) - length += int(pk.y.EncodedLength()) + length += uint32(pk.p.EncodedLength()) + length += uint32(pk.g.EncodedLength()) + length += uint32(pk.y.EncodedLength()) case PubKeyAlgoECDSA: - length += int(pk.oid.EncodedLength()) - length += int(pk.p.EncodedLength()) + length += uint32(pk.oid.EncodedLength()) + length += uint32(pk.p.EncodedLength()) case PubKeyAlgoECDH: - length += int(pk.oid.EncodedLength()) - length += int(pk.p.EncodedLength()) - length += int(pk.kdf.EncodedLength()) + length += uint32(pk.oid.EncodedLength()) + length += uint32(pk.p.EncodedLength()) + length += uint32(pk.kdf.EncodedLength()) case PubKeyAlgoEdDSA: - length += int(pk.oid.EncodedLength()) - length += int(pk.p.EncodedLength()) + length += uint32(pk.oid.EncodedLength()) + length += uint32(pk.p.EncodedLength()) + case PubKeyAlgoX25519: + length += x25519.KeySize + case PubKeyAlgoX448: + length += x448.KeySize + case PubKeyAlgoEd25519: + length += ed25519.PublicKeySize + case PubKeyAlgoEd448: + length += ed448.PublicKeySize default: panic("unknown public key algorithm") } @@ -522,7 +699,7 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { return } - if pk.Version == 5 { + if pk.Version >= 5 { n := pk.algorithmSpecificByteCount() if _, err = w.Write([]byte{ byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n), @@ -580,6 +757,22 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { } _, err = w.Write(pk.p.EncodedBytes()) return + case PubKeyAlgoX25519: + publicKey := pk.PublicKey.(*x25519.PublicKey) + _, err = w.Write(publicKey.Point) + return + case PubKeyAlgoX448: + publicKey := pk.PublicKey.(*x448.PublicKey) + _, err = w.Write(publicKey.Point) + return + case PubKeyAlgoEd25519: + publicKey := pk.PublicKey.(*ed25519.PublicKey) + _, err = w.Write(publicKey.Point) + return + case PubKeyAlgoEd448: + publicKey := pk.PublicKey.(*ed448.PublicKey) + _, err = w.Write(publicKey.Point) + return } return errors.InvalidArgumentError("bad public-key algorithm") } @@ -589,6 +782,20 @@ func (pk *PublicKey) CanSign() bool { return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH } +// VerifyHashTag returns nil iff sig appears to be a plausible signature of the data +// hashed into signed, based solely on its HashTag. signed is mutated by this call. +func VerifyHashTag(signed hash.Hash, sig *Signature) (err error) { + if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) { + sig.AddMetadataToHashSuffix() + } + signed.Write(sig.HashSuffix) + hashBytes := signed.Sum(nil) + if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { + return errors.SignatureError("hash tag doesn't match") + } + return nil +} + // VerifySignature returns nil iff sig is a valid signature, made by this // public key, of the data hashed into signed. signed is mutated by this call. func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) { @@ -600,7 +807,8 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro } signed.Write(sig.HashSuffix) hashBytes := signed.Sum(nil) - if sig.Version == 5 && (hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1]) { + // see discussion https://github.com/ProtonMail/go-crypto/issues/107 + if sig.Version >= 5 && (hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1]) { return errors.SignatureError("hash tag doesn't match") } @@ -639,6 +847,18 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro return errors.SignatureError("EdDSA verification failure") } return nil + case PubKeyAlgoEd25519: + ed25519PublicKey := pk.PublicKey.(*ed25519.PublicKey) + if !ed25519.Verify(ed25519PublicKey, hashBytes, sig.EdSig) { + return errors.SignatureError("Ed25519 verification failure") + } + return nil + case PubKeyAlgoEd448: + ed448PublicKey := pk.PublicKey.(*ed448.PublicKey) + if !ed448.Verify(ed448PublicKey, hashBytes, sig.EdSig) { + return errors.SignatureError("ed448 verification failure") + } + return nil default: return errors.SignatureError("Unsupported public key algorithm used in signature") } @@ -646,11 +866,8 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro // keySignatureHash returns a Hash of the message that needs to be signed for // pk to assert a subkey relationship to signed. -func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { - if !hashFunc.Available() { - return nil, errors.UnsupportedError("hash function") - } - h = hashFunc.New() +func keySignatureHash(pk, signed signingKey, hashFunc hash.Hash) (h hash.Hash, err error) { + h = hashFunc // RFC 4880, section 5.2.4 err = pk.SerializeForHash(h) @@ -662,10 +879,28 @@ func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, return } +// VerifyKeyHashTag returns nil iff sig appears to be a plausible signature over this +// primary key and subkey, based solely on its HashTag. +func (pk *PublicKey) VerifyKeyHashTag(signed *PublicKey, sig *Signature) error { + preparedHash, err := sig.PrepareVerify() + if err != nil { + return err + } + h, err := keySignatureHash(pk, signed, preparedHash) + if err != nil { + return err + } + return VerifyHashTag(h, sig) +} + // VerifyKeySignature returns nil iff sig is a valid signature, made by this // public key, of signed. func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error { - h, err := keySignatureHash(pk, signed, sig.Hash) + preparedHash, err := sig.PrepareVerify() + if err != nil { + return err + } + h, err := keySignatureHash(pk, signed, preparedHash) if err != nil { return err } @@ -679,10 +914,14 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error if sig.EmbeddedSignature == nil { return errors.StructuralError("signing subkey is missing cross-signature") } + preparedHashEmbedded, err := sig.EmbeddedSignature.PrepareVerify() + if err != nil { + return err + } // Verify the cross-signature. This is calculated over the same // data as the main signature, so we cannot just recursively // call signed.VerifyKeySignature(...) - if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil { + if h, err = keySignatureHash(pk, signed, preparedHashEmbedded); err != nil { return errors.StructuralError("error while hashing for cross-signature: " + err.Error()) } if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil { @@ -693,32 +932,44 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error return nil } -func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { - if !hashFunc.Available() { - return nil, errors.UnsupportedError("hash function") - } - h = hashFunc.New() - - // RFC 4880, section 5.2.4 - err = pk.SerializeForHash(h) +func keyRevocationHash(pk signingKey, hashFunc hash.Hash) (err error) { + return pk.SerializeForHash(hashFunc) +} - return +// VerifyRevocationHashTag returns nil iff sig appears to be a plausible signature +// over this public key, based solely on its HashTag. +func (pk *PublicKey) VerifyRevocationHashTag(sig *Signature) (err error) { + preparedHash, err := sig.PrepareVerify() + if err != nil { + return err + } + if err = keyRevocationHash(pk, preparedHash); err != nil { + return err + } + return VerifyHashTag(preparedHash, sig) } // VerifyRevocationSignature returns nil iff sig is a valid signature, made by this // public key. func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) { - h, err := keyRevocationHash(pk, sig.Hash) + preparedHash, err := sig.PrepareVerify() if err != nil { return err } - return pk.VerifySignature(h, sig) + if err = keyRevocationHash(pk, preparedHash); err != nil { + return err + } + return pk.VerifySignature(preparedHash, sig) } // VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature, // made by this public key, of signed. func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *PublicKey) (err error) { - h, err := keySignatureHash(pk, signed, sig.Hash) + preparedHash, err := sig.PrepareVerify() + if err != nil { + return err + } + h, err := keySignatureHash(pk, signed, preparedHash) if err != nil { return err } @@ -727,15 +978,15 @@ func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *Pub // userIdSignatureHash returns a Hash of the message that needs to be signed // to assert that pk is a valid key for id. -func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { - if !hashFunc.Available() { - return nil, errors.UnsupportedError("hash function") - } - h = hashFunc.New() +func userIdSignatureHash(id string, pk *PublicKey, h hash.Hash) (err error) { // RFC 4880, section 5.2.4 - pk.SerializeSignaturePrefix(h) - pk.serializeWithoutHeaders(h) + if err := pk.SerializeSignaturePrefix(h); err != nil { + return err + } + if err := pk.serializeWithoutHeaders(h); err != nil { + return err + } var buf [5]byte buf[0] = 0xb4 @@ -746,16 +997,51 @@ func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash h.Write(buf[:]) h.Write([]byte(id)) - return + return nil +} + +// directKeySignatureHash returns a Hash of the message that needs to be signed. +func directKeySignatureHash(pk *PublicKey, h hash.Hash) (err error) { + return pk.SerializeForHash(h) +} + +// VerifyUserIdHashTag returns nil iff sig appears to be a plausible signature over this +// public key and UserId, based solely on its HashTag +func (pk *PublicKey) VerifyUserIdHashTag(id string, sig *Signature) (err error) { + preparedHash, err := sig.PrepareVerify() + if err != nil { + return err + } + err = userIdSignatureHash(id, pk, preparedHash) + if err != nil { + return err + } + return VerifyHashTag(preparedHash, sig) } // VerifyUserIdSignature returns nil iff sig is a valid signature, made by this // public key, that id is the identity of pub. func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) { - h, err := userIdSignatureHash(id, pub, sig.Hash) + h, err := sig.PrepareVerify() + if err != nil { + return err + } + if err := userIdSignatureHash(id, pub, h); err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + +// VerifyDirectKeySignature returns nil iff sig is a valid signature, made by this +// public key. +func (pk *PublicKey) VerifyDirectKeySignature(sig *Signature) (err error) { + h, err := sig.PrepareVerify() if err != nil { return err } + if err := directKeySignatureHash(pk, h); err != nil { + return err + } return pk.VerifySignature(h, sig) } @@ -786,21 +1072,49 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) { bitLength = pk.p.BitLength() case PubKeyAlgoEdDSA: bitLength = pk.p.BitLength() + case PubKeyAlgoX25519: + bitLength = x25519.KeySize * 8 + case PubKeyAlgoX448: + bitLength = x448.KeySize * 8 + case PubKeyAlgoEd25519: + bitLength = ed25519.PublicKeySize * 8 + case PubKeyAlgoEd448: + bitLength = ed448.PublicKeySize * 8 default: err = errors.InvalidArgumentError("bad public-key algorithm") } return } +// Curve returns the used elliptic curve of this public key. +// Returns an error if no elliptic curve is used. +func (pk *PublicKey) Curve() (curve Curve, err error) { + switch pk.PubKeyAlgo { + case PubKeyAlgoECDSA, PubKeyAlgoECDH, PubKeyAlgoEdDSA: + curveInfo := ecc.FindByOid(pk.oid) + if curveInfo == nil { + return "", errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid)) + } + curve = Curve(curveInfo.GenName) + case PubKeyAlgoEd25519, PubKeyAlgoX25519: + curve = Curve25519 + case PubKeyAlgoEd448, PubKeyAlgoX448: + curve = Curve448 + default: + err = errors.InvalidArgumentError("public key does not operate with an elliptic curve") + } + return +} + // KeyExpired returns whether sig is a self-signature of a key that has // expired or is created in the future. func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool { - if pk.CreationTime.After(currentTime) { + if pk.CreationTime.Unix() > currentTime.Unix() { return true } if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 { return false } expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) - return currentTime.After(expiry) + return currentTime.Unix() > expiry.Unix() } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go index 10215fe5f2..dd84092392 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go @@ -10,6 +10,12 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/errors" ) +type PacketReader interface { + Next() (p Packet, err error) + Push(reader io.Reader) (err error) + Unread(p Packet) +} + // Reader reads packets from an io.Reader and allows packets to be 'unread' so // that they result from the next call to Next. type Reader struct { @@ -26,37 +32,81 @@ type Reader struct { const maxReaders = 32 // Next returns the most recently unread Packet, or reads another packet from -// the top-most io.Reader. Unknown packet types are skipped. +// the top-most io.Reader. Unknown/unsupported/Marker packet types are skipped. func (r *Reader) Next() (p Packet, err error) { + for { + p, err := r.read() + if err == io.EOF { + break + } else if err != nil { + if _, ok := err.(errors.UnknownPacketTypeError); ok { + continue + } + if _, ok := err.(errors.UnsupportedError); ok { + switch p.(type) { + case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData: + return nil, err + } + continue + } + return nil, err + } else { + //A marker packet MUST be ignored when received + switch p.(type) { + case *Marker: + continue + } + return p, nil + } + } + return nil, io.EOF +} + +// Next returns the most recently unread Packet, or reads another packet from +// the top-most io.Reader. Unknown/Marker packet types are skipped while unsupported +// packets are returned as UnsupportedPacket type. +func (r *Reader) NextWithUnsupported() (p Packet, err error) { + for { + p, err = r.read() + if err == io.EOF { + break + } else if err != nil { + if _, ok := err.(errors.UnknownPacketTypeError); ok { + continue + } + if casteErr, ok := err.(errors.UnsupportedError); ok { + return &UnsupportedPacket{ + IncompletePacket: p, + Error: casteErr, + }, nil + } + return + } else { + //A marker packet MUST be ignored when received + switch p.(type) { + case *Marker: + continue + } + return + } + } + return nil, io.EOF +} + +func (r *Reader) read() (p Packet, err error) { if len(r.q) > 0 { p = r.q[len(r.q)-1] r.q = r.q[:len(r.q)-1] return } - for len(r.readers) > 0 { p, err = Read(r.readers[len(r.readers)-1]) - if err == nil { - return - } if err == io.EOF { r.readers = r.readers[:len(r.readers)-1] continue } - // TODO: Add strict mode that rejects unknown packets, instead of ignoring them. - if _, ok := err.(errors.UnknownPacketTypeError); ok { - continue - } - if _, ok := err.(errors.UnsupportedError); ok { - switch p.(type) { - case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData: - return nil, err - } - continue - } - return nil, err + return p, err } - return nil, io.EOF } @@ -84,3 +134,76 @@ func NewReader(r io.Reader) *Reader { readers: []io.Reader{r}, } } + +// CheckReader is similar to Reader but additionally +// uses the pushdown automata to verify the read packet sequence. +type CheckReader struct { + Reader + verifier *SequenceVerifier + fullyRead bool +} + +// Next returns the most recently unread Packet, or reads another packet from +// the top-most io.Reader. Unknown packet types are skipped. +// If the read packet sequence does not conform to the packet composition +// rules in rfc4880, it returns an error. +func (r *CheckReader) Next() (p Packet, err error) { + if r.fullyRead { + return nil, io.EOF + } + if len(r.q) > 0 { + p = r.q[len(r.q)-1] + r.q = r.q[:len(r.q)-1] + return + } + var errMsg error + for len(r.readers) > 0 { + p, errMsg, err = ReadWithCheck(r.readers[len(r.readers)-1], r.verifier) + if errMsg != nil { + err = errMsg + return + } + if err == nil { + return + } + if err == io.EOF { + r.readers = r.readers[:len(r.readers)-1] + continue + } + //A marker packet MUST be ignored when received + switch p.(type) { + case *Marker: + continue + } + if _, ok := err.(errors.UnknownPacketTypeError); ok { + continue + } + if _, ok := err.(errors.UnsupportedError); ok { + switch p.(type) { + case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData: + return nil, err + } + continue + } + return nil, err + } + if errMsg = r.verifier.Next(EOSSymbol); errMsg != nil { + return nil, errMsg + } + if errMsg = r.verifier.AssertValid(); errMsg != nil { + return nil, errMsg + } + r.fullyRead = true + return nil, io.EOF +} + +func NewCheckReader(r io.Reader) *CheckReader { + return &CheckReader{ + Reader: Reader{ + q: nil, + readers: []io.Reader{r}, + }, + verifier: NewSequenceVerifier(), + fullyRead: false, + } +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go new file mode 100644 index 0000000000..fb2e362e4a --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go @@ -0,0 +1,15 @@ +package packet + +// Recipient type represents a Intended Recipient Fingerprint subpacket +// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr +type Recipient struct { + KeyVersion int + Fingerprint []byte +} + +func (r *Recipient) Serialize() []byte { + packet := make([]byte, len(r.Fingerprint)+1) + packet[0] = byte(r.KeyVersion) + copy(packet[1:], r.Fingerprint) + return packet +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go index 80d0bb98e0..3a4b366d87 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go @@ -8,13 +8,17 @@ import ( "bytes" "crypto" "crypto/dsa" + "encoding/asn1" "encoding/binary" "hash" "io" + "math/big" "strconv" "time" "github.com/ProtonMail/go-crypto/openpgp/ecdsa" + "github.com/ProtonMail/go-crypto/openpgp/ed25519" + "github.com/ProtonMail/go-crypto/openpgp/ed448" "github.com/ProtonMail/go-crypto/openpgp/eddsa" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" @@ -22,7 +26,8 @@ import ( ) const ( - // See RFC 4880, section 5.2.3.21 for details. + // First octet of key flags. + // See RFC 9580, section 5.2.3.29 for details. KeyFlagCertify = 1 << iota KeyFlagSign KeyFlagEncryptCommunications @@ -33,12 +38,30 @@ const ( KeyFlagGroupKey ) -// Signature represents a signature. See RFC 4880, section 5.2. +const ( + // First octet of keyserver preference flags. + // See RFC 9580, section 5.2.3.25 for details. + _ = 1 << iota + _ + _ + _ + _ + _ + _ + KeyserverPrefNoModify +) + +const SaltNotationName = "salt@notations.openpgpjs.org" + +// Signature represents a signature. See RFC 9580, section 5.2. type Signature struct { Version int SigType SignatureType PubKeyAlgo PublicKeyAlgorithm Hash crypto.Hash + // salt contains a random salt value for v6 signatures + // See RFC 9580 Section 5.2.4. + salt []byte // HashSuffix is extra data that is hashed in after the signed data. HashSuffix []byte @@ -57,6 +80,7 @@ type Signature struct { DSASigR, DSASigS encoding.Field ECDSASigR, ECDSASigS encoding.Field EdDSASigR, EdDSASigS encoding.Field + EdSig []byte // rawSubpackets contains the unparsed subpackets, in order. rawSubpackets []outputSubpacket @@ -72,31 +96,42 @@ type Signature struct { SignerUserId *string IsPrimaryId *bool Notations []*Notation + IntendedRecipients []*Recipient // TrustLevel and TrustAmount can be set by the signer to assert that // the key is not only valid but also trustworthy at the specified // level. - // See RFC 4880, section 5.2.3.13 for details. + // See RFC 9580, section 5.2.3.21 for details. TrustLevel TrustLevel TrustAmount TrustAmount // TrustRegularExpression can be used in conjunction with trust Signature // packets to limit the scope of the trust that is extended. - // See RFC 4880, section 5.2.3.14 for details. + // See RFC 9580, section 5.2.3.22 for details. TrustRegularExpression *string + // KeyserverPrefsValid is set if any keyserver preferences were given. See RFC 9580, section + // 5.2.3.25 for details. + KeyserverPrefsValid bool + KeyserverPrefNoModify bool + + // PreferredKeyserver can be set to a URI where the latest version of the + // key that this signature is made over can be found. See RFC 9580, section + // 5.2.3.26 for details. + PreferredKeyserver string + // PolicyURI can be set to the URI of a document that describes the - // policy under which the signature was issued. See RFC 4880, section - // 5.2.3.20 for details. + // policy under which the signature was issued. See RFC 9580, section + // 5.2.3.28 for details. PolicyURI string - // FlagsValid is set if any flags were given. See RFC 4880, section - // 5.2.3.21 for details. + // FlagsValid is set if any flags were given. See RFC 9580, section + // 5.2.3.29 for details. FlagsValid bool FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage, FlagSplitKey, FlagAuthenticate, FlagGroupKey bool // RevocationReason is set if this signature has been revoked. - // See RFC 4880, section 5.2.3.23 for details. + // See RFC 9580, section 5.2.3.31 for details. RevocationReason *ReasonForRevocation RevocationReasonText string @@ -113,26 +148,57 @@ type Signature struct { outSubpackets []outputSubpacket } +// VerifiableSignature internally keeps state if the +// the signature has been verified before. +type VerifiableSignature struct { + Valid *bool // nil if it has not been verified yet + Packet *Signature +} + +// NewVerifiableSig returns a struct of type VerifiableSignature referencing the input signature. +func NewVerifiableSig(signature *Signature) *VerifiableSignature { + return &VerifiableSignature{ + Packet: signature, + } +} + +// Salt returns the signature salt for v6 signatures. +func (sig *Signature) Salt() []byte { + if sig == nil { + return nil + } + return sig.salt +} + func (sig *Signature) parse(r io.Reader) (err error) { - // RFC 4880, section 5.2.3 - var buf [5]byte + // RFC 9580, section 5.2.3 + var buf [7]byte _, err = readFull(r, buf[:1]) if err != nil { return } - if buf[0] != 4 && buf[0] != 5 { + sig.Version = int(buf[0]) + if sig.Version != 4 && sig.Version != 5 && sig.Version != 6 { err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) return } - sig.Version = int(buf[0]) - _, err = readFull(r, buf[:5]) + + if V5Disabled && sig.Version == 5 { + return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed") + } + + if sig.Version == 6 { + _, err = readFull(r, buf[:7]) + } else { + _, err = readFull(r, buf[:5]) + } if err != nil { return } sig.SigType = SignatureType(buf[0]) sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA: + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA, PubKeyAlgoEd25519, PubKeyAlgoEd448: default: err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) return @@ -150,7 +216,17 @@ func (sig *Signature) parse(r io.Reader) (err error) { return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) } - hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) + var hashedSubpacketsLength int + if sig.Version == 6 { + // For a v6 signature, a four-octet length is used. + hashedSubpacketsLength = + int(buf[3])<<24 | + int(buf[4])<<16 | + int(buf[5])<<8 | + int(buf[6]) + } else { + hashedSubpacketsLength = int(buf[3])<<8 | int(buf[4]) + } hashedSubpackets := make([]byte, hashedSubpacketsLength) _, err = readFull(r, hashedSubpackets) if err != nil { @@ -166,11 +242,21 @@ func (sig *Signature) parse(r io.Reader) (err error) { return } - _, err = readFull(r, buf[:2]) + if sig.Version == 6 { + _, err = readFull(r, buf[:4]) + } else { + _, err = readFull(r, buf[:2]) + } + if err != nil { return } - unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) + var unhashedSubpacketsLength uint32 + if sig.Version == 6 { + unhashedSubpacketsLength = uint32(buf[0])<<24 | uint32(buf[1])<<16 | uint32(buf[2])<<8 | uint32(buf[3]) + } else { + unhashedSubpacketsLength = uint32(buf[0])<<8 | uint32(buf[1]) + } unhashedSubpackets := make([]byte, unhashedSubpacketsLength) _, err = readFull(r, unhashedSubpackets) if err != nil { @@ -186,6 +272,30 @@ func (sig *Signature) parse(r io.Reader) (err error) { return } + if sig.Version == 6 { + // Only for v6 signatures, a variable-length field containing the salt + _, err = readFull(r, buf[:1]) + if err != nil { + return + } + saltLength := int(buf[0]) + var expectedSaltLength int + expectedSaltLength, err = SaltLengthForHash(sig.Hash) + if err != nil { + return + } + if saltLength != expectedSaltLength { + err = errors.StructuralError("unexpected salt size for the given hash algorithm") + return + } + salt := make([]byte, expectedSaltLength) + _, err = readFull(r, salt) + if err != nil { + return + } + sig.salt = salt + } + switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sig.RSASignature = new(encoding.MPI) @@ -216,6 +326,16 @@ func (sig *Signature) parse(r io.Reader) (err error) { if _, err = sig.EdDSASigS.ReadFrom(r); err != nil { return } + case PubKeyAlgoEd25519: + sig.EdSig, err = ed25519.ReadSignature(r) + if err != nil { + return + } + case PubKeyAlgoEd448: + sig.EdSig, err = ed448.ReadSignature(r) + if err != nil { + return + } default: panic("unreachable") } @@ -223,7 +343,7 @@ func (sig *Signature) parse(r io.Reader) (err error) { } // parseSignatureSubpackets parses subpackets of the main signature packet. See -// RFC 4880, section 5.2.3.1. +// RFC 9580, section 5.2.3.1. func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { for len(subpackets) > 0 { subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) @@ -244,6 +364,7 @@ type signatureSubpacketType uint8 const ( creationTimeSubpacket signatureSubpacketType = 2 signatureExpirationSubpacket signatureSubpacketType = 3 + exportableCertSubpacket signatureSubpacketType = 4 trustSubpacket signatureSubpacketType = 5 regularExpressionSubpacket signatureSubpacketType = 6 keyExpirationSubpacket signatureSubpacketType = 9 @@ -252,6 +373,8 @@ const ( notationDataSubpacket signatureSubpacketType = 20 prefHashAlgosSubpacket signatureSubpacketType = 21 prefCompressionSubpacket signatureSubpacketType = 22 + keyserverPrefsSubpacket signatureSubpacketType = 23 + prefKeyserverSubpacket signatureSubpacketType = 24 primaryUserIdSubpacket signatureSubpacketType = 25 policyUriSubpacket signatureSubpacketType = 26 keyFlagsSubpacket signatureSubpacketType = 27 @@ -260,12 +383,13 @@ const ( featuresSubpacket signatureSubpacketType = 30 embeddedSignatureSubpacket signatureSubpacketType = 32 issuerFingerprintSubpacket signatureSubpacketType = 33 + intendedRecipientSubpacket signatureSubpacketType = 35 prefCipherSuitesSubpacket signatureSubpacketType = 39 ) // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { - // RFC 4880, section 5.2.3.1 + // RFC 9580, section 5.2.3.7 var ( length uint32 packetType signatureSubpacketType @@ -323,19 +447,24 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r t := binary.BigEndian.Uint32(subpacket) sig.CreationTime = time.Unix(int64(t), 0) case signatureExpirationSubpacket: - // Signature expiration time, section 5.2.3.10 + // Signature expiration time, section 5.2.3.18 if len(subpacket) != 4 { err = errors.StructuralError("expiration subpacket with bad length") return } sig.SigLifetimeSecs = new(uint32) *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) + case exportableCertSubpacket: + if subpacket[0] == 0 { + err = errors.UnsupportedError("signature with non-exportable certification") + return + } case trustSubpacket: if len(subpacket) != 2 { err = errors.StructuralError("trust subpacket with bad length") return } - // Trust level and amount, section 5.2.3.13 + // Trust level and amount, section 5.2.3.21 sig.TrustLevel = TrustLevel(subpacket[0]) sig.TrustAmount = TrustAmount(subpacket[1]) case regularExpressionSubpacket: @@ -343,7 +472,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r err = errors.StructuralError("regexp subpacket with bad length") return } - // Trust regular expression, section 5.2.3.14 + // Trust regular expression, section 5.2.3.22 // RFC specifies the string should be null-terminated; remove a null byte from the end if subpacket[len(subpacket)-1] != 0x00 { err = errors.StructuralError("expected regular expression to be null-terminated") @@ -352,7 +481,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r trustRegularExpression := string(subpacket[:len(subpacket)-1]) sig.TrustRegularExpression = &trustRegularExpression case keyExpirationSubpacket: - // Key expiration time, section 5.2.3.6 + // Key expiration time, section 5.2.3.13 if len(subpacket) != 4 { err = errors.StructuralError("key expiration subpacket with bad length") return @@ -360,23 +489,25 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r sig.KeyLifetimeSecs = new(uint32) *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) case prefSymmetricAlgosSubpacket: - // Preferred symmetric algorithms, section 5.2.3.7 + // Preferred symmetric algorithms, section 5.2.3.14 sig.PreferredSymmetric = make([]byte, len(subpacket)) copy(sig.PreferredSymmetric, subpacket) case issuerSubpacket: - // Issuer, section 5.2.3.5 - if sig.Version > 4 { - err = errors.StructuralError("issuer subpacket found in v5 key") + // Issuer, section 5.2.3.12 + if sig.Version > 4 && isHashed { + err = errors.StructuralError("issuer subpacket found in v6 key") return } if len(subpacket) != 8 { err = errors.StructuralError("issuer subpacket with bad length") return } - sig.IssuerKeyId = new(uint64) - *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) + if sig.Version <= 4 { + sig.IssuerKeyId = new(uint64) + *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) + } case notationDataSubpacket: - // Notation data, section 5.2.3.16 + // Notation data, section 5.2.3.24 if len(subpacket) < 8 { err = errors.StructuralError("notation data subpacket with bad length") return @@ -398,15 +529,27 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r sig.Notations = append(sig.Notations, ¬ation) case prefHashAlgosSubpacket: - // Preferred hash algorithms, section 5.2.3.8 + // Preferred hash algorithms, section 5.2.3.16 sig.PreferredHash = make([]byte, len(subpacket)) copy(sig.PreferredHash, subpacket) case prefCompressionSubpacket: - // Preferred compression algorithms, section 5.2.3.9 + // Preferred compression algorithms, section 5.2.3.17 sig.PreferredCompression = make([]byte, len(subpacket)) copy(sig.PreferredCompression, subpacket) + case keyserverPrefsSubpacket: + // Keyserver preferences, section 5.2.3.25 + sig.KeyserverPrefsValid = true + if len(subpacket) == 0 { + return + } + if subpacket[0]&KeyserverPrefNoModify != 0 { + sig.KeyserverPrefNoModify = true + } + case prefKeyserverSubpacket: + // Preferred keyserver, section 5.2.3.26 + sig.PreferredKeyserver = string(subpacket) case primaryUserIdSubpacket: - // Primary User ID, section 5.2.3.19 + // Primary User ID, section 5.2.3.27 if len(subpacket) != 1 { err = errors.StructuralError("primary user id subpacket with bad length") return @@ -416,12 +559,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r *sig.IsPrimaryId = true } case keyFlagsSubpacket: - // Key flags, section 5.2.3.21 + // Key flags, section 5.2.3.29 + sig.FlagsValid = true if len(subpacket) == 0 { - err = errors.StructuralError("empty key flags subpacket") return } - sig.FlagsValid = true if subpacket[0]&KeyFlagCertify != 0 { sig.FlagCertify = true } @@ -447,16 +589,16 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r userId := string(subpacket) sig.SignerUserId = &userId case reasonForRevocationSubpacket: - // Reason For Revocation, section 5.2.3.23 + // Reason For Revocation, section 5.2.3.31 if len(subpacket) == 0 { err = errors.StructuralError("empty revocation reason subpacket") return } sig.RevocationReason = new(ReasonForRevocation) - *sig.RevocationReason = ReasonForRevocation(subpacket[0]) + *sig.RevocationReason = NewReasonForRevocation(subpacket[0]) sig.RevocationReasonText = string(subpacket[1:]) case featuresSubpacket: - // Features subpacket, section 5.2.3.24 specifies a very general + // Features subpacket, section 5.2.3.32 specifies a very general // mechanism for OpenPGP implementations to signal support for new // features. if len(subpacket) > 0 { @@ -470,16 +612,13 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r } case embeddedSignatureSubpacket: // Only usage is in signatures that cross-certify - // signing subkeys. section 5.2.3.26 describes the + // signing subkeys. section 5.2.3.34 describes the // format, with its usage described in section 11.1 if sig.EmbeddedSignature != nil { err = errors.StructuralError("Cannot have multiple embedded signatures") return } sig.EmbeddedSignature = new(Signature) - // Embedded signatures are required to be v4 signatures see - // section 12.1. However, we only parse v4 signatures in this - // file anyway. if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { return nil, err } @@ -487,7 +626,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) } case policyUriSubpacket: - // Policy URI, section 5.2.3.20 + // Policy URI, section 5.2.3.28 sig.PolicyURI = string(subpacket) case issuerFingerprintSubpacket: if len(subpacket) == 0 { @@ -495,20 +634,31 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r return } v, l := subpacket[0], len(subpacket[1:]) - if v == 5 && l != 32 || v != 5 && l != 20 { + if v >= 5 && l != 32 || v < 5 && l != 20 { return nil, errors.StructuralError("bad fingerprint length") } sig.IssuerFingerprint = make([]byte, l) copy(sig.IssuerFingerprint, subpacket[1:]) sig.IssuerKeyId = new(uint64) - if v == 5 { + if v >= 5 { *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[1:9]) } else { *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21]) } + case intendedRecipientSubpacket: + // Intended Recipient Fingerprint, section 5.2.3.36 + if len(subpacket) < 1 { + return nil, errors.StructuralError("invalid intended recipient fingerpring length") + } + version, length := subpacket[0], len(subpacket[1:]) + if version >= 5 && length != 32 || version < 5 && length != 20 { + return nil, errors.StructuralError("invalid fingerprint length") + } + fingerprint := make([]byte, length) + copy(fingerprint, subpacket[1:]) + sig.IntendedRecipients = append(sig.IntendedRecipients, &Recipient{int(version), fingerprint}) case prefCipherSuitesSubpacket: - // Preferred AEAD cipher suites - // See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#name-preferred-aead-ciphersuites + // Preferred AEAD cipher suites, section 5.2.3.15 if len(subpacket)%2 != 0 { err = errors.StructuralError("invalid aead cipher suite length") return @@ -550,9 +700,16 @@ func (sig *Signature) CheckKeyIdOrFingerprint(pk *PublicKey) bool { return sig.IssuerKeyId != nil && *sig.IssuerKeyId == pk.KeyId } +func (sig *Signature) CheckKeyIdOrFingerprintExplicit(fingerprint []byte, keyId uint64) bool { + if sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) >= 20 && fingerprint != nil { + return bytes.Equal(sig.IssuerFingerprint, fingerprint) + } + return sig.IssuerKeyId != nil && *sig.IssuerKeyId == keyId +} + // serializeSubpacketLength marshals the given length into to. func serializeSubpacketLength(to []byte, length int) int { - // RFC 4880, Section 4.2.2. + // RFC 9580, Section 4.2.1. if length < 192 { to[0] = byte(length) return 1 @@ -598,20 +755,19 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { to = to[n:] } } - return } // SigExpired returns whether sig is a signature that has expired or is created // in the future. func (sig *Signature) SigExpired(currentTime time.Time) bool { - if sig.CreationTime.After(currentTime) { + if sig.CreationTime.Unix() > currentTime.Unix() { return true } if sig.SigLifetimeSecs == nil || *sig.SigLifetimeSecs == 0 { return false } expiry := sig.CreationTime.Add(time.Duration(*sig.SigLifetimeSecs) * time.Second) - return currentTime.After(expiry) + return currentTime.Unix() > expiry.Unix() } // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. @@ -635,20 +791,36 @@ func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) { uint8(sig.SigType), uint8(sig.PubKeyAlgo), uint8(hashId), - uint8(len(hashedSubpackets) >> 8), - uint8(len(hashedSubpackets)), }) + hashedSubpacketsLength := len(hashedSubpackets) + if sig.Version == 6 { + // v6 signatures store the length in 4 octets + hashedFields.Write([]byte{ + uint8(hashedSubpacketsLength >> 24), + uint8(hashedSubpacketsLength >> 16), + uint8(hashedSubpacketsLength >> 8), + uint8(hashedSubpacketsLength), + }) + } else { + hashedFields.Write([]byte{ + uint8(hashedSubpacketsLength >> 8), + uint8(hashedSubpacketsLength), + }) + } + lenPrefix := hashedFields.Len() hashedFields.Write(hashedSubpackets) - var l uint64 = uint64(6 + len(hashedSubpackets)) + var l uint64 = uint64(lenPrefix + len(hashedSubpackets)) if sig.Version == 5 { + // v5 case hashedFields.Write([]byte{0x05, 0xff}) hashedFields.Write([]byte{ uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32), uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), }) } else { - hashedFields.Write([]byte{0x04, 0xff}) + // v4 and v6 case + hashedFields.Write([]byte{byte(sig.Version), 0xff}) hashedFields.Write([]byte{ uint8(l >> 24), uint8(l >> 16), uint8(l >> 8), uint8(l), }) @@ -676,6 +848,67 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { return } +// PrepareSign must be called to create a hash object before Sign for v6 signatures. +// The created hash object initially hashes a randomly generated salt +// as required by v6 signatures. The generated salt is stored in sig. If the signature is not v6, +// the method returns an empty hash object. +// See RFC 9580 Section 5.2.4. +func (sig *Signature) PrepareSign(config *Config) (hash.Hash, error) { + if !sig.Hash.Available() { + return nil, errors.UnsupportedError("hash function") + } + hasher := sig.Hash.New() + if sig.Version == 6 { + if sig.salt == nil { + var err error + sig.salt, err = SignatureSaltForHash(sig.Hash, config.Random()) + if err != nil { + return nil, err + } + } + hasher.Write(sig.salt) + } + return hasher, nil +} + +// SetSalt sets the signature salt for v6 signatures. +// Assumes salt is generated correctly and checks if length matches. +// If the signature is not v6, the method ignores the salt. +// Use PrepareSign whenever possible instead of generating and +// hashing the salt externally. +// See RFC 9580 Section 5.2.4. +func (sig *Signature) SetSalt(salt []byte) error { + if sig.Version == 6 { + expectedSaltLength, err := SaltLengthForHash(sig.Hash) + if err != nil { + return err + } + if salt == nil || len(salt) != expectedSaltLength { + return errors.InvalidArgumentError("unexpected salt size for the given hash algorithm") + } + sig.salt = salt + } + return nil +} + +// PrepareVerify must be called to create a hash object before verifying v6 signatures. +// The created hash object initially hashes the internally stored salt. +// If the signature is not v6, the method returns an empty hash object. +// See RFC 9580 Section 5.2.4. +func (sig *Signature) PrepareVerify() (hash.Hash, error) { + if !sig.Hash.Available() { + return nil, errors.UnsupportedError("hash function") + } + hasher := sig.Hash.New() + if sig.Version == 6 { + if sig.salt == nil { + return nil, errors.StructuralError("v6 requires a salt for the hash to be signed") + } + hasher.Write(sig.salt) + } + return hasher, nil +} + // Sign signs a message with a private key. The hash, h, must contain // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. @@ -686,6 +919,20 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e } sig.Version = priv.PublicKey.Version sig.IssuerFingerprint = priv.PublicKey.Fingerprint + if sig.Version < 6 && config.RandomizeSignaturesViaNotation() { + sig.removeNotationsWithName(SaltNotationName) + salt, err := SignatureSaltForHash(sig.Hash, config.Random()) + if err != nil { + return err + } + notation := Notation{ + Name: SaltNotationName, + Value: salt, + IsCritical: false, + IsHumanReadable: false, + } + sig.Notations = append(sig.Notations, ¬ation) + } sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey) if err != nil { return err @@ -715,8 +962,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e sig.DSASigS = new(encoding.MPI).SetBig(s) } case PubKeyAlgoECDSA: - sk := priv.PrivateKey.(*ecdsa.PrivateKey) - r, s, err := ecdsa.Sign(config.Random(), sk, digest) + var r, s *big.Int + if sk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok { + r, s, err = ecdsa.Sign(config.Random(), sk, digest) + } else { + var b []byte + b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) + if err == nil { + r, s, err = unwrapECDSASig(b) + } + } if err == nil { sig.ECDSASigR = new(encoding.MPI).SetBig(r) @@ -729,6 +984,18 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e sig.EdDSASigR = encoding.NewMPI(r) sig.EdDSASigS = encoding.NewMPI(s) } + case PubKeyAlgoEd25519: + sk := priv.PrivateKey.(*ed25519.PrivateKey) + signature, err := ed25519.Sign(sk, digest) + if err == nil { + sig.EdSig = signature + } + case PubKeyAlgoEd448: + sk := priv.PrivateKey.(*ed448.PrivateKey) + signature, err := ed448.Sign(sk, digest) + if err == nil { + sig.EdSig = signature + } default: err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) } @@ -736,6 +1003,18 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e return } +// unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA signature. +func unwrapECDSASig(b []byte) (r, s *big.Int, err error) { + var ecsdaSig struct { + R, S *big.Int + } + _, err = asn1.Unmarshal(b, &ecsdaSig) + if err != nil { + return + } + return ecsdaSig.R, ecsdaSig.S, nil +} + // SignUserId computes a signature from priv, asserting that pub is a valid // key for the identity id. On success, the signature is stored in sig. Call // Serialize to write it out. @@ -744,11 +1023,32 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co if priv.Dummy() { return errors.ErrDummyPrivateKey("dummy key found") } - h, err := userIdSignatureHash(id, pub, sig.Hash) + prepareHash, err := sig.PrepareSign(config) if err != nil { return err } - return sig.Sign(h, priv, config) + if err := userIdSignatureHash(id, pub, prepareHash); err != nil { + return err + } + return sig.Sign(prepareHash, priv, config) +} + +// SignDirectKeyBinding computes a signature from priv +// On success, the signature is stored in sig. +// Call Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignDirectKeyBinding(pub *PublicKey, priv *PrivateKey, config *Config) error { + if priv.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + prepareHash, err := sig.PrepareSign(config) + if err != nil { + return err + } + if err := directKeySignatureHash(pub, prepareHash); err != nil { + return err + } + return sig.Sign(prepareHash, priv, config) } // CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success, @@ -756,7 +1056,11 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co // If config is nil, sensible defaults will be used. func (sig *Signature) CrossSignKey(pub *PublicKey, hashKey *PublicKey, signingKey *PrivateKey, config *Config) error { - h, err := keySignatureHash(hashKey, pub, sig.Hash) + prepareHash, err := sig.PrepareSign(config) + if err != nil { + return err + } + h, err := keySignatureHash(hashKey, pub, prepareHash) if err != nil { return err } @@ -770,7 +1074,11 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) if priv.Dummy() { return errors.ErrDummyPrivateKey("dummy key found") } - h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) + prepareHash, err := sig.PrepareSign(config) + if err != nil { + return err + } + h, err := keySignatureHash(&priv.PublicKey, pub, prepareHash) if err != nil { return err } @@ -781,11 +1089,14 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) // stored in sig. Call Serialize to write it out. // If config is nil, sensible defaults will be used. func (sig *Signature) RevokeKey(pub *PublicKey, priv *PrivateKey, config *Config) error { - h, err := keyRevocationHash(pub, sig.Hash) + prepareHash, err := sig.PrepareSign(config) if err != nil { return err } - return sig.Sign(h, priv, config) + if err := keyRevocationHash(pub, prepareHash); err != nil { + return err + } + return sig.Sign(prepareHash, priv, config) } // RevokeSubkey computes a subkey revocation signature of pub using priv. @@ -802,7 +1113,7 @@ func (sig *Signature) Serialize(w io.Writer) (err error) { if len(sig.outSubpackets) == 0 { sig.outSubpackets = sig.rawSubpackets } - if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil { + if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil && sig.EdDSASigR == nil && sig.EdSig == nil { return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") } @@ -819,16 +1130,24 @@ func (sig *Signature) Serialize(w io.Writer) (err error) { case PubKeyAlgoEdDSA: sigLength = int(sig.EdDSASigR.EncodedLength()) sigLength += int(sig.EdDSASigS.EncodedLength()) + case PubKeyAlgoEd25519: + sigLength = ed25519.SignatureSize + case PubKeyAlgoEd448: + sigLength = ed448.SignatureSize default: panic("impossible") } + hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) - length := len(sig.HashSuffix) - 6 /* trailer not included */ + + length := 4 + /* length of version|signature type|public-key algorithm|hash algorithm */ + 2 /* length of hashed subpackets */ + hashedSubpacketsLen + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + 2 /* hash tag */ + sigLength - if sig.Version == 5 { - length -= 4 // eight-octet instead of four-octet big endian + if sig.Version == 6 { + length += 4 + /* the two length fields are four-octet instead of two */ + 1 + /* salt length */ + len(sig.salt) /* length salt */ } err = serializeHeader(w, packetTypeSignature, length) if err != nil { @@ -842,18 +1161,41 @@ func (sig *Signature) Serialize(w io.Writer) (err error) { } func (sig *Signature) serializeBody(w io.Writer) (err error) { - hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) | uint16(sig.HashSuffix[5]) - fields := sig.HashSuffix[:6+hashedSubpacketsLen] + var fields []byte + if sig.Version == 6 { + // v6 signatures use 4 octets for length + hashedSubpacketsLen := + uint32(uint32(sig.HashSuffix[4])<<24) | + uint32(uint32(sig.HashSuffix[5])<<16) | + uint32(uint32(sig.HashSuffix[6])<<8) | + uint32(sig.HashSuffix[7]) + fields = sig.HashSuffix[:8+hashedSubpacketsLen] + } else { + hashedSubpacketsLen := uint16(uint16(sig.HashSuffix[4])<<8) | + uint16(sig.HashSuffix[5]) + fields = sig.HashSuffix[:6+hashedSubpacketsLen] + + } _, err = w.Write(fields) if err != nil { return } unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) - unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) - unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) - unhashedSubpackets[1] = byte(unhashedSubpacketsLen) - serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) + var unhashedSubpackets []byte + if sig.Version == 6 { + unhashedSubpackets = make([]byte, 4+unhashedSubpacketsLen) + unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 24) + unhashedSubpackets[1] = byte(unhashedSubpacketsLen >> 16) + unhashedSubpackets[2] = byte(unhashedSubpacketsLen >> 8) + unhashedSubpackets[3] = byte(unhashedSubpacketsLen) + serializeSubpackets(unhashedSubpackets[4:], sig.outSubpackets, false) + } else { + unhashedSubpackets = make([]byte, 2+unhashedSubpacketsLen) + unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) + unhashedSubpackets[1] = byte(unhashedSubpacketsLen) + serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) + } _, err = w.Write(unhashedSubpackets) if err != nil { @@ -864,6 +1206,18 @@ func (sig *Signature) serializeBody(w io.Writer) (err error) { return } + if sig.Version == 6 { + // write salt for v6 signatures + _, err = w.Write([]byte{uint8(len(sig.salt))}) + if err != nil { + return + } + _, err = w.Write(sig.salt) + if err != nil { + return + } + } + switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: _, err = w.Write(sig.RSASignature.EncodedBytes()) @@ -882,6 +1236,10 @@ func (sig *Signature) serializeBody(w io.Writer) (err error) { return } _, err = w.Write(sig.EdDSASigS.EncodedBytes()) + case PubKeyAlgoEd25519: + err = ed25519.WriteSignature(w, sig.EdSig) + case PubKeyAlgoEd448: + err = ed448.WriteSignature(w, sig.EdSig) default: panic("impossible") } @@ -899,28 +1257,81 @@ type outputSubpacket struct { func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) { creationTime := make([]byte, 4) binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) - subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) - + // Signature Creation Time + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, true, creationTime}) + // Signature Expiration Time + if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { + sigLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + } + // Trust Signature + if sig.TrustLevel != 0 { + subpackets = append(subpackets, outputSubpacket{true, trustSubpacket, true, []byte{byte(sig.TrustLevel), byte(sig.TrustAmount)}}) + } + // Regular Expression + if sig.TrustRegularExpression != nil { + // RFC specifies the string should be null-terminated; add a null byte to the end + subpackets = append(subpackets, outputSubpacket{true, regularExpressionSubpacket, true, []byte(*sig.TrustRegularExpression + "\000")}) + } + // Key Expiration Time + if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { + keyLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) + } + // Preferred Symmetric Ciphers for v1 SEIPD + if len(sig.PreferredSymmetric) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) + } + // Issuer Key ID if sig.IssuerKeyId != nil && sig.Version == 4 { keyId := make([]byte, 8) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) - subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId}) } - if sig.IssuerFingerprint != nil { - contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...) - subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, sig.Version == 5, contents}) + // Notation Data + for _, notation := range sig.Notations { + subpackets = append( + subpackets, + outputSubpacket{ + true, + notationDataSubpacket, + notation.IsCritical, + notation.getData(), + }) } - if sig.SignerUserId != nil { - subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)}) + // Preferred Hash Algorithms + if len(sig.PreferredHash) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) } - if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { - sigLifetime := make([]byte, 4) - binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) - subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + // Preferred Compression Algorithms + if len(sig.PreferredCompression) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) } - + // Keyserver Preferences + // Keyserver preferences may only appear in self-signatures or certification signatures. + if sig.KeyserverPrefsValid { + var prefs byte + if sig.KeyserverPrefNoModify { + prefs |= KeyserverPrefNoModify + } + subpackets = append(subpackets, outputSubpacket{true, keyserverPrefsSubpacket, false, []byte{prefs}}) + } + // Preferred Keyserver + if len(sig.PreferredKeyserver) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefKeyserverSubpacket, false, []uint8(sig.PreferredKeyserver)}) + } + // Primary User ID + if sig.IsPrimaryId != nil && *sig.IsPrimaryId { + subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) + } + // Policy URI + if len(sig.PolicyURI) > 0 { + subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)}) + } + // Key Flags // Key flags may only appear in self-signatures or certification signatures. - if sig.FlagsValid { var flags byte if sig.FlagCertify { @@ -944,22 +1355,19 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp if sig.FlagGroupKey { flags |= KeyFlagGroupKey } - subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) + subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, true, []byte{flags}}) } - - for _, notation := range sig.Notations { - subpackets = append( - subpackets, - outputSubpacket{ - true, - notationDataSubpacket, - notation.IsCritical, - notation.getData(), - }) + // Signer's User ID + if sig.SignerUserId != nil { + subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)}) } - - // The following subpackets may only appear in self-signatures. - + // Reason for Revocation + // Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.31. + if sig.RevocationReason != nil { + subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true, + append([]uint8{uint8(*sig.RevocationReason)}, []uint8(sig.RevocationReasonText)...)}) + } + // Features var features = byte(0x00) if sig.SEIPDv1 { features |= 0x01 @@ -967,46 +1375,36 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp if sig.SEIPDv2 { features |= 0x08 } - if features != 0x00 { subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}}) } - - if sig.TrustLevel != 0 { - subpackets = append(subpackets, outputSubpacket{true, trustSubpacket, true, []byte{byte(sig.TrustLevel), byte(sig.TrustAmount)}}) - } - - if sig.TrustRegularExpression != nil { - // RFC specifies the string should be null-terminated; add a null byte to the end - subpackets = append(subpackets, outputSubpacket{true, regularExpressionSubpacket, true, []byte(*sig.TrustRegularExpression + "\000")}) - } - - if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { - keyLifetime := make([]byte, 4) - binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) - subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) - } - - if sig.IsPrimaryId != nil && *sig.IsPrimaryId { - subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) - } - - if len(sig.PreferredSymmetric) > 0 { - subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) - } - - if len(sig.PreferredHash) > 0 { - subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) + // Embedded Signature + // EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.34. + if sig.EmbeddedSignature != nil { + var buf bytes.Buffer + err = sig.EmbeddedSignature.serializeBody(&buf) + if err != nil { + return + } + subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()}) } - - if len(sig.PreferredCompression) > 0 { - subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) + // Issuer Fingerprint + if sig.IssuerFingerprint != nil { + contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...) + subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, sig.Version >= 5, contents}) } - - if len(sig.PolicyURI) > 0 { - subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)}) + // Intended Recipient Fingerprint + for _, recipient := range sig.IntendedRecipients { + subpackets = append( + subpackets, + outputSubpacket{ + true, + intendedRecipientSubpacket, + false, + recipient.Serialize(), + }) } - + // Preferred AEAD Ciphersuites if len(sig.PreferredCipherSuites) > 0 { serialized := make([]byte, len(sig.PreferredCipherSuites)*2) for i, cipherSuite := range sig.PreferredCipherSuites { @@ -1015,23 +1413,6 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp } subpackets = append(subpackets, outputSubpacket{true, prefCipherSuitesSubpacket, false, serialized}) } - - // Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23. - if sig.RevocationReason != nil { - subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true, - append([]uint8{uint8(*sig.RevocationReason)}, []uint8(sig.RevocationReasonText)...)}) - } - - // EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26. - if sig.EmbeddedSignature != nil { - var buf bytes.Buffer - err = sig.EmbeddedSignature.serializeBody(&buf) - if err != nil { - return - } - subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()}) - } - return } @@ -1073,8 +1454,6 @@ func (sig *Signature) AddMetadataToHashSuffix() { binary.BigEndian.PutUint32(buf[:], lit.Time) suffix.Write(buf[:]) - // Update the counter and restore trailing bytes - l = uint64(suffix.Len()) suffix.Write([]byte{0x05, 0xff}) suffix.Write([]byte{ uint8(l >> 56), uint8(l >> 48), uint8(l >> 40), uint8(l >> 32), @@ -1082,3 +1461,49 @@ func (sig *Signature) AddMetadataToHashSuffix() { }) sig.HashSuffix = suffix.Bytes() } + +// SaltLengthForHash selects the required salt length for the given hash algorithm, +// as per Table 23 (Hash algorithm registry) of the crypto refresh. +// See RFC 9580 Section 9.5. +func SaltLengthForHash(hash crypto.Hash) (int, error) { + switch hash { + case crypto.SHA256, crypto.SHA224, crypto.SHA3_256: + return 16, nil + case crypto.SHA384: + return 24, nil + case crypto.SHA512, crypto.SHA3_512: + return 32, nil + default: + return 0, errors.UnsupportedError("hash function not supported for V6 signatures") + } +} + +// SignatureSaltForHash generates a random signature salt +// with the length for the given hash algorithm. +// See RFC 9580 Section 9.5. +func SignatureSaltForHash(hash crypto.Hash, randReader io.Reader) ([]byte, error) { + saltLength, err := SaltLengthForHash(hash) + if err != nil { + return nil, err + } + salt := make([]byte, saltLength) + _, err = io.ReadFull(randReader, salt) + if err != nil { + return nil, err + } + return salt, nil +} + +// removeNotationsWithName removes all notations in this signature with the given name. +func (sig *Signature) removeNotationsWithName(name string) { + if sig == nil || sig.Notations == nil { + return + } + updatedNotations := make([]*Notation, 0, len(sig.Notations)) + for _, notation := range sig.Notations { + if notation.Name != name { + updatedNotations = append(updatedNotations, notation) + } + } + sig.Notations = updatedNotations +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go index bac2b132ea..2812a1db88 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go @@ -7,11 +7,13 @@ package packet import ( "bytes" "crypto/cipher" + "crypto/sha256" "io" "strconv" "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/ProtonMail/go-crypto/openpgp/s2k" + "golang.org/x/crypto/hkdf" ) // This is the largest session key that we'll support. Since at most 256-bit cipher @@ -39,10 +41,21 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { return err } ske.Version = int(buf[0]) - if ske.Version != 4 && ske.Version != 5 { + if ske.Version != 4 && ske.Version != 5 && ske.Version != 6 { return errors.UnsupportedError("unknown SymmetricKeyEncrypted version") } + if V5Disabled && ske.Version == 5 { + return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed") + } + + if ske.Version > 5 { + // Scalar octet count + if _, err := readFull(r, buf[:]); err != nil { + return err + } + } + // Cipher function if _, err := readFull(r, buf[:]); err != nil { return err @@ -52,7 +65,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[0]))) } - if ske.Version == 5 { + if ske.Version >= 5 { // AEAD mode if _, err := readFull(r, buf[:]); err != nil { return errors.StructuralError("cannot read AEAD octet from packet") @@ -60,6 +73,13 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { ske.Mode = AEADMode(buf[0]) } + if ske.Version > 5 { + // Scalar octet count + if _, err := readFull(r, buf[:]); err != nil { + return err + } + } + var err error if ske.s2k, err = s2k.Parse(r); err != nil { if _, ok := err.(errors.ErrDummyPrivateKey); ok { @@ -68,7 +88,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error { return err } - if ske.Version == 5 { + if ske.Version >= 5 { // AEAD IV iv := make([]byte, ske.Mode.IvLength()) _, err := readFull(r, iv) @@ -109,8 +129,8 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunc case 4: plaintextKey, cipherFunc, err := ske.decryptV4(key) return plaintextKey, cipherFunc, err - case 5: - plaintextKey, err := ske.decryptV5(key) + case 5, 6: + plaintextKey, err := ske.aeadDecrypt(ske.Version, key) return plaintextKey, CipherFunction(0), err } err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version") @@ -136,9 +156,9 @@ func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction, return plaintextKey, cipherFunc, nil } -func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) { - adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)} - aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata) +func (ske *SymmetricKeyEncrypted) aeadDecrypt(version int, key []byte) ([]byte, error) { + adata := []byte{0xc3, byte(version), byte(ske.CipherFunc), byte(ske.Mode)} + aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata, version) plaintextKey, err := aead.Open(nil, ske.iv, ske.encryptedKey, adata) if err != nil { @@ -175,10 +195,22 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Conf // the given passphrase. The returned session key must be passed to // SerializeSymmetricallyEncrypted. // If config is nil, sensible defaults will be used. +// Deprecated: Use SerializeSymmetricKeyEncryptedAEADReuseKey instead. func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) { + return SerializeSymmetricKeyEncryptedAEADReuseKey(w, sessionKey, passphrase, config.AEAD() != nil, config) +} + +// SerializeSymmetricKeyEncryptedAEADReuseKey serializes a symmetric key packet to w. +// The packet contains the given session key, encrypted by a key derived from +// the given passphrase. The returned session key must be passed to +// SerializeSymmetricallyEncrypted. +// If aeadSupported is set, SKESK v6 is used, otherwise v4. +// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted. +// If config is nil, sensible defaults will be used. +func SerializeSymmetricKeyEncryptedAEADReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, aeadSupported bool, config *Config) (err error) { var version int - if config.AEAD() != nil { - version = 5 + if aeadSupported { + version = 6 } else { version = 4 } @@ -203,11 +235,15 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass switch version { case 4: packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize - case 5: + case 5, 6: ivLen := config.AEAD().Mode().IvLength() tagLen := config.AEAD().Mode().TagLength() packetLength = 3 + len(s2kBytes) + ivLen + keySize + tagLen } + if version > 5 { + packetLength += 2 // additional octet count fields + } + err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) if err != nil { return @@ -216,13 +252,22 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass // Symmetric Key Encrypted Version buf := []byte{byte(version)} + if version > 5 { + // Scalar octet count + buf = append(buf, byte(3+len(s2kBytes)+config.AEAD().Mode().IvLength())) + } + // Cipher function buf = append(buf, byte(cipherFunc)) - if version == 5 { + if version >= 5 { // AEAD mode buf = append(buf, byte(config.AEAD().Mode())) } + if version > 5 { + // Scalar octet count + buf = append(buf, byte(len(s2kBytes))) + } _, err = w.Write(buf) if err != nil { return @@ -243,10 +288,10 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass if err != nil { return } - case 5: + case 5, 6: mode := config.AEAD().Mode() - adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)} - aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata) + adata := []byte{0xc3, byte(version), byte(cipherFunc), byte(mode)} + aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata, version) // Sample iv using random reader iv := make([]byte, config.AEAD().Mode().IvLength()) @@ -270,7 +315,17 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass return } -func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte) (aead cipher.AEAD) { - blockCipher := c.new(inputKey) +func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte, version int) (aead cipher.AEAD) { + var blockCipher cipher.Block + if version > 5 { + hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData) + + encryptionKey := make([]byte, c.KeySize()) + _, _ = readFull(hkdfReader, encryptionKey) + + blockCipher = c.new(encryptionKey) + } else { + blockCipher = c.new(inputKey) + } return mode.new(blockCipher) } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go index e9bbf0327e..0e898742cf 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go @@ -74,6 +74,10 @@ func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.Read // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet // to w and returns a WriteCloser to which the to-be-encrypted packets can be // written. +// If aeadSupported is set to true, SEIPDv2 is used with the indicated CipherSuite. +// Otherwise, SEIPDv1 is used with the indicated CipherFunction. +// Note: aeadSupported MUST match the value passed to SerializeEncryptedKeyAEAD +// and/or SerializeSymmetricKeyEncryptedAEADReuseKey. // If config is nil, sensible defaults will be used. func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, aeadSupported bool, cipherSuite CipherSuite, key []byte, config *Config) (Contents io.WriteCloser, err error) { writeCloser := noOpCloser{w} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go index e96252c196..3957b2d53e 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go @@ -7,7 +7,9 @@ package packet import ( "crypto/cipher" "crypto/sha256" + "fmt" "io" + "strconv" "github.com/ProtonMail/go-crypto/openpgp/errors" "golang.org/x/crypto/hkdf" @@ -25,19 +27,19 @@ func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error { se.Cipher = CipherFunction(headerData[0]) // cipherFunc must have block size 16 to use AEAD if se.Cipher.blockSize() != 16 { - return errors.UnsupportedError("invalid aead cipher: " + string(se.Cipher)) + return errors.UnsupportedError("invalid aead cipher: " + strconv.Itoa(int(se.Cipher))) } // Mode se.Mode = AEADMode(headerData[1]) if se.Mode.TagLength() == 0 { - return errors.UnsupportedError("unknown aead mode: " + string(se.Mode)) + return errors.UnsupportedError("unknown aead mode: " + strconv.Itoa(int(se.Mode))) } // Chunk size se.ChunkSizeByte = headerData[2] if se.ChunkSizeByte > 16 { - return errors.UnsupportedError("invalid aead chunk size byte: " + string(se.ChunkSizeByte)) + return errors.UnsupportedError("invalid aead chunk size byte: " + strconv.Itoa(int(se.ChunkSizeByte))) } // Salt @@ -62,8 +64,11 @@ func (se *SymmetricallyEncrypted) associatedData() []byte { // decryptAead decrypts a V2 SEIPD packet (AEAD) as specified in // https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2 func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, error) { - aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData()) + if se.Cipher.KeySize() != len(inputKey) { + return nil, errors.StructuralError(fmt.Sprintf("invalid session key length for cipher: got %d bytes, but expected %d bytes", len(inputKey), se.Cipher.KeySize())) + } + aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData()) // Carry the first tagLen bytes tagLen := se.Mode.TagLength() peekedBytes := make([]byte, tagLen) @@ -115,7 +120,7 @@ func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite // Random salt salt := make([]byte, aeadSaltSize) - if _, err := rand.Read(salt); err != nil { + if _, err := io.ReadFull(rand, salt); err != nil { return nil, err } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go index fa26bebe38..8b18623684 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go @@ -148,7 +148,7 @@ const mdcPacketTagByte = byte(0x80) | 0x40 | 19 func (ser *seMDCReader) Close() error { if ser.error { - return errors.ErrMDCMissing + return errors.ErrMDCHashMismatch } for !ser.eof { @@ -159,7 +159,7 @@ func (ser *seMDCReader) Close() error { break } if err != nil { - return errors.ErrMDCMissing + return errors.ErrMDCHashMismatch } } @@ -172,7 +172,7 @@ func (ser *seMDCReader) Close() error { // The hash already includes the MDC header, but we still check its value // to confirm encryption correctness if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { - return errors.ErrMDCMissing + return errors.ErrMDCHashMismatch } return nil } @@ -237,9 +237,9 @@ func serializeSymmetricallyEncryptedMdc(ciphertext io.WriteCloser, c CipherFunct block := c.new(key) blockSize := block.BlockSize() iv := make([]byte, blockSize) - _, err = config.Random().Read(iv) + _, err = io.ReadFull(config.Random(), iv) if err != nil { - return + return nil, err } s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync) _, err = ciphertext.Write(prefix) diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go index 88ec72c6c4..63814ed132 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go @@ -9,7 +9,6 @@ import ( "image" "image/jpeg" "io" - "io/ioutil" ) const UserAttrImageSubpacket = 1 @@ -63,7 +62,7 @@ func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { func (uat *UserAttribute) parse(r io.Reader) (err error) { // RFC 4880, section 5.13 - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { return } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go index 614fbafd5e..3c7451a3c3 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go @@ -6,7 +6,6 @@ package packet import ( "io" - "io/ioutil" "strings" ) @@ -66,7 +65,7 @@ func NewUserId(name, comment, email string) *UserId { func (uid *UserId) parse(r io.Reader) (err error) { // RFC 4880, section 5.11 - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { return } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go index 8499c73790..e6dd9b5fd3 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/read.go @@ -46,6 +46,7 @@ type MessageDetails struct { DecryptedWith Key // the private key used to decrypt the message, if any. IsSigned bool // true if the message is signed. SignedByKeyId uint64 // the key id of the signer, if any. + SignedByFingerprint []byte // the key fingerprint of the signer, if any. SignedBy *Key // the key of the signer, if available. LiteralData *packet.LiteralData // the metadata of the contents UnverifiedBody io.Reader // the contents of the message. @@ -117,7 +118,7 @@ ParsePackets: // This packet contains the decryption key encrypted to a public key. md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) switch p.Algo { - case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH: + case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH, packet.PubKeyAlgoX25519, packet.PubKeyAlgoX448: break default: continue @@ -232,7 +233,7 @@ FindKey: } mdFinal, sensitiveParsingErr := readSignedMessage(packets, md, keyring, config) if sensitiveParsingErr != nil { - return nil, errors.StructuralError("parsing error") + return nil, errors.HandleSensitiveParsingError(sensitiveParsingErr, md.decrypted != nil) } return mdFinal, nil } @@ -270,13 +271,17 @@ FindLiteralData: prevLast = true } - h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) + h, wrappedHash, err = hashForSignature(p.Hash, p.SigType, p.Salt) if err != nil { md.SignatureError = err } md.IsSigned = true + if p.Version == 6 { + md.SignedByFingerprint = p.KeyFingerprint + } md.SignedByKeyId = p.KeyId + if keyring != nil { keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign) if len(keys) > 0 { @@ -292,7 +297,7 @@ FindLiteralData: if md.IsSigned && md.SignatureError == nil { md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md, config} } else if md.decrypted != nil { - md.UnverifiedBody = checkReader{md} + md.UnverifiedBody = &checkReader{md, false} } else { md.UnverifiedBody = md.LiteralData.Body } @@ -300,12 +305,22 @@ FindLiteralData: return md, nil } +func wrapHashForSignature(hashFunc hash.Hash, sigType packet.SignatureType) (hash.Hash, error) { + switch sigType { + case packet.SigTypeBinary: + return hashFunc, nil + case packet.SigTypeText: + return NewCanonicalTextHash(hashFunc), nil + } + return nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) +} + // hashForSignature returns a pair of hashes that can be used to verify a // signature. The signature may specify that the contents of the signed message // should be preprocessed (i.e. to normalize line endings). Thus this function // returns two hashes. The second should be used to hash the message itself and // performs any needed preprocessing. -func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { +func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType, sigSalt []byte) (hash.Hash, hash.Hash, error) { if _, ok := algorithm.HashToHashIdWithSha1(hashFunc); !ok { return nil, nil, errors.UnsupportedError("unsupported hash function") } @@ -313,14 +328,19 @@ func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType) (hash. return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashFunc))) } h := hashFunc.New() - + if sigSalt != nil { + h.Write(sigSalt) + } + wrappedHash, err := wrapHashForSignature(h, sigType) + if err != nil { + return nil, nil, err + } switch sigType { case packet.SigTypeBinary: - return h, h, nil + return h, wrappedHash, nil case packet.SigTypeText: - return h, NewCanonicalTextHash(h), nil + return h, wrappedHash, nil } - return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) } @@ -328,21 +348,27 @@ func hashForSignature(hashFunc crypto.Hash, sigType packet.SignatureType) (hash. // it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger // MDC checks. type checkReader struct { - md *MessageDetails + md *MessageDetails + checked bool } -func (cr checkReader) Read(buf []byte) (int, error) { +func (cr *checkReader) Read(buf []byte) (int, error) { n, sensitiveParsingError := cr.md.LiteralData.Body.Read(buf) if sensitiveParsingError == io.EOF { + if cr.checked { + // Only check once + return n, io.EOF + } mdcErr := cr.md.decrypted.Close() if mdcErr != nil { return n, mdcErr } + cr.checked = true return n, io.EOF } if sensitiveParsingError != nil { - return n, errors.StructuralError("parsing error") + return n, errors.HandleSensitiveParsingError(sensitiveParsingError, true) } return n, nil @@ -366,6 +392,7 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) { scr.wrappedHash.Write(buf[:n]) } + readsDecryptedData := scr.md.decrypted != nil if sensitiveParsingError == io.EOF { var p packet.Packet var readError error @@ -384,7 +411,7 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) { key := scr.md.SignedBy signatureError := key.PublicKey.VerifySignature(scr.h, sig) if signatureError == nil { - signatureError = checkSignatureDetails(key, sig, scr.config) + signatureError = checkMessageSignatureDetails(key, sig, scr.config) } scr.md.Signature = sig scr.md.SignatureError = signatureError @@ -408,16 +435,15 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) { // unsigned hash of its own. In order to check this we need to // close that Reader. if scr.md.decrypted != nil { - mdcErr := scr.md.decrypted.Close() - if mdcErr != nil { - return n, mdcErr + if sensitiveParsingError := scr.md.decrypted.Close(); sensitiveParsingError != nil { + return n, errors.HandleSensitiveParsingError(sensitiveParsingError, true) } } return n, io.EOF } if sensitiveParsingError != nil { - return n, errors.StructuralError("parsing error") + return n, errors.HandleSensitiveParsingError(sensitiveParsingError, readsDecryptedData) } return n, nil @@ -428,14 +454,13 @@ func (scr *signatureCheckReader) Read(buf []byte) (int, error) { // if any, and a possible signature verification error. // If the signer isn't known, ErrUnknownIssuer is returned. func VerifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) { - var expectedHashes []crypto.Hash - return verifyDetachedSignature(keyring, signed, signature, expectedHashes, config) + return verifyDetachedSignature(keyring, signed, signature, nil, false, config) } // VerifyDetachedSignatureAndHash performs the same actions as // VerifyDetachedSignature and checks that the expected hash functions were used. func VerifyDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) { - return verifyDetachedSignature(keyring, signed, signature, expectedHashes, config) + return verifyDetachedSignature(keyring, signed, signature, expectedHashes, true, config) } // CheckDetachedSignature takes a signed file and a detached signature and @@ -443,25 +468,24 @@ func VerifyDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader // signature verification error. If the signer isn't known, // ErrUnknownIssuer is returned. func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader, config *packet.Config) (signer *Entity, err error) { - var expectedHashes []crypto.Hash - return CheckDetachedSignatureAndHash(keyring, signed, signature, expectedHashes, config) + _, signer, err = verifyDetachedSignature(keyring, signed, signature, nil, false, config) + return } // CheckDetachedSignatureAndHash performs the same actions as // CheckDetachedSignature and checks that the expected hash functions were used. func CheckDetachedSignatureAndHash(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (signer *Entity, err error) { - _, signer, err = verifyDetachedSignature(keyring, signed, signature, expectedHashes, config) + _, signer, err = verifyDetachedSignature(keyring, signed, signature, expectedHashes, true, config) return } -func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) { +func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expectedHashes []crypto.Hash, checkHashes bool, config *packet.Config) (sig *packet.Signature, signer *Entity, err error) { var issuerKeyId uint64 var hashFunc crypto.Hash var sigType packet.SignatureType var keys []Key var p packet.Packet - expectedHashesLen := len(expectedHashes) packets := packet.NewReader(signature) for { p, err = packets.Next() @@ -483,16 +507,19 @@ func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expec issuerKeyId = *sig.IssuerKeyId hashFunc = sig.Hash sigType = sig.SigType - - for i, expectedHash := range expectedHashes { - if hashFunc == expectedHash { - break + if checkHashes { + matchFound := false + // check for hashes + for _, expectedHash := range expectedHashes { + if hashFunc == expectedHash { + matchFound = true + break + } } - if i+1 == expectedHashesLen { - return nil, nil, errors.StructuralError("hash algorithm mismatch with cleartext message headers") + if !matchFound { + return nil, nil, errors.StructuralError("hash algorithm or salt mismatch with cleartext message headers") } } - keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) if len(keys) > 0 { break @@ -503,7 +530,11 @@ func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expec panic("unreachable") } - h, wrappedHash, err := hashForSignature(hashFunc, sigType) + h, err := sig.PrepareVerify() + if err != nil { + return nil, nil, err + } + wrappedHash, err := wrapHashForSignature(h, sigType) if err != nil { return nil, nil, err } @@ -515,7 +546,7 @@ func verifyDetachedSignature(keyring KeyRing, signed, signature io.Reader, expec for _, key := range keys { err = key.PublicKey.VerifySignature(h, sig) if err == nil { - return sig, key.Entity, checkSignatureDetails(&key, sig, config) + return sig, key.Entity, checkMessageSignatureDetails(&key, sig, config) } } @@ -533,7 +564,7 @@ func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader, return CheckDetachedSignature(keyring, signed, body, config) } -// checkSignatureDetails returns an error if: +// checkMessageSignatureDetails returns an error if: // - The signature (or one of the binding signatures mentioned below) // has a unknown critical notation data subpacket // - The primary key of the signing entity is revoked @@ -551,15 +582,11 @@ func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader, // NOTE: The order of these checks is important, as the caller may choose to // ignore ErrSignatureExpired or ErrKeyExpired errors, but should never // ignore any other errors. -// -// TODO: Also return an error if: -// - The primary key is expired according to a direct-key signature -// - (For V5 keys only:) The direct-key signature (exists and) is expired -func checkSignatureDetails(key *Key, signature *packet.Signature, config *packet.Config) error { +func checkMessageSignatureDetails(key *Key, signature *packet.Signature, config *packet.Config) error { now := config.Now() - primaryIdentity := key.Entity.PrimaryIdentity() + primarySelfSignature, primaryIdentity := key.Entity.PrimarySelfSignature() signedBySubKey := key.PublicKey != key.Entity.PrimaryKey - sigsToCheck := []*packet.Signature{signature, primaryIdentity.SelfSignature} + sigsToCheck := []*packet.Signature{signature, primarySelfSignature} if signedBySubKey { sigsToCheck = append(sigsToCheck, key.SelfSignature, key.SelfSignature.EmbeddedSignature) } @@ -572,10 +599,10 @@ func checkSignatureDetails(key *Key, signature *packet.Signature, config *packet } if key.Entity.Revoked(now) || // primary key is revoked (signedBySubKey && key.Revoked(now)) || // subkey is revoked - primaryIdentity.Revoked(now) { // primary identity is revoked + (primaryIdentity != nil && primaryIdentity.Revoked(now)) { // primary identity is revoked for v4 return errors.ErrKeyRevoked } - if key.Entity.PrimaryKey.KeyExpired(primaryIdentity.SelfSignature, now) { // primary key is expired + if key.Entity.PrimaryKey.KeyExpired(primarySelfSignature, now) { // primary key is expired return errors.ErrKeyExpired } if signedBySubKey { diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go index db6dad5c0b..670d60226a 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.go @@ -26,6 +26,8 @@ const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a43129 const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" +const ed25519wX25519Key = "c54b0663877fe31b00000020f94da7bb48d60a61e567706a6587d0331999bb9d891a08242ead84543df895a3001972817b12be707e8d5f586ce61361201d344eb266a2c82fde6835762b65b0b7c2b1061f1b0a00000042058263877fe3030b090705150a0e080c021600029b03021e09222106cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc905270902070200000000ad2820103e2d7d227ec0e6d7ce4471db36bfc97083253690271498a7ef0576c07faae14585b3b903b0127ec4fda2f023045a2ec76bcb4f9571a9651e14aee1137a1d668442c88f951e33c4ffd33fb9a17d511eed758fc6d9cc50cb5fd793b2039d5804c74b0663877fe319000000208693248367f9e5015db922f8f48095dda784987f2d5985b12fbad16caf5e4435004d600a4f794d44775c57a26e0feefed558e9afffd6ad0d582d57fb2ba2dcedb8c29b06181b0a0000002c050263877fe322a106cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9021b0c00000000defa20a6e9186d9d5935fc8fe56314cdb527486a5a5120f9b762a235a729f039010a56b89c658568341fbef3b894e9834ad9bc72afae2f4c9c47a43855e65f1cb0a3f77bbc5f61085c1f8249fe4e7ca59af5f0bcee9398e0fa8d76e522e1d8ab42bb0d" + const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" @@ -160,18 +162,78 @@ TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw== =IiS2 -----END PGP PRIVATE KEY BLOCK-----` -// Generated with the above private key -const v5PrivKeyMsg = `-----BEGIN PGP MESSAGE----- -Version: OpenPGP.js v4.10.7 -Comment: https://openpgpjs.org +// See OpenPGP crypto refresh Section A.3. +const v6PrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK-----` + +// See OpenPGP crypto refresh merge request: +// https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/304 +const v6PrivKeyMsg = `-----BEGIN PGP MESSAGE----- + +wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO +WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS +aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l +yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo +bhF30A+IitsxxA== +-----END PGP MESSAGE-----` + +// See OpenPGP crypto refresh merge request: +// https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/305 +const v6PrivKeyInlineSignMsg = `-----BEGIN PGP MESSAGE----- -xA0DAQoWGTR7yYckZAIByxF1B21zZy50eHRfbIGSdGVzdMJ3BQEWCgAGBQJf -bIGSACMiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVDQvAP9G -y29VPonFXqi2zKkpZrvyvZxg+n5e8Nt9wNbuxeCd3QD/TtO2s+JvjrE4Siwv -UQdl5MlBka1QSNbMq2Bz7XwNPg4= -=6lbM +wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO +WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS +aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l +yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo +bhF30A+IitsxxA== -----END PGP MESSAGE-----` +// See https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/274 +// decryption password: "correct horse battery staple" +const v6ArgonSealedPrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xYIGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laP9JgkC +FARdb9ccngltHraRe25uHuyuAQQVtKipJ0+r5jL4dacGWSAheCWPpITYiyfyIOPS +3gIDyg8f7strd1OB4+LZsUhcIjOMpVHgmiY/IutJkulneoBYwrEGHxsKAAAAQgWC +Y4d/4wMLCQcFFQoOCAwCFgACmwMCHgkiIQbLGGxPBgmml+TVLfpscisMHx4nwYpW +cI9lJewnutmsyQUnCQIHAgAAAACtKCAQPi19In7A5tfORHHbNr/JcIMlNpAnFJin +7wV2wH+q4UWFs7kDsBJ+xP2i8CMEWi7Ha8tPlXGpZR4UruETeh1mhELIj5UeM8T/ +0z+5oX1RHu11j8bZzFDLX9eTsgOdWATHggZjh3/jGQAAACCGkySDZ/nlAV25Ivj0 +gJXdp4SYfy1ZhbEvutFsr15ENf0mCQIUBA5hhGgp2oaavg6mFUXcFMwBBBUuE8qf +9Ock+xwusd+GAglBr5LVyr/lup3xxQvHXFSjjA2haXfoN6xUGRdDEHI6+uevKjVR +v5oAxgu7eJpaXNjCmwYYGwoAAAAsBYJjh3/jApsMIiEGyxhsTwYJppfk1S36bHIr +DB8eJ8GKVnCPZSXsJ7rZrMkAAAAABAEgpukYbZ1ZNfyP5WMUzbUnSGpaUSD5t2Ki +Nacp8DkBClZRa2c3AMQzSDXa9jGhYzxjzVb5scHDzTkjyRZWRdTq8U6L4da+/+Kt +ruh8m7Xo2ehSSFyWRSuTSZe5tm/KXgYG +-----END PGP PRIVATE KEY BLOCK-----` + +const v4Key25519 = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUkEZB3qzRto01j2k2pwN5ux9w70stPinAdXULLr20CRW7U7h2GSeACch0M+ +qzQg8yjFQ8VBvu3uwgKH9senoHmj72lLSCLTmhFKzQR0ZXN0wogEEBsIAD4F +gmQd6s0ECwkHCAmQIf45+TuC+xMDFQgKBBYAAgECGQECmwMCHgEWIQSWEzMi +jJUHvyIbVKIh/jn5O4L7EwAAUhaHNlgudvxARdPPETUzVgjuWi+YIz8w1xIb +lHQMvIrbe2sGCQIethpWofd0x7DHuv/ciHg+EoxJ/Td6h4pWtIoKx0kEZB3q +zRm4CyA7quliq7yx08AoOqHTuuCgvpkSdEhpp3pEyejQOgBo0p6ywIiLPllY +0t+jpNspHpAGfXID6oqjpYuJw3AfVRBlwnQEGBsIACoFgmQd6s0JkCH+Ofk7 +gvsTApsMFiEElhMzIoyVB78iG1SiIf45+TuC+xMAAGgQuN9G73446ykvJ/mL +sCZ7zGFId2gBd1EnG0FTC4npfOKpck0X8dngByrCxU8LDSfvjsEp/xDAiKsQ +aU71tdtNBQ== +=e7jT +-----END PGP PRIVATE KEY BLOCK-----` + const keyWithExpiredCrossSig = `-----BEGIN PGP PUBLIC KEY BLOCK----- xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv @@ -272,3 +334,124 @@ AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY hz3tYjKhoFTKEIq3y3Pp =h/aX -----END PGP PUBLIC KEY BLOCK-----` + +const keyv5Test = `-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +` + +const certv5Test = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +lGEFXJH05BYAAAAtCSsGAQQB2kcPAQEHQFhZlVcVVtwf+21xNQPX+ecMJJBL0MPd +fj75iux+my8QAAAAAAAiAQCHZ1SnSUmWqxEsoI6facIVZQu6mph3cBFzzTvcm5lA +Ng5ctBhlbW1hLmdvbGRtYW5AZXhhbXBsZS5uZXSIlgUTFggASCIhBRk0e8mHJGQC +X5nfPsLgAA7ZiEiS4fez6kyUAJFZVptUBQJckfTkAhsDBQsJCAcCAyICAQYVCgkI +CwIEFgIDAQIeBwIXgAAA9cAA/jiR3yMsZMeEQ40u6uzEoXa6UXeV/S3wwJAXRJy9 +M8s0AP9vuL/7AyTfFXwwzSjDnYmzS0qAhbLDQ643N+MXGBJ2BZxmBVyR9OQSAAAA +MgorBgEEAZdVAQUBAQdA+nysrzml2UCweAqtpDuncSPlvrcBWKU0yfU0YvYWWAoD +AQgHAAAAAAAiAP9OdAPppjU1WwpqjIItkxr+VPQRT8Zm/Riw7U3F6v3OiBFHiHoF +GBYIACwiIQUZNHvJhyRkAl+Z3z7C4AAO2YhIkuH3s+pMlACRWVabVAUCXJH05AIb +DAAAOSQBAP4BOOIR/sGLNMOfeb5fPs/02QMieoiSjIBnijhob2U5AQC+RtOHCHx7 +TcIYl5/Uyoi+FOvPLcNw4hOv2nwUzSSVAw== +=IiS2 +-----END PGP PRIVATE KEY BLOCK----- +` + +const msgv5Test = `-----BEGIN PGP MESSAGE----- + +wcDMA3wvqk35PDeyAQv+PcQiLsoYTH30nJYQh3j3cJaO2+jErtVCrIQRIU0+ +rmgMddERYST4A9mA0DQIiTI4FQ0Lp440D3BWCgpq3LlNWewGzduaWwym5rN6 +cwHz5ccDqOcqbd9X0GXXGy/ZH/ljSgzuVMIytMAXKdF/vrRrVgH/+I7cxvm9 +HwnhjMN5dF0j4aEt996H2T7cbtzSr2GN9SWGW8Gyu7I8Zx73hgrGUI7gDiJB +Afaff+P6hfkkHSGOItr94dde8J/7AUF4VEwwxdVVPvsNEFyvv6gRIbYtOCa2 +6RE6h1V/QTxW2O7zZgzWALrE2ui0oaYr9QuqQSssd9CdgExLfdPbI+3/ZAnE +v31Idzpk3/6ILiakYHtXkElPXvf46mCNpobty8ysT34irF+fy3C1p3oGwAsx +5VDV9OSFU6z5U+UPbSPYAy9rkc5ZssuIKxCER2oTvZ2L8Q5cfUvEUiJtRGGn +CJlHrVDdp3FssKv2tlKgLkvxJLyoOjuEkj44H1qRk+D02FzmmUT/0sAHAYYx +lTir6mjHeLpcGjn4waUuWIAJyph8SxUexP60bic0L0NBa6Qp5SxxijKsPIDb +FPHxWwfJSDZRrgUyYT7089YFB/ZM4FHyH9TZcnxn0f0xIB7NS6YNDsxzN2zT +EVEYf+De4qT/dQTsdww78Chtcv9JY9r2kDm77dk2MUGHL2j7n8jasbLtgA7h +pn2DMIWLrGamMLWRmlwslolKr1sMV5x8w+5Ias6C33iBMl9phkg42an0gYmc +byVJHvLO/XErtC+GNIJeMg== +=liRq +-----END PGP MESSAGE----- +` diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go index a43695964b..6871b84fc9 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.go @@ -87,10 +87,10 @@ func decodeCount(c uint8) int { // encodeMemory converts the Argon2 "memory" in the range parallelism*8 to // 2**31, inclusive, to an encoded memory. The return value is the // octet that is actually stored in the GPG file. encodeMemory panics -// if is not in the above range +// if is not in the above range // See OpenPGP crypto refresh Section 3.7.1.4. func encodeMemory(memory uint32, parallelism uint8) uint8 { - if memory < (8 * uint32(parallelism)) || memory > uint32(2147483648) { + if memory < (8*uint32(parallelism)) || memory > uint32(2147483648) { panic("Memory argument memory is outside the required range") } @@ -199,8 +199,8 @@ func Generate(rand io.Reader, c *Config) (*Params, error) { } params = &Params{ - mode: SaltedS2K, - hashId: hashId, + mode: SaltedS2K, + hashId: hashId, } } else { // Enforce IteratedSaltedS2K method otherwise hashId, ok := algorithm.HashToHashId(c.hash()) @@ -211,7 +211,7 @@ func Generate(rand io.Reader, c *Config) (*Params, error) { c.S2KMode = IteratedSaltedS2K } params = &Params{ - mode: IteratedSaltedS2K, + mode: IteratedSaltedS2K, hashId: hashId, countByte: c.EncodedCount(), } @@ -283,6 +283,9 @@ func ParseIntoParams(r io.Reader) (params *Params, err error) { params.passes = buf[Argon2SaltSize] params.parallelism = buf[Argon2SaltSize+1] params.memoryExp = buf[Argon2SaltSize+2] + if err := validateArgon2Params(params); err != nil { + return nil, err + } return params, nil case GnuS2K: // This is a GNU extension. See @@ -300,15 +303,22 @@ func ParseIntoParams(r io.Reader) (params *Params, err error) { return nil, errors.UnsupportedError("S2K function") } +func (params *Params) Mode() Mode { + return params.mode +} + func (params *Params) Dummy() bool { return params != nil && params.mode == GnuS2K } func (params *Params) salt() []byte { switch params.mode { - case SaltedS2K, IteratedSaltedS2K: return params.saltBytes[:8] - case Argon2S2K: return params.saltBytes[:Argon2SaltSize] - default: return nil + case SaltedS2K, IteratedSaltedS2K: + return params.saltBytes[:8] + case Argon2S2K: + return params.saltBytes[:Argon2SaltSize] + default: + return nil } } @@ -405,3 +415,22 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte, c *Co f(key, passphrase) return nil } + +// validateArgon2Params checks that the argon2 parameters are valid according to RFC9580. +func validateArgon2Params(params *Params) error { + // The number of passes t and the degree of parallelism p MUST be non-zero. + if params.parallelism == 0 { + return errors.StructuralError("invalid argon2 params: parallelism is 0") + } + if params.passes == 0 { + return errors.StructuralError("invalid argon2 params: iterations is 0") + } + + // The encoded memory size MUST be a value from 3+ceil(log2(p)) to 31, + // such that the decoded memory size m is a value from 8*p to 2^31. + if params.memoryExp > 31 || decodeMemory(params.memoryExp) < 8*uint32(params.parallelism) { + return errors.StructuralError("invalid argon2 params: memory is out of bounds") + } + + return nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go index 25a4442dfb..616e0d12c6 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_cache.go @@ -5,7 +5,7 @@ package s2k // the same parameters. type Cache map[Params][]byte -// GetOrComputeDerivedKey tries to retrieve the key +// GetOrComputeDerivedKey tries to retrieve the key // for the given s2k parameters from the cache. // If there is no hit, it derives the key with the s2k function from the passphrase, // updates the cache, and returns the key. diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go index b40be5228f..b93db1ab85 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k_config.go @@ -50,9 +50,9 @@ type Config struct { type Argon2Config struct { NumberOfPasses uint8 DegreeOfParallelism uint8 - // The memory parameter for Argon2 specifies desired memory usage in kibibytes. + // Memory specifies the desired Argon2 memory usage in kibibytes. // For example memory=64*1024 sets the memory cost to ~64 MB. - Memory uint32 + Memory uint32 } func (c *Config) Mode() Mode { @@ -115,7 +115,7 @@ func (c *Argon2Config) EncodedMemory() uint8 { } memory := c.Memory - lowerBound := uint32(c.Parallelism())*8 + lowerBound := uint32(c.Parallelism()) * 8 upperBound := uint32(2147483648) switch { diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go index 7fdd13a3dd..b0f6ef7b09 100644 --- a/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/write.go @@ -76,7 +76,11 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S sig := createSignaturePacket(signingKey.PublicKey, sigType, config) - h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) + h, err := sig.PrepareSign(config) + if err != nil { + return + } + wrappedHash, err := wrapHashForSignature(h, sig.SigType) if err != nil { return } @@ -275,14 +279,28 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") } + var salt []byte if signer != nil { + var opsVersion = 3 + if signer.Version == 6 { + opsVersion = signer.Version + } ops := &packet.OnePassSignature{ + Version: opsVersion, SigType: sigType, Hash: hash, PubKeyAlgo: signer.PubKeyAlgo, KeyId: signer.KeyId, IsLast: true, } + if opsVersion == 6 { + ops.KeyFingerprint = signer.Fingerprint + salt, err = packet.SignatureSaltForHash(hash, config.Random()) + if err != nil { + return nil, err + } + ops.Salt = salt + } if err := ops.Serialize(payload); err != nil { return nil, err } @@ -310,19 +328,19 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit } if signer != nil { - h, wrappedHash, err := hashForSignature(hash, sigType) + h, wrappedHash, err := hashForSignature(hash, sigType, salt) if err != nil { return nil, err } metadata := &packet.LiteralData{ - Format: 't', + Format: 'u', FileName: hints.FileName, Time: epochSeconds, } if hints.IsBinary { metadata.Format = 'b' } - return signatureWriter{payload, literalData, hash, wrappedHash, h, signer, sigType, config, metadata}, nil + return signatureWriter{payload, literalData, hash, wrappedHash, h, salt, signer, sigType, config, metadata}, nil } return literalData, nil } @@ -380,15 +398,19 @@ func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *En return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no valid encryption keys") } - sig := to[i].PrimaryIdentity().SelfSignature - if !sig.SEIPDv2 { + primarySelfSignature, _ := to[i].PrimarySelfSignature() + if primarySelfSignature == nil { + return nil, errors.InvalidArgumentError("entity without a self-signature") + } + + if !primarySelfSignature.SEIPDv2 { aeadSupported = false } - candidateCiphers = intersectPreferences(candidateCiphers, sig.PreferredSymmetric) - candidateHashes = intersectPreferences(candidateHashes, sig.PreferredHash) - candidateCipherSuites = intersectCipherSuites(candidateCipherSuites, sig.PreferredCipherSuites) - candidateCompression = intersectPreferences(candidateCompression, sig.PreferredCompression) + candidateCiphers = intersectPreferences(candidateCiphers, primarySelfSignature.PreferredSymmetric) + candidateHashes = intersectPreferences(candidateHashes, primarySelfSignature.PreferredHash) + candidateCipherSuites = intersectCipherSuites(candidateCipherSuites, primarySelfSignature.PreferredCipherSuites) + candidateCompression = intersectPreferences(candidateCompression, primarySelfSignature.PreferredCompression) } // In the event that the intersection of supported algorithms is empty we use the ones @@ -422,13 +444,19 @@ func encrypt(keyWriter io.Writer, dataWriter io.Writer, to []*Entity, signed *En } } - symKey := make([]byte, cipher.KeySize()) + var symKey []byte + if aeadSupported { + symKey = make([]byte, aeadCipherSuite.Cipher.KeySize()) + } else { + symKey = make([]byte, cipher.KeySize()) + } + if _, err := io.ReadFull(config.Random(), symKey); err != nil { return nil, err } for _, key := range encryptKeys { - if err := packet.SerializeEncryptedKey(keyWriter, key.PublicKey, cipher, symKey, config); err != nil { + if err := packet.SerializeEncryptedKeyAEAD(keyWriter, key.PublicKey, cipher, aeadSupported, symKey, config); err != nil { return nil, err } } @@ -465,13 +493,17 @@ func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Con hashToHashId(crypto.SHA3_512), } defaultHashes := candidateHashes[0:1] - preferredHashes := signed.PrimaryIdentity().SelfSignature.PreferredHash + primarySelfSignature, _ := signed.PrimarySelfSignature() + if primarySelfSignature == nil { + return nil, errors.StructuralError("signed entity has no self-signature") + } + preferredHashes := primarySelfSignature.PreferredHash if len(preferredHashes) == 0 { preferredHashes = defaultHashes } candidateHashes = intersectPreferences(candidateHashes, preferredHashes) if len(candidateHashes) == 0 { - return nil, errors.InvalidArgumentError("cannot sign because signing key shares no common algorithms with candidate hashes") + return nil, errors.StructuralError("cannot sign because signing key shares no common algorithms with candidate hashes") } return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, packet.SigTypeBinary, config) @@ -486,6 +518,7 @@ type signatureWriter struct { hashType crypto.Hash wrappedHash hash.Hash h hash.Hash + salt []byte // v6 only signer *packet.PrivateKey sigType packet.SignatureType config *packet.Config @@ -509,6 +542,10 @@ func (s signatureWriter) Close() error { sig.Hash = s.hashType sig.Metadata = s.metadata + if err := sig.SetSalt(s.salt); err != nil { + return err + } + if err := sig.Sign(s.h, s.signer, s.config); err != nil { return err } diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go new file mode 100644 index 0000000000..38afcc74fa --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/x25519/x25519.go @@ -0,0 +1,221 @@ +package x25519 + +import ( + "crypto/sha256" + "crypto/subtle" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/aes/keywrap" + "github.com/ProtonMail/go-crypto/openpgp/errors" + x25519lib "github.com/cloudflare/circl/dh/x25519" + "golang.org/x/crypto/hkdf" +) + +const ( + hkdfInfo = "OpenPGP X25519" + aes128KeySize = 16 + // The size of a public or private key in bytes. + KeySize = x25519lib.Size +) + +type PublicKey struct { + // Point represents the encoded elliptic curve point of the public key. + Point []byte +} + +type PrivateKey struct { + PublicKey + // Secret represents the secret of the private key. + Secret []byte +} + +// NewPrivateKey creates a new empty private key including the public key. +func NewPrivateKey(key PublicKey) *PrivateKey { + return &PrivateKey{ + PublicKey: key, + } +} + +// Validate validates that the provided public key matches the private key. +func Validate(pk *PrivateKey) (err error) { + var expectedPublicKey, privateKey x25519lib.Key + subtle.ConstantTimeCopy(1, privateKey[:], pk.Secret) + x25519lib.KeyGen(&expectedPublicKey, &privateKey) + if subtle.ConstantTimeCompare(expectedPublicKey[:], pk.PublicKey.Point) == 0 { + return errors.KeyInvalidError("x25519: invalid key") + } + return nil +} + +// GenerateKey generates a new x25519 key pair. +func GenerateKey(rand io.Reader) (*PrivateKey, error) { + var privateKey, publicKey x25519lib.Key + privateKeyOut := new(PrivateKey) + err := generateKey(rand, &privateKey, &publicKey) + if err != nil { + return nil, err + } + privateKeyOut.PublicKey.Point = publicKey[:] + privateKeyOut.Secret = privateKey[:] + return privateKeyOut, nil +} + +func generateKey(rand io.Reader, privateKey *x25519lib.Key, publicKey *x25519lib.Key) error { + maxRounds := 10 + isZero := true + for round := 0; isZero; round++ { + if round == maxRounds { + return errors.InvalidArgumentError("x25519: zero keys only, randomness source might be corrupt") + } + _, err := io.ReadFull(rand, privateKey[:]) + if err != nil { + return err + } + isZero = constantTimeIsZero(privateKey[:]) + } + x25519lib.KeyGen(publicKey, privateKey) + return nil +} + +// Encrypt encrypts a sessionKey with x25519 according to +// the OpenPGP crypto refresh specification section 5.1.6. The function assumes that the +// sessionKey has the correct format and padding according to the specification. +func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) { + var ephemeralPrivate, ephemeralPublic, staticPublic, shared x25519lib.Key + // Check that the input static public key has 32 bytes + if len(publicKey.Point) != KeySize { + err = errors.KeyInvalidError("x25519: the public key has the wrong size") + return + } + copy(staticPublic[:], publicKey.Point) + // Generate ephemeral keyPair + err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic) + if err != nil { + return + } + // Compute shared key + ok := x25519lib.Shared(&shared, &ephemeralPrivate, &staticPublic) + if !ok { + err = errors.KeyInvalidError("x25519: the public key is a low order point") + return + } + // Derive the encryption key from the shared secret + encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:]) + ephemeralPublicKey = &PublicKey{ + Point: ephemeralPublic[:], + } + // Encrypt the sessionKey with aes key wrapping + encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey) + return +} + +// Decrypt decrypts a session key stored in ciphertext with the provided x25519 +// private key and ephemeral public key. +func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) { + var ephemeralPublic, staticPrivate, shared x25519lib.Key + // Check that the input ephemeral public key has 32 bytes + if len(ephemeralPublicKey.Point) != KeySize { + err = errors.KeyInvalidError("x25519: the public key has the wrong size") + return + } + copy(ephemeralPublic[:], ephemeralPublicKey.Point) + subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret) + // Compute shared key + ok := x25519lib.Shared(&shared, &staticPrivate, &ephemeralPublic) + if !ok { + err = errors.KeyInvalidError("x25519: the ephemeral public key is a low order point") + return + } + // Derive the encryption key from the shared secret + encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:]) + // Decrypt the session key with aes key wrapping + encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext) + return +} + +func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte { + inputKey := make([]byte, 3*KeySize) + // ephemeral public key | recipient public key | shared secret + subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey) + subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey) + subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret) + hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, []byte(hkdfInfo)) + encryptionKey := make([]byte, aes128KeySize) + _, _ = io.ReadFull(hkdfReader, encryptionKey) + return encryptionKey +} + +func constantTimeIsZero(bytes []byte) bool { + isZero := byte(0) + for _, b := range bytes { + isZero |= b + } + return isZero == 0 +} + +// ENCODING/DECODING ciphertexts: + +// EncodeFieldsLength returns the length of the ciphertext encoding +// given the encrypted session key. +func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int { + lenCipherFunction := 0 + if !v6 { + lenCipherFunction = 1 + } + return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction +} + +// EncodeField encodes x25519 session key encryption fields as +// ephemeral x25519 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey +// and writes it to writer. +func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) { + lenAlgorithm := 0 + if !v6 { + lenAlgorithm = 1 + } + if _, err = writer.Write(ephemeralPublicKey.Point); err != nil { + return err + } + if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil { + return err + } + if !v6 { + if _, err = writer.Write([]byte{cipherFunction}); err != nil { + return err + } + } + _, err = writer.Write(encryptedSessionKey) + return err +} + +// DecodeField decodes a x25519 session key encryption as +// ephemeral x25519 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey. +func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) { + var buf [1]byte + ephemeralPublicKey = &PublicKey{ + Point: make([]byte, KeySize), + } + // 32 octets representing an ephemeral x25519 public key. + if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil { + return nil, nil, 0, err + } + // A one-octet size of the following fields. + if _, err = io.ReadFull(reader, buf[:]); err != nil { + return nil, nil, 0, err + } + followingLen := buf[0] + // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet). + if !v6 { + if _, err = io.ReadFull(reader, buf[:]); err != nil { + return nil, nil, 0, err + } + cipherFunction = buf[0] + followingLen -= 1 + } + // The encrypted session key. + encryptedSessionKey = make([]byte, followingLen) + if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil { + return nil, nil, 0, err + } + return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil +} diff --git a/vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go b/vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go new file mode 100644 index 0000000000..65a082dabd --- /dev/null +++ b/vendor/github.com/ProtonMail/go-crypto/openpgp/x448/x448.go @@ -0,0 +1,229 @@ +package x448 + +import ( + "crypto/sha512" + "crypto/subtle" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/aes/keywrap" + "github.com/ProtonMail/go-crypto/openpgp/errors" + x448lib "github.com/cloudflare/circl/dh/x448" + "golang.org/x/crypto/hkdf" +) + +const ( + hkdfInfo = "OpenPGP X448" + aes256KeySize = 32 + // The size of a public or private key in bytes. + KeySize = x448lib.Size +) + +type PublicKey struct { + // Point represents the encoded elliptic curve point of the public key. + Point []byte +} + +type PrivateKey struct { + PublicKey + // Secret represents the secret of the private key. + Secret []byte +} + +// NewPrivateKey creates a new empty private key including the public key. +func NewPrivateKey(key PublicKey) *PrivateKey { + return &PrivateKey{ + PublicKey: key, + } +} + +// Validate validates that the provided public key matches +// the private key. +func Validate(pk *PrivateKey) (err error) { + var expectedPublicKey, privateKey x448lib.Key + subtle.ConstantTimeCopy(1, privateKey[:], pk.Secret) + x448lib.KeyGen(&expectedPublicKey, &privateKey) + if subtle.ConstantTimeCompare(expectedPublicKey[:], pk.PublicKey.Point) == 0 { + return errors.KeyInvalidError("x448: invalid key") + } + return nil +} + +// GenerateKey generates a new x448 key pair. +func GenerateKey(rand io.Reader) (*PrivateKey, error) { + var privateKey, publicKey x448lib.Key + privateKeyOut := new(PrivateKey) + err := generateKey(rand, &privateKey, &publicKey) + if err != nil { + return nil, err + } + privateKeyOut.PublicKey.Point = publicKey[:] + privateKeyOut.Secret = privateKey[:] + return privateKeyOut, nil +} + +func generateKey(rand io.Reader, privateKey *x448lib.Key, publicKey *x448lib.Key) error { + maxRounds := 10 + isZero := true + for round := 0; isZero; round++ { + if round == maxRounds { + return errors.InvalidArgumentError("x448: zero keys only, randomness source might be corrupt") + } + _, err := io.ReadFull(rand, privateKey[:]) + if err != nil { + return err + } + isZero = constantTimeIsZero(privateKey[:]) + } + x448lib.KeyGen(publicKey, privateKey) + return nil +} + +// Encrypt encrypts a sessionKey with x448 according to +// the OpenPGP crypto refresh specification section 5.1.7. The function assumes that the +// sessionKey has the correct format and padding according to the specification. +func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) { + var ephemeralPrivate, ephemeralPublic, staticPublic, shared x448lib.Key + // Check that the input static public key has 56 bytes. + if len(publicKey.Point) != KeySize { + err = errors.KeyInvalidError("x448: the public key has the wrong size") + return nil, nil, err + } + copy(staticPublic[:], publicKey.Point) + // Generate ephemeral keyPair. + if err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic); err != nil { + return nil, nil, err + } + // Compute shared key. + ok := x448lib.Shared(&shared, &ephemeralPrivate, &staticPublic) + if !ok { + err = errors.KeyInvalidError("x448: the public key is a low order point") + return nil, nil, err + } + // Derive the encryption key from the shared secret. + encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:]) + ephemeralPublicKey = &PublicKey{ + Point: ephemeralPublic[:], + } + // Encrypt the sessionKey with aes key wrapping. + encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey) + if err != nil { + return nil, nil, err + } + return ephemeralPublicKey, encryptedSessionKey, nil +} + +// Decrypt decrypts a session key stored in ciphertext with the provided x448 +// private key and ephemeral public key. +func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) { + var ephemeralPublic, staticPrivate, shared x448lib.Key + // Check that the input ephemeral public key has 56 bytes. + if len(ephemeralPublicKey.Point) != KeySize { + err = errors.KeyInvalidError("x448: the public key has the wrong size") + return nil, err + } + copy(ephemeralPublic[:], ephemeralPublicKey.Point) + subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret) + // Compute shared key. + ok := x448lib.Shared(&shared, &staticPrivate, &ephemeralPublic) + if !ok { + err = errors.KeyInvalidError("x448: the ephemeral public key is a low order point") + return nil, err + } + // Derive the encryption key from the shared secret. + encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:]) + // Decrypt the session key with aes key wrapping. + encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext) + if err != nil { + return nil, err + } + return encodedSessionKey, nil +} + +func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte { + inputKey := make([]byte, 3*KeySize) + // ephemeral public key | recipient public key | shared secret. + subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey) + subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey) + subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret) + hkdfReader := hkdf.New(sha512.New, inputKey, []byte{}, []byte(hkdfInfo)) + encryptionKey := make([]byte, aes256KeySize) + _, _ = io.ReadFull(hkdfReader, encryptionKey) + return encryptionKey +} + +func constantTimeIsZero(bytes []byte) bool { + isZero := byte(0) + for _, b := range bytes { + isZero |= b + } + return isZero == 0 +} + +// ENCODING/DECODING ciphertexts: + +// EncodeFieldsLength returns the length of the ciphertext encoding +// given the encrypted session key. +func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int { + lenCipherFunction := 0 + if !v6 { + lenCipherFunction = 1 + } + return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction +} + +// EncodeField encodes x448 session key encryption fields as +// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey +// and writes it to writer. +func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) { + lenAlgorithm := 0 + if !v6 { + lenAlgorithm = 1 + } + if _, err = writer.Write(ephemeralPublicKey.Point); err != nil { + return err + } + if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil { + return err + } + if !v6 { + if _, err = writer.Write([]byte{cipherFunction}); err != nil { + return err + } + } + if _, err = writer.Write(encryptedSessionKey); err != nil { + return err + } + return nil +} + +// DecodeField decodes a x448 session key encryption as +// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey. +func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) { + var buf [1]byte + ephemeralPublicKey = &PublicKey{ + Point: make([]byte, KeySize), + } + // 56 octets representing an ephemeral x448 public key. + if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil { + return nil, nil, 0, err + } + // A one-octet size of the following fields. + if _, err = io.ReadFull(reader, buf[:]); err != nil { + return nil, nil, 0, err + } + followingLen := buf[0] + // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet). + if !v6 { + if _, err = io.ReadFull(reader, buf[:]); err != nil { + return nil, nil, 0, err + } + cipherFunction = buf[0] + followingLen -= 1 + } + // The encrypted session key. + encryptedSessionKey = make([]byte, followingLen) + if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil { + return nil, nil, 0, err + } + return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md b/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md new file mode 100644 index 0000000000..cb1252b53e --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md @@ -0,0 +1,209 @@ +# Changelog # +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] ## + +## [0.3.6] - 2024-12-17 ## + +### Compatibility ### +- The minimum Go version requirement for `filepath-securejoin` is now Go 1.18 + (we use generics internally). + + For reference, `filepath-securejoin@v0.3.0` somewhat-arbitrarily bumped the + Go version requirement to 1.21. + + While we did make some use of Go 1.21 stdlib features (and in principle Go + versions <= 1.21 are no longer even supported by upstream anymore), some + downstreams have complained that the version bump has meant that they have to + do workarounds when backporting fixes that use the new `filepath-securejoin` + API onto old branches. This is not an ideal situation, but since using this + library is probably better for most downstreams than a hand-rolled + workaround, we now have compatibility shims that allow us to build on older + Go versions. +- Lower minimum version requirement for `golang.org/x/sys` to `v0.18.0` (we + need the wrappers for `fsconfig(2)`), which should also make backporting + patches to older branches easier. + +## [0.3.5] - 2024-12-06 ## + +### Fixed ### +- `MkdirAll` will now no longer return an `EEXIST` error if two racing + processes are creating the same directory. We will still verify that the path + is a directory, but this will avoid spurious errors when multiple threads or + programs are trying to `MkdirAll` the same path. opencontainers/runc#4543 + +## [0.3.4] - 2024-10-09 ## + +### Fixed ### +- Previously, some testing mocks we had resulted in us doing `import "testing"` + in non-`_test.go` code, which made some downstreams like Kubernetes unhappy. + This has been fixed. (#32) + +## [0.3.3] - 2024-09-30 ## + +### Fixed ### +- The mode and owner verification logic in `MkdirAll` has been removed. This + was originally intended to protect against some theoretical attacks but upon + further consideration these protections don't actually buy us anything and + they were causing spurious errors with more complicated filesystem setups. +- The "is the created directory empty" logic in `MkdirAll` has also been + removed. This was not causing us issues yet, but some pseudofilesystems (such + as `cgroup`) create non-empty directories and so this logic would've been + wrong for such cases. + +## [0.3.2] - 2024-09-13 ## + +### Changed ### +- Passing the `S_ISUID` or `S_ISGID` modes to `MkdirAllInRoot` will now return + an explicit error saying that those bits are ignored by `mkdirat(2)`. In the + past a different error was returned, but since the silent ignoring behaviour + is codified in the man pages a more explicit error seems apt. While silently + ignoring these bits would be the most compatible option, it could lead to + users thinking their code sets these bits when it doesn't. Programs that need + to deal with compatibility can mask the bits themselves. (#23, #25) + +### Fixed ### +- If a directory has `S_ISGID` set, then all child directories will have + `S_ISGID` set when created and a different gid will be used for any inode + created under the directory. Previously, the "expected owner and mode" + validation in `securejoin.MkdirAll` did not correctly handle this. We now + correctly handle this case. (#24, #25) + +## [0.3.1] - 2024-07-23 ## + +### Changed ### +- By allowing `Open(at)InRoot` to opt-out of the extra work done by `MkdirAll` + to do the necessary "partial lookups", `Open(at)InRoot` now does less work + for both implementations (resulting in a many-fold decrease in the number of + operations for `openat2`, and a modest improvement for non-`openat2`) and is + far more guaranteed to match the correct `openat2(RESOLVE_IN_ROOT)` + behaviour. +- We now use `readlinkat(fd, "")` where possible. For `Open(at)InRoot` this + effectively just means that we no longer risk getting spurious errors during + rename races. However, for our hardened procfs handler, this in theory should + prevent mount attacks from tricking us when doing magic-link readlinks (even + when using the unsafe host `/proc` handle). Unfortunately `Reopen` is still + potentially vulnerable to those kinds of somewhat-esoteric attacks. + + Technically this [will only work on post-2.6.39 kernels][linux-readlinkat-emptypath] + but it seems incredibly unlikely anyone is using `filepath-securejoin` on a + pre-2011 kernel. + +### Fixed ### +- Several improvements were made to the errors returned by `Open(at)InRoot` and + `MkdirAll` when dealing with invalid paths under the emulated (ie. + non-`openat2`) implementation. Previously, some paths would return the wrong + error (`ENOENT` when the last component was a non-directory), and other paths + would be returned as though they were acceptable (trailing-slash components + after a non-directory would be ignored by `Open(at)InRoot`). + + These changes were done to match `openat2`'s behaviour and purely is a + consistency fix (most users are going to be using `openat2` anyway). + +[linux-readlinkat-emptypath]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=65cfc6722361570bfe255698d9cd4dccaf47570d + +## [0.3.0] - 2024-07-11 ## + +### Added ### +- A new set of `*os.File`-based APIs have been added. These are adapted from + [libpathrs][] and we strongly suggest using them if possible (as they provide + far more protection against attacks than `SecureJoin`): + + - `Open(at)InRoot` resolves a path inside a rootfs and returns an `*os.File` + handle to the path. Note that the handle returned is an `O_PATH` handle, + which cannot be used for reading or writing (as well as some other + operations -- [see open(2) for more details][open.2]) + + - `Reopen` takes an `O_PATH` file handle and safely re-opens it to upgrade + it to a regular handle. This can also be used with non-`O_PATH` handles, + but `O_PATH` is the most obvious application. + + - `MkdirAll` is an implementation of `os.MkdirAll` that is safe to use to + create a directory tree within a rootfs. + + As these are new APIs, they may change in the future. However, they should be + safe to start migrating to as we have extensive tests ensuring they behave + correctly and are safe against various races and other attacks. + +[libpathrs]: https://github.com/openSUSE/libpathrs +[open.2]: https://www.man7.org/linux/man-pages/man2/open.2.html + +## [0.2.5] - 2024-05-03 ## + +### Changed ### +- Some minor changes were made to how lexical components (like `..` and `.`) + are handled during path generation in `SecureJoin`. There is no behaviour + change as a result of this fix (the resulting paths are the same). + +### Fixed ### +- The error returned when we hit a symlink loop now references the correct + path. (#10) + +## [0.2.4] - 2023-09-06 ## + +### Security ### +- This release fixes a potential security issue in filepath-securejoin when + used on Windows ([GHSA-6xv5-86q9-7xr8][], which could be used to generate + paths outside of the provided rootfs in certain cases), as well as improving + the overall behaviour of filepath-securejoin when dealing with Windows paths + that contain volume names. Thanks to Paulo Gomes for discovering and fixing + these issues. + +### Fixed ### +- Switch to GitHub Actions for CI so we can test on Windows as well as Linux + and MacOS. + +[GHSA-6xv5-86q9-7xr8]: https://github.com/advisories/GHSA-6xv5-86q9-7xr8 + +## [0.2.3] - 2021-06-04 ## + +### Changed ### +- Switch to Go 1.13-style `%w` error wrapping, letting us drop the dependency + on `github.com/pkg/errors`. + +## [0.2.2] - 2018-09-05 ## + +### Changed ### +- Use `syscall.ELOOP` as the base error for symlink loops, rather than our own + (internal) error. This allows callers to more easily use `errors.Is` to check + for this case. + +## [0.2.1] - 2018-09-05 ## + +### Fixed ### +- Use our own `IsNotExist` implementation, which lets us handle `ENOTDIR` + properly within `SecureJoin`. + +## [0.2.0] - 2017-07-19 ## + +We now have 100% test coverage! + +### Added ### +- Add a `SecureJoinVFS` API that can be used for mocking (as we do in our new + tests) or for implementing custom handling of lookup operations (such as for + rootless containers, where work is necessary to access directories with weird + modes because we don't have `CAP_DAC_READ_SEARCH` or `CAP_DAC_OVERRIDE`). + +## 0.1.0 - 2017-07-19 + +This is our first release of `github.com/cyphar/filepath-securejoin`, +containing a full implementation with a coverage of 93.5% (the only missing +cases are the error cases, which are hard to mocktest at the moment). + +[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...HEAD +[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6 +[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5 +[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.5...v0.3.0 +[0.2.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.4...v0.2.5 +[0.2.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.3...v0.2.4 +[0.2.3]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.2...v0.2.3 +[0.2.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.1.0...v0.2.0 diff --git a/vendor/github.com/cyphar/filepath-securejoin/LICENSE b/vendor/github.com/cyphar/filepath-securejoin/LICENSE index bec842f294..cb1ab88da0 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/LICENSE +++ b/vendor/github.com/cyphar/filepath-securejoin/LICENSE @@ -1,5 +1,5 @@ Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. -Copyright (C) 2017 SUSE LLC. All rights reserved. +Copyright (C) 2017-2024 SUSE LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/cyphar/filepath-securejoin/README.md b/vendor/github.com/cyphar/filepath-securejoin/README.md index 4eca0f2355..eaeb53fcd0 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/README.md +++ b/vendor/github.com/cyphar/filepath-securejoin/README.md @@ -1,32 +1,26 @@ ## `filepath-securejoin` ## +[![Go Documentation](https://pkg.go.dev/badge/github.com/cyphar/filepath-securejoin.svg)](https://pkg.go.dev/github.com/cyphar/filepath-securejoin) [![Build Status](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml/badge.svg)](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml) -An implementation of `SecureJoin`, a [candidate for inclusion in the Go -standard library][go#20126]. The purpose of this function is to be a "secure" -alternative to `filepath.Join`, and in particular it provides certain -guarantees that are not provided by `filepath.Join`. - -> **NOTE**: This code is *only* safe if you are not at risk of other processes -> modifying path components after you've used `SecureJoin`. If it is possible -> for a malicious process to modify path components of the resolved path, then -> you will be vulnerable to some fairly trivial TOCTOU race conditions. [There -> are some Linux kernel patches I'm working on which might allow for a better -> solution.][lwn-obeneath] -> -> In addition, with a slightly modified API it might be possible to use -> `O_PATH` and verify that the opened path is actually the resolved one -- but -> I have not done that yet. I might add it in the future as a helper function -> to help users verify the path (we can't just return `/proc/self/fd/` -> because that doesn't always work transparently for all users). - -This is the function prototype: +### Old API ### -```go -func SecureJoin(root, unsafePath string) (string, error) -``` +This library was originally just an implementation of `SecureJoin` which was +[intended to be included in the Go standard library][go#20126] as a safer +`filepath.Join` that would restrict the path lookup to be inside a root +directory. + +The implementation was based on code that existed in several container +runtimes. Unfortunately, this API is **fundamentally unsafe** against attackers +that can modify path components after `SecureJoin` returns and before the +caller uses the path, allowing for some fairly trivial TOCTOU attacks. + +`SecureJoin` (and `SecureJoinVFS`) are still provided by this library to +support legacy users, but new users are strongly suggested to avoid using +`SecureJoin` and instead use the [new api](#new-api) or switch to +[libpathrs][libpathrs]. -This library **guarantees** the following: +With the above limitations in mind, this library guarantees the following: * If no error is set, the resulting string **must** be a child path of `root` and will not contain any symlink path components (they will all be @@ -47,7 +41,7 @@ This library **guarantees** the following: A (trivial) implementation of this function on GNU/Linux systems could be done with the following (note that this requires root privileges and is far more opaque than the implementation in this library, and also requires that -`readlink` is inside the `root` path): +`readlink` is inside the `root` path and is trustworthy): ```go package securejoin @@ -70,9 +64,105 @@ func SecureJoin(root, unsafePath string) (string, error) { } ``` -[lwn-obeneath]: https://lwn.net/Articles/767547/ +[libpathrs]: https://github.com/openSUSE/libpathrs [go#20126]: https://github.com/golang/go/issues/20126 +### New API ### + +While we recommend users switch to [libpathrs][libpathrs] as soon as it has a +stable release, some methods implemented by libpathrs have been ported to this +library to ease the transition. These APIs are only supported on Linux. + +These APIs are implemented such that `filepath-securejoin` will +opportunistically use certain newer kernel APIs that make these operations far +more secure. In particular: + +* All of the lookup operations will use [`openat2`][openat2.2] on new enough + kernels (Linux 5.6 or later) to restrict lookups through magic-links and + bind-mounts (for certain operations) and to make use of `RESOLVE_IN_ROOT` to + efficiently resolve symlinks within a rootfs. + +* The APIs provide hardening against a malicious `/proc` mount to either detect + or avoid being tricked by a `/proc` that is not legitimate. This is done + using [`openat2`][openat2.2] for all users, and privileged users will also be + further protected by using [`fsopen`][fsopen.2] and [`open_tree`][open_tree.2] + (Linux 5.2 or later). + +[openat2.2]: https://www.man7.org/linux/man-pages/man2/openat2.2.html +[fsopen.2]: https://github.com/brauner/man-pages-md/blob/main/fsopen.md +[open_tree.2]: https://github.com/brauner/man-pages-md/blob/main/open_tree.md + +#### `OpenInRoot` #### + +```go +func OpenInRoot(root, unsafePath string) (*os.File, error) +func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error) +func Reopen(handle *os.File, flags int) (*os.File, error) +``` + +`OpenInRoot` is a much safer version of + +```go +path, err := securejoin.SecureJoin(root, unsafePath) +file, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC) +``` + +that protects against various race attacks that could lead to serious security +issues, depending on the application. Note that the returned `*os.File` is an +`O_PATH` file descriptor, which is quite restricted. Callers will probably need +to use `Reopen` to get a more usable handle (this split is done to provide +useful features like PTY spawning and to avoid users accidentally opening bad +inodes that could cause a DoS). + +Callers need to be careful in how they use the returned `*os.File`. Usually it +is only safe to operate on the handle directly, and it is very easy to create a +security issue. [libpathrs][libpathrs] provides far more helpers to make using +these handles safer -- there is currently no plan to port them to +`filepath-securejoin`. + +`OpenatInRoot` is like `OpenInRoot` except that the root is provided using an +`*os.File`. This allows you to ensure that multiple `OpenatInRoot` (or +`MkdirAllHandle`) calls are operating on the same rootfs. + +> **NOTE**: Unlike `SecureJoin`, `OpenInRoot` will error out as soon as it hits +> a dangling symlink or non-existent path. This is in contrast to `SecureJoin` +> which treated non-existent components as though they were real directories, +> and would allow for partial resolution of dangling symlinks. These behaviours +> are at odds with how Linux treats non-existent paths and dangling symlinks, +> and so these are no longer allowed. + +#### `MkdirAll` #### + +```go +func MkdirAll(root, unsafePath string, mode int) error +func MkdirAllHandle(root *os.File, unsafePath string, mode int) (*os.File, error) +``` + +`MkdirAll` is a much safer version of + +```go +path, err := securejoin.SecureJoin(root, unsafePath) +err = os.MkdirAll(path, mode) +``` + +that protects against the same kinds of races that `OpenInRoot` protects +against. + +`MkdirAllHandle` is like `MkdirAll` except that the root is provided using an +`*os.File` (the reason for this is the same as with `OpenatInRoot`) and an +`*os.File` of the final created directory is returned (this directory is +guaranteed to be effectively identical to the directory created by +`MkdirAllHandle`, which is not possible to ensure by just using `OpenatInRoot` +after `MkdirAll`). + +> **NOTE**: Unlike `SecureJoin`, `MkdirAll` will error out as soon as it hits +> a dangling symlink or non-existent path. This is in contrast to `SecureJoin` +> which treated non-existent components as though they were real directories, +> and would allow for partial resolution of dangling symlinks. These behaviours +> are at odds with how Linux treats non-existent paths and dangling symlinks, +> and so these are no longer allowed. This means that `MkdirAll` will not +> create non-existent directories referenced by a dangling symlink. + ### License ### The license of this project is the same as Go, which is a BSD 3-clause license diff --git a/vendor/github.com/cyphar/filepath-securejoin/VERSION b/vendor/github.com/cyphar/filepath-securejoin/VERSION index abd410582d..449d7e73a9 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/VERSION +++ b/vendor/github.com/cyphar/filepath-securejoin/VERSION @@ -1 +1 @@ -0.2.4 +0.3.6 diff --git a/vendor/github.com/cyphar/filepath-securejoin/doc.go b/vendor/github.com/cyphar/filepath-securejoin/doc.go new file mode 100644 index 0000000000..1ec7d065ef --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/doc.go @@ -0,0 +1,39 @@ +// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. +// Copyright (C) 2017-2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package securejoin implements a set of helpers to make it easier to write Go +// code that is safe against symlink-related escape attacks. The primary idea +// is to let you resolve a path within a rootfs directory as if the rootfs was +// a chroot. +// +// securejoin has two APIs, a "legacy" API and a "modern" API. +// +// The legacy API is [SecureJoin] and [SecureJoinVFS]. These methods are +// **not** safe against race conditions where an attacker changes the +// filesystem after (or during) the [SecureJoin] operation. +// +// The new API is made up of [OpenInRoot] and [MkdirAll] (and derived +// functions). These are safe against racing attackers and have several other +// protections that are not provided by the legacy API. There are many more +// operations that most programs expect to be able to do safely, but we do not +// provide explicit support for them because we want to encourage users to +// switch to [libpathrs](https://github.com/openSUSE/libpathrs) which is a +// cross-language next-generation library that is entirely designed around +// operating on paths safely. +// +// securejoin has been used by several container runtimes (Docker, runc, +// Kubernetes, etc) for quite a few years as a de-facto standard for operating +// on container filesystem paths "safely". However, most users still use the +// legacy API which is unsafe against various attacks (there is a fairly long +// history of CVEs in dependent as a result). Users should switch to the modern +// API as soon as possible (or even better, switch to libpathrs). +// +// This project was initially intended to be included in the Go standard +// library, but [it was rejected](https://go.dev/issue/20126). There is now a +// [new Go proposal](https://go.dev/issue/67002) for a safe path resolution API +// that shares some of the goals of filepath-securejoin. However, that design +// is intended to work like `openat2(RESOLVE_BENEATH)` which does not fit the +// usecase of container runtimes and most system tools. +package securejoin diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go new file mode 100644 index 0000000000..42452bbf9b --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_go120.go @@ -0,0 +1,18 @@ +//go:build linux && go1.20 + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "fmt" +) + +// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except +// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap) +// is only guaranteed to give you baseErr. +func wrapBaseError(baseErr, extraErr error) error { + return fmt.Errorf("%w: %w", extraErr, baseErr) +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go new file mode 100644 index 0000000000..e7adca3fd1 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_errors_unsupported.go @@ -0,0 +1,38 @@ +//go:build linux && !go1.20 + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "fmt" +) + +type wrappedError struct { + inner error + isError error +} + +func (err wrappedError) Is(target error) bool { + return err.isError == target +} + +func (err wrappedError) Unwrap() error { + return err.inner +} + +func (err wrappedError) Error() string { + return fmt.Sprintf("%v: %v", err.isError, err.inner) +} + +// wrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except +// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap) +// is only guaranteed to give you baseErr. +func wrapBaseError(baseErr, extraErr error) error { + return wrappedError{ + inner: baseErr, + isError: extraErr, + } +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go new file mode 100644 index 0000000000..ddd6fa9a41 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_go121.go @@ -0,0 +1,32 @@ +//go:build linux && go1.21 + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "slices" + "sync" +) + +func slices_DeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S { + return slices.DeleteFunc(slice, delFn) +} + +func slices_Contains[S ~[]E, E comparable](slice S, val E) bool { + return slices.Contains(slice, val) +} + +func slices_Clone[S ~[]E, E any](slice S) S { + return slices.Clone(slice) +} + +func sync_OnceValue[T any](f func() T) func() T { + return sync.OnceValue(f) +} + +func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { + return sync.OnceValues(f) +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go new file mode 100644 index 0000000000..f1e6fe7e71 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/gocompat_generics_unsupported.go @@ -0,0 +1,124 @@ +//go:build linux && !go1.21 + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "sync" +) + +// These are very minimal implementations of functions that appear in Go 1.21's +// stdlib, included so that we can build on older Go versions. Most are +// borrowed directly from the stdlib, and a few are modified to be "obviously +// correct" without needing to copy too many other helpers. + +// clearSlice is equivalent to the builtin clear from Go 1.21. +// Copied from the Go 1.24 stdlib implementation. +func clearSlice[S ~[]E, E any](slice S) { + var zero E + for i := range slice { + slice[i] = zero + } +} + +// Copied from the Go 1.24 stdlib implementation. +func slices_IndexFunc[S ~[]E, E any](s S, f func(E) bool) int { + for i := range s { + if f(s[i]) { + return i + } + } + return -1 +} + +// Copied from the Go 1.24 stdlib implementation. +func slices_DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S { + i := slices_IndexFunc(s, del) + if i == -1 { + return s + } + // Don't start copying elements until we find one to delete. + for j := i + 1; j < len(s); j++ { + if v := s[j]; !del(v) { + s[i] = v + i++ + } + } + clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC + return s[:i] +} + +// Similar to the stdlib slices.Contains, except that we don't have +// slices.Index so we need to use slices.IndexFunc for this non-Func helper. +func slices_Contains[S ~[]E, E comparable](s S, v E) bool { + return slices_IndexFunc(s, func(e E) bool { return e == v }) >= 0 +} + +// Copied from the Go 1.24 stdlib implementation. +func slices_Clone[S ~[]E, E any](s S) S { + // Preserve nil in case it matters. + if s == nil { + return nil + } + return append(S([]E{}), s...) +} + +// Copied from the Go 1.24 stdlib implementation. +func sync_OnceValue[T any](f func() T) func() T { + var ( + once sync.Once + valid bool + p any + result T + ) + g := func() { + defer func() { + p = recover() + if !valid { + panic(p) + } + }() + result = f() + f = nil + valid = true + } + return func() T { + once.Do(g) + if !valid { + panic(p) + } + return result + } +} + +// Copied from the Go 1.24 stdlib implementation. +func sync_OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { + var ( + once sync.Once + valid bool + p any + r1 T1 + r2 T2 + ) + g := func() { + defer func() { + p = recover() + if !valid { + panic(p) + } + }() + r1, r2 = f() + f = nil + valid = true + } + return func() (T1, T2) { + once.Do(g) + if !valid { + panic(p) + } + return r1, r2 + } +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/join.go b/vendor/github.com/cyphar/filepath-securejoin/join.go index aa32b85fb8..e0ee3f2b57 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/join.go +++ b/vendor/github.com/cyphar/filepath-securejoin/join.go @@ -1,17 +1,11 @@ // Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. -// Copyright (C) 2017 SUSE LLC. All rights reserved. +// Copyright (C) 2017-2024 SUSE LLC. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package securejoin is an implementation of the hopefully-soon-to-be-included -// SecureJoin helper that is meant to be part of the "path/filepath" package. -// The purpose of this project is to provide a PoC implementation to make the -// SecureJoin proposal (https://github.com/golang/go/issues/20126) more -// tangible. package securejoin import ( - "bytes" "errors" "os" "path/filepath" @@ -19,26 +13,34 @@ import ( "syscall" ) +const maxSymlinkLimit = 255 + // IsNotExist tells you if err is an error that implies that either the path // accessed does not exist (or path components don't exist). This is -// effectively a more broad version of os.IsNotExist. +// effectively a more broad version of [os.IsNotExist]. func IsNotExist(err error) bool { // Check that it's not actually an ENOTDIR, which in some cases is a more // convoluted case of ENOENT (usually involving weird paths). return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT) } -// SecureJoinVFS joins the two given path components (similar to Join) except +// SecureJoinVFS joins the two given path components (similar to [filepath.Join]) except // that the returned path is guaranteed to be scoped inside the provided root // path (when evaluated). Any symbolic links in the path are evaluated with the // given root treated as the root of the filesystem, similar to a chroot. The -// filesystem state is evaluated through the given VFS interface (if nil, the -// standard os.* family of functions are used). +// filesystem state is evaluated through the given [VFS] interface (if nil, the +// standard [os].* family of functions are used). // // Note that the guarantees provided by this function only apply if the path // components in the returned string are not modified (in other words are not // replaced with symlinks on the filesystem) after this function has returned. -// Such a symlink race is necessarily out-of-scope of SecureJoin. +// Such a symlink race is necessarily out-of-scope of SecureJoinVFS. +// +// NOTE: Due to the above limitation, Linux users are strongly encouraged to +// use [OpenInRoot] instead, which does safely protect against these kinds of +// attacks. There is no way to solve this problem with SecureJoinVFS because +// the API is fundamentally wrong (you cannot return a "safe" path string and +// guarantee it won't be modified afterwards). // // Volume names in unsafePath are always discarded, regardless if they are // provided via direct input or when evaluating symlinks. Therefore: @@ -51,75 +53,73 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) { } unsafePath = filepath.FromSlash(unsafePath) - var path bytes.Buffer - n := 0 - for unsafePath != "" { - if n > 255 { - return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP} + var ( + currentPath string + remainingPath = unsafePath + linksWalked int + ) + for remainingPath != "" { + if v := filepath.VolumeName(remainingPath); v != "" { + remainingPath = remainingPath[len(v):] } - if v := filepath.VolumeName(unsafePath); v != "" { - unsafePath = unsafePath[len(v):] - } - - // Next path component, p. - i := strings.IndexRune(unsafePath, filepath.Separator) - var p string - if i == -1 { - p, unsafePath = unsafePath, "" + // Get the next path component. + var part string + if i := strings.IndexRune(remainingPath, filepath.Separator); i == -1 { + part, remainingPath = remainingPath, "" } else { - p, unsafePath = unsafePath[:i], unsafePath[i+1:] + part, remainingPath = remainingPath[:i], remainingPath[i+1:] } - // Create a cleaned path, using the lexical semantics of /../a, to - // create a "scoped" path component which can safely be joined to fullP - // for evaluation. At this point, path.String() doesn't contain any - // symlink components. - cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p) - if cleanP == string(filepath.Separator) { - path.Reset() + // Apply the component lexically to the path we are building. + // currentPath does not contain any symlinks, and we are lexically + // dealing with a single component, so it's okay to do a filepath.Clean + // here. + nextPath := filepath.Join(string(filepath.Separator), currentPath, part) + if nextPath == string(filepath.Separator) { + currentPath = "" continue } - fullP := filepath.Clean(root + cleanP) + fullPath := root + string(filepath.Separator) + nextPath // Figure out whether the path is a symlink. - fi, err := vfs.Lstat(fullP) + fi, err := vfs.Lstat(fullPath) if err != nil && !IsNotExist(err) { return "", err } // Treat non-existent path components the same as non-symlinks (we // can't do any better here). if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 { - path.WriteString(p) - path.WriteRune(filepath.Separator) + currentPath = nextPath continue } - // Only increment when we actually dereference a link. - n++ + // It's a symlink, so get its contents and expand it by prepending it + // to the yet-unparsed path. + linksWalked++ + if linksWalked > maxSymlinkLimit { + return "", &os.PathError{Op: "SecureJoin", Path: root + string(filepath.Separator) + unsafePath, Err: syscall.ELOOP} + } - // It's a symlink, expand it by prepending it to the yet-unparsed path. - dest, err := vfs.Readlink(fullP) + dest, err := vfs.Readlink(fullPath) if err != nil { return "", err } + remainingPath = dest + string(filepath.Separator) + remainingPath // Absolute symlinks reset any work we've already done. if filepath.IsAbs(dest) { - path.Reset() + currentPath = "" } - unsafePath = dest + string(filepath.Separator) + unsafePath } - // We have to clean path.String() here because it may contain '..' - // components that are entirely lexical, but would be misleading otherwise. - // And finally do a final clean to ensure that root is also lexically - // clean. - fullP := filepath.Clean(string(filepath.Separator) + path.String()) - return filepath.Clean(root + fullP), nil + // There should be no lexical components like ".." left in the path here, + // but for safety clean up the path before joining it to the root. + finalPath := filepath.Join(string(filepath.Separator), currentPath) + return filepath.Join(root, finalPath), nil } -// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library -// of functions as the VFS. If in doubt, use this function over SecureJoinVFS. +// SecureJoin is a wrapper around [SecureJoinVFS] that just uses the [os].* library +// of functions as the [VFS]. If in doubt, use this function over [SecureJoinVFS]. func SecureJoin(root, unsafePath string) (string, error) { return SecureJoinVFS(root, unsafePath, nil) } diff --git a/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go b/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go new file mode 100644 index 0000000000..be81e498d7 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/lookup_linux.go @@ -0,0 +1,388 @@ +//go:build linux + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "errors" + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "golang.org/x/sys/unix" +) + +type symlinkStackEntry struct { + // (dir, remainingPath) is what we would've returned if the link didn't + // exist. This matches what openat2(RESOLVE_IN_ROOT) would return in + // this case. + dir *os.File + remainingPath string + // linkUnwalked is the remaining path components from the original + // Readlink which we have yet to walk. When this slice is empty, we + // drop the link from the stack. + linkUnwalked []string +} + +func (se symlinkStackEntry) String() string { + return fmt.Sprintf("<%s>/%s [->%s]", se.dir.Name(), se.remainingPath, strings.Join(se.linkUnwalked, "/")) +} + +func (se symlinkStackEntry) Close() { + _ = se.dir.Close() +} + +type symlinkStack []*symlinkStackEntry + +func (s *symlinkStack) IsEmpty() bool { + return s == nil || len(*s) == 0 +} + +func (s *symlinkStack) Close() { + if s != nil { + for _, link := range *s { + link.Close() + } + // TODO: Switch to clear once we switch to Go 1.21. + *s = nil + } +} + +var ( + errEmptyStack = errors.New("[internal] stack is empty") + errBrokenSymlinkStack = errors.New("[internal error] broken symlink stack") +) + +func (s *symlinkStack) popPart(part string) error { + if s == nil || s.IsEmpty() { + // If there is nothing in the symlink stack, then the part was from the + // real path provided by the user, and this is a no-op. + return errEmptyStack + } + if part == "." { + // "." components are no-ops -- we drop them when doing SwapLink. + return nil + } + + tailEntry := (*s)[len(*s)-1] + + // Double-check that we are popping the component we expect. + if len(tailEntry.linkUnwalked) == 0 { + return fmt.Errorf("%w: trying to pop component %q of empty stack entry %s", errBrokenSymlinkStack, part, tailEntry) + } + headPart := tailEntry.linkUnwalked[0] + if headPart != part { + return fmt.Errorf("%w: trying to pop component %q but the last stack entry is %s (%q)", errBrokenSymlinkStack, part, tailEntry, headPart) + } + + // Drop the component, but keep the entry around in case we are dealing + // with a "tail-chained" symlink. + tailEntry.linkUnwalked = tailEntry.linkUnwalked[1:] + return nil +} + +func (s *symlinkStack) PopPart(part string) error { + if err := s.popPart(part); err != nil { + if errors.Is(err, errEmptyStack) { + // Skip empty stacks. + err = nil + } + return err + } + + // Clean up any of the trailing stack entries that are empty. + for lastGood := len(*s) - 1; lastGood >= 0; lastGood-- { + entry := (*s)[lastGood] + if len(entry.linkUnwalked) > 0 { + break + } + entry.Close() + (*s) = (*s)[:lastGood] + } + return nil +} + +func (s *symlinkStack) push(dir *os.File, remainingPath, linkTarget string) error { + if s == nil { + return nil + } + // Split the link target and clean up any "" parts. + linkTargetParts := slices_DeleteFunc( + strings.Split(linkTarget, "/"), + func(part string) bool { return part == "" || part == "." }) + + // Copy the directory so the caller doesn't close our copy. + dirCopy, err := dupFile(dir) + if err != nil { + return err + } + + // Add to the stack. + *s = append(*s, &symlinkStackEntry{ + dir: dirCopy, + remainingPath: remainingPath, + linkUnwalked: linkTargetParts, + }) + return nil +} + +func (s *symlinkStack) SwapLink(linkPart string, dir *os.File, remainingPath, linkTarget string) error { + // If we are currently inside a symlink resolution, remove the symlink + // component from the last symlink entry, but don't remove the entry even + // if it's empty. If we are a "tail-chained" symlink (a trailing symlink we + // hit during a symlink resolution) we need to keep the old symlink until + // we finish the resolution. + if err := s.popPart(linkPart); err != nil { + if !errors.Is(err, errEmptyStack) { + return err + } + // Push the component regardless of whether the stack was empty. + } + return s.push(dir, remainingPath, linkTarget) +} + +func (s *symlinkStack) PopTopSymlink() (*os.File, string, bool) { + if s == nil || s.IsEmpty() { + return nil, "", false + } + tailEntry := (*s)[0] + *s = (*s)[1:] + return tailEntry.dir, tailEntry.remainingPath, true +} + +// partialLookupInRoot tries to lookup as much of the request path as possible +// within the provided root (a-la RESOLVE_IN_ROOT) and opens the final existing +// component of the requested path, returning a file handle to the final +// existing component and a string containing the remaining path components. +func partialLookupInRoot(root *os.File, unsafePath string) (*os.File, string, error) { + return lookupInRoot(root, unsafePath, true) +} + +func completeLookupInRoot(root *os.File, unsafePath string) (*os.File, error) { + handle, remainingPath, err := lookupInRoot(root, unsafePath, false) + if remainingPath != "" && err == nil { + // should never happen + err = fmt.Errorf("[bug] non-empty remaining path when doing a non-partial lookup: %q", remainingPath) + } + // lookupInRoot(partial=false) will always close the handle if an error is + // returned, so no need to double-check here. + return handle, err +} + +func lookupInRoot(root *os.File, unsafePath string, partial bool) (Handle *os.File, _ string, _ error) { + unsafePath = filepath.ToSlash(unsafePath) // noop + + // This is very similar to SecureJoin, except that we operate on the + // components using file descriptors. We then return the last component we + // managed open, along with the remaining path components not opened. + + // Try to use openat2 if possible. + if hasOpenat2() { + return lookupOpenat2(root, unsafePath, partial) + } + + // Get the "actual" root path from /proc/self/fd. This is necessary if the + // root is some magic-link like /proc/$pid/root, in which case we want to + // make sure when we do checkProcSelfFdPath that we are using the correct + // root path. + logicalRootPath, err := procSelfFdReadlink(root) + if err != nil { + return nil, "", fmt.Errorf("get real root path: %w", err) + } + + currentDir, err := dupFile(root) + if err != nil { + return nil, "", fmt.Errorf("clone root fd: %w", err) + } + defer func() { + // If a handle is not returned, close the internal handle. + if Handle == nil { + _ = currentDir.Close() + } + }() + + // symlinkStack is used to emulate how openat2(RESOLVE_IN_ROOT) treats + // dangling symlinks. If we hit a non-existent path while resolving a + // symlink, we need to return the (dir, remainingPath) that we had when we + // hit the symlink (treating the symlink as though it were a regular file). + // The set of (dir, remainingPath) sets is stored within the symlinkStack + // and we add and remove parts when we hit symlink and non-symlink + // components respectively. We need a stack because of recursive symlinks + // (symlinks that contain symlink components in their target). + // + // Note that the stack is ONLY used for book-keeping. All of the actual + // path walking logic is still based on currentPath/remainingPath and + // currentDir (as in SecureJoin). + var symStack *symlinkStack + if partial { + symStack = new(symlinkStack) + defer symStack.Close() + } + + var ( + linksWalked int + currentPath string + remainingPath = unsafePath + ) + for remainingPath != "" { + // Save the current remaining path so if the part is not real we can + // return the path including the component. + oldRemainingPath := remainingPath + + // Get the next path component. + var part string + if i := strings.IndexByte(remainingPath, '/'); i == -1 { + part, remainingPath = remainingPath, "" + } else { + part, remainingPath = remainingPath[:i], remainingPath[i+1:] + } + // If we hit an empty component, we need to treat it as though it is + // "." so that trailing "/" and "//" components on a non-directory + // correctly return the right error code. + if part == "" { + part = "." + } + + // Apply the component lexically to the path we are building. + // currentPath does not contain any symlinks, and we are lexically + // dealing with a single component, so it's okay to do a filepath.Clean + // here. + nextPath := path.Join("/", currentPath, part) + // If we logically hit the root, just clone the root rather than + // opening the part and doing all of the other checks. + if nextPath == "/" { + if err := symStack.PopPart(part); err != nil { + return nil, "", fmt.Errorf("walking into root with part %q failed: %w", part, err) + } + // Jump to root. + rootClone, err := dupFile(root) + if err != nil { + return nil, "", fmt.Errorf("clone root fd: %w", err) + } + _ = currentDir.Close() + currentDir = rootClone + currentPath = nextPath + continue + } + + // Try to open the next component. + nextDir, err := openatFile(currentDir, part, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) + switch { + case err == nil: + st, err := nextDir.Stat() + if err != nil { + _ = nextDir.Close() + return nil, "", fmt.Errorf("stat component %q: %w", part, err) + } + + switch st.Mode() & os.ModeType { + case os.ModeSymlink: + // readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See + // Linux commit 65cfc6722361 ("readlinkat(), fchownat() and + // fstatat() with empty relative pathnames"). + linkDest, err := readlinkatFile(nextDir, "") + // We don't need the handle anymore. + _ = nextDir.Close() + if err != nil { + return nil, "", err + } + + linksWalked++ + if linksWalked > maxSymlinkLimit { + return nil, "", &os.PathError{Op: "securejoin.lookupInRoot", Path: logicalRootPath + "/" + unsafePath, Err: unix.ELOOP} + } + + // Swap out the symlink's component for the link entry itself. + if err := symStack.SwapLink(part, currentDir, oldRemainingPath, linkDest); err != nil { + return nil, "", fmt.Errorf("walking into symlink %q failed: push symlink: %w", part, err) + } + + // Update our logical remaining path. + remainingPath = linkDest + "/" + remainingPath + // Absolute symlinks reset any work we've already done. + if path.IsAbs(linkDest) { + // Jump to root. + rootClone, err := dupFile(root) + if err != nil { + return nil, "", fmt.Errorf("clone root fd: %w", err) + } + _ = currentDir.Close() + currentDir = rootClone + currentPath = "/" + } + + default: + // If we are dealing with a directory, simply walk into it. + _ = currentDir.Close() + currentDir = nextDir + currentPath = nextPath + + // The part was real, so drop it from the symlink stack. + if err := symStack.PopPart(part); err != nil { + return nil, "", fmt.Errorf("walking into directory %q failed: %w", part, err) + } + + // If we are operating on a .., make sure we haven't escaped. + // We only have to check for ".." here because walking down + // into a regular component component cannot cause you to + // escape. This mirrors the logic in RESOLVE_IN_ROOT, except we + // have to check every ".." rather than only checking after a + // rename or mount on the system. + if part == ".." { + // Make sure the root hasn't moved. + if err := checkProcSelfFdPath(logicalRootPath, root); err != nil { + return nil, "", fmt.Errorf("root path moved during lookup: %w", err) + } + // Make sure the path is what we expect. + fullPath := logicalRootPath + nextPath + if err := checkProcSelfFdPath(fullPath, currentDir); err != nil { + return nil, "", fmt.Errorf("walking into %q had unexpected result: %w", part, err) + } + } + } + + default: + if !partial { + return nil, "", err + } + // If there are any remaining components in the symlink stack, we + // are still within a symlink resolution and thus we hit a dangling + // symlink. So pretend that the first symlink in the stack we hit + // was an ENOENT (to match openat2). + if oldDir, remainingPath, ok := symStack.PopTopSymlink(); ok { + _ = currentDir.Close() + return oldDir, remainingPath, err + } + // We have hit a final component that doesn't exist, so we have our + // partial open result. Note that we have to use the OLD remaining + // path, since the lookup failed. + return currentDir, oldRemainingPath, err + } + } + + // If the unsafePath had a trailing slash, we need to make sure we try to + // do a relative "." open so that we will correctly return an error when + // the final component is a non-directory (to match openat2). In the + // context of openat2, a trailing slash and a trailing "/." are completely + // equivalent. + if strings.HasSuffix(unsafePath, "/") { + nextDir, err := openatFile(currentDir, ".", unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) + if err != nil { + if !partial { + _ = currentDir.Close() + currentDir = nil + } + return currentDir, "", err + } + _ = currentDir.Close() + currentDir = nextDir + } + + // All of the components existed! + return currentDir, "", nil +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go b/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go new file mode 100644 index 0000000000..5e559bb7a8 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go @@ -0,0 +1,215 @@ +//go:build linux + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "golang.org/x/sys/unix" +) + +var ( + errInvalidMode = errors.New("invalid permission mode") + errPossibleAttack = errors.New("possible attack detected") +) + +// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use +// in two respects: +// +// - The caller provides the root directory as an *[os.File] (preferably O_PATH) +// handle. This means that the caller can be sure which root directory is +// being used. Note that this can be emulated by using /proc/self/fd/... as +// the root path with [os.MkdirAll]. +// +// - Once all of the directories have been created, an *[os.File] O_PATH handle +// to the directory at unsafePath is returned to the caller. This is done in +// an effectively-race-free way (an attacker would only be able to swap the +// final directory component), which is not possible to emulate with +// [MkdirAll]. +// +// In addition, the returned handle is obtained far more efficiently than doing +// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after +// doing [MkdirAll]. If you intend to open the directory after creating it, you +// should use MkdirAllHandle. +func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err error) { + // Make sure there are no os.FileMode bits set. + if mode&^0o7777 != 0 { + return nil, fmt.Errorf("%w for mkdir 0o%.3o", errInvalidMode, mode) + } + // On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid + // bits. We could also silently ignore them but since we have very few + // users it seems more prudent to return an error so users notice that + // these bits will not be set. + if mode&^0o1777 != 0 { + return nil, fmt.Errorf("%w for mkdir 0o%.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode) + } + + // Try to open as much of the path as possible. + currentDir, remainingPath, err := partialLookupInRoot(root, unsafePath) + defer func() { + if Err != nil { + _ = currentDir.Close() + } + }() + if err != nil && !errors.Is(err, unix.ENOENT) { + return nil, fmt.Errorf("find existing subpath of %q: %w", unsafePath, err) + } + + // If there is an attacker deleting directories as we walk into them, + // detect this proactively. Note this is guaranteed to detect if the + // attacker deleted any part of the tree up to currentDir. + // + // Once we walk into a dead directory, partialLookupInRoot would not be + // able to walk further down the tree (directories must be empty before + // they are deleted), and if the attacker has removed the entire tree we + // can be sure that anything that was originally inside a dead directory + // must also be deleted and thus is a dead directory in its own right. + // + // This is mostly a quality-of-life check, because mkdir will simply fail + // later if the attacker deletes the tree after this check. + if err := isDeadInode(currentDir); err != nil { + return nil, fmt.Errorf("finding existing subpath of %q: %w", unsafePath, err) + } + + // Re-open the path to match the O_DIRECTORY reopen loop later (so that we + // always return a non-O_PATH handle). We also check that we actually got a + // directory. + if reopenDir, err := Reopen(currentDir, unix.O_DIRECTORY|unix.O_CLOEXEC); errors.Is(err, unix.ENOTDIR) { + return nil, fmt.Errorf("cannot create subdirectories in %q: %w", currentDir.Name(), unix.ENOTDIR) + } else if err != nil { + return nil, fmt.Errorf("re-opening handle to %q: %w", currentDir.Name(), err) + } else { + _ = currentDir.Close() + currentDir = reopenDir + } + + remainingParts := strings.Split(remainingPath, string(filepath.Separator)) + if slices_Contains(remainingParts, "..") { + // The path contained ".." components after the end of the "real" + // components. We could try to safely resolve ".." here but that would + // add a bunch of extra logic for something that it's not clear even + // needs to be supported. So just return an error. + // + // If we do filepath.Clean(remainingPath) then we end up with the + // problem that ".." can erase a trailing dangling symlink and produce + // a path that doesn't quite match what the user asked for. + return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath) + } + + // Make sure the mode doesn't have any type bits. + mode &^= unix.S_IFMT + + // Create the remaining components. + for _, part := range remainingParts { + switch part { + case "", ".": + // Skip over no-op paths. + continue + } + + // NOTE: mkdir(2) will not follow trailing symlinks, so we can safely + // create the final component without worrying about symlink-exchange + // attacks. + // + // If we get -EEXIST, it's possible that another program created the + // directory at the same time as us. In that case, just continue on as + // if we created it (if the created inode is not a directory, the + // following open call will fail). + if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil && !errors.Is(err, unix.EEXIST) { + err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err} + // Make the error a bit nicer if the directory is dead. + if deadErr := isDeadInode(currentDir); deadErr != nil { + // TODO: Once we bump the minimum Go version to 1.20, we can use + // multiple %w verbs for this wrapping. For now we need to use a + // compatibility shim for older Go versions. + //err = fmt.Errorf("%w (%w)", err, deadErr) + err = wrapBaseError(err, deadErr) + } + return nil, err + } + + // Get a handle to the next component. O_DIRECTORY means we don't need + // to use O_PATH. + var nextDir *os.File + if hasOpenat2() { + nextDir, err = openat2File(currentDir, part, &unix.OpenHow{ + Flags: unix.O_NOFOLLOW | unix.O_DIRECTORY | unix.O_CLOEXEC, + Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_NO_XDEV, + }) + } else { + nextDir, err = openatFile(currentDir, part, unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) + } + if err != nil { + return nil, err + } + _ = currentDir.Close() + currentDir = nextDir + + // It's possible that the directory we just opened was swapped by an + // attacker. Unfortunately there isn't much we can do to protect + // against this, and MkdirAll's behaviour is that we will reuse + // existing directories anyway so the need to protect against this is + // incredibly limited (and arguably doesn't even deserve mention here). + // + // Ideally we might want to check that the owner and mode match what we + // would've created -- unfortunately, it is non-trivial to verify that + // the owner and mode of the created directory match. While plain Unix + // DAC rules seem simple enough to emulate, there are a bunch of other + // factors that can change the mode or owner of created directories + // (default POSIX ACLs, mount options like uid=1,gid=2,umask=0 on + // filesystems like vfat, etc etc). We used to try to verify this but + // it just lead to a series of spurious errors. + // + // We could also check that the directory is non-empty, but + // unfortunately some pseduofilesystems (like cgroupfs) create + // non-empty directories, which would result in different spurious + // errors. + } + return currentDir, nil +} + +// MkdirAll is a race-safe alternative to the [os.MkdirAll] function, +// where the new directory is guaranteed to be within the root directory (if an +// attacker can move directories from inside the root to outside the root, the +// created directory tree might be outside of the root but the key constraint +// is that at no point will we walk outside of the directory tree we are +// creating). +// +// Effectively, MkdirAll(root, unsafePath, mode) is equivalent to +// +// path, _ := securejoin.SecureJoin(root, unsafePath) +// err := os.MkdirAll(path, mode) +// +// But is much safer. The above implementation is unsafe because if an attacker +// can modify the filesystem tree between [SecureJoin] and [os.MkdirAll], it is +// possible for MkdirAll to resolve unsafe symlink components and create +// directories outside of the root. +// +// If you plan to open the directory after you have created it or want to use +// an open directory handle as the root, you should use [MkdirAllHandle] instead. +// This function is a wrapper around [MkdirAllHandle]. +// +// NOTE: The mode argument must be set the unix mode bits (unix.S_I...), not +// the Go generic mode bits ([os.FileMode]...). +func MkdirAll(root, unsafePath string, mode int) error { + rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) + if err != nil { + return err + } + defer rootDir.Close() + + f, err := MkdirAllHandle(rootDir, unsafePath, mode) + if err != nil { + return err + } + _ = f.Close() + return nil +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/open_linux.go b/vendor/github.com/cyphar/filepath-securejoin/open_linux.go new file mode 100644 index 0000000000..230be73f0e --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/open_linux.go @@ -0,0 +1,103 @@ +//go:build linux + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "fmt" + "os" + "strconv" + + "golang.org/x/sys/unix" +) + +// OpenatInRoot is equivalent to [OpenInRoot], except that the root is provided +// using an *[os.File] handle, to ensure that the correct root directory is used. +func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error) { + handle, err := completeLookupInRoot(root, unsafePath) + if err != nil { + return nil, &os.PathError{Op: "securejoin.OpenInRoot", Path: unsafePath, Err: err} + } + return handle, nil +} + +// OpenInRoot safely opens the provided unsafePath within the root. +// Effectively, OpenInRoot(root, unsafePath) is equivalent to +// +// path, _ := securejoin.SecureJoin(root, unsafePath) +// handle, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC) +// +// But is much safer. The above implementation is unsafe because if an attacker +// can modify the filesystem tree between [SecureJoin] and [os.OpenFile], it is +// possible for the returned file to be outside of the root. +// +// Note that the returned handle is an O_PATH handle, meaning that only a very +// limited set of operations will work on the handle. This is done to avoid +// accidentally opening an untrusted file that could cause issues (such as a +// disconnected TTY that could cause a DoS, or some other issue). In order to +// use the returned handle, you can "upgrade" it to a proper handle using +// [Reopen]. +func OpenInRoot(root, unsafePath string) (*os.File, error) { + rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) + if err != nil { + return nil, err + } + defer rootDir.Close() + return OpenatInRoot(rootDir, unsafePath) +} + +// Reopen takes an *[os.File] handle and re-opens it through /proc/self/fd. +// Reopen(file, flags) is effectively equivalent to +// +// fdPath := fmt.Sprintf("/proc/self/fd/%d", file.Fd()) +// os.OpenFile(fdPath, flags|unix.O_CLOEXEC) +// +// But with some extra hardenings to ensure that we are not tricked by a +// maliciously-configured /proc mount. While this attack scenario is not +// common, in container runtimes it is possible for higher-level runtimes to be +// tricked into configuring an unsafe /proc that can be used to attack file +// operations. See [CVE-2019-19921] for more details. +// +// [CVE-2019-19921]: https://github.com/advisories/GHSA-fh74-hm69-rqjw +func Reopen(handle *os.File, flags int) (*os.File, error) { + procRoot, err := getProcRoot() + if err != nil { + return nil, err + } + + // We can't operate on /proc/thread-self/fd/$n directly when doing a + // re-open, so we need to open /proc/thread-self/fd and then open a single + // final component. + procFdDir, closer, err := procThreadSelf(procRoot, "fd/") + if err != nil { + return nil, fmt.Errorf("get safe /proc/thread-self/fd handle: %w", err) + } + defer procFdDir.Close() + defer closer() + + // Try to detect if there is a mount on top of the magic-link we are about + // to open. If we are using unsafeHostProcRoot(), this could change after + // we check it (and there's nothing we can do about that) but for + // privateProcRoot() this should be guaranteed to be safe (at least since + // Linux 5.12[1], when anonymous mount namespaces were completely isolated + // from external mounts including mount propagation events). + // + // [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts + // onto targets that reside on shared mounts"). + fdStr := strconv.Itoa(int(handle.Fd())) + if err := checkSymlinkOvermount(procRoot, procFdDir, fdStr); err != nil { + return nil, fmt.Errorf("check safety of /proc/thread-self/fd/%s magiclink: %w", fdStr, err) + } + + flags |= unix.O_CLOEXEC + // Rather than just wrapping openatFile, open-code it so we can copy + // handle.Name(). + reopenFd, err := unix.Openat(int(procFdDir.Fd()), fdStr, flags, 0) + if err != nil { + return nil, fmt.Errorf("reopen fd %d: %w", handle.Fd(), err) + } + return os.NewFile(uintptr(reopenFd), handle.Name()), nil +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go b/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go new file mode 100644 index 0000000000..f7a13e69ce --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/openat2_linux.go @@ -0,0 +1,127 @@ +//go:build linux + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "golang.org/x/sys/unix" +) + +var hasOpenat2 = sync_OnceValue(func() bool { + fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{ + Flags: unix.O_PATH | unix.O_CLOEXEC, + Resolve: unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_IN_ROOT, + }) + if err != nil { + return false + } + _ = unix.Close(fd) + return true +}) + +func scopedLookupShouldRetry(how *unix.OpenHow, err error) bool { + // RESOLVE_IN_ROOT (and RESOLVE_BENEATH) can return -EAGAIN if we resolve + // ".." while a mount or rename occurs anywhere on the system. This could + // happen spuriously, or as the result of an attacker trying to mess with + // us during lookup. + // + // In addition, scoped lookups have a "safety check" at the end of + // complete_walk which will return -EXDEV if the final path is not in the + // root. + return how.Resolve&(unix.RESOLVE_IN_ROOT|unix.RESOLVE_BENEATH) != 0 && + (errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EXDEV)) +} + +const scopedLookupMaxRetries = 10 + +func openat2File(dir *os.File, path string, how *unix.OpenHow) (*os.File, error) { + fullPath := dir.Name() + "/" + path + // Make sure we always set O_CLOEXEC. + how.Flags |= unix.O_CLOEXEC + var tries int + for tries < scopedLookupMaxRetries { + fd, err := unix.Openat2(int(dir.Fd()), path, how) + if err != nil { + if scopedLookupShouldRetry(how, err) { + // We retry a couple of times to avoid the spurious errors, and + // if we are being attacked then returning -EAGAIN is the best + // we can do. + tries++ + continue + } + return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: err} + } + // If we are using RESOLVE_IN_ROOT, the name we generated may be wrong. + // NOTE: The procRoot code MUST NOT use RESOLVE_IN_ROOT, otherwise + // you'll get infinite recursion here. + if how.Resolve&unix.RESOLVE_IN_ROOT == unix.RESOLVE_IN_ROOT { + if actualPath, err := rawProcSelfFdReadlink(fd); err == nil { + fullPath = actualPath + } + } + return os.NewFile(uintptr(fd), fullPath), nil + } + return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: errPossibleAttack} +} + +func lookupOpenat2(root *os.File, unsafePath string, partial bool) (*os.File, string, error) { + if !partial { + file, err := openat2File(root, unsafePath, &unix.OpenHow{ + Flags: unix.O_PATH | unix.O_CLOEXEC, + Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS, + }) + return file, "", err + } + return partialLookupOpenat2(root, unsafePath) +} + +// partialLookupOpenat2 is an alternative implementation of +// partialLookupInRoot, using openat2(RESOLVE_IN_ROOT) to more safely get a +// handle to the deepest existing child of the requested path within the root. +func partialLookupOpenat2(root *os.File, unsafePath string) (*os.File, string, error) { + // TODO: Implement this as a git-bisect-like binary search. + + unsafePath = filepath.ToSlash(unsafePath) // noop + endIdx := len(unsafePath) + var lastError error + for endIdx > 0 { + subpath := unsafePath[:endIdx] + + handle, err := openat2File(root, subpath, &unix.OpenHow{ + Flags: unix.O_PATH | unix.O_CLOEXEC, + Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS, + }) + if err == nil { + // Jump over the slash if we have a non-"" remainingPath. + if endIdx < len(unsafePath) { + endIdx += 1 + } + // We found a subpath! + return handle, unsafePath[endIdx:], lastError + } + if errors.Is(err, unix.ENOENT) || errors.Is(err, unix.ENOTDIR) { + // That path doesn't exist, let's try the next directory up. + endIdx = strings.LastIndexByte(subpath, '/') + lastError = err + continue + } + return nil, "", fmt.Errorf("open subpath: %w", err) + } + // If we couldn't open anything, the whole subpath is missing. Return a + // copy of the root fd so that the caller doesn't close this one by + // accident. + rootClone, err := dupFile(root) + if err != nil { + return nil, "", err + } + return rootClone, unsafePath, lastError +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/openat_linux.go b/vendor/github.com/cyphar/filepath-securejoin/openat_linux.go new file mode 100644 index 0000000000..949fb5f2d8 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/openat_linux.go @@ -0,0 +1,59 @@ +//go:build linux + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "os" + "path/filepath" + + "golang.org/x/sys/unix" +) + +func dupFile(f *os.File) (*os.File, error) { + fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0) + if err != nil { + return nil, os.NewSyscallError("fcntl(F_DUPFD_CLOEXEC)", err) + } + return os.NewFile(uintptr(fd), f.Name()), nil +} + +func openatFile(dir *os.File, path string, flags int, mode int) (*os.File, error) { + // Make sure we always set O_CLOEXEC. + flags |= unix.O_CLOEXEC + fd, err := unix.Openat(int(dir.Fd()), path, flags, uint32(mode)) + if err != nil { + return nil, &os.PathError{Op: "openat", Path: dir.Name() + "/" + path, Err: err} + } + // All of the paths we use with openatFile(2) are guaranteed to be + // lexically safe, so we can use path.Join here. + fullPath := filepath.Join(dir.Name(), path) + return os.NewFile(uintptr(fd), fullPath), nil +} + +func fstatatFile(dir *os.File, path string, flags int) (unix.Stat_t, error) { + var stat unix.Stat_t + if err := unix.Fstatat(int(dir.Fd()), path, &stat, flags); err != nil { + return stat, &os.PathError{Op: "fstatat", Path: dir.Name() + "/" + path, Err: err} + } + return stat, nil +} + +func readlinkatFile(dir *os.File, path string) (string, error) { + size := 4096 + for { + linkBuf := make([]byte, size) + n, err := unix.Readlinkat(int(dir.Fd()), path, linkBuf) + if err != nil { + return "", &os.PathError{Op: "readlinkat", Path: dir.Name() + "/" + path, Err: err} + } + if n != size { + return string(linkBuf[:n]), nil + } + // Possible truncation, resize the buffer. + size *= 2 + } +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go b/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go new file mode 100644 index 0000000000..809a579cbd --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/procfs_linux.go @@ -0,0 +1,452 @@ +//go:build linux + +// Copyright (C) 2024 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import ( + "errors" + "fmt" + "os" + "runtime" + "strconv" + + "golang.org/x/sys/unix" +) + +func fstat(f *os.File) (unix.Stat_t, error) { + var stat unix.Stat_t + if err := unix.Fstat(int(f.Fd()), &stat); err != nil { + return stat, &os.PathError{Op: "fstat", Path: f.Name(), Err: err} + } + return stat, nil +} + +func fstatfs(f *os.File) (unix.Statfs_t, error) { + var statfs unix.Statfs_t + if err := unix.Fstatfs(int(f.Fd()), &statfs); err != nil { + return statfs, &os.PathError{Op: "fstatfs", Path: f.Name(), Err: err} + } + return statfs, nil +} + +// The kernel guarantees that the root inode of a procfs mount has an +// f_type of PROC_SUPER_MAGIC and st_ino of PROC_ROOT_INO. +const ( + procSuperMagic = 0x9fa0 // PROC_SUPER_MAGIC + procRootIno = 1 // PROC_ROOT_INO +) + +func verifyProcRoot(procRoot *os.File) error { + if statfs, err := fstatfs(procRoot); err != nil { + return err + } else if statfs.Type != procSuperMagic { + return fmt.Errorf("%w: incorrect procfs root filesystem type 0x%x", errUnsafeProcfs, statfs.Type) + } + if stat, err := fstat(procRoot); err != nil { + return err + } else if stat.Ino != procRootIno { + return fmt.Errorf("%w: incorrect procfs root inode number %d", errUnsafeProcfs, stat.Ino) + } + return nil +} + +var hasNewMountApi = sync_OnceValue(func() bool { + // All of the pieces of the new mount API we use (fsopen, fsconfig, + // fsmount, open_tree) were added together in Linux 5.1[1,2], so we can + // just check for one of the syscalls and the others should also be + // available. + // + // Just try to use open_tree(2) to open a file without OPEN_TREE_CLONE. + // This is equivalent to openat(2), but tells us if open_tree is + // available (and thus all of the other basic new mount API syscalls). + // open_tree(2) is most light-weight syscall to test here. + // + // [1]: merge commit 400913252d09 + // [2]: + fd, err := unix.OpenTree(-int(unix.EBADF), "/", unix.OPEN_TREE_CLOEXEC) + if err != nil { + return false + } + _ = unix.Close(fd) + return true +}) + +func fsopen(fsName string, flags int) (*os.File, error) { + // Make sure we always set O_CLOEXEC. + flags |= unix.FSOPEN_CLOEXEC + fd, err := unix.Fsopen(fsName, flags) + if err != nil { + return nil, os.NewSyscallError("fsopen "+fsName, err) + } + return os.NewFile(uintptr(fd), "fscontext:"+fsName), nil +} + +func fsmount(ctx *os.File, flags, mountAttrs int) (*os.File, error) { + // Make sure we always set O_CLOEXEC. + flags |= unix.FSMOUNT_CLOEXEC + fd, err := unix.Fsmount(int(ctx.Fd()), flags, mountAttrs) + if err != nil { + return nil, os.NewSyscallError("fsmount "+ctx.Name(), err) + } + return os.NewFile(uintptr(fd), "fsmount:"+ctx.Name()), nil +} + +func newPrivateProcMount() (*os.File, error) { + procfsCtx, err := fsopen("proc", unix.FSOPEN_CLOEXEC) + if err != nil { + return nil, err + } + defer procfsCtx.Close() + + // Try to configure hidepid=ptraceable,subset=pid if possible, but ignore errors. + _ = unix.FsconfigSetString(int(procfsCtx.Fd()), "hidepid", "ptraceable") + _ = unix.FsconfigSetString(int(procfsCtx.Fd()), "subset", "pid") + + // Get an actual handle. + if err := unix.FsconfigCreate(int(procfsCtx.Fd())); err != nil { + return nil, os.NewSyscallError("fsconfig create procfs", err) + } + return fsmount(procfsCtx, unix.FSMOUNT_CLOEXEC, unix.MS_RDONLY|unix.MS_NODEV|unix.MS_NOEXEC|unix.MS_NOSUID) +} + +func openTree(dir *os.File, path string, flags uint) (*os.File, error) { + dirFd := -int(unix.EBADF) + dirName := "." + if dir != nil { + dirFd = int(dir.Fd()) + dirName = dir.Name() + } + // Make sure we always set O_CLOEXEC. + flags |= unix.OPEN_TREE_CLOEXEC + fd, err := unix.OpenTree(dirFd, path, flags) + if err != nil { + return nil, &os.PathError{Op: "open_tree", Path: path, Err: err} + } + return os.NewFile(uintptr(fd), dirName+"/"+path), nil +} + +func clonePrivateProcMount() (_ *os.File, Err error) { + // Try to make a clone without using AT_RECURSIVE if we can. If this works, + // we can be sure there are no over-mounts and so if the root is valid then + // we're golden. Otherwise, we have to deal with over-mounts. + procfsHandle, err := openTree(nil, "/proc", unix.OPEN_TREE_CLONE) + if err != nil || hookForcePrivateProcRootOpenTreeAtRecursive(procfsHandle) { + procfsHandle, err = openTree(nil, "/proc", unix.OPEN_TREE_CLONE|unix.AT_RECURSIVE) + } + if err != nil { + return nil, fmt.Errorf("creating a detached procfs clone: %w", err) + } + defer func() { + if Err != nil { + _ = procfsHandle.Close() + } + }() + if err := verifyProcRoot(procfsHandle); err != nil { + return nil, err + } + return procfsHandle, nil +} + +func privateProcRoot() (*os.File, error) { + if !hasNewMountApi() || hookForceGetProcRootUnsafe() { + return nil, fmt.Errorf("new mount api: %w", unix.ENOTSUP) + } + // Try to create a new procfs mount from scratch if we can. This ensures we + // can get a procfs mount even if /proc is fake (for whatever reason). + procRoot, err := newPrivateProcMount() + if err != nil || hookForcePrivateProcRootOpenTree(procRoot) { + // Try to clone /proc then... + procRoot, err = clonePrivateProcMount() + } + return procRoot, err +} + +func unsafeHostProcRoot() (_ *os.File, Err error) { + procRoot, err := os.OpenFile("/proc", unix.O_PATH|unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) + if err != nil { + return nil, err + } + defer func() { + if Err != nil { + _ = procRoot.Close() + } + }() + if err := verifyProcRoot(procRoot); err != nil { + return nil, err + } + return procRoot, nil +} + +func doGetProcRoot() (*os.File, error) { + procRoot, err := privateProcRoot() + if err != nil { + // Fall back to using a /proc handle if making a private mount failed. + // If we have openat2, at least we can avoid some kinds of over-mount + // attacks, but without openat2 there's not much we can do. + procRoot, err = unsafeHostProcRoot() + } + return procRoot, err +} + +var getProcRoot = sync_OnceValues(func() (*os.File, error) { + return doGetProcRoot() +}) + +var hasProcThreadSelf = sync_OnceValue(func() bool { + return unix.Access("/proc/thread-self/", unix.F_OK) == nil +}) + +var errUnsafeProcfs = errors.New("unsafe procfs detected") + +type procThreadSelfCloser func() + +// procThreadSelf returns a handle to /proc/thread-self/ (or an +// equivalent handle on older kernels where /proc/thread-self doesn't exist). +// Once finished with the handle, you must call the returned closer function +// (runtime.UnlockOSThread). You must not pass the returned *os.File to other +// Go threads or use the handle after calling the closer. +// +// This is similar to ProcThreadSelf from runc, but with extra hardening +// applied and using *os.File. +func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThreadSelfCloser, Err error) { + // We need to lock our thread until the caller is done with the handle + // because between getting the handle and using it we could get interrupted + // by the Go runtime and hit the case where the underlying thread is + // swapped out and the original thread is killed, resulting in + // pull-your-hair-out-hard-to-debug issues in the caller. + runtime.LockOSThread() + defer func() { + if Err != nil { + runtime.UnlockOSThread() + } + }() + + // Figure out what prefix we want to use. + threadSelf := "thread-self/" + if !hasProcThreadSelf() || hookForceProcSelfTask() { + /// Pre-3.17 kernels don't have /proc/thread-self, so do it manually. + threadSelf = "self/task/" + strconv.Itoa(unix.Gettid()) + "/" + if _, err := fstatatFile(procRoot, threadSelf, unix.AT_SYMLINK_NOFOLLOW); err != nil || hookForceProcSelf() { + // In this case, we running in a pid namespace that doesn't match + // the /proc mount we have. This can happen inside runc. + // + // Unfortunately, there is no nice way to get the correct TID to + // use here because of the age of the kernel, so we have to just + // use /proc/self and hope that it works. + threadSelf = "self/" + } + } + + // Grab the handle. + var ( + handle *os.File + err error + ) + if hasOpenat2() { + // We prefer being able to use RESOLVE_NO_XDEV if we can, to be + // absolutely sure we are operating on a clean /proc handle that + // doesn't have any cheeky overmounts that could trick us (including + // symlink mounts on top of /proc/thread-self). RESOLVE_BENEATH isn't + // strictly needed, but just use it since we have it. + // + // NOTE: /proc/self is technically a magic-link (the contents of the + // symlink are generated dynamically), but it doesn't use + // nd_jump_link() so RESOLVE_NO_MAGICLINKS allows it. + // + // NOTE: We MUST NOT use RESOLVE_IN_ROOT here, as openat2File uses + // procSelfFdReadlink to clean up the returned f.Name() if we use + // RESOLVE_IN_ROOT (which would lead to an infinite recursion). + handle, err = openat2File(procRoot, threadSelf+subpath, &unix.OpenHow{ + Flags: unix.O_PATH | unix.O_NOFOLLOW | unix.O_CLOEXEC, + Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_MAGICLINKS, + }) + if err != nil { + // TODO: Once we bump the minimum Go version to 1.20, we can use + // multiple %w verbs for this wrapping. For now we need to use a + // compatibility shim for older Go versions. + //err = fmt.Errorf("%w: %w", errUnsafeProcfs, err) + return nil, nil, wrapBaseError(err, errUnsafeProcfs) + } + } else { + handle, err = openatFile(procRoot, threadSelf+subpath, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) + if err != nil { + // TODO: Once we bump the minimum Go version to 1.20, we can use + // multiple %w verbs for this wrapping. For now we need to use a + // compatibility shim for older Go versions. + //err = fmt.Errorf("%w: %w", errUnsafeProcfs, err) + return nil, nil, wrapBaseError(err, errUnsafeProcfs) + } + defer func() { + if Err != nil { + _ = handle.Close() + } + }() + // We can't detect bind-mounts of different parts of procfs on top of + // /proc (a-la RESOLVE_NO_XDEV), but we can at least be sure that we + // aren't on the wrong filesystem here. + if statfs, err := fstatfs(handle); err != nil { + return nil, nil, err + } else if statfs.Type != procSuperMagic { + return nil, nil, fmt.Errorf("%w: incorrect /proc/self/fd filesystem type 0x%x", errUnsafeProcfs, statfs.Type) + } + } + return handle, runtime.UnlockOSThread, nil +} + +// STATX_MNT_ID_UNIQUE is provided in golang.org/x/sys@v0.20.0, but in order to +// avoid bumping the requirement for a single constant we can just define it +// ourselves. +const STATX_MNT_ID_UNIQUE = 0x4000 + +var hasStatxMountId = sync_OnceValue(func() bool { + var ( + stx unix.Statx_t + // We don't care which mount ID we get. The kernel will give us the + // unique one if it is supported. + wantStxMask uint32 = STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID + ) + err := unix.Statx(-int(unix.EBADF), "/", 0, int(wantStxMask), &stx) + return err == nil && stx.Mask&wantStxMask != 0 +}) + +func getMountId(dir *os.File, path string) (uint64, error) { + // If we don't have statx(STATX_MNT_ID*) support, we can't do anything. + if !hasStatxMountId() { + return 0, nil + } + + var ( + stx unix.Statx_t + // We don't care which mount ID we get. The kernel will give us the + // unique one if it is supported. + wantStxMask uint32 = STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID + ) + + err := unix.Statx(int(dir.Fd()), path, unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW, int(wantStxMask), &stx) + if stx.Mask&wantStxMask == 0 { + // It's not a kernel limitation, for some reason we couldn't get a + // mount ID. Assume it's some kind of attack. + err = fmt.Errorf("%w: could not get mount id", errUnsafeProcfs) + } + if err != nil { + return 0, &os.PathError{Op: "statx(STATX_MNT_ID_...)", Path: dir.Name() + "/" + path, Err: err} + } + return stx.Mnt_id, nil +} + +func checkSymlinkOvermount(procRoot *os.File, dir *os.File, path string) error { + // Get the mntId of our procfs handle. + expectedMountId, err := getMountId(procRoot, "") + if err != nil { + return err + } + // Get the mntId of the target magic-link. + gotMountId, err := getMountId(dir, path) + if err != nil { + return err + } + // As long as the directory mount is alive, even with wrapping mount IDs, + // we would expect to see a different mount ID here. (Of course, if we're + // using unsafeHostProcRoot() then an attaker could change this after we + // did this check.) + if expectedMountId != gotMountId { + return fmt.Errorf("%w: symlink %s/%s has an overmount obscuring the real link (mount ids do not match %d != %d)", errUnsafeProcfs, dir.Name(), path, expectedMountId, gotMountId) + } + return nil +} + +func doRawProcSelfFdReadlink(procRoot *os.File, fd int) (string, error) { + fdPath := fmt.Sprintf("fd/%d", fd) + procFdLink, closer, err := procThreadSelf(procRoot, fdPath) + if err != nil { + return "", fmt.Errorf("get safe /proc/thread-self/%s handle: %w", fdPath, err) + } + defer procFdLink.Close() + defer closer() + + // Try to detect if there is a mount on top of the magic-link. Since we use the handle directly + // provide to the closure. If the closure uses the handle directly, this + // should be safe in general (a mount on top of the path afterwards would + // not affect the handle itself) and will definitely be safe if we are + // using privateProcRoot() (at least since Linux 5.12[1], when anonymous + // mount namespaces were completely isolated from external mounts including + // mount propagation events). + // + // [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts + // onto targets that reside on shared mounts"). + if err := checkSymlinkOvermount(procRoot, procFdLink, ""); err != nil { + return "", fmt.Errorf("check safety of /proc/thread-self/fd/%d magiclink: %w", fd, err) + } + + // readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See Linux commit + // 65cfc6722361 ("readlinkat(), fchownat() and fstatat() with empty + // relative pathnames"). + return readlinkatFile(procFdLink, "") +} + +func rawProcSelfFdReadlink(fd int) (string, error) { + procRoot, err := getProcRoot() + if err != nil { + return "", err + } + return doRawProcSelfFdReadlink(procRoot, fd) +} + +func procSelfFdReadlink(f *os.File) (string, error) { + return rawProcSelfFdReadlink(int(f.Fd())) +} + +var ( + errPossibleBreakout = errors.New("possible breakout detected") + errInvalidDirectory = errors.New("wandered into deleted directory") + errDeletedInode = errors.New("cannot verify path of deleted inode") +) + +func isDeadInode(file *os.File) error { + // If the nlink of a file drops to 0, there is an attacker deleting + // directories during our walk, which could result in weird /proc values. + // It's better to error out in this case. + stat, err := fstat(file) + if err != nil { + return fmt.Errorf("check for dead inode: %w", err) + } + if stat.Nlink == 0 { + err := errDeletedInode + if stat.Mode&unix.S_IFMT == unix.S_IFDIR { + err = errInvalidDirectory + } + return fmt.Errorf("%w %q", err, file.Name()) + } + return nil +} + +func checkProcSelfFdPath(path string, file *os.File) error { + if err := isDeadInode(file); err != nil { + return err + } + actualPath, err := procSelfFdReadlink(file) + if err != nil { + return fmt.Errorf("get path of handle: %w", err) + } + if actualPath != path { + return fmt.Errorf("%w: handle path %q doesn't match expected path %q", errPossibleBreakout, actualPath, path) + } + return nil +} + +// Test hooks used in the procfs tests to verify that the fallback logic works. +// See testing_mocks_linux_test.go and procfs_linux_test.go for more details. +var ( + hookForcePrivateProcRootOpenTree = hookDummyFile + hookForcePrivateProcRootOpenTreeAtRecursive = hookDummyFile + hookForceGetProcRootUnsafe = hookDummy + + hookForceProcSelfTask = hookDummy + hookForceProcSelf = hookDummy +) + +func hookDummy() bool { return false } +func hookDummyFile(_ *os.File) bool { return false } diff --git a/vendor/github.com/cyphar/filepath-securejoin/vfs.go b/vendor/github.com/cyphar/filepath-securejoin/vfs.go index a82a5eae11..36373f8c51 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/vfs.go +++ b/vendor/github.com/cyphar/filepath-securejoin/vfs.go @@ -1,4 +1,4 @@ -// Copyright (C) 2017 SUSE LLC. All rights reserved. +// Copyright (C) 2017-2024 SUSE LLC. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -10,19 +10,19 @@ import "os" // are several projects (umoci and go-mtree) that are using this sort of // interface. -// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is -// equivalent to using the standard os.* family of functions. This is mainly +// VFS is the minimal interface necessary to use [SecureJoinVFS]. A nil VFS is +// equivalent to using the standard [os].* family of functions. This is mainly // used for the purposes of mock testing, but also can be used to otherwise use -// SecureJoin with VFS-like system. +// [SecureJoinVFS] with VFS-like system. type VFS interface { - // Lstat returns a FileInfo describing the named file. If the file is a - // symbolic link, the returned FileInfo describes the symbolic link. Lstat - // makes no attempt to follow the link. These semantics are identical to - // os.Lstat. + // Lstat returns an [os.FileInfo] describing the named file. If the + // file is a symbolic link, the returned [os.FileInfo] describes the + // symbolic link. Lstat makes no attempt to follow the link. + // The semantics are identical to [os.Lstat]. Lstat(name string) (os.FileInfo, error) - // Readlink returns the destination of the named symbolic link. These - // semantics are identical to os.Readlink. + // Readlink returns the destination of the named symbolic link. + // The semantics are identical to [os.Readlink]. Readlink(name string) (string, error) } @@ -30,12 +30,6 @@ type VFS interface { // module. type osVFS struct{} -// Lstat returns a FileInfo describing the named file. If the file is a -// symbolic link, the returned FileInfo describes the symbolic link. Lstat -// makes no attempt to follow the link. These semantics are identical to -// os.Lstat. func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } -// Readlink returns the destination of the named symbolic link. These -// semantics are identical to os.Readlink. func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) } diff --git a/vendor/github.com/go-git/go-billy/v5/Makefile b/vendor/github.com/go-git/go-billy/v5/Makefile index 74dad8b491..3c95ddeaac 100644 --- a/vendor/github.com/go-git/go-billy/v5/Makefile +++ b/vendor/github.com/go-git/go-billy/v5/Makefile @@ -1,6 +1,7 @@ # Go parameters GOCMD = go GOTEST = $(GOCMD) test +WASIRUN_WRAPPER := $(CURDIR)/scripts/wasirun-wrapper .PHONY: test test: @@ -9,3 +10,9 @@ test: test-coverage: echo "" > $(COVERAGE_REPORT); \ $(GOTEST) -coverprofile=$(COVERAGE_REPORT) -coverpkg=./... -covermode=$(COVERAGE_MODE) ./... + +.PHONY: wasitest +wasitest: export GOARCH=wasm +wasitest: export GOOS=wasip1 +wasitest: + $(GOTEST) -exec $(WASIRUN_WRAPPER) ./... diff --git a/vendor/github.com/go-git/go-billy/v5/fs.go b/vendor/github.com/go-git/go-billy/v5/fs.go index a9efccdeb2..d86f9d8236 100644 --- a/vendor/github.com/go-git/go-billy/v5/fs.go +++ b/vendor/github.com/go-git/go-billy/v5/fs.go @@ -164,6 +164,8 @@ type File interface { // Name returns the name of the file as presented to Open. Name() string io.Writer + // TODO: Add io.WriterAt for v6 + // io.WriterAt io.Reader io.ReaderAt io.Seeker diff --git a/vendor/github.com/go-git/go-billy/v5/memfs/memory.go b/vendor/github.com/go-git/go-billy/v5/memfs/memory.go index dab73968b6..6cbd7d08ca 100644 --- a/vendor/github.com/go-git/go-billy/v5/memfs/memory.go +++ b/vendor/github.com/go-git/go-billy/v5/memfs/memory.go @@ -9,6 +9,7 @@ import ( "path/filepath" "sort" "strings" + "syscall" "time" "github.com/go-git/go-billy/v5" @@ -18,16 +19,19 @@ import ( const separator = filepath.Separator -// Memory a very convenient filesystem based on memory files +var errNotLink = errors.New("not a link") + +// Memory a very convenient filesystem based on memory files. type Memory struct { s *storage tempCount int } -//New returns a new Memory filesystem. +// New returns a new Memory filesystem. func New() billy.Filesystem { fs := &Memory{s: newStorage()} + fs.s.New("/", 0755|os.ModeDir, 0) return chroot.New(fs, string(separator)) } @@ -57,7 +61,9 @@ func (fs *Memory) OpenFile(filename string, flag int, perm os.FileMode) (billy.F } if target, isLink := fs.resolveLink(filename, f); isLink { - return fs.OpenFile(target, flag, perm) + if target != filename { + return fs.OpenFile(target, flag, perm) + } } } @@ -68,8 +74,6 @@ func (fs *Memory) OpenFile(filename string, flag int, perm os.FileMode) (billy.F return f.Duplicate(filename, perm, flag), nil } -var errNotLink = errors.New("not a link") - func (fs *Memory) resolveLink(fullpath string, f *file) (target string, isLink bool) { if !isSymlink(f.mode) { return fullpath, false @@ -131,8 +135,12 @@ func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (fs *Memory) ReadDir(path string) ([]os.FileInfo, error) { if f, has := fs.s.Get(path); has { if target, isLink := fs.resolveLink(path, f); isLink { - return fs.ReadDir(target) + if target != path { + return fs.ReadDir(target) + } } + } else { + return nil, &os.PathError{Op: "open", Path: path, Err: syscall.ENOENT} } var entries []os.FileInfo @@ -169,17 +177,19 @@ func (fs *Memory) Remove(filename string) error { return fs.s.Remove(filename) } +// Falls back to Go's filepath.Join, which works differently depending on the +// OS where the code is being executed. func (fs *Memory) Join(elem ...string) string { return filepath.Join(elem...) } func (fs *Memory) Symlink(target, link string) error { - _, err := fs.Stat(link) + _, err := fs.Lstat(link) if err == nil { return os.ErrExist } - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { return err } @@ -230,7 +240,7 @@ func (f *file) Read(b []byte) (int, error) { n, err := f.ReadAt(b, f.position) f.position += int64(n) - if err == io.EOF && n != 0 { + if errors.Is(err, io.EOF) && n != 0 { err = nil } @@ -269,6 +279,10 @@ func (f *file) Seek(offset int64, whence int) (int64, error) { } func (f *file) Write(p []byte) (int, error) { + return f.WriteAt(p, f.position) +} + +func (f *file) WriteAt(p []byte, off int64) (int, error) { if f.isClosed { return 0, os.ErrClosed } @@ -277,8 +291,8 @@ func (f *file) Write(p []byte) (int, error) { return 0, errors.New("write not supported") } - n, err := f.content.WriteAt(p, f.position) - f.position += int64(n) + n, err := f.content.WriteAt(p, off) + f.position = off + int64(n) return n, err } diff --git a/vendor/github.com/go-git/go-billy/v5/memfs/storage.go b/vendor/github.com/go-git/go-billy/v5/memfs/storage.go index e3c4e38bff..16b48ce002 100644 --- a/vendor/github.com/go-git/go-billy/v5/memfs/storage.go +++ b/vendor/github.com/go-git/go-billy/v5/memfs/storage.go @@ -6,6 +6,7 @@ import ( "io" "os" "path/filepath" + "strings" "sync" ) @@ -112,7 +113,7 @@ func (s *storage) Rename(from, to string) error { move := [][2]string{{from, to}} for pathFrom := range s.files { - if pathFrom == from || !filepath.HasPrefix(pathFrom, from) { + if pathFrom == from || !strings.HasPrefix(pathFrom, from) { continue } diff --git a/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go b/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go index b4b6dbc07a..c0a6109901 100644 --- a/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go +++ b/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go @@ -246,6 +246,10 @@ func (fs *BoundOS) insideBaseDir(filename string) (bool, error) { // a dir that is within the fs.baseDir, by first evaluating any symlinks // that either filename or fs.baseDir may contain. func (fs *BoundOS) insideBaseDirEval(filename string) (bool, error) { + // "/" contains all others. + if fs.baseDir == "/" { + return true, nil + } dir, err := filepath.EvalSymlinks(filepath.Dir(filename)) if dir == "" || os.IsNotExist(err) { dir = filepath.Dir(filename) @@ -255,7 +259,7 @@ func (fs *BoundOS) insideBaseDirEval(filename string) (bool, error) { wd = fs.baseDir } if filename != wd && dir != wd && !strings.HasPrefix(dir, wd+string(filepath.Separator)) { - return false, fmt.Errorf("path outside base dir") + return false, fmt.Errorf("%q: path outside base dir %q: %w", filename, fs.baseDir, os.ErrNotExist) } return true, nil } diff --git a/vendor/github.com/go-git/go-billy/v5/osfs/os_posix.go b/vendor/github.com/go-git/go-billy/v5/osfs/os_posix.go index d834a1145a..6fb8273f17 100644 --- a/vendor/github.com/go-git/go-billy/v5/osfs/os_posix.go +++ b/vendor/github.com/go-git/go-billy/v5/osfs/os_posix.go @@ -1,5 +1,5 @@ -//go:build !plan9 && !windows && !js -// +build !plan9,!windows,!js +//go:build !plan9 && !windows && !wasm +// +build !plan9,!windows,!wasm package osfs diff --git a/vendor/github.com/go-git/go-billy/v5/osfs/os_wasip1.go b/vendor/github.com/go-git/go-billy/v5/osfs/os_wasip1.go new file mode 100644 index 0000000000..79e6e33192 --- /dev/null +++ b/vendor/github.com/go-git/go-billy/v5/osfs/os_wasip1.go @@ -0,0 +1,34 @@ +//go:build wasip1 +// +build wasip1 + +package osfs + +import ( + "os" + "syscall" +) + +func (f *file) Lock() error { + f.m.Lock() + defer f.m.Unlock() + return nil +} + +func (f *file) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + return nil +} + +func rename(from, to string) error { + return os.Rename(from, to) +} + +// umask sets umask to a new value, and returns a func which allows the +// caller to reset it back to what it was originally. +func umask(new int) func() { + old := syscall.Umask(new) + return func() { + syscall.Umask(old) + } +} diff --git a/vendor/github.com/go-git/go-billy/v5/util/util.go b/vendor/github.com/go-git/go-billy/v5/util/util.go index 5c77128c3c..2cdd832c73 100644 --- a/vendor/github.com/go-git/go-billy/v5/util/util.go +++ b/vendor/github.com/go-git/go-billy/v5/util/util.go @@ -1,6 +1,7 @@ package util import ( + "errors" "io" "os" "path/filepath" @@ -33,14 +34,14 @@ func removeAll(fs billy.Basic, path string) error { // Simple case: if Remove works, we're done. err := fs.Remove(path) - if err == nil || os.IsNotExist(err) { + if err == nil || errors.Is(err, os.ErrNotExist) { return nil } // Otherwise, is this a directory we need to recurse into? dir, serr := fs.Stat(path) if serr != nil { - if os.IsNotExist(serr) { + if errors.Is(serr, os.ErrNotExist) { return nil } @@ -60,7 +61,7 @@ func removeAll(fs billy.Basic, path string) error { // Directory. fis, err := dirfs.ReadDir(path) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { // Race. It was deleted between the Lstat and Open. // Return nil per RemoveAll's docs. return nil @@ -81,7 +82,7 @@ func removeAll(fs billy.Basic, path string) error { // Remove directory. err1 := fs.Remove(path) - if err1 == nil || os.IsNotExist(err1) { + if err1 == nil || errors.Is(err1, os.ErrNotExist) { return nil } @@ -96,22 +97,26 @@ func removeAll(fs billy.Basic, path string) error { // WriteFile writes data to a file named by filename in the given filesystem. // If the file does not exist, WriteFile creates it with permissions perm; // otherwise WriteFile truncates it before writing. -func WriteFile(fs billy.Basic, filename string, data []byte, perm os.FileMode) error { +func WriteFile(fs billy.Basic, filename string, data []byte, perm os.FileMode) (err error) { f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { return err } + defer func() { + if f != nil { + err1 := f.Close() + if err == nil { + err = err1 + } + } + }() n, err := f.Write(data) if err == nil && n < len(data) { err = io.ErrShortWrite } - if err1 := f.Close(); err == nil { - err = err1 - } - - return err + return nil } // Random number state. @@ -154,7 +159,7 @@ func TempFile(fs billy.Basic, dir, prefix string) (f billy.File, err error) { for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextSuffix()) f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) - if os.IsExist(err) { + if errors.Is(err, os.ErrExist) { if nconflict++; nconflict > 10 { randmu.Lock() rand = reseed() @@ -185,7 +190,7 @@ func TempDir(fs billy.Dir, dir, prefix string) (name string, err error) { for i := 0; i < 10000; i++ { try := filepath.Join(dir, prefix+nextSuffix()) err = fs.MkdirAll(try, 0700) - if os.IsExist(err) { + if errors.Is(err, os.ErrExist) { if nconflict++; nconflict > 10 { randmu.Lock() rand = reseed() @@ -193,8 +198,8 @@ func TempDir(fs billy.Dir, dir, prefix string) (name string, err error) { } continue } - if os.IsNotExist(err) { - if _, err := os.Stat(dir); os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { + if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { return "", err } } @@ -272,7 +277,7 @@ func ReadFile(fs billy.Basic, name string) ([]byte, error) { data = data[:len(data)+n] if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { err = nil } diff --git a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md index ff0c22c896..ba1fb90ac5 100644 --- a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md +++ b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md @@ -11,7 +11,7 @@ compatibility status with go-git. | `init` | `--bare` | ✅ | | | | `init` | `--template`
`--separate-git-dir`
`--shared` | ❌ | | | | `clone` | | ✅ | | - [PlainClone](_examples/clone/main.go) | -| `clone` | Authentication:
- none
- access token
- username + password
- ssh | ✅ | | - [clone ssh](_examples/clone/auth/ssh/main.go)
- [clone access token](_examples/clone/auth/basic/access_token/main.go)
- [clone user + password](_examples/clone/auth/basic/username_password/main.go) | +| `clone` | Authentication:
- none
- access token
- username + password
- ssh | ✅ | | - [clone ssh (private_key)](_examples/clone/auth/ssh/private_key/main.go)
- [clone ssh (ssh_agent)](_examples/clone/auth/ssh/ssh_agent/main.go)
- [clone access token](_examples/clone/auth/basic/access_token/main.go)
- [clone user + password](_examples/clone/auth/basic/username_password/main.go) | | `clone` | `--progress`
`--single-branch`
`--depth`
`--origin`
`--recurse-submodules`
`--shared` | ✅ | | - [recurse submodules](_examples/clone/main.go)
- [progress](_examples/progress/main.go) | ## Basic snapshotting @@ -34,6 +34,7 @@ compatibility status with go-git. | `merge` | | ⚠️ (partial) | Fast-forward only | | | `mergetool` | | ❌ | | | | `stash` | | ❌ | | | +| `sparse-checkout` | | ✅ | | - [sparse-checkout](_examples/sparse-checkout/main.go) | | `tag` | | ✅ | | - [tag](_examples/tag/main.go)
- [tag create and push](_examples/tag-create-push/main.go) | ## Sharing and updating projects diff --git a/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md b/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md index fce25328a7..a5b01823bf 100644 --- a/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md +++ b/vendor/github.com/go-git/go-git/v5/CONTRIBUTING.md @@ -31,6 +31,13 @@ In order for a PR to be accepted it needs to pass a list of requirements: - If the PR is a new feature, it has to come with a suite of unit tests, that tests the new functionality. - In any case, all the PRs have to pass the personal evaluation of at least one of the maintainers of go-git. +### Branches + +The `master` branch is currently used for maintaining the `v5` major release only. The accepted changes would +be dependency bumps, bug fixes and small changes that aren't needed for `v6`. New development should target the +`v6-exp` branch, and if agreed with at least one go-git maintainer, it can be back ported to `v5` by creating +a new PR that targets `master`. + ### Format of the commit message Every commit message should describe what was changed, under which context and, if applicable, the GitHub issue it relates to: diff --git a/vendor/github.com/go-git/go-git/v5/blame.go b/vendor/github.com/go-git/go-git/v5/blame.go index 2a877dcdf9..e3cb39aec3 100644 --- a/vendor/github.com/go-git/go-git/v5/blame.go +++ b/vendor/github.com/go-git/go-git/v5/blame.go @@ -97,13 +97,10 @@ func Blame(c *object.Commit, path string) (*BlameResult, error) { if err != nil { return nil, err } - if finished == true { + if finished { break } } - if err != nil { - return nil, err - } b.lineToCommit = make([]*object.Commit, finalLength) for i := range needsMap { @@ -309,8 +306,8 @@ func (b *blame) addBlames(curItems []*queueItem) (bool, error) { for h := range hunks { hLines := countLines(hunks[h].Text) for hl := 0; hl < hLines; hl++ { - switch { - case hunks[h].Type == diffmatchpatch.DiffEqual: + switch hunks[h].Type { + case diffmatchpatch.DiffEqual: prevl++ curl++ if curl == curItem.NeedsMap[need].Cur { @@ -322,7 +319,7 @@ func (b *blame) addBlames(curItems []*queueItem) (bool, error) { break out } } - case hunks[h].Type == diffmatchpatch.DiffInsert: + case diffmatchpatch.DiffInsert: curl++ if curl == curItem.NeedsMap[need].Cur { // the line we want is added, it may have been added here (or by another parent), skip it for now @@ -331,7 +328,7 @@ func (b *blame) addBlames(curItems []*queueItem) (bool, error) { break out } } - case hunks[h].Type == diffmatchpatch.DiffDelete: + case diffmatchpatch.DiffDelete: prevl += hLines continue out default: diff --git a/vendor/github.com/go-git/go-git/v5/config/config.go b/vendor/github.com/go-git/go-git/v5/config/config.go index 6d41c15dcd..33f6e37d26 100644 --- a/vendor/github.com/go-git/go-git/v5/config/config.go +++ b/vendor/github.com/go-git/go-git/v5/config/config.go @@ -252,6 +252,7 @@ const ( extensionsSection = "extensions" fetchKey = "fetch" urlKey = "url" + pushurlKey = "pushurl" bareKey = "bare" worktreeKey = "worktree" commentCharKey = "commentChar" @@ -633,6 +634,7 @@ func (c *RemoteConfig) unmarshal(s *format.Subsection) error { c.Name = c.raw.Name c.URLs = append([]string(nil), c.raw.Options.GetAll(urlKey)...) + c.URLs = append(c.URLs, c.raw.Options.GetAll(pushurlKey)...) c.Fetch = fetch c.Mirror = c.raw.Options.Get(mirrorKey) == "true" diff --git a/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go b/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go index c46c21b795..2444f33ec2 100644 --- a/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go +++ b/vendor/github.com/go-git/go-git/v5/internal/revision/scanner.go @@ -43,6 +43,11 @@ func tokenizeExpression(ch rune, tokenType token, check runeCategoryValidator, r return tokenType, string(data), nil } +// maxRevisionLength holds the maximum length that will be parsed for a +// revision. Git itself doesn't enforce a max length, but rather leans on +// the OS to enforce it via its ARG_MAX. +const maxRevisionLength = 128 * 1024 // 128kb + var zeroRune = rune(0) // scanner represents a lexical scanner. @@ -52,7 +57,7 @@ type scanner struct { // newScanner returns a new instance of scanner. func newScanner(r io.Reader) *scanner { - return &scanner{r: bufio.NewReader(r)} + return &scanner{r: bufio.NewReader(io.LimitReader(r, maxRevisionLength))} } // Scan extracts tokens and their strings counterpart diff --git a/vendor/github.com/go-git/go-git/v5/options.go b/vendor/github.com/go-git/go-git/v5/options.go index d7776dad5e..3cd0f952c3 100644 --- a/vendor/github.com/go-git/go-git/v5/options.go +++ b/vendor/github.com/go-git/go-git/v5/options.go @@ -416,6 +416,9 @@ type ResetOptions struct { // the index (resetting it to the tree of Commit) and the working tree // depending on Mode. If empty MixedReset is used. Mode ResetMode + // Files, if not empty will constrain the reseting the index to only files + // specified in this list. + Files []string } // Validate validates the fields and sets the default values. @@ -790,3 +793,26 @@ type PlainInitOptions struct { // Validate validates the fields and sets the default values. func (o *PlainInitOptions) Validate() error { return nil } + +var ( + ErrNoRestorePaths = errors.New("you must specify path(s) to restore") +) + +// RestoreOptions describes how a restore should be performed. +type RestoreOptions struct { + // Marks to restore the content in the index + Staged bool + // Marks to restore the content of the working tree + Worktree bool + // List of file paths that will be restored + Files []string +} + +// Validate validates the fields and sets the default values. +func (o *RestoreOptions) Validate() error { + if len(o.Files) == 0 { + return ErrNoRestorePaths + } + + return nil +} diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go index aca5d0dbd2..92df5a3de7 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/gitignore/dir.go @@ -64,6 +64,10 @@ func ReadPatterns(fs billy.Filesystem, path []string) (ps []Pattern, err error) for _, fi := range fis { if fi.IsDir() && fi.Name() != gitDir { + if NewMatcher(ps).Match(append(path, fi.Name()), true) { + continue + } + var subps []Pattern subps, err = ReadPatterns(fs, append(path, fi.Name())) if err != nil { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go index 6778cf74ec..fc25d37022 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go @@ -24,8 +24,8 @@ var ( // ErrInvalidChecksum is returned by Decode if the SHA1 hash mismatch with // the read content ErrInvalidChecksum = errors.New("invalid checksum") - - errUnknownExtension = errors.New("unknown extension") + // ErrUnknownExtension is returned when an index extension is encountered that is considered mandatory + ErrUnknownExtension = errors.New("unknown extension") ) const ( @@ -39,6 +39,7 @@ const ( // A Decoder reads and decodes index files from an input stream. type Decoder struct { + buf *bufio.Reader r io.Reader hash hash.Hash lastEntry *Entry @@ -49,8 +50,10 @@ type Decoder struct { // NewDecoder returns a new decoder that reads from r. func NewDecoder(r io.Reader) *Decoder { h := hash.New(hash.CryptoType) + buf := bufio.NewReader(r) return &Decoder{ - r: io.TeeReader(r, h), + buf: buf, + r: io.TeeReader(buf, h), hash: h, extReader: bufio.NewReader(nil), } @@ -210,71 +213,75 @@ func (d *Decoder) readExtensions(idx *Index) error { // count that they are not supported by jgit or libgit var expected []byte + var peeked []byte var err error - var header [4]byte + // we should always be able to peek for 4 bytes (header) + 4 bytes (extlen) + final hash + // if this fails, we know that we're at the end of the index + peekLen := 4 + 4 + d.hash.Size() + for { expected = d.hash.Sum(nil) - - var n int - if n, err = io.ReadFull(d.r, header[:]); err != nil { - if n == 0 { - err = io.EOF - } - + peeked, err = d.buf.Peek(peekLen) + if len(peeked) < peekLen { + // there can't be an extension at this point, so let's bail out break } + if err != nil { + return err + } - err = d.readExtension(idx, header[:]) + err = d.readExtension(idx) if err != nil { - break + return err } } - if err != errUnknownExtension { + return d.readChecksum(expected) +} + +func (d *Decoder) readExtension(idx *Index) error { + var header [4]byte + + if _, err := io.ReadFull(d.r, header[:]); err != nil { return err } - return d.readChecksum(expected, header) -} + r, err := d.getExtensionReader() + if err != nil { + return err + } -func (d *Decoder) readExtension(idx *Index, header []byte) error { switch { - case bytes.Equal(header, treeExtSignature): - r, err := d.getExtensionReader() - if err != nil { - return err - } - + case bytes.Equal(header[:], treeExtSignature): idx.Cache = &Tree{} d := &treeExtensionDecoder{r} if err := d.Decode(idx.Cache); err != nil { return err } - case bytes.Equal(header, resolveUndoExtSignature): - r, err := d.getExtensionReader() - if err != nil { - return err - } - + case bytes.Equal(header[:], resolveUndoExtSignature): idx.ResolveUndo = &ResolveUndo{} d := &resolveUndoDecoder{r} if err := d.Decode(idx.ResolveUndo); err != nil { return err } - case bytes.Equal(header, endOfIndexEntryExtSignature): - r, err := d.getExtensionReader() - if err != nil { - return err - } - + case bytes.Equal(header[:], endOfIndexEntryExtSignature): idx.EndOfIndexEntry = &EndOfIndexEntry{} d := &endOfIndexEntryDecoder{r} if err := d.Decode(idx.EndOfIndexEntry); err != nil { return err } default: - return errUnknownExtension + // See https://git-scm.com/docs/index-format, which says: + // If the first byte is 'A'..'Z' the extension is optional and can be ignored. + if header[0] < 'A' || header[0] > 'Z' { + return ErrUnknownExtension + } + + d := &unknownExtensionDecoder{r} + if err := d.Decode(); err != nil { + return err + } } return nil @@ -290,11 +297,10 @@ func (d *Decoder) getExtensionReader() (*bufio.Reader, error) { return d.extReader, nil } -func (d *Decoder) readChecksum(expected []byte, alreadyRead [4]byte) error { +func (d *Decoder) readChecksum(expected []byte) error { var h plumbing.Hash - copy(h[:4], alreadyRead[:]) - if _, err := io.ReadFull(d.r, h[4:]); err != nil { + if _, err := io.ReadFull(d.r, h[:]); err != nil { return err } @@ -476,3 +482,22 @@ func (d *endOfIndexEntryDecoder) Decode(e *EndOfIndexEntry) error { _, err = io.ReadFull(d.r, e.Hash[:]) return err } + +type unknownExtensionDecoder struct { + r *bufio.Reader +} + +func (d *unknownExtensionDecoder) Decode() error { + var buf [1024]byte + + for { + _, err := d.r.Read(buf[:]) + if err == io.EOF { + break + } + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go index fa2d814454..c232e03231 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go @@ -3,8 +3,11 @@ package index import ( "bytes" "errors" + "fmt" "io" + "path" "sort" + "strings" "time" "github.com/go-git/go-git/v5/plumbing/hash" @@ -13,7 +16,7 @@ import ( var ( // EncodeVersionSupported is the range of supported index versions - EncodeVersionSupported uint32 = 3 + EncodeVersionSupported uint32 = 4 // ErrInvalidTimestamp is returned by Encode if a Index with a Entry with // negative timestamp values @@ -22,20 +25,25 @@ var ( // An Encoder writes an Index to an output stream. type Encoder struct { - w io.Writer - hash hash.Hash + w io.Writer + hash hash.Hash + lastEntry *Entry } // NewEncoder returns a new encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { h := hash.New(hash.CryptoType) mw := io.MultiWriter(w, h) - return &Encoder{mw, h} + return &Encoder{mw, h, nil} } // Encode writes the Index to the stream of the encoder. func (e *Encoder) Encode(idx *Index) error { - // TODO: support v4 + return e.encode(idx, true) +} + +func (e *Encoder) encode(idx *Index, footer bool) error { + // TODO: support extensions if idx.Version > EncodeVersionSupported { return ErrUnsupportedVersion @@ -49,7 +57,10 @@ func (e *Encoder) Encode(idx *Index) error { return err } - return e.encodeFooter() + if footer { + return e.encodeFooter() + } + return nil } func (e *Encoder) encodeHeader(idx *Index) error { @@ -64,7 +75,7 @@ func (e *Encoder) encodeEntries(idx *Index) error { sort.Sort(byName(idx.Entries)) for _, entry := range idx.Entries { - if err := e.encodeEntry(entry); err != nil { + if err := e.encodeEntry(idx, entry); err != nil { return err } entryLength := entryHeaderLength @@ -73,7 +84,7 @@ func (e *Encoder) encodeEntries(idx *Index) error { } wrote := entryLength + len(entry.Name) - if err := e.padEntry(wrote); err != nil { + if err := e.padEntry(idx, wrote); err != nil { return err } } @@ -81,7 +92,7 @@ func (e *Encoder) encodeEntries(idx *Index) error { return nil } -func (e *Encoder) encodeEntry(entry *Entry) error { +func (e *Encoder) encodeEntry(idx *Index, entry *Entry) error { sec, nsec, err := e.timeToUint32(&entry.CreatedAt) if err != nil { return err @@ -132,9 +143,68 @@ func (e *Encoder) encodeEntry(entry *Entry) error { return err } + switch idx.Version { + case 2, 3: + err = e.encodeEntryName(entry) + case 4: + err = e.encodeEntryNameV4(entry) + default: + err = ErrUnsupportedVersion + } + + return err +} + +func (e *Encoder) encodeEntryName(entry *Entry) error { return binary.Write(e.w, []byte(entry.Name)) } +func (e *Encoder) encodeEntryNameV4(entry *Entry) error { + name := entry.Name + l := 0 + if e.lastEntry != nil { + dir := path.Dir(e.lastEntry.Name) + "/" + if strings.HasPrefix(entry.Name, dir) { + l = len(e.lastEntry.Name) - len(dir) + name = strings.TrimPrefix(entry.Name, dir) + } else { + l = len(e.lastEntry.Name) + } + } + + e.lastEntry = entry + + err := binary.WriteVariableWidthInt(e.w, int64(l)) + if err != nil { + return err + } + + return binary.Write(e.w, []byte(name+string('\x00'))) +} + +func (e *Encoder) encodeRawExtension(signature string, data []byte) error { + if len(signature) != 4 { + return fmt.Errorf("invalid signature length") + } + + _, err := e.w.Write([]byte(signature)) + if err != nil { + return err + } + + err = binary.WriteUint32(e.w, uint32(len(data))) + if err != nil { + return err + } + + _, err = e.w.Write(data) + if err != nil { + return err + } + + return nil +} + func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) { if t.IsZero() { return 0, 0, nil @@ -147,7 +217,11 @@ func (e *Encoder) timeToUint32(t *time.Time) (uint32, uint32, error) { return uint32(t.Unix()), uint32(t.Nanosecond()), nil } -func (e *Encoder) padEntry(wrote int) error { +func (e *Encoder) padEntry(idx *Index, wrote int) error { + if idx.Version == 4 { + return nil + } + padLen := 8 - wrote%8 _, err := e.w.Write(bytes.Repeat([]byte{'\x00'}, padLen)) diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go index 07a61120e5..a60ec0b24d 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/delta_index.go @@ -32,19 +32,17 @@ func (idx *deltaIndex) findMatch(src, tgt []byte, tgtOffset int) (srcOffset, l i return 0, -1 } - if len(tgt) >= tgtOffset+s && len(src) >= blksz { - h := hashBlock(tgt, tgtOffset) - tIdx := h & idx.mask - eIdx := idx.table[tIdx] - if eIdx != 0 { - srcOffset = idx.entries[eIdx] - } else { - return - } - - l = matchLength(src, tgt, tgtOffset, srcOffset) + h := hashBlock(tgt, tgtOffset) + tIdx := h & idx.mask + eIdx := idx.table[tIdx] + if eIdx == 0 { + return } + srcOffset = idx.entries[eIdx] + + l = matchLength(src, tgt, tgtOffset, srcOffset) + return } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go index 960769c7c8..a9c6b9b56f 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go @@ -26,6 +26,13 @@ var ( const ( payload = 0x7f // 0111 1111 continuation = 0x80 // 1000 0000 + + // maxPatchPreemptionSize defines what is the max size of bytes to be + // premptively made available for a patch operation. + maxPatchPreemptionSize uint = 65536 + + // minDeltaSize defines the smallest size for a delta. + minDeltaSize = 4 ) type offset struct { @@ -86,9 +93,13 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) { } // PatchDelta returns the result of applying the modification deltas in delta to src. -// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command +// An error will be returned if delta is corrupted (ErrInvalidDelta) or an action command // is not copy from source or copy from delta (ErrDeltaCmd). func PatchDelta(src, delta []byte) ([]byte, error) { + if len(src) == 0 || len(delta) < minDeltaSize { + return nil, ErrInvalidDelta + } + b := &bytes.Buffer{} if err := patchDelta(b, src, delta); err != nil { return nil, err @@ -239,7 +250,9 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { remainingTargetSz := targetSz var cmd byte - dst.Grow(int(targetSz)) + + growSz := min(targetSz, maxPatchPreemptionSize) + dst.Grow(int(growSz)) for { if len(delta) == 0 { return ErrInvalidDelta @@ -403,6 +416,10 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader, // This must be called twice on the delta data buffer, first to get the // expected source buffer size, and again to get the target buffer size. func decodeLEB128(input []byte) (uint, []byte) { + if len(input) == 0 { + return 0, input + } + var num, sz uint var b byte for { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go index fbb137de06..706d984ee0 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go @@ -140,6 +140,8 @@ func asciiHexToByte(b byte) (byte, error) { return b - '0', nil case b >= 'a' && b <= 'f': return b - 'a' + 10, nil + case b >= 'A' && b <= 'F': + return b - 'A' + 10, nil default: return 0, ErrInvalidPktLen } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go index 91cf371f0c..f9c3d306bd 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/signature.go @@ -19,6 +19,7 @@ var ( // a PKCS#7 (S/MIME) signature. x509SignatureFormat = signatureFormat{ []byte("-----BEGIN CERTIFICATE-----"), + []byte("-----BEGIN SIGNED MESSAGE-----"), } // sshSignatureFormat is the format of an SSH signature. diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go index 0fd0e51398..2e1b789156 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go @@ -295,6 +295,7 @@ func (s TreeEntrySorter) Swap(i, j int) { } // Encode transforms a Tree into a plumbing.EncodedObject. +// The tree entries must be sorted by name. func (t *Tree) Encode(o plumbing.EncodedObject) (err error) { o.SetType(plumbing.TreeObject) w, err := o.Writer() diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go new file mode 100644 index 0000000000..145fc711ca --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/filter.go @@ -0,0 +1,76 @@ +package packp + +import ( + "errors" + "fmt" + "github.com/go-git/go-git/v5/plumbing" + "net/url" + "strings" +) + +var ErrUnsupportedObjectFilterType = errors.New("unsupported object filter type") + +// Filter values enable the partial clone capability which causes +// the server to omit objects that match the filter. +// +// See [Git's documentation] for more details. +// +// [Git's documentation]: https://github.com/git/git/blob/e02ecfcc534e2021aae29077a958dd11c3897e4c/Documentation/rev-list-options.txt#L948 +type Filter string + +type BlobLimitPrefix string + +const ( + BlobLimitPrefixNone BlobLimitPrefix = "" + BlobLimitPrefixKibi BlobLimitPrefix = "k" + BlobLimitPrefixMebi BlobLimitPrefix = "m" + BlobLimitPrefixGibi BlobLimitPrefix = "g" +) + +// FilterBlobNone omits all blobs. +func FilterBlobNone() Filter { + return "blob:none" +} + +// FilterBlobLimit omits blobs of size at least n bytes (when prefix is +// BlobLimitPrefixNone), n kibibytes (when prefix is BlobLimitPrefixKibi), +// n mebibytes (when prefix is BlobLimitPrefixMebi) or n gibibytes (when +// prefix is BlobLimitPrefixGibi). n can be zero, in which case all blobs +// will be omitted. +func FilterBlobLimit(n uint64, prefix BlobLimitPrefix) Filter { + return Filter(fmt.Sprintf("blob:limit=%d%s", n, prefix)) +} + +// FilterTreeDepth omits all blobs and trees whose depth from the root tree +// is larger or equal to depth. +func FilterTreeDepth(depth uint64) Filter { + return Filter(fmt.Sprintf("tree:%d", depth)) +} + +// FilterObjectType omits all objects which are not of the requested type t. +// Supported types are TagObject, CommitObject, TreeObject and BlobObject. +func FilterObjectType(t plumbing.ObjectType) (Filter, error) { + switch t { + case plumbing.TagObject: + fallthrough + case plumbing.CommitObject: + fallthrough + case plumbing.TreeObject: + fallthrough + case plumbing.BlobObject: + return Filter(fmt.Sprintf("object:type=%s", t.String())), nil + default: + return "", fmt.Errorf("%w: %s", ErrUnsupportedObjectFilterType, t.String()) + } +} + +// FilterCombine combines multiple Filter values together. +func FilterCombine(filters ...Filter) Filter { + var escapedFilters []string + + for _, filter := range filters { + escapedFilters = append(escapedFilters, url.QueryEscape(string(filter))) + } + + return Filter(fmt.Sprintf("combine:%s", strings.Join(escapedFilters, "+"))) +} diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go index 0116f962ef..01d95a3aba 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband/demux.go @@ -114,7 +114,7 @@ func (d *Demuxer) nextPackData() ([]byte, error) { size := len(content) if size == 0 { - return nil, nil + return nil, io.EOF } else if size > d.max { return nil, ErrMaxPackedExceeded } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go index a9ddb538b2..d760ad6609 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/srvresp.go @@ -120,6 +120,9 @@ func (r *ServerResponse) decodeACKLine(line []byte) error { } sp := bytes.Index(line, []byte(" ")) + if sp+41 > len(line) { + return fmt.Errorf("malformed ACK %q", line) + } h := plumbing.NewHash(string(line[sp+1 : sp+41])) r.ACKs = append(r.ACKs, h) return nil diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go index 344f8c7e3a..ef4e08a10a 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq.go @@ -17,6 +17,7 @@ type UploadRequest struct { Wants []plumbing.Hash Shallows []plumbing.Hash Depth Depth + Filter Filter } // Depth values stores the desired depth of the requested packfile: see diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go index c451e23164..8b19c0f674 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/ulreq_encode.go @@ -132,6 +132,17 @@ func (e *ulReqEncoder) encodeDepth() stateFn { return nil } + return e.encodeFilter +} + +func (e *ulReqEncoder) encodeFilter() stateFn { + if filter := e.data.Filter; filter != "" { + if err := e.pe.Encodef("filter %s\n", filter); err != nil { + e.err = fmt.Errorf("encoding filter %s: %s", filter, err) + return nil + } + } + return e.encodeFlush } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/reference.go b/vendor/github.com/go-git/go-git/v5/plumbing/reference.go index ddba930292..4daa341649 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/reference.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/reference.go @@ -188,7 +188,7 @@ func (r ReferenceName) Validate() error { isBranch := r.IsBranch() isTag := r.IsTag() - for _, part := range parts { + for i, part := range parts { // rule 6 if len(part) == 0 { return ErrInvalidReferenceName @@ -205,7 +205,7 @@ func (r ReferenceName) Validate() error { return ErrInvalidReferenceName } - if (isBranch || isTag) && strings.HasPrefix(part, "-") { // branches & tags can't start with - + if (isBranch || isTag) && strings.HasPrefix(part, "-") && (i == 2) { // branches & tags can't start with - return ErrInvalidReferenceName } } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go index b05437fbfc..fae1aa98ca 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go @@ -19,6 +19,7 @@ import ( "fmt" "io" "net/url" + "path/filepath" "strconv" "strings" @@ -295,7 +296,11 @@ func parseFile(endpoint string) (*Endpoint, bool) { return nil, false } - path := endpoint + path, err := filepath.Abs(endpoint) + if err != nil { + return nil, false + } + return &Endpoint{ Protocol: "file", Path: path, diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go index 38714e2ad1..d921d0a5a4 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strings" "github.com/go-git/go-git/v5/plumbing/transport" @@ -95,7 +96,23 @@ func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.Auth } } - return &command{cmd: execabs.Command(cmd, ep.Path)}, nil + return &command{cmd: execabs.Command(cmd, adjustPathForWindows(ep.Path))}, nil +} + +func isDriveLetter(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + +// On Windows, the path that results from a file: URL has a leading slash. This +// has to be removed if there's a drive letter +func adjustPathForWindows(p string) string { + if runtime.GOOS != "windows" { + return p + } + if len(p) >= 3 && p[0] == '/' && isDriveLetter(p[1]) && p[2] == ':' { + return p[1:] + } + return p } type command struct { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go index 1c4ceee68d..120008db1c 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go @@ -430,11 +430,11 @@ func NewErr(r *http.Response) error { switch r.StatusCode { case http.StatusUnauthorized: - return transport.ErrAuthenticationRequired + return fmt.Errorf("%w: %s", transport.ErrAuthenticationRequired, reason) case http.StatusForbidden: - return transport.ErrAuthorizationFailed + return fmt.Errorf("%w: %s", transport.ErrAuthorizationFailed, reason) case http.StatusNotFound: - return transport.ErrRepositoryNotFound + return fmt.Errorf("%w: %s", transport.ErrRepositoryNotFound, reason) } return plumbing.NewUnexpectedError(&Err{r, reason}) diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go index e7e2b075e5..f03a91c6d1 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/loader.go @@ -40,8 +40,16 @@ func (l *fsLoader) Load(ep *transport.Endpoint) (storer.Storer, error) { return nil, err } - if _, err := fs.Stat("config"); err != nil { - return nil, transport.ErrRepositoryNotFound + var bare bool + if _, err := fs.Stat("config"); err == nil { + bare = true + } + + if !bare { + // do not use git.GitDirName due to import cycle + if _, err := fs.Stat(".git"); err != nil { + return nil, transport.ErrRepositoryNotFound + } } return filesystem.NewStorage(fs, cache.NewObjectLRUDefault()), nil diff --git a/vendor/github.com/go-git/go-git/v5/remote.go b/vendor/github.com/go-git/go-git/v5/remote.go index 7cc0db9b7d..e2c734e751 100644 --- a/vendor/github.com/go-git/go-git/v5/remote.go +++ b/vendor/github.com/go-git/go-git/v5/remote.go @@ -9,6 +9,7 @@ import ( "time" "github.com/go-git/go-billy/v5/osfs" + "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/internal/url" "github.com/go-git/go-git/v5/plumbing" @@ -82,7 +83,7 @@ func (r *Remote) String() string { var fetch, push string if len(r.c.URLs) > 0 { fetch = r.c.URLs[0] - push = r.c.URLs[0] + push = r.c.URLs[len(r.c.URLs)-1] } return fmt.Sprintf("%s\t%s (fetch)\n%[1]s\t%[3]s (push)", r.c.Name, fetch, push) @@ -109,8 +110,8 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) { return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name) } - if o.RemoteURL == "" { - o.RemoteURL = r.c.URLs[0] + if o.RemoteURL == "" && len(r.c.URLs) > 0 { + o.RemoteURL = r.c.URLs[len(r.c.URLs)-1] } s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions) @@ -491,7 +492,18 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen } if !updated && !updatedPrune { - return remoteRefs, NoErrAlreadyUpToDate + // No references updated, but may have fetched new objects, check if we now have any of our wants + for _, hash := range req.Wants { + exists, _ := objectExists(r.s, hash) + if exists { + updated = true + break + } + } + + if !updated { + return remoteRefs, NoErrAlreadyUpToDate + } } return remoteRefs, nil @@ -878,17 +890,12 @@ func getHavesFromRef( return nil } - // No need to load the commit if we know the remote already - // has this hash. - if remoteRefs[h] { - haves[h] = true - return nil - } - commit, err := object.GetCommit(s, h) if err != nil { - // Ignore the error if this isn't a commit. - haves[ref.Hash()] = true + if !errors.Is(err, plumbing.ErrObjectNotFound) { + // Ignore the error if this isn't a commit. + haves[ref.Hash()] = true + } return nil } diff --git a/vendor/github.com/go-git/go-git/v5/repository.go b/vendor/github.com/go-git/go-git/v5/repository.go index a57c7141f8..200098e7a0 100644 --- a/vendor/github.com/go-git/go-git/v5/repository.go +++ b/vendor/github.com/go-git/go-git/v5/repository.go @@ -956,7 +956,7 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error { } if o.RecurseSubmodules != NoRecurseSubmodules { - if err := w.updateSubmodules(&SubmoduleUpdateOptions{ + if err := w.updateSubmodules(ctx, &SubmoduleUpdateOptions{ RecurseSubmodules: o.RecurseSubmodules, Depth: func() int { if o.ShallowSubmodules { @@ -1037,7 +1037,7 @@ func (r *Repository) setIsBare(isBare bool) error { return r.Storer.SetConfig(cfg) } -func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.RemoteConfig, head *plumbing.Reference) error { +func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.RemoteConfig, _ *plumbing.Reference) error { if !o.SingleBranch { return nil } diff --git a/vendor/github.com/go-git/go-git/v5/status.go b/vendor/github.com/go-git/go-git/v5/status.go index 7f18e02278..d14f7e6572 100644 --- a/vendor/github.com/go-git/go-git/v5/status.go +++ b/vendor/github.com/go-git/go-git/v5/status.go @@ -4,6 +4,9 @@ import ( "bytes" "fmt" "path/filepath" + + mindex "github.com/go-git/go-git/v5/utils/merkletrie/index" + "github.com/go-git/go-git/v5/utils/merkletrie/noder" ) // Status represents the current status of a Worktree. @@ -77,3 +80,69 @@ const ( Copied StatusCode = 'C' UpdatedButUnmerged StatusCode = 'U' ) + +// StatusStrategy defines the different types of strategies when processing +// the worktree status. +type StatusStrategy int + +const ( + // TODO: (V6) Review the default status strategy. + // TODO: (V6) Review the type used to represent Status, to enable lazy + // processing of statuses going direct to the backing filesystem. + defaultStatusStrategy = Empty + + // Empty starts its status map from empty. Missing entries for a given + // path means that the file is untracked. This causes a known issue (#119) + // whereby unmodified files can be incorrectly reported as untracked. + // + // This can be used when returning the changed state within a modified Worktree. + // For example, to check whether the current worktree is clean. + Empty StatusStrategy = 0 + // Preload goes through all existing nodes from the index and add them to the + // status map as unmodified. This is currently the most reliable strategy + // although it comes at a performance cost in large repositories. + // + // This method is recommended when fetching the status of unmodified files. + // For example, to confirm the status of a specific file that is either + // untracked or unmodified. + Preload StatusStrategy = 1 +) + +func (s StatusStrategy) new(w *Worktree) (Status, error) { + switch s { + case Preload: + return preloadStatus(w) + case Empty: + return make(Status), nil + } + return nil, fmt.Errorf("%w: %+v", ErrUnsupportedStatusStrategy, s) +} + +func preloadStatus(w *Worktree) (Status, error) { + idx, err := w.r.Storer.Index() + if err != nil { + return nil, err + } + + idxRoot := mindex.NewRootNode(idx) + nodes := []noder.Noder{idxRoot} + + status := make(Status) + for len(nodes) > 0 { + var node noder.Noder + node, nodes = nodes[0], nodes[1:] + if node.IsDir() { + children, err := node.Children() + if err != nil { + return nil, err + } + nodes = append(nodes, children...) + continue + } + fs := status.File(node.Name()) + fs.Worktree = Unmodified + fs.Staging = Unmodified + } + + return status, nil +} diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go index 31c4694816..72c9ccfc14 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go @@ -72,6 +72,9 @@ var ( // ErrIsDir is returned when a reference file is attempting to be read, // but the path specified is a directory. ErrIsDir = errors.New("reference path is a directory") + // ErrEmptyRefFile is returned when a reference file is attempted to be read, + // but the file is empty + ErrEmptyRefFile = errors.New("ref file is empty") ) // Options holds configuration for the storage. @@ -249,7 +252,7 @@ func (d *DotGit) objectPacks() ([]plumbing.Hash, error) { continue } - h := plumbing.NewHash(n[5 : len(n)-5]) //pack-(hash).pack + h := plumbing.NewHash(n[5 : len(n)-5]) // pack-(hash).pack if h.IsZero() { // Ignore files with badly-formatted names. continue @@ -661,18 +664,33 @@ func (d *DotGit) readReferenceFrom(rd io.Reader, name string) (ref *plumbing.Ref return nil, err } + if len(b) == 0 { + return nil, ErrEmptyRefFile + } + line := strings.TrimSpace(string(b)) return plumbing.NewReferenceFromStrings(name, line), nil } +// checkReferenceAndTruncate reads the reference from the given file, or the `pack-refs` file if +// the file was empty. Then it checks that the old reference matches the stored reference and +// truncates the file. func (d *DotGit) checkReferenceAndTruncate(f billy.File, old *plumbing.Reference) error { if old == nil { return nil } + ref, err := d.readReferenceFrom(f, old.Name().String()) + if errors.Is(err, ErrEmptyRefFile) { + // This may happen if the reference is being read from a newly created file. + // In that case, try getting the reference from the packed refs file. + ref, err = d.packedRef(old.Name()) + } + if err != nil { return err } + if ref.Hash() != old.Hash() { return storage.ErrReferenceHasChanged } @@ -701,16 +719,16 @@ func (d *DotGit) SetRef(r, old *plumbing.Reference) error { // Symbolic references are resolved and included in the output. func (d *DotGit) Refs() ([]*plumbing.Reference, error) { var refs []*plumbing.Reference - var seen = make(map[plumbing.ReferenceName]bool) - if err := d.addRefsFromRefDir(&refs, seen); err != nil { + seen := make(map[plumbing.ReferenceName]bool) + if err := d.addRefFromHEAD(&refs); err != nil { return nil, err } - if err := d.addRefsFromPackedRefs(&refs, seen); err != nil { + if err := d.addRefsFromRefDir(&refs, seen); err != nil { return nil, err } - if err := d.addRefFromHEAD(&refs); err != nil { + if err := d.addRefsFromPackedRefs(&refs, seen); err != nil { return nil, err } @@ -815,7 +833,8 @@ func (d *DotGit) addRefsFromPackedRefsFile(refs *[]*plumbing.Reference, f billy. } func (d *DotGit) openAndLockPackedRefs(doCreate bool) ( - pr billy.File, err error) { + pr billy.File, err error, +) { var f billy.File defer func() { if err != nil && f != nil { @@ -1020,7 +1039,7 @@ func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference, func (d *DotGit) CountLooseRefs() (int, error) { var refs []*plumbing.Reference - var seen = make(map[plumbing.ReferenceName]bool) + seen := make(map[plumbing.ReferenceName]bool) if err := d.addRefsFromRefDir(&refs, seen); err != nil { return 0, err } diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go index a19176f83d..a86ef3e2e5 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go @@ -48,7 +48,7 @@ func (s *IndexStorage) Index() (i *index.Index, err error) { defer ioutil.CheckClose(f, &err) - d := index.NewDecoder(bufio.NewReader(f)) + d := index.NewDecoder(f) err = d.Decode(idx) return idx, err } diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go index e812fe934d..91b4aceae1 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go @@ -431,13 +431,13 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb defer ioutil.CheckClose(w, &err) - s.objectCache.Put(obj) - bufp := copyBufferPool.Get().(*[]byte) buf := *bufp _, err = io.CopyBuffer(w, r, buf) copyBufferPool.Put(bufp) + s.objectCache.Put(obj) + return obj, err } diff --git a/vendor/github.com/go-git/go-git/v5/submodule.go b/vendor/github.com/go-git/go-git/v5/submodule.go index 84f020dc72..afabb6acad 100644 --- a/vendor/github.com/go-git/go-git/v5/submodule.go +++ b/vendor/github.com/go-git/go-git/v5/submodule.go @@ -214,10 +214,10 @@ func (s *Submodule) update(ctx context.Context, o *SubmoduleUpdateOptions, force return err } - return s.doRecursiveUpdate(r, o) + return s.doRecursiveUpdate(ctx, r, o) } -func (s *Submodule) doRecursiveUpdate(r *Repository, o *SubmoduleUpdateOptions) error { +func (s *Submodule) doRecursiveUpdate(ctx context.Context, r *Repository, o *SubmoduleUpdateOptions) error { if o.RecurseSubmodules == NoRecurseSubmodules { return nil } @@ -236,7 +236,7 @@ func (s *Submodule) doRecursiveUpdate(r *Repository, o *SubmoduleUpdateOptions) *new = *o new.RecurseSubmodules-- - return l.Update(new) + return l.UpdateContext(ctx, new) } func (s *Submodule) fetchAndCheckout( diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go index cc6dc89071..450feb4bac 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go +++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go @@ -1,12 +1,17 @@ package merkletrie import ( + "errors" "fmt" "io" "github.com/go-git/go-git/v5/utils/merkletrie/noder" ) +var ( + ErrEmptyFileName = errors.New("empty filename in tree entry") +) + // Action values represent the kind of things a Change can represent: // insertion, deletions or modifications of files. type Action int @@ -121,6 +126,10 @@ func (l *Changes) AddRecursiveDelete(root noder.Path) error { type noderToChangeFn func(noder.Path) Change // NewInsert or NewDelete func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error { + if root.String() == "" { + return ErrEmptyFileName + } + if !root.IsDir() { l.Add(ctor(root)) return nil diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go index 8090942ddb..4ef2d9907a 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go +++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go @@ -11,7 +11,7 @@ package merkletrie // corresponding changes and move the iterators further over both // trees. // -// The table bellow show all the possible comparison results, along +// The table below shows all the possible comparison results, along // with what changes should we produce and how to advance the // iterators. // diff --git a/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go b/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go index 5009ea8047..42f60f7ea1 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go +++ b/vendor/github.com/go-git/go-git/v5/utils/sync/bufio.go @@ -13,7 +13,7 @@ var bufioReader = sync.Pool{ } // GetBufioReader returns a *bufio.Reader that is managed by a sync.Pool. -// Returns a bufio.Reader that is resetted with reader and ready for use. +// Returns a bufio.Reader that is reset with reader and ready for use. // // After use, the *bufio.Reader should be put back into the sync.Pool // by calling PutBufioReader. diff --git a/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go b/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go index dd06fc0bc6..c67b978375 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go +++ b/vendor/github.com/go-git/go-git/v5/utils/sync/bytes.go @@ -35,7 +35,7 @@ func PutByteSlice(buf *[]byte) { } // GetBytesBuffer returns a *bytes.Buffer that is managed by a sync.Pool. -// Returns a buffer that is resetted and ready for use. +// Returns a buffer that is reset and ready for use. // // After use, the *bytes.Buffer should be put back into the sync.Pool // by calling PutBytesBuffer. diff --git a/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go b/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go index c613885957..edf674d852 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go +++ b/vendor/github.com/go-git/go-git/v5/utils/sync/zlib.go @@ -35,7 +35,7 @@ type ZLibReader struct { } // GetZlibReader returns a ZLibReader that is managed by a sync.Pool. -// Returns a ZLibReader that is resetted using a dictionary that is +// Returns a ZLibReader that is reset using a dictionary that is // also managed by a sync.Pool. // // After use, the ZLibReader should be put back into the sync.Pool @@ -58,7 +58,7 @@ func PutZlibReader(z ZLibReader) { } // GetZlibWriter returns a *zlib.Writer that is managed by a sync.Pool. -// Returns a writer that is resetted with w and ready for use. +// Returns a writer that is reset with w and ready for use. // // After use, the *zlib.Writer should be put back into the sync.Pool // by calling PutZlibWriter. diff --git a/vendor/github.com/go-git/go-git/v5/worktree.go b/vendor/github.com/go-git/go-git/v5/worktree.go index ab11d42db8..8dfa50b1b3 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree.go +++ b/vendor/github.com/go-git/go-git/v5/worktree.go @@ -25,11 +25,12 @@ import ( ) var ( - ErrWorktreeNotClean = errors.New("worktree is not clean") - ErrSubmoduleNotFound = errors.New("submodule not found") - ErrUnstagedChanges = errors.New("worktree contains unstaged changes") - ErrGitModulesSymlink = errors.New(gitmodulesFile + " is a symlink") - ErrNonFastForwardUpdate = errors.New("non-fast-forward update") + ErrWorktreeNotClean = errors.New("worktree is not clean") + ErrSubmoduleNotFound = errors.New("submodule not found") + ErrUnstagedChanges = errors.New("worktree contains unstaged changes") + ErrGitModulesSymlink = errors.New(gitmodulesFile + " is a symlink") + ErrNonFastForwardUpdate = errors.New("non-fast-forward update") + ErrRestoreWorktreeOnlyNotSupported = errors.New("worktree only is not supported") ) // Worktree represents a git worktree. @@ -139,7 +140,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { } if o.RecurseSubmodules != NoRecurseSubmodules { - return w.updateSubmodules(&SubmoduleUpdateOptions{ + return w.updateSubmodules(ctx, &SubmoduleUpdateOptions{ RecurseSubmodules: o.RecurseSubmodules, Auth: o.Auth, }) @@ -148,13 +149,13 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { return nil } -func (w *Worktree) updateSubmodules(o *SubmoduleUpdateOptions) error { +func (w *Worktree) updateSubmodules(ctx context.Context, o *SubmoduleUpdateOptions) error { s, err := w.Submodules() if err != nil { return err } o.Init = true - return s.Update(o) + return s.UpdateContext(ctx, o) } // Checkout switch branches or restore working tree files. @@ -307,13 +308,13 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error { } if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset { - if err := w.resetIndex(t, dirs); err != nil { + if err := w.resetIndex(t, dirs, opts.Files); err != nil { return err } } if opts.Mode == MergeReset || opts.Mode == HardReset { - if err := w.resetWorktree(t); err != nil { + if err := w.resetWorktree(t, opts.Files); err != nil { return err } } @@ -321,20 +322,52 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error { return nil } +// Restore restores specified files in the working tree or stage with contents from +// a restore source. If a path is tracked but does not exist in the restore, +// source, it will be removed to match the source. +// +// If Staged and Worktree are true, then the restore source will be the index. +// If only Staged is true, then the restore source will be HEAD. +// If only Worktree is true or neither Staged nor Worktree are true, will +// result in ErrRestoreWorktreeOnlyNotSupported because restoring the working +// tree while leaving the stage untouched is not currently supported. +// +// Restore with no files specified will return ErrNoRestorePaths. +func (w *Worktree) Restore(o *RestoreOptions) error { + if err := o.Validate(); err != nil { + return err + } + + if o.Staged { + opts := &ResetOptions{ + Files: o.Files, + } + + if o.Worktree { + // If we are doing both Worktree and Staging then it is a hard reset + opts.Mode = HardReset + } else { + // If we are doing just staging then it is a mixed reset + opts.Mode = MixedReset + } + + return w.Reset(opts) + } + + return ErrRestoreWorktreeOnlyNotSupported +} + // Reset the worktree to a specified state. func (w *Worktree) Reset(opts *ResetOptions) error { return w.ResetSparsely(opts, nil) } -func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error { +func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) error { idx, err := w.r.Storer.Index() - if len(dirs) > 0 { - idx.SkipUnless(dirs) - } - if err != nil { return err } + b := newIndexBuilder(idx) changes, err := w.diffTreeWithStaging(t, true) @@ -362,6 +395,13 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error { name = ch.From.String() } + if len(files) > 0 { + contains := inFiles(files, name) + if !contains { + continue + } + } + b.Remove(name) if e == nil { continue @@ -376,10 +416,25 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error { } b.Write(idx) + + if len(dirs) > 0 { + idx.SkipUnless(dirs) + } + return w.r.Storer.SetIndex(idx) } -func (w *Worktree) resetWorktree(t *object.Tree) error { +func inFiles(files []string, v string) bool { + for _, s := range files { + if s == v { + return true + } + } + + return false +} + +func (w *Worktree) resetWorktree(t *object.Tree, files []string) error { changes, err := w.diffStagingWithWorktree(true, false) if err != nil { return err @@ -395,6 +450,25 @@ func (w *Worktree) resetWorktree(t *object.Tree) error { if err := w.validChange(ch); err != nil { return err } + + if len(files) > 0 { + file := "" + if ch.From != nil { + file = ch.From.String() + } else if ch.To != nil { + file = ch.To.String() + } + + if file == "" { + continue + } + + contains := inFiles(files, file) + if !contains { + continue + } + } + if err := w.checkoutChange(ch, t, b); err != nil { return err } @@ -642,7 +716,7 @@ func (w *Worktree) checkoutChangeRegularFile(name string, return err } - return w.addIndexFromFile(name, e.Hash, idx) + return w.addIndexFromFile(name, e.Hash, f.Mode, idx) } return nil @@ -725,18 +799,13 @@ func (w *Worktree) addIndexFromTreeEntry(name string, f *object.TreeEntry, idx * return nil } -func (w *Worktree) addIndexFromFile(name string, h plumbing.Hash, idx *indexBuilder) error { +func (w *Worktree) addIndexFromFile(name string, h plumbing.Hash, mode filemode.FileMode, idx *indexBuilder) error { idx.Remove(name) fi, err := w.Filesystem.Lstat(name) if err != nil { return err } - mode, err := filemode.NewFromOSFileMode(fi.Mode()) - if err != nil { - return err - } - e := &index.Entry{ Hash: h, Name: name, @@ -1058,7 +1127,7 @@ func rmFileAndDirsIfEmpty(fs billy.Filesystem, name string) error { dir := filepath.Dir(name) for { removed, err := removeDirIfEmpty(fs, dir) - if err != nil { + if err != nil && !os.IsNotExist(err) { return err } diff --git a/vendor/github.com/go-git/go-git/v5/worktree_commit.go b/vendor/github.com/go-git/go-git/v5/worktree_commit.go index f62054bcb4..9b1988ae6b 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree_commit.go +++ b/vendor/github.com/go-git/go-git/v5/worktree_commit.go @@ -5,6 +5,7 @@ import ( "errors" "io" "path" + "regexp" "sort" "strings" @@ -23,6 +24,10 @@ var ( // ErrEmptyCommit occurs when a commit is attempted using a clean // working tree, with no changes to be committed. ErrEmptyCommit = errors.New("cannot create empty commit: clean working tree") + + // characters to be removed from user name and/or email before using them to build a commit object + // See https://git-scm.com/docs/git-commit#_commit_information + invalidCharactersRe = regexp.MustCompile(`[<>\n]`) ) // Commit stores the current contents of the index in a new commit along with @@ -38,8 +43,6 @@ func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error } } - var treeHash plumbing.Hash - if opts.Amend { head, err := w.r.Head() if err != nil { @@ -61,16 +64,34 @@ func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error return plumbing.ZeroHash, err } + // First handle the case of the first commit in the repository being empty. + if len(opts.Parents) == 0 && len(idx.Entries) == 0 && !opts.AllowEmptyCommits { + return plumbing.ZeroHash, ErrEmptyCommit + } + h := &buildTreeHelper{ fs: w.Filesystem, s: w.r.Storer, } - treeHash, err = h.BuildTree(idx, opts) + treeHash, err := h.BuildTree(idx, opts) if err != nil { return plumbing.ZeroHash, err } + previousTree := plumbing.ZeroHash + if len(opts.Parents) > 0 { + parentCommit, err := w.r.CommitObject(opts.Parents[0]) + if err != nil { + return plumbing.ZeroHash, err + } + previousTree = parentCommit.TreeHash + } + + if treeHash == previousTree && !opts.AllowEmptyCommits { + return plumbing.ZeroHash, ErrEmptyCommit + } + commit, err := w.buildCommitObject(msg, opts, treeHash) if err != nil { return plumbing.ZeroHash, err @@ -121,8 +142,8 @@ func (w *Worktree) updateHEAD(commit plumbing.Hash) error { func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumbing.Hash) (plumbing.Hash, error) { commit := &object.Commit{ - Author: *opts.Author, - Committer: *opts.Committer, + Author: w.sanitize(*opts.Author), + Committer: w.sanitize(*opts.Committer), Message: msg, TreeHash: tree, ParentHashes: opts.Parents, @@ -148,6 +169,14 @@ func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumb return w.r.Storer.SetEncodedObject(obj) } +func (w *Worktree) sanitize(signature object.Signature) object.Signature { + return object.Signature{ + Name: invalidCharactersRe.ReplaceAllString(signature.Name, ""), + Email: invalidCharactersRe.ReplaceAllString(signature.Email, ""), + When: signature.When, + } +} + type gpgSigner struct { key *openpgp.Entity cfg *packet.Config @@ -175,10 +204,6 @@ type buildTreeHelper struct { // BuildTree builds the tree objects and push its to the storer, the hash // of the root tree is returned. func (h *buildTreeHelper) BuildTree(idx *index.Index, opts *CommitOptions) (plumbing.Hash, error) { - if len(idx.Entries) == 0 && (opts == nil || !opts.AllowEmptyCommits) { - return plumbing.ZeroHash, ErrEmptyCommit - } - const rootNode = "" h.trees = map[string]*object.Tree{rootNode: {}} h.entries = map[string]*object.TreeEntry{} diff --git a/vendor/github.com/go-git/go-git/v5/worktree_linux.go b/vendor/github.com/go-git/go-git/v5/worktree_linux.go index 6fcace2f93..f6b85fe3df 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree_linux.go +++ b/vendor/github.com/go-git/go-git/v5/worktree_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package git @@ -21,6 +22,6 @@ func init() { } } -func isSymlinkWindowsNonAdmin(err error) bool { +func isSymlinkWindowsNonAdmin(_ error) bool { return false } diff --git a/vendor/github.com/go-git/go-git/v5/worktree_status.go b/vendor/github.com/go-git/go-git/v5/worktree_status.go index dd9b2439cf..6e72db9744 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree_status.go +++ b/vendor/github.com/go-git/go-git/v5/worktree_status.go @@ -29,10 +29,23 @@ var ( // ErrGlobNoMatches in an AddGlob if the glob pattern does not match any // files in the worktree. ErrGlobNoMatches = errors.New("glob pattern did not match any files") + // ErrUnsupportedStatusStrategy occurs when an invalid StatusStrategy is used + // when processing the Worktree status. + ErrUnsupportedStatusStrategy = errors.New("unsupported status strategy") ) // Status returns the working tree status. func (w *Worktree) Status() (Status, error) { + return w.StatusWithOptions(StatusOptions{Strategy: defaultStatusStrategy}) +} + +// StatusOptions defines the options for Worktree.StatusWithOptions(). +type StatusOptions struct { + Strategy StatusStrategy +} + +// StatusWithOptions returns the working tree status. +func (w *Worktree) StatusWithOptions(o StatusOptions) (Status, error) { var hash plumbing.Hash ref, err := w.r.Head() @@ -44,11 +57,14 @@ func (w *Worktree) Status() (Status, error) { hash = ref.Hash() } - return w.status(hash) + return w.status(o.Strategy, hash) } -func (w *Worktree) status(commit plumbing.Hash) (Status, error) { - s := make(Status) +func (w *Worktree) status(ss StatusStrategy, commit plumbing.Hash) (Status, error) { + s, err := ss.new(w) + if err != nil { + return nil, err + } left, err := w.diffCommitWithStaging(commit, false) if err != nil { @@ -488,7 +504,7 @@ func (w *Worktree) copyFileToStorage(path string) (hash plumbing.Hash, err error return w.r.Storer.SetEncodedObject(obj) } -func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, fi os.FileInfo) (err error) { +func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, _ os.FileInfo) (err error) { src, err := w.Filesystem.Open(path) if err != nil { return err @@ -503,7 +519,7 @@ func (w *Worktree) fillEncodedObjectFromFile(dst io.Writer, path string, fi os.F return err } -func (w *Worktree) fillEncodedObjectFromSymlink(dst io.Writer, path string, fi os.FileInfo) error { +func (w *Worktree) fillEncodedObjectFromSymlink(dst io.Writer, path string, _ os.FileInfo) error { target, err := w.Filesystem.Readlink(path) if err != nil { return err @@ -543,9 +559,11 @@ func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbi return err } - if e.Mode.IsRegular() { - e.Size = uint32(info.Size()) - } + // The entry size must always reflect the current state, otherwise + // it will cause go-git's Worktree.Status() to divert from "git status". + // The size of a symlink is the length of the path to the target. + // The size of Regular and Executable files is the size of the files. + e.Size = uint32(info.Size()) fillSystemInfo(e, info.Sys()) return nil diff --git a/vendor/github.com/skeema/knownhosts/CONTRIBUTING.md b/vendor/github.com/skeema/knownhosts/CONTRIBUTING.md new file mode 100644 index 0000000000..9624f82760 --- /dev/null +++ b/vendor/github.com/skeema/knownhosts/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing to skeema/knownhosts + +Thank you for your interest in contributing! This document provides guidelines for submitting pull requests. + +### Link to an issue + +Before starting the pull request process, initial discussion should take place on a GitHub issue first. For bug reports, the issue should track the open bug and confirm it is reproducible. For feature requests, the issue should cover why the feature is necessary. + +In the issue comments, discuss your suggested approach for a fix/implementation, and please wait to get feedback before opening a pull request. + +### Test coverage + +In general, please provide reasonably thorough test coverage. Whenever possible, your PR should aim to match or improve the overall test coverage percentage of the package. You can run tests and check coverage locally using `go test -cover`. We also have CI automation in GitHub Actions which will comment on each pull request with a coverage percentage. + +That said, it is fine to submit an initial draft / work-in-progress PR without coverage, if you are waiting on implementation feedback before writing the tests. + +We intentionally avoid hard-coding SSH keys or known_hosts files into the test logic. Instead, the tests generate new keys and then use them to generate a known_hosts file, which is then cached/reused for that overall test run, in order to keep performance reasonable. + +### Documentation + +Exported types require doc comments. The linter CI step will catch this if missing. + +### Backwards compatibility + +Because this package is imported by [nearly 7000 repos on GitHub](https://github.com/skeema/knownhosts/network/dependents), we must be very strict about backwards compatibility of exported symbols and function signatures. + +Backwards compatibility can be very tricky in some situations. In this case, a maintainer may need to add additional commits to your branch to adjust the approach. Please do not take offense if this occurs; it is sometimes simply faster to implement a refactor on our end directly. When the PR/branch is merged, a merge commit will be used, to ensure your commits appear as-is in the repo history and are still properly credited to you. + +### Avoid rewriting core x/crypto/ssh/knownhosts logic + +skeema/knownhosts is intended to be a relatively thin *wrapper* around x/crypto/ssh/knownhosts, without duplicating or re-implementing the core known_hosts file parsing and host key handling logic. Importers of this package should be confident that it can be used as a nearly-drop-in replacement for x/crypto/ssh/knownhosts without introducing substantial risk, security flaws, parser differentials, or unexpected behavior changes. + +To solve shortcomings in x/crypto/ssh/knownhosts, we try to come up with workarounds that still utilize x/crypto/ssh/knownhosts functionality whenever possible. + +Some bugs in x/crypto/ssh/knownhosts do require re-reading the known_hosts file here to solve, but we make that *optional* by offering separate constructors/types with and without that behavior. + diff --git a/vendor/github.com/skeema/knownhosts/README.md b/vendor/github.com/skeema/knownhosts/README.md index 36b847614c..046bc0edcb 100644 --- a/vendor/github.com/skeema/knownhosts/README.md +++ b/vendor/github.com/skeema/knownhosts/README.md @@ -1,31 +1,33 @@ # knownhosts: enhanced Golang SSH known_hosts management [![build status](https://img.shields.io/github/actions/workflow/status/skeema/knownhosts/tests.yml?branch=main)](https://github.com/skeema/knownhosts/actions) +[![code coverage](https://img.shields.io/coveralls/skeema/knownhosts.svg)](https://coveralls.io/r/skeema/knownhosts) [![godoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/skeema/knownhosts) > This repo is brought to you by [Skeema](https://github.com/skeema/skeema), a > declarative pure-SQL schema management system for MySQL and MariaDB. Our -> premium products include extensive [SSH tunnel](https://www.skeema.io/docs/options/#ssh) +> premium products include extensive [SSH tunnel](https://www.skeema.io/docs/features/ssh/) > functionality, which internally makes use of this package. Go provides excellent functionality for OpenSSH known_hosts files in its external package [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts). -However, that package is somewhat low-level, making it difficult to implement full known_hosts management similar to command-line `ssh`'s behavior for `StrictHostKeyChecking=no` configuration. +However, that package is somewhat low-level, making it difficult to implement full known_hosts management similar to OpenSSH's command-line behavior. Additionally, [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) has several known issues in edge cases, some of which have remained open for multiple years. -This repo ([github.com/skeema/knownhosts](https://github.com/skeema/knownhosts)) is a thin wrapper package around [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts), adding the following functionality: +Package [github.com/skeema/knownhosts](https://github.com/skeema/knownhosts) provides a *thin wrapper* around [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts), adding the following improvements and fixes without duplicating its core logic: * Look up known_hosts public keys for any given host -* Auto-populate ssh.ClientConfig.HostKeyAlgorithms easily based on known_hosts, providing a solution for [golang/go#29286](https://github.com/golang/go/issues/29286) +* Auto-populate ssh.ClientConfig.HostKeyAlgorithms easily based on known_hosts, providing a solution for [golang/go#29286](https://github.com/golang/go/issues/29286). (This also properly handles cert algorithms for hosts using CA keys when [using the NewDB constructor](#enhancements-requiring-extra-parsing) added in skeema/knownhosts v1.3.0.) +* Properly match wildcard hostname known_hosts entries regardless of port number, providing a solution for [golang/go#52056](https://github.com/golang/go/issues/52056). (Added in v1.3.0; requires [using the NewDB constructor](#enhancements-requiring-extra-parsing)) * Write new known_hosts entries to an io.Writer * Properly format/normalize new known_hosts entries containing ipv6 addresses, providing a solution for [golang/go#53463](https://github.com/golang/go/issues/53463) -* Determine if an ssh.HostKeyCallback's error corresponds to a host whose key has changed (indicating potential MitM attack) vs a host that just isn't known yet +* Easily determine if an ssh.HostKeyCallback's error corresponds to a host whose key has changed (indicating potential MitM attack) vs a host that just isn't known yet ## How host key lookup works Although [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) doesn't directly expose a way to query its known_host map, we use a subtle trick to do so: invoke the HostKeyCallback with a valid host but a bogus key. The resulting KeyError allows us to determine which public keys are actually present for that host. -By using this technique, [github.com/skeema/knownhosts](https://github.com/skeema/knownhosts) doesn't need to duplicate or re-implement any of the actual known_hosts management from [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts). +By using this technique, [github.com/skeema/knownhosts](https://github.com/skeema/knownhosts) doesn't need to duplicate any of the core known_hosts host-lookup logic from [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts). ## Populating ssh.ClientConfig.HostKeyAlgorithms based on known_hosts @@ -42,20 +44,33 @@ import ( ) func sshConfigForHost(hostWithPort string) (*ssh.ClientConfig, error) { - kh, err := knownhosts.New("/home/myuser/.ssh/known_hosts") + kh, err := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") if err != nil { return nil, err } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ /* ... */ }, - HostKeyCallback: kh.HostKeyCallback(), // or, equivalently, use ssh.HostKeyCallback(kh) + HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms(hostWithPort), } return config, nil } ``` +## Enhancements requiring extra parsing + +Originally, this package did not re-read/re-parse the known_hosts files at all, relying entirely on [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) for all known_hosts file reading and processing. This package only offered a constructor called `New`, returning a host key callback, identical to the call pattern of [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) but with extra methods available on the callback type. + +However, a couple shortcomings in [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) cannot possibly be solved without re-reading the known_hosts file. Therefore, as of v1.3.0 of this package, we now offer an alternative constructor `NewDB`, which does an additional read of the known_hosts file (after the one from [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts)), in order to detect: + +* @cert-authority lines, so that we can correctly return cert key algorithms instead of normal host key algorithms when appropriate +* host pattern wildcards, so that we can match OpenSSH's behavior for non-standard port numbers, unlike how [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) normally treats them + +Aside from *detecting* these special cases, this package otherwise still directly uses [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) for host lookups and all other known_hosts file processing. We do **not** fork or re-implement those core behaviors of [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts). + +The performance impact of this extra known_hosts read should be minimal, as the file should typically be in the filesystem cache already from the original read by [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts). That said, users who wish to avoid the extra read can stay with the `New` constructor, which intentionally retains its pre-v1.3.0 behavior as-is. However, the extra fixes for @cert-authority and host pattern wildcards will not be enabled in that case. + ## Writing new known_hosts entries If you wish to mimic the behavior of OpenSSH's `StrictHostKeyChecking=no` or `StrictHostKeyChecking=ask`, this package provides a few functions to simplify this task. For example: @@ -63,7 +78,7 @@ If you wish to mimic the behavior of OpenSSH's `StrictHostKeyChecking=no` or `St ```golang sshHost := "yourserver.com:22" khPath := "/home/myuser/.ssh/known_hosts" -kh, err := knownhosts.New(khPath) +kh, err := knownhosts.NewDB(khPath) if err != nil { log.Fatal("Failed to read known_hosts: ", err) } @@ -71,7 +86,8 @@ if err != nil { // Create a custom permissive hostkey callback which still errors on hosts // with changed keys, but allows unknown hosts and adds them to known_hosts cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { - err := kh(hostname, remote, key) + innerCallback := kh.HostKeyCallback() + err := innerCallback(hostname, remote, key) if knownhosts.IsHostKeyChanged(err) { return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for host %s! This may indicate a MitM attack.", hostname) } else if knownhosts.IsHostUnknown(err) { diff --git a/vendor/github.com/skeema/knownhosts/knownhosts.go b/vendor/github.com/skeema/knownhosts/knownhosts.go index 4dad7771b8..2b7536e0da 100644 --- a/vendor/github.com/skeema/knownhosts/knownhosts.go +++ b/vendor/github.com/skeema/knownhosts/knownhosts.go @@ -3,11 +3,14 @@ package knownhosts import ( + "bufio" + "bytes" "encoding/base64" "errors" "fmt" "io" "net" + "os" "sort" "strings" @@ -15,23 +18,133 @@ import ( xknownhosts "golang.org/x/crypto/ssh/knownhosts" ) -// HostKeyCallback wraps ssh.HostKeyCallback with an additional method to -// perform host key algorithm lookups from the known_hosts entries. -type HostKeyCallback ssh.HostKeyCallback +// HostKeyDB wraps logic in golang.org/x/crypto/ssh/knownhosts with additional +// behaviors, such as the ability to perform host key/algorithm lookups from +// known_hosts entries. +type HostKeyDB struct { + callback ssh.HostKeyCallback + isCert map[string]bool // keyed by "filename:line" + isWildcard map[string]bool // keyed by "filename:line" +} -// New creates a host key callback from the given OpenSSH host key files. The -// returned value may be used in ssh.ClientConfig.HostKeyCallback by casting it -// to ssh.HostKeyCallback, or using its HostKeyCallback method. Otherwise, it -// operates the same as the New function in golang.org/x/crypto/ssh/knownhosts. -func New(files ...string) (HostKeyCallback, error) { +// NewDB creates a HostKeyDB from the given OpenSSH known_hosts file(s). It +// reads and parses the provided files one additional time (beyond logic in +// golang.org/x/crypto/ssh/knownhosts) in order to: +// +// - Handle CA lines properly and return ssh.CertAlgo* values when calling the +// HostKeyAlgorithms method, for use in ssh.ClientConfig.HostKeyAlgorithms +// - Allow * wildcards in hostnames to match on non-standard ports, providing +// a workaround for https://github.com/golang/go/issues/52056 in order to +// align with OpenSSH's wildcard behavior +// +// When supplying multiple files, their order does not matter. +func NewDB(files ...string) (*HostKeyDB, error) { cb, err := xknownhosts.New(files...) - return HostKeyCallback(cb), err + if err != nil { + return nil, err + } + hkdb := &HostKeyDB{ + callback: cb, + isCert: make(map[string]bool), + isWildcard: make(map[string]bool), + } + + // Re-read each file a single time, looking for @cert-authority lines. The + // logic for reading the file is designed to mimic hostKeyDB.Read from + // golang.org/x/crypto/ssh/knownhosts + for _, filename := range files { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + scanner := bufio.NewScanner(f) + lineNum := 0 + for scanner.Scan() { + lineNum++ + line := scanner.Bytes() + line = bytes.TrimSpace(line) + // Does the line start with "@cert-authority" followed by whitespace? + if len(line) > 15 && bytes.HasPrefix(line, []byte("@cert-authority")) && (line[15] == ' ' || line[15] == '\t') { + mapKey := fmt.Sprintf("%s:%d", filename, lineNum) + hkdb.isCert[mapKey] = true + line = bytes.TrimSpace(line[16:]) + } + // truncate line to just the host pattern field + if i := bytes.IndexAny(line, "\t "); i >= 0 { + line = line[:i] + } + // Does the host pattern contain a * wildcard and no specific port? + if i := bytes.IndexRune(line, '*'); i >= 0 && !bytes.Contains(line[i:], []byte("]:")) { + mapKey := fmt.Sprintf("%s:%d", filename, lineNum) + hkdb.isWildcard[mapKey] = true + } + } + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("knownhosts: %s:%d: %w", filename, lineNum, err) + } + } + return hkdb, nil } -// HostKeyCallback simply casts the receiver back to ssh.HostKeyCallback, for -// use in ssh.ClientConfig.HostKeyCallback. -func (hkcb HostKeyCallback) HostKeyCallback() ssh.HostKeyCallback { - return ssh.HostKeyCallback(hkcb) +// HostKeyCallback returns an ssh.HostKeyCallback. This can be used directly in +// ssh.ClientConfig.HostKeyCallback, as shown in the example for NewDB. +// Alternatively, you can wrap it with an outer callback to potentially handle +// appending a new entry to the known_hosts file; see example in WriteKnownHost. +func (hkdb *HostKeyDB) HostKeyCallback() ssh.HostKeyCallback { + // Either NewDB found no wildcard host patterns, or hkdb was created from + // HostKeyCallback.ToDB in which case we didn't scan known_hosts for them: + // return the callback (which came from x/crypto/ssh/knownhosts) as-is + if len(hkdb.isWildcard) == 0 { + return hkdb.callback + } + + // If we scanned for wildcards and found at least one, return a wrapped + // callback with extra behavior: if the host lookup found no matches, and the + // host arg had a non-standard port, re-do the lookup on standard port 22. If + // that second call returns a *xknownhosts.KeyError, filter down any resulting + // Want keys to known wildcard entries. + f := func(hostname string, remote net.Addr, key ssh.PublicKey) error { + callbackErr := hkdb.callback(hostname, remote, key) + if callbackErr == nil || IsHostKeyChanged(callbackErr) { // hostname has known_host entries as-is + return callbackErr + } + justHost, port, splitErr := net.SplitHostPort(hostname) + if splitErr != nil || port == "" || port == "22" { // hostname already using standard port + return callbackErr + } + // If we reach here, the port was non-standard and no known_host entries + // were found for the non-standard port. Try again with standard port. + if tcpAddr, ok := remote.(*net.TCPAddr); ok && tcpAddr.Port != 22 { + remote = &net.TCPAddr{ + IP: tcpAddr.IP, + Port: 22, + Zone: tcpAddr.Zone, + } + } + callbackErr = hkdb.callback(justHost+":22", remote, key) + var keyErr *xknownhosts.KeyError + if errors.As(callbackErr, &keyErr) && len(keyErr.Want) > 0 { + wildcardKeys := make([]xknownhosts.KnownKey, 0, len(keyErr.Want)) + for _, wantKey := range keyErr.Want { + if hkdb.isWildcard[fmt.Sprintf("%s:%d", wantKey.Filename, wantKey.Line)] { + wildcardKeys = append(wildcardKeys, wantKey) + } + } + callbackErr = &xknownhosts.KeyError{ + Want: wildcardKeys, + } + } + return callbackErr + } + return ssh.HostKeyCallback(f) +} + +// PublicKey wraps ssh.PublicKey with an additional field, to identify +// whether the key corresponds to a certificate authority. +type PublicKey struct { + ssh.PublicKey + Cert bool } // HostKeys returns a slice of known host public keys for the supplied host:port @@ -39,12 +152,16 @@ func (hkcb HostKeyCallback) HostKeyCallback() ssh.HostKeyCallback { // already known. For hosts that have multiple known_hosts entries (for // different key types), the result will be sorted by known_hosts filename and // line number. -func (hkcb HostKeyCallback) HostKeys(hostWithPort string) (keys []ssh.PublicKey) { +// If hkdb was originally created by calling NewDB, the Cert boolean field of +// each result entry reports whether the key corresponded to a @cert-authority +// line. If hkdb was NOT obtained from NewDB, then Cert will always be false. +func (hkdb *HostKeyDB) HostKeys(hostWithPort string) (keys []PublicKey) { var keyErr *xknownhosts.KeyError placeholderAddr := &net.TCPAddr{IP: []byte{0, 0, 0, 0}} placeholderPubKey := &fakePublicKey{} var kkeys []xknownhosts.KnownKey - if hkcbErr := hkcb(hostWithPort, placeholderAddr, placeholderPubKey); errors.As(hkcbErr, &keyErr) { + callback := hkdb.HostKeyCallback() + if hkcbErr := callback(hostWithPort, placeholderAddr, placeholderPubKey); errors.As(hkcbErr, &keyErr) { kkeys = append(kkeys, keyErr.Want...) knownKeyLess := func(i, j int) bool { if kkeys[i].Filename < kkeys[j].Filename { @@ -53,9 +170,14 @@ func (hkcb HostKeyCallback) HostKeys(hostWithPort string) (keys []ssh.PublicKey) return (kkeys[i].Filename == kkeys[j].Filename && kkeys[i].Line < kkeys[j].Line) } sort.Slice(kkeys, knownKeyLess) - keys = make([]ssh.PublicKey, len(kkeys)) + keys = make([]PublicKey, len(kkeys)) for n := range kkeys { - keys[n] = kkeys[n].Key + keys[n] = PublicKey{ + PublicKey: kkeys[n].Key, + } + if len(hkdb.isCert) > 0 { + keys[n].Cert = hkdb.isCert[fmt.Sprintf("%s:%d", kkeys[n].Filename, kkeys[n].Line)] + } } } return keys @@ -66,17 +188,23 @@ func (hkcb HostKeyCallback) HostKeys(hostWithPort string) (keys []ssh.PublicKey) // is not already known. The result may be used in ssh.ClientConfig's // HostKeyAlgorithms field, either as-is or after filtering (if you wish to // ignore or prefer particular algorithms). For hosts that have multiple -// known_hosts entries (for different key types), the result will be sorted by +// known_hosts entries (of different key types), the result will be sorted by // known_hosts filename and line number. -func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string) { +// If hkdb was originally created by calling NewDB, any @cert-authority lines +// in the known_hosts file will properly be converted to the corresponding +// ssh.CertAlgo* values. +func (hkdb *HostKeyDB) HostKeyAlgorithms(hostWithPort string) (algos []string) { // We ensure that algos never contains duplicates. This is done for robustness // even though currently golang.org/x/crypto/ssh/knownhosts never exposes // multiple keys of the same type. This way our behavior here is unaffected // even if https://github.com/golang/go/issues/28870 is implemented, for // example by https://github.com/golang/crypto/pull/254. - hostKeys := hkcb.HostKeys(hostWithPort) + hostKeys := hkdb.HostKeys(hostWithPort) seen := make(map[string]struct{}, len(hostKeys)) - addAlgo := func(typ string) { + addAlgo := func(typ string, cert bool) { + if cert { + typ = keyTypeToCertAlgo(typ) + } if _, already := seen[typ]; !already { algos = append(algos, typ) seen[typ] = struct{}{} @@ -88,25 +216,143 @@ func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []stri // KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, // not public key formats, so they can't appear as a PublicKey.Type. // The corresponding PublicKey.Type is KeyAlgoRSA. See RFC 8332, Section 2. - addAlgo(ssh.KeyAlgoRSASHA512) - addAlgo(ssh.KeyAlgoRSASHA256) + addAlgo(ssh.KeyAlgoRSASHA512, key.Cert) + addAlgo(ssh.KeyAlgoRSASHA256, key.Cert) } - addAlgo(typ) + addAlgo(typ, key.Cert) } return algos } +func keyTypeToCertAlgo(keyType string) string { + switch keyType { + case ssh.KeyAlgoRSA: + return ssh.CertAlgoRSAv01 + case ssh.KeyAlgoRSASHA256: + return ssh.CertAlgoRSASHA256v01 + case ssh.KeyAlgoRSASHA512: + return ssh.CertAlgoRSASHA512v01 + case ssh.KeyAlgoDSA: + return ssh.CertAlgoDSAv01 + case ssh.KeyAlgoECDSA256: + return ssh.CertAlgoECDSA256v01 + case ssh.KeyAlgoSKECDSA256: + return ssh.CertAlgoSKECDSA256v01 + case ssh.KeyAlgoECDSA384: + return ssh.CertAlgoECDSA384v01 + case ssh.KeyAlgoECDSA521: + return ssh.CertAlgoECDSA521v01 + case ssh.KeyAlgoED25519: + return ssh.CertAlgoED25519v01 + case ssh.KeyAlgoSKED25519: + return ssh.CertAlgoSKED25519v01 + } + return "" +} + +// HostKeyCallback wraps ssh.HostKeyCallback with additional methods to +// perform host key and algorithm lookups from the known_hosts entries. It is +// otherwise identical to ssh.HostKeyCallback, and does not introduce any file- +// parsing behavior beyond what is in golang.org/x/crypto/ssh/knownhosts. +// +// In most situations, use HostKeyDB and its constructor NewDB instead of using +// the HostKeyCallback type. The HostKeyCallback type is only provided for +// backwards compatibility with older versions of this package, as well as for +// very strict situations where any extra known_hosts file-parsing is +// undesirable. +// +// Methods of HostKeyCallback do not provide any special treatment for +// @cert-authority lines, which will (incorrectly) look like normal non-CA host +// keys. Additionally, HostKeyCallback lacks the fix for applying * wildcard +// known_host entries to all ports, like OpenSSH's behavior. +type HostKeyCallback ssh.HostKeyCallback + +// New creates a HostKeyCallback from the given OpenSSH known_hosts file(s). The +// returned value may be used in ssh.ClientConfig.HostKeyCallback by casting it +// to ssh.HostKeyCallback, or using its HostKeyCallback method. Otherwise, it +// operates the same as the New function in golang.org/x/crypto/ssh/knownhosts. +// When supplying multiple files, their order does not matter. +// +// In most situations, you should avoid this function, as the returned value +// lacks several enhanced behaviors. See doc comment for HostKeyCallback for +// more information. Instead, most callers should use NewDB to create a +// HostKeyDB, which includes these enhancements. +func New(files ...string) (HostKeyCallback, error) { + cb, err := xknownhosts.New(files...) + return HostKeyCallback(cb), err +} + +// HostKeyCallback simply casts the receiver back to ssh.HostKeyCallback, for +// use in ssh.ClientConfig.HostKeyCallback. +func (hkcb HostKeyCallback) HostKeyCallback() ssh.HostKeyCallback { + return ssh.HostKeyCallback(hkcb) +} + +// ToDB converts the receiver into a HostKeyDB. However, the returned HostKeyDB +// lacks the enhanced behaviors described in the doc comment for NewDB: proper +// CA support, and wildcard matching on nonstandard ports. +// +// It is generally preferable to create a HostKeyDB by using NewDB. The ToDB +// method is only provided for situations in which the calling code needs to +// make the extra NewDB behaviors optional / user-configurable, perhaps for +// reasons of performance or code trust (since NewDB reads the known_host file +// an extra time, which may be undesirable in some strict situations). This way, +// callers can conditionally create a non-enhanced HostKeyDB by using New and +// ToDB. See code example. +func (hkcb HostKeyCallback) ToDB() *HostKeyDB { + // This intentionally leaves the isCert and isWildcard map fields as nil, as + // there is no way to retroactively populate them from just a HostKeyCallback. + // Methods of HostKeyDB will skip any related enhanced behaviors accordingly. + return &HostKeyDB{callback: ssh.HostKeyCallback(hkcb)} +} + +// HostKeys returns a slice of known host public keys for the supplied host:port +// found in the known_hosts file(s), or an empty slice if the host is not +// already known. For hosts that have multiple known_hosts entries (for +// different key types), the result will be sorted by known_hosts filename and +// line number. +// In the returned values, there is no way to distinguish between CA keys +// (known_hosts lines beginning with @cert-authority) and regular keys. To do +// so, see NewDB and HostKeyDB.HostKeys instead. +func (hkcb HostKeyCallback) HostKeys(hostWithPort string) []ssh.PublicKey { + annotatedKeys := hkcb.ToDB().HostKeys(hostWithPort) + rawKeys := make([]ssh.PublicKey, len(annotatedKeys)) + for n, ak := range annotatedKeys { + rawKeys[n] = ak.PublicKey + } + return rawKeys +} + +// HostKeyAlgorithms returns a slice of host key algorithms for the supplied +// host:port found in the known_hosts file(s), or an empty slice if the host +// is not already known. The result may be used in ssh.ClientConfig's +// HostKeyAlgorithms field, either as-is or after filtering (if you wish to +// ignore or prefer particular algorithms). For hosts that have multiple +// known_hosts entries (for different key types), the result will be sorted by +// known_hosts filename and line number. +// The returned values will not include ssh.CertAlgo* values. If any +// known_hosts lines had @cert-authority prefixes, their original key algo will +// be returned instead. For proper CA support, see NewDB and +// HostKeyDB.HostKeyAlgorithms instead. +func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string) { + return hkcb.ToDB().HostKeyAlgorithms(hostWithPort) +} + // HostKeyAlgorithms is a convenience function for performing host key algorithm // lookups on an ssh.HostKeyCallback directly. It is intended for use in code // paths that stay with the New method of golang.org/x/crypto/ssh/knownhosts -// rather than this package's New method. +// rather than this package's New or NewDB methods. +// The returned values will not include ssh.CertAlgo* values. If any +// known_hosts lines had @cert-authority prefixes, their original key algo will +// be returned instead. For proper CA support, see NewDB and +// HostKeyDB.HostKeyAlgorithms instead. func HostKeyAlgorithms(cb ssh.HostKeyCallback, hostWithPort string) []string { return HostKeyCallback(cb).HostKeyAlgorithms(hostWithPort) } // IsHostKeyChanged returns a boolean indicating whether the error indicates // the host key has changed. It is intended to be called on the error returned -// from invoking a HostKeyCallback to check whether an SSH host is known. +// from invoking a host key callback, to check whether an SSH host is known. func IsHostKeyChanged(err error) bool { var keyErr *xknownhosts.KeyError return errors.As(err, &keyErr) && len(keyErr.Want) > 0 @@ -114,7 +360,7 @@ func IsHostKeyChanged(err error) bool { // IsHostUnknown returns a boolean indicating whether the error represents an // unknown host. It is intended to be called on the error returned from invoking -// a HostKeyCallback to check whether an SSH host is known. +// a host key callback to check whether an SSH host is known. func IsHostUnknown(err error) bool { var keyErr *xknownhosts.KeyError return errors.As(err, &keyErr) && len(keyErr.Want) == 0 @@ -154,11 +400,12 @@ func Line(addresses []string, key ssh.PublicKey) string { }, " ") } -// WriteKnownHost writes a known_hosts line to writer for the supplied hostname, +// WriteKnownHost writes a known_hosts line to w for the supplied hostname, // remote, and key. This is useful when writing a custom hostkey callback which -// wraps a callback obtained from knownhosts.New to provide additional -// known_hosts management functionality. The hostname, remote, and key typically -// correspond to the callback's args. +// wraps a callback obtained from this package to provide additional known_hosts +// management functionality. The hostname, remote, and key typically correspond +// to the callback's args. This function does not support writing +// @cert-authority lines. func WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.PublicKey) error { // Always include hostname; only also include remote if it isn't a zero value // and doesn't normalize to the same string as hostname. @@ -177,6 +424,14 @@ func WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.Publi return err } +// WriteKnownHostCA writes a @cert-authority line to w for the supplied host +// name/pattern and key. +func WriteKnownHostCA(w io.Writer, hostPattern string, key ssh.PublicKey) error { + encodedKey := base64.StdEncoding.EncodeToString(key.Marshal()) + _, err := fmt.Fprintf(w, "@cert-authority %s %s %s\n", hostPattern, key.Type(), encodedKey) + return err +} + // fakePublicKey is used as part of the work-around for // https://github.com/golang/go/issues/29286 type fakePublicKey struct{} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 4d4b4aad6f..7e19eba090 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -7,10 +7,13 @@ import ( "time" ) -type CompareType int +// Deprecated: CompareType has only ever been for internal use and has accidentally been published since v1.6.0. Do not use it. +type CompareType = compareResult + +type compareResult int const ( - compareLess CompareType = iota - 1 + compareLess compareResult = iota - 1 compareEqual compareGreater ) @@ -39,7 +42,7 @@ var ( bytesType = reflect.TypeOf([]byte{}) ) -func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { +func compare(obj1, obj2 interface{}, kind reflect.Kind) (compareResult, bool) { obj1Value := reflect.ValueOf(obj1) obj2Value := reflect.ValueOf(obj2) @@ -325,7 +328,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) } - return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) + if timeObj1.Before(timeObj2) { + return compareLess, true + } + if timeObj1.Equal(timeObj2) { + return compareEqual, true + } + return compareGreater, true } case reflect.Slice: { @@ -345,7 +354,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) } - return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true + return compareResult(bytes.Compare(bytesObj1, bytesObj2)), true } case reflect.Uintptr: { @@ -381,7 +390,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -394,7 +403,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // Less asserts that the first element is less than the second @@ -406,7 +415,7 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -419,7 +428,7 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter if h, ok := t.(tHelper); ok { h.Helper() } - return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } // Positive asserts that the specified element is positive @@ -431,7 +440,7 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...) } // Negative asserts that the specified element is negative @@ -443,10 +452,10 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) + return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...) } -func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { +func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } @@ -469,7 +478,7 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare return true } -func containsValue(values []CompareType, value CompareType) bool { +func containsValue(values []compareResult, value compareResult) bool { for _, v := range values { if v == value { return true diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 3ddab109ad..1906341657 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -104,8 +104,8 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// EqualValuesf asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. // // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { @@ -186,7 +186,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -568,6 +568,23 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) } +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// assert.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) +} + // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // @@ -604,7 +621,16 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + +// NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index a84e09bd40..21629087ba 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -186,8 +186,8 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface return EqualExportedValuesf(a.t, expected, actual, msg, args...) } -// EqualValues asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. // // a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { @@ -197,8 +197,8 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn return EqualValues(a.t, expected, actual, msgAndArgs...) } -// EqualValuesf asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { @@ -336,7 +336,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // a.EventuallyWithT(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -361,7 +361,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor // a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1128,6 +1128,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin return NotContainsf(a.t, s, contains, msg, args...) } +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true +// +// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true +func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotElementsMatchf(a.t, listA, listB, msg, args...) +} + // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // @@ -1200,7 +1234,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str return NotEqualf(a.t, expected, actual, msg, args...) } -// NotErrorIs asserts that at none of the errors in err's chain matches target. +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAsf(a.t, err, target, msg, args...) +} + +// NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -1209,7 +1261,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface return NotErrorIs(a.t, err, target, msgAndArgs...) } -// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 00df62a059..1d2f71824a 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -6,7 +6,7 @@ import ( ) // isOrdered checks that collection contains orderable elements. -func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { +func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { return false @@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing @@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing @@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing @@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) + return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 0b7570f21c..4e91332bb5 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -19,7 +19,9 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" - "gopkg.in/yaml.v3" + + // Wrapper around gopkg.in/yaml.v3 + "github.com/stretchr/testify/assert/yaml" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" @@ -45,6 +47,10 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool +// PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful +// for table driven tests. +type PanicAssertionFunc = func(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool + // Comparison is a custom function that returns true on success and false on failure type Comparison func() (success bool) @@ -496,7 +502,13 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b h.Helper() } - if !samePointers(expected, actual) { + same, ok := samePointers(expected, actual) + if !ok { + return Fail(t, "Both arguments must be pointers", msgAndArgs...) + } + + if !same { + // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ "expected: %p %#v\n"+ "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) @@ -516,7 +528,13 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} h.Helper() } - if samePointers(expected, actual) { + same, ok := samePointers(expected, actual) + if !ok { + //fails when the arguments are not pointers + return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) + } + + if same { return Fail(t, fmt.Sprintf( "Expected and actual point to the same object: %p %#v", expected, expected), msgAndArgs...) @@ -524,21 +542,23 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} return true } -// samePointers compares two generic interface objects and returns whether -// they point to the same object -func samePointers(first, second interface{}) bool { +// samePointers checks if two generic interface objects are pointers of the same +// type pointing to the same object. It returns two values: same indicating if +// they are the same type and point to the same object, and ok indicating that +// both inputs are pointers. +func samePointers(first, second interface{}) (same bool, ok bool) { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { - return false + return false, false //not both are pointers } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) if firstType != secondType { - return false + return false, true // both are pointers, but of different types } // compare pointer addresses - return first == second + return first == second, true } // formatUnequalValues takes two values of arbitrary types and returns string @@ -572,8 +592,8 @@ func truncatingFormat(data interface{}) string { return value } -// EqualValues asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { @@ -615,21 +635,6 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs .. return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } - if aType.Kind() == reflect.Ptr { - aType = aType.Elem() - } - if bType.Kind() == reflect.Ptr { - bType = bType.Elem() - } - - if aType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) - } - - if bType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) - } - expected = copyExportedFields(expected) actual = copyExportedFields(actual) @@ -1170,6 +1175,39 @@ func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) stri return msg.String() } +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true +// +// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +func NotElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if isEmpty(listA) && isEmpty(listB) { + return Fail(t, "listA and listB contain the same elements", msgAndArgs) + } + + if !isList(t, listA, msgAndArgs...) { + return Fail(t, "listA is not a list type", msgAndArgs...) + } + if !isList(t, listB, msgAndArgs...) { + return Fail(t, "listB is not a list type", msgAndArgs...) + } + + extraA, extraB := diffLists(listA, listB) + if len(extraA) == 0 && len(extraB) == 0 { + return Fail(t, "listA and listB contain the same elements", msgAndArgs) + } + + return true +} + // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -1488,6 +1526,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd if err != nil { return Fail(t, err.Error(), msgAndArgs...) } + if math.IsNaN(actualEpsilon) { + return Fail(t, "relative error is NaN", msgAndArgs...) + } if actualEpsilon > epsilon { return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) @@ -1611,7 +1652,6 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { - var r *regexp.Regexp if rr, ok := rx.(*regexp.Regexp); ok { r = rr @@ -1619,7 +1659,14 @@ func matchRegexp(rx interface{}, str interface{}) bool { r = regexp.MustCompile(fmt.Sprint(rx)) } - return (r.FindStringIndex(fmt.Sprint(str)) != nil) + switch v := str.(type) { + case []byte: + return r.Match(v) + case string: + return r.MatchString(v) + default: + return r.MatchString(fmt.Sprint(v)) + } } @@ -1872,7 +1919,7 @@ var spewConfigStringerEnabled = spew.ConfigState{ MaxDepth: 10, } -type tHelper interface { +type tHelper = interface { Helper() } @@ -1911,6 +1958,9 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // CollectT implements the TestingT interface and collects all errors. type CollectT struct { + // A slice of errors. Non-nil slice denotes a failure. + // If it's non-nil but len(c.errors) == 0, this is also a failure + // obtained by direct c.FailNow() call. errors []error } @@ -1919,9 +1969,10 @@ func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) } -// FailNow panics. -func (*CollectT) FailNow() { - panic("Assertion failed") +// FailNow stops execution by calling runtime.Goexit. +func (c *CollectT) FailNow() { + c.fail() + runtime.Goexit() } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. @@ -1934,6 +1985,16 @@ func (*CollectT) Copy(TestingT) { panic("Copy() is deprecated") } +func (c *CollectT) fail() { + if !c.failed() { + c.errors = []error{} // Make it non-nil to mark a failure. + } +} + +func (c *CollectT) failed() bool { + return c.errors != nil +} + // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition @@ -1951,14 +2012,14 @@ func (*CollectT) Copy(TestingT) { // assert.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } var lastFinishedTickErrs []error - ch := make(chan []error, 1) + ch := make(chan *CollectT, 1) timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1978,16 +2039,16 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time go func() { collect := new(CollectT) defer func() { - ch <- collect.errors + ch <- collect }() condition(collect) }() - case errs := <-ch: - if len(errs) == 0 { + case collect := <-ch: + if !collect.failed() { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. - lastFinishedTickErrs = errs + lastFinishedTickErrs = collect.errors tick = ticker.C } } @@ -2049,7 +2110,7 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { ), msgAndArgs...) } -// NotErrorIs asserts that at none of the errors in err's chain matches target. +// NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -2090,6 +2151,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ ), msgAndArgs...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %q\n"+ + "in chain: %s", target, chain, + ), msgAndArgs...) +} + func buildErrorChainString(err error) string { if err == nil { return "" diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go new file mode 100644 index 0000000000..baa0cc7d7f --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go @@ -0,0 +1,25 @@ +//go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default +// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default + +// Package yaml is an implementation of YAML functions that calls a pluggable implementation. +// +// This implementation is selected with the testify_yaml_custom build tag. +// +// go test -tags testify_yaml_custom +// +// This implementation can be used at build time to replace the default implementation +// to avoid linking with [gopkg.in/yaml.v3]. +// +// In your test package: +// +// import assertYaml "github.com/stretchr/testify/assert/yaml" +// +// func init() { +// assertYaml.Unmarshal = func (in []byte, out interface{}) error { +// // ... +// return nil +// } +// } +package yaml + +var Unmarshal func(in []byte, out interface{}) error diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go new file mode 100644 index 0000000000..b83c6cf64c --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go @@ -0,0 +1,37 @@ +//go:build !testify_yaml_fail && !testify_yaml_custom +// +build !testify_yaml_fail,!testify_yaml_custom + +// Package yaml is just an indirection to handle YAML deserialization. +// +// This package is just an indirection that allows the builder to override the +// indirection with an alternative implementation of this package that uses +// another implementation of YAML deserialization. This allows to not either not +// use YAML deserialization at all, or to use another implementation than +// [gopkg.in/yaml.v3] (for example for license compatibility reasons, see [PR #1120]). +// +// Alternative implementations are selected using build tags: +// +// - testify_yaml_fail: [Unmarshal] always fails with an error +// - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it +// before calling any of [github.com/stretchr/testify/assert.YAMLEq] or +// [github.com/stretchr/testify/assert.YAMLEqf]. +// +// Usage: +// +// go test -tags testify_yaml_fail +// +// You can check with "go list" which implementation is linked: +// +// go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml +// go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml +// go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml +// +// [PR #1120]: https://github.com/stretchr/testify/pull/1120 +package yaml + +import goyaml "gopkg.in/yaml.v3" + +// Unmarshal is just a wrapper of [gopkg.in/yaml.v3.Unmarshal]. +func Unmarshal(in []byte, out interface{}) error { + return goyaml.Unmarshal(in, out) +} diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go new file mode 100644 index 0000000000..e78f7dfe69 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go @@ -0,0 +1,18 @@ +//go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default +// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default + +// Package yaml is an implementation of YAML functions that always fail. +// +// This implementation can be used at build time to replace the default implementation +// to avoid linking with [gopkg.in/yaml.v3]: +// +// go test -tags testify_yaml_fail +package yaml + +import "errors" + +var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") + +func Unmarshal([]byte, interface{}) error { + return errNotImplemented +} diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 506a82f807..d8921950d7 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -34,9 +34,9 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// require.Contains(t, "Hello World", "World") +// require.Contains(t, ["Hello", "World"], "World") +// require.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -50,9 +50,9 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// require.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// require.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// require.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -91,7 +91,7 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // -// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +// require.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -106,7 +106,7 @@ func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // -// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +// require.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -120,7 +120,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// require.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -134,7 +134,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// require.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +147,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// require.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -166,7 +166,7 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // and that it is equal to the provided error. // // actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// require.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -181,7 +181,7 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // and that it is equal to the provided error. // // actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// require.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -200,8 +200,8 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args // Exported int // notExported int // } -// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true -// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +// require.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// require.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -220,8 +220,8 @@ func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, m // Exported int // notExported int // } -// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true -// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +// require.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// require.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -232,10 +232,10 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, t.FailNow() } -// EqualValues asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// require.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -246,10 +246,10 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg t.FailNow() } -// EqualValuesf asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// require.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -262,7 +262,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// require.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -280,8 +280,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) +// if require.Error(t, err) { +// require.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -321,7 +321,7 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // and that the error contains the specified substring. // // actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// require.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -336,7 +336,7 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // and that the error contains the specified substring. // // actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// require.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -374,8 +374,8 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) +// if require.Errorf(t, err, "error message %s", "formatted") { +// require.Equal(t, expectedErrorf, err) // } func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -390,7 +390,7 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// require.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -415,10 +415,10 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// require.EventuallyWithT(t, func(c *require.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// require.True(c, externalValue, "expected 'externalValue' to be true") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -443,10 +443,10 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c *require.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// require.True(c, externalValue, "expected 'externalValue' to be true") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -460,7 +460,7 @@ func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), wait // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// require.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -473,7 +473,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// require.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -486,7 +486,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// require.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -543,7 +543,7 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// require.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -556,7 +556,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// require.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -593,9 +593,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// require.Greater(t, 2, 1) +// require.Greater(t, float64(2), float64(1)) +// require.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -608,10 +608,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// require.GreaterOrEqual(t, 2, 1) +// require.GreaterOrEqual(t, 2, 2) +// require.GreaterOrEqual(t, "b", "a") +// require.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -624,10 +624,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// require.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// require.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// require.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// require.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -640,9 +640,9 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// require.Greaterf(t, 2, 1, "error message %s", "formatted") +// require.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// require.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -656,7 +656,7 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// require.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -672,7 +672,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// require.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -688,7 +688,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// require.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -704,7 +704,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// require.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -719,7 +719,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -734,7 +734,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -749,7 +749,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -764,7 +764,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// require.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -779,7 +779,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// require.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -794,7 +794,7 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// require.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -809,7 +809,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// require.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -824,7 +824,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// require.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -839,7 +839,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// require.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -852,7 +852,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// require.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -865,7 +865,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// require.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -922,7 +922,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// require.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -979,9 +979,9 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// require.IsDecreasing(t, []int{2, 1, 0}) +// require.IsDecreasing(t, []float{2, 1}) +// require.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -994,9 +994,9 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// require.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// require.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// require.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1009,9 +1009,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// require.IsIncreasing(t, []int{1, 2, 3}) +// require.IsIncreasing(t, []float{1, 2}) +// require.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1024,9 +1024,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// require.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// require.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// require.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1039,9 +1039,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// require.IsNonDecreasing(t, []int{1, 1, 2}) +// require.IsNonDecreasing(t, []float{1, 2}) +// require.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1054,9 +1054,9 @@ func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// require.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1069,9 +1069,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// require.IsNonIncreasing(t, []int{2, 1, 1}) +// require.IsNonIncreasing(t, []float{2, 1}) +// require.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1084,9 +1084,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// require.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1121,7 +1121,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1134,7 +1134,7 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// require.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1148,7 +1148,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// require.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1162,7 +1162,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// require.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1175,9 +1175,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// require.Less(t, 1, 2) +// require.Less(t, float64(1), float64(2)) +// require.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1190,10 +1190,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// require.LessOrEqual(t, 1, 2) +// require.LessOrEqual(t, 2, 2) +// require.LessOrEqual(t, "a", "b") +// require.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1206,10 +1206,10 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// require.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// require.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// require.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// require.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1222,9 +1222,9 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// require.Lessf(t, 1, 2, "error message %s", "formatted") +// require.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// require.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1237,8 +1237,8 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// require.Negative(t, -1) +// require.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1251,8 +1251,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// require.Negativef(t, -1, "error message %s", "formatted") +// require.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1266,7 +1266,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// require.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1280,7 +1280,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// require.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1293,7 +1293,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// require.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1306,7 +1306,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// require.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1344,8 +1344,8 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) +// if require.NoError(t, err) { +// require.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1360,8 +1360,8 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) +// if require.NoErrorf(t, err, "error message %s", "formatted") { +// require.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1400,9 +1400,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// require.NotContains(t, "Hello World", "Earth") +// require.NotContains(t, ["Hello", "World"], "Earth") +// require.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1416,9 +1416,9 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// require.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// require.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// require.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1429,11 +1429,51 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a t.FailNow() } +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true +// +// require.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true +func NotElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotElementsMatch(t, listA, listB, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// require.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotElementsMatchf(t, listA, listB, msg, args...) { + return + } + t.FailNow() +} + // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) +// if require.NotEmpty(t, obj) { +// require.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1448,8 +1488,8 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) +// if require.NotEmptyf(t, obj, "error message %s", "formatted") { +// require.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1463,7 +1503,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// require.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1479,7 +1519,7 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// require.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1492,7 +1532,7 @@ func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAnd // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// require.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1505,7 +1545,7 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// require.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1519,7 +1559,31 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotErrorIs asserts that at none of the errors in err's chain matches target. +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + +// NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1531,7 +1595,7 @@ func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) t.FailNow() } -// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { @@ -1545,7 +1609,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotImplements asserts that an object does not implement the specified interface. // -// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) +// require.NotImplements(t, (*MyInterface)(nil), new(MyObject)) func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1558,7 +1622,7 @@ func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, // NotImplementsf asserts that an object does not implement the specified interface. // -// assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// require.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1571,7 +1635,7 @@ func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// require.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1584,7 +1648,7 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// require.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1597,7 +1661,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// require.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1610,7 +1674,7 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// require.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1623,8 +1687,8 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// require.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// require.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1637,8 +1701,8 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// require.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// require.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1651,7 +1715,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// require.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1667,7 +1731,7 @@ func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// require.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1685,8 +1749,8 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // contain all elements given in the specified subset list(array, slice...) or // map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2]) -// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) +// require.NotSubset(t, [1, 3, 4], [1, 2]) +// require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1701,8 +1765,8 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // contain all elements given in the specified subset list(array, slice...) or // map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") -// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") +// require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1737,7 +1801,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// require.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1752,7 +1816,7 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// require.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1767,7 +1831,7 @@ func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAn // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// require.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1781,7 +1845,7 @@ func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// require.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1795,7 +1859,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// require.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1808,7 +1872,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// require.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1821,8 +1885,8 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// require.Positive(t, 1) +// require.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1835,8 +1899,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// require.Positivef(t, 1, "error message %s", "formatted") +// require.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1849,8 +1913,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// require.Regexp(t, regexp.MustCompile("start"), "it's starting") +// require.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1863,8 +1927,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// require.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// require.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1877,7 +1941,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// require.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1893,7 +1957,7 @@ func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// require.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1910,8 +1974,8 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subset asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2]) -// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) +// require.Subset(t, [1, 2, 3], [1, 2]) +// require.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1925,8 +1989,8 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // Subsetf asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") -// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") +// require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1939,7 +2003,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // True asserts that the specified value is true. // -// assert.True(t, myBool) +// require.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1952,7 +2016,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// require.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1965,7 +2029,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// require.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1978,7 +2042,7 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// require.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1991,7 +2055,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// require.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2004,7 +2068,7 @@ func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, m // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// require.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl index 55e42ddebd..8b32836850 100644 --- a/vendor/github.com/stretchr/testify/require/require.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -1,4 +1,4 @@ -{{.Comment}} +{{ replace .Comment "assert." "require."}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index eee8310a5f..1bd87304f4 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -187,8 +187,8 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface EqualExportedValuesf(a.t, expected, actual, msg, args...) } -// EqualValues asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValues asserts that two objects are equal or convertible to the larger +// type and equal. // // a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { @@ -198,8 +198,8 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn EqualValues(a.t, expected, actual, msgAndArgs...) } -// EqualValuesf asserts that two objects are equal or convertible to the same types -// and equal. +// EqualValuesf asserts that two objects are equal or convertible to the larger +// type and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { @@ -337,7 +337,7 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti // a.EventuallyWithT(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -362,7 +362,7 @@ func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), w // a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1129,6 +1129,40 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin NotContainsf(a.t, s, contains, msg, args...) } +// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false +// +// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true +// +// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true +func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should not match. +// This is an inverse of ElementsMatch. +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false +// +// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true +// +// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true +func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotElementsMatchf(a.t, listA, listB, msg, args...) +} + // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // @@ -1201,7 +1235,25 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str NotEqualf(a.t, expected, actual, msg, args...) } -// NotErrorIs asserts that at none of the errors in err's chain matches target. +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAsf(a.t, err, target, msg, args...) +} + +// NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { @@ -1210,7 +1262,7 @@ func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface NotErrorIs(a.t, err, target, msgAndArgs...) } -// NotErrorIsf asserts that at none of the errors in err's chain matches target. +// NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go index 91772dfeb9..6b7ce929eb 100644 --- a/vendor/github.com/stretchr/testify/require/requirements.go +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -6,7 +6,7 @@ type TestingT interface { FailNow() } -type tHelper interface { +type tHelper = interface { Helper() } diff --git a/vendor/github.com/stretchr/testify/suite/doc.go b/vendor/github.com/stretchr/testify/suite/doc.go index 8d55a3aa89..05a562f721 100644 --- a/vendor/github.com/stretchr/testify/suite/doc.go +++ b/vendor/github.com/stretchr/testify/suite/doc.go @@ -5,6 +5,8 @@ // or individual tests (depending on which interface(s) you // implement). // +// The suite package does not support parallel tests. See [issue 934]. +// // A testing suite is usually built by first extending the built-in // suite functionality from suite.Suite in testify. Alternatively, // you could reproduce that logic on your own if you wanted (you @@ -63,4 +65,6 @@ // func TestExampleTestSuite(t *testing.T) { // suite.Run(t, new(ExampleTestSuite)) // } +// +// [issue 934]: https://github.com/stretchr/testify/issues/934 package suite diff --git a/vendor/modules.txt b/vendor/modules.txt index f8700bbe01..d0b2b635d8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -37,8 +37,8 @@ github.com/Microsoft/go-winio/internal/stringbuffer github.com/Microsoft/go-winio/pkg/guid # github.com/Netflix/go-expect v0.0.0-20201125194554-85d881c3777e ## explicit; go 1.13 -# github.com/ProtonMail/go-crypto v1.0.0 -## explicit; go 1.13 +# github.com/ProtonMail/go-crypto v1.1.3 +## explicit; go 1.17 github.com/ProtonMail/go-crypto/bitcurves github.com/ProtonMail/go-crypto/brainpool github.com/ProtonMail/go-crypto/eax @@ -49,6 +49,8 @@ github.com/ProtonMail/go-crypto/openpgp/aes/keywrap github.com/ProtonMail/go-crypto/openpgp/armor github.com/ProtonMail/go-crypto/openpgp/ecdh github.com/ProtonMail/go-crypto/openpgp/ecdsa +github.com/ProtonMail/go-crypto/openpgp/ed25519 +github.com/ProtonMail/go-crypto/openpgp/ed448 github.com/ProtonMail/go-crypto/openpgp/eddsa github.com/ProtonMail/go-crypto/openpgp/elgamal github.com/ProtonMail/go-crypto/openpgp/errors @@ -57,6 +59,8 @@ github.com/ProtonMail/go-crypto/openpgp/internal/ecc github.com/ProtonMail/go-crypto/openpgp/internal/encoding github.com/ProtonMail/go-crypto/openpgp/packet github.com/ProtonMail/go-crypto/openpgp/s2k +github.com/ProtonMail/go-crypto/openpgp/x25519 +github.com/ProtonMail/go-crypto/openpgp/x448 # github.com/PuerkitoBio/purell v1.1.1 ## explicit github.com/PuerkitoBio/purell @@ -187,8 +191,8 @@ github.com/containerd/console # github.com/creack/pty v1.1.11 ## explicit; go 1.13 github.com/creack/pty -# github.com/cyphar/filepath-securejoin v0.2.4 -## explicit; go 1.13 +# github.com/cyphar/filepath-securejoin v0.3.6 +## explicit; go 1.18 github.com/cyphar/filepath-securejoin # github.com/dave/jennifer v0.18.0 ## explicit @@ -236,16 +240,16 @@ github.com/go-git/gcfg github.com/go-git/gcfg/scanner github.com/go-git/gcfg/token github.com/go-git/gcfg/types -# github.com/go-git/go-billy/v5 v5.5.0 -## explicit; go 1.19 +# github.com/go-git/go-billy/v5 v5.6.1 +## explicit; go 1.21 github.com/go-git/go-billy/v5 github.com/go-git/go-billy/v5/helper/chroot github.com/go-git/go-billy/v5/helper/polyfill github.com/go-git/go-billy/v5/memfs github.com/go-git/go-billy/v5/osfs github.com/go-git/go-billy/v5/util -# github.com/go-git/go-git/v5 v5.12.0 -## explicit; go 1.19 +# github.com/go-git/go-git/v5 v5.13.1 +## explicit; go 1.21 github.com/go-git/go-git/v5 github.com/go-git/go-git/v5/config github.com/go-git/go-git/v5/internal/path_util @@ -588,7 +592,7 @@ github.com/shirou/gopsutil/v3/process # github.com/shoenig/go-m1cpu v0.1.6 ## explicit; go 1.20 github.com/shoenig/go-m1cpu -# github.com/skeema/knownhosts v1.2.2 +# github.com/skeema/knownhosts v1.3.0 ## explicit; go 1.17 github.com/skeema/knownhosts # github.com/skratchdot/open-golang v0.0.0-20190104022628-a2dfa6d0dab6 @@ -608,9 +612,10 @@ github.com/spf13/cobra github.com/spf13/pflag # github.com/stretchr/objx v0.5.2 ## explicit; go 1.20 -# github.com/stretchr/testify v1.9.0 +# github.com/stretchr/testify v1.10.0 ## explicit; go 1.17 github.com/stretchr/testify/assert +github.com/stretchr/testify/assert/yaml github.com/stretchr/testify/require github.com/stretchr/testify/suite # github.com/thoas/go-funk v0.8.0 From 988832b2725f956525baa2a10521f9e10e4df9ff Mon Sep 17 00:00:00 2001 From: mitchell Date: Fri, 10 Jan 2025 09:34:44 -0500 Subject: [PATCH 440/440] Solver V3 for Ruby now works on the Platform. --- test/integration/install_int_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/install_int_test.go b/test/integration/install_int_test.go index f7c0a2d3c5..eb939642ca 100644 --- a/test/integration/install_int_test.go +++ b/test/integration/install_int_test.go @@ -228,7 +228,7 @@ func (suite *InstallIntegrationTestSuite) TestInstall_SolverV3() { "Ruby", "ActiveState-CLI/ruby-V3#6db5b307-d63a-45e2-9d3b-70a1a1f6c10a", "base64", - true, + false, }, }