Skip to content

WIP: Automatically look up release notes from repo if no release notes explicitly provided #416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion src/commentbot/CommentBot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,24 @@ function make_pull_request(pp::ProcessedParams, rp::RequestParams, rbrn::RegBran

description = something(rp.evt.repository.description, "")

release_notes, releasenotes_source = if !isempty(rp.release_notes)
# these came from the invoke
rp.release_notes, :invoke
elseif pp.release_notes isa String && !isempty(pp.release_notes)
# these came from the changelog search
pp.release_notes, :changelog
else
"", :none
end

params["title"], params["body"] = pull_request_contents(;
registration_type=get(rbrn.metadata, "kind", ""),
package=name,
repo=string(rp.evt.repository.html_url),
user="$(mention(creator))",
version=ver,
commit=pp.sha,
release_notes=rp.release_notes,
release_notes=release_notes,
reviewer="$(mention(reviewer))",
reference=ref,
meta=enc_meta,
Expand All @@ -98,8 +108,48 @@ function make_pull_request(pp::ProcessedParams, rp::RequestParams, rbrn::RegBran
pr, msg = create_or_find_pull_request(repo, params, rbrn)
tag = tag_name(ver, subdir)

releasenotes_note = if releasenotes_source == :invoke
""
elseif releasenotes_source == :changelog
"""
### Release Notes

The following release notes were found for the given version from a CHANGELOG/NEWS/HISTORY.md file.
To provide custom release notes invoke with text headed by `Release notes:`

```
$release_notes
```

"""
elseif releasenotes_source == :none
"""

### Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

```
@JuliaRegistrator register

Release notes:

## Breaking changes

- blah
```

To add them here just re-invoke and the PR will be updated.

"""
end

cbody = """
Registration pull request $msg: [$(repo)/$(pr.number)]($(pr.html_url))
$(releasenotes_note)
### Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

Expand Down
23 changes: 21 additions & 2 deletions src/commentbot/param_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ struct ProcessedParams
tree_sha::Union{Nothing, String}
cloneurl::Union{Nothing, String}
cparams::CommonParams
release_notes::Union{Nothing,String}

function ProcessedParams(rp::RequestParams)
if rp.cparams.error !== nothing
Expand All @@ -174,6 +175,10 @@ struct ProcessedParams
project = nothing
projectfile_found = false
projectfile_valid = false
changelog = nothing
changelog_found = false
changelog_valid = false
release_notes = nothing
sha = nothing
tree_sha = nothing
cloneurl = nothing
Expand All @@ -190,11 +195,25 @@ struct ProcessedParams
cloneurl, sha, err = get_cloneurl_and_sha(rp, auth)

if err === nothing && sha !== nothing
project, tree_sha, projectfile_found, projectfile_valid, err = verify_projectfile_from_sha(rp.reponame, sha; auth = auth, subdir = rp.subdir)
t = get_git_commit_tree(rp.reponame, sha; auth = auth, subdir = rp.subdir)
project, tree_sha, projectfile_found, projectfile_valid, err = verify_projectfile_from_sha(t, rp.reponame; auth = auth, subdir = rp.subdir)
if !projectfile_found
err = "File (Julia)Project.toml not found"
@debug(err)
end

# Non-critical. Don't error if fails.
changelog, changelog_found, changelog_valid, err = verify_changelog_from_sha(t, rp.reponame; auth = auth, subdir = rp.subdir)
if !changelog_found
@debug "Release notes from a CHANGELOG/NEWS/HISTORY.md file not found"
else
if haskey(changelog, project.version)
release_notes = changelog[project.version]
else
changelog_versions = keys(changelog)
@debug "No entry found in changelog for version being tagged" version changelog_versions
end
end
end

wrongtag = false
Expand All @@ -211,6 +230,6 @@ struct ProcessedParams
@debug("Event validity: $(isvalid)")

new(project, projectfile_found, projectfile_valid, sha, tree_sha, cloneurl,
CommonParams(isvalid, err, report_error))
CommonParams(isvalid, err, report_error), release_notes)
end
end
99 changes: 94 additions & 5 deletions src/commentbot/verify_projectfile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,20 @@ function pfile_hasfields(p::RegistryTools.Project)
return true, nothing
end

function verify_projectfile_from_sha(reponame, commit_sha; auth=GitHub.AnonymousAuth(), subdir = "")
project = nothing
projectfile_found = false
projectfile_valid = false
err = nothing
function get_git_commit_tree(reponame, commit_sha; auth=GitHub.AnonymousAuth(), subdir = "")
@debug("Getting gitcommit object for sha")
gcom = gitcommit(reponame, GitCommit(Dict("sha"=>commit_sha)); auth=auth)
@debug("Getting tree object for sha")
recurse = subdir != ""
t = tree(reponame, Tree(gcom.tree); auth=auth, params = Dict(:recursive => recurse))
return t
end

function verify_projectfile_from_sha(t, reponame; auth=GitHub.AnonymousAuth(), subdir = "")
project = nothing
projectfile_found = false
projectfile_valid = false
err = nothing
tree_sha = t.sha
project_files = joinpath.(subdir, Base.project_names)

Expand Down Expand Up @@ -102,3 +106,88 @@ function verify_projectfile_from_sha(reponame, commit_sha; auth=GitHub.Anonymous

return project, tree_sha, projectfile_found, projectfile_valid, err
end

function is_cfile_parseable(c::AbstractString)
@debug("Checking whether change notes file is non-empty and parseable")
if length(c) != 0
#TODO
try
parse_changelog(c)
return true, ""
catch err
return false, err
end
else
err = "Change notes file is empty"
@debug(err)
return false, err
end
end

function parse_changelog(c::AbstractString)
changelog = Dict{VersionNumber,String}()
current_version = nothing
current_changes = ""
for line in split(c, "\n")
# TODO: Make this only detect versions in headers, not in the body which it currently does
version_match = match(r"v?(\d+\.\d+)", line)
if version_match !== nothing
if current_version !== nothing
changelog[current_version] = strip(current_changes)
end
current_version = VersionNumber(version_match.match)
current_changes = ""
else
current_changes *= line * "\n"
end
end
if !isnothing(current_version)
changelog[current_version] = strip(current_changes)
end
return changelog
end

function verify_changelog_from_sha(t, reponame; auth=GitHub.AnonymousAuth(), subdir = "")
changelog = nothing
changelog_found = false
changelog_valid = false
changelog_files = joinpath.(subdir, ("CHANGELOG.md", "NEWS.md", "HISTORY.md"))
for tr in t.tree, file in changelog_files
if tr["path"] == file
changelog_found = true
@debug("Changelog file file found:", file)

@debug("Getting changelog file blob")
if isa(auth, GitHub.AnonymousAuth)
a = get_user_auth()
else
a = auth
end
b = blob(reponame, Blob(tr["sha"]); auth=a)

@debug("Decoding base64 changelog contents")
changelog_contents = decodeb64(b.content)

@debug("Checking changelog file validity")
changelog_parseable, err = is_cfile_parseable(changelog_contents)

if changelog_parseable
try
changelog = parse_changelog(changelog_contents)::Dict{VersionNumber,String}
catch ex
err = "Failed to read changelog file"
if isdefined(ex, :msg)
err = err * ": $(e.msg)"
end
@error(err)
end
if changelog isa Dict{VersionNumber,String}
changelog_valid = !isempty(changelog)
end
end
break
end
end

return changelog, changelog_found, changelog_valid, err
end