Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cli/command/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"go.wpm.so/cli/cli/command"
"go.wpm.so/cli/cli/command/auth"
"go.wpm.so/cli/cli/command/disttag"
pmInit "go.wpm.so/cli/cli/command/init"
"go.wpm.so/cli/cli/command/install"
"go.wpm.so/cli/cli/command/ls"
Expand All @@ -22,6 +23,7 @@ func AddCommands(cmd *cobra.Command, wpmCli command.Cli) {
auth.NewAuthCommand(wpmCli),
pmInit.NewInitCommand(wpmCli),
whoami.NewWhoamiCommand(wpmCli),
disttag.NewDistTagCommand(wpmCli),
publish.NewPublishCommand(wpmCli),
install.NewInstallCommand(wpmCli),
outdated.NewOutdatedCommand(wpmCli),
Expand Down
6 changes: 3 additions & 3 deletions cli/command/completion/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ func PackageVisibility() cobra.CompletionFunc {
)
}

// PublishTags suggests a non-exhaustive list of common dist-tags for
// `wpm publish --tag`.
func PublishTags() cobra.CompletionFunc {
// DistTags suggests a non-exhaustive list of common distribution tags, used by
// `wpm publish --tag` and `wpm dist-tag add`.
func DistTags() cobra.CompletionFunc {
return func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"latest", "next", "beta", "alpha"}, cobra.ShellCompDirectiveNoFileComp
}
Expand Down
101 changes: 101 additions & 0 deletions cli/command/disttag/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package disttag

import (
"context"
"errors"
"fmt"
"strings"

"github.com/spf13/cobra"

"go.wpm.so/cli/cli"
"go.wpm.so/cli/cli/command"
"go.wpm.so/cli/cli/command/completion"
"go.wpm.so/cli/pkg/pm/wpmjson/validator"
)
Comment thread
thelovekesh marked this conversation as resolved.

const defaultDistTag = "latest"

func newAddCommand(wpmCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "add PACKAGE@VERSION [TAG]",
Short: "Point a dist tag at a package version",
Args: cli.RequiresRangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
return runAdd(cmd.Context(), wpmCli, args)
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return completion.DistTags()(cmd, args, toComplete)
}
return nil, cobra.ShellCompDirectiveNoFileComp
},
}

return cmd
}

func runAdd(ctx context.Context, wpmCli command.Cli, args []string) error {
name, version, err := parsePackageVersion(args[0])
if err != nil {
return err
}

tag := defaultDistTag
if len(args) == 2 {
tag = args[1]
}

if err := validator.IsValidDistTag(tag); err != nil {
return fmt.Errorf("invalid dist tag %q: %w", tag, err)
}

if err := validateAuth(wpmCli); err != nil {
return err
}

client, err := wpmCli.RegistryClient()
if err != nil {
return err
}

if err := wpmCli.Progress().RunWithProgress(
"",
func() error { return client.AddDistTag(ctx, name, tag, version) },
wpmCli.Err(),
); err != nil {
return err
}

wpmCli.Out().WriteString(fmt.Sprintf("+%s: %s@%s\n", tag, name, version))

return nil
}

func validateAuth(wpmCli command.Cli) error {
cfg := wpmCli.ConfigFile()
if cfg.DefaultUser == "" || cfg.AuthToken == "" {
return errors.New("user must be logged in to perform this action")
}
return nil
}
Comment thread
thelovekesh marked this conversation as resolved.

func parsePackageVersion(arg string) (name, version string, err error) {
lastAt := strings.LastIndex(arg, "@")
if lastAt <= 0 {
return "", "", fmt.Errorf("invalid package spec %q: expected <pkg>@<version>", arg)
}

name = arg[:lastAt]
version = arg[lastAt+1:]

if err := validator.IsValidPackageName(name); err != nil {
return "", "", fmt.Errorf("invalid package name %q: %w", name, err)
}

if err := validator.IsValidVersion(version); err != nil {
return "", "", fmt.Errorf("invalid version %q: %w", version, err)
}

return name, version, nil
}
26 changes: 26 additions & 0 deletions cli/command/disttag/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package disttag

import (
"github.com/spf13/cobra"

"go.wpm.so/cli/cli"
"go.wpm.so/cli/cli/command"
)

func NewDistTagCommand(wpmCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "dist-tag",
Short: "Manage package distribution tags",
Aliases: []string{"dist-tags"},
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.SetOut(wpmCli.Out())
cmd.HelpFunc()(cmd, args)
return nil
},
}

cmd.AddCommand(newAddCommand(wpmCli))

return cmd
}
2 changes: 1 addition & 1 deletion cli/command/publish/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func NewPublishCommand(wpmCli command.Cli) *cobra.Command {
flags.StringVarP(&opts.access, "access", "a", "private", "Set the package access level to either public or private")
flags.BoolVar(&opts.dryRun, "dry-run", false, "Perform a publish operation without actually publishing the package")

_ = cmd.RegisterFlagCompletionFunc("tag", completion.PublishTags())
_ = cmd.RegisterFlagCompletionFunc("tag", completion.DistTags())
_ = cmd.RegisterFlagCompletionFunc("access", completion.PackageVisibility())

return cmd
Expand Down
33 changes: 33 additions & 0 deletions docs/reference/cli/dist-tag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# wpm dist-tag

<!-- prettier-ignore-start -->
<!---MARKER_GEN_START-->
Manage package distribution tags

### Aliases

`wpm dist-tag`, `wpm dist-tags`

### Subcommands

| Name | Description |
|:-------------------------|:--------------------------------------|
| [`add`](dist-tag_add.md) | Point a dist tag at a package version |



<!---MARKER_GEN_END-->
<!-- prettier-ignore-end -->

## Description

`wpm dist-tag` groups the subcommands that manage a package's distribution tags.

A distribution tag is a human-friendly label, such as `latest` or `beta`, that
points at a specific published version. Tags give consumers a stable name to
install against instead of pinning an exact version: `wpm install acme-blocks`
resolves through the `latest` tag to whatever version it currently points at.

The `latest` tag is special and it is what `wpm install <pkg>` uses when no
version or tag is requested. Any other tag (`beta`, `next`, `canary`, …) is a
convention you define for your own release workflow.
45 changes: 45 additions & 0 deletions docs/reference/cli/dist-tag_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# wpm dist-tag add

<!-- prettier-ignore-start -->
<!---MARKER_GEN_START-->
Point a dist tag at a package version


<!---MARKER_GEN_END-->
<!-- prettier-ignore-end -->

## Description

Point a distribution tag at an already-published version of a package.

The spec is `<pkg>@<version>`, where the version must be a concrete semantic
version that already exists in the registry and the tag always resolves to an
exact release, never to another tag. If you omit the tag, it defaults to
`latest`.

You must be logged in (`wpm auth login`) or have `WPM_TOKEN` set, and you need
write access to the package. On success wpm prints a one-line summary:

```console
$ wpm dist-tag add acme-blocks@1.4.0 beta
+beta: acme-blocks@1.4.0
```

Moving an existing tag is the same operation as creating one: re-run `add` with
a different version and the tag is re-pointed.

## Examples

### Tag a version as `latest`

```console
$ wpm dist-tag add acme-blocks@1.4.0
+latest: acme-blocks@1.4.0
```

### Create a pre-release tag

```console
$ wpm dist-tag add acme-blocks@2.0.0-beta.1 beta
+beta: acme-blocks@2.0.0-beta.1
```
1 change: 1 addition & 0 deletions docs/reference/cli/wpm.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Package Manager for WordPress ecosystem
| Name | Description |
|:----------------------------|:-------------------------------------------------------------------|
| [`auth`](auth.md) | Authenticate with the wpm registry |
| [`dist-tag`](dist-tag.md) | Manage package distribution tags |
| [`init`](init.md) | Initialize a new WordPress package or init wpm in existing project |
| [`install`](install.md) | Install project dependencies and add new packages |
| [`ls`](ls.md) | List installed dependencies |
Expand Down
21 changes: 21 additions & 0 deletions pkg/pm/registry/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Client interface {
DownloadTarball(ctx context.Context, url string) (io.ReadCloser, error)
PutPackage(ctx context.Context, data *manifest.Package, tarball io.Reader) error
GetPackageManifest(ctx context.Context, packageName, versionOrTag string, force bool) (*manifest.Package, error)
AddDistTag(ctx context.Context, packageName, tag, version string) error
}

var _ Client = &client{}
Expand Down Expand Up @@ -91,6 +92,26 @@ func (c *client) PutPackage(ctx context.Context, data *manifest.Package, tarball
)
}

type distTagRequest struct {
Version string `json:"version"`
}

// AddDistTag sets a distribution tag to point at a specific package version in the registry.
func (c *client) AddDistTag(ctx context.Context, packageName, tag, version string) error {
body, err := json.Marshal(distTagRequest{Version: version})
if err != nil {
return err
}

return c.restClient.DoWithContext(
ctx,
http.MethodPut,
"/-/dist-tags/"+packageName+"/"+tag,
bytes.NewReader(body),
nil,
)
}

// GetPackageManifest retrieves a package manifest from the registry
func (c *client) GetPackageManifest(ctx context.Context, packageName, versionOrTag string, force bool) (*manifest.Package, error) {
var pkg *manifest.Package
Expand Down
Loading