mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CI: Split /bin/build artifacts * subcommand (#66005)
* Split go files * Modify publishartifacts.go * Split main * FIXME: Temp add GCSCopy to gcloud/storage * Fix lint * Exported flags * Update starlark - make drone * Further backend lint fixes * Add fallback_test.go * Fix go imports order * make drone * Remove GCSCopy from static assets * Remove GCSCopy from storybook * Fix lint * Remove GCSCopy * Remove success logline * drone yaml cleanup * Add artifacts-editions flag * Fix starlark * Add default sting slice in artifacts packages command
This commit is contained in:
committed by
GitHub
parent
1290dc824a
commit
24d348f804
43
.drone.yml
43
.drone.yml
@@ -4171,7 +4171,7 @@ steps:
|
||||
image: golang:1.20.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts publish --security --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET}
|
||||
- ./bin/build artifacts packages --security --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET}
|
||||
depends_on:
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
@@ -4183,10 +4183,21 @@ steps:
|
||||
from_secret: prerelease_bucket
|
||||
SECURITY_DEST_BUCKET:
|
||||
from_secret: security_dest_bucket
|
||||
image: grafana/grafana-ci-deploy:1.3.3
|
||||
name: publish-artifacts
|
||||
- commands:
|
||||
- ./bin/build artifacts static-assets --tag ${DRONE_TAG}
|
||||
depends_on:
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
GCP_KEY:
|
||||
from_secret: gcp_key
|
||||
PRERELEASE_BUCKET:
|
||||
from_secret: prerelease_bucket
|
||||
STATIC_ASSET_EDITIONS:
|
||||
from_secret: static_asset_editions
|
||||
image: grafana/grafana-ci-deploy:1.3.3
|
||||
name: publish-artifacts
|
||||
name: publish-static-assets
|
||||
trigger:
|
||||
event:
|
||||
- promote
|
||||
@@ -4222,7 +4233,7 @@ steps:
|
||||
image: golang:1.20.1
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts publish --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET}
|
||||
- ./bin/build artifacts packages --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET}
|
||||
depends_on:
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
@@ -4234,10 +4245,32 @@ steps:
|
||||
from_secret: prerelease_bucket
|
||||
SECURITY_DEST_BUCKET:
|
||||
from_secret: security_dest_bucket
|
||||
image: grafana/grafana-ci-deploy:1.3.3
|
||||
name: publish-artifacts
|
||||
- commands:
|
||||
- ./bin/build artifacts static-assets --tag ${DRONE_TAG}
|
||||
depends_on:
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
GCP_KEY:
|
||||
from_secret: gcp_key
|
||||
PRERELEASE_BUCKET:
|
||||
from_secret: prerelease_bucket
|
||||
STATIC_ASSET_EDITIONS:
|
||||
from_secret: static_asset_editions
|
||||
image: grafana/grafana-ci-deploy:1.3.3
|
||||
name: publish-artifacts
|
||||
name: publish-static-assets
|
||||
- commands:
|
||||
- ./bin/build artifacts storybook --tag ${DRONE_TAG}
|
||||
depends_on:
|
||||
- compile-build-cmd
|
||||
environment:
|
||||
GCP_KEY:
|
||||
from_secret: gcp_key
|
||||
PRERELEASE_BUCKET:
|
||||
from_secret: prerelease_bucket
|
||||
image: grafana/grafana-ci-deploy:1.3.3
|
||||
name: publish-storybook
|
||||
trigger:
|
||||
event:
|
||||
- promote
|
||||
@@ -6753,6 +6786,6 @@ kind: secret
|
||||
name: enterprise2_security_prefix
|
||||
---
|
||||
kind: signature
|
||||
hmac: 54bded4dc7c3ffbbf3859a4f8f6364f59f3b6e0696152a0b835211eca7119e50
|
||||
hmac: 421b968fc475a71ac3367a4f9d2da3b6ff68208c04d28d5fd1074cf03688a52a
|
||||
|
||||
...
|
||||
|
||||
@@ -51,4 +51,22 @@ var (
|
||||
Name: "tag",
|
||||
Usage: "Grafana version tag",
|
||||
}
|
||||
securityFlag = cli.BoolFlag{
|
||||
Name: "security",
|
||||
Usage: "Security release",
|
||||
}
|
||||
srcFlag = cli.StringFlag{
|
||||
Name: "src-bucket",
|
||||
Value: "grafana-prerelease",
|
||||
Usage: "Google Cloud Storage bucket",
|
||||
}
|
||||
securityDestBucketFlag = cli.StringFlag{
|
||||
Name: "security-dest-bucket",
|
||||
Usage: "Google Cloud Storage bucket for security packages (or $SECURITY_DEST_BUCKET)",
|
||||
}
|
||||
destFlag = cli.StringFlag{
|
||||
Name: "dest-bucket",
|
||||
Value: "grafana-downloads",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -198,42 +198,31 @@ func main() {
|
||||
Usage: "Handle Grafana artifacts",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "publish",
|
||||
Usage: "Publish Grafana artifacts",
|
||||
Action: PublishArtifactsAction,
|
||||
Name: "storybook",
|
||||
Usage: "Publish Grafana storybook",
|
||||
Action: PublishStorybookAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&cli.BoolFlag{
|
||||
Name: "security",
|
||||
Usage: "Security release",
|
||||
},
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "security-dest-bucket",
|
||||
Usage: "Google Cloud Storage bucket for security packages (or $SECURITY_DEST_BUCKET)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Grafana version tag",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "src-bucket",
|
||||
Value: "grafana-prerelease",
|
||||
Usage: "Google Cloud Storage bucket",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "dest-bucket",
|
||||
Value: "grafana-downloads",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-dest-bucket",
|
||||
Value: "grafana-downloads-enterprise2",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-security-prefix",
|
||||
Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)",
|
||||
Name: "storybook-bucket",
|
||||
Value: "grafana-storybook",
|
||||
Usage: "Google Cloud Storage bucket for storybooks",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "static-assets",
|
||||
Usage: "Publish Grafana static assets",
|
||||
Action: PublishStaticAssetsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "static-assets-bucket",
|
||||
Value: "grafana-static-assets",
|
||||
@@ -243,10 +232,32 @@ func main() {
|
||||
Name: "static-asset-editions",
|
||||
Usage: "All the editions of the static assets (or $STATIC_ASSET_EDITIONS)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "packages",
|
||||
Usage: "Publish Grafana packages",
|
||||
Action: PublishArtifactsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringSliceFlag{
|
||||
Name: "artifacts-editions",
|
||||
Value: cli.NewStringSlice("oss", "enterprise", "enterprise2"),
|
||||
Usage: "Editions for which the artifacts should be delivered (oss,enterprise,enterprise2), (or $ARTIFACTS_EDITIONS)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "storybook-bucket",
|
||||
Value: "grafana-storybook",
|
||||
Usage: "Google Cloud Storage bucket for storybooks",
|
||||
Name: "enterprise2-dest-bucket",
|
||||
Value: "grafana-downloads-enterprise2",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-security-prefix",
|
||||
Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,60 +3,16 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/env"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/versions"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type publishConfig struct {
|
||||
tag string
|
||||
srcBucket string
|
||||
destBucket string
|
||||
enterprise2DestBucket string
|
||||
enterprise2SecurityPrefix string
|
||||
staticAssetsBucket string
|
||||
staticAssetEditions []string
|
||||
storybookBucket string
|
||||
security bool
|
||||
}
|
||||
|
||||
// requireListWithEnvFallback first checks the CLI for a flag with the required
|
||||
// name. If this is empty, it falls back to taking the environment variable.
|
||||
// Sadly, we cannot use cli.Flag.EnvVars for this due to it potentially leaking
|
||||
// environment variables as default values in usage-errors.
|
||||
func requireListWithEnvFallback(cctx *cli.Context, name string, envName string) ([]string, error) {
|
||||
result := cctx.StringSlice(name)
|
||||
if len(result) == 0 {
|
||||
for _, v := range strings.Split(os.Getenv(envName), ",") {
|
||||
value := strings.TrimSpace(v)
|
||||
if value != "" {
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, cli.Exit(fmt.Sprintf("Required flag (%s) or environment variable (%s) not set", name, envName), 1)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func requireStringWithEnvFallback(cctx *cli.Context, name string, envName string) (string, error) {
|
||||
result := cctx.String(name)
|
||||
if result == "" {
|
||||
result = os.Getenv(envName)
|
||||
}
|
||||
if result == "" {
|
||||
return "", cli.Exit(fmt.Sprintf("Required flag (%s) or environment variable (%s) not set", name, envName), 1)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Action implements the sub-command "publish-artifacts".
|
||||
// PublishArtifactsAction Action implements the sub-command "publish-artifacts".
|
||||
func PublishArtifactsAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
@@ -65,17 +21,23 @@ func PublishArtifactsAction(c *cli.Context) error {
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
staticAssetEditions, err := requireListWithEnvFallback(c, "static-asset-editions", "STATIC_ASSET_EDITIONS")
|
||||
security := c.Bool("security")
|
||||
var securityDestBucket, enterprise2SecurityPrefix string
|
||||
|
||||
artifactsEditions, err := env.RequireListWithEnvFallback(c, "artifacts-editions", "ARTIFACTS_EDITIONS")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
securityDestBucket, err := requireStringWithEnvFallback(c, "security-dest-bucket", "SECURITY_DEST_BUCKET")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enterprise2SecurityPrefix, err := requireStringWithEnvFallback(c, "enterprise2-security-prefix", "ENTERPRISE2_SECURITY_PREFIX")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if security {
|
||||
securityDestBucket, err = env.RequireStringWithEnvFallback(c, "security-dest-bucket", "SECURITY_DEST_BUCKET")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enterprise2SecurityPrefix, err = env.RequireStringWithEnvFallback(c, "enterprise2-security-prefix", "ENTERPRISE2_SECURITY_PREFIX")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
@@ -87,10 +49,7 @@ func PublishArtifactsAction(c *cli.Context) error {
|
||||
destBucket: c.String("dest-bucket"),
|
||||
enterprise2DestBucket: c.String("enterprise2-dest-bucket"),
|
||||
enterprise2SecurityPrefix: enterprise2SecurityPrefix,
|
||||
staticAssetsBucket: c.String("static-assets-bucket"),
|
||||
staticAssetEditions: staticAssetEditions,
|
||||
storybookBucket: c.String("storybook-bucket"),
|
||||
security: c.Bool("security"),
|
||||
security: security,
|
||||
tag: strings.TrimPrefix(c.String("tag"), "v"),
|
||||
}
|
||||
|
||||
@@ -98,114 +57,56 @@ func PublishArtifactsAction(c *cli.Context) error {
|
||||
cfg.destBucket = securityDestBucket
|
||||
}
|
||||
|
||||
err = copyStaticAssets(cfg)
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = copyStorybook(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = copyDownloads(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = copyEnterprise2Downloads(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
for _, edition := range artifactsEditions {
|
||||
switch edition {
|
||||
case "oss", "enterprise":
|
||||
err = copyArtifacts(c, gcs, cfg, edition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "enterprise2":
|
||||
err = copyEnterprise2Artifacts(c, gcs, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Printf("unrecognised artifacts edition: %s\n", edition)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyStaticAssets(cfg publishConfig) error {
|
||||
for _, edition := range cfg.staticAssetEditions {
|
||||
log.Printf("Copying static assets for %s", edition)
|
||||
srcURL := fmt.Sprintf("%s/artifacts/static-assets/%s/%s/*", cfg.srcBucket, edition, cfg.tag)
|
||||
destURL := fmt.Sprintf("%s/%s/%s/", cfg.staticAssetsBucket, edition, cfg.tag)
|
||||
err := gcsCopy("static assets", srcURL, destURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying static assets, %q", err)
|
||||
}
|
||||
func copyArtifacts(c *cli.Context, gcs *storage.Client, cfg publishConfig, edition string) error {
|
||||
bucket := gcs.Bucket(cfg.destBucket)
|
||||
destURL := edition
|
||||
if !cfg.security {
|
||||
destURL = filepath.Join(destURL, "release")
|
||||
}
|
||||
log.Printf("Successfully copied static assets!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyStorybook(cfg publishConfig) error {
|
||||
if cfg.security {
|
||||
log.Printf("skipping storybook copy - not needed for a security release")
|
||||
return nil
|
||||
}
|
||||
log.Printf("Copying storybooks...")
|
||||
srcURL := fmt.Sprintf("%s/artifacts/storybook/v%s/*", cfg.srcBucket, cfg.tag)
|
||||
destURL := fmt.Sprintf("%s/%s", cfg.storybookBucket, cfg.tag)
|
||||
err := gcsCopy("storybook", srcURL, destURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying storybook. %q", err)
|
||||
}
|
||||
stableVersion, err := versions.GetLatestVersion(versions.LatestStableVersionURL)
|
||||
if err != nil {
|
||||
log.Printf("Copying downloads for %s, from %s bucket to %s bucket", edition, cfg.srcBucket, destURL)
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/downloads/v%s/%s/release", cfg.tag, edition), bucket, destURL); err != nil {
|
||||
return err
|
||||
}
|
||||
isLatest, err := versions.IsGreaterThanOrEqual(cfg.tag, stableVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isLatest {
|
||||
log.Printf("Copying storybooks to latest...")
|
||||
srcURL := fmt.Sprintf("%s/artifacts/storybook/v%s/*", cfg.srcBucket, cfg.tag)
|
||||
destURL := fmt.Sprintf("%s/latest", cfg.storybookBucket)
|
||||
err := gcsCopy("storybook (latest)", srcURL, destURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying storybook to latest. %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Successfully copied storybook!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyDownloads(cfg publishConfig) error {
|
||||
for _, edition := range []string{
|
||||
"oss", "enterprise",
|
||||
} {
|
||||
destURL := fmt.Sprintf("%s/%s/", cfg.destBucket, edition)
|
||||
srcURL := fmt.Sprintf("%s/artifacts/downloads/v%s/%s/release/*", cfg.srcBucket, cfg.tag, edition)
|
||||
if !cfg.security {
|
||||
destURL = filepath.Join(destURL, "release")
|
||||
}
|
||||
log.Printf("Copying downloads for %s, from %s bucket to %s bucket", edition, srcURL, destURL)
|
||||
err := gcsCopy("downloads", srcURL, destURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying downloads, %q", err)
|
||||
}
|
||||
}
|
||||
log.Printf("Successfully copied downloads.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyEnterprise2Downloads(cfg publishConfig) error {
|
||||
func copyEnterprise2Artifacts(c *cli.Context, gcs *storage.Client, cfg publishConfig) error {
|
||||
bucket := gcs.Bucket(cfg.enterprise2DestBucket)
|
||||
var prefix string
|
||||
if cfg.security {
|
||||
prefix = cfg.enterprise2SecurityPrefix
|
||||
}
|
||||
srcURL := fmt.Sprintf("%s/artifacts/downloads-enterprise2/v%s/enterprise2/release/*", cfg.srcBucket, cfg.tag)
|
||||
destURL := fmt.Sprintf("%s/enterprise2/%srelease", cfg.enterprise2DestBucket, prefix)
|
||||
log.Printf("Copying downloads for enterprise2, from %s bucket to %s bucket", srcURL, destURL)
|
||||
err := gcsCopy("enterprise2 downloads", srcURL, destURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying ")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func gcsCopy(desc, src, dest string) error {
|
||||
args := strings.Split(fmt.Sprintf("-m cp -r gs://%s gs://%s", src, dest), " ")
|
||||
// nolint:gosec
|
||||
cmd := exec.Command("gsutil", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to publish %s: %w\n%s", desc, err, out)
|
||||
destURL := fmt.Sprintf("enterprise2/%srelease", prefix)
|
||||
log.Printf("Copying downloads for enterprise2, from %s bucket to %s bucket", cfg.srcBucket, destURL)
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/downloads-enterprise2/v%s/enterprise2/release", cfg.tag), bucket, destURL); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
13
pkg/build/cmd/publishconfig.go
Normal file
13
pkg/build/cmd/publishconfig.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
type publishConfig struct {
|
||||
tag string
|
||||
srcBucket string
|
||||
destBucket string
|
||||
enterprise2DestBucket string
|
||||
enterprise2SecurityPrefix string
|
||||
staticAssetsBucket string
|
||||
staticAssetEditions []string
|
||||
storybookBucket string
|
||||
security bool
|
||||
}
|
||||
51
pkg/build/cmd/publishstaticassets.go
Normal file
51
pkg/build/cmd/publishstaticassets.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/env"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// PublishStaticAssetsAction Action implements the sub-command "artifacts static-assets".
|
||||
func PublishStaticAssetsAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
staticAssetEditions, err := env.RequireListWithEnvFallback(c, "static-asset-editions", "STATIC_ASSET_EDITIONS")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("error connecting to gcp, %q", err)
|
||||
}
|
||||
|
||||
cfg := publishConfig{
|
||||
srcBucket: c.String("src-bucket"),
|
||||
staticAssetsBucket: c.String("static-assets-bucket"),
|
||||
staticAssetEditions: staticAssetEditions,
|
||||
tag: strings.TrimPrefix(c.String("tag"), "v"),
|
||||
}
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := gcs.Bucket(cfg.staticAssetsBucket)
|
||||
|
||||
for _, edition := range staticAssetEditions {
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/static-assets/%s/%s", edition, cfg.tag), bucket, fmt.Sprintf("%s/%s", edition, cfg.tag)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
64
pkg/build/cmd/publishstorybook.go
Normal file
64
pkg/build/cmd/publishstorybook.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||
"github.com/grafana/grafana/pkg/build/gcloud/storage"
|
||||
"github.com/grafana/grafana/pkg/build/versions"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// PublishStorybookAction Action implements the sub-command "publish-artifacts".
|
||||
func PublishStorybookAction(c *cli.Context) error {
|
||||
if c.NArg() > 0 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
return cli.Exit("", 1)
|
||||
}
|
||||
|
||||
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||
return fmt.Errorf("error connecting to gcp, %q", err)
|
||||
}
|
||||
|
||||
cfg := publishConfig{
|
||||
srcBucket: c.String("src-bucket"),
|
||||
storybookBucket: c.String("storybook-bucket"),
|
||||
tag: strings.TrimPrefix(c.String("tag"), "v"),
|
||||
}
|
||||
|
||||
gcs, err := storage.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket := gcs.Bucket(cfg.storybookBucket)
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/storybook/v%s", cfg.tag), bucket, cfg.tag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if latest, err := isLatest(cfg); err != nil && latest {
|
||||
log.Printf("Copying storybooks to latest...")
|
||||
if err := gcs.CopyRemoteDir(c.Context, gcs.Bucket(cfg.srcBucket), fmt.Sprintf("artifacts/storybook/v%s", cfg.tag), bucket, "latest"); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLatest(cfg publishConfig) (bool, error) {
|
||||
stableVersion, err := versions.GetLatestVersion(versions.LatestStableVersionURL)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
isLatest, err := versions.IsGreaterThanOrEqual(cfg.tag, stableVersion)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return isLatest, nil
|
||||
}
|
||||
40
pkg/build/env/fallback.go
vendored
Normal file
40
pkg/build/env/fallback.go
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// RequireListWithEnvFallback first checks the CLI for a flag with the required
|
||||
// name. If this is empty, it falls back to taking the environment variable.
|
||||
// Sadly, we cannot use cli.Flag.EnvVars for this due to it potentially leaking
|
||||
// environment variables as default values in usage-errors.
|
||||
func RequireListWithEnvFallback(cctx *cli.Context, name string, envName string) ([]string, error) {
|
||||
result := cctx.StringSlice(name)
|
||||
if len(result) == 0 {
|
||||
for _, v := range strings.Split(os.Getenv(envName), ",") {
|
||||
value := strings.TrimSpace(v)
|
||||
if value != "" {
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, cli.Exit(fmt.Sprintf("Required flag (%s) or environment variable (%s) not set", name, envName), 1)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func RequireStringWithEnvFallback(cctx *cli.Context, name string, envName string) (string, error) {
|
||||
result := cctx.String(name)
|
||||
if result == "" {
|
||||
result = os.Getenv(envName)
|
||||
}
|
||||
if result == "" {
|
||||
return "", cli.Exit(fmt.Sprintf("Required flag (%s) or environment variable (%s) not set", name, envName), 1)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
143
pkg/build/env/fallback_test.go
vendored
Normal file
143
pkg/build/env/fallback_test.go
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
flag1 = "flag1"
|
||||
flag2 = "flag2"
|
||||
)
|
||||
|
||||
func TestRequireListWithEnvFallback(t *testing.T) {
|
||||
var app = cli.NewApp()
|
||||
tests := []struct {
|
||||
testName string
|
||||
ctx *cli.Context
|
||||
name string
|
||||
envName string
|
||||
expected []string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
testName: "string slice present in context",
|
||||
ctx: cli.NewContext(app, applyFlagSet(t, flag1, "a"), nil),
|
||||
name: flag1,
|
||||
envName: "",
|
||||
expected: []string{"a"},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "string slice present in env",
|
||||
ctx: cli.NewContext(app, flag.NewFlagSet("test", 0), nil),
|
||||
name: flag1,
|
||||
envName: setEnv(t, flag1, "a"),
|
||||
expected: []string{"a"},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "string slice absent in both context and env",
|
||||
ctx: cli.NewContext(app, flag.NewFlagSet("test", 0), nil),
|
||||
name: flag1,
|
||||
envName: "",
|
||||
expected: []string(nil),
|
||||
expectedErr: cli.Exit("Required flag (flag1) or environment variable () not set", 1),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
flagList, err := RequireListWithEnvFallback(tt.ctx, tt.name, tt.envName)
|
||||
if tt.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
}
|
||||
require.Equal(t, tt.expected, flagList)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireStringWithEnvFallback(t *testing.T) {
|
||||
var app = cli.NewApp()
|
||||
tests := []struct {
|
||||
testName string
|
||||
ctx *cli.Context
|
||||
name string
|
||||
envName string
|
||||
expected string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
testName: "string present in the context",
|
||||
ctx: cli.NewContext(app, setFlags(t, flag1, flag2, flag.NewFlagSet("test", flag.ContinueOnError)), nil),
|
||||
name: flag1,
|
||||
envName: "",
|
||||
expected: "a",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "string present in env",
|
||||
ctx: cli.NewContext(app, setFlags(t, "", "", flag.NewFlagSet("test", flag.ContinueOnError)), nil),
|
||||
name: flag1,
|
||||
envName: setEnv(t, flag1, "a"),
|
||||
expected: "a",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
testName: "string absent from both context and env",
|
||||
ctx: cli.NewContext(app, setFlags(t, "", flag2, flag.NewFlagSet("test", flag.ContinueOnError)), nil),
|
||||
name: flag1,
|
||||
envName: "",
|
||||
expected: "",
|
||||
expectedErr: cli.Exit("Required flag (flag1) or environment variable () not set", 1),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.testName, func(t *testing.T) {
|
||||
flagString, err := RequireStringWithEnvFallback(tt.ctx, tt.name, tt.envName)
|
||||
if tt.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
}
|
||||
require.Equal(t, tt.expected, flagString)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func applyFlagSet(t *testing.T, aFlag, aValue string) *flag.FlagSet {
|
||||
t.Helper()
|
||||
var val cli.StringSlice
|
||||
fl := cli.StringSliceFlag{Name: aFlag, EnvVars: []string{"FLAG"}, Value: &val}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
parseInput := []string{fmt.Sprintf("-%s", aFlag), aValue}
|
||||
err := fl.Apply(set)
|
||||
require.NoError(t, err)
|
||||
err = set.Parse(parseInput)
|
||||
require.NoError(t, err)
|
||||
return set
|
||||
}
|
||||
|
||||
func setFlags(t *testing.T, flag1, flag2 string, flagSet *flag.FlagSet) *flag.FlagSet {
|
||||
t.Helper()
|
||||
if flag1 != "" {
|
||||
flagSet.StringVar(&flag1, "flag1", "a", "")
|
||||
}
|
||||
if flag2 != "" {
|
||||
flagSet.StringVar(&flag2, "flag2", "b", "")
|
||||
}
|
||||
return flagSet
|
||||
}
|
||||
|
||||
func setEnv(t *testing.T, key, value string) string {
|
||||
t.Helper()
|
||||
os.Clearenv()
|
||||
|
||||
err := os.Setenv(key, value)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
@@ -207,7 +207,6 @@ func (client *Client) RemoteCopy(ctx context.Context, file File, fromBucket, toB
|
||||
return fmt.Errorf("failed to copy object %s, to %s, err: %w", file.FullPath, dstObject, err)
|
||||
}
|
||||
|
||||
log.Printf("%s was successfully copied to %v bucket!.\n\n", file.FullPath, toBucket)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -557,17 +557,54 @@ def publish_artifacts_step(mode):
|
||||
"PRERELEASE_BUCKET": from_secret("prerelease_bucket"),
|
||||
"ENTERPRISE2_SECURITY_PREFIX": from_secret("enterprise2_security_prefix"),
|
||||
"SECURITY_DEST_BUCKET": from_secret("security_dest_bucket"),
|
||||
"STATIC_ASSET_EDITIONS": from_secret("static_asset_editions"),
|
||||
},
|
||||
"commands": [
|
||||
"./bin/build artifacts publish {}--tag $${{DRONE_TAG}} --src-bucket $${{PRERELEASE_BUCKET}}".format(
|
||||
"./bin/build artifacts packages {}--tag $${{DRONE_TAG}} --src-bucket $${{PRERELEASE_BUCKET}}".format(
|
||||
security,
|
||||
),
|
||||
],
|
||||
"depends_on": ["compile-build-cmd"],
|
||||
}
|
||||
|
||||
def publish_static_assets_step():
|
||||
return {
|
||||
"name": "publish-static-assets",
|
||||
"image": publish_image,
|
||||
"environment": {
|
||||
"GCP_KEY": from_secret("gcp_key"),
|
||||
"PRERELEASE_BUCKET": from_secret("prerelease_bucket"),
|
||||
"STATIC_ASSET_EDITIONS": from_secret("static_asset_editions"),
|
||||
},
|
||||
"commands": [
|
||||
"./bin/build artifacts static-assets --tag ${DRONE_TAG}",
|
||||
],
|
||||
"depends_on": ["compile-build-cmd"],
|
||||
}
|
||||
|
||||
def publish_storybook_step():
|
||||
return {
|
||||
"name": "publish-storybook",
|
||||
"image": publish_image,
|
||||
"environment": {
|
||||
"GCP_KEY": from_secret("gcp_key"),
|
||||
"PRERELEASE_BUCKET": from_secret("prerelease_bucket"),
|
||||
},
|
||||
"commands": [
|
||||
"./bin/build artifacts storybook --tag ${DRONE_TAG}",
|
||||
],
|
||||
"depends_on": ["compile-build-cmd"],
|
||||
}
|
||||
|
||||
def publish_artifacts_pipelines(mode):
|
||||
"""Published artifacts after they've been stored and tested in prerelease buckets.
|
||||
|
||||
Args:
|
||||
mode: public or security.
|
||||
Defaults to ''.
|
||||
|
||||
Returns:
|
||||
List of Drone pipelines.
|
||||
"""
|
||||
trigger = {
|
||||
"event": ["promote"],
|
||||
"target": [mode],
|
||||
@@ -575,7 +612,10 @@ def publish_artifacts_pipelines(mode):
|
||||
steps = [
|
||||
compile_build_cmd(),
|
||||
publish_artifacts_step(mode),
|
||||
publish_static_assets_step(),
|
||||
]
|
||||
if mode != "security":
|
||||
steps.extend([publish_storybook_step()])
|
||||
|
||||
return [
|
||||
pipeline(
|
||||
|
||||
Reference in New Issue
Block a user