diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7e3e555..16a52d4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,13 @@ jobs: if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - run: | + - name: Checkout + uses: actions/checkout@v2 + - name: Install NPM + run: | npm install - - run: | + - name: Run NPM + run: | npm run all env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -22,6 +25,8 @@ jobs: if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: + # the below actions use the local state of the action. please replace `./` with `mikepenz/release-changelog-builder-action@{latest-release}` + # Showcases how to use the action without a prior checkout # Won't support providing a configuration file, as no checkout was done - name: "Configuration without Checkout" id: without_checkout @@ -37,8 +42,9 @@ jobs: - name: Checkout uses: actions/checkout@v2 with: - fetch-depth: 0 # Checkout full depth so tags can be discovered automatically if not specified + fetch-depth: 0 + # Showcase the most minimal configuration possible - name: "Minimal Configuration" id: minimal_release uses: ./ @@ -48,6 +54,7 @@ jobs: - name: Echo Minimal Configuration Changelog run: echo "${{steps.minimal_release.outputs.changelog}}" + # Showcases a more complex configuration, providing a configuration, and specifically referencing owner, repo, from and to tag - name: "Complex Configuration" id: complex_release uses: ./ @@ -62,6 +69,21 @@ jobs: - name: Echo Complex Configuration Changelog run: echo "${{steps.complex_release.outputs.changelog}}" + # Showcases the capability to generate the changelog for an external repository provided + - name: "External Repo Configuration" + id: external_changelog + uses: ./ + with: + configuration: "configs/configuration_complex.json" + owner: "mikepenz" + repo: "MaterialDrawer" + fromTag: "v8.1.0" + toTag: "v8.1.6" + token: ${{ secrets.PERSONAL_TOKEN }} + + - name: Echo External Repo Configuration Changelog + run: echo "${{steps.external_changelog.outputs.changelog}}" + release: if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest @@ -69,17 +91,11 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Prepare Tag - if: startsWith(github.ref, 'refs/tags/') - id: tag_version - run: echo ::set-output name=VERSION::$(echo ${GITHUB_REF:10}) - - name: "Build Changelog" id: github_release uses: mikepenz/release-changelog-builder-action@main with: configuration: "configs/configuration_repo.json" - toTag: ${{ steps.tag_version.outputs.VERSION }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 6e822b9e..d9a7ec62 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
- ... a github action that builds your release notes fast, easy and exactly the way you want. + ... a GitHub action that builds your release notes fast, easy and exactly the way you want.
What's included đ • Setup đ ī¸ • + Full Sample đĨī¸ • Customization đī¸ • Contribute đ§Ŧ • - Complete Sample đĨī¸ • License đ
@@ -32,13 +32,14 @@ ### What's included đ - Super simple integration - - even on huge repositories with hundreds of tags + - ...even on huge repositories with hundreds of tags - Parallel releases support - Blazingly fast execution - Supports any git project - Highly flexible configuration - Lightweight - Supports any branch +- Rich build log output ------- @@ -51,26 +52,72 @@ Specify the action as part of your GitHub actions workflow: ```yml - name: "Build Changelog" id: build_changelog - if: startsWith(github.ref, 'refs/tags/') uses: mikepenz/release-changelog-builder-action@{latest-release} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` -By default the action will try to automatically retrieve the `tag` from the current commit and automtacally resolve the `tag` before. Read more about this here. +By default the action will try to automatically retrieve the `tag` from the current commit and automatically resolve the `tag` before. Read more about this here. ### Action outputs -The action will succeed and return the `changelog` as a step output. Use it in any follow up step by referencing via its id. For example `build_changelog`. +After action execution it will return the `changelog` and additional information as step output. You can use it in any follow-up step by referencing the output by referencing it via the id of the step. For example `build_changelog`. ```yml # ${{steps.{CHANGELOG_STEP_ID}.outputs.changelog}} ${{steps.build_changelog.outputs.changelog}} ``` +A full set list of possible output values for this action. + +| **Output** | **Description** | +|---------------------|---------------------------------------------------------------------------------------------------------------------------| +| `outputs.changelog` | The built release changelog built from the merged pull requests | +| `outputs.owner` | Specifies the owner of the repository 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.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] | + + +## Full Sample đĨī¸ + +Below is a complete example showcasing how to define a build, which is executed when tagging the project. It consists of: +- Prepare tag, via the GITHUB_REF environment variable +- Build changelog, given the tag +- Create release on GitHub - specifying body with constructed changelog + +```yml +name: 'CI' +on: + push: + tags: + - '*' + + release: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Build Changelog + id: github_release + uses: mikepenz/release-changelog-builder-action@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Release + uses: actions/create-release@v1 + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + body: ${{steps.github_release.outputs.changelog}} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + + ## Customization đī¸ -### Changelog Configuration +### Configuration The action supports flexible configuration options to modify vast areas of its behavior. To do so, provide the configuration file to the workflow using the `configuration` setting. @@ -120,13 +167,15 @@ This configuration is a `.json` file in the following format. } ``` -Any section of the configruation can be ommited to have defaults apply. -Defaults for the configuraiton can be found in the [configuration.ts](https://github.com/mikepenz/release-changelog-builder-action/blob/develop/src/configuration.ts) +Any section of the configuration can be omitted to have defaults apply. +Defaults for the configuration can be found in the [configuration.ts](https://github.com/mikepenz/release-changelog-builder-action/blob/develop/src/configuration.ts) + +Please see the [Configuration Specification](#configuration-specification) for detailed descriptions on the offered configuration options. ### Advanced workflow specification -For advanced usecases additional settings can be provided to the action +For advanced use cases additional settings can be provided to the action ```yml - name: "Complex Configuration" @@ -138,12 +187,26 @@ For advanced usecases additional settings can be provided to the action owner: "mikepenz" repo: "release-changelog-builder-action" ignorePreReleases: "false" - fromTag: "0.0.2" - toTag: "0.0.3" + fromTag: "0.3.0" + toTag: "0.5.0" 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 provide 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 repositories please use a PAT (Personal Access Token). ### PR Template placeholders @@ -171,47 +234,26 @@ Table of supported placeholders allowed to be used in the `pr_template` configur | `${{CHANGELOG}}` | The contents of the changelog, matching the labels as specified in the categories configuration | | `${{UNCATEGORIZED}}` | All pull requests not matching a specified label in categories | - -## Complete Sample đĨī¸ - -Below is a complete example showcasing how to define a build, which is executed when tagging the project. It consists of: -- Prepare tag, via the GITHUB_REF environment variable -- Build changelog, given the tag -- Create release on GitHub - specifying body with constructed changelog - -```yml -name: 'CI' -on: - push: - tags: - - '*' - - release: - if: startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - name: Retrieve tag - if: startsWith(github.ref, 'refs/tags/') - id: tag_version - run: echo ::set-output name=VERSION::$(echo ${GITHUB_REF:10}) - - - name: Build Changelog - id: github_release - uses: mikepenz/release-changelog-builder-action@main - with: - toTag: ${{ steps.tag_version.outputs.VERSION }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - uses: actions/create-release@v1 - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - body: ${{steps.github_release.outputs.changelog}} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -``` +### Configuration Specification + +Table of descriptions for the `configuration.json` options. + +| **Input** | **Description** | +|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| categories | An array of `category` specifications, offering a flexible way to group changes into categories | +| category.title | The display name of a category in the changelog | +| category.labels | An array of labels, to match pull request labels against. If any PR label, matches any category label, the pull request will show up under this category | +| sort | The sort order of pull requests. [ASC, DESC] | +| template | Specifies the global template to pick for creating the changelog. See [Template placeholders](#template-placeholders) for possible values | +| pr_template | Defines the per pull request template. See [PR Template placeholders](#pr-template-placeholders) for possible values | +| empty_template | Template to pick if no changes are detected. Does not support placeholders | +| transformers | An array of `transform` specifications, offering a flexible API to modify the text per pull request. This is applied on the change text created with `pr_template`. `transformers` are executed per change, in the order specified | +| transformer.pattern | A `regex` pattern, extracting values of the change message. | +| transformer.target | The result pattern, the regex groups will be filled into. Allows for full transformation of a pull request message. Including potentially specified texts | +| max_tags_to_fetch | The maximum amount of tags to load from the API to find the previous tag. Loaded paginated with 100 per page | +| max_pull_requests | The maximum amount of pull requests to load from the API. Loaded paginated with 30 per page | +| max_back_track_time_days | Defines the max amount of days to go back in time per changelog | +| exclude_merge_branches | An array of branches to be ignored from processing as merge commits | ## Contribute đ§Ŧ @@ -229,7 +271,7 @@ $ npm test $ npm run lint -- --fix ``` -It's suggested to export the token to your path before running the tests, so that API calls can be done to github. +It's suggested to export the token to your path before running the tests so that API calls can be done to GitHub. ```bash export GITHUB_TOKEN=your_personal_github_pat @@ -248,7 +290,8 @@ Core parts of the PR fetching logic are based on [pull-release-notes](https://gi ## License - Copyright for portions of pr-release-notes are held by Nikolay Blagoev, 2019-2020 as part of project pull-release-notes. All other copyright for project pr-release-notes are held by Mike Penz, 2020. + Copyright for portions of pr-release-notes are held by Nikolay Blagoev, 2019-2020 as part of project pull-release-notes. + All other copyright for project pr-release-notes are held by Mike Penz, 2020. ## Fork License diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index d82074f0..cfa86bdf 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -1,5 +1,5 @@ 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 /* @@ -17,31 +17,33 @@ test('test runs', () => { it('Should have empty changelog (tags)', async () => { jest.setTimeout(180000) - const configuration = readConfiguration('configs/configuration.json')!! + const configuration = resolveConfiguration('', 'configs/configuration.json') const releaseNotes = new ReleaseNotes({ owner: 'mikepenz', repo: 'release-changelog-builder-action', fromTag: 'v0.0.1', toTag: 'v0.0.2', ignorePreReleases: false, + failOnError: false, configuration: configuration }) const changeLog = await releaseNotes.pull() console.log(changeLog) - expect(changeLog).toStrictEqual(`- no changes`) + expect(changeLog).toStrictEqual(null) }) it('Should match generated changelog (tags)', async () => { jest.setTimeout(180000) - const configuration = readConfiguration('configs/configuration.json')!! + const configuration = resolveConfiguration('', 'configs/configuration.json') const releaseNotes = new ReleaseNotes({ owner: 'mikepenz', repo: 'release-changelog-builder-action', fromTag: 'v0.0.1', toTag: 'v0.0.3', ignorePreReleases: false, + failOnError: false, configuration: configuration }) @@ -58,13 +60,14 @@ it('Should match generated changelog (tags)', async () => { it('Should match generated changelog (unspecified fromTag)', async () => { jest.setTimeout(180000) - const configuration = readConfiguration('configs/configuration.json')!! + const configuration = resolveConfiguration('', 'configs/configuration.json') const releaseNotes = new ReleaseNotes({ owner: 'mikepenz', repo: 'release-changelog-builder-action', fromTag: null, toTag: 'v0.0.3', ignorePreReleases: false, + failOnError: false, configuration: configuration }) @@ -81,13 +84,14 @@ it('Should match generated changelog (unspecified fromTag)', async () => { it('Should match generated changelog (refs)', async () => { jest.setTimeout(180000) - const configuration = readConfiguration('configs/configuration_all_placeholders.json')!! + const configuration = resolveConfiguration('', 'configs_test/configuration_all_placeholders.json') const releaseNotes = new ReleaseNotes({ owner: 'mikepenz', repo: 'release-changelog-builder-action', fromTag: '5ec7a2d86fe9f43fdd38d5e254a1117c8a51b4c3', toTag: 'fa3788c8c4b3373ef8424ce3eb008a5cd07cc5aa', ignorePreReleases: false, + failOnError: false, configuration: configuration }) @@ -108,3 +112,41 @@ nhoelzl `) }) + +it('Should match ordered ASC', async () => { + jest.setTimeout(180000) + + const configuration = resolveConfiguration('', 'configs_test/configuration_asc.json') + const releaseNotes = new ReleaseNotes({ + owner: 'mikepenz', + repo: 'release-changelog-builder-action', + fromTag: 'v0.3.0', + toTag: 'v0.5.0', + ignorePreReleases: false, + failOnError: false, + configuration: configuration + }) + + const changeLog = await releaseNotes.pull() + console.log(changeLog) + expect(changeLog).toStrictEqual(`## đ Features\n\n22\n24\n25\n26\n28\n\n## đ Fixes\n\n23\n\n`) +}) + +it('Should match ordered DESC', async () => { + jest.setTimeout(180000) + + const configuration = resolveConfiguration('', 'configs_test/configuration_desc.json') + const releaseNotes = new ReleaseNotes({ + owner: 'mikepenz', + repo: 'release-changelog-builder-action', + fromTag: 'v0.3.0', + toTag: 'v0.5.0', + ignorePreReleases: false, + failOnError: false, + configuration: configuration + }) + + const changeLog = await releaseNotes.pull() + console.log(changeLog) + expect(changeLog).toStrictEqual(`## đ Features\n\n28\n26\n25\n24\n22\n\n## đ Fixes\n\n23\n\n`) +}) \ No newline at end of file diff --git a/action.yml b/action.yml index d875347f..51c324e2 100644 --- a/action.yml +++ b/action.yml @@ -20,6 +20,9 @@ inputs: 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' default: "false" + failOnError: + description: 'Defines if the action should result in a build failure, if an error was discovered' + default: "false" token: description: 'Defines the token to use to execute the git API requests with, uses `env.GITHUB_TOKEN` by default' outputs: diff --git a/configs/configuration_all_placeholders.json b/configs_test/configuration_all_placeholders.json similarity index 100% rename from configs/configuration_all_placeholders.json rename to configs_test/configuration_all_placeholders.json diff --git a/configs_test/configuration_asc.json b/configs_test/configuration_asc.json new file mode 100644 index 00000000..6e1fa39f --- /dev/null +++ b/configs_test/configuration_asc.json @@ -0,0 +1,4 @@ +{ + "sort": "ASC", + "pr_template": "${{NUMBER}}" +} \ No newline at end of file diff --git a/configs_test/configuration_desc.json b/configs_test/configuration_desc.json new file mode 100644 index 00000000..8d81997f --- /dev/null +++ b/configs_test/configuration_desc.json @@ -0,0 +1,4 @@ +{ + "sort": "DESC", + "pr_template": "${{NUMBER}}" +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 6968453a..7c3f514d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,98 +1,81 @@ import * as core from '@actions/core' -import {readConfiguration} from './utils' +import { + failOrError, + retrieveRepositoryPath, + resolveConfiguration +} from './utils' import {ReleaseNotes} from './releaseNotes' import {createCommandManager} from './gitHelper' import * as github from '@actions/github' -import * as path from 'path' import {DefaultConfiguration} from './configuration' async function run(): Promise