Merge pull request #37 from mikepenz/feature/fail_on_error
Introduce configuration to fail on errors
This commit is contained in:
@@ -32,13 +32,14 @@
|
|||||||
### What's included 🚀
|
### What's included 🚀
|
||||||
|
|
||||||
- Super simple integration
|
- Super simple integration
|
||||||
- even on huge repositories with hundreds of tags
|
- ...even on huge repositories with hundreds of tags
|
||||||
- Parallel releases support
|
- Parallel releases support
|
||||||
- Blazingly fast execution
|
- Blazingly fast execution
|
||||||
- Supports any git project
|
- Supports any git project
|
||||||
- Highly flexible configuration
|
- Highly flexible configuration
|
||||||
- Lightweight
|
- Lightweight
|
||||||
- Supports any branch
|
- Supports any branch
|
||||||
|
- Rich build log output
|
||||||
|
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -69,13 +70,14 @@ ${{steps.build_changelog.outputs.changelog}}
|
|||||||
|
|
||||||
A full set list of possible output values for this action.
|
A full set list of possible output values for this action.
|
||||||
|
|
||||||
| **Output** | **Description** |
|
| **Output** | **Description** |
|
||||||
|---------------------|-------------------------------------------------------------------------------------|
|
|---------------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `outputs.changelog` | The built release changelog built from the merged pull requests |
|
| `outputs.changelog` | The built release changelog built from the merged pull requests |
|
||||||
| `outputs.owner` | Specifies the owner of the repository processed |
|
| `outputs.owner` | Specifies the owner of the repository processed |
|
||||||
| `outputs.repo` | Describes the repository name, which was processed |
|
| `outputs.repo` | Describes the repository name, which was processed |
|
||||||
| `outputs.fromTag` | Defines the `fromTag` which describes the lower bound to process pull requests for |
|
| `outputs.fromTag` | Defines the `fromTag` which describes the lower bound to process pull requests for |
|
||||||
| `outputs.toTag` | Defines the `toTag` which describes the upper bound to process pull request for |
|
| `outputs.toTag` | Defines the `toTag` which describes the upper bound to process pull request for |
|
||||||
|
| `outputs.failed` | Defines if there was an issue with the action run, and the changelog may not have been generated correctly. [true, false] |
|
||||||
|
|
||||||
## Customization 🖍️
|
## Customization 🖍️
|
||||||
|
|
||||||
@@ -152,7 +154,21 @@ For advanced usecases additional settings can be provided to the action
|
|||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
```
|
```
|
||||||
|
|
||||||
💡 `ignorePreReleases` will be ignored, if `fromTag` is specified. `${{ secrets.GITHUB_TOKEN }}` only grants rights to the current repository, for other repos please use a PAT (Personal Access Token).
|
💡 All input values are optional. It is only required to privde the `token` either via the input, or as `env` variable.
|
||||||
|
|
||||||
|
| **Input** | **Description** |
|
||||||
|
|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| configuration | Relative path, to the `configuration.json` file, providing additional configurations |
|
||||||
|
| owner | The owner of the repository to generate the changelog for |
|
||||||
|
| repo | Name of the repository we want to process |
|
||||||
|
| fromTag | Defines the 'start' from where the changelog will consider merged pull requests |
|
||||||
|
| toTag | Defines until which tag the changelog will consider merged pull requests |
|
||||||
|
| path | Allows to specify an alternative sub directory, to use as base |
|
||||||
|
| token | Alternative config to specify token. You should prefer `env.GITHUB_TOKEN` instead though |
|
||||||
|
| ignorePreReleases | Allows to ignore pre-releases for changelog generation (E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick). Only used if `fromTag` was not specified. Default: false |
|
||||||
|
| failOnError | Defines if the action will result in a build failure, if problems occurred. Default: false |
|
||||||
|
|
||||||
|
💡 `${{ secrets.GITHUB_TOKEN }}` only grants rights to the current repository, for other repos please use a PAT (Personal Access Token).
|
||||||
|
|
||||||
### PR Template placeholders
|
### PR Template placeholders
|
||||||
|
|
||||||
|
|||||||
+14
-8
@@ -1,5 +1,5 @@
|
|||||||
import {ReleaseNotes} from '../src/releaseNotes'
|
import {ReleaseNotes} from '../src/releaseNotes'
|
||||||
import {readConfiguration} from '../src/utils'
|
import { resolveConfiguration } from '../src/utils';
|
||||||
|
|
||||||
// shows how the runner will run a javascript action with env / stdout protocol
|
// shows how the runner will run a javascript action with env / stdout protocol
|
||||||
/*
|
/*
|
||||||
@@ -17,31 +17,33 @@ test('test runs', () => {
|
|||||||
it('Should have empty changelog (tags)', async () => {
|
it('Should have empty changelog (tags)', async () => {
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
const configuration = readConfiguration('configs/configuration.json')!!
|
const configuration = resolveConfiguration('', 'configs/configuration.json')
|
||||||
const releaseNotes = new ReleaseNotes({
|
const releaseNotes = new ReleaseNotes({
|
||||||
owner: 'mikepenz',
|
owner: 'mikepenz',
|
||||||
repo: 'release-changelog-builder-action',
|
repo: 'release-changelog-builder-action',
|
||||||
fromTag: 'v0.0.1',
|
fromTag: 'v0.0.1',
|
||||||
toTag: 'v0.0.2',
|
toTag: 'v0.0.2',
|
||||||
ignorePreReleases: false,
|
ignorePreReleases: false,
|
||||||
|
failOnError: false,
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
const changeLog = await releaseNotes.pull()
|
const changeLog = await releaseNotes.pull()
|
||||||
console.log(changeLog)
|
console.log(changeLog)
|
||||||
expect(changeLog).toStrictEqual(`- no changes`)
|
expect(changeLog).toStrictEqual(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should match generated changelog (tags)', async () => {
|
it('Should match generated changelog (tags)', async () => {
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
const configuration = readConfiguration('configs/configuration.json')!!
|
const configuration = resolveConfiguration('', 'configs/configuration.json')
|
||||||
const releaseNotes = new ReleaseNotes({
|
const releaseNotes = new ReleaseNotes({
|
||||||
owner: 'mikepenz',
|
owner: 'mikepenz',
|
||||||
repo: 'release-changelog-builder-action',
|
repo: 'release-changelog-builder-action',
|
||||||
fromTag: 'v0.0.1',
|
fromTag: 'v0.0.1',
|
||||||
toTag: 'v0.0.3',
|
toTag: 'v0.0.3',
|
||||||
ignorePreReleases: false,
|
ignorePreReleases: false,
|
||||||
|
failOnError: false,
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -58,13 +60,14 @@ it('Should match generated changelog (tags)', async () => {
|
|||||||
it('Should match generated changelog (unspecified fromTag)', async () => {
|
it('Should match generated changelog (unspecified fromTag)', async () => {
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
const configuration = readConfiguration('configs/configuration.json')!!
|
const configuration = resolveConfiguration('', 'configs/configuration.json')
|
||||||
const releaseNotes = new ReleaseNotes({
|
const releaseNotes = new ReleaseNotes({
|
||||||
owner: 'mikepenz',
|
owner: 'mikepenz',
|
||||||
repo: 'release-changelog-builder-action',
|
repo: 'release-changelog-builder-action',
|
||||||
fromTag: null,
|
fromTag: null,
|
||||||
toTag: 'v0.0.3',
|
toTag: 'v0.0.3',
|
||||||
ignorePreReleases: false,
|
ignorePreReleases: false,
|
||||||
|
failOnError: false,
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -81,13 +84,14 @@ it('Should match generated changelog (unspecified fromTag)', async () => {
|
|||||||
it('Should match generated changelog (refs)', async () => {
|
it('Should match generated changelog (refs)', async () => {
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
const configuration = readConfiguration('configs_test/configuration_all_placeholders.json')!!
|
const configuration = resolveConfiguration('', 'configs_test/configuration_all_placeholders.json')
|
||||||
const releaseNotes = new ReleaseNotes({
|
const releaseNotes = new ReleaseNotes({
|
||||||
owner: 'mikepenz',
|
owner: 'mikepenz',
|
||||||
repo: 'release-changelog-builder-action',
|
repo: 'release-changelog-builder-action',
|
||||||
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
|
fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3',
|
||||||
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
|
toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa',
|
||||||
ignorePreReleases: false,
|
ignorePreReleases: false,
|
||||||
|
failOnError: false,
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -112,13 +116,14 @@ nhoelzl
|
|||||||
it('Should match ordered ASC', async () => {
|
it('Should match ordered ASC', async () => {
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
const configuration = readConfiguration('configs_test/configuration_asc.json')!!
|
const configuration = resolveConfiguration('', 'configs_test/configuration_asc.json')
|
||||||
const releaseNotes = new ReleaseNotes({
|
const releaseNotes = new ReleaseNotes({
|
||||||
owner: 'mikepenz',
|
owner: 'mikepenz',
|
||||||
repo: 'release-changelog-builder-action',
|
repo: 'release-changelog-builder-action',
|
||||||
fromTag: 'v0.3.0',
|
fromTag: 'v0.3.0',
|
||||||
toTag: 'v0.5.0',
|
toTag: 'v0.5.0',
|
||||||
ignorePreReleases: false,
|
ignorePreReleases: false,
|
||||||
|
failOnError: false,
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -130,13 +135,14 @@ it('Should match ordered ASC', async () => {
|
|||||||
it('Should match ordered DESC', async () => {
|
it('Should match ordered DESC', async () => {
|
||||||
jest.setTimeout(180000)
|
jest.setTimeout(180000)
|
||||||
|
|
||||||
const configuration = readConfiguration('configs_test/configuration_desc.json')!!
|
const configuration = resolveConfiguration('', 'configs_test/configuration_desc.json')
|
||||||
const releaseNotes = new ReleaseNotes({
|
const releaseNotes = new ReleaseNotes({
|
||||||
owner: 'mikepenz',
|
owner: 'mikepenz',
|
||||||
repo: 'release-changelog-builder-action',
|
repo: 'release-changelog-builder-action',
|
||||||
fromTag: 'v0.3.0',
|
fromTag: 'v0.3.0',
|
||||||
toTag: 'v0.5.0',
|
toTag: 'v0.5.0',
|
||||||
ignorePreReleases: false,
|
ignorePreReleases: false,
|
||||||
|
failOnError: false,
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ inputs:
|
|||||||
ignorePreReleases:
|
ignorePreReleases:
|
||||||
description: 'Defines if the action will only use full releases to compare against (Only used if fromTag is not defined). E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick'
|
description: 'Defines if the action will only use full releases to compare against (Only used if fromTag is not defined). E.g. for 1.0.1... 1.0.0-rc02 <- ignore, 1.0.0 <- pick'
|
||||||
default: "false"
|
default: "false"
|
||||||
|
failOnError:
|
||||||
|
description: 'Defines if the action should result in a build failure, if an error was discovered'
|
||||||
|
default: "false"
|
||||||
token:
|
token:
|
||||||
description: 'Defines the token to use to execute the git API requests with, uses `env.GITHUB_TOKEN` by default'
|
description: 'Defines the token to use to execute the git API requests with, uses `env.GITHUB_TOKEN` by default'
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
+34
-58
@@ -1,52 +1,42 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {readConfiguration} from './utils'
|
import {
|
||||||
|
failOrError,
|
||||||
|
retrieveRepositoryPath,
|
||||||
|
resolveConfiguration
|
||||||
|
} from './utils'
|
||||||
import {ReleaseNotes} from './releaseNotes'
|
import {ReleaseNotes} from './releaseNotes'
|
||||||
import {createCommandManager} from './gitHelper'
|
import {createCommandManager} from './gitHelper'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as path from 'path'
|
|
||||||
import {DefaultConfiguration} from './configuration'
|
import {DefaultConfiguration} from './configuration'
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
|
core.setOutput('failed', false) // mark the action not failed by default
|
||||||
|
|
||||||
core.startGroup(`📘 Reading input values`)
|
core.startGroup(`📘 Reading input values`)
|
||||||
try {
|
try {
|
||||||
let githubWorkspacePath = process.env['GITHUB_WORKSPACE']
|
// read in path specification, resolve github workspace, and repo path
|
||||||
if (!githubWorkspacePath) {
|
const inputPath = core.getInput('path')
|
||||||
throw new Error('GITHUB_WORKSPACE not defined')
|
const repositoryPath = retrieveRepositoryPath(inputPath)
|
||||||
}
|
|
||||||
githubWorkspacePath = path.resolve(githubWorkspacePath)
|
|
||||||
core.debug(`GITHUB_WORKSPACE = '${githubWorkspacePath}'`)
|
|
||||||
|
|
||||||
let repositoryPath = core.getInput('path') || '.'
|
|
||||||
repositoryPath = path.resolve(githubWorkspacePath, repositoryPath)
|
|
||||||
core.debug(`repositoryPath = '${repositoryPath}'`)
|
|
||||||
|
|
||||||
|
// read in configuration file if possible
|
||||||
const configurationFile: string = core.getInput('configuration')
|
const configurationFile: string = core.getInput('configuration')
|
||||||
let configuration = DefaultConfiguration
|
const configuration = resolveConfiguration(
|
||||||
if (configurationFile) {
|
repositoryPath,
|
||||||
const configurationPath = path.resolve(
|
configurationFile
|
||||||
githubWorkspacePath,
|
)
|
||||||
configurationFile
|
|
||||||
)
|
|
||||||
core.debug(`configurationPath = '${configurationPath}'`)
|
|
||||||
const providedConfiguration = readConfiguration(configurationPath)
|
|
||||||
if (!providedConfiguration) {
|
|
||||||
core.info(
|
|
||||||
`⚠️ Configuration provided, but it couldn't be found, or failed to parse. Fallback to Defaults`
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
configuration = providedConfiguration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// read in repository inputs
|
||||||
const token = core.getInput('token')
|
const token = core.getInput('token')
|
||||||
let owner = core.getInput('owner')
|
const owner = core.getInput('owner') ?? github.context.repo.owner
|
||||||
let repo = core.getInput('repo')
|
const repo = core.getInput('repo') ?? github.context.repo.repo
|
||||||
|
// read in from, to tag inputs
|
||||||
const fromTag = core.getInput('fromTag')
|
const fromTag = core.getInput('fromTag')
|
||||||
let toTag = core.getInput('toTag')
|
let toTag = core.getInput('toTag')
|
||||||
|
// read in flags
|
||||||
|
const ignorePreReleases = core.getInput('ignorePreReleases') === 'true'
|
||||||
|
const failOnError = core.getInput('failOnError') === 'true'
|
||||||
|
|
||||||
const ignorePreReleases = core.getInput('ignorePreReleases')
|
// ensure to resolve the toTag if it was not provided
|
||||||
|
|
||||||
if (!toTag) {
|
if (!toTag) {
|
||||||
// if not specified try to retrieve tag from github.context.ref
|
// if not specified try to retrieve tag from github.context.ref
|
||||||
if (github.context.ref.startsWith('refs/tags/')) {
|
if (github.context.ref.startsWith('refs/tags/')) {
|
||||||
@@ -65,28 +55,8 @@ async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!owner || !repo) {
|
|
||||||
// Qualified repository
|
|
||||||
const qualifiedRepository =
|
|
||||||
core.getInput('repository') ||
|
|
||||||
`${github.context.repo.owner}/${github.context.repo.repo}`
|
|
||||||
core.debug(`qualified repository = '${qualifiedRepository}'`)
|
|
||||||
const splitRepository = qualifiedRepository.split('/')
|
|
||||||
if (
|
|
||||||
splitRepository.length !== 2 ||
|
|
||||||
!splitRepository[0] ||
|
|
||||||
!splitRepository[1]
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid repository '${qualifiedRepository}'. Expected format {owner}/{repo}.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
owner = splitRepository[0]
|
|
||||||
repo = splitRepository[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!owner) {
|
if (!owner) {
|
||||||
core.error(`💥 Missing or couldn't resolve 'owner'`)
|
failOrError(`💥 Missing or couldn't resolve 'owner'`, failOnError)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
core.setOutput('owner', owner)
|
core.setOutput('owner', owner)
|
||||||
@@ -94,7 +64,7 @@ async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
core.error(`💥 Missing or couldn't resolve 'owner'`)
|
failOrError(`💥 Missing or couldn't resolve 'owner'`, failOnError)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
core.setOutput('repo', repo)
|
core.setOutput('repo', repo)
|
||||||
@@ -102,7 +72,7 @@ async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!toTag) {
|
if (!toTag) {
|
||||||
core.error(`💥 Missing or couldn't resolve 'toTag'`)
|
failOrError(`💥 Missing or couldn't resolve 'toTag'`, failOnError)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
core.setOutput('toTag', toTag)
|
core.setOutput('toTag', toTag)
|
||||||
@@ -115,11 +85,17 @@ async function run(): Promise<void> {
|
|||||||
repo,
|
repo,
|
||||||
fromTag,
|
fromTag,
|
||||||
toTag,
|
toTag,
|
||||||
ignorePreReleases: ignorePreReleases === 'true',
|
ignorePreReleases,
|
||||||
|
failOnError,
|
||||||
configuration
|
configuration
|
||||||
})
|
})
|
||||||
|
|
||||||
core.setOutput('changelog', await releaseNotes.pull(token))
|
core.setOutput(
|
||||||
|
'changelog',
|
||||||
|
(await releaseNotes.pull(token)) ??
|
||||||
|
configuration.empty_template ??
|
||||||
|
DefaultConfiguration.empty_template
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(error.message)
|
core.setFailed(error.message)
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-11
@@ -5,6 +5,7 @@ import {buildChangelog} from './transform'
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Tags} from './tags'
|
import {Tags} from './tags'
|
||||||
import {Configuration, DefaultConfiguration} from './configuration'
|
import {Configuration, DefaultConfiguration} from './configuration'
|
||||||
|
import {failOrError} from './utils'
|
||||||
|
|
||||||
export interface ReleaseNotesOptions {
|
export interface ReleaseNotesOptions {
|
||||||
owner: string // the owner of the repository
|
owner: string // the owner of the repository
|
||||||
@@ -12,18 +13,26 @@ export interface ReleaseNotesOptions {
|
|||||||
fromTag: string | null // the tag/ref to start from
|
fromTag: string | null // the tag/ref to start from
|
||||||
toTag: string // the tag/ref up to
|
toTag: string // the tag/ref up to
|
||||||
ignorePreReleases: boolean // defines if we should ignore any pre-releases for matching, only relevant if fromTag is null
|
ignorePreReleases: boolean // defines if we should ignore any pre-releases for matching, only relevant if fromTag is null
|
||||||
|
failOnError: boolean // defines if we should fail the action in case of an error
|
||||||
configuration: Configuration // the configuration as defined in `configuration.ts`
|
configuration: Configuration // the configuration as defined in `configuration.ts`
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReleaseNotes {
|
export class ReleaseNotes {
|
||||||
constructor(private options: ReleaseNotesOptions) {}
|
constructor(private options: ReleaseNotesOptions) {}
|
||||||
|
|
||||||
async pull(token?: string): Promise<string> {
|
async pull(token?: string): Promise<string | null> {
|
||||||
const octokit = new Octokit({
|
const octokit = new Octokit({
|
||||||
auth: `token ${token || process.env.GITHUB_TOKEN}`
|
auth: `token ${token || process.env.GITHUB_TOKEN}`
|
||||||
})
|
})
|
||||||
|
|
||||||
const {owner, repo, toTag, ignorePreReleases, configuration} = this.options
|
const {
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
toTag,
|
||||||
|
ignorePreReleases,
|
||||||
|
failOnError,
|
||||||
|
configuration
|
||||||
|
} = this.options
|
||||||
|
|
||||||
if (!this.options.fromTag) {
|
if (!this.options.fromTag) {
|
||||||
core.startGroup(`🔖 Resolve previous tag`)
|
core.startGroup(`🔖 Resolve previous tag`)
|
||||||
@@ -39,10 +48,11 @@ export class ReleaseNotes {
|
|||||||
DefaultConfiguration.max_tags_to_fetch
|
DefaultConfiguration.max_tags_to_fetch
|
||||||
)
|
)
|
||||||
if (previousTag == null) {
|
if (previousTag == null) {
|
||||||
core.error(`💥 Unable to retrieve previous tag given ${toTag}`)
|
failOrError(
|
||||||
return (
|
`💥 Unable to retrieve previous tag given ${toTag}`,
|
||||||
configuration.empty_template ?? DefaultConfiguration.empty_template
|
failOnError
|
||||||
)
|
)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
this.options.fromTag = previousTag.name
|
this.options.fromTag = previousTag.name
|
||||||
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`)
|
core.debug(`fromTag resolved via previousTag as: ${previousTag.name}`)
|
||||||
@@ -50,8 +60,8 @@ export class ReleaseNotes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.options.fromTag) {
|
if (!this.options.fromTag) {
|
||||||
core.error(`💥 Missing or couldn't resolve 'fromTag'`)
|
failOrError(`💥 Missing or couldn't resolve 'fromTag'`, failOnError)
|
||||||
return configuration.empty_template ?? DefaultConfiguration.empty_template
|
return null
|
||||||
} else {
|
} else {
|
||||||
core.setOutput('fromTag', this.options.fromTag)
|
core.setOutput('fromTag', this.options.fromTag)
|
||||||
}
|
}
|
||||||
@@ -62,7 +72,7 @@ export class ReleaseNotes {
|
|||||||
|
|
||||||
if (mergedPullRequests.length === 0) {
|
if (mergedPullRequests.length === 0) {
|
||||||
core.warning(`⚠️ No pull requests found`)
|
core.warning(`⚠️ No pull requests found`)
|
||||||
return configuration.empty_template ?? DefaultConfiguration.empty_template
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
core.startGroup('📦 Build changelog')
|
core.startGroup('📦 Build changelog')
|
||||||
@@ -74,7 +84,14 @@ export class ReleaseNotes {
|
|||||||
private async getMergedPullRequests(
|
private async getMergedPullRequests(
|
||||||
octokit: Octokit
|
octokit: Octokit
|
||||||
): Promise<PullRequestInfo[]> {
|
): Promise<PullRequestInfo[]> {
|
||||||
const {owner, repo, fromTag, toTag, configuration} = this.options
|
const {
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
fromTag,
|
||||||
|
toTag,
|
||||||
|
failOnError,
|
||||||
|
configuration
|
||||||
|
} = this.options
|
||||||
core.info(`ℹ️ Comparing ${owner}/${repo} - '${fromTag}...${toTag}'`)
|
core.info(`ℹ️ Comparing ${owner}/${repo} - '${fromTag}...${toTag}'`)
|
||||||
|
|
||||||
const commitsApi = new Commits(octokit)
|
const commitsApi = new Commits(octokit)
|
||||||
@@ -82,11 +99,14 @@ export class ReleaseNotes {
|
|||||||
try {
|
try {
|
||||||
commits = await commitsApi.getDiff(owner, repo, fromTag!!, toTag)
|
commits = await commitsApi.getDiff(owner, repo, fromTag!!, toTag)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error(`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`)
|
failOrError(
|
||||||
|
`💥 Failed to retrieve - Invalid tag? - Because of: ${error}`,
|
||||||
|
failOnError
|
||||||
|
)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
if (commits.length === 0) {
|
if (commits.length === 0) {
|
||||||
core.warning(`💥 No commits found between - ${fromTag}...${toTag}`)
|
core.warning(`⚠️ No commits found between - ${fromTag}...${toTag}`)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+78
-8
@@ -1,7 +1,71 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import {Configuration} from './configuration'
|
import {Configuration, DefaultConfiguration} from './configuration'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
export function readConfiguration(filename: string): Configuration | null {
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will automatically either report the message to the log, or mark the action as failed. Additionally defining the output failed, allowing it to be read in by other actions
|
||||||
|
*/
|
||||||
|
export function failOrError(
|
||||||
|
message: string | Error,
|
||||||
|
failOnError: boolean
|
||||||
|
): void {
|
||||||
|
// if we report any failure, consider the action to have failed, may not make the build fail
|
||||||
|
core.setOutput('failed', true)
|
||||||
|
if (failOnError) {
|
||||||
|
core.setFailed(message)
|
||||||
|
} else {
|
||||||
|
core.error(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the configuration given the file path, if not found it will fallback to the `DefaultConfiguration`
|
||||||
|
*/
|
||||||
|
export function resolveConfiguration(
|
||||||
|
githubWorkspacePath: string,
|
||||||
|
configurationFile: string
|
||||||
|
): Configuration {
|
||||||
|
let configuration = DefaultConfiguration
|
||||||
|
if (configurationFile) {
|
||||||
|
const configurationPath = path.resolve(
|
||||||
|
githubWorkspacePath,
|
||||||
|
configurationFile
|
||||||
|
)
|
||||||
|
core.debug(`configurationPath = '${configurationPath}'`)
|
||||||
|
const providedConfiguration = readConfiguration(configurationPath)
|
||||||
|
if (!providedConfiguration) {
|
||||||
|
core.info(
|
||||||
|
`⚠️ Configuration provided, but it couldn't be found, or failed to parse. Fallback to Defaults`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
configuration = providedConfiguration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads in the configuration from the JSON file
|
||||||
|
*/
|
||||||
|
function readConfiguration(filename: string): Configuration | null {
|
||||||
try {
|
try {
|
||||||
const rawdata = fs.readFileSync(filename, 'utf8')
|
const rawdata = fs.readFileSync(filename, 'utf8')
|
||||||
const configurationJSON: Configuration = JSON.parse(rawdata)
|
const configurationJSON: Configuration = JSON.parse(rawdata)
|
||||||
@@ -11,25 +75,31 @@ export function readConfiguration(filename: string): Configuration | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function directoryExistsSync(path: string, required?: boolean): boolean {
|
/**
|
||||||
if (!path) {
|
* Checks if a given directory exists
|
||||||
|
*/
|
||||||
|
export function directoryExistsSync(
|
||||||
|
inputPath: string,
|
||||||
|
required?: boolean
|
||||||
|
): boolean {
|
||||||
|
if (!inputPath) {
|
||||||
throw new Error("Arg 'path' must not be empty")
|
throw new Error("Arg 'path' must not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
let stats: fs.Stats
|
let stats: fs.Stats
|
||||||
try {
|
try {
|
||||||
stats = fs.statSync(path)
|
stats = fs.statSync(inputPath)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
if (!required) {
|
if (!required) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Directory '${path}' does not exist`)
|
throw new Error(`Directory '${inputPath}' does not exist`)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Encountered an error when checking whether path '${path}' exists: ${error.message}`
|
`Encountered an error when checking whether path '${inputPath}' exists: ${error.message}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,5 +109,5 @@ export function directoryExistsSync(path: string, required?: boolean): boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Directory '${path}' does not exist`)
|
throw new Error(`Directory '${inputPath}' does not exist`)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user