@grafana/sign-plugin 3.1.0-canary.698.89df8be.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,88 @@
1
+ # v3.1.0 (Thu Mar 13 2025)
2
+
3
+ #### 🚀 Enhancement
4
+
5
+ - Feature: Consistent CLI output [#1597](https://github.com/grafana/plugin-tools/pull/1597) ([@jackw](https://github.com/jackw))
6
+
7
+ #### Authors: 1
8
+
9
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
10
+
11
+ ---
12
+
13
+ # v3.0.8 (Thu Jan 09 2025)
14
+
15
+ #### 🐛 Bug Fix
16
+
17
+ - Sign Plugin: Remove console.log on failed version check [#1444](https://github.com/grafana/plugin-tools/pull/1444) ([@jackw](https://github.com/jackw))
18
+
19
+ #### Authors: 1
20
+
21
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
22
+
23
+ ---
24
+
25
+ # v3.0.5 (Wed Dec 11 2024)
26
+
27
+ #### 🐛 Bug Fix
28
+
29
+ - fix(deps): update dependency proxy-agent to v6.5.0 [#1387](https://github.com/grafana/plugin-tools/pull/1387) ([@renovate[bot]](https://github.com/renovate[bot]))
30
+
31
+ #### Authors: 1
32
+
33
+ - [@renovate[bot]](https://github.com/renovate[bot])
34
+
35
+ ---
36
+
37
+ # v3.0.4 (Wed Sep 25 2024)
38
+
39
+ #### 🐛 Bug Fix
40
+
41
+ - Feat: Adding provenance publish config [#1127](https://github.com/grafana/plugin-tools/pull/1127) ([@tolzhabayev](https://github.com/tolzhabayev))
42
+
43
+ #### Authors: 1
44
+
45
+ - Timur Olzhabayev ([@tolzhabayev](https://github.com/tolzhabayev))
46
+
47
+ ---
48
+
49
+ # v3.0.3 (Tue Jul 09 2024)
50
+
51
+ #### 🐛 Bug Fix
52
+
53
+ - Adjust license text [#994](https://github.com/grafana/plugin-tools/pull/994) ([@academo](https://github.com/academo))
54
+
55
+ #### Authors: 1
56
+
57
+ - Esteban Beltran ([@academo](https://github.com/academo))
58
+
59
+ ---
60
+
61
+ # v3.0.2 (Tue May 14 2024)
62
+
63
+ #### 🐛 Bug Fix
64
+
65
+ - Chore(deps): Bump proxy-agent from 6.3.1 to 6.4.0 [#792](https://github.com/grafana/plugin-tools/pull/792) ([@dependabot[bot]](https://github.com/dependabot[bot]))
66
+
67
+ #### Authors: 1
68
+
69
+ - [@dependabot[bot]](https://github.com/dependabot[bot])
70
+
71
+ ---
72
+
73
+ # v3.0.1 (Thu Jan 25 2024)
74
+
75
+ #### 🐛 Bug Fix
76
+
77
+ - Build: Migrate plugin-e2e to Vitest and use tsconfigs/base configs [#694](https://github.com/grafana/plugin-tools/pull/694) ([@jackw](https://github.com/jackw) [@sunker](https://github.com/sunker))
78
+
79
+ #### Authors: 2
80
+
81
+ - Erik Sundell ([@sunker](https://github.com/sunker))
82
+ - Jack Westbrook ([@jackw](https://github.com/jackw))
83
+
84
+ ---
85
+
1
86
  # v3.0.0 (Tue Jan 16 2024)
2
87
 
3
88
  #### 💥 Breaking Change
package/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright [yyyy] [name of copyright owner]
189
+ Copyright 2024 Grafana Labs
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -48,7 +48,7 @@ In your plugin directory, run the following to create a MANIFEST.txt file in the
48
48
  npx @grafana/sign-plugin@latest --rootUrls https://example.com/grafana
49
49
  ```
50
50
 
51
- Alterntives:
51
+ Alternatives:
52
52
 
53
53
  #### [`npx`](https://github.com/npm/npx)
54
54
 
package/dist/bin/run.js CHANGED
@@ -1,11 +1,26 @@
1
1
  #!/usr/bin/env node
2
- import minimist from 'minimist';
3
- import { sign, version } from '../commands/index.js';
4
- const args = process.argv.slice(2);
5
- const argv = minimist(args);
6
- const commands = {
7
- sign,
8
- version,
2
+ import "../chunk-5G4NZZCD.js";
3
+ import {
4
+ sign
5
+ } from "../chunk-MBEAHLAV.js";
6
+ import {
7
+ version
8
+ } from "../chunk-H643NG2Y.js";
9
+ import "../chunk-KUNFRRHT.js";
10
+ import "../chunk-RWF366QA.js";
11
+ import "../chunk-CCIR4QK3.js";
12
+ import "../chunk-JZ2A43R6.js";
13
+ import "../chunk-W6EFWLCZ.js";
14
+ import "../chunk-2Y3K42NH.js";
15
+ import "../chunk-3RG5ZIWI.js";
16
+
17
+ // src/bin/run.ts
18
+ import minimist from "minimist";
19
+ var args = process.argv.slice(2);
20
+ var argv = minimist(args);
21
+ var commands = {
22
+ sign,
23
+ version
9
24
  };
10
- const command = commands[argv._[0]] || commands.sign;
25
+ var command = commands[argv._[0]] || commands.sign;
11
26
  command(argv);
@@ -0,0 +1,41 @@
1
+ // src/utils/request.ts
2
+ import https from "node:https";
3
+ import { URL } from "node:url";
4
+ import { ProxyAgent } from "proxy-agent";
5
+ var agent = new ProxyAgent();
6
+ async function postData(urlString, data, headers) {
7
+ return new Promise((resolve, reject) => {
8
+ const url = new URL(urlString);
9
+ const postData2 = JSON.stringify(data);
10
+ const options = {
11
+ hostname: url.hostname,
12
+ port: url.port || 443,
13
+ path: url.pathname,
14
+ method: "POST",
15
+ headers: {
16
+ ...headers,
17
+ "Content-Type": "application/json"
18
+ },
19
+ agent
20
+ };
21
+ const req = https.request(options, (res) => {
22
+ const chunks = [];
23
+ res.on("data", (chunk) => chunks.push(chunk));
24
+ res.on("end", () => {
25
+ const results = Buffer.concat(chunks);
26
+ resolve({
27
+ data: results.toString(),
28
+ status: res.statusCode ?? 200
29
+ });
30
+ });
31
+ res.on("error", reject);
32
+ });
33
+ req.on("error", reject);
34
+ req.write(postData2);
35
+ req.end();
36
+ });
37
+ }
38
+
39
+ export {
40
+ postData
41
+ };
@@ -0,0 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
File without changes
@@ -0,0 +1,140 @@
1
+ import {
2
+ CURRENT_APP_VERSION
3
+ } from "./chunk-JZ2A43R6.js";
4
+
5
+ // ../../libs/output/src/index.mts
6
+ import chalk from "chalk";
7
+ import { EOL } from "os";
8
+ function isCI() {
9
+ return process.env.CI && process.env.CI !== "false" || // Drone CI plus others
10
+ process.env.GITHUB_ACTIONS === "true";
11
+ }
12
+ if (isCI()) {
13
+ chalk.level = 0;
14
+ }
15
+ var Output = class {
16
+ appName;
17
+ appVersion;
18
+ constructor(name, version) {
19
+ this.appName = name;
20
+ this.appVersion = version;
21
+ }
22
+ write(str) {
23
+ process.stdout.write(str);
24
+ }
25
+ get separator() {
26
+ let separator = "";
27
+ for (let i = 0; i < process.stdout.columns - 1; i++) {
28
+ separator += "\u2014";
29
+ }
30
+ return separator;
31
+ }
32
+ addHorizontalLine(color) {
33
+ const separator = chalk.dim[color](this.separator);
34
+ this.write(`${separator}${EOL}`);
35
+ }
36
+ addNewLine() {
37
+ this.write(EOL);
38
+ }
39
+ writeTitle(color, title, withPrefix = true) {
40
+ if (withPrefix) {
41
+ this.write(`${this.addPrefix(color, title)}${EOL}`);
42
+ } else {
43
+ this.write(`${title}${EOL}`);
44
+ }
45
+ }
46
+ getStatusIcon(taskStatus) {
47
+ switch (taskStatus) {
48
+ case "success":
49
+ return chalk.green("\u2713");
50
+ case "failure":
51
+ return chalk.red("\u2A2F");
52
+ case "skipped":
53
+ return chalk.yellow("\u2212");
54
+ }
55
+ }
56
+ addPrefix(color, text) {
57
+ const namePrefix = chalk.reset.inverse.bold[color](` ${this.appName} `);
58
+ if (!this.appVersion) {
59
+ return `${namePrefix} ${text}`;
60
+ }
61
+ const nameAndVersionPrefix = chalk.reset.inverse.bold[color](` ${this.appName}@${this.appVersion} `);
62
+ return `${nameAndVersionPrefix} ${text}`;
63
+ }
64
+ writeBody(body) {
65
+ if (!body) {
66
+ return;
67
+ }
68
+ this.addNewLine();
69
+ body.forEach((line) => {
70
+ this.write(`${line}${EOL}`);
71
+ });
72
+ }
73
+ error({
74
+ title,
75
+ body,
76
+ link,
77
+ withPrefix = true
78
+ }) {
79
+ this.addNewLine();
80
+ this.writeTitle("red", chalk.red.bold(title), withPrefix);
81
+ this.writeBody(body);
82
+ if (link) {
83
+ this.addNewLine();
84
+ this.write(`${chalk.gray("Learn more about this error: ")}
85
+ ${chalk.cyan(link)}`);
86
+ }
87
+ this.addNewLine();
88
+ }
89
+ warning({
90
+ title,
91
+ body,
92
+ link,
93
+ withPrefix = true
94
+ }) {
95
+ this.addNewLine();
96
+ this.writeTitle("yellow", chalk.yellow.bold(title), withPrefix);
97
+ this.writeBody(body);
98
+ if (link) {
99
+ this.addNewLine();
100
+ this.write(`${chalk.gray("Learn more about this warning: ")}
101
+ ${this.formatUrl(link)}`);
102
+ }
103
+ this.addNewLine();
104
+ }
105
+ success({ title, body, withPrefix = true }) {
106
+ this.addNewLine();
107
+ this.writeTitle("green", chalk.green.bold(title), withPrefix);
108
+ this.writeBody(body);
109
+ this.addNewLine();
110
+ }
111
+ log({ title, body, withPrefix = true }) {
112
+ this.addNewLine();
113
+ this.writeTitle("cyan", chalk.cyan.bold(title), withPrefix);
114
+ this.writeBody(body);
115
+ this.addNewLine();
116
+ }
117
+ bulletList(list) {
118
+ return list.map((item) => {
119
+ return ` \u2022 ${item}`;
120
+ });
121
+ }
122
+ formatCode(code) {
123
+ return chalk.italic.cyan(code);
124
+ }
125
+ formatUrl(url) {
126
+ return chalk.reset.blue.underline(url);
127
+ }
128
+ statusList(status, list) {
129
+ return list.map((item) => {
130
+ return ` ${this.getStatusIcon(status)} ${item}`;
131
+ });
132
+ }
133
+ };
134
+
135
+ // src/utils/utils.output.ts
136
+ var output = new Output("sign-plugin", CURRENT_APP_VERSION);
137
+
138
+ export {
139
+ output
140
+ };
@@ -0,0 +1,17 @@
1
+ import {
2
+ output
3
+ } from "./chunk-CCIR4QK3.js";
4
+
5
+ // src/commands/version.command.ts
6
+ var version = async () => {
7
+ try {
8
+ output.log({ title: "" });
9
+ } catch (error) {
10
+ console.error(error);
11
+ process.exit(1);
12
+ }
13
+ };
14
+
15
+ export {
16
+ version
17
+ };
@@ -0,0 +1,24 @@
1
+ // ../../libs/version/src/index.mts
2
+ import { findUpSync } from "find-up";
3
+ import { readFileSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ var __dirname = fileURLToPath(new URL(".", import.meta.url));
6
+ function getVersion() {
7
+ const packageJsonPath = findUpSync("package.json", { cwd: __dirname });
8
+ if (!packageJsonPath) {
9
+ throw `Could not find package.json`;
10
+ }
11
+ const pkg = readFileSync(packageJsonPath, "utf8");
12
+ const { version } = JSON.parse(pkg);
13
+ if (!version) {
14
+ throw `Could not find the version of create-plugin`;
15
+ }
16
+ return version;
17
+ }
18
+
19
+ // src/utils/utils.version.ts
20
+ var CURRENT_APP_VERSION = getVersion();
21
+
22
+ export {
23
+ CURRENT_APP_VERSION
24
+ };
@@ -0,0 +1,21 @@
1
+ // src/utils/getCreatePluginVersion.ts
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ var getCreatePluginVersion = () => {
5
+ try {
6
+ const currDir = process.cwd();
7
+ const crpcJSONPath = resolve(currDir, ".config", ".cprc.json");
8
+ if (!existsSync(crpcJSONPath)) {
9
+ return null;
10
+ }
11
+ const crpcJSON = readFileSync(crpcJSONPath, "utf-8");
12
+ const { version } = JSON.parse(crpcJSON);
13
+ return version;
14
+ } catch (err) {
15
+ return null;
16
+ }
17
+ };
18
+
19
+ export {
20
+ getCreatePluginVersion
21
+ };
@@ -0,0 +1,75 @@
1
+ import {
2
+ getCreatePluginVersion
3
+ } from "./chunk-KUNFRRHT.js";
4
+ import {
5
+ buildManifest,
6
+ saveManifest,
7
+ signManifest
8
+ } from "./chunk-RWF366QA.js";
9
+ import {
10
+ output
11
+ } from "./chunk-CCIR4QK3.js";
12
+ import {
13
+ CURRENT_APP_VERSION
14
+ } from "./chunk-JZ2A43R6.js";
15
+ import {
16
+ assertRootUrlIsValid
17
+ } from "./chunk-W6EFWLCZ.js";
18
+
19
+ // src/commands/sign.command.ts
20
+ import chalk from "chalk";
21
+ import { existsSync } from "node:fs";
22
+ import path from "node:path";
23
+ var sign = async (argv) => {
24
+ const distDir = argv.distDir ?? "dist";
25
+ const pluginDistDir = path.resolve(distDir);
26
+ const signatureType = argv.signatureType;
27
+ const rootUrls = argv.rootUrls?.split(",") ?? [];
28
+ if (!existsSync(pluginDistDir)) {
29
+ output.error({
30
+ title: "Plugin directory not found.",
31
+ body: [
32
+ `Directory ${chalk.bold(pluginDistDir)} not found.`,
33
+ "Make sure to build the plugin before attempting to sign it."
34
+ ]
35
+ });
36
+ process.exit(1);
37
+ }
38
+ try {
39
+ output.log({
40
+ title: "Signing plugin.",
41
+ body: [`Plugin found in ${pluginDistDir}`]
42
+ });
43
+ const manifest = await buildManifest(pluginDistDir);
44
+ if (signatureType) {
45
+ manifest.signatureType = signatureType;
46
+ }
47
+ if (rootUrls && rootUrls.length > 0) {
48
+ rootUrls.forEach(assertRootUrlIsValid);
49
+ manifest.rootUrls = rootUrls;
50
+ }
51
+ manifest.signPlugin = { version: CURRENT_APP_VERSION };
52
+ const createPluginVersion = getCreatePluginVersion();
53
+ if (createPluginVersion) {
54
+ manifest.createPlugin = { version: createPluginVersion };
55
+ }
56
+ const signedManifest = await signManifest(manifest);
57
+ saveManifest(pluginDistDir, signedManifest);
58
+ output.success({
59
+ title: `Plugin signed successsfully.`,
60
+ body: [`Signed manifest saved to ${pluginDistDir}.`]
61
+ });
62
+ } catch (err) {
63
+ if (err instanceof Error) {
64
+ output.error({
65
+ title: "Failed to sign plugin.",
66
+ body: [err.message]
67
+ });
68
+ }
69
+ process.exit(1);
70
+ }
71
+ };
72
+
73
+ export {
74
+ sign
75
+ };
@@ -0,0 +1,115 @@
1
+ import {
2
+ output
3
+ } from "./chunk-CCIR4QK3.js";
4
+ import {
5
+ postData
6
+ } from "./chunk-2Y3K42NH.js";
7
+
8
+ // src/utils/manifest.ts
9
+ import crypto from "crypto";
10
+ import { readFileSync, writeFileSync } from "node:fs";
11
+ import fs from "node:fs/promises";
12
+ import path from "node:path";
13
+ import chalk from "chalk";
14
+ var MANIFEST_FILE = "MANIFEST.txt";
15
+ async function* walk(dir, baseDir) {
16
+ for await (const d of await fs.opendir(dir)) {
17
+ const entry = path.join(dir, d.name);
18
+ if (d.isDirectory()) {
19
+ yield* await walk(entry, baseDir);
20
+ } else if (d.isFile()) {
21
+ yield path.relative(baseDir, entry);
22
+ } else if (d.isSymbolicLink()) {
23
+ const realPath = await fs.realpath(entry);
24
+ if (!realPath.startsWith(baseDir)) {
25
+ throw new Error(
26
+ `symbolic link ${path.relative(baseDir, entry)} targets a file outside of the base directory: ${baseDir}`
27
+ );
28
+ }
29
+ const stats = await fs.stat(realPath);
30
+ if (stats.isFile()) {
31
+ yield path.relative(baseDir, entry);
32
+ }
33
+ }
34
+ }
35
+ }
36
+ async function buildManifest(dir) {
37
+ const pluginJson = JSON.parse(readFileSync(path.join(dir, "plugin.json"), { encoding: "utf8" }));
38
+ const manifest = {
39
+ plugin: pluginJson.id,
40
+ version: pluginJson.info.version,
41
+ files: {}
42
+ };
43
+ for await (const filePath of await walk(dir, dir)) {
44
+ if (filePath === MANIFEST_FILE) {
45
+ continue;
46
+ }
47
+ const sanitisedFilePath = filePath.split(path.sep).join(path.posix.sep);
48
+ manifest.files[sanitisedFilePath] = crypto.createHash("sha256").update(readFileSync(path.join(dir, filePath))).digest("hex");
49
+ }
50
+ return manifest;
51
+ }
52
+ async function signManifest(manifest) {
53
+ const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY;
54
+ const GRAFANA_ACCESS_POLICY_TOKEN = process.env.GRAFANA_ACCESS_POLICY_TOKEN;
55
+ if (!GRAFANA_ACCESS_POLICY_TOKEN && !GRAFANA_API_KEY) {
56
+ output.error({
57
+ title: "Missing GRAFANA_ACCESS_POLICY_TOKEN.",
58
+ body: ["You must create a GRAFANA_ACCESS_POLICY_TOKEN env variable to sign plugins."],
59
+ link: "https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin#generate-an-access-policy-token"
60
+ });
61
+ process.exit(1);
62
+ }
63
+ if (GRAFANA_API_KEY) {
64
+ output.warning({
65
+ title: "Usage of GRAFANA_API_KEY is deprecated.",
66
+ body: ["Please migrate to using a GRAFANA_ACCESS_POLICY_TOKEN instead."],
67
+ link: "https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin"
68
+ });
69
+ }
70
+ const GRAFANA_COM_URL = process.env.GRAFANA_COM_URL || "https://grafana.com/api";
71
+ const url = GRAFANA_COM_URL + "/plugins/ci/sign";
72
+ const token = GRAFANA_ACCESS_POLICY_TOKEN ? GRAFANA_ACCESS_POLICY_TOKEN : GRAFANA_API_KEY;
73
+ try {
74
+ const info = await postData(url, manifest, {
75
+ Authorization: "Bearer " + token
76
+ });
77
+ if (info.status !== 200) {
78
+ const dataAsArray = Object.entries(JSON.parse(info.data)).map(([key, value]) => `${key}: ${value}`);
79
+ output.error({
80
+ title: "Error signing manifest.",
81
+ body: [
82
+ `Server responded with status code ${chalk.yellow(info.status)} along with:`,
83
+ ...output.bulletList(dataAsArray)
84
+ ]
85
+ });
86
+ process.exit(1);
87
+ }
88
+ return info.data;
89
+ } catch (err) {
90
+ const body = err.response?.data?.message ? [err.response.data.message] : [err.message];
91
+ output.error({
92
+ title: "Error signing manifest.",
93
+ body
94
+ });
95
+ process.exit(1);
96
+ }
97
+ }
98
+ function saveManifest(dir, signedManifest) {
99
+ try {
100
+ writeFileSync(path.join(dir, MANIFEST_FILE), signedManifest);
101
+ return true;
102
+ } catch (error) {
103
+ output.error({
104
+ title: "Error saving manifest",
105
+ body: [`Failed to write signed manifest to ${dir}.`]
106
+ });
107
+ process.exit(1);
108
+ }
109
+ }
110
+
111
+ export {
112
+ buildManifest,
113
+ signManifest,
114
+ saveManifest
115
+ };
@@ -0,0 +1,47 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-3RG5ZIWI.js";
4
+
5
+ // src/utils/pluginValidation.ts
6
+ var validatePluginJson = (pluginJson) => {
7
+ if (!pluginJson.id) {
8
+ throw new Error("Plugin id is missing in plugin.json");
9
+ }
10
+ if (!pluginJson.info) {
11
+ throw new Error("Plugin info node is missing in plugin.json");
12
+ }
13
+ if (!pluginJson.info.version) {
14
+ throw new Error("Plugin info.version is missing in plugin.json");
15
+ }
16
+ const types = ["panel", "datasource", "app"];
17
+ const type = pluginJson.type;
18
+ if (!types.includes(type)) {
19
+ throw new Error("Invalid plugin type in plugin.json: " + type);
20
+ }
21
+ if (!pluginJson.id.endsWith("-" + type)) {
22
+ throw new Error("[plugin.json] id should end with: -" + type);
23
+ }
24
+ };
25
+ var getPluginJson = (path) => {
26
+ let pluginJson;
27
+ try {
28
+ pluginJson = __require(path);
29
+ } catch (e) {
30
+ throw new Error("Unable to find: " + path);
31
+ }
32
+ validatePluginJson(pluginJson);
33
+ return pluginJson;
34
+ };
35
+ var assertRootUrlIsValid = (rootUrl) => {
36
+ try {
37
+ new URL(rootUrl);
38
+ } catch (err) {
39
+ throw new Error(`${rootUrl} is not a valid URL`);
40
+ }
41
+ };
42
+
43
+ export {
44
+ validatePluginJson,
45
+ getPluginJson,
46
+ assertRootUrlIsValid
47
+ };
@@ -1,2 +1,18 @@
1
- export * from './sign.command.js';
2
- export * from './version.command.js';
1
+ import "../chunk-5G4NZZCD.js";
2
+ import {
3
+ sign
4
+ } from "../chunk-MBEAHLAV.js";
5
+ import {
6
+ version
7
+ } from "../chunk-H643NG2Y.js";
8
+ import "../chunk-KUNFRRHT.js";
9
+ import "../chunk-RWF366QA.js";
10
+ import "../chunk-CCIR4QK3.js";
11
+ import "../chunk-JZ2A43R6.js";
12
+ import "../chunk-W6EFWLCZ.js";
13
+ import "../chunk-2Y3K42NH.js";
14
+ import "../chunk-3RG5ZIWI.js";
15
+ export {
16
+ sign,
17
+ version
18
+ };
@@ -1,35 +1,13 @@
1
- import { existsSync } from 'node:fs';
2
- import path from 'node:path';
3
- import { getVersion } from '../utils/getVersion.js';
4
- import { buildManifest, saveManifest, signManifest } from '../utils/manifest.js';
5
- import { assertRootUrlIsValid } from '../utils/pluginValidation.js';
6
- export const sign = async (argv) => {
7
- const distDir = argv.distDir ?? 'dist';
8
- const pluginDistDir = path.resolve(distDir);
9
- const signatureType = argv.signatureType;
10
- const rootUrls = argv.rootUrls?.split(',') ?? [];
11
- if (!existsSync(pluginDistDir)) {
12
- throw new Error(`Plugin \`${distDir}\` directory is missing. Did you build the plugin before attempting to sign?`);
13
- }
14
- try {
15
- console.log('Building manifest...');
16
- const manifest = await buildManifest(pluginDistDir);
17
- console.log('Signing manifest...');
18
- if (signatureType) {
19
- manifest.signatureType = signatureType;
20
- }
21
- if (rootUrls && rootUrls.length > 0) {
22
- rootUrls.forEach(assertRootUrlIsValid);
23
- manifest.rootUrls = rootUrls;
24
- }
25
- manifest.signPlugin = { version: getVersion() };
26
- const signedManifest = await signManifest(manifest);
27
- console.log('Saving signed manifest...');
28
- saveManifest(pluginDistDir, signedManifest);
29
- console.log('Signed successfully');
30
- }
31
- catch (err) {
32
- console.warn(err);
33
- process.exitCode = 1;
34
- }
1
+ import {
2
+ sign
3
+ } from "../chunk-MBEAHLAV.js";
4
+ import "../chunk-KUNFRRHT.js";
5
+ import "../chunk-RWF366QA.js";
6
+ import "../chunk-CCIR4QK3.js";
7
+ import "../chunk-JZ2A43R6.js";
8
+ import "../chunk-W6EFWLCZ.js";
9
+ import "../chunk-2Y3K42NH.js";
10
+ import "../chunk-3RG5ZIWI.js";
11
+ export {
12
+ sign
35
13
  };
@@ -1,10 +1,9 @@
1
- import { getVersion } from '../utils/getVersion.js';
2
- export const version = async () => {
3
- try {
4
- console.log(getVersion());
5
- }
6
- catch (error) {
7
- console.error(error);
8
- process.exit(1);
9
- }
1
+ import {
2
+ version
3
+ } from "../chunk-H643NG2Y.js";
4
+ import "../chunk-CCIR4QK3.js";
5
+ import "../chunk-JZ2A43R6.js";
6
+ import "../chunk-3RG5ZIWI.js";
7
+ export {
8
+ version
10
9
  };
@@ -0,0 +1,7 @@
1
+ import {
2
+ getCreatePluginVersion
3
+ } from "../chunk-KUNFRRHT.js";
4
+ import "../chunk-3RG5ZIWI.js";
5
+ export {
6
+ getCreatePluginVersion
7
+ };
@@ -1,85 +1,14 @@
1
- import crypto from 'crypto';
2
- import { readFileSync, writeFileSync } from 'node:fs';
3
- import fs from 'node:fs/promises';
4
- import path from 'node:path';
5
- import { postData } from './request.js';
6
- const MANIFEST_FILE = 'MANIFEST.txt';
7
- async function* walk(dir, baseDir) {
8
- for await (const d of await fs.opendir(dir)) {
9
- const entry = path.join(dir, d.name);
10
- if (d.isDirectory()) {
11
- yield* await walk(entry, baseDir);
12
- }
13
- else if (d.isFile()) {
14
- yield path.relative(baseDir, entry);
15
- }
16
- else if (d.isSymbolicLink()) {
17
- const realPath = await fs.realpath(entry);
18
- if (!realPath.startsWith(baseDir)) {
19
- throw new Error(`symbolic link ${path.relative(baseDir, entry)} targets a file outside of the base directory: ${baseDir}`);
20
- }
21
- const stats = await fs.stat(realPath);
22
- if (stats.isFile()) {
23
- yield path.relative(baseDir, entry);
24
- }
25
- }
26
- }
27
- }
28
- export async function buildManifest(dir) {
29
- const pluginJson = JSON.parse(readFileSync(path.join(dir, 'plugin.json'), { encoding: 'utf8' }));
30
- const manifest = {
31
- plugin: pluginJson.id,
32
- version: pluginJson.info.version,
33
- files: {},
34
- };
35
- for await (const filePath of await walk(dir, dir)) {
36
- if (filePath === MANIFEST_FILE) {
37
- continue;
38
- }
39
- const sanitisedFilePath = filePath.split(path.sep).join(path.posix.sep);
40
- manifest.files[sanitisedFilePath] = crypto
41
- .createHash('sha256')
42
- .update(readFileSync(path.join(dir, filePath)))
43
- .digest('hex');
44
- }
45
- return manifest;
46
- }
47
- export async function signManifest(manifest) {
48
- const GRAFANA_API_KEY = process.env.GRAFANA_API_KEY;
49
- const GRAFANA_ACCESS_POLICY_TOKEN = process.env.GRAFANA_ACCESS_POLICY_TOKEN;
50
- if (!GRAFANA_ACCESS_POLICY_TOKEN && !GRAFANA_API_KEY) {
51
- throw new Error('You must create a GRAFANA_ACCESS_POLICY_TOKEN env variable to sign plugins. Please see: https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin#generate-an-access-policy-token for instructions.');
52
- }
53
- if (GRAFANA_API_KEY) {
54
- console.warn(`\x1b[33m%s\x1b[0m`, 'The usage of GRAFANA_API_KEY is deprecated, please consider using GRAFANA_ACCESS_POLICY_TOKEN instead. For more info visit https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin');
55
- }
56
- const GRAFANA_COM_URL = process.env.GRAFANA_COM_URL || 'https://grafana.com/api';
57
- const url = GRAFANA_COM_URL + '/plugins/ci/sign';
58
- const token = GRAFANA_ACCESS_POLICY_TOKEN ? GRAFANA_ACCESS_POLICY_TOKEN : GRAFANA_API_KEY;
59
- try {
60
- const info = await postData(url, manifest, {
61
- Authorization: 'Bearer ' + token,
62
- });
63
- if (info.status !== 200) {
64
- console.error('Error: ', info);
65
- throw new Error('Error signing manifest');
66
- }
67
- return info.data;
68
- }
69
- catch (err) {
70
- if (err.response?.data?.message) {
71
- throw new Error('Error signing manifest: ' + err.response.data.message);
72
- }
73
- throw new Error('Error signing manifest: ' + err.message);
74
- }
75
- }
76
- export function saveManifest(dir, signedManifest) {
77
- try {
78
- writeFileSync(path.join(dir, MANIFEST_FILE), signedManifest);
79
- return true;
80
- }
81
- catch (error) {
82
- console.error(error);
83
- throw new Error('Error saving manifest');
84
- }
85
- }
1
+ import {
2
+ buildManifest,
3
+ saveManifest,
4
+ signManifest
5
+ } from "../chunk-RWF366QA.js";
6
+ import "../chunk-CCIR4QK3.js";
7
+ import "../chunk-JZ2A43R6.js";
8
+ import "../chunk-2Y3K42NH.js";
9
+ import "../chunk-3RG5ZIWI.js";
10
+ export {
11
+ buildManifest,
12
+ saveManifest,
13
+ signManifest
14
+ };
@@ -1,38 +1,11 @@
1
- export const validatePluginJson = (pluginJson) => {
2
- if (!pluginJson.id) {
3
- throw new Error('Plugin id is missing in plugin.json');
4
- }
5
- if (!pluginJson.info) {
6
- throw new Error('Plugin info node is missing in plugin.json');
7
- }
8
- if (!pluginJson.info.version) {
9
- throw new Error('Plugin info.version is missing in plugin.json');
10
- }
11
- const types = ['panel', 'datasource', 'app'];
12
- const type = pluginJson.type;
13
- if (!types.includes(type)) {
14
- throw new Error('Invalid plugin type in plugin.json: ' + type);
15
- }
16
- if (!pluginJson.id.endsWith('-' + type)) {
17
- throw new Error('[plugin.json] id should end with: -' + type);
18
- }
19
- };
20
- export const getPluginJson = (path) => {
21
- let pluginJson;
22
- try {
23
- pluginJson = require(path);
24
- }
25
- catch (e) {
26
- throw new Error('Unable to find: ' + path);
27
- }
28
- validatePluginJson(pluginJson);
29
- return pluginJson;
30
- };
31
- export const assertRootUrlIsValid = (rootUrl) => {
32
- try {
33
- new URL(rootUrl);
34
- }
35
- catch (err) {
36
- throw new Error(`${rootUrl} is not a valid URL`);
37
- }
1
+ import {
2
+ assertRootUrlIsValid,
3
+ getPluginJson,
4
+ validatePluginJson
5
+ } from "../chunk-W6EFWLCZ.js";
6
+ import "../chunk-3RG5ZIWI.js";
7
+ export {
8
+ assertRootUrlIsValid,
9
+ getPluginJson,
10
+ validatePluginJson
38
11
  };
@@ -1,36 +1,7 @@
1
- import https from 'node:https';
2
- import { URL } from 'node:url';
3
- import { ProxyAgent } from 'proxy-agent';
4
- const agent = new ProxyAgent();
5
- export async function postData(urlString, data, headers) {
6
- return new Promise((resolve, reject) => {
7
- const url = new URL(urlString);
8
- const postData = JSON.stringify(data);
9
- const options = {
10
- hostname: url.hostname,
11
- port: url.port || 443,
12
- path: url.pathname,
13
- method: 'POST',
14
- headers: {
15
- ...headers,
16
- 'Content-Type': 'application/json',
17
- },
18
- agent,
19
- };
20
- const req = https.request(options, (res) => {
21
- const chunks = [];
22
- res.on('data', (chunk) => chunks.push(chunk));
23
- res.on('end', () => {
24
- const results = Buffer.concat(chunks);
25
- resolve({
26
- data: results.toString(),
27
- status: res.statusCode ?? 200,
28
- });
29
- });
30
- res.on('error', reject);
31
- });
32
- req.on('error', reject);
33
- req.write(postData);
34
- req.end();
35
- });
36
- }
1
+ import {
2
+ postData
3
+ } from "../chunk-2Y3K42NH.js";
4
+ import "../chunk-3RG5ZIWI.js";
5
+ export {
6
+ postData
7
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ output
3
+ } from "../chunk-CCIR4QK3.js";
4
+ import "../chunk-JZ2A43R6.js";
5
+ import "../chunk-3RG5ZIWI.js";
6
+ export {
7
+ output
8
+ };
@@ -0,0 +1,7 @@
1
+ import {
2
+ CURRENT_APP_VERSION
3
+ } from "../chunk-JZ2A43R6.js";
4
+ import "../chunk-3RG5ZIWI.js";
5
+ export {
6
+ CURRENT_APP_VERSION
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grafana/sign-plugin",
3
- "version": "3.1.0-canary.698.89df8be.0",
3
+ "version": "3.1.0",
4
4
  "repository": {
5
5
  "directory": "packages/sign-plugin",
6
6
  "url": "https://github.com/grafana/plugin-tools"
@@ -17,35 +17,29 @@
17
17
  },
18
18
  "publishConfig": {
19
19
  "registry": "https://registry.npmjs.org/",
20
- "access": "public"
20
+ "access": "public",
21
+ "provenance": true
21
22
  },
22
23
  "scripts": {
23
24
  "clean": "rm -rf ./dist",
24
- "build": "npm run clean && tsc && chmod +x ./dist/bin/run.js",
25
- "dev": "nodemon --exec 'tsc'",
26
- "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./src",
25
+ "build": "tsup",
26
+ "dev": "tsup --watch ./src --watch '../../libs'",
27
+ "lint": "eslint --cache ./src",
27
28
  "lint:fix": "npm run lint -- --fix",
28
29
  "test": "vitest",
29
30
  "typecheck": "tsc --noEmit"
30
31
  },
31
32
  "dependencies": {
33
+ "chalk": "^5.3.0",
34
+ "find-up": "^7.0.0",
32
35
  "minimist": "^1.2.2",
33
- "proxy-agent": "6.3.1"
36
+ "proxy-agent": "6.5.0"
34
37
  },
35
38
  "devDependencies": {
36
- "@types/minimist": "^1.2.2"
37
- },
38
- "nodemonConfig": {
39
- "watch": [
40
- "src/**/*"
41
- ],
42
- "ext": "*",
43
- "events": {
44
- "start": "cls || clear"
45
- }
39
+ "@types/minimist": "^1.2.5"
46
40
  },
47
41
  "engines": {
48
42
  "node": ">=20"
49
43
  },
50
- "gitHead": "89df8be7ce8ffc3a78214ff9c00e1a8a55082187"
44
+ "gitHead": "b622bcaa222af1bf67aa602eb2accecad44f1afc"
51
45
  }
@@ -1,9 +1,12 @@
1
+ import chalk from 'chalk';
1
2
  import minimist from 'minimist';
2
3
  import { existsSync } from 'node:fs';
3
4
  import path from 'node:path';
4
- import { getVersion } from '../utils/getVersion.js';
5
+ import { CURRENT_APP_VERSION } from '../utils/utils.version.js';
5
6
  import { buildManifest, saveManifest, signManifest } from '../utils/manifest.js';
6
7
  import { assertRootUrlIsValid } from '../utils/pluginValidation.js';
8
+ import { getCreatePluginVersion } from '../utils/getCreatePluginVersion.js';
9
+ import { output } from '../utils/utils.output.js';
7
10
 
8
11
  export const sign = async (argv: minimist.ParsedArgs) => {
9
12
  const distDir = argv.distDir ?? 'dist';
@@ -12,14 +15,23 @@ export const sign = async (argv: minimist.ParsedArgs) => {
12
15
  const rootUrls: string[] = argv.rootUrls?.split(',') ?? [];
13
16
 
14
17
  if (!existsSync(pluginDistDir)) {
15
- throw new Error(`Plugin \`${distDir}\` directory is missing. Did you build the plugin before attempting to sign?`);
18
+ output.error({
19
+ title: 'Plugin directory not found.',
20
+ body: [
21
+ `Directory ${chalk.bold(pluginDistDir)} not found.`,
22
+ 'Make sure to build the plugin before attempting to sign it.',
23
+ ],
24
+ });
25
+ process.exit(1);
16
26
  }
17
27
 
18
28
  try {
19
- console.log('Building manifest...');
29
+ output.log({
30
+ title: 'Signing plugin.',
31
+ body: [`Plugin found in ${pluginDistDir}`],
32
+ });
20
33
  const manifest = await buildManifest(pluginDistDir);
21
34
 
22
- console.log('Signing manifest...');
23
35
  if (signatureType) {
24
36
  manifest.signatureType = signatureType;
25
37
  }
@@ -28,15 +40,26 @@ export const sign = async (argv: minimist.ParsedArgs) => {
28
40
  manifest.rootUrls = rootUrls;
29
41
  }
30
42
 
31
- manifest.signPlugin = { version: getVersion() };
43
+ manifest.signPlugin = { version: CURRENT_APP_VERSION };
44
+ const createPluginVersion = getCreatePluginVersion();
45
+ if (createPluginVersion) {
46
+ manifest.createPlugin = { version: createPluginVersion };
47
+ }
48
+
32
49
  const signedManifest = await signManifest(manifest);
33
50
 
34
- console.log('Saving signed manifest...');
35
51
  saveManifest(pluginDistDir, signedManifest);
36
-
37
- console.log('Signed successfully');
52
+ output.success({
53
+ title: `Plugin signed successsfully.`,
54
+ body: [`Signed manifest saved to ${pluginDistDir}.`],
55
+ });
38
56
  } catch (err) {
39
- console.warn(err);
40
- process.exitCode = 1;
57
+ if (err instanceof Error) {
58
+ output.error({
59
+ title: 'Failed to sign plugin.',
60
+ body: [err.message],
61
+ });
62
+ }
63
+ process.exit(1);
41
64
  }
42
65
  };
@@ -1,8 +1,8 @@
1
- import { getVersion } from '../utils/getVersion.js';
1
+ import { output } from '../utils/utils.output.js';
2
2
 
3
3
  export const version = async () => {
4
4
  try {
5
- console.log(getVersion());
5
+ output.log({ title: '' });
6
6
  } catch (error) {
7
7
  console.error(error);
8
8
  process.exit(1);
@@ -0,0 +1,18 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ export const getCreatePluginVersion = () => {
5
+ try {
6
+ const currDir = process.cwd();
7
+ const crpcJSONPath = resolve(currDir, '.config', '.cprc.json');
8
+ if (!existsSync(crpcJSONPath)) {
9
+ return null;
10
+ }
11
+
12
+ const crpcJSON = readFileSync(crpcJSONPath, 'utf-8');
13
+ const { version } = JSON.parse(crpcJSON);
14
+ return version as string;
15
+ } catch (err) {
16
+ return null;
17
+ }
18
+ };
@@ -3,6 +3,8 @@ import { readFileSync, writeFileSync } from 'node:fs';
3
3
  import fs from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import { postData } from './request.js';
6
+ import { output } from './utils.output.js';
7
+ import chalk from 'chalk';
6
8
 
7
9
  const MANIFEST_FILE = 'MANIFEST.txt';
8
10
 
@@ -19,6 +21,9 @@ export interface ManifestInfo {
19
21
  signPlugin?: {
20
22
  version: string;
21
23
  };
24
+ createPlugin?: {
25
+ version: string;
26
+ };
22
27
  }
23
28
 
24
29
  type RecursiveWalk = AsyncGenerator<string, void | RecursiveWalk>;
@@ -79,15 +84,19 @@ export async function signManifest(manifest: ManifestInfo): Promise<string> {
79
84
  const GRAFANA_ACCESS_POLICY_TOKEN = process.env.GRAFANA_ACCESS_POLICY_TOKEN;
80
85
 
81
86
  if (!GRAFANA_ACCESS_POLICY_TOKEN && !GRAFANA_API_KEY) {
82
- throw new Error(
83
- 'You must create a GRAFANA_ACCESS_POLICY_TOKEN env variable to sign plugins. Please see: https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin#generate-an-access-policy-token for instructions.'
84
- );
87
+ output.error({
88
+ title: 'Missing GRAFANA_ACCESS_POLICY_TOKEN.',
89
+ body: ['You must create a GRAFANA_ACCESS_POLICY_TOKEN env variable to sign plugins.'],
90
+ link: 'https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin#generate-an-access-policy-token',
91
+ });
92
+ process.exit(1);
85
93
  }
86
94
  if (GRAFANA_API_KEY) {
87
- console.warn(
88
- `\x1b[33m%s\x1b[0m`,
89
- 'The usage of GRAFANA_API_KEY is deprecated, please consider using GRAFANA_ACCESS_POLICY_TOKEN instead. For more info visit https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin'
90
- );
95
+ output.warning({
96
+ title: 'Usage of GRAFANA_API_KEY is deprecated.',
97
+ body: ['Please migrate to using a GRAFANA_ACCESS_POLICY_TOKEN instead.'],
98
+ link: 'https://grafana.com/developers/plugin-tools/publish-a-plugin/sign-a-plugin',
99
+ });
91
100
  }
92
101
 
93
102
  const GRAFANA_COM_URL = process.env.GRAFANA_COM_URL || 'https://grafana.com/api';
@@ -99,17 +108,25 @@ export async function signManifest(manifest: ManifestInfo): Promise<string> {
99
108
  Authorization: 'Bearer ' + token,
100
109
  });
101
110
  if (info.status !== 200) {
102
- console.error('Error: ', info);
103
- throw new Error('Error signing manifest');
111
+ const dataAsArray = Object.entries(JSON.parse(info.data)).map(([key, value]) => `${key}: ${value}`);
112
+ output.error({
113
+ title: 'Error signing manifest.',
114
+ body: [
115
+ `Server responded with status code ${chalk.yellow(info.status)} along with:`,
116
+ ...output.bulletList(dataAsArray),
117
+ ],
118
+ });
119
+ process.exit(1);
104
120
  }
105
121
 
106
122
  return info.data;
107
123
  } catch (err: any) {
108
- if (err.response?.data?.message) {
109
- throw new Error('Error signing manifest: ' + err.response.data.message);
110
- }
111
-
112
- throw new Error('Error signing manifest: ' + err.message);
124
+ const body = err.response?.data?.message ? [err.response.data.message] : [err.message];
125
+ output.error({
126
+ title: 'Error signing manifest.',
127
+ body,
128
+ });
129
+ process.exit(1);
113
130
  }
114
131
  }
115
132
 
@@ -118,7 +135,10 @@ export function saveManifest(dir: string, signedManifest: string) {
118
135
  writeFileSync(path.join(dir, MANIFEST_FILE), signedManifest);
119
136
  return true;
120
137
  } catch (error) {
121
- console.error(error);
122
- throw new Error('Error saving manifest');
138
+ output.error({
139
+ title: 'Error saving manifest',
140
+ body: [`Failed to write signed manifest to ${dir}.`],
141
+ });
142
+ process.exit(1);
123
143
  }
124
144
  }
@@ -0,0 +1,4 @@
1
+ import { Output } from '@libs/output';
2
+ import { CURRENT_APP_VERSION } from './utils.version.js';
3
+
4
+ export const output = new Output('sign-plugin', CURRENT_APP_VERSION);
@@ -0,0 +1,3 @@
1
+ import { getVersion } from '@libs/version';
2
+
3
+ export const CURRENT_APP_VERSION = getVersion();
@@ -1,13 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- const __dirname = fileURLToPath(new URL('.', import.meta.url));
5
- export const getVersion = () => {
6
- const packageJsonPath = resolve(__dirname, '..', '..', 'package.json');
7
- const pkg = readFileSync(packageJsonPath, 'utf8');
8
- const { version } = JSON.parse(pkg);
9
- if (!version) {
10
- throw `Could not find the version of sign-plugin`;
11
- }
12
- return version;
13
- };
@@ -1,13 +0,0 @@
1
- import { getPluginJson, validatePluginJson } from './pluginValidation.js';
2
- describe('pluginValidation', () => {
3
- describe('plugin.json', () => {
4
- test('missing plugin.json file', () => {
5
- expect(() => getPluginJson(`${__dirname}/mocks/missing-plugin.json`)).toThrowError();
6
- });
7
- });
8
- describe('validatePluginJson', () => {
9
- test('missing "id" field in the plugin.json file', () => {
10
- expect(() => validatePluginJson({})).toThrow('Plugin id is missing in plugin.json');
11
- });
12
- });
13
- });
@@ -1,15 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- const __dirname = fileURLToPath(new URL('.', import.meta.url));
6
-
7
- export const getVersion = () => {
8
- const packageJsonPath = resolve(__dirname, '..', '..', 'package.json');
9
- const pkg = readFileSync(packageJsonPath, 'utf8');
10
- const { version } = JSON.parse(pkg);
11
- if (!version) {
12
- throw `Could not find the version of sign-plugin`;
13
- }
14
- return version;
15
- };