- introduce capability of the action running 2 split

- first collect all pull requests and export to environment
  - second take exported data and continue generation of changelogs (makes it cheaper, by only collecting PRs once)
This commit is contained in:
Mike Penz
2023-05-26 13:41:59 +00:00
committed by GitHub
parent 66470e5f14
commit 0a80bc9a3b
12 changed files with 515 additions and 240 deletions
+158 -97
View File
@@ -2,11 +2,13 @@ import * as core from '@actions/core'
import {Configuration} from './configuration'
import {Octokit} from '@octokit/rest'
import {TagInfo, Tags} from './tags'
import {failOrError} from './utils'
import {checkExportedData, failOrError} from './utils'
import {HttpsProxyAgent} from 'https-proxy-agent'
import {PullRequestInfo, PullRequests} from './pullRequests'
import {Commits, DiffInfo} from './commits'
import {buildChangelog} from './transform'
import * as fs from 'fs'
import path from 'path'
export interface ReleaseNotesOptions {
owner: string // the owner of the repository
@@ -43,114 +45,166 @@ export class ReleaseNotesBuilder {
private fetchReviewers: boolean = false,
private fetchReleaseInformation: boolean = false,
private fetchReviews: boolean = false,
private commitMode: boolean,
private commitMode: boolean = false,
private exportCollected: boolean = false,
private exportOnly: boolean = false,
private configuration: Configuration
) {}
async build(): Promise<string | null> {
if (!this.owner) {
failOrError(`💥 Missing or couldn't resolve 'owner'`, this.failOnError)
return null
} else {
core.setOutput('owner', this.owner)
core.debug(`Resolved 'owner' as ${this.owner}`)
}
let releaseNotesData = checkExportedData()
if (releaseNotesData == null) {
if (!this.owner) {
failOrError(`💥 Missing or couldn't resolve 'owner'`, this.failOnError)
return null
} else {
core.setOutput('owner', this.owner)
core.debug(`Resolved 'owner' as ${this.owner}`)
}
if (!this.repo) {
failOrError(`💥 Missing or couldn't resolve 'owner'`, this.failOnError)
return null
} else {
core.setOutput('repo', this.repo)
core.debug(`Resolved 'repo' as ${this.repo}`)
}
core.endGroup()
if (!this.repo) {
failOrError(`💥 Missing or couldn't resolve 'owner'`, this.failOnError)
return null
} else {
core.setOutput('repo', this.repo)
core.debug(`Resolved 'repo' as ${this.repo}`)
}
core.endGroup()
// check proxy setup for GHES environments
const proxy = process.env.https_proxy || process.env.HTTPS_PROXY
const noProxy = process.env.no_proxy || process.env.NO_PROXY
let noProxyArray: string[] = []
if (noProxy) {
noProxyArray = noProxy.split(',')
}
// check proxy setup for GHES environments
const proxy = process.env.https_proxy || process.env.HTTPS_PROXY
const noProxy = process.env.no_proxy || process.env.NO_PROXY
let noProxyArray: string[] = []
if (noProxy) {
noProxyArray = noProxy.split(',')
}
// load octokit instance
const octokit = new Octokit({
auth: `token ${this.token || process.env.GITHUB_TOKEN}`,
baseUrl: `${this.baseUrl || 'https://api.github.com'}`
})
if (proxy) {
const agent = new HttpsProxyAgent(proxy)
octokit.hook.before('request', options => {
if (noProxyArray.includes(options.request.hostname)) {
return
}
options.request.agent = agent
// load octokit instance
const octokit = new Octokit({
auth: `token ${this.token || process.env.GITHUB_TOKEN}`,
baseUrl: `${this.baseUrl || 'https://api.github.com'}`
})
}
// ensure proper from <-> to tag range
core.startGroup(`🔖 Resolve tags`)
const tagsApi = new Tags(octokit)
const tagRange = await tagsApi.retrieveRange(
this.repositoryPath,
this.owner,
this.repo,
this.fromTag,
this.toTag,
this.ignorePreReleases,
this.configuration.max_tags_to_fetch,
this.configuration.tag_resolver
)
if (proxy) {
const agent = new HttpsProxyAgent(proxy)
octokit.hook.before('request', options => {
if (noProxyArray.includes(options.request.hostname)) {
return
}
options.request.agent = agent
})
}
let thisTag = tagRange.to
if (!thisTag) {
failOrError(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError)
return null
// ensure proper from <-> to tag range
core.startGroup(`🔖 Resolve tags`)
const tagsApi = new Tags(octokit)
const tagRange = await tagsApi.retrieveRange(
this.repositoryPath,
this.owner,
this.repo,
this.fromTag,
this.toTag,
this.ignorePreReleases,
this.configuration.max_tags_to_fetch,
this.configuration.tag_resolver
)
let thisTag = tagRange.to
if (!thisTag) {
failOrError(`💥 Missing or couldn't resolve 'toTag'`, this.failOnError)
return null
} else {
core.setOutput('toTag', thisTag.name)
core.debug(`Resolved 'toTag' as ${thisTag.name}`)
}
let previousTag = tagRange.from
if (previousTag == null) {
failOrError(`💥 Unable to retrieve previous tag given ${this.toTag}`, this.failOnError)
return null
}
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: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
fetchReviews: this.fetchReviews,
commitMode: this.commitMode,
configuration: this.configuration
}
releaseNotesData = await pullData(octokit, options, this.exportCollected, this.exportOnly)
} else {
core.setOutput('toTag', thisTag.name)
core.debug(`Resolved 'toTag' as ${thisTag.name}`)
}
core.info(`️ Retrieved previously exported collected data`)
let previousTag = tagRange.from
if (previousTag == null) {
failOrError(`💥 Unable to retrieve previous tag given ${this.toTag}`, this.failOnError)
// merge input with options (in case some data was updated)
const diffInfo = releaseNotesData.diffInfo
const mergedPullRequests = releaseNotesData.mergedPullRequests
const orgOptions = releaseNotesData.options
// merge fromTag info with provided info || otherwise use cached info
const fromTag: TagInfo = orgOptions.fromTag
if (this.fromTag != null) {
fromTag.name = this.fromTag
}
const toTag: TagInfo = orgOptions.toTag
if (this.toTag != null) {
toTag.name = this.toTag
}
// merge provided values with previous options (prefer provided)
const options: ReleaseNotesOptions = {
owner: this.owner || orgOptions.owner,
repo: this.repo || orgOptions.repo,
fromTag,
toTag,
includeOpen: this.includeOpen || orgOptions.includeOpen,
failOnError: this.failOnError || orgOptions.failOnError,
fetchReviewers: this.fetchReviewers || orgOptions.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation || orgOptions.fetchReleaseInformation,
fetchReviews: this.fetchReviews || orgOptions.fetchReviews,
commitMode: this.commitMode || orgOptions.commitMode,
configuration: this.configuration || orgOptions.configuration
}
releaseNotesData = {
diffInfo,
mergedPullRequests,
options
}
}
if (releaseNotesData != null) {
return buildChangelog(releaseNotesData.diffInfo, releaseNotesData.mergedPullRequests, releaseNotesData.options)
} else {
return null
}
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: previousTag,
toTag: thisTag,
includeOpen: this.includeOpen,
failOnError: this.failOnError,
fetchReviewers: this.fetchReviewers,
fetchReleaseInformation: this.fetchReleaseInformation,
fetchReviews: this.fetchReviews,
commitMode: this.commitMode,
configuration: this.configuration
}
const releaseNotesData = await pullData(octokit, options)
return buildChangelog(releaseNotesData.diffInfo, releaseNotesData.mergedPullRequests, releaseNotesData.options)
}
}
export async function pullData(octokit: Octokit, options: ReleaseNotesOptions): Promise<ReleaseNotesData> {
export async function pullData(
octokit: Octokit,
options: ReleaseNotesOptions,
exportCollected: boolean,
exportOnly: boolean
): Promise<ReleaseNotesData | null> {
let mergedPullRequests: PullRequestInfo[]
let diffInfo: DiffInfo
@@ -184,12 +238,19 @@ export async function pullData(octokit: Octokit, options: ReleaseNotesOptions):
core.setOutput('changes', diffInfo.changes)
core.setOutput('commits', diffInfo.commits)
const collectAndExport = true
if (collectAndExport) {
if (exportCollected) {
core.info('📦 Exporting collected data')
core.exportVariable('_diffInfo', JSON.stringify(diffInfo))
core.exportVariable('_mergedPullRequests', JSON.stringify(mergedPullRequests))
core.exportVariable('_options', JSON.stringify(options))
core.exportVariable(`RCBA_EXPORT_diffInfo`, JSON.stringify(diffInfo))
//fs.writeFileSync(path.resolve('diffInfo.json'), JSON.stringify(diffInfo))
core.exportVariable(`RCBA_EXPORT_mergedPullRequests`, JSON.stringify(mergedPullRequests))
//fs.writeFileSync(path.resolve('mergedPullRequests.json'), JSON.stringify(mergedPullRequests))
core.exportVariable(`RCBA_EXPORT_options`, JSON.stringify(options))
//fs.writeFileSync(path.resolve('options.json'), JSON.stringify(options))
if (exportOnly) {
core.endGroup()
return null
}
}
core.endGroup()