Skip to content

Commit c7c1abf

Browse files
committed
Display dependencies and pack extensions after add
1 parent 18a7ecf commit c7c1abf

File tree

12 files changed

+218
-69
lines changed

12 files changed

+218
-69
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,9 @@ directory does not need to be created beforehand.
6363
./code-marketplace add https://domain.tld/extension.vsix --extensions-dir ./extensions
6464
```
6565

66-
Extensions listed as dependencies must also be added.
67-
68-
If the extension is part of an extension pack the other extensions in the pack
69-
can also be added but doing so is optional.
66+
If the extension has dependencies or is in an extension pack those details will
67+
be printed. Extensions listed as dependencies must also be added but extensions
68+
in a pack are optional.
7069

7170
If an extension is open source you can get it from one of three locations:
7271

api/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func (api *API) extensionQuery(rw http.ResponseWriter, r *http.Request) {
208208
func (api *API) assetRedirect(rw http.ResponseWriter, r *http.Request) {
209209
// TODO: Asset URIs can contain a targetPlatform query variable.
210210
baseURL := httpapi.RequestBaseURL(r, "/")
211-
assetType := chi.URLParam(r, "type")
211+
assetType := storage.AssetType(chi.URLParam(r, "type"))
212212
if assetType == "vspackage" {
213213
assetType = storage.VSIXAssetType
214214
}

api/api_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import (
2626

2727
type fakeStorage struct{}
2828

29-
func (s *fakeStorage) AddExtension(ctx context.Context, source string) (string, error) {
30-
return "", errors.New("not implemented")
29+
func (s *fakeStorage) AddExtension(ctx context.Context, source string) (*storage.Extension, error) {
30+
return nil, errors.New("not implemented")
3131
}
3232

3333
func (s *fakeStorage) FileServer() http.Handler {

cli/add.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"context"
55
"fmt"
66
"path/filepath"
7+
"strings"
78

89
"github.com/spf13/cobra"
910

1011
"cdr.dev/slog"
1112
"cdr.dev/slog/sloggers/sloghuman"
1213

1314
"github.com/coder/code-marketplace/storage"
15+
"github.com/coder/code-marketplace/util"
1416
)
1517

1618
func add() *cobra.Command {
@@ -46,13 +48,35 @@ func add() *cobra.Command {
4648
Logger: logger,
4749
}
4850

49-
dest, err := store.AddExtension(ctx, args[0])
51+
ext, err := store.AddExtension(ctx, args[0])
5052
if err != nil {
5153
return err
5254
}
5355

54-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Extension unpacked to %s\n", dest)
55-
return nil
56+
depCount := len(ext.Dependencies)
57+
summary := []string{
58+
fmt.Sprintf("Unpacked %s to %s", ext.ID, ext.Location),
59+
fmt.Sprintf("%s has %s", ext.ID, util.Plural(depCount, "dependency", "dependencies")),
60+
}
61+
62+
if depCount > 0 {
63+
for _, id := range ext.Dependencies {
64+
summary = append(summary, fmt.Sprintf(" - %s", id))
65+
}
66+
}
67+
68+
packCount := len(ext.Pack)
69+
if packCount > 0 {
70+
summary = append(summary, fmt.Sprintf("%s is in a pack with %s", ext.ID, util.Plural(packCount, "other extension", "")))
71+
for _, id := range ext.Pack {
72+
summary = append(summary, fmt.Sprintf(" - %s", id))
73+
}
74+
} else {
75+
summary = append(summary, fmt.Sprintf("%s is not in a pack", ext.ID))
76+
}
77+
78+
_, err = fmt.Fprintln(cmd.OutOrStdout(), strings.Join(summary, "\n"))
79+
return err
5680
},
5781
}
5882

database/database.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"net/url"
66
"time"
7+
8+
"github.com/coder/code-marketplace/storage"
79
)
810

911
// API references:
@@ -133,15 +135,15 @@ type ExtVersion struct {
133135
// ExtFile implements IRawGalleryExtensionFile.
134136
// https://github.com/microsoft/vscode/blob/29234f0219bdbf649d6107b18651a1038d6357ac/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L32-L35
135137
type ExtFile struct {
136-
Type string `json:"assetType"`
137-
Source string `json:"source"`
138+
Type storage.AssetType `json:"assetType"`
139+
Source string `json:"source"`
138140
}
139141

140142
// ExtProperty implements IRawGalleryExtensionProperty.
141143
// https://github.com/microsoft/vscode/blob/29234f0219bdbf649d6107b18651a1038d6357ac/src/vs/platform/extensionManagement/common/extensionGalleryService.ts#L37-L40
142144
type ExtProperty struct {
143-
Key string `json:"key"`
144-
Value string `json:"value"`
145+
Key storage.PropertyType `json:"key"`
146+
Value string `json:"value"`
145147
}
146148

147149
// ExtStat implements IRawGalleryExtensionStatistics.
@@ -154,7 +156,7 @@ type ExtStat struct {
154156
type Asset struct {
155157
Extension string
156158
Publisher string
157-
Type string
159+
Type storage.AssetType
158160
Version string
159161
}
160162

database/database_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import (
2020

2121
type memoryStorage struct{}
2222

23-
func (s *memoryStorage) AddExtension(ctx context.Context, source string) (string, error) {
24-
return "", errors.New("not implemented")
23+
func (s *memoryStorage) AddExtension(ctx context.Context, source string) (*storage.Extension, error) {
24+
return nil, errors.New("not implemented")
2525
}
2626

2727
func (s *memoryStorage) FileServer() http.Handler {
@@ -505,7 +505,7 @@ func TestGetExtensions(t *testing.T) {
505505
require.Len(t, ext.Versions, 5, "versions")
506506
for _, version := range ext.Versions {
507507
require.Empty(t, version.Files, "files")
508-
require.Len(t, version.Properties, 1, "properties")
508+
require.Len(t, version.Properties, 2, "properties")
509509
}
510510
},
511511
},
@@ -546,7 +546,7 @@ func TestGetExtensions(t *testing.T) {
546546
for _, version := range ext.Versions {
547547
require.Len(t, version.Files, 1, "files")
548548
require.Equal(t, fmt.Sprintf("%s/files/foo/zany/%s/icon.png", base, version.Version), version.Files[0].Source)
549-
require.Len(t, version.Properties, 1, "properties")
549+
require.Len(t, version.Properties, 2, "properties")
550550
require.Equal(t, version.AssetURI, version.FallbackAssetURI)
551551
}
552552
},

storage/local.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path/filepath"
1111
"sort"
12+
"strings"
1213

1314
"golang.org/x/mod/semver"
1415

@@ -21,26 +22,26 @@ type Local struct {
2122
Logger slog.Logger
2223
}
2324

24-
func (s *Local) AddExtension(ctx context.Context, source string) (string, error) {
25+
func (s *Local) AddExtension(ctx context.Context, source string) (*Extension, error) {
2526
vsixBytes, err := readVSIX(ctx, source)
2627
if err != nil {
27-
return "", err
28+
return nil, err
2829
}
2930

3031
mr, err := GetZipFileReader(vsixBytes, "extension.vsixmanifest")
3132
if err != nil {
32-
return "", err
33+
return nil, err
3334
}
3435
defer mr.Close()
3536

3637
manifest, err := parseVSIXManifest(mr)
3738
if err != nil {
38-
return "", err
39+
return nil, err
3940
}
4041

4142
err = validateManifest(manifest)
4243
if err != nil {
43-
return "", err
44+
return nil, err
4445
}
4546

4647
// Extract the zip to the correct path.
@@ -55,21 +56,35 @@ func (s *Local) AddExtension(ctx context.Context, source string) (string, error)
5556
return os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
5657
})
5758
if err != nil {
58-
return "", err
59+
return nil, err
5960
}
6061

6162
// Copy the VSIX itself as well.
62-
vsixName := fmt.Sprintf("%s.%s-%s.vsix", identity.Publisher, identity.ID, identity.Version)
63+
name := fmt.Sprintf("%s.%s-%s", identity.Publisher, identity.ID, identity.Version)
64+
vsixName := fmt.Sprintf("%s.vsix", name)
6365
dst, err := os.OpenFile(filepath.Join(dir, vsixName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
6466
if err != nil {
65-
return "", err
67+
return nil, err
6668
}
6769
_, err = io.Copy(dst, bytes.NewReader(vsixBytes))
6870
if err != nil {
69-
return "", err
71+
return nil, err
72+
}
73+
74+
ext := &Extension{ID: name, Location: dir}
75+
for _, prop := range manifest.Metadata.Properties.Property {
76+
if prop.Value == "" {
77+
continue
78+
}
79+
switch prop.ID {
80+
case DependencyPropertyType:
81+
ext.Dependencies = append(ext.Dependencies, strings.Split(prop.Value, ",")...)
82+
case PackPropertyType:
83+
ext.Pack = append(ext.Pack, strings.Split(prop.Value, ",")...)
84+
}
7085
}
7186

72-
return dir, nil
87+
return ext, nil
7388
}
7489

7590
func (s *Local) FileServer() http.Handler {

storage/storage.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import (
1212
"golang.org/x/xerrors"
1313
)
1414

15-
const VSIXAssetType = "Microsoft.VisualStudio.Services.VSIXPackage"
16-
1715
// VSIXManifest implement XMLManifest.PackageManifest.
1816
// https://github.com/microsoft/vscode-vsce/blob/main/src/xml.ts#L9-L26
1917
type VSIXManifest struct {
@@ -57,11 +55,18 @@ type VSIXProperties struct {
5755
Property []VSIXProperty
5856
}
5957

58+
type PropertyType string
59+
60+
const (
61+
DependencyPropertyType PropertyType = "Microsoft.VisualStudio.Code.ExtensionDependencies"
62+
PackPropertyType PropertyType = "Microsoft.VisualStudio.Code.ExtensionPack"
63+
)
64+
6065
// VSIXProperty implements XMLManifest.PackageManifest.Metadata.Properties.Property.
6166
// https://github.com/microsoft/vscode-vsce/blob/main/src/xml.ts#L19
6267
type VSIXProperty struct {
63-
ID string `xml:"Id,attr"`
64-
Value string `xml:",attr"`
68+
ID PropertyType `xml:"Id,attr"`
69+
Value string `xml:",attr"`
6570
}
6671

6772
// VSIXAssets implements XMLManifest.PackageManifest.Assets.
@@ -70,20 +75,33 @@ type VSIXAssets struct {
7075
Asset []VSIXAsset
7176
}
7277

78+
type AssetType string
79+
80+
const (
81+
VSIXAssetType AssetType = "Microsoft.VisualStudio.Services.VSIXPackage"
82+
)
83+
7384
// VSIXAsset implements XMLManifest.PackageManifest.Assets.Asset.
7485
// https://github.com/microsoft/vscode-vsce/blob/main/src/xml.ts#L25
7586
type VSIXAsset struct {
76-
Type string `xml:",attr"`
77-
Path string `xml:",attr"`
78-
Addressable string `xml:",attr"`
87+
Type AssetType `xml:",attr"`
88+
Path string `xml:",attr"`
89+
Addressable string `xml:",attr"`
90+
}
91+
92+
type Extension struct {
93+
ID string
94+
Location string
95+
Dependencies []string
96+
Pack []string
7997
}
8098

8199
// TODO: Add Artifactory implementation of Storage.
82100
type Storage interface {
83101
// AddExtension adds the extension found at the specified source by copying it
84-
// into the extension storage directory and returns the location of the new
102+
// into the extension storage directory and returns details about the added
85103
// extension. The source may be an URI or a local file path.
86-
AddExtension(ctx context.Context, source string) (string, error)
104+
AddExtension(ctx context.Context, source string) (*Extension, error)
87105
// FileServer provides a handler for fetching extension repository files from
88106
// a client.
89107
FileServer() http.Handler

0 commit comments

Comments
 (0)