Skip to content

Commit

Permalink
Several improvements to package version handling
Browse files Browse the repository at this point in the history
  • Loading branch information
guicaulada committed Aug 25, 2023
1 parent a82df32 commit a12c0b8
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 51 deletions.
7 changes: 3 additions & 4 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ var GCPFlags = []cli.Flag{
// NPMFlags are used in commands that need to authenticate with package registries to publish NPM packages
var NPMFlags = []cli.Flag{
&cli.StringFlag{
Name: "registry",
Usage: "The package registry to publish packages",
Required: true,
Value: "registry.npmjs.org",
Name: "registry",
Usage: "The package registry to publish packages",
Value: "registry.npmjs.org",
},
&cli.StringFlag{
Name: "token",
Expand Down
7 changes: 4 additions & 3 deletions cmd/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
)

var NPMCommand = &cli.Command{
Name: "npm",
Action: PipelineActionWithPackageInput(pipelines.NPM),
Usage: "Using a grafana.tar.gz as input (ideally one built using the 'package' command), take the npm artifacts and upload them to the destination. This can be used to put Grafana's npm artifacts into a bucket for external use.",
Name: "npm",
Action: PipelineActionWithPackageInput(pipelines.NPM),
Usage: "Using a grafana.tar.gz as input (ideally one built using the 'package' command), take the npm artifacts and upload them to the destination. This can be used to put Grafana's npm artifacts into a bucket for external use.",
Subcommands: []*cli.Command{PublishNPMCommand},
Flags: JoinFlagsWithDefault(
PackageInputFlags,
PublishFlags,
Expand Down
38 changes: 30 additions & 8 deletions containers/npm_publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,39 @@ package containers
import (
"context"
"fmt"
"strings"

"dagger.io/dagger"
"github.com/grafana/grafana-build/executil"
)

// PublishNPM publishes a npm package to the given destination.
func PublishNPM(ctx context.Context, d *dagger.Client, pkg *dagger.File, name string, version string, opts *NPMOpts) (string, error) {
isLatestStable, err := IsLatest(ctx, d, executil.Stable, version)
func PublishNPM(ctx context.Context, d *dagger.Client, pkg *dagger.File, opts *NPMOpts) (string, error) {
src := ExtractedArchive(d, pkg, "pkg.tgz")
version, err := GetJSONValue(ctx, d, src, "package.json", "version")

Check failure on line 15 in containers/npm_publish.go

View workflow job for this annotation

GitHub Actions / pr

ineffectual assignment to err (ineffassign)
name, err := GetJSONValue(ctx, d, src, "package.json", "name")

Check failure on line 16 in containers/npm_publish.go

View workflow job for this annotation

GitHub Actions / pr

ineffectual assignment to err (ineffassign)

isLatestStable, err := IsLatestGrafana(ctx, d, executil.Stable, version)
if err != nil {
return "", err
}

isLatestPreview, err := IsLatestGrafana(ctx, d, executil.Preview, version)
if err != nil {
return "", err
}

isLatestPreview, err := IsLatest(ctx, d, executil.Preview, version)
latestStable, err := GetLatestGrafanaVersion(ctx, d, executil.Stable)
if err != nil {
return "", err
}

latest, err := GetLatestVersion(ctx, d, executil.Stable)
latestPreview, err := GetLatestGrafanaVersion(ctx, d, executil.Preview)
if err != nil {
return "", err
}

isLaterThanPreview, err := IsLaterThan(ctx, d, version, latestPreview)
if err != nil {
return "", err
}
Expand All @@ -36,14 +51,21 @@ func PublishNPM(ctx context.Context, d *dagger.Client, pkg *dagger.File, name st

c := d.Container().From(NodeImage("lts")).
WithFile("/pkg.tgz", pkg).
WithExec([]string{"npm", "set", fmt.Sprintf("//%s/:_authToken", opts.Registry), opts.Token}).
WithExec([]string{"npm", "publish", "/pkg.tgz", "--registry", opts.Registry, "--tag", tag})
// Workaround for now (maybe unnecessary?): set a NAME environment variable so that we don't accidentally cache
WithEnvVariable("NAME", name).
WithExec([]string{"npm", "set", fmt.Sprintf("//%s/:_authToken", opts.Registry), opts.Token})

out, err := c.WithExec([]string{"npm", "view", name, "versions"}).Stdout(ctx)

Check failure on line 58 in containers/npm_publish.go

View workflow job for this annotation

GitHub Actions / pr

ineffectual assignment to err (ineffassign)
if !strings.Contains(out, fmt.Sprintf("'%s'", version)) {
// Publish only if this version is not published already
c = c.WithExec([]string{"npm", "publish", "/pkg.tgz", fmt.Sprintf("--registry https://%s", opts.Registry), "--tag", tag})
}

if !isLatestStable {
c = c.WithExec([]string{"npm", "dist-tag", "add", fmt.Sprintf("%s@%s", name, latest), "latest"})
c = c.WithExec([]string{"npm", "dist-tag", "add", fmt.Sprintf("%s@%s", name, latestStable), "latest"})
}

if isLatestPreview {
if isLatestPreview || (isLatestStable && isLaterThanPreview) {
c = c.WithExec([]string{"npm", "dist-tag", "add", fmt.Sprintf("%s@%s", name, version), "next"})
}

Expand Down
2 changes: 1 addition & 1 deletion containers/opts_grafana.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (g *GrafanaOpts) DetectVersion(ctx context.Context, client *dagger.Client,
}

log.Println("Version not provided; getting version from package.json...")
v, err := GetPackageJSONVersion(ctx, client, grafanaDir)
v, err := GetJSONValue(ctx, client, grafanaDir, "package.json", "version")
if err != nil {
return "", err
}
Expand Down
49 changes: 34 additions & 15 deletions containers/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (
"github.com/grafana/grafana-build/executil"
)

// GetPackageJSONVersion gets the "version" field from package.json in the 'src' directory.
func GetPackageJSONVersion(ctx context.Context, d *dagger.Client, src *dagger.Directory) (string, error) {
// GetJSONValue gets the value of a JSON field from a JSON file in the 'src' directory.
func GetJSONValue(ctx context.Context, d *dagger.Client, src *dagger.Directory, file string, field string) (string, error) {
c := d.Container().From("alpine").
WithExec([]string{"apk", "--update", "add", "jq"}).
WithMountedDirectory("/src", src).
WithWorkdir("/src").
WithExec([]string{"/bin/sh", "-c", "cat package.json | jq -r .version"})
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("cat %s | jq -r .%s", file, field)})

if stdout, err := c.Stdout(ctx); err == nil {
return strings.TrimSpace(stdout), nil
Expand All @@ -24,36 +24,55 @@ func GetPackageJSONVersion(ctx context.Context, d *dagger.Client, src *dagger.Di
return c.Stderr(ctx)
}

// GetLatestVersion gets the "version" field from https://grafana.com/api/grafana/versions/<channel>.
func GetLatestVersion(ctx context.Context, d *dagger.Client, channel executil.VersionChannel) (string, error) {
// GetLatestGrafanaVersion gets the "version" field from https://grafana.com/api/grafana/versions/<channel>.
func GetLatestGrafanaVersion(ctx context.Context, d *dagger.Client, channel executil.VersionChannel) (string, error) {
c := d.Container().From("alpine").
WithExec([]string{"apk", "--update", "add", "jq"}).
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("curl https://grafana.com/api/grafana/versions/%s | jq -r .version", channel)})
WithExec([]string{"apk", "--update", "add", "jq", "curl"}).
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("curl -s https://grafana.com/api/grafana/versions/%s | jq -r .version", channel)})

if stdout, err := c.Stdout(ctx); err == nil {
return strings.TrimSpace(stdout), nil
out := strings.TrimSpace(stdout)
if out == "" {
return out, fmt.Errorf("failed to retrieve grafana version from grafana.com")
}
return out, nil
}

return c.Stderr(ctx)
}

// IsLatest compares versions and returns true if the version provided is grater or equal the latest version on the channel.
func IsLatest(ctx context.Context, d *dagger.Client, channel executil.VersionChannel, version string) (bool, error) {
// IsLatestGrafana compares versions and returns true if the version provided is grater or equal the latest version of Grafana on the channel.
func IsLatestGrafana(ctx context.Context, d *dagger.Client, channel executil.VersionChannel, version string) (bool, error) {
if versionChannel := executil.GetVersionChannel(version); versionChannel != channel {
return false, nil
}

latest, err := GetLatestVersion(ctx, d, channel)
latestGrafana, err := GetLatestGrafanaVersion(ctx, d, channel)
if err != nil {
return false, err
}

return IsLaterThan(ctx, d, version, latestGrafana)
}

// GetLatestVersion compares versions and returns the latest version provided in the slice.
func GetLatestVersion(ctx context.Context, d *dagger.Client, versions []string) (string, error) {
c := d.Container().From("alpine").
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("echo -e '%s\n%s' | sort -V | tail -1", latest, version)})
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("echo -e '%s' | sort -V | tail -1", strings.Join(versions, "\\n"))})

if stdout, err := c.Stdout(ctx); err == nil {
return strings.TrimSpace(stdout) == version, nil
stdout, err := c.Stdout(ctx)
if err != nil {
return "", err
}

return false, nil
return strings.TrimSpace(stdout), nil
}

// IsLaterThan compares versions and returns true if v1 is later than v2
func IsLaterThan(ctx context.Context, d *dagger.Client, v1 string, v2 string) (bool, error) {
latest, err := GetLatestVersion(ctx, d, []string{v1, v2})
if err != nil {
return false, err
}
return latest == v1, nil
}
3 changes: 2 additions & 1 deletion pipelines/docker_publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"dagger.io/dagger"
"github.com/grafana/grafana-build/containers"
"github.com/grafana/grafana-build/executil"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
)
Expand Down Expand Up @@ -60,7 +61,7 @@ func PublishDocker(ctx context.Context, d *dagger.Client, args PipelineArgs) err
base = BaseImageUbuntu
}

isLatest, err := containers.IsLatest(ctx, d, "stable", tarOpts.Version)
isLatest, err := containers.IsLatestGrafana(ctx, d, executil.Stable, tarOpts.Version)
if err != nil {
return err
}
Expand Down
26 changes: 7 additions & 19 deletions pipelines/npm_publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,13 @@ import (
"context"
"fmt"
"log"
"path/filepath"
"strings"

"dagger.io/dagger"
"github.com/grafana/grafana-build/containers"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
)

func NPMPackageName(path string) (string, string) {
filename := filepath.Base(path)
name := WithoutExt(filename)
parts := strings.Split(name, "-")
packageName := strings.Join([]string{parts[0], parts[1]}, "/")
packageVersion := strings.Join(parts[2:], "-")
return packageName, packageVersion
}

func PublishNPM(ctx context.Context, d *dagger.Client, args PipelineArgs) error {
var (
opts = args.NPMOpts
Expand Down Expand Up @@ -57,21 +46,20 @@ func PublishNPM(ctx context.Context, d *dagger.Client, args PipelineArgs) error

func PublishNPMFunc(ctx context.Context, sm *semaphore.Weighted, d *dagger.Client, pkg *dagger.File, path string, opts *containers.NPMOpts) func() error {
return func() error {
name, version := NPMPackageName(path)
log.Printf("[%s@%s] Attempting to publish package", name, version)
log.Printf("[%s@%s] Acquiring semaphore", name, version)
log.Printf("[%s] Attempting to publish package", path)
log.Printf("[%s] Acquiring semaphore", path)
if err := sm.Acquire(ctx, 1); err != nil {
return fmt.Errorf("failed to acquire semaphore: %w", err)
}
defer sm.Release(1)
log.Printf("[%s@%s] Acquired semaphore", name, version)
log.Printf("[%s] Acquired semaphore", path)

log.Printf("[%s@%s] Publishing package", name, version)
out, err := containers.PublishNPM(ctx, d, pkg, name, version, opts)
log.Printf("[%s] Publishing package", path)
out, err := containers.PublishNPM(ctx, d, pkg, opts)
if err != nil {
return fmt.Errorf("[%s] error: %w", name, err)
return fmt.Errorf("[%s] error: %w", path, err)
}
log.Printf("[%s@%s] Done publishing package", name, version)
log.Printf("[%s] Done publishing package", path)

fmt.Fprintln(Stdout, out)
return nil
Expand Down

0 comments on commit a12c0b8

Please sign in to comment.