Files
release-changelog-builder-a…/src/utils.ts
T

228 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as core from '@actions/core'
import * as fs from 'fs'
import * as path from 'path'
import {Configuration, DefaultConfiguration} from './configuration'
import moment from 'moment'
import {DiffInfo} from './pr-collector/commits'
import {PullRequestInfo} from './pr-collector/pullRequests'
import {Data, ReleaseNotesOptions} from './releaseNotesBuilder'
import {env} from 'process'
/**
* Resolves the repository path, relatively to the GITHUB_WORKSPACE
*/
export function retrieveRepositoryPath(providedPath: string): string {
let githubWorkspacePath = process.env['GITHUB_WORKSPACE']
if (!githubWorkspacePath) {
throw new Error('GITHUB_WORKSPACE not defined')
}
githubWorkspacePath = path.resolve(githubWorkspacePath)
core.debug(`GITHUB_WORKSPACE = '${githubWorkspacePath}'`)
let repositoryPath = providedPath || '.'
repositoryPath = path.resolve(githubWorkspacePath, repositoryPath)
core.debug(`repositoryPath = '${repositoryPath}'`)
return repositoryPath
}
export function writeCacheData(data: Data, cacheOutput: string | null): void {
let cacheFile: string
if (cacheOutput && !cacheOutput.startsWith('{')) {
// legacy handling, originally we allowed cache as direct string.
// However, this can result in a "Argument list too long" exception for very long caches
cacheFile = cacheOutput
} else {
if (env.RUNNER_TEMP && !fs.existsSync(env.RUNNER_TEMP)) {
fs.mkdirSync(env.RUNNER_TEMP)
}
cacheFile = `${env.RUNNER_TEMP}/rcba-cache.json`
core.debug(`Defined cacheFile as ${cacheFile}`)
}
try {
fs.writeFileSync(cacheFile, JSON.stringify(data))
core.setOutput(`cache`, cacheFile)
} catch (error) {
core.warning(`Failed to write cache file. (${error})`)
}
}
/**
* Retrieves the exported information from a previous run of the `release-changelog-builder-action`.
* If available, return a [ReleaseNotesData].
*/
export function checkExportedData(exportCache: boolean, cacheInput: string | null): Data | null {
if (exportCache) {
return null
}
if (cacheInput) {
// legacy handling, originally we allowed cache as direct string.
// However, this can result in a "Argument list too long" exception for very long caches
const legacyJsonCache = cacheInput.startsWith('{')
let cache: Data
if (legacyJsonCache) {
cache = JSON.parse(cacheInput)
} else {
if (!fs.existsSync(cacheInput)) {
throw new Error(`💥 The provided cache file does not exist`)
} else {
cache = JSON.parse(fs.readFileSync(cacheInput, 'utf8'))
}
}
const diffInfo: DiffInfo = cache.diffInfo
const mergedPullRequests: PullRequestInfo[] = cache.mergedPullRequests
for (const pr of mergedPullRequests) {
pr.createdAt = moment(pr.createdAt)
if (pr.mergedAt) {
pr.mergedAt = moment(pr.mergedAt)
}
if (pr.reviews) {
for (const review of pr.reviews) {
if (review.submittedAt) {
review.submittedAt = moment(review.submittedAt)
}
}
}
}
const options: ReleaseNotesOptions = cache.options
if (options.fromTag.date) {
options.fromTag.date = moment(options.fromTag.date)
}
if (options.toTag.date) {
options.toTag.date = moment(options.toTag.date)
}
return {
diffInfo,
mergedPullRequests,
options
}
} else {
return null
}
}
/**
* Retrieves the configuration given the file path, if not found it will fallback to the `DefaultConfiguration`
*/
export function resolveConfiguration(githubWorkspacePath: string, configurationFile: string): Configuration | undefined {
if (configurationFile) {
const configurationPath = path.resolve(githubWorkspacePath, configurationFile)
core.debug(`configurationPath = '${configurationPath}'`)
const providedConfiguration = readConfiguration(configurationPath)
if (providedConfiguration) {
const configuration = providedConfiguration
core.info(`️ Configuration successfully loaded.`)
if (core.isDebug()) {
core.debug(`configuration = ${JSON.stringify(configuration)}`)
}
return configuration
} else {
core.debug(`Configuration file could not be read.`)
}
} else {
core.debug(`Configuration file not provided.`)
}
return undefined
}
/**
* Reads in the configuration from the JSON file
*/
function readConfiguration(filename: string): Configuration | undefined {
try {
const rawdata = fs.readFileSync(filename, 'utf8')
if (rawdata) {
return parseConfiguration(rawdata)
}
} catch (error) {
core.debug(`Failed to load configuration due to: ${error}`)
}
core.info(`⚠️ Configuration provided, but it couldn't be found. Fallback to Defaults.`)
return undefined
}
/**
* Parses the configuration from the JSON file
*/
export function parseConfiguration(config: string): Configuration | undefined {
try {
// for compatiblity with the `yml` file we require to use `#{{}}` instead of `${{}}` - replace it here.
const configurationJSON: Configuration = JSON.parse(config.replace(/#{{/g, '${{'))
return configurationJSON
} catch (error) {
core.info(`⚠️ Configuration provided, but it couldn't be parsed. Fallback to Defaults.`)
return undefined
}
}
/**
* Merges the configurations, will fallback to the DefaultConfiguration value
*/
export function mergeConfiguration(jc?: Configuration, fc?: Configuration): Configuration {
return {
max_tags_to_fetch: jc?.max_tags_to_fetch || fc?.max_tags_to_fetch || DefaultConfiguration.max_tags_to_fetch,
max_pull_requests: jc?.max_pull_requests || fc?.max_pull_requests || DefaultConfiguration.max_pull_requests,
max_back_track_time_days: jc?.max_back_track_time_days || fc?.max_back_track_time_days || DefaultConfiguration.max_back_track_time_days,
exclude_merge_branches: jc?.exclude_merge_branches || fc?.exclude_merge_branches || DefaultConfiguration.exclude_merge_branches,
sort: jc?.sort || fc?.sort || DefaultConfiguration.sort,
template: jc?.template || fc?.template || DefaultConfiguration.template,
pr_template: jc?.pr_template || fc?.pr_template || DefaultConfiguration.pr_template,
empty_template: jc?.empty_template || fc?.empty_template || DefaultConfiguration.empty_template,
categories: jc?.categories || fc?.categories || DefaultConfiguration.categories,
ignore_labels: jc?.ignore_labels || fc?.ignore_labels || DefaultConfiguration.ignore_labels,
label_extractor: jc?.label_extractor || fc?.label_extractor || DefaultConfiguration.label_extractor,
duplicate_filter: jc?.duplicate_filter || fc?.duplicate_filter || DefaultConfiguration.duplicate_filter,
transformers: jc?.transformers || fc?.transformers || DefaultConfiguration.transformers,
tag_resolver: jc?.tag_resolver || fc?.tag_resolver || DefaultConfiguration.tag_resolver,
base_branches: jc?.base_branches || fc?.base_branches || DefaultConfiguration.base_branches,
custom_placeholders: jc?.custom_placeholders || fc?.custom_placeholders || DefaultConfiguration.custom_placeholders,
trim_values: jc?.trim_values || fc?.trim_values || DefaultConfiguration.trim_values
}
}
/**
* Writes the changelog to the given the file
*/
export function writeOutput(githubWorkspacePath: string, outputFile: string, changelog: string | null): void {
if (outputFile && changelog) {
const outputPath = path.resolve(githubWorkspacePath, outputFile)
core.debug(`outputPath = '${outputPath}'`)
try {
fs.writeFileSync(outputPath, changelog)
} catch (error: any /* eslint-disable-line @typescript-eslint/no-explicit-any */) {
core.warning(`⚠️ Could not write the file to disk - ${error.message}`)
}
}
}
export function createOrSet<T>(map: Map<string, T[]>, key: string, value: T): void {
const entry = map.get(key)
if (!entry) {
map.set(key, [value])
} else {
entry.push(value)
}
}
export function haveCommonElements(arr1: string[], arr2: Set<string>): boolean {
return arr1.some(item => arr2.has(item))
}
export function haveCommonElementsArr(arr1: string[], arr2: string[]): boolean {
return haveCommonElements(arr1, new Set(arr2))
}
export function haveEveryElements(arr1: string[], arr2: Set<string>): boolean {
return arr1.every(item => arr2.has(item))
}
export function haveEveryElementsArr(arr1: string[], arr2: string[]): boolean {
return haveEveryElements(arr1, new Set(arr2))
}