├── .all-contributorsrc ├── .editorconfig ├── .eslintrc.json ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── .gitignore ├── .prettierrc.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── SPONSORS.md ├── action.yml ├── dist └── index.js ├── docs └── how-to.md ├── index.js ├── package-lock.json ├── package.json └── src ├── add-links.js ├── add-links.test.js ├── get-entries.js ├── get-entries.test.js ├── get-entry-by-version-id.js ├── get-entry-by-version-id.test.js ├── get-links.js ├── get-links.test.js ├── main.js ├── parse-entry-content.js ├── parse-entry.js ├── parse-entry.test.js ├── rules ├── has-chronological-order.js ├── has-chronological-order.test.js ├── has-correct-sections.js ├── has-correct-sections.test.js ├── has-sections.js ├── has-sections.test.js ├── is-semver.js └── is-semver.test.js ├── validate-entry.js └── validate-entry.test.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "CONTRIBUTORS.md" 4 | ], 5 | "skipCi": true, 6 | "contributors": [ 7 | { 8 | "login": "mindsers", 9 | "name": "Nathanael CHERRIER", 10 | "avatar_url": "https://avatars0.githubusercontent.com/u/3090112?v=4", 11 | "profile": "https://nathanaelcherrier.com", 12 | "contributions": [ 13 | "code", 14 | "doc", 15 | "review", 16 | "question", 17 | "test", 18 | "maintenance" 19 | ] 20 | }, 21 | { 22 | "login": "aroemen", 23 | "name": "Alan Roemen", 24 | "avatar_url": "https://avatars1.githubusercontent.com/u/796505?v=4", 25 | "profile": "https://roemen.company", 26 | "contributions": [ 27 | "code", 28 | "test", 29 | "ideas" 30 | ] 31 | }, 32 | { 33 | "login": "endormi", 34 | "name": "Erno Salo", 35 | "avatar_url": "https://avatars3.githubusercontent.com/u/39559256?v=4", 36 | "profile": "http://endormi.io", 37 | "contributions": [ 38 | "doc" 39 | ] 40 | }, 41 | { 42 | "login": "andrekosak", 43 | "name": "Andre Kosak", 44 | "avatar_url": "https://avatars1.githubusercontent.com/u/6382243?v=4", 45 | "profile": "https://github.com/andrekosak", 46 | "contributions": [ 47 | "example" 48 | ] 49 | }, 50 | { 51 | "login": "svenstaro", 52 | "name": "Sven-Hendrik Haase", 53 | "avatar_url": "https://avatars0.githubusercontent.com/u/1664?v=4", 54 | "profile": "https://svenstaro.org", 55 | "contributions": [ 56 | "example" 57 | ] 58 | }, 59 | { 60 | "login": "alexesprit", 61 | "name": "Alexey", 62 | "avatar_url": "https://avatars1.githubusercontent.com/u/1119267?v=4", 63 | "profile": "https://alexesprit.com", 64 | "contributions": [ 65 | "doc" 66 | ] 67 | }, 68 | { 69 | "login": "farfromrefug", 70 | "name": "farfromrefuge", 71 | "avatar_url": "https://avatars.githubusercontent.com/u/655344?v=4", 72 | "profile": "https://github.com/farfromrefug", 73 | "contributions": [ 74 | "code" 75 | ] 76 | }, 77 | { 78 | "login": "xt0rted", 79 | "name": "Brian Surowiec", 80 | "avatar_url": "https://avatars.githubusercontent.com/u/831974?v=4", 81 | "profile": "http://onlypans.pizza", 82 | "contributions": [ 83 | "bug" 84 | ] 85 | }, 86 | { 87 | "login": "danilogco", 88 | "name": "Danilo Carolino", 89 | "avatar_url": "https://avatars.githubusercontent.com/u/3433530?v=4", 90 | "profile": "https://github.com/danilogco", 91 | "contributions": [ 92 | "code", 93 | "bug" 94 | ] 95 | }, 96 | { 97 | "login": "SwabianCoder", 98 | "name": "SwabianCoder", 99 | "avatar_url": "https://avatars.githubusercontent.com/u/43047586?v=4", 100 | "profile": "https://github.com/SwabianCoder", 101 | "contributions": [ 102 | "bug" 103 | ] 104 | }, 105 | { 106 | "login": "jon-whit", 107 | "name": "Jonathan Whitaker", 108 | "avatar_url": "https://avatars.githubusercontent.com/u/2899204?v=4", 109 | "profile": "http://jon-whit.me", 110 | "contributions": [ 111 | "ideas" 112 | ] 113 | }, 114 | { 115 | "login": "mrks115", 116 | "name": "Markus Brüx", 117 | "avatar_url": "https://avatars.githubusercontent.com/u/3636209?v=4", 118 | "profile": "https://github.com/mrks115", 119 | "contributions": [ 120 | "code" 121 | ] 122 | }, 123 | { 124 | "login": "guihkx", 125 | "name": "Guilherme Silva", 126 | "avatar_url": "https://avatars.githubusercontent.com/u/626206?v=4", 127 | "profile": "https://github.com/guihkx", 128 | "contributions": [ 129 | "bug" 130 | ] 131 | }, 132 | { 133 | "login": "stronk7", 134 | "name": "Eloy Lafuente", 135 | "avatar_url": "https://avatars.githubusercontent.com/u/167147?v=4", 136 | "profile": "https://github.com/stronk7", 137 | "contributions": [ 138 | "doc" 139 | ] 140 | } 141 | ], 142 | "projectName": "changelog-reader-action", 143 | "projectOwner": "mindsers", 144 | "repoType": "github", 145 | "repoHost": "https://github.com", 146 | "contributorsPerLine": 7, 147 | "commitType": "docs", 148 | "commitConvention": "angular" 149 | } 150 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | 16 | [Dockerfile] 17 | indent_size = 4 18 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true, 6 | "jest": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 2018 15 | }, 16 | "rules": { 17 | "arrow-body-style": ["error", "as-needed"], 18 | "arrow-parens": ["error", "as-needed"], 19 | "arrow-spacing": "error", 20 | "default-case": "error", 21 | "guard-for-in": "error", 22 | "generator-star-spacing": ["error", { "before": true, "after": false }], 23 | "getter-return": ["error", { "allowImplicit": true }], 24 | "no-compare-neg-zero": "error", 25 | "no-confusing-arrow": "error", 26 | "no-else-return": "error", 27 | "no-empty": "error", 28 | "no-implicit-coercion": "error", 29 | "no-useless-return": "error", 30 | "no-redeclare": "error", 31 | "no-var": "error", 32 | "prefer-arrow-callback": "error", 33 | "prefer-const": "error", 34 | "prefer-rest-params": "error", 35 | "prefer-spread": "error", 36 | "prefer-template": "error", 37 | "space-before-function-paren": ["error", { 38 | "anonymous": "always", 39 | "named": "never", 40 | "asyncArrow": "always" 41 | }] 42 | } 43 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [mindsers] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # mindsers # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: mindsers # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: mindsers # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # nathanaelcherrier.dev/en/support # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: 'units-test' 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - 'releases/*' 8 | 9 | jobs: 10 | # unit tests 11 | unit: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - run: npm ci 16 | - run: npm test 17 | 18 | # test action works running from the graph 19 | e2e: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v2 24 | - name: Run the changelog reader 25 | id: changelog_reader 26 | uses: ./ 27 | - name: Display output 28 | run: | 29 | echo "Version: ${{ steps.changelog_reader.outputs.version }}" 30 | echo "Date: ${{ steps.changelog_reader.outputs.date }}" 31 | echo "Status: ${{ steps.changelog_reader.outputs.status }}" 32 | echo "Changes: ${{ steps.changelog_reader.outputs.changes }}" 33 | - name: Check output 34 | run: | 35 | test "Changes: ${{ steps.changelog_reader.outputs.changes }}" != "" 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Editors 4 | .vscode 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Other Dependency directories 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # next.js build output 65 | .next 66 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | trailingComma: "es5" 2 | tabWidth: 2 3 | semi: false 4 | singleQuote: true 5 | printWidth: 100 6 | jsxSingleQuote: false 7 | arrowParens: "avoid" 8 | endOfLine: "lf" 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [2.2.3] - 2024-03-10 11 | 12 | ### Fixed 13 | 14 | - Upgrade dependencies to solve deprecation issues. 15 | - Use node v20 16 | - Remove useless empty line between links in the body of a version 17 | 18 | ## [2.2.2] - 2022-11-23 19 | 20 | ### Fixed 21 | 22 | - Upgrade dependencies to solve deprecation issues. 23 | 24 | ## [2.2.1] - 2022-11-10 25 | 26 | ### Fixed 27 | 28 | - Change node engine for a non-deprecated version. 29 | 30 | ## [2.2.0] - 2022-09-01 31 | 32 | ### Changed 33 | 34 | - Allow more section types into prerelease versions. #67 35 | 36 | ### Fixed 37 | 38 | - Change the links' syntax to make them correctly recognized by GitHub. 39 | 40 | ## [2.1.1] - 2022-07-03 41 | 42 | ### Fixed 43 | 44 | - The action was returning empty data since the last version. Now correctly returns selected entries data. 45 | 46 | ## [2.1.0] - 2022-06-14 47 | 48 | ### Added 49 | 50 | - Introduced changelog validation to help keep the release version in line with [Semantic Versioning](https://semver.org/) 51 | - New input param of `validation_level` and `validation_depth` to allow for configuration of changelog validation. 52 | - Support Angular CHANGELOG format. (Doesn't force title emphasis) 53 | 54 | ### Changed 55 | 56 | - The project now implement the [All Contributors](https://allcontributors.org). 57 | _This is not a change in the code but a change in how the projet recognize the 58 | external contributions._ 59 | 60 | ### Fixed 61 | 62 | - Retrieve links (external to the entry) and add them back in the related entry. 63 | 64 | ## [2.0.0] - 2020-08-30 65 | 66 | ### Added 67 | 68 | - New output properties: 69 | - The `version` number of the returned entry 70 | - The released `date` of the returned entry 71 | - The `status` of the release based on the version number and the title line of the entry. 72 | Could be equal to `unreleased`, `prereleased`, `released` or `yanked`. 73 | Please refer to https://semver.org/#semantic-versioning-specification-semver for more informations about this. 74 | 75 | ### Changed 76 | 77 | - **[BREAKING CHANGE]** If given a specific target version, action will now generate an error response if that version is not found in the changelog. 78 | - **[BREAKING CHANGE]** `log_entry` output property is renamed to `changes`. 79 | 80 | ## [1.3.1] - 2020-07-08 81 | 82 | ### Fixed 83 | 84 | - Allow developers to NOT use a date for each version entries. 85 | 86 | ## [1.3.0] - 2020-07-06 87 | 88 | ### Changed 89 | 90 | - Dates should follow the format used in the ["Keep a Changelog"](https://keepachangelog.com/en/1.0.0/) specification 91 | which is `YYYY-MM-DD`. Another format that may work is `YYYY-DD-MM`. 92 | Given the current disclaimer in the `README.md`, this change is **not** a _breaking_ change. 93 | 94 | ### Fixed 95 | 96 | - Improve SEMVER support. Now recognize complex version number based on https://semver.org. 97 | 98 | ## [1.2.0] - 2020-06-24 99 | 100 | ### Added 101 | 102 | - New support for "Unreleased" section. It is possible to ask for the "Unreleased" section 103 | by setting `version: "Unreleased"` in the `workflow.yml` file. 104 | 105 | ### Fixed 106 | 107 | - Improve the way the project is linted 108 | 109 | ## [1.1.0] - 2020-03-13 110 | 111 | ### Added 112 | 113 | - The project has now a CHANGELOG 114 | - Add logging message to make the debugging session easier 115 | 116 | ### Changed 117 | 118 | - When no log entry is found, the error state of the action doesn't break the workflow anymore 119 | 120 | ### Fixed 121 | 122 | - README now uses the correct version number in the examples 123 | - Support X.X.X version pattern 124 | 125 | ## [1.0.1] - 2020-02-12 126 | 127 | ### Fixed 128 | 129 | - Remove template's old behavior 130 | 131 | ## [1.0.0] - 2020-02-12 132 | 133 | ### Added 134 | 135 | - CHANGELOG can be parsed by the github action 136 | 137 | [unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v2.2.3...HEAD 138 | [2.2.3]: https://github.com/mindsers/changelog-reader-action/compare/v2.2.2...v2.2.3 139 | [2.2.2]: https://github.com/mindsers/changelog-reader-action/compare/v2.2.1...v2.2.2 140 | [2.2.1]: https://github.com/mindsers/changelog-reader-action/compare/v2.2.0...v2.2.1 141 | [2.2.0]: https://github.com/mindsers/changelog-reader-action/compare/v2.1.1...v2.2.0 142 | [2.1.1]: https://github.com/mindsers/changelog-reader-action/compare/v2.1.0...v2.1.1 143 | [2.1.0]: https://github.com/mindsers/changelog-reader-action/compare/v2.0.0...v2.1.0 144 | [2.0.0]: https://github.com/mindsers/changelog-reader-action/compare/v1.3.1...v2.0.0 145 | [1.3.1]: https://github.com/mindsers/changelog-reader-action/compare/v1.3.0...v1.3.1 146 | [1.3.0]: https://github.com/mindsers/changelog-reader-action/compare/v1.2.0...v1.3.0 147 | [1.2.0]: https://github.com/mindsers/changelog-reader-action/compare/v1.1.0...v1.2.0 148 | [1.1.0]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.1...v1.1.0 149 | [1.0.1]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0...v1.0.1 150 | [1.0.0]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0 151 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to Changelog Reader Action 2 | 3 | *First off, thanks for taking the time to contribute!* 4 | 5 | This file is a set of guilines for contributing to *Changelog Reader Action* project. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 6 | 7 | **All the contributors are/will be added to our [Contributors Wall](CONTRIBUTORS.md).** If we forgot someone, please let us know. We'll be glad to add them to the list. 8 | 9 | #### Table of contents 10 | 11 | [How can I contribute?](#how-can-i-contribute) 12 | 13 | * [Did you find a bug?](#did-you-find-a-bug) 14 | * [Did you write a patch that fixes a bug?](#did-you-write-a-patch-that-fixes-a-bug) 15 | * [Did you fix whitespace, format code, or make a purely cosmetic patch?](#did-you-fix-whitespace-format-code-or-make-a-purely-cosmetic-patch) 16 | * [Do you intend to add a new feature or change an existing one?](#do-you-intend-to-add-a-new-feature-or-change-an-existing-one) 17 | * [Do you have questions about the source code?](#do-you-have-questions-about-the-source-code) 18 | * [Do you want to contribute to the Changelog Reader Action documentation?](#do-you-want-to-contribute-to-the-Configfile-documentation) 19 | 20 | [Styleguides](#styleguides) 21 | 22 | * [JavaScript styleguide](#javaScript-styleguide) 23 | * [Git commit messages](#git-commit-messages) 24 | 25 | ## How can I contribute? 26 | 27 | ### Did you find a bug? 28 | 29 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues][Issues]. 30 | 31 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/mindsers/changelog-reader-action/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. 32 | 33 | * If possible, use the relevant bug report templates to create the issue. 34 | 35 | ### Did you write a patch that fixes a bug? 36 | 37 | * Write new unit test(s) that match the bug case to limit future regression. 38 | 39 | * Open a new GitHub pull request with the patch. 40 | 41 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 42 | 43 | * Before submitting, please ensure your code follow the code convention. 44 | 45 | ### Did you fix whitespace, format code, or make a purely cosmetic patch? 46 | 47 | * Your changes must follow the coding convention. 48 | 49 | * Please ensure that your changes does not include regression. 50 | 51 | ### Do you intend to add a new feature or change an existing one? 52 | 53 | * Please ask first ([open an issue][Issues] or [talk about it on the community forum][Forum]) before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project. 54 | 55 | * Please adhere to the coding conventions used in this project (indentation, accurate comments, etc.) and any other requirements (such as test coverage, documentation). 56 | 57 | ### Do you have questions about the source code? 58 | 59 | * Ask any question about how to use Configfile in the [forum category][Forum] or on [Stack Overflow](https://stackoverflow.com). 60 | 61 | ### Do you want to contribute to the Configfile documentation? 62 | 63 | Documentation file are stored in the project source code. 64 | 65 | * Please refer to "Do you intend to add a new feature or change an existing one?" section. 66 | 67 | ## Styleguides 68 | 69 | ### JavaScript styleguide 70 | 71 | All JavaScript must adhere to [JavaScript Standard Style](https://standardjs.com). 72 | 73 | Exepctions to Standard style: 74 | 75 | * We do not want space after function name `function name(arg) { ... }` 76 | 77 | Additional rules: 78 | 79 | * Prefer spread operator (`prefer-spread`) 80 | * No useless brackets for arrow functions (`arrow-body-style`) 81 | * No useless parens for arrow functions (`arrow-parens`) 82 | * Require space before/after arrow function’s arrow (`arrow-spacing`) 83 | * `switch` must have a default case (`default-case`) 84 | * `for...in` loop must be guard by an `if` (`guard-for-in`) 85 | * Require space before the star of generator function (`generator-star-spacing`) 86 | * Getter properties must return a value (`getter-return`) 87 | * Compare to `-0` is an error (`no-compare-neg-zero`) 88 | * Use brackets if arrow function body could be confused with comparisons (`no-confusing-arrow`) 89 | * No `else` when return is used (`no-else-return`) 90 | * No empty block statements (`no-empty`) 91 | * No type conversion with shorter notations (`no-implicit-coercion`) 92 | * No useless `return` statement (`no-useless-return`) 93 | * No redeclare variables (`no-redeclare`) 94 | * Using `var` statement is an error (`no-var`) 95 | * Prefer arrow function for callback (`prefer-arrow-callback`) 96 | * Prefer using constant (`prefer-const`) 97 | * Prefer rest parameter (`prefer-rest-params`) 98 | * Prefer using template literals (`prefer-template`) 99 | 100 | All the rules are listed in `.eslintrc.json` on the root directory. 101 | 102 | ### Git commit messages 103 | 104 | * Use the present tense ("Add feature" not "Added feature"). 105 | 106 | * Use the imperative mood ("Move cursor to..." not "Moves cursor to..."). 107 | 108 | * Limit the first line to 72 characters or less. 109 | 110 | * Reference issues and pull requests liberally after the first line. 111 | 112 | 113 | [Issues]: https://github.com/mindsers/changelog-reader-action/issues 114 | [Forum]: https://community.nathanaelcherrier.com/c/open-source/changelog-reader-action/11 115 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributors ✨ 3 | 4 | Thanks goes to these wonderful people 5 | ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Nathanael CHERRIER
Nathanael CHERRIER

💻 📖 👀 💬 ⚠️ 🚧
Alan Roemen
Alan Roemen

💻 ⚠️ 🤔
Erno Salo
Erno Salo

📖
Andre Kosak
Andre Kosak

💡
Sven-Hendrik Haase
Sven-Hendrik Haase

💡
Alexey
Alexey

📖
farfromrefuge
farfromrefuge

💻
Brian Surowiec
Brian Surowiec

🐛
Danilo Carolino
Danilo Carolino

💻 🐛
SwabianCoder
SwabianCoder

🐛
Jonathan Whitaker
Jonathan Whitaker

🤔
Markus Brüx
Markus Brüx

💻
Guilherme Silva
Guilherme Silva

🐛
Eloy Lafuente
Eloy Lafuente

📖
32 | 33 | 34 | 35 | 36 | 37 | 38 | This project follows the 39 | [all-contributors](https://github.com/all-contributors/all-contributors) 40 | specification. Contributions of any kind are welcome! 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nathanaël Cherrier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Changelog Reader 2 | 3 | changelog-reader-action status 4 | 5 | A GitHub action to read and get data from the `CHANGELOG.md` file :rocket: 6 | 7 | **This action only works if your `CHANGELOG.md` file follows the [_Keep a Changelog_](https://github.com/olivierlacan/keep-a-changelog) standard for now.** 8 | 9 | ## Usage 10 | 11 | ### Pre-requisites 12 | 13 | Create a workflow `.yml` file in your repositories `.github/workflows` directory. An [example workflow](#example-workflow---upload-a-release-asset) is available below. For more information, reference the GitHub Help Documentation for [Creating a workflow file](https://help.github.com/en/articles/configuring-a-workflow#creating-a-workflow-file). 14 | 15 | ### Inputs 16 | 17 | - `path`: The path the action can find the CHANGELOG. Optional. Defaults to `./CHANGELOG.md`. 18 | - `version`: The [exact version](https://semver.org) of the log entry you want to retreive or "Unreleased" for the unreleased entry. Optional. Defaults to the last version number. 19 | - `validation_level`: Specifies at which level the validation system is set. Can be 'none', 'warn', 'error'. Optional. Defaults to `none`. 20 | - `validation_depth`: Specifies how many entries to validate in the CHANGELOG.md file. Optional. Defaults to `10`. 21 | 22 | ### Outputs 23 | 24 | - `version`: Version of the log entry found. Ex: `2.0.0`. 25 | - `date`: Release date of the log entry found. Ex: `2020-08-22`. 26 | - `status`: Status of the log entry found (`prereleased`, `released`, `unreleased`, or `yanked`). 27 | - `changes`: Description text of the log entry found. 28 | 29 | ### Validation / Linting 30 | 31 | A validation engine is available in _Changelog Reader_. It is by default disabled but can be enabled by setting `validation_level` to 'warn' or 'error'. 32 | 33 | The validation engine will enforce [Semantic Versioning 2.0.0](https://semver.org/) standards as well as [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) standards and formatting. **If your project doesn't follow Semantic Versioning 2.0.0 or Keep a Changelog, do not enable the validation engine.** It might break your build unnecessarily. 34 | 35 | You can utilize the `validation_depth` input param to specify how many entries to validate. Changelog Reader will by default validates only the last 10 changelog entries. 36 | 37 | When `validation_level` is set at 'warn', Changelog Reader will print check results as warnings in your logs without breaking your build. 38 | When `validation_level` is set at 'error', Changelog Reader will print check results as errors in your logs and will throw an error to prevent the build to go further. 39 | 40 | ### Example workflow - create a release from changelog 41 | 42 | On every `push` to a tag matching the pattern `v*`, [create a release](https://developer.github.com/v3/repos/releases/#create-a-release) using the CHANGELOG.md content. 43 | This Workflow example assumes you'll use the [`@actions/create-release`](https://www.github.com/actions/create-release) Action to create the release step: 44 | 45 | ```yaml 46 | on: 47 | push: 48 | # Sequence of patterns matched against refs/tags 49 | tags: 50 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 51 | 52 | name: Create Release 53 | 54 | jobs: 55 | build: 56 | name: Create Release 57 | runs-on: ubuntu-latest 58 | steps: 59 | - name: Get version from tag 60 | id: tag_name 61 | run: | 62 | echo "current_version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT 63 | shell: bash 64 | - name: Checkout code 65 | uses: actions/checkout@v2 66 | - name: Get Changelog Entry 67 | id: changelog_reader 68 | uses: mindsers/changelog-reader-action@v2 69 | with: 70 | validation_level: warn 71 | version: ${{ steps.tag_name.outputs.current_version }} 72 | path: ./CHANGELOG.md 73 | - name: Create/update release 74 | uses: ncipollo/release-action@v1 75 | with: 76 | # This pulls from the "Get Changelog Entry" step above, referencing it's ID to get its outputs object. 77 | # See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 78 | tag: ${{ steps.changelog_reader.outputs.version }} 79 | name: Release ${{ steps.changelog_reader.outputs.version }} 80 | body: ${{ steps.changelog_reader.outputs.changes }} 81 | prerelease: ${{ steps.changelog_reader.outputs.status == 'prereleased' }} 82 | draft: ${{ steps.changelog_reader.outputs.status == 'unreleased' }} 83 | allowUpdates: true 84 | token: ${{ secrets.GITHUB_TOKEN }} 85 | ``` 86 | 87 | ## Contribution 88 | 89 | Contributions to the source code of _Changelog Reader Action_ are welcomed and greatly appreciated. 90 | For help on how to contribute in this project, please refer to [How to contribute to Changelog Reader Action](CONTRIBUTING.md). 91 | 92 | To see the project's list of **awesome contributors**, please refer to our [Contributors Wall](CONTRIBUTORS.md). 93 | 94 | ## Support 95 | 96 | _Changelog Reader Action_ is licensed under an MIT license, which means that it's a completely free open source software. Unfortunately, _Changelog Reader Action_ doesn't make itself. Version 2.0.0 is the next step, which will result in many late, beer-filled nights of development. 97 | 98 | If you're using _Changelog Reader Action_ and want to support the development, you now have the chance! Go on my [GitHub Sponsor page](https://github.com/sponsors/mindsers) and become my joyful sponsor!! 99 | 100 | For more help on how to support Changelog Reader Action, please refer to [The awesome people who support Changelog Reader Action](SPONSORS.md). 101 | 102 | 103 | 104 | ## License 105 | 106 | The scripts and documentation in this project are released under the [MIT License](LICENSE) 107 | -------------------------------------------------------------------------------- /SPONSORS.md: -------------------------------------------------------------------------------- 1 | # The awesome people who support Changelog Reader Action 2 | 3 | *First off, thanks for being a part of this project!* 4 | 5 | This file is an extension of the **Support** section in `README.md`. You can find here all the people who support *Changelog Reader Action* organized by their contribution. 6 | 7 | #### Table of content 8 | 9 | [How can I support this project?](#how-can-i-support-this-project) 10 | 11 | [Who are the people behind this project?](#who-are-the-people-behind-this-project) 12 | 13 | * [Sponsors](#sponsors) 14 | * [Supporters](#supporters) 15 | 16 | ## How can I support this project? 17 | 18 | *Changelog Reader Action* is licensed under an Apache-2.0 license, which means that it's a completely free open source software. Unfortunately, *Changelog Reader Action* doesn't make itself and I'm happy to build it on my free time. 19 | 20 | If you're using *Changelog Reader Action* and want to support the development, you can do it via the GitHub Sponsor service. On this platform you can choose exactly what you want to give from $5 to whatever you want... You can click on the "Become a sponsor" link bellow to see Mindsers' GitHub Sponsor page. 21 | 22 | Be sure your effort to support this project will be greatly appreciated. 23 | 24 | [**_Become a Sponsor!_**](https://github.com/sponsors/mindsers) 25 | 26 | ## Who are the people behind this project? 27 | 28 | ### Sponsors 29 | 30 | No one is in the **Sponsors** section for now. Be the first! Read the **How can I support this project** section above. 31 | 32 | ### Supporters 33 | 34 | No one is in the **Supporters** section for now. Be the first! Read the **How can I support this project** section above. 35 | 36 | 39 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Changelog Reader' 2 | description: 'Read and parse the CHANGELOG file of the project' 3 | branding: 4 | icon: 'align-right' 5 | color: 'gray-dark' 6 | inputs: 7 | path: 8 | description: 'Path to the CHANGELOG file containing the log entries' 9 | required: false 10 | default: './CHANGELOG.md' 11 | version: 12 | description: 'Version of the log entry wanted' 13 | required: false 14 | validation_level: 15 | description: 'Specifies if the CHANGELOG.md file should be validated and the behavior of the action' 16 | required: false 17 | default: 'none' 18 | validation_depth: 19 | description: 'Specifies how many entries to validate in the CHANGELOG.md file' 20 | required: false 21 | default: '10' 22 | outputs: 23 | version: 24 | description: 'Version of the log entry found' 25 | date: 26 | description: 'Release date of the log entry found' 27 | status: 28 | description: 'Status of the log entry found. Possibly one of the following: prereleased, released, unreleased, or yanked' 29 | changes: 30 | description: 'Description text of the log entry found' 31 | runs: 32 | using: 'node20' 33 | main: 'dist/index.js' 34 | -------------------------------------------------------------------------------- /docs/how-to.md: -------------------------------------------------------------------------------- 1 |

2 | javscript-action status 3 |

4 | 5 | # Create a JavaScript Action 6 | 7 | ## Code in Develop 8 | 9 | Install the dependencies: 10 | 11 | ```bash 12 | $ npm install 13 | ``` 14 | 15 | Run the tests: 16 | 17 | ```bash 18 | $ npm test 19 | 20 | PASS ./index.test.js 21 | ✓ throws invalid number (3ms) 22 | ✓ wait 500 ms (504ms) 23 | ✓ test runs (95ms) 24 | 25 | ... 26 | ``` 27 | 28 | ## Change the Code 29 | 30 | Most toolkit and CI/CD operations involve async operations so the action is run in an async function. 31 | 32 | ```javascript 33 | const core = require('@actions/core'); 34 | ... 35 | 36 | async function run() { 37 | try { 38 | ... 39 | } 40 | catch (error) { 41 | core.setFailed(error.message); 42 | } 43 | } 44 | 45 | run() 46 | ``` 47 | 48 | See the [toolkit documentation](https://github.com/actions/toolkit/blob/master/README.md#packages) for the various packages. 49 | 50 | ## Package for distribution 51 | 52 | GitHub Actions will run the entry point from the action.yml. Packaging assembles the code into one file that can be checked in to Git, enabling fast and reliable execution and preventing the need to check in node_modules. 53 | 54 | Actions are run from GitHub repos. Packaging the action will create a packaged action in the `dist` folder. 55 | 56 | Run package: 57 | 58 | ```bash 59 | npm run package 60 | ``` 61 | 62 | Since the packaged `index.js` is ran from the `dist` folder. 63 | 64 | ```bash 65 | git add dist 66 | ``` 67 | 68 | ## Create a release tags 69 | 70 | Changelog Reader follow semantic versionning. So you have to create your tag. 71 | 72 | ```bash 73 | $ git tag -s v1.0.3 74 | ``` 75 | 76 | To ease the work of maintainer we also create (or update) shortcut tags: 77 | 78 | ```bash 79 | $ git tag -d v1 80 | $ git push --delete origin v1 81 | $ git tag -a v1 82 | ``` 83 | 84 | ```bash 85 | $ git push --tags 86 | ``` 87 | 88 | Now you have to create the GitHub Release for this new version (v1.0.3) and check "marketplace". This action is now published! :rocket: 89 | 90 | See the [versioning documentation](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md) 91 | 92 | ## Usage 93 | 94 | You can now consume the action by referencing the v1 branch: 95 | 96 | ```yaml 97 | uses: actions/javascript-action@v1 98 | with: 99 | milliseconds: 1000 100 | ``` 101 | 102 | See the [actions tab](https://github.com/actions/javascript-action/actions) for runs of this action! :rocket: 103 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { main } = require('./src/main') 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "changelog-reader-action", 3 | "version": "2.2.3", 4 | "description": "A GitHub Action to read your CHANGELOG", 5 | "main": "index.js", 6 | "homepage": "https://github.com/mindsers/changelog-reader-action#readme", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/mindsers/changelog-reader-action" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/mindsers/changelog-reader-action/issues" 14 | }, 15 | "author": { 16 | "name": "Nathanaël Cherrier", 17 | "email": "dev@nathanaelcherrier.com", 18 | "url": "https://nathanaelcherrier.com" 19 | }, 20 | "scripts": { 21 | "lint": "eslint src/**/*.js", 22 | "package": "NODE_OPTIONS=--openssl-legacy-provider ncc build index.js -o dist", 23 | "test": "npm run lint && jest" 24 | }, 25 | "dependencies": { 26 | "@actions/core": "^1.10.0" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "^29.2.2", 30 | "@types/semver": "^7.3.13", 31 | "@zeit/ncc": "^0.22.3", 32 | "eslint": "^8.27.0", 33 | "jest": "^29.3.1", 34 | "semver": "^7.3.8" 35 | }, 36 | "keywords": [ 37 | "GitHub", 38 | "Actions", 39 | "Changelog" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /src/add-links.js: -------------------------------------------------------------------------------- 1 | exports.addLinks = links => entry => { 2 | const { text } = entry 3 | const linkRegex = /(\[.+\])\[\]/gi 4 | 5 | let tempText = `${text}` 6 | let results = null 7 | while ((results = linkRegex.exec(text)) != null) { 8 | const link = links.find(element => element.includes(results[1])) 9 | tempText = `${tempText}\n${link}` 10 | } 11 | 12 | return { ...entry, text: tempText.trim() } 13 | } 14 | -------------------------------------------------------------------------------- /src/add-links.test.js: -------------------------------------------------------------------------------- 1 | const { addLinks } = require('./add-links') 2 | 3 | test('retreive add correct links to entry', () => { 4 | const output = addLinks([ 5 | `[1.1.2+meta]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.1-rc.1+build.123...1.1.2+meta`, 6 | `[1.1.1-rc.1+build.123]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-rc.1+build.123`, 7 | `[1.1.1-DEV-SNAPSHOT]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-DEV-SNAPSHOT`, 8 | `[1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay`, 9 | `[github]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay`, 10 | ])({ 11 | text: `### Added 12 | - CHANGELOG can be parsed by the [github][] action 13 | - [1.1.1-DEV-SNAPSHOT][] can be parsed by the github action`, 14 | }) 15 | 16 | expect(output.text).toEqual( 17 | `### Added 18 | - CHANGELOG can be parsed by the [github][] action 19 | - [1.1.1-DEV-SNAPSHOT][] can be parsed by the github action 20 | [github]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay 21 | [1.1.1-DEV-SNAPSHOT]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-DEV-SNAPSHOT` 22 | ) 23 | }) 24 | 25 | test('add nothing when there is no references', () => { 26 | const output = addLinks([ 27 | `[1.1.2+meta]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.1-rc.1+build.123...1.1.2+meta`, 28 | `[1.1.1-rc.1+build.123]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-rc.1+build.123`, 29 | `[1.1.1-DEV-SNAPSHOT]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-DEV-SNAPSHOT`, 30 | `[1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay`, 31 | ])({ 32 | text: `### Added 33 | - CHANGELOG can be parsed by the github action`, 34 | }) 35 | 36 | expect(output.text).toEqual(`### Added 37 | - CHANGELOG can be parsed by the github action`) 38 | }) 39 | -------------------------------------------------------------------------------- /src/get-entries.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core') 2 | 3 | const versionSeparator = '\n## ' 4 | const semverLinkRegex = 5 | /^\[v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?\]/ 6 | const unreleasedLinkRegex = /^\[unreleased\]/i 7 | const avoidNonVersionData = version => 8 | semverLinkRegex.test(version) || unreleasedLinkRegex.test(version) 9 | 10 | exports.getEntries = rawData => { 11 | const content = String(rawData) 12 | 13 | core.debug(`CHANGELOG content: ${content}`) 14 | 15 | return content.split(versionSeparator).filter(avoidNonVersionData) 16 | } 17 | -------------------------------------------------------------------------------- /src/get-entries.test.js: -------------------------------------------------------------------------------- 1 | const { getEntries } = require('./get-entries') 2 | 3 | const DATA_v = ` 4 | # Changelog 5 | All notable changes to this project will be documented in this file. 6 | 7 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 8 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 9 | 10 | ## [Unreleased] 11 | ### Added 12 | - The project has now a CHANGELOG 13 | 14 | ### Fixed 15 | - README now uses the correct version number in the examples 16 | 17 | ## [v1.0.1] - 2020-02-12 18 | ### Fixed 19 | - Remove template's old behavior 20 | 21 | ## [v1.0.0] - 2020-02-12 22 | ### Added 23 | - CHANGELOG can be parsed by the github action 24 | 25 | [Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.1...HEAD 26 | [1.0.1]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0...v1.0.1 27 | [1.0.0]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0 28 | ` 29 | 30 | const DATA_complex = ` 31 | # Changelog 32 | All notable changes to this project will be documented in this file. 33 | 34 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 35 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 36 | 37 | ## [1.1.2+meta] 38 | ### Added 39 | - The project has now a CHANGELOG 40 | 41 | ### Fixed 42 | - README now uses the correct version number in the examples 43 | 44 | ## [1.1.1-rc.1+build.123] - 2020-02-12 45 | ### Fixed 46 | - Remove template's old behavior 47 | 48 | ## [1.1.1-DEV-SNAPSHOT] - 2020-02-12 49 | ### Added 50 | - CHANGELOG can be parsed by the github action 51 | 52 | ## [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay] - 2019-12-09 53 | ### Added 54 | - CHANGELOG can be parsed by the github action 55 | 56 | [1.1.2+meta]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.1-rc.1+build.123...1.1.2+meta 57 | [1.1.1-rc.1+build.123]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-rc.1+build.123 58 | [1.1.1-DEV-SNAPSHOT]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-DEV-SNAPSHOT 59 | [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay 60 | ` 61 | 62 | const DATA = ` 63 | # Changelog 64 | All notable changes to this project will be documented in this file. 65 | 66 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 67 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 68 | 69 | ## [Unreleased] 70 | ### Added 71 | - The project has now a CHANGELOG 72 | 73 | ### Fixed 74 | - README now uses the correct version number in the examples 75 | 76 | ## [1.0.1] - 2020-02-12 77 | ### Fixed 78 | - Remove template's old behavior 79 | 80 | ## [1.0.0] - 2020-02-12 81 | ### Added 82 | - CHANGELOG can be parsed by the github action 83 | 84 | [Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.1...HEAD 85 | [1.0.1]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0...v1.0.1 86 | [1.0.0]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0 87 | ` 88 | 89 | test('retreive entries from test (tag patern: vX.X.X)', () => { 90 | const output = getEntries(DATA_v) 91 | const versionRegex = /^\[(v[0-1]+|unreleased)/i 92 | 93 | expect(output.length).toEqual(3) 94 | expect(output[0]).toMatch(versionRegex) 95 | expect(output[1]).toMatch(versionRegex) 96 | expect(output[2]).toMatch(versionRegex) 97 | }) 98 | 99 | test('retreive entries from test (tag patern: X.X.X)', () => { 100 | const output = getEntries(DATA) 101 | const versionRegex = /^\[([0-1]+|unreleased)/i 102 | 103 | expect(output.length).toEqual(3) 104 | expect(output[0]).toMatch(versionRegex) 105 | expect(output[1]).toMatch(versionRegex) 106 | expect(output[2]).toMatch(versionRegex) 107 | }) 108 | 109 | // https://github.com/mindsers/changelog-reader-action/issues/8 110 | test('retreive entries from test (complex SEMVER)', () => { 111 | const output = getEntries(DATA_complex) 112 | const versionRegex = 113 | /^\[(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/ 114 | 115 | expect(output.length).toEqual(4) 116 | expect(output[0]).toMatch(versionRegex) 117 | expect(output[1]).toMatch(versionRegex) 118 | expect(output[2]).toMatch(versionRegex) 119 | expect(output[3]).toMatch(versionRegex) 120 | }) 121 | -------------------------------------------------------------------------------- /src/get-entry-by-version-id.js: -------------------------------------------------------------------------------- 1 | exports.getEntryByVersionID = (versions, id) => { 2 | if (id != null) { 3 | return versions.find(version => version.id === id) 4 | } 5 | 6 | return [...versions].filter(version => !['Unreleased', 'unreleased'].includes(version.id)).shift() 7 | } 8 | -------------------------------------------------------------------------------- /src/get-entry-by-version-id.test.js: -------------------------------------------------------------------------------- 1 | const { getEntryByVersionID } = require('./get-entry-by-version-id') 2 | 3 | test('get latest if no version provided', () => { 4 | const input = [ 5 | { 6 | id: 'Unreleased', 7 | text: `blablabla`, 8 | }, 9 | { 10 | id: 'v2.0.2', 11 | date: '2019-12-08', 12 | text: `blablabla`, 13 | }, 14 | { 15 | id: 'v2.0.1', 16 | date: '2019-12-02', 17 | text: `blablabla`, 18 | }, 19 | ] 20 | const output = getEntryByVersionID(input) 21 | 22 | expect(output.id).toEqual(input[1].id) 23 | }) 24 | 25 | test('return null if bad version provided', () => { 26 | const input = [ 27 | { 28 | id: 'Unreleased', 29 | text: `blablabla`, 30 | }, 31 | { 32 | id: 'v2.0.2', 33 | date: '2019-12-08', 34 | text: `blablabla`, 35 | }, 36 | { 37 | id: 'v2.0.1', 38 | date: '2019-12-02', 39 | text: `blablabla`, 40 | }, 41 | ] 42 | const output = getEntryByVersionID(input, 'v1.2.12') 43 | 44 | expect(output).toBeUndefined() 45 | }) 46 | 47 | test('support X.X.X version patern', () => { 48 | const input = [ 49 | { 50 | id: 'Unreleased', 51 | text: `blablabla`, 52 | }, 53 | { 54 | id: 'v2.0.2', 55 | date: '2019-12-08', 56 | text: `blablabla`, 57 | }, 58 | { 59 | id: '2.0.1', 60 | date: '2019-12-02', 61 | text: `blablabla`, 62 | }, 63 | { 64 | id: '1.13.2', 65 | date: '2019-12-02', 66 | text: `blablabla`, 67 | }, 68 | ] 69 | const output = getEntryByVersionID(input, '2.0.1') 70 | 71 | expect(output.id).toEqual('2.0.1') 72 | }) 73 | 74 | test('get the correct version', () => { 75 | const input = [ 76 | { 77 | id: 'Unreleased', 78 | text: `blablabla`, 79 | }, 80 | { 81 | id: 'v2.1.1', 82 | date: '2019-12-08', 83 | text: `blablabla`, 84 | }, 85 | { 86 | id: 'v2.1.0', 87 | date: '2019-12-02', 88 | text: `blablabla`, 89 | }, 90 | { 91 | id: 'v2.0.0', 92 | date: '2019-12-02', 93 | text: `blablabla`, 94 | }, 95 | { 96 | id: 'v1.2.12', 97 | date: '2019-12-02', 98 | text: `blablabla`, 99 | }, 100 | ] 101 | const output = getEntryByVersionID(input, 'v2.1.0') 102 | 103 | expect(output.id).toEqual(input[2].id) 104 | }) 105 | 106 | test('get the unreleased version', () => { 107 | const input = [ 108 | { 109 | id: 'Unreleased', 110 | text: `blablabla`, 111 | }, 112 | { 113 | id: 'v2.1.1', 114 | date: '2019-12-08', 115 | text: `blablabla`, 116 | }, 117 | { 118 | id: 'v2.1.0', 119 | date: '2019-12-02', 120 | text: `blablabla`, 121 | }, 122 | { 123 | id: 'v2.0.0', 124 | date: '2019-12-02', 125 | text: `blablabla`, 126 | }, 127 | { 128 | id: 'v1.2.12', 129 | date: '2019-12-02', 130 | text: `blablabla`, 131 | }, 132 | ] 133 | const output = getEntryByVersionID(input, 'Unreleased') 134 | 135 | expect(output.id).toEqual(input[0].id) 136 | }) 137 | -------------------------------------------------------------------------------- /src/get-links.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core') 2 | 3 | const linkRegex = 4 | /^\[.+\]:\s?(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/ 5 | const avoidNonVersionData = text => linkRegex.test(text) 6 | 7 | exports.getLinks = rawData => { 8 | const content = String(rawData) 9 | 10 | core.debug(`CHANGELOG content: ${content}`) 11 | 12 | return content.trim().split('\n').filter(avoidNonVersionData) 13 | } 14 | -------------------------------------------------------------------------------- /src/get-links.test.js: -------------------------------------------------------------------------------- 1 | const { getLinks } = require('./get-links') 2 | 3 | const DATA_v = ` 4 | # Changelog 5 | All notable changes to this project will be documented in this file. 6 | 7 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 8 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 9 | 10 | ## [Unreleased] 11 | ### Added 12 | - The project has now a CHANGELOG 13 | 14 | ### Fixed 15 | - README now uses the correct version number in the examples 16 | 17 | ## [v1.0.1] - 2020-02-12 18 | ### Fixed 19 | - Remove template's old behavior 20 | 21 | ## [v1.0.0] - 2020-02-12 22 | ### Added 23 | - CHANGELOG can be parsed by the github action 24 | 25 | [Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.1...HEAD 26 | [1.0.1]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0...v1.0.1 27 | [1.0.0]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0 28 | ` 29 | 30 | const DATA_complex = ` 31 | # Changelog 32 | All notable changes to this project will be documented in this file. 33 | 34 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 35 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 36 | 37 | ## [1.1.2+meta] 38 | ### Added 39 | - The project has now a CHANGELOG 40 | 41 | ### Fixed 42 | - README now uses the correct version number in the examples 43 | 44 | ## [1.1.1-rc.1+build.123] - 2020-02-12 45 | ### Fixed 46 | - Remove template's old behavior 47 | 48 | ## [1.1.1-DEV-SNAPSHOT] - 2020-02-12 49 | ### Added 50 | - CHANGELOG can be parsed by the github action 51 | 52 | ## [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay] - 2019-12-09 53 | ### Added 54 | - CHANGELOG can be parsed by the github action 55 | 56 | [1.1.2+meta]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.1-rc.1+build.123...1.1.2+meta 57 | [1.1.1-rc.1+build.123]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-rc.1+build.123 58 | [1.1.1-DEV-SNAPSHOT]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay...v1.1.1-DEV-SNAPSHOT 59 | [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay 60 | ` 61 | 62 | const DATA = ` 63 | # Changelog 64 | All notable changes to this project will be documented in this file. 65 | 66 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 67 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 68 | 69 | ## [Unreleased] 70 | ### Added 71 | - The project has now a CHANGELOG 72 | 73 | ### Fixed 74 | - README now uses the correct version number in the examples 75 | 76 | ## [1.0.1] - 2020-02-12 77 | ### Fixed 78 | - Remove template's old behavior 79 | 80 | ## [1.0.0] - 2020-02-12 81 | ### Added 82 | - CHANGELOG can be parsed by the github action 83 | 84 | [Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.1...HEAD 85 | [1.0.1]: https://github.com/mindsers/changelog-reader-action/compare/v1.0.0...v1.0.1 86 | [1.0.0]: https://github.com/mindsers/changelog-reader-action/releases/tag/v1.0.0 87 | ` 88 | 89 | const linkRegex = 90 | /^\[.+\]:\s?(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/ 91 | 92 | test('retreive links from test (tag patern: vX.X.X)', () => { 93 | const output = getLinks(DATA_v) 94 | 95 | expect(output.length).toEqual(3) 96 | for (const text of output) { 97 | expect(text).toMatch(linkRegex) 98 | } 99 | }) 100 | 101 | test('retreive links from test (tag patern: X.X.X)', () => { 102 | const output = getLinks(DATA) 103 | 104 | expect(output.length).toEqual(3) 105 | for (const text of output) { 106 | expect(text).toMatch(linkRegex) 107 | } 108 | }) 109 | 110 | // https://github.com/mindsers/changelog-reader-action/issues/8 111 | test('retreive links from test (complex SEMVER)', () => { 112 | const output = getLinks(DATA_complex) 113 | 114 | expect(output.length).toEqual(4) 115 | for (const text of output) { 116 | expect(text).toMatch(linkRegex) 117 | } 118 | }) 119 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | const utils = require('util') 2 | const fs = require('fs') 3 | const core = require('@actions/core') 4 | 5 | const { validateEntry } = require('./validate-entry') 6 | const { parseEntry } = require('./parse-entry') 7 | const { getEntries } = require('./get-entries') 8 | const { getEntryByVersionID } = require('./get-entry-by-version-id') 9 | const { getLinks } = require('./get-links') 10 | const { addLinks } = require('./add-links') 11 | 12 | const readFile = utils.promisify(fs.readFile) 13 | 14 | exports.main = async function main() { 15 | try { 16 | const changelogPath = core.getInput('path') || './CHANGELOG.md' 17 | const targetVersion = core.getInput('version') || null 18 | const validationLevel = core.getInput('validation_level') || 'none' 19 | 20 | if (targetVersion == null) { 21 | core.warning( 22 | `No target version specified. Will try to return the most recent one in the changelog file.` 23 | ) 24 | } 25 | 26 | // Parse data 27 | core.startGroup('Parse data') 28 | const rawData = await readFile(changelogPath) 29 | const linkList = getLinks(rawData) 30 | const versions = getEntries(rawData).map(parseEntry).map(addLinks(linkList)) 31 | 32 | core.debug(`${versions.length} version logs found`) 33 | core.endGroup() 34 | 35 | // Validate data 36 | core.startGroup('Validate data') 37 | if (validationLevel === 'none') { 38 | core.info(`Validation level set to 'none'. Skipping validation.`) 39 | } 40 | 41 | if (validationLevel !== 'none') { 42 | const validationDepth = parseInt(core.getInput('validation_depth'), 10) 43 | const releasedVersions = versions.filter(version => version.status != 'unreleased') 44 | releasedVersions 45 | .reverse() 46 | .slice(Math.max(0, releasedVersions.length - validationDepth)) 47 | .forEach(validateEntry(validationLevel)) 48 | } 49 | core.endGroup() 50 | 51 | // Return data 52 | const entry = getEntryByVersionID(versions, targetVersion) 53 | 54 | if (entry == null) { 55 | throw new Error( 56 | `No log entry found${targetVersion != null ? ` for version ${targetVersion}` : ''}` 57 | ) 58 | } 59 | 60 | core.setOutput('version', entry.id) 61 | core.setOutput('date', entry.date) 62 | core.setOutput('status', entry.status) 63 | core.setOutput('changes', entry.text) 64 | } catch (error) { 65 | core.setFailed(error.message) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/parse-entry-content.js: -------------------------------------------------------------------------------- 1 | exports.parseEntryContent = function (text) { 2 | return text 3 | .split(/^###\s*/gm) 4 | .filter(content => content.replace(/\s+/g, '') != '') 5 | .map(content => { 6 | const [type, ...items] = content.trim().split(/\r*\n/) 7 | 8 | return { type: type.toLowerCase().trim(), items } 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /src/parse-entry.js: -------------------------------------------------------------------------------- 1 | const { prerelease } = require('semver') 2 | 3 | exports.parseEntry = entry => { 4 | const [title, ...other] = entry.trim().split('\n') 5 | 6 | const [versionPart, datePart] = title.split(' - ') 7 | const [versionNumber] = versionPart.match(/[a-zA-Z0-9.\-+]+/) 8 | const [versionDate] = (datePart != null && datePart.match(/[0-9-]+/)) || [] 9 | 10 | return { 11 | id: versionNumber, 12 | date: versionDate, 13 | status: computeStatus(versionNumber, title), 14 | text: other.filter(item => !/\[.*\]: http/.test(item)).join('\n'), 15 | } 16 | } 17 | 18 | function computeStatus(version, title) { 19 | if (prerelease(version)) { 20 | return 'prereleased' 21 | } 22 | 23 | if (title.match(/\[yanked\]/i)) { 24 | return 'yanked' 25 | } 26 | 27 | if (title.match(/\[unreleased\]/i)) { 28 | return 'unreleased' 29 | } 30 | 31 | return 'released' 32 | } 33 | -------------------------------------------------------------------------------- /src/parse-entry.test.js: -------------------------------------------------------------------------------- 1 | const { parseEntry } = require('./parse-entry') 2 | 3 | const entryDescription = ` 4 | ### Added 5 | - New attribute isOrdered on TextFieldList. Basicaly,
    becomes