@tahminator/pipeline 1.0.73 → 1.0.74-beta.6f734379

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.
@@ -1,12 +1,14 @@
1
1
  import { GitHubOutputManager } from "./output";
2
2
  import { GitHubPRManager } from "./pr";
3
3
  import { GitHubTagManager } from "./tag";
4
+ import { GitHubTeamManager } from "./team";
4
5
  export declare class GitHubClient {
5
6
  private readonly client;
6
7
  private readonly isExplicitToken;
7
8
  private readonly tagManager;
8
9
  private readonly outputManager;
9
10
  private readonly prManager;
11
+ private readonly teamManager;
10
12
  private constructor();
11
13
  /**
12
14
  * @note `appId` can be `Client ID` or `App ID`. Hover over `appId` for more details.
@@ -34,9 +36,93 @@ export declare class GitHubClient {
34
36
  * @deprecated use `createWithGithubAppToken`
35
37
  */
36
38
  static createWithPatCiToken(pat: string): Promise<GitHubClient>;
39
+ /**
40
+ * Utilizes the GitHub API to create a new tag version in the given repository.
41
+ *
42
+ * @note You **must** pass in a GitHub token because the regular Github bot token
43
+ * cannot trigger actions (due to fear of recursion). You must either provide a GitHub App token or
44
+ * a GitHub PAT.
45
+ *
46
+ * @note you should use `VersioningClient` to generate `nextTag`
47
+ */
37
48
  createTag(...args: Parameters<GitHubTagManager["createTag"]>): Promise<void>;
49
+ /**
50
+ * Returns the latest stable semver tag in the repository.
51
+ */
38
52
  getLatestTag(...args: Parameters<GitHubTagManager["getLatestTag"]>): Promise<string | null>;
53
+ /**
54
+ * Write an output back to Github Actions in order to re-use / pass
55
+ * data between steps, jobs, etc.
56
+ *
57
+ * @see documentation for passing outputs between jobs [here](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs)
58
+ */
39
59
  outputToGithubOutput(...args: Parameters<GitHubOutputManager["outputToGithubOutput"]>): Promise<void>;
60
+ /**
61
+ *
62
+ * update k8s manifest repo with new tag version.
63
+ *
64
+ * @note `kustomizationFile` must look like this:
65
+ *
66
+ * ```yaml
67
+ * apiVersion: kustomize.config.k8s.io/v1beta1
68
+ * kind: Kustomization
69
+ * resources:
70
+ * - deployment.yaml
71
+ * - secrets.yaml
72
+ * - service.yaml
73
+ * - monitor.yaml
74
+ * commonLabels:
75
+ * app: instalock-web
76
+ * environment: production
77
+ * # This part specifically
78
+ * images:
79
+ * - name: tahminator/instalock-web
80
+ * newTag: a70ee0e
81
+ * ```
82
+ */
40
83
  updateK8sTagWithPR(...args: Parameters<GitHubPRManager["updateK8sTagWithPR"]>): Promise<void>;
41
84
  sendPrMessage(...args: Parameters<GitHubPRManager["sendPrMessage"]>): Promise<void>;
85
+ /**
86
+ * @note if the PR doesn't exist yet, just use `updateK8sTagWithPR` instead, since it
87
+ * creates the PR with the necessary changes and merges it for you in one call.
88
+ */
89
+ mergePr(...args: Parameters<GitHubPRManager["mergePr"]>): Promise<void>;
90
+ /**
91
+ * Create or update a check run (the "status checks" shown on a PR/commit), dispatched by
92
+ * `action`.
93
+ */
94
+ writeStatusCheck(...args: Parameters<GitHubPRManager["writeStatusCheck"]>): Promise<{
95
+ id: number;
96
+ head_sha: string;
97
+ node_id: string;
98
+ external_id: string | null;
99
+ url: string;
100
+ html_url: string | null;
101
+ details_url: string | null;
102
+ status: "queued" | "in_progress" | "completed" | "waiting" | "requested" | "pending";
103
+ conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required" | null;
104
+ started_at: string | null;
105
+ completed_at: string | null;
106
+ output: {
107
+ title: string | null;
108
+ summary: string | null;
109
+ text: string | null;
110
+ annotations_count: number;
111
+ annotations_url: string;
112
+ };
113
+ name: string;
114
+ check_suite: {
115
+ id: number;
116
+ } | null;
117
+ app: import("@octokit/openapi-types").components["schemas"]["nullable-integration"];
118
+ pull_requests: import("@octokit/openapi-types").components["schemas"]["pull-request-minimal"][];
119
+ deployment?: import("@octokit/openapi-types").components["schemas"]["deployment-simple"];
120
+ }>;
121
+ /**
122
+ * Confirms whether the given user is an active member of a team within an org.
123
+ *
124
+ * @note this only returns `true` for `active` memberships. A user with a `pending`
125
+ * invite (has not accepted yet) will return `false`.
126
+ */
127
+ isTeamMember(...args: Parameters<GitHubTeamManager["isTeamMember"]>): Promise<boolean>;
42
128
  }
package/dist/gh/client.js CHANGED
@@ -3,18 +3,21 @@ import { Octokit } from "@octokit/rest";
3
3
  import { GitHubOutputManager } from "./output";
4
4
  import { GitHubPRManager } from "./pr";
5
5
  import { GitHubTagManager } from "./tag";
6
+ import { GitHubTeamManager } from "./team";
6
7
  export class GitHubClient {
7
8
  client;
8
9
  isExplicitToken;
9
10
  tagManager;
10
11
  outputManager;
11
12
  prManager;
13
+ teamManager;
12
14
  constructor(client, isExplicitToken) {
13
15
  this.client = client;
14
16
  this.isExplicitToken = isExplicitToken;
15
17
  this.tagManager = new GitHubTagManager(this.client, this.isExplicitToken);
16
18
  this.outputManager = new GitHubOutputManager();
17
19
  this.prManager = new GitHubPRManager(this.client);
20
+ this.teamManager = new GitHubTeamManager(this.client);
18
21
  }
19
22
  /**
20
23
  * @note `appId` can be `Client ID` or `App ID`. Hover over `appId` for more details.
@@ -53,19 +56,83 @@ export class GitHubClient {
53
56
  auth: pat,
54
57
  }), true);
55
58
  }
59
+ /**
60
+ * Utilizes the GitHub API to create a new tag version in the given repository.
61
+ *
62
+ * @note You **must** pass in a GitHub token because the regular Github bot token
63
+ * cannot trigger actions (due to fear of recursion). You must either provide a GitHub App token or
64
+ * a GitHub PAT.
65
+ *
66
+ * @note you should use `VersioningClient` to generate `nextTag`
67
+ */
56
68
  createTag(...args) {
57
69
  return this.tagManager.createTag(...args);
58
70
  }
71
+ /**
72
+ * Returns the latest stable semver tag in the repository.
73
+ */
59
74
  getLatestTag(...args) {
60
75
  return this.tagManager.getLatestTag(...args);
61
76
  }
77
+ /**
78
+ * Write an output back to Github Actions in order to re-use / pass
79
+ * data between steps, jobs, etc.
80
+ *
81
+ * @see documentation for passing outputs between jobs [here](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs)
82
+ */
62
83
  outputToGithubOutput(...args) {
63
84
  return this.outputManager.outputToGithubOutput(...args);
64
85
  }
86
+ /**
87
+ *
88
+ * update k8s manifest repo with new tag version.
89
+ *
90
+ * @note `kustomizationFile` must look like this:
91
+ *
92
+ * ```yaml
93
+ * apiVersion: kustomize.config.k8s.io/v1beta1
94
+ * kind: Kustomization
95
+ * resources:
96
+ * - deployment.yaml
97
+ * - secrets.yaml
98
+ * - service.yaml
99
+ * - monitor.yaml
100
+ * commonLabels:
101
+ * app: instalock-web
102
+ * environment: production
103
+ * # This part specifically
104
+ * images:
105
+ * - name: tahminator/instalock-web
106
+ * newTag: a70ee0e
107
+ * ```
108
+ */
65
109
  updateK8sTagWithPR(...args) {
66
110
  return this.prManager.updateK8sTagWithPR(...args);
67
111
  }
68
112
  sendPrMessage(...args) {
69
113
  return this.prManager.sendPrMessage(...args);
70
114
  }
115
+ /**
116
+ * @note if the PR doesn't exist yet, just use `updateK8sTagWithPR` instead, since it
117
+ * creates the PR with the necessary changes and merges it for you in one call.
118
+ */
119
+ mergePr(...args) {
120
+ return this.prManager.mergePr(...args);
121
+ }
122
+ /**
123
+ * Create or update a check run (the "status checks" shown on a PR/commit), dispatched by
124
+ * `action`.
125
+ */
126
+ writeStatusCheck(...args) {
127
+ return this.prManager.writeStatusCheck(...args);
128
+ }
129
+ /**
130
+ * Confirms whether the given user is an active member of a team within an org.
131
+ *
132
+ * @note this only returns `true` for `active` memberships. A user with a `pending`
133
+ * invite (has not accepted yet) will return `false`.
134
+ */
135
+ isTeamMember(...args) {
136
+ return this.teamManager.isTeamMember(...args);
137
+ }
71
138
  }
@@ -1,11 +1,5 @@
1
1
  export declare class GitHubOutputManager {
2
2
  constructor();
3
- /**
4
- * Write an output back to Github Actions in order to re-use / pass
5
- * data between steps, jobs, etc.
6
- *
7
- * @see documentation for passing outputs between jobs [here](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs)
8
- */
9
3
  outputToGithubOutput({ overrideGithubOutputFile, ctx, }: {
10
4
  overrideGithubOutputFile?: string;
11
5
  ctx: Record<string, unknown>;
@@ -1,11 +1,5 @@
1
1
  export class GitHubOutputManager {
2
2
  constructor() { }
3
- /**
4
- * Write an output back to Github Actions in order to re-use / pass
5
- * data between steps, jobs, etc.
6
- *
7
- * @see documentation for passing outputs between jobs [here](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs)
8
- */
9
3
  async outputToGithubOutput({ overrideGithubOutputFile, ctx, }) {
10
4
  const githubOutputEnv = (() => {
11
5
  const v = process.env.GITHUB_OUTPUT;
@@ -1,31 +1,38 @@
1
1
  import type { Octokit } from "@octokit/rest";
2
2
  import type { Environment, OwnerString, RepoString } from "../../types";
3
+ type CheckRunStatus = "queued" | "in_progress" | "completed";
4
+ type CheckRunConclusion = "action_required" | "cancelled" | "failure" | "neutral" | "success" | "skipped" | "stale" | "timed_out";
5
+ type CheckRunOutput = {
6
+ title: string;
7
+ summary: string;
8
+ text?: string;
9
+ };
10
+ type WriteStatusCheckArgs = {
11
+ action: "create";
12
+ owner: string;
13
+ repository: string;
14
+ /**
15
+ * commit sha the check run applies to
16
+ */
17
+ sha: string;
18
+ name: string;
19
+ status?: CheckRunStatus;
20
+ conclusion?: CheckRunConclusion;
21
+ output?: CheckRunOutput;
22
+ detailsUrl?: string;
23
+ } | {
24
+ action: "update";
25
+ owner: string;
26
+ repository: string;
27
+ checkRunId: number;
28
+ status?: CheckRunStatus;
29
+ conclusion?: CheckRunConclusion;
30
+ output?: CheckRunOutput;
31
+ detailsUrl?: string;
32
+ };
3
33
  export declare class GitHubPRManager {
4
34
  private readonly client;
5
35
  constructor(client: Octokit);
6
- /**
7
- *
8
- * update k8s manifest repo with new tag version.
9
- *
10
- * @note `kustomizationFile` must look like this:
11
- *
12
- * ```yaml
13
- * apiVersion: kustomize.config.k8s.io/v1beta1
14
- * kind: Kustomization
15
- * resources:
16
- * - deployment.yaml
17
- * - secrets.yaml
18
- * - service.yaml
19
- * - monitor.yaml
20
- * commonLabels:
21
- * app: instalock-web
22
- * environment: production
23
- * # This part specifically
24
- * images:
25
- * - name: tahminator/instalock-web
26
- * newTag: a70ee0e
27
- * ```
28
- */
29
36
  updateK8sTagWithPR({ newTag, imageName, kustomizationFilePath, environment, originRepo, manifestRepo, }: {
30
37
  newTag: string;
31
38
  imageName: string;
@@ -34,6 +41,39 @@ export declare class GitHubPRManager {
34
41
  originRepo: [OwnerString, RepoString];
35
42
  manifestRepo: [OwnerString, RepoString];
36
43
  }): Promise<void>;
44
+ mergePr({ prId, owner, repository, mergeMethod, }: {
45
+ prId: number;
46
+ owner: string;
47
+ repository: string;
48
+ mergeMethod?: "merge" | "squash" | "rebase";
49
+ }): Promise<void>;
50
+ writeStatusCheck(args: WriteStatusCheckArgs): Promise<{
51
+ id: number;
52
+ head_sha: string;
53
+ node_id: string;
54
+ external_id: string | null;
55
+ url: string;
56
+ html_url: string | null;
57
+ details_url: string | null;
58
+ status: "queued" | "in_progress" | "completed" | "waiting" | "requested" | "pending";
59
+ conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required" | null;
60
+ started_at: string | null;
61
+ completed_at: string | null;
62
+ output: {
63
+ title: string | null;
64
+ summary: string | null;
65
+ text: string | null;
66
+ annotations_count: number;
67
+ annotations_url: string;
68
+ };
69
+ name: string;
70
+ check_suite: {
71
+ id: number;
72
+ } | null;
73
+ app: import("@octokit/openapi-types").components["schemas"]["nullable-integration"];
74
+ pull_requests: import("@octokit/openapi-types").components["schemas"]["pull-request-minimal"][];
75
+ deployment?: import("@octokit/openapi-types").components["schemas"]["deployment-simple"];
76
+ }>;
37
77
  sendPrMessage({ prId, owner, repository, message, }: {
38
78
  prId: number;
39
79
  owner: string;
@@ -41,3 +81,4 @@ export declare class GitHubPRManager {
41
81
  message: string;
42
82
  }): Promise<void>;
43
83
  }
84
+ export {};
@@ -6,29 +6,6 @@ export class GitHubPRManager {
6
6
  constructor(client) {
7
7
  this.client = client;
8
8
  }
9
- /**
10
- *
11
- * update k8s manifest repo with new tag version.
12
- *
13
- * @note `kustomizationFile` must look like this:
14
- *
15
- * ```yaml
16
- * apiVersion: kustomize.config.k8s.io/v1beta1
17
- * kind: Kustomization
18
- * resources:
19
- * - deployment.yaml
20
- * - secrets.yaml
21
- * - service.yaml
22
- * - monitor.yaml
23
- * commonLabels:
24
- * app: instalock-web
25
- * environment: production
26
- * # This part specifically
27
- * images:
28
- * - name: tahminator/instalock-web
29
- * newTag: a70ee0e
30
- * ```
31
- */
32
9
  async updateK8sTagWithPR({ newTag, imageName, kustomizationFilePath, environment, originRepo, manifestRepo, }) {
33
10
  const [manifestOwner, manifestRepository] = manifestRepo;
34
11
  const [originOwner, originRepository] = originRepo;
@@ -91,13 +68,51 @@ export class GitHubPRManager {
91
68
  base: baseBranch,
92
69
  body: `Automated image tag change to ${newTag} for ${imageName} in ${environment} triggered by [${originOwner}/${originRepository}](https://github.com/${originOwner}/${originRepository}).`,
93
70
  });
94
- await this.client.rest.pulls.merge({
71
+ await this.mergePr({
72
+ prId: pr.number,
95
73
  owner: manifestOwner,
96
- repo: manifestRepository,
97
- pull_number: pr.number,
98
- merge_method: "squash",
74
+ repository: manifestRepository,
99
75
  });
100
76
  }
77
+ async mergePr({ prId, owner, repository, mergeMethod = "squash", }) {
78
+ await this.client.rest.pulls.merge({
79
+ owner,
80
+ repo: repository,
81
+ pull_number: prId,
82
+ merge_method: mergeMethod,
83
+ });
84
+ }
85
+ async writeStatusCheck(args) {
86
+ switch (args.action) {
87
+ case "create": {
88
+ const { owner, repository, sha, name, status, conclusion, output, detailsUrl, } = args;
89
+ const { data } = await this.client.rest.checks.create({
90
+ owner,
91
+ repo: repository,
92
+ head_sha: sha,
93
+ name,
94
+ status,
95
+ conclusion,
96
+ output,
97
+ details_url: detailsUrl,
98
+ });
99
+ return data;
100
+ }
101
+ case "update": {
102
+ const { owner, repository, checkRunId, status, conclusion, output, detailsUrl, } = args;
103
+ const { data } = await this.client.rest.checks.update({
104
+ owner,
105
+ repo: repository,
106
+ check_run_id: checkRunId,
107
+ status,
108
+ conclusion,
109
+ output,
110
+ details_url: detailsUrl,
111
+ });
112
+ return data;
113
+ }
114
+ }
115
+ }
101
116
  async sendPrMessage({ prId, owner, repository, message, }) {
102
117
  await this.client.rest.issues.createComment({
103
118
  issue_number: prId,
@@ -5,21 +5,9 @@ export declare class GitHubTagManager {
5
5
  private readonly isExplicitToken;
6
6
  constructor(client: Octokit, isExplicitToken: boolean);
7
7
  private checkToken;
8
- /**
9
- * Returns the latest stable semver tag in the repository.
10
- */
11
8
  getLatestTag({ repositoryOverride, }?: {
12
9
  repositoryOverride?: [OwnerString, RepoString];
13
10
  }): Promise<string | null>;
14
- /**
15
- * Utilizes the GitHub API to create a new tag version in the given repository.
16
- *
17
- * @note You **must** pass in a GitHub token because the regular Github bot token
18
- * cannot trigger actions (due to fear of recursion). You must either provide a GitHub App token or
19
- * a GitHub PAT.
20
- *
21
- * @note you should use `VersioningClient` to generate `nextTag`
22
- */
23
11
  createTag({ nextTag, repositoryOverride, onPreTagCreate, }: {
24
12
  nextTag: string;
25
13
  repositoryOverride?: [OwnerString, RepoString];
@@ -13,9 +13,6 @@ export class GitHubTagManager {
13
13
  throw new Error("You must pass in an explicit GitHub token for this operation. You may either use a PAT or a GitHub App Token");
14
14
  }
15
15
  }
16
- /**
17
- * Returns the latest stable semver tag in the repository.
18
- */
19
16
  async getLatestTag({ repositoryOverride, } = {}) {
20
17
  const [owner, repo] = this.parseRepository(repositoryOverride);
21
18
  const { data: tags } = await this.client.rest.repos.listTags({
@@ -30,15 +27,6 @@ export class GitHubTagManager {
30
27
  })
31
28
  .sort(semver.rcompare)[0] ?? null);
32
29
  }
33
- /**
34
- * Utilizes the GitHub API to create a new tag version in the given repository.
35
- *
36
- * @note You **must** pass in a GitHub token because the regular Github bot token
37
- * cannot trigger actions (due to fear of recursion). You must either provide a GitHub App token or
38
- * a GitHub PAT.
39
- *
40
- * @note you should use `VersioningClient` to generate `nextTag`
41
- */
42
30
  async createTag({ nextTag, repositoryOverride, onPreTagCreate, }) {
43
31
  this.checkToken();
44
32
  const [owner, repo] = this.parseRepository(repositoryOverride);
@@ -0,0 +1,10 @@
1
+ import type { Octokit } from "@octokit/rest";
2
+ export declare class GitHubTeamManager {
3
+ private readonly client;
4
+ constructor(client: Octokit);
5
+ isTeamMember({ org, teamSlug, username, }: {
6
+ org: string;
7
+ teamSlug: string;
8
+ username: string;
9
+ }): Promise<boolean>;
10
+ }
@@ -0,0 +1,25 @@
1
+ export class GitHubTeamManager {
2
+ client;
3
+ constructor(client) {
4
+ this.client = client;
5
+ }
6
+ async isTeamMember({ org, teamSlug, username, }) {
7
+ try {
8
+ const { data } = await this.client.rest.teams.getMembershipForUserInOrg({
9
+ org,
10
+ team_slug: teamSlug,
11
+ username,
12
+ });
13
+ return data.state === "active";
14
+ }
15
+ catch (e) {
16
+ if (e &&
17
+ typeof e === "object" &&
18
+ "status" in e &&
19
+ e.status === 404) {
20
+ return false;
21
+ }
22
+ throw e;
23
+ }
24
+ }
25
+ }
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.73",
6
+ "version": "1.0.74-beta.6f734379",
7
7
  "repository": {
8
8
  "url": "git+https://github.com/tahminator/pipeline.git"
9
9
  },