mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CI: Move publish-packages command over from grabpl (#55850)
* Move publish-packages command over from * Lint fixes * Update test
This commit is contained in:
committed by
GitHub
parent
b39d629142
commit
9a68f8704f
22
pkg/build/cmd/artifacts.go
Normal file
22
pkg/build/cmd/artifacts.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/build/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ReleaseFolder = "release"
|
||||||
|
const EnterpriseSfx = "-enterprise"
|
||||||
|
const CacheSettings = "Cache-Control:public, max-age="
|
||||||
|
|
||||||
|
type PublishConfig struct {
|
||||||
|
config.Config
|
||||||
|
|
||||||
|
Edition config.Edition
|
||||||
|
ReleaseMode config.ReleaseMode
|
||||||
|
GrafanaAPIKey string
|
||||||
|
WhatsNewURL string
|
||||||
|
ReleaseNotesURL string
|
||||||
|
DryRun bool
|
||||||
|
TTL string
|
||||||
|
SimulateRelease bool
|
||||||
|
}
|
||||||
243
pkg/build/cmd/deb.go
Normal file
243
pkg/build/cmd/deb.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/config"
|
||||||
|
"github.com/grafana/grafana/pkg/build/packaging"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/fs"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeAptlyConf(dbDir, repoDir string) error {
|
||||||
|
aptlyConf := fmt.Sprintf(`{
|
||||||
|
"rootDir": "%s",
|
||||||
|
"downloadConcurrency": 4,
|
||||||
|
"downloadSpeedLimit": 0,
|
||||||
|
"architectures": [],
|
||||||
|
"dependencyFollowSuggests": false,
|
||||||
|
"dependencyFollowRecommends": false,
|
||||||
|
"dependencyFollowAllVariants": false,
|
||||||
|
"dependencyFollowSource": false,
|
||||||
|
"dependencyVerboseResolve": false,
|
||||||
|
"gpgDisableSign": false,
|
||||||
|
"gpgDisableVerify": false,
|
||||||
|
"gpgProvider": "gpg2",
|
||||||
|
"downloadSourcePackages": false,
|
||||||
|
"skipLegacyPool": true,
|
||||||
|
"ppaDistributorID": "ubuntu",
|
||||||
|
"ppaCodename": "",
|
||||||
|
"skipContentsPublishing": false,
|
||||||
|
"FileSystemPublishEndpoints": {
|
||||||
|
"repo": {
|
||||||
|
"rootDir": "%s",
|
||||||
|
"linkMethod": "copy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"S3PublishEndpoints": {},
|
||||||
|
"SwiftPublishEndpoints": {}
|
||||||
|
}
|
||||||
|
`, dbDir, repoDir)
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(filepath.Join(home, ".aptly.conf"), []byte(aptlyConf), 0600)
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadDebs downloads Deb packages.
|
||||||
|
func downloadDebs(cfg PublishConfig, workDir string) error {
|
||||||
|
if cfg.Bucket == "" {
|
||||||
|
panic("cfg.Bucket has to be set")
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(workDir, string(filepath.Separator)) {
|
||||||
|
workDir += string(filepath.Separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
var version string
|
||||||
|
if cfg.ReleaseMode.Mode == config.TagMode {
|
||||||
|
if cfg.ReleaseMode.IsBeta {
|
||||||
|
version = strings.ReplaceAll(cfg.Version, "-", "~")
|
||||||
|
} else {
|
||||||
|
version = cfg.Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if version == "" {
|
||||||
|
panic(fmt.Sprintf("Unrecognized version mode %s", cfg.ReleaseMode.Mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sfx string
|
||||||
|
switch cfg.Edition {
|
||||||
|
case config.EditionOSS:
|
||||||
|
case config.EditionEnterprise:
|
||||||
|
sfx = EnterpriseSfx
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := fmt.Sprintf("gs://%s/%s/%s/grafana%s_%s_*.deb*", cfg.Bucket,
|
||||||
|
strings.ToLower(string(cfg.Edition)), ReleaseFolder, sfx, version)
|
||||||
|
log.Printf("Downloading Deb packages %q...\n", u)
|
||||||
|
args := []string{
|
||||||
|
"-m",
|
||||||
|
"cp",
|
||||||
|
u,
|
||||||
|
workDir,
|
||||||
|
}
|
||||||
|
//nolint:gosec
|
||||||
|
cmd := exec.Command("gsutil", args...)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to download Deb packages %q: %w\n%s", u, err, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateDebRepo updates the Debian repository with the new release.
|
||||||
|
func updateDebRepo(cfg PublishConfig, workDir string) error {
|
||||||
|
if cfg.ReleaseMode.Mode != config.TagMode {
|
||||||
|
panic(fmt.Sprintf("Unsupported version mode: %s", cfg.ReleaseMode.Mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ReleaseMode.IsTest {
|
||||||
|
if cfg.Config.DebDBBucket == packaging.DefaultDebDBBucket {
|
||||||
|
return fmt.Errorf("in test-release mode, the default Deb DB bucket shouldn't be used")
|
||||||
|
}
|
||||||
|
if cfg.Config.DebRepoBucket == packaging.DefaultDebRepoBucket {
|
||||||
|
return fmt.Errorf("in test-release mode, the default Deb repo bucket shouldn't be used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := downloadDebs(cfg, workDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoName := "grafana"
|
||||||
|
if cfg.ReleaseMode.IsBeta {
|
||||||
|
repoName = "beta"
|
||||||
|
}
|
||||||
|
|
||||||
|
repoRoot := path.Join(os.TempDir(), "deb-repo")
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(repoRoot); err != nil {
|
||||||
|
log.Printf("Failed to remove temporary directory %q: %s\n", repoRoot, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dbDir := filepath.Join(repoRoot, "db")
|
||||||
|
repoDir := filepath.Join(repoRoot, "repo")
|
||||||
|
tmpDir := filepath.Join(repoRoot, "tmp")
|
||||||
|
for _, dpath := range []string{dbDir, repoDir, tmpDir} {
|
||||||
|
if err := os.MkdirAll(dpath, 0750); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeAptlyConf(dbDir, repoDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the Debian repo database
|
||||||
|
u := fmt.Sprintf("gs://%s/%s", cfg.DebDBBucket, strings.ToLower(string(cfg.Edition)))
|
||||||
|
log.Printf("Downloading Debian repo database from %s...\n", u)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd := exec.Command("gsutil", "-m", "rsync", "-r", "-d", u, dbDir)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to download Debian repo database: %w\n%s", err, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := addPkgsToRepo(cfg, workDir, tmpDir, repoName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Updating local Debian package repository...")
|
||||||
|
// Update published local repository. This assumes that there exists already a local, published repo.
|
||||||
|
for _, tp := range []string{"stable", "beta"} {
|
||||||
|
passArg := fmt.Sprintf("-passphrase-file=%s", cfg.GPGPassPath)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd := exec.Command("aptly", "publish", "update", "-batch", passArg, "-force-overwrite", tp,
|
||||||
|
"filesystem:repo:grafana")
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return cli.NewExitError(fmt.Sprintf("failed to update Debian %q repository: %s", tp, output), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update database in GCS
|
||||||
|
u = fmt.Sprintf("gs://%s/%s", cfg.DebDBBucket, strings.ToLower(string(cfg.Edition)))
|
||||||
|
if cfg.DryRun {
|
||||||
|
log.Printf("Simulating upload of Debian repo database to GCS (%s)\n", u)
|
||||||
|
} else {
|
||||||
|
log.Printf("Uploading Debian repo database to GCS (%s)...\n", u)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("gsutil", "-m", "rsync", "-r", "-d", dbDir, u)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return cli.NewExitError(fmt.Sprintf("failed to upload Debian repo database to GCS: %s", output), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update metadata and binaries in repository bucket
|
||||||
|
u = fmt.Sprintf("gs://%s/%s/deb", cfg.DebRepoBucket, strings.ToLower(string(cfg.Edition)))
|
||||||
|
grafDir := filepath.Join(repoDir, "grafana")
|
||||||
|
if cfg.DryRun {
|
||||||
|
log.Printf("Simulating upload of Debian repo resources to GCS (%s)\n", u)
|
||||||
|
} else {
|
||||||
|
log.Printf("Uploading Debian repo resources to GCS (%s)...\n", u)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("gsutil", "-m", "rsync", "-r", "-d", grafDir, u)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return cli.NewExitError(fmt.Sprintf("failed to upload Debian repo resources to GCS: %s", output), 1)
|
||||||
|
}
|
||||||
|
allRepoResources := fmt.Sprintf("%s/**/*", u)
|
||||||
|
log.Printf("Setting cache ttl for Debian repo resources on GCS (%s)...\n", allRepoResources)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("gsutil", "-m", "setmeta", "-h", CacheSettings+cfg.TTL, allRepoResources)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return cli.NewExitError(fmt.Sprintf("failed to set cache ttl for Debian repo resources on GCS: %s", output), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPkgsToRepo(cfg PublishConfig, workDir, tmpDir, repoName string) error {
|
||||||
|
var sfx string
|
||||||
|
switch cfg.Edition {
|
||||||
|
case config.EditionOSS:
|
||||||
|
case config.EditionEnterprise:
|
||||||
|
sfx = EnterpriseSfx
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported edition %q", cfg.Edition)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Adding packages to Debian %q repo...\n", repoName)
|
||||||
|
// TODO: Be more specific about filename pattern
|
||||||
|
debs, err := filepath.Glob(filepath.Join(workDir, fmt.Sprintf("grafana%s*.deb", sfx)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, deb := range debs {
|
||||||
|
basename := filepath.Base(deb)
|
||||||
|
if strings.Contains(basename, "latest") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tgt := filepath.Join(tmpDir, basename)
|
||||||
|
if err := fs.CopyFile(deb, tgt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XXX: Adds too many packages in enterprise (Arve: What does this mean exactly?)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd := exec.Command("aptly", "repo", "add", "-force-replace", repoName, tmpDir)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return cli.NewExitError(fmt.Sprintf("failed to add packages to local Debian repository: %s", output), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -37,4 +37,13 @@ var (
|
|||||||
Name: "sign",
|
Name: "sign",
|
||||||
Usage: "Enable plug-in signing (you must set GRAFANA_API_KEY)",
|
Usage: "Enable plug-in signing (you must set GRAFANA_API_KEY)",
|
||||||
}
|
}
|
||||||
|
dryRunFlag = cli.BoolFlag{
|
||||||
|
Name: "dry-run",
|
||||||
|
Usage: "Only simulate actions",
|
||||||
|
}
|
||||||
|
gcpKeyFlag = cli.StringFlag{
|
||||||
|
Name: "gcp-key",
|
||||||
|
Usage: "Google Cloud Platform key file",
|
||||||
|
Required: true,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/build/docker"
|
"github.com/grafana/grafana/pkg/build/docker"
|
||||||
|
"github.com/grafana/grafana/pkg/build/packaging"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -169,6 +170,54 @@ func main() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "publish",
|
||||||
|
Usage: "Publish packages to Grafana com and repositories",
|
||||||
|
Subcommands: cli.Commands{
|
||||||
|
{
|
||||||
|
Name: "packages",
|
||||||
|
Usage: "publish Grafana packages",
|
||||||
|
ArgsUsage: "[version]",
|
||||||
|
Action: PublishPackages,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&jobsFlag,
|
||||||
|
&editionFlag,
|
||||||
|
&buildIDFlag,
|
||||||
|
&dryRunFlag,
|
||||||
|
&gcpKeyFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "packages-bucket",
|
||||||
|
Value: "grafana-downloads",
|
||||||
|
Usage: "Google Cloud Storage Debian database bucket",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "deb-db-bucket",
|
||||||
|
Value: packaging.DefaultDebDBBucket,
|
||||||
|
Usage: "Google Cloud Storage Debian database bucket",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "deb-repo-bucket",
|
||||||
|
Value: packaging.DefaultDebRepoBucket,
|
||||||
|
Usage: "Google Cloud Storage Debian repo bucket",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "rpm-repo-bucket",
|
||||||
|
Value: packaging.DefaultRPMRepoBucket,
|
||||||
|
Usage: "Google Cloud Storage RPM repo bucket",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "ttl",
|
||||||
|
Value: packaging.DefaultTTLSeconds,
|
||||||
|
Usage: "Cache time to live for uploaded packages",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "simulate-release",
|
||||||
|
Usage: "Only simulate creating release at grafana.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
|||||||
112
pkg/build/cmd/publishpackages.go
Normal file
112
pkg/build/cmd/publishpackages.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/config"
|
||||||
|
"github.com/grafana/grafana/pkg/build/gcloud"
|
||||||
|
"github.com/grafana/grafana/pkg/build/gpg"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublishPackages implements the sub-command "publish-packages".
|
||||||
|
func PublishPackages(c *cli.Context) error {
|
||||||
|
if err := gcloud.ActivateServiceAccount(); err != nil {
|
||||||
|
return fmt.Errorf("couldn't activate service account, err: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata, err := GenerateMetadata(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseMode, err := metadata.GetReleaseMode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dryRun := c.Bool("dry-run")
|
||||||
|
simulateRelease := c.Bool("simulate-release")
|
||||||
|
// Test release mode and dryRun imply simulateRelease
|
||||||
|
if releaseMode.IsTest || dryRun {
|
||||||
|
simulateRelease = true
|
||||||
|
}
|
||||||
|
|
||||||
|
grafanaAPIKey := strings.TrimSpace(os.Getenv("GRAFANA_COM_API_KEY"))
|
||||||
|
if grafanaAPIKey == "" {
|
||||||
|
return cli.NewExitError("the environment variable GRAFANA_COM_API_KEY must be set", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
edition := config.Edition(c.String("edition"))
|
||||||
|
|
||||||
|
// TODO: Verify config values
|
||||||
|
cfg := PublishConfig{
|
||||||
|
Config: config.Config{
|
||||||
|
Version: metadata.GrafanaVersion,
|
||||||
|
Bucket: c.String("packages-bucket"),
|
||||||
|
DebDBBucket: c.String("deb-db-bucket"),
|
||||||
|
DebRepoBucket: c.String("deb-repo-bucket"),
|
||||||
|
RPMRepoBucket: c.String("rpm-repo-bucket"),
|
||||||
|
},
|
||||||
|
Edition: edition,
|
||||||
|
ReleaseMode: releaseMode,
|
||||||
|
GrafanaAPIKey: grafanaAPIKey,
|
||||||
|
DryRun: dryRun,
|
||||||
|
TTL: c.String("ttl"),
|
||||||
|
SimulateRelease: simulateRelease,
|
||||||
|
}
|
||||||
|
if err := gpg.LoadGPGKeys(&cfg.Config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer gpg.RemoveGPGFiles(cfg.Config)
|
||||||
|
|
||||||
|
// Only update package manager repos for releases.
|
||||||
|
// In test release mode, the operator should configure different GCS buckets for the package repos,
|
||||||
|
// so should be safe.
|
||||||
|
if cfg.ReleaseMode.Mode == config.TagMode {
|
||||||
|
workDir := os.TempDir()
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(workDir); err != nil {
|
||||||
|
log.Printf("Failed to remove temporary directory %q: %s\n", workDir, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := updatePkgRepos(cfg, workDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Successfully published packages!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updatePkgRepos updates package manager repositories.
|
||||||
|
func updatePkgRepos(cfg PublishConfig, workDir string) error {
|
||||||
|
if err := gpg.Import(cfg.Config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If updating the Deb repo fails, still continue with the RPM repo, so we don't have to retry
|
||||||
|
// both by hand
|
||||||
|
debErr := updateDebRepo(cfg, workDir)
|
||||||
|
if debErr != nil {
|
||||||
|
log.Printf("Updating Deb repo failed: %s\n", debErr)
|
||||||
|
}
|
||||||
|
rpmErr := updateRPMRepo(cfg, workDir)
|
||||||
|
if rpmErr != nil {
|
||||||
|
log.Printf("Updating RPM repo failed: %s\n", rpmErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if debErr != nil {
|
||||||
|
return debErr
|
||||||
|
}
|
||||||
|
if rpmErr != nil {
|
||||||
|
return rpmErr
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Updated Deb and RPM repos successfully!")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
365
pkg/build/cmd/rpm.go
Normal file
365
pkg/build/cmd/rpm.go
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/config"
|
||||||
|
"github.com/grafana/grafana/pkg/build/packaging"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/fs"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// updateRPMRepo updates the RPM repository with the new release.
|
||||||
|
func updateRPMRepo(cfg PublishConfig, workDir string) error {
|
||||||
|
if cfg.ReleaseMode.Mode != config.TagMode {
|
||||||
|
panic(fmt.Sprintf("Unsupported version mode %s", cfg.ReleaseMode.Mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ReleaseMode.IsTest && cfg.Config.RPMRepoBucket == packaging.DefaultRPMRepoBucket {
|
||||||
|
return fmt.Errorf("in test-release mode, the default RPM repo bucket shouldn't be used")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := downloadRPMs(cfg, workDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repoRoot := path.Join(os.TempDir(), "rpm-repo")
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(repoRoot); err != nil {
|
||||||
|
log.Printf("Failed to remove %q: %s\n", repoRoot, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
repoName := "rpm"
|
||||||
|
if cfg.ReleaseMode.IsBeta {
|
||||||
|
repoName = "rpm-beta"
|
||||||
|
}
|
||||||
|
folderURI := fmt.Sprintf("gs://%s/%s/%s", cfg.RPMRepoBucket, strings.ToLower(string(cfg.Edition)), repoName)
|
||||||
|
|
||||||
|
// Download the RPM database
|
||||||
|
log.Printf("Downloading RPM database from GCS (%s)...\n", folderURI)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd := exec.Command("gsutil", "-m", "rsync", "-r", "-d", folderURI, repoRoot)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to download RPM database from GCS: %w\n%s", err, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new release to the repo
|
||||||
|
var sfx string
|
||||||
|
switch cfg.Edition {
|
||||||
|
case config.EditionOSS:
|
||||||
|
case config.EditionEnterprise:
|
||||||
|
sfx = EnterpriseSfx
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported edition %q", cfg.Edition)
|
||||||
|
}
|
||||||
|
allRPMs, err := filepath.Glob(filepath.Join(workDir, fmt.Sprintf("grafana%s-*.rpm", sfx)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list RPMs in %q: %w", workDir, err)
|
||||||
|
}
|
||||||
|
rpms := []string{}
|
||||||
|
for _, rpm := range allRPMs {
|
||||||
|
if strings.Contains(rpm, "-latest") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rpms = append(rpms, rpm)
|
||||||
|
}
|
||||||
|
// XXX: What does the following comment mean?
|
||||||
|
// adds to many files for enterprise
|
||||||
|
for _, rpm := range rpms {
|
||||||
|
if err := fs.CopyFile(rpm, filepath.Join(repoRoot, filepath.Base(rpm))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("createrepo", repoRoot)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to create repo at %q: %w\n%s", repoRoot, err, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := signRPMRepo(repoRoot, cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the repo in GCS
|
||||||
|
|
||||||
|
// Sync packages first to avoid cache misses
|
||||||
|
if cfg.DryRun {
|
||||||
|
log.Printf("Simulating upload of RPMs to GCS (%s)\n", folderURI)
|
||||||
|
} else {
|
||||||
|
log.Printf("Uploading RPMs to GCS (%s)...\n", folderURI)
|
||||||
|
args := []string{"-m", "cp"}
|
||||||
|
args = append(args, rpms...)
|
||||||
|
args = append(args, folderURI)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("gsutil", args...)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to upload RPMs to GCS: %w\n%s", err, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.DryRun {
|
||||||
|
log.Printf("Simulating upload of RPM repo metadata to GCS (%s)\n", folderURI)
|
||||||
|
} else {
|
||||||
|
log.Printf("Uploading RPM repo metadata to GCS (%s)...\n", folderURI)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("gsutil", "-m", "rsync", "-r", "-d", repoRoot, folderURI)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to upload RPM repo metadata to GCS: %w\n%s", err, output)
|
||||||
|
}
|
||||||
|
allRepoResources := fmt.Sprintf("%s/**/*", folderURI)
|
||||||
|
log.Printf("Setting cache ttl for RPM repo resources on GCS (%s)...\n", allRepoResources)
|
||||||
|
//nolint:gosec
|
||||||
|
cmd = exec.Command("gsutil", "-m", "setmeta", "-h", CacheSettings+cfg.TTL, allRepoResources)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to set cache ttl for RPM repo resources on GCS: %w\n%s", err, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadRPMs downloads RPM packages.
|
||||||
|
func downloadRPMs(cfg PublishConfig, workDir string) error {
|
||||||
|
if !strings.HasSuffix(workDir, string(filepath.Separator)) {
|
||||||
|
workDir += string(filepath.Separator)
|
||||||
|
}
|
||||||
|
var version string
|
||||||
|
if cfg.ReleaseMode.Mode == config.TagMode {
|
||||||
|
if cfg.ReleaseMode.IsBeta {
|
||||||
|
version = strings.ReplaceAll(cfg.Version, "-", "~")
|
||||||
|
} else {
|
||||||
|
version = cfg.Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if version == "" {
|
||||||
|
panic(fmt.Sprintf("Unrecognized version mode %s", cfg.ReleaseMode.Mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
var sfx string
|
||||||
|
switch cfg.Edition {
|
||||||
|
case config.EditionOSS:
|
||||||
|
case config.EditionEnterprise:
|
||||||
|
sfx = EnterpriseSfx
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := fmt.Sprintf("gs://%s/%s/%s/grafana%s-%s-*.*.rpm*", cfg.Bucket,
|
||||||
|
strings.ToLower(string(cfg.Edition)), ReleaseFolder, sfx, version)
|
||||||
|
log.Printf("Downloading RPM packages %q...\n", u)
|
||||||
|
args := []string{
|
||||||
|
"-m",
|
||||||
|
"cp",
|
||||||
|
u,
|
||||||
|
workDir,
|
||||||
|
}
|
||||||
|
//nolint:gosec
|
||||||
|
cmd := exec.Command("gsutil", args...)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to download RPM packages %q: %w\n%s", u, err, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPublicKey(cfg PublishConfig) (*packet.PublicKey, error) {
|
||||||
|
f, err := os.Open(cfg.GPGPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open %q: %w", cfg.GPGPublicKey, err)
|
||||||
|
}
|
||||||
|
defer func(f *os.File) {
|
||||||
|
err := f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(f)
|
||||||
|
|
||||||
|
block, err := armor.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != openpgp.PublicKeyType {
|
||||||
|
return nil, fmt.Errorf("invalid public key block type: %q", block.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
packetReader := packet.NewReader(block.Body)
|
||||||
|
pkt, err := packetReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, ok := pkt.(*packet.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("got non-public key from packet reader: %T", pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrivateKey(cfg PublishConfig) (*packet.PrivateKey, error) {
|
||||||
|
f, err := os.Open(cfg.GPGPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open %q: %w", cfg.GPGPrivateKey, err)
|
||||||
|
}
|
||||||
|
defer func(f *os.File) {
|
||||||
|
err := f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(f)
|
||||||
|
|
||||||
|
passphraseB, err := os.ReadFile(cfg.GPGPassPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read %q: %w", cfg.GPGPrivateKey, err)
|
||||||
|
}
|
||||||
|
passphraseB = bytes.TrimSuffix(passphraseB, []byte("\n"))
|
||||||
|
|
||||||
|
block, err := armor.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != openpgp.PrivateKeyType {
|
||||||
|
return nil, fmt.Errorf("invalid private key block type: %q", block.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
packetReader := packet.NewReader(block.Body)
|
||||||
|
pkt, err := packetReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, ok := pkt.(*packet.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("got non-private key from packet reader: %T", pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := key.Decrypt(passphraseB); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decrypt private key: %w", err)
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// signRPMRepo signs an RPM repository using PGP.
|
||||||
|
// The signature gets written to the file repodata/repomd.xml.asc.
|
||||||
|
func signRPMRepo(repoRoot string, cfg PublishConfig) error {
|
||||||
|
if cfg.GPGPublicKey == "" || cfg.GPGPrivateKey == "" {
|
||||||
|
return fmt.Errorf("private or public key is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Signing RPM repo")
|
||||||
|
|
||||||
|
pubKey, err := getPublicKey(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey, err := getPrivateKey(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pcfg := packet.Config{
|
||||||
|
DefaultHash: crypto.SHA256,
|
||||||
|
DefaultCipher: packet.CipherAES256,
|
||||||
|
DefaultCompressionAlgo: packet.CompressionZLIB,
|
||||||
|
CompressionConfig: &packet.CompressionConfig{
|
||||||
|
Level: 9,
|
||||||
|
},
|
||||||
|
RSABits: 4096,
|
||||||
|
}
|
||||||
|
currentTime := pcfg.Now()
|
||||||
|
uid := packet.NewUserId("", "", "")
|
||||||
|
|
||||||
|
isPrimaryID := false
|
||||||
|
keyLifetimeSecs := uint32(86400 * 365)
|
||||||
|
signer := openpgp.Entity{
|
||||||
|
PrimaryKey: pubKey,
|
||||||
|
PrivateKey: privKey,
|
||||||
|
Identities: map[string]*openpgp.Identity{
|
||||||
|
uid.Id: &openpgp.Identity{
|
||||||
|
Name: uid.Name,
|
||||||
|
UserId: uid,
|
||||||
|
SelfSignature: &packet.Signature{
|
||||||
|
CreationTime: currentTime,
|
||||||
|
SigType: packet.SigTypePositiveCert,
|
||||||
|
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||||
|
Hash: pcfg.Hash(),
|
||||||
|
IsPrimaryId: &isPrimaryID,
|
||||||
|
FlagsValid: true,
|
||||||
|
FlagSign: true,
|
||||||
|
FlagCertify: true,
|
||||||
|
IssuerKeyId: &pubKey.KeyId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Subkeys: []openpgp.Subkey{
|
||||||
|
{
|
||||||
|
PublicKey: pubKey,
|
||||||
|
PrivateKey: privKey,
|
||||||
|
Sig: &packet.Signature{
|
||||||
|
CreationTime: currentTime,
|
||||||
|
SigType: packet.SigTypeSubkeyBinding,
|
||||||
|
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||||
|
Hash: pcfg.Hash(),
|
||||||
|
PreferredHash: []uint8{8}, // SHA-256
|
||||||
|
FlagsValid: true,
|
||||||
|
FlagEncryptStorage: true,
|
||||||
|
FlagEncryptCommunications: true,
|
||||||
|
IssuerKeyId: &pubKey.KeyId,
|
||||||
|
KeyLifetimeSecs: &keyLifetimeSecs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore gosec G304 as this function is only used in the build process.
|
||||||
|
//nolint:gosec
|
||||||
|
freader, err := os.Open(filepath.Join(repoRoot, "repodata", "repomd.xml"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func(freader *os.File) {
|
||||||
|
err := freader.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(freader)
|
||||||
|
|
||||||
|
// Ignore gosec G304 as this function is only used in the build process.
|
||||||
|
//nolint:gosec
|
||||||
|
sigwriter, err := os.Create(filepath.Join(repoRoot, "repodata", "repomd.xml.asc"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func(sigwriter *os.File) {
|
||||||
|
err := sigwriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(sigwriter)
|
||||||
|
|
||||||
|
if err := openpgp.ArmoredDetachSignText(sigwriter, &signer, freader, nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to write PGP signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sigwriter.Close(); err != nil {
|
||||||
|
return fmt.Errorf("failed to write PGP signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
146
pkg/build/cmd/rpm_test.go
Normal file
146
pkg/build/cmd/rpm_test.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/build/config"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const pubKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v4.10.10
|
||||||
|
Comment: https://openpgpjs.org
|
||||||
|
|
||||||
|
xsBNBGM1b9wBCADZM49X7vwOS93KbgA6yhpwrYf8ZlzksGcDaYgp1IzvqHbs
|
||||||
|
xeU1mmBYVH/bSKRDG0tt3Qdky4Nvl4Oqd+g0e2ZGjmlEy9zUiPTTK/BtXT+5
|
||||||
|
s8oqih2NIAkyF91BNZABAgvh/vJdYImhYeUQBDqMJgqZ/Y/Ha31N7rSW+jUt
|
||||||
|
LHspbN0ztJYjuEd/bg2NKH7Gs/AyNvX9IQTC4k7iRRafx7q/PBCVtsk+NCwz
|
||||||
|
BEkL93xpAdcdYiMNrRP2eIHQjBmNZ/oUCkcDsLCBvcSq6P2lGpNnpPzVoTJf
|
||||||
|
v2qrWkVn5txJJsOkmBGpEDbECPunVilrWO6RPomP0yYkr6NE4XeCJ3QhABEB
|
||||||
|
AAHNGWR1bW15IDxkdW1teUBob3RtYWlsLmNvbT7CwI0EEAEIACAFAmM1b9wG
|
||||||
|
CwkHCAMCBBUICgIEFgIBAAIZAQIbAwIeAQAhCRAoJ1i5w6kkAxYhBCQv+iwt
|
||||||
|
IFn7vj9PLygnWLnDqSQDPxkH/0Ju2Cah+bOxl09uv2Ft2BVlQh0u+wJyRVgs
|
||||||
|
KxTxldAXFZwMrN4wK/GUoGWDiy2tzNtoVE6GpxWUj+LvSGFaVLNVjW+Le77I
|
||||||
|
BP/sl1wKHJbQhseKc7Mz5Zj3i0F1FPM+rLik7tNk6kiEBqYVyyXahyT98Hu1
|
||||||
|
1OKEV+8NiRG47iNgd/dpgEdVSS4DN/dL6m5q+CVy9YnlR+wXxF/2xcMmWBzR
|
||||||
|
V2cPVw0JzunpUV8lDDQ/n1sPw61D3oL1aH0bkn8aA8pEceKOVIYOaja7LkLX
|
||||||
|
uSlROlALA/M2fuubradW9I3FcrJNn+/xA52el2l/Hn/Syf9GQV/Ll/R+qKIo
|
||||||
|
Z57xWd7OwE0EYzVv3AEIAJl/PNYOF2prNKY58BfZ74XurDb9mNlZ1wsIqrOu
|
||||||
|
J/euzHEnzkCAjMUuXV7wcugjQlmpcZn6Y0QmQ2uX7SwPCMovDvngbXeAfbdd
|
||||||
|
6FUKecQ0sG54Plm8HSMNdjetdUVl7ACxjJO8Rdc/Asx7ua7gMm42CVfqMj4L
|
||||||
|
qN5foUBlaKJ1iGKUpQ+673UQWMYeOBuu9G8awbSzGaphN97CIX7xEMGzGeff
|
||||||
|
yHLHK+MsfX935uDgDwJQzxJKEugIJDMKgWOLgVz1jRCsJKHlywHTWpVuMiKY
|
||||||
|
Wnuq4tDNLBUQtaRL7uclG7Wejw/XNN0uD/zNHPgF5rmlYHVhrtDbBCP2XqTn
|
||||||
|
WU8AEQEAAcLAdgQYAQgACQUCYzVv3AIbDAAhCRAoJ1i5w6kkAxYhBCQv+iwt
|
||||||
|
IFn7vj9PLygnWLnDqSQDFqYH/AkdNaPUQlE7RQBigNRGOFBuqjhbLsV/rZf+
|
||||||
|
/4K6wDHojM606lgLm57T4NUXnk53VIF3KO8+v8N11mCtPb+zBngfvVU14COC
|
||||||
|
HEDNdOK19TlR+tH25cftfUiF+OJsgMQysErGuFEtwLE6TNzpQIcnw7SbjxMr
|
||||||
|
EGacF9xCBKexB6MlR3GwJ2LBUJm3Lq/fvqImztoTlKDsrpk4JOH5FfYG+G2f
|
||||||
|
1zU73fVsCCElX4qA/49rRQf0RNfhjRjmHULP8hSvCXUEhfiBggEgxof/vKlC
|
||||||
|
qauHC55luuIeabju8HaXTjpz019cq+3IUgewX/ky0PhQXEW9SoODKabPY2yS
|
||||||
|
yUbHFm4=
|
||||||
|
=OCSx
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const privKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v4.10.10
|
||||||
|
Comment: https://openpgpjs.org
|
||||||
|
|
||||||
|
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
|
||||||
|
RscWbg==
|
||||||
|
=KJNy
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
|
`
|
||||||
|
|
||||||
|
// Dummy GPG keys, used only for testing
|
||||||
|
// nolint:gosec
|
||||||
|
const passPhrase = `MkDgjkrgdGxt`
|
||||||
|
|
||||||
|
func TestSignRPMRepo(t *testing.T) {
|
||||||
|
repoDir := t.TempDir()
|
||||||
|
workDir := t.TempDir()
|
||||||
|
pubKeyPath := filepath.Join(workDir, "pub.key")
|
||||||
|
err := os.WriteFile(pubKeyPath, []byte(pubKey), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
privKeyPath := filepath.Join(workDir, "priv.key")
|
||||||
|
err = os.WriteFile(privKeyPath, []byte(privKey), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
passPhrasePath := filepath.Join(workDir, "passphrase")
|
||||||
|
err = os.WriteFile(passPhrasePath, []byte(passPhrase), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = os.Mkdir(filepath.Join(repoDir, "repodata"), 0700)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = os.WriteFile(filepath.Join(repoDir, "repodata", "repomd.xml"), []byte("<xml></xml>"), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cfg := PublishConfig{
|
||||||
|
Config: config.Config{
|
||||||
|
GPGPrivateKey: privKeyPath,
|
||||||
|
GPGPublicKey: pubKeyPath,
|
||||||
|
GPGPassPath: passPhrasePath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = signRPMRepo(repoDir, cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user