Repository: projen/projen (TypeScript)

Issue:
Version class only compatible with Node projects as of v0.86.2
PR #3801 modified the `Version` class by moving the addition of the version bump dependency from `NodeProject` to `Version`. While this makes sense intuitively, the `Version` class was previously written in a way that could be run against any project type provided the running environment had npx installed. It did not actually require `commit-and-tag-version` (previously `standard-version`) to be added to the project's dependencies.
This causes a problem if you were using the `Version` class with, say, a `JavaProject`, as it attempts to add the invalid `commit-and-tag-version` dependency to the project's pom file:
```
invalid maven coordinates in dependency named "commit-and-tag-version". format is "<groupId>/<artifactId>". For example "org.junit.jupiter/junit-jupiter-engine"
```
I'm unsure what the best approach to resolve this is as I agree that `Version` should somehow indicate that it requires Node, npx and commit-and-tag-version, though clearly adding it to the project's build dependencies only works for Node projects.
The options I see are:
- Each project type is responsible for implementing its own `Version` class, using an appropriate package for that language
- The pattern of using utilities in workflows via npx is supported regardless of project language
- Node tools like commit-and-tag-version are packaged with projen so that they can be invoked from the language specific JSII bundle (e.g. via mvn exec instead of npx for Java projects)


Discussion:
A funky, my bad! Thanks for the report! 

Yes this is an odd one, but I reckon in short term there should be an easy fix with a couple of extra conditions.

In general the versioning tasks for non node projects is currently relying on npx which isn't great but works. It's even less great to not have the version locked, but okay. 

I guess ideally, each language/ecosystem would define its own way of bumping versions in a safe way?! And then the  release component could stay language independent. 🤔



Not super keen on bundling more stuff into projen. 



Patch:
diff --git a/src/version.ts b/src/version.ts
index 8e3dfc75fb4..000ccb9c451 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -2,6 +2,7 @@ import { posix } from "path";
 import { IConstruct } from "constructs";
 import { Component } from "./component";
 import { Dependencies, DependencyType } from "./dependencies";
+import { NodePackage } from "./javascript/node-package";
 import { Task } from "./task";
 
 /**
@@ -107,16 +108,21 @@ export class Version extends Component {
     this.releaseTagFileName = "releasetag.txt";
     this.bumpPackage = options.bumpPackage ?? COMMIT_AND_TAG_VERSION_DEFAULT;
 
-    const { name: bumpName, version: bumpVersion } =
-      Dependencies.parseDependency(this.bumpPackage);
-    if (
-      !this.project.deps.isDependencySatisfied(
-        bumpName,
-        DependencyType.BUILD,
-        bumpVersion ?? "*"
-      )
-    ) {
-      this.project.deps.addDependency(this.bumpPackage, DependencyType.BUILD);
+    // This component is language independent.
+    // However, when in the Node.js ecosystem, we can improve the experience by adding a dev dependency on the bump package.
+    const node = NodePackage.of(this.project);
+    if (node) {
+      const { name: bumpName, version: bumpVersion } =
+        Dependencies.parseDependency(this.bumpPackage);
+      if (
+        !node.project.deps.isDependencySatisfied(
+          bumpName,
+          DependencyType.BUILD,
+          bumpVersion ?? "*"
+        )
+      ) {
+        node.project.deps.addDependency(this.bumpPackage, DependencyType.BUILD);
+      }
     }
 
     const versionInputFile = options.versionInputFile;


Test patch:
diff --git a/test/release/__snapshots__/release.test.ts.snap b/test/release/__snapshots__/release.test.ts.snap
index 47e1288dd79..e700fc8000b 100644
--- a/test/release/__snapshots__/release.test.ts.snap
+++ b/test/release/__snapshots__/release.test.ts.snap
@@ -191,16 +191,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -587,16 +577,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -990,16 +970,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -1350,16 +1320,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -1826,16 +1786,6 @@ node_modules/
 !/.github/workflows/release-2.x.yml
 !/.github/workflows/release-10.x.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -2357,16 +2307,6 @@ node_modules/
 !/.github/workflows/release.yml
 !/.github/workflows/foo-workflow.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -2733,16 +2673,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -3255,16 +3185,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -3979,16 +3899,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -5078,16 +4988,6 @@ node_modules/
 !/.github/workflows/release-firefox.yml
 !/.github/workflows/release-safari.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -5579,16 +5479,6 @@ node_modules/
 !/.github/workflows/release-3.x.yml
 !/.github/workflows/release-next.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -5964,16 +5854,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
@@ -6262,16 +6142,6 @@ node_modules/
 /dist/version.txt
 !/.github/workflows/release.yml
 ",
-  ".projen/deps.json": {
-    "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
-    "dependencies": [
-      {
-        "name": "commit-and-tag-version",
-        "type": "build",
-        "version": "^12",
-      },
-    ],
-  },
   ".projen/files.json": {
     "//": "~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".",
     "files": [
diff --git a/test/util.ts b/test/util.ts
index 12cb72d4489..5cce2c1be10 100644
--- a/test/util.ts
+++ b/test/util.ts
@@ -4,7 +4,10 @@ import * as os from "os";
 import * as path from "path";
 import { Project } from "../src";
 import { installPackage } from "../src/cli/util";
-import { GitHubProject, GitHubProjectOptions } from "../src/github";
+import {
+  GitHubProject,
+  GitHubProjectOptions,
+} from "../src/github/github-project";
 import * as logging from "../src/logging";
 import { Task } from "../src/task";
 import { exec } from "../src/util";
diff --git a/test/version.test.ts b/test/version.test.ts
index c72d281393f..090d8e31611 100644
--- a/test/version.test.ts
+++ b/test/version.test.ts
@@ -2,7 +2,13 @@ import { execSync } from "child_process";
 import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "fs";
 import { tmpdir } from "os";
 import { dirname, join } from "path";
-import { execProjenCLI, TestProject, withProjectDir } from "./util";
+import {
+  execProjenCLI,
+  synthSnapshot,
+  TestProject,
+  withProjectDir,
+} from "./util";
+import { JavaProject } from "../src/java";
 import { Version } from "../src/version";
 
 describe("Version", () => {
@@ -24,6 +30,41 @@ describe("Version", () => {
   });
 });
 
+describe("with JavaProject", () => {
+  test("version component can be used", () => {
+    const p = new JavaProject({
+      name: "test-project",
+      version: "1.0.0",
+      groupId: "io.github.cdklabs",
+      artifactId: "projen",
+    });
+
+    new Version(p, {
+      versionInputFile: "pom.xml",
+      artifactsDirectory: "dist",
+      bumpPackage: "foobar-bump",
+    });
+
+    const snap = synthSnapshot(p);
+    const pom = snap["pom.xml"];
+    const tasks = snap[".projen/tasks.json"];
+
+    // should not be in the pom file
+    expect(pom).not.toContain("foobar-bump");
+
+    // but should be in the task environment
+    expect(tasks).toMatchObject({
+      tasks: {
+        bump: {
+          env: {
+            BUMP_PACKAGE: "foobar-bump",
+          },
+        },
+      },
+    });
+  });
+});
+
 describe("bump task", () => {
   test("will bump if last commit is not a tag", async () => {
     withProjectDir((projectdir) => {