Skip to content

Commit

Permalink
feat: Check the PR milestone matches the upcoming draft release.
Browse files Browse the repository at this point in the history
When the PR is milestoned, we also check, but don't have access to the
draft release. Instead, we check against the local version, which we
check against the draft release in another check run.
  • Loading branch information
iphydf committed Nov 10, 2024
1 parent 97066a4 commit cb7bff5
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 52 deletions.
1 change: 1 addition & 0 deletions .github/workflows/check-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ jobs:
- name: Check version against GitHub releases
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_MILESTONE: ${{ github.event.pull_request.milestone.title }}
run: $GITHUB_WORKSPACE/ci-tools/bin/check_release
12 changes: 12 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: checks

on:
pull_request:
branches: [master]
types: [opened, reopened, synchronize, milestoned]
pull_request_target:
branches: [master]

jobs:
checks:
uses: ./.github/workflows/check-release.yml
5 changes: 5 additions & 0 deletions .restyled.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
restylers:
- pyment:
enabled: false
- "*"
39 changes: 0 additions & 39 deletions .travis.yml

This file was deleted.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Continuous integration tool repository

Version: 0.8.1

This repository contains tools for working with the TokTok Haskell
repositories. Its purpose is mainly to centralise the costly building of
external tools like `hlint` and `stylish-haskell`, which otherwise each Travis
Expand Down
83 changes: 70 additions & 13 deletions bin/check_release
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/env python3
import ast
import os
import re
import subprocess
import sys
from typing import cast
from typing import List
from typing import Optional

import requests

Expand All @@ -19,7 +21,7 @@ def github_repo() -> str:
capture_output=True).stdout.decode("utf-8").strip().split(":")[1])


def release_github() -> str:
def release_github() -> Optional[str]:
resp = requests.get(
f"{os.environ['GITHUB_API_URL']}/repos/{github_repo()}/releases",
auth=("", os.environ["GITHUB_TOKEN"]),
Expand All @@ -28,19 +30,40 @@ def release_github() -> str:
releases = resp.json()
if not releases:
print("WARNING: GitHub API produced empty response.")
print("WARNING: Skipping this check.")
sys.exit(0)
print("WARNING: Aborting.")
sys.exit(1)

release = releases[0]
if not release["draft"]:
print("WARNING: Could not find the latest draft release.")
print("WARNING: Skipping this check.")
print("WARNING: Skipping the draft release check.")
print(f"Latest release found was {release['name']}")
sys.exit(0)
return cast(str, release["name"][1:])
return None

name = cast(str, release["name"])
if not name.startswith("v"):
print(f"WARNING: GitHub release {name} does not start with 'v'.")
print("WARNING: Aborting.")
sys.exit(1)
return name[1:] # Remove the 'v' prefix.


def release_milestone() -> str:
if "PR_MILESTONE" not in os.environ:
print(
"WARNING: Could not find the milestone in the PR_MIESTONE environment variable."
)
print("WARNING: Skipping this check.")
sys.exit(1)
version = os.environ["PR_MILESTONE"]
if not version.startswith("v"):
print(f"WARNING: Milestone {version} does not start with 'v'.")
print("WARNING: Aborting.")
sys.exit(1)
return version[1:] # Remove the 'v' prefix.


def release_bazel(path: str) -> str:
def release_local(path: str) -> tuple[str, str]:
with open(os.path.join(path, "BUILD.bazel"), "r") as fh:
bzl = ast.parse(fh.read(), filename=path)
for stmt in bzl.body:
Expand All @@ -52,23 +75,57 @@ def release_bazel(path: str) -> str:
if (arg.arg == "version"
and isinstance(arg.value, ast.Constant)
and isinstance(arg.value.s, str)):
return arg.value.s
return arg.value.s, "BUILD.bazel"

# Check if configure.ac exists.
if os.path.exists(os.path.join(path, "configure.ac")):
with open(os.path.join(path, "configure.ac"), "r") as fh:
for line in fh:
# AC_INIT([tox], [0.2.19]) -> 0.2.19
res = re.match(r"AC_INIT\(\[.*\], \[(.*)\]\)", line)
if res:
return res.group(1), "configure.ac"

# Check if README.md contains "Version: x.y.z".
if os.path.exists(os.path.join(path, "README.md")):
with open(os.path.join(path, "README.md"), "r") as fh:
for line in fh:
res = re.match(r"Version: (.*)", line)
if res:
return res.group(1), "README.md"

raise Exception(f"Could not find a haskell_library.version in {path}")
raise Exception(f"Could not find a version in {path}")


def main(prog: str, args: List[str]) -> None:
path = args[0] if args else "."

gh_release = release_github()
bzl_release = release_bazel(path)
ms_release = release_milestone()
local_release, local_origin = release_local(path)

print(f"GitHub release: {gh_release}")
print(f"Milestone release: {ms_release}")
print(f"Local release: {local_release} ({local_origin})")

if gh_release is None:
# Default to the milestone version if we can't read the draft release
# version. This happens when we call the workflow from a pull_request
# event as opposed to a pull_request_target event.
gh_release = ms_release

if gh_release == bzl_release:
if gh_release == local_release and gh_release == ms_release:
print(f"PASS: Upcoming release version is {gh_release}")
else:
elif gh_release != local_release:
print(f"FAIL: GitHub draft release {gh_release} does not match "
f"{local_origin} {local_release}")
sys.exit(1)
elif gh_release != ms_release:
print(f"FAIL: GitHub draft release {gh_release} does not match "
f"BUILD.bazel {bzl_release}")
f"milestone {ms_release}")
sys.exit(1)
else:
raise Exception("This should not happen.")


if __name__ == "__main__":
Expand Down

0 comments on commit cb7bff5

Please sign in to comment.