From 60dbbc53ac2de49a61a3ae71af948e2237dcd301 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 30 May 2023 09:18:58 -0800 Subject: [PATCH 01/69] Release v1.2.2 --- CHANGELOG.md | 9 +++++++++ helm/Chart.yaml | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f062393..015a7bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [1.2.2](https://github.com/coder/code-marketplace/releases/tag/v1.2.2) - 2023-05-30 + +### Changed + +- Help/usage outputs the binary name as `code-marketplace` instead of + `marketplace` to be consistent with documentation. +- Binary is symlinked into /usr/local/bin in the Docker image so it can be + invoked as simply `code-marketplace`. + ## [1.2.1](https://github.com/coder/code-marketplace/releases/tag/v1.2.1) - 2022-10-31 ### Fixed diff --git a/helm/Chart.yaml b/helm/Chart.yaml index d64d574..df8d240 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.1 +version: 0.3.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.2.1" +appVersion: "v1.2.2" From d484d278478cc5d064d7016de27ac4df7110add1 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 30 May 2023 09:36:20 -0800 Subject: [PATCH 02/69] Add details around EXTENSIONS_GALLERY Also tweak the wording on the air-gapped paragraph, swap an `as` for an `in`, remove an accidental newline in a command, and avoid parenthesis. --- README.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index beb2594..5a27b71 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,12 @@ The Code Extension Marketplace is an open-source alternative to the VS Code Marketplace for use in editors like -[code-server](https://github.com/coder/code-server) or [VSCodium](https://github.com/VSCodium/vscodium). +[code-server](https://github.com/coder/code-server) or [VSCodium](https://github.com/VSCodium/vscodium). -It is maintained by [Coder](https://www.coder.com) and is used by our -enterprise customers in regulated and security-conscious industries like -banking, asset management, military, and intelligence where they deploy -Coder in an air-gapped network. Accessing an Internet-hosted marketplace -is not allowed. +It is maintained by [Coder](https://www.coder.com) and is used by our enterprise +customers in regulated and security-conscious industries like banking, asset +management, military, and intelligence where they deploy Coder in an air-gapped +network and accessing an internet-hosted marketplace is not allowed. This marketplace reads extensions from file storage and provides an API for editors to consume. It does not have a frontend or any mechanisms for extension @@ -64,7 +63,7 @@ export ARTIFACTORY_TOKEN="my-token" ./code-marketplace [command] --artifactory http://artifactory.server/artifactory --repo extensions ``` -The token will be used as the `Authorization` header with the value `Bearer +The token will be used in the `Authorization` header with the value `Bearer `. ### Exposing the marketplace @@ -122,14 +121,13 @@ For example to add the Python extension from Open VSX: Or the Vim extension from GitHub: ```console -./code-marketplace add -https://github.com/VSCodeVim/Vim/releases/download/v1.24.1/vim-1.24.1.vsix [flags] +./code-marketplace add https://github.com/VSCodeVim/Vim/releases/download/v1.24.1/vim-1.24.1.vsix [flags] ``` ## Removing extensions -Extensions can be removed from the marketplace by ID and version (or use `--all` -to remove all versions). +Extensions can be removed from the marketplace by ID and version or `--all` to +remove all versions. ```console ./code-marketplace remove ms-python.python-2022.14.0 [flags] @@ -138,6 +136,22 @@ to remove all versions). ## Usage in code-server +You can point code-server to your marketplace by setting the +`EXTENSIONS_GALLERY` environment variable. + +The value of this variable is a JSON blob that specifies the service URL, item +URL, and resource URL template. + +- `serviceURL`: specifies the location of the API (`https:///api`). +- `itemURL`: the frontend for extensions which is currently just a mostly blank + page that says "not supported" (`https:///item`) +- `resourceURLTemplate`: used to download web extensions like Vim; code-server + itself will replace the `{publisher}`, `{name}`, `{version}`, and `{path}` + template variables so use them verbatim + (`https:///files/{publisher}/{name}/{version}/{path}`). + +For example (replace `` with your marketplace's domain): + ```console export EXTENSIONS_GALLERY='{"serviceUrl":"https:///api", "itemUrl":"https:///item", "resourceUrlTemplate": "https:///files/{publisher}/{name}/{version}/{path}"}' code-server From f2cf923a6b9910ec2e5b18b04b792376612cfd42 Mon Sep 17 00:00:00 2001 From: Dov Benyomin Sohacheski Date: Tue, 20 Jun 2023 22:35:02 +0300 Subject: [PATCH 03/69] =?UTF-8?q?=F0=9F=93=9D=20Add=20example=20of=20conne?= =?UTF-8?q?cting=20with=20other=20IDEs=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 5a27b71..396f8d6 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,13 @@ code-server If code-server reports content security policy errors ensure that the marketplace is running behind an https URL. +## Usage in VS Code & VSCodium + +Although not officially supported, you can follow the examples below to start using code-marketplace with VS Code and VSCodium: + +- [VS Code](https://github.com/eclipse/openvsx/wiki/Using-Open-VSX-in-VS-Code) +- [VSCodium](https://github.com/VSCodium/vscodium/blob/master/DOCS.md#how-to-use-the-vs-code-marketplace) + ## Development ```console From 37778ad206d050a0951a3db7eb0e956fb79c4155 Mon Sep 17 00:00:00 2001 From: podedra92 Date: Fri, 22 Sep 2023 23:19:40 +0100 Subject: [PATCH 04/69] Use @ to delineate versions (#24) Co-authored-by: Prakash Odedra Co-authored-by: Asher --- README.md | 2 +- cli/remove.go | 4 ++-- cli/remove_test.go | 2 +- fixtures/generate.bash | 6 +++--- storage/storage.go | 6 +++--- storage/storage_test.go | 20 ++++++++++---------- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 396f8d6..63702ca 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Extensions can be removed from the marketplace by ID and version or `--all` to remove all versions. ```console -./code-marketplace remove ms-python.python-2022.14.0 [flags] +./code-marketplace remove ms-python.python@2022.14.0 [flags] ./code-marketplace remove ms-python.python --all [flags] ``` diff --git a/cli/remove.go b/cli/remove.go index 1c52df7..ddd85c0 100644 --- a/cli/remove.go +++ b/cli/remove.go @@ -29,7 +29,7 @@ func remove() *cobra.Command { Use: "remove ", Short: "Remove an extension from the marketplace", Example: strings.Join([]string{ - " marketplace remove publisher.extension-1.0.0 --extensions-dir ./extensions", + " marketplace remove publisher.extension@1.0.0 --extensions-dir ./extensions", " marketplace remove publisher.extension --all --artifactory http://artifactory.server/artifactory --repo extensions", }, "\n"), Args: cobra.ExactArgs(1), @@ -78,7 +78,7 @@ func remove() *cobra.Command { return xerrors.Errorf("%s.%s has no versions to delete", publisher, name) } else if version == "" && !all { return xerrors.Errorf( - "use %s- to target a specific version or pass --all to delete %s of %s", + "use %s@ to target a specific version or pass --all to delete %s of %s", id, util.Plural(versionCount, "version", ""), id, diff --git a/cli/remove_test.go b/cli/remove_test.go index 90c1e7f..e3bb77d 100644 --- a/cli/remove_test.go +++ b/cli/remove_test.go @@ -108,7 +108,7 @@ func TestRemove(t *testing.T) { id := fmt.Sprintf("%s.%s", test.extension.Publisher, test.extension.Name) if test.version != "" { - id = fmt.Sprintf("%s-%s", id, test.version) + id = fmt.Sprintf("%s@%s", id, test.version) } cmd := cli.Root() diff --git a/fixtures/generate.bash b/fixtures/generate.bash index a27b3a3..5e2099b 100755 --- a/fixtures/generate.bash +++ b/fixtures/generate.bash @@ -105,7 +105,7 @@ EOF cat< "$dest/extension/extension.js" const vscode = require("vscode"); function activate(context) { - vscode.window.showInformationMessage("mock extension $publisher.$name-$version loaded"); + vscode.window.showInformationMessage("mock extension $publisher.$name@$version loaded"); } exports.activate = activate; EOF @@ -121,8 +121,8 @@ mock changelog EOF cp "$dir/icon.png" "$dest/extension/images/icon.png" pushd "$dest" >/dev/null - rm "$publisher.$name-$version.vsix" - zip -r "$publisher.$name-$version.vsix" * -q + rm "$publisher.$name@$version.vsix" + zip -r "$publisher.$name@$version.vsix" * -q popd >/dev/null done < "$dir/versions" done < "$dir/names" diff --git a/storage/storage.go b/storage/storage.go index dcbeb21..90767ee 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -250,16 +250,16 @@ func ExtensionIDFromManifest(manifest *VSIXManifest) string { // ExtensionID returns the full ID of an extension. func ExtensionID(publisher, name, version string) string { - return fmt.Sprintf("%s.%s-%s", publisher, name, version) + return fmt.Sprintf("%s.%s@%s", publisher, name, version) } // ParseExtensionID parses an extension ID into its separate parts: publisher, // name, and version (version may be blank). func ParseExtensionID(id string) (string, string, string, error) { - re := regexp.MustCompile(`^([^.]+)\.([^-]+)-?(.*)$`) + re := regexp.MustCompile(`^([^.]+)\.([^@]+)@?(.*)$`) match := re.FindAllStringSubmatch(id, -1) if match == nil { - return "", "", "", xerrors.Errorf("\"%s\" does not match . or .-", id) + return "", "", "", xerrors.Errorf("\"%s\" does not match . or .@", id) } return match[0][1], match[0][2], match[0][3], nil } diff --git a/storage/storage_test.go b/storage/storage_test.go index 812c60e..02fdd8b 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -230,7 +230,7 @@ func testManifest(t *testing.T, factory storageFactory) { name: "MissingVersion", error: fs.ErrNotExist, extension: testutil.Extensions[0], - version: "some-nonexistent-version", + version: "some-nonexistent@version", }, { name: "MissingExtension", @@ -275,7 +275,7 @@ func testManifest(t *testing.T, factory storageFactory) { // manifest since it is not on the actual manifest on disk. expected.Assets.Asset = append(expected.Assets.Asset, storage.VSIXAsset{ Type: storage.VSIXAssetType, - Path: fmt.Sprintf("%s.%s-%s.vsix", test.extension.Publisher, test.extension.Name, version), + Path: fmt.Sprintf("%s.%s@%s.vsix", test.extension.Publisher, test.extension.Name, version), Addressable: "true", }) require.NoError(t, err) @@ -335,7 +335,7 @@ func testWalkExtensions(t *testing.T, factory storageFactory) { // manifest since it is not on the actual manifest on disk. manifest.Assets.Asset = append(manifest.Assets.Asset, storage.VSIXAsset{ Type: storage.VSIXAssetType, - Path: fmt.Sprintf("%s.%s-%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion), + Path: fmt.Sprintf("%s.%s@%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion), Addressable: "true", }) expected = append(expected, extension{ @@ -734,7 +734,7 @@ func testAddExtension(t *testing.T, factory storageFactory) { // Put a directory in the way of the vsix. f := factory(t) ext := testutil.Extensions[3] - vsixName := fmt.Sprintf("%s.%s-%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion) + vsixName := fmt.Sprintf("%s.%s@%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion) f.write([]byte("foo"), ext.Publisher, ext.Name, ext.LatestVersion, vsixName, "foo") for _, test := range tests { @@ -909,7 +909,7 @@ func TestExtensionID(t *testing.T) { }{ { name: "OK", - expected: "foo.bar-test", + expected: "foo.bar@test", manifest: &storage.VSIXManifest{ Metadata: storage.VSIXMetadata{ Identity: storage.VSIXIdentity{ @@ -948,12 +948,12 @@ func TestParseExtensionID(t *testing.T) { { name: "OK", expected: []string{"foo", "bar", "test"}, - id: "foo.bar-test", + id: "foo.bar@test", }, { name: "VersionWithDots", expected: []string{"foo", "bar", "test.test"}, - id: "foo.bar-test.test", + id: "foo.bar@test.test", }, { name: "EmptyID", @@ -963,12 +963,12 @@ func TestParseExtensionID(t *testing.T) { { name: "MissingPublisher", error: true, - id: ".qux-bar", + id: ".qux@bar", }, { name: "MissingExtension", error: true, - id: "foo.-baz", + id: "foo.@baz", }, { name: "MissingExtensionAndVersion", @@ -983,7 +983,7 @@ func TestParseExtensionID(t *testing.T) { { name: "InvalidID", error: true, - id: "publisher-version", + id: "publisher@version", }, } From 50a786956e9a4a9251bb353d13bca213c284e8f3 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 6 Oct 2023 14:16:36 -0800 Subject: [PATCH 05/69] Encode platform into version On the storage level, the platform is appended to the version number whenever writing or reading from the file system. Everywhere else the version is passed around as a struct with the version number and the platform string. --- api/api.go | 7 +- api/api_test.go | 18 +++ cli/add_test.go | 27 +++- cli/remove.go | 48 +++--- cli/remove_test.go | 62 ++++++-- database/database.go | 5 +- database/database_test.go | 70 +++++++-- database/nodb.go | 34 +++-- storage/artifactory.go | 59 ++++---- storage/local.go | 31 ++-- storage/storage.go | 147 ++++++++++++++++-- storage/storage_test.go | 295 ++++++++++++++++++++++++++++-------- testutil/extensions.go | 39 +++-- testutil/extensions_test.go | 11 +- testutil/mockdb.go | 2 +- testutil/mockstorage.go | 18 ++- util/util.go | 12 +- 17 files changed, 677 insertions(+), 208 deletions(-) diff --git a/api/api.go b/api/api.go index fa369c8..34041b9 100644 --- a/api/api.go +++ b/api/api.go @@ -217,17 +217,20 @@ func (api *API) extensionQuery(rw http.ResponseWriter, r *http.Request) { } func (api *API) assetRedirect(rw http.ResponseWriter, r *http.Request) { - // TODO: Asset URIs can contain a targetPlatform query variable. baseURL := httpapi.RequestBaseURL(r, "/") assetType := storage.AssetType(chi.URLParam(r, "type")) if assetType == "vspackage" { assetType = storage.VSIXAssetType } + version := storage.VersionFromString(chi.URLParam(r, "version")) + if version.TargetPlatform == "" { + version.TargetPlatform = storage.Platform(r.URL.Query().Get("targetPlatform")) + } url, err := api.Database.GetExtensionAssetPath(r.Context(), &database.Asset{ Extension: chi.URLParam(r, "extension"), Publisher: chi.URLParam(r, "publisher"), Type: assetType, - Version: chi.URLParam(r, "version"), + Version: version, }, baseURL) if err != nil && os.IsNotExist(err) { httpapi.Write(rw, http.StatusNotFound, httpapi.ErrorResponse{ diff --git a/api/api_test.go b/api/api_test.go index f5941f0..f511577 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -198,6 +198,24 @@ func TestAPI(t *testing.T) { Status: http.StatusMovedPermanently, Response: "/files/publisher/extension/version/foo", }, + { + Name: "AssetOKPlatform", + Path: "/assets/publisher/extension/version@linux-x64/type", + Status: http.StatusMovedPermanently, + Response: "/files/publisher/extension/version@linux-x64/foo", + }, + { + Name: "AssetOKPlatformQuery", + Path: "/assets/publisher/extension/version/type?targetPlatform=linux-x64", + Status: http.StatusMovedPermanently, + Response: "/files/publisher/extension/version@linux-x64/foo", + }, + { + Name: "AssetOKDuplicatedPlatformQuery", + Path: "/assets/publisher/extension/version@darwin-x64/type?targetPlatform=linux-x64", + Status: http.StatusMovedPermanently, + Response: "/files/publisher/extension/version@darwin-x64/foo", + }, { Name: "DownloadNotExist", Path: "/publishers/notexist/vsextensions/extension/version/vspackage", diff --git a/cli/add_test.go b/cli/add_test.go index fc5a95b..6397213 100644 --- a/cli/add_test.go +++ b/cli/add_test.go @@ -41,6 +41,8 @@ func TestAdd(t *testing.T) { extensions []testutil.Extension // name is the name of the test. name string + // platforms to add for the latest version of each extension. + platforms []storage.Platform // vsixes contains raw bytes of extensions to add. Use for failure cases. vsixes [][]byte }{ @@ -48,10 +50,21 @@ func TestAdd(t *testing.T) { name: "OK", extensions: []testutil.Extension{testutil.Extensions[0]}, }, + { + name: "OKPlatforms", + extensions: []testutil.Extension{testutil.Extensions[0]}, + platforms: []storage.Platform{ + storage.PlatformUnknown, + storage.PlatformWin32X64, + storage.PlatformLinuxX64, + storage.PlatformDarwinX64, + storage.PlatformWeb, + }, + }, { name: "InvalidVSIX", error: "not a valid zip", - vsixes: [][]byte{[]byte{}}, + vsixes: [][]byte{{}}, }, { name: "BulkOK", @@ -72,7 +85,7 @@ func TestAdd(t *testing.T) { testutil.Extensions[3], }, vsixes: [][]byte{ - []byte{}, + {}, []byte("foo"), }, }, @@ -95,7 +108,13 @@ func TestAdd(t *testing.T) { create(vsix) } for _, ext := range test.extensions { - create(testutil.CreateVSIXFromExtension(t, ext)) + if len(test.platforms) > 0 { + for _, platform := range test.platforms { + create(testutil.CreateVSIXFromExtension(t, ext, storage.Version{Version: ext.LatestVersion, TargetPlatform: platform})) + } + } else { + create(testutil.CreateVSIXFromExtension(t, ext, storage.Version{Version: ext.LatestVersion})) + } } // With multiple extensions use bulk add by pointing to the directory @@ -108,7 +127,7 @@ func TestAdd(t *testing.T) { handler := func(rw http.ResponseWriter, r *http.Request) { var vsix []byte if test.vsixes == nil { - vsix = testutil.CreateVSIXFromExtension(t, test.extensions[0]) + vsix = testutil.CreateVSIXFromExtension(t, test.extensions[0], storage.Version{Version: test.extensions[0].LatestVersion}) } else { vsix = test.vsixes[0] } diff --git a/cli/remove.go b/cli/remove.go index ddd85c0..2fd4ef2 100644 --- a/cli/remove.go +++ b/cli/remove.go @@ -56,13 +56,14 @@ func remove() *cobra.Command { return err } - id := args[0] - publisher, name, version, err := storage.ParseExtensionID(id) + targetId := args[0] + publisher, name, versionStr, err := storage.ParseExtensionID(targetId) if err != nil { return err } - if version != "" && all { + version := storage.Version{Version: versionStr} + if version.Version != "" && all { return xerrors.Errorf("cannot specify both --all and version %s", version) } @@ -72,34 +73,39 @@ func remove() *cobra.Command { } versionCount := len(allVersions) - if !all && version != "" && !util.Contains(allVersions, version) { - return xerrors.Errorf("%s does not exist", id) - } else if versionCount == 0 { - return xerrors.Errorf("%s.%s has no versions to delete", publisher, name) - } else if version == "" && !all { + if version.Version == "" && !all { return xerrors.Errorf( "use %s@ to target a specific version or pass --all to delete %s of %s", - id, + targetId, util.Plural(versionCount, "version", ""), - id, + targetId, ) } - err = store.RemoveExtension(ctx, publisher, name, version) - if err != nil { - return err - } - summary := []string{} + // TODO: Allow deleting by platform as well? + var toDelete []storage.Version if all { - removedCount := len(allVersions) - summary = append(summary, fmt.Sprintf("Removed %s", util.Plural(removedCount, "version", ""))) - for _, version := range allVersions { - summary = append(summary, fmt.Sprintf(" - %s", version)) - } + toDelete = allVersions } else { - summary = append(summary, fmt.Sprintf("Removed %s", version)) + for _, sv := range allVersions { + if version.Version == sv.Version { + toDelete = append(toDelete, sv) + } + } + } + if len(toDelete) == 0 { + return xerrors.Errorf("%s does not exist", targetId) } + summary := []string{fmt.Sprintf("Removed %s", util.Plural(len(toDelete), "version", ""))} + for _, delete := range toDelete { + err = store.RemoveExtension(ctx, publisher, name, delete) + if err != nil { + summary = append(summary, fmt.Sprintf(" - %s (%s)", delete, err)) + } else { + summary = append(summary, fmt.Sprintf(" - %s", delete)) + } + } _, err = fmt.Fprintln(cmd.OutOrStdout(), strings.Join(summary, "\n")) return err }, diff --git a/cli/remove_test.go b/cli/remove_test.go index e3bb77d..1fc2ec4 100644 --- a/cli/remove_test.go +++ b/cli/remove_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/coder/code-marketplace/cli" + "github.com/coder/code-marketplace/storage" "github.com/coder/code-marketplace/testutil" ) @@ -36,8 +37,11 @@ func TestRemove(t *testing.T) { all bool // error is the expected error. error string - // extension is the extension to remove. testutil.Extensions[0] will be - // added with versions a, b, and c before each test. + // expected contains the versions should have been deleted. It is ignored + // in the case of an expected error. + expected []storage.Version + // extension is the extension to remove. Every version of + // testutil.Extensions[0] will be added before each test. extension testutil.Extension // name is the name of the test. name string @@ -47,12 +51,41 @@ func TestRemove(t *testing.T) { { name: "RemoveOne", extension: testutil.Extensions[0], + version: "2.0.0", + expected: []storage.Version{ + {Version: "2.0.0"}, + }, + }, + { + name: "RemovePlatforms", + extension: testutil.Extensions[0], version: testutil.Extensions[0].LatestVersion, + expected: []storage.Version{ + {Version: "3.0.0"}, + {Version: "3.0.0", TargetPlatform: storage.PlatformAlpineX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformDarwinX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxArm64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformWin32X64}, + }, }, { name: "All", extension: testutil.Extensions[0], all: true, + expected: []storage.Version{ + {Version: "1.0.0"}, + {Version: "1.0.0", TargetPlatform: storage.PlatformWin32X64}, + {Version: "1.5.2"}, + {Version: "2.0.0"}, + {Version: "2.2.2"}, + {Version: "3.0.0"}, + {Version: "3.0.0", TargetPlatform: storage.PlatformAlpineX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformDarwinX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxArm64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformWin32X64}, + }, }, { name: "MissingTarget", @@ -61,7 +94,7 @@ func TestRemove(t *testing.T) { }, { name: "MissingTargetNoVersions", - error: "has no versions", + error: "target a specific version or pass --all", extension: testutil.Extensions[1], }, { @@ -85,10 +118,19 @@ func TestRemove(t *testing.T) { }, { name: "AllNoVersions", - error: "has no versions", + error: "does not exist", extension: testutil.Extensions[1], all: true, }, + { + // Cannot target specific platforms at the moment. If we wanted this + // we would likely need to use a `--platform` flag since we already use @ + // to delineate the version. + name: "NoPlatformTarget", + error: "does not exist", + extension: testutil.Extensions[0], + version: "1.0.0@win32-x64", + }, } for _, test := range tests { @@ -99,7 +141,7 @@ func TestRemove(t *testing.T) { extdir := t.TempDir() ext := testutil.Extensions[0] for _, version := range ext.Versions { - manifestPath := filepath.Join(extdir, ext.Publisher, ext.Name, version, "extension.vsixmanifest") + manifestPath := filepath.Join(extdir, ext.Publisher, ext.Name, version.String(), "extension.vsixmanifest") err := os.MkdirAll(filepath.Dir(manifestPath), 0o755) require.NoError(t, err) err = os.WriteFile(manifestPath, testutil.ConvertExtensionToManifestBytes(t, ext, version), 0o644) @@ -128,13 +170,9 @@ func TestRemove(t *testing.T) { require.Regexp(t, test.error, err.Error()) } else { require.NoError(t, err) - if test.all { - require.Contains(t, output, fmt.Sprintf("Removed %d versions", len(test.extension.Versions))) - for _, version := range test.extension.Versions { - require.Contains(t, output, fmt.Sprintf(" - %s", version)) - } - } else { - require.Contains(t, output, fmt.Sprintf("Removed %s", test.version)) + require.Contains(t, output, fmt.Sprintf("Removed %d version", len(test.expected))) + for _, version := range test.expected { + require.Contains(t, output, fmt.Sprintf(" - %s\n", version)) } } }) diff --git a/database/database.go b/database/database.go index ed84160..e164495 100644 --- a/database/database.go +++ b/database/database.go @@ -123,13 +123,12 @@ type ExtPublisher struct { // ExtVersion implements IRawGalleryExtensionVersion. // https://github.com/microsoft/vscode/blob/29234f0219bdbf649d6107b18651a1038d6357ac/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L42-L50 type ExtVersion struct { - Version string `json:"version"` + storage.Version LastUpdated time.Time `json:"lastUpdated"` AssetURI string `json:"assetUri"` FallbackAssetURI string `json:"fallbackAssetUri"` Files []ExtFile `json:"files"` Properties []ExtProperty `json:"properties,omitempty"` - TargetPlatform string `json:"targetPlatform,omitempty"` } // ExtFile implements IRawGalleryExtensionFile. @@ -157,7 +156,7 @@ type Asset struct { Extension string Publisher string Type storage.AssetType - Version string + Version storage.Version } type Database interface { diff --git a/database/database_test.go b/database/database_test.go index fbb4405..74e2d5d 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -11,6 +11,7 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" "github.com/coder/code-marketplace/database" + "github.com/coder/code-marketplace/storage" "github.com/coder/code-marketplace/testutil" ) @@ -28,45 +29,87 @@ func TestGetExtensionAssetPath(t *testing.T) { } t.Run("NoExtension", func(t *testing.T) { + t.Parallel() + _, err := db.GetExtensionAssetPath(context.Background(), &database.Asset{ Publisher: "publisher", Extension: "extension", Type: "type", - Version: "version", + Version: storage.Version{Version: "version"}, }, *baseURL) require.Error(t, err) }) t.Run("NoAsset", func(t *testing.T) { + t.Parallel() + _, err := db.GetExtensionAssetPath(context.Background(), &database.Asset{ Publisher: "foo", Extension: "zany", Type: "nope", - Version: "1.0.0", + Version: storage.Version{Version: "1.0.0"}, + }, *baseURL) + require.Error(t, err) + + _, err = db.GetExtensionAssetPath(context.Background(), &database.Asset{ + Publisher: "foo", + Extension: "zany", + Type: "nope", + Version: storage.Version{Version: "1.0.0", TargetPlatform: storage.PlatformDarwinX64}, }, *baseURL) require.Error(t, err) }) t.Run("UnaddressableAsset", func(t *testing.T) { + t.Parallel() + _, err := db.GetExtensionAssetPath(context.Background(), &database.Asset{ Publisher: "foo", Extension: "zany", Type: "Unaddressable", - Version: "1.0.0", + Version: storage.Version{Version: "1.0.0"}, }, *baseURL) require.Error(t, err) }) t.Run("GetAsset", func(t *testing.T) { + t.Parallel() + path, err := db.GetExtensionAssetPath(context.Background(), &database.Asset{ Publisher: "foo", Extension: "zany", Type: "Microsoft.VisualStudio.Services.Icons.Default", - Version: "1.0.0", + Version: storage.Version{Version: "1.0.0"}, }, *baseURL) require.NoError(t, err) require.Equal(t, fmt.Sprintf("%s/files/foo/zany/1.0.0/icon.png", base), path) }) + + t.Run("GetAssetWithPlatform", func(t *testing.T) { + t.Parallel() + + path, err := db.GetExtensionAssetPath(context.Background(), &database.Asset{ + Publisher: "foo", + Extension: "zany", + Type: "Microsoft.VisualStudio.Services.Icons.Default", + Version: storage.Version{Version: "1.0.0", TargetPlatform: storage.PlatformWin32X64}, + }, *baseURL) + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("%s/files/foo/zany/1.0.0@win32-x64/icon.png", base), path) + + // All these do not append platform strings. + platforms := []storage.Platform{storage.PlatformUniversal, storage.PlatformUnknown, storage.PlatformUndefined} + for _, platform := range platforms { + path, err = db.GetExtensionAssetPath(context.Background(), &database.Asset{ + Publisher: "foo", + Extension: "zany", + Type: "Microsoft.VisualStudio.Services.Icons.Default", + Version: storage.Version{Version: "1.0.0", TargetPlatform: platform}, + }, *baseURL) + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("%s/files/foo/zany/1.0.0/icon.png", base), path) + } + }) } type checkFunc func(t *testing.T, ext *database.Extension) @@ -383,7 +426,7 @@ func TestGetExtensions(t *testing.T) { CheckFunc: func(t *testing.T, ext *database.Extension) { require.Empty(t, ext.Categories, "categories") require.Empty(t, ext.Tags, "tags") - require.Len(t, ext.Versions, 5, "versions") + require.Len(t, ext.Versions, 11, "versions") for _, version := range ext.Versions { require.Empty(t, version.Files, "files") require.Empty(t, version.Properties, "properties") @@ -403,11 +446,11 @@ func TestGetExtensions(t *testing.T) { CheckFunc: func(t *testing.T, ext *database.Extension) { require.Empty(t, ext.Categories, "categories") require.Empty(t, ext.Tags, "tags") - require.Len(t, ext.Versions, 5, "versions") + require.Len(t, ext.Versions, 11, "versions") for _, version := range ext.Versions { // Should ignore non-addressable files. require.Len(t, version.Files, 1, "files") - require.Equal(t, fmt.Sprintf("%s/files/foo/zany/%s/icon.png", base, version.Version), version.Files[0].Source) + require.Equal(t, fmt.Sprintf("%s/files/foo/zany/%s/icon.png", base, version), version.Files[0].Source) require.Empty(t, version.Properties, "properties") } }, @@ -425,11 +468,11 @@ func TestGetExtensions(t *testing.T) { CheckFunc: func(t *testing.T, ext *database.Extension) { require.Empty(t, ext.Categories, "categories") require.Empty(t, ext.Tags, "tags") - require.Len(t, ext.Versions, 5, "versions") + require.Len(t, ext.Versions, 11, "versions") for _, version := range ext.Versions { require.Empty(t, version.Files, "files") require.Empty(t, version.Properties, "properties") - require.Equal(t, fmt.Sprintf("%s/assets/foo/zany/%s", base, version.Version), version.AssetURI) + require.Equal(t, fmt.Sprintf("%s/assets/foo/zany/%s", base, version), version.AssetURI) require.Equal(t, version.AssetURI, version.FallbackAssetURI) } }, @@ -463,7 +506,7 @@ func TestGetExtensions(t *testing.T) { CheckFunc: func(t *testing.T, ext *database.Extension) { require.Empty(t, ext.Categories, "categories") require.Empty(t, ext.Tags, "tags") - require.Len(t, ext.Versions, 5, "versions") + require.Len(t, ext.Versions, 11, "versions") for _, version := range ext.Versions { require.Empty(t, version.Files, "files") require.Len(t, version.Properties, 2, "properties") @@ -483,8 +526,9 @@ func TestGetExtensions(t *testing.T) { CheckFunc: func(t *testing.T, ext *database.Extension) { require.Empty(t, ext.Categories, "categories") require.Empty(t, ext.Tags, "tags") - require.Len(t, ext.Versions, 1, "versions") + require.Len(t, ext.Versions, 6, "versions") // One for each platform. for _, version := range ext.Versions { + require.Equal(t, "3.0.0", version.Version.Version) require.Empty(t, version.Files, "files") require.Empty(t, version.Properties, "properties") } @@ -503,10 +547,10 @@ func TestGetExtensions(t *testing.T) { CheckFunc: func(t *testing.T, ext *database.Extension) { require.Len(t, ext.Categories, 1, "categories") require.Len(t, ext.Tags, 1, "tags") - require.Len(t, ext.Versions, 5, "versions") + require.Len(t, ext.Versions, 11, "versions") for _, version := range ext.Versions { require.Len(t, version.Files, 1, "files") - require.Equal(t, fmt.Sprintf("%s/files/foo/zany/%s/icon.png", base, version.Version), version.Files[0].Source) + require.Equal(t, fmt.Sprintf("%s/files/foo/zany/%s/icon.png", base, version), version.Files[0].Source) require.Len(t, version.Properties, 2, "properties") require.Equal(t, version.AssetURI, version.FallbackAssetURI) } diff --git a/database/nodb.go b/database/nodb.go index bb8971b..75da4cb 100644 --- a/database/nodb.go +++ b/database/nodb.go @@ -40,7 +40,7 @@ func (db *NoDB) GetExtensionAssetPath(ctx context.Context, asset *Asset, baseURL "files", asset.Publisher, asset.Extension, - asset.Version), + asset.Version.String()), }).String() for _, a := range manifest.Assets.Asset { @@ -56,7 +56,7 @@ func (db *NoDB) GetExtensions(ctx context.Context, filter Filter, flags Flag, ba vscodeExts := []*noDBExtension{} start := time.Now() - err := db.Storage.WalkExtensions(ctx, func(manifest *storage.VSIXManifest, versions []string) error { + err := db.Storage.WalkExtensions(ctx, func(manifest *storage.VSIXManifest, versions []storage.Version) error { vscodeExt := convertManifestToExtension(manifest) if matched, distances := getMatches(vscodeExt, filter); matched { vscodeExt.versions = versions @@ -315,15 +315,26 @@ func (db *NoDB) getVersions(ctx context.Context, ext *noDBExtension, flags Flag, slog.F("publisher", ext.Publisher.PublisherName), slog.F("extension", ext.Name)) - versionStrs := ext.versions + var storageVers []storage.Version if flags&IncludeLatestVersionOnly != 0 { - versionStrs = []string{ext.versions[0]} + // There might be multiple platforms for this version so find all the ones + // that match. Since they are sorted we can bail once one does not match. + latestVersion := ext.versions[0].Version + for _, version := range ext.versions { + if version.Version == latestVersion { + storageVers = append(storageVers, version) + } else { + break + } + } + } else { + storageVers = ext.versions } versions := []ExtVersion{} - for _, versionStr := range versionStrs { - ctx := slog.With(ctx, slog.F("version", versionStr)) - manifest, err := db.Storage.Manifest(ctx, ext.Publisher.PublisherName, ext.Name, versionStr) + for _, storageVer := range storageVers { + ctx := slog.With(ctx, slog.F("version", storageVer)) + manifest, err := db.Storage.Manifest(ctx, ext.Publisher.PublisherName, ext.Name, storageVer) if err != nil && errors.Is(err, context.Canceled) { return nil, err } else if err != nil { @@ -332,9 +343,8 @@ func (db *NoDB) getVersions(ctx context.Context, ext *noDBExtension, flags Flag, } version := ExtVersion{ - Version: versionStr, + Version: storageVer, // LastUpdated: time.Now(), // TODO: Use modified time? - TargetPlatform: manifest.Metadata.Identity.TargetPlatform, } if flags&IncludeFiles != 0 { @@ -346,7 +356,7 @@ func (db *NoDB) getVersions(ctx context.Context, ext *noDBExtension, flags Flag, "/files", ext.Publisher.PublisherName, ext.Name, - versionStr), + version.String()), }).String() for _, asset := range manifest.Assets.Asset { if asset.Addressable != "true" { @@ -378,7 +388,7 @@ func (db *NoDB) getVersions(ctx context.Context, ext *noDBExtension, flags Flag, "assets", ext.Publisher.PublisherName, ext.Name, - versionStr), + version.String()), }).String() version.FallbackAssetURI = version.AssetURI } @@ -394,7 +404,7 @@ type noDBExtension struct { // Used internally for ranking. Lower means more relevant. distances []int `json:"-"` // Used internally to avoid reading and sorting versions twice. - versions []string `json:"-"` + versions []storage.Version `json:"-"` } func convertManifestToExtension(manifest *storage.VSIXManifest) *noDBExtension { diff --git a/storage/artifactory.go b/storage/artifactory.go index 479af4d..e46e408 100644 --- a/storage/artifactory.go +++ b/storage/artifactory.go @@ -15,7 +15,6 @@ import ( "sync" "time" - "golang.org/x/mod/semver" "golang.org/x/sync/errgroup" "golang.org/x/xerrors" @@ -90,7 +89,7 @@ func NewArtifactoryStorage(ctx context.Context, options *ArtifactoryOptions) (*A start := time.Now() count := 0 var eg errgroup.Group - err := s.WalkExtensions(ctx, func(manifest *VSIXManifest, versions []string) error { + err := s.WalkExtensions(ctx, func(manifest *VSIXManifest, versions []Version) error { for _, ver := range versions { count++ ver := ver @@ -100,8 +99,10 @@ func NewArtifactoryStorage(ctx context.Context, options *ArtifactoryOptions) (*A if err != nil && !errors.Is(err, context.Canceled) { return err } else if err != nil { - id := ExtensionID(identity.Publisher, identity.ID, ver) - s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err), slog.F("id", id)) + id := ExtensionID(identity.Publisher, identity.ID, ver.Version) + s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err), + slog.F("id", id), + slog.F("targetPlatform", ver.TargetPlatform)) } return nil }) @@ -215,7 +216,10 @@ func (s *Artifactory) upload(ctx context.Context, endpoint string, r io.Reader) func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) { // Extract the zip to the correct path. identity := manifest.Metadata.Identity - dir := path.Join(identity.Publisher, identity.ID, identity.Version) + dir := path.Join(identity.Publisher, identity.ID, Version{ + Version: identity.Version, + TargetPlatform: identity.TargetPlatform, + }.String()) // Uploading every file in an extension such as ms-python.python can take // quite a while (16 minutes!!). As a compromise only extract a file if it @@ -252,7 +256,7 @@ func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, } // Copy the VSIX itself as well. - vsixName := fmt.Sprintf("%s.vsix", ExtensionIDFromManifest(manifest)) + vsixName := fmt.Sprintf("%s.vsix", ExtensionVSIXNameFromManifest(manifest)) _, err = s.upload(ctx, path.Join(dir, vsixName), bytes.NewReader(vsix)) if err != nil { return "", err @@ -282,24 +286,24 @@ func (s *Artifactory) FileServer() http.Handler { }) } -func (s *Artifactory) Manifest(ctx context.Context, publisher, name, version string) (*VSIXManifest, error) { +func (s *Artifactory) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { // These queries are so slow it seems worth the extra memory to cache the // manifests for future use. // TODO: Remove manifests that are no longer found in the list to prevent // indefinitely caching manifests belonging to extensions that have since been // removed or dump the cache periodically. - id := ExtensionID(publisher, name, version) - rawMutex, _ := s.manifestMutexes.LoadOrStore(id, &sync.Mutex{}) + vsixName := ExtensionVSIXName(publisher, name, version) + rawMutex, _ := s.manifestMutexes.LoadOrStore(vsixName, &sync.Mutex{}) mutex := rawMutex.(*sync.Mutex) mutex.Lock() defer mutex.Unlock() - rawManifest, ok := s.manifests.Load(id) + rawManifest, ok := s.manifests.Load(vsixName) if ok { return rawManifest.(*VSIXManifest), nil } - reader, _, err := s.read(ctx, path.Join(publisher, name, version, "extension.vsixmanifest")) + reader, _, err := s.read(ctx, path.Join(publisher, name, version.String(), "extension.vsixmanifest")) if err != nil { return nil, err } @@ -316,16 +320,16 @@ func (s *Artifactory) Manifest(ctx context.Context, publisher, name, version str manifest.Assets.Asset = append(manifest.Assets.Asset, VSIXAsset{ Type: VSIXAssetType, - Path: fmt.Sprintf("%s.vsix", ExtensionIDFromManifest(manifest)), + Path: fmt.Sprintf("%s.vsix", ExtensionVSIXNameFromManifest(manifest)), Addressable: "true", }) - rawManifest, _ = s.manifests.LoadOrStore(id, manifest) + rawManifest, _ = s.manifests.LoadOrStore(vsixName, manifest) return rawManifest.(*VSIXManifest), nil } -func (s *Artifactory) RemoveExtension(ctx context.Context, publisher, name, version string) error { - _, err := s.delete(ctx, path.Join(publisher, name, version)) +func (s *Artifactory) RemoveExtension(ctx context.Context, publisher, name string, version Version) error { + _, err := s.delete(ctx, path.Join(publisher, name, version.String())) return err } @@ -333,7 +337,7 @@ type extension struct { manifest *VSIXManifest name string publisher string - versions []string + versions []Version } func (s *Artifactory) listWithCache(ctx context.Context) *[]ArtifactoryFile { @@ -350,7 +354,7 @@ func (s *Artifactory) listWithCache(ctx context.Context) *[]ArtifactoryFile { return s.listCache } -func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []string) error) error { +func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error { // Listing one directory at a time is very slow so get them all at once. If // we already fetched it recently just use that since getting them all at once // is also pretty slow (on the parsing end). @@ -368,12 +372,12 @@ func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIX id := fmt.Sprintf("%s.%s", parts[1], parts[2]) e, ok := extensions[id] if ok { - e.versions = append(e.versions, parts[3]) + e.versions = append(e.versions, VersionFromString(parts[3])) } else { extensions[id] = &extension{ name: parts[2], publisher: parts[1], - versions: []string{parts[3]}, + versions: []Version{VersionFromString(parts[3])}, } } } @@ -387,14 +391,16 @@ func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIX defer cancel() for _, ext := range extensions { ext := ext - sort.Sort(sort.Reverse(semver.ByVersion(ext.versions))) + sort.Sort(ByVersion(ext.versions)) eg.Go(func() error { manifest, err := s.Manifest(ctx, ext.publisher, ext.name, ext.versions[0]) if err != nil && errors.Is(err, context.Canceled) { return err } else if err != nil { - id := ExtensionID(ext.publisher, ext.name, ext.versions[0]) - s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err), slog.F("id", id)) + id := ExtensionID(ext.publisher, ext.name, ext.versions[0].Version) + s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err), + slog.F("id", id), + slog.F("targetPlatform", ext.versions[0].TargetPlatform)) } else { ext.manifest = manifest } @@ -413,19 +419,20 @@ func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIX return nil } -func (s *Artifactory) Versions(ctx context.Context, publisher, name string) ([]string, error) { +func (s *Artifactory) Versions(ctx context.Context, publisher, name string) ([]Version, error) { files, _, err := s.list(ctx, path.Join(publisher, name), 1) if err != nil { return nil, err } - versions := []string{} + versions := []Version{} for _, file := range files { // There should only be directories but check just in case. if file.Folder { // The files come with leading slashes so remove them. - versions = append(versions, strings.TrimLeft(file.URI, "/")) + versionDir := strings.TrimLeft(file.URI, "/") + versions = append(versions, VersionFromString(versionDir)) } } - sort.Sort(sort.Reverse(semver.ByVersion(versions))) + sort.Sort(ByVersion(versions)) return versions, nil } diff --git a/storage/local.go b/storage/local.go index 7611ef7..1ac8b38 100644 --- a/storage/local.go +++ b/storage/local.go @@ -9,8 +9,6 @@ import ( "path/filepath" "sort" - "golang.org/x/mod/semver" - "cdr.dev/slog" ) @@ -36,7 +34,10 @@ func NewLocalStorage(extdir string, logger slog.Logger) (*Local, error) { func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) { // Extract the zip to the correct path. identity := manifest.Metadata.Identity - dir := filepath.Join(s.extdir, identity.Publisher, identity.ID, identity.Version) + dir := filepath.Join(s.extdir, identity.Publisher, identity.ID, Version{ + Version: identity.Version, + TargetPlatform: identity.TargetPlatform, + }.String()) err := ExtractZip(vsix, func(name string, r io.Reader) error { path := filepath.Join(dir, name) err := os.MkdirAll(filepath.Dir(path), 0o755) @@ -56,7 +57,7 @@ func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix [ } // Copy the VSIX itself as well. - vsixPath := filepath.Join(dir, fmt.Sprintf("%s.vsix", ExtensionIDFromManifest(manifest))) + vsixPath := filepath.Join(dir, fmt.Sprintf("%s.vsix", ExtensionVSIXNameFromManifest(manifest))) err = os.WriteFile(vsixPath, vsix, 0o644) if err != nil { return "", err @@ -69,8 +70,8 @@ func (s *Local) FileServer() http.Handler { return http.FileServer(http.Dir(s.extdir)) } -func (s *Local) Manifest(ctx context.Context, publisher, name, version string) (*VSIXManifest, error) { - reader, err := os.Open(filepath.Join(s.extdir, publisher, name, version, "extension.vsixmanifest")) +func (s *Local) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { + reader, err := os.Open(filepath.Join(s.extdir, publisher, name, version.String(), "extension.vsixmanifest")) if err != nil { return nil, err } @@ -87,15 +88,15 @@ func (s *Local) Manifest(ctx context.Context, publisher, name, version string) ( manifest.Assets.Asset = append(manifest.Assets.Asset, VSIXAsset{ Type: VSIXAssetType, - Path: fmt.Sprintf("%s.vsix", ExtensionIDFromManifest(manifest)), + Path: fmt.Sprintf("%s.vsix", ExtensionVSIXNameFromManifest(manifest)), Addressable: "true", }) return manifest, nil } -func (s *Local) RemoveExtension(ctx context.Context, publisher, name, version string) error { - dir := filepath.Join(s.extdir, publisher, name, version) +func (s *Local) RemoveExtension(ctx context.Context, publisher, name string, version Version) error { + dir := filepath.Join(s.extdir, publisher, name, version.String()) // RemoveAll() will not error if the directory does not exist so check first // as this function should error when removing versions that do not exist. _, err := os.Stat(dir) @@ -105,15 +106,19 @@ func (s *Local) RemoveExtension(ctx context.Context, publisher, name, version st return os.RemoveAll(dir) } -func (s *Local) Versions(ctx context.Context, publisher, name string) ([]string, error) { +func (s *Local) Versions(ctx context.Context, publisher, name string) ([]Version, error) { dir := filepath.Join(s.extdir, publisher, name) - versions, err := s.getDirNames(ctx, dir) + versionDirs, err := s.getDirNames(ctx, dir) + var versions []Version + for _, versionDir := range versionDirs { + versions = append(versions, VersionFromString(versionDir)) + } // Return anything we did get even if there was an error. - sort.Sort(sort.Reverse(semver.ByVersion(versions))) + sort.Sort(ByVersion(versions)) return versions, err } -func (s *Local) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []string) error) error { +func (s *Local) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error { publishers, err := s.getDirNames(ctx, s.extdir) if err != nil { s.logger.Error(ctx, "Error reading publisher", slog.Error(err)) diff --git a/storage/storage.go b/storage/storage.go index 90767ee..6b5ace8 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "golang.org/x/mod/semver" "golang.org/x/xerrors" "cdr.dev/slog" @@ -44,14 +45,40 @@ type VSIXMetadata struct { Categories string } -// VSIXManifest implement XMLManifest.PackageManifest.Metadata.Identity. +// Platform implements TargetPlatform. +// https://github.com/microsoft/vscode/blob/main/src/vs/platform/extensions/common/extensions.ts#L291-L311 +type Platform string + +const ( + PlatformWin32X64 Platform = "win32-x64" + PlatformWin32Ia32 Platform = "win32-ia32" + PlatformWin32Arm64 Platform = "win32-arm64" + + PlatformLinuxX64 Platform = "linux-x64" + PlatformLinuxArm64 Platform = "linux-arm64" + PlatformLinuxArmhf Platform = "linux-armhf" + + PlatformAlpineX64 Platform = "alpine-x64" + PlatformAlpineArm64 Platform = "alpine-arm64" + + PlatformDarwinX64 Platform = "darwin-x64" + PlatformDarwinArm64 Platform = "darwin-arm64" + + PlatformWeb Platform = "web" + + PlatformUniversal Platform = "universal" + PlatformUnknown Platform = "unknown" + PlatformUndefined Platform = "undefined" +) + +// VSIXManifest implements XMLManifest.PackageManifest.Metadata.Identity. // https://github.com/microsoft/vscode-vsce/blob/main/src/xml.ts#L14 type VSIXIdentity struct { // ID correlates to ExtensionName, *not* ExtensionID. - ID string `xml:"Id,attr"` - Version string `xml:",attr"` - Publisher string `xml:",attr"` - TargetPlatform string `xml:",attr"` + ID string `xml:"Id,attr"` + Version string `xml:",attr"` + Publisher string `xml:",attr"` + TargetPlatform Platform `xml:",attr"` } // VSIXProperties implements XMLManifest.PackageManifest.Metadata.Properties. @@ -102,6 +129,69 @@ type Options struct { Logger slog.Logger } +// Version is a subset of database.ExtVersion. +type Version struct { + TargetPlatform Platform `json:"targetPlatform,omitempty"` + Version string `json:"version"` +} + +func (v Version) isUniversal() bool { + switch v.TargetPlatform { + case PlatformUniversal, PlatformUnknown, PlatformUndefined, "": + return true + default: + return false + } +} + +// Strings encodes the version and platform into a string that can be reversed +// by VersionFromString. For example 1.0.0@linux-x64. For universal versions +// the @platform will be omitted. +// +// For directory names it might have been ideal to a nested path such as +// `version/platform` but we use this instead for backwards compatibility since +// we were unpacking directly into the `version` directory. Otherwise, we would +// have to migrate existing extensions or have a mechanism for detecting in +// which format the extension was being stored. +func (v Version) String() string { + if v.isUniversal() { + return v.Version + } else { + return fmt.Sprintf("%s@%s", v.Version, v.TargetPlatform) + } +} + +// VersionFromString creates a version from a version directory. More or less it +// reverses Version.String(). Since @ is not allowed in semantic versions this +// should be future-proof. +func VersionFromString(dir string) Version { + parts := strings.SplitN(dir, "@", 2) + var platform Platform + if len(parts) > 1 { + platform = Platform(parts[1]) + } + return Version{ + Version: parts[0], + TargetPlatform: platform, + } +} + +// ByVersion implements sort.Interface for sorting Version slices. +type ByVersion []Version + +func (vs ByVersion) Len() int { return len(vs) } +func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } +func (vs ByVersion) Less(i, j int) bool { + cmp := semver.Compare(vs[i].Version, vs[j].Version) + if cmp != 0 { + return cmp >= 0 + } + if vs[i].Version == vs[j].Version { + return vs[i].TargetPlatform < vs[j].TargetPlatform + } + return vs[i].Version >= vs[j].Version +} + type Storage interface { // AddExtension adds the provided VSIX into storage and returns the location // for verification purposes. @@ -112,20 +202,23 @@ type Storage interface { // Manifest returns the manifest bytes for the provided extension. The // extension asset itself (the VSIX) will be included on the manifest even if // it does not exist on the manifest on disk. - Manifest(ctx context.Context, publisher, name, version string) (*VSIXManifest, error) + Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) // RemoveExtension removes the provided version of the extension. It errors - // if the provided version does not exist or if removing it fails. If version - // is blank all versions of that extension will be removed. - RemoveExtension(ctx context.Context, publisher, name, version string) error + // if the version does not exist or if removing it fails. If both the version + // and platform are blank all versions of that extension will be removed. If + // only the platform is blank the universal version will be removed. If only + // the version is blank it will error; it is not currently possible to delete + // all versions for a specific platform. + RemoveExtension(ctx context.Context, publisher, name string, version Version) error // Versions returns the available versions of the provided extension in sorted // order. If the extension does not exits it returns an error. - Versions(ctx context.Context, publisher, name string) ([]string, error) + Versions(ctx context.Context, publisher, name string) ([]Version, error) // WalkExtensions applies a function over every extension. The extension // points to the latest version and the versions slice includes all the // versions in sorted order including the latest version (which will be in // [0]). If the function returns an error the error is immediately returned // which aborts the walk. - WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []string) error) error + WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error } const ArtifactoryTokenEnvKey = "ARTIFACTORY_TOKEN" @@ -240,7 +333,8 @@ func ReadVSIX(ctx context.Context, source string) ([]byte, error) { }) } -// ExtensionIDFromManifest returns the full ID of an extension. +// ExtensionIDFromManifest returns the full ID of an extension without the the +// platform, for example publisher.name@0.0.1. func ExtensionIDFromManifest(manifest *VSIXManifest) string { return ExtensionID( manifest.Metadata.Identity.Publisher, @@ -248,13 +342,36 @@ func ExtensionIDFromManifest(manifest *VSIXManifest) string { manifest.Metadata.Identity.Version) } -// ExtensionID returns the full ID of an extension. +// ExtensionID returns the full ID of an extension without the platform, for +// example publisher.name@0.0.1. func ExtensionID(publisher, name, version string) string { return fmt.Sprintf("%s.%s@%s", publisher, name, version) } -// ParseExtensionID parses an extension ID into its separate parts: publisher, -// name, and version (version may be blank). +// ExtensionVSIXNameFromManifest returns the full ID of an extension including +// the platform if not universal, for example publisher.name-0.0.1 or +// publisher.name-0.0.1@linux-x64. +func ExtensionVSIXNameFromManifest(manifest *VSIXManifest) string { + return ExtensionVSIXName( + manifest.Metadata.Identity.Publisher, + manifest.Metadata.Identity.ID, + Version{ + Version: manifest.Metadata.Identity.Version, + TargetPlatform: manifest.Metadata.Identity.TargetPlatform, + }) +} + +// ExtensionVSIXName returns the full ID of an extension including the +// platform if not universal, for example publisher.name-0.0.1 or +// publisher.name-0.0.1@linux-x64. +func ExtensionVSIXName(publisher, name string, version Version) string { + return fmt.Sprintf("%s.%s-%s", publisher, name, version) +} + +// ParseExtensionID parses an full or partial extension ID into its separate +// parts: publisher, name, and version (version may be blank). It does not +// support specifying the platform and requires that the delimiter for the +// version be @. func ParseExtensionID(id string) (string, string, string, error) { re := regexp.MustCompile(`^([^.]+)\.([^@]+)@?(.*)$`) match := re.FindAllStringSubmatch(id, -1) diff --git a/storage/storage_test.go b/storage/storage_test.go index 02fdd8b..1484d3c 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -16,7 +16,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "golang.org/x/mod/semver" "github.com/coder/code-marketplace/storage" "github.com/coder/code-marketplace/testutil" @@ -214,46 +213,95 @@ func testManifest(t *testing.T, factory storageFactory) { tests := []struct { // error is the expected error, if any. error error + // expected is the manifest we should get back, if there is no error. + expected *storage.VSIXManifest // extension contains the expected manifest. extension testutil.Extension // name is the name of the test. name string - // version is the version to expect in the manifest. Defaults to the - // extension's latest version. - version string + // version is the version to use in the manifest request. + version storage.Version }{ { - name: "OK", + name: "PlatformDefault", + extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion}, + expected: testutil.ConvertExtensionToManifest(testutil.Extensions[0], + storage.Version{Version: testutil.Extensions[0].LatestVersion}), + }, + { + name: "PlatformUniversal", + extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion, + TargetPlatform: storage.PlatformUniversal}, + expected: testutil.ConvertExtensionToManifest(testutil.Extensions[0], + storage.Version{Version: testutil.Extensions[0].LatestVersion}), + }, + { + name: "PlatformUnknown", + extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion, + TargetPlatform: storage.PlatformUnknown}, + expected: testutil.ConvertExtensionToManifest(testutil.Extensions[0], + storage.Version{Version: testutil.Extensions[0].LatestVersion}), + }, + { + name: "PlatformUndefined", + extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion, + TargetPlatform: storage.PlatformUndefined}, + expected: testutil.ConvertExtensionToManifest(testutil.Extensions[0], + storage.Version{Version: testutil.Extensions[0].LatestVersion}), + }, + { + name: "PlatformLinuxX64", + extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion, + TargetPlatform: storage.PlatformLinuxX64}, + expected: testutil.ConvertExtensionToManifest(testutil.Extensions[0], + storage.Version{Version: testutil.Extensions[0].LatestVersion, + TargetPlatform: storage.PlatformLinuxX64}), + }, + { + name: "MissingPlatform", + error: fs.ErrNotExist, extension: testutil.Extensions[0], + version: storage.Version{TargetPlatform: storage.PlatformWeb}, }, { name: "MissingVersion", error: fs.ErrNotExist, extension: testutil.Extensions[0], - version: "some-nonexistent@version", + version: storage.Version{Version: "some-nonexistent@version"}, }, { name: "MissingExtension", error: fs.ErrNotExist, extension: testutil.Extensions[1], + version: storage.Version{Version: testutil.Extensions[1].LatestVersion}, }, { name: "MissingPublisher", error: fs.ErrNotExist, extension: testutil.Extensions[2], + version: storage.Version{Version: testutil.Extensions[2].LatestVersion}, }, { name: "ParseError", error: io.EOF, extension: testutil.Extensions[3], + version: storage.Version{Version: testutil.Extensions[3].LatestVersion}, }, } f := factory(t) ext := testutil.Extensions[0] - manifestBytes := testutil.ConvertExtensionToManifestBytes(t, ext, ext.LatestVersion) + manifestBytes := testutil.ConvertExtensionToManifestBytes(t, ext, storage.Version{Version: ext.LatestVersion}) f.write(manifestBytes, ext.Publisher, ext.Name, ext.LatestVersion, "extension.vsixmanifest") + manifestBytes = testutil.ConvertExtensionToManifestBytes(t, ext, storage.Version{Version: ext.LatestVersion, TargetPlatform: storage.PlatformLinuxX64}) + f.write(manifestBytes, ext.Publisher, ext.Name, ext.LatestVersion+"@linux-x64", "extension.vsixmanifest") + ext = testutil.Extensions[3] f.write([]byte("invalid"), ext.Publisher, ext.Name, ext.LatestVersion, "extension.vsixmanifest") @@ -261,25 +309,20 @@ func testManifest(t *testing.T, factory storageFactory) { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() - version := test.version - if version == "" { - version = test.extension.LatestVersion - } - manifest, err := f.storage.Manifest(context.Background(), test.extension.Publisher, test.extension.Name, version) + manifest, err := f.storage.Manifest(context.Background(), test.extension.Publisher, test.extension.Name, test.version) if test.error != nil { require.Error(t, err) require.True(t, errors.Is(err, test.error)) } else { - expected := testutil.ConvertExtensionToManifest(testutil.Extensions[0], version) + require.NoError(t, err) // The storage interface should add the extension asset when it reads the // manifest since it is not on the actual manifest on disk. - expected.Assets.Asset = append(expected.Assets.Asset, storage.VSIXAsset{ + test.expected.Assets.Asset = append(test.expected.Assets.Asset, storage.VSIXAsset{ Type: storage.VSIXAssetType, - Path: fmt.Sprintf("%s.%s@%s.vsix", test.extension.Publisher, test.extension.Name, version), + Path: fmt.Sprintf("%s.%s-%s.vsix", test.extension.Publisher, test.extension.Name, test.version), Addressable: "true", }) - require.NoError(t, err) - require.Equal(t, expected, manifest) + require.Equal(t, test.expected, manifest) } }) } @@ -287,7 +330,7 @@ func testManifest(t *testing.T, factory storageFactory) { type extension struct { manifest *storage.VSIXManifest - versions []string + versions []storage.Version } func testWalkExtensions(t *testing.T, factory storageFactory) { @@ -301,7 +344,7 @@ func testWalkExtensions(t *testing.T, factory storageFactory) { // name is then ame of the test name string // run is an optional walk callback. - run func() error + run func(_ []storage.Version) error }{ { name: "OK", @@ -314,7 +357,7 @@ func testWalkExtensions(t *testing.T, factory storageFactory) { name: "PropagateError", error: "propagate", extensions: []testutil.Extension{testutil.Extensions[0]}, - run: func() error { + run: func(_ []storage.Version) error { return errors.New("propagate") }, }, @@ -327,15 +370,15 @@ func testWalkExtensions(t *testing.T, factory storageFactory) { f := factory(t) expected := []extension{} for _, ext := range test.extensions { - versions := make([]string, len(ext.Versions)) + versions := make([]storage.Version, len(ext.Versions)) copy(versions, ext.Versions) - sort.Sort(sort.Reverse(semver.ByVersion(versions))) - manifest := testutil.ConvertExtensionToManifest(ext, ext.LatestVersion) + sort.Sort(storage.ByVersion(versions)) + manifest := testutil.ConvertExtensionToManifest(ext, storage.Version{Version: ext.LatestVersion}) // The storage interface should add the extension asset when it reads the // manifest since it is not on the actual manifest on disk. manifest.Assets.Asset = append(manifest.Assets.Asset, storage.VSIXAsset{ Type: storage.VSIXAssetType, - Path: fmt.Sprintf("%s.%s@%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion), + Path: fmt.Sprintf("%s.%s-%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion), Addressable: "true", }) expected = append(expected, extension{ @@ -343,17 +386,17 @@ func testWalkExtensions(t *testing.T, factory storageFactory) { versions: versions, }) for _, version := range ext.Versions { - f.write(testutil.ConvertExtensionToManifestBytes(t, ext, version), ext.Publisher, ext.Name, version, "extension.vsixmanifest") + f.write(testutil.ConvertExtensionToManifestBytes(t, ext, version), ext.Publisher, ext.Name, version.String(), "extension.vsixmanifest") } } got := []extension{} - err := f.storage.WalkExtensions(context.Background(), func(manifest *storage.VSIXManifest, versions []string) error { + err := f.storage.WalkExtensions(context.Background(), func(manifest *storage.VSIXManifest, versions []storage.Version) error { got = append(got, extension{ manifest: manifest, versions: versions, }) if test.run != nil { - return test.run() + return test.run(versions) } return nil }) @@ -363,6 +406,7 @@ func testWalkExtensions(t *testing.T, factory storageFactory) { } else { require.NoError(t, err) } + // Ignores the extension order, but the version order will matter. require.ElementsMatch(t, expected, got) }) } @@ -379,7 +423,7 @@ func TestReadVSIX(t *testing.T) { error string // expected is compared with the return VSIX. It is not checked if an // error is expected. - expected testutil.Extension + expected []byte // handler is the handler for the HTTP server returning the VSIX. By // default it returns the `expected` extension. handler http.HandlerFunc @@ -388,7 +432,11 @@ func TestReadVSIX(t *testing.T) { }{ { name: "OK", - expected: testutil.Extensions[0], + expected: testutil.CreateVSIXFromExtension(t, testutil.Extensions[0], storage.Version{Version: testutil.Extensions[0].LatestVersion}), + }, + { + name: "OKPlatform", + expected: testutil.CreateVSIXFromExtension(t, testutil.Extensions[0], storage.Version{Version: testutil.Extensions[0].LatestVersion, TargetPlatform: storage.PlatformLinuxX64}), }, { name: "InternalError", @@ -406,10 +454,10 @@ func TestReadVSIX(t *testing.T) { }, { name: "Redirect", - expected: testutil.Extensions[0], + expected: testutil.CreateVSIXFromExtension(t, testutil.Extensions[0], storage.Version{Version: testutil.Extensions[0].LatestVersion}), handler: func(rw http.ResponseWriter, r *http.Request) { if r.URL.Path == "/redirected" { - vsix := testutil.CreateVSIXFromExtension(t, testutil.Extensions[0]) + vsix := testutil.CreateVSIXFromExtension(t, testutil.Extensions[0], storage.Version{Version: testutil.Extensions[0].LatestVersion}) _, err := rw.Write(vsix) require.NoError(t, err) } else { @@ -434,8 +482,7 @@ func TestReadVSIX(t *testing.T) { handler := test.handler if handler == nil { handler = func(rw http.ResponseWriter, r *http.Request) { - vsix := testutil.CreateVSIXFromExtension(t, test.expected) - _, err := rw.Write(vsix) + _, err := rw.Write(test.expected) require.NoError(t, err) } } @@ -448,7 +495,7 @@ func TestReadVSIX(t *testing.T) { require.Error(t, err) require.Regexp(t, test.error, err.Error()) } else { - require.Equal(t, testutil.CreateVSIXFromExtension(t, test.expected), got) + require.Equal(t, test.expected, got) } }) } @@ -462,7 +509,7 @@ func TestReadVSIX(t *testing.T) { error error // expected is compared with the return VSIX. It is not checked if an // error is expected. - expected testutil.Extension + expected []byte // name is the name of the test. name string // skip indicates whether to skip the test since some failure modes are @@ -470,21 +517,28 @@ func TestReadVSIX(t *testing.T) { skip bool // source sets up the extension on disk and returns the path to that // extension. - source func(t *testing.T, extdir string) (string, error) + source func(t *testing.T, expected []byte, extdir string) (string, error) }{ { name: "OK", - expected: testutil.Extensions[0], - source: func(t *testing.T, extdir string) (string, error) { - vsix := testutil.CreateVSIXFromExtension(t, testutil.Extensions[0]) + expected: testutil.CreateVSIXFromExtension(t, testutil.Extensions[0], storage.Version{Version: testutil.Extensions[0].LatestVersion}), + source: func(t *testing.T, expected []byte, extdir string) (string, error) { vsixPath := filepath.Join(extdir, "extension.vsix") - return vsixPath, os.WriteFile(vsixPath, vsix, 0o644) + return vsixPath, os.WriteFile(vsixPath, expected, 0o644) + }, + }, + { + name: "OKPlatform", + expected: testutil.CreateVSIXFromExtension(t, testutil.Extensions[0], storage.Version{Version: testutil.Extensions[0].LatestVersion, TargetPlatform: storage.PlatformLinuxX64}), + source: func(t *testing.T, expected []byte, extdir string) (string, error) { + vsixPath := filepath.Join(extdir, "extension.vsix") + return vsixPath, os.WriteFile(vsixPath, expected, 0o644) }, }, { name: "NotFound", error: os.ErrNotExist, - source: func(t *testing.T, extdir string) (string, error) { + source: func(t *testing.T, _ []byte, extdir string) (string, error) { return filepath.Join(extdir, "foo.vsix"), nil }, }, @@ -494,7 +548,7 @@ func TestReadVSIX(t *testing.T) { // It does not appear possible to create a file that is not readable on // Windows? skip: runtime.GOOS == "windows", - source: func(t *testing.T, extdir string) (string, error) { + source: func(t *testing.T, _ []byte, extdir string) (string, error) { vsixPath := filepath.Join(extdir, "extension.vsix") return vsixPath, os.WriteFile(vsixPath, []byte{}, 0o222) }, @@ -510,7 +564,7 @@ func TestReadVSIX(t *testing.T) { } extdir := t.TempDir() - source, err := test.source(t, extdir) + source, err := test.source(t, test.expected, extdir) require.NoError(t, err) got, err := storage.ReadVSIX(context.Background(), source) @@ -518,7 +572,7 @@ func TestReadVSIX(t *testing.T) { require.Error(t, err) require.True(t, errors.Is(err, test.error)) } else { - require.Equal(t, testutil.CreateVSIXFromExtension(t, test.expected), got) + require.Equal(t, test.expected, got) } }) } @@ -702,22 +756,33 @@ func testAddExtension(t *testing.T, factory storageFactory) { extension testutil.Extension // name is the name of the test. name string + // version is the version of the extension to add. Use `vsix` to specify + // raw bytes instead. + version storage.Version // vsix contains the raw bytes of the extension to add. If omitted it will - // be created from `extension`. For non-error cases always use `extension` - // instead so we can check the result. + // be created from `extension` and `version`. For non-error cases always + // use `extension` instead so we can check the result. vsix []byte }{ { name: "OK", extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion}, + }, + { + name: "OKPlatform", + extension: testutil.Extensions[0], + version: storage.Version{Version: testutil.Extensions[0].LatestVersion, TargetPlatform: storage.PlatformLinuxX64}, }, { name: "EmptyDependencies", extension: testutil.Extensions[1], + version: storage.Version{Version: testutil.Extensions[1].LatestVersion}, }, { name: "NoDependencies", extension: testutil.Extensions[2], + version: storage.Version{Version: testutil.Extensions[2].LatestVersion}, }, { name: "InvalidZip", @@ -727,6 +792,7 @@ func testAddExtension(t *testing.T, factory storageFactory) { { name: "CopyOverDirectory", extension: testutil.Extensions[3], + version: storage.Version{Version: testutil.Extensions[3].LatestVersion}, error: "is a directory|found a folder", }, } @@ -734,7 +800,7 @@ func testAddExtension(t *testing.T, factory storageFactory) { // Put a directory in the way of the vsix. f := factory(t) ext := testutil.Extensions[3] - vsixName := fmt.Sprintf("%s.%s@%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion) + vsixName := fmt.Sprintf("%s.%s-%s.vsix", ext.Publisher, ext.Name, ext.LatestVersion) f.write([]byte("foo"), ext.Publisher, ext.Name, ext.LatestVersion, vsixName, "foo") for _, test := range tests { @@ -744,7 +810,7 @@ func testAddExtension(t *testing.T, factory storageFactory) { expected := &storage.VSIXManifest{} vsix := test.vsix if vsix == nil { - expected = testutil.ConvertExtensionToManifest(test.extension, test.extension.LatestVersion) + expected = testutil.ConvertExtensionToManifest(test.extension, test.version) vsix = testutil.CreateVSIXFromManifest(t, expected) } location, err := f.storage.AddExtension(context.Background(), expected, vsix) @@ -757,8 +823,11 @@ func testAddExtension(t *testing.T, factory storageFactory) { require.Contains(t, location, test.extension.Publisher) require.Contains(t, location, test.extension.Name) require.Contains(t, location, test.extension.LatestVersion) + if test.version.TargetPlatform != "" { + require.Contains(t, location, test.version.TargetPlatform) + } // There should be a manifest now. - require.True(t, f.exists(test.extension.Publisher, test.extension.Name, test.extension.LatestVersion, "extension.vsixmanifest")) + require.True(t, f.exists(test.extension.Publisher, test.extension.Name, test.version.String(), "extension.vsixmanifest")) } }) } @@ -776,37 +845,51 @@ func testRemoveExtension(t *testing.T, factory storageFactory) { // name is the name of the test. name string // version is the version to remove. - version string + version storage.Version }{ { name: "OK", extension: testutil.Extensions[0], - version: testutil.Extensions[0].LatestVersion, + version: storage.Version{Version: testutil.Extensions[0].LatestVersion}, }, { name: "NoVersionMatch", error: os.ErrNotExist, extension: testutil.Extensions[0], - version: "does-not-exist", + version: storage.Version{Version: "does-not-exist"}, }, { name: "NoPublisherMatch", error: os.ErrNotExist, // [3]'s publisher does not exist. extension: testutil.Extensions[3], - version: testutil.Extensions[3].LatestVersion, + version: storage.Version{Version: testutil.Extensions[3].LatestVersion}, }, { name: "NoNameMatch", error: os.ErrNotExist, // [1] shares a publisher with [0] but the extension does not exist. extension: testutil.Extensions[1], - version: testutil.Extensions[1].LatestVersion, + version: storage.Version{Version: testutil.Extensions[1].LatestVersion}, + }, + { + name: "NoPlatformMatch", + error: os.ErrNotExist, + extension: testutil.Extensions[1], + version: storage.Version{Version: testutil.Extensions[1].LatestVersion, TargetPlatform: storage.PlatformWeb}, + }, + { + // We could support this as a way to delete all versions belonging to a + // specific platform, but for now it is an error. + name: "NoVersion", + error: os.ErrNotExist, + extension: testutil.Extensions[1], + version: storage.Version{TargetPlatform: storage.PlatformWeb}, }, { name: "All", extension: testutil.Extensions[0], - version: "", + version: storage.Version{}, }, } @@ -818,7 +901,7 @@ func testRemoveExtension(t *testing.T, factory storageFactory) { f := factory(t) for _, ext := range []testutil.Extension{testutil.Extensions[0], testutil.Extensions[2]} { for _, version := range ext.Versions { - f.write(testutil.ConvertExtensionToManifestBytes(t, ext, version), ext.Publisher, ext.Name, version, "extension.vsixmanifest") + f.write(testutil.ConvertExtensionToManifestBytes(t, ext, version), ext.Publisher, ext.Name, version.String(), "extension.vsixmanifest") } } @@ -830,9 +913,9 @@ func testRemoveExtension(t *testing.T, factory storageFactory) { require.NoError(t, err) // If a version was specified the parent extension directory should // still exist otherwise the whole thing should have been removed. - if test.version != "" { + if test.version.Version != "" { require.True(t, f.exists(test.extension.Publisher, test.extension.Name)) - require.False(t, f.exists(test.extension.Publisher, test.extension.Name, test.version)) + require.False(t, f.exists(test.extension.Publisher, test.extension.Name, test.version.String())) } else { require.False(t, f.exists(test.extension.Publisher, test.extension.Name)) } @@ -873,7 +956,7 @@ func testVersions(t *testing.T, factory storageFactory) { f := factory(t) ext := testutil.Extensions[0] for _, version := range ext.Versions { - f.write([]byte("stub"), ext.Publisher, ext.Name, version, "extension.vsixmanifest") + f.write([]byte("stub"), ext.Publisher, ext.Name, version.String(), "extension.vsixmanifest") } for _, test := range tests { @@ -887,10 +970,7 @@ func testVersions(t *testing.T, factory storageFactory) { require.True(t, errors.Is(err, test.error)) } else { require.NoError(t, err) - versions := make([]string, len(test.extension.Versions)) - copy(versions, test.extension.Versions) - sort.Sort(sort.Reverse(semver.ByVersion(versions))) - require.Equal(t, versions, got) + require.True(t, sort.IsSorted(storage.ByVersion(got))) } }) } @@ -931,6 +1011,56 @@ func TestExtensionID(t *testing.T) { } } +func TestExtensionVSIXNameWithPlatform(t *testing.T) { + t.Parallel() + + tests := []struct { + // expected is the expected VSIX name. + expected string + // manifest is the manifest from which to build the ID. + manifest *storage.VSIXManifest + // name is the name of the test. + name string + }{ + { + name: "NoPlatform", + expected: "foo.bar-1.0.0", + manifest: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "1.0.0", + }, + }, + }, + }, + { + name: "PlatformWeb", + expected: "foo.bar-1.0.0@web", + manifest: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "1.0.0", + TargetPlatform: storage.PlatformWeb, + }, + }, + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + require.Equal(t, test.expected, + storage.ExtensionVSIXNameFromManifest(test.manifest)) + }) + } +} + func TestParseExtensionID(t *testing.T) { t.Parallel() @@ -1001,3 +1131,42 @@ func TestParseExtensionID(t *testing.T) { }) } } + +func TestSortByVersion(t *testing.T) { + t.Parallel() + + versions := make([]storage.Version, len(testutil.Extensions[0].Versions)) + copy(versions, testutil.Extensions[0].Versions) + tests := []struct { + name string + versions []storage.Version + expected []storage.Version + }{ + { + name: "Compare", + versions: versions, + expected: []storage.Version{ + {Version: "3.0.0"}, + {Version: "3.0.0", TargetPlatform: storage.PlatformAlpineX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformDarwinX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxArm64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformWin32X64}, + {Version: "2.2.2"}, + {Version: "2.0.0"}, + {Version: "1.5.2"}, + {Version: "1.0.0"}, + {Version: "1.0.0", TargetPlatform: storage.PlatformWin32X64}, + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + sort.Sort(storage.ByVersion(test.versions)) + require.Equal(t, test.expected, test.versions) + }) + } +} diff --git a/testutil/extensions.go b/testutil/extensions.go index 6b730bd..62fb385 100644 --- a/testutil/extensions.go +++ b/testutil/extensions.go @@ -20,7 +20,7 @@ type Extension struct { Properties []storage.VSIXProperty Description string Categories string - Versions []string + Versions []storage.Version LatestVersion string Dependencies []string Pack []string @@ -47,7 +47,19 @@ var Extensions = []Extension{ Value: "d.e", }, }, - Versions: []string{"1.0.0", "2.0.0", "3.0.0", "1.5.2", "2.2.2"}, + Versions: []storage.Version{ + {Version: "1.0.0"}, + {Version: "1.0.0", TargetPlatform: storage.PlatformWin32X64}, + {Version: "2.0.0"}, + {Version: "3.0.0"}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformLinuxArm64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformWin32X64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformAlpineX64}, + {Version: "3.0.0", TargetPlatform: storage.PlatformDarwinX64}, + {Version: "1.5.2"}, + {Version: "2.2.2"}, + }, LatestVersion: "3.0.0", Dependencies: []string{"d.e"}, Pack: []string{"a.b", "b.c"}, @@ -68,7 +80,7 @@ var Extensions = []Extension{ Value: "", }, }, - Versions: []string{"version1"}, + Versions: []storage.Version{{Version: "version1"}}, LatestVersion: "version1", }, { @@ -77,7 +89,7 @@ var Extensions = []Extension{ Description: "squigly foo and more foo bar baz", Tags: "tag1,tag2", Categories: "category1,category2", - Versions: []string{"version1", "version2"}, + Versions: []storage.Version{{Version: "version1"}, {Version: "version2"}}, LatestVersion: "version2", }, { @@ -86,7 +98,7 @@ var Extensions = []Extension{ Description: "frobbles the frobnozzle", Tags: "tag3,tag4,tag5", Categories: "category1", - Versions: []string{"version1", "version2"}, + Versions: []storage.Version{{Version: "version1"}, {Version: "version2"}}, LatestVersion: "version2", }, { @@ -95,18 +107,19 @@ var Extensions = []Extension{ Description: "qqqqqqqqqqqqqqqqqqq", Tags: "qq,qqq,qqqq", Categories: "q", - Versions: []string{"qqq", "q"}, + Versions: []storage.Version{{Version: "qqq"}, {Version: "q"}}, LatestVersion: "qqq", }, } -func ConvertExtensionToManifest(ext Extension, version string) *storage.VSIXManifest { +func ConvertExtensionToManifest(ext Extension, version storage.Version) *storage.VSIXManifest { return &storage.VSIXManifest{ Metadata: storage.VSIXMetadata{ Identity: storage.VSIXIdentity{ - ID: ext.Name, - Version: version, - Publisher: ext.Publisher, + ID: ext.Name, + Version: version.Version, + Publisher: ext.Publisher, + TargetPlatform: version.TargetPlatform, }, Properties: storage.VSIXProperties{ Property: ext.Properties, @@ -121,7 +134,7 @@ func ConvertExtensionToManifest(ext Extension, version string) *storage.VSIXMani } } -func ConvertExtensionToManifestBytes(t *testing.T, ext Extension, version string) []byte { +func ConvertExtensionToManifestBytes(t *testing.T, ext Extension, version storage.Version) []byte { manifestBytes, err := xml.Marshal(ConvertExtensionToManifest(ext, version)) require.NoError(t, err) return manifestBytes @@ -171,6 +184,6 @@ func CreateVSIXFromPackageJSON(t *testing.T, packageJSON *storage.VSIXPackageJSO // CreateVSIXFromExtension returns the bytes for a VSIX file containing the // manifest for the provided test extension and an icon. -func CreateVSIXFromExtension(t *testing.T, ext Extension) []byte { - return CreateVSIXFromManifest(t, ConvertExtensionToManifest(ext, ext.LatestVersion)) +func CreateVSIXFromExtension(t *testing.T, ext Extension, version storage.Version) []byte { + return CreateVSIXFromManifest(t, ConvertExtensionToManifest(ext, version)) } diff --git a/testutil/extensions_test.go b/testutil/extensions_test.go index 9653d51..7dae0c5 100644 --- a/testutil/extensions_test.go +++ b/testutil/extensions_test.go @@ -5,13 +5,22 @@ import ( "github.com/stretchr/testify/require" + "github.com/coder/code-marketplace/storage" "github.com/coder/code-marketplace/testutil" ) func TestConvert(t *testing.T) { ext := testutil.Extensions[0] - manifest := testutil.ConvertExtensionToManifest(ext, "a") + + manifest := testutil.ConvertExtensionToManifest(ext, storage.Version{Version: "a"}) + require.Equal(t, manifest.Metadata.Identity.ID, ext.Name) + require.Equal(t, manifest.Metadata.Identity.Publisher, ext.Publisher) + require.Equal(t, manifest.Metadata.Identity.Version, "a") + require.Equal(t, manifest.Metadata.Identity.TargetPlatform, storage.Platform("")) + + manifest = testutil.ConvertExtensionToManifest(ext, storage.Version{Version: "a", TargetPlatform: storage.PlatformDarwinX64}) require.Equal(t, manifest.Metadata.Identity.ID, ext.Name) require.Equal(t, manifest.Metadata.Identity.Publisher, ext.Publisher) require.Equal(t, manifest.Metadata.Identity.Version, "a") + require.Equal(t, manifest.Metadata.Identity.TargetPlatform, storage.PlatformDarwinX64) } diff --git a/testutil/mockdb.go b/testutil/mockdb.go index 2a1983b..781bdd1 100644 --- a/testutil/mockdb.go +++ b/testutil/mockdb.go @@ -31,7 +31,7 @@ func (db *MockDB) GetExtensionAssetPath(ctx context.Context, asset *database.Ass if asset.Type == storage.VSIXAssetType { assetPath = "extension.vsix" } - return strings.Join([]string{baseURL.Path, "files", asset.Publisher, asset.Extension, asset.Version, assetPath}, "/"), nil + return strings.Join([]string{baseURL.Path, "files", asset.Publisher, asset.Extension, asset.Version.String(), assetPath}, "/"), nil } func (db *MockDB) GetExtensions(ctx context.Context, filter database.Filter, flags database.Flag, baseURL url.URL) ([]*database.Extension, int, error) { diff --git a/testutil/mockstorage.go b/testutil/mockstorage.go index 41cc99a..db49fa2 100644 --- a/testutil/mockstorage.go +++ b/testutil/mockstorage.go @@ -5,6 +5,7 @@ import ( "errors" "net/http" "os" + "sort" "github.com/coder/code-marketplace/storage" ) @@ -30,11 +31,13 @@ func (s *MockStorage) FileServer() http.Handler { }) } -func (s *MockStorage) Manifest(ctx context.Context, publisher, name, version string) (*storage.VSIXManifest, error) { +func (s *MockStorage) Manifest(ctx context.Context, publisher, name string, version storage.Version) (*storage.VSIXManifest, error) { for _, ext := range Extensions { if ext.Publisher == publisher && ext.Name == name { for _, ver := range ext.Versions { - if ver == version { + // Use the string encoding to match since that is how the real storage + // implementations will do it too. + if ver.String() == version.String() { return ConvertExtensionToManifest(ext, ver), nil } } @@ -44,19 +47,22 @@ func (s *MockStorage) Manifest(ctx context.Context, publisher, name, version str return nil, os.ErrNotExist } -func (s *MockStorage) RemoveExtension(ctx context.Context, publisher, name, version string) error { +func (s *MockStorage) RemoveExtension(ctx context.Context, publisher, name string, version storage.Version) error { return errors.New("not implemented") } -func (s *MockStorage) WalkExtensions(ctx context.Context, fn func(manifest *storage.VSIXManifest, versions []string) error) error { +func (s *MockStorage) WalkExtensions(ctx context.Context, fn func(manifest *storage.VSIXManifest, versions []storage.Version) error) error { for _, ext := range Extensions { - if err := fn(ConvertExtensionToManifest(ext, ext.Versions[0]), ext.Versions); err != nil { + versions := make([]storage.Version, len(ext.Versions)) + copy(versions, ext.Versions) + sort.Sort(storage.ByVersion(versions)) + if err := fn(ConvertExtensionToManifest(ext, versions[0]), versions); err != nil { return nil } } return nil } -func (s *MockStorage) Versions(ctx context.Context, publisher, name string) ([]string, error) { +func (s *MockStorage) Versions(ctx context.Context, publisher, name string) ([]storage.Version, error) { return nil, errors.New("not implemented") } diff --git a/util/util.go b/util/util.go index 5f54363..9572480 100644 --- a/util/util.go +++ b/util/util.go @@ -14,11 +14,17 @@ func Plural(count int, singular, plural string) string { return strconv.Itoa(count) + " " + plural } -func Contains(a []string, b string) bool { - for _, astr := range a { - if astr == b { +func ContainsCompare[T any](haystack []T, needle T, equal func(a, b T) bool) bool { + for _, hay := range haystack { + if equal(needle, hay) { return true } } return false } + +func Contains[T comparable](haystack []T, needle T) bool { + return ContainsCompare(haystack, needle, func(a, b T) bool { + return a == b + }) +} From d95bd205f5f7dbc1a625b64fd2a49f43ab90d2c9 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 6 Oct 2023 15:47:56 -0800 Subject: [PATCH 06/69] Add some output when adding vsixs If the download or extraction takes a while it can make it seem like the command got stuck trying to start, so maybe this helps. --- cli/add.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/add.go b/cli/add.go index c567702..bd042c2 100644 --- a/cli/add.go +++ b/cli/add.go @@ -74,7 +74,9 @@ func add() *cobra.Command { return err } for _, file := range files { - s, err := doAdd(ctx, filepath.Join(args[0], file.Name()), store) + vsixPath := filepath.Join(args[0], file.Name()) + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Adding %s...\r\n", vsixPath) + s, err := doAdd(ctx, vsixPath, store) if err != nil { failed = append(failed, file.Name()) summary = append(summary, fmt.Sprintf("Failed to unpack %s: %s", file.Name(), err.Error())) @@ -83,6 +85,7 @@ func add() *cobra.Command { } } } else { + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Adding %s...\r\n", args[0]) summary, err = doAdd(ctx, args[0], store) if err != nil { return err From 241ec655b3c6d5a46456ec7c9eea2979150c327a Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 9 Oct 2023 11:24:03 -0800 Subject: [PATCH 07/69] Fix version sorting This whole time it was just coincidentally passing the tests. --- storage/storage.go | 3 ++- storage/storage_test.go | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/storage/storage.go b/storage/storage.go index 6b5ace8..cc2abb0 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -182,7 +182,8 @@ type ByVersion []Version func (vs ByVersion) Len() int { return len(vs) } func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } func (vs ByVersion) Less(i, j int) bool { - cmp := semver.Compare(vs[i].Version, vs[j].Version) + // Go's semver library requires a v prefix. + cmp := semver.Compare("v"+vs[i].Version, "v"+vs[j].Version) if cmp != 0 { return cmp >= 0 } diff --git a/storage/storage_test.go b/storage/storage_test.go index 1484d3c..71d3d4e 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -1159,6 +1159,17 @@ func TestSortByVersion(t *testing.T) { {Version: "1.0.0", TargetPlatform: storage.PlatformWin32X64}, }, }, + { + name: "CompareMSPythonStyle", + versions: []storage.Version{ + {Version: "2023.9.1102792234"}, + {Version: "2023.10.1002811100"}, + }, + expected: []storage.Version{ + {Version: "2023.10.1002811100"}, + {Version: "2023.9.1102792234"}, + }, + }, } for _, test := range tests { From 1008fa2796fe32b5496f949d8436e0c00e0ebaff Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 10 Oct 2023 09:11:43 -0800 Subject: [PATCH 08/69] Tweak add and remove output Rather than outputting all at the end, output as we go. Also return the actual error when adding/removing, not the error when trying to output. --- cli/add.go | 21 ++++++++------------- cli/add_test.go | 2 ++ cli/remove.go | 18 +++++++++++++----- cli/remove_test.go | 16 ++++++++++++---- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/cli/add.go b/cli/add.go index bd042c2..05d61f8 100644 --- a/cli/add.go +++ b/cli/add.go @@ -66,7 +66,6 @@ func add() *cobra.Command { isDir = stat.IsDir() } - var summary []string var failed []string if isDir { files, err := os.ReadDir(args[0]) @@ -74,33 +73,29 @@ func add() *cobra.Command { return err } for _, file := range files { - vsixPath := filepath.Join(args[0], file.Name()) - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Adding %s...\r\n", vsixPath) - s, err := doAdd(ctx, vsixPath, store) + s, err := doAdd(ctx, filepath.Join(args[0], file.Name()), store) if err != nil { + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Failed to unpack %s: %s\n", file.Name(), err.Error()) failed = append(failed, file.Name()) - summary = append(summary, fmt.Sprintf("Failed to unpack %s: %s", file.Name(), err.Error())) } else { - summary = append(summary, s...) + _, _ = fmt.Fprintln(cmd.OutOrStdout(), strings.Join(s, "\n")) } } } else { - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Adding %s...\r\n", args[0]) - summary, err = doAdd(ctx, args[0], store) + s, err := doAdd(ctx, args[0], store) if err != nil { return err } + _, _ = fmt.Fprintln(cmd.OutOrStdout(), strings.Join(s, "\n")) } - _, err = fmt.Fprintln(cmd.OutOrStdout(), strings.Join(summary, "\n")) - failedCount := len(failed) - if failedCount > 0 { + if len(failed) > 0 { return xerrors.Errorf( "Failed to add %s: %s", - util.Plural(failedCount, "extension", ""), + util.Plural(len(failed), "extension", ""), strings.Join(failed, ", ")) } - return err + return nil }, } diff --git a/cli/add_test.go b/cli/add_test.go index 6397213..b2a45f8 100644 --- a/cli/add_test.go +++ b/cli/add_test.go @@ -155,7 +155,9 @@ func TestAdd(t *testing.T) { require.Regexp(t, test.error, err.Error()) } else { require.NoError(t, err) + require.NotContains(t, output, "Failed to add") } + // Should list all the extensions that worked. for _, ext := range test.extensions { // Should exist on disk. diff --git a/cli/remove.go b/cli/remove.go index 2fd4ef2..5c3add7 100644 --- a/cli/remove.go +++ b/cli/remove.go @@ -97,17 +97,25 @@ func remove() *cobra.Command { return xerrors.Errorf("%s does not exist", targetId) } - summary := []string{fmt.Sprintf("Removed %s", util.Plural(len(toDelete), "version", ""))} + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Removing %s...\n", util.Plural(len(toDelete), "version", "")) + var failed []string for _, delete := range toDelete { err = store.RemoveExtension(ctx, publisher, name, delete) if err != nil { - summary = append(summary, fmt.Sprintf(" - %s (%s)", delete, err)) + _, _ = fmt.Fprintf(cmd.OutOrStdout(), " - %s (%s)\n", delete, err) + failed = append(failed, delete.String()) } else { - summary = append(summary, fmt.Sprintf(" - %s", delete)) + _, _ = fmt.Fprintf(cmd.OutOrStdout(), " - %s\n", delete) } } - _, err = fmt.Fprintln(cmd.OutOrStdout(), strings.Join(summary, "\n")) - return err + + if len(failed) > 0 { + return xerrors.Errorf( + "Failed to remove %s: %s", + util.Plural(len(failed), "version", ""), + strings.Join(failed, ", ")) + } + return nil }, } diff --git a/cli/remove_test.go b/cli/remove_test.go index 1fc2ec4..45fd7c8 100644 --- a/cli/remove_test.go +++ b/cli/remove_test.go @@ -35,10 +35,9 @@ func TestRemove(t *testing.T) { tests := []struct { // all means to pass --all. all bool - // error is the expected error. + // error is the expected error, if any. error string - // expected contains the versions should have been deleted. It is ignored - // in the case of an expected error. + // expected contains the versions should have been deleted, if any. expected []storage.Version // extension is the extension to remove. Every version of // testutil.Extensions[0] will be added before each test. @@ -170,8 +169,17 @@ func TestRemove(t *testing.T) { require.Regexp(t, test.error, err.Error()) } else { require.NoError(t, err) - require.Contains(t, output, fmt.Sprintf("Removed %d version", len(test.expected))) + require.NotContains(t, output, "Failed to remove") + } + + // Should list all the extensions that were able to be removed. + if len(test.expected) > 0 { + require.Contains(t, output, fmt.Sprintf("Removing %d version", len(test.expected))) for _, version := range test.expected { + // Should not exist on disk. + dest := filepath.Join(extdir, test.extension.Publisher, test.extension.Name, version.String()) + _, err := os.Stat(dest) + require.Error(t, err) require.Contains(t, output, fmt.Sprintf(" - %s\n", version)) } } From 18c6fa49ad2ee840c76318ed42dd9326f32771e2 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 11 Oct 2023 14:16:13 -0800 Subject: [PATCH 09/69] Release v2.0.0 --- CHANGELOG.md | 18 ++++++++++++++++++ helm/Chart.yaml | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 015a7bc..8cd23c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.0.0](https://github.com/coder/code-marketplace/releases/tag/v2.0.0) - 2023-10-11 + +### Breaking changes + +- When removing extensions, the version is now delineated by `@` instead of `-` + (for example `remove vscodevim.vim@1.0.0`). This fixes being unable to remove + extensions with `-` in their names. Removal is the only backwards-incompatible + change; extensions are still added, stored, and queried the same way. + +### Added + +- Support for platform-specific extensions. Previously all versions would have + been treated as universal and overwritten each other but now versions for + different platforms will be stored separately and show up separately in the + API response. If there are platform-specific versions that have already been + added, they will continue to be treated as universal versions so these should + be removed and re-added to be properly registered as platform-specific. + ## [1.2.2](https://github.com/coder/code-marketplace/releases/tag/v1.2.2) - 2023-05-30 ### Changed diff --git a/helm/Chart.yaml b/helm/Chart.yaml index df8d240..1c0700c 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.2 +version: 1.0.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.2.2" +appVersion: "v2.0.0" From 355a9e10a98e08297dce1cfacb0d66162dd6e4e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:58:50 +0000 Subject: [PATCH 10/69] Bump gopkg.in/yaml.v3 from 3.0.0-20200313102051-9f266ea9e77c to 3.0.0 Bumps gopkg.in/yaml.v3 from 3.0.0-20200313102051-9f266ea9e77c to 3.0.0. --- updated-dependencies: - dependency-name: gopkg.in/yaml.v3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 1e7dc6e..6098e7e 100644 --- a/go.mod +++ b/go.mod @@ -35,5 +35,5 @@ require ( golang.org/x/sys v0.1.0 // indirect golang.org/x/term v0.1.0 // indirect golang.org/x/text v0.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.0 // indirect ) diff --git a/go.sum b/go.sum index 9166608..5b99a47 100644 --- a/go.sum +++ b/go.sum @@ -542,8 +542,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From c180354f57c384d71c35c4e632a30603e14208b1 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 5 Dec 2023 11:06:34 -0900 Subject: [PATCH 11/69] Convert gallery flags to the expected format It does not seem to affect much other than making VS Code properly display a "preview" tag for extensions that are in preview mode, although maybe there are other consequences. --- storage/storage.go | 16 +++++++- storage/storage_test.go | 81 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/storage/storage.go b/storage/storage.go index cc2abb0..f8152b1 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -271,7 +271,21 @@ func parseVSIXManifest(reader io.Reader) (*VSIXManifest, error) { if err != nil { return nil, err } - return vm, validateManifest(vm) + err = validateManifest(vm) + if err != nil { + return vm, err + } + // The manifest stores these as capitalized space-delimited strings but we + // want to present them as lowercased comma-separated strings to VS Code. + // For example, "Public Preview" becomes "public, preview". Make sure to + // handle the case where they are already comma-separated, just in case. + flags := strings.Fields(vm.Metadata.GalleryFlags) + converted := make([]string, len(flags)) + for i, flag := range flags { + converted[i] = strings.ToLower(strings.TrimRight(flag, ",")) + } + vm.Metadata.GalleryFlags = strings.Join(converted, ", ") + return vm, nil } // validateManifest checks a manifest for issues. diff --git a/storage/storage_test.go b/storage/storage_test.go index 71d3d4e..4bbd433 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -585,6 +585,9 @@ func TestReadVSIXManifest(t *testing.T) { tests := []struct { // error is the expected error, if any. error string + // expected is the expected manifest. If not provided, check against + // `manifest` instead. + expected *storage.VSIXManifest // manifest is the manifest from which to create the VSIX. Use `vsix` to // specify raw bytes instead. manifest *storage.VSIXManifest @@ -607,6 +610,78 @@ func TestReadVSIXManifest(t *testing.T) { }, }, }, + { + name: "SpaceSeparatedFlags", + manifest: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "baz", + }, + GalleryFlags: "Public Preview", + }, + }, + expected: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "baz", + }, + GalleryFlags: "public, preview", + }, + }, + }, + { + name: "CommaSpaceSeparatedFlags", + manifest: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "baz", + }, + GalleryFlags: "public, preview", + }, + }, + }, + { + name: "CommaSpaceSpaceSeparatedFlags", + manifest: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "baz", + }, + GalleryFlags: "public, preview", + }, + }, + expected: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "baz", + }, + GalleryFlags: "public, preview", + }, + }, + }, + { + name: "CommaSeparatedFlags", + manifest: &storage.VSIXManifest{ + Metadata: storage.VSIXMetadata{ + Identity: storage.VSIXIdentity{ + Publisher: "foo", + ID: "bar", + Version: "baz", + }, + GalleryFlags: "public,preview", + }, + }, + }, { name: "MissingManifest", error: "not found", @@ -670,8 +745,12 @@ func TestReadVSIXManifest(t *testing.T) { require.Error(t, err) require.Regexp(t, test.error, err.Error()) } else { + expected := test.expected + if expected == nil { + expected = test.manifest + } require.NoError(t, err) - require.Equal(t, test.manifest, manifest) + require.Equal(t, expected, manifest) } }) } From 4bef2c57cf68db5b5b30e0cde014d962996b203b Mon Sep 17 00:00:00 2001 From: Eivind Date: Fri, 8 Dec 2023 18:25:56 +0100 Subject: [PATCH 12/69] Upgrade fuzzysearch dependency to handle extensions with problematic utf8 characters (#31) --- go.mod | 12 ++++++------ go.sum | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6098e7e..29eddbf 100644 --- a/go.mod +++ b/go.mod @@ -8,11 +8,11 @@ require ( github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.7.0 github.com/google/uuid v1.3.0 - github.com/lithammer/fuzzysearch v1.1.5 + github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.6.1 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/mod v0.8.0 + golang.org/x/sync v0.1.0 golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f ) @@ -32,8 +32,8 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.9.0 // indirect gopkg.in/yaml.v3 v3.0.0 // indirect ) diff --git a/go.sum b/go.sum index 5b99a47..6c98329 100644 --- a/go.sum +++ b/go.sum @@ -173,6 +173,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -203,6 +205,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -216,6 +219,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -255,6 +259,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -290,7 +296,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/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 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -315,6 +324,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -356,11 +368,19 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -372,6 +392,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= 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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -423,6 +446,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +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/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= From 98731f381a90cb74d2b35f419082a71728e6d2fd Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 8 Dec 2023 08:37:37 -0900 Subject: [PATCH 13/69] Release v2.0.1 --- CHANGELOG.md | 7 +++++++ helm/Chart.yaml | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cd23c9..4825065 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.0.1](https://github.com/coder/code-marketplace/releases/tag/v2.0.1) - 2023-12-08 + +### Fixed + +- Extensions with problematic UTF-8 characters will no longer cause a panic. +- Preview extensions will now show up as such. + ## [2.0.0](https://github.com/coder/code-marketplace/releases/tag/v2.0.0) - 2023-10-11 ### Breaking changes diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 1c0700c..4ee42b1 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.0 +version: 1.0.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.0.0" +appVersion: "v2.0.1" From c061a1e469deff1c141ee874b0d8f4810b69de54 Mon Sep 17 00:00:00 2001 From: Eivind Date: Thu, 21 Dec 2023 22:59:36 +0100 Subject: [PATCH 14/69] Add caching to local storage provider (#33) * Add cache to local storage * Make cache duration configurable --- cli/server.go | 19 ++++--- storage/artifactory.go | 7 --- storage/local.go | 116 ++++++++++++++++++++++++++++------------- storage/local_test.go | 2 +- storage/storage.go | 23 +++++--- 5 files changed, 108 insertions(+), 59 deletions(-) diff --git a/cli/server.go b/cli/server.go index a77b5ee..d8a03fb 100644 --- a/cli/server.go +++ b/cli/server.go @@ -23,10 +23,11 @@ import ( func server() *cobra.Command { var ( - address string - artifactory string - extdir string - repo string + address string + artifactory string + extdir string + repo string + listcacheduration time.Duration ) cmd := &cobra.Command{ @@ -53,10 +54,11 @@ func server() *cobra.Command { } store, err := storage.NewStorage(ctx, &storage.Options{ - Artifactory: artifactory, - ExtDir: extdir, - Logger: logger, - Repo: repo, + Artifactory: artifactory, + ExtDir: extdir, + Logger: logger, + Repo: repo, + ListCacheDuration: listcacheduration, }) if err != nil { return err @@ -137,6 +139,7 @@ func server() *cobra.Command { cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.") cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.") cmd.Flags().StringVar(&address, "address", "127.0.0.1:3001", "The address on which to serve the marketplace API.") + cmd.Flags().DurationVar(&listcacheduration, "list-cache-duration", time.Minute, "The duration of the extension cache.") return cmd } diff --git a/storage/artifactory.go b/storage/artifactory.go index e46e408..a8523a5 100644 --- a/storage/artifactory.go +++ b/storage/artifactory.go @@ -333,13 +333,6 @@ func (s *Artifactory) RemoveExtension(ctx context.Context, publisher, name strin return err } -type extension struct { - manifest *VSIXManifest - name string - publisher string - versions []Version -} - func (s *Artifactory) listWithCache(ctx context.Context) *[]ArtifactoryFile { s.listMutex.Lock() defer s.listMutex.Unlock() diff --git a/storage/local.go b/storage/local.go index 1ac8b38..1033de1 100644 --- a/storage/local.go +++ b/storage/local.go @@ -8,6 +8,8 @@ import ( "os" "path/filepath" "sort" + "sync" + "time" "cdr.dev/slog" ) @@ -16,21 +18,77 @@ import ( // copying the VSIX and extracting said VSIX to a tree structure in the form of // publisher/extension/version to easily serve individual assets via HTTP. type Local struct { - extdir string - logger slog.Logger + listCache []extension + listDuration time.Duration + listExpiration time.Time + listMutex sync.Mutex + extdir string + logger slog.Logger } -func NewLocalStorage(extdir string, logger slog.Logger) (*Local, error) { - extdir, err := filepath.Abs(extdir) +type LocalOptions struct { + // How long to cache the list of extensions with their manifests. Zero means + // no cache. + ListCacheDuration time.Duration + ExtDir string +} + +func NewLocalStorage(options *LocalOptions, logger slog.Logger) (*Local, error) { + extdir, err := filepath.Abs(options.ExtDir) if err != nil { return nil, err } return &Local{ - extdir: extdir, - logger: logger, + // TODO: Eject the cache when adding/removing extensions and/or add a + // command to eject the cache? + extdir: extdir, + listDuration: options.ListCacheDuration, + logger: logger, }, nil } +func (s *Local) list(ctx context.Context) []extension { + var list []extension + publishers, err := s.getDirNames(ctx, s.extdir) + if err != nil { + s.logger.Error(ctx, "Error reading publisher", slog.Error(err)) + } + for _, publisher := range publishers { + ctx := slog.With(ctx, slog.F("publisher", publisher)) + dir := filepath.Join(s.extdir, publisher) + + extensions, err := s.getDirNames(ctx, dir) + if err != nil { + s.logger.Error(ctx, "Error reading extensions", slog.Error(err)) + } + for _, name := range extensions { + ctx := slog.With(ctx, slog.F("extension", name)) + versions, err := s.Versions(ctx, publisher, name) + if err != nil { + s.logger.Error(ctx, "Error reading versions", slog.Error(err)) + } + if len(versions) == 0 { + continue + } + + // The manifest from the latest version is used for filtering. + manifest, err := s.Manifest(ctx, publisher, name, versions[0]) + if err != nil { + s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err)) + continue + } + + list = append(list, extension{ + manifest, + name, + publisher, + versions, + }) + } + } + return list +} + func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) { // Extract the zip to the correct path. identity := manifest.Metadata.Identity @@ -118,39 +176,23 @@ func (s *Local) Versions(ctx context.Context, publisher, name string) ([]Version return versions, err } -func (s *Local) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error { - publishers, err := s.getDirNames(ctx, s.extdir) - if err != nil { - s.logger.Error(ctx, "Error reading publisher", slog.Error(err)) +func (s *Local) listWithCache(ctx context.Context) []extension { + s.listMutex.Lock() + defer s.listMutex.Unlock() + if s.listCache == nil || time.Now().After(s.listExpiration) { + s.listExpiration = time.Now().Add(s.listDuration) + s.listCache = s.list(ctx) } - for _, publisher := range publishers { - ctx := slog.With(ctx, slog.F("publisher", publisher)) - dir := filepath.Join(s.extdir, publisher) - - extensions, err := s.getDirNames(ctx, dir) - if err != nil { - s.logger.Error(ctx, "Error reading extensions", slog.Error(err)) - } - for _, extension := range extensions { - ctx := slog.With(ctx, slog.F("extension", extension)) - versions, err := s.Versions(ctx, publisher, extension) - if err != nil { - s.logger.Error(ctx, "Error reading versions", slog.Error(err)) - } - if len(versions) == 0 { - continue - } - - // The manifest from the latest version is used for filtering. - manifest, err := s.Manifest(ctx, publisher, extension, versions[0]) - if err != nil { - s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err)) - continue - } + return s.listCache +} - if err = fn(manifest, versions); err != nil { - return err - } +func (s *Local) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error { + // Walking through directories on disk and parsing manifest files takes several + // minutes with many extensions installed, so if we already did that within + // a specified duration, just load extensions from the cache instead. + for _, extension := range s.listWithCache(ctx) { + if err := fn(extension.manifest, extension.versions); err != nil { + return err } } return nil diff --git a/storage/local_test.go b/storage/local_test.go index d79fcfa..ae5a8e6 100644 --- a/storage/local_test.go +++ b/storage/local_test.go @@ -15,7 +15,7 @@ import ( func localFactory(t *testing.T) testStorage { extdir := t.TempDir() logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) - s, err := storage.NewLocalStorage(extdir, logger) + s, err := storage.NewLocalStorage(&storage.LocalOptions{ExtDir: extdir}, logger) require.NoError(t, err) return testStorage{ storage: s, diff --git a/storage/storage.go b/storage/storage.go index f8152b1..ecdaa50 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -123,10 +123,18 @@ type VSIXAsset struct { } type Options struct { - Artifactory string - ExtDir string - Repo string - Logger slog.Logger + Artifactory string + ExtDir string + Repo string + Logger slog.Logger + ListCacheDuration time.Duration +} + +type extension struct { + manifest *VSIXManifest + name string + publisher string + versions []Version } // Version is a subset of database.ExtVersion. @@ -238,14 +246,17 @@ func NewStorage(ctx context.Context, options *Options) (Storage, error) { return nil, xerrors.Errorf("the %s environment variable must be set", ArtifactoryTokenEnvKey) } return NewArtifactoryStorage(ctx, &ArtifactoryOptions{ - ListCacheDuration: time.Minute, + ListCacheDuration: options.ListCacheDuration, Logger: options.Logger, Repo: options.Repo, Token: token, URI: options.Artifactory, }) } else if options.ExtDir != "" { - return NewLocalStorage(options.ExtDir, options.Logger) + return NewLocalStorage(&LocalOptions{ + ListCacheDuration: options.ListCacheDuration, + ExtDir: options.ExtDir, + }, options.Logger) } return nil, xerrors.Errorf("must provide an Artifactory repository or local directory") } From 6fb4e72e7578916e279835f94214182dcaf11d77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:00:02 -0900 Subject: [PATCH 15/69] Bump golang.org/x/crypto from 0.1.0 to 0.17.0 (#32) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 26 +++++++++----------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 29eddbf..0a0da9c 100644 --- a/go.mod +++ b/go.mod @@ -31,9 +31,9 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.23.0 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.0 // indirect ) diff --git a/go.sum b/go.sum index 6c98329..69ba6ab 100644 --- a/go.sum +++ b/go.sum @@ -171,8 +171,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= -github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -220,8 +218,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 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= @@ -257,7 +255,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -297,9 +294,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/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 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 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= @@ -322,7 +318,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -371,16 +366,14 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -390,11 +383,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 6e16be9ac4e277ba424a02de16c7c559cd40647a Mon Sep 17 00:00:00 2001 From: Asher Date: Thu, 21 Dec 2023 13:11:21 -0900 Subject: [PATCH 16/69] Release v2.1.0 --- CHANGELOG.md | 12 ++++++++++++ helm/Chart.yaml | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4825065..4c02ce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.1.0](https://github.com/coder/code-marketplace/releases/tag/v2.1.0) - 2023-12-21 + +### Changed + +- The duration of the cache used when listing/searching extensions is now + configurable with `--list-cache-duration`. The default is still one minute. + +### Added + +- Local storage will also use a cache for listing/searching extensions + (previously only Artifactory storage used a cache). + ## [2.0.1](https://github.com/coder/code-marketplace/releases/tag/v2.0.1) - 2023-12-08 ### Fixed diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 4ee42b1..d891e1f 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.1 +version: 1.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.0.1" +appVersion: "v2.1.0" From e8aba0bed2c649a5a47c787164b9bcced630925b Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 22 Mar 2024 11:02:22 -0800 Subject: [PATCH 17/69] Add CORS tests --- api/api.go | 11 +--- api/httpmw/cors.go | 38 +++++++++++++ api/httpmw/cors_test.go | 123 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 api/httpmw/cors.go create mode 100644 api/httpmw/cors_test.go diff --git a/api/api.go b/api/api.go index 34041b9..a06381e 100644 --- a/api/api.go +++ b/api/api.go @@ -7,7 +7,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" - "github.com/go-chi/cors" "cdr.dev/slog" "github.com/coder/code-marketplace/api/httpapi" @@ -74,16 +73,8 @@ func New(options *Options) *API { r := chi.NewRouter() - cors := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowedMethods: []string{"POST", "GET", "OPTIONS"}, - AllowedHeaders: []string{"*"}, - AllowCredentials: true, - MaxAge: 300, - }) - r.Use( - cors.Handler, + httpmw.Cors(), httpmw.RateLimitPerMinute(options.RateLimit), middleware.GetHead, httpmw.AttachRequestID, diff --git a/api/httpmw/cors.go b/api/httpmw/cors.go new file mode 100644 index 0000000..e0a2469 --- /dev/null +++ b/api/httpmw/cors.go @@ -0,0 +1,38 @@ +package httpmw + +import ( + "net/http" + + "github.com/go-chi/cors" +) + +const ( + // Server headers. + AccessControlAllowOriginHeader = "Access-Control-Allow-Origin" + AccessControlAllowCredentialsHeader = "Access-Control-Allow-Credentials" + AccessControlAllowMethodsHeader = "Access-Control-Allow-Methods" + AccessControlAllowHeadersHeader = "Access-Control-Allow-Headers" + VaryHeader = "Vary" + + // Client headers. + OriginHeader = "Origin" + AccessControlRequestMethodHeader = "Access-Control-Request-Method" + AccessControlRequestHeadersHeader = "Access-Control-Request-Headers" +) + +func Cors() func(next http.Handler) http.Handler { + return cors.Handler(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{ + http.MethodHead, + http.MethodGet, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + }, + AllowedHeaders: []string{"*"}, + AllowCredentials: true, + MaxAge: 300, + }) +} diff --git a/api/httpmw/cors_test.go b/api/httpmw/cors_test.go new file mode 100644 index 0000000..2e1fedf --- /dev/null +++ b/api/httpmw/cors_test.go @@ -0,0 +1,123 @@ +package httpmw_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/code-marketplace/api/httpmw" +) + +func TestCors(t *testing.T) { + t.Parallel() + + methods := []string{ + http.MethodOptions, + http.MethodHead, + http.MethodGet, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + } + + tests := []struct { + name string + origin string + allowedOrigin string + headers string + allowedHeaders string + }{ + { + name: "LocalHTTP", + origin: "http://localhost:3000", + allowedOrigin: "*", + }, + { + name: "LocalHTTPS", + origin: "https://localhost:3000", + allowedOrigin: "*", + }, + { + name: "HTTP", + origin: "http://code-server.domain.tld", + allowedOrigin: "*", + }, + { + name: "HTTPS", + origin: "https://code-server.domain.tld", + allowedOrigin: "*", + }, + { + // VS Code appears to use this origin. + name: "VSCode", + origin: "vscode-file://vscode-app", + allowedOrigin: "*", + }, + { + name: "NoOrigin", + allowedOrigin: "", + }, + { + name: "Headers", + origin: "foobar", + allowedOrigin: "*", + headers: "X-TEST,X-TEST2", + allowedHeaders: "X-Test, X-Test2", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + for _, method := range methods { + method := method + t.Run(method, func(t *testing.T) { + t.Parallel() + + r := httptest.NewRequest(method, "http://dev.coder.com", nil) + if test.origin != "" { + r.Header.Set(httpmw.OriginHeader, test.origin) + } + + // OPTIONS requests need to know what method will be requested, or + // go-chi/cors will error. Both request headers and methods should be + // ignored for regular requests even if they are set, although that is + // not tested here. + if method == http.MethodOptions { + r.Header.Set(httpmw.AccessControlRequestMethodHeader, http.MethodGet) + if test.headers != "" { + r.Header.Set(httpmw.AccessControlRequestHeadersHeader, test.headers) + } + } + + rw := httptest.NewRecorder() + handler := httpmw.Cors()(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusNoContent) + })) + handler.ServeHTTP(rw, r) + + // Should always set some kind of allowed origin, if allowed. + require.Equal(t, test.allowedOrigin, rw.Header().Get(httpmw.AccessControlAllowOriginHeader)) + + // OPTIONS should echo back the request method and headers and we + // should never get to our handler as the middleware short-circuits + // with a 200. + if method == http.MethodOptions { + require.Equal(t, http.MethodGet, rw.Header().Get(httpmw.AccessControlAllowMethodsHeader)) + require.Equal(t, test.allowedHeaders, rw.Header().Get(httpmw.AccessControlAllowHeadersHeader)) + require.Equal(t, http.StatusOK, rw.Code) + } else { + require.Equal(t, "", rw.Header().Get(httpmw.AccessControlAllowMethodsHeader)) + require.Equal(t, "", rw.Header().Get(httpmw.AccessControlAllowHeadersHeader)) + require.Equal(t, http.StatusNoContent, rw.Code) + } + }) + } + }) + } +} From f44f339defbe03aa95da291cb4e61a42013c0e9b Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 3 Jul 2024 12:58:39 -0800 Subject: [PATCH 18/69] Add contributing docs --- CONTRIBUTING.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 35 ----------------------------------- 2 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a0193fa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing + +## Development + +```console +mkdir extensions +go run ./cmd/marketplace/main.go server [flags] +``` + +When you make a change that affects people deploying the marketplace please +update the changelog as part of your PR. + +You can use `make gen` to generate a mock `extensions` directory for testing and +`make upload` to upload them to an Artifactory repository. + +## Tests + +To run the tests: + +``` +make test +``` + +To run the Artifactory tests against a real repository instead of a mock: + +``` +export ARTIFACTORY_URI=myuri +export ARTIFACTORY_REPO=myrepo +export ARTIFACTORY_TOKEN=mytoken +make test +``` + +See the readme for using the marketplace with code-server. + +When testing with code-server you may run into issues with content security +policy if the marketplace runs on a different domain over HTTP; in this case you +will need to disable content security policy in your browser or manually edit +the policy in code-server's source. + +## Releasing + +1. Check that the changelog lists all the important changes. +2. Update the changelog with the release date. +3. Push a tag with the new version. +4. Update the resulting draft release with the changelog contents. +5. Publish the draft release. +6. Bump the Helm chart version once the Docker images have published. diff --git a/README.md b/README.md index 63702ca..3210ed3 100644 --- a/README.md +++ b/README.md @@ -167,41 +167,6 @@ Although not officially supported, you can follow the examples below to start us - [VS Code](https://github.com/eclipse/openvsx/wiki/Using-Open-VSX-in-VS-Code) - [VSCodium](https://github.com/VSCodium/vscodium/blob/master/DOCS.md#how-to-use-the-vs-code-marketplace) -## Development - -```console -mkdir extensions -go run ./cmd/marketplace/main.go server [flags] -``` - -When testing with code-server you may run into issues with content security -policy if the marketplace runs on a different domain over HTTP; in this case you -will need to disable content security policy in your browser or manually edit -the policy in code-server's source. - -When you make a change that affects people deploying the marketplace please -update the changelog as part of your PR. - -You can use `make gen` to generate a mock `extensions` directory for testing and -`make upload` to upload them to an Artifactory repository. - -### Tests - -To run the tests: - -``` -make test -``` - -To run the Artifactory tests against a real repository instead of a mock: - -``` -export ARTIFACTORY_URI=myuri -export ARTIFACTORY_REPO=myrepo -export ARTIFACTORY_TOKEN=mytoken -make test -``` - ## Missing features - Recommended extensions. From a4e7edb0cb48baee0976d8c98f29b718f4e0c60c Mon Sep 17 00:00:00 2001 From: Benjamin Peinhardt <61021968+bcpeinhardt@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:30:01 -0500 Subject: [PATCH 19/69] add -f to rm mock extension file in /fixtures/generate (#41) --- fixtures/generate.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fixtures/generate.bash b/fixtures/generate.bash index 5e2099b..a653425 100755 --- a/fixtures/generate.bash +++ b/fixtures/generate.bash @@ -121,7 +121,7 @@ mock changelog EOF cp "$dir/icon.png" "$dest/extension/images/icon.png" pushd "$dest" >/dev/null - rm "$publisher.$name@$version.vsix" + rm -f "$publisher.$name@$version.vsix" zip -r "$publisher.$name@$version.vsix" * -q popd >/dev/null done < "$dir/versions" From fcce6b1f2b6b9b4556de7e5d42849f9f36d582ff Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 15 Jul 2024 11:38:40 -0800 Subject: [PATCH 20/69] Remove $ from curl command It makes copying the command annoying since you have to delete the $. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3210ed3..aa5a2d3 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ One way to test this is to make a query and check one of the URLs in the response: ```console -$ curl 'https://example.com/api/extensionquery' -H 'Accept: application/json;api-version=3.0-preview.1' --compressed -H 'Content-Type: application/json' --data-raw '{"filters":[{"criteria":[{"filterType":8,"value":"Microsoft.VisualStudio.Code"}],"pageSize":1}],"flags":439}' | jq .results[0].extensions[0].versions[0].assetUri +curl 'https://example.com/api/extensionquery' -H 'Accept: application/json;api-version=3.0-preview.1' --compressed -H 'Content-Type: application/json' --data-raw '{"filters":[{"criteria":[{"filterType":8,"value":"Microsoft.VisualStudio.Code"}],"pageSize":1}],"flags":439}' | jq .results[0].extensions[0].versions[0].assetUri "https://example.com/assets/vscodevim/vim/1.24.1" ``` From e6ad677d09f1dcfdb9e71b2d6c05e8b187d63aa9 Mon Sep 17 00:00:00 2001 From: Benjamin Peinhardt <61021968+bcpeinhardt@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:28:31 -0500 Subject: [PATCH 21/69] make max page size configurable initial code (#42) * make max page size configurable initial code * extract constant for max page size default, double to 100 * update default page size to 200 + tweak error message * remember to stage changes * update changelog --- CHANGELOG.md | 8 ++++++++ api/api.go | 32 +++++++++++++++++++++----------- api/api_test.go | 11 ++++++----- cli/server.go | 9 ++++++--- storage/local.go | 2 +- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c02ce1..11af348 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Default max page size is now 200. + +### Added + +- Added a flag --max-page-size to server subcommand to set the max page size. + ## [2.1.0](https://github.com/coder/code-marketplace/releases/tag/v2.1.0) - 2023-12-21 ### Changed diff --git a/api/api.go b/api/api.go index a06381e..0d4bbee 100644 --- a/api/api.go +++ b/api/api.go @@ -4,6 +4,7 @@ import ( "encoding/json" "net/http" "os" + "strconv" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" @@ -15,6 +16,8 @@ import ( "github.com/coder/code-marketplace/storage" ) +const MaxPageSizeDefault int = 200 + // QueryRequest implements an untyped object. It is the data sent to the API to // query for extensions. // https://github.com/microsoft/vscode/blob/a69f95fdf3dc27511517eef5ff62b21c7a418015/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L338-L342 @@ -55,14 +58,16 @@ type Options struct { Database database.Database Logger slog.Logger // Set to <0 to disable. - RateLimit int - Storage storage.Storage + RateLimit int + Storage storage.Storage + MaxPageSize int } type API struct { - Database database.Database - Handler http.Handler - Logger slog.Logger + Database database.Database + Handler http.Handler + Logger slog.Logger + MaxPageSize int } // New creates a new API server. @@ -71,6 +76,10 @@ func New(options *Options) *API { options.RateLimit = 512 } + if options.MaxPageSize == 0 { + options.MaxPageSize = MaxPageSizeDefault + } + r := chi.NewRouter() r.Use( @@ -84,9 +93,10 @@ func New(options *Options) *API { ) api := &API{ - Database: options.Database, - Handler: r, - Logger: options.Logger, + Database: options.Database, + Handler: r, + Logger: options.Logger, + MaxPageSize: options.MaxPageSize, } r.Get("/", func(rw http.ResponseWriter, r *http.Request) { @@ -163,10 +173,10 @@ func (api *API) extensionQuery(rw http.ResponseWriter, r *http.Request) { }) } for _, filter := range query.Filters { - if filter.PageSize < 0 || filter.PageSize > 50 { + if filter.PageSize < 0 || filter.PageSize > api.MaxPageSize { httpapi.Write(rw, http.StatusBadRequest, httpapi.ErrorResponse{ - Message: "Invalid page size", - Detail: "Check that the page size is between zero and fifty", + Message: "The page size must be between 0 and " + strconv.Itoa(api.MaxPageSize), + Detail: "Contact an administrator to increase the page size", RequestID: httpmw.RequestID(r), }) } diff --git a/api/api_test.go b/api/api_test.go index f511577..62b9144 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -119,8 +119,8 @@ func TestAPI(t *testing.T) { }}, }, Response: &httpapi.ErrorResponse{ - Message: "Invalid page size", - Detail: "Check that the page size is between zero and fifty", + Message: "The page size must be between 0 and 200", + Detail: "Contact an administrator to increase the page size", }, }, { @@ -255,9 +255,10 @@ func TestAPI(t *testing.T) { logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) apiServer := api.New(&api.Options{ - Database: testutil.NewMockDB(exts), - Storage: testutil.NewMockStorage(), - Logger: logger, + Database: testutil.NewMockDB(exts), + Storage: testutil.NewMockStorage(), + Logger: logger, + MaxPageSize: api.MaxPageSizeDefault, }) server := httptest.NewServer(apiServer.Handler) diff --git a/cli/server.go b/cli/server.go index d8a03fb..befc20b 100644 --- a/cli/server.go +++ b/cli/server.go @@ -28,6 +28,7 @@ func server() *cobra.Command { extdir string repo string listcacheduration time.Duration + maxpagesize int ) cmd := &cobra.Command{ @@ -85,9 +86,10 @@ func server() *cobra.Command { // Start the API server. mapi := api.New(&api.Options{ - Database: database, - Storage: store, - Logger: logger, + Database: database, + Storage: store, + Logger: logger, + MaxPageSize: maxpagesize, }) server := &http.Server{ Handler: mapi.Handler, @@ -136,6 +138,7 @@ func server() *cobra.Command { } cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.") + cmd.Flags().IntVar(&maxpagesize, "max-page-size", api.MaxPageSizeDefault, "The maximum number of pages to request") cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.") cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.") cmd.Flags().StringVar(&address, "address", "127.0.0.1:3001", "The address on which to serve the marketplace API.") diff --git a/storage/local.go b/storage/local.go index 1033de1..363eae2 100644 --- a/storage/local.go +++ b/storage/local.go @@ -27,7 +27,7 @@ type Local struct { } type LocalOptions struct { - // How long to cache the list of extensions with their manifests. Zero means + // How long to cache the list of extensions with their manifests. Zero means // no cache. ListCacheDuration time.Duration ExtDir string From 8ad47d62bc02fcb4bc986cc853543835565c1781 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Jul 2024 12:10:50 -0800 Subject: [PATCH 22/69] Fix no origin test with CORS It seems the middleware does not send back any headers when the origin header is missing. --- api/httpmw/cors_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/api/httpmw/cors_test.go b/api/httpmw/cors_test.go index 2e1fedf..37aeba1 100644 --- a/api/httpmw/cors_test.go +++ b/api/httpmw/cors_test.go @@ -104,10 +104,14 @@ func TestCors(t *testing.T) { // Should always set some kind of allowed origin, if allowed. require.Equal(t, test.allowedOrigin, rw.Header().Get(httpmw.AccessControlAllowOriginHeader)) - // OPTIONS should echo back the request method and headers and we - // should never get to our handler as the middleware short-circuits - // with a 200. - if method == http.MethodOptions { + // OPTIONS should echo back the request method and headers (if there + // is an origin header set) and we should never get to our handler as + // the middleware short-circuits with a 200. + if method == http.MethodOptions && test.origin == "" { + require.Equal(t, "", rw.Header().Get(httpmw.AccessControlAllowMethodsHeader)) + require.Equal(t, "", rw.Header().Get(httpmw.AccessControlAllowHeadersHeader)) + require.Equal(t, http.StatusOK, rw.Code) + } else if method == http.MethodOptions { require.Equal(t, http.MethodGet, rw.Header().Get(httpmw.AccessControlAllowMethodsHeader)) require.Equal(t, test.allowedHeaders, rw.Header().Get(httpmw.AccessControlAllowHeadersHeader)) require.Equal(t, http.StatusOK, rw.Code) From bf59bee3ee966743f1a2c0ebb8cfad25dd255338 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Jul 2024 12:18:05 -0800 Subject: [PATCH 23/69] Use `go install` to install gotestsum The macOS runner keeps saying gotestsum does not exist. --- .github/workflows/test.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3a1e4eb..420be98 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -56,11 +56,7 @@ jobs: key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} - name: Install gotestsum - uses: jaxxstorm/action-install-gh-release@v1.7.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - repo: gotestyourself/gotestsum - tag: v1.7.0 + shell: bash + run: go install gotest.tools/gotestsum@latest - run: make test From c5afa311bdae287f6da7c1e2817cbee5616dc587 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Jul 2024 12:21:34 -0800 Subject: [PATCH 24/69] v2.2.0 --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11af348..9bfe895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.2.0](https://github.com/coder/code-marketplace/releases/tag/v2.2.0) - 2024-07-17 + ### Changed -- Default max page size is now 200. +- Default max page size increased from 50 to 200. ### Added -- Added a flag --max-page-size to server subcommand to set the max page size. +- New `server` sub-command flag `--max-page-size` for setting the max page size. ## [2.1.0](https://github.com/coder/code-marketplace/releases/tag/v2.1.0) - 2023-12-21 From 948daf067e7308f349b818e6485b4e98bde1b838 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Jul 2024 12:27:19 -0800 Subject: [PATCH 25/69] Update Helm chart with v2.2.0 --- helm/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index d891e1f..43e9cbe 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.0 +version: 1.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.1.0" +appVersion: "v2.2.0" From 3ae094e75206f36bf7341b0da24effa79bbb639f Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Jul 2024 12:27:34 -0800 Subject: [PATCH 26/69] Configure Dependabot --- .github/dependabot.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..fa24f06 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + time: "06:00" + timezone: "America/Chicago" + labels: [] + groups: + github-actions: + patterns: + - "*" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + time: "06:00" + timezone: "America/Chicago" + labels: [] + open-pull-requests-limit: 15 + groups: + x: + patterns: + - "golang.org/x/*" From 0bb38f4e90857a2f53f5dba3fd2117e8dc4b5555 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 17 Jul 2024 12:32:18 -0800 Subject: [PATCH 27/69] Move --list-cache-duration in changelog A new flag feels more like an addition than a change, especially since not using the flag results in no change. Change the wording to match the recently added --max-page-size flag as well. --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bfe895..fff2bd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,13 +19,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.1.0](https://github.com/coder/code-marketplace/releases/tag/v2.1.0) - 2023-12-21 -### Changed - -- The duration of the cache used when listing/searching extensions is now - configurable with `--list-cache-duration`. The default is still one minute. - ### Added +- New `server` sub-command flag `--list-cache-duration` for setting the duration + of the cache used when listing and searching extensions. The default is still + one minute. - Local storage will also use a cache for listing/searching extensions (previously only Artifactory storage used a cache). From 5c7060f741018f2bbdcb572bfc89a4791fdaf1a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:48:35 -0800 Subject: [PATCH 28/69] Bump the github-actions group with 11 updates (#50) Bumps the github-actions group with 11 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `3` | `4` | | [actions/setup-go](https://github.com/actions/setup-go) | `3` | `5` | | [actions/cache](https://github.com/actions/cache) | `3` | `4` | | [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `1` | `2` | | [contributor-assistant/github-action](https://github.com/contributor-assistant/github-action) | `2.2.1` | `2.4.0` | | [azure/setup-helm](https://github.com/azure/setup-helm) | `3.5` | `4` | | [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) | `3.2.0` | `6.0.1` | | [robinraju/release-downloader](https://github.com/robinraju/release-downloader) | `1.5` | `1.11` | | [docker/login-action](https://github.com/docker/login-action) | `2` | `3` | | [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) | `2` | `3` | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `2` | `3` | Updates `actions/checkout` from 3 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) Updates `actions/setup-go` from 3 to 5 - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v5) Updates `actions/cache` from 3 to 4 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) Updates `softprops/action-gh-release` from 1 to 2 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2) Updates `contributor-assistant/github-action` from 2.2.1 to 2.4.0 - [Release notes](https://github.com/contributor-assistant/github-action/releases) - [Commits](https://github.com/contributor-assistant/github-action/compare/v2.2.1...v2.4.0) Updates `azure/setup-helm` from 3.5 to 4 - [Release notes](https://github.com/azure/setup-helm/releases) - [Changelog](https://github.com/Azure/setup-helm/blob/main/CHANGELOG.md) - [Commits](https://github.com/azure/setup-helm/compare/v3.5...v4) Updates `golangci/golangci-lint-action` from 3.2.0 to 6.0.1 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.2.0...v6.0.1) Updates `robinraju/release-downloader` from 1.5 to 1.11 - [Release notes](https://github.com/robinraju/release-downloader/releases) - [Commits](https://github.com/robinraju/release-downloader/compare/v1.5...v1.11) Updates `docker/login-action` from 2 to 3 - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) Updates `docker/setup-qemu-action` from 2 to 3 - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3) Updates `docker/setup-buildx-action` from 2 to 3 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: contributor-assistant/github-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: azure/setup-helm dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: robinraju/release-downloader dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 10 +++++----- .github/workflows/cla.yaml | 2 +- .github/workflows/helm.yaml | 4 ++-- .github/workflows/lint.yaml | 6 +++--- .github/workflows/publish.yaml | 10 +++++----- .github/workflows/test.yaml | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2986b33..4b2a6be 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,8 +13,8 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version: "~1.19" @@ -25,20 +25,20 @@ jobs: echo "::set-output name=go-mod::$(go env GOMODCACHE)" - name: Go build cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.go-cache-paths.outputs.go-build }} key: ${{ runner.os }}-release-go-build-${{ hashFiles('**/go.sum') }} - name: Go mod cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.go-cache-paths.outputs.go-mod }} key: ${{ runner.os }}-release-go-mod-${{ hashFiles('**/go.sum') }} - run: make build - - uses: softprops/action-gh-release@v1 + - uses: softprops/action-gh-release@v2 with: draft: true files: ./bin/* diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index 81aafca..412cac6 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -11,7 +11,7 @@ jobs: steps: - name: "CLA Assistant" if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - uses: contributor-assistant/github-action@v2.2.1 + uses: contributor-assistant/github-action@v2.4.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # the below token should have repo scope and must be manually added by you in the repository's secret diff --git a/.github/workflows/helm.yaml b/.github/workflows/helm.yaml index 23f736c..824a108 100644 --- a/.github/workflows/helm.yaml +++ b/.github/workflows/helm.yaml @@ -24,8 +24,8 @@ jobs: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: azure/setup-helm@v3.5 + - uses: actions/checkout@v4 + - uses: azure/setup-helm@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - run: helm lint --strict ./helm diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 66bbd0d..a3854ea 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -26,11 +26,11 @@ jobs: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version: "~1.19" - name: golangci-lint - uses: golangci/golangci-lint-action@v3.2.0 + uses: golangci/golangci-lint-action@v6.0.1 with: version: v1.48.0 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 5809659..692e728 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -18,22 +18,22 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: robinraju/release-downloader@v1.5 + - uses: actions/checkout@v4 + - uses: robinraju/release-downloader@v1.11 with: repository: "coder/code-marketplace" tag: ${{ github.event.inputs.version || github.ref_name }} fileName: "code-marketplace-linux-*" out-file-path: "bin" - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/setup-qemu-action@v2 - - uses: docker/setup-buildx-action@v2 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 - run: docker buildx bake -f ./docker-bake.hcl --push env: VERSION: ${{ github.event.inputs.version || github.ref_name }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 420be98..fe7a8e6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -32,8 +32,8 @@ jobs: - macos-latest - windows-2022 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version: "~1.19" @@ -44,13 +44,13 @@ jobs: echo "::set-output name=go-mod::$(go env GOMODCACHE)" - name: Go Build Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.go-cache-paths.outputs.go-build }} key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.**', '**.go') }} - name: Go Mod Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.go-cache-paths.outputs.go-mod }} key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} From 27dadb5adcdc3479cac0647e619c05610cc495a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:50:00 -0800 Subject: [PATCH 29/69] Bump github.com/go-chi/httprate from 0.7.0 to 0.9.0 (#49) Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.7.0 to 0.9.0. - [Commits](https://github.com/go-chi/httprate/compare/v0.7.0...v0.9.0) --- updated-dependencies: - dependency-name: github.com/go-chi/httprate dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0a0da9c..b925575 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cdr.dev/slog v1.4.1 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 - github.com/go-chi/httprate v0.7.0 + github.com/go-chi/httprate v0.9.0 github.com/google/uuid v1.3.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.5.0 diff --git a/go.sum b/go.sum index 69ba6ab..a7329cb 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/httprate v0.7.0 h1:8W0dF7Xa2Duz2p8ncGaehIphrxQGNlOtoGY0+NRRfjQ= -github.com/go-chi/httprate v0.7.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A= +github.com/go-chi/httprate v0.9.0 h1:21A+4WDMDA5FyWcg7mNrhj63aNT8CGh+Z1alOE/piU8= +github.com/go-chi/httprate v0.9.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= From 94ab9937f7859d7e38d73434d53d32cfeb0d4ec0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:50:10 -0800 Subject: [PATCH 30/69] Bump github.com/spf13/cobra from 1.5.0 to 1.8.1 (#48) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.5.0 to 1.8.1. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.5.0...v1.8.1) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index b925575..42ea1ba 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/go-chi/httprate v0.9.0 github.com/google/uuid v1.3.0 github.com/lithammer/fuzzysearch v1.1.8 - github.com/spf13/cobra v1.5.0 + github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.6.1 golang.org/x/mod v0.8.0 golang.org/x/sync v0.1.0 @@ -25,7 +25,7 @@ require ( github.com/fatih/color v1.12.0 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/google/go-cmp v0.5.8 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -35,5 +35,5 @@ require ( golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index a7329cb..ba267b6 100644 --- a/go.sum +++ b/go.sum @@ -61,7 +61,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -161,8 +161,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -187,8 +187,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -558,10 +558,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From dc254034d5bda2729c3a6990265eae1a3666c960 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:50:25 -0800 Subject: [PATCH 31/69] Bump github.com/go-chi/chi/v5 from 5.0.7 to 5.1.0 (#46) Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.0.7 to 5.1.0. - [Release notes](https://github.com/go-chi/chi/releases) - [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-chi/chi/compare/v5.0.7...v5.1.0) --- updated-dependencies: - dependency-name: github.com/go-chi/chi/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 42ea1ba..23aa4db 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( cdr.dev/slog v1.4.1 - github.com/go-chi/chi/v5 v5.0.7 + github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.9.0 github.com/google/uuid v1.3.0 diff --git a/go.sum b/go.sum index ba267b6..bd74779 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= -github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/httprate v0.9.0 h1:21A+4WDMDA5FyWcg7mNrhj63aNT8CGh+Z1alOE/piU8= From f4ab83ca79a39c0a657504ecb109ec0ffcff0709 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:50:53 -0800 Subject: [PATCH 32/69] Bump the x group with 2 updates (#43) Bumps the x group with 2 updates: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/sync](https://github.com/golang/sync). Updates `golang.org/x/mod` from 0.8.0 to 0.19.0 - [Commits](https://github.com/golang/mod/compare/v0.8.0...v0.19.0) Updates `golang.org/x/sync` from 0.1.0 to 0.7.0 - [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor dependency-group: x - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor dependency-group: x ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 23aa4db..4511713 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.6.1 - golang.org/x/mod v0.8.0 - golang.org/x/sync v0.1.0 + golang.org/x/mod v0.19.0 + golang.org/x/sync v0.7.0 golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f ) diff --git a/go.sum b/go.sum index bd74779..a5382da 100644 --- a/go.sum +++ b/go.sum @@ -256,8 +256,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -320,8 +321,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 2b31bd8d3bb288640f130c047febdbaa1a6b3155 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:53:52 -0800 Subject: [PATCH 33/69] Bump github.com/google/uuid from 1.3.0 to 1.6.0 (#44) Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.3.0 to 1.6.0. - [Release notes](https://github.com/google/uuid/releases) - [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/uuid/compare/v1.3.0...v1.6.0) --- updated-dependencies: - dependency-name: github.com/google/uuid dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4511713..bd701c5 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.9.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index a5382da..3544c46 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/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/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= From 020c973cc876e3b74049dab1cec20ecc478d1497 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:54:03 -0800 Subject: [PATCH 34/69] Bump github.com/stretchr/testify from 1.6.1 to 1.9.0 (#47) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.6.1 to 1.9.0. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.6.1...v1.9.0) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index bd701c5..c909f20 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.19.0 golang.org/x/sync v0.7.0 golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f diff --git a/go.sum b/go.sum index 3544c46..56ce809 100644 --- a/go.sum +++ b/go.sum @@ -196,8 +196,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From dd9b525e2f03c2419673b932c913c529c324e93c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:56:58 -0800 Subject: [PATCH 35/69] Bump cdr.dev/slog from 1.4.1 to 1.6.1 (#45) Bumps [cdr.dev/slog](https://github.com/coder/slog) from 1.4.1 to 1.6.1. - [Release notes](https://github.com/coder/slog/releases) - [Commits](https://github.com/coder/slog/compare/v1.4.1...v1.6.1) --- updated-dependencies: - dependency-name: cdr.dev/slog dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 23 +-- go.sum | 549 +++++---------------------------------------------------- 2 files changed, 57 insertions(+), 515 deletions(-) diff --git a/go.mod b/go.mod index c909f20..2cd9e46 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/coder/code-marketplace go 1.19 require ( - cdr.dev/slog v1.4.1 + cdr.dev/slog v1.6.1 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.9.0 @@ -13,24 +13,25 @@ require ( github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.19.0 golang.org/x/sync v0.7.0 - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) require ( - github.com/alecthomas/chroma v0.9.1 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect + github.com/charmbracelet/lipgloss v0.7.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/fatih/color v1.12.0 // indirect - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect - github.com/google/go-cmp v0.5.8 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.opencensus.io v0.23.0 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect diff --git a/go.sum b/go.sum index 56ce809..0e845cc 100644 --- a/go.sum +++ b/go.sum @@ -1,375 +1,90 @@ -cdr.dev/slog v1.4.1 h1:Q8+X63m8/WB4geelMTDO8t4CTwVh1f7+5Cxi7kS/SZg= -cdr.dev/slog v1.4.1/go.mod h1:O76C6gZJxa5HK1SXMrjd48V2kJxYZKFRTcFfn/V9OhA= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0 h1:bAMqZidYkmIsUqe6PtkEPT7Q+vfizScn+jfNA6jwK9c= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= -github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= -github.com/alecthomas/chroma v0.9.1 h1:cBmvQqRImzR5aWqdMxYZByND4S7BCS/g0svZb28h0Dc= -github.com/alecthomas/chroma v0.9.1/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk= -github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= -github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= -github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= -github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= -github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +cdr.dev/slog v1.6.1 h1:IQjWZD0x6//sfv5n+qEhbu3wBkmtBQY5DILXNvMaIv4= +cdr.dev/slog v1.6.1/go.mod h1:eHEYQLaZvxnIAXC+XdTSNLb/kgA/X2RVSF72v5wsxEI= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= +cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= +github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= -github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/httprate v0.9.0 h1:21A+4WDMDA5FyWcg7mNrhj63aNT8CGh+Z1alOE/piU8= github.com/go-chi/httprate v0.9.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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/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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -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= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -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.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -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-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -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= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -377,200 +92,26 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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/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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -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/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= +google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 71f29f1a3d9c937ff7bbf82002601ec93cf39a45 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 14 Aug 2024 11:09:18 -0800 Subject: [PATCH 36/69] Fix vsix name in generate script The vsix still uses a dash. The @ is only used within the cli itself. --- fixtures/generate.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fixtures/generate.bash b/fixtures/generate.bash index a653425..9b9b7cc 100755 --- a/fixtures/generate.bash +++ b/fixtures/generate.bash @@ -121,8 +121,8 @@ mock changelog EOF cp "$dir/icon.png" "$dest/extension/images/icon.png" pushd "$dest" >/dev/null - rm -f "$publisher.$name@$version.vsix" - zip -r "$publisher.$name@$version.vsix" * -q + rm -f "$publisher.$name-$version.vsix" + zip -r "$publisher.$name-$version.vsix" * -q popd >/dev/null done < "$dir/versions" done < "$dir/names" From 63890716fd0e995f93b18f07bb31fa2dd3686c2c Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 14 Aug 2024 11:20:52 -0800 Subject: [PATCH 37/69] Add correct vspackage endpoint See https://github.com/coder/code-marketplace/issues/57 --- CHANGELOG.md | 4 ++++ api/api.go | 7 ++++++- api/api_test.go | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fff2bd9..176380a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Fixed + +- The "attempt to download manually" URL in VS Code will now work. + ## [2.2.0](https://github.com/coder/code-marketplace/releases/tag/v2.2.0) - 2024-07-17 ### Changed diff --git a/api/api.go b/api/api.go index 0d4bbee..50f07c5 100644 --- a/api/api.go +++ b/api/api.go @@ -120,8 +120,13 @@ func New(options *Options) *API { r.Get("/assets/{publisher}/{extension}/{version}/{type}", api.assetRedirect) // This is the "download manually" URL, which like /assets is hardcoded and - // ignores the VSIX asset URL provided to VS Code in the response. + // ignores the VSIX asset URL provided to VS Code in the response. We provide + // it at /publishers for backwards compatibility since that is where we + // originally had it, but VS Code appends to the service URL which means the + // path VS Code actually uses is /api/publishers. + // https://github.com/microsoft/vscode/blob/c727b5484ebfbeff1e1d29654cae5c17af1c826f/build/lib/extensions.ts#L228 r.Get("/publishers/{publisher}/vsextensions/{extension}/{version}/{type}", api.assetRedirect) + r.Get("/api/publishers/{publisher}/vsextensions/{extension}/{version}/{type}", api.assetRedirect) // This is the URL you get taken to when you click the extension's names, // ratings, etc from the extension details page. diff --git a/api/api_test.go b/api/api_test.go index 62b9144..a144482 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -36,6 +36,7 @@ func TestAPI(t *testing.T) { Request any Response any Status int + Method string }{ { Name: "Root", @@ -216,6 +217,7 @@ func TestAPI(t *testing.T) { Status: http.StatusMovedPermanently, Response: "/files/publisher/extension/version@darwin-x64/foo", }, + // Old vspackage path, for backwards compatibility. { Name: "DownloadNotExist", Path: "/publishers/notexist/vsextensions/extension/version/vspackage", @@ -231,6 +233,24 @@ func TestAPI(t *testing.T) { Status: http.StatusMovedPermanently, Response: "/files/publisher/extension/version/extension.vsix", }, + // The vspackage path currently generated by VS Code. + { + Name: "APIDownloadNotExist", + Path: "/api/publishers/notexist/vsextensions/extension/version/vspackage", + Status: http.StatusNotFound, + Response: &httpapi.ErrorResponse{ + Message: "Extension asset does not exist", + Detail: "Please check the asset path", + }, + Method: http.MethodGet, + }, + { + Name: "APIDownloadOK", + Path: "/api/publishers/publisher/vsextensions/extension/version/vspackage", + Status: http.StatusMovedPermanently, + Response: "/files/publisher/extension/version/extension.vsix", + Method: http.MethodGet, + }, { Name: "Item", Path: "/item", @@ -273,9 +293,19 @@ func TestAPI(t *testing.T) { }, } + // Most /api calls are POSTs, the rest are GETs. + var method = c.Method + if method == "" { + if strings.HasPrefix(c.Path, "/api") { + method = http.MethodPost + } else { + method = http.MethodGet + } + } + var resp *http.Response var err error - if strings.HasPrefix(c.Path, "/api") { + if method == http.MethodPost { var body []byte if str, ok := c.Request.(string); ok { body = []byte(str) @@ -284,8 +314,10 @@ func TestAPI(t *testing.T) { require.NoError(t, err) } resp, err = client.Post(url, "application/json", bytes.NewReader(body)) - } else { + } else if method == http.MethodGet { resp, err = client.Get(url) + } else { + t.Fatal(method + " is not handled in the test yet, please add it now") } require.NoError(t, err) require.Equal(t, c.Status, resp.StatusCode) From 5da059cb11d5501a8e06f3821a5a062e88569d90 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 14 Aug 2024 12:02:35 -0800 Subject: [PATCH 38/69] v2.2.1 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 176380a..435e6e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.2.1](https://github.com/coder/code-marketplace/releases/tag/v2.2.1) - 2024-08-14 + ### Fixed - The "attempt to download manually" URL in VS Code will now work. From 2da833080889c44090b0921a1a70c30b791629d0 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 14 Aug 2024 12:07:56 -0800 Subject: [PATCH 39/69] Update Helm chart with v2.2.1 --- helm/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 43e9cbe..4306f04 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.2.0 +version: 1.2.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.2.0" +appVersion: "v2.2.1" From a91b741373c01b00d8151fbeb52419d9ea4d7dbf Mon Sep 17 00:00:00 2001 From: Jan Losinski Date: Wed, 30 Oct 2024 20:43:39 +0100 Subject: [PATCH 40/69] fix(artifactory): ignore extensions without a manifest (#69) This ignores extensions where the manifest download from artifactory failed instead of crashing the process with a segfault. It fixes #68 --- storage/artifactory.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/artifactory.go b/storage/artifactory.go index a8523a5..a20c331 100644 --- a/storage/artifactory.go +++ b/storage/artifactory.go @@ -391,7 +391,7 @@ func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIX return err } else if err != nil { id := ExtensionID(ext.publisher, ext.name, ext.versions[0].Version) - s.logger.Error(ctx, "Unable to read extension manifest", slog.Error(err), + s.logger.Error(ctx, "Unable to read extension manifest; extension will be ignored", slog.Error(err), slog.F("id", id), slog.F("targetPlatform", ext.versions[0].TargetPlatform)) } else { @@ -405,6 +405,9 @@ func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIX return err } for _, ext := range extensions { + if ext.manifest == nil { + continue + } if err = fn(ext.manifest, ext.versions); err != nil { return err } From 2268531216f7927b2cd6c74e5ef251301f5c04f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:54:34 -0800 Subject: [PATCH 41/69] Bump the github-actions group across 1 directory with 2 updates (#64) Bumps the github-actions group with 2 updates in the / directory: [contributor-assistant/github-action](https://github.com/contributor-assistant/github-action) and [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action). Updates `contributor-assistant/github-action` from 2.4.0 to 2.6.1 - [Release notes](https://github.com/contributor-assistant/github-action/releases) - [Commits](https://github.com/contributor-assistant/github-action/compare/v2.4.0...v2.6.1) Updates `golangci/golangci-lint-action` from 6.0.1 to 6.1.1 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v6.0.1...v6.1.1) --- updated-dependencies: - dependency-name: contributor-assistant/github-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cla.yaml | 2 +- .github/workflows/lint.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index 412cac6..71c2e90 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -11,7 +11,7 @@ jobs: steps: - name: "CLA Assistant" if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - uses: contributor-assistant/github-action@v2.4.0 + uses: contributor-assistant/github-action@v2.6.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # the below token should have repo scope and must be manually added by you in the repository's secret diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a3854ea..beeb080 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,6 +31,6 @@ jobs: with: go-version: "~1.19" - name: golangci-lint - uses: golangci/golangci-lint-action@v6.0.1 + uses: golangci/golangci-lint-action@v6.1.1 with: version: v1.48.0 From 3e808a63c6894102d1c6d879e54344225aa96efe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:54:54 -0800 Subject: [PATCH 42/69] Bump github.com/go-chi/httprate from 0.9.0 to 0.14.1 (#60) Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.9.0 to 0.14.1. - [Release notes](https://github.com/go-chi/httprate/releases) - [Commits](https://github.com/go-chi/httprate/compare/v0.9.0...v0.14.1) --- updated-dependencies: - dependency-name: github.com/go-chi/httprate dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2cd9e46..10bbe7d 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cdr.dev/slog v1.6.1 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 - github.com/go-chi/httprate v0.9.0 + github.com/go-chi/httprate v0.14.1 github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.8.1 @@ -18,7 +18,7 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/lipgloss v0.7.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index 0e845cc..f49f711 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5 cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -17,8 +17,8 @@ github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/httprate v0.9.0 h1:21A+4WDMDA5FyWcg7mNrhj63aNT8CGh+Z1alOE/piU8= -github.com/go-chi/httprate v0.9.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A= +github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= +github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= From 17ec2512e73a05f485d4e52ecc002c97a4afd2b6 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 11 Dec 2024 13:26:08 -0600 Subject: [PATCH 43/69] chore: update codium custom marketplace (#78) --- .gitignore | 1 + README.md | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7a0c72b..504cb83 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ bin coverage extensions +.idea \ No newline at end of file diff --git a/README.md b/README.md index aa5a2d3..26a49f0 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,13 @@ marketplace is running behind an https URL. Although not officially supported, you can follow the examples below to start using code-marketplace with VS Code and VSCodium: - [VS Code](https://github.com/eclipse/openvsx/wiki/Using-Open-VSX-in-VS-Code) -- [VSCodium](https://github.com/VSCodium/vscodium/blob/master/DOCS.md#how-to-use-the-vs-code-marketplace) +- [VSCodium](https://github.com/VSCodium/vscodium/blob/master/docs/index.md#howto-switch-marketplace) + ``` + export VSCODE_GALLERY_SERVICE_URL="https:///api + export VSCODE_GALLERY_ITEM_URL="https:///item" + # Or set a product.json file in `~/.config/VSCodium/product.json` + codium + ``` ## Missing features From 2c12582e2c659b234efd294cb25f7cc1ad227b83 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 12 Dec 2024 06:27:20 -0600 Subject: [PATCH 44/69] chore: log hostname in verbose request logs (#77) --- api/httpmw/logger.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api/httpmw/logger.go b/api/httpmw/logger.go index c3ecb51..28d500f 100644 --- a/api/httpmw/logger.go +++ b/api/httpmw/logger.go @@ -15,6 +15,7 @@ func Logger(log slog.Logger) func(next http.Handler) http.Handler { sw := &httpapi.StatusWriter{ResponseWriter: w} httplog := log.With( + slog.F("host", r.Host), slog.F("path", r.URL.Path), slog.F("remote_addr", r.RemoteAddr), slog.F("client_id", r.Header.Get("x-market-client-id")), From f35db417af6c2ec6b58b4204dbde4ccbd8e96ec6 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 13 Dec 2024 10:52:56 -0600 Subject: [PATCH 45/69] chore: implement signing of extensions (#79) * chore: begin work implementing vsix signatures, manifest matching Based on https://github.com/filiptronicek/node-ovsx-sign --- .github/workflows/build.yaml | 2 +- .github/workflows/lint.yaml | 4 +- .github/workflows/test.yaml | 2 +- .gitignore | 2 +- api/api.go | 2 +- api/api_test.go | 2 +- cli/add.go | 31 +------ cli/remove.go | 29 +------ cli/root.go | 5 +- cli/server.go | 82 ++++++++++++------ cli/signature.go | 63 ++++++++++++++ extensionsign/doc.go | 2 + extensionsign/key.go | 14 ++++ extensionsign/sigmanifest.go | 114 +++++++++++++++++++++++++ extensionsign/sigzip.go | 73 ++++++++++++++++ go.mod | 11 ++- go.sum | 45 +++++++--- storage/artifactory.go | 64 +++++++++----- storage/{ => easyzip}/zip.go | 2 +- storage/{ => easyzip}/zip_test.go | 2 +- storage/local.go | 26 +++++- storage/signature.go | 135 ++++++++++++++++++++++++++++++ storage/signature_test.go | 37 ++++++++ storage/storage.go | 68 ++++++++++++--- storage/storage_test.go | 21 ++++- testutil/extensions.go | 8 ++ testutil/mockstorage.go | 17 +++- 27 files changed, 716 insertions(+), 147 deletions(-) create mode 100644 cli/signature.go create mode 100644 extensionsign/doc.go create mode 100644 extensionsign/key.go create mode 100644 extensionsign/sigmanifest.go create mode 100644 extensionsign/sigzip.go rename storage/{ => easyzip}/zip.go (99%) rename storage/{ => easyzip}/zip_test.go (99%) create mode 100644 storage/signature.go create mode 100644 storage/signature_test.go diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4b2a6be..5f7f4f0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "~1.19" + go-version: "~1.22" - name: Get Go cache paths id: go-cache-paths diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index beeb080..62dc0f6 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -29,8 +29,8 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "~1.19" + go-version: "~1.22" - name: golangci-lint uses: golangci/golangci-lint-action@v6.1.1 with: - version: v1.48.0 + version: v1.58.0 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fe7a8e6..e8bca42 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "~1.19" + go-version: "~1.22" - name: Echo Go Cache Paths id: go-cache-paths diff --git a/.gitignore b/.gitignore index 504cb83..92996d1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ bin coverage extensions -.idea \ No newline at end of file +.idea diff --git a/api/api.go b/api/api.go index 50f07c5..91a92df 100644 --- a/api/api.go +++ b/api/api.go @@ -112,7 +112,7 @@ func New(options *Options) *API { r.Post("/api/extensionquery", api.extensionQuery) // Endpoint for getting an extension's files or the extension zip. - r.Mount("/files", http.StripPrefix("/files", options.Storage.FileServer())) + r.Mount("/files", http.StripPrefix("/files", storage.HTTPFileServer(options.Storage))) // VS Code can use the files in the response to get file paths but it will // sometimes ignore that and use requests to /assets with hardcoded types to diff --git a/api/api_test.go b/api/api_test.go index a144482..ab7ff40 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -171,7 +171,7 @@ func TestAPI(t *testing.T) { Response: "foobar", }, { - Name: "FileAPI", + Name: "FileAPINotExists", Path: "/files/nonexistent", Status: http.StatusNotFound, }, diff --git a/cli/add.go b/cli/add.go index 05d61f8..1bf17d4 100644 --- a/cli/add.go +++ b/cli/add.go @@ -10,20 +10,12 @@ import ( "github.com/spf13/cobra" "golang.org/x/xerrors" - "cdr.dev/slog" - "cdr.dev/slog/sloggers/sloghuman" - "github.com/coder/code-marketplace/storage" "github.com/coder/code-marketplace/util" ) func add() *cobra.Command { - var ( - artifactory string - extdir string - repo string - ) - + addFlags, opts := serverFlags() cmd := &cobra.Command{ Use: "add ", Short: "Add an extension to the marketplace", @@ -37,21 +29,7 @@ func add() *cobra.Command { ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() - verbose, err := cmd.Flags().GetBool("verbose") - if err != nil { - return err - } - logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if verbose { - logger = logger.Leveled(slog.LevelDebug) - } - - store, err := storage.NewStorage(ctx, &storage.Options{ - Artifactory: artifactory, - ExtDir: extdir, - Logger: logger, - Repo: repo, - }) + store, err := storage.NewStorage(ctx, opts) if err != nil { return err } @@ -98,10 +76,7 @@ func add() *cobra.Command { return nil }, } - - cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.") - cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.") - cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.") + addFlags(cmd) return cmd } diff --git a/cli/remove.go b/cli/remove.go index 5c3add7..45aa686 100644 --- a/cli/remove.go +++ b/cli/remove.go @@ -10,20 +10,15 @@ import ( "github.com/spf13/cobra" "golang.org/x/xerrors" - "cdr.dev/slog" - "cdr.dev/slog/sloggers/sloghuman" - "github.com/coder/code-marketplace/storage" "github.com/coder/code-marketplace/util" ) func remove() *cobra.Command { var ( - all bool - artifactory string - extdir string - repo string + all bool ) + addFlags, opts := serverFlags() cmd := &cobra.Command{ Use: "remove ", @@ -37,21 +32,7 @@ func remove() *cobra.Command { ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() - verbose, err := cmd.Flags().GetBool("verbose") - if err != nil { - return err - } - logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if verbose { - logger = logger.Leveled(slog.LevelDebug) - } - - store, err := storage.NewStorage(ctx, &storage.Options{ - Artifactory: artifactory, - ExtDir: extdir, - Logger: logger, - Repo: repo, - }) + store, err := storage.NewStorage(ctx, opts) if err != nil { return err } @@ -120,9 +101,7 @@ func remove() *cobra.Command { } cmd.Flags().BoolVar(&all, "all", false, "Whether to delete all versions of the extension.") - cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.") - cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.") - cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.") + addFlags(cmd) return cmd } diff --git a/cli/root.go b/cli/root.go index 435e28f..b6638c3 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1,8 +1,9 @@ package cli import ( - "github.com/spf13/cobra" "strings" + + "github.com/spf13/cobra" ) func Root() *cobra.Command { @@ -16,7 +17,7 @@ func Root() *cobra.Command { }, "\n"), } - cmd.AddCommand(add(), remove(), server(), version()) + cmd.AddCommand(add(), remove(), server(), version(), signature()) cmd.PersistentFlags().BoolP("verbose", "v", false, "Enable verbose output") diff --git a/cli/server.go b/cli/server.go index befc20b..8cfb7ea 100644 --- a/cli/server.go +++ b/cli/server.go @@ -15,21 +15,68 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/code-marketplace/extensionsign" "github.com/coder/code-marketplace/api" "github.com/coder/code-marketplace/database" "github.com/coder/code-marketplace/storage" ) +func serverFlags() (addFlags func(cmd *cobra.Command), opts *storage.Options) { + opts = &storage.Options{} + var sign bool + return func(cmd *cobra.Command) { + cmd.Flags().StringVar(&opts.ExtDir, "extensions-dir", "", "The path to extensions.") + cmd.Flags().StringVar(&opts.Artifactory, "artifactory", "", "Artifactory server URL.") + cmd.Flags().StringVar(&opts.Repo, "repo", "", "Artifactory repository.") + cmd.Flags().BoolVar(&sign, "sign", false, "Sign extensions.") + _ = cmd.Flags().MarkHidden("sign") // This flag needs to import a key, not just be a bool + + if cmd.Use == "server" { + // Server only flags + cmd.Flags().DurationVar(&opts.ListCacheDuration, "list-cache-duration", time.Minute, "The duration of the extension cache.") + } + + var before func(cmd *cobra.Command, args []string) error + if cmd.PreRunE != nil { + before = cmd.PreRunE + } + if cmd.PreRun != nil { + beforeNoE := cmd.PreRun + before = func(cmd *cobra.Command, args []string) error { + beforeNoE(cmd, args) + return nil + } + } + + cmd.PreRunE = func(cmd *cobra.Command, args []string) error { + opts.Logger = cmdLogger(cmd) + if before != nil { + return before(cmd, args) + } + if sign { // TODO: Remove this for an actual key import + opts.Signer, _ = extensionsign.GenerateKey() + } + return nil + } + }, opts +} + +func cmdLogger(cmd *cobra.Command) slog.Logger { + verbose, _ := cmd.Flags().GetBool("verbose") + logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) + if verbose { + logger = logger.Leveled(slog.LevelDebug) + } + return logger +} + func server() *cobra.Command { var ( - address string - artifactory string - extdir string - repo string - listcacheduration time.Duration - maxpagesize int + address string + maxpagesize int ) + addFlags, opts := serverFlags() cmd := &cobra.Command{ Use: "server", @@ -41,26 +88,12 @@ func server() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() + logger := opts.Logger notifyCtx, notifyStop := signal.NotifyContext(ctx, interruptSignals...) defer notifyStop() - verbose, err := cmd.Flags().GetBool("verbose") - if err != nil { - return err - } - logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr())) - if verbose { - logger = logger.Leveled(slog.LevelDebug) - } - - store, err := storage.NewStorage(ctx, &storage.Options{ - Artifactory: artifactory, - ExtDir: extdir, - Logger: logger, - Repo: repo, - ListCacheDuration: listcacheduration, - }) + store, err := storage.NewStorage(ctx, opts) if err != nil { return err } @@ -137,12 +170,9 @@ func server() *cobra.Command { }, } - cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.") cmd.Flags().IntVar(&maxpagesize, "max-page-size", api.MaxPageSizeDefault, "The maximum number of pages to request") - cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.") - cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.") cmd.Flags().StringVar(&address, "address", "127.0.0.1:3001", "The address on which to serve the marketplace API.") - cmd.Flags().DurationVar(&listcacheduration, "list-cache-duration", time.Minute, "The duration of the extension cache.") + addFlags(cmd) return cmd } diff --git a/cli/signature.go b/cli/signature.go new file mode 100644 index 0000000..432a14a --- /dev/null +++ b/cli/signature.go @@ -0,0 +1,63 @@ +package cli + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "golang.org/x/xerrors" + + "github.com/coder/code-marketplace/extensionsign" +) + +func signature() *cobra.Command { + cmd := &cobra.Command{ + Use: "signature", + Short: "Commands for debugging and working with signatures.", + Hidden: true, // Debugging tools + Aliases: []string{"sig", "sigs", "signatures"}, + } + cmd.AddCommand(compareSignatureSigZips()) + return cmd +} + +func compareSignatureSigZips() *cobra.Command { + cmd := &cobra.Command{ + Use: "compare", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + decode := func(path string) (extensionsign.SignatureManifest, error) { + data, err := os.ReadFile(path) + if err != nil { + return extensionsign.SignatureManifest{}, xerrors.Errorf("read %q: %w", args[0], err) + } + + sig, err := extensionsign.ExtractSignatureManifest(data) + if err != nil { + return extensionsign.SignatureManifest{}, xerrors.Errorf("unmarshal %q: %w", path, err) + } + return sig, nil + } + + a, err := decode(args[0]) + if err != nil { + return err + } + b, err := decode(args[1]) + if err != nil { + return err + } + + _, _ = fmt.Fprintf(os.Stdout, "Signature A:%s\n", a) + _, _ = fmt.Fprintf(os.Stdout, "Signature B:%s\n", b) + err = a.Equal(b) + if err != nil { + return err + } + + _, _ = fmt.Fprintf(os.Stdout, "Signatures are equal\n") + return nil + }, + } + return cmd +} diff --git a/extensionsign/doc.go b/extensionsign/doc.go new file mode 100644 index 0000000..b51e216 --- /dev/null +++ b/extensionsign/doc.go @@ -0,0 +1,2 @@ +// Package extensionsign is a Go implementation of https://github.com/filiptronicek/node-ovsx-sign +package extensionsign diff --git a/extensionsign/key.go b/extensionsign/key.go new file mode 100644 index 0000000..9af9778 --- /dev/null +++ b/extensionsign/key.go @@ -0,0 +1,14 @@ +package extensionsign + +import ( + "crypto/ed25519" + "crypto/rand" +) + +func GenerateKey() (ed25519.PrivateKey, error) { + _, private, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + return private, nil +} diff --git a/extensionsign/sigmanifest.go b/extensionsign/sigmanifest.go new file mode 100644 index 0000000..3ff67e6 --- /dev/null +++ b/extensionsign/sigmanifest.go @@ -0,0 +1,114 @@ +package extensionsign + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "io" + + "golang.org/x/xerrors" + + "github.com/coder/code-marketplace/storage/easyzip" +) + +// SignatureManifest should be serialized to JSON before being signed. +type SignatureManifest struct { + Package File + // Entries is base64(filepath) -> File + Entries map[string]File +} + +func (a SignatureManifest) String() string { + return fmt.Sprintf("Package %q with Entries: %d", a.Package.Digests.SHA256, len(a.Entries)) +} + +// Equal is helpful for debugging to know if two manifests are equal. +// They can change if any file is removed/added/edited to an extension. +func (a SignatureManifest) Equal(b SignatureManifest) error { + var errs []error + if err := a.Package.Equal(b.Package); err != nil { + errs = append(errs, xerrors.Errorf("package: %w", err)) + } + + if len(a.Entries) != len(b.Entries) { + errs = append(errs, xerrors.Errorf("entry count mismatch: %d != %d", len(a.Entries), len(b.Entries))) + } + + for k, v := range a.Entries { + if _, ok := b.Entries[k]; !ok { + errs = append(errs, xerrors.Errorf("entry %q not found in second set", k)) + continue + } + if err := v.Equal(b.Entries[k]); err != nil { + errs = append(errs, xerrors.Errorf("entry %q: %w", k, err)) + } + } + return errors.Join(errs...) +} + +type File struct { + Size int64 `json:"size"` + Digests Digests `json:"digests"` +} + +func (f File) Equal(b File) error { + if f.Size != b.Size { + return xerrors.Errorf("size mismatch: %d != %d", f.Size, b.Size) + } + if f.Digests.SHA256 != b.Digests.SHA256 { + return xerrors.Errorf("sha256 mismatch: %s != %s", f.Digests.SHA256, b.Digests.SHA256) + } + return nil +} + +func FileManifest(file io.Reader) (File, error) { + hash := sha256.New() + + n, err := io.Copy(hash, file) + if err != nil { + return File{}, xerrors.Errorf("hash file: %w", err) + } + + return File{ + Size: n, + Digests: Digests{ + SHA256: base64.StdEncoding.EncodeToString(hash.Sum(nil)), + }, + }, nil +} + +type Digests struct { + SHA256 string `json:"sha256"` +} + +// GenerateSignatureManifest generates a signature manifest for a VSIX file. +// It does not sign the manifest. The manifest is the base64 encoded file path +// followed by the sha256 hash of the file, and it's size. +func GenerateSignatureManifest(vsixFile []byte) (SignatureManifest, error) { + pkgManifest, err := FileManifest(bytes.NewReader(vsixFile)) + if err != nil { + return SignatureManifest{}, xerrors.Errorf("package manifest: %w", err) + } + + manifest := SignatureManifest{ + Package: pkgManifest, + Entries: make(map[string]File), + } + + err = easyzip.ExtractZip(vsixFile, func(name string, reader io.Reader) error { + fm, err := FileManifest(reader) + if err != nil { + return xerrors.Errorf("file %q: %w", name, err) + } + manifest.Entries[base64.StdEncoding.EncodeToString([]byte(name))] = fm + return nil + }) + + if err != nil { + return SignatureManifest{}, err + } + + return manifest, nil +} diff --git a/extensionsign/sigzip.go b/extensionsign/sigzip.go new file mode 100644 index 0000000..c6948d1 --- /dev/null +++ b/extensionsign/sigzip.go @@ -0,0 +1,73 @@ +package extensionsign + +import ( + "archive/zip" + "bytes" + "crypto" + "crypto/rand" + "encoding/json" + + "golang.org/x/xerrors" + + "github.com/coder/code-marketplace/storage/easyzip" +) + +func ExtractSignatureManifest(zip []byte) (SignatureManifest, error) { + r, err := easyzip.GetZipFileReader(zip, ".signature.manifest") + if err != nil { + return SignatureManifest{}, xerrors.Errorf("get manifest: %w", err) + } + + defer r.Close() + var manifest SignatureManifest + err = json.NewDecoder(r).Decode(&manifest) + if err != nil { + return SignatureManifest{}, xerrors.Errorf("decode manifest: %w", err) + } + return manifest, nil +} + +// SignAndZipManifest signs a manifest and zips it up +func SignAndZipManifest(secret crypto.Signer, manifest json.RawMessage) ([]byte, error) { + var buf bytes.Buffer + w := zip.NewWriter(&buf) + + manFile, err := w.Create(".signature.manifest") + if err != nil { + return nil, xerrors.Errorf("create manifest: %w", err) + } + + _, err = manFile.Write(manifest) + if err != nil { + return nil, xerrors.Errorf("write manifest: %w", err) + } + + // Empty file + _, err = w.Create(".signature.p7s") + if err != nil { + return nil, xerrors.Errorf("create empty p7s signature: %w", err) + } + + // Actual sig + sigFile, err := w.Create(".signature.sig") + if err != nil { + return nil, xerrors.Errorf("create signature: %w", err) + } + + signature, err := secret.Sign(rand.Reader, manifest, crypto.Hash(0)) + if err != nil { + return nil, xerrors.Errorf("sign: %w", err) + } + + _, err = sigFile.Write(signature) + if err != nil { + return nil, xerrors.Errorf("write signature: %w", err) + } + + err = w.Close() + if err != nil { + return nil, xerrors.Errorf("close zip: %w", err) + } + + return buf.Bytes(), nil +} diff --git a/go.mod b/go.mod index 10bbe7d..52220e5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/coder/code-marketplace -go 1.19 +go 1.22.8 require ( cdr.dev/slog v1.6.1 @@ -9,6 +9,7 @@ require ( github.com/go-chi/httprate v0.14.1 github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 + github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.19.0 @@ -32,9 +33,11 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f49f711..f19c00c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,14 @@ cdr.dev/slog v1.6.1 h1:IQjWZD0x6//sfv5n+qEhbu3wBkmtBQY5DILXNvMaIv4= cdr.dev/slog v1.6.1/go.mod h1:eHEYQLaZvxnIAXC+XdTSNLb/kgA/X2RVSF72v5wsxEI= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -20,9 +25,13 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -47,6 +56,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -57,13 +68,15 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= @@ -72,7 +85,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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= @@ -85,13 +99,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -106,11 +120,16 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= -google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/storage/artifactory.go b/storage/artifactory.go index a20c331..ae4ea88 100644 --- a/storage/artifactory.go +++ b/storage/artifactory.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "io/fs" "net/http" "os" "path" @@ -15,10 +16,12 @@ import ( "sync" "time" + "github.com/spf13/afero/mem" "golang.org/x/sync/errgroup" "golang.org/x/xerrors" "cdr.dev/slog" + "github.com/coder/code-marketplace/storage/easyzip" "github.com/coder/code-marketplace/util" ) @@ -41,6 +44,8 @@ type ArtifactoryList struct { Files []ArtifactoryFile `json:"files"` } +var _ Storage = (*Artifactory)(nil) + // Artifactory implements Storage. It stores extensions remotely through // Artifactory by both copying the VSIX and extracting said VSIX to a tree // structure in the form of publisher/extension/version to easily serve @@ -213,7 +218,7 @@ func (s *Artifactory) upload(ctx context.Context, endpoint string, r io.Reader) return code, nil } -func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) { +func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte, extra ...File) (string, error) { // Extract the zip to the correct path. identity := manifest.Metadata.Identity dir := path.Join(identity.Publisher, identity.ID, Version{ @@ -244,7 +249,7 @@ func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, } } - err := ExtractZip(vsix, func(name string, r io.Reader) error { + err := easyzip.ExtractZip(vsix, func(name string, r io.Reader) error { if util.Contains(assets, name) || (browser != "" && strings.HasPrefix(name, browser)) { _, err := s.upload(ctx, path.Join(dir, name), r) return err @@ -262,28 +267,47 @@ func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, return "", err } + for _, file := range extra { + _, err := s.upload(ctx, path.Join(dir, file.RelativePath), bytes.NewReader(file.Content)) + if err != nil { + return "", err + } + } + return s.uri + dir, nil } -func (s *Artifactory) FileServer() http.Handler { - // TODO: Since we only extract a subset of files perhaps if the file does not - // exist we should download the vsix and extract the requested file as a - // fallback. Obviously this seems like quite a bit of overhead so we would - // then emit a warning so we can notice that VS Code has added new asset types - // that we should be extracting to avoid that overhead. Other solutions could - // be implemented though like extracting the VSIX to disk locally and only - // going to Artifactory for the VSIX when it is missing on disk (basically - // using the disk as a cache). - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - reader, code, err := s.read(r.Context(), r.URL.Path) - if err != nil { - http.Error(rw, err.Error(), code) - return +// Open returns a file from Artifactory. +// TODO: Since we only extract a subset of files perhaps if the file does not +// exist we should download the vsix and extract the requested file as a +// fallback. Obviously this seems like quite a bit of overhead so we would +// then emit a warning so we can notice that VS Code has added new asset types +// that we should be extracting to avoid that overhead. Other solutions could +// be implemented though like extracting the VSIX to disk locally and only +// going to Artifactory for the VSIX when it is missing on disk (basically +// using the disk as a cache). +func (s *Artifactory) Open(ctx context.Context, fp string) (fs.File, error) { + resp, code, err := s.read(ctx, fp) + if code != http.StatusOK || err != nil { + switch code { + case http.StatusNotFound: + return nil, fs.ErrNotExist + case http.StatusForbidden: + return nil, fs.ErrPermission + default: + return nil, err } - defer reader.Close() - rw.WriteHeader(http.StatusOK) - _, _ = io.Copy(rw, reader) - }) + } + + // TODO: Do no copy the bytes into memory, stream them rather than + // storing the entire file into memory. + f := mem.NewFileHandle(mem.CreateFile(fp)) + _, err = io.Copy(f, resp) + if err != nil { + return nil, err + } + + return f, nil } func (s *Artifactory) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { diff --git a/storage/zip.go b/storage/easyzip/zip.go similarity index 99% rename from storage/zip.go rename to storage/easyzip/zip.go index da2526f..9a76e89 100644 --- a/storage/zip.go +++ b/storage/easyzip/zip.go @@ -1,4 +1,4 @@ -package storage +package easyzip import ( "archive/zip" diff --git a/storage/zip_test.go b/storage/easyzip/zip_test.go similarity index 99% rename from storage/zip_test.go rename to storage/easyzip/zip_test.go index 3ae2ceb..febe877 100644 --- a/storage/zip_test.go +++ b/storage/easyzip/zip_test.go @@ -1,4 +1,4 @@ -package storage +package easyzip import ( "archive/zip" diff --git a/storage/local.go b/storage/local.go index 363eae2..5293e39 100644 --- a/storage/local.go +++ b/storage/local.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/fs" "net/http" "os" "path/filepath" @@ -11,9 +12,14 @@ import ( "sync" "time" + "golang.org/x/xerrors" + "cdr.dev/slog" + "github.com/coder/code-marketplace/storage/easyzip" ) +var _ Storage = (*Local)(nil) + // Local implements Storage. It stores extensions locally on disk by both // copying the VSIX and extracting said VSIX to a tree structure in the form of // publisher/extension/version to easily serve individual assets via HTTP. @@ -89,14 +95,14 @@ func (s *Local) list(ctx context.Context) []extension { return list } -func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) { +func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte, extra ...File) (string, error) { // Extract the zip to the correct path. identity := manifest.Metadata.Identity dir := filepath.Join(s.extdir, identity.Publisher, identity.ID, Version{ Version: identity.Version, TargetPlatform: identity.TargetPlatform, }.String()) - err := ExtractZip(vsix, func(name string, r io.Reader) error { + err := easyzip.ExtractZip(vsix, func(name string, r io.Reader) error { path := filepath.Join(dir, name) err := os.MkdirAll(filepath.Dir(path), 0o755) if err != nil { @@ -121,11 +127,23 @@ func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix [ return "", err } + for _, file := range extra { + path := filepath.Join(dir, file.RelativePath) + err := os.MkdirAll(filepath.Dir(path), 0o755) + if err != nil { + return "", err + } + err = os.WriteFile(path, file.Content, 0o644) + if err != nil { + return dir, xerrors.Errorf("write extra file %q: %w", path, err) + } + } + return dir, nil } -func (s *Local) FileServer() http.Handler { - return http.FileServer(http.Dir(s.extdir)) +func (s *Local) Open(_ context.Context, fp string) (fs.File, error) { + return http.Dir(s.extdir).Open(fp) } func (s *Local) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { diff --git a/storage/signature.go b/storage/signature.go new file mode 100644 index 0000000..4bc3afd --- /dev/null +++ b/storage/signature.go @@ -0,0 +1,135 @@ +package storage + +import ( + "context" + "crypto" + "encoding/json" + "io" + "io/fs" + "path/filepath" + + "github.com/spf13/afero/mem" + "golang.org/x/xerrors" + + "github.com/coder/code-marketplace/extensionsign" +) + +var _ Storage = (*Signature)(nil) + +const ( + SigzipFilename = "extension.sigzip" + sigManifestName = ".signature.manifest" +) + +// Signature is a storage wrapper that can sign extensions on demand. +type Signature struct { + // Signer if provided, will be used to sign extensions. If not provided, + // no extensions will be signed. + Signer crypto.Signer + Storage +} + +func NewSignatureStorage(signer crypto.Signer, s Storage) *Signature { + return &Signature{ + Signer: signer, + Storage: s, + } +} + +func (s *Signature) SigningEnabled() bool { + return s.Signer != nil +} + +// AddExtension includes the signature manifest of the vsix. Signing happens on +// demand, so leave the manifest unsigned. This is safe to do even if +// 'signExtensions' is disabled, as these files lay dormant until signed. +func (s *Signature) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte, extra ...File) (string, error) { + sigManifest, err := extensionsign.GenerateSignatureManifest(vsix) + if err != nil { + return "", xerrors.Errorf("generate signature manifest: %w", err) + } + + data, err := json.Marshal(sigManifest) + if err != nil { + return "", xerrors.Errorf("encode signature manifest: %w", err) + } + + return s.Storage.AddExtension(ctx, manifest, vsix, append(extra, File{ + RelativePath: sigManifestName, + Content: data, + })...) +} + +func (s *Signature) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { + manifest, err := s.Storage.Manifest(ctx, publisher, name, version) + if err != nil { + return nil, err + } + + if s.SigningEnabled() { + for _, asset := range manifest.Assets.Asset { + if asset.Path == SigzipFilename { + // Already signed + return manifest, nil + } + } + manifest.Assets.Asset = append(manifest.Assets.Asset, VSIXAsset{ + Type: VSIXSignatureType, + Path: SigzipFilename, + Addressable: "true", + }) + return manifest, nil + } + return manifest, nil +} + +// Open will intercept requests for signed extensions payload. +// It does this by looking for 'SigzipFilename' or p7s.sig. +// +// The signed payload and signing process is taken from: +// https://github.com/filiptronicek/node-ovsx-sign +// +// Some notes: +// +// - VSCodium requires a signature to exist, but it does appear to actually read +// the signature. Meaning the signature could be empty, incorrect, or a +// picture of cat and it would work. There is no signature verification. +// +// - VSCode requires a signature payload to exist, but the context appear +// to be somewhat optional. +// Following another open source implementation, it appears the '.signature.p7s' +// file must exist, but it can be empty. +// The signature is stored in a '.signature.sig' file, although it is unclear +// is VSCode ever reads this file. +// TODO: Properly implement the p7s file, and diverge from the other open +// source implementation. Ideally this marketplace would match Microsoft's +// marketplace API. +func (s *Signature) Open(ctx context.Context, fp string) (fs.File, error) { + if s.SigningEnabled() && filepath.Base(fp) == SigzipFilename { + // hijack this request, sign the sig manifest + manifest, err := s.Storage.Open(ctx, filepath.Join(filepath.Dir(fp), sigManifestName)) + if err != nil { + // If this file is missing, it means the extension was added before + // signatures were handled by the marketplace. + // TODO: Generate the sig manifest payload and insert it? + return nil, xerrors.Errorf("open signature manifest: %w", err) + } + defer manifest.Close() + + manifestData, err := io.ReadAll(manifest) + if err != nil { + return nil, xerrors.Errorf("read signature manifest: %w", err) + } + + signed, err := extensionsign.SignAndZipManifest(s.Signer, manifestData) + if err != nil { + return nil, xerrors.Errorf("sign and zip manifest: %w", err) + } + + f := mem.NewFileHandle(mem.CreateFile(SigzipFilename)) + _, err = f.Write(signed) + return f, err + } + + return s.Storage.Open(ctx, fp) +} diff --git a/storage/signature_test.go b/storage/signature_test.go new file mode 100644 index 0000000..bfcef6d --- /dev/null +++ b/storage/signature_test.go @@ -0,0 +1,37 @@ +package storage_test + +import ( + "crypto" + "testing" + + "github.com/coder/code-marketplace/extensionsign" + "github.com/coder/code-marketplace/storage" +) + +func expectSignature(manifest *storage.VSIXManifest) { + manifest.Assets.Asset = append(manifest.Assets.Asset, storage.VSIXAsset{ + Type: storage.VSIXSignatureType, + Path: storage.SigzipFilename, + Addressable: "true", + }) +} + +//nolint:revive // test control flag +func signed(signer bool, factory func(t *testing.T) testStorage) func(t *testing.T) testStorage { + return func(t *testing.T) testStorage { + st := factory(t) + var key crypto.Signer + var exp func(*storage.VSIXManifest) + if signer { + key, _ = extensionsign.GenerateKey() + exp = expectSignature + } + + return testStorage{ + storage: storage.NewSignatureStorage(key, st.storage), + write: st.write, + exists: st.exists, + expectedManifest: exp, + } + } +} diff --git a/storage/storage.go b/storage/storage.go index ecdaa50..94e5505 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -2,10 +2,12 @@ package storage import ( "context" + "crypto" "encoding/json" "encoding/xml" "fmt" "io" + "io/fs" "net/http" "os" "regexp" @@ -13,9 +15,11 @@ import ( "time" "golang.org/x/mod/semver" + "golang.org/x/xerrors" "cdr.dev/slog" + "github.com/coder/code-marketplace/storage/easyzip" ) // VSIXManifest implement XMLManifest.PackageManifest. @@ -112,6 +116,7 @@ type AssetType string const ( ManifestAssetType AssetType = "Microsoft.VisualStudio.Code.Manifest" // This is the package.json. VSIXAssetType AssetType = "Microsoft.VisualStudio.Services.VSIXPackage" + VSIXSignatureType AssetType = "Microsoft.VisualStudio.Services.VsixSignature" ) // VSIXAsset implements XMLManifest.PackageManifest.Assets.Asset. @@ -123,6 +128,7 @@ type VSIXAsset struct { } type Options struct { + Signer crypto.Signer Artifactory string ExtDir string Repo string @@ -203,11 +209,13 @@ func (vs ByVersion) Less(i, j int) bool { type Storage interface { // AddExtension adds the provided VSIX into storage and returns the location - // for verification purposes. - AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) - // FileServer provides a handler for fetching extension repository files from - // a client. - FileServer() http.Handler + // for verification purposes. Extra files can be included, but not required. + // All extra files will be placed relative to the manifest outside the vsix. + AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte, extra ...File) (string, error) + // Open mirrors the fs.FS interface of Open, except with a context. + // The Open should return files from the extension storage, and used for + // serving extensions. + Open(ctx context.Context, name string) (fs.File, error) // Manifest returns the manifest bytes for the provided extension. The // extension asset itself (the VSIX) will be included on the manifest even if // it does not exist on the manifest on disk. @@ -230,6 +238,22 @@ type Storage interface { WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error } +// HTTPFileServer creates an http.Handler that serves files from the provided +// storage. +func HTTPFileServer(s Storage) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + http.FileServerFS(&contextFs{ + ctx: r.Context(), + open: s.Open, + }).ServeHTTP(rw, r) + }) +} + +type File struct { + RelativePath string + Content []byte +} + const ArtifactoryTokenEnvKey = "ARTIFACTORY_TOKEN" // NewStorage returns a storage instance based on the provided extension @@ -240,31 +264,42 @@ func NewStorage(ctx context.Context, options *Options) (Storage, error) { return nil, xerrors.Errorf("cannot use both Artifactory and extension directory") } else if options.Artifactory != "" && options.Repo == "" { return nil, xerrors.Errorf("must provide repository") - } else if options.Artifactory != "" { + } + + var store Storage + var err error + switch { + case options.Artifactory != "": token := os.Getenv(ArtifactoryTokenEnvKey) if token == "" { return nil, xerrors.Errorf("the %s environment variable must be set", ArtifactoryTokenEnvKey) } - return NewArtifactoryStorage(ctx, &ArtifactoryOptions{ + store, err = NewArtifactoryStorage(ctx, &ArtifactoryOptions{ ListCacheDuration: options.ListCacheDuration, Logger: options.Logger, Repo: options.Repo, Token: token, URI: options.Artifactory, }) - } else if options.ExtDir != "" { - return NewLocalStorage(&LocalOptions{ + case options.ExtDir != "": + store, err = NewLocalStorage(&LocalOptions{ ListCacheDuration: options.ListCacheDuration, ExtDir: options.ExtDir, }, options.Logger) + default: + return nil, xerrors.Errorf("must provide an Artifactory repository or local directory") } - return nil, xerrors.Errorf("must provide an Artifactory repository or local directory") + if err != nil { + return nil, err + } + + return NewSignatureStorage(options.Signer, store), nil } // ReadVSIXManifest reads and parses an extension manifest from a vsix file. If // the manifest is invalid it will be returned along with the validation error. func ReadVSIXManifest(vsix []byte) (*VSIXManifest, error) { - vmr, err := GetZipFileReader(vsix, "extension.vsixmanifest") + vmr, err := easyzip.GetZipFileReader(vsix, "extension.vsixmanifest") if err != nil { return nil, err } @@ -322,7 +357,7 @@ type VSIXPackageJSON struct { // ReadVSIXPackageJSON reads and parses an extension's package.json from a vsix // file. func ReadVSIXPackageJSON(vsix []byte, packageJsonPath string) (*VSIXPackageJSON, error) { - vpjr, err := GetZipFileReader(vsix, packageJsonPath) + vpjr, err := easyzip.GetZipFileReader(vsix, packageJsonPath) if err != nil { return nil, err } @@ -406,3 +441,12 @@ func ParseExtensionID(id string) (string, string, string, error) { } return match[0][1], match[0][2], match[0][3], nil } + +type contextFs struct { + ctx context.Context + open func(ctx context.Context, name string) (fs.File, error) +} + +func (c *contextFs) Open(name string) (fs.File, error) { + return c.open(c.ctx, name) +} diff --git a/storage/storage_test.go b/storage/storage_test.go index 4bbd433..8bb649c 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -25,6 +25,8 @@ type testStorage struct { storage storage.Storage write func(content []byte, elem ...string) exists func(elem ...string) bool + + expectedManifest func(man *storage.VSIXManifest) } type storageFactory = func(t *testing.T) testStorage @@ -104,11 +106,13 @@ func TestNewStorage(t *testing.T) { require.Error(t, err) require.Regexp(t, test.error, err.Error()) } else if test.local { - _, ok := s.(*storage.Local) + under := s.(*storage.Signature) + _, ok := under.Storage.(*storage.Local) require.True(t, ok) require.NoError(t, err) } else { - _, ok := s.(*storage.Artifactory) + under := s.(*storage.Signature) + _, ok := under.Storage.(*storage.Artifactory) require.True(t, ok) require.NoError(t, err) } @@ -130,6 +134,14 @@ func TestStorage(t *testing.T) { name: "Artifactory", factory: artifactoryFactory, }, + { + name: "SignedLocal", + factory: signed(true, localFactory), + }, + { + name: "SignedArtifactory", + factory: signed(true, artifactoryFactory), + }, } for _, sf := range factories { t.Run(sf.name, func(t *testing.T) { @@ -189,7 +201,7 @@ func testFileServer(t *testing.T, factory storageFactory) { req := httptest.NewRequest("GET", test.path, nil) rec := httptest.NewRecorder() - server := f.storage.FileServer() + server := storage.HTTPFileServer(f.storage) server.ServeHTTP(rec, req) resp := rec.Result() @@ -322,6 +334,9 @@ func testManifest(t *testing.T, factory storageFactory) { Path: fmt.Sprintf("%s.%s-%s.vsix", test.extension.Publisher, test.extension.Name, test.version), Addressable: "true", }) + if f.expectedManifest != nil { + f.expectedManifest(test.expected) + } require.Equal(t, test.expected, manifest) } }) diff --git a/testutil/extensions.go b/testutil/extensions.go index 62fb385..a32967e 100644 --- a/testutil/extensions.go +++ b/testutil/extensions.go @@ -26,6 +26,13 @@ type Extension struct { Pack []string } +func (e Extension) Copy() Extension { + var n Extension + data, _ := json.Marshal(e) + _ = json.Unmarshal(data, &n) + return n +} + var Extensions = []Extension{ { Publisher: "foo", @@ -113,6 +120,7 @@ var Extensions = []Extension{ } func ConvertExtensionToManifest(ext Extension, version storage.Version) *storage.VSIXManifest { + ext = ext.Copy() return &storage.VSIXManifest{ Metadata: storage.VSIXMetadata{ Identity: storage.VSIXIdentity{ diff --git a/testutil/mockstorage.go b/testutil/mockstorage.go index db49fa2..0066c40 100644 --- a/testutil/mockstorage.go +++ b/testutil/mockstorage.go @@ -3,13 +3,19 @@ package testutil import ( "context" "errors" + "io/fs" "net/http" "os" + "path/filepath" "sort" + "github.com/spf13/afero/mem" + "github.com/coder/code-marketplace/storage" ) +var _ storage.Storage = (*MockStorage)(nil) + // MockStorage implements storage.Storage for tests. type MockStorage struct{} @@ -17,9 +23,18 @@ func NewMockStorage() *MockStorage { return &MockStorage{} } -func (s *MockStorage) AddExtension(ctx context.Context, manifest *storage.VSIXManifest, vsix []byte) (string, error) { +func (s *MockStorage) AddExtension(ctx context.Context, manifest *storage.VSIXManifest, vsix []byte, extra ...storage.File) (string, error) { return "", errors.New("not implemented") } +func (s *MockStorage) Open(ctx context.Context, path string) (fs.File, error) { + if filepath.Base(path) == "nonexistent" { + return nil, fs.ErrNotExist + } + + f := mem.NewFileHandle(mem.CreateFile(path)) + _, _ = f.Write([]byte("foobar")) + return f, nil +} func (s *MockStorage) FileServer() http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { From 0971b715258355df18ce011acd791e44ccd66153 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 13 Dec 2024 11:48:23 -0600 Subject: [PATCH 46/69] chore: sign the vsix package, not the manifest (#83) * chore: sign the vsix package, not the manifest --- cli/server.go | 2 + extensionsign/sigzip.go | 4 +- storage/signature.go | 79 +++++++++++++++++++++++++++++++++------ storage/signature_test.go | 5 ++- storage/storage.go | 8 +++- 5 files changed, 82 insertions(+), 16 deletions(-) diff --git a/cli/server.go b/cli/server.go index 8cfb7ea..7a63fe0 100644 --- a/cli/server.go +++ b/cli/server.go @@ -31,6 +31,8 @@ func serverFlags() (addFlags func(cmd *cobra.Command), opts *storage.Options) { cmd.Flags().StringVar(&opts.Repo, "repo", "", "Artifactory repository.") cmd.Flags().BoolVar(&sign, "sign", false, "Sign extensions.") _ = cmd.Flags().MarkHidden("sign") // This flag needs to import a key, not just be a bool + cmd.Flags().BoolVar(&opts.SaveSigZips, "save-sigs", false, "Save signed extensions to disk for debugging.") + _ = cmd.Flags().MarkHidden("save-sigs") if cmd.Use == "server" { // Server only flags diff --git a/extensionsign/sigzip.go b/extensionsign/sigzip.go index c6948d1..5d9f536 100644 --- a/extensionsign/sigzip.go +++ b/extensionsign/sigzip.go @@ -28,7 +28,7 @@ func ExtractSignatureManifest(zip []byte) (SignatureManifest, error) { } // SignAndZipManifest signs a manifest and zips it up -func SignAndZipManifest(secret crypto.Signer, manifest json.RawMessage) ([]byte, error) { +func SignAndZipManifest(secret crypto.Signer, vsixData []byte, manifest json.RawMessage) ([]byte, error) { var buf bytes.Buffer w := zip.NewWriter(&buf) @@ -54,7 +54,7 @@ func SignAndZipManifest(secret crypto.Signer, manifest json.RawMessage) ([]byte, return nil, xerrors.Errorf("create signature: %w", err) } - signature, err := secret.Sign(rand.Reader, manifest, crypto.Hash(0)) + signature, err := secret.Sign(rand.Reader, vsixData, crypto.Hash(0)) if err != nil { return nil, xerrors.Errorf("sign: %w", err) } diff --git a/storage/signature.go b/storage/signature.go index 4bc3afd..ce917bc 100644 --- a/storage/signature.go +++ b/storage/signature.go @@ -7,35 +7,53 @@ import ( "io" "io/fs" "path/filepath" + "strings" "github.com/spf13/afero/mem" "golang.org/x/xerrors" + "cdr.dev/slog" + "github.com/coder/code-marketplace/extensionsign" ) var _ Storage = (*Signature)(nil) const ( - SigzipFilename = "extension.sigzip" - sigManifestName = ".signature.manifest" + SigzipFileExtension = ".signature.p7s" + sigManifestName = ".signature.manifest" ) +func SignatureZipFilename(manifest *VSIXManifest) string { + return ExtensionVSIXNameFromManifest(manifest) + SigzipFileExtension +} + // Signature is a storage wrapper that can sign extensions on demand. type Signature struct { // Signer if provided, will be used to sign extensions. If not provided, // no extensions will be signed. Signer crypto.Signer + Logger slog.Logger + // SaveSigZips is a flag that will save the signed extension to disk. + // This is useful for debugging, but the server will never use this file. + saveSigZips bool Storage } -func NewSignatureStorage(signer crypto.Signer, s Storage) *Signature { +func NewSignatureStorage(logger slog.Logger, signer crypto.Signer, s Storage) *Signature { return &Signature{ Signer: signer, Storage: s, } } +func (s *Signature) SaveSigZips() { + if !s.saveSigZips { + s.Logger.Info(context.Background(), "extension signatures will be saved to disk, do not use this in production") + } + s.saveSigZips = true +} + func (s *Signature) SigningEnabled() bool { return s.Signer != nil } @@ -49,14 +67,26 @@ func (s *Signature) AddExtension(ctx context.Context, manifest *VSIXManifest, vs return "", xerrors.Errorf("generate signature manifest: %w", err) } - data, err := json.Marshal(sigManifest) + sigManifestJSON, err := json.Marshal(sigManifest) if err != nil { return "", xerrors.Errorf("encode signature manifest: %w", err) } + if s.SigningEnabled() && s.saveSigZips { + signed, err := s.SigZip(ctx, vsix, sigManifestJSON) + if err != nil { + s.Logger.Error(ctx, "signing manifest", slog.Error(err)) + return "", xerrors.Errorf("sign and zip manifest: %w", err) + } + extra = append(extra, File{ + RelativePath: SignatureZipFilename(manifest), + Content: signed, + }) + } + return s.Storage.AddExtension(ctx, manifest, vsix, append(extra, File{ RelativePath: sigManifestName, - Content: data, + Content: sigManifestJSON, })...) } @@ -68,14 +98,14 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio if s.SigningEnabled() { for _, asset := range manifest.Assets.Asset { - if asset.Path == SigzipFilename { + if asset.Path == SignatureZipFilename(manifest) { // Already signed return manifest, nil } } manifest.Assets.Asset = append(manifest.Assets.Asset, VSIXAsset{ Type: VSIXSignatureType, - Path: SigzipFilename, + Path: SignatureZipFilename(manifest), Addressable: "true", }) return manifest, nil @@ -84,7 +114,7 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio } // Open will intercept requests for signed extensions payload. -// It does this by looking for 'SigzipFilename' or p7s.sig. +// It does this by looking for 'SigzipFileExtension' or p7s.sig. // // The signed payload and signing process is taken from: // https://github.com/filiptronicek/node-ovsx-sign @@ -105,7 +135,10 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio // source implementation. Ideally this marketplace would match Microsoft's // marketplace API. func (s *Signature) Open(ctx context.Context, fp string) (fs.File, error) { - if s.SigningEnabled() && filepath.Base(fp) == SigzipFilename { + if s.SigningEnabled() && strings.HasSuffix(filepath.Base(fp), SigzipFileExtension) { + base := filepath.Base(fp) + vsixPath := strings.TrimSuffix(base, SigzipFileExtension) + // hijack this request, sign the sig manifest manifest, err := s.Storage.Open(ctx, filepath.Join(filepath.Dir(fp), sigManifestName)) if err != nil { @@ -121,15 +154,39 @@ func (s *Signature) Open(ctx context.Context, fp string) (fs.File, error) { return nil, xerrors.Errorf("read signature manifest: %w", err) } - signed, err := extensionsign.SignAndZipManifest(s.Signer, manifestData) + vsix, err := s.Storage.Open(ctx, filepath.Join(filepath.Dir(fp), vsixPath+".vsix")) + if err != nil { + // If this file is missing, it means the extension was added before + // signatures were handled by the marketplace. + // TODO: Generate the sig manifest payload and insert it? + return nil, xerrors.Errorf("open signature manifest: %w", err) + } + defer vsix.Close() + + vsixData, err := io.ReadAll(vsix) + if err != nil { + return nil, xerrors.Errorf("read signature manifest: %w", err) + } + + // TODO: Fetch the VSIX payload from the storage + signed, err := s.SigZip(ctx, vsixData, manifestData) if err != nil { return nil, xerrors.Errorf("sign and zip manifest: %w", err) } - f := mem.NewFileHandle(mem.CreateFile(SigzipFilename)) + f := mem.NewFileHandle(mem.CreateFile(fp)) _, err = f.Write(signed) return f, err } return s.Storage.Open(ctx, fp) } + +func (s *Signature) SigZip(ctx context.Context, vsix []byte, sigManifest []byte) ([]byte, error) { + signed, err := extensionsign.SignAndZipManifest(s.Signer, vsix, sigManifest) + if err != nil { + s.Logger.Error(ctx, "signing manifest", slog.Error(err)) + return nil, xerrors.Errorf("sign and zip manifest: %w", err) + } + return signed, nil +} diff --git a/storage/signature_test.go b/storage/signature_test.go index bfcef6d..452bea1 100644 --- a/storage/signature_test.go +++ b/storage/signature_test.go @@ -4,6 +4,7 @@ import ( "crypto" "testing" + "cdr.dev/slog" "github.com/coder/code-marketplace/extensionsign" "github.com/coder/code-marketplace/storage" ) @@ -11,7 +12,7 @@ import ( func expectSignature(manifest *storage.VSIXManifest) { manifest.Assets.Asset = append(manifest.Assets.Asset, storage.VSIXAsset{ Type: storage.VSIXSignatureType, - Path: storage.SigzipFilename, + Path: storage.SignatureZipFilename(manifest), Addressable: "true", }) } @@ -28,7 +29,7 @@ func signed(signer bool, factory func(t *testing.T) testStorage) func(t *testing } return testStorage{ - storage: storage.NewSignatureStorage(key, st.storage), + storage: storage.NewSignatureStorage(slog.Make(), key, st.storage), write: st.write, exists: st.exists, expectedManifest: exp, diff --git a/storage/storage.go b/storage/storage.go index 94e5505..ad5ed13 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -132,6 +132,7 @@ type Options struct { Artifactory string ExtDir string Repo string + SaveSigZips bool Logger slog.Logger ListCacheDuration time.Duration } @@ -293,7 +294,12 @@ func NewStorage(ctx context.Context, options *Options) (Storage, error) { return nil, err } - return NewSignatureStorage(options.Signer, store), nil + signingStorage := NewSignatureStorage(options.Logger, options.Signer, store) + if options.SaveSigZips { + signingStorage.SaveSigZips() + } + + return signingStorage, nil } // ReadVSIXManifest reads and parses an extension manifest from a vsix file. If From cd7ab5f511b214b9fe1934c7918d68d3f27879c9 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 18 Dec 2024 14:55:32 -0600 Subject: [PATCH 47/69] chore: staple empty signatures (#84) * chore: staple empty signatures Since signatures are not checked, or not valid (static public key in VSCode), then signing is just incorrect. --- cli/server.go | 11 +--- extensionsign/doc.go | 2 +- extensionsign/key.go | 14 ----- extensionsign/sigzip.go | 23 +------ storage/signature.go | 124 ++++++-------------------------------- storage/signature_test.go | 6 +- storage/storage.go | 19 +++--- 7 files changed, 30 insertions(+), 169 deletions(-) delete mode 100644 extensionsign/key.go diff --git a/cli/server.go b/cli/server.go index 7a63fe0..e6bf344 100644 --- a/cli/server.go +++ b/cli/server.go @@ -15,8 +15,6 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" - "github.com/coder/code-marketplace/extensionsign" - "github.com/coder/code-marketplace/api" "github.com/coder/code-marketplace/database" "github.com/coder/code-marketplace/storage" @@ -24,18 +22,14 @@ import ( func serverFlags() (addFlags func(cmd *cobra.Command), opts *storage.Options) { opts = &storage.Options{} - var sign bool return func(cmd *cobra.Command) { cmd.Flags().StringVar(&opts.ExtDir, "extensions-dir", "", "The path to extensions.") cmd.Flags().StringVar(&opts.Artifactory, "artifactory", "", "Artifactory server URL.") cmd.Flags().StringVar(&opts.Repo, "repo", "", "Artifactory repository.") - cmd.Flags().BoolVar(&sign, "sign", false, "Sign extensions.") - _ = cmd.Flags().MarkHidden("sign") // This flag needs to import a key, not just be a bool - cmd.Flags().BoolVar(&opts.SaveSigZips, "save-sigs", false, "Save signed extensions to disk for debugging.") - _ = cmd.Flags().MarkHidden("save-sigs") if cmd.Use == "server" { // Server only flags + cmd.Flags().BoolVar(&opts.IncludeEmptySignatures, "sign", false, "Includes an empty signature for all extensions.") cmd.Flags().DurationVar(&opts.ListCacheDuration, "list-cache-duration", time.Minute, "The duration of the extension cache.") } @@ -56,9 +50,6 @@ func serverFlags() (addFlags func(cmd *cobra.Command), opts *storage.Options) { if before != nil { return before(cmd, args) } - if sign { // TODO: Remove this for an actual key import - opts.Signer, _ = extensionsign.GenerateKey() - } return nil } }, opts diff --git a/extensionsign/doc.go b/extensionsign/doc.go index b51e216..14ec5b5 100644 --- a/extensionsign/doc.go +++ b/extensionsign/doc.go @@ -1,2 +1,2 @@ -// Package extensionsign is a Go implementation of https://github.com/filiptronicek/node-ovsx-sign +// Package extensionsign provides utilities for working with extension signatures. package extensionsign diff --git a/extensionsign/key.go b/extensionsign/key.go deleted file mode 100644 index 9af9778..0000000 --- a/extensionsign/key.go +++ /dev/null @@ -1,14 +0,0 @@ -package extensionsign - -import ( - "crypto/ed25519" - "crypto/rand" -) - -func GenerateKey() (ed25519.PrivateKey, error) { - _, private, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, err - } - return private, nil -} diff --git a/extensionsign/sigzip.go b/extensionsign/sigzip.go index 5d9f536..31c82bf 100644 --- a/extensionsign/sigzip.go +++ b/extensionsign/sigzip.go @@ -3,8 +3,6 @@ package extensionsign import ( "archive/zip" "bytes" - "crypto" - "crypto/rand" "encoding/json" "golang.org/x/xerrors" @@ -27,8 +25,7 @@ func ExtractSignatureManifest(zip []byte) (SignatureManifest, error) { return manifest, nil } -// SignAndZipManifest signs a manifest and zips it up -func SignAndZipManifest(secret crypto.Signer, vsixData []byte, manifest json.RawMessage) ([]byte, error) { +func IncludeEmptySignature() ([]byte, error) { var buf bytes.Buffer w := zip.NewWriter(&buf) @@ -37,7 +34,7 @@ func SignAndZipManifest(secret crypto.Signer, vsixData []byte, manifest json.Raw return nil, xerrors.Errorf("create manifest: %w", err) } - _, err = manFile.Write(manifest) + _, err = manFile.Write([]byte{}) if err != nil { return nil, xerrors.Errorf("write manifest: %w", err) } @@ -48,22 +45,6 @@ func SignAndZipManifest(secret crypto.Signer, vsixData []byte, manifest json.Raw return nil, xerrors.Errorf("create empty p7s signature: %w", err) } - // Actual sig - sigFile, err := w.Create(".signature.sig") - if err != nil { - return nil, xerrors.Errorf("create signature: %w", err) - } - - signature, err := secret.Sign(rand.Reader, vsixData, crypto.Hash(0)) - if err != nil { - return nil, xerrors.Errorf("sign: %w", err) - } - - _, err = sigFile.Write(signature) - if err != nil { - return nil, xerrors.Errorf("write signature: %w", err) - } - err = w.Close() if err != nil { return nil, xerrors.Errorf("close zip: %w", err) diff --git a/storage/signature.go b/storage/signature.go index ce917bc..59c5a62 100644 --- a/storage/signature.go +++ b/storage/signature.go @@ -2,9 +2,6 @@ package storage import ( "context" - "crypto" - "encoding/json" - "io" "io/fs" "path/filepath" "strings" @@ -30,64 +27,24 @@ func SignatureZipFilename(manifest *VSIXManifest) string { // Signature is a storage wrapper that can sign extensions on demand. type Signature struct { - // Signer if provided, will be used to sign extensions. If not provided, - // no extensions will be signed. - Signer crypto.Signer - Logger slog.Logger - // SaveSigZips is a flag that will save the signed extension to disk. - // This is useful for debugging, but the server will never use this file. - saveSigZips bool + Logger slog.Logger + IncludeEmptySignatures bool Storage } -func NewSignatureStorage(logger slog.Logger, signer crypto.Signer, s Storage) *Signature { - return &Signature{ - Signer: signer, - Storage: s, +func NewSignatureStorage(logger slog.Logger, includeEmptySignatures bool, s Storage) *Signature { + if includeEmptySignatures { + logger.Info(context.Background(), "Signature storage enabled, if using VSCode on Windows, this will not work.") } -} - -func (s *Signature) SaveSigZips() { - if !s.saveSigZips { - s.Logger.Info(context.Background(), "extension signatures will be saved to disk, do not use this in production") + return &Signature{ + Logger: logger, + IncludeEmptySignatures: includeEmptySignatures, + Storage: s, } - s.saveSigZips = true } func (s *Signature) SigningEnabled() bool { - return s.Signer != nil -} - -// AddExtension includes the signature manifest of the vsix. Signing happens on -// demand, so leave the manifest unsigned. This is safe to do even if -// 'signExtensions' is disabled, as these files lay dormant until signed. -func (s *Signature) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte, extra ...File) (string, error) { - sigManifest, err := extensionsign.GenerateSignatureManifest(vsix) - if err != nil { - return "", xerrors.Errorf("generate signature manifest: %w", err) - } - - sigManifestJSON, err := json.Marshal(sigManifest) - if err != nil { - return "", xerrors.Errorf("encode signature manifest: %w", err) - } - - if s.SigningEnabled() && s.saveSigZips { - signed, err := s.SigZip(ctx, vsix, sigManifestJSON) - if err != nil { - s.Logger.Error(ctx, "signing manifest", slog.Error(err)) - return "", xerrors.Errorf("sign and zip manifest: %w", err) - } - extra = append(extra, File{ - RelativePath: SignatureZipFilename(manifest), - Content: signed, - }) - } - - return s.Storage.AddExtension(ctx, manifest, vsix, append(extra, File{ - RelativePath: sigManifestName, - Content: sigManifestJSON, - })...) + return s.IncludeEmptySignatures } func (s *Signature) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { @@ -116,8 +73,7 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio // Open will intercept requests for signed extensions payload. // It does this by looking for 'SigzipFileExtension' or p7s.sig. // -// The signed payload and signing process is taken from: -// https://github.com/filiptronicek/node-ovsx-sign +// The signed payload is completely empty. Nothing it actually signed. // // Some notes: // @@ -125,51 +81,14 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio // the signature. Meaning the signature could be empty, incorrect, or a // picture of cat and it would work. There is no signature verification. // -// - VSCode requires a signature payload to exist, but the context appear -// to be somewhat optional. -// Following another open source implementation, it appears the '.signature.p7s' -// file must exist, but it can be empty. -// The signature is stored in a '.signature.sig' file, although it is unclear -// is VSCode ever reads this file. -// TODO: Properly implement the p7s file, and diverge from the other open -// source implementation. Ideally this marketplace would match Microsoft's -// marketplace API. +// - VSCode requires a signature payload to exist, but the content is optional +// for linux users. +// For windows users, the signature must be valid, and this implementation +// will not work. func (s *Signature) Open(ctx context.Context, fp string) (fs.File, error) { if s.SigningEnabled() && strings.HasSuffix(filepath.Base(fp), SigzipFileExtension) { - base := filepath.Base(fp) - vsixPath := strings.TrimSuffix(base, SigzipFileExtension) - - // hijack this request, sign the sig manifest - manifest, err := s.Storage.Open(ctx, filepath.Join(filepath.Dir(fp), sigManifestName)) - if err != nil { - // If this file is missing, it means the extension was added before - // signatures were handled by the marketplace. - // TODO: Generate the sig manifest payload and insert it? - return nil, xerrors.Errorf("open signature manifest: %w", err) - } - defer manifest.Close() - - manifestData, err := io.ReadAll(manifest) - if err != nil { - return nil, xerrors.Errorf("read signature manifest: %w", err) - } - - vsix, err := s.Storage.Open(ctx, filepath.Join(filepath.Dir(fp), vsixPath+".vsix")) - if err != nil { - // If this file is missing, it means the extension was added before - // signatures were handled by the marketplace. - // TODO: Generate the sig manifest payload and insert it? - return nil, xerrors.Errorf("open signature manifest: %w", err) - } - defer vsix.Close() - - vsixData, err := io.ReadAll(vsix) - if err != nil { - return nil, xerrors.Errorf("read signature manifest: %w", err) - } - - // TODO: Fetch the VSIX payload from the storage - signed, err := s.SigZip(ctx, vsixData, manifestData) + // hijack this request, return an empty signature payload + signed, err := extensionsign.IncludeEmptySignature() if err != nil { return nil, xerrors.Errorf("sign and zip manifest: %w", err) } @@ -181,12 +100,3 @@ func (s *Signature) Open(ctx context.Context, fp string) (fs.File, error) { return s.Storage.Open(ctx, fp) } - -func (s *Signature) SigZip(ctx context.Context, vsix []byte, sigManifest []byte) ([]byte, error) { - signed, err := extensionsign.SignAndZipManifest(s.Signer, vsix, sigManifest) - if err != nil { - s.Logger.Error(ctx, "signing manifest", slog.Error(err)) - return nil, xerrors.Errorf("sign and zip manifest: %w", err) - } - return signed, nil -} diff --git a/storage/signature_test.go b/storage/signature_test.go index 452bea1..2aead64 100644 --- a/storage/signature_test.go +++ b/storage/signature_test.go @@ -1,11 +1,9 @@ package storage_test import ( - "crypto" "testing" "cdr.dev/slog" - "github.com/coder/code-marketplace/extensionsign" "github.com/coder/code-marketplace/storage" ) @@ -21,10 +19,10 @@ func expectSignature(manifest *storage.VSIXManifest) { func signed(signer bool, factory func(t *testing.T) testStorage) func(t *testing.T) testStorage { return func(t *testing.T) testStorage { st := factory(t) - var key crypto.Signer + key := false var exp func(*storage.VSIXManifest) if signer { - key, _ = extensionsign.GenerateKey() + key = true exp = expectSignature } diff --git a/storage/storage.go b/storage/storage.go index ad5ed13..2eff0d9 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -2,7 +2,6 @@ package storage import ( "context" - "crypto" "encoding/json" "encoding/xml" "fmt" @@ -128,13 +127,12 @@ type VSIXAsset struct { } type Options struct { - Signer crypto.Signer - Artifactory string - ExtDir string - Repo string - SaveSigZips bool - Logger slog.Logger - ListCacheDuration time.Duration + IncludeEmptySignatures bool + Artifactory string + ExtDir string + Repo string + Logger slog.Logger + ListCacheDuration time.Duration } type extension struct { @@ -294,10 +292,7 @@ func NewStorage(ctx context.Context, options *Options) (Storage, error) { return nil, err } - signingStorage := NewSignatureStorage(options.Logger, options.Signer, store) - if options.SaveSigZips { - signingStorage.SaveSigZips() - } + signingStorage := NewSignatureStorage(options.Logger, options.IncludeEmptySignatures, store) return signingStorage, nil } From 1d8b69a59643a749861ff589e1e10787f13c30ba Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 20 Dec 2024 15:03:34 -0600 Subject: [PATCH 48/69] chore: revert back to FileServer to prevent bytes in memory (#87) --- api/api.go | 2 +- go.mod | 5 ++++- go.sum | 10 ++++----- storage/artifactory.go | 50 +++++++++++++++-------------------------- storage/local.go | 5 ++--- storage/signature.go | 43 ++++++++++++++++++++--------------- storage/storage.go | 28 +++-------------------- storage/storage_test.go | 2 +- testutil/mockstorage.go | 13 ----------- 9 files changed, 58 insertions(+), 100 deletions(-) diff --git a/api/api.go b/api/api.go index 91a92df..50f07c5 100644 --- a/api/api.go +++ b/api/api.go @@ -112,7 +112,7 @@ func New(options *Options) *API { r.Post("/api/extensionquery", api.extensionQuery) // Endpoint for getting an extension's files or the extension zip. - r.Mount("/files", http.StripPrefix("/files", storage.HTTPFileServer(options.Storage))) + r.Mount("/files", http.StripPrefix("/files", options.Storage.FileServer())) // VS Code can use the files in the response to get file paths but it will // sometimes ignore that and use requests to /assets with hardcoded types to diff --git a/go.mod b/go.mod index 52220e5..06d49a0 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/go-chi/httprate v0.14.1 github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 - github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.19.0 @@ -18,6 +17,7 @@ require ( ) require ( + cloud.google.com/go/logging v1.8.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/lipgloss v0.7.1 // indirect @@ -38,6 +38,9 @@ require ( golang.org/x/sys v0.17.0 // indirect golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f19c00c..9dac1e7 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,10 @@ cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiV cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/logging v1.8.1 h1:26skQWPeYhvIasWKm48+Eq7oUqdcdbwsCVwz5Ys0FvU= +cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= +cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg= +cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -56,8 +56,6 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/storage/artifactory.go b/storage/artifactory.go index ae4ea88..9b15e20 100644 --- a/storage/artifactory.go +++ b/storage/artifactory.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/fs" "net/http" "os" "path" @@ -16,7 +15,6 @@ import ( "sync" "time" - "github.com/spf13/afero/mem" "golang.org/x/sync/errgroup" "golang.org/x/xerrors" @@ -277,37 +275,25 @@ func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, return s.uri + dir, nil } -// Open returns a file from Artifactory. -// TODO: Since we only extract a subset of files perhaps if the file does not -// exist we should download the vsix and extract the requested file as a -// fallback. Obviously this seems like quite a bit of overhead so we would -// then emit a warning so we can notice that VS Code has added new asset types -// that we should be extracting to avoid that overhead. Other solutions could -// be implemented though like extracting the VSIX to disk locally and only -// going to Artifactory for the VSIX when it is missing on disk (basically -// using the disk as a cache). -func (s *Artifactory) Open(ctx context.Context, fp string) (fs.File, error) { - resp, code, err := s.read(ctx, fp) - if code != http.StatusOK || err != nil { - switch code { - case http.StatusNotFound: - return nil, fs.ErrNotExist - case http.StatusForbidden: - return nil, fs.ErrPermission - default: - return nil, err +func (s *Artifactory) FileServer() http.Handler { + // TODO: Since we only extract a subset of files perhaps if the file does not + // exist we should download the vsix and extract the requested file as a + // fallback. Obviously this seems like quite a bit of overhead so we would + // then emit a warning so we can notice that VS Code has added new asset types + // that we should be extracting to avoid that overhead. Other solutions could + // be implemented though like extracting the VSIX to disk locally and only + // going to Artifactory for the VSIX when it is missing on disk (basically + // using the disk as a cache). + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + reader, code, err := s.read(r.Context(), r.URL.Path) + if err != nil { + http.Error(rw, err.Error(), code) + return } - } - - // TODO: Do no copy the bytes into memory, stream them rather than - // storing the entire file into memory. - f := mem.NewFileHandle(mem.CreateFile(fp)) - _, err = io.Copy(f, resp) - if err != nil { - return nil, err - } - - return f, nil + defer reader.Close() + rw.WriteHeader(http.StatusOK) + _, _ = io.Copy(rw, reader) + }) } func (s *Artifactory) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { diff --git a/storage/local.go b/storage/local.go index 5293e39..1aeedd3 100644 --- a/storage/local.go +++ b/storage/local.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/fs" "net/http" "os" "path/filepath" @@ -142,8 +141,8 @@ func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix [ return dir, nil } -func (s *Local) Open(_ context.Context, fp string) (fs.File, error) { - return http.Dir(s.extdir).Open(fp) +func (s *Local) FileServer() http.Handler { + return http.FileServer(http.Dir(s.extdir)) } func (s *Local) Manifest(ctx context.Context, publisher, name string, version Version) (*VSIXManifest, error) { diff --git a/storage/signature.go b/storage/signature.go index 59c5a62..e64cd7b 100644 --- a/storage/signature.go +++ b/storage/signature.go @@ -2,14 +2,13 @@ package storage import ( "context" - "io/fs" - "path/filepath" + "net/http" + "strconv" "strings" - "github.com/spf13/afero/mem" - "golang.org/x/xerrors" - "cdr.dev/slog" + "github.com/coder/code-marketplace/api/httpapi" + "github.com/coder/code-marketplace/api/httpmw" "github.com/coder/code-marketplace/extensionsign" ) @@ -70,7 +69,7 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio return manifest, nil } -// Open will intercept requests for signed extensions payload. +// FileServer will intercept requests for signed extensions payload. // It does this by looking for 'SigzipFileExtension' or p7s.sig. // // The signed payload is completely empty. Nothing it actually signed. @@ -85,18 +84,26 @@ func (s *Signature) Manifest(ctx context.Context, publisher, name string, versio // for linux users. // For windows users, the signature must be valid, and this implementation // will not work. -func (s *Signature) Open(ctx context.Context, fp string) (fs.File, error) { - if s.SigningEnabled() && strings.HasSuffix(filepath.Base(fp), SigzipFileExtension) { - // hijack this request, return an empty signature payload - signed, err := extensionsign.IncludeEmptySignature() - if err != nil { - return nil, xerrors.Errorf("sign and zip manifest: %w", err) - } +func (s *Signature) FileServer() http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if s.SigningEnabled() && strings.HasSuffix(r.URL.Path, SigzipFileExtension) { + // hijack this request, return an empty signature payload + signed, err := extensionsign.IncludeEmptySignature() + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.ErrorResponse{ + Message: "Unable to generate empty signature for extension", + Detail: err.Error(), + RequestID: httpmw.RequestID(r), + }) + return + } - f := mem.NewFileHandle(mem.CreateFile(fp)) - _, err = f.Write(signed) - return f, err - } + rw.Header().Set("Content-Length", strconv.FormatInt(int64(len(signed)), 10)) + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write(signed) + return + } - return s.Storage.Open(ctx, fp) + s.Storage.FileServer().ServeHTTP(rw, r) + }) } diff --git a/storage/storage.go b/storage/storage.go index 2eff0d9..1ab6ffd 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -6,7 +6,6 @@ import ( "encoding/xml" "fmt" "io" - "io/fs" "net/http" "os" "regexp" @@ -211,10 +210,9 @@ type Storage interface { // for verification purposes. Extra files can be included, but not required. // All extra files will be placed relative to the manifest outside the vsix. AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte, extra ...File) (string, error) - // Open mirrors the fs.FS interface of Open, except with a context. - // The Open should return files from the extension storage, and used for - // serving extensions. - Open(ctx context.Context, name string) (fs.File, error) + // FileServer provides a handler for fetching extension repository files from + // a client. + FileServer() http.Handler // Manifest returns the manifest bytes for the provided extension. The // extension asset itself (the VSIX) will be included on the manifest even if // it does not exist on the manifest on disk. @@ -237,17 +235,6 @@ type Storage interface { WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []Version) error) error } -// HTTPFileServer creates an http.Handler that serves files from the provided -// storage. -func HTTPFileServer(s Storage) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - http.FileServerFS(&contextFs{ - ctx: r.Context(), - open: s.Open, - }).ServeHTTP(rw, r) - }) -} - type File struct { RelativePath string Content []byte @@ -442,12 +429,3 @@ func ParseExtensionID(id string) (string, string, string, error) { } return match[0][1], match[0][2], match[0][3], nil } - -type contextFs struct { - ctx context.Context - open func(ctx context.Context, name string) (fs.File, error) -} - -func (c *contextFs) Open(name string) (fs.File, error) { - return c.open(c.ctx, name) -} diff --git a/storage/storage_test.go b/storage/storage_test.go index 8bb649c..d3c4739 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -201,7 +201,7 @@ func testFileServer(t *testing.T, factory storageFactory) { req := httptest.NewRequest("GET", test.path, nil) rec := httptest.NewRecorder() - server := storage.HTTPFileServer(f.storage) + server := f.storage.FileServer() server.ServeHTTP(rec, req) resp := rec.Result() diff --git a/testutil/mockstorage.go b/testutil/mockstorage.go index 0066c40..c1f6249 100644 --- a/testutil/mockstorage.go +++ b/testutil/mockstorage.go @@ -3,14 +3,10 @@ package testutil import ( "context" "errors" - "io/fs" "net/http" "os" - "path/filepath" "sort" - "github.com/spf13/afero/mem" - "github.com/coder/code-marketplace/storage" ) @@ -26,15 +22,6 @@ func NewMockStorage() *MockStorage { func (s *MockStorage) AddExtension(ctx context.Context, manifest *storage.VSIXManifest, vsix []byte, extra ...storage.File) (string, error) { return "", errors.New("not implemented") } -func (s *MockStorage) Open(ctx context.Context, path string) (fs.File, error) { - if filepath.Base(path) == "nonexistent" { - return nil, fs.ErrNotExist - } - - f := mem.NewFileHandle(mem.CreateFile(path)) - _, _ = f.Write([]byte("foobar")) - return f, nil -} func (s *MockStorage) FileServer() http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { From f8791bb4aba9bf1fe2a16454a6437859ae0a32e0 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 20 Dec 2024 12:14:25 -0900 Subject: [PATCH 49/69] v2.3.0 --- CHANGELOG.md | 15 +++++++++++++++ storage/signature.go | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 435e6e0..16aca4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.3.0](https://github.com/coder/code-marketplace/releases/tag/v2.3.0) - 2024-12-20 + +### Added + +- Add empty signatures when starting the server with --sign. This will not work + with VS Code on Windows and macOS as we do not have the key required, but it + will work for open source versions of VS Code (VSCodium, code-server) and VS + Code on Linux where signatures must exist but are not actually checked. + +### Changed + +- Ignore extensions without a manifest. This is not expected in normal use, but + could happen if, for example, a manifest temporarily failed to download, which + would then crash the entire process with a segfault. + ## [2.2.1](https://github.com/coder/code-marketplace/releases/tag/v2.2.1) - 2024-08-14 ### Fixed diff --git a/storage/signature.go b/storage/signature.go index e64cd7b..6d15a9f 100644 --- a/storage/signature.go +++ b/storage/signature.go @@ -33,7 +33,7 @@ type Signature struct { func NewSignatureStorage(logger slog.Logger, includeEmptySignatures bool, s Storage) *Signature { if includeEmptySignatures { - logger.Info(context.Background(), "Signature storage enabled, if using VSCode on Windows, this will not work.") + logger.Info(context.Background(), "Signature storage enabled, if using VS Code on Windows or macOS, this will not work.") } return &Signature{ Logger: logger, From 590fadd38f12370c9e04957d9e55fa5f0777d568 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 20 Dec 2024 12:26:41 -0900 Subject: [PATCH 50/69] Update Helm chart with v2.3.0 --- helm/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 4306f04..ccf51df 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.2.1 +version: 1.3.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.2.1" +appVersion: "v2.3.0" From c8cd93849a60dbd95bc0ce55ec7955d41fb53914 Mon Sep 17 00:00:00 2001 From: anael-l <79835910+anael-l@users.noreply.github.com> Date: Thu, 6 Mar 2025 23:26:24 +0100 Subject: [PATCH 51/69] chore(#94): allow specifying other custom volumes (#95) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #94 Implemented with the same values as when you create a new helm chart with the latest version of helm `helm create mychart` Co-authored-by: Anaƫl LATASSA --- helm/templates/deployment.yaml | 14 ++++++++++++-- helm/values.yaml | 13 +++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index f3be32a..ab25b0f 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -25,12 +25,17 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "code-marketplace.serviceAccountName" . }} - {{- if not .Values.persistence.artifactory.enabled }} + {{- if or (.Values.volumes) (not .Values.persistence.artifactory.enabled) }} volumes: + {{- if not .Values.persistence.artifactory.enabled }} - name: extensions persistentVolumeClaim: claimName: {{ include "code-marketplace.fullname" . }} {{- end }} + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: @@ -63,11 +68,16 @@ spec: - --extensions-dir - /extensions {{- end }} - {{- if not .Values.persistence.artifactory.enabled }} + {{- if or (.Values.volumeMounts) (not .Values.persistence.artifactory.enabled) }} volumeMounts: + {{- if not .Values.persistence.artifactory.enabled }} - name: extensions mountPath: /extensions {{- end }} + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} livenessProbe: httpGet: path: /healthz diff --git a/helm/values.yaml b/helm/values.yaml index 29bb4dd..c16cae6 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -77,6 +77,19 @@ autoscaling: targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + nodeSelector: {} tolerations: [] From a15a46d48d5465c5f5f66c2ba3da8aba489b510f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:26:40 -0900 Subject: [PATCH 52/69] Bump golangci/golangci-lint-action in the github-actions group (#90) Bumps the github-actions group with 1 update: [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action). Updates `golangci/golangci-lint-action` from 6.1.1 to 6.2.0 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v6.1.1...v6.2.0) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 62dc0f6..fc55977 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,6 +31,6 @@ jobs: with: go-version: "~1.22" - name: golangci-lint - uses: golangci/golangci-lint-action@v6.1.1 + uses: golangci/golangci-lint-action@v6.2.0 with: version: v1.58.0 From e90ae651a7e0e54d124973bf5ab2dbe0d3283481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:26:54 -0900 Subject: [PATCH 53/69] Bump github.com/go-chi/chi/v5 from 5.1.0 to 5.2.0 (#85) Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.1.0 to 5.2.0. - [Release notes](https://github.com/go-chi/chi/releases) - [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-chi/chi/compare/v5.1.0...v5.2.0) --- updated-dependencies: - dependency-name: github.com/go-chi/chi/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 06d49a0..5002985 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.8 require ( cdr.dev/slog v1.6.1 - github.com/go-chi/chi/v5 v5.1.0 + github.com/go-chi/chi/v5 v5.2.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.14.1 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 9dac1e7..27a0aa9 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNW github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= From b252f7e4e997f0981aea5a21a2e6864242b8af28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:27:03 -0900 Subject: [PATCH 54/69] Bump google.golang.org/protobuf from 1.32.0 to 1.33.0 (#82) Bumps google.golang.org/protobuf from 1.32.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5002985..09c415c 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,6 @@ require ( google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 27a0aa9..a2aabf8 100644 --- a/go.sum +++ b/go.sum @@ -126,8 +126,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 5e00a7ba05978f95b886f1a90bce53d1bb064cc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:27:13 -0900 Subject: [PATCH 55/69] Bump golang.org/x/crypto from 0.17.0 to 0.31.0 (#80) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 11 +++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 09c415c..0133dc1 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.19.0 - golang.org/x/sync v0.7.0 + golang.org/x/sync v0.10.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) @@ -33,11 +33,10 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.17.0 // indirect - golang.org/x/term v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect diff --git a/go.sum b/go.sum index a2aabf8..7a783e8 100644 --- a/go.sum +++ b/go.sum @@ -73,8 +73,8 @@ go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZE go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +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/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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= @@ -83,13 +83,13 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -97,20 +97,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From cae24c16cbdf78a8531639d86c466bd6c7795233 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:28:17 -0900 Subject: [PATCH 56/69] Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#74) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0133dc1..a3b0a3b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 golang.org/x/mod v0.19.0 golang.org/x/sync v0.10.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 diff --git a/go.sum b/go.sum index 7a783e8..d9024ab 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,8 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= From 72e18123d3ed0b6252255f3698e1ca10d66fd26c Mon Sep 17 00:00:00 2001 From: anael-l <79835910+anael-l@users.noreply.github.com> Date: Fri, 7 Mar 2025 01:18:51 +0100 Subject: [PATCH 57/69] chore(#89): allow specifying pod port (#93) closes #89 --- helm/templates/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index ab25b0f..325b3b9 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -54,11 +54,11 @@ spec: {{- end }} ports: - name: http - containerPort: 80 + containerPort: {{ .Values.service.port }} protocol: TCP args: - --address - - 0.0.0.0:80 + - "0.0.0.0:{{ .Values.service.port }}" {{- if .Values.persistence.artifactory.enabled }} - --artifactory - {{ .Values.persistence.artifactory.uri }} From ce3a083bb9f080884d2182dd53711f94d32990da Mon Sep 17 00:00:00 2001 From: Asher Date: Thu, 6 Mar 2025 15:23:55 -0900 Subject: [PATCH 58/69] v2.3.1 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16aca4a..e44b5ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [2.3.1](https://github.com/coder/code-marketplace/releases/tag/v2.3.1) - 2025-03-06 + +### Changed + +- Updated several dependencies with CVEs. + ## [2.3.0](https://github.com/coder/code-marketplace/releases/tag/v2.3.0) - 2024-12-20 ### Added From bd1d8a9aff6e3524c0fb59dc5c50cb08b9e5b7ac Mon Sep 17 00:00:00 2001 From: Asher Date: Thu, 6 Mar 2025 15:25:02 -0900 Subject: [PATCH 59/69] Update Helm chart with 2.3.1 --- helm/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index ccf51df..1b8ac0d 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.3.0 +version: 1.3.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.3.0" +appVersion: "v2.3.1" From 626e5d7d8b0e0d97c4b2b93ef6d9b68c3ac18a05 Mon Sep 17 00:00:00 2001 From: anael-l <79835910+anael-l@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:41:51 +0200 Subject: [PATCH 60/69] Allow setting other environment variables in Helm chart (#103) --- helm/templates/deployment.yaml | 5 ++++- helm/values.yaml | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 325b3b9..27c4895 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -44,14 +44,17 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- if .Values.persistence.artifactory.enabled }} env: + {{- if .Values.persistence.artifactory.enabled }} - name: "ARTIFACTORY_TOKEN" valueFrom: secretKeyRef: name: artifactory key: token {{- end }} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} ports: - name: http containerPort: {{ .Values.service.port }} diff --git a/helm/values.yaml b/helm/values.yaml index c16cae6..7b0280e 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -10,6 +10,8 @@ image: # Overrides the image tag whose default is the chart appVersion. tag: "" +extraEnv: [] + imagePullSecrets: [] nameOverride: "" fullnameOverride: "" From bea24c0d02c2ae5b0e69b376fab33b983d9d7aa8 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 18 Apr 2025 15:44:01 -0800 Subject: [PATCH 61/69] Add details about scanning and caching --- README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26a49f0..11893aa 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,38 @@ remove all versions. ./code-marketplace remove ms-python.python --all [flags] ``` +## Scanning frequency and caching + +The marketplace does not utilize a database. When an extension query is made, +the marketplace scans the local file system or queries Artifactory on demand to +find all the available extensions. + +However, for Artifactory in particular this can be slow, so this full list of +extensions is cached in memory for a default of one minute and reused for any +subsequent requests that fall within that duration. This duration can be +configured or disabled with `--list-cache-duration` and applies to both storage +backends. + +This means that when you add or remove an extension, depending on when the last +request was made, it can take a duration between zero and +`--list-cache-duration` for the query response to reflect that change. + +Artifactory storage also uses a second in-memory cache for extension manifests, +which are referenced in extension queries (for things like categories). This +cache is initially populated with all available extension manifests on startup. +Extensions added after the server is running are added to the cache on-demand +the next time extensions are scanned. + +The manifest cache has no expiration and never evicts manifests because it was +expected that extensions are typically only ever added and individual extension +version manifests never change; however we would like to implement evicting +manifests of extensions that have been removed. + +With local storage, manifests are read directly from the file system on +demand. Requests for other extension assets (such as icons) for both storage +backends have no cache and are read/proxied directly from the file system or +Artifactory since they are not in the extension query hot path. + ## Usage in code-server You can point code-server to your marketplace by setting the @@ -171,7 +203,7 @@ Although not officially supported, you can follow the examples below to start us export VSCODE_GALLERY_ITEM_URL="https:///item" # Or set a product.json file in `~/.config/VSCodium/product.json` codium - ``` + ``` ## Missing features From c1133eb8aa6c1a8e92b02a3fb1f346c485b6dae1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 11:28:40 -0800 Subject: [PATCH 62/69] Bump github.com/go-chi/chi/v5 from 5.2.0 to 5.2.1 (#99) Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.2.0 to 5.2.1. - [Release notes](https://github.com/go-chi/chi/releases) - [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-chi/chi/compare/v5.2.0...v5.2.1) --- updated-dependencies: - dependency-name: github.com/go-chi/chi/v5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a3b0a3b..6d418a7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.8 require ( cdr.dev/slog v1.6.1 - github.com/go-chi/chi/v5 v5.2.0 + github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.14.1 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index d9024ab..368875a 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNW github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= -github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= +github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= From ac2aa96b0e45de0270a00e04ceeb890e9c0da340 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 9 May 2025 11:23:14 -0800 Subject: [PATCH 63/69] Add details about using a custom certificate authority --- README.md | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 11893aa..3c6c2d2 100644 --- a/README.md +++ b/README.md @@ -192,18 +192,39 @@ code-server If code-server reports content security policy errors ensure that the marketplace is running behind an https URL. +### Custom certificate authority + +If you are using a custom certificate authority or a self-signed certificate and +get errors like "unable to verify the first certificate", you may need to set +the [NODE_EXTRA_CA_CERTS](https://nodejs.org/api/cli.html#node_extra_ca_certsfile) +environment variable for code-server to find your certificates bundle. + +Make sure your bundle contains the full certificate chain. This can be necessary +because Node does not read system certificates by default and while VS Code has +code for reading them, it appears not to work or be enabled for the web version. + +Some so-called "web" extensions (like `vscodevim.vim`) are installed in the +browser, and extension searches are also performed from the browser, so your +certificate bundle may also need to be installed on the client machine in +addition to the remote machine. + ## Usage in VS Code & VSCodium -Although not officially supported, you can follow the examples below to start using code-marketplace with VS Code and VSCodium: +Although not officially supported, you can follow the examples below to start +using code-marketplace with VS Code and VSCodium: - [VS Code](https://github.com/eclipse/openvsx/wiki/Using-Open-VSX-in-VS-Code) + + Extension signing may have to be disabled in VS Code. + - [VSCodium](https://github.com/VSCodium/vscodium/blob/master/docs/index.md#howto-switch-marketplace) + + ``` + export VSCODE_GALLERY_SERVICE_URL="https:///api + export VSCODE_GALLERY_ITEM_URL="https:///item" + # Or set a product.json file in `~/.config/VSCodium/product.json` + codium ``` - export VSCODE_GALLERY_SERVICE_URL="https:///api - export VSCODE_GALLERY_ITEM_URL="https:///item" - # Or set a product.json file in `~/.config/VSCodium/product.json` - codium - ``` ## Missing features From d4b4ceaeb69f9178be95c842075919694ec96668 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:04:51 +0000 Subject: [PATCH 64/69] Bump the github-actions group with 2 updates Bumps the github-actions group with 2 updates: [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) and [robinraju/release-downloader](https://github.com/robinraju/release-downloader). Updates `golangci/golangci-lint-action` from 6.2.0 to 6.5.0 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v6.2.0...v6.5.0) Updates `robinraju/release-downloader` from 1.11 to 1.12 - [Release notes](https://github.com/robinraju/release-downloader/releases) - [Commits](https://github.com/robinraju/release-downloader/compare/v1.11...v1.12) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: robinraju/release-downloader dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/lint.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index fc55977..a890a07 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,6 +31,6 @@ jobs: with: go-version: "~1.22" - name: golangci-lint - uses: golangci/golangci-lint-action@v6.2.0 + uses: golangci/golangci-lint-action@v7.0.0 with: version: v1.58.0 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 692e728..839a122 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: robinraju/release-downloader@v1.11 + - uses: robinraju/release-downloader@v1.12 with: repository: "coder/code-marketplace" tag: ${{ github.event.inputs.version || github.ref_name }} From 0b6df5a20551f548f03894eca54690ac7f835749 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 9 May 2025 11:47:39 -0800 Subject: [PATCH 65/69] Update go and golangci-lint --- .github/workflows/lint.yaml | 4 ++-- .golangci.yaml | 4 ++++ api/api_test.go | 7 ++++--- flake.lock | 35 ++++++++++++++++++++++++++--------- flake.nix | 2 +- 5 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 .golangci.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a890a07..551f9d0 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -29,8 +29,8 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "~1.22" + go-version: 1.24.2 - name: golangci-lint uses: golangci/golangci-lint-action@v7.0.0 with: - version: v1.58.0 + version: v2.1.2 diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..387c5e4 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,4 @@ +version: "2" +linters: + disable: + - errcheck diff --git a/api/api_test.go b/api/api_test.go index ab7ff40..86bd1ab 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -305,7 +305,8 @@ func TestAPI(t *testing.T) { var resp *http.Response var err error - if method == http.MethodPost { + switch method { + case http.MethodPost: var body []byte if str, ok := c.Request.(string); ok { body = []byte(str) @@ -314,9 +315,9 @@ func TestAPI(t *testing.T) { require.NoError(t, err) } resp, err = client.Post(url, "application/json", bytes.NewReader(body)) - } else if method == http.MethodGet { + case http.MethodGet: resp, err = client.Get(url) - } else { + default: t.Fatal(method + " is not handled in the test yet, please add it now") } require.NoError(t, err) diff --git a/flake.lock b/flake.lock index d6c2c5a..9f3817b 100644 --- a/flake.lock +++ b/flake.lock @@ -1,12 +1,15 @@ { "nodes": { "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -17,12 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1661353537, - "narHash": "sha256-1E2IGPajOsrkR49mM5h55OtYnU0dGyre6gl60NXKITE=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "0e304ff0d9db453a4b230e9386418fd974d5804a", - "type": "github" + "lastModified": 1744932701, + "narHash": "sha256-fusHbZCyv126cyArUwwKrLdCkgVAIaa/fQJYFlCEqiU=", + "path": "/nix/store/isfbldda5j8j6x3nbv1zim0c0dpf90v8-source", + "rev": "b024ced1aac25639f8ca8fdfc2f8c4fbd66c48ef", + "type": "path" }, "original": { "id": "nixpkgs", @@ -34,6 +36,21 @@ "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index fdc309b..be8e3d2 100644 --- a/flake.nix +++ b/flake.nix @@ -10,7 +10,7 @@ in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ - go_1_19 + go_1_24 golangci-lint gotestsum kubernetes-helm From 88e1279b3d0050b4a046e44b62b15b5646d27c2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 19:52:42 +0000 Subject: [PATCH 66/69] Bump golang.org/x/crypto from 0.31.0 to 0.35.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.35.0. - [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.35.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.35.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 12 ++++++------ go.sum | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 6d418a7..1e3e91e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/coder/code-marketplace -go 1.22.8 +go 1.23.0 require ( cdr.dev/slog v1.6.1 @@ -12,7 +12,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 golang.org/x/mod v0.19.0 - golang.org/x/sync v0.10.0 + golang.org/x/sync v0.11.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) @@ -33,10 +33,10 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect diff --git a/go.sum b/go.sum index 368875a..5331186 100644 --- a/go.sum +++ b/go.sum @@ -73,8 +73,8 @@ go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZE go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -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/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= 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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= @@ -88,8 +88,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -97,20 +97,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -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/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 46eef58e0a79073562de7d0825f8f3f62ca2d652 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 20:00:20 +0000 Subject: [PATCH 67/69] Bump github.com/go-chi/httprate from 0.14.1 to 0.15.0 Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.14.1 to 0.15.0. - [Release notes](https://github.com/go-chi/httprate/releases) - [Commits](https://github.com/go-chi/httprate/compare/v0.14.1...v0.15.0) --- updated-dependencies: - dependency-name: github.com/go-chi/httprate dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 7 ++++--- go.sum | 16 ++++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 1e3e91e..c5722c9 100644 --- a/go.mod +++ b/go.mod @@ -6,23 +6,23 @@ require ( cdr.dev/slog v1.6.1 github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/cors v1.2.1 - github.com/go-chi/httprate v0.14.1 + github.com/go-chi/httprate v0.15.0 github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.10.0 golang.org/x/mod v0.19.0 - golang.org/x/sync v0.11.0 + golang.org/x/sync v0.12.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) require ( cloud.google.com/go/logging v1.8.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/lipgloss v0.7.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect @@ -31,6 +31,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect golang.org/x/crypto v0.35.0 // indirect diff --git a/go.sum b/go.sum index 5331186..ab06f86 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,6 @@ cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgG cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -22,8 +20,8 @@ github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= -github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0= +github.com/go-chi/httprate v0.15.0 h1:j54xcWV9KGmPf/X4H32/aTH+wBlrvxL7P+SdnRqxh5g= +github.com/go-chi/httprate v0.15.0/go.mod h1:rzGHhVrsBn3IMLYDOZQsSU4fJNWcjui4fWKJcCId1R4= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -36,6 +34,8 @@ 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -63,6 +63,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An 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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= @@ -88,8 +92,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 2c5406048faef6b3a182974503b50db7b99c1e0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 19:53:00 +0000 Subject: [PATCH 68/69] Bump github.com/spf13/cobra from 1.8.1 to 1.9.1 Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.9.1. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- cli/add_test.go | 2 +- cli/remove_test.go | 2 +- go.mod | 4 ++-- go.sum | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/add_test.go b/cli/add_test.go index b2a45f8..8349abb 100644 --- a/cli/add_test.go +++ b/cli/add_test.go @@ -145,7 +145,7 @@ func TestAdd(t *testing.T) { args := []string{"add", source, "--extensions-dir", extdir} cmd.SetArgs(args) buf := new(bytes.Buffer) - cmd.SetOutput(buf) + cmd.SetOut(buf) err := cmd.Execute() output := buf.String() diff --git a/cli/remove_test.go b/cli/remove_test.go index 45fd7c8..b9b3770 100644 --- a/cli/remove_test.go +++ b/cli/remove_test.go @@ -159,7 +159,7 @@ func TestRemove(t *testing.T) { } cmd.SetArgs(args) buf := new(bytes.Buffer) - cmd.SetOutput(buf) + cmd.SetOut(buf) err := cmd.Execute() output := buf.String() diff --git a/go.mod b/go.mod index c5722c9..0be8531 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/go-chi/httprate v0.15.0 github.com/google/uuid v1.6.0 github.com/lithammer/fuzzysearch v1.1.8 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 golang.org/x/mod v0.19.0 golang.org/x/sync v0.12.0 @@ -30,7 +30,7 @@ require ( github.com/muesli/termenv v0.15.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect diff --git a/go.sum b/go.sum index ab06f86..ca6c6a3 100644 --- a/go.sum +++ b/go.sum @@ -13,7 +13,7 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= @@ -56,10 +56,10 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= From 785ded36ec4dda5fd060fdd8c301ffe4bdb79f8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 20:37:33 +0000 Subject: [PATCH 69/69] Bump the x group with 2 updates Bumps the x group with 2 updates: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/sync](https://github.com/golang/sync). Updates `golang.org/x/mod` from 0.19.0 to 0.24.0 - [Commits](https://github.com/golang/mod/compare/v0.19.0...v0.24.0) Updates `golang.org/x/sync` from 0.10.0 to 0.12.0 - [Commits](https://github.com/golang/sync/compare/v0.10.0...v0.12.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor dependency-group: x - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor dependency-group: x ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0be8531..9d8cf50 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/lithammer/fuzzysearch v1.1.8 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 - golang.org/x/mod v0.19.0 - golang.org/x/sync v0.12.0 + golang.org/x/mod v0.24.0 + golang.org/x/sync v0.14.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 ) diff --git a/go.sum b/go.sum index ca6c6a3..e2352ee 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= 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.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -92,8 +92,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=