danger-changelog 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -4
- data/README.md +141 -8
- data/doc/intridea.md +40 -5
- data/doc/keep_a_changelog.md +2 -0
- data/lib/changelog/added_lines.rb +19 -0
- data/lib/changelog/changelog_line/changelog_entry_line.rb +15 -11
- data/lib/changelog/changelog_line/changelog_header_line.rb +25 -9
- data/lib/changelog/changelog_line/changelog_line.rb +6 -4
- data/lib/changelog/changelog_line/changelog_line_parser.rb +5 -3
- data/lib/changelog/changelog_line/changelog_placeholder_line.rb +3 -3
- data/lib/changelog/config.rb +76 -44
- data/lib/changelog/gem_version.rb +1 -1
- data/lib/changelog/intent_check.rb +78 -0
- data/lib/changelog/markdown_references.rb +57 -0
- data/lib/changelog/parsers/base.rb +14 -2
- data/lib/changelog/parsers/intridea_format.rb +15 -4
- data/lib/changelog/parsers/keep_a_changelog.rb +4 -4
- data/lib/changelog/parsers.rb +3 -3
- data/lib/changelog/plugin.rb +138 -30
- data/lib/changelog/pr_metadata.rb +15 -2
- data/lib/changelog/unreleased_section.rb +34 -0
- metadata +7 -47
- data/.github/workflows/danger-comment.yml +0 -10
- data/.github/workflows/danger.yml +0 -9
- data/.github/workflows/lint.yml +0 -16
- data/.github/workflows/test.yml +0 -20
- data/.gitignore +0 -6
- data/.rspec +0 -2
- data/.rubocop.yml +0 -31
- data/.rubocop_todo.yml +0 -118
- data/CONTRIBUTING.md +0 -125
- data/Dangerfile +0 -4
- data/Gemfile +0 -21
- data/Guardfile +0 -19
- data/RELEASING.md +0 -67
- data/Rakefile +0 -23
- data/danger-changelog.gemspec +0 -21
- data/images/have_you_updated_changelog.png +0 -0
- data/images/is_changelog_format_correct.png +0 -0
- data/spec/changelog_spec.rb +0 -144
- data/spec/config_spec.rb +0 -104
- data/spec/intridea/changelog_entry_line_spec.rb +0 -174
- data/spec/intridea/changelog_file_spec.rb +0 -155
- data/spec/intridea/changelog_header_line_spec.rb +0 -93
- data/spec/intridea/changelog_line_parser_spec.rb +0 -62
- data/spec/intridea/changelog_placeholder_line_spec.rb +0 -32
- data/spec/intridea/changelog_spec.rb +0 -168
- data/spec/intridea/fixtures/bot_authors.md +0 -5
- data/spec/intridea/fixtures/customized.md +0 -3
- data/spec/intridea/fixtures/dates.md +0 -16
- data/spec/intridea/fixtures/extra_trailing_space.md +0 -3
- data/spec/intridea/fixtures/imbalanced.md +0 -7
- data/spec/intridea/fixtures/lines.md +0 -10
- data/spec/intridea/fixtures/minimal.md +0 -3
- data/spec/intridea/fixtures/missing_your_contribution_here.md +0 -2
- data/spec/intridea/fixtures/semver.md +0 -13
- data/spec/intridea/fixtures/validation_result.md +0 -6
- data/spec/keep_a_changelog/changelog_spec.rb +0 -87
- data/spec/keep_a_changelog/fixtures/complete.md +0 -161
- data/spec/keep_a_changelog/fixtures/invalid_line.md +0 -23
- data/spec/keep_a_changelog/fixtures/lines_with_links.md +0 -14
- data/spec/keep_a_changelog/fixtures/missing_a_version_header.md +0 -20
- data/spec/plugin_spec.rb +0 -11
- data/spec/pr_metadata_spec.rb +0 -126
- data/spec/spec_helper.rb +0 -47
- data/spec/support/shared/changelog.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9344e34064e086a963ba58734bc0468c3feaf3e70404b63215d9140b2c31ea99
|
|
4
|
+
data.tar.gz: 27caf32026da6f6273c6b9dcd91e6b4c0629c73bd63ca034645de8f4a8ac2b4b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 68121502e1ce015b0047ccd75faec0ac462d408f4033db1fcf0094d98a79fe518afe9d831adec96b83a2a73d29c3c08f4c207c820afedc27a4c7420755fa6dfe
|
|
7
|
+
data.tar.gz: 10371d27eb574c7f47970d969d745508c4bf6402655c400bdc8855dc2997ccaaeb6563717a3e568f85ff76a263baabdd6974298b4d64e7dd1cd207d6f50e12c0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
-
### 0.
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
|
|
3
|
+
### 0.10.0 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
* [#84](https://github.com/dblock/danger-changelog/pull/84): Add global and per-check unreleased label lists for exact, case-sensitive header validation while preserving permissive defaults - [@dblock](https://github.com/dblock).
|
|
6
|
+
* [#82](https://github.com/dblock/danger-changelog/pull/82): Add opt-in intent-word checks with a configurable dictionary, consistent forms within each verb family, and full-file or added-only validation - [@dblock](https://github.com/dblock).
|
|
7
|
+
* [#83](https://github.com/dblock/danger-changelog/pull/83): Add global and per-check only_added scope for format diagnostics while keeping full-file validation as the default and file-wide requirements unconditional - [@dblock](https://github.com/dblock).
|
|
8
|
+
* [#81](https://github.com/dblock/danger-changelog/pull/81): Require added changelog entries to belong to an unreleased section by default, with global and per-check heading overrides and opt-out - [@dblock](https://github.com/dblock).
|
|
9
|
+
* [#80](https://github.com/dblock/danger-changelog/pull/80): Keep invalid-line reports fenced correctly when changelog text contains backticks and verify consecutive Keep a Changelog error rendering - [@dblock](https://github.com/dblock).
|
|
10
|
+
* [#79](https://github.com/dblock/danger-changelog/pull/79): Add global and per-check strict_semver settings to allow opt-in numeric versions with two or more components in both changelog formats - [@dblock](https://github.com/dblock).
|
|
11
|
+
* [#78](https://github.com/dblock/danger-changelog/pull/78): Support GitLab.com merge request and author links and generate reminders from GitLab metadata without requiring GitHub - [@dblock](https://github.com/dblock).
|
|
12
|
+
* [#77](https://github.com/dblock/danger-changelog/pull/77): Support Markdown reference-style PR and author links in Intridea changelogs, reporting undefined references and preserving original entries in diagnostics - [@dblock](https://github.com/dblock).
|
|
13
|
+
* [#76](https://github.com/dblock/danger-changelog/pull/76): Allow uncased scripts and emoji at the start of Intridea descriptions while retaining capitalization rules for cased letters - [@dblock](https://github.com/dblock).
|
|
14
|
+
* [#75](https://github.com/dblock/danger-changelog/pull/75): Support all global settings as isolated per-check keyword overrides, including placeholder validation and parser selection - [@dblock](https://github.com/dblock).
|
|
15
|
+
* [#74](https://github.com/dblock/danger-changelog/pull/74): Explain missing colons after pull request links, separate suggested entries with a heading, and document conditional changelog reminders - [@dblock](https://github.com/dblock).
|
|
16
|
+
* [#73](https://github.com/dblock/danger-changelog/pull/73): Require Ruby 3.1, test through Ruby 4.0 with compatible ActiveSupport versions, modernize pinned tokenless workflows, and restrict packaged files - [@dblock](https://github.com/dblock).
|
|
7
17
|
### 0.9.0 (2026/10/14)
|
|
8
18
|
|
|
9
19
|
* [#72](https://github.com/dblock/danger-changelog/pull/72): Accept bot author links with a `[bot]` suffix without changing their attribution - [@dblock](https://github.com/dblock).
|
data/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Danger-Changelog
|
|
2
2
|
|
|
3
|
-
A plugin for [danger.systems](
|
|
4
|
-
It can make sure, for example, that changes are attributed properly, have a valid version number, a date in the ISO8601 format, balanced parenthesis and brackets, and that they’re always terminated with a period.
|
|
3
|
+
A plugin for [danger.systems](https://danger.systems) that obsessive-compulsively lints your project’s `CHANGELOG.md`. It can make sure, for example, that changes are attributed properly, have a valid version number, a date in the ISO8601 format, balanced parenthesis and brackets, and that they’re always terminated with a period.
|
|
5
4
|
|
|
6
5
|
[](https://badge.fury.io/rb/danger-changelog)
|
|
7
6
|
[](https://github.com/dblock/danger-changelog/actions/workflows/test.yml)
|
|
@@ -16,6 +15,12 @@ It can make sure, for example, that changes are attributed properly, have a vali
|
|
|
16
15
|
- [changelog.format](#changelogformat)
|
|
17
16
|
- [changelog.placeholder_line](#changelogplaceholder_line)
|
|
18
17
|
- [changelog.ignore_files](#changelogignore_files)
|
|
18
|
+
- [changelog.strict_semver](#changelogstrict_semver)
|
|
19
|
+
- [changelog.unreleased_labels](#changelogunreleased_labels)
|
|
20
|
+
- [changelog.unreleased_heading](#changelogunreleased_heading)
|
|
21
|
+
- [changelog.only_added](#changelogonly_added)
|
|
22
|
+
- [changelog.check_intent and changelog.intent_words](#changelogcheck_intent-and-changelogintent_words)
|
|
23
|
+
- [Conditional reminders](#conditional-reminders)
|
|
19
24
|
- [Checks](#checks)
|
|
20
25
|
- [changelog.check!](#changelogcheck)
|
|
21
26
|
- [changelog.have_you_updated_changelog?](#changeloghave_you_updated_changelog)
|
|
@@ -27,12 +32,14 @@ It can make sure, for example, that changes are attributed properly, have a vali
|
|
|
27
32
|
|
|
28
33
|
By design, `danger-changelog` is quite strict with what it allows as a valid changelog file, using the [Intridea style](doc/intridea.md), [used by this library itself](CHANGELOG.md). It also supports the [Keep a Changelog](doc/keep_a_changelog.md) format.
|
|
29
34
|
|
|
35
|
+
Intridea PR and author links can be inline links or [Markdown references](doc/intridea.md#changelog-entries), with definitions elsewhere in the file. Undefined references are reported without changing the original entry shown in the report.
|
|
36
|
+
|
|
30
37
|
## Installation
|
|
31
38
|
|
|
32
|
-
Add `danger-changelog` to your Gemfile.
|
|
39
|
+
Requires Ruby 3.1 or newer. Add `danger-changelog` to your Gemfile.
|
|
33
40
|
|
|
34
41
|
```
|
|
35
|
-
gem 'danger-changelog'
|
|
42
|
+
gem 'danger-changelog'
|
|
36
43
|
```
|
|
37
44
|
|
|
38
45
|
Call `changelog.check!` from your `Dangerfile`. Make a pull request and see this plugin in action.
|
|
@@ -43,7 +50,16 @@ Methods and attributes from this plugin are available in your `Dangerfile` under
|
|
|
43
50
|
|
|
44
51
|
## Configuration
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
Every option can be set globally on `changelog` or passed as a keyword to a check. Per-check keywords override global defaults only for that call; parsing and reports use an immutable snapshot. Unknown options and invalid values raise `ArgumentError`.
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
changelog.ignore_files = ['README.md', /\.txt$/]
|
|
57
|
+
changelog.check!
|
|
58
|
+
changelog.check!(filename: 'packages/api/CHANGELOG.md')
|
|
59
|
+
changelog.check!(filename: 'CHANGES.md', format: :keep_a_changelog)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Global strings and arrays are copied and frozen; assign a new value rather than modifying a setting in place. The deprecated `Danger::Changelog.configure` block still sets global defaults.
|
|
47
63
|
|
|
48
64
|
### changelog.filename
|
|
49
65
|
|
|
@@ -51,6 +67,7 @@ Set the CHANGELOG file name, defaults to `CHANGELOG.md`.
|
|
|
51
67
|
|
|
52
68
|
```ruby
|
|
53
69
|
changelog.filename = 'CHANGES.md'
|
|
70
|
+
changelog.check!(filename: 'CHANGES.md')
|
|
54
71
|
```
|
|
55
72
|
|
|
56
73
|
### changelog.format
|
|
@@ -59,9 +76,10 @@ Set the format of the CHANGELOG file.
|
|
|
59
76
|
|
|
60
77
|
```ruby
|
|
61
78
|
changelog.format = :keep_a_changelog
|
|
79
|
+
changelog.check!(format: :keep_a_changelog)
|
|
62
80
|
```
|
|
63
81
|
|
|
64
|
-
Available formats are [Intridea](doc/intridea.md) (default) and [Keep a Changelog](doc/keep_a_changelog.md).
|
|
82
|
+
Available formats are [Intridea](doc/intridea.md) (default) and [Keep a Changelog](doc/keep_a_changelog.md). Symbol and string format names are accepted.
|
|
65
83
|
|
|
66
84
|
### changelog.placeholder_line
|
|
67
85
|
|
|
@@ -69,8 +87,11 @@ Customize the `* Your contribution here.` line. Set the value to `nil` to stop c
|
|
|
69
87
|
|
|
70
88
|
```ruby
|
|
71
89
|
changelog.placeholder_line = "* Your change here.\n"
|
|
90
|
+
changelog.check!(placeholder_line: 'Your change here.')
|
|
72
91
|
```
|
|
73
92
|
|
|
93
|
+
The leading `* ` and trailing newline are added when omitted. This option applies to the Intridea parser; Keep a Changelog does not require a placeholder.
|
|
94
|
+
|
|
74
95
|
### changelog.ignore_files
|
|
75
96
|
|
|
76
97
|
Ignore additions and changes with a certain name or expression, default is to ignore `README.md` changes.
|
|
@@ -79,26 +100,138 @@ For example, ignore `UPGRADING.md` and all `.txt` files.
|
|
|
79
100
|
|
|
80
101
|
```ruby
|
|
81
102
|
changelog.ignore_files = ['README.md', 'UPGRADING.md', /\.txt$/]
|
|
103
|
+
changelog.check!(ignore_files: ['README.md', 'UPGRADING.md', /\.txt$/])
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### changelog.strict_semver
|
|
107
|
+
|
|
108
|
+
Require three-component Semantic Versions by default. Set `strict_semver: false` to accept numeric versions with two or more components, such as `2.0` or `1.2.3.4`, with optional prerelease/build suffixes. Date, delimiter, and other formatting checks remain active. This applies to both supported formats.
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
changelog.strict_semver = false
|
|
112
|
+
changelog.check!(filename: 'CHANGES.md', strict_semver: false)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### changelog.unreleased_labels
|
|
116
|
+
|
|
117
|
+
By default (`nil`), header validation accepts existing label spellings such as `Next`, `next`, `Unreleased`, and project-specific words. Supply a list to replace that permissive behavior with exact, case-sensitive matching in both formats:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
changelog.unreleased_labels = ['Next']
|
|
121
|
+
changelog.check! # Accepts 0.4.0 (Next), rejects 0.4.0 (next).
|
|
122
|
+
changelog.is_changelog_format_correct?(unreleased_labels: ['Next', 'Unreleased'])
|
|
123
|
+
changelog.check!(unreleased_labels: nil) # Restore permissive validation for this call.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The list restricts labels following a version (with or without delimiters) and standalone labels enclosed in parentheses, brackets, or braces, such as `[Unreleased]`. Bare versions, release dates, and ordinary unwrapped titles such as `Changelog` and `Changed:` remain unaffected. Labels are literal strings, not regular expressions; custom labels may contain spaces and punctuation. Each must be nonempty, have no leading/trailing whitespace, and contain no newlines or enclosing delimiter characters. An empty list rejects all labels while still allowing dated and bare versions.
|
|
127
|
+
|
|
128
|
+
This option validates header spelling, not entry placement. Configure `unreleased_heading` separately if you adopt a custom heading convention; setting the label list does not change its pattern.
|
|
129
|
+
|
|
130
|
+
### changelog.unreleased_heading
|
|
131
|
+
|
|
132
|
+
By default, added list entries must belong to an unreleased section. The default heading pattern, `/(?:\(Next\)|\[Unreleased\])/i`, recognizes Intridea's `(Next)` and Keep a Changelog's `[Unreleased]`, case-insensitively. This global or per-check setting applies to both formats and is enforced by `check!` after its existing checks pass:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
changelog.unreleased_heading = /\(Next\)/
|
|
136
|
+
changelog.check!
|
|
137
|
+
changelog.check!(format: :keep_a_changelog, unreleased_heading: /\[Unreleased\]/i)
|
|
138
|
+
changelog.check!(unreleased_heading: nil) # Allow intentional historical corrections.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Only added `*` or `-` list lines in Danger's changelog diff are checked. Nested category headings stay inside the release; the next heading at the same or shallower level ends it. Unchanged historical entries are ignored. Historical corrections appear as additions too; use `unreleased_heading: nil` for those PRs and release-preparation changes. Customize the pattern for your project's heading convention.
|
|
142
|
+
|
|
143
|
+
Call `changelog.entries_in_unreleased_section?(unreleased_heading: /\(Next\)/)` to run this check separately. Missing diff data produces an explicit error rather than silently skipping the enabled check.
|
|
144
|
+
|
|
145
|
+
### changelog.only_added
|
|
146
|
+
|
|
147
|
+
Format validation and enabled intent validation check the entire file by default (`only_added: false`). Set `only_added: true` globally or per check to report line-level format problems and compare intent words only on added lines in Danger's diff. Edited historical entries count as additions. Headers, punctuation, capitalization, attribution, and reference diagnostics follow this scope; reference definitions elsewhere in the file remain available for resolution.
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
changelog.only_added = true
|
|
151
|
+
changelog.check!
|
|
152
|
+
changelog.is_changelog_format_correct?(only_added: false)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Missing files, required placeholders, and Keep a Changelog preamble requirements always apply to the whole file. Missing diff data is an explicit failure when added-only checking is requested for a changed changelog. An unchanged file has no added-line diagnostics but still receives global checks.
|
|
156
|
+
|
|
157
|
+
Release-section placement always checks additions only, regardless of `only_added`; checking released history against an unreleased section would incorrectly reject existing releases. Use `unreleased_heading: nil` to disable placement validation.
|
|
158
|
+
|
|
159
|
+
### changelog.check_intent and changelog.intent_words
|
|
160
|
+
|
|
161
|
+
Opt into a finite dictionary of intent words and consistent wording within each verb family with `check_intent: true`. This rule is disabled by default and works with both formats. Once enabled, it checks the entire file by default, skipping the configured placeholder and fenced code examples. Set `only_added: true` to compare only added entries, excluding unchanged history. Missing diff data is an error only when added-only checking is requested for a changed changelog.
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
changelog.check_intent = true
|
|
165
|
+
changelog.check!
|
|
166
|
+
changelog.check!(check_intent: true, intent_words: %w[Add Fix Remove])
|
|
167
|
+
changelog.intent_correct?(check_intent: true)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The default verb families are **Add, Allow, Change, Clarify, Deprecate, Disable, Document, Enable, Fix, Improve, Optimize, Refactor, Remove, Rename, Replace, Revert, Simplify, Support, Update, Upgrade**, including their past, third-person present, and gerund forms (for example, `Add`, `Added`, `Adds`, `Adding`; `Fix`, `Fixed`, `Fixes`, `Fixing`). Any form is acceptable on its own, but mixing forms within a family fails: `Add` with `Added` fails, while repeated `Added` entries pass. Each family is independent, so `Added` with `Fix` passes. Diagnostics ask later entries to match the first form used among the checked entries and include its line number. Unknown words show the allowed dictionary.
|
|
171
|
+
|
|
172
|
+
`intent_words` replaces the entire dictionary and must be a nonempty array of single words. Custom words are accepted exactly as listed, without automatic inflection; known forms still belong to their built-in verb family for consistency checks. Matching is case-sensitive, supports Unicode letters, and checks the first word after an optional PR reference or inline link. Setting a dictionary alone does not enable the rule. Projects with non-English prose, emoji-first entries, or inline-code-first descriptions should leave it disabled or adopt a compatible custom word list. Existing format validation remains independent; a custom dictionary does not relax capitalization rules.
|
|
173
|
+
|
|
174
|
+
### Conditional reminders
|
|
175
|
+
|
|
176
|
+
By default, only `README.md` is ignored. To skip reminders when all added or modified files are documentation at the repository root, use an anchored pattern. Subdirectory files still require a changelog update.
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
changelog.ignore_files = [/\A[^\/]+\.md\z/]
|
|
180
|
+
changelog.check!
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
For code-only reminders, use Danger's changed files directly. Keep format validation unconditional, so an edited changelog is checked even when no code changes:
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
changed_files = git.modified_files + git.added_files + git.deleted_files
|
|
187
|
+
changelog.have_you_updated_changelog? if changed_files.any? { |file| file.start_with?('lib/') }
|
|
188
|
+
changelog.is_changelog_format_correct?
|
|
82
189
|
```
|
|
83
190
|
|
|
191
|
+
Other policies can use the same separate checks, such as reminding only for changes larger than 50 lines unless the pull request is marked trivial:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
declared_trivial = github.pr_labels.include?('trivial')
|
|
195
|
+
changelog.have_you_updated_changelog? if git.lines_of_code > 50 && !declared_trivial
|
|
196
|
+
changelog.is_changelog_format_correct?
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The label example uses the GitHub plugin; adapt the condition to your provider. Do not also call `check!` in these conditional examples, because it would run the reminder unconditionally.
|
|
200
|
+
|
|
84
201
|
## Checks
|
|
85
202
|
|
|
86
|
-
|
|
203
|
+
All checks accept the same configuration keywords: `filename`, `format`, `placeholder_line`, `ignore_files`, `strict_semver`, `unreleased_labels`, `unreleased_heading`, `only_added`, `check_intent`, and `intent_words`. Each uses only the relevant settings.
|
|
87
204
|
|
|
88
205
|
### changelog.check!
|
|
89
206
|
|
|
90
|
-
Run all checks with defaults, including `have_you_updated_changelog
|
|
207
|
+
Run all checks with defaults, including `have_you_updated_changelog?`, `is_changelog_format_correct?`, and `entries_in_unreleased_section?`. When enabled, intent validation runs after those checks pass.
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
changelog.check!(filename: 'CHANGES.md', placeholder_line: nil)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
As before, format validation runs only if the update reminder passes. The deprecated `check` also accepts keyword overrides and its legacy positional format, without modifying global defaults.
|
|
91
214
|
|
|
92
215
|
#### changelog.have_you_updated_changelog?
|
|
93
216
|
|
|
94
217
|
Checks whether you have updated CHANGELOG.md.
|
|
95
218
|
|
|
219
|
+
```ruby
|
|
220
|
+
changelog.have_you_updated_changelog?(filename: 'CHANGES.md', ignore_files: [/\.md$/])
|
|
221
|
+
```
|
|
222
|
+
|
|
96
223
|

|
|
97
224
|
|
|
98
225
|
#### changelog.is_changelog_format_correct?
|
|
99
226
|
|
|
100
227
|
Checks whether the CHANGELOG format is correct.
|
|
101
228
|
|
|
229
|
+
```ruby
|
|
230
|
+
changelog.is_changelog_format_correct?(filename: 'CHANGES.md', format: :keep_a_changelog)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The change predicates `changelog_changes?` and `file_changes?` accept the same keywords, for example `changelog.changelog_changes?(filename: 'CHANGES.md')`.
|
|
234
|
+
|
|
102
235
|

|
|
103
236
|
|
|
104
237
|
## Contributing
|
data/doc/intridea.md
CHANGED
|
@@ -92,6 +92,10 @@ The Intridea style changelog was pioneered on the Hashie project. It has many el
|
|
|
92
92
|
# Not valid (2019/12/31)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### Unreleased labels
|
|
96
|
+
|
|
97
|
+
Version headers can use labels such as `## 0.4.0 (Next)` or `## 0.4.0 (next)` by default. To enforce one spelling, set `changelog.unreleased_labels = ['Next']` or pass `unreleased_labels: ['Next']` to a check. This replaces permissive label matching with an exact, case-sensitive list and also applies to standalone delimited labels such as `## [Next]`. Dates, bare versions, and ordinary unwrapped titles are unaffected. The default `nil` preserves existing behavior; `[]` allows no labels. See [configuration](../README.md#changelogunreleased_labels).
|
|
98
|
+
|
|
95
99
|
### Changelog entries
|
|
96
100
|
|
|
97
101
|
✅ Under each title, you can have changelog entries in the format: `* {optional link to GitHub pull request that must be followed by a colon} {a single sentence of text description} - {author github link}.`:
|
|
@@ -105,12 +109,35 @@ The Intridea style changelog was pioneered on the Hashie project. It has many el
|
|
|
105
109
|
|
|
106
110
|
Bot author names may end in a literal `[bot]` suffix, such as `github-actions[bot]` or `dependabot[bot]`. Keep the suffix in the attribution; links to GitHub app pages and bot profiles are supported.
|
|
107
111
|
|
|
108
|
-
|
|
112
|
+
✅ Entry descriptions can start with uppercase letters, uncased scripts (such as Japanese, Chinese, or Arabic), emoji, or inline code
|
|
113
|
+
|
|
114
|
+
```markdown
|
|
115
|
+
* 日本語の変更 - [@ivoanjo](https://github.com/ivoanjo).
|
|
116
|
+
* 修复问题 - [@ivoanjo](https://github.com/ivoanjo).
|
|
117
|
+
* 🚀 Add a feature - [@ivoanjo](https://github.com/ivoanjo).
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
✅ Pull request and author links may use Markdown references: full (`[@dblock][author]`), collapsed (`[@dblock][]`), or shortcut (`[@dblock]`)
|
|
121
|
+
|
|
122
|
+
```markdown
|
|
123
|
+
* [#1]: Fix an issue - [@dblock].
|
|
124
|
+
* [#2][second-pr]: Add a feature - [@dblock][author].
|
|
125
|
+
|
|
126
|
+
[#1]: https://github.com/dblock/danger-changelog/pull/1
|
|
127
|
+
[second-pr]: https://github.com/dblock/danger-changelog/pull/2
|
|
128
|
+
[@dblock]: https://github.com/dblock
|
|
129
|
+
[author]: https://github.com/dblock
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Definitions may appear before or after entries. Labels are case-insensitive and consecutive whitespace is collapsed; the first definition wins when a label is repeated. Angle-bracket destinations and optional titles are accepted, including an indented destination or title on the following line. Inline links and reference links can be mixed. Bot links can use a named reference such as `[@dependabot[bot]][bot]`.
|
|
133
|
+
|
|
134
|
+
Undefined references are reported explicitly. Resolved destinations must satisfy the same GitHub URL rules as inline links. Reports show the original entry, not its expanded form. Reference expansion applies to PR and author links, not arbitrary references or inline-code examples inside the description.
|
|
135
|
+
|
|
136
|
+
❌ When a description starts with a cased letter, that letter must be uppercase
|
|
109
137
|
|
|
110
138
|
```markdown
|
|
111
139
|
* not valid - [@ivoanjo](https://github.com/ivoanjo).
|
|
112
|
-
*
|
|
113
|
-
* ❌ - [@ivoanjo](https://github.com/ivoanjo).
|
|
140
|
+
* écriture en minuscule - [@ivoanjo](https://github.com/ivoanjo).
|
|
114
141
|
* [#1](https://github.com/dblock/danger-changelog/pull/1): not valid - [@dblock](https://github.com/dblock).
|
|
115
142
|
```
|
|
116
143
|
|
|
@@ -120,10 +147,18 @@ Bot author names may end in a literal `[bot]` suffix, such as `github-actions[bo
|
|
|
120
147
|
* [#1](https://github.com/dblock/danger-changelog/pull/1) Not valid - [@dblock](https://github.com/dblock).
|
|
121
148
|
```
|
|
122
149
|
|
|
123
|
-
|
|
150
|
+
✅ GitHub pull requests and GitLab.com merge requests and author profiles are supported, including GitLab subgroups
|
|
151
|
+
|
|
152
|
+
```markdown
|
|
153
|
+
* [#1](https://gitlab.com/group/subgroup/project/-/merge_requests/1): Fix an issue - [@contributor](https://gitlab.com/contributor).
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
GitLab reminders use merge-request metadata for their suggested entries. Self-managed GitLab hosts are not currently accepted by the Intridea URL rules; Keep a Changelog accepts arbitrary link destinations.
|
|
157
|
+
|
|
158
|
+
❌ Links must use supported hosts and `https`
|
|
124
159
|
|
|
125
160
|
```markdown
|
|
126
|
-
*
|
|
161
|
+
* Unsupported host - [@ivoanjo](https://example.com/ivoanjo).
|
|
127
162
|
* No insecure http - [@ivoanjo](http://github.com/ivoanjo).
|
|
128
163
|
* [#1](https://you-get-the-point.com): Entry with a PR link - [@dblock](https://github.com/dblock).
|
|
129
164
|
```
|
data/doc/keep_a_changelog.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The Keep a Changelog style format checks that your changelog is formatted like the specification outlined at [KeepAChangelog.com](https://keepachangelog.com).
|
|
4
4
|
|
|
5
|
+
Header labels are permissive by default. Set `changelog.unreleased_labels = ['Unreleased']` or pass `unreleased_labels: ['Unreleased']` to a check to require that exact spelling in `## [Unreleased]`; lowercase `[unreleased]` then fails. The list also restricts labels following version numbers but does not restrict dates, bare versions, the `Changelog` title, or category headings. The default `nil` preserves existing behavior; `[]` allows no labels. This does not change the separate entry-placement pattern, `unreleased_heading`. See [configuration](../README.md#changelogunreleased_labels).
|
|
6
|
+
|
|
5
7
|
As an example, here is a changelog formatted in the Keep a Changelog style, as borrowed from the Keep a Changelog project:
|
|
6
8
|
|
|
7
9
|
```markdown
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Danger
|
|
2
|
+
module Changelog
|
|
3
|
+
module AddedLines
|
|
4
|
+
def self.from_patch(patch)
|
|
5
|
+
number = nil
|
|
6
|
+
added = []
|
|
7
|
+
patch.each_line do |line|
|
|
8
|
+
if (hunk = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/))
|
|
9
|
+
number = hunk[1].to_i
|
|
10
|
+
elsif number && line.start_with?('+', ' ')
|
|
11
|
+
added << number if line.start_with?('+')
|
|
12
|
+
number += 1
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
added
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -4,8 +4,11 @@ module Danger
|
|
|
4
4
|
module Changelog
|
|
5
5
|
# A CHANGELOG.md line represents the change entry.
|
|
6
6
|
class ChangelogEntryLine < ChangelogLine
|
|
7
|
-
AUTHOR_NAME = /[\w-]+(?:\[bot\])
|
|
8
|
-
AUTHOR_LINK = %r{\[@#{AUTHOR_NAME}\]\(https://github\.com/.*#{AUTHOR_NAME}\)}
|
|
7
|
+
AUTHOR_NAME = /[\w-]+(?:\[bot\])?/
|
|
8
|
+
AUTHOR_LINK = %r{\[@#{AUTHOR_NAME}\]\(https://(?:github\.com/.*#{AUTHOR_NAME}|gitlab\.com/#{AUTHOR_NAME})\)}
|
|
9
|
+
REQUEST_URL = %r{(?:github\.com/[^\s)]+?\d+|gitlab\.com/[^\s)]+/-/merge_requests/\d+)}
|
|
10
|
+
PR_LINK = %r{\[\#\d+\]\(https?://#{REQUEST_URL}/?\)}
|
|
11
|
+
DESCRIPTION_START = /[`\p{Lu}\p{Lt}\p{Lo}\p{Lm}\p{So}]/
|
|
9
12
|
|
|
10
13
|
def valid?
|
|
11
14
|
return validation_result.valid? if validation_result
|
|
@@ -15,18 +18,19 @@ module Danger
|
|
|
15
18
|
validation_result.error! 'too many parenthesis' unless balanced?(line)
|
|
16
19
|
return false if validation_result.invalid?
|
|
17
20
|
|
|
18
|
-
return true if line =~ /^\*\s[
|
|
19
|
-
return true if line =~ %r{^\*\s\[\#\d+\]\(https
|
|
21
|
+
return true if line =~ /^\*\s#{DESCRIPTION_START}(?:.*[^.,])? - #{AUTHOR_LINK}.$/
|
|
22
|
+
return true if line =~ %r{^\*\s\[\#\d+\]\(https://#{REQUEST_URL}\): #{DESCRIPTION_START}(?:.*[^.,])? - #{AUTHOR_LINK}.$}
|
|
20
23
|
|
|
21
24
|
validation_result.error! 'does not start with a star' unless ChangelogEntryLine.starts_with_star?(line)
|
|
22
25
|
validation_result.error! 'does not include a pull request link' unless ChangelogEntryLine.with_pr_link?(line)
|
|
26
|
+
validation_result.error! 'is missing a colon after the pull request link' if ChangelogEntryLine.with_pr_link?(line) && !/#{PR_LINK}:/.match?(line)
|
|
23
27
|
validation_result.error! 'does not have a description' unless ChangelogEntryLine.with_changelog_description?(line)
|
|
24
28
|
validation_result.error! 'does not include an author link' unless ChangelogEntryLine.with_author_link?(line)
|
|
25
29
|
validation_result.error! 'has an extra trailing space' if ChangelogEntryLine.ends_with_space?(line)
|
|
26
30
|
validation_result.error! 'is missing a period at the end of the line' unless ChangelogEntryLine.ends_with_period?(line)
|
|
27
31
|
validation_result.error! 'has an extra period or comma at the end of the description' if
|
|
28
|
-
line =~ /^\*\s
|
|
29
|
-
line =~ %r{^\*\s\[\#\d+\]\(https
|
|
32
|
+
line =~ /^\*\s#{DESCRIPTION_START}.*[.,] - #{AUTHOR_LINK}.$/ ||
|
|
33
|
+
line =~ %r{^\*\s\[\#\d+\]\(https://#{REQUEST_URL}\): #{DESCRIPTION_START}.*[.,] - #{AUTHOR_LINK}.$}
|
|
30
34
|
|
|
31
35
|
false
|
|
32
36
|
end
|
|
@@ -48,7 +52,7 @@ module Danger
|
|
|
48
52
|
.sub(/[?.!,;]?$/, '')
|
|
49
53
|
.capitalize
|
|
50
54
|
pr_author = github.pr_author
|
|
51
|
-
pr_author_url = "https://github.com/#{github.pr_author}"
|
|
55
|
+
pr_author_url = github.is_a?(PRMetadata) ? github.author_url : "https://github.com/#{github.pr_author}"
|
|
52
56
|
"* [##{pr_number}](#{pr_url}): #{pr_title} - [@#{pr_author}](#{pr_author_url})."
|
|
53
57
|
end
|
|
54
58
|
|
|
@@ -75,21 +79,21 @@ module Danger
|
|
|
75
79
|
|
|
76
80
|
# checks whether line contains a MARKDOWN link to a PR
|
|
77
81
|
def self.with_pr_link?(line)
|
|
78
|
-
return true if line
|
|
82
|
+
return true if PR_LINK.match?(line)
|
|
79
83
|
|
|
80
84
|
false
|
|
81
85
|
end
|
|
82
86
|
|
|
83
|
-
#
|
|
87
|
+
# Checks for a description starting with uppercase or uncased text, code, or a symbol.
|
|
84
88
|
def self.with_changelog_description?(line)
|
|
85
|
-
return true if line
|
|
89
|
+
return true if DESCRIPTION_START.match?(line)
|
|
86
90
|
|
|
87
91
|
false
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
# checks whether line contains a MARKDOWN link to an author
|
|
91
95
|
def self.with_author_link?(line)
|
|
92
|
-
return true if line =~ %r{\[@#{AUTHOR_NAME}\]\(https?://github\.com/.*#{AUTHOR_NAME}/?\)}
|
|
96
|
+
return true if line =~ %r{\[@#{AUTHOR_NAME}\]\(https?://(?:github\.com/.*#{AUTHOR_NAME}|gitlab\.com/#{AUTHOR_NAME})/?\)}
|
|
93
97
|
|
|
94
98
|
false
|
|
95
99
|
end
|
|
@@ -2,21 +2,23 @@ module Danger
|
|
|
2
2
|
module Changelog
|
|
3
3
|
# A CHANGELOG.md line represents the version header.
|
|
4
4
|
class ChangelogHeaderLine < ChangelogLine
|
|
5
|
-
OPEN_PARENS = /[(\[{]
|
|
6
|
-
CLOSE_PARENS = /[)\]}]
|
|
5
|
+
OPEN_PARENS = /[(\[{]?/
|
|
6
|
+
CLOSE_PARENS = /[)\]}]?/
|
|
7
7
|
|
|
8
|
-
HASHES = /\#{1,4}
|
|
9
|
-
SEMVER = /(?<semver>(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-]+)*))?)
|
|
10
|
-
|
|
8
|
+
HASHES = /\#{1,4}/
|
|
9
|
+
SEMVER = /(?<semver>(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-]+)*))?)/
|
|
10
|
+
RELAXED_VERSION = /(?:0|[1-9]\d*)(?:\.(?:0|[1-9]\d*))+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?/
|
|
11
|
+
ISO8601_DATETIME = %r{(?<datetime>([0-9]{4})[-/]?(1[0-2]|0?[1-9])[-/]+(3[01]|0?[1-9]|[12][0-9])(T[0-9]{2}:[0-9]{2}:[0-9]{2})?Z?)}
|
|
11
12
|
|
|
12
13
|
def valid?
|
|
13
14
|
stripped_line = line.strip
|
|
15
|
+
version = config.strict_semver ? SEMVER : RELAXED_VERSION
|
|
14
16
|
|
|
15
|
-
m = stripped_line
|
|
16
|
-
m ||= stripped_line.match(/^#{HASHES}\s#{OPEN_PARENS}#{
|
|
17
|
-
m ||= stripped_line.match(/^#{HASHES}\s#{OPEN_PARENS}#{
|
|
17
|
+
m = title_valid?(stripped_line) # title
|
|
18
|
+
m ||= stripped_line.match(/^#{HASHES}\s#{OPEN_PARENS}#{version}#{CLOSE_PARENS}$/) # version only
|
|
19
|
+
m ||= stripped_line.match(/^#{HASHES}\s#{OPEN_PARENS}#{version}#{CLOSE_PARENS}[\s-]+#{OPEN_PARENS}(#{ISO8601_DATETIME}|#{labels})#{CLOSE_PARENS}$/) # version and date or next version description
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
!!m && balanced?(stripped_line)
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def self.validates_as_changelog_line?(line)
|
|
@@ -24,6 +26,20 @@ module Danger
|
|
|
24
26
|
|
|
25
27
|
false
|
|
26
28
|
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def labels
|
|
33
|
+
config.unreleased_labels ? Regexp.union(config.unreleased_labels) : /\w*/
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def title_valid?(title)
|
|
37
|
+
if config.unreleased_labels && (match = title.match(/^#{HASHES}\s[(\[{](.*)[)\]}]$/))
|
|
38
|
+
config.unreleased_labels.include?(match[1])
|
|
39
|
+
else
|
|
40
|
+
title.match?(/^#{HASHES}\s#{OPEN_PARENS}[\w\s:]*#{CLOSE_PARENS}$/)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
27
43
|
end
|
|
28
44
|
end
|
|
29
45
|
end
|
|
@@ -2,13 +2,15 @@ module Danger
|
|
|
2
2
|
module Changelog
|
|
3
3
|
# An abstract CHANGELOG.md line.
|
|
4
4
|
class ChangelogLine
|
|
5
|
-
NON_DELIMITERS = /[^(){}\[\]]
|
|
6
|
-
PAIRED = /\(#{NON_DELIMITERS}\)|\{#{NON_DELIMITERS}\}|\[#{NON_DELIMITERS}\]
|
|
7
|
-
DELIMITER = /[(){}\[\]]
|
|
5
|
+
NON_DELIMITERS = /[^(){}\[\]]*/
|
|
6
|
+
PAIRED = /\(#{NON_DELIMITERS}\)|\{#{NON_DELIMITERS}\}|\[#{NON_DELIMITERS}\]/
|
|
7
|
+
DELIMITER = /[(){}\[\]]/
|
|
8
8
|
|
|
9
9
|
attr_accessor :line, :validation_result
|
|
10
|
+
attr_reader :config
|
|
10
11
|
|
|
11
|
-
def initialize(line)
|
|
12
|
+
def initialize(line, config: Danger::Changelog.config.with)
|
|
13
|
+
@config = config
|
|
12
14
|
self.line = line
|
|
13
15
|
self.validation_result = nil
|
|
14
16
|
end
|
|
@@ -7,18 +7,20 @@ module Danger
|
|
|
7
7
|
# A parser of the CHANGELOG.md lines
|
|
8
8
|
class ChangelogLineParser
|
|
9
9
|
# Returns an instance of Changelog::ChangelogLine class based on the given line
|
|
10
|
-
def self.parse(line)
|
|
10
|
+
def self.parse(line, config: Danger::Changelog.config.with)
|
|
11
|
+
return ChangelogPlaceholderLine.new(line, config: config) if ChangelogPlaceholderLine.validates_as_changelog_line?(line, config: config)
|
|
12
|
+
|
|
11
13
|
changelog_line_class = available_changelog_lines.find do |changelog_line|
|
|
12
14
|
changelog_line.validates_as_changelog_line?(line)
|
|
13
15
|
end
|
|
14
16
|
return nil unless changelog_line_class
|
|
15
17
|
|
|
16
|
-
changelog_line_class.new(line)
|
|
18
|
+
changelog_line_class.new(line, config: config)
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def self.available_changelog_lines
|
|
20
22
|
# Order is important
|
|
21
|
-
[
|
|
23
|
+
[ChangelogEntryLine, ChangelogHeaderLine]
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
private_class_method :available_changelog_lines
|
|
@@ -3,11 +3,11 @@ module Danger
|
|
|
3
3
|
# A CHANGELOG.md line represents the "Your contribution here".
|
|
4
4
|
class ChangelogPlaceholderLine < ChangelogLine
|
|
5
5
|
def valid?
|
|
6
|
-
ChangelogPlaceholderLine.validates_as_changelog_line?(line)
|
|
6
|
+
ChangelogPlaceholderLine.validates_as_changelog_line?(line, config: config)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def self.validates_as_changelog_line?(line)
|
|
10
|
-
return true if line ==
|
|
9
|
+
def self.validates_as_changelog_line?(line, config: Danger::Changelog.config)
|
|
10
|
+
return true if line == config.placeholder_line
|
|
11
11
|
|
|
12
12
|
false
|
|
13
13
|
end
|