-
Notifications
You must be signed in to change notification settings - Fork 226
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[plugins] Use reflikes for plugins #1845
Changes from 18 commits
1ef5d29
6386016
8fa019e
b98ab45
511bcbb
e67d8e6
1239bcc
b7eec16
100a77f
8866689
d8bb837
f8404d9
a7d0dbe
5e804f2
275fd49
dd716a7
8aaec6b
908f9a8
8f7d329
1bf645a
3261194
cfa1b15
8a6f071
c4c31c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,56 +4,56 @@ import ( | |
"io" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/samber/lo" | ||
"go.jetpack.io/devbox/internal/boxcli/usererr" | ||
"go.jetpack.io/devbox/internal/cachehash" | ||
) | ||
|
||
type githubPlugin struct { | ||
raw string | ||
org string | ||
repo string | ||
revision string | ||
dir string | ||
RefLike | ||
} | ||
|
||
// newGithubPlugin returns a plugin that is hosted on github. | ||
// url is of the form org/repo?dir=<dir> | ||
// The (optional) dir must have a plugin.json" | ||
func newGithubPlugin(rawURL string) (*githubPlugin, error) { | ||
pluginURL, err := url.Parse(rawURL) | ||
func newGithubPlugin(ref RefLike) *githubPlugin { | ||
if ref.Ref.Ref == "" && ref.Rev == "" { | ||
ref.Ref.Ref = "master" | ||
} | ||
return &githubPlugin{RefLike: ref} | ||
} | ||
|
||
func (p *githubPlugin) Fetch() ([]byte, error) { | ||
// Github redirects "master" to "main" in new repos. They don't do the reverse | ||
// so setting master here is better. | ||
contentURL, err := url.JoinPath( | ||
"https://raw.githubusercontent.com/", | ||
p.Owner, | ||
p.Repo, | ||
lo.Ternary(p.Rev == "", "master", p.Rev), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to use golang std lib: |
||
p.withFilename(p.Dir), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure I follow: why are we passing in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. different plugin types have different fields represent the path. Local plugins use Maybe a cleaner solution could be to implement a |
||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
parts := strings.SplitN(pluginURL.Path, "/", 3) | ||
|
||
if len(parts) < 2 { | ||
res, err := http.Get(contentURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer res.Body.Close() | ||
if res.StatusCode != http.StatusOK { | ||
return nil, usererr.New( | ||
"invalid github plugin url %q. Must be of the form org/repo/[revision]", | ||
rawURL, | ||
"failed to fetch github import:%s (Status code %d). \nPlease make sure a "+ | ||
"%s file exists in the directory.", | ||
contentURL, | ||
res.StatusCode, | ||
p.filename, | ||
) | ||
} | ||
|
||
plugin := &githubPlugin{ | ||
raw: rawURL, | ||
org: parts[0], | ||
repo: parts[1], | ||
revision: "master", | ||
dir: pluginURL.Query().Get("dir"), | ||
} | ||
|
||
if len(parts) == 3 { | ||
plugin.revision = parts[2] | ||
} | ||
|
||
return plugin, nil | ||
return io.ReadAll(res.Body) | ||
} | ||
|
||
func (p *githubPlugin) CanonicalName() string { | ||
return p.org + "-" + p.repo | ||
return p.Owner + "-" + p.Repo | ||
} | ||
|
||
func (p *githubPlugin) Hash() string { | ||
|
@@ -66,10 +66,10 @@ func (p *githubPlugin) FileContent(subpath string) ([]byte, error) { | |
// so setting master here is better. | ||
contentURL, err := url.JoinPath( | ||
"https://raw.githubusercontent.com/", | ||
p.org, | ||
p.repo, | ||
p.revision, | ||
p.dir, | ||
p.Owner, | ||
p.Repo, | ||
lo.Ternary(p.Rev == "", "master", p.Rev), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consider: |
||
p.Dir, | ||
subpath, | ||
) | ||
if err != nil { | ||
|
@@ -85,17 +85,9 @@ func (p *githubPlugin) FileContent(subpath string) ([]byte, error) { | |
return nil, usererr.New( | ||
"failed to get plugin github:%s (Status code %d). \nPlease make sure a "+ | ||
"plugin.json file exists in plugin directory.", | ||
p.raw, | ||
p.String(), | ||
res.StatusCode, | ||
) | ||
} | ||
return io.ReadAll(res.Body) | ||
} | ||
|
||
func (p *githubPlugin) buildConfig(projectDir string) (*config, error) { | ||
content, err := p.FileContent("plugin.json") | ||
if err != nil { | ||
return nil, errors.WithStack(err) | ||
} | ||
return buildConfig(p, projectDir, string(content)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,14 @@ | ||
package plugin | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
|
||
"go.jetpack.io/devbox/internal/boxcli/usererr" | ||
"go.jetpack.io/devbox/internal/cachehash" | ||
"go.jetpack.io/devbox/internal/devpkg" | ||
) | ||
|
||
type Includable interface { | ||
CanonicalName() string | ||
Hash() string | ||
FileContent(subpath string) ([]byte, error) | ||
} | ||
|
||
func (m *Manager) ParseInclude(include string) (Includable, error) { | ||
includeType, name, _ := strings.Cut(include, ":") | ||
if name == "" { | ||
return nil, usererr.New("include name is required") | ||
} else if includeType == "plugin" { | ||
if t, name, _ := strings.Cut(include, ":"); t == "plugin" { | ||
return devpkg.PackageFromStringWithDefaults(name, m.lockfile), nil | ||
} else if includeType == "path" { | ||
absPath := filepath.Join(m.ProjectDir(), name) | ||
return newLocalPlugin(absPath) | ||
} else if includeType == "github" { | ||
return newGithubPlugin(name) | ||
} | ||
return nil, usererr.New("unknown include type %q", includeType) | ||
} | ||
|
||
type localPlugin struct { | ||
name string | ||
path string | ||
} | ||
|
||
var nameRegex = regexp.MustCompile(`^[a-zA-Z0-9_\- ]+$`) | ||
|
||
func newLocalPlugin(path string) (*localPlugin, error) { | ||
content, err := os.ReadFile(path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
m := map[string]any{} | ||
if err := json.Unmarshal(content, &m); err != nil { | ||
return nil, err | ||
} | ||
name, ok := m["name"].(string) | ||
if !ok || name == "" { | ||
return nil, | ||
usererr.New("plugin %s is missing a required field 'name'", path) | ||
} | ||
if !nameRegex.MatchString(name) { | ||
return nil, usererr.New( | ||
"plugin %s has an invalid name %q. Name must match %s", | ||
path, name, nameRegex, | ||
) | ||
} | ||
return &localPlugin{ | ||
name: name, | ||
path: path, | ||
}, nil | ||
} | ||
|
||
func (l *localPlugin) CanonicalName() string { | ||
return l.name | ||
} | ||
|
||
func (l *localPlugin) IsLocal() bool { | ||
return true | ||
} | ||
|
||
func (l *localPlugin) Hash() string { | ||
h, _ := cachehash.Bytes([]byte(l.path)) | ||
return h | ||
} | ||
|
||
func (l *localPlugin) FileContent(subpath string) ([]byte, error) { | ||
return os.ReadFile(filepath.Join(filepath.Dir(l.path), subpath)) | ||
return parseReflike(include) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of embedding the struct, could we make it an explicit field to distinguish methods/fields that are from RefLike versus native to
githubPlugin
?But its tolerable if it means having to re-implement a lot of methods unnecessarily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can make that change. It does mean that
plugin.Repo
becomesplugin.ref.Repo
so the code is a big uglier. (There's roughly 12 callsites in this file that would change)