Repository: googleapis/release-please (TypeScript)

Issue:
Allow forcing the use of the generic updater in `extra-files`
 **Is your feature request related to a problem? Please describe.**
Since #2072, any JSON, XML, YAML or TOML file is updated using their corresponding updater, meaning that the value of a top level `version` key, if existing, is bumped. It should be possible to override this default behavior.

My use case is to bump a docker image tag in a `docker-compose.yaml` file, that contains a top-level `version` key, like such:
```yaml
# docker-compose.yaml
version: "3.8"  # Docker version: https://docs.docker.com/compose/compose-file/04-version-and-name/

services:
  my-service:
    ...
    image: my-org/my-repo:v0.1.0 # x-release-please-version
```
I tried two things:

```yaml
"extra-files": ["docker-compose.yaml"]
```
When using this config, the `version: "3.8"` is replaced by `version: v0.1.1`, and the `image` value is left untouched.

```json
"extra-files": [
  {
    "type": "yaml",
    "path": "docker-compose.yaml",
    "jsonpath": "services.my-service.image"
  }
]
```
When using this second config, the `image` value is transformed to `v0.1.1`, deleting the `my-org/my-repo:` part.


 **Describe the solution you'd like**
Allow something like this, that won't attempt to modify the top-level `version`:
```json
"extra-files": [
  {
    "type": "generic",
    "path": "docker-compose.yaml"
  }
]
```

 **Describe alternatives you've considered**
As specified [there](https://docs.docker.com/compose/compose-file/04-version-and-name/), the `version` key is not strictly necessary for a docker-compose file, so removing this key leads to the desired behavior. However, this is only a temporary workaround.



Discussion:
The `jsonpath` must begin with a [`$`](https://github.com/googleapis/release-please/blob/HEAD/docs/customizing.md#updating-arbitrary-yaml-files). You'll get an error because [_jsonpath_](https://www.npmjs.com/package/jsonpath) library doesn't support `-` (https://github.com/dchester/jsonpath/issues/90#issuecomment-397335671):

```
Error: release-please failed: Lexical error on line 1. Unrecognized text.
$.services.my-service.image
-------------^
```

The property must be enclosed in quotes:

```JSON
"extra-files": [
  {
    "type": "yaml",
    "path": "docker-compose.yaml",
    "jsonpath": "$.services[\"my-service\"].image"
  }
]
```
@regseb this won't work with type as it will reformat the file, remove comments and purge quotes as per issues https://github.com/google-github-actions/release-please-action/issues/896 and https://github.com/googleapis/release-please/issues/2195

We would want to keep the generic updater with yaml files if we want or prevent the yaml updater from reformating the whole file when setting the new version



Patch:
diff --git a/docs/customizing.md b/docs/customizing.md
index 604d71d5f..0c5df16ea 100644
--- a/docs/customizing.md
+++ b/docs/customizing.md
@@ -182,6 +182,21 @@ You can annotate a block by starting with a line containing:
 and close the block with a line containing `x-release-please-end`. Within
 the block, we will attempt to replace version values.
 
+Default updaters are applied depending on the file extension. If you want to
+force the [Generic](/src/updaters/generic.ts) updater, you must use type
+`"generic"`.
+
+```json
+{
+  "extra-files": [
+    {
+      "type": "generic",
+      "path": "path/to/file.yml"
+    }
+  ]
+}
+```
+
 ## Updating arbitrary JSON files
 
 For files with the `.xml` extension, the `version` property is updated.
diff --git a/src/manifest.ts b/src/manifest.ts
index e41847a2b..9e7454c22 100644
--- a/src/manifest.ts
+++ b/src/manifest.ts
@@ -48,6 +48,11 @@ import {
 import {signoffCommitMessage} from './util/signoff-commit-message';
 import {CommitExclude} from './util/commit-exclude';
 
+type ExtraGenericFile = {
+  type: 'generic';
+  path: string;
+  glob?: boolean;
+};
 type ExtraJsonFile = {
   type: 'json';
   path: string;
@@ -79,6 +84,7 @@ type ExtraTomlFile = {
 };
 export type ExtraFile =
   | string
+  | ExtraGenericFile
   | ExtraJsonFile
   | ExtraYamlFile
   | ExtraXmlFile
diff --git a/src/strategies/base.ts b/src/strategies/base.ts
index d0e766ed0..b076cc07b 100644
--- a/src/strategies/base.ts
+++ b/src/strategies/base.ts
@@ -393,6 +393,13 @@ export abstract class BaseStrategy implements Strategy {
         const paths = await this.extraFilePaths(extraFile);
         for (const path of paths) {
           switch (extraFile.type) {
+            case 'generic':
+              extraFileUpdates.push({
+                path: this.addPath(path),
+                createIfMissing: false,
+                updater: new Generic({version, versionsMap}),
+              });
+              break;
             case 'json':
               extraFileUpdates.push({
                 path: this.addPath(path),


Test patch:
diff --git a/test/strategies/base.ts b/test/strategies/base.ts
index 4aa3d8a08..38115d01b 100644
--- a/test/strategies/base.ts
+++ b/test/strategies/base.ts
@@ -177,6 +177,23 @@ describe('Strategy', () => {
       expect(updates).to.be.an('array');
       assertHasUpdates(updates!, 'pom.xml', GenericXml, Generic);
     });
+    it('updates extra generic files', async () => {
+      const strategy = new TestStrategy({
+        targetBranch: 'main',
+        github,
+        component: 'google-cloud-automl',
+        extraFiles: ['0', {type: 'generic', path: '/1.yml'}],
+      });
+      const pullRequest = await strategy.buildReleasePullRequest(
+        buildMockConventionalCommit('fix: a bugfix'),
+        undefined
+      );
+      expect(pullRequest).to.exist;
+      const updates = pullRequest?.updates;
+      expect(updates).to.be.an('array');
+      assertHasUpdate(updates!, '0', Generic);
+      assertHasUpdate(updates!, '1.yml', Generic);
+    });
     it('updates extra JSON files', async () => {
       const strategy = new TestStrategy({
         targetBranch: 'main',