Skip to content

Commit c9fbf78

Browse files
author
Abhinav Khanna
authored
feat: add support for helm (googleapis#748)
1 parent 869f1a1 commit c9fbf78

File tree

10 files changed

+282
-4
lines changed

10 files changed

+282
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ system-test/fixtures/gh/*
77
__pycache__
88
package-lock.json
99
debug.sh
10+
.idea/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Release Please automates releases for the following flavors of repositories:
8787
| rust | A Rust repository, with a Cargo.toml (either as a crate or workspace) and a CHANGELOG.md |
8888
| ocaml | [An OCaml repository, containing 1 or more opam or esy files and a CHANGELOG.md](https://github.com/grain-lang/binaryen.ml) |
8989
| simple | [A repository with a version.txt and a CHANGELOG.md](https://github.com/googleapis/gapic-generator) |
90-
90+
| helm | A repository with a Chart.yaml and a CHANGELOG.md |
9191
## Adding additional release types
9292

9393
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)

__snapshots__/chart-yaml.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
exports['ChartYaml updateContent updates version in Chart.yaml 1'] = `
2+
name: helm-test-repo
3+
version: 1.1.0
4+
apiVersion: v2
5+
appVersion: 2.0.0
6+
dependencies:
7+
- name: another-repo
8+
version: 0.15.3
9+
repository: linkToHelmChartRepo
10+
maintainers:
11+
- Abhinav Khanna
12+
13+
`

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"nock": "^13.0.0",
5252
"sinon": "^9.0.3",
5353
"snap-shot-it": "^7.0.0",
54-
"typescript": "^3.8.3"
54+
"js-yaml": "^4.0.0",
55+
"@types/js-yaml": "^4.0.0"
5556
},
5657
"dependencies": {
5758
"@conventional-commits/parser": "^0.4.1",
@@ -70,7 +71,8 @@
7071
"type-fest": "^0.20.0",
7172
"unist-util-visit": "^2.0.3",
7273
"unist-util-visit-parents": "^3.1.1",
73-
"yargs": "^16.0.0"
74+
"yargs": "^16.0.0",
75+
"typescript": "^3.8.3"
7476
},
7577
"engines": {
7678
"node": ">=10.12.0"

src/releasers/helm.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {ReleasePR, ReleaseCandidate} from '../release-pr';
16+
17+
import {ConventionalCommits} from '../conventional-commits';
18+
import {GitHub, GitHubTag, GitHubFileContents} from '../github';
19+
import {checkpoint, CheckpointType} from '../util/checkpoint';
20+
import {Update} from '../updaters/update';
21+
import {Commit} from '../graphql-to-commits';
22+
23+
// Generic
24+
import {Changelog} from '../updaters/changelog';
25+
import * as yaml from 'js-yaml';
26+
// helm
27+
import {ChartYaml} from '../updaters/helm/chart-yaml';
28+
29+
export class Helm extends ReleasePR {
30+
static releaserName = 'helm';
31+
32+
protected async _run(): Promise<number | undefined> {
33+
// Make an effort to populate packageName from the contents of
34+
// the package.json, rather than forcing this to be set:
35+
const contents: GitHubFileContents = await this.gh.getFileContents(
36+
this.addPath('Chart.yaml')
37+
);
38+
const file = yaml.load(contents.parsedContent, {json: true});
39+
if (file === null || file === undefined) {
40+
return undefined;
41+
}
42+
const pkg = JSON.parse(JSON.stringify(file));
43+
if (pkg.name) {
44+
this.packageName = pkg.name;
45+
// we've rewritten the package name, recalculate the package prefix
46+
this.packagePrefix = this.coercePackagePrefix(pkg.name);
47+
}
48+
49+
const latestTag: GitHubTag | undefined = await this.latestTag(
50+
this.monorepoTags ? `${this.packagePrefix}-` : undefined
51+
);
52+
const commits: Commit[] = await this.commits({
53+
sha: latestTag ? latestTag.sha : undefined,
54+
path: this.path,
55+
});
56+
57+
const cc = new ConventionalCommits({
58+
commits,
59+
githubRepoUrl: this.repoUrl,
60+
bumpMinorPreMajor: this.bumpMinorPreMajor,
61+
changelogSections: this.changelogSections,
62+
});
63+
const candidate: ReleaseCandidate = await this.coerceReleaseCandidate(
64+
cc,
65+
latestTag
66+
);
67+
const changelogEntry: string = await cc.generateChangelogEntry({
68+
version: candidate.version,
69+
currentTag: `v${candidate.version}`,
70+
previousTag: candidate.previousTag,
71+
});
72+
73+
// don't create a release candidate until user facing changes
74+
// (fix, feat, BREAKING CHANGE) have been made; a CHANGELOG that's
75+
// one line is a good indicator that there were no interesting commits.
76+
if (this.changelogEmpty(changelogEntry)) {
77+
checkpoint(
78+
`no user facing commits found since ${
79+
latestTag ? latestTag.sha : 'beginning of time'
80+
}`,
81+
CheckpointType.Failure
82+
);
83+
return undefined;
84+
}
85+
86+
const updates: Update[] = [];
87+
88+
updates.push(
89+
new Changelog({
90+
path: this.addPath('CHANGELOG.md'),
91+
changelogEntry,
92+
version: candidate.version,
93+
packageName: this.packageName,
94+
})
95+
);
96+
97+
updates.push(
98+
new ChartYaml({
99+
path: this.addPath('Chart.yaml'),
100+
changelogEntry,
101+
version: candidate.version,
102+
packageName: this.packageName,
103+
contents,
104+
})
105+
);
106+
107+
return await this.openPR({
108+
sha: commits[0].sha!,
109+
changelogEntry: `${changelogEntry}\n---\n`,
110+
updates,
111+
version: candidate.version,
112+
includePackageName: this.monorepoTags,
113+
});
114+
}
115+
116+
// A releaser can implement this method to automatically detect
117+
// the release name when creating a GitHub release, for instance by returning
118+
// name in package.json, or setup.py.
119+
static async lookupPackageName(
120+
gh: GitHub,
121+
path?: string
122+
): Promise<string | undefined> {
123+
// Make an effort to populate packageName from the contents of
124+
// the package.json, rather than forcing this to be set:
125+
const contents: GitHubFileContents = await gh.getFileContents(
126+
this.addPathStatic('Chart.yaml', path)
127+
);
128+
const file = yaml.load(contents.parsedContent, {json: true});
129+
if (file === null || file === undefined) {
130+
return undefined;
131+
}
132+
const pkg = JSON.parse(JSON.stringify(file));
133+
if (pkg.name) return pkg.name;
134+
else return undefined;
135+
}
136+
137+
// Parse the package prefix for releases from the full package name
138+
// The package name usually looks like `@[group]/[library]`
139+
protected coercePackagePrefix(packageName: string): string {
140+
return packageName.match(/^@[\w-]+\//)
141+
? packageName.split('/')[1]
142+
: packageName;
143+
}
144+
}

src/releasers/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {Simple} from './simple';
2626
import {TerraformModule} from './terraform-module';
2727
import {Rust} from './rust';
2828
import {OCaml} from './ocaml';
29+
import {Helm} from './helm';
2930

3031
// add any new releasers you create to this type as well as the `releasers`
3132
// object below.
@@ -43,7 +44,8 @@ export type ReleaseType =
4344
| 'ruby-yoshi'
4445
| 'rust'
4546
| 'simple'
46-
| 'terraform-module';
47+
| 'terraform-module'
48+
| 'helm';
4749

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

@@ -61,6 +63,7 @@ const releasers: Releasers = {
6163
rust: Rust,
6264
simple: Simple,
6365
'terraform-module': TerraformModule,
66+
helm: Helm,
6467
};
6568

6669
export function getReleasers(): Releasers {

src/updaters/helm/chart-yaml.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {checkpoint, CheckpointType} from '../../util/checkpoint';
16+
import {Update, UpdateOptions, VersionsMap} from '../update';
17+
import {GitHubFileContents} from '../../github';
18+
import * as yaml from 'js-yaml';
19+
20+
export class ChartYaml implements Update {
21+
path: string;
22+
changelogEntry: string;
23+
version: string;
24+
versions?: VersionsMap;
25+
packageName: string;
26+
create: boolean;
27+
contents?: GitHubFileContents;
28+
29+
constructor(options: UpdateOptions) {
30+
this.create = false;
31+
this.path = options.path;
32+
this.changelogEntry = options.changelogEntry;
33+
this.version = options.version;
34+
this.packageName = options.packageName;
35+
}
36+
37+
updateContent(content: string): string {
38+
const data = yaml.load(content, {json: true});
39+
if (data === null || data === undefined) {
40+
return '';
41+
}
42+
const parsed = JSON.parse(JSON.stringify(data));
43+
checkpoint(
44+
`updating ${this.path} from ${parsed.version} to ${this.version}`,
45+
CheckpointType.Success
46+
);
47+
parsed.version = this.version;
48+
return yaml.dump(parsed);
49+
}
50+
}

test/cli.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ describe('CLI', () => {
7474
'rust',
7575
'simple',
7676
'terraform-module',
77+
'helm',
7778
];
7879
const parseCallback: ParseCallback = (err, _argv, _output) => {
7980
expect(err).to.be.an('Error');

test/updaters/chart-yaml.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {readFileSync} from 'fs';
16+
import {resolve} from 'path';
17+
import * as snapshot from 'snap-shot-it';
18+
import {describe, it} from 'mocha';
19+
import {ChartYaml} from '../../src/updaters/helm/chart-yaml';
20+
21+
const fixturesPath = './test/updaters/fixtures';
22+
23+
describe('ChartYaml', () => {
24+
describe('updateContent', () => {
25+
it('updates version in Chart.yaml', async () => {
26+
const oldContent = readFileSync(
27+
resolve(fixturesPath, './helm/Chart.yaml'),
28+
'utf8'
29+
).replace(/\r\n/g, '\n');
30+
const version = new ChartYaml({
31+
path: './helm/Chart.yaml',
32+
changelogEntry: '',
33+
version: '1.1.0',
34+
packageName: '',
35+
});
36+
const newContent = version.updateContent(oldContent);
37+
snapshot(newContent);
38+
});
39+
});
40+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: helm-test-repo
16+
version: 1.0.0
17+
apiVersion: v2
18+
appVersion: 2.0.0
19+
dependencies:
20+
- name: another-repo
21+
version: 0.15.3
22+
repository: "linkToHelmChartRepo"
23+
maintainers:
24+
- Abhinav Khanna

0 commit comments

Comments
 (0)