@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 CHANGED
@@ -4,7 +4,13 @@
4
4
  </a>
5
5
  </h1>
6
6
 
7
- A collection of Bun shell scripts that can be re-used in various CICD pipelines.
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
+ ```
@@ -31,7 +31,6 @@ export declare class DockerClient {
31
31
  * @example ["staging-latest", `staging-${sha}`, `staging-%{timestamp.toString()}`]
32
32
  */
33
33
  tags: string[];
34
- dockerUsername: string;
35
34
  dockerRepository: string;
36
35
  shouldUpload?: boolean;
37
36
  platforms?: string[];
@@ -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, dockerUsername, dockerRepository, shouldUpload = true, platforms = ["linux/amd64"], buildArgs = {}, } = args;
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
- `${dockerUsername}/${dockerRepository}:${tag}`,
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 ${dockerUsername}/${dockerRepository}`
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.24",
6
+ "version": "1.0.25",
7
7
  "repository": {
8
8
  "url": "git+https://github.com/tahminator/pipeline.git"
9
9
  },