Skip to content

Commit 55af42e

Browse files
committed
Add artifactory stub
1 parent 38ecb34 commit 55af42e

File tree

9 files changed

+243
-43
lines changed

9 files changed

+243
-43
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
### Added
11+
12+
- Artifactory integration.
13+
1014
## [1.1.0](https://github.com/coder/code-marketplace/releases/tag/v1.1.0) - 2022-10-03
1115

1216
### Added

README.md

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,35 @@ wget https://github.com/coder/code-marketplace/releases/latest/download/code-mar
2929
chmod +x ./code-marketplace
3030
```
3131

32-
### Running the binary
32+
### Running the server
3333

3434
The marketplace server can be ran using the `server` sub-command.
3535

3636
```console
37-
./code-marketplace server --extensions-dir ./extensions
37+
./code-marketplace server [flags]
3838
```
3939

4040
Run `./code-marketplace --help` for a full list of options.
4141

42+
### Local storage
43+
44+
To use a local directory for extension storage use the `--extensions-dir` flag.
45+
46+
```console
47+
48+
./code-marketplace [command] --extensions-dir ./extensions
49+
```
50+
51+
### Artifactory storage
52+
53+
It is possible use Artifactory as a file store instead of local storage. For
54+
this to work the `ARTIFACTORY_TOKEN` environment variable must be set.
55+
56+
```console
57+
export ARTIFACTORY_TOKEN="my-token"
58+
./code-marketplace [command] --artifactory http://artifactory.server/artifactory --repo extensions
59+
```
60+
4261
### Exposing the marketplace
4362

4463
The marketplace must be put behind TLS otherwise code-server will reject
@@ -71,8 +90,8 @@ Extensions can be added to the marketplace by file or URL. The extensions
7190
directory does not need to be created beforehand.
7291

7392
```console
74-
./code-marketplace add extension.vsix --extensions-dir ./extensions
75-
./code-marketplace add https://domain.tld/extension.vsix --extensions-dir ./extensions
93+
./code-marketplace add extension.vsix [flags]
94+
./code-marketplace add https://domain.tld/extension.vsix [flags]
7695
```
7796

7897
If the extension has dependencies or is in an extension pack those details will
@@ -88,13 +107,14 @@ If an extension is open source you can get it from one of three locations:
88107
For example to add the Python extension from Open VSX:
89108

90109
```console
91-
./code-marketplace add https://open-vsx.org/api/ms-python/python/2022.14.0/file/ms-python.python-2022.14.0.vsix --extensions-dir ./extensions
110+
./code-marketplace add https://open-vsx.org/api/ms-python/python/2022.14.0/file/ms-python.python-2022.14.0.vsix [flags]
92111
```
93112

94113
Or the Vim extension from GitHub:
95114

96115
```console
97-
./code-marketplace add https://github.com/VSCodeVim/Vim/releases/download/v1.24.1/vim-1.24.1.vsix --extensions-dir ./extensions
116+
./code-marketplace add
117+
https://github.com/VSCodeVim/Vim/releases/download/v1.24.1/vim-1.24.1.vsix [flags]
98118
```
99119

100120
## Removing extensions
@@ -103,8 +123,8 @@ Extensions can be removed from the marketplace by ID and version (or use `--all`
103123
to remove all versions).
104124

105125
```console
106-
./code-marketplace remove ms-python.python-2022.14.0 --extensions-dir ./extensions
107-
./code-marketplace remove ms-python.python --all --extensions-dir ./extensions
126+
./code-marketplace remove ms-python.python-2022.14.0 [flags]
127+
./code-marketplace remove ms-python.python --all [flags]
108128
```
109129

110130
## Usage in code-server
@@ -121,8 +141,8 @@ marketplace is running behind an https URL.
121141

122142
```console
123143
make test
124-
mkdir -p extensions
125-
go run ./cmd/marketplace/main.go server --extensions-dir ./extensions
144+
mkdir extensions
145+
go run ./cmd/marketplace/main.go server [flags]
126146
```
127147

128148
When testing with code-server you may run into issues with content security
@@ -148,4 +168,6 @@ update the changelog as part of your PR.
148168

149169
## Planned work
150170

151-
- jFrog integration for file storage.
171+
- Bulk add.
172+
- Bulk add from one Artifactory repository to another (or to itself).
173+
- Progress indicators when adding/removing extensions.

cli/add.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cli
33
import (
44
"context"
55
"fmt"
6-
"path/filepath"
76
"strings"
87

98
"github.com/spf13/cobra"
@@ -17,15 +16,17 @@ import (
1716

1817
func add() *cobra.Command {
1918
var (
20-
extdir string
19+
artifactory string
20+
extdir string
21+
repo string
2122
)
2223

2324
cmd := &cobra.Command{
2425
Use: "add <source>",
2526
Short: "Add an extension to the marketplace",
2627
Example: strings.Join([]string{
2728
" marketplace add https://domain.tld/extension.vsix --extensions-dir ./extensions",
28-
" marketplace add extension.vsix --extensions-dir ./extensions",
29+
" marketplace add extension.vsix --artifactory http://artifactory.server/artifactory --repo extensions",
2930
}, "\n"),
3031
Args: cobra.ExactArgs(1),
3132
RunE: func(cmd *cobra.Command, args []string) error {
@@ -41,7 +42,12 @@ func add() *cobra.Command {
4142
logger = logger.Leveled(slog.LevelDebug)
4243
}
4344

44-
extdir, err = filepath.Abs(extdir)
45+
store, err := storage.NewStorage(&storage.Options{
46+
Artifactory: artifactory,
47+
ExtDir: extdir,
48+
Logger: logger,
49+
Repo: repo,
50+
})
4551
if err != nil {
4652
return err
4753
}
@@ -59,8 +65,6 @@ func add() *cobra.Command {
5965
return err
6066
}
6167

62-
// Always local storage for now.
63-
store := storage.NewLocalStorage(extdir, logger)
6468
location, err := store.AddExtension(ctx, manifest, vsix)
6569
if err != nil {
6670
return err
@@ -109,7 +113,8 @@ func add() *cobra.Command {
109113
}
110114

111115
cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.")
112-
_ = cmd.MarkFlagRequired("extensions-dir")
116+
cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.")
117+
cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.")
113118

114119
return cmd
115120
}

cli/remove.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"os"
8-
"path/filepath"
98
"strings"
109

1110
"github.com/spf13/cobra"
@@ -20,16 +19,18 @@ import (
2019

2120
func remove() *cobra.Command {
2221
var (
23-
extdir string
24-
all bool
22+
all bool
23+
artifactory string
24+
extdir string
25+
repo string
2526
)
2627

2728
cmd := &cobra.Command{
2829
Use: "remove <id>",
2930
Short: "Remove an extension from the marketplace",
3031
Example: strings.Join([]string{
3132
" marketplace remove publisher.extension-1.0.0 --extensions-dir ./extensions",
32-
" marketplace remove publisher.extension --all --extensions-dir ./extensions",
33+
" marketplace remove publisher.extension --all --artifactory http://artifactory.server/artifactory --repo extensions",
3334
}, "\n"),
3435
Args: cobra.ExactArgs(1),
3536
RunE: func(cmd *cobra.Command, args []string) error {
@@ -45,7 +46,12 @@ func remove() *cobra.Command {
4546
logger = logger.Leveled(slog.LevelDebug)
4647
}
4748

48-
extdir, err = filepath.Abs(extdir)
49+
store, err := storage.NewStorage(&storage.Options{
50+
Artifactory: artifactory,
51+
ExtDir: extdir,
52+
Logger: logger,
53+
Repo: repo,
54+
})
4955
if err != nil {
5056
return err
5157
}
@@ -60,9 +66,6 @@ func remove() *cobra.Command {
6066
return xerrors.Errorf("cannot specify both --all and version %s", version)
6167
}
6268

63-
// Always local storage for now.
64-
store := storage.NewLocalStorage(extdir, logger)
65-
6669
allVersions, err := store.Versions(ctx, publisher, name)
6770
if err != nil && !errors.Is(err, os.ErrNotExist) {
6871
return err
@@ -104,7 +107,8 @@ func remove() *cobra.Command {
104107

105108
cmd.Flags().BoolVar(&all, "all", false, "Whether to delete all versions of the extension.")
106109
cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.")
107-
_ = cmd.MarkFlagRequired("extensions-dir")
110+
cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.")
111+
cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.")
108112

109113
return cmd
110114
}

cli/server.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"net"
77
"net/http"
88
"os/signal"
9-
"path/filepath"
109
"strings"
1110
"time"
1211

@@ -24,15 +23,18 @@ import (
2423

2524
func server() *cobra.Command {
2625
var (
27-
extdir string
28-
address string
26+
address string
27+
artifactory string
28+
extdir string
29+
repo string
2930
)
3031

3132
cmd := &cobra.Command{
3233
Use: "server",
3334
Short: "Start the Code extension marketplace",
3435
Example: strings.Join([]string{
3536
" marketplace server --extensions-dir ./extensions",
37+
" marketplace server --artifactory http://artifactory.server/artifactory --repo extensions",
3638
}, "\n"),
3739
RunE: func(cmd *cobra.Command, args []string) error {
3840
ctx, cancel := context.WithCancel(cmd.Context())
@@ -50,6 +52,16 @@ func server() *cobra.Command {
5052
logger = logger.Leveled(slog.LevelDebug)
5153
}
5254

55+
store, err := storage.NewStorage(&storage.Options{
56+
Artifactory: artifactory,
57+
ExtDir: extdir,
58+
Logger: logger,
59+
Repo: repo,
60+
})
61+
if err != nil {
62+
return err
63+
}
64+
5365
// A separate listener is required to get the resulting address (as
5466
// opposed to using http.ListenAndServe()).
5567
listener, err := net.Listen("tcp", address)
@@ -63,14 +75,6 @@ func server() *cobra.Command {
6375
}
6476
logger.Info(ctx, "Starting API server", slog.F("address", tcpAddr))
6577

66-
extdir, err = filepath.Abs(extdir)
67-
if err != nil {
68-
return err
69-
}
70-
71-
// Always local storage for now.
72-
store := storage.NewLocalStorage(extdir, logger)
73-
7478
// Always no database for now.
7579
database := &database.NoDB{
7680
Storage: store,
@@ -130,7 +134,8 @@ func server() *cobra.Command {
130134
}
131135

132136
cmd.Flags().StringVar(&extdir, "extensions-dir", "", "The path to extensions.")
133-
_ = cmd.MarkFlagRequired("extensions-dir")
137+
cmd.Flags().StringVar(&artifactory, "artifactory", "", "Artifactory server URL.")
138+
cmd.Flags().StringVar(&repo, "repo", "", "Artifactory repository.")
134139
cmd.Flags().StringVar(&address, "address", "127.0.0.1:3001", "The address on which to serve the marketplace API.")
135140

136141
return cmd

storage/artifactory.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package storage
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
"os"
8+
9+
"golang.org/x/xerrors"
10+
11+
"cdr.dev/slog"
12+
)
13+
14+
const ArtifactoryTokenEnvKey = "ARTIFACTORY_TOKEN"
15+
16+
// Artifactory implements Storage. It stores extensions remotely through
17+
// Artifactory by both copying the VSIX and extracting said VSIX to a tree
18+
// structure in the form of publisher/extension/version to easily serve
19+
// individual assets via HTTP.
20+
type Artifactory struct {
21+
logger slog.Logger
22+
repo string
23+
token string
24+
uri string
25+
}
26+
27+
func NewArtifactoryStorage(uri, repo string, logger slog.Logger) (*Artifactory, error) {
28+
token := os.Getenv(ArtifactoryTokenEnvKey)
29+
if token == "" {
30+
return nil, xerrors.Errorf("the %s environment variable must be set", ArtifactoryTokenEnvKey)
31+
}
32+
33+
if !strings.HasSuffix(uri, "/") {
34+
uri = uri + "/"
35+
}
36+
37+
return &Artifactory{
38+
logger: logger,
39+
repo: repo,
40+
token: token,
41+
uri: uri,
42+
}, nil
43+
}
44+
45+
func (s *Artifactory) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) {
46+
return "", errors.New("not implemented")
47+
}
48+
49+
func (s *Artifactory) FileServer() http.Handler {
50+
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
51+
http.Error(rw, "not found", http.StatusNotFound)
52+
})
53+
}
54+
55+
func (s *Artifactory) Manifest(ctx context.Context, publisher, name, version string) (*VSIXManifest, error) {
56+
return nil, errors.New("not implemented")
57+
}
58+
59+
func (s *Artifactory) RemoveExtension(ctx context.Context, publisher, name, version string) error {
60+
return errors.New("not implemented")
61+
}
62+
63+
func (s *Artifactory) WalkExtensions(ctx context.Context, fn func(manifest *VSIXManifest, versions []string) error) error {
64+
return errors.New("not implemented")
65+
}
66+
67+
func (s *Artifactory) Versions(ctx context.Context, publisher, name string) ([]string, error) {
68+
return nil, errors.New("not implemented")
69+
}

storage/local.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ type Local struct {
2222
logger slog.Logger
2323
}
2424

25-
func NewLocalStorage(extdir string, logger slog.Logger) *Local {
25+
func NewLocalStorage(extdir string, logger slog.Logger) (*Local, error) {
26+
extdir, err := filepath.Abs(extdir)
27+
if err != nil {
28+
return nil, err
29+
}
2630
return &Local{
2731
extdir: extdir,
2832
logger: logger,
29-
}
33+
}, nil
3034
}
3135

3236
func (s *Local) AddExtension(ctx context.Context, manifest *VSIXManifest, vsix []byte) (string, error) {

0 commit comments

Comments
 (0)