- introduce new configuration flag to fetchReleaseInformation

- if enabled, it will unlock the ability to use new placeholders
  - `${{ DAYS_SINCE }}`
  - `${{ FROM_TAG_DATE }}`
  - `${{ TO_TAG_DATE }}`
- refactor to carry on the full `TagInfo` information instead of only the tagname
- refresh testcases to new apis
This commit is contained in:
Mike Penz
2022-07-26 16:47:23 +02:00
parent 09ae38aa7f
commit c061d0e577
12 changed files with 291 additions and 83 deletions
+30 -20
View File
@@ -14,11 +14,12 @@ it('Should have empty changelog (tags)', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.0.1',
toTag: 'v0.0.2',
fromTag: { name: 'v0.0.1' },
toTag: { name: 'v0.0.2' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -33,11 +34,12 @@ it('Should match generated changelog (tags)', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.0.1',
toTag: 'v0.0.3',
fromTag: { name: 'v0.0.1'},
toTag: { name: 'v0.0.3' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -60,11 +62,12 @@ it('Should match generated changelog (refs)', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
fromTag: { name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3' },
toTag: { name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -95,11 +98,12 @@ it('Should match generated changelog and replace all occurrences (refs)', async
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
fromTag: { name: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3' },
toTag: { name: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -132,11 +136,12 @@ it('Should match ordered ASC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -156,11 +161,12 @@ it('Should match ordered DESC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -180,11 +186,12 @@ it('Should match ordered by title ASC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -204,11 +211,12 @@ it('Should match ordered by title DESC', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v0.3.0',
toTag: 'v0.5.0',
fromTag: { name: 'v0.3.0' },
toTag: { name: 'v0.5.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -228,11 +236,12 @@ it('Should ignore PRs not merged into develop branch', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v1.3.1',
toTag: 'v1.4.0',
fromTag: { name: 'v1.3.1' },
toTag: { name: 'v1.4.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -250,11 +259,12 @@ it('Should ignore PRs not merged into main branch', async () => {
const releaseNotes = new ReleaseNotes(octokit, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v1.3.1',
toTag: 'v1.4.0',
fromTag: { name: 'v1.3.1' },
toTag: { name: 'v1.4.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
+12
View File
@@ -17,6 +17,7 @@ it('Should match generated changelog (unspecified fromTag)', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -45,6 +46,7 @@ it('Should match generated changelog (unspecified tags)', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -70,6 +72,7 @@ it('Should use empty placeholder', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -96,6 +99,7 @@ it('Should fill empty placeholders', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -124,6 +128,7 @@ it('Should fill `template` placeholders', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -152,6 +157,7 @@ it('Should fill `template` placeholders, ignore', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -180,6 +186,7 @@ it('Uncategorized category', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false,
configuration
)
@@ -208,6 +215,7 @@ it('Verify commit based changelog', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
true,
configuration
)
@@ -236,6 +244,7 @@ it('Verify commit based changelog, with emoji categorisation', async () => {
false,
false,
false, // enable to fetch reviewers
false, // enable to fetch tag release information
true,
configuration
)
@@ -264,6 +273,7 @@ it('Verify default inclusion of open PRs', async () => {
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
@@ -292,6 +302,7 @@ it('Verify custom categorisation of open PRs', async () => {
false, // failOnError
false, // ignorePrePrelease
false, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
@@ -320,6 +331,7 @@ it('Verify reviewers who approved are fetched', async () => {
false, // failOnError
false, // ignorePrePrelease
true, // enable to fetch reviewers
false, // enable to fetch tag release information
false, // commitMode
configuration // configuration
)
+41 -28
View File
@@ -134,11 +134,12 @@ it('Extract label from title, combined regex', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -163,11 +164,12 @@ it('Extract label from title and body, combined regex', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, prs, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -194,11 +196,12 @@ it('Extract label from title, split regex', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -225,11 +228,12 @@ it('Extract label from title, match', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -251,11 +255,12 @@ it('Extract label from title, match multiple', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -278,11 +283,12 @@ it('Extract label from title, match multiple, custon non matching label', async
const resultChangelog = buildChangelog(DefaultDiffInfo, mergedPullRequests, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration
})
@@ -392,11 +398,12 @@ it('Match multiple labels exhaustive for category', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -417,11 +424,12 @@ it('Deduplicate duplicated PRs', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -443,11 +451,12 @@ it('Deduplicate duplicated PRs DESC', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -475,11 +484,12 @@ it('Use empty_content for empty category', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
@@ -497,11 +507,12 @@ it('Commit SHA-1 in commitMode', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: true,
configuration: customConfig
})
@@ -513,22 +524,23 @@ it('Commit SHA-1 in commitMode', async () => {
it('Release Diff', async () => {
const customConfig = Object.assign({}, DefaultConfiguration)
customConfig.template = "${{RELEASE_DIFF}}"
customConfig.template = "${{RELEASE_DIFF}}\n${{DAYS_SINCE}}"
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'release-changelog-builder-action',
fromTag: 'v2.8.0',
toTag: 'v2.8.1',
fromTag: { name: 'v2.8.0' },
toTag: { name: 'v2.8.1' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: true,
commitMode: true,
configuration: customConfig
})
expect(resultChangelog).toStrictEqual(
`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1`
`https://github.com/mikepenz/release-changelog-builder-action/compare/v2.8.0...v2.8.1\n`
)
})
@@ -557,11 +569,12 @@ it('Use exclude labels to not include a PR within a category.', async () => {
const resultChangelog = buildChangelog(DefaultDiffInfo, pullRequestsWithLabels, {
owner: 'mikepenz',
repo: 'test-repo',
fromTag: '1.0.0',
toTag: '2.0.0',
fromTag: { name: '1.0.0' },
toTag: { name: '2.0.0' },
includeOpen: false,
failOnError: false,
fetchReviewers: false,
fetchReleaseInformation: false,
commitMode: false,
configuration: customConfig
})
+3
View File
@@ -29,6 +29,9 @@ inputs:
fetchReviewers:
description: 'Will enable fetching the users/reviewers who approved the PR'
default: "false"
fetchReleaseInformation:
description: 'Will enable fetching release information for the tags. E.g. creation date'
default: "false"
commitMode:
description: 'Enables a `light` commit based mode. This mode generates changelogs based on the commits. Please note that this is not officially supported, and lacks a lot of features only possible with PRs.'
default: "false"
Generated Vendored
+82 -17
View File
@@ -307,6 +307,16 @@ class GitCommandManager {
return revListOutput.stdout.trim();
});
}
tagCreation(tagName) {
return __awaiter(this, void 0, void 0, function* () {
const creationDate = yield this.execGit([
'for-each-ref',
'--format="%(creatordate:rfc)"',
`refs/tags/${tagName}`
]);
return creationDate.stdout.trim().replace(/"/g, '');
});
}
static createCommandManager(workingDirectory) {
return __awaiter(this, void 0, void 0, function* () {
const result = new GitCommandManager();
@@ -417,8 +427,9 @@ function run() {
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true';
const failOnError = core.getInput('failOnError') === 'true';
const fetchReviewers = core.getInput('fetchReviewers') === 'true';
const fetchReleaseInformation = core.getInput('fetchReleaseInformation') === 'true';
const commitMode = core.getInput('commitMode') === 'true';
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchReviewers, commitMode, configuration).build();
const result = yield new releaseNotesBuilder_1.ReleaseNotesBuilder(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen, failOnError, ignorePreReleases, fetchReviewers, fetchReleaseInformation, commitMode, configuration).build();
core.setOutput('changelog', result);
// write the result in changelog to file if possible
const outputFile = core.getInput('outputFile');
@@ -795,7 +806,7 @@ class ReleaseNotes {
const commitsApi = new commits_1.Commits(octokit);
let diffInfo;
try {
diffInfo = yield commitsApi.getDiff(owner, repo, fromTag, toTag);
diffInfo = yield commitsApi.getDiff(owner, repo, fromTag.name, toTag.name);
}
catch (error) {
(0, utils_1.failOrError)(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`, failOnError);
@@ -876,6 +887,9 @@ class ReleaseNotes {
}
}
}
else {
core.debug(`️ Fetching reviewers was disabled`);
}
return [diffInfo, finalPrs];
});
}
@@ -965,7 +979,7 @@ const releaseNotes_1 = __nccwpck_require__(5882);
const tags_1 = __nccwpck_require__(7532);
const utils_1 = __nccwpck_require__(918);
class ReleaseNotesBuilder {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, commitMode, configuration) {
constructor(baseUrl, token, repositoryPath, owner, repo, fromTag, toTag, includeOpen = false, failOnError, ignorePreReleases, fetchReviewers = false, fetchReleaseInformation = false, commitMode, configuration) {
this.baseUrl = baseUrl;
this.token = token;
this.repositoryPath = repositoryPath;
@@ -977,11 +991,11 @@ class ReleaseNotesBuilder {
this.failOnError = failOnError;
this.ignorePreReleases = ignorePreReleases;
this.fetchReviewers = fetchReviewers;
this.fetchReleaseInformation = fetchReleaseInformation;
this.commitMode = commitMode;
this.configuration = configuration;
}
build() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (!this.owner) {
(0, utils_1.failOrError)(`💥 Missing or couldn't resolve 'owner'`, this.failOnError);
@@ -1010,33 +1024,41 @@ class ReleaseNotesBuilder {
const tagsApi = new tags_1.Tags(octokit);
const tagRange = yield tagsApi.retrieveRange(this.repositoryPath, this.owner, this.repo, this.fromTag, this.toTag, this.ignorePreReleases, this.configuration.max_tags_to_fetch ||
configuration_1.DefaultConfiguration.max_tags_to_fetch, this.configuration.tag_resolver || configuration_1.DefaultConfiguration.tag_resolver);
const thisTag = (_a = tagRange.to) === null || _a === void 0 ? void 0 : _a.name;
let thisTag = tagRange.to;
if (!thisTag) {
(0, utils_1.failOrError)(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError);
return null;
}
else {
this.toTag = thisTag;
core.setOutput('toTag', thisTag);
core.debug(`Resolved 'toTag' as ${thisTag}`);
core.setOutput('toTag', thisTag.name);
core.debug(`Resolved 'toTag' as ${thisTag.name}`);
}
const previousTag = (_b = tagRange.from) === null || _b === void 0 ? void 0 : _b.name;
let previousTag = tagRange.from;
if (previousTag == null) {
(0, utils_1.failOrError)(`💥 Unable to retrieve previous tag given ${this.toTag}`, this.failOnError);
return null;
}
this.fromTag = previousTag;
core.setOutput('fromTag', previousTag);
core.debug(`fromTag resolved via previousTag as: ${previousTag}`);
core.setOutput('fromTag', previousTag.name);
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`);
if (this.fetchReleaseInformation) {
// load release information from the GitHub API
core.info(`️ Fetching release information was enabled`);
thisTag = yield tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, thisTag);
previousTag = yield tagsApi.fillTagInformation(this.repositoryPath, this.owner, this.repo, previousTag);
}
else {
core.debug(`️ Fetching release information was disabled`);
}
core.endGroup();
const options = {
owner: this.owner,
repo: this.repo,
fromTag: this.fromTag,
toTag: this.toTag,
fromTag: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
commitMode: this.commitMode,
configuration: this.configuration
};
@@ -1094,6 +1116,9 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.sortTags = exports.filterTags = exports.Tags = void 0;
const core = __importStar(__nccwpck_require__(2186));
@@ -1102,6 +1127,7 @@ const semver = __importStar(__nccwpck_require__(1383));
const semver_1 = __nccwpck_require__(1383);
const gitHelper_1 = __nccwpck_require__(353);
const transform_1 = __nccwpck_require__(1644);
const moment_1 = __importDefault(__nccwpck_require__(9623));
class Tags {
constructor(octokit) {
this.octokit = octokit;
@@ -1143,6 +1169,32 @@ class Tags {
return tagsInfo;
});
}
fillTagInformation(repositoryPath, owner, repo, tagInfo) {
return __awaiter(this, void 0, void 0, function* () {
const options = this.octokit.repos.getReleaseByTag.endpoint.merge({
owner,
repo,
tag: tagInfo.name
});
try {
const response = yield this.octokit.request(options);
const release = response.data;
tagInfo.date = (0, moment_1.default)(release.created_at);
core.info(`️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API for ${owner}/${repo}`);
}
catch (error) {
core.info(`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`);
const gitHelper = yield (0, gitHelper_1.createCommandManager)(repositoryPath);
const creationTimeString = yield gitHelper.tagCreation(tagInfo.name);
const creationTime = (0, moment_1.default)(creationTimeString);
if (creationTimeString !== null && creationTime.isValid()) {
tagInfo.date = creationTime;
core.info(`️ Resolved tag creation time (${creationTimeString}) from 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}`);
}
}
return tagInfo;
});
}
findPredecessorTag(sortedTags, repositoryPath, tag, ignorePreReleases) {
return __awaiter(this, void 0, void 0, function* () {
const tags = sortedTags;
@@ -1601,13 +1653,26 @@ function buildChangelog(diffInfo, prs, options) {
}
exports.buildChangelog = buildChangelog;
function fillAdditionalPlaceholders(text, options) {
var _a, _b;
let transformed = text;
// repository placeholders
transformed = transformed.replace(/\${{OWNER}}/g, options.owner);
transformed = transformed.replace(/\${{REPO}}/g, options.repo);
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag);
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag);
transformed = transformed.replace(/\${{RELEASE_DIFF}}/g, `https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag}...${options.toTag}`);
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag.name);
transformed = transformed.replace(/\${{FROM_TAG_SHA}}/g, options.fromTag.commit || '');
transformed = transformed.replace(/\${{FROM_TAG_DATE}}/g, ((_a = options.fromTag.date) === null || _a === void 0 ? void 0 : _a.toISOString()) || '');
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag.name);
transformed = transformed.replace(/\${{TO_TAG_SHA}}/g, options.toTag.commit || '');
transformed = transformed.replace(/\${{TO_TAG_DATE}}/g, ((_b = options.toTag.date) === null || _b === void 0 ? void 0 : _b.toISOString()) || '');
const fromDate = options.fromTag.date;
const toDate = options.toTag.date;
if (fromDate !== undefined && toDate !== undefined) {
transformed = transformed.replace(/\${{DAYS_SINCE}}/g, toDate.diff(fromDate, 'days').toString() || '');
}
else {
transformed = transformed.replace(/\${{DAYS_SINCE}}/g, '');
}
transformed = transformed.replace(/\${{RELEASE_DIFF}}/g, `https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`);
return transformed;
}
exports.fillAdditionalPlaceholders = fillAdditionalPlaceholders;
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+9
View File
@@ -44,6 +44,15 @@ class GitCommandManager {
return revListOutput.stdout.trim()
}
async tagCreation(tagName: string): Promise<string> {
const creationDate = await this.execGit([
'for-each-ref',
'--format="%(creatordate:rfc)"',
`refs/tags/${tagName}`
])
return creationDate.stdout.trim().replace(/"/g, '')
}
static async createCommandManager(
workingDirectory: string
): Promise<GitCommandManager> {
+3
View File
@@ -36,6 +36,8 @@ async function run(): Promise<void> {
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'
const failOnError = core.getInput('failOnError') === 'true'
const fetchReviewers = core.getInput('fetchReviewers') === 'true'
const fetchReleaseInformation =
core.getInput('fetchReleaseInformation') === 'true'
const commitMode = core.getInput('commitMode') === 'true'
const result = await new ReleaseNotesBuilder(
@@ -50,6 +52,7 @@ async function run(): Promise<void> {
failOnError,
ignorePreReleases,
fetchReviewers,
fetchReleaseInformation,
commitMode,
configuration
).build()
+7 -3
View File
@@ -5,15 +5,17 @@ import {PullRequestInfo, PullRequests} from './pullRequests'
import {Octokit} from '@octokit/rest'
import {buildChangelog, fillAdditionalPlaceholders} from './transform'
import {failOrError} from './utils'
import {TagInfo} from './tags'
export interface ReleaseNotesOptions {
owner: string // the owner of the repository
repo: string // the repository
fromTag: string // the tag/ref to start from
toTag: string // the tag/ref up to
fromTag: TagInfo // the tag/ref to start from
toTag: TagInfo // the tag/ref up to
includeOpen: boolean // defines if we should also fetch open pull requests
failOnError: boolean // defines if we should fail the action in case of an error
fetchReviewers: boolean // defines if the action should fetch the reviewers for PRs - approved reviewers are not included in the default PR listing
fetchReleaseInformation: boolean // defines if the action should fetch the release information for the from and to tag - e.g. the creation date for the associated release
commitMode: boolean // defines if we use the alternative commit based mode. note: this is only partially supported
configuration: Configuration // the configuration as defined in `configuration.ts`
}
@@ -83,7 +85,7 @@ export class ReleaseNotes {
const commitsApi = new Commits(octokit)
let diffInfo: DiffInfo
try {
diffInfo = await commitsApi.getDiff(owner, repo, fromTag, toTag)
diffInfo = await commitsApi.getDiff(owner, repo, fromTag.name, toTag.name)
} catch (error) {
failOrError(
`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`,
@@ -212,6 +214,8 @@ export class ReleaseNotes {
)
}
}
} else {
core.debug(`️ Fetching reviewers was disabled`)
}
return [diffInfo, finalPrs]
+30 -10
View File
@@ -18,6 +18,7 @@ export class ReleaseNotesBuilder {
private failOnError: boolean,
private ignorePreReleases: boolean,
private fetchReviewers: boolean = false,
private fetchReleaseInformation: boolean = false,
private commitMode: boolean,
private configuration: Configuration
) {}
@@ -61,17 +62,16 @@ export class ReleaseNotesBuilder {
this.configuration.tag_resolver || DefaultConfiguration.tag_resolver
)
const thisTag = tagRange.to?.name
let thisTag = tagRange.to
if (!thisTag) {
failOrError(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError)
return null
} else {
this.toTag = thisTag
core.setOutput('toTag', thisTag)
core.debug(`Resolved 'toTag' as ${thisTag}`)
core.setOutput('toTag', thisTag.name)
core.debug(`Resolved 'toTag' as ${thisTag.name}`)
}
const previousTag = tagRange.from?.name
let previousTag = tagRange.from
if (previousTag == null) {
failOrError(
`💥 Unable to retrieve previous tag given ${this.toTag}`,
@@ -79,19 +79,39 @@ export class ReleaseNotesBuilder {
)
return null
}
this.fromTag = previousTag
core.setOutput('fromTag', previousTag)
core.debug(`fromTag resolved via previousTag as: ${previousTag}`)
core.setOutput('fromTag', previousTag.name)
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`)
if (this.fetchReleaseInformation) {
// load release information from the GitHub API
core.info(`️ Fetching release information was enabled`)
thisTag = await tagsApi.fillTagInformation(
this.repositoryPath,
this.owner,
this.repo,
thisTag
)
previousTag = await tagsApi.fillTagInformation(
this.repositoryPath,
this.owner,
this.repo,
previousTag
)
} else {
core.debug(`️ Fetching release information was disabled`)
}
core.endGroup()
const options = {
owner: this.owner,
repo: this.repo,
fromTag: this.fromTag,
toTag: this.toTag,
fromTag: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
commitMode: this.commitMode,
configuration: this.configuration
}
+44 -1
View File
@@ -6,6 +6,7 @@ import {SemVer} from 'semver'
import {TagResolver} from './configuration'
import {createCommandManager} from './gitHelper'
import {RegexTransformer, validateTransformer} from './transform'
import moment from 'moment'
export interface TagResult {
from: TagInfo | null
@@ -14,7 +15,8 @@ export interface TagResult {
export interface TagInfo {
name: string
commit: string
commit?: string
date?: moment.Moment
}
export interface SortableTagInfo extends TagInfo {
@@ -61,6 +63,47 @@ export class Tags {
return tagsInfo
}
async fillTagInformation(
repositoryPath: string,
owner: string,
repo: string,
tagInfo: TagInfo
): Promise<TagInfo> {
const options = this.octokit.repos.getReleaseByTag.endpoint.merge({
owner,
repo,
tag: tagInfo.name
})
try {
const response = await this.octokit.request(options)
type ReleaseInformation =
RestEndpointMethodTypes['repos']['getReleaseByTag']['response']['data']
const release: ReleaseInformation = response.data as ReleaseInformation
tagInfo.date = moment(release.created_at)
core.info(
`️ Retrieved information about the release associated with ${tagInfo.name} from the GitHub API for ${owner}/${repo}`
)
} catch (error) {
core.info(
`⚠️ No release information found for ${tagInfo.name}, trying to retrieve tag creation time as fallback.`
)
const gitHelper = await createCommandManager(repositoryPath)
const creationTimeString = await gitHelper.tagCreation(tagInfo.name)
const creationTime = moment(creationTimeString)
if (creationTimeString !== null && creationTime.isValid()) {
tagInfo.date = creationTime
core.info(
`️ Resolved tag creation time (${creationTimeString}) from 'git for-each-ref --format="%(creatordate:rfc)" "refs/tags/${tagInfo.name}`
)
}
}
return tagInfo
}
async findPredecessorTag(
sortedTags: TagInfo[],
repositoryPath: string,
+29 -3
View File
@@ -312,11 +312,37 @@ export function fillAdditionalPlaceholders(
// repository placeholders
transformed = transformed.replace(/\${{OWNER}}/g, options.owner)
transformed = transformed.replace(/\${{REPO}}/g, options.repo)
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag)
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag)
transformed = transformed.replace(/\${{FROM_TAG}}/g, options.fromTag.name)
transformed = transformed.replace(
/\${{FROM_TAG_SHA}}/g,
options.fromTag.commit || ''
)
transformed = transformed.replace(
/\${{FROM_TAG_DATE}}/g,
options.fromTag.date?.toISOString() || ''
)
transformed = transformed.replace(/\${{TO_TAG}}/g, options.toTag.name)
transformed = transformed.replace(
/\${{TO_TAG_SHA}}/g,
options.toTag.commit || ''
)
transformed = transformed.replace(
/\${{TO_TAG_DATE}}/g,
options.toTag.date?.toISOString() || ''
)
const fromDate = options.fromTag.date
const toDate = options.toTag.date
if (fromDate !== undefined && toDate !== undefined) {
transformed = transformed.replace(
/\${{DAYS_SINCE}}/g,
toDate.diff(fromDate, 'days').toString() || ''
)
} else {
transformed = transformed.replace(/\${{DAYS_SINCE}}/g, '')
}
transformed = transformed.replace(
/\${{RELEASE_DIFF}}/g,
`https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag}...${options.toTag}`
`https://github.com/${options.owner}/${options.repo}/compare/${options.fromTag.name}...${options.toTag.name}`
)
return transformed
}