Repository: mheap/pin-github-action (js)

Issue:
Feature request: Coexist/integrate with Renovate
I’d like to use this as a complement to Renovate which already does this, as per:
- https://docs.renovatebot.com/modules/manager/github-actions/

I.e. “just” change the comment you make from:
`# pin@0.4.0`
…into:
`# 0.4.0`

Thus I could use Renovate for:
- Upgrade to new versions
- Pin any non-pinned in master/main

And use this action for:
- Pin any non-pinned in open PRs

Would you be open to this?

Possibly configurable if changing the defaults for this breaks any consumers…?

Pull request:
Allow pin@{ref} comment to be customised
Adds the `-c` flag to customise the comment that is added to pinned workflows

Resolves #140 

Patch:
diff --git a/README.md b/README.md
index d28af09..8611bb1 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,16 @@ Examples:
 - Partial match: `actions/*`
 - Negated match: `!actions/*` (will only pin `actions/*` actions)
 
+## Customising the pin@{ref} comment
+
+You can specify a comment containing the `{ref}` placeholder to customise the comment added.
+
+To add [support for renovate](https://github.com/mheap/pin-github-action/issues/140), run with the following options (note the leading space):
+
+```bash
+pin-github-action -c " {ref}" /path/to/workflow.yaml
+```
+
 ## How it works
 
 - Load the workflow file provided
diff --git a/bin.js b/bin.js
index f5a6b55..8208815 100755
--- a/bin.js
+++ b/bin.js
@@ -37,6 +37,11 @@ const packageDetails = require(path.join(__dirname, "package.json"));
         "set string representation for null values",
         "null"
       )
+      .option(
+        "-c, --comment <string>",
+        "comment to add inline when pinning an action",
+        " pin@{ref}"
+      )
       .parse(process.argv);
 
     if (program.args.length == 0) {
@@ -49,6 +54,7 @@ const packageDetails = require(path.join(__dirname, "package.json"));
     let allowEmpty = program.opts().allowEmpty;
     let yamlLineWidth = program.opts().yamlLineWidth;
     let yamlNullStr = program.opts().yamlNullStr;
+    let comment = program.opts().comment;
 
     for (const filename of program.args) {
       if (!fs.existsSync(filename)) {
@@ -65,7 +71,8 @@ const packageDetails = require(path.join(__dirname, "package.json"));
         allowEmpty,
         debug,
         yamlLineWidth,
-        yamlNullStr
+        yamlNullStr,
+        comment
       );
       fs.writeFileSync(filename, output.workflow);
     }
diff --git a/index.js b/index.js
index 29292f1..37f31bd 100644
--- a/index.js
+++ b/index.js
@@ -13,7 +13,8 @@ module.exports = async function (
   allowEmpty,
   debug,
   yamlLineWidth,
-  yamlNullStr
+  yamlNullStr,
+  comment
 ) {
   allowed = allowed || [];
   ignoreShas = ignoreShas || false;
@@ -40,7 +41,7 @@ module.exports = async function (
     actions[i].newVersion = newVersion;
 
     // Rewrite each action, replacing the uses block with a specific sha
-    workflow = replaceActions(workflow, actions[i]);
+    workflow = replaceActions(workflow, actions[i], comment);
   }
 
   stringOpts = {
diff --git a/replaceActions.js b/replaceActions.js
index 116c10b..56e08b4 100644
--- a/replaceActions.js
+++ b/replaceActions.js
@@ -1,14 +1,14 @@
-module.exports = function (input, action) {
+module.exports = function (input, action, comment) {
   let runs = input.contents.items.filter((n) => n.key == "runs");
   if (runs.length) {
-    return replaceInComposite(input, action);
+    return replaceInComposite(input, action, comment);
   }
-  return replaceInWorkflow(input, action);
+  return replaceInWorkflow(input, action, comment);
 };
 
 function replaceInReusable(input, action) {}
 
-function replaceInComposite(input, action) {
+function replaceInComposite(input, action, comment) {
   let actionString = `${action.owner}/${action.repo}`;
   if (action.path) {
     actionString += `/${action.path}`;
@@ -27,7 +27,7 @@ function replaceInComposite(input, action) {
     for (let use of uses) {
       if (use.value.value == actionString) {
         use.value.value = replacement;
-        use.value.comment = ` pin@${action.pinnedVersion}`;
+        use.value.comment = generateComment(action, comment);
       }
     }
   }
@@ -35,7 +35,7 @@ function replaceInComposite(input, action) {
   return input;
 }
 
-function replaceInWorkflow(input, action) {
+function replaceInWorkflow(input, action, comment) {
   let actionString = `${action.owner}/${action.repo}`;
   if (action.path) {
     actionString += `/${action.path}`;
@@ -57,33 +57,41 @@ function replaceInWorkflow(input, action) {
         stepKeys[0].value.items,
         actionString,
         replacement,
-        action
+        action,
+        comment
       );
     } else if (usesKeys.length) {
-      replaceReusable(usesKeys, actionString, replacement, action);
+      replaceReusable(usesKeys, actionString, replacement, action, comment);
     }
   }
 
   return input;
 }
 
-function replaceReusable(uses, actionString, replacement, action) {
+function replaceReusable(uses, actionString, replacement, action, comment) {
   for (let use of uses) {
     if (use.value.value == actionString) {
       use.value.value = replacement;
-      use.value.comment = ` pin@${action.pinnedVersion}`;
+      use.value.comment = generateComment(action, comment);
     }
   }
 }
 
-function replaceStandard(steps, actionString, replacement, action) {
+function replaceStandard(steps, actionString, replacement, action, comment) {
   for (let step of steps) {
     const uses = step.items.filter((n) => n.key == "uses");
     for (let use of uses) {
       if (use.value.value == actionString) {
         use.value.value = replacement;
-        use.value.comment = ` pin@${action.pinnedVersion}`;
+        use.value.comment = generateComment(action, comment);
       }
     }
   }
 }
+
+function generateComment(action, comment) {
+  if (!comment) {
+    comment = ` pin@{ref}`;
+  }
+  return `${comment.replace("{ref}", action.pinnedVersion)}`;
+}


Test patch:
diff --git a/replaceActions.test.js b/replaceActions.test.js
index 6882b77..75814a6 100644
--- a/replaceActions.test.js
+++ b/replaceActions.test.js
@@ -32,6 +32,28 @@ test("replaces a single action with a sha (workflow)", () => {
   expect(actual).toContain("uses: mheap/test-action@sha-here # pin@master");
 });
 
+test("supports a custom comment format (workflow)", () => {
+  const input = convertToAst({
+    name: "PR",
+    on: ["pull_request"],
+    jobs: {
+      "test-job": {
+        "runs-on": "ubuntu-latest",
+        steps: [
+          {
+            name: "test action step",
+            uses: "mheap/test-action@master",
+          },
+        ],
+      },
+    },
+  });
+
+  const actual = replaceActions(input, { ...action }, " {ref}").toString();
+
+  expect(actual).toContain("uses: mheap/test-action@sha-here # master");
+});
+
 test("replaces a single action with a sha (composite)", () => {
   const input = convertToAst({
     name: "Sample Composite",
@@ -51,6 +73,25 @@ test("replaces a single action with a sha (composite)", () => {
   expect(actual).toContain("uses: mheap/test-action@sha-here # pin@master");
 });
 
+test("supports a custom comment format (composite)", () => {
+  const input = convertToAst({
+    name: "Sample Composite",
+    runs: {
+      using: "composite",
+      steps: [
+        {
+          name: "With An Action",
+          uses: "mheap/test-action@master",
+        },
+      ],
+    },
+  });
+
+  const actual = replaceActions(input, { ...action }, " {ref}").toString();
+
+  expect(actual).toContain("uses: mheap/test-action@sha-here # master");
+});
+
 test("replaces a single action with a sha (reusable)", () => {
   const input = convertToAst({
     name: "Sample Reusable",
@@ -66,6 +107,21 @@ test("replaces a single action with a sha (reusable)", () => {
   expect(actual).toContain("uses: mheap/test-action@sha-here # pin@master");
 });
 
+test("supports a custom comment format (reusable)", () => {
+  const input = convertToAst({
+    name: "Sample Reusable",
+    jobs: {
+      test: {
+        uses: "mheap/test-action@master",
+      },
+    },
+  });
+
+  const actual = replaceActions(input, { ...action }, " {ref}").toString();
+
+  expect(actual).toContain("uses: mheap/test-action@sha-here # master");
+});
+
 test("replaces an existing sha with a different sha, not changing the pinned branch", () => {
   const input = convertToAst({
     name: "PR",