@tahminator/pipeline 1.0.76 → 1.0.77

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.
Files changed (38) hide show
  1. package/README.md +33 -0
  2. package/dist/gh/client.d.ts +7 -0
  3. package/dist/gh/client.js +9 -0
  4. package/dist/gh/pr/index.d.ts +13 -0
  5. package/dist/gh/pr/index.js +123 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/internal/create-tag/index.js +10 -13
  9. package/dist/internal/upload-npm/beta/index.js +10 -12
  10. package/dist/protobuf/backend/artifact-keeper.d.ts +22 -0
  11. package/dist/protobuf/backend/artifact-keeper.js +233 -0
  12. package/dist/protobuf/backend/index.d.ts +5 -0
  13. package/dist/protobuf/backend/index.js +10 -0
  14. package/dist/protobuf/backend/types.d.ts +9 -0
  15. package/dist/protobuf/backend/types.js +0 -0
  16. package/dist/protobuf/client.d.ts +10 -0
  17. package/dist/protobuf/client.js +115 -0
  18. package/dist/protobuf/index.d.ts +2 -0
  19. package/dist/protobuf/index.js +2 -0
  20. package/dist/protobuf/strategy/go.d.ts +5 -0
  21. package/dist/protobuf/strategy/go.js +4 -0
  22. package/dist/protobuf/strategy/index.d.ts +4 -0
  23. package/dist/protobuf/strategy/index.js +4 -0
  24. package/dist/protobuf/strategy/java.d.ts +5 -0
  25. package/dist/protobuf/strategy/java.js +4 -0
  26. package/dist/protobuf/strategy/rust.d.ts +5 -0
  27. package/dist/protobuf/strategy/rust.js +4 -0
  28. package/dist/protobuf/strategy/types.d.ts +4 -0
  29. package/dist/protobuf/strategy/types.js +0 -0
  30. package/dist/protobuf/types.d.ts +56 -0
  31. package/dist/protobuf/types.js +14 -0
  32. package/dist/utils/cmd.js +1 -1
  33. package/dist/versioning/client.js +3 -0
  34. package/dist/versioning/types.d.ts +4 -1
  35. package/dist/versioning/types.js +2 -1
  36. package/dist/versioning/updating/rust/cargo/index.d.ts +6 -0
  37. package/dist/versioning/updating/rust/cargo/index.js +50 -0
  38. package/package.json +2 -3
package/README.md CHANGED
@@ -38,6 +38,10 @@ import {
38
38
  EnvClient,
39
39
  GitHubClient,
40
40
  NPMClient,
41
+ ProtobufCompilerBackend,
42
+ ProtobufCompilerClient,
43
+ ProtobufSourceLanguage,
44
+ ProtobufTargetLanguage,
41
45
  PulumiClient,
42
46
  SonarScannerClient,
43
47
  Utils,
@@ -53,6 +57,7 @@ Jump to client documentation
53
57
 
54
58
  - [`GitHubClient`](#githubclient)
55
59
  - [`DockerClient`](#dockerclient)
60
+ - [`ProtobufCompilerClient`](#protobufcompilerclient)
56
61
  - [`NPMClient`](#npmclient)
57
62
  - [`SonarScannerClient`](#sonarscannerclient)
58
63
  - [`Utils`](#utils)
@@ -178,6 +183,33 @@ await client.promoteDockerImage({
178
183
  });
179
184
  ```
180
185
 
186
+ ### `ProtobufCompilerClient`
187
+
188
+ Generate protobuf clients with the [Buf CLI](https://buf.build/docs/installation/) and its remote plugins. Java and Rust targets can be published to Artifact Keeper with Maven, Gradle, or Cargo. The client installs Buf globally with npm when necessary.
189
+
190
+ ```ts
191
+ const protobuf = new ProtobufCompilerClient();
192
+
193
+ await protobuf.compile({
194
+ backend: {
195
+ type: ProtobufCompilerBackend.ARTIFACT_KEEPER,
196
+ token: process.env.ARTIFACT_KEEPER_TOKEN!,
197
+ url: process.env.ARTIFACT_KEEPER_URL!,
198
+ username: process.env.ARTIFACT_KEEPER_USERNAME!,
199
+ },
200
+ sourceLanguage: ProtobufSourceLanguage.RUST,
201
+ bufToken: process.env.BUF_TOKEN,
202
+ targetLanguages: {
203
+ [ProtobufTargetLanguage.JAVA]: {
204
+ artifactId: "hello-world-grpc-service",
205
+ groupId: "org.my-org",
206
+ version: "1.0.0",
207
+ },
208
+ },
209
+ protoFilesLocation: "./proto",
210
+ });
211
+ ```
212
+
181
213
  ### `NPMClient`
182
214
 
183
215
  Interface with NPM registry in order to publish packages to `npmjs.com`
@@ -360,6 +392,7 @@ Utilities to compute the next semver tag from GitHub and update versions in code
360
392
  ```ts
361
393
  const gh = await GitHubClient.createWithDefaultCiToken();
362
394
 
395
+ // available strategies: JSTS, JAVA_MAVEN, RUST_CARGO, NONE
363
396
  const versioning = new VersioningClient(gh, VersionUpdatingStrategy.JSTS);
364
397
 
365
398
  // if your repo has no tags yet, this will return `1.0.0`
@@ -118,6 +118,13 @@ export declare class GitHubClient {
118
118
  pull_requests: import("@octokit/openapi-types").components["schemas"]["pull-request-minimal"][];
119
119
  deployment?: import("@octokit/openapi-types").components["schemas"]["deployment-simple"];
120
120
  } | null>;
121
+ /**
122
+ * Uploads a local directory into a repository path using the authenticated GitHub API.
123
+ * Existing files under `directory` that are not present locally are deleted.
124
+ *
125
+ * **PLEASE DO NOT USE THIS FUNCTION UNLESS YOU KNOW WHAT YOU ARE DOING.**
126
+ */
127
+ DANGEROUS__uploadDirectoryContents(...args: Parameters<GitHubPRManager["uploadDirectoryContents"]>): Promise<void>;
121
128
  /**
122
129
  * Confirms whether the given user is an active member of a team within an org.
123
130
  *
package/dist/gh/client.js CHANGED
@@ -126,6 +126,15 @@ export class GitHubClient {
126
126
  statusCheck(...args) {
127
127
  return this.prManager.statusCheck(...args);
128
128
  }
129
+ /**
130
+ * Uploads a local directory into a repository path using the authenticated GitHub API.
131
+ * Existing files under `directory` that are not present locally are deleted.
132
+ *
133
+ * **PLEASE DO NOT USE THIS FUNCTION UNLESS YOU KNOW WHAT YOU ARE DOING.**
134
+ */
135
+ DANGEROUS__uploadDirectoryContents(...args) {
136
+ return this.prManager.uploadDirectoryContents(...args);
137
+ }
129
138
  /**
130
139
  * Confirms whether the given user is an active member of a team within an org.
131
140
  *
@@ -7,6 +7,14 @@ type CheckRunOutput = {
7
7
  summary: string;
8
8
  text?: string;
9
9
  };
10
+ type UploadDirectoryContentsArgs = {
11
+ owner: string;
12
+ repository: string;
13
+ sourceDirectory: string;
14
+ directory?: string;
15
+ message?: string;
16
+ branch?: string;
17
+ };
10
18
  type StatusCheckArgs = {
11
19
  action: "create";
12
20
  owner: string;
@@ -42,6 +50,7 @@ type StatusCheckArgs = {
42
50
  export declare class GitHubPRManager {
43
51
  private readonly client;
44
52
  constructor(client: Octokit);
53
+ uploadDirectoryContents({ owner, repository, sourceDirectory, directory, message, branch, }: UploadDirectoryContentsArgs): Promise<void>;
45
54
  updateK8sTagWithPR({ newTag, imageName, kustomizationFilePath, environment, originRepo, manifestRepo, }: {
46
55
  newTag: string;
47
56
  imageName: string;
@@ -89,5 +98,9 @@ export declare class GitHubPRManager {
89
98
  repository: string;
90
99
  message: string;
91
100
  }): Promise<void>;
101
+ private createDirectoryTree;
102
+ private createDirectoryBlobEntries;
103
+ private getDirectoryPathPrefix;
104
+ private collectDirectoryFiles;
92
105
  }
93
106
  export {};
@@ -1,3 +1,5 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import path from "node:path";
1
3
  import yaml from "yaml";
2
4
  import { Utils } from "../../utils";
3
5
  import { kustomizeSchema } from "./schema";
@@ -6,6 +8,47 @@ export class GitHubPRManager {
6
8
  constructor(client) {
7
9
  this.client = client;
8
10
  }
11
+ async uploadDirectoryContents({ owner, repository, sourceDirectory, directory = "", message = "chore: update generated files", branch, }) {
12
+ const { data: repo } = await this.client.rest.repos.get({
13
+ owner,
14
+ repo: repository,
15
+ });
16
+ const targetBranch = branch ?? repo.default_branch;
17
+ const { data: ref } = await this.client.rest.git.getRef({
18
+ owner,
19
+ repo: repository,
20
+ ref: `heads/${targetBranch}`,
21
+ });
22
+ const { data: baseCommit } = await this.client.rest.git.getCommit({
23
+ owner,
24
+ repo: repository,
25
+ commit_sha: ref.object.sha,
26
+ });
27
+ const treeSha = await this.createDirectoryTree({
28
+ directory,
29
+ owner,
30
+ repository,
31
+ sourceDirectory,
32
+ baseTreeSha: baseCommit.tree.sha,
33
+ });
34
+ if (treeSha === baseCommit.tree.sha) {
35
+ console.log("No directory changes to upload.");
36
+ return;
37
+ }
38
+ const { data: commit } = await this.client.rest.git.createCommit({
39
+ owner,
40
+ repo: repository,
41
+ message,
42
+ tree: treeSha,
43
+ parents: [ref.object.sha],
44
+ });
45
+ await this.client.rest.git.updateRef({
46
+ owner,
47
+ repo: repository,
48
+ ref: `heads/${targetBranch}`,
49
+ sha: commit.sha,
50
+ });
51
+ }
9
52
  async updateK8sTagWithPR({ newTag, imageName, kustomizationFilePath, environment, originRepo, manifestRepo, }) {
10
53
  const [manifestOwner, manifestRepository] = manifestRepo;
11
54
  const [originOwner, originRepository] = originRepo;
@@ -131,4 +174,84 @@ export class GitHubPRManager {
131
174
  body: message,
132
175
  });
133
176
  }
177
+ async createDirectoryTree({ owner, repository, sourceDirectory, directory, baseTreeSha, }) {
178
+ const pathPrefix = this.getDirectoryPathPrefix(directory);
179
+ const files = await this.collectDirectoryFiles(sourceDirectory);
180
+ const localPaths = new Set(files.map(({ relativePath }) => `${pathPrefix}${relativePath}`));
181
+ const { data: existingTree } = await this.client.rest.git.getTree({
182
+ owner,
183
+ repo: repository,
184
+ tree_sha: baseTreeSha,
185
+ recursive: "true",
186
+ });
187
+ const deletionEntries = existingTree.tree.flatMap((entry) => {
188
+ if (entry.type !== "blob" ||
189
+ !entry.path?.startsWith(pathPrefix) ||
190
+ localPaths.has(entry.path)) {
191
+ return [];
192
+ }
193
+ return [
194
+ {
195
+ path: entry.path,
196
+ mode: "100644",
197
+ type: "blob",
198
+ sha: null,
199
+ },
200
+ ];
201
+ });
202
+ const blobEntries = await this.createDirectoryBlobEntries({
203
+ files,
204
+ owner,
205
+ repository,
206
+ pathPrefix,
207
+ });
208
+ const { data: tree } = await this.client.rest.git.createTree({
209
+ owner,
210
+ repo: repository,
211
+ base_tree: baseTreeSha,
212
+ tree: [...deletionEntries, ...blobEntries],
213
+ });
214
+ return tree.sha;
215
+ }
216
+ async createDirectoryBlobEntries({ owner, repository, files, pathPrefix, }) {
217
+ return Promise.all(files.map(async ({ absolutePath, relativePath }) => {
218
+ const content = await readFile(absolutePath);
219
+ const { data: blob } = await this.client.rest.git.createBlob({
220
+ owner,
221
+ repo: repository,
222
+ content: content.toString("base64"),
223
+ encoding: "base64",
224
+ });
225
+ return {
226
+ path: `${pathPrefix}${relativePath}`,
227
+ mode: "100644",
228
+ type: "blob",
229
+ sha: blob.sha,
230
+ };
231
+ }));
232
+ }
233
+ getDirectoryPathPrefix(directory) {
234
+ const normalizedDirectory = directory.replace(/^\/+|\/+$/g, "");
235
+ return normalizedDirectory ? `${normalizedDirectory}/` : "";
236
+ }
237
+ async collectDirectoryFiles(directory, baseDirectory = directory) {
238
+ const files = [];
239
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
240
+ const absolutePath = path.join(directory, entry.name);
241
+ if (entry.isDirectory()) {
242
+ files.push(...(await this.collectDirectoryFiles(absolutePath, baseDirectory)));
243
+ continue;
244
+ }
245
+ if (entry.isFile()) {
246
+ files.push({
247
+ absolutePath,
248
+ relativePath: path
249
+ .relative(baseDirectory, absolutePath)
250
+ .split(path.sep)
251
+ .join("/"),
252
+ });
253
+ }
254
+ }
255
+ return files;
256
+ }
134
257
  }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./docker";
3
3
  export * from "./npm";
4
4
  export * from "./sonar";
5
5
  export * from "./pulumi";
6
+ export * from "./protobuf";
6
7
  export * from "./types";
7
8
  export * from "./env";
8
9
  export * from "./postgres";
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export * from "./docker";
3
3
  export * from "./npm";
4
4
  export * from "./sonar";
5
5
  export * from "./pulumi";
6
+ export * from "./protobuf";
6
7
  export * from "./types";
7
8
  export * from "./env";
8
9
  export * from "./postgres";
@@ -1,14 +1,11 @@
1
- import { EnvClient, EnvClientStrategy } from "../../env";
2
1
  import { GitHubClient } from "../../gh";
3
- import { Utils } from "../../utils";
4
2
  import { VersioningClient, VersionUpdatingStrategy } from "../../versioning";
5
3
  async function main() {
6
- const envClient = EnvClient.create(EnvClientStrategy.GIT_CRYPT);
7
- const { githubAppAppId, githubAppInstallationId, githubAppPrivateKeyB64 } = parseCiEnv(await envClient.readFromEnv(".env.ci"));
4
+ const { githubAppAppId, githubAppInstallationId, githubAppPrivateKey } = parseCiEnv(process.env);
8
5
  const ghClient = await GitHubClient.createWithGithubAppToken({
9
6
  appId: githubAppAppId,
10
7
  installationId: githubAppInstallationId,
11
- privateKey: await Utils.decodeBase64EncodedString(githubAppPrivateKeyB64),
8
+ privateKey: githubAppPrivateKey,
12
9
  });
13
10
  const versioningClient = new VersioningClient(ghClient, VersionUpdatingStrategy.JSTS);
14
11
  const rootPkgJson = await Bun.file("./package.json").json();
@@ -25,27 +22,27 @@ async function main() {
25
22
  }
26
23
  function parseCiEnv(ciEnv) {
27
24
  const githubAppAppId = (() => {
28
- const v = ciEnv["GITHUB_APP_APP_ID"];
25
+ const v = ciEnv["_GITHUB_APP_APP_ID"];
29
26
  if (!v) {
30
- throw new Error("Missing GITHUB_APP_APP_ID from .env.ci");
27
+ throw new Error("Missing _GITHUB_APP_APP_ID from env");
31
28
  }
32
29
  return v;
33
30
  })();
34
31
  const githubAppInstallationId = (() => {
35
- const v = ciEnv["GITHUB_APP_INSTALLATION_ID"];
32
+ const v = ciEnv["_GITHUB_APP_INSTALLATION_ID"];
36
33
  if (!v) {
37
- throw new Error("Missing GITHUB_APP_INSTALLATION_ID from .env.ci");
34
+ throw new Error("Missing _GITHUB_APP_INSTALLATION_ID from env");
38
35
  }
39
36
  return v;
40
37
  })();
41
- const githubAppPrivateKeyB64 = (() => {
42
- const v = ciEnv["GITHUB_APP_PRIVATE_KEY_B64"];
38
+ const githubAppPrivateKey = (() => {
39
+ const v = ciEnv["_GITHUB_APP_PEM_CONTENT"];
43
40
  if (!v) {
44
- throw new Error("Missing GITHUB_APP_PRIVATE_KEY_B64 from .env.ci");
41
+ throw new Error("Missing _GITHUB_APP_PEM_CONTENT from env");
45
42
  }
46
43
  return v;
47
44
  })();
48
- return { githubAppAppId, githubAppInstallationId, githubAppPrivateKeyB64 };
45
+ return { githubAppAppId, githubAppInstallationId, githubAppPrivateKey };
49
46
  }
50
47
  main()
51
48
  .then(() => {
@@ -1,6 +1,5 @@
1
1
  import yargs from "yargs";
2
2
  import { hideBin } from "yargs/helpers";
3
- import { EnvClient, EnvClientStrategy } from "../../../env";
4
3
  import { GitHubClient } from "../../../gh";
5
4
  import { NPMClient } from "../../../npm";
6
5
  import { Utils } from "../../../utils";
@@ -18,12 +17,11 @@ const { sha, prId } = await yargs(hideBin(process.argv))
18
17
  .strict()
19
18
  .parse();
20
19
  async function main() {
21
- const envClient = EnvClient.create(EnvClientStrategy.GIT_CRYPT);
22
- const { githubAppAppId, githubAppInstallationId, githubAppPrivateKeyB64 } = parseCiEnv(await envClient.readFromEnv(".env.ci"));
20
+ const { githubAppAppId, githubAppInstallationId, githubAppPrivateKey } = parseCiEnv(process.env);
23
21
  const ghClient = await GitHubClient.createWithGithubAppToken({
24
22
  appId: githubAppAppId,
25
23
  installationId: githubAppInstallationId,
26
- privateKey: await Utils.decodeBase64EncodedString(githubAppPrivateKeyB64),
24
+ privateKey: githubAppPrivateKey,
27
25
  });
28
26
  const npmClient = await NPMClient.create();
29
27
  const versioningClient = new VersioningClient(ghClient, VersionUpdatingStrategy.JSTS);
@@ -55,27 +53,27 @@ async function getShortSha(sha) {
55
53
  }
56
54
  function parseCiEnv(ciEnv) {
57
55
  const githubAppAppId = (() => {
58
- const v = ciEnv["GITHUB_APP_APP_ID"];
56
+ const v = ciEnv["_GITHUB_APP_APP_ID"];
59
57
  if (!v) {
60
- throw new Error("Missing GITHUB_APP_APP_ID from .env.ci");
58
+ throw new Error("Missing _GITHUB_APP_APP_ID from env");
61
59
  }
62
60
  return v;
63
61
  })();
64
62
  const githubAppInstallationId = (() => {
65
- const v = ciEnv["GITHUB_APP_INSTALLATION_ID"];
63
+ const v = ciEnv["_GITHUB_APP_INSTALLATION_ID"];
66
64
  if (!v) {
67
- throw new Error("Missing GITHUB_APP_INSTALLATION_ID from .env.ci");
65
+ throw new Error("Missing _GITHUB_APP_INSTALLATION_ID from env");
68
66
  }
69
67
  return v;
70
68
  })();
71
- const githubAppPrivateKeyB64 = (() => {
72
- const v = ciEnv["GITHUB_APP_PRIVATE_KEY_B64"];
69
+ const githubAppPrivateKey = (() => {
70
+ const v = ciEnv["_GITHUB_APP_PEM_CONTENT"];
73
71
  if (!v) {
74
- throw new Error("Missing GITHUB_APP_PRIVATE_KEY_B64 from .env.ci");
72
+ throw new Error("Missing _GITHUB_APP_PEM_CONTENT from env");
75
73
  }
76
74
  return v;
77
75
  })();
78
- return { githubAppAppId, githubAppInstallationId, githubAppPrivateKeyB64 };
76
+ return { githubAppAppId, githubAppInstallationId, githubAppPrivateKey };
79
77
  }
80
78
  main()
81
79
  .then(() => {
@@ -0,0 +1,22 @@
1
+ import type { IProtobufCompilerBackendStrategy, ProtobufCompilerBackendPublishArgs } from "./types";
2
+ import { type ProtobufArtifactKeeperBackend } from "../types";
3
+ export declare class ArtifactKeeperProtobufCompilerBackend implements IProtobufCompilerBackendStrategy {
4
+ private readonly config;
5
+ constructor(config: ProtobufArtifactKeeperBackend);
6
+ publish({ generatedDirectory, targetLanguage, options, }: ProtobufCompilerBackendPublishArgs): Promise<void>;
7
+ private publishJava;
8
+ private publishMaven;
9
+ private publishGradle;
10
+ private publishRust;
11
+ private moveJavaSources;
12
+ private findRustFiles;
13
+ private createMavenPom;
14
+ private createMavenSettings;
15
+ private createGradleBuild;
16
+ private createCargoToml;
17
+ private createRustLibrary;
18
+ private escapeXml;
19
+ private get artifactKeeperUrl();
20
+ private get mavenRepositoryUrl();
21
+ private get cargoIndexUrl();
22
+ }
@@ -0,0 +1,233 @@
1
+ import { $ } from "bun";
2
+ import { mkdir, mkdtemp, readdir, rename, rm, writeFile, } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import { ProtobufTargetLanguage, } from "../types";
6
+ const DEFAULT_PROTOBUF_JAVA_VERSION = "4.32.1";
7
+ const DEFAULT_PROST_VERSION = "0.14";
8
+ export class ArtifactKeeperProtobufCompilerBackend {
9
+ config;
10
+ constructor(config) {
11
+ this.config = config;
12
+ }
13
+ async publish({ generatedDirectory, targetLanguage, options, }) {
14
+ switch (targetLanguage) {
15
+ case ProtobufTargetLanguage.JAVA:
16
+ await this.publishJava({
17
+ generatedDirectory,
18
+ options: options,
19
+ });
20
+ return;
21
+ case ProtobufTargetLanguage.RUST:
22
+ await this.publishRust({
23
+ generatedDirectory,
24
+ options: options,
25
+ });
26
+ return;
27
+ case ProtobufTargetLanguage.GO:
28
+ throw new Error("Artifact Keeper Go module publishing requires its upload workflow; only Go module consumption has been configured.");
29
+ }
30
+ }
31
+ async publishJava({ generatedDirectory, options, }) {
32
+ await this.moveJavaSources(generatedDirectory);
33
+ if (options.buildTool === "gradle") {
34
+ await this.publishGradle({ generatedDirectory, options });
35
+ return;
36
+ }
37
+ await this.publishMaven({ generatedDirectory, options });
38
+ }
39
+ async publishMaven({ generatedDirectory, options, }) {
40
+ await writeFile(path.join(generatedDirectory, "pom.xml"), this.createMavenPom(options));
41
+ const settingsDirectory = await mkdtemp(path.join(tmpdir(), "artifact-keeper-maven-"));
42
+ const settingsPath = path.join(settingsDirectory, "settings.xml");
43
+ await writeFile(settingsPath, this.createMavenSettings());
44
+ try {
45
+ await $ `mvn --settings ${settingsPath} deploy`.cwd(generatedDirectory);
46
+ }
47
+ finally {
48
+ await rm(settingsDirectory, { force: true, recursive: true });
49
+ }
50
+ }
51
+ async publishGradle({ generatedDirectory, options, }) {
52
+ await Promise.all([
53
+ writeFile(path.join(generatedDirectory, "build.gradle"), this.createGradleBuild(options)),
54
+ writeFile(path.join(generatedDirectory, "settings.gradle"), `rootProject.name = "${options.artifactId}"\n`),
55
+ ]);
56
+ await $ `gradle -PmavenUsername=${this.config.username} -PmavenPassword=${this.config.token} publish`.cwd(generatedDirectory);
57
+ }
58
+ async publishRust({ generatedDirectory, options, }) {
59
+ const generatedFiles = await this.findRustFiles(generatedDirectory);
60
+ await mkdir(path.join(generatedDirectory, "src"), { recursive: true });
61
+ await Promise.all([
62
+ writeFile(path.join(generatedDirectory, "Cargo.toml"), this.createCargoToml(options)),
63
+ writeFile(path.join(generatedDirectory, "src", "lib.rs"), this.createRustLibrary(generatedFiles)),
64
+ ]);
65
+ await $ `cargo publish --registry cargo --token ${this.config.token} --config ${`registries.cargo.index=${this.cargoIndexUrl}`}`.cwd(generatedDirectory);
66
+ }
67
+ async moveJavaSources(generatedDirectory) {
68
+ const sourceDirectory = path.join(generatedDirectory, "src", "main", "java");
69
+ await mkdir(sourceDirectory, { recursive: true });
70
+ for (const entry of await readdir(generatedDirectory, {
71
+ withFileTypes: true,
72
+ })) {
73
+ if (entry.name === "src" ||
74
+ entry.name === "pom.xml" ||
75
+ entry.name === "build.gradle" ||
76
+ entry.name === "settings.gradle") {
77
+ continue;
78
+ }
79
+ await rename(path.join(generatedDirectory, entry.name), path.join(sourceDirectory, entry.name));
80
+ }
81
+ }
82
+ async findRustFiles(directory, baseDirectory = directory) {
83
+ const files = [];
84
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
85
+ const filePath = path.join(directory, entry.name);
86
+ if (entry.isDirectory()) {
87
+ if (entry.name === "src")
88
+ continue;
89
+ files.push(...(await this.findRustFiles(filePath, baseDirectory)));
90
+ continue;
91
+ }
92
+ if (entry.isFile() && entry.name.endsWith(".rs")) {
93
+ files.push(path.relative(baseDirectory, filePath).split(path.sep).join("/"));
94
+ }
95
+ }
96
+ return files;
97
+ }
98
+ createMavenPom(options) {
99
+ const protobufVersion = options.protobufJavaVersion ?? DEFAULT_PROTOBUF_JAVA_VERSION;
100
+ return `<?xml version="1.0" encoding="UTF-8"?>
101
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
102
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
103
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
104
+ <modelVersion>4.0.0</modelVersion>
105
+ <groupId>${this.escapeXml(options.groupId)}</groupId>
106
+ <artifactId>${this.escapeXml(options.artifactId)}</artifactId>
107
+ <version>${this.escapeXml(options.version)}</version>
108
+ <properties>
109
+ <maven.compiler.release>17</maven.compiler.release>
110
+ </properties>
111
+ <dependencies>
112
+ <dependency>
113
+ <groupId>com.google.protobuf</groupId>
114
+ <artifactId>protobuf-java</artifactId>
115
+ <version>${this.escapeXml(protobufVersion)}</version>
116
+ </dependency>
117
+ </dependencies>
118
+ <distributionManagement>
119
+ <repository>
120
+ <id>maven</id>
121
+ <url>${this.escapeXml(this.mavenRepositoryUrl)}</url>
122
+ </repository>
123
+ </distributionManagement>
124
+ </project>
125
+ `;
126
+ }
127
+ createMavenSettings() {
128
+ return `<settings>
129
+ <servers>
130
+ <server>
131
+ <id>maven</id>
132
+ <username>${this.escapeXml(this.config.username)}</username>
133
+ <password>${this.escapeXml(this.config.token)}</password>
134
+ </server>
135
+ </servers>
136
+ </settings>
137
+ `;
138
+ }
139
+ createGradleBuild(options) {
140
+ const protobufVersion = options.protobufJavaVersion ?? DEFAULT_PROTOBUF_JAVA_VERSION;
141
+ return `plugins {
142
+ id "java"
143
+ id "maven-publish"
144
+ }
145
+
146
+ group = "${options.groupId}"
147
+ version = "${options.version}"
148
+
149
+ repositories {
150
+ mavenCentral()
151
+ }
152
+
153
+ dependencies {
154
+ implementation "com.google.protobuf:protobuf-java:${protobufVersion}"
155
+ }
156
+
157
+ java {
158
+ toolchain {
159
+ languageVersion = JavaLanguageVersion.of(17)
160
+ }
161
+ }
162
+
163
+ publishing {
164
+ publications {
165
+ mavenJava(MavenPublication) {
166
+ from components.java
167
+ artifactId = "${options.artifactId}"
168
+ }
169
+ }
170
+ repositories {
171
+ maven {
172
+ url = uri("${this.mavenRepositoryUrl}")
173
+ credentials {
174
+ username = project.findProperty("mavenUsername")
175
+ password = project.findProperty("mavenPassword")
176
+ }
177
+ }
178
+ }
179
+ }
180
+ `;
181
+ }
182
+ createCargoToml(options) {
183
+ const prostVersion = options.prostVersion ?? DEFAULT_PROST_VERSION;
184
+ return `[package]
185
+ name = "${options.crateName}"
186
+ version = "${options.version}"
187
+ edition = "2021"
188
+
189
+ [dependencies]
190
+ prost = "${prostVersion}"
191
+ `;
192
+ }
193
+ createRustLibrary(generatedFiles) {
194
+ return generatedFiles
195
+ .map((relativePath) => {
196
+ const moduleName = relativePath
197
+ .replace(/\.rs$/, "")
198
+ .replace(/[^a-zA-Z0-9_]/g, "_");
199
+ return `pub mod ${moduleName} {
200
+ include!(concat!(env!("CARGO_MANIFEST_DIR"), "/${relativePath}"));
201
+ }
202
+ `;
203
+ })
204
+ .join("\n");
205
+ }
206
+ escapeXml(value) {
207
+ return value.replace(/[<>&'"]/g, (character) => {
208
+ switch (character) {
209
+ case "<":
210
+ return "&lt;";
211
+ case ">":
212
+ return "&gt;";
213
+ case "&":
214
+ return "&amp;";
215
+ case "'":
216
+ return "&apos;";
217
+ case '"':
218
+ return "&quot;";
219
+ default:
220
+ return character;
221
+ }
222
+ });
223
+ }
224
+ get artifactKeeperUrl() {
225
+ return this.config.url.replace(/\/$/, "");
226
+ }
227
+ get mavenRepositoryUrl() {
228
+ return `${this.artifactKeeperUrl}/maven/maven/`;
229
+ }
230
+ get cargoIndexUrl() {
231
+ return `${this.artifactKeeperUrl}/cargo/cargo/index`;
232
+ }
233
+ }
@@ -0,0 +1,5 @@
1
+ import type { IProtobufCompilerBackendStrategy } from "./types";
2
+ import { type ProtobufCompilerBackendConfig } from "../types";
3
+ export * from "./artifact-keeper";
4
+ export * from "./types";
5
+ export declare function createProtobufCompilerBackendStrategy(backend: ProtobufCompilerBackendConfig): IProtobufCompilerBackendStrategy;
@@ -0,0 +1,10 @@
1
+ import { ProtobufCompilerBackend, } from "../types";
2
+ import { ArtifactKeeperProtobufCompilerBackend } from "./artifact-keeper";
3
+ export * from "./artifact-keeper";
4
+ export * from "./types";
5
+ const BACKEND_STRATEGY_FACTORIES = {
6
+ [ProtobufCompilerBackend.ARTIFACT_KEEPER]: (backend) => new ArtifactKeeperProtobufCompilerBackend(backend),
7
+ };
8
+ export function createProtobufCompilerBackendStrategy(backend) {
9
+ return BACKEND_STRATEGY_FACTORIES[backend.type](backend);
10
+ }
@@ -0,0 +1,9 @@
1
+ import type { ProtobufTargetLanguage, ProtobufTargetLanguageOptions } from "../types";
2
+ export interface ProtobufCompilerBackendPublishArgs {
3
+ generatedDirectory: string;
4
+ targetLanguage: ProtobufTargetLanguage;
5
+ options: ProtobufTargetLanguageOptions;
6
+ }
7
+ export interface IProtobufCompilerBackendStrategy {
8
+ publish(args: ProtobufCompilerBackendPublishArgs): Promise<void>;
9
+ }
File without changes
@@ -0,0 +1,10 @@
1
+ import { type ProtobufCompilerCompileArgs, type ProtobufCompilerCompileResult } from "./types";
2
+ export declare class ProtobufCompilerClient {
3
+ compile({ backend, bufToken, targetLanguages, protoFilesLocation, outputDirectory, }: ProtobufCompilerCompileArgs): Promise<ProtobufCompilerCompileResult>;
4
+ installToolchains(): Promise<void>;
5
+ private generateWithBuf;
6
+ private getCompilationTargets;
7
+ private getConfiguredTargetLanguages;
8
+ private getBufGenerateInput;
9
+ private findBufModuleDirectory;
10
+ }
@@ -0,0 +1,115 @@
1
+ import { $ } from "bun";
2
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import yaml from "yaml";
6
+ import { isCmdAvailable } from "../utils/cmd";
7
+ import { createProtobufCompilerBackendStrategy } from "./backend";
8
+ import { GoProtobufTargetLanguageStrategy, JavaProtobufTargetLanguageStrategy, RustProtobufTargetLanguageStrategy, } from "./strategy";
9
+ import { ProtobufTargetLanguage, } from "./types";
10
+ const TARGET_LANGUAGE_STRATEGY_FACTORIES = {
11
+ [ProtobufTargetLanguage.JAVA]: () => new JavaProtobufTargetLanguageStrategy(),
12
+ [ProtobufTargetLanguage.GO]: () => new GoProtobufTargetLanguageStrategy(),
13
+ [ProtobufTargetLanguage.RUST]: () => new RustProtobufTargetLanguageStrategy(),
14
+ };
15
+ export class ProtobufCompilerClient {
16
+ async compile({ backend, bufToken, targetLanguages, protoFilesLocation, outputDirectory, }) {
17
+ await this.installToolchains();
18
+ const outputDirectoryPath = path.resolve(outputDirectory ??
19
+ (await mkdtemp(path.join(tmpdir(), "protobuf-compiler-"))));
20
+ const targets = this.getCompilationTargets(targetLanguages);
21
+ const compilerBackend = backend ? createProtobufCompilerBackendStrategy(backend) : undefined;
22
+ await mkdir(outputDirectoryPath, { recursive: true });
23
+ await this.generateWithBuf({
24
+ bufToken,
25
+ outputDirectory: outputDirectoryPath,
26
+ protoFilesLocation,
27
+ targetStrategies: targets.map(({ strategy }) => strategy),
28
+ });
29
+ const generatedDirectories = {};
30
+ for (const { language, strategy } of targets) {
31
+ generatedDirectories[language] = path.join(outputDirectoryPath, strategy.outputDirectoryName);
32
+ }
33
+ if (compilerBackend) {
34
+ for (const { language, options } of targets) {
35
+ const generatedDirectory = generatedDirectories[language];
36
+ if (!generatedDirectory) {
37
+ throw new Error("Generated code directory was not created.");
38
+ }
39
+ await compilerBackend.publish({
40
+ generatedDirectory,
41
+ options,
42
+ targetLanguage: language,
43
+ });
44
+ }
45
+ }
46
+ return { generatedDirectories, outputDirectory: outputDirectoryPath };
47
+ }
48
+ async installToolchains() {
49
+ if (!(await isCmdAvailable("buf"))) {
50
+ console.log("Buf is missing, installing globally via NPM...");
51
+ await $ `npm i -g @bufbuild/buf`;
52
+ }
53
+ }
54
+ async generateWithBuf({ bufToken, protoFilesLocation, outputDirectory, targetStrategies, }) {
55
+ const templateDirectory = await mkdtemp(path.join(tmpdir(), "protobuf-buf-template-"));
56
+ const templatePath = path.join(templateDirectory, "buf.gen.yaml");
57
+ const { input, pathFilter } = await this.getBufGenerateInput(protoFilesLocation);
58
+ const pathFlag = pathFilter ? ["--path", pathFilter] : [];
59
+ await writeFile(templatePath, yaml.stringify({
60
+ version: "v2",
61
+ plugins: targetStrategies.map((strategy) => ({
62
+ out: strategy.outputDirectoryName,
63
+ remote: strategy.remotePlugin,
64
+ })),
65
+ }));
66
+ try {
67
+ const command = bufToken ?
68
+ $.env({
69
+ ...process.env,
70
+ BUF_TOKEN: bufToken,
71
+ }) `buf generate ${input} --template ${templatePath} ${pathFlag}`.cwd(outputDirectory)
72
+ : $ `buf generate ${input} --template ${templatePath} ${pathFlag}`.cwd(outputDirectory);
73
+ await command;
74
+ }
75
+ finally {
76
+ await rm(templateDirectory, { force: true, recursive: true });
77
+ }
78
+ }
79
+ getCompilationTargets(targetLanguages) {
80
+ return this.getConfiguredTargetLanguages(targetLanguages).map(([language, options]) => ({
81
+ language,
82
+ options,
83
+ strategy: TARGET_LANGUAGE_STRATEGY_FACTORIES[language](),
84
+ }));
85
+ }
86
+ getConfiguredTargetLanguages(targetLanguages) {
87
+ return Object.entries(targetLanguages).flatMap(([language, options]) => options ? [[Number(language), options]] : []);
88
+ }
89
+ async getBufGenerateInput(protoFilesLocation) {
90
+ const resolvedLocation = path.resolve(protoFilesLocation);
91
+ const stats = await Bun.file(resolvedLocation).stat();
92
+ if (stats.isFile() && !resolvedLocation.endsWith(".proto")) {
93
+ throw new Error(`${protoFilesLocation} is not a .proto file.`);
94
+ }
95
+ const sourceDirectory = stats.isFile() ? path.dirname(resolvedLocation) : resolvedLocation;
96
+ const moduleDirectory = (await this.findBufModuleDirectory(sourceDirectory)) ?? sourceDirectory;
97
+ return {
98
+ input: moduleDirectory,
99
+ pathFilter: moduleDirectory === resolvedLocation ? undefined : resolvedLocation,
100
+ };
101
+ }
102
+ async findBufModuleDirectory(directory) {
103
+ let currentDirectory = path.resolve(directory);
104
+ while (true) {
105
+ if (await Bun.file(path.join(currentDirectory, "buf.yaml")).exists()) {
106
+ return currentDirectory;
107
+ }
108
+ const parentDirectory = path.dirname(currentDirectory);
109
+ if (parentDirectory === currentDirectory) {
110
+ return null;
111
+ }
112
+ currentDirectory = parentDirectory;
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./client";
2
+ export * from "./types";
@@ -0,0 +1,2 @@
1
+ export * from "./client";
2
+ export * from "./types";
@@ -0,0 +1,5 @@
1
+ import type { IProtobufTargetLanguageStrategy } from "./types";
2
+ export declare class GoProtobufTargetLanguageStrategy implements IProtobufTargetLanguageStrategy {
3
+ readonly outputDirectoryName = "go";
4
+ readonly remotePlugin = "buf.build/protocolbuffers/go";
5
+ }
@@ -0,0 +1,4 @@
1
+ export class GoProtobufTargetLanguageStrategy {
2
+ outputDirectoryName = "go";
3
+ remotePlugin = "buf.build/protocolbuffers/go";
4
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./go";
2
+ export * from "./java";
3
+ export * from "./rust";
4
+ export * from "./types";
@@ -0,0 +1,4 @@
1
+ export * from "./go";
2
+ export * from "./java";
3
+ export * from "./rust";
4
+ export * from "./types";
@@ -0,0 +1,5 @@
1
+ import type { IProtobufTargetLanguageStrategy } from "./types";
2
+ export declare class JavaProtobufTargetLanguageStrategy implements IProtobufTargetLanguageStrategy {
3
+ readonly outputDirectoryName = "java";
4
+ readonly remotePlugin = "buf.build/protocolbuffers/java";
5
+ }
@@ -0,0 +1,4 @@
1
+ export class JavaProtobufTargetLanguageStrategy {
2
+ outputDirectoryName = "java";
3
+ remotePlugin = "buf.build/protocolbuffers/java";
4
+ }
@@ -0,0 +1,5 @@
1
+ import type { IProtobufTargetLanguageStrategy } from "./types";
2
+ export declare class RustProtobufTargetLanguageStrategy implements IProtobufTargetLanguageStrategy {
3
+ readonly outputDirectoryName = "rust";
4
+ readonly remotePlugin = "buf.build/community/neoeinstein-prost";
5
+ }
@@ -0,0 +1,4 @@
1
+ export class RustProtobufTargetLanguageStrategy {
2
+ outputDirectoryName = "rust";
3
+ remotePlugin = "buf.build/community/neoeinstein-prost";
4
+ }
@@ -0,0 +1,4 @@
1
+ export interface IProtobufTargetLanguageStrategy {
2
+ readonly outputDirectoryName: string;
3
+ readonly remotePlugin: string;
4
+ }
File without changes
@@ -0,0 +1,56 @@
1
+ export declare enum ProtobufCompilerBackend {
2
+ ARTIFACT_KEEPER = 0
3
+ }
4
+ export declare enum ProtobufSourceLanguage {
5
+ RUST = 0
6
+ }
7
+ export declare enum ProtobufTargetLanguage {
8
+ JAVA = 0,
9
+ RUST = 1,
10
+ GO = 2
11
+ }
12
+ export interface ProtobufArtifactKeeperBackend {
13
+ type: ProtobufCompilerBackend.ARTIFACT_KEEPER;
14
+ url: string;
15
+ username: string;
16
+ token: string;
17
+ }
18
+ export type ProtobufCompilerBackendConfig = ProtobufArtifactKeeperBackend;
19
+ export interface ProtobufJavaTargetLanguageOptions {
20
+ groupId: string;
21
+ artifactId: string;
22
+ version: string;
23
+ buildTool?: "maven" | "gradle";
24
+ protobufJavaVersion?: string;
25
+ }
26
+ export interface ProtobufRustTargetLanguageOptions {
27
+ crateName: string;
28
+ version: string;
29
+ prostVersion?: string;
30
+ }
31
+ export interface ProtobufGoTargetLanguageOptions {
32
+ modulePath: string;
33
+ version: string;
34
+ }
35
+ export type ProtobufTargetLanguageOptions = ProtobufGoTargetLanguageOptions | ProtobufJavaTargetLanguageOptions | ProtobufRustTargetLanguageOptions;
36
+ export type ProtobufTargetLanguages = {
37
+ [ProtobufTargetLanguage.JAVA]?: ProtobufJavaTargetLanguageOptions;
38
+ [ProtobufTargetLanguage.RUST]?: ProtobufRustTargetLanguageOptions;
39
+ [ProtobufTargetLanguage.GO]?: ProtobufGoTargetLanguageOptions;
40
+ };
41
+ export interface ProtobufCompilerCompileArgs {
42
+ /** All targets are published through this one backend. */
43
+ backend?: ProtobufCompilerBackendConfig;
44
+ sourceLanguage: ProtobufSourceLanguage;
45
+ /** Optional BSR API token used for Buf remote plugins. */
46
+ bufToken?: string;
47
+ targetLanguages: ProtobufTargetLanguages;
48
+ /** Directory containing proto files, or one specific `.proto` file. */
49
+ protoFilesLocation: string;
50
+ /** Defaults to a temporary directory. */
51
+ outputDirectory?: string;
52
+ }
53
+ export interface ProtobufCompilerCompileResult {
54
+ outputDirectory: string;
55
+ generatedDirectories: Partial<Record<ProtobufTargetLanguage, string>>;
56
+ }
@@ -0,0 +1,14 @@
1
+ export var ProtobufCompilerBackend;
2
+ (function (ProtobufCompilerBackend) {
3
+ ProtobufCompilerBackend[ProtobufCompilerBackend["ARTIFACT_KEEPER"] = 0] = "ARTIFACT_KEEPER";
4
+ })(ProtobufCompilerBackend || (ProtobufCompilerBackend = {}));
5
+ export var ProtobufSourceLanguage;
6
+ (function (ProtobufSourceLanguage) {
7
+ ProtobufSourceLanguage[ProtobufSourceLanguage["RUST"] = 0] = "RUST";
8
+ })(ProtobufSourceLanguage || (ProtobufSourceLanguage = {}));
9
+ export var ProtobufTargetLanguage;
10
+ (function (ProtobufTargetLanguage) {
11
+ ProtobufTargetLanguage[ProtobufTargetLanguage["JAVA"] = 0] = "JAVA";
12
+ ProtobufTargetLanguage[ProtobufTargetLanguage["RUST"] = 1] = "RUST";
13
+ ProtobufTargetLanguage[ProtobufTargetLanguage["GO"] = 2] = "GO";
14
+ })(ProtobufTargetLanguage || (ProtobufTargetLanguage = {}));
package/dist/utils/cmd.js CHANGED
@@ -3,7 +3,7 @@ import { fromPromise } from "neverthrow";
3
3
  const GENERIC_ERROR = 1;
4
4
  const SUCCESS = 0;
5
5
  export async function isCmdAvailable(cmd) {
6
- return ((await fromPromise($ `which -v ${cmd}`.quiet(), (e) => e)
6
+ return ((await fromPromise($ `which ${cmd}`.quiet(), (e) => e)
7
7
  .map((s) => s.exitCode)
8
8
  .unwrapOr(GENERIC_ERROR)) === SUCCESS);
9
9
  }
@@ -3,6 +3,7 @@ import { VersionUpdatingStrategy, } from "./types";
3
3
  import { JavaMavenVersioningClient } from "./updating/java/maven";
4
4
  import { JavascriptPackageJsonVersioningClient } from "./updating/jsts";
5
5
  import { NoneVersioningClient } from "./updating/none";
6
+ import { RustCargoVersioningClient } from "./updating/rust/cargo";
6
7
  export class VersioningClient {
7
8
  githubClient;
8
9
  static INITIAL_VERSION = "1.0.0";
@@ -17,6 +18,8 @@ export class VersioningClient {
17
18
  return new JavascriptPackageJsonVersioningClient();
18
19
  case VersionUpdatingStrategy.JAVA_MAVEN:
19
20
  return new JavaMavenVersioningClient();
21
+ case VersionUpdatingStrategy.RUST_CARGO:
22
+ return new RustCargoVersioningClient();
20
23
  case VersionUpdatingStrategy.NONE:
21
24
  return new NoneVersioningClient();
22
25
  }
@@ -1,7 +1,8 @@
1
1
  export declare enum VersionUpdatingStrategy {
2
2
  JSTS = 0,
3
3
  JAVA_MAVEN = 1,
4
- NONE = 2
4
+ RUST_CARGO = 2,
5
+ NONE = 3
5
6
  }
6
7
  export interface IVersionUpdatingClient {
7
8
  update(version: string): Promise<void>;
@@ -37,3 +38,5 @@ export interface IJavascriptPackageJsonVersionUpdatingClient extends IVersionUpd
37
38
  }
38
39
  export interface IJavaMavenVersionUpdatingClient extends IVersionUpdatingClient {
39
40
  }
41
+ export interface IRustCargoVersionUpdatingClient extends IVersionUpdatingClient {
42
+ }
@@ -2,5 +2,6 @@ export var VersionUpdatingStrategy;
2
2
  (function (VersionUpdatingStrategy) {
3
3
  VersionUpdatingStrategy[VersionUpdatingStrategy["JSTS"] = 0] = "JSTS";
4
4
  VersionUpdatingStrategy[VersionUpdatingStrategy["JAVA_MAVEN"] = 1] = "JAVA_MAVEN";
5
- VersionUpdatingStrategy[VersionUpdatingStrategy["NONE"] = 2] = "NONE";
5
+ VersionUpdatingStrategy[VersionUpdatingStrategy["RUST_CARGO"] = 2] = "RUST_CARGO";
6
+ VersionUpdatingStrategy[VersionUpdatingStrategy["NONE"] = 3] = "NONE";
6
7
  })(VersionUpdatingStrategy || (VersionUpdatingStrategy = {}));
@@ -0,0 +1,6 @@
1
+ import type { IRustCargoVersionUpdatingClient } from "../../../types";
2
+ import { BaseVersionUpdatingClient } from "../../base";
3
+ export declare class RustCargoVersioningClient extends BaseVersionUpdatingClient implements IRustCargoVersionUpdatingClient {
4
+ update(version: string): Promise<void>;
5
+ private updatePackageVersionInCargoToml;
6
+ }
@@ -0,0 +1,50 @@
1
+ import { BaseVersionUpdatingClient } from "../../base";
2
+ export class RustCargoVersioningClient extends BaseVersionUpdatingClient {
3
+ async update(version) {
4
+ const files = await this.findFiles("**/Cargo.toml");
5
+ for (const fileLocation of files) {
6
+ const file = Bun.file(fileLocation);
7
+ const oldCargoToml = await file.text();
8
+ const newCargoToml = this.updatePackageVersionInCargoToml(oldCargoToml, version);
9
+ if (!newCargoToml) {
10
+ continue;
11
+ }
12
+ await Bun.write(fileLocation, newCargoToml);
13
+ this.logFileLocationUpdated(fileLocation, version);
14
+ }
15
+ }
16
+ updatePackageVersionInCargoToml(cargoToml, version) {
17
+ const lines = cargoToml.split("\n");
18
+ let insidePackageSection = false;
19
+ let packageSectionFound = false;
20
+ let packageVersionLineIndex = -1;
21
+ for (let index = 0; index < lines.length; index++) {
22
+ const line = lines[index];
23
+ if (!line)
24
+ continue;
25
+ const sectionMatch = line.match(/^\s*\[(.+)\]\s*$/);
26
+ if (sectionMatch) {
27
+ const sectionName = sectionMatch[1]?.trim();
28
+ insidePackageSection = sectionName === "package";
29
+ if (insidePackageSection) {
30
+ packageSectionFound = true;
31
+ }
32
+ continue;
33
+ }
34
+ if (!insidePackageSection)
35
+ continue;
36
+ if (/^\s*version(?:\.[A-Za-z0-9_-]+)?\s*=/.test(line)) {
37
+ packageVersionLineIndex = index;
38
+ break;
39
+ }
40
+ }
41
+ if (!packageSectionFound) {
42
+ return null;
43
+ }
44
+ if (packageVersionLineIndex === -1) {
45
+ throw new Error("Can't find version key inside of package section in Cargo.toml");
46
+ }
47
+ lines[packageVersionLineIndex] = `version = "${version}"`;
48
+ return `${lines.join("\n").replace(/\n*$/, "\n")}`;
49
+ }
50
+ }
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.76",
6
+ "version": "1.0.77",
7
7
  "repository": {
8
8
  "url": "git+https://github.com/tahminator/pipeline.git"
9
9
  },
@@ -21,14 +21,13 @@
21
21
  },
22
22
  "scripts": {
23
23
  "typecheck": "tsc --noEmit",
24
- "test": "bun run typecheck && bun run fmt && bun run lint",
24
+ "test": "bun run typecheck && bun run fmt && bun run lint && bun test",
25
25
  "fmt": "bun run prettier",
26
26
  "lint": "bun run eslint",
27
27
  "eslint": "eslint .",
28
28
  "eslint:fix": "eslint . --fix",
29
29
  "prettier": "prettier --check .",
30
30
  "prettier:fix": "prettier --write .",
31
- "vt": "vitest run",
32
31
  "build": "tsc -p tsconfig.build.json"
33
32
  },
34
33
  "devDependencies": {