mirror of
https://github.com/grafana/grafana.git
synced 2025-08-06 20:49:25 +08:00
build: automatically publish releases to grafana.com.
This commit is contained in:
@ -158,14 +158,18 @@ jobs:
|
|||||||
name: sha-sum packages
|
name: sha-sum packages
|
||||||
command: 'go run build.go sha-dist'
|
command: 'go run build.go sha-dist'
|
||||||
- run:
|
- run:
|
||||||
name: Build Grafana.com publisher
|
name: Build Grafana.com master publisher
|
||||||
command: 'go build -o scripts/publish scripts/build/publish.go'
|
command: 'go build -o scripts/publish scripts/build/publish.go'
|
||||||
|
- run:
|
||||||
|
name: Build Grafana.com release publisher
|
||||||
|
command: 'cd scripts/build/release_publisher && go build -o release_publisher .'
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
- dist/grafana*
|
- dist/grafana*
|
||||||
- scripts/*.sh
|
- scripts/*.sh
|
||||||
- scripts/publish
|
- scripts/publish
|
||||||
|
- scripts/build/release_publisher/release_publisher
|
||||||
|
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
@ -299,8 +303,8 @@ jobs:
|
|||||||
name: deploy to s3
|
name: deploy to s3
|
||||||
command: 'aws s3 sync ./dist s3://$BUCKET_NAME/release'
|
command: 'aws s3 sync ./dist s3://$BUCKET_NAME/release'
|
||||||
- run:
|
- run:
|
||||||
name: Trigger Windows build
|
name: Deploy to Grafana.com
|
||||||
command: './scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release'
|
command: './scripts/build/publish.sh'
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -73,3 +73,5 @@ debug.test
|
|||||||
|
|
||||||
/devenv/bulk-dashboards/*.json
|
/devenv/bulk-dashboards/*.json
|
||||||
/devenv/bulk_alerting_dashboards/*.json
|
/devenv/bulk_alerting_dashboards/*.json
|
||||||
|
|
||||||
|
/scripts/build/release_publisher/release_publisher
|
||||||
|
14
scripts/build/publish.sh
Executable file
14
scripts/build/publish.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#/bin/bash
|
||||||
|
|
||||||
|
# no relation to publish.go
|
||||||
|
|
||||||
|
# Right now we hack this in into the publish script.
|
||||||
|
# Eventually we might want to keep a list of all previous releases somewhere.
|
||||||
|
_releaseNoteUrl="https://community.grafana.com/t/release-notes-v5-3-x/10244"
|
||||||
|
_whatsNewUrl="http://docs.grafana.org/guides/whats-new-in-v5-3/"
|
||||||
|
|
||||||
|
./release_publisher/release_publisher \
|
||||||
|
--wn ${_whatsNewUrl} \
|
||||||
|
--rn ${_releaseNoteUrl} \
|
||||||
|
--version ${CIRCLE_TAG} \
|
||||||
|
--apikey ${GRAFANA_COM_API_KEY}
|
40
scripts/build/release_publisher/main.go
Normal file
40
scripts/build/release_publisher/main.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var baseUri string = "https://grafana.com/api"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var version string
|
||||||
|
var whatsNewUrl string
|
||||||
|
var releaseNotesUrl string
|
||||||
|
var dryRun bool
|
||||||
|
var apiKey string
|
||||||
|
|
||||||
|
flag.StringVar(&version, "version", "", "Grafana version (ex: --version v5.2.0-beta1)")
|
||||||
|
flag.StringVar(&whatsNewUrl, "wn", "", "What's new url (ex: --wn http://docs.grafana.org/guides/whats-new-in-v5-2/)")
|
||||||
|
flag.StringVar(&releaseNotesUrl, "rn", "", "Grafana version (ex: --rn https://community.grafana.com/t/release-notes-v5-2-x/7894)")
|
||||||
|
flag.StringVar(&apiKey, "apikey", "", "Grafana.com API key (ex: --apikey ABCDEF)")
|
||||||
|
flag.BoolVar(&dryRun, "dry-run", false, "--dry-run")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
fmt.Println("Usage: go run publisher.go main.go --version <v> --wn <what's new url> --rn <release notes url> --apikey <api key> --dry-run false")
|
||||||
|
fmt.Println("example: go run publisher.go main.go --version v5.2.0-beta2 --wn http://docs.grafana.org/guides/whats-new-in-v5-2/ --rn https://community.grafana.com/t/release-notes-v5-2-x/7894 --apikey ASDF123 --dry-run true")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
log.Println("Dry-run has been enabled.")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := publisher{apiKey: apiKey}
|
||||||
|
if err := p.doRelease(version, whatsNewUrl, releaseNotesUrl, dryRun); err != nil {
|
||||||
|
log.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
266
scripts/build/release_publisher/publisher.go
Normal file
266
scripts/build/release_publisher/publisher.go
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type publisher struct {
|
||||||
|
apiKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) doRelease(version string, whatsNewUrl string, releaseNotesUrl string, dryRun bool) error {
|
||||||
|
currentRelease, err := newRelease(version, whatsNewUrl, releaseNotesUrl, buildArtifactConfigurations, getHttpContents{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
relJson, err := json.Marshal(currentRelease)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println(string(relJson))
|
||||||
|
|
||||||
|
for _, b := range currentRelease.Builds {
|
||||||
|
artifactJson, err := json.Marshal(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println(string(artifactJson))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := p.postRelease(currentRelease); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) postRelease(r *release) error {
|
||||||
|
err := p.postRequest("/grafana/versions", r, fmt.Sprintf("Create Release %s", r.Version))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = p.postRequest("/grafana/versions/"+r.Version, r, fmt.Sprintf("Update Release %s", r.Version))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, b := range r.Builds {
|
||||||
|
err = p.postRequest(fmt.Sprintf("/grafana/versions/%s/packages", r.Version), b, fmt.Sprintf("Create Build %s %s", b.Os, b.Arch))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = p.postRequest(fmt.Sprintf("/grafana/versions/%s/packages/%s/%s", r.Version, b.Arch, b.Os), b, fmt.Sprintf("Update Build %s %s", b.Os, b.Arch))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseArhiveUrl = "https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana"
|
||||||
|
|
||||||
|
type buildArtifact struct {
|
||||||
|
os string
|
||||||
|
arch string
|
||||||
|
urlPostfix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t buildArtifact) getUrl(version string, isBeta bool) string {
|
||||||
|
prefix := "-"
|
||||||
|
rhelReleaseExtra := ""
|
||||||
|
|
||||||
|
if t.os == "deb" {
|
||||||
|
prefix = "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isBeta && t.os == "rhel" {
|
||||||
|
rhelReleaseExtra = "-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
url := strings.Join([]string{baseArhiveUrl, prefix, version, rhelReleaseExtra, t.urlPostfix}, "")
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildArtifactConfigurations = []buildArtifact{
|
||||||
|
{
|
||||||
|
os: "deb",
|
||||||
|
arch: "arm64",
|
||||||
|
urlPostfix: "_arm64.deb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "rhel",
|
||||||
|
arch: "arm64",
|
||||||
|
urlPostfix: ".aarch64.rpm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "arm64",
|
||||||
|
urlPostfix: ".linux-arm64.tar.gz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "deb",
|
||||||
|
arch: "armv7",
|
||||||
|
urlPostfix: "_armhf.deb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "rhel",
|
||||||
|
arch: "armv7",
|
||||||
|
urlPostfix: ".armhfp.rpm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "armv7",
|
||||||
|
urlPostfix: ".linux-armv7.tar.gz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "darwin",
|
||||||
|
arch: "amd64",
|
||||||
|
urlPostfix: ".darwin-amd64.tar.gz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "deb",
|
||||||
|
arch: "amd64",
|
||||||
|
urlPostfix: "_amd64.deb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "rhel",
|
||||||
|
arch: "amd64",
|
||||||
|
urlPostfix: ".x86_64.rpm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "linux",
|
||||||
|
arch: "amd64",
|
||||||
|
urlPostfix: ".linux-amd64.tar.gz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
os: "win",
|
||||||
|
arch: "amd64",
|
||||||
|
urlPostfix: ".windows-amd64.zip",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRelease(rawVersion string, whatsNewUrl string, releaseNotesUrl string, artifactConfigurations []buildArtifact, getter urlGetter) (*release, error) {
|
||||||
|
version := rawVersion[1:]
|
||||||
|
now := time.Now()
|
||||||
|
isBeta := strings.Contains(version, "beta")
|
||||||
|
|
||||||
|
builds := []build{}
|
||||||
|
for _, ba := range artifactConfigurations {
|
||||||
|
sha256, err := getter.getContents(fmt.Sprintf("%s.sha256", ba.getUrl(version, isBeta)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
builds = append(builds, newBuild(ba, version, isBeta, sha256))
|
||||||
|
}
|
||||||
|
|
||||||
|
r := release{
|
||||||
|
Version: version,
|
||||||
|
ReleaseDate: time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local),
|
||||||
|
Stable: !isBeta,
|
||||||
|
Beta: isBeta,
|
||||||
|
Nightly: false,
|
||||||
|
WhatsNewUrl: whatsNewUrl,
|
||||||
|
ReleaseNotesUrl: releaseNotesUrl,
|
||||||
|
Builds: builds,
|
||||||
|
}
|
||||||
|
return &r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuild(ba buildArtifact, version string, isBeta bool, sha256 string) build {
|
||||||
|
return build{
|
||||||
|
Os: ba.os,
|
||||||
|
Url: ba.getUrl(version, isBeta),
|
||||||
|
Sha256: sha256,
|
||||||
|
Arch: ba.arch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *publisher) postRequest(url string, obj interface{}, desc string) error {
|
||||||
|
jsonBytes, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, baseUri+url, bytes.NewReader(jsonBytes))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", "Bearer "+p.apiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode == http.StatusOK {
|
||||||
|
log.Printf("Action: %s \t OK", desc)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Body != nil {
|
||||||
|
defer res.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(string(body), "already exists") || strings.Contains(string(body), "Nothing to update") {
|
||||||
|
log.Printf("Action: %s \t Already exists", desc)
|
||||||
|
} else {
|
||||||
|
log.Printf("Action: %s \t Failed - Status: %v", desc, res.Status)
|
||||||
|
log.Printf("Resp: %s", body)
|
||||||
|
log.Fatalf("Quiting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type release struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
ReleaseDate time.Time `json:"releaseDate"`
|
||||||
|
Stable bool `json:"stable"`
|
||||||
|
Beta bool `json:"beta"`
|
||||||
|
Nightly bool `json:"nightly"`
|
||||||
|
WhatsNewUrl string `json:"whatsNewUrl"`
|
||||||
|
ReleaseNotesUrl string `json:"releaseNotesUrl"`
|
||||||
|
Builds []build `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type build struct {
|
||||||
|
Os string `json:"os"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Sha256 string `json:"sha256"`
|
||||||
|
Arch string `json:"arch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type urlGetter interface {
|
||||||
|
getContents(url string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type getHttpContents struct{}
|
||||||
|
|
||||||
|
func (getHttpContents) getContents(url string) (string, error) {
|
||||||
|
response, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
all, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(all), nil
|
||||||
|
}
|
43
scripts/build/release_publisher/publisher_test.go
Normal file
43
scripts/build/release_publisher/publisher_test.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewRelease(t *testing.T) {
|
||||||
|
versionIn := "v5.2.0-beta1"
|
||||||
|
expectedVersion := "5.2.0-beta1"
|
||||||
|
whatsNewUrl := "https://whatsnews.foo/"
|
||||||
|
relNotesUrl := "https://relnotes.foo/"
|
||||||
|
expectedArch := "amd64"
|
||||||
|
expectedOs := "linux"
|
||||||
|
buildArtifacts := []buildArtifact{{expectedOs, expectedArch, ".linux-amd64.tar.gz"}}
|
||||||
|
|
||||||
|
rel, _ := newRelease(versionIn, whatsNewUrl, relNotesUrl, buildArtifacts, mockHttpGetter{})
|
||||||
|
|
||||||
|
if !rel.Beta || rel.Stable {
|
||||||
|
t.Errorf("%s should have been tagged as beta (not stable), but wasn't .", versionIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rel.Version != expectedVersion {
|
||||||
|
t.Errorf("Expected version to be %s, but it was %s.", expectedVersion, rel.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedBuilds := len(buildArtifacts)
|
||||||
|
if len(rel.Builds) != expectedBuilds {
|
||||||
|
t.Errorf("Expected %v builds, but got %v.", expectedBuilds, len(rel.Builds))
|
||||||
|
}
|
||||||
|
|
||||||
|
build := rel.Builds[0]
|
||||||
|
if build.Arch != expectedArch {
|
||||||
|
t.Errorf("Expected arch to be %v, but it was %v", expectedArch, build.Arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
if build.Os != expectedOs {
|
||||||
|
t.Errorf("Expected arch to be %v, but it was %v", expectedOs, build.Os)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockHttpGetter struct{}
|
||||||
|
|
||||||
|
func (mockHttpGetter) getContents(url string) (string, error) {
|
||||||
|
return url, nil
|
||||||
|
}
|
Reference in New Issue
Block a user