mirror of
https://github.com/fmtlib/fmt.git
synced 2026-06-15 08:26:13 +08:00
210 lines
5.9 KiB
Python
Executable File
210 lines
5.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Make a release.
|
|
|
|
Usage:
|
|
release.py [<branch>]
|
|
|
|
For the release command $FMT_TOKEN should contain a GitHub personal access token
|
|
obtained from https://github.com/settings/tokens.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import datetime
|
|
import errno
|
|
import fileinput
|
|
import json
|
|
import os
|
|
import re
|
|
import shutil
|
|
import sys
|
|
import urllib.request
|
|
from subprocess import check_call
|
|
|
|
import docopt
|
|
|
|
|
|
class Git:
|
|
def __init__(self, dir):
|
|
self.dir = dir
|
|
|
|
def call(self, method, args, **kwargs):
|
|
return check_call(["git", method] + list(args), **kwargs)
|
|
|
|
def add(self, *args):
|
|
return self.call("add", args, cwd=self.dir)
|
|
|
|
def checkout(self, *args):
|
|
return self.call("checkout", args, cwd=self.dir)
|
|
|
|
def clean(self, *args):
|
|
return self.call("clean", args, cwd=self.dir)
|
|
|
|
def clone(self, *args):
|
|
return self.call("clone", list(args) + [self.dir])
|
|
|
|
def commit(self, *args):
|
|
return self.call("commit", args, cwd=self.dir)
|
|
|
|
def pull(self, *args):
|
|
return self.call("pull", args, cwd=self.dir)
|
|
|
|
def push(self, *args):
|
|
return self.call("push", args, cwd=self.dir)
|
|
|
|
def reset(self, *args):
|
|
return self.call("reset", args, cwd=self.dir)
|
|
|
|
def update(self, *args):
|
|
clone = not os.path.exists(self.dir)
|
|
if clone:
|
|
self.clone(*args)
|
|
return clone
|
|
|
|
|
|
def clean_checkout(repo, branch):
|
|
repo.clean("-f", "-d")
|
|
repo.reset("--hard")
|
|
repo.checkout(branch)
|
|
|
|
|
|
class Runner:
|
|
def __init__(self, cwd):
|
|
self.cwd = cwd
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
kwargs["cwd"] = kwargs.get("cwd", self.cwd)
|
|
check_call(args, **kwargs)
|
|
|
|
|
|
def create_build_env():
|
|
"""Create a build environment."""
|
|
|
|
class Env:
|
|
pass
|
|
|
|
env = Env()
|
|
env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
env.build_dir = "build"
|
|
env.fmt_repo = Git(os.path.join(env.build_dir, "fmt"))
|
|
return env
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = docopt.docopt(__doc__)
|
|
env = create_build_env()
|
|
fmt_repo = env.fmt_repo
|
|
|
|
branch = args.get("<branch>")
|
|
if branch is None:
|
|
branch = "master"
|
|
if not fmt_repo.update("-b", branch, "git@github.com:fmtlib/fmt"):
|
|
clean_checkout(fmt_repo, branch)
|
|
|
|
# Update the date in the changelog and extract the version and the first
|
|
# section content.
|
|
changelog = "ChangeLog.md"
|
|
changelog_path = os.path.join(fmt_repo.dir, changelog)
|
|
is_first_section = True
|
|
first_section = []
|
|
for i, line in enumerate(fileinput.input(changelog_path, inplace=True)):
|
|
if i == 0:
|
|
version = re.match(r"# (.*) - TBD", line).group(1)
|
|
line = "# {} - {}\n".format(version, datetime.date.today().isoformat())
|
|
elif not is_first_section:
|
|
pass
|
|
elif line.startswith("#"):
|
|
is_first_section = False
|
|
else:
|
|
first_section.append(line)
|
|
sys.stdout.write(line)
|
|
if first_section[0] == "\n":
|
|
first_section.pop(0)
|
|
|
|
ns_version = None
|
|
base_h_path = os.path.join(fmt_repo.dir, "include", "fmt", "base.h")
|
|
for line in fileinput.input(base_h_path):
|
|
m = re.match(r"\s*inline namespace v(.*) .*", line)
|
|
if m:
|
|
ns_version = m.group(1)
|
|
break
|
|
major_version = version.split(".")[0]
|
|
if not ns_version or ns_version != major_version:
|
|
raise Exception(f"Version mismatch {ns_version} != {major_version}")
|
|
|
|
# Workaround GitHub-flavored Markdown treating newlines as <br>.
|
|
changes = ""
|
|
code_block = False
|
|
stripped = False
|
|
for line in first_section:
|
|
if re.match(r"^\s*```", line):
|
|
code_block = not code_block
|
|
changes += line
|
|
stripped = False
|
|
continue
|
|
if code_block:
|
|
changes += line
|
|
continue
|
|
if line == "\n" or re.match(r"^\s*\|.*", line):
|
|
if stripped:
|
|
changes += "\n"
|
|
stripped = False
|
|
changes += line
|
|
continue
|
|
if stripped:
|
|
line = " " + line.lstrip()
|
|
changes += line.rstrip()
|
|
stripped = True
|
|
|
|
fmt_repo.checkout("-B", "release")
|
|
fmt_repo.add(changelog)
|
|
fmt_repo.commit("-m", "Update version")
|
|
|
|
# Build the docs and package.
|
|
run = Runner(fmt_repo.dir)
|
|
run("cmake", ".")
|
|
run("make", "doc", "package_source")
|
|
|
|
# Create a release on GitHub.
|
|
fmt_repo.push("origin", "release")
|
|
auth_headers = {"Authorization": "token " + os.getenv("FMT_TOKEN")}
|
|
req = urllib.request.Request(
|
|
"https://api.github.com/repos/fmtlib/fmt/releases",
|
|
data=json.dumps(
|
|
{
|
|
"tag_name": version,
|
|
"target_commitish": "release",
|
|
"body": changes,
|
|
"draft": True,
|
|
}
|
|
).encode("utf-8"),
|
|
headers=auth_headers,
|
|
method="POST",
|
|
)
|
|
with urllib.request.urlopen(req) as response:
|
|
if response.status != 201:
|
|
raise Exception(
|
|
f"Failed to create a release " + "{response.status} {response.reason}"
|
|
)
|
|
response_data = json.loads(response.read().decode("utf-8"))
|
|
id = response_data["id"]
|
|
|
|
# Upload the package.
|
|
uploads_url = "https://uploads.github.com/repos/fmtlib/fmt/releases"
|
|
package = "fmt-{}.zip".format(version)
|
|
req = urllib.request.Request(
|
|
f"{uploads_url}/{id}/assets?name={package}",
|
|
headers={"Content-Type": "application/zip"} | auth_headers,
|
|
data=open("build/fmt/" + package, "rb").read(),
|
|
method="POST",
|
|
)
|
|
with urllib.request.urlopen(req) as response:
|
|
if response.status != 201:
|
|
raise Exception(
|
|
f"Failed to upload an asset " "{response.status} {response.reason}"
|
|
)
|
|
|
|
short_version = ".".join(version.split(".")[:-1])
|
|
check_call(["./mkdocs", "deploy", short_version])
|