import json
import os
import re
import shutil
import sys
import time
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import List
from invoke import run, task
ARTIFACTS_DIR = Path("artifacts")
MAIN_BRANCH = "main"
def get_version():
return os.environ["VERSION"]
def erun(*args, **kwargs):
kwargs["echo"] = True
return run(*args, **kwargs)
def cerun(c, *args, **kwargs):
kwargs["echo"] = True
return c.run(*args, **kwargs)
def ask(msg: str) -> str:
print(msg, end=" ")
return input()
def is_ok(msg: str) -> bool:
answer = ask(f"{msg} (y/n)").lower()
return answer == "y"
@task(help={"skip_changelog": "Add skip-changelog label"})
def create_pr(c, skip_changelog=False):
cmd = "gh pr create --fill"
if skip_changelog:
cmd += " --label skip-changelog"
result = cerun(c, cmd, warn=True)
if not result:
sys.exit(1)
cerun(c, "gh pr merge --auto -dm")
@task
def update_version(c):
version = get_version()
path = Path("Cargo.toml")
text = path.read_text()
text, count = re.subn(
r"^version = .*", f'version = "{version}"', text, flags=re.MULTILINE
)
assert count == 0 or count == 1
path.write_text(text)
@task
def prepare_release(c):
version = get_version()
run(f"gh issue list -m {version}", pty=True)
run("gh pr list", pty=True)
if not is_ok("Continue?"):
sys.exit(1)
erun(f"git checkout {MAIN_BRANCH}")
erun("git pull")
erun("git status -s")
if not is_ok("Continue?"):
sys.exit(1)
prepare_release2(c)
@task
def prepare_release2(c):
version = get_version()
erun("git checkout -b prep-release")
update_version(c)
erun(f"changie batch {version}")
print(f"Review/edit changelog (.changes/{version}.md)")
if not is_ok("Looks good?"):
sys.exit(1)
erun("changie merge")
print("Review CHANGELOG.md")
if not is_ok("Looks good?"):
sys.exit(1)
prepare_release3(c)
@task
def prepare_release3(c):
version = get_version()
erun("cargo build")
erun("git add Cargo.toml Cargo.lock CHANGELOG.md .changes")
erun(f"git commit -m 'Prepare {version}'")
prepare_release4(c)
@task
def prepare_release4(c):
erun("cargo publish --dry-run")
erun("git push -u origin prep-release")
create_pr(c)
@task
def tag(c):
version = get_version()
erun(f"git checkout {MAIN_BRANCH}")
erun("git pull")
changes_file = Path(".changes") / f"{version}.md"
if not changes_file.exists():
print(f"{changes_file} does not exist, check previous PR has been merged")
sys.exit(1)
if not is_ok("Create tag?"):
sys.exit(1)
erun(f"git tag -a {version} -m 'Releasing version {version}'")
erun("git push")
erun("git push --tags")
def get_artifact_list() -> List[Path]:
assert ARTIFACTS_DIR.exists()
return list(ARTIFACTS_DIR.rglob("*.tar.gz")) + list(ARTIFACTS_DIR.rglob("*.zip"))
@task
def wait_for_tag_run(c) -> int:
version = get_version()
while True:
proc = run(f"gh run list -b {version} --json databaseId,status")
results = json.loads(proc.stdout)
assert isinstance(results, list)
assert len(results) == 1
run_id = results[0]["databaseId"]
status = results[0]["status"]
print(f"tag run ID: {run_id}, status: {status}")
if status == "completed":
return run_id
print("Waiting 30s")
time.sleep(30)
@task
def download_artifacts(c):
run_id = wait_for_tag_run(c)
if ARTIFACTS_DIR.exists():
shutil.rmtree(ARTIFACTS_DIR)
ARTIFACTS_DIR.mkdir()
erun(f"gh run download {run_id} --dir {ARTIFACTS_DIR}", pty=True)
def prepare_release_notes(version_md: Path) -> str:
content = re.sub("^## .*", "", version_md.read_text())
content = re.sub("^### ", "## ", content, flags=re.MULTILINE)
return content.strip() + "\n"
@task
def publish(c):
version = get_version()
files_str = " ".join(str(x) for x in get_artifact_list())
print("Creating GitHub release")
with NamedTemporaryFile() as tmp_file:
content = prepare_release_notes(Path(".changes") / f"{version}.md")
tmp_file.write(content.encode("utf-8"))
tmp_file.flush()
erun(f"gh release create {version} -F{tmp_file.name} {files_str}")
print("Publishing on crates.io")
erun("cargo publish")
@task
def update_store(c):
version = get_version()
with c.cd("../clyde-store"):
cerun(c, "git checkout main")
cerun(c, "git pull")
cerun(c, "git checkout -b update-clyde")
cerun(c, "clydetools fetch clyde.yaml", pty=True)
cerun(c, "git add clyde.yaml")
cerun(c, f"git commit -m 'Update clyde to {version}'")
cerun(c, "git push -u origin update-clyde")
create_pr(c)