Toolkit: simplify the plugin ci docker image (#23267)

* removing src dir on publish

* Moved from binary to native typescript
1. Moved to a native typescrpt github publish using the existing
github client.
2. Change dist.js to detect running in a linked environment.
Todo: Optimize docker image for build size.

* Optimized build of docker container
Much smaller. From 5.47 gb to 2.88

* Feedback from discussion with Ryan
- Added gget for getting grafana versions
- Added infrastructure for testing
- Uploaded new docker image

* Fixed typo... Not sure what happened there :)

* Added command to download canary

* small fix for displaying versions in help

* Removed --dev option
Should really just rename version to (ex: 1.2.0-dev)

* removing src dir on publish

* Moved from binary to native typescript
1. Moved to a native typescrpt github publish using the existing
github client.
2. Change dist.js to detect running in a linked environment.
Todo: Optimize docker image for build size.

* Optimized build of docker container
Much smaller. From 5.47 gb to 2.88

* Feedback from discussion with Ryan
- Added gget for getting grafana versions
- Added infrastructure for testing
- Uploaded new docker image

* Fixed typo... Not sure what happened there :)

* Added command to download canary

* small fix for displaying versions in help

* Removed --dev option
Should really just rename version to (ex: 1.2.0-dev)
pull/23272/head
Stephanie Closson 5 years ago committed by GitHub
parent 06ba5201bb
commit c5252f1b64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      packages/grafana-data/src/types/plugin.ts
  2. 21
      packages/grafana-toolkit/bin/grafana-toolkit.dist.js
  3. 53
      packages/grafana-toolkit/docker/grafana-plugin-ci/Dockerfile
  4. 14
      packages/grafana-toolkit/docker/grafana-plugin-ci/README.md
  5. 8
      packages/grafana-toolkit/docker/grafana-plugin-ci/build.sh
  6. 7
      packages/grafana-toolkit/docker/grafana-plugin-ci/common.sh
  7. 63
      packages/grafana-toolkit/docker/grafana-plugin-ci/install/gget
  8. 38
      packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-common.sh
  9. 3
      packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy-user.sh
  10. 43
      packages/grafana-toolkit/docker/grafana-plugin-ci/scripts/deploy.sh
  11. 8
      packages/grafana-toolkit/docker/grafana-plugin-ci/test/docker-compose.yml
  12. 4
      packages/grafana-toolkit/docker/grafana-plugin-ci/test/start.sh
  13. 4
      packages/grafana-toolkit/src/cli/index.ts
  14. 36
      packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts
  15. 7
      packages/grafana-toolkit/src/cli/utils/githubClient.ts
  16. 123
      packages/grafana-toolkit/src/cli/utils/githubRelease.ts

@ -34,6 +34,7 @@ export interface PluginMeta<T extends KeyValue = {}> {
enabled?: boolean;
defaultNavUrl?: string;
hasUpdate?: boolean;
enterprise?: boolean;
latestVersion?: string;
pinned?: boolean;
}

@ -1,5 +1,22 @@
#!/usr/bin/env node
// This bin is used for cli installed from npm
const fs = require('fs');
require('../src/cli/index.js').run();
entrypoint = () => {
const defaultEntryPoint = '../src/cli/index.js';
// We are running in dev mode. Don't use compiled binaries, rather use the dev entrypoint.
if (fs.existsSync(`${process.env['HOME']}/.config/yarn/link/@grafana/toolkit`)) {
console.log('Running in linked mode');
return `${__dirname}/grafana-toolkit.js`;
}
// We are using npx, and a relative path does not find index.js
if (!fs.existsSync(defaultEntryPoint) && fs.existsSync(`${__dirname}/../dist/src/cli/index.js`)) {
return `${__dirname}/../dist/src/cli/index.js`;
}
// The default entrypoint must exist, return it now.
return defaultEntryPoint;
};
require(entrypoint()).run();

@ -1,51 +1,6 @@
FROM circleci/node:12-browsers
USER root
WORKDIR /tmp
# Install Go
ADD https://dl.google.com/go/go1.14.linux-amd64.tar.gz /tmp
RUN echo 08df79b46b0adf498ea9f320a0f23d6ec59e9003660b4c9c1ce8e5e2c6f823ca go1.14.linux-amd64.tar.gz | sha256sum --check --status
RUN tar -C /usr/local -xf go1.14.linux-amd64.tar.gz
# Install golangci-lint
ADD https://github.com/golangci/golangci-lint/releases/download/v1.23.7/golangci-lint-1.23.7-linux-amd64.tar.gz /tmp
RUN echo 34df1794a2ea8e168b3c98eed3cc0f3e13ed4cba735e4e40ef141df5c41bc086 golangci-lint-1.23.7-linux-amd64.tar.gz | sha256sum --check --status
RUN tar xf golangci-lint-1.23.7-linux-amd64.tar.gz
RUN mv golangci-lint-1.23.7-linux-amd64/golangci-lint /usr/local/bin
RUN ln -s /usr/local/go/bin/go /usr/local/bin/go
RUN ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt
RUN chmod 755 /usr/local/bin/golangci-lint
# Install dependencies
RUN apt-get update -y && apt-get install -y adduser libfontconfig1 locate && /bin/rm -rf /var/lib/apt/lists/*
# Install code climate
ADD https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 /usr/local/bin/cc-test-reporter
RUN echo 38f2442892027f61a07f52c845818750261b2ba58bffb043a582495339d37c05 /usr/local/bin/cc-test-reporter | sha256sum --check --status
RUN chmod +x /usr/local/bin/cc-test-reporter
# Download, but don't install previous grafana releases
RUN mkdir -pv /usr/local/grafana/deb
ADD https://dl.grafana.com/oss/release/grafana_6.6.2_amd64.deb /usr/local/grafana/deb
ADD https://dl.grafana.com/oss/release/grafana_6.5.3_amd64.deb /usr/local/grafana/deb
ADD https://dl.grafana.com/oss/release/grafana_6.4.5_amd64.deb /usr/local/grafana/deb
ADD https://dl.grafana.com/oss/release/grafana_6.3.7_amd64.deb /usr/local/grafana/deb
# Perform user specific initialization
USER circleci
RUN mkdir -pv ${HOME}/plugin ${HOME}/go/bin ${HOME}/bin ${HOME}/src ${HOME}/tmp
# Install grafana release with yarn
RUN git clone https://github.com/grafana/grafana.git ${HOME}/src/grafana
WORKDIR /home/circleci/src/grafana
RUN git checkout tags/v$(curl -s https://raw.githubusercontent.com/grafana/grafana/master/latest.json | jq -r '.stable')
RUN yarn cache clean && yarn install --frozen-lockfile
# Install Mage
RUN git clone https://github.com/magefile/mage.git ${HOME}/src/mage
WORKDIR /home/circleci/src/mage
RUN go run bootstrap.go
ENV PATH /home/circleci/go/bin:/usr/local/go/bin:/home/circleci/.local/bin:/home/circleci/bin:${PATH}
WORKDIR /home/circleci/plugin
ADD scripts scripts
WORKDIR scripts
RUN ./deploy.sh
ADD install/gget /usr/local/bin/gget

@ -20,7 +20,7 @@ The home directory will be `/home/circleci`
All of the above directories are in the path, so there is no need to specify fully qualified paths.
## Grafana source
## Grafana
- Installed in `/home/circleci/src/grafana`
- `yarn install` has been run
@ -47,5 +47,15 @@ To test, your CircleCI config will need a run section with something similar to
# Building
To build, cd to `<srcroot>/packages/grafana-toolkit/docker/grafana-plugin-ci`
```
docker build .
./build.sh
```
# Developing/Testing
To test, you should have docker-compose installed.
```
cd test
./start.sh
```
You will be in /home/circleci/test with the buildscripts installed to the local directory.
Do your edits/run tests. When saving, your edits will be available in the container immediately.

@ -0,0 +1,8 @@
#!/bin/bash
source ./common.sh
output=$(docker build . | tee /dev/tty)
hash=$(echo "$output" | tail -1 | sed -ne "s/^Successfully built \(.*\)/\1/p")
docker tag "$hash" $DOCKER_IMAGE_NAME:latest
docker push $DOCKER_IMAGE_NAME:latest

@ -0,0 +1,7 @@
#!/bin/bash
##
## Common variable declarations
##
DOCKER_IMAGE_NAME="srclosson/grafana-plugin-ci"

@ -0,0 +1,63 @@
#!/bin/bash
##
# gget
# A script to get and install grafana versions
# for usage information see "show_help" below.
#
latest=$(curl -s 'https://raw.githubusercontent.com/grafana/grafana/master/latest.json' | jq -r '.stable')
canary=$(curl -s "https://grafana.com/api/grafana/versions" | jq ".items[0].version" | tr -d '"')
show_help() {
echo "Usage: gget <version>"
echo ""
echo "where <version> can be:"
echo " 1) A version from https://grafana.com/grafana/download (ex x.y.z)"
echo " 2) latest (currently $latest)"
echo " 3) canary (currently $canary)"
echo ""
echo " -h, --help: Display this help message"
echo ""
exit 0
}
opts=$(getopt -o h --long help -n 'gget' -- "$@")
[ $? -eq 0 ] || {
show_help
}
eval set -- "$opts"
while true; do
case "$1" in
-h | --help)
show_help
;;
--)
shift
break
;;
*)
break
;;
esac
shift
done
[ -z "$1" ] && show_help
# Make sure the script is being run as root
if [ $EUID -ne 0 ]; then
echo "This script must be run as root"
exit 1
fi
##
# MAIN
#
# Enough setup, let's actually do something
#
version=$1
[ "$version" == "latest" ] && version="$latest"
[ "$version" == "canary" ] && version="$canary"
wget "https://dl.grafana.com/oss/release/grafana_${version}_amd64.deb" -O "/tmp/grafana_${version}_amd64.deb"
dpkg -i "/tmp/grafana_${version}_amd64.deb" && /bin/rm -rfv "/tmp/grafana_${version}_amd64.deb"

@ -0,0 +1,38 @@
#!/bin/bash
##
# Script to deploy a docker image. Must return exit code 0
#
do_exit() {
message="$1"
exit_code="$2"
echo "$message"
exit $exit_code
}
##
# Get file, get's a file, validates the SHA
# @param filename
# @param expected sha value
# @returns 0 if successful, -1 of checksum validation failed.
#
get_file () {
[ -n "$1" ] && url=$1 || do_exit "url required" -1
[ -n "$2" ] && dest=$2 || do_exit "destination required" -2
sha=$3
file=$(basename $dest)
wget "$url" -O "$dest"
if [ -n "$sha" ]; then
echo "$sha $dest" | sha256sum --check --status || do_exit "Checksum validation failed for $file. Exiting" -1
fi
}
untar_file () {
[ -n "$1" ] && src=$1 || do_exit "src required" -1
[ -n "$2" ] && dest=$2 || dest="/usr/local"
tar -C "$dest" -xf "$src" && /bin/rm -rf "$src"
}

@ -0,0 +1,43 @@
#!/bin/bash
source "./deploy-common.sh"
# Install Go
filename="go1.14.linux-amd64.tar.gz"
get_file "https://dl.google.com/go/$filename" "/tmp/$filename" "08df79b46b0adf498ea9f320a0f23d6ec59e9003660b4c9c1ce8e5e2c6f823ca"
untar_file "/tmp/$filename"
# Install golangci-lint
filename="golangci-lint-1.23.7-linux-amd64.tar.gz"
get_file "https://github.com/golangci/golangci-lint/releases/download/v1.23.7/$filename" \
"/tmp/$filename" \
"34df1794a2ea8e168b3c98eed3cc0f3e13ed4cba735e4e40ef141df5c41bc086"
untar_file "/tmp/$filename"
chmod 755 /usr/local/bin/golangci-lint
ln -s /usr/local/golangci-lint-1.23.7-linux-amd64/golangci-lint /usr/local/bin/golangci-lint
ln -s /usr/local/go/bin/go /usr/local/bin/go
ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt
# Install dependencies
apt-get update -y && apt-get install -y adduser libfontconfig1 locate && /bin/rm -rf /var/lib/apt/lists/*
# Install code climate
get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64" \
"/usr/local/bin/cc-test-reporter" \
"38f2442892027f61a07f52c845818750261b2ba58bffb043a582495339d37c05"
chmod +x /usr/local/bin/cc-test-reporter
# Install Mage
mkdir -pv /tmp/mage $HOME/go/bin
git clone https://github.com/magefile/mage.git /tmp/mage
pushd /tmp/mage && go run bootstrap.go && popd
mv $HOME/go/bin/mage /usr/local/bin
# Cleanup after yourself
/bin/rm -rf /tmp/mage
/bin/rm -rf $HOME/go
# Perform user specific initialization
sudo -u circleci ./deploy-user.sh
# Get the size down
/bin/rm -rf /var/lib/apt/lists

@ -0,0 +1,8 @@
version: '3'
services:
citest:
image: "circleci/node:12-browsers"
user: root
volumes:
- ../scripts:/home/circleci/scripts
- ../install:/home/circleci/install

@ -0,0 +1,4 @@
#!/bin/bash
# Enter the docker container
docker-compose run citest bash -c "cd /home/circleci; exec bash --login -i"

@ -200,14 +200,12 @@ export const run = (includeInternalScripts = false) => {
.option('--dryrun', 'Do a dry run only', false)
.option('--verbose', 'Print verbose', false)
.option('--commitHash <hashKey>', 'Specify the commit hash')
.option('--recreate', 'Recreate the release if already present')
.description('Publish to github ... etc etc etc')
.description('Publish to github')
.action(async cmd => {
await execTask(githubPublishTask)({
dryrun: cmd.dryrun,
verbose: cmd.verbose,
commitHash: cmd.commitHash,
recreate: cmd.recreate,
});
});

@ -4,7 +4,6 @@ import { GitHubRelease } from '../utils/githubRelease';
import { getPluginId } from '../../config/utils/getPluginId';
import { getCiFolder } from '../../plugins/env';
import { useSpinner } from '../utils/useSpinner';
import path = require('path');
// @ts-ignore
@ -20,10 +19,11 @@ const releaseNotes = async (): Promise<string> => {
const checkoutBranch = async (branchName: string): Promise<Command> => {
const currentBranch = await execa.shell(`git rev-parse --abbrev-ref HEAD`);
const branchesAvailable = await execa.shell(
`(git branch -a | grep ${branchName} | grep -v remote) || echo 'No release found'`
`(git branch -a | grep "${branchName}$" | grep -v remote) || echo 'No release found'`
);
if (currentBranch.stdout !== branchName) {
console.log('available', branchesAvailable.stdout.trim());
if (branchesAvailable.stdout.trim() === branchName) {
return ['git', ['checkout', branchName]];
} else {
@ -61,27 +61,28 @@ const prepareRelease = useSpinner<any>('Preparing release', async ({ dryrun, ver
const distDir = path.resolve(ciDir, 'dist');
const distContentDir = path.resolve(distDir, getPluginId());
const pluginJsonFile = path.resolve(distContentDir, 'plugin.json');
const pluginVersion = getPluginJson(pluginJsonFile).info.version;
const pluginJson = getPluginJson(pluginJsonFile);
const GIT_EMAIL = 'eng@grafana.com';
const GIT_USERNAME = 'CircleCI Automation';
const githubPublishScript: Command = [
['git', ['config', 'user.email', GIT_EMAIL]],
['git', ['config', 'user.name', GIT_USERNAME]],
await checkoutBranch(`release-${pluginVersion}`),
['cp', ['-rf', distContentDir, 'dist'], { dryrun }],
await checkoutBranch(`release-${pluginJson.info.version}`),
['cp', ['-rf', distContentDir, 'dist']],
['git', ['add', '--force', distDir], { dryrun }],
['git', ['add', '--force', 'dist'], { dryrun }],
['/bin/rm', ['-rf', 'src'], { enterprise: true }],
[
'git',
['commit', '-m', `automated release ${pluginVersion} [skip ci]`],
['commit', '-m', `automated release ${pluginJson.info.version} [skip ci]`],
{
dryrun,
okOnError: [/nothing to commit/g, /nothing added to commit/g, /no changes added to commit/g],
},
],
['git', ['tag', '-f', pluginVersion]],
['git', ['push', '-f', 'origin', `release-${pluginVersion}`], { dryrun }],
['git', ['tag', '-f', pluginJson.info.version]],
['git', ['push', '-f', 'origin', `release-${pluginJson.info.version}`], { dryrun }],
];
for (let line of githubPublishScript) {
@ -98,6 +99,11 @@ const prepareRelease = useSpinner<any>('Preparing release', async ({ dryrun, ver
if (opts['dryrun']) {
line[1].push('--dry-run');
}
if (pluginJson.enterprise && !opts['enterprise']) {
continue;
}
const { stdout } = await execa(command, args);
if (verbose) {
console.log(stdout);
@ -129,19 +135,18 @@ const prepareRelease = useSpinner<any>('Preparing release', async ({ dryrun, ver
}
});
interface GithubPluglishReleaseOptions {
interface GithubPublishReleaseOptions {
commitHash?: string;
recreate?: boolean;
githubToken: string;
gitRepoOwner: string;
gitRepoName: string;
}
const createRelease = useSpinner<GithubPluglishReleaseOptions>(
const createRelease = useSpinner<GithubPublishReleaseOptions>(
'Creating release',
async ({ commitHash, recreate, githubToken, gitRepoName, gitRepoOwner }) => {
async ({ commitHash, githubToken, gitRepoName, gitRepoOwner }) => {
const gitRelease = new GitHubRelease(githubToken, gitRepoOwner, gitRepoName, await releaseNotes(), commitHash);
return gitRelease.release(recreate || false);
return gitRelease.release();
}
);
@ -149,10 +154,10 @@ export interface GithubPublishOptions {
dryrun?: boolean;
verbose?: boolean;
commitHash?: string;
recreate?: boolean;
dev?: boolean;
}
const githubPublishRunner: TaskRunner<GithubPublishOptions> = async ({ dryrun, verbose, commitHash, recreate }) => {
const githubPublishRunner: TaskRunner<GithubPublishOptions> = async ({ dryrun, verbose, commitHash }) => {
if (!process.env['CIRCLE_REPOSITORY_URL']) {
throw `The release plugin requires you specify the repository url as environment variable CIRCLE_REPOSITORY_URL`;
}
@ -172,7 +177,6 @@ const githubPublishRunner: TaskRunner<GithubPublishOptions> = async ({ dryrun, v
await createRelease({
commitHash,
recreate,
githubToken,
gitRepoOwner: parsedUrl.owner,
gitRepoName: parsedUrl.name,

@ -1,6 +1,6 @@
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
const grafanaURL = 'https://api.github.com/repos/grafana/grafana';
const grafanaURL = (repo: string) => `https://api.github.com/repos/grafana/${repo}`;
const enterpriseURL = 'https://api.github.com/repos/grafana/grafana-enterprise';
// Encapsulates the creation of a client for the Github API
@ -14,17 +14,18 @@ const enterpriseURL = 'https://api.github.com/repos/grafana/grafana-enterprise';
interface GithubClientProps {
required?: boolean;
enterprise?: boolean;
repo?: string;
}
class GithubClient {
client: AxiosInstance;
constructor({ required = false, enterprise = false }: GithubClientProps = {}) {
constructor({ required = false, enterprise = false, repo = 'grafana' }: GithubClientProps = {}) {
const username = process.env.GITHUB_USERNAME;
const token = process.env.GITHUB_ACCESS_TOKEN;
const clientConfig: AxiosRequestConfig = {
baseURL: enterprise ? enterpriseURL : grafanaURL,
baseURL: enterprise ? enterpriseURL : grafanaURL(repo),
timeout: 10000,
};

@ -2,19 +2,22 @@ import { getPluginId } from '../../config/utils/getPluginId';
import { getPluginJson } from '../../config/utils/pluginValidation';
import { getCiFolder } from '../../plugins/env';
import path = require('path');
import fs = require('fs');
// @ts-ignore
import execa = require('execa');
// import execa = require('execa');
import GithubClient from './githubClient';
import { AxiosResponse } from 'axios';
const ghrPlatform = (): string => {
switch (process.platform) {
case 'win32':
return 'windows';
case 'darwin':
return 'darwin';
case 'linux':
return 'linux';
const resolveContentType = (extension: string): string => {
switch (extension) {
case 'zip':
return 'application/zip';
case 'json':
return 'application/json';
case 'sha1':
return 'text/plain';
default:
return process.platform;
return 'application/octet-stream';
}
};
@ -24,6 +27,7 @@ class GitHubRelease {
repository: string;
releaseNotes: string;
commitHash?: string;
git: GithubClient;
constructor(token: string, username: string, repository: string, releaseNotes: string, commitHash?: string) {
this.token = token;
@ -31,36 +35,37 @@ class GitHubRelease {
this.repository = repository;
this.releaseNotes = releaseNotes;
this.commitHash = commitHash;
}
/**
* Get the ghr binary to perform the release
*/
private async getGhr(): Promise<string> {
const GHR_VERSION = '0.13.0';
const GHR_ARCH = process.arch === 'x64' ? 'amd64' : '386';
const GHR_PLATFORM = ghrPlatform();
const GHR_EXTENSION = process.platform === 'linux' ? 'tar.gz' : 'zip';
const outName = `./ghr.${GHR_EXTENSION}`;
const archiveName = `ghr_v${GHR_VERSION}_${GHR_PLATFORM}_${GHR_ARCH}`;
const exeName = process.platform === 'linux' ? 'ghr' : 'ghr.exe';
const exeNameFullPath = path.resolve(process.cwd(), archiveName, exeName);
const ghrUrl = `https://github.com/tcnksm/ghr/releases/download/v${GHR_VERSION}/${archiveName}.${GHR_EXTENSION}`;
await execa('wget', [ghrUrl, `--output-document=${outName}`]);
if (GHR_EXTENSION === 'tar.gz') {
await execa('tar', ['zxvf', outName]);
} else {
await execa('unzip', ['-p', outName]);
}
this.git = new GithubClient({
repo: repository,
});
}
if (process.platform === 'linux') {
await execa('chmod', ['755', exeNameFullPath]);
}
async publishAssets(srcLocation: string, destUrl: string) {
// Add the assets. Loop through files in the ci/dist folder and upload each asset.
fs.readdir(srcLocation, (err: NodeJS.ErrnoException | null, files: string[]) => {
if (err) {
throw err;
}
return exeNameFullPath;
files.forEach(async (file: string) => {
const fileStat = fs.statSync(`${srcLocation}/${file}`);
const fileData = fs.readFileSync(`${srcLocation}/${file}`);
try {
await this.git.client.post(`${destUrl}?name=${file}`, fileData, {
headers: {
'Content-Type': resolveContentType(path.extname(file)),
'Content-Length': fileStat.size,
},
});
} catch (reason) {
console.log('Could not post', reason);
}
});
});
}
async release(recreate: boolean) {
async release() {
const ciDir = getCiFolder();
const distDir = path.resolve(ciDir, 'dist');
const distContentDir = path.resolve(distDir, getPluginId());
@ -69,37 +74,31 @@ class GitHubRelease {
const PUBLISH_DIR = path.resolve(getCiFolder(), 'packages');
const commitHash = this.commitHash || pluginInfo.build?.hash;
// Get the ghr binary according to platform
const ghrExe = await this.getGhr();
try {
const latestRelease: AxiosResponse<any> = await this.git.client.get('releases/latest');
if (!commitHash) {
throw 'The release plugin was not able to locate a commithash for release. Either build using the ci, or specify the commit hash with --commitHash <value>';
}
// Re-release if the version is the same as an existing release
if (latestRelease.data.tag_name === `v${pluginInfo.version}`) {
await this.git.client.delete(`releases/${latestRelease.data.id}`);
}
const args = [
'-t',
this.token,
'-u',
this.username,
'-r',
this.repository, // should override --- may not be the same
'-c',
commitHash,
'-n',
`${this.repository}_v${pluginInfo.version}`,
'-b',
this.releaseNotes,
`v${pluginInfo.version}`,
PUBLISH_DIR,
];
// Now make the release
const newReleaseResponse = await this.git.client.post('releases', {
tag_name: `v${pluginInfo.version}`,
target_commitish: commitHash,
name: `v${pluginInfo.version}`,
body: this.releaseNotes,
draft: false,
prerelease: false,
});
if (recreate) {
args.splice(12, 0, '-recreate');
this.publishAssets(
PUBLISH_DIR,
`https://uploads.github.com/repos/${this.username}/${this.repository}/releases/${newReleaseResponse.data.id}/assets`
);
} catch (reason) {
console.error('error', reason);
}
const { stdout } = await execa(ghrExe, args);
console.log(stdout);
}
}

Loading…
Cancel
Save