Skip to content
This repository has been archived by the owner on Jun 23, 2020. It is now read-only.

Commit

Permalink
Use pushes to perform releases (#36)
Browse files Browse the repository at this point in the history
For security reasons, GH Actions will not allow commits from a PR event; they don't differentiate between a closed PR and a merged one, so no way to know if the PR was accepted.
  • Loading branch information
brettcannon authored May 16, 2020
1 parent 9bb1b4c commit 7ddc560
Show file tree
Hide file tree
Showing 14 changed files with 719 additions and 541 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on: pull_request

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-python@v2
with:
python-version: '3.8'

- name: 'Install Poetry'
run: |
python -m pip install poetry
poetry config virtualenvs.create false
- run: poetry install

- run: pytest tests

- run: mypy --ignore-missing-imports release_often/

- run: black --check .
14 changes: 5 additions & 9 deletions .github/workflows/main.yml → .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
# This is a basic workflow to help you get started with Actions

name: CI
name: Release

on:
pull_request:
types: [opened, synchronize, reopened, closed]
push:
branches:
- master

jobs:
test:
if: github.event.action != 'closed' || github.event.pull_request.merged

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-python@v1
- uses: actions/setup-python@v2
with:
python-version: '3.8'

Expand All @@ -35,7 +32,6 @@ jobs:

release:
needs: [test]
if: github.event_name == 'pull_request' && github.ref == 'master' && github.event.action == 'closed' && github.event.pull_request.merged

runs-on: ubuntu-latest

Expand Down
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,45 @@ Do note that this action is not designed to work for all projects. This action i
6. Create a release on GitHub

## Caveats
Due to the fact that the action commits back to the repository you cannot have required status checks on PRs as that prevents direct commits from non-admins.
Due to the fact that this action commits back to the repository, you cannot have srequired status checks on PRs as that prevents direct commits from non-admins.

## Action instructions
### Configuration example
```YAML
name: Release

on:
push:
branches: [ master ]
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- master

jobs:
test:
if: github.event.action != 'closed' || github.event.pull_request.merged

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-python@v1
- uses: actions/setup-python@v2
with:
python-version: '3.8'

# ... more steps for testing.

lint:
if: github.event.action != 'closed' || github.event.pull_request.merged

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-python@v1
- uses: actions/setup-python@v2
with:
python-version: '3.8'

# ... more steps for linting.

release:
needs: [test, lint]
if: github.event_name == 'pull_request' && github.ref == 'master' && github.event.action == 'closed' && github.event.pull_request.merged

runs-on: ubuntu-latest

Expand Down
19 changes: 18 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ twine = "^3.1.1"
black = "^19.10b0"
mypy = "^0.770"
pytest = "^5.4.2"
pytest-asyncio = "^0.12.0"

[build-system]
requires = ["poetry>=0.12"]
Expand Down
60 changes: 42 additions & 18 deletions release_often/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import argparse
import os
import pathlib
import re
import subprocess
import sys

Expand All @@ -15,6 +16,9 @@
from . import version


PR_NUM = re.compile(r"\(#(?P<number>\d+)\)")


def error(message):
gidgethub.actions.command("error", message)
sys.exit(1)
Expand All @@ -28,7 +32,16 @@ def parse_args():
return parser.parse_args()


def update_version():
async def matching_pr(gh, push_event):
pr_number_match = PR_NUM.search(push_event["commits"][0]["message"])
if not pr_number_match:
return None
pr_number = pr_number_match.group("number")
pr_url_template = push_event["repository"]["pulls_url"]
return await gh.getitem(pr_url_template, {"number": pr_number})


def update_version(pr_event):
build_tool, version_file = version.find_details(gidgethub.actions.workspace())
if build_tool is None:
error("build tool not detected; unable to update version")
Expand All @@ -39,7 +52,7 @@ def update_version():
file_contents = version_file.read_text(encoding="utf-8")
current_version = build_tool.read_version(file_contents)
gidgethub.actions.command("debug", f"Current/old version is {current_version}")
new_version = version.bump_by_label(gidgethub.actions.event(), current_version)
new_version = version.bump_by_label(pr_event, current_version)
gidgethub.actions.command("debug", f"New version is {new_version}")
try:
new_contents = build_tool.change_version(
Expand All @@ -52,13 +65,14 @@ def update_version():
return new_version


def update_changelog(path, new_version):
def update_changelog(path, new_version, pr_event):
if not path.exists():
error(f"The path to the changelog does not exist: {path}")
gidgethub.actions.command("debug", f"Changelog file path is {path}")
current_changelog = path.read_text(encoding="utf-8")
event = gidgethub.actions.event()
new_changelog = changelog.update(current_changelog, path.suffix, new_version, event)
new_changelog = changelog.update(
current_changelog, path.suffix, new_version, pr_event
)
path.write_text(new_changelog, encoding="utf-8")
return event["pull_request"]["title"]

Expand Down Expand Up @@ -109,23 +123,33 @@ async def create_release(gh, version, changelog_entry):

async def main():
args = parse_args()
new_version = update_version()
if new_version is None:
gidgethub.actions.command("debug", "No version update requested")
sys.exit()
changelog_entry = update_changelog(pathlib.Path(args.changelog_path), new_version)
output_dir = build()
commit(new_version)
if args.pypi_token != "-":
upload(output_dir, args.pypi_token)
else:
gidgethub.actions.command(
"debug", "PyPI uploading skipped; no API token provided"
)
async with httpx.AsyncClient() as client:
gh = gidgethub.httpx.GitHubAPI(
client, "brettcannon/release_often", oauth_token=args.github_token
)

push_event = gidgethub.actions.event()
pr_event = await matching_pr(gh, push_event)
if not pr_event:
gidgethub.actions.command(
"debug", "No pull request number found in first commit's title"
)
sys.exit()
new_version = update_version(pr_event)
if new_version is None:
gidgethub.actions.command("debug", "No version update requested")
sys.exit()
changelog_entry = update_changelog(
pathlib.Path(args.changelog_path), new_version, pr_event
)
output_dir = build()
commit(new_version)
if args.pypi_token != "-":
upload(output_dir, args.pypi_token)
else:
gidgethub.actions.command(
"debug", "PyPI uploading skipped; no API token provided"
)
await create_release(gh, new_version, changelog_entry)


Expand Down
11 changes: 5 additions & 6 deletions release_often/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@ def update(current_changelog, path_extension, version, pr_event):
header, entry_template = TEMPLATES[path_extension.lower()]
if current_changelog.strip() and not current_changelog.startswith(header):
raise ValueError("Changelog has a non-standard header")
pull_request = pr_event["pull_request"]
details = {
"version": version,
"pr_number": pull_request["number"],
"pr_url": pull_request["html_url"],
"summary": pull_request["title"],
"committer": pull_request["user"]["login"],
"committer_url": pull_request["user"]["html_url"],
"pr_number": pr_event["number"],
"pr_url": pr_event["html_url"],
"summary": pr_event["title"],
"committer": pr_event["user"]["login"],
"committer_url": pr_event["user"]["html_url"],
}
entry = entry_template.format_map(details)
changelog_no_header = current_changelog[len(header) :]
Expand Down
4 changes: 2 additions & 2 deletions release_often/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ def find_details(directory):
return None, None


def bump_by_label(event, old_version):
def bump_by_label(pr_event, old_version):
"""Calculate the new version based on the pull request event."""
for label in event["pull_request"]["labels"]:
for label in pr_event["labels"]:
label_name = label["name"]
try:
level = BumpLevel(label_name)
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ def poetry_example_path(data_path):

@pytest.fixture
def pr_event(data_path):
path = data_path / "merged_PR.json"
path = data_path / "PR.json"
return json.loads(path.read_text(encoding="utf-8"))
Loading

0 comments on commit 7ddc560

Please sign in to comment.