- 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:
@@ -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> {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user