Toolkit: Remove deprecated plugin:github-publish command (#67471)
This commit is contained in:
@@ -876,21 +876,11 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"packages/grafana-toolkit/src/cli/tasks/plugin.utils.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"packages/grafana-toolkit/src/cli/tasks/task.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"packages/grafana-toolkit/src/cli/utils/githubRelease.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||
],
|
||||
"packages/grafana-toolkit/src/cli/utils/useSpinner.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
parameters:
|
||||
ssh-fingerprint:
|
||||
type: string
|
||||
default: ${GITHUB_SSH_FINGERPRINT}
|
||||
|
||||
aliases:
|
||||
# Workflow filters
|
||||
- &filter-only-master
|
||||
branches:
|
||||
only: master
|
||||
- &filter-only-release
|
||||
branches:
|
||||
only: /^v[1-9]*[0-9]+\.[1-9]*[0-9]+\.x$/
|
||||
|
||||
workflows:
|
||||
plugin_workflow:
|
||||
jobs:
|
||||
- yarn_install
|
||||
- build_docs:
|
||||
requires:
|
||||
- yarn_install
|
||||
- build_frontend:
|
||||
requires:
|
||||
- yarn_install
|
||||
- build_backend:
|
||||
requires:
|
||||
- yarn_install
|
||||
- package:
|
||||
requires:
|
||||
- build_frontend
|
||||
- build_backend
|
||||
- build_docs
|
||||
- provisioning:
|
||||
requires:
|
||||
- build_frontend
|
||||
- build_backend
|
||||
- build_docs
|
||||
- e2e_canary:
|
||||
requires:
|
||||
- provisioning
|
||||
- package
|
||||
- report:
|
||||
requires:
|
||||
- package
|
||||
- e2e_canary
|
||||
- approve_release:
|
||||
type: approval
|
||||
requires:
|
||||
- report
|
||||
filters: *filter-only-release
|
||||
- publish_github_release:
|
||||
requires:
|
||||
- approve_release
|
||||
filters: *filter-only-release
|
||||
|
||||
executors:
|
||||
default_exec: # declares a reusable executor
|
||||
docker:
|
||||
- image: srclosson/grafana-plugin-ci-alpine:latest
|
||||
e2e_exec:
|
||||
docker:
|
||||
- image: srclosson/grafana-plugin-ci-e2e:latest
|
||||
|
||||
jobs:
|
||||
yarn_install:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
mkdir ci
|
||||
yarn bin grafana-toolkit ] || yarn install --immutable
|
||||
- save_cache:
|
||||
name: save yarn cache
|
||||
paths:
|
||||
- ~/project/.yarn
|
||||
key: build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- save_cache:
|
||||
name: save cypress cache
|
||||
paths:
|
||||
- ~/.cache/Cypress
|
||||
key: cypress-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
build_docs:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Build docs
|
||||
command: |
|
||||
yarn run grafana-toolkit plugin:ci-docs
|
||||
[ -d "dist" ] || circleci-agent step halt
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- dist
|
||||
|
||||
build_frontend:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Build and test frontend
|
||||
command: yarn run grafana-toolkit plugin:ci-build
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- dist
|
||||
|
||||
build_backend:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Exit if no backend
|
||||
command: |
|
||||
[ -f "Magefile.go" ] || circleci-agent step halt
|
||||
- run:
|
||||
name: Build backend
|
||||
command: mage -v buildAll
|
||||
- run:
|
||||
name: Test backend
|
||||
command: |
|
||||
mage -v lint
|
||||
mage -v coverage
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- dist
|
||||
|
||||
package:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Move results to ci folder
|
||||
command: yarn run grafana-toolkit plugin:ci-build --finish
|
||||
- run:
|
||||
name: Package distribution
|
||||
command: |
|
||||
yarn run grafana-toolkit plugin:ci-package
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ci/jobs/package
|
||||
- ci/packages
|
||||
- ci/dist
|
||||
- ci/grafana-test-env
|
||||
- store_artifacts:
|
||||
path: ci
|
||||
|
||||
provisioning:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- run:
|
||||
name: Continue if this plugin has a provisioning path
|
||||
command: |
|
||||
[ -z "${PROVISIONING_REPO_PATH}" ] && circleci-agent step halt
|
||||
- checkout
|
||||
- add_ssh_keys:
|
||||
fingerprints:
|
||||
- << pipeline.parameters.ssh-fingerprint >>
|
||||
- run:
|
||||
name: Checkout provisioning files
|
||||
command: git clone --depth 1 git@github.com:grafana/plugin-provisioning.git
|
||||
- run:
|
||||
name: Prepare task output dir
|
||||
command: |
|
||||
mkdir ci # Avoid error if not exists
|
||||
mkdir ci/jobs # Avoid error if not exists
|
||||
mkdir ci/jobs/provisioning
|
||||
mv plugin-provisioning/${PROVISIONING_REPO_PATH}/* ci/jobs/provisioning
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ci/jobs/provisioning
|
||||
|
||||
e2e_canary:
|
||||
executor: e2e_exec
|
||||
steps:
|
||||
- run:
|
||||
name: Continue if this plugin has a provisioning path
|
||||
command: |
|
||||
[ -z "${PROVISIONING_REPO_PATH}" ] && circleci-agent step halt
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- restore_cache:
|
||||
name: Restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- restore_cache:
|
||||
name: Restore cypress cache
|
||||
keys:
|
||||
- cypress-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Exit if no e2e tests configured
|
||||
command: |
|
||||
[ -d cypress ] || circleci-agent step halt
|
||||
- run:
|
||||
name: Setup Grafana (local install)
|
||||
command: |
|
||||
ginstall canary
|
||||
/opt/grafana/bin/grafana-server -config ci/grafana-test-env/custom.ini -homepath /opt/grafana &
|
||||
## To make sure grafana has started up
|
||||
while ! $(netstat -tulpn | grep 3000 >/dev/null 2>&1); do sleep 1; done
|
||||
/opt/grafana/bin/grafana-cli --version
|
||||
- run:
|
||||
name: Copy provisioning files
|
||||
command: cp -r ci/jobs/provisioning provisioning/
|
||||
- run:
|
||||
name: Run e2e tests
|
||||
command: |
|
||||
# If the tests fail, but GRAFANACI_STRICT_E2E=0, don't worry about it
|
||||
yarn run grafana-e2e run \
|
||||
|| (
|
||||
[ "$GRAFANACI_STRICT_E2E" -eq 0 ] && echo "Bypassing fail. ci-nostrict enabled"
|
||||
)
|
||||
- run:
|
||||
when: always
|
||||
name: Prepare task output dir
|
||||
command: |
|
||||
# TODO: probably move all of this to `@grafana/toolkit plugin:ci-test`
|
||||
mkdir ci/jobs/e2e_canary
|
||||
# only copy if they exist
|
||||
cp cypress/screenshots/*.* ci/jobs/e2e_canary 2>/dev/null || :
|
||||
cp cypress/videos/*.* ci/jobs/e2e_canary 2>/dev/null || :
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ci/jobs/e2e_canary
|
||||
- store_test_results:
|
||||
path: ci/jobs/e2e_canary
|
||||
- store_artifacts:
|
||||
path: ci/jobs/e2e_canary
|
||||
|
||||
report:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Toolkit report
|
||||
command: |
|
||||
yarn run grafana-toolkit plugin:ci-report
|
||||
- store_artifacts:
|
||||
path: ci
|
||||
|
||||
publish_github_release:
|
||||
executor: default_exec
|
||||
steps:
|
||||
- checkout
|
||||
- add_ssh_keys:
|
||||
fingerprints:
|
||||
- "dc:60:ab:c7:2d:8c:82:50:2a:2a:97:1a:c0:66:83:14"
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- restore_cache:
|
||||
name: restore yarn cache
|
||||
keys:
|
||||
- build-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: "Publish Release on GitHub"
|
||||
command: |
|
||||
yarn run grafana-toolkit plugin:github-publish
|
||||
@@ -57,7 +57,6 @@
|
||||
"@grafana/ui": "10.0.0-pre",
|
||||
"@jest/core": "27.5.1",
|
||||
"@types/eslint": "8.4.1",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/lodash": "4.14.181",
|
||||
"@types/node": "16.11.26",
|
||||
@@ -68,7 +67,6 @@
|
||||
"@types/tmp": "0.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "5.36.2",
|
||||
"@typescript-eslint/parser": "5.36.2",
|
||||
"axios": "^0.26.1",
|
||||
"babel-jest": "27.5.1",
|
||||
"babel-loader": "^8.2.5",
|
||||
"babel-plugin-angularjs-annotate": "0.10.0",
|
||||
@@ -84,7 +82,6 @@
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"execa": "^5.1.1",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"globby": "^11.0.4",
|
||||
"html-loader": "^3.1.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { program } from 'commander';
|
||||
import { nodeVersionCheckerTask } from './tasks/nodeVersionChecker';
|
||||
import { buildPackageTask } from './tasks/package.build';
|
||||
import { pluginBuildTask } from './tasks/plugin.build';
|
||||
import { getToolkitVersion, githubPublishTask } from './tasks/plugin.utils';
|
||||
import { getToolkitVersion } from './tasks/plugin.utils';
|
||||
import { templateTask } from './tasks/template';
|
||||
import { toolkitBuildTask } from './tasks/toolkit.build';
|
||||
import { execTask } from './utils/execTask';
|
||||
@@ -127,26 +127,6 @@ export const run = (includeInternalScripts = false) => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
program
|
||||
.command('plugin:github-publish')
|
||||
.option('--dryrun', 'Do a dry run only', false)
|
||||
.option('--verbose', 'Print verbose', false)
|
||||
.option('--commitHash <hashKey>', 'Specify the commit hash')
|
||||
.description('[Deprecated] Publish to github')
|
||||
.action(async (cmd) => {
|
||||
console.log(
|
||||
chalk.yellow.bold(`⚠️ This command is deprecated and will be removed . No further support will be provided. ⚠️`)
|
||||
);
|
||||
console.log(
|
||||
'We recommend using github actions directly for plugin releasing. You can find an example here: https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin/templates/github/ci/.github/workflows'
|
||||
);
|
||||
await execTask(githubPublishTask)({
|
||||
dryrun: cmd.dryrun,
|
||||
verbose: cmd.verbose,
|
||||
commitHash: cmd.commitHash,
|
||||
});
|
||||
});
|
||||
|
||||
program.on('command:*', () => {
|
||||
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,147 +1,4 @@
|
||||
import execa = require('execa');
|
||||
import { readFileSync } from 'fs';
|
||||
import path = require('path');
|
||||
|
||||
import { getPluginId } from '../../config/utils/getPluginId';
|
||||
import { getPluginJson } from '../../config/utils/pluginValidation';
|
||||
import { getCiFolder } from '../../plugins/env';
|
||||
import { GitHubRelease } from '../utils/githubRelease';
|
||||
import { useSpinner } from '../utils/useSpinner';
|
||||
|
||||
import { Task, TaskRunner } from './task';
|
||||
|
||||
interface Command extends Array<any> {}
|
||||
const DEFAULT_EMAIL_ADDRESS = 'eng@grafana.com';
|
||||
const DEFAULT_USERNAME = 'CircleCI Automation';
|
||||
|
||||
const releaseNotes = async (): Promise<string> => {
|
||||
const { stdout } = await execa(`awk 'BEGIN {FS="##"; RS="##"} FNR==3 {print "##" $1; exit}' CHANGELOG.md`, {
|
||||
shell: true,
|
||||
});
|
||||
return stdout;
|
||||
};
|
||||
|
||||
const checkoutBranch = async (branchName: string): Promise<Command> => {
|
||||
const currentBranch = await execa(`git rev-parse --abbrev-ref HEAD`, { shell: true });
|
||||
const branchesAvailable = await execa(
|
||||
`(git branch -a | grep "${branchName}$" | grep -v remote) || echo 'No release found'`,
|
||||
{ shell: true }
|
||||
);
|
||||
|
||||
if (currentBranch.stdout !== branchName) {
|
||||
console.log('available', branchesAvailable.stdout.trim());
|
||||
if (branchesAvailable.stdout.trim() === branchName) {
|
||||
return ['git', ['checkout', branchName]];
|
||||
} else {
|
||||
return ['git', ['checkout', '-b', branchName]];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const gitUrlParse = (url: string): { owner: string; name: string } => {
|
||||
let matchResult: RegExpMatchArray | null = [];
|
||||
|
||||
if (url.match(/^git@github.com/)) {
|
||||
// We have an ssh style url.
|
||||
matchResult = url.match(/^git@github.com:(.*?)\/(.*?)\.git/);
|
||||
}
|
||||
|
||||
if (url.match(/^https:\/\/github.com\//)) {
|
||||
// We have an https style url
|
||||
matchResult = url.match(/^https:\/\/github.com\/(.*?)\/(.*?)\/.git/);
|
||||
}
|
||||
|
||||
if (matchResult && matchResult.length > 2) {
|
||||
return {
|
||||
owner: matchResult[1],
|
||||
name: matchResult[2],
|
||||
};
|
||||
}
|
||||
|
||||
throw `Could not find a suitable git repository. Received [${url}]`;
|
||||
};
|
||||
|
||||
const prepareRelease = ({ dryrun, verbose }: any) =>
|
||||
useSpinner('Preparing release', async () => {
|
||||
const ciDir = getCiFolder();
|
||||
const distDir = path.resolve(ciDir, 'dist');
|
||||
const distContentDir = path.resolve(distDir, getPluginId());
|
||||
const pluginJsonFile = path.resolve(distContentDir, 'plugin.json');
|
||||
const pluginJson = getPluginJson(pluginJsonFile);
|
||||
|
||||
const githubPublishScript: Command = [
|
||||
['git', ['config', 'user.email', DEFAULT_EMAIL_ADDRESS]],
|
||||
['git', ['config', 'user.name', DEFAULT_USERNAME]],
|
||||
await checkoutBranch(`release-${pluginJson.info.version}`),
|
||||
['/bin/rm', ['-rf', 'dist'], { dryrun }],
|
||||
['mv', ['-v', distContentDir, 'dist']],
|
||||
['git', ['add', '--force', 'dist'], { dryrun }],
|
||||
['/bin/rm', ['-rf', 'src'], { enterprise: true }],
|
||||
['git', ['rm', '-rf', 'src'], { enterprise: true }],
|
||||
[
|
||||
'git',
|
||||
['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', ['push', '-f', 'origin', `release-${pluginJson.info.version}`], { dryrun }],
|
||||
['git', ['tag', '-f', `v${pluginJson.info.version}`]],
|
||||
['git', ['push', '-f', 'origin', `v${pluginJson.info.version}`]],
|
||||
];
|
||||
|
||||
for (let line of githubPublishScript) {
|
||||
const opts = line.length === 3 ? line[2] : {};
|
||||
const command = line[0];
|
||||
const args = line[1];
|
||||
|
||||
try {
|
||||
if (verbose) {
|
||||
console.log('executing >>', line);
|
||||
}
|
||||
|
||||
if (line.length > 0 && line[0].length > 0) {
|
||||
if (opts['dryrun']) {
|
||||
line[1].push('--dry-run');
|
||||
}
|
||||
|
||||
// Exit if the plugin is NOT an enterprise plugin
|
||||
if (pluginJson.enterprise && !opts['enterprise']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { stdout } = await execa(command, args);
|
||||
if (verbose) {
|
||||
console.log(stdout);
|
||||
}
|
||||
} else {
|
||||
if (verbose) {
|
||||
console.log('skipping empty line');
|
||||
}
|
||||
}
|
||||
} catch (ex: any) {
|
||||
const err: string = ex.message;
|
||||
if (opts['okOnError'] && Array.isArray(opts['okOnError'])) {
|
||||
let trueError = true;
|
||||
for (let regex of opts['okOnError']) {
|
||||
if (err.match(regex)) {
|
||||
trueError = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trueError) {
|
||||
// This is not an error
|
||||
continue;
|
||||
}
|
||||
}
|
||||
console.error(err);
|
||||
process.exit(-1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const getToolkitVersion = () => {
|
||||
const pkg = readFileSync(`${__dirname}/../../../package.json`, 'utf8');
|
||||
@@ -151,73 +8,3 @@ export const getToolkitVersion = () => {
|
||||
}
|
||||
return version;
|
||||
};
|
||||
interface GithubPublishReleaseOptions {
|
||||
commitHash?: string;
|
||||
githubToken: string;
|
||||
githubUser: string;
|
||||
gitRepoName: string;
|
||||
}
|
||||
|
||||
const createRelease = ({ commitHash, githubUser, githubToken, gitRepoName }: GithubPublishReleaseOptions) =>
|
||||
useSpinner('Creating release', async () => {
|
||||
const gitRelease = new GitHubRelease(githubToken, githubUser, gitRepoName, await releaseNotes(), commitHash);
|
||||
return gitRelease.release();
|
||||
});
|
||||
|
||||
export interface GithubPublishOptions {
|
||||
dryrun?: boolean;
|
||||
verbose?: boolean;
|
||||
commitHash?: string;
|
||||
dev?: boolean;
|
||||
}
|
||||
|
||||
const githubPublishRunner: TaskRunner<GithubPublishOptions> = async ({ dryrun, verbose, commitHash }) => {
|
||||
let repoUrl: string | undefined = process.env.DRONE_REPO_LINK || process.env.CIRCLE_REPOSITORY_URL;
|
||||
if (!repoUrl) {
|
||||
// Try and figure it out
|
||||
const repo = await execa('git', ['config', '--local', 'remote.origin.url']);
|
||||
if (repo && repo.stdout) {
|
||||
repoUrl = repo.stdout;
|
||||
} else {
|
||||
throw new Error(
|
||||
'The release plugin requires you specify the repository url as environment variable DRONE_REPO_LINK or ' +
|
||||
'CIRCLE_REPOSITORY_URL'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!process.env['GITHUB_ACCESS_TOKEN']) {
|
||||
// Try to use GITHUB_TOKEN, which may be set.
|
||||
if (process.env['GITHUB_TOKEN']) {
|
||||
process.env['GITHUB_ACCESS_TOKEN'] = process.env['GITHUB_TOKEN'];
|
||||
} else {
|
||||
throw new Error(
|
||||
`GitHub publish requires that you set the environment variable GITHUB_ACCESS_TOKEN to a valid github api token.
|
||||
See: https://github.com/settings/tokens for more details.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!process.env['GITHUB_USERNAME']) {
|
||||
// We can default this one
|
||||
process.env['GITHUB_USERNAME'] = DEFAULT_EMAIL_ADDRESS;
|
||||
}
|
||||
|
||||
const parsedUrl = gitUrlParse(repoUrl);
|
||||
const githubToken = process.env['GITHUB_ACCESS_TOKEN'];
|
||||
const githubUser = parsedUrl.owner;
|
||||
|
||||
await prepareRelease({
|
||||
dryrun,
|
||||
verbose,
|
||||
});
|
||||
|
||||
await createRelease({
|
||||
commitHash,
|
||||
githubUser,
|
||||
githubToken,
|
||||
gitRepoName: parsedUrl.name,
|
||||
});
|
||||
};
|
||||
|
||||
export const githubPublishTask = new Task<GithubPublishOptions>('GitHub Publish', githubPublishRunner);
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import GithubClient from './githubClient';
|
||||
|
||||
const fakeClient = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.GITHUB_USERNAME;
|
||||
delete process.env.GITHUB_ACCESS_TOKEN;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.GITHUB_USERNAME;
|
||||
delete process.env.GITHUB_ACCESS_TOKEN;
|
||||
});
|
||||
|
||||
describe('GithubClient', () => {
|
||||
it('should initialise a GithubClient', () => {
|
||||
const github = new GithubClient();
|
||||
const githubEnterprise = new GithubClient({ enterprise: true });
|
||||
expect(github).toBeInstanceOf(GithubClient);
|
||||
expect(githubEnterprise).toBeInstanceOf(GithubClient);
|
||||
});
|
||||
|
||||
describe('#client', () => {
|
||||
it('it should contain a grafana client', () => {
|
||||
// @ts-ignore
|
||||
const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient);
|
||||
|
||||
const github = new GithubClient();
|
||||
const client = github.client;
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({
|
||||
baseURL: 'https://api.github.com/repos/grafana/grafana',
|
||||
timeout: 10000,
|
||||
});
|
||||
expect(client).toEqual(fakeClient);
|
||||
});
|
||||
|
||||
it('it should contain a grafana enterprise client', () => {
|
||||
// @ts-ignore
|
||||
const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient);
|
||||
|
||||
const github = new GithubClient({ enterprise: true });
|
||||
const client = github.client;
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({
|
||||
baseURL: 'https://api.github.com/repos/grafana/grafana-enterprise',
|
||||
timeout: 10000,
|
||||
});
|
||||
expect(client).toEqual(fakeClient);
|
||||
});
|
||||
|
||||
describe('when the credentials are required', () => {
|
||||
it('should create the client when the credentials are defined', () => {
|
||||
const username = 'grafana';
|
||||
const token = 'averysecureaccesstoken';
|
||||
|
||||
process.env.GITHUB_USERNAME = username;
|
||||
process.env.GITHUB_ACCESS_TOKEN = token;
|
||||
|
||||
// @ts-ignore
|
||||
const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient);
|
||||
|
||||
const github = new GithubClient({ required: true });
|
||||
const client = github.client;
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({
|
||||
baseURL: 'https://api.github.com/repos/grafana/grafana',
|
||||
timeout: 10000,
|
||||
auth: { username, password: token },
|
||||
});
|
||||
|
||||
expect(client).toEqual(fakeClient);
|
||||
});
|
||||
|
||||
it('should create the enterprise client when the credentials are defined', () => {
|
||||
const username = 'grafana';
|
||||
const token = 'averysecureaccesstoken';
|
||||
|
||||
process.env.GITHUB_USERNAME = username;
|
||||
process.env.GITHUB_ACCESS_TOKEN = token;
|
||||
|
||||
// @ts-ignore
|
||||
const spy = jest.spyOn(GithubClient.prototype, 'createClient').mockImplementation(() => fakeClient);
|
||||
|
||||
const github = new GithubClient({ required: true, enterprise: true });
|
||||
const client = github.client;
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({
|
||||
baseURL: 'https://api.github.com/repos/grafana/grafana-enterprise',
|
||||
timeout: 10000,
|
||||
auth: { username, password: token },
|
||||
});
|
||||
|
||||
expect(client).toEqual(fakeClient);
|
||||
});
|
||||
|
||||
describe('when the credentials are not defined', () => {
|
||||
it('should throw an error', () => {
|
||||
expect(() => {
|
||||
// eslint-disable-next-line
|
||||
new GithubClient({ required: true });
|
||||
}).toThrow(/operation needs a GITHUB_USERNAME and GITHUB_ACCESS_TOKEN environment variables/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
|
||||
const grafanaURL = (owner: string, repo: string) => `https://api.github.com/repos/${owner}/${repo}`;
|
||||
const enterpriseURL = 'https://api.github.com/repos/grafana/grafana-enterprise';
|
||||
|
||||
// Encapsulates the creation of a client for the GitHub API
|
||||
//
|
||||
// Two key things:
|
||||
// 1. You can specify whenever you want the credentials to be required or not when imported.
|
||||
// 2. If the credentials are available as part of the environment, even if
|
||||
// they're not required - the library will use them. This allows us to overcome
|
||||
// any API rate limiting imposed without authentication.
|
||||
|
||||
interface GithubClientProps {
|
||||
required?: boolean;
|
||||
enterprise?: boolean;
|
||||
owner?: string;
|
||||
repo?: string;
|
||||
}
|
||||
|
||||
class GithubClient {
|
||||
client: AxiosInstance;
|
||||
|
||||
constructor({ required = false, enterprise = false, owner = 'grafana', repo = 'grafana' }: GithubClientProps = {}) {
|
||||
const username = process.env.GITHUB_USERNAME;
|
||||
const token = process.env.GITHUB_ACCESS_TOKEN;
|
||||
|
||||
const clientConfig: AxiosRequestConfig = {
|
||||
baseURL: enterprise ? enterpriseURL : grafanaURL(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);
|
||||
}
|
||||
|
||||
private createClient(clientConfig: AxiosRequestConfig) {
|
||||
return axios.create(clientConfig);
|
||||
}
|
||||
}
|
||||
|
||||
export default GithubClient;
|
||||
@@ -1,10 +0,0 @@
|
||||
import { GitHubRelease } from './githubRelease';
|
||||
|
||||
describe('GithubRelease', () => {
|
||||
it('should initialise a GithubRelease', () => {
|
||||
process.env.GITHUB_ACCESS_TOKEN = '12345';
|
||||
process.env.GITHUB_USERNAME = 'test@grafana.com';
|
||||
const github = new GitHubRelease('A token', 'A username', 'A repo', 'Some release notes');
|
||||
expect(github).toBeInstanceOf(GitHubRelease);
|
||||
});
|
||||
});
|
||||
@@ -1,113 +0,0 @@
|
||||
import { AxiosResponse } from 'axios';
|
||||
import fs = require('fs');
|
||||
import path = require('path');
|
||||
|
||||
import { getPluginId } from '../../config/utils/getPluginId';
|
||||
import { getPluginJson } from '../../config/utils/pluginValidation';
|
||||
import { getCiFolder } from '../../plugins/env';
|
||||
|
||||
import GithubClient from './githubClient';
|
||||
|
||||
const resolveContentType = (extension: string): string => {
|
||||
if (extension.startsWith('.')) {
|
||||
extension = extension.slice(1);
|
||||
}
|
||||
switch (extension) {
|
||||
case 'zip':
|
||||
return 'application/zip';
|
||||
case 'json':
|
||||
return 'application/json';
|
||||
case 'sha1':
|
||||
return 'text/plain';
|
||||
default:
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
};
|
||||
|
||||
class GitHubRelease {
|
||||
token: string;
|
||||
username: string;
|
||||
repository: string;
|
||||
releaseNotes: string;
|
||||
commitHash?: string;
|
||||
git: GithubClient;
|
||||
|
||||
constructor(token: string, username: string, repository: string, releaseNotes: string, commitHash?: string) {
|
||||
this.token = token;
|
||||
this.username = username;
|
||||
this.repository = repository;
|
||||
this.releaseNotes = releaseNotes;
|
||||
this.commitHash = commitHash;
|
||||
|
||||
this.git = new GithubClient({
|
||||
required: true,
|
||||
owner: username,
|
||||
repo: repository,
|
||||
});
|
||||
}
|
||||
|
||||
publishAssets(srcLocation: string, destUrl: string) {
|
||||
// Add the assets. Loop through files in the ci/dist folder and upload each asset.
|
||||
const files = fs.readdirSync(srcLocation);
|
||||
|
||||
return files.map(async (file: string) => {
|
||||
const fileStat = fs.statSync(`${srcLocation}/${file}`);
|
||||
const fileData = fs.readFileSync(`${srcLocation}/${file}`);
|
||||
return this.git.client.post(`${destUrl}?name=${file}`, fileData, {
|
||||
headers: {
|
||||
'Content-Type': resolveContentType(path.extname(file)),
|
||||
'Content-Length': fileStat.size,
|
||||
},
|
||||
maxContentLength: fileStat.size * 2 * 1024 * 1024,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async release() {
|
||||
const ciDir = getCiFolder();
|
||||
const distDir = path.resolve(ciDir, 'dist');
|
||||
const distContentDir = path.resolve(distDir, getPluginId());
|
||||
const pluginJsonFile = path.resolve(distContentDir, 'plugin.json');
|
||||
const pluginInfo = getPluginJson(pluginJsonFile).info;
|
||||
const PUBLISH_DIR = path.resolve(getCiFolder(), 'packages');
|
||||
const commitHash = this.commitHash || pluginInfo.build?.hash;
|
||||
|
||||
try {
|
||||
const latestRelease: AxiosResponse<any> = await this.git.client.get(`releases/tags/v${pluginInfo.version}`);
|
||||
|
||||
// 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}`);
|
||||
}
|
||||
} catch (reason: any) {
|
||||
if (reason.response.status !== 404) {
|
||||
// 404 just means no release found. Not an error. Anything else though, re throw the error
|
||||
throw reason;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 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,
|
||||
});
|
||||
|
||||
const publishPromises = this.publishAssets(
|
||||
PUBLISH_DIR,
|
||||
`https://uploads.github.com/repos/${this.username}/${this.repository}/releases/${newReleaseResponse.data.id}/assets`
|
||||
);
|
||||
await Promise.all(publishPromises);
|
||||
} catch (reason: any) {
|
||||
console.error(reason.data?.message ?? reason.response.data ?? reason);
|
||||
// Rethrow the error so that we can trigger a non-zero exit code to circle-ci
|
||||
throw reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { GitHubRelease };
|
||||
@@ -1,10 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export const getCiFolder = () => {
|
||||
const dir = path.resolve(process.cwd(), 'ci');
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
return dir;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './env';
|
||||
@@ -3399,7 +3399,6 @@ __metadata:
|
||||
"@grafana/ui": 10.0.0-pre
|
||||
"@jest/core": 27.5.1
|
||||
"@types/eslint": 8.4.1
|
||||
"@types/fs-extra": ^9.0.13
|
||||
"@types/jest": 27.4.1
|
||||
"@types/lodash": 4.14.181
|
||||
"@types/node": 16.11.26
|
||||
@@ -3410,7 +3409,6 @@ __metadata:
|
||||
"@types/tmp": 0.2.3
|
||||
"@typescript-eslint/eslint-plugin": 5.36.2
|
||||
"@typescript-eslint/parser": 5.36.2
|
||||
axios: ^0.26.1
|
||||
babel-jest: 27.5.1
|
||||
babel-loader: ^8.2.5
|
||||
babel-plugin-angularjs-annotate: 0.10.0
|
||||
@@ -3426,7 +3424,6 @@ __metadata:
|
||||
eslint-plugin-react-hooks: 4.6.0
|
||||
execa: ^5.1.1
|
||||
fork-ts-checker-webpack-plugin: ^7.2.1
|
||||
fs-extra: ^10.0.0
|
||||
globby: ^11.0.4
|
||||
html-loader: ^3.1.0
|
||||
html-webpack-plugin: ^5.5.0
|
||||
@@ -9495,15 +9492,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/fs-extra@npm:^9.0.13":
|
||||
version: 9.0.13
|
||||
resolution: "@types/fs-extra@npm:9.0.13"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: add79e212acd5ac76b97b9045834e03a7996aef60a814185e0459088fd290519a3c1620865d588fa36c4498bf614210d2a703af5cf80aa1dbc125db78f6edac3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/geojson@npm:*":
|
||||
version: 7946.0.8
|
||||
resolution: "@types/geojson@npm:7946.0.8"
|
||||
@@ -12471,15 +12459,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^0.26.1":
|
||||
version: 0.26.1
|
||||
resolution: "axios@npm:0.26.1"
|
||||
dependencies:
|
||||
follow-redirects: ^1.14.8
|
||||
checksum: d9eb58ff4bc0b36a04783fc9ff760e9245c829a5a1052ee7ca6013410d427036b1d10d04e7380c02f3508c5eaf3485b1ae67bd2adbfec3683704745c8d7a6e1a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^0.27.2":
|
||||
version: 0.27.2
|
||||
resolution: "axios@npm:0.27.2"
|
||||
@@ -19119,7 +19098,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.8":
|
||||
"follow-redirects@npm:^1.0.0":
|
||||
version: 1.14.8
|
||||
resolution: "follow-redirects@npm:1.14.8"
|
||||
peerDependenciesMeta:
|
||||
|
||||
Reference in New Issue
Block a user