@tahminator/pipeline 1.0.24 → 1.0.25
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/README.md +216 -1
- package/dist/docker/client.d.ts +0 -1
- package/dist/docker/client.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
</a>
|
|
5
5
|
</h1>
|
|
6
6
|
|
|
7
|
-
A collection of Bun
|
|
7
|
+
A collection of APIs built around Bun Shell that can be re-used in various CICD pipelines.
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
- [`tahminator/instalock-web/.github/workflows`](https://github.com/tahminator/instalock-web/blob/main/.github), a monorepo with over 30k tracked users & 650 active in production.
|
|
12
|
+
- [`tahminator/sapling/.github/workflows`](https://github.com/tahminator/instalock-web/blob/main/.github), an Express library that makes backend development easier & less painful (used in `instalock-web`)
|
|
13
|
+
- [`tahminator/pipeline/src/internal`](https://github.com/tahminator/pipeline/blob/main/src/internal), which is used to help build, package & test this library
|
|
8
14
|
|
|
9
15
|
## Setup
|
|
10
16
|
|
|
@@ -17,5 +23,214 @@ bun install
|
|
|
17
23
|
To run:
|
|
18
24
|
|
|
19
25
|
```bash
|
|
26
|
+
# note not all dependencies may run directly in a local environment
|
|
20
27
|
bun run src/index.ts
|
|
21
28
|
```
|
|
29
|
+
|
|
30
|
+
> [!WARNING]
|
|
31
|
+
> This repository is iterating quickly & as such may have rough edges. I will always be happy to respond to & fix any issues anyone may have :)
|
|
32
|
+
|
|
33
|
+
## Available Clients
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import {
|
|
37
|
+
DockerClient,
|
|
38
|
+
GitHubClient,
|
|
39
|
+
NPMClient,
|
|
40
|
+
SonarScannerClient,
|
|
41
|
+
Utils,
|
|
42
|
+
} from "@tahminator/pipeline";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> [!NOTE]
|
|
46
|
+
> While there is documentation below, each API should be relatively well commented and be more up to date than what is seen below.
|
|
47
|
+
|
|
48
|
+
### `GitHubClient`
|
|
49
|
+
|
|
50
|
+
Interface with GitHub / GitHub Actions API
|
|
51
|
+
|
|
52
|
+
This client has two auth strategies: `createWithGithubAppToken()` and `createWithDefaultCiToken()`
|
|
53
|
+
|
|
54
|
+
**NOTE: If you want to automate pull requests, tags, or anything else that may trigger GitHub Actions, you must use `createWithGithubAppToken()`; there is no exception**
|
|
55
|
+
|
|
56
|
+
It is strongly recommended that you basically always use `createWithGithubAppToken()`
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const client = await GitHubClient.createWithGithubAppToken({
|
|
60
|
+
appId: process.env.GH_APP_ID!,
|
|
61
|
+
privateKey: await Utils.decodeBase64EncodedString(
|
|
62
|
+
process.env.GH_PRIVATE_KEY_B64!,
|
|
63
|
+
),
|
|
64
|
+
installationId: process.env.GH_INSTALLATION_ID!,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// requires gh app
|
|
68
|
+
await client.createTag({
|
|
69
|
+
releaseType: "patch", // default is patch
|
|
70
|
+
// can also be automatically be inferred from env.GITHUB_REPOSITORY which is automatically injected in Actions
|
|
71
|
+
repositoryOverride: ["tahminator", "my-service"],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await client.createTag({
|
|
75
|
+
releaseType: "minor",
|
|
76
|
+
onPreTagCreate: async (tag) => {
|
|
77
|
+
// will set `version` key for all `package.json` excluding `node_modules/`
|
|
78
|
+
await Utils.updateAllPackageJsonsWithVersion(tag);
|
|
79
|
+
// you can write you own logic here if you would like
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// output to env.GITHUB_OUTPUT to re-use outputs across steps, jobs, outputs, etc.
|
|
84
|
+
// Hover over type in IDE to see more details
|
|
85
|
+
await client.outputToGithubOutput({
|
|
86
|
+
ctx: {
|
|
87
|
+
imageTag: "1.2.3",
|
|
88
|
+
deploy: { env: "production", healthy: true },
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// updates kubernetes manifest repo. Hover over type of function and each key in argument in IDE to see more details
|
|
93
|
+
await client.updateK8sTagWithPR({
|
|
94
|
+
newTag: "1.2.3",
|
|
95
|
+
imageName: "tahminator/my-service",
|
|
96
|
+
kustomizationFilePath: "apps/prod/kustomization.yaml",
|
|
97
|
+
environment: "production",
|
|
98
|
+
originRepo: ["tahminator", "pipeline"],
|
|
99
|
+
manifestRepo: ["tahminator", "infra"],
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// client cannot do most automations without getting skipped by CICD, but it can do many read operations and all CI operations just fine
|
|
105
|
+
const client = await GitHubClient.createWithDefaultCiToken();
|
|
106
|
+
|
|
107
|
+
await client.outputToGithubOutput({
|
|
108
|
+
ctx: { imageTag: "1.2.3" },
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `DockerClient`
|
|
113
|
+
|
|
114
|
+
Interface with Docker to build & deploy images
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management
|
|
118
|
+
await using client = await DockerClient.create(
|
|
119
|
+
process.env.DOCKER_USERNAME!,
|
|
120
|
+
process.env.DOCKER_PAT!,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// `buildImage` will build AND deploy
|
|
124
|
+
await client.buildImage({
|
|
125
|
+
dockerFileLocation: "./Dockerfile",
|
|
126
|
+
dockerRepository: "my-service",
|
|
127
|
+
tags: ["latest", "sha-abc123"], // define as many as you would like
|
|
128
|
+
platforms: ["linux/amd64", "linux/arm64"], // build for multiple platforms
|
|
129
|
+
buildArgs: { NODE_ENV: "production" }, // pass in build args
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await client.buildImage({
|
|
133
|
+
dockerFileLocation: "./Dockerfile",
|
|
134
|
+
dockerRepository: "my-service",
|
|
135
|
+
tags: ["local-dev"],
|
|
136
|
+
shouldUpload: false, // dry run, do not actually upload
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// find tag by given name,
|
|
140
|
+
await client.promoteDockerImage({
|
|
141
|
+
repository: "my-service",
|
|
142
|
+
originalTag: "sha-abc123",
|
|
143
|
+
newGithubTags: ["staging", "1.2.3"],
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `NPMClient`
|
|
148
|
+
|
|
149
|
+
Interface with NPM registry in order to publish packages to `npmjs.com`
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// uses trusted publishing, token based uploads are not supported
|
|
153
|
+
const npm = await NPMClient.create();
|
|
154
|
+
|
|
155
|
+
await npm.publish();
|
|
156
|
+
|
|
157
|
+
// dry run
|
|
158
|
+
await npm.publish(true);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### `SonarScannerClient`
|
|
162
|
+
|
|
163
|
+
Interface with SonarQube and upload test coverage for basically any language / framework that is supported by Sonar Scanner.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { $ } from "bun";
|
|
167
|
+
|
|
168
|
+
const backendClient = new SonarScannerClient({
|
|
169
|
+
auth: {
|
|
170
|
+
token: process.env.SONAR_TOKEN!,
|
|
171
|
+
},
|
|
172
|
+
run: {
|
|
173
|
+
runTestsCmd: $`./mvnw clean verify -Dspring.profiles.active=ci`,
|
|
174
|
+
},
|
|
175
|
+
scan: {
|
|
176
|
+
projectKey: "my-org_my-java-service",
|
|
177
|
+
organization: "my-org",
|
|
178
|
+
sourceCodeDir: "src/main/java",
|
|
179
|
+
additionalArgs: { // all args are automatically wrapped in `-Dsonar.${key}=${value}`
|
|
180
|
+
java.binaries: "target/classes",
|
|
181
|
+
coverage.jacoco.xmlReportPaths: "target/site/jacoco/jacoco.xml",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await backendClient.runTests();
|
|
187
|
+
await backendClient.uploadTestCoverage();
|
|
188
|
+
|
|
189
|
+
const frontendClient = new SonarScannerClient({
|
|
190
|
+
auth: {
|
|
191
|
+
token: process.env.SONAR_TOKEN!,
|
|
192
|
+
},
|
|
193
|
+
run: {
|
|
194
|
+
runTestsCmd: $`pnpm run --dir=js test`,
|
|
195
|
+
},
|
|
196
|
+
scan: {
|
|
197
|
+
projectKey: "my-org_my-ts-service",
|
|
198
|
+
organization: "my-org",
|
|
199
|
+
sourceCodeDir: "js/src",
|
|
200
|
+
additionalArgs: {
|
|
201
|
+
javascript.lcov.reportPaths: "js/coverage/lcov.info",
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await frontendClient.runTests();
|
|
207
|
+
await frontendClient.uploadTestCoverage();
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### `Utils`
|
|
211
|
+
|
|
212
|
+
Assorted helper class used by clients and useful as an exported class to pipelines.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
const githubPrivateKey = await Utils.decodeBase64EncodedString(
|
|
216
|
+
process.env.GH_PRIVATE_KEY_B64!,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// will read from git-crypt encrypted variable so long as git-crypt & gpg are setup
|
|
220
|
+
// will only consume in memory
|
|
221
|
+
const env = await Utils.getEnvVariables(["shared", "production"], {
|
|
222
|
+
baseDir: "apps/backend",
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const shortId = Utils.generateShortId();
|
|
226
|
+
|
|
227
|
+
await Utils.updateAllPackageJsonsWithVersion("1.2.3");
|
|
228
|
+
|
|
229
|
+
if (await Utils.isCmdAvailable("gh")) {
|
|
230
|
+
console.log(Utils.Colors.green(`gh is installed (${shortId})`));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (Utils.Log.isDebug) {
|
|
234
|
+
console.log(env.DATABASE_URL);
|
|
235
|
+
}
|
|
236
|
+
```
|
package/dist/docker/client.d.ts
CHANGED
package/dist/docker/client.js
CHANGED
|
@@ -41,7 +41,7 @@ export class DockerClient {
|
|
|
41
41
|
console.log(`Promoted ${originalTag} to: ${newGithubTags.join(", ")}`);
|
|
42
42
|
}
|
|
43
43
|
async buildImage(args) {
|
|
44
|
-
const { dockerFileLocation, tags,
|
|
44
|
+
const { dockerFileLocation, tags, dockerRepository, shouldUpload = true, platforms = ["linux/amd64"], buildArgs = {}, } = args;
|
|
45
45
|
if (!tags.length) {
|
|
46
46
|
throw new Error("You must provide atleast one tag.");
|
|
47
47
|
}
|
|
@@ -56,7 +56,7 @@ export class DockerClient {
|
|
|
56
56
|
const buildModeFlag = shouldUpload ? "--push" : "--load";
|
|
57
57
|
const tagFlags = tags.flatMap((tag) => [
|
|
58
58
|
"--tag",
|
|
59
|
-
`${
|
|
59
|
+
`${this.username}/${dockerRepository}:${tag}`,
|
|
60
60
|
]);
|
|
61
61
|
const buildArgFlags = Object.entries(buildArgs).flatMap(([k, v]) => [
|
|
62
62
|
"--build-arg",
|
|
@@ -71,7 +71,7 @@ export class DockerClient {
|
|
|
71
71
|
${tagFlags} \
|
|
72
72
|
.`;
|
|
73
73
|
console.log(shouldUpload ?
|
|
74
|
-
`Image build & successfully uploaded to ${
|
|
74
|
+
`Image build & successfully uploaded to ${this.username}/${dockerRepository}`
|
|
75
75
|
: "Image has been built (upload skipped.)");
|
|
76
76
|
}
|
|
77
77
|
async [Symbol.asyncDispose]() {
|
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.25",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "git+https://github.com/tahminator/pipeline.git"
|
|
9
9
|
},
|