Skip to content

Commit

Permalink
feat: add support for helm (#748)
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhinav Khanna authored Feb 11, 2021
1 parent 869f1a1 commit c9fbf78
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ system-test/fixtures/gh/*
__pycache__
package-lock.json
debug.sh
.idea/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Release Please automates releases for the following flavors of repositories:
| rust | A Rust repository, with a Cargo.toml (either as a crate or workspace) and a CHANGELOG.md |
| ocaml | [An OCaml repository, containing 1 or more opam or esy files and a CHANGELOG.md](https://github.com/grain-lang/binaryen.ml) |
| simple | [A repository with a version.txt and a CHANGELOG.md](https://github.com/googleapis/gapic-generator) |

| helm | A repository with a Chart.yaml and a CHANGELOG.md |
## Adding additional release types

To add a new release type, simply use the existing [releasers](https://github.com/googleapis/release-please/tree/master/src/releasers) and [updaters](https://github.com/googleapis/release-please/tree/master/src/updaters)
Expand Down
13 changes: 13 additions & 0 deletions __snapshots__/chart-yaml.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
exports['ChartYaml updateContent updates version in Chart.yaml 1'] = `
name: helm-test-repo
version: 1.1.0
apiVersion: v2
appVersion: 2.0.0
dependencies:
- name: another-repo
version: 0.15.3
repository: linkToHelmChartRepo
maintainers:
- Abhinav Khanna
`
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"nock": "^13.0.0",
"sinon": "^9.0.3",
"snap-shot-it": "^7.0.0",
"typescript": "^3.8.3"
"js-yaml": "^4.0.0",
"@types/js-yaml": "^4.0.0"
},
"dependencies": {
"@conventional-commits/parser": "^0.4.1",
Expand All @@ -70,7 +71,8 @@
"type-fest": "^0.20.0",
"unist-util-visit": "^2.0.3",
"unist-util-visit-parents": "^3.1.1",
"yargs": "^16.0.0"
"yargs": "^16.0.0",
"typescript": "^3.8.3"
},
"engines": {
"node": ">=10.12.0"
Expand Down
144 changes: 144 additions & 0 deletions src/releasers/helm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2021 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 {ReleasePR, ReleaseCandidate} from '../release-pr';

import {ConventionalCommits} from '../conventional-commits';
import {GitHub, GitHubTag, GitHubFileContents} from '../github';
import {checkpoint, CheckpointType} from '../util/checkpoint';
import {Update} from '../updaters/update';
import {Commit} from '../graphql-to-commits';

// Generic
import {Changelog} from '../updaters/changelog';
import * as yaml from 'js-yaml';
// helm
import {ChartYaml} from '../updaters/helm/chart-yaml';

export class Helm extends ReleasePR {
static releaserName = 'helm';

protected async _run(): Promise<number | undefined> {
// Make an effort to populate packageName from the contents of
// the package.json, rather than forcing this to be set:
const contents: GitHubFileContents = await this.gh.getFileContents(
this.addPath('Chart.yaml')
);
const file = yaml.load(contents.parsedContent, {json: true});
if (file === null || file === undefined) {
return undefined;
}
const pkg = JSON.parse(JSON.stringify(file));
if (pkg.name) {
this.packageName = pkg.name;
// we've rewritten the package name, recalculate the package prefix
this.packagePrefix = this.coercePackagePrefix(pkg.name);
}

const latestTag: GitHubTag | undefined = await this.latestTag(
this.monorepoTags ? `${this.packagePrefix}-` : undefined
);
const commits: Commit[] = await this.commits({
sha: latestTag ? latestTag.sha : undefined,
path: this.path,
});

const cc = new ConventionalCommits({
commits,
githubRepoUrl: this.repoUrl,
bumpMinorPreMajor: this.bumpMinorPreMajor,
changelogSections: this.changelogSections,
});
const candidate: ReleaseCandidate = await this.coerceReleaseCandidate(
cc,
latestTag
);
const changelogEntry: string = await cc.generateChangelogEntry({
version: candidate.version,
currentTag: `v${candidate.version}`,
previousTag: candidate.previousTag,
});

// don't create a release candidate until user facing changes
// (fix, feat, BREAKING CHANGE) have been made; a CHANGELOG that's
// one line is a good indicator that there were no interesting commits.
if (this.changelogEmpty(changelogEntry)) {
checkpoint(
`no user facing commits found since ${
latestTag ? latestTag.sha : 'beginning of time'
}`,
CheckpointType.Failure
);
return undefined;
}

const updates: Update[] = [];

updates.push(
new Changelog({
path: this.addPath('CHANGELOG.md'),
changelogEntry,
version: candidate.version,
packageName: this.packageName,
})
);

updates.push(
new ChartYaml({
path: this.addPath('Chart.yaml'),
changelogEntry,
version: candidate.version,
packageName: this.packageName,
contents,
})
);

return await this.openPR({
sha: commits[0].sha!,
changelogEntry: `${changelogEntry}\n---\n`,
updates,
version: candidate.version,
includePackageName: this.monorepoTags,
});
}

// A releaser can implement this method to automatically detect
// the release name when creating a GitHub release, for instance by returning
// name in package.json, or setup.py.
static async lookupPackageName(
gh: GitHub,
path?: string
): Promise<string | undefined> {
// Make an effort to populate packageName from the contents of
// the package.json, rather than forcing this to be set:
const contents: GitHubFileContents = await gh.getFileContents(
this.addPathStatic('Chart.yaml', path)
);
const file = yaml.load(contents.parsedContent, {json: true});
if (file === null || file === undefined) {
return undefined;
}
const pkg = JSON.parse(JSON.stringify(file));
if (pkg.name) return pkg.name;
else return undefined;
}

// Parse the package prefix for releases from the full package name
// The package name usually looks like `@[group]/[library]`
protected coercePackagePrefix(packageName: string): string {
return packageName.match(/^@[\w-]+\//)
? packageName.split('/')[1]
: packageName;
}
}
5 changes: 4 additions & 1 deletion src/releasers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {Simple} from './simple';
import {TerraformModule} from './terraform-module';
import {Rust} from './rust';
import {OCaml} from './ocaml';
import {Helm} from './helm';

// add any new releasers you create to this type as well as the `releasers`
// object below.
Expand All @@ -43,7 +44,8 @@ export type ReleaseType =
| 'ruby-yoshi'
| 'rust'
| 'simple'
| 'terraform-module';
| 'terraform-module'
| 'helm';

type Releasers = Partial<Record<ReleaseType, typeof ReleasePR>>;

Expand All @@ -61,6 +63,7 @@ const releasers: Releasers = {
rust: Rust,
simple: Simple,
'terraform-module': TerraformModule,
helm: Helm,
};

export function getReleasers(): Releasers {
Expand Down
50 changes: 50 additions & 0 deletions src/updaters/helm/chart-yaml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2021 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 {checkpoint, CheckpointType} from '../../util/checkpoint';
import {Update, UpdateOptions, VersionsMap} from '../update';
import {GitHubFileContents} from '../../github';
import * as yaml from 'js-yaml';

export class ChartYaml implements Update {
path: string;
changelogEntry: string;
version: string;
versions?: VersionsMap;
packageName: string;
create: boolean;
contents?: GitHubFileContents;

constructor(options: UpdateOptions) {
this.create = false;
this.path = options.path;
this.changelogEntry = options.changelogEntry;
this.version = options.version;
this.packageName = options.packageName;
}

updateContent(content: string): string {
const data = yaml.load(content, {json: true});
if (data === null || data === undefined) {
return '';
}
const parsed = JSON.parse(JSON.stringify(data));
checkpoint(
`updating ${this.path} from ${parsed.version} to ${this.version}`,
CheckpointType.Success
);
parsed.version = this.version;
return yaml.dump(parsed);
}
}
1 change: 1 addition & 0 deletions test/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ describe('CLI', () => {
'rust',
'simple',
'terraform-module',
'helm',
];
const parseCallback: ParseCallback = (err, _argv, _output) => {
expect(err).to.be.an('Error');
Expand Down
40 changes: 40 additions & 0 deletions test/updaters/chart-yaml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2021 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 {ChartYaml} from '../../src/updaters/helm/chart-yaml';

const fixturesPath = './test/updaters/fixtures';

describe('ChartYaml', () => {
describe('updateContent', () => {
it('updates version in Chart.yaml', async () => {
const oldContent = readFileSync(
resolve(fixturesPath, './helm/Chart.yaml'),
'utf8'
).replace(/\r\n/g, '\n');
const version = new ChartYaml({
path: './helm/Chart.yaml',
changelogEntry: '',
version: '1.1.0',
packageName: '',
});
const newContent = version.updateContent(oldContent);
snapshot(newContent);
});
});
});
24 changes: 24 additions & 0 deletions test/updaters/fixtures/helm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2021 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.

name: helm-test-repo
version: 1.0.0
apiVersion: v2
appVersion: 2.0.0
dependencies:
- name: another-repo
version: 0.15.3
repository: "linkToHelmChartRepo"
maintainers:
- Abhinav Khanna

0 comments on commit c9fbf78

Please sign in to comment.