diff --git a/__snapshots__/python.js b/__snapshots__/python.js new file mode 100644 index 000000000..3db3ced56 --- /dev/null +++ b/__snapshots__/python.js @@ -0,0 +1,37 @@ +exports['Python buildUpdates 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": "0.1.0", + "language": "PYTHON", + "artifactName": "google-cloud-automl", + "id": "abc-123-efd-qwerty", + "createTime": "2023-01-05T16:42:33.446Z" + } + ], + "updateTime": "2023-01-05T16:42:33.446Z" +} +` diff --git a/src/strategies/python.ts b/src/strategies/python.ts index 8b7df84eb..5287b9451 100644 --- a/src/strategies/python.ts +++ b/src/strategies/python.ts @@ -15,6 +15,7 @@ import {BaseStrategy, BuildUpdatesOptions, BaseStrategyOptions} from './base'; import {Update} from '../update'; import {Changelog} from '../updaters/changelog'; +import {ChangelogJson} from '../updaters/changelog-json'; import {Version} from '../version'; import {SetupCfg} from '../updaters/python/setup-cfg'; import {SetupPy} from '../updaters/python/setup-py'; @@ -24,6 +25,8 @@ import { PyProjectToml, } from '../updaters/python/pyproject-toml'; import {PythonFileWithVersion} from '../updaters/python/python-file-with-version'; +import {FileNotFoundError} from '../errors'; +import {filterCommits} from '../util/filter-commits'; const CHANGELOG_SECTIONS = [ {type: 'feat', section: 'Features'}, @@ -133,6 +136,22 @@ export class Python extends BaseStrategy { }); }); + // If a machine readable changelog.json exists update it: + const artifactName = projectName ?? (await this.getNameFromSetupPy()); + if (options.commits && artifactName) { + const commits = filterCommits(options.commits, this.changelogSections); + updates.push({ + path: 'changelog.json', + createIfMissing: false, + updater: new ChangelogJson({ + artifactName, + version, + commits, + language: 'PYTHON', + }), + }); + } + return updates; } @@ -148,6 +167,35 @@ export class Python extends BaseStrategy { } } + protected async getNameFromSetupPy(): Promise { + const ARTIFACT_NAME_REGEX = /name *= *['"](?.*)['"](\r|\n|$)/; + const setupPyContents = await this.getSetupPyContents(); + if (setupPyContents) { + const match = setupPyContents.match(ARTIFACT_NAME_REGEX); + if (match && match?.groups?.name) { + return match.groups.name; + } + } + return null; + } + + protected async getSetupPyContents(): Promise { + try { + return ( + await this.github.getFileContentsOnBranch( + this.addPath('setup.py'), + this.targetBranch + ) + ).parsedContent; + } catch (e) { + if (e instanceof FileNotFoundError) { + return null; + } else { + throw e; + } + } + } + protected initialReleaseVersion(): Version { return Version.parse('0.1.0'); } diff --git a/test/fixtures/strategies/python/changelog.json b/test/fixtures/strategies/python/changelog.json new file mode 100644 index 000000000..32d6e21aa --- /dev/null +++ b/test/fixtures/strategies/python/changelog.json @@ -0,0 +1,4 @@ +{ + "repository": "google-cloud-foo", + "entries": [] +} diff --git a/test/fixtures/strategies/python/setup.py b/test/fixtures/strategies/python/setup.py new file mode 100644 index 000000000..cfe2b4b2e --- /dev/null +++ b/test/fixtures/strategies/python/setup.py @@ -0,0 +1,78 @@ +# Copyright 2018 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 +# +# https://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 io +import os + +import setuptools + +name = "google-cloud-automl" +description = "Cloud AutoML API client library" +version = "0.5.0" +release_status = "Development Status :: 3 - Alpha" +dependencies = [ + "google-api-core[grpc] >= 1.14.0, < 2.0.0dev", + 'enum34; python_version < "3.4"', +] +extras = { + "pandas": ["pandas>=0.24.0"], + "storage": ["google-cloud-storage >= 1.18.0, < 2.0.0dev"], +} + +package_root = os.path.abspath(os.path.dirname(__file__)) + +readme_filename = os.path.join(package_root, "README.rst") +with io.open(readme_filename, encoding="utf-8") as readme_file: + readme = readme_file.read() + +packages = [ + package for package in setuptools.find_packages() if package.startswith("google") +] + +namespaces = ["google"] +if "google.cloud" in packages: + namespaces.append("google.cloud") + +setuptools.setup( + name=name, + version=version, + description=description, + long_description=readme, + author="Google LLC", + author_email="googleapis-packages@oogle.com", + license="Apache 2.0", + url="https://github.com/GoogleCloudPlatform/google-cloud-python", + classifiers=[ + release_status, + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Operating System :: OS Independent", + "Topic :: Internet", + ], + platforms="Posix; MacOS X; Windows", + packages=packages, + namespace_packages=namespaces, + install_requires=dependencies, + extras_require=extras, + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + include_package_data=True, + zip_safe=False, +) \ No newline at end of file diff --git a/test/strategies/python.ts b/test/strategies/python.ts index eaf8194a0..b304cca03 100644 --- a/test/strategies/python.ts +++ b/test/strategies/python.ts @@ -26,8 +26,16 @@ import {PyProjectToml} from '../../src/updaters/python/pyproject-toml'; import {SetupCfg} from '../../src/updaters/python/setup-cfg'; import {SetupPy} from '../../src/updaters/python/setup-py'; import {Changelog} from '../../src/updaters/changelog'; +import {ChangelogJson} from '../../src/updaters/changelog-json'; +import * as snapshot from 'snap-shot-it'; const sandbox = sinon.createSandbox(); +const fixturesPath = './test/fixtures/strategies/python'; + +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 COMMITS = [ ...buildMockConventionalCommit( @@ -59,6 +67,9 @@ describe('Python', () => { github, component: 'google-cloud-automl', }); + sandbox + .stub(github, 'getFileContentsOnBranch') + .resolves(buildGitHubFileContent(fixturesPath, 'setup.py')); sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); const latestRelease = undefined; const release = await strategy.buildReleasePullRequest( @@ -74,6 +85,9 @@ describe('Python', () => { github, component: 'google-cloud-automl', }); + sandbox + .stub(github, 'getFileContentsOnBranch') + .resolves(buildGitHubFileContent(fixturesPath, 'setup.py')); sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); const latestRelease = { tag: new TagName(Version.parse('0.123.4'), 'google-cloud-automl'), @@ -94,6 +108,9 @@ describe('Python', () => { github, component: 'google-cloud-automl', }); + sandbox + .stub(github, 'getFileContentsOnBranch') + .resolves(buildGitHubFileContent(fixturesPath, 'setup.py')); sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); const latestRelease = undefined; const release = await strategy.buildReleasePullRequest( @@ -153,6 +170,9 @@ describe('Python', () => { github, component: 'google-cloud-automl', }); + sandbox + .stub(github, 'getFileContentsOnBranch') + .resolves(buildGitHubFileContent(fixturesPath, 'setup.py')); sandbox .stub(github, 'findFilesByFilenameAndRef') .resolves(['src/version.py']); @@ -164,5 +184,52 @@ describe('Python', () => { const updates = release!.updates; assertHasUpdate(updates, 'src/version.py', PythonFileWithVersion); }); + + 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 Python({ + targetBranch: 'main', + github, + component: 'google-cloud-automl', + }); + sandbox.stub(github, 'findFilesByFilenameAndRef').resolves([]); + const getFileContentsStub = sandbox.stub( + github, + 'getFileContentsOnBranch' + ); + getFileContentsStub + .withArgs('changelog.json', 'main') + .resolves(buildGitHubFileContent(fixturesPath, 'changelog.json')); + getFileContentsStub + .withArgs('setup.py', 'main') + .resolves(buildGitHubFileContent(fixturesPath, 'setup.py')); + 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') + ); + }); }); });