From ec48fa0e5586e5b5ec5151e9809b6edbc7b9ccab Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Jun 2020 12:52:56 +0300 Subject: [PATCH] New release process --- .circleci/config.yml | 39 +++++++++++++- .circleci/github/githubClient.js | 31 +++++++++++ .circleci/github/publishRelease.js | 84 ++++++++++++++++++++++++++++++ .circleci/make-package.sh | 27 ++++++++++ .jshintrc | 3 +- Makefile | 2 +- package.json | 1 + yarn.lock | 21 ++++++++ 8 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 .circleci/github/githubClient.js create mode 100644 .circleci/github/publishRelease.js create mode 100755 .circleci/make-package.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f0b306..bb8e7e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -120,6 +120,43 @@ jobs: - store_artifacts: path: /tmp/circleci-test-results + make-github-release: + working_directory: ~/alexanderzobnin/grafana-zabbix + docker: + - image: circleci/golang:1.13-node + environment: + CI_GIT_USER: CircleCI + CI_GIT_EMAIL: ci@grafana.com + steps: + - checkout + - restore_cache: + keys: + - dependency-cache-npm-{{ checksum "yarn.lock" }} + - restore_cache: + keys: + - dependency-cache-go-{{ checksum "go.sum" }} + - run: + name: Install Dependencies + command: 'make install' + no_output_timeout: 15m + - save_cache: + key: dependency-cache-npm-{{ checksum "yarn.lock" }} + paths: + - ./node_modules + - save_cache: + key: dependency-cache-go-{{ checksum "go.sum" }} + paths: + - ./vendor + - /go/pkg/mod + - run: + name: Build plugin for all platforms + command: 'make dist' + no_output_timeout: 15m + - run: + name: Package plugin + command: './.circleci/make-package.sh' + no_output_timeout: 15m + make-release: working_directory: ~/alexanderzobnin/grafana-zabbix docker: @@ -233,7 +270,7 @@ workflows: filters: *filter-only-release - test: filters: *filter-only-release - - make-release: + - make-github-release: requires: - build - codespell diff --git a/.circleci/github/githubClient.js b/.circleci/github/githubClient.js new file mode 100644 index 0000000..438e4fb --- /dev/null +++ b/.circleci/github/githubClient.js @@ -0,0 +1,31 @@ +const axios = require('axios'); + +const githubURL = (owner, repo) => `https://api.github.com/repos/${owner}/${repo}`; + +class GithubClient { + constructor(owner, repo, required = false) { + const username = process.env.GITHUB_USERNAME; + const token = process.env.GITHUB_ACCESS_TOKEN; + + const clientConfig = { + baseURL: githubURL(owner, repo), + timeout: 10000, + }; + + if (required && !username && !token) { + throw new Error('operation needs a GITHUB_USERNAME and GITHUB_ACCESS_TOKEN environment variables'); + } + + if (username && token) { + clientConfig.auth = { username: username, password: token }; + } + + this.client = this.createClient(clientConfig); + } + + createClient(clientConfig) { + return axios.create(clientConfig); + } +} + +module.exports = GithubClient; diff --git a/.circleci/github/publishRelease.js b/.circleci/github/publishRelease.js new file mode 100644 index 0000000..ba3f910 --- /dev/null +++ b/.circleci/github/publishRelease.js @@ -0,0 +1,84 @@ +const GithubClient = require('./githubClient'); +const fs = require('fs'); +const path = require('path'); + +const GRAFANA_ZABBIX_OWNER = 'alexanderzobnin'; +const GRAFANA_ZABBIX_REPO = 'grafana-zabbix'; + +const github = new GithubClient(GRAFANA_ZABBIX_OWNER, GRAFANA_ZABBIX_REPO, true); + +async function main() { + const tag = process.env.CIRCLE_TAG; + const tagRegex = /v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)/; + if (!tagRegex.test(tag)) { + console.error(`Release tag should has format v1.2.3[-meta], got ${tag}`); + process.exit(1); + } + + const releaseVersion = tag.slice(1); + + let releaseId; + try { + const latestRelease = await github.client.get(`releases/tags/v${releaseVersion}`); + releaseId = latestRelease.data.id; + } catch (reason) { + if (reason.response.status !== 404) { + // 404 just means no release found. Not an error. Anything else though, re throw the error + throw reason; + } else { + console.error(`No release found`); + process.exit(1); + } + } + + try { + const updateReleaseResponse = await github.client.put(`releases/${releaseId}`, { + tag_name: `v${releaseVersion}`, + name: `${releaseVersion}`, + body: `Grafana-Zabbix ${releaseVersion}`, + draft: false, + prerelease: false, + }); + + await publishAssets( + `./grafana-zabbix-${releaseVersion}.zip`, + `https://uploads.github.com/repos/${GRAFANA_ZABBIX_OWNER}/${GRAFANA_ZABBIX_REPO}/releases/${updateReleaseResponse.data.id}/assets` + ); + } catch (reason) { + console.error(reason.data || reason.response || reason); + // Rethrow the error so that we can trigger a non-zero exit code to circle-ci + throw reason; + } +} + +async function publishAssets(fileName, destUrl) { + // Add the assets. Loop through files in the ci/dist folder and upload each asset. + + const fileStat = fs.statSync(`${fileName}`); + const fileData = fs.readFileSync(`${fileName}`); + return await github.client.post(`${destUrl}?name=${fileName}`, fileData, { + headers: { + 'Content-Type': resolveContentType(path.extname(fileName)), + 'Content-Length': fileStat.size, + }, + maxContentLength: fileStat.size * 2 * 1024 * 1024, + }); +} + +const resolveContentType = (extension) => { + if (extension.startsWith('.')) { + extension = extension.substr(1); + } + switch (extension) { + case 'zip': + return 'application/zip'; + case 'json': + return 'application/json'; + case 'sha1': + return 'text/plain'; + default: + return 'application/octet-stream'; + } +}; + +main(); diff --git a/.circleci/make-package.sh b/.circleci/make-package.sh new file mode 100755 index 0000000..20211e6 --- /dev/null +++ b/.circleci/make-package.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Exit script if you try to use an uninitialized variable. +set -o nounset +# Exit script if a statement returns a non-true return value. +set -o errexit +# Use the error status of the first failure, rather than that of the last item in a pipeline. +set -o pipefail + +RELEASE_VER=$(echo "$CIRCLE_TAG" | grep -Po "(?<=v)[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)") + +if [ -z "$RELEASE_VER" ]; then + echo "No release version provided" + exit 1 +fi +if [[ $RELEASE_VER =~ ^[0-9]+(\.[0-9]+){2}(-.+|[^-.]*) ]]; then + echo "Preparing release $RELEASE_VER" +else + echo "Release should has format 1.2.3[-meta], got $RELEASE_VER" + exit 1 +fi + +# Create zip package +PACKAGE_NAME="grafana-zabbix-${RELEASE_VER}.zip" +echo "packaging into $PACKAGE_NAME" +mv ./dist alexanderzobnin-zabbix-app +zip -r $PACKAGE_NAME ./alexanderzobnin-zabbix-app diff --git a/.jshintrc b/.jshintrc index 5c94f7b..3921a87 100644 --- a/.jshintrc +++ b/.jshintrc @@ -43,6 +43,7 @@ "module": true, "beforeEach": true, "inject": true, - "__dirname": true + "__dirname": true, + "process": true } } diff --git a/Makefile b/Makefile index 1cf1b75..ba78498 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ run-backend: bra run # Build plugin for all platforms (ready for distribution) -dist: install dist-frontend dist-backend +dist: dist-frontend dist-backend dist-frontend: yarn build dist-backend: dist-backend-linux dist-backend-darwin dist-backend-windows diff --git a/package.json b/package.json index 6a87c80..747f14c 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@types/react": "16.8.16", "@types/react-dom": "16.8.4", "@types/react-transition-group": "4.2.4", + "axios": "^0.19.2", "babel-jest": "24.8.0", "babel-loader": "8.0.6", "babel-plugin-angularjs-annotate": "0.10.0", diff --git a/yarn.lock b/yarn.lock index f2fd31e..d498bd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2075,6 +2075,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -3624,6 +3631,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4463,6 +4477,13 @@ fmin@0.0.2: tape "^4.5.1" uglify-js "^2.6.2" +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"