Skip to content

Commit

Permalink
Add capability to gather the commit log since the last release and us…
Browse files Browse the repository at this point in the history
…e that to start the release notes.

Warning: This is experimental.

- Add a git toolchain
  - It gets the path to the git binary and the path to your source tree
  - therefore it can only be used locally.
  - Fixes bazelbuild#257
- Add a `git_changelog()` rule that will run git to get the change log.
  - Advances bazelbuild#228
- Change print_relnotes to accept a changelog file, instead of running git locally.

Next steps:
- Add the capability to reduce the change log from everything to only
  the commits that have RELNOTES sections.
  • Loading branch information
aiuto committed May 28, 2021
1 parent e24fbdd commit 2bd5077
Show file tree
Hide file tree
Showing 11 changed files with 474 additions and 7 deletions.
8 changes: 8 additions & 0 deletions pkg/WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ find_system_rpmbuild(
verbose = True,
)

# Needed for making our release notes
load("@rules_pkg//toolchains/git:git_configure.bzl", "experimental_find_system_git")

experimental_find_system_git(
name = "rules_pkg_git",
verbose = True,
)

http_archive(
name = "bazel_stardoc",
sha256 = "36b8d6c2260068b9ff82faea2f7add164bf3436eac9ba3ec14809f335346d66a",
Expand Down
9 changes: 8 additions & 1 deletion pkg/distro/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
load("//:pkg.bzl", "pkg_tar")
load("//:version.bzl", "version")
load("//releasing:defs.bzl", "print_rel_notes")
load("//releasing:git.bzl", "git_changelog")
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@bazel_stardoc//stardoc:stardoc.bzl", "stardoc")
load("@rules_python//python:defs.bzl", "py_test")
Expand Down Expand Up @@ -59,13 +60,19 @@ genrule(
print_rel_notes(
name = "relnotes",
outs = ["relnotes.txt"],
changelog = ":changelog",
deps_method = "rules_pkg_dependencies",
mirror_host = "mirror.bazel.build",
org = "bazelbuild",
repo = "rules_pkg",
version = version,
)

git_changelog(
name = "changelog",
out = "changelog.txt",
)

py_test(
name = "packaging_test",
size = "large",
Expand Down Expand Up @@ -98,13 +105,13 @@ genrule(
bzl_library(
name = "rules_pkg_lib",
srcs = [
"//private:util.bzl",
"//:package_variables.bzl",
"//:path.bzl",
"//:pkg.bzl",
"//:providers.bzl",
"//:rpm.bzl",
"//:version.bzl",
"//private:util.bzl",
],
visibility = ["//visibility:private"],
)
Expand Down
14 changes: 13 additions & 1 deletion pkg/releasing/BUILD
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")
load("git.bzl", "git_changelog")

licenses(["notice"])

package(
default_visibility = ["//visibility:public"],
)

# WARNING: 2019-08-05. This is experimental and subject to change.
# WARNING: 2021-03-05. This is experimental and subject to change.

# Sample usage:
# load("@rules_pkg//releasing:defs.bzl", "print_rel_notes")
Expand Down Expand Up @@ -55,3 +56,14 @@ py_test(
":release_utils",
],
)

# This is an internal tool. Use at your own risk.
py_binary(
name = "git_changelog_private",
srcs = [
"git_changelog_private.py",
],
srcs_version = "PY3",
# TODO(https://github.com/bazelbuild/bazel/issues/7377): Make this private.
visibility = ["//visibility:public"],
)
10 changes: 7 additions & 3 deletions pkg/releasing/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def print_rel_notes(
deps_method = "",
toolchains_method = "",
org = "bazelbuild",
changelog = None,
mirror_host = None):
tarball_name = ":%s-%s.tar.gz" % (repo, version)
cmd = [
Expand All @@ -21,9 +22,11 @@ def print_rel_notes(
if deps_method:
cmd.append("--deps_method=%s" % deps_method)
if toolchains_method:
cmd.append("--toolchains_method=%s" % toolchains_method)
cmd.append("--toolchains_method=%s" % toolchains_method)
if changelog:
cmd.append("--changelog=$(location %s)" % changelog)
if mirror_host:
cmd.append("--mirror_host=%s" % mirror_host)
cmd.append("--mirror_host=%s" % mirror_host)
cmd.append(">$@")
native.genrule(
name = "relnotes",
Expand All @@ -34,5 +37,6 @@ def print_rel_notes(
cmd = " ".join(cmd),
tools = [
"//releasing:print_rel_notes",
],
] + [changelog],
local = True,
)
111 changes: 111 additions & 0 deletions pkg/releasing/git.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright 2021 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A rule to extract the git changelog."""

def _git_changelog_impl(ctx):
"""Implements to git_changelog rule."""

args = ctx.actions.args()
tools = []

toolchain = ctx.toolchains["@rules_pkg//toolchains/git:git_toolchain_type"].git
if not toolchain.valid:
fail("The git_toolchain is not properly configured: " +
toolchain.name)
if toolchain.path:
args.add("--git_path", toolchain.path)
else:
executable = toolchain.label.files_to_run.executable
tools += [executable]
tools += toolchain.label.default_runfiles.files.to_list()
args.add("--git_path", executable.path)
args.add("--git_root", toolchain.workspace_top)
args.add("--from_ref", ctx.attr.from_ref)
args.add("--to_ref", ctx.attr.to_ref)
args.add("--out", ctx.outputs.out.path)
if ctx.attr.verbose:
args.add("--verbose")

# TODO(aiuto): Remove this when we graduate from experimental
# Here is the place where we would try to get the absolute path to self.
# What we need is a method to return a value of type path, but everything
# gives us strings.
# print(dir(ctx.file.self))
# print(dir(ctx.file.self.root))
# print(type(ctx.file.self.path))
# print(ctx.file.self.path)
# rctx has path(), but ctx does not :-(
# print(ctx.path(ctx.attr.self.label).dirname)

ctx.actions.run(
mnemonic = "GitChangelog",
executable = ctx.executable._git_changelog,
use_default_shell_env = True,
arguments = [args],
outputs = [ctx.outputs.out],
env = {
"LANG": "en_US.UTF-8",
"LC_CTYPE": "UTF-8",
"PYTHONIOENCODING": "UTF-8",
"PYTHONUTF8": "1",
},
execution_requirements = {
"local": "1",
"no-sandbox": "1",
"no-remote": "1",
},
tools = tools,
)

# Define the rule.
_git_changelog = rule(
doc = "Extracts the git changelog between two tags.",
attrs = {
"from_ref": attr.string(
doc = "lower commit tag. The default is to use the latest tag",
default = "_auto_",
),
"to_ref": attr.string(
doc = "upper commit tag. The default is HEAD",
default = "HEAD",
),
"out": attr.output(mandatory = True),
"verbose": attr.bool(
doc = "Be verbose",
default = False,
),
"_git_changelog": attr.label(
default = Label("//releasing:git_changelog_private"),
cfg = "host",
executable = True,
allow_files = True,
),
# TODO(aiuto): Remove this when we graduate from experimental
# This does not work.
#"self": attr.label(
# # default = Label("//releasing:git.bzl"),
# cfg = "host",
# allow_single_file = True,
#),
},
executable = False,
implementation = _git_changelog_impl,
toolchains = ["@rules_pkg//toolchains/git:git_toolchain_type"],
)

def git_changelog(name, **kwargs):
_git_changelog(
name = name,
**kwargs
)
69 changes: 69 additions & 0 deletions pkg/releasing/git_changelog_private.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2021 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Utilities to extract git commit descriptions in useful ways."""

import argparse
import os
import subprocess
import sys


def guess_previous_release_tag(git_path, pattern=None):
most_recent = None
cmd = [git_path, 'tag']
if pattern:
cmd.extend(['--list', pattern])
with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
most_recent = proc.stdout.read().decode('utf-8')
most_recent = most_recent.strip().replace('\n\n', '\n').split('\n')[-1]
return most_recent


def git_changelog(from_ref='_auto_', to_ref='HEAD', git_path=None):
assert from_ref
assert to_ref
assert git_path
cmd = [git_path, 'log', '%s..%s' % (from_ref, to_ref)]
with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
return proc.stdout.read().decode('utf-8')


def main():
parser = argparse.ArgumentParser(
description='Helper for extracting git changelog',
fromfile_prefix_chars='@')
parser.add_argument('--git_path', required=True, help='path to git binary')
parser.add_argument('--git_root', required=True, help='path to git workspace')
parser.add_argument('--out', required=True, help='output path')
parser.add_argument('--from_ref', help='from REF')
parser.add_argument('--to_ref', help='to REF')
parser.add_argument('--verbose', action='store_true')

options = parser.parse_args()

with open(options.out, 'w', encoding='utf-8') as out:
os.chdir(options.git_root)
from_ref = options.from_ref
if not from_ref or from_ref == '_auto_':
from_ref = guess_previous_release_tag(options.git_path)
to_ref = options.to_ref or 'HEAD'
if options.verbose:
print('Getting changelog from %s to %s' % (from_ref, to_ref))
changelog = git_changelog(
from_ref=from_ref, to_ref=to_ref, git_path=options.git_path)
out.write(changelog)
return 0

if __name__ == '__main__':
main()
19 changes: 17 additions & 2 deletions pkg/releasing/print_rel_notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@


def print_notes(org, repo, version, tarball_path, mirror_host=None,
deps_method=None, setup_file=None, toolchains_method=None):
deps_method=None, setup_file=None, toolchains_method=None,
changelog=''):
file_name = release_tools.package_basename(repo, version)
sha256 = release_tools.get_package_sha256(tarball_path)

Expand All @@ -42,6 +43,9 @@ def print_notes(org, repo, version, tarball_path, mirror_host=None,
**Incompatible Changes**
**Change Log**
${changelog}
**WORKSPACE setup**
```
Expand All @@ -55,6 +59,7 @@ def print_notes(org, repo, version, tarball_path, mirror_host=None,
""").strip())
print(relnotes_template.substitute({
'changelog': changelog,
'org': org,
'repo': repo,
'version': version,
Expand Down Expand Up @@ -99,7 +104,7 @@ def main():
required=True, help='path to release tarball')
parser.add_argument(
'--mirror_host', default=None,
help='If provider, the hostname of a mirror for the download url.')
help='If provider, the hostname of a mirror for the download url')
parser.add_argument(
'--setup_file', default=None,
help='Alternate name for setup file. Default: deps.bzl')
Expand All @@ -109,10 +114,20 @@ def main():
parser.add_argument(
'--toolchains_method', default=None,
help='Alternate name for toolchains method. Default: {repo}_toolchains')
parser.add_argument(
'--changelog', default=None,
help='Pre-fill release notes with changes from this file')

options = parser.parse_args()
if options.changelog:
with open(options.changelog, 'r', encoding='utf-8') as f:
changelog = f.read()
else:
changelog = 'TBD'

print_notes(options.org, options.repo, options.version, options.tarball_path,
deps_method=options.deps_method,
changelog=changelog,
mirror_host=options.mirror_host,
setup_file=options.setup_file,
toolchains_method=options.toolchains_method)
Expand Down
Loading

0 comments on commit 2bd5077

Please sign in to comment.