@tahminator/pipeline 1.0.27 → 1.0.28
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/dist/env/client.d.ts +8 -0
- package/dist/env/client.js +31 -0
- package/dist/env/index.d.ts +2 -0
- package/dist/env/index.js +2 -0
- package/dist/env/strategy/git-crypt.d.ts +7 -0
- package/dist/env/strategy/git-crypt.js +44 -0
- package/dist/env/strategy/index.d.ts +1 -0
- package/dist/env/strategy/index.js +0 -0
- package/dist/env/strategy/sops.d.ts +15 -0
- package/dist/env/strategy/sops.js +40 -0
- package/dist/env/strategy/types.d.ts +4 -0
- package/dist/env/strategy/types.js +0 -0
- package/dist/env/types.d.ts +7 -0
- package/dist/env/types.js +5 -0
- package/dist/internal/create-tag/index.js +3 -1
- package/dist/utils/client.d.ts +0 -2
- package/dist/utils/client.js +0 -4
- package/package.json +1 -1
- package/dist/utils/env.d.ts +0 -20
- package/dist/utils/env.js +0 -67
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EnvClientStrategy, type EnvClientReadOpts } from "./types";
|
|
2
|
+
export declare class EnvClient {
|
|
3
|
+
private readonly strategy;
|
|
4
|
+
private constructor();
|
|
5
|
+
static create(strategy: EnvClientStrategy): EnvClient;
|
|
6
|
+
readFromEnv(fileName: string, opts?: EnvClientReadOpts): Promise<Record<string, string>>;
|
|
7
|
+
private maskEnv;
|
|
8
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GitCryptEnvClientStrategy } from "./strategy/git-crypt";
|
|
2
|
+
import { SopsEnvClientStrategy } from "./strategy/sops";
|
|
3
|
+
import { EnvClientStrategy } from "./types";
|
|
4
|
+
export class EnvClient {
|
|
5
|
+
strategy;
|
|
6
|
+
constructor(strategy) {
|
|
7
|
+
this.strategy = strategy;
|
|
8
|
+
}
|
|
9
|
+
static create(strategy) {
|
|
10
|
+
switch (strategy) {
|
|
11
|
+
case EnvClientStrategy.SOPS:
|
|
12
|
+
return new this(new SopsEnvClientStrategy());
|
|
13
|
+
case EnvClientStrategy.GIT_CRYPT:
|
|
14
|
+
return new this(new GitCryptEnvClientStrategy());
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async readFromEnv(fileName, opts) {
|
|
18
|
+
return this.maskEnv(await this.strategy.readFromEnv(fileName, opts));
|
|
19
|
+
}
|
|
20
|
+
maskEnv(envs) {
|
|
21
|
+
for (const [varName, value] of envs.entries()) {
|
|
22
|
+
if (value === "true" || value === "false" || value === "") {
|
|
23
|
+
console.log(`Not masking ${varName}: true/false/empty value`);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
console.log(`Masking ${varName}`);
|
|
27
|
+
console.log(`::add-mask::${value}`);
|
|
28
|
+
}
|
|
29
|
+
return Object.fromEntries(envs);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EnvClientReadOpts } from "../types";
|
|
2
|
+
import type { IEnvClientStrategy } from "./types";
|
|
3
|
+
export declare class GitCryptEnvClientStrategy implements IEnvClientStrategy {
|
|
4
|
+
isGitCryptUnlocked: boolean;
|
|
5
|
+
constructor();
|
|
6
|
+
readFromEnv(fileName: string, opts?: EnvClientReadOpts): Promise<Map<string, string>>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
2
|
+
export class GitCryptEnvClientStrategy {
|
|
3
|
+
// TODO: replace with a more robust solution
|
|
4
|
+
isGitCryptUnlocked = false;
|
|
5
|
+
constructor() { }
|
|
6
|
+
async readFromEnv(fileName, opts) {
|
|
7
|
+
const { baseDir = "" } = opts ?? {};
|
|
8
|
+
if (!this.isGitCryptUnlocked) {
|
|
9
|
+
await $ `git-crypt unlock`.nothrow();
|
|
10
|
+
this.isGitCryptUnlocked = true;
|
|
11
|
+
}
|
|
12
|
+
const loaded = new Map();
|
|
13
|
+
const path = baseDir ? `${baseDir}/${fileName}` : `${fileName}`;
|
|
14
|
+
const envFile = Bun.file(path);
|
|
15
|
+
if (await envFile.exists()) {
|
|
16
|
+
console.log(`Loading ${envFile.name}`);
|
|
17
|
+
const content = await envFile.text();
|
|
18
|
+
const lines = content.split("\n").filter((s) => s.length > 0);
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
22
|
+
continue;
|
|
23
|
+
const match = (() => {
|
|
24
|
+
const [key, ...rest] = trimmed.split("=");
|
|
25
|
+
return [key, rest.join("=")];
|
|
26
|
+
})();
|
|
27
|
+
if (match.length === 2) {
|
|
28
|
+
const [key, value] = match;
|
|
29
|
+
const cleanKey = key.trim();
|
|
30
|
+
let cleanValue = value.trim();
|
|
31
|
+
if ((cleanValue.startsWith('"') && cleanValue.endsWith('"')) ||
|
|
32
|
+
(cleanValue.startsWith("'") && cleanValue.endsWith("'"))) {
|
|
33
|
+
cleanValue = cleanValue.slice(1, -1);
|
|
34
|
+
}
|
|
35
|
+
loaded.set(cleanKey, cleanValue);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.warn(`Warning: ${envFile.name} not found`);
|
|
41
|
+
}
|
|
42
|
+
return loaded;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EnvClientReadOpts } from "../types";
|
|
2
|
+
import type { IEnvClientStrategy } from "./types";
|
|
3
|
+
export declare class SopsEnvClientStrategy implements IEnvClientStrategy {
|
|
4
|
+
/**
|
|
5
|
+
* __NOTE: Only yaml files are currently supported.__
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```yaml
|
|
9
|
+
* GITHUB_APP_ID: abc123
|
|
10
|
+
* GITHUB_INSTALLATION_ID: abc123
|
|
11
|
+
* GITHUB_PEM_CONTENT: abc123
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
readFromEnv(fileName: string, opts?: EnvClientReadOpts): Promise<Map<string, string>>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
2
|
+
import yaml from "yaml";
|
|
3
|
+
export class SopsEnvClientStrategy {
|
|
4
|
+
/**
|
|
5
|
+
* __NOTE: Only yaml files are currently supported.__
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```yaml
|
|
9
|
+
* GITHUB_APP_ID: abc123
|
|
10
|
+
* GITHUB_INSTALLATION_ID: abc123
|
|
11
|
+
* GITHUB_PEM_CONTENT: abc123
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
async readFromEnv(fileName, opts) {
|
|
15
|
+
const { baseDir = "" } = opts ?? {};
|
|
16
|
+
if (!fileName.endsWith("yaml")) {
|
|
17
|
+
throw new Error("Only yaml files are currently supported");
|
|
18
|
+
}
|
|
19
|
+
const loaded = new Map();
|
|
20
|
+
const path = baseDir ? `${baseDir}/${fileName}` : `${fileName}`;
|
|
21
|
+
const envFile = Bun.file(path);
|
|
22
|
+
if (!(await envFile.exists())) {
|
|
23
|
+
console.warn(`Warning: ${envFile.name} not found`);
|
|
24
|
+
return loaded;
|
|
25
|
+
}
|
|
26
|
+
console.log(`Loading ${envFile.name}`);
|
|
27
|
+
const content = await $ `sops decrypt ${path}`.text();
|
|
28
|
+
const parsed = yaml.parse(content);
|
|
29
|
+
if (!parsed || Array.isArray(parsed)) {
|
|
30
|
+
throw new Error(`Expected ${envFile.name} to decrypt to a root-level YAML object.`);
|
|
31
|
+
}
|
|
32
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
33
|
+
if (typeof value !== "string") {
|
|
34
|
+
throw new Error(`Expected ${envFile.name} key \`${key}\` to contain a string value.`);
|
|
35
|
+
}
|
|
36
|
+
loaded.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
return loaded;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
File without changes
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { EnvClient, EnvClientStrategy } from "../../env";
|
|
1
2
|
import { GitHubClient } from "../../gh";
|
|
2
3
|
import { Utils } from "../../utils";
|
|
3
4
|
async function main() {
|
|
4
|
-
const
|
|
5
|
+
const envClient = EnvClient.create(EnvClientStrategy.GIT_CRYPT);
|
|
6
|
+
const { githubAppAppId, githubAppInstallationId, githubAppPrivateKeyB64 } = parseCiEnv(await envClient.readFromEnv(".env.ci"));
|
|
5
7
|
const ghClient = await GitHubClient.createWithGithubAppToken({
|
|
6
8
|
appId: githubAppAppId,
|
|
7
9
|
installationId: githubAppInstallationId,
|
package/dist/utils/client.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { decodeBase64EncodedString } from "./b64";
|
|
2
2
|
import { isCmdAvailable } from "./cmd";
|
|
3
3
|
import { Colors } from "./colors";
|
|
4
|
-
import { getEnvVariables } from "./env";
|
|
5
4
|
import { Log } from "./log";
|
|
6
5
|
import { generateShortId } from "./short";
|
|
7
6
|
export declare class Utils {
|
|
8
7
|
static Colors: typeof Colors;
|
|
9
8
|
static Log: typeof Log;
|
|
10
|
-
static getEnvVariables(...args: Parameters<typeof getEnvVariables>): Promise<Record<string, string>>;
|
|
11
9
|
static generateShortId(...args: Parameters<typeof generateShortId>): string;
|
|
12
10
|
static updateAllPackageJsonsWithVersion(version: string): Promise<void>;
|
|
13
11
|
static isCmdAvailable(...args: Parameters<typeof isCmdAvailable>): Promise<boolean>;
|
package/dist/utils/client.js
CHANGED
|
@@ -2,16 +2,12 @@ import { $ } from "bun";
|
|
|
2
2
|
import { decodeBase64EncodedString } from "./b64";
|
|
3
3
|
import { isCmdAvailable } from "./cmd";
|
|
4
4
|
import { Colors } from "./colors";
|
|
5
|
-
import { getEnvVariables } from "./env";
|
|
6
5
|
import { Log } from "./log";
|
|
7
6
|
import { generateShortId } from "./short";
|
|
8
7
|
export class Utils {
|
|
9
8
|
// hoist
|
|
10
9
|
static Colors = Colors;
|
|
11
10
|
static Log = Log;
|
|
12
|
-
static getEnvVariables(...args) {
|
|
13
|
-
return getEnvVariables(...args);
|
|
14
|
-
}
|
|
15
11
|
static generateShortId(...args) {
|
|
16
12
|
return generateShortId(...args);
|
|
17
13
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"type": "module",
|
|
4
4
|
"author": "Tahmid Ahmed",
|
|
5
5
|
"description": "A collection of Bun shell scripts that can be re-used in various CICD pipelines.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.28",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "git+https://github.com/tahminator/pipeline.git"
|
|
9
9
|
},
|
package/dist/utils/env.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
type Opts = {
|
|
2
|
-
baseDir?: string;
|
|
3
|
-
mask_PLZ_DO_NOT_TURN_OFF_UNLESS_YOU_KNOW_WHAT_UR_DOING?: boolean;
|
|
4
|
-
};
|
|
5
|
-
/**
|
|
6
|
-
* Load environment variables from encrypted `.env.*` files. This method will call `git-crypt unlock` for you if necessary.
|
|
7
|
-
*
|
|
8
|
-
* @param environments - List of environment files to load.
|
|
9
|
-
*
|
|
10
|
-
* @param opts - Optional overrides
|
|
11
|
-
* @param opts.baseDir - Directory of environment variable. Defaults to the root directory / ""
|
|
12
|
-
* @param opts.mask_PLZ_DO_NOT_TURN_OFF_UNLESS_YOU_KNOW_WHAT_UR_DOING - Should variables be masked. Defaults to `true`. __NOTE: This will only work in a GitHub Action runner.__
|
|
13
|
-
*
|
|
14
|
-
* @returns a map of the loaded environments as a key and value inside of a map.
|
|
15
|
-
*
|
|
16
|
-
* @note _Please note that duplicate environment variables will be overwritten, so
|
|
17
|
-
* the order in which you define `environments` does matter._
|
|
18
|
-
*/
|
|
19
|
-
export declare function getEnvVariables(environments: string[], opts?: Opts): Promise<Record<string, string>>;
|
|
20
|
-
export {};
|
package/dist/utils/env.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { $ } from "bun";
|
|
2
|
-
// TODO: replace with a more robust solution
|
|
3
|
-
let isGitCryptUnlocked = false;
|
|
4
|
-
/**
|
|
5
|
-
* Load environment variables from encrypted `.env.*` files. This method will call `git-crypt unlock` for you if necessary.
|
|
6
|
-
*
|
|
7
|
-
* @param environments - List of environment files to load.
|
|
8
|
-
*
|
|
9
|
-
* @param opts - Optional overrides
|
|
10
|
-
* @param opts.baseDir - Directory of environment variable. Defaults to the root directory / ""
|
|
11
|
-
* @param opts.mask_PLZ_DO_NOT_TURN_OFF_UNLESS_YOU_KNOW_WHAT_UR_DOING - Should variables be masked. Defaults to `true`. __NOTE: This will only work in a GitHub Action runner.__
|
|
12
|
-
*
|
|
13
|
-
* @returns a map of the loaded environments as a key and value inside of a map.
|
|
14
|
-
*
|
|
15
|
-
* @note _Please note that duplicate environment variables will be overwritten, so
|
|
16
|
-
* the order in which you define `environments` does matter._
|
|
17
|
-
*/
|
|
18
|
-
export async function getEnvVariables(environments, opts) {
|
|
19
|
-
const { baseDir = "", mask_PLZ_DO_NOT_TURN_OFF_UNLESS_YOU_KNOW_WHAT_UR_DOING = true, } = opts ?? {};
|
|
20
|
-
if (!isGitCryptUnlocked) {
|
|
21
|
-
await $ `git-crypt unlock`.nothrow();
|
|
22
|
-
isGitCryptUnlocked = true;
|
|
23
|
-
}
|
|
24
|
-
const loaded = new Map();
|
|
25
|
-
for (const env of environments) {
|
|
26
|
-
const path = baseDir ? `${baseDir}/.env.${env}` : `.env.${env}`;
|
|
27
|
-
const envFile = Bun.file(path);
|
|
28
|
-
if (await envFile.exists()) {
|
|
29
|
-
console.log(`Loading ${envFile.name}`);
|
|
30
|
-
const content = await envFile.text();
|
|
31
|
-
const lines = content.split("\n").filter((s) => s.length > 0);
|
|
32
|
-
for (const line of lines) {
|
|
33
|
-
const trimmed = line.trim();
|
|
34
|
-
if (!trimmed || trimmed.startsWith("#"))
|
|
35
|
-
continue;
|
|
36
|
-
const match = (() => {
|
|
37
|
-
const [key, ...rest] = trimmed.split("=");
|
|
38
|
-
return [key, rest.join("=")];
|
|
39
|
-
})();
|
|
40
|
-
if (match.length === 2) {
|
|
41
|
-
const [key, value] = match;
|
|
42
|
-
const cleanKey = key.trim();
|
|
43
|
-
let cleanValue = value.trim();
|
|
44
|
-
if ((cleanValue.startsWith('"') && cleanValue.endsWith('"')) ||
|
|
45
|
-
(cleanValue.startsWith("'") && cleanValue.endsWith("'"))) {
|
|
46
|
-
cleanValue = cleanValue.slice(1, -1);
|
|
47
|
-
}
|
|
48
|
-
loaded.set(cleanKey, cleanValue);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
console.warn(`Warning: ${envFile.name} not found`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (mask_PLZ_DO_NOT_TURN_OFF_UNLESS_YOU_KNOW_WHAT_UR_DOING) {
|
|
57
|
-
for (const [varName, value] of loaded.entries()) {
|
|
58
|
-
if (value === "true" || value === "false" || value === "") {
|
|
59
|
-
console.log(`Not masking ${varName}: true/false/empty value`);
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
console.log(`Masking ${varName}`);
|
|
63
|
-
console.log(`::add-mask::${value}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return Object.fromEntries(loaded);
|
|
67
|
-
}
|