mirror of
				https://github.com/cookiecutter/cookiecutter-django.git
				synced 2025-11-04 09:57:30 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			164 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import datetime as dt
 | 
						|
import os
 | 
						|
import re
 | 
						|
from collections.abc import Iterable
 | 
						|
from pathlib import Path
 | 
						|
 | 
						|
import git
 | 
						|
import github.PullRequest
 | 
						|
import github.Repository
 | 
						|
from github import Github
 | 
						|
from jinja2 import Template
 | 
						|
 | 
						|
CURRENT_FILE = Path(__file__)
 | 
						|
ROOT = CURRENT_FILE.parents[1]
 | 
						|
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
 | 
						|
GITHUB_REPO = os.getenv("GITHUB_REPOSITORY")
 | 
						|
GIT_BRANCH = os.getenv("GITHUB_REF_NAME")
 | 
						|
 | 
						|
 | 
						|
def main() -> None:
 | 
						|
    """
 | 
						|
    Script entry point.
 | 
						|
    """
 | 
						|
    # Generate changelog for PRs merged yesterday
 | 
						|
    merged_date = dt.date.today() - dt.timedelta(days=1)
 | 
						|
    repo = Github(login_or_token=GITHUB_TOKEN).get_repo(GITHUB_REPO)
 | 
						|
    merged_pulls = list(iter_pulls(repo, merged_date))
 | 
						|
    print(f"Merged pull requests: {merged_pulls}")
 | 
						|
    if not merged_pulls:
 | 
						|
        print("Nothing was merged, existing.")
 | 
						|
        return
 | 
						|
 | 
						|
    # Group pull requests by type of change
 | 
						|
    grouped_pulls = group_pulls_by_change_type(merged_pulls)
 | 
						|
    if not any(grouped_pulls.values()):
 | 
						|
        print("Pull requests merged aren't worth a changelog mention.")
 | 
						|
        return
 | 
						|
 | 
						|
    # Generate portion of markdown
 | 
						|
    release_changes_summary = generate_md(grouped_pulls)
 | 
						|
    print(f"Summary of changes: {release_changes_summary}")
 | 
						|
 | 
						|
    # Update CHANGELOG.md file
 | 
						|
    release = f"{merged_date:%Y.%m.%d}"
 | 
						|
    changelog_path = ROOT / "CHANGELOG.md"
 | 
						|
    write_changelog(changelog_path, release, release_changes_summary)
 | 
						|
    print(f"Wrote {changelog_path}")
 | 
						|
 | 
						|
    # Update version
 | 
						|
    setup_py_path = ROOT / "setup.py"
 | 
						|
    update_version(setup_py_path, release)
 | 
						|
    print(f"Updated version in {setup_py_path}")
 | 
						|
 | 
						|
    # Commit changes, create tag and push
 | 
						|
    update_git_repo([changelog_path, setup_py_path], release)
 | 
						|
 | 
						|
    # Create GitHub release
 | 
						|
    github_release = repo.create_git_release(
 | 
						|
        tag=release,
 | 
						|
        name=release,
 | 
						|
        message=release_changes_summary,
 | 
						|
    )
 | 
						|
    print(f"Created release on GitHub {github_release}")
 | 
						|
 | 
						|
 | 
						|
def iter_pulls(
 | 
						|
    repo: github.Repository.Repository,
 | 
						|
    merged_date: dt.date,
 | 
						|
) -> Iterable[github.PullRequest.PullRequest]:
 | 
						|
    """Fetch merged pull requests at the date we're interested in."""
 | 
						|
    recent_pulls = repo.get_pulls(
 | 
						|
        state="closed",
 | 
						|
        sort="updated",
 | 
						|
        direction="desc",
 | 
						|
    ).get_page(0)
 | 
						|
    for pull in recent_pulls:
 | 
						|
        if pull.merged and pull.merged_at.date() == merged_date:
 | 
						|
            yield pull
 | 
						|
 | 
						|
 | 
						|
def group_pulls_by_change_type(
 | 
						|
    pull_requests_list: list[github.PullRequest.PullRequest],
 | 
						|
) -> dict[str, list[github.PullRequest.PullRequest]]:
 | 
						|
    """Group pull request by change type."""
 | 
						|
    grouped_pulls = {
 | 
						|
        "Changed": [],
 | 
						|
        "Fixed": [],
 | 
						|
        "Documentation": [],
 | 
						|
        "Updated": [],
 | 
						|
    }
 | 
						|
    for pull in pull_requests_list:
 | 
						|
        label_names = {label.name for label in pull.labels}
 | 
						|
        if "project infrastructure" in label_names:
 | 
						|
            # Don't mention it in the changelog
 | 
						|
            continue
 | 
						|
        if "update" in label_names:
 | 
						|
            group_name = "Updated"
 | 
						|
        elif "bug" in label_names:
 | 
						|
            group_name = "Fixed"
 | 
						|
        elif "docs" in label_names:
 | 
						|
            group_name = "Documentation"
 | 
						|
        else:
 | 
						|
            group_name = "Changed"
 | 
						|
        grouped_pulls[group_name].append(pull)
 | 
						|
    return grouped_pulls
 | 
						|
 | 
						|
 | 
						|
def generate_md(grouped_pulls: dict[str, list[github.PullRequest.PullRequest]]) -> str:
 | 
						|
    """Generate markdown file from Jinja template."""
 | 
						|
    changelog_template = ROOT / ".github" / "changelog-template.md"
 | 
						|
    template = Template(changelog_template.read_text(), autoescape=True)
 | 
						|
    return template.render(grouped_pulls=grouped_pulls)
 | 
						|
 | 
						|
 | 
						|
def write_changelog(file_path: Path, release: str, content: str) -> None:
 | 
						|
    """Write Release details to the changelog file."""
 | 
						|
    content = f"## {release}\n{content}"
 | 
						|
    old_content = file_path.read_text()
 | 
						|
    updated_content = old_content.replace(
 | 
						|
        "<!-- GENERATOR_PLACEHOLDER -->",
 | 
						|
        f"<!-- GENERATOR_PLACEHOLDER -->\n\n{content}",
 | 
						|
    )
 | 
						|
    file_path.write_text(updated_content)
 | 
						|
 | 
						|
 | 
						|
def update_version(file_path: Path, release: str) -> None:
 | 
						|
    """Update template version in setup.py."""
 | 
						|
    old_content = file_path.read_text()
 | 
						|
    updated_content = re.sub(
 | 
						|
        r'\nversion = "\d+\.\d+\.\d+"\n',
 | 
						|
        f'\nversion = "{release}"\n',
 | 
						|
        old_content,
 | 
						|
    )
 | 
						|
    file_path.write_text(updated_content)
 | 
						|
 | 
						|
 | 
						|
def update_git_repo(paths: list[Path], release: str) -> None:
 | 
						|
    """Commit, tag changes in git repo and push to origin."""
 | 
						|
    repo = git.Repo(ROOT)
 | 
						|
    for path in paths:
 | 
						|
        repo.git.add(path)
 | 
						|
    message = f"Release {release}"
 | 
						|
 | 
						|
    user = repo.git.config("--get", "user.name")
 | 
						|
    email = repo.git.config("--get", "user.email")
 | 
						|
 | 
						|
    repo.git.commit(
 | 
						|
        m=message,
 | 
						|
        author=f"{user} <{email}>",
 | 
						|
    )
 | 
						|
    repo.git.tag("-a", release, m=message)
 | 
						|
    server = f"https://{GITHUB_TOKEN}@github.com/{GITHUB_REPO}.git"
 | 
						|
    print(f"Pushing changes to {GIT_BRANCH} branch of {GITHUB_REPO}")
 | 
						|
    repo.git.push(server, GIT_BRANCH)
 | 
						|
    repo.git.push("--tags", server, GIT_BRANCH)
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    if GITHUB_REPO is None:
 | 
						|
        raise RuntimeError("No github repo, please set the environment variable GITHUB_REPOSITORY")
 | 
						|
    if GIT_BRANCH is None:
 | 
						|
        raise RuntimeError("No git branch set, please set the GITHUB_REF_NAME environment variable")
 | 
						|
    main()
 |