From 115b4ced54313e612a4827afa3402de259134505 Mon Sep 17 00:00:00 2001 From: ryohidaka <39184410+ryohidaka@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:10:02 +0900 Subject: [PATCH 1/4] feat(deno): add support for deno (and jsr) --- README.md | 1 + __snapshots__/cli.js | 20 +++--- docs/customizing.md | 1 + src/factory.ts | 2 + src/strategies/deno.ts | 112 +++++++++++++++++++++++++++++++++ src/updaters/deno/deno-json.ts | 47 ++++++++++++++ 6 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 src/strategies/deno.ts create mode 100644 src/updaters/deno/deno-json.ts diff --git a/README.md b/README.md index 2f4118e6d..0ddcc9d9a 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ Release Please automates releases for the following flavors of repositories: | `sfdx` | A repository with a [sfdx-project.json](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm) and a CHANGELOG.md | | `simple` | [A repository with a version.txt and a CHANGELOG.md](https://github.com/googleapis/gapic-generator) | | `terraform-module` | [A terraform module, with a version in the README.md, and a CHANGELOG.md](https://github.com/terraform-google-modules/terraform-google-project-factory) | +| `deno` | A repository with a deno.json, deno.jsonc, or jsr.json | ## Setting up Release Please diff --git a/__snapshots__/cli.js b/__snapshots__/cli.js index 696b90e27..94e85fa3b 100644 --- a/__snapshots__/cli.js +++ b/__snapshots__/cli.js @@ -43,11 +43,11 @@ Options: [string] --release-type what type of repo is a release being created for? - [choices: "bazel", "dart", "dotnet-yoshi", "elixir", "expo", "go", "go-yoshi", - "helm", "java", "java-backport", "java-bom", "java-lts", "java-yoshi", - "java-yoshi-mono-repo", "krm-blueprint", "maven", "node", "ocaml", "php", - "php-yoshi", "python", "r", "ruby", "ruby-yoshi", "rust", "salesforce", - "sfdx", "simple", "terraform-module"] + [choices: "bazel", "dart", "deno", "dotnet-yoshi", "elixir", "expo", "go", + "go-yoshi", "helm", "java", "java-backport", "java-bom", "java-lts", + "java-yoshi", "java-yoshi-mono-repo", "krm-blueprint", "maven", "node", + "ocaml", "php", "php-yoshi", "python", "r", "ruby", "ruby-yoshi", "rust", + "salesforce", "sfdx", "simple", "terraform-module"] --config-file where can the config file be found in the project? [default: "release-please-config.json"] --manifest-file where can the manifest file be found in the @@ -250,11 +250,11 @@ Options: [string] --release-type what type of repo is a release being created for? - [choices: "bazel", "dart", "dotnet-yoshi", "elixir", "expo", "go", "go-yoshi", - "helm", "java", "java-backport", "java-bom", "java-lts", "java-yoshi", - "java-yoshi-mono-repo", "krm-blueprint", "maven", "node", "ocaml", "php", - "php-yoshi", "python", "r", "ruby", "ruby-yoshi", "rust", "salesforce", - "sfdx", "simple", "terraform-module"] + [choices: "bazel", "dart", "deno", "dotnet-yoshi", "elixir", "expo", "go", + "go-yoshi", "helm", "java", "java-backport", "java-bom", "java-lts", + "java-yoshi", "java-yoshi-mono-repo", "krm-blueprint", "maven", "node", + "ocaml", "php", "php-yoshi", "python", "r", "ruby", "ruby-yoshi", "rust", + "salesforce", "sfdx", "simple", "terraform-module"] --config-file where can the config file be found in the project? [default: "release-please-config.json"] diff --git a/docs/customizing.md b/docs/customizing.md index 98c796789..5d7e88cf0 100644 --- a/docs/customizing.md +++ b/docs/customizing.md @@ -26,6 +26,7 @@ Release Please automates releases for the following flavors of repositories: | `sfdx` | A repository with a [sfdx-project.json](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm) and a CHANGELOG.md | | `simple` | [A repository with a version.txt and a CHANGELOG.md](https://github.com/googleapis/gapic-generator) | | `terraform-module` | [A terraform module, with a version in the README.md, and a CHANGELOG.md](https://github.com/terraform-google-modules/terraform-google-project-factory) | +| `deno` | A repository with a deno.json, deno.jsonc, or jsr.json | ### Adding additional strategy types diff --git a/src/factory.ts b/src/factory.ts index 025cf1e48..f2b92ed0b 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -20,6 +20,7 @@ import {ReleaserConfig} from './manifest'; import {BaseStrategyOptions} from './strategies/base'; import {Bazel} from './strategies/bazel'; import {Dart} from './strategies/dart'; +import {Deno} from './strategies/deno'; import {DotnetYoshi} from './strategies/dotnet-yoshi'; import {Elixir} from './strategies/elixir'; import {Expo} from './strategies/expo'; @@ -111,6 +112,7 @@ const releasers: Record = { elixir: options => new Elixir(options), dart: options => new Dart(options), bazel: options => new Bazel(options), + deno: options => new Deno(options), }; export async function buildStrategy( diff --git a/src/strategies/deno.ts b/src/strategies/deno.ts new file mode 100644 index 000000000..ceda08198 --- /dev/null +++ b/src/strategies/deno.ts @@ -0,0 +1,112 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Copyright 2025 Google LLC +// Licensed under the Apache License, Version 2.0 + +import {GitHubFileContents} from '@google-automations/git-file-utils'; +import {FileNotFoundError, MissingRequiredFileError} from '../errors'; +import {Update} from '../update'; +import {Changelog} from '../updaters/changelog'; +import {ChangelogJson} from '../updaters/changelog-json'; +import {filterCommits} from '../util/filter-commits'; +import {BaseStrategy, BuildUpdatesOptions} from './base'; +import {DenoJson} from '../updaters/deno/deno-json'; + +const CONFIG_FILES = ['deno.json', 'deno.jsonc', 'jsr.json']; + +export class Deno extends BaseStrategy { + private denoConfigContents?: GitHubFileContents; + private denoConfigPath?: string; + + protected async buildUpdates( + options: BuildUpdatesOptions + ): Promise { + const updates: Update[] = []; + const version = options.newVersion; + const packageName = (await this.getDefaultPackageName()) ?? ''; + + // Update config file + if (this.denoConfigPath && this.denoConfigContents) { + updates.push({ + path: this.addPath(this.denoConfigPath), + createIfMissing: false, + cachedFileContents: this.denoConfigContents, + updater: new DenoJson({version}), + }); + } + + // Changelog + if (!this.skipChangelog) { + updates.push({ + path: this.addPath(this.changelogPath), + createIfMissing: true, + updater: new Changelog({ + version, + changelogEntry: options.changelogEntry, + }), + }); + } + + // If a machine readable changelog.json exists update it: + if (options.commits && packageName && !this.skipChangelog) { + const commits = filterCommits(options.commits, this.changelogSections); + updates.push({ + path: 'changelog.json', + createIfMissing: false, + updater: new ChangelogJson({ + artifactName: packageName, + version, + commits, + language: 'TYPESCRIPT', + }), + }); + } + + return updates; + } + + async getDefaultPackageName(): Promise { + const configContents = await this.getDenoConfigContents(); + const config = JSON.parse(configContents.parsedContent); + return config.name; + } + + protected async getDenoConfigContents(): Promise { + if (!this.denoConfigContents) { + for (const file of CONFIG_FILES) { + try { + this.denoConfigContents = await this.github.getFileContentsOnBranch( + this.addPath(file), + this.targetBranch + ); + this.denoConfigPath = file; + break; + } catch (e) { + if (!(e instanceof FileNotFoundError)) { + throw e; + } + } + } + + if (!this.denoConfigContents || !this.denoConfigPath) { + throw new MissingRequiredFileError( + CONFIG_FILES.join(', '), + 'deno', + `${this.repository.owner}/${this.repository.repo}` + ); + } + } + return this.denoConfigContents; + } +} diff --git a/src/updaters/deno/deno-json.ts b/src/updaters/deno/deno-json.ts new file mode 100644 index 000000000..3058398ee --- /dev/null +++ b/src/updaters/deno/deno-json.ts @@ -0,0 +1,47 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {jsonStringify} from '../../util/json-stringify'; +import {logger as defaultLogger, Logger} from '../../util/logger'; +import {DefaultUpdater, UpdateOptions} from '../default'; + +export type DenoJsonDescriptor = { + name?: string; + version: string; +}; + +export type DenoJsonOptions = UpdateOptions; + +/** + * This updates a Deno deno.json file's main version. + */ +export class DenoJson extends DefaultUpdater { + constructor(options: DenoJsonOptions) { + super(options); + } + + /** + * Given initial file contents, return updated contents. + * @param {string} content The initial content + * @param logger + * @returns {string} The updated content + */ + updateContent(content: string, logger: Logger = defaultLogger): string { + const parsed = JSON.parse(content) as DenoJsonDescriptor; + logger.info(`updating from ${parsed.version} to ${this.version}`); + parsed.version = this.version.toString(); + + return jsonStringify(parsed, content); + } +} From 99b3a13946b1a121aa1be08b4176650d5912c99d Mon Sep 17 00:00:00 2001 From: ryohidaka <39184410+ryohidaka@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:28:50 +0900 Subject: [PATCH 2/4] test(deno): add test for deno updaters --- __snapshots__/deno-json.js | 56 +++++++++++++++++++++++ test/updaters/deno-json.ts | 62 ++++++++++++++++++++++++++ test/updaters/fixtures/deno/deno.json | 15 +++++++ test/updaters/fixtures/deno/deno.jsonc | 15 +++++++ test/updaters/fixtures/deno/jsr.json | 15 +++++++ 5 files changed, 163 insertions(+) create mode 100644 __snapshots__/deno-json.js create mode 100644 test/updaters/deno-json.ts create mode 100644 test/updaters/fixtures/deno/deno.json create mode 100644 test/updaters/fixtures/deno/deno.jsonc create mode 100644 test/updaters/fixtures/deno/jsr.json diff --git a/__snapshots__/deno-json.js b/__snapshots__/deno-json.js new file mode 100644 index 000000000..b4a5c63c7 --- /dev/null +++ b/__snapshots__/deno-json.js @@ -0,0 +1,56 @@ +exports['Deno updateContent updates the package version with deno.json 1'] = ` +{ + "name": "@deno/esbuild-plugin", + "version": "2.0.0", + "license": "MIT", + "imports": { + "@deno/loader": "jsr:@deno/loader@^0.3.3", + "@std/expect": "jsr:@std/expect@^1.0.16", + "@std/path": "jsr:@std/path@^1.1.1", + "esbuild": "npm:esbuild@^0.25.5", + "fflate": "npm:fflate@^0.8.2", + "mime-db": "npm:mime-db@^1.54.0", + "preact": "npm:preact@^10.26.8", + "mapped": "./tests/fixtures/simple.ts" + } +} + +` + +exports['Deno updateContent updates the package version with deno.jsonc 1'] = ` +{ + "name": "@deno/esbuild-plugin", + "version": "2.0.0", + "license": "MIT", + "imports": { + "@deno/loader": "jsr:@deno/loader@^0.3.3", + "@std/expect": "jsr:@std/expect@^1.0.16", + "@std/path": "jsr:@std/path@^1.1.1", + "esbuild": "npm:esbuild@^0.25.5", + "fflate": "npm:fflate@^0.8.2", + "mime-db": "npm:mime-db@^1.54.0", + "preact": "npm:preact@^10.26.8", + "mapped": "./tests/fixtures/simple.ts" + } +} + +` + +exports['Deno updateContent updates the package version with jsr.json 1'] = ` +{ + "name": "@deno/esbuild-plugin", + "version": "2.0.0", + "license": "MIT", + "imports": { + "@deno/loader": "jsr:@deno/loader@^0.3.3", + "@std/expect": "jsr:@std/expect@^1.0.16", + "@std/path": "jsr:@std/path@^1.1.1", + "esbuild": "npm:esbuild@^0.25.5", + "fflate": "npm:fflate@^0.8.2", + "mime-db": "npm:mime-db@^1.54.0", + "preact": "npm:preact@^10.26.8", + "mapped": "./tests/fixtures/simple.ts" + } +} + +` diff --git a/test/updaters/deno-json.ts b/test/updaters/deno-json.ts new file mode 100644 index 000000000..f253a658d --- /dev/null +++ b/test/updaters/deno-json.ts @@ -0,0 +1,62 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {readFileSync} from 'fs'; +import {resolve} from 'path'; +import * as snapshot from 'snap-shot-it'; +import {describe, it} from 'mocha'; +import {DenoJson} from '../../src/updaters/deno/deno-json'; +import {Version} from '../../src/version'; + +const fixturesPath = './test/updaters/fixtures/deno'; + +describe('Deno', () => { + describe('updateContent', () => { + it('updates the package version with deno.json', async () => { + const oldContent = readFileSync( + resolve(fixturesPath, './deno.json'), + 'utf8' + ); + const packageJson = new DenoJson({ + version: Version.parse('2.0.0'), + }); + const newContent = packageJson.updateContent(oldContent); + snapshot(newContent.replace(/\r\n/g, '\n')); + }); + + it('updates the package version with deno.jsonc', async () => { + const oldContent = readFileSync( + resolve(fixturesPath, './deno.jsonc'), + 'utf8' + ); + const packageJson = new DenoJson({ + version: Version.parse('2.0.0'), + }); + const newContent = packageJson.updateContent(oldContent); + snapshot(newContent.replace(/\r\n/g, '\n')); + }); + + it('updates the package version with jsr.json', async () => { + const oldContent = readFileSync( + resolve(fixturesPath, './jsr.json'), + 'utf8' + ); + const packageJson = new DenoJson({ + version: Version.parse('2.0.0'), + }); + const newContent = packageJson.updateContent(oldContent); + snapshot(newContent.replace(/\r\n/g, '\n')); + }); + }); +}); diff --git a/test/updaters/fixtures/deno/deno.json b/test/updaters/fixtures/deno/deno.json new file mode 100644 index 000000000..7b83ed0fe --- /dev/null +++ b/test/updaters/fixtures/deno/deno.json @@ -0,0 +1,15 @@ +{ + "name": "@deno/esbuild-plugin", + "version": "1.1.5", + "license": "MIT", + "imports": { + "@deno/loader": "jsr:@deno/loader@^0.3.3", + "@std/expect": "jsr:@std/expect@^1.0.16", + "@std/path": "jsr:@std/path@^1.1.1", + "esbuild": "npm:esbuild@^0.25.5", + "fflate": "npm:fflate@^0.8.2", + "mime-db": "npm:mime-db@^1.54.0", + "preact": "npm:preact@^10.26.8", + "mapped": "./tests/fixtures/simple.ts" + } +} diff --git a/test/updaters/fixtures/deno/deno.jsonc b/test/updaters/fixtures/deno/deno.jsonc new file mode 100644 index 000000000..7b83ed0fe --- /dev/null +++ b/test/updaters/fixtures/deno/deno.jsonc @@ -0,0 +1,15 @@ +{ + "name": "@deno/esbuild-plugin", + "version": "1.1.5", + "license": "MIT", + "imports": { + "@deno/loader": "jsr:@deno/loader@^0.3.3", + "@std/expect": "jsr:@std/expect@^1.0.16", + "@std/path": "jsr:@std/path@^1.1.1", + "esbuild": "npm:esbuild@^0.25.5", + "fflate": "npm:fflate@^0.8.2", + "mime-db": "npm:mime-db@^1.54.0", + "preact": "npm:preact@^10.26.8", + "mapped": "./tests/fixtures/simple.ts" + } +} diff --git a/test/updaters/fixtures/deno/jsr.json b/test/updaters/fixtures/deno/jsr.json new file mode 100644 index 000000000..7b83ed0fe --- /dev/null +++ b/test/updaters/fixtures/deno/jsr.json @@ -0,0 +1,15 @@ +{ + "name": "@deno/esbuild-plugin", + "version": "1.1.5", + "license": "MIT", + "imports": { + "@deno/loader": "jsr:@deno/loader@^0.3.3", + "@std/expect": "jsr:@std/expect@^1.0.16", + "@std/path": "jsr:@std/path@^1.1.1", + "esbuild": "npm:esbuild@^0.25.5", + "fflate": "npm:fflate@^0.8.2", + "mime-db": "npm:mime-db@^1.54.0", + "preact": "npm:preact@^10.26.8", + "mapped": "./tests/fixtures/simple.ts" + } +} From 45d96d558cd308d1b7f3ca01db6aa2624611ca42 Mon Sep 17 00:00:00 2001 From: ryohidaka <39184410+ryohidaka@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:04:34 +0900 Subject: [PATCH 3/4] test(deno): add test for deno strategy --- __snapshots__/deno.js | 37 +++ test/fixtures/strategies/deno/changelog.json | 4 + test/fixtures/strategies/deno/commits.json | 97 +++++++ test/fixtures/strategies/deno/deno.json | 7 + test/strategies/deno.ts | 252 +++++++++++++++++++ 5 files changed, 397 insertions(+) create mode 100644 __snapshots__/deno.js create mode 100644 test/fixtures/strategies/deno/changelog.json create mode 100644 test/fixtures/strategies/deno/commits.json create mode 100644 test/fixtures/strategies/deno/deno.json create mode 100644 test/strategies/deno.ts diff --git a/__snapshots__/deno.js b/__snapshots__/deno.js new file mode 100644 index 000000000..a66560b1d --- /dev/null +++ b/__snapshots__/deno.js @@ -0,0 +1,37 @@ +exports['Deno buildReleasePullRequest updates changelog.json if present 1'] = ` +{ + "entries": [ + { + "changes": [ + { + "type": "fix", + "sha": "845db1381b3d5d20151cad2588f85feb", + "message": "update dependency com.google.cloud:google-cloud-storage to v1.120.0", + "issues": [], + "scope": "deps" + }, + { + "type": "chore", + "sha": "b3f8966b023b8f21ce127142aa91841c", + "message": "update a very important dep", + "issues": [], + "breakingChangeNote": "update a very important dep" + }, + { + "type": "fix", + "sha": "08ca01180a91c0a1ba8992b491db9212", + "message": "update dependency com.google.cloud:google-cloud-spanner to v1.50.0", + "issues": [], + "scope": "deps" + } + ], + "version": "1.0.0", + "language": "TYPESCRIPT", + "artifactName": "deno-test-repo", + "id": "abc-123-efd-qwerty", + "createTime": "2023-01-05T16:42:33.446Z" + } + ], + "updateTime": "2023-01-05T16:42:33.446Z" +} +` diff --git a/test/fixtures/strategies/deno/changelog.json b/test/fixtures/strategies/deno/changelog.json new file mode 100644 index 000000000..f471e7111 --- /dev/null +++ b/test/fixtures/strategies/deno/changelog.json @@ -0,0 +1,4 @@ +{ + "repository": "google-cloud-deno", + "entries": [] +} diff --git a/test/fixtures/strategies/deno/commits.json b/test/fixtures/strategies/deno/commits.json new file mode 100644 index 000000000..1942077af --- /dev/null +++ b/test/fixtures/strategies/deno/commits.json @@ -0,0 +1,97 @@ +{ + "repository": { + "ref": { + "target": { + "history": { + "edges": [ + { + "node": { + "message": "fix(deps): update dependency com.google.cloud:google-cloud-storage to v1.120.0", + "oid": "fcd1c890dc1526f4d62ceedad561f498195c8939", + "associatedPullRequests": { + "edges": [ + { + "node": { + "mergeCommit": { + "oid": "fcd1c890dc1526f4d62ceedad561f498195c8939" + }, + "number": 292, + "labels": { + "edges": [ + { + "node": { + "name": "cla: yes" + } + } + ] + } + } + } + ] + } + } + }, + { + "node": { + "message": "fix(deps): update dependency com.google.cloud:google-cloud-spanner to v1.50.0", + "oid": "1f9663cf08ab1cf3b68d95dee4dc99b7c4aac373", + "associatedPullRequests": { + "edges": [ + { + "node": { + "mergeCommit": { + "oid": "1f9663cf08ab1cf3b68d95dee4dc99b7c4aac373" + }, + "number": 301, + "labels": { + "edges": [ + { + "node": { + "name": "cla: yes" + } + } + ] + } + } + } + ] + } + } + }, + { + "node": { + "message": "chore: update common templates", + "oid": "3006009a2b1b2cb4bd5108c0f469c410759f3a6a", + "associatedPullRequests": { + "edges": [ + { + "node": { + "mergeCommit": { + "oid": "3006009a2b1b2cb4bd5108c0f469c410759f3a6a" + }, + "number": 300, + "labels": { + "edges": [ + { + "node": { + "name": "cla: yes" + } + } + ] + } + } + } + ] + } + } + } + ], + "pageInfo": { + "endCursor": "fcd1c890dc1526f4d62ceedad561f498195c8939 99", + "hasNextPage": false + } + } + } + } + } +} diff --git a/test/fixtures/strategies/deno/deno.json b/test/fixtures/strategies/deno/deno.json new file mode 100644 index 000000000..09e038de4 --- /dev/null +++ b/test/fixtures/strategies/deno/deno.json @@ -0,0 +1,7 @@ +{ + "name": "deno-test-repo", + "version": "0.123.4", + "repository": { + "url": "git@github.com:samples/deno-test-repo.git" + } +} diff --git a/test/strategies/deno.ts b/test/strategies/deno.ts new file mode 100644 index 000000000..56f72f2b3 --- /dev/null +++ b/test/strategies/deno.ts @@ -0,0 +1,252 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {describe, it, afterEach, beforeEach} from 'mocha'; +import {Deno} from '../../src/strategies/deno'; +import { + buildMockConventionalCommit, + buildGitHubFileContent, + assertHasUpdate, + assertNoHasUpdate, +} from '../helpers'; +import * as nock from 'nock'; +import * as sinon from 'sinon'; +import {GitHub} from '../../src/github'; +import {Version} from '../../src/version'; +import {TagName} from '../../src/util/tag-name'; +import {expect} from 'chai'; +import {Changelog} from '../../src/updaters/changelog'; +import {DenoJson} from '../../src/updaters/deno/deno-json'; +import {ChangelogJson} from '../../src/updaters/changelog-json'; +import * as assert from 'assert'; +import {MissingRequiredFileError, FileNotFoundError} from '../../src/errors'; +import * as snapshot from 'snap-shot-it'; + +nock.disableNetConnect(); +const sandbox = sinon.createSandbox(); +const fixturesPath = './test/fixtures/strategies/deno'; + +const UUID_REGEX = + /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/g; +const ISO_DATE_REGEX = + /[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+Z/g; // 2023-01-05T16:42:33.446Z + +const stubFileContent = ( + stub: sinon.SinonStub, + file: string, + branch = 'main' +) => { + stub + .withArgs(file, branch) + .resolves(buildGitHubFileContent(fixturesPath, file)); +}; + +describe('Deno', () => { + let github: GitHub; + let getFileContentsStub: sinon.SinonStub; + const commits = [ + ...buildMockConventionalCommit( + 'fix(deps): update dependency com.google.cloud:google-cloud-storage to v1.120.0' + ), + ]; + beforeEach(async () => { + github = await GitHub.create({ + owner: 'googleapis', + repo: 'deno-test-repo', + defaultBranch: 'main', + }); + getFileContentsStub = sandbox.stub(github, 'getFileContentsOnBranch'); + }); + afterEach(() => { + sandbox.restore(); + }); + describe('buildReleasePullRequest', () => { + it('returns release PR changes with defaultInitialVersion', async () => { + const expectedVersion = '1.0.0'; + const strategy = new Deno({ + targetBranch: 'main', + github, + component: 'google-cloud-automl', + packageName: 'google-cloud-automl', + }); + const latestRelease = undefined; + stubFileContent(getFileContentsStub, 'deno.json'); + const release = await strategy.buildReleasePullRequest( + commits, + latestRelease + ); + expect(release!.version?.toString()).to.eql(expectedVersion); + }); + it('builds a release pull request', async () => { + const expectedVersion = '0.123.5'; + const strategy = new Deno({ + targetBranch: 'main', + github, + component: 'some-deno-package', + packageName: 'some-deno-package', + }); + const latestRelease = { + tag: new TagName(Version.parse('0.123.4'), 'some-deno-package'), + sha: 'abc123', + notes: 'some notes', + }; + stubFileContent(getFileContentsStub, 'deno.json'); + const pullRequest = await strategy.buildReleasePullRequest( + commits, + latestRelease + ); + expect(pullRequest!.version?.toString()).to.eql(expectedVersion); + }); + it('detects a default component', async () => { + const expectedVersion = '0.123.5'; + const strategy = new Deno({ + targetBranch: 'main', + github, + }); + const commits = [ + ...buildMockConventionalCommit( + 'fix(deps): update dependency com.google.cloud:google-cloud-storage to v1.120.0' + ), + ]; + const latestRelease = { + tag: new TagName(Version.parse('0.123.4'), 'deno-test-repo'), + sha: 'abc123', + notes: 'some notes', + }; + stubFileContent(getFileContentsStub, 'deno.json'); + const pullRequest = await strategy.buildReleasePullRequest( + commits, + latestRelease + ); + expect(pullRequest!.version?.toString()).to.eql(expectedVersion); + }); + it('detects a default packageName', async () => { + const expectedVersion = '0.123.5'; + const strategy = new Deno({ + targetBranch: 'main', + github, + component: 'abc-123', + }); + const commits = [ + ...buildMockConventionalCommit( + 'fix(deps): update dependency com.google.cloud:google-cloud-storage to v1.120.0' + ), + ]; + const latestRelease = { + tag: new TagName(Version.parse('0.123.4'), 'deno-test-repo'), + sha: 'abc123', + notes: 'some notes', + }; + stubFileContent(getFileContentsStub, 'deno.json'); + const pullRequest = await strategy.buildReleasePullRequest( + commits, + latestRelease + ); + expect(pullRequest!.version?.toString()).to.eql(expectedVersion); + }); + it('handles missing deno.json', async () => { + getFileContentsStub.rejects(new FileNotFoundError('stub/path')); + const strategy = new Deno({ + targetBranch: 'main', + github, + }); + const latestRelease = { + tag: new TagName(Version.parse('0.123.4'), 'some-deno-package'), + sha: 'abc123', + notes: 'some notes', + }; + assert.rejects(async () => { + await strategy.buildReleasePullRequest(commits, latestRelease); + }, MissingRequiredFileError); + }); + it('updates changelog.json if present', async () => { + const COMMITS = [ + ...buildMockConventionalCommit( + 'fix(deps): update dependency com.google.cloud:google-cloud-storage to v1.120.0' + ), + ...buildMockConventionalCommit('chore: update deps'), + ...buildMockConventionalCommit('chore!: update a very important dep'), + ...buildMockConventionalCommit( + 'fix(deps): update dependency com.google.cloud:google-cloud-spanner to v1.50.0' + ), + ...buildMockConventionalCommit('chore: update common templates'), + ]; + const strategy = new Deno({ + targetBranch: 'main', + github, + component: 'google-cloud-deno', + }); + sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); + stubFileContent(getFileContentsStub, 'changelog.json'); + stubFileContent(getFileContentsStub, 'deno.json'); + const latestRelease = undefined; + const release = await strategy.buildReleasePullRequest( + COMMITS, + latestRelease + ); + const updates = release!.updates; + assertHasUpdate(updates, 'CHANGELOG.md', Changelog); + const update = assertHasUpdate(updates, 'changelog.json', ChangelogJson); + const newContent = update.updater.updateContent( + JSON.stringify({entries: []}) + ); + snapshot( + newContent + .replace(/\r\n/g, '\n') // make newline consistent regardless of OS. + .replace(UUID_REGEX, 'abc-123-efd-qwerty') + .replace(ISO_DATE_REGEX, '2023-01-05T16:42:33.446Z') + ); + }); + }); + describe('buildUpdates', () => { + it('builds common files', async () => { + const strategy = new Deno({ + targetBranch: 'main', + github, + component: 'google-cloud-automl', + packageName: 'google-cloud-automl-pkg', + }); + sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); + const latestRelease = undefined; + stubFileContent(getFileContentsStub, 'deno.json'); + const release = await strategy.buildReleasePullRequest( + commits, + latestRelease + ); + const updates = release!.updates; + assertHasUpdate(updates, 'CHANGELOG.md', Changelog); + assertHasUpdate(updates, 'deno.json', DenoJson); + }); + + it('omits changelog if skipChangelog=true', async () => { + const strategy = new Deno({ + targetBranch: 'main', + github, + component: 'google-cloud-automl', + packageName: 'google-cloud-automl-pkg', + skipChangelog: true, + }); + sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); + const latestRelease = undefined; + stubFileContent(getFileContentsStub, 'deno.json'); + const release = await strategy.buildReleasePullRequest( + commits, + latestRelease + ); + const updates = release!.updates; + assertNoHasUpdate(updates, 'CHANGELOG.md'); + assertHasUpdate(updates, 'deno.json', DenoJson); + }); + }); +}); From bc3390794493a3ad05af5080da3883a28785ff56 Mon Sep 17 00:00:00 2001 From: ryohidaka <39184410+ryohidaka@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:29:49 +0900 Subject: [PATCH 4/4] docs(deno): add CHANGELOG.md to description --- README.md | 2 +- docs/customizing.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ddcc9d9a..c44bc7d21 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ Release Please automates releases for the following flavors of repositories: | `sfdx` | A repository with a [sfdx-project.json](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm) and a CHANGELOG.md | | `simple` | [A repository with a version.txt and a CHANGELOG.md](https://github.com/googleapis/gapic-generator) | | `terraform-module` | [A terraform module, with a version in the README.md, and a CHANGELOG.md](https://github.com/terraform-google-modules/terraform-google-project-factory) | -| `deno` | A repository with a deno.json, deno.jsonc, or jsr.json | +| `deno` | A repository with a deno.json, deno.jsonc, or jsr.json and a CHANGELOG.md | ## Setting up Release Please diff --git a/docs/customizing.md b/docs/customizing.md index 5d7e88cf0..c4e0587fe 100644 --- a/docs/customizing.md +++ b/docs/customizing.md @@ -26,7 +26,7 @@ Release Please automates releases for the following flavors of repositories: | `sfdx` | A repository with a [sfdx-project.json](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm) and a CHANGELOG.md | | `simple` | [A repository with a version.txt and a CHANGELOG.md](https://github.com/googleapis/gapic-generator) | | `terraform-module` | [A terraform module, with a version in the README.md, and a CHANGELOG.md](https://github.com/terraform-google-modules/terraform-google-project-factory) | -| `deno` | A repository with a deno.json, deno.jsonc, or jsr.json | +| `deno` | A repository with a deno.json, deno.jsonc, or jsr.json and a CHANGELOG.md | ### Adding additional strategy types