Skip to content

Commit 7eda9e9

Browse files
add requirement and detection that breaking changes are explained in release notes (#586)
* add requirement and detection that breaking changes are explained in release notes * add unit tests * fix get_release_notes * look up the PR at guideline check time to protect against slow labelling * Update src/AutoMerge/guidelines.jl Co-authored-by: Eric Hanson <[email protected]> * accommodate unit tests * make it opt-in in `run` * tryfix * fix * expand message with example * indent? * refactor and fix indentation * update reference tests * add more details and include JuliaHub * Update Project.toml --------- Co-authored-by: Eric Hanson <[email protected]>
1 parent 11d89a1 commit 7eda9e9

File tree

24 files changed

+778
-5
lines changed

24 files changed

+778
-5
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "RegistryCI"
22
uuid = "0c95cc5f-2f7e-43fe-82dd-79dbcba86b32"
33
authors = ["Dilum Aluthge <[email protected]>", "Fredrik Ekre <[email protected]>", "contributors"]
4-
version = "10.9.1"
4+
version = "10.10.0"
55

66
[deps]
77
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

docs/src/guidelines.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function guidelines_to_markdown_output(guidelines_function::Function)
2020
guidelines = guidelines_function(
2121
registration_type;
2222
check_license = true,
23+
check_breaking_explanation = true,
2324
this_is_jll_package = false,
2425
this_pr_can_use_special_jll_exceptions = false,
2526
use_distance_check = false,
@@ -50,6 +51,7 @@ function guidelines_to_markdown_output(guidelines_function::Function)
5051
guidelines = guidelines_function(
5152
registration_type;
5253
check_license = true,
54+
check_breaking_explanation = true,
5355
this_is_jll_package = false,
5456
this_pr_can_use_special_jll_exceptions = false,
5557
use_distance_check = false,
@@ -130,6 +132,28 @@ very possible for a perfectly good name to not pass the automatic checks and
130132
require manual merging. They simply exist to provide a fast path so that
131133
manual review is not required for every new package.
132134

135+
### Providing and updating release notes
136+
137+
When invoking a registration with the `@JuliaRegister` bot, release notes can be added in the form
138+
```
139+
@JuliaRegistrator register
140+
141+
Release notes:
142+
143+
## Breaking changes
144+
145+
- Explanation of breaking change, ideally with upgrade tips
146+
- ...
147+
```
148+
149+
These can also be added/updated on the General PR by re-invoking with the above.
150+
151+
Doing this has two benefits:
152+
- helps explanations during the registration process, especially for breaking changes
153+
- release notes are picked up by TagBot such that they are added to the new release on the orignial repo
154+
155+
Automerge is disabled for breaking changes where release notes are not provided mentioning "breaking".
156+
133157
## List of all GitHub PR labels that can influence AutoMerge
134158

135159
AutoMerge reads certain labels on GitHub registration pull requests to influence its decisions.

docs/src/private-registries.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ RegistryCI.AutoMerge.run(
7373
additional_statuses = String[],
7474
additional_check_runs = String[],
7575
check_license = true,
76+
check_breaking_explanation = true,
7677
public_registries = String["https://github.com/HolyLab/HolyLabRegistry"],
7778
)
7879
```

example_github_workflow_files/automerge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ jobs:
3535
AUTOMERGE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3636
AUTOMERGE_TAGBOT_TOKEN: ${{ secrets.TAGBOT_TOKEN }}
3737
JULIA_DEBUG: RegistryCI,AutoMerge
38-
run: julia --project=.ci/ -e 'using RegistryCI; using Dates; RegistryCI.AutoMerge.run(merge_new_packages = true, merge_new_versions = true, new_package_waiting_period = Day(3), new_jll_package_waiting_period = Minute(15), new_version_waiting_period = Minute(15), new_jll_version_waiting_period = Minute(15), registry = "JuliaRegistries/General", tagbot_enabled=true, authorized_authors = String["JuliaRegistrator"], authorized_authors_special_jll_exceptions = String["jlbuild"], suggest_onepointzero = false, additional_statuses = String[], additional_check_runs = String["Travis CI - Pull Request"])'
38+
run: julia --project=.ci/ -e 'using RegistryCI; using Dates; RegistryCI.AutoMerge.run(merge_new_packages = true, merge_new_versions = true, new_package_waiting_period = Day(3), new_jll_package_waiting_period = Minute(15), new_version_waiting_period = Minute(15), new_jll_version_waiting_period = Minute(15), registry = "JuliaRegistries/General", tagbot_enabled=true, authorized_authors = String["JuliaRegistrator"], authorized_authors_special_jll_exceptions = String["jlbuild"], suggest_onepointzero = false, additional_statuses = String[], additional_check_runs = String["Travis CI - Pull Request"], check_breaking_explanation = true)'

src/AutoMerge/guidelines.jl

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,74 @@ function meets_name_match_check(
322322
return (true, "")
323323
end
324324

325+
# This check checks for an explanation of why a breaking change is breaking
326+
const guideline_breaking_explanation = Guideline(;
327+
info = "Release notes have not been provided that explain why this is a breaking change.",
328+
docs = "If this is a breaking change, release notes must be given that explain why this is a breaking change (i.e. mention \"breaking\"). To update the release notes, please see the \"Providing and updating release notes\" subsection under \"Additional information\" below.",
329+
check=data -> meets_breaking_explanation_check(data))
330+
331+
function meets_breaking_explanation_check(data::GitHubAutoMergeData)
332+
# Look up PR here in case the labels are slow to be applied by the Registrator bot
333+
# which decides whether to add the BREAKING label
334+
pr = GitHub.pull_request(data.api, data.registry, data.pr.number; auth=data.auth)
335+
return meets_breaking_explanation_check(pr.labels, pr.body)
336+
end
337+
338+
function breaking_explanation_message(has_release_notes)
339+
example_detail = """
340+
<details><summary>Example of adding release notes with breaking notice</summary>
341+
342+
If you are using the comment bot `@JuliaRegistrator`, you can add release notes to this registration by re-triggering registration while specifying release notes:
343+
344+
```
345+
@JuliaRegistrator register
346+
347+
Release notes:
348+
349+
## Breaking changes
350+
351+
- Explanation of breaking change, ideally with upgrade tips
352+
- ...
353+
```
354+
355+
If you are using JuliaHub, trigger registration the same way you did the first time, but enter release notes that specify the breaking changes.
356+
</details>
357+
"""
358+
if has_release_notes
359+
return """
360+
This is a breaking change, but no release notes have been provided. Please add release notes that explain the breaking change.
361+
$(example_detail)
362+
"""
363+
else
364+
return """
365+
This is a breaking change, but the release notes do not mention it. Please add a mention of the breaking change to the release notes.
366+
$(example_detail)
367+
"""
368+
end
369+
end
370+
371+
function meets_breaking_explanation_check(labels::Vector, body::AbstractString)
372+
if any(==("BREAKING"), labels)
373+
release_notes = get_release_notes(body)
374+
if release_notes === nothing
375+
msg = breaking_explanation_message(false)
376+
return false, msg
377+
elseif !occursin(r"breaking", lowercase(release_notes))
378+
msg = breaking_explanation_message(true)
379+
return false, msg
380+
else
381+
return true, ""
382+
end
383+
else
384+
return true, ""
385+
end
386+
end
387+
388+
function get_release_notes(body::AbstractString)
389+
pattern = r"<!-- BEGIN RELEASE NOTES -->(?s)(.*?)<!-- END RELEASE NOTES -->"
390+
m = match(pattern, body)
391+
return m === nothing ? nothing : m.captures[1]
392+
end
325393

326394
# This check looks for similar (but not exactly matching) names. It can be
327395
# overridden by a label.
@@ -1078,7 +1146,8 @@ function get_automerge_guidelines(
10781146
this_is_jll_package::Bool,
10791147
this_pr_can_use_special_jll_exceptions::Bool,
10801148
use_distance_check::Bool,
1081-
package_author_approved::Bool # currently unused for new packages
1149+
package_author_approved::Bool, # currently unused for new packages
1150+
check_breaking_explanation::Bool # not valid for new packages
10821151
)
10831152
guidelines = [
10841153
# We first verify the name is a valid Julia identifier.
@@ -1124,6 +1193,7 @@ end
11241193
function get_automerge_guidelines(
11251194
::NewVersion;
11261195
check_license::Bool,
1196+
check_breaking_explanation::Bool,
11271197
this_is_jll_package::Bool,
11281198
this_pr_can_use_special_jll_exceptions::Bool,
11291199
use_distance_check::Bool, # unused for new versions
@@ -1151,6 +1221,7 @@ function get_automerge_guidelines(
11511221
(guideline_version_has_osi_license, check_license),
11521222
(guideline_src_names_OK, true),
11531223
(guideline_version_can_be_imported, true),
1224+
(guideline_breaking_explanation, check_breaking_explanation && !this_is_jll_package),
11541225
]
11551226
return guidelines
11561227
end

src/AutoMerge/public.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Run the `RegistryCI.AutoMerge` service.
3131
- `registry_deps`: list of registry dependencies, e.g your packages may depend on `General`.
3232
- `api_url`: the registry host API URL, default is `"https://api.github.com"`.
3333
- `check_license`: check package has a valid license, default is `false`.
34+
- `check_breaking_explanation`: Check whether the PR has release notes (collected via Registrator.jl) with a breaking change explanation, default is `false`.
3435
- `public_registries`: If a new package registration has a UUID that matches
3536
that of a package already registered in one of these registries supplied here
3637
(and has either a different name or different URL) then an error will be thrown.
@@ -62,6 +63,7 @@ RegistryCI.AutoMerge.run(
6263
additional_statuses = String[],
6364
additional_check_runs = String[],
6465
check_license = true,
66+
check_breaking_explanation = true,
6567
public_registries = String["https://github.com/HolyLab/HolyLabRegistry"],
6668
)
6769
```
@@ -95,6 +97,7 @@ function run(;
9597
registry_deps::Vector{<:AbstractString}=String[],
9698
api_url::String="https://api.github.com",
9799
check_license::Bool=false,
100+
check_breaking_explanation::Bool=false,
98101
# A list of public Julia registries (repository URLs)
99102
# which will be checked for UUID collisions in order to
100103
# mitigate the dependency confusion vulnerability. See
@@ -158,6 +161,7 @@ function run(;
158161
whoami=whoami,
159162
registry_deps=registry_deps,
160163
check_license=check_license,
164+
check_breaking_explanation=check_breaking_explanation,
161165
public_registries=public_registries,
162166
read_only=read_only,
163167
environment_variables_to_pass=environment_variables_to_pass,

src/AutoMerge/pull_requests.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ function pull_request_build(
8686
point_to_slack::Bool,
8787
registry_deps::Vector{<:AbstractString}=String[],
8888
check_license::Bool,
89+
check_breaking_explanation::Bool,
8990
public_registries::Vector{<:AbstractString}=String[],
9091
read_only::Bool,
9192
environment_variables_to_pass::Vector{<:AbstractString}=String[],
@@ -166,12 +167,12 @@ function pull_request_build(
166167
read_only=read_only,
167168
environment_variables_to_pass=environment_variables_to_pass,
168169
)
169-
pull_request_build(data; check_license=check_license, new_package_waiting_period=new_package_waiting_period)
170+
pull_request_build(data; check_license=check_license, check_breaking_explanation=check_breaking_explanation, new_package_waiting_period=new_package_waiting_period)
170171
rm(registry_master; force=true, recursive=true)
171172
return nothing
172173
end
173174

174-
function pull_request_build(data::GitHubAutoMergeData; check_license, new_package_waiting_period)::Nothing
175+
function pull_request_build(data::GitHubAutoMergeData; check_license, check_breaking_explanation, new_package_waiting_period)::Nothing
175176
kind = package_or_version(data.registration_type)
176177
this_is_jll_package = is_jll_name(data.pkg)
177178
@info(
@@ -194,6 +195,7 @@ function pull_request_build(data::GitHubAutoMergeData; check_license, new_packag
194195
guidelines = get_automerge_guidelines(
195196
data.registration_type;
196197
check_license=check_license,
198+
check_breaking_explanation=check_breaking_explanation,
197199
this_is_jll_package=this_is_jll_package,
198200
this_pr_can_use_special_jll_exceptions=this_pr_can_use_special_jll_exceptions,
199201
use_distance_check=perform_distance_check(data.pr.labels),

test/automerge-unit.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ function comment_reference_test()
7070
"_version_", version, "_point_to_slack_", point_to_slack)
7171
reasons = [
7272
AutoMerge.compat_violation_message(["julia"]),
73+
AutoMerge.breaking_explanation_message(true),
74+
AutoMerge.breaking_explanation_message(false),
7375
"Example guideline failed. Please fix it."]
7476
fail_text = AutoMerge.comment_text_fail(type, reasons, suggest_onepointzero, version; point_to_slack=point_to_slack)
7577

@@ -321,6 +323,35 @@ end
321323
@test !AutoMerge.range_did_not_narrow(r2, r3)[1]
322324
@test !AutoMerge.range_did_not_narrow(r3, r2)[1]
323325
end
326+
327+
@testset "Breaking change releases must have explanatory release notes" begin
328+
body_good = """
329+
<!-- BEGIN RELEASE NOTES -->
330+
`````
331+
## Breaking changes
332+
- something
333+
- something else
334+
`````
335+
<!-- END RELEASE NOTES -->
336+
"""
337+
body_bad = """
338+
<!-- BEGIN RELEASE NOTES -->
339+
`````
340+
## Features
341+
- something
342+
- something else
343+
`````
344+
<!-- END RELEASE NOTES -->
345+
"""
346+
body_bad_no_notes = ""
347+
348+
@test AutoMerge.meets_breaking_explanation_check(["BREAKING"], body_good)[1]
349+
@test !AutoMerge.meets_breaking_explanation_check(["BREAKING"], body_bad)[1]
350+
@test !AutoMerge.meets_breaking_explanation_check(["BREAKING"], body_bad_no_notes)[1]
351+
352+
# Maybe this should fail as the label isn't applied by JuliaRegistrator, so the version isn't breaking?
353+
@test AutoMerge.meets_breaking_explanation_check([], body_good)[1]
354+
end
324355
end
325356

326357
@testset "Guidelines for both new packages and new versions" begin

test/reference_comments/comment_pass_false_type_new_package_suggest_onepointzero_false_version_0.1.0_point_to_slack_false.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,46 @@ Please make sure that you have read the [package naming guidelines](https://juli
2929

3030
</details>
3131

32+
- This is a breaking change, but no release notes have been provided. Please add release notes that explain the breaking change.
33+
<details><summary>Example of adding release notes with breaking notice</summary>
34+
35+
If you are using the comment bot `@JuliaRegistrator`, you can add release notes to this registration by re-triggering registration while specifying release notes:
36+
37+
```
38+
@JuliaRegistrator register
39+
40+
Release notes:
41+
42+
## Breaking changes
43+
44+
- Explanation of breaking change, ideally with upgrade tips
45+
- ...
46+
```
47+
48+
If you are using JuliaHub, trigger registration the same way you did the first time, but enter release notes that specify the breaking changes.
49+
</details>
50+
51+
52+
- This is a breaking change, but the release notes do not mention it. Please add a mention of the breaking change to the release notes.
53+
<details><summary>Example of adding release notes with breaking notice</summary>
54+
55+
If you are using the comment bot `@JuliaRegistrator`, you can add release notes to this registration by re-triggering registration while specifying release notes:
56+
57+
```
58+
@JuliaRegistrator register
59+
60+
Release notes:
61+
62+
## Breaking changes
63+
64+
- Explanation of breaking change, ideally with upgrade tips
65+
- ...
66+
```
67+
68+
If you are using JuliaHub, trigger registration the same way you did the first time, but enter release notes that specify the breaking changes.
69+
</details>
70+
71+
3272
- Example guideline failed. Please fix it.
3373

3474
## 3. *Needs action*: here's what to do next

test/reference_comments/comment_pass_false_type_new_package_suggest_onepointzero_false_version_0.1.0_point_to_slack_true.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,46 @@ Please make sure that you have read the [package naming guidelines](https://juli
2929

3030
</details>
3131

32+
- This is a breaking change, but no release notes have been provided. Please add release notes that explain the breaking change.
33+
<details><summary>Example of adding release notes with breaking notice</summary>
34+
35+
If you are using the comment bot `@JuliaRegistrator`, you can add release notes to this registration by re-triggering registration while specifying release notes:
36+
37+
```
38+
@JuliaRegistrator register
39+
40+
Release notes:
41+
42+
## Breaking changes
43+
44+
- Explanation of breaking change, ideally with upgrade tips
45+
- ...
46+
```
47+
48+
If you are using JuliaHub, trigger registration the same way you did the first time, but enter release notes that specify the breaking changes.
49+
</details>
50+
51+
52+
- This is a breaking change, but the release notes do not mention it. Please add a mention of the breaking change to the release notes.
53+
<details><summary>Example of adding release notes with breaking notice</summary>
54+
55+
If you are using the comment bot `@JuliaRegistrator`, you can add release notes to this registration by re-triggering registration while specifying release notes:
56+
57+
```
58+
@JuliaRegistrator register
59+
60+
Release notes:
61+
62+
## Breaking changes
63+
64+
- Explanation of breaking change, ideally with upgrade tips
65+
- ...
66+
```
67+
68+
If you are using JuliaHub, trigger registration the same way you did the first time, but enter release notes that specify the breaking changes.
69+
</details>
70+
71+
3272
- Example guideline failed. Please fix it.
3373

3474
## 3. *Needs action*: here's what to do next

0 commit comments

Comments
 (0)