Skip to content

Commit 38ecb34

Browse files
committed
Add package.json parsing
This will be necessary to determine what assets are browser assets in order to reduce the amount we need to upload to Artifactory since uploads are so slow.
1 parent fd33bd3 commit 38ecb34

File tree

3 files changed

+114
-14
lines changed

3 files changed

+114
-14
lines changed

storage/storage.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package storage
22

33
import (
44
"context"
5+
"encoding/json"
56
"encoding/xml"
67
"fmt"
78
"io"
@@ -79,7 +80,8 @@ type VSIXAssets struct {
7980
type AssetType string
8081

8182
const (
82-
VSIXAssetType AssetType = "Microsoft.VisualStudio.Services.VSIXPackage"
83+
ManifestAssetType AssetType = "Microsoft.VisualStudio.Code.Manifest" // This is the package.json.
84+
VSIXAssetType AssetType = "Microsoft.VisualStudio.Services.VSIXPackage"
8385
)
8486

8587
// VSIXAsset implements XMLManifest.PackageManifest.Assets.Asset.
@@ -117,8 +119,8 @@ type Storage interface {
117119
WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []string) error) error
118120
}
119121

120-
// Read and parse an extension manifest from a vsix file. If the manifest is
121-
// invalid it will be returned along with the validation error.
122+
// ReadVSIXManifest reads and parses an extension manifest from a vsix file. If
123+
// the manifest is invalid it will be returned along with the validation error.
122124
func ReadVSIXManifest(vsix []byte) (*VSIXManifest, error) {
123125
vmr, err := GetZipFileReader(vsix, "extension.vsixmanifest")
124126
if err != nil {
@@ -128,18 +130,16 @@ func ReadVSIXManifest(vsix []byte) (*VSIXManifest, error) {
128130
return parseVSIXManifest(vmr)
129131
}
130132

131-
// Parse an extension manifest from a reader. If the manifest is invalid it
132-
// will be returned along with the validation error.
133+
// parseVSIXManifest parses an extension manifest from a reader. If the
134+
// manifest is invalid it will be returned along with the validation error.
133135
func parseVSIXManifest(reader io.Reader) (*VSIXManifest, error) {
134136
var vm *VSIXManifest
135-
136137
decoder := xml.NewDecoder(reader)
137138
decoder.Strict = false
138139
err := decoder.Decode(&vm)
139140
if err != nil {
140141
return nil, err
141142
}
142-
143143
return vm, validateManifest(vm)
144144
}
145145

@@ -157,6 +157,28 @@ func validateManifest(manifest *VSIXManifest) error {
157157
return nil
158158
}
159159

160+
// VSIXPackageJSON partially implements Manifest.
161+
// https://github.com/microsoft/vscode-vsce/blob/main/src/manifest.ts#L40-L99
162+
type VSIXPackageJSON struct {
163+
Browser string `json:"browser"`
164+
}
165+
166+
// ReadVSIXPackageJSON reads and parses an extension's package.json from a vsix
167+
// file.
168+
func ReadVSIXPackageJSON(vsix []byte, packageJsonPath string) (*VSIXPackageJSON, error) {
169+
vpjr, err := GetZipFileReader(vsix, packageJsonPath)
170+
if err != nil {
171+
return nil, err
172+
}
173+
defer vpjr.Close()
174+
var pj *VSIXPackageJSON
175+
err = json.NewDecoder(vpjr).Decode(&pj)
176+
if err != nil {
177+
return nil, err
178+
}
179+
return pj, nil
180+
}
181+
160182
// ReadVSIX reads the bytes of a VSIX from the specified source. The source
161183
// might be a URI or a local file path.
162184
func ReadVSIX(ctx context.Context, source string) ([]byte, error) {

storage/storage_test.go

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -357,22 +357,22 @@ func TestReadVSIXManifest(t *testing.T) {
357357
{
358358
name: "MissingManifest",
359359
error: "not found",
360-
vsix: testutil.CreateVSIX(t, nil),
360+
vsix: testutil.CreateVSIX(t, nil, nil),
361361
},
362362
{
363363
name: "EmptyManifest",
364364
error: "EOF",
365-
vsix: testutil.CreateVSIX(t, []byte("")),
365+
vsix: testutil.CreateVSIX(t, []byte(""), nil),
366366
},
367367
{
368368
name: "TextFileManifest",
369369
error: "EOF",
370-
vsix: testutil.CreateVSIX(t, []byte("just some random text")),
370+
vsix: testutil.CreateVSIX(t, []byte("just some random text"), nil),
371371
},
372372
{
373373
name: "ManifestSyntaxError",
374374
error: "XML syntax error",
375-
vsix: testutil.CreateVSIX(t, []byte("<PackageManifest/PackageManifest>")),
375+
vsix: testutil.CreateVSIX(t, []byte("<PackageManifest/PackageManifest>"), nil),
376376
},
377377
{
378378
name: "ManifestMissingPublisher",
@@ -424,6 +424,74 @@ func TestReadVSIXManifest(t *testing.T) {
424424
}
425425
}
426426

427+
func TestReadVSIXPackageJson(t *testing.T) {
428+
t.Parallel()
429+
430+
tests := []struct {
431+
// error is the expected error, if any.
432+
error string
433+
// json is the package.json from which to create the VSIX. Use `vsix` to
434+
// specify raw bytes instead.
435+
json *storage.VSIXPackageJSON
436+
// name is the name of the test.
437+
name string
438+
// vsix contains the raw bytes for the VSIX from which to read the manifest.
439+
// If omitted it will be created from `manifest`. For non-error cases
440+
// always use `manifest` instead so the result can be checked.
441+
vsix []byte
442+
}{
443+
{
444+
name: "OK",
445+
json: &storage.VSIXPackageJSON{},
446+
},
447+
{
448+
name: "WithBrowser",
449+
json: &storage.VSIXPackageJSON{
450+
Browser: "foo",
451+
},
452+
},
453+
{
454+
name: "MissingPackageJson",
455+
error: "not found",
456+
vsix: testutil.CreateVSIX(t, nil, nil),
457+
},
458+
{
459+
name: "EmptyPackageJson",
460+
error: "EOF",
461+
vsix: testutil.CreateVSIX(t, nil, []byte("")),
462+
},
463+
{
464+
name: "TextFilePackageJson",
465+
error: "invalid character",
466+
vsix: testutil.CreateVSIX(t, nil, []byte("just some random text")),
467+
},
468+
{
469+
name: "PackageJsonSyntaxError",
470+
error: "invalid character",
471+
vsix: testutil.CreateVSIX(t, nil, []byte("{\"foo\": bar}")),
472+
},
473+
}
474+
475+
for _, test := range tests {
476+
test := test
477+
t.Run(test.name, func(t *testing.T) {
478+
t.Parallel()
479+
vsix := test.vsix
480+
if vsix == nil {
481+
vsix = testutil.CreateVSIXFromPackageJSON(t, test.json)
482+
}
483+
json, err := storage.ReadVSIXPackageJSON(vsix, "extension/package.json")
484+
if test.error != "" {
485+
require.Error(t, err)
486+
require.Regexp(t, test.error, err.Error())
487+
} else {
488+
require.NoError(t, err)
489+
require.Equal(t, test.json, json)
490+
}
491+
})
492+
}
493+
}
494+
427495
func TestAddExtension(t *testing.T) {
428496
t.Parallel()
429497

testutil/extensions.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package testutil
33
import (
44
"archive/zip"
55
"bytes"
6+
"encoding/json"
67
"encoding/xml"
78
"fmt"
89
"os"
@@ -153,12 +154,15 @@ type file struct {
153154
}
154155

155156
// createVSIX returns the bytes for a VSIX file containing the provided raw
156-
// manifest bytes (if not nil) and an icon.
157-
func CreateVSIX(t *testing.T, manifestBytes []byte) []byte {
157+
// manifest and package.json bytes (if not nil) and an icon.
158+
func CreateVSIX(t *testing.T, manifestBytes []byte, packageJSONBytes []byte) []byte {
158159
files := []file{{"icon.png", []byte("fake icon")}}
159160
if manifestBytes != nil {
160161
files = append(files, file{"extension.vsixmanifest", manifestBytes})
161162
}
163+
if packageJSONBytes != nil {
164+
files = append(files, file{"extension/package.json", packageJSONBytes})
165+
}
162166
buf := bytes.NewBuffer(nil)
163167
zw := zip.NewWriter(buf)
164168
for _, file := range files {
@@ -177,7 +181,13 @@ func CreateVSIX(t *testing.T, manifestBytes []byte) []byte {
177181
func CreateVSIXFromManifest(t *testing.T, manifest *storage.VSIXManifest) []byte {
178182
manifestBytes, err := xml.Marshal(manifest)
179183
require.NoError(t, err)
180-
return CreateVSIX(t, manifestBytes)
184+
return CreateVSIX(t, manifestBytes, nil)
185+
}
186+
187+
func CreateVSIXFromPackageJSON(t *testing.T, packageJSON *storage.VSIXPackageJSON) []byte {
188+
packageJSONBytes, err := json.Marshal(packageJSON)
189+
require.NoError(t, err)
190+
return CreateVSIX(t, nil, packageJSONBytes)
181191
}
182192

183193
// CreateVSIXFromExtension returns the bytes for a VSIX file containing the

0 commit comments

Comments
 (0)