diff --git a/.gitignore b/.gitignore index a135bac..d352240 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -__pycache__ -.python-version - lib/ node_modules/ diff --git a/dist/cpr/common.py b/dist/cpr/common.py deleted file mode 100644 index faf0d03..0000000 --- a/dist/cpr/common.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -import random -import re -import string - - -def get_random_string(length=7, chars=string.ascii_lowercase + string.digits): - return "".join(random.choice(chars) for _ in range(length)) - - -def parse_github_repository(url): - # Parse the protocol and github repository from a URL - # e.g. HTTPS, peter-evans/create-pull-request - https_pattern = re.compile(r"^https://.*@?github.com/(.+/.+)$") - ssh_pattern = re.compile(r"^git@github.com:(.+/.+).git$") - - match = https_pattern.match(url) - if match is not None: - return "HTTPS", match.group(1) - - match = ssh_pattern.match(url) - if match is not None: - return "SSH", match.group(1) - - raise ValueError(f"The format of '{url}' is not a valid GitHub repository URL") - - -def parse_display_name_email(display_name_email): - # Parse the name and email address from a string in the following format - # Display Name - pattern = re.compile(r"^([^<]+)\s*<([^>]+)>$") - - # Check we have a match - match = pattern.match(display_name_email) - if match is None: - raise ValueError( - f"The format of '{display_name_email}' is not a valid email address with display name" - ) - - # Check that name and email are not just whitespace - name = match.group(1).strip() - email = match.group(2).strip() - if len(name) == 0 or len(email) == 0: - raise ValueError( - f"The format of '{display_name_email}' is not a valid email address with display name" - ) - - return name, email diff --git a/dist/cpr/create_or_update_branch.py b/dist/cpr/create_or_update_branch.py deleted file mode 100644 index 2cb24d1..0000000 --- a/dist/cpr/create_or_update_branch.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python3 -""" Create or Update Branch """ -import common as cmn -from git import Repo, GitCommandError -import os - - -CHERRYPICK_EMPTY = ( - "The previous cherry-pick is now empty, possibly due to conflict resolution." -) - - -def fetch_successful(repo, repo_url, branch): - try: - repo.git.fetch(repo_url, f"{branch}:refs/remotes/origin/{branch}") - except GitCommandError: - return False - return True - - -def is_ahead(repo, branch_1, branch_2): - # Return true if branch_2 is ahead of branch_1 - return ( - int(repo.git.rev_list("--right-only", "--count", f"{branch_1}...{branch_2}")) - > 0 - ) - - -def is_behind(repo, branch_1, branch_2): - # Return true if branch_2 is behind branch_1 - return ( - int(repo.git.rev_list("--left-only", "--count", f"{branch_1}...{branch_2}")) > 0 - ) - - -def is_even(repo, branch_1, branch_2): - # Return true if branch_2 is even with branch_1 - return not is_ahead(repo, branch_1, branch_2) and not is_behind( - repo, branch_1, branch_2 - ) - - -def has_diff(repo, branch_1, branch_2): - diff = repo.git.diff(f"{branch_1}..{branch_2}") - return len(diff) > 0 - - -def create_or_update_branch(repo, repo_url, commit_message, base, branch): - # Set the default return values - action = "none" - diff = False - - # Get the working base. This may or may not be the actual base. - working_base = repo.git.symbolic_ref("HEAD", "--short") - # If the base is not specified it is assumed to be the working base - if base is None: - base = working_base - - # Save the working base changes to a temporary branch - temp_branch = cmn.get_random_string(length=20) - repo.git.checkout("HEAD", b=temp_branch) - # Commit any uncomitted changes - if repo.is_dirty(untracked_files=True): - print(f"Uncommitted changes found. Adding a commit.") - repo.git.add("-A") - repo.git.commit(m=commit_message) - - # Perform fetch and reset the working base - # Commits made during the workflow will be removed - repo.git.fetch("--force", repo_url, f"{working_base}:{working_base}") - - # If the working base is not the base, rebase the temp branch commits - if working_base != base: - print( - f"Rebasing commits made to branch '{working_base}' on to base branch '{base}'" - ) - # Checkout the actual base - repo.git.fetch("--force", repo_url, f"{base}:{base}") - repo.git.checkout(base) - # Cherrypick commits from the temporary branch starting from the working base - commits = repo.git.rev_list("--reverse", f"{working_base}..{temp_branch}", ".") - for commit in commits.splitlines(): - try: - repo.git.cherry_pick( - "--strategy", - "recursive", - "--strategy-option", - "theirs", - f"{commit}", - ) - except GitCommandError as e: - if CHERRYPICK_EMPTY not in e.stderr: - print("Unexpected error: ", e) - raise - # Reset the temp branch to the working index - repo.git.checkout("-B", temp_branch, "HEAD") - # Reset the base - repo.git.fetch("--force", repo_url, f"{base}:{base}") - - # Try to fetch the pull request branch - if not fetch_successful(repo, repo_url, branch): - # The pull request branch does not exist - print(f"Pull request branch '{branch}' does not exist yet") - # Create the pull request branch - repo.git.checkout("HEAD", b=branch) - # Check if the pull request branch is ahead of the base - diff = is_ahead(repo, base, branch) - if diff: - action = "created" - print(f"Created branch '{branch}'") - else: - print( - f"Branch '{branch}' is not ahead of base '{base}' and will not be created" - ) - else: - # The pull request branch exists - print( - f"Pull request branch '{branch}' already exists as remote branch 'origin/{branch}'" - ) - # Checkout the pull request branch - repo.git.checkout(branch) - - if has_diff(repo, branch, temp_branch): - # If the branch differs from the recreated temp version then the branch is reset - # For changes on base this action is similar to a rebase of the pull request branch - print(f"Resetting '{branch}'") - repo.git.checkout("-B", branch, temp_branch) - # repo.git.switch("-C", branch, temp_branch) - - # Check if the pull request branch has been updated - # If the branch was reset or updated it will be ahead - # It may be behind if a reset now results in no diff with the base - if not is_even(repo, f"origin/{branch}", branch): - action = "updated" - print(f"Updated branch '{branch}'") - else: - print(f"Branch '{branch}' is even with its remote and will not be updated") - - # Check if the pull request branch is ahead of the base - diff = is_ahead(repo, base, branch) - - # Delete the temporary branch - repo.git.branch("--delete", "--force", temp_branch) - - return {"action": action, "diff": diff, "base": base} diff --git a/dist/cpr/create_or_update_pull_request.py b/dist/cpr/create_or_update_pull_request.py deleted file mode 100644 index a34b090..0000000 --- a/dist/cpr/create_or_update_pull_request.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python3 -""" Create or Update Pull Request """ -from github import Github, GithubException -import os - - -def string_to_bool(str): - if str is None: - return False - else: - return str.lower() in [ - "true", - "1", - "t", - "y", - "yes", - "on", - ] - - -def cs_string_to_list(str): - # Split the comma separated string into a list - l = [i.strip() for i in str.split(",")] - # Remove empty strings - return list(filter(None, l)) - - -def create_project_card(github_repo, project_name, project_column_name, pull_request): - # Locate the project by name - project = None - for project_item in github_repo.get_projects("all"): - if project_item.name == project_name: - project = project_item - break - - if not project: - print("::error::Project not found. Unable to create project card.") - return - - # Locate the column by name - column = None - for column_item in project.get_columns(): - if column_item.name == project_column_name: - column = column_item - break - - if not column: - print("::error::Project column not found. Unable to create project card.") - return - - # Create a project card for the pull request - column.create_card(content_id=pull_request.id, content_type="PullRequest") - print( - "Added pull request #%d to project '%s' under column '%s'" - % (pull_request.number, project.name, column.name) - ) - - -def create_or_update_pull_request( - github_token, - github_repository, - branch, - base, - title, - body, - labels, - assignees, - milestone, - reviewers, - team_reviewers, - project_name, - project_column_name, - draft, - request_to_parent, -): - github_repo = head_repo = Github(github_token).get_repo(github_repository) - if string_to_bool(request_to_parent): - github_repo = github_repo.parent - if github_repo is None: - raise ValueError( - "The checked out repository is not a fork. Input 'request-to-parent' should be set to false." - ) - - head_branch = f"{head_repo.owner.login}:{branch}" - - # Create the pull request - try: - pull_request = github_repo.create_pull( - title=title, - body=body, - base=base, - head=head_branch, - draft=string_to_bool(draft), - ) - print( - f"Created pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})" - ) - except GithubException as e: - if e.status == 422: - # A pull request exists for this branch and base - # Get the pull request - pull_request = github_repo.get_pulls( - state="open", base=base, head=head_branch - )[0] - # Update title and body - pull_request.as_issue().edit(title=title, body=body) - print( - f"Updated pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})" - ) - else: - print(str(e)) - raise - - # Set the output variables - os.system(f"echo ::set-env name=PULL_REQUEST_NUMBER::{pull_request.number}") - os.system(f"echo ::set-output name=pull-request-number::{pull_request.number}") - # 'pr_number' is deprecated - os.system(f"echo ::set-output name=pr_number::{pull_request.number}") - - # Set labels, assignees and milestone - if labels is not None: - print(f"Applying labels '{labels}'") - pull_request.as_issue().edit(labels=cs_string_to_list(labels)) - if assignees is not None: - print(f"Applying assignees '{assignees}'") - pull_request.as_issue().edit(assignees=cs_string_to_list(assignees)) - if milestone is not None: - print(f"Applying milestone '{milestone}'") - milestone = github_repo.get_milestone(int(milestone)) - pull_request.as_issue().edit(milestone=milestone) - - # Set pull request reviewers - if reviewers is not None: - print(f"Requesting reviewers '{reviewers}'") - try: - pull_request.create_review_request(reviewers=cs_string_to_list(reviewers)) - except GithubException as e: - # Likely caused by "Review cannot be requested from pull request author." - if e.status == 422: - print("Request reviewers failed - {}".format(e.data["message"])) - - # Set pull request team reviewers - if team_reviewers is not None: - print(f"Requesting team reviewers '{team_reviewers}'") - pull_request.create_review_request( - team_reviewers=cs_string_to_list(team_reviewers) - ) - - # Create a project card for the pull request - if project_name is not None and project_column_name is not None: - try: - create_project_card( - github_repo, project_name, project_column_name, pull_request - ) - except GithubException as e: - # Likely caused by "Project already has the associated issue." - if e.status == 422: - print( - "Create project card failed - {}".format( - e.data["errors"][0]["message"] - ) - ) diff --git a/dist/cpr/create_pull_request.py b/dist/cpr/create_pull_request.py deleted file mode 100644 index ead7f02..0000000 --- a/dist/cpr/create_pull_request.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/env python3 -""" Create Pull Request """ -import base64 -import common as cmn -import create_or_update_branch as coub -import create_or_update_pull_request as coupr -from git import Repo, GitCommandError -import json -import os -import sys -import time - - -# Default the committer and author to the GitHub Actions bot -DEFAULT_COMMITTER = "GitHub " -DEFAULT_AUTHOR = ( - "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" -) -DEFAULT_COMMIT_MESSAGE = "[create-pull-request] automated change" -DEFAULT_TITLE = "Changes by create-pull-request action" -DEFAULT_BODY = ( - "Automated changes by " - + "[create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action" -) -DEFAULT_BRANCH = "create-pull-request/patch" - - -def get_git_config_value(repo, name): - try: - return repo.git.config("--get", name) - except GitCommandError: - return None - - -def get_repository_detail(repo): - remote_origin_url = get_git_config_value(repo, "remote.origin.url") - if remote_origin_url is None: - raise ValueError("Failed to fetch 'remote.origin.url' from git config") - protocol, github_repository = cmn.parse_github_repository(remote_origin_url) - return remote_origin_url, protocol, github_repository - - -def git_user_config_is_set(repo): - name = get_git_config_value(repo, "user.name") - email = get_git_config_value(repo, "user.email") - - if name is not None and email is not None: - print(f"Git user already configured as '{name} <{email}>'") - return True - - committer_name = get_git_config_value(repo, "committer.name") - committer_email = get_git_config_value(repo, "committer.email") - author_name = get_git_config_value(repo, "author.name") - author_email = get_git_config_value(repo, "author.email") - - if ( - committer_name is not None - and committer_email is not None - and author_name is not None - and author_email is not None - ): - print( - f"Git committer already configured as '{committer_name} <{committer_email}>'" - ) - print(f"Git author already configured as '{author_name} <{author_email}>'") - return True - - return False - - -def set_committer_author(repo, committer, author): - # If either committer or author is supplied they will be cross used - if committer is None and author is not None: - print("Supplied author will also be used as the committer.") - committer = author - if author is None and committer is not None: - print("Supplied committer will also be used as the author.") - author = committer - - # If no committer/author has been supplied but user configuration already - # exists in git config we can exit and use the existing config as-is. - if committer is None and author is None: - if git_user_config_is_set(repo): - return - - # Set defaults if no committer/author has been supplied - if committer is None and author is None: - committer = DEFAULT_COMMITTER - author = DEFAULT_AUTHOR - - # Set git environment. This will not persist after the action completes. - committer_name, committer_email = cmn.parse_display_name_email(committer) - author_name, author_email = cmn.parse_display_name_email(author) - repo.git.update_environment( - GIT_COMMITTER_NAME=committer_name, - GIT_COMMITTER_EMAIL=committer_email, - GIT_AUTHOR_NAME=author_name, - GIT_AUTHOR_EMAIL=author_email, - ) - print(f"Configured git committer as '{committer_name} <{committer_email}>'") - print(f"Configured git author as '{author_name} <{author_email}>'") - - -# Get required environment variables -github_token = os.environ["GITHUB_TOKEN"] -# Get environment variables with defaults -path = os.getenv("CPR_PATH", os.getcwd()) -branch = os.getenv("CPR_BRANCH", DEFAULT_BRANCH) -commit_message = os.getenv("CPR_COMMIT_MESSAGE", DEFAULT_COMMIT_MESSAGE) -# Get environment variables with a default of 'None' -committer = os.environ.get("CPR_COMMITTER") -author = os.environ.get("CPR_AUTHOR") -base = os.environ.get("CPR_BASE") - -# Set the repo path -repo = Repo(path) - -# Determine the GitHub repository from git config -# This will be the target repository for the pull request -repo_url, protocol, github_repository = get_repository_detail(repo) -print(f"Target repository set to {github_repository}") - -if protocol == "HTTPS": - print(f"::debug::Using HTTPS protocol") - # Encode and configure the basic credential for HTTPS access - basic_credential = base64.b64encode( - f"x-access-token:{github_token}".encode("utf-8") - ).decode("utf-8") - # Mask the basic credential in logs and debug output - print(f"::add-mask::{basic_credential}") - repo.git.set_persistent_git_options( - c=f"http.https://github.com/.extraheader=AUTHORIZATION: basic {basic_credential}" - ) - -# Determine if the checked out ref is a valid base for a pull request -# The action needs the checked out HEAD ref to be a branch -# This check will fail in the following cases: -# - HEAD is detached -# - HEAD is a merge commit (pull_request events) -# - HEAD is a tag -try: - working_base = repo.git.symbolic_ref("HEAD", "--short") -except GitCommandError as e: - print(f"::debug::{e.stderr}") - print( - f"::error::The checked out ref is not a valid base for a pull request. " - + "Unable to continue. Exiting." - ) - sys.exit(1) - -# Exit if the working base is a PR branch created by this action. -# This may occur when using a PAT instead of GITHUB_TOKEN because -# a PAT allows workflow actions to trigger further events. -if working_base.startswith(branch): - print( - f"::error::Working base branch '{working_base}' was created by this action. " - + "Unable to continue. Exiting." - ) - sys.exit(1) - -# Fetch an optional environment variable to determine the branch suffix -branch_suffix = os.environ.get("CPR_BRANCH_SUFFIX") -if branch_suffix is not None: - if branch_suffix == "short-commit-hash": - # Suffix with the short SHA1 hash - branch = "{}-{}".format(branch, repo.git.rev_parse("--short", "HEAD")) - elif branch_suffix == "timestamp": - # Suffix with the current timestamp - branch = "{}-{}".format(branch, int(time.time())) - elif branch_suffix == "random": - # Suffix with a 7 character random string - branch = "{}-{}".format(branch, cmn.get_random_string()) - else: - print( - f"::error::Branch suffix '{branch_suffix}' is not a valid value. " - + "Unable to continue. Exiting." - ) - sys.exit(1) - -# Output head branch -print(f"Pull request branch to create or update set to '{branch}'") - -# Set the committer and author -try: - set_committer_author(repo, committer, author) -except ValueError as e: - print(f"::error::{e} " + "Unable to continue. Exiting.") - sys.exit(1) - -# Create or update the pull request branch -result = coub.create_or_update_branch(repo, repo_url, commit_message, base, branch) - -if result["action"] in ["created", "updated"]: - # The branch was created or updated - print(f"Pushing pull request branch to 'origin/{branch}'") - repo.git.push("--force", repo_url, f"HEAD:refs/heads/{branch}") - - # Set the base. It would have been 'None' if not specified as an input - base = result["base"] - - # If there is no longer a diff with the base delete the branch and exit - if not result["diff"]: - print(f"Branch '{branch}' no longer differs from base branch '{base}'") - print(f"Closing pull request and deleting branch '{branch}'") - repo.git.push("--delete", "--force", repo_url, f"refs/heads/{branch}") - sys.exit() - - # Fetch optional environment variables with default values - title = os.getenv("CPR_TITLE", DEFAULT_TITLE) - body = os.getenv("CPR_BODY", DEFAULT_BODY) - - # Create or update the pull request - coupr.create_or_update_pull_request( - github_token, - github_repository, - branch, - base, - title, - body, - os.environ.get("CPR_LABELS"), - os.environ.get("CPR_ASSIGNEES"), - os.environ.get("CPR_MILESTONE"), - os.environ.get("CPR_REVIEWERS"), - os.environ.get("CPR_TEAM_REVIEWERS"), - os.environ.get("CPR_PROJECT_NAME"), - os.environ.get("CPR_PROJECT_COLUMN_NAME"), - os.environ.get("CPR_DRAFT"), - os.environ.get("CPR_REQUEST_TO_PARENT"), - ) diff --git a/dist/cpr/requirements.txt b/dist/cpr/requirements.txt deleted file mode 100644 index 5afd424..0000000 --- a/dist/cpr/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -setuptools==46.4.0 -wheel==0.34.2 -GitPython==3.1.2 -PyGithub==1.51 diff --git a/dist/cpr/test_common.py b/dist/cpr/test_common.py deleted file mode 100644 index 9409258..0000000 --- a/dist/cpr/test_common.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -""" Test Common """ -import common as cmn -import pytest - - -def test_get_random_string(): - assert len(cmn.get_random_string()) == 7 - assert len(cmn.get_random_string(length=20)) == 20 - - -def test_parse_github_repository_success(): - protocol, repository = cmn.parse_github_repository( - "https://github.com/peter-evans/create-pull-request" - ) - assert protocol == "HTTPS" - assert repository == "peter-evans/create-pull-request" - - protocol, repository = cmn.parse_github_repository( - "https://xxx:x-oauth-basic@github.com/peter-evans/create-pull-request" - ) - assert protocol == "HTTPS" - assert repository == "peter-evans/create-pull-request" - - protocol, repository = cmn.parse_github_repository( - "git@github.com:peter-evans/create-pull-request.git" - ) - assert protocol == "SSH" - assert repository == "peter-evans/create-pull-request" - - -def test_parse_github_repository_failure(): - url = "https://github.com/peter-evans" - with pytest.raises(ValueError) as e_info: - cmn.parse_github_repository(url) - assert ( - e_info.value.args[0] - == f"The format of '{url}' is not a valid GitHub repository URL" - ) - - -def test_parse_display_name_email_success(): - name, email = cmn.parse_display_name_email("abc def ") - assert name == "abc def" - assert email == "abc@def.com" - - name, email = cmn.parse_display_name_email( - "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" - ) - assert name == "github-actions[bot]" - assert email == "41898282+github-actions[bot]@users.noreply.github.com" - - -def test_parse_display_name_email_failure(): - display_name_email = "abc@def.com" - with pytest.raises(ValueError) as e_info: - cmn.parse_display_name_email(display_name_email) - assert ( - e_info.value.args[0] - == f"The format of '{display_name_email}' is not a valid email address with display name" - ) - - display_name_email = " < >" - with pytest.raises(ValueError) as e_info: - cmn.parse_display_name_email(display_name_email) - assert ( - e_info.value.args[0] - == f"The format of '{display_name_email}' is not a valid email address with display name" - ) diff --git a/dist/cpr/test_create_or_update_branch.py b/dist/cpr/test_create_or_update_branch.py deleted file mode 100644 index a0f400c..0000000 --- a/dist/cpr/test_create_or_update_branch.py +++ /dev/null @@ -1,757 +0,0 @@ -#!/usr/bin/env python3 -""" Test Create or Update Branch """ -import create_or_update_branch as coub -from git import Repo -import os -import pytest -import sys -import time - - -# Set git repo -REPO_PATH = os.getenv("COUB_REPO_PATH", os.getcwd()) -repo = Repo(REPO_PATH) - -# Set git environment -author_name = "github-actions[bot]" -author_email = "41898282+github-actions[bot]@users.noreply.github.com" -committer_name = "GitHub" -committer_email = "noreply@github.com" -repo.git.update_environment( - GIT_AUTHOR_NAME=author_name, - GIT_AUTHOR_EMAIL=author_email, - GIT_COMMITTER_NAME=committer_name, - GIT_COMMITTER_EMAIL=committer_email, -) - -REPO_URL = repo.git.config("--get", "remote.origin.url") - -TRACKED_FILE = "tracked-file.txt" -UNTRACKED_FILE = "untracked-file.txt" - -DEFAULT_BRANCH = "tests/master" -NOT_BASE_BRANCH = "tests/branch-that-is-not-the-base" -NOT_EXIST_BRANCH = "tests/branch-that-does-not-exist" - -COMMIT_MESSAGE = "[create-pull-request] automated change" -BRANCH = "tests/create-pull-request/patch" -BASE = DEFAULT_BRANCH - - -def create_tracked_change(content=None): - if content is None: - content = str(time.time()) - # Create a tracked file change - with open(os.path.join(REPO_PATH, TRACKED_FILE), "w") as f: - f.write(content) - return content - - -def create_untracked_change(content=None): - if content is None: - content = str(time.time()) - # Create an untracked file change - with open(os.path.join(REPO_PATH, UNTRACKED_FILE), "w") as f: - f.write(content) - return content - - -def get_tracked_content(): - # Read the content of the tracked file - with open(os.path.join(REPO_PATH, TRACKED_FILE), "r") as f: - return f.read() - - -def get_untracked_content(): - # Read the content of the untracked file - with open(os.path.join(REPO_PATH, UNTRACKED_FILE), "r") as f: - return f.read() - - -def create_changes(tracked_content=None, untracked_content=None): - tracked_content = create_tracked_change(tracked_content) - untracked_content = create_untracked_change(untracked_content) - return tracked_content, untracked_content - - -def create_commits(number=2, final_tracked_content=None, final_untracked_content=None): - for i in range(number): - commit_number = i + 1 - if commit_number == number: - tracked_content, untracked_content = create_changes( - final_tracked_content, final_untracked_content - ) - else: - tracked_content, untracked_content = create_changes() - repo.git.add("-A") - repo.git.commit(m=f"Commit {commit_number}") - return tracked_content, untracked_content - - -@pytest.fixture(scope="module", autouse=True) -def before_after_all(): - print("Before all tests") - # Check there are no local changes that might be - # destroyed by running these tests - assert not repo.is_dirty(untracked_files=True) - - # Create a new default branch for the test run - repo.remotes.origin.fetch() - repo.git.checkout("master") - repo.git.checkout("HEAD", b=NOT_BASE_BRANCH) - create_tracked_change() - repo.git.add("-A") - repo.git.commit(m="This commit should not appear in pr branches") - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{NOT_BASE_BRANCH}") - # Create a new default branch for the test run - repo.git.checkout("master") - repo.git.checkout("HEAD", b=DEFAULT_BRANCH) - create_tracked_change() - repo.git.add("-A") - repo.git.commit(m="Add file to be a tracked file for tests") - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - - yield - - print("After all tests") - repo.git.checkout("master") - # Delete the "not base branch" created for the test run - repo.git.branch("--delete", "--force", NOT_BASE_BRANCH) - repo.git.push("--delete", "--force", REPO_URL, f"refs/heads/{NOT_BASE_BRANCH}") - # Delete the default branch created for the test run - repo.git.branch("--delete", "--force", DEFAULT_BRANCH) - repo.git.push("--delete", "--force", REPO_URL, f"refs/heads/{DEFAULT_BRANCH}") - - -def before_test(): - print("Before test") - # Checkout the default branch - repo.git.checkout(DEFAULT_BRANCH) - - -def after_test(delete_remote=True): - print("After test") - # Output git log - print(repo.git.log("-5", pretty="oneline")) - # Delete the pull request branch if it exists - repo.git.checkout(DEFAULT_BRANCH) - print(f"Deleting {BRANCH}") - for branch in repo.branches: - if branch.name == BRANCH: - repo.git.branch("--delete", "--force", BRANCH) - break - if delete_remote: - print(f"Deleting origin/{BRANCH}") - for ref in repo.remotes.origin.refs: - if ref.name == f"origin/{BRANCH}": - repo.git.push("--delete", "--force", REPO_URL, f"refs/heads/{BRANCH}") - repo.remotes.origin.fetch("--prune") - break - - -@pytest.fixture(autouse=True) -def before_after_tests(): - before_test() - yield - after_test() - - -# Tests if a branch exists and can be fetched -def coub_fetch_successful(): - assert coub.fetch_successful(repo, REPO_URL, NOT_BASE_BRANCH) - assert not coub.fetch_successful(repo, REPO_URL, NOT_EXIST_BRANCH) - - -# Tests no changes resulting in no new branch being created -def coub_no_changes_on_create(): - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "none" - - -# Tests create and update with a tracked file change -def coub_tracked_changes(): - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - - -# Tests create and update with an untracked file change -def coub_untracked_changes(): - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_untracked_content() == untracked_content - - -# Tests create and update with identical changes -# The pull request branch will not be updated -def coub_identical_changes(): - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create identical tracked and untracked file changes - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "none" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with commits on the base inbetween -def coub_commits_on_base(): - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and then an update with no changes -# This effectively reverts the branch back to match the base and results in no diff -def coub_changes_no_diff(): - # Save the default branch tracked content - default_tracked_content = get_tracked_content() - - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Running with no update effectively reverts the branch back to match the base - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == default_tracked_content - - -# Tests create and update with commits on the base inbetween -# The changes on base effectively revert the branch back to match the base and results in no diff -def coub_commits_on_base_no_diff(): - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - tracked_content, untracked_content = create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Create the same tracked and untracked file changes that were made to the base - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with commits on the working base (during the workflow) -def coub_commits_on_working_base(): - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with changes and commits on the working base (during the workflow) -def coub_changes_and_commits_on_working_base(): - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with changes and commits on the working base (during the workflow) -# with commits on the base inbetween -def coub_changes_and_commits_on_base_and_working_base(): - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests no changes resulting in no new branch being created -def coub_wbnb_no_changes_on_create(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "none" - - -# Working Base is Not Base (WBNB) -# Tests create and update with a tracked file change -def coub_wbnb_tracked_changes(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with an untracked file change -def coub_wbnb_untracked_changes(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with identical changes -# The pull request branch will not be updated -def coub_wbnb_identical_changes(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create identical tracked and untracked file changes - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "none" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with commits on the base inbetween -def coub_wbnb_commits_on_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and then an update with no changes -# This effectively reverts the branch back to match the base and results in no diff -def coub_wbnb_changes_no_diff(): - # Save the default branch tracked content - default_tracked_content = get_tracked_content() - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Running with no update effectively reverts the branch back to match the base - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == default_tracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with commits on the base inbetween -# The changes on base effectively revert the branch back to match the base and results in no diff -# This scenario will cause cherrypick to fail due to an empty commit. -# The commit is empty because the changes now exist on the base. -def coub_wbnb_commits_on_base_no_diff(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - tracked_content, untracked_content = create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create the same tracked and untracked file changes that were made to the base - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with commits on the working base (during the workflow) -def coub_wbnb_commits_on_working_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with changes and commits on the working base (during the workflow) -def coub_wbnb_changes_and_commits_on_working_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with changes and commits on the working base (during the workflow) -# with commits on the base inbetween -def coub_wbnb_changes_and_commits_on_base_and_working_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# pytest -v -s ~/git/create-pull-request/src - -test_coub_fetch_successful = coub_fetch_successful - -test_coub_no_changes_on_create = coub_no_changes_on_create -test_coub_tracked_changes = coub_tracked_changes -test_coub_untracked_changes = coub_untracked_changes -test_coub_identical_changes = coub_identical_changes -test_coub_commits_on_base = coub_commits_on_base - -test_coub_changes_no_diff = coub_changes_no_diff -test_coub_commits_on_base_no_diff = coub_commits_on_base_no_diff - -test_coub_commits_on_working_base = coub_commits_on_working_base -test_coub_changes_and_commits_on_working_base = coub_changes_and_commits_on_working_base -test_coub_changes_and_commits_on_base_and_working_base = ( - coub_changes_and_commits_on_base_and_working_base -) - -# WBNB -test_coub_wbnb_no_changes_on_create = coub_wbnb_no_changes_on_create -test_coub_wbnb_tracked_changes = coub_wbnb_tracked_changes -test_coub_wbnb_untracked_changes = coub_wbnb_untracked_changes -test_coub_wbnb_identical_changes = coub_wbnb_identical_changes -test_coub_wbnb_commits_on_base = coub_wbnb_commits_on_base - -test_coub_wbnb_changes_no_diff = coub_wbnb_changes_no_diff -test_coub_wbnb_commits_on_base_no_diff = coub_wbnb_commits_on_base_no_diff - -test_coub_wbnb_commits_on_working_base = coub_wbnb_commits_on_working_base -test_coub_wbnb_changes_and_commits_on_working_base = ( - coub_wbnb_changes_and_commits_on_working_base -) -test_coub_wbnb_changes_and_commits_on_base_and_working_base = ( - coub_wbnb_changes_and_commits_on_base_and_working_base -) diff --git a/dist/vendor/Deprecated-1.2.10.tar.gz b/dist/vendor/Deprecated-1.2.10.tar.gz deleted file mode 100644 index 77715a5..0000000 Binary files a/dist/vendor/Deprecated-1.2.10.tar.gz and /dev/null differ diff --git a/dist/vendor/GitPython-3.1.2.tar.gz b/dist/vendor/GitPython-3.1.2.tar.gz deleted file mode 100644 index 59fc285..0000000 Binary files a/dist/vendor/GitPython-3.1.2.tar.gz and /dev/null differ diff --git a/dist/vendor/PyGithub-1.51.tar.gz b/dist/vendor/PyGithub-1.51.tar.gz deleted file mode 100644 index 0a8b580..0000000 Binary files a/dist/vendor/PyGithub-1.51.tar.gz and /dev/null differ diff --git a/dist/vendor/PyJWT-1.7.1.tar.gz b/dist/vendor/PyJWT-1.7.1.tar.gz deleted file mode 100644 index 2ce6449..0000000 Binary files a/dist/vendor/PyJWT-1.7.1.tar.gz and /dev/null differ diff --git a/dist/vendor/certifi-2020.6.20.tar.gz b/dist/vendor/certifi-2020.6.20.tar.gz deleted file mode 100644 index bafb932..0000000 Binary files a/dist/vendor/certifi-2020.6.20.tar.gz and /dev/null differ diff --git a/dist/vendor/chardet-3.0.4.tar.gz b/dist/vendor/chardet-3.0.4.tar.gz deleted file mode 100644 index 13028bf..0000000 Binary files a/dist/vendor/chardet-3.0.4.tar.gz and /dev/null differ diff --git a/dist/vendor/gitdb-4.0.5.tar.gz b/dist/vendor/gitdb-4.0.5.tar.gz deleted file mode 100644 index f540f9a..0000000 Binary files a/dist/vendor/gitdb-4.0.5.tar.gz and /dev/null differ diff --git a/dist/vendor/idna-2.9.tar.gz b/dist/vendor/idna-2.9.tar.gz deleted file mode 100644 index ce4301b..0000000 Binary files a/dist/vendor/idna-2.9.tar.gz and /dev/null differ diff --git a/dist/vendor/requests-2.24.0.tar.gz b/dist/vendor/requests-2.24.0.tar.gz deleted file mode 100644 index 19e790c..0000000 Binary files a/dist/vendor/requests-2.24.0.tar.gz and /dev/null differ diff --git a/dist/vendor/setuptools-46.4.0.zip b/dist/vendor/setuptools-46.4.0.zip deleted file mode 100644 index 4568c94..0000000 Binary files a/dist/vendor/setuptools-46.4.0.zip and /dev/null differ diff --git a/dist/vendor/smmap-3.0.4.tar.gz b/dist/vendor/smmap-3.0.4.tar.gz deleted file mode 100644 index 592d7e9..0000000 Binary files a/dist/vendor/smmap-3.0.4.tar.gz and /dev/null differ diff --git a/dist/vendor/urllib3-1.25.9.tar.gz b/dist/vendor/urllib3-1.25.9.tar.gz deleted file mode 100644 index b20c913..0000000 Binary files a/dist/vendor/urllib3-1.25.9.tar.gz and /dev/null differ diff --git a/dist/vendor/wheel-0.34.2.tar.gz b/dist/vendor/wheel-0.34.2.tar.gz deleted file mode 100644 index d31f4fb..0000000 Binary files a/dist/vendor/wheel-0.34.2.tar.gz and /dev/null differ diff --git a/dist/vendor/wrapt-1.12.1.tar.gz b/dist/vendor/wrapt-1.12.1.tar.gz deleted file mode 100644 index 0e54678..0000000 Binary files a/dist/vendor/wrapt-1.12.1.tar.gz and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 48b5000..baf9360 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "create-pull-request", - "version": "2.0.0", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4011,7 +4011,9 @@ "is-docker": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==" + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true, + "optional": true }, "is-extendable": { "version": "0.1.1", diff --git a/package.json b/package.json index 53b4b4f..33b3d4e 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "create-pull-request", - "version": "2.0.0", + "version": "3.0.0", "private": true, "description": "Creates a pull request for changes to your repository in the actions workspace", "main": "lib/main.js", "scripts": { - "clean": "rm -rf dist", + "clean": "echo Temporarily kept for backwards compatibility", "build": "tsc && ncc build", "format": "prettier --write '**/*.ts'", "format-check": "prettier --check '**/*.ts'", @@ -13,9 +13,9 @@ "test:unit": "jest unit", "test:int": "__test__/integration-tests.sh", "test": "npm run test:unit && npm run test:int", - "pack-assets": "mkdir -p dist/cpr && cp -rv src/cpr/* dist/cpr", - "vendor-deps": "pip download -r src/cpr/requirements.txt --no-binary=:all: -d dist/vendor", - "package": "npm run build && npm run pack-assets && npm run vendor-deps" + "pack-assets": "echo Temporarily kept for backwards compatibility", + "vendor-deps": "echo Temporarily kept for backwards compatibility", + "package": "echo Temporarily kept for backwards compatibility" }, "repository": { "type": "git", @@ -39,7 +39,6 @@ "@octokit/core": "3.1.0", "@octokit/plugin-paginate-rest": "2.2.3", "@octokit/plugin-rest-endpoint-methods": "4.0.0", - "is-docker": "2.0.0", "uuid": "8.2.0" }, "devDependencies": { diff --git a/renovate.json b/renovate.json deleted file mode 100644 index bfd90d0..0000000 --- a/renovate.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": [ - "config:base" - ], - "enabledManagers": ["pip_requirements"], - "ignorePaths": [ - "**/dist/**" - ] -} diff --git a/src/cpr/common.py b/src/cpr/common.py deleted file mode 100644 index faf0d03..0000000 --- a/src/cpr/common.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -import random -import re -import string - - -def get_random_string(length=7, chars=string.ascii_lowercase + string.digits): - return "".join(random.choice(chars) for _ in range(length)) - - -def parse_github_repository(url): - # Parse the protocol and github repository from a URL - # e.g. HTTPS, peter-evans/create-pull-request - https_pattern = re.compile(r"^https://.*@?github.com/(.+/.+)$") - ssh_pattern = re.compile(r"^git@github.com:(.+/.+).git$") - - match = https_pattern.match(url) - if match is not None: - return "HTTPS", match.group(1) - - match = ssh_pattern.match(url) - if match is not None: - return "SSH", match.group(1) - - raise ValueError(f"The format of '{url}' is not a valid GitHub repository URL") - - -def parse_display_name_email(display_name_email): - # Parse the name and email address from a string in the following format - # Display Name - pattern = re.compile(r"^([^<]+)\s*<([^>]+)>$") - - # Check we have a match - match = pattern.match(display_name_email) - if match is None: - raise ValueError( - f"The format of '{display_name_email}' is not a valid email address with display name" - ) - - # Check that name and email are not just whitespace - name = match.group(1).strip() - email = match.group(2).strip() - if len(name) == 0 or len(email) == 0: - raise ValueError( - f"The format of '{display_name_email}' is not a valid email address with display name" - ) - - return name, email diff --git a/src/cpr/create_or_update_branch.py b/src/cpr/create_or_update_branch.py deleted file mode 100644 index 2cb24d1..0000000 --- a/src/cpr/create_or_update_branch.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python3 -""" Create or Update Branch """ -import common as cmn -from git import Repo, GitCommandError -import os - - -CHERRYPICK_EMPTY = ( - "The previous cherry-pick is now empty, possibly due to conflict resolution." -) - - -def fetch_successful(repo, repo_url, branch): - try: - repo.git.fetch(repo_url, f"{branch}:refs/remotes/origin/{branch}") - except GitCommandError: - return False - return True - - -def is_ahead(repo, branch_1, branch_2): - # Return true if branch_2 is ahead of branch_1 - return ( - int(repo.git.rev_list("--right-only", "--count", f"{branch_1}...{branch_2}")) - > 0 - ) - - -def is_behind(repo, branch_1, branch_2): - # Return true if branch_2 is behind branch_1 - return ( - int(repo.git.rev_list("--left-only", "--count", f"{branch_1}...{branch_2}")) > 0 - ) - - -def is_even(repo, branch_1, branch_2): - # Return true if branch_2 is even with branch_1 - return not is_ahead(repo, branch_1, branch_2) and not is_behind( - repo, branch_1, branch_2 - ) - - -def has_diff(repo, branch_1, branch_2): - diff = repo.git.diff(f"{branch_1}..{branch_2}") - return len(diff) > 0 - - -def create_or_update_branch(repo, repo_url, commit_message, base, branch): - # Set the default return values - action = "none" - diff = False - - # Get the working base. This may or may not be the actual base. - working_base = repo.git.symbolic_ref("HEAD", "--short") - # If the base is not specified it is assumed to be the working base - if base is None: - base = working_base - - # Save the working base changes to a temporary branch - temp_branch = cmn.get_random_string(length=20) - repo.git.checkout("HEAD", b=temp_branch) - # Commit any uncomitted changes - if repo.is_dirty(untracked_files=True): - print(f"Uncommitted changes found. Adding a commit.") - repo.git.add("-A") - repo.git.commit(m=commit_message) - - # Perform fetch and reset the working base - # Commits made during the workflow will be removed - repo.git.fetch("--force", repo_url, f"{working_base}:{working_base}") - - # If the working base is not the base, rebase the temp branch commits - if working_base != base: - print( - f"Rebasing commits made to branch '{working_base}' on to base branch '{base}'" - ) - # Checkout the actual base - repo.git.fetch("--force", repo_url, f"{base}:{base}") - repo.git.checkout(base) - # Cherrypick commits from the temporary branch starting from the working base - commits = repo.git.rev_list("--reverse", f"{working_base}..{temp_branch}", ".") - for commit in commits.splitlines(): - try: - repo.git.cherry_pick( - "--strategy", - "recursive", - "--strategy-option", - "theirs", - f"{commit}", - ) - except GitCommandError as e: - if CHERRYPICK_EMPTY not in e.stderr: - print("Unexpected error: ", e) - raise - # Reset the temp branch to the working index - repo.git.checkout("-B", temp_branch, "HEAD") - # Reset the base - repo.git.fetch("--force", repo_url, f"{base}:{base}") - - # Try to fetch the pull request branch - if not fetch_successful(repo, repo_url, branch): - # The pull request branch does not exist - print(f"Pull request branch '{branch}' does not exist yet") - # Create the pull request branch - repo.git.checkout("HEAD", b=branch) - # Check if the pull request branch is ahead of the base - diff = is_ahead(repo, base, branch) - if diff: - action = "created" - print(f"Created branch '{branch}'") - else: - print( - f"Branch '{branch}' is not ahead of base '{base}' and will not be created" - ) - else: - # The pull request branch exists - print( - f"Pull request branch '{branch}' already exists as remote branch 'origin/{branch}'" - ) - # Checkout the pull request branch - repo.git.checkout(branch) - - if has_diff(repo, branch, temp_branch): - # If the branch differs from the recreated temp version then the branch is reset - # For changes on base this action is similar to a rebase of the pull request branch - print(f"Resetting '{branch}'") - repo.git.checkout("-B", branch, temp_branch) - # repo.git.switch("-C", branch, temp_branch) - - # Check if the pull request branch has been updated - # If the branch was reset or updated it will be ahead - # It may be behind if a reset now results in no diff with the base - if not is_even(repo, f"origin/{branch}", branch): - action = "updated" - print(f"Updated branch '{branch}'") - else: - print(f"Branch '{branch}' is even with its remote and will not be updated") - - # Check if the pull request branch is ahead of the base - diff = is_ahead(repo, base, branch) - - # Delete the temporary branch - repo.git.branch("--delete", "--force", temp_branch) - - return {"action": action, "diff": diff, "base": base} diff --git a/src/cpr/create_or_update_pull_request.py b/src/cpr/create_or_update_pull_request.py deleted file mode 100644 index a34b090..0000000 --- a/src/cpr/create_or_update_pull_request.py +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env python3 -""" Create or Update Pull Request """ -from github import Github, GithubException -import os - - -def string_to_bool(str): - if str is None: - return False - else: - return str.lower() in [ - "true", - "1", - "t", - "y", - "yes", - "on", - ] - - -def cs_string_to_list(str): - # Split the comma separated string into a list - l = [i.strip() for i in str.split(",")] - # Remove empty strings - return list(filter(None, l)) - - -def create_project_card(github_repo, project_name, project_column_name, pull_request): - # Locate the project by name - project = None - for project_item in github_repo.get_projects("all"): - if project_item.name == project_name: - project = project_item - break - - if not project: - print("::error::Project not found. Unable to create project card.") - return - - # Locate the column by name - column = None - for column_item in project.get_columns(): - if column_item.name == project_column_name: - column = column_item - break - - if not column: - print("::error::Project column not found. Unable to create project card.") - return - - # Create a project card for the pull request - column.create_card(content_id=pull_request.id, content_type="PullRequest") - print( - "Added pull request #%d to project '%s' under column '%s'" - % (pull_request.number, project.name, column.name) - ) - - -def create_or_update_pull_request( - github_token, - github_repository, - branch, - base, - title, - body, - labels, - assignees, - milestone, - reviewers, - team_reviewers, - project_name, - project_column_name, - draft, - request_to_parent, -): - github_repo = head_repo = Github(github_token).get_repo(github_repository) - if string_to_bool(request_to_parent): - github_repo = github_repo.parent - if github_repo is None: - raise ValueError( - "The checked out repository is not a fork. Input 'request-to-parent' should be set to false." - ) - - head_branch = f"{head_repo.owner.login}:{branch}" - - # Create the pull request - try: - pull_request = github_repo.create_pull( - title=title, - body=body, - base=base, - head=head_branch, - draft=string_to_bool(draft), - ) - print( - f"Created pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})" - ) - except GithubException as e: - if e.status == 422: - # A pull request exists for this branch and base - # Get the pull request - pull_request = github_repo.get_pulls( - state="open", base=base, head=head_branch - )[0] - # Update title and body - pull_request.as_issue().edit(title=title, body=body) - print( - f"Updated pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})" - ) - else: - print(str(e)) - raise - - # Set the output variables - os.system(f"echo ::set-env name=PULL_REQUEST_NUMBER::{pull_request.number}") - os.system(f"echo ::set-output name=pull-request-number::{pull_request.number}") - # 'pr_number' is deprecated - os.system(f"echo ::set-output name=pr_number::{pull_request.number}") - - # Set labels, assignees and milestone - if labels is not None: - print(f"Applying labels '{labels}'") - pull_request.as_issue().edit(labels=cs_string_to_list(labels)) - if assignees is not None: - print(f"Applying assignees '{assignees}'") - pull_request.as_issue().edit(assignees=cs_string_to_list(assignees)) - if milestone is not None: - print(f"Applying milestone '{milestone}'") - milestone = github_repo.get_milestone(int(milestone)) - pull_request.as_issue().edit(milestone=milestone) - - # Set pull request reviewers - if reviewers is not None: - print(f"Requesting reviewers '{reviewers}'") - try: - pull_request.create_review_request(reviewers=cs_string_to_list(reviewers)) - except GithubException as e: - # Likely caused by "Review cannot be requested from pull request author." - if e.status == 422: - print("Request reviewers failed - {}".format(e.data["message"])) - - # Set pull request team reviewers - if team_reviewers is not None: - print(f"Requesting team reviewers '{team_reviewers}'") - pull_request.create_review_request( - team_reviewers=cs_string_to_list(team_reviewers) - ) - - # Create a project card for the pull request - if project_name is not None and project_column_name is not None: - try: - create_project_card( - github_repo, project_name, project_column_name, pull_request - ) - except GithubException as e: - # Likely caused by "Project already has the associated issue." - if e.status == 422: - print( - "Create project card failed - {}".format( - e.data["errors"][0]["message"] - ) - ) diff --git a/src/cpr/create_pull_request.py b/src/cpr/create_pull_request.py deleted file mode 100755 index ead7f02..0000000 --- a/src/cpr/create_pull_request.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/env python3 -""" Create Pull Request """ -import base64 -import common as cmn -import create_or_update_branch as coub -import create_or_update_pull_request as coupr -from git import Repo, GitCommandError -import json -import os -import sys -import time - - -# Default the committer and author to the GitHub Actions bot -DEFAULT_COMMITTER = "GitHub " -DEFAULT_AUTHOR = ( - "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" -) -DEFAULT_COMMIT_MESSAGE = "[create-pull-request] automated change" -DEFAULT_TITLE = "Changes by create-pull-request action" -DEFAULT_BODY = ( - "Automated changes by " - + "[create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action" -) -DEFAULT_BRANCH = "create-pull-request/patch" - - -def get_git_config_value(repo, name): - try: - return repo.git.config("--get", name) - except GitCommandError: - return None - - -def get_repository_detail(repo): - remote_origin_url = get_git_config_value(repo, "remote.origin.url") - if remote_origin_url is None: - raise ValueError("Failed to fetch 'remote.origin.url' from git config") - protocol, github_repository = cmn.parse_github_repository(remote_origin_url) - return remote_origin_url, protocol, github_repository - - -def git_user_config_is_set(repo): - name = get_git_config_value(repo, "user.name") - email = get_git_config_value(repo, "user.email") - - if name is not None and email is not None: - print(f"Git user already configured as '{name} <{email}>'") - return True - - committer_name = get_git_config_value(repo, "committer.name") - committer_email = get_git_config_value(repo, "committer.email") - author_name = get_git_config_value(repo, "author.name") - author_email = get_git_config_value(repo, "author.email") - - if ( - committer_name is not None - and committer_email is not None - and author_name is not None - and author_email is not None - ): - print( - f"Git committer already configured as '{committer_name} <{committer_email}>'" - ) - print(f"Git author already configured as '{author_name} <{author_email}>'") - return True - - return False - - -def set_committer_author(repo, committer, author): - # If either committer or author is supplied they will be cross used - if committer is None and author is not None: - print("Supplied author will also be used as the committer.") - committer = author - if author is None and committer is not None: - print("Supplied committer will also be used as the author.") - author = committer - - # If no committer/author has been supplied but user configuration already - # exists in git config we can exit and use the existing config as-is. - if committer is None and author is None: - if git_user_config_is_set(repo): - return - - # Set defaults if no committer/author has been supplied - if committer is None and author is None: - committer = DEFAULT_COMMITTER - author = DEFAULT_AUTHOR - - # Set git environment. This will not persist after the action completes. - committer_name, committer_email = cmn.parse_display_name_email(committer) - author_name, author_email = cmn.parse_display_name_email(author) - repo.git.update_environment( - GIT_COMMITTER_NAME=committer_name, - GIT_COMMITTER_EMAIL=committer_email, - GIT_AUTHOR_NAME=author_name, - GIT_AUTHOR_EMAIL=author_email, - ) - print(f"Configured git committer as '{committer_name} <{committer_email}>'") - print(f"Configured git author as '{author_name} <{author_email}>'") - - -# Get required environment variables -github_token = os.environ["GITHUB_TOKEN"] -# Get environment variables with defaults -path = os.getenv("CPR_PATH", os.getcwd()) -branch = os.getenv("CPR_BRANCH", DEFAULT_BRANCH) -commit_message = os.getenv("CPR_COMMIT_MESSAGE", DEFAULT_COMMIT_MESSAGE) -# Get environment variables with a default of 'None' -committer = os.environ.get("CPR_COMMITTER") -author = os.environ.get("CPR_AUTHOR") -base = os.environ.get("CPR_BASE") - -# Set the repo path -repo = Repo(path) - -# Determine the GitHub repository from git config -# This will be the target repository for the pull request -repo_url, protocol, github_repository = get_repository_detail(repo) -print(f"Target repository set to {github_repository}") - -if protocol == "HTTPS": - print(f"::debug::Using HTTPS protocol") - # Encode and configure the basic credential for HTTPS access - basic_credential = base64.b64encode( - f"x-access-token:{github_token}".encode("utf-8") - ).decode("utf-8") - # Mask the basic credential in logs and debug output - print(f"::add-mask::{basic_credential}") - repo.git.set_persistent_git_options( - c=f"http.https://github.com/.extraheader=AUTHORIZATION: basic {basic_credential}" - ) - -# Determine if the checked out ref is a valid base for a pull request -# The action needs the checked out HEAD ref to be a branch -# This check will fail in the following cases: -# - HEAD is detached -# - HEAD is a merge commit (pull_request events) -# - HEAD is a tag -try: - working_base = repo.git.symbolic_ref("HEAD", "--short") -except GitCommandError as e: - print(f"::debug::{e.stderr}") - print( - f"::error::The checked out ref is not a valid base for a pull request. " - + "Unable to continue. Exiting." - ) - sys.exit(1) - -# Exit if the working base is a PR branch created by this action. -# This may occur when using a PAT instead of GITHUB_TOKEN because -# a PAT allows workflow actions to trigger further events. -if working_base.startswith(branch): - print( - f"::error::Working base branch '{working_base}' was created by this action. " - + "Unable to continue. Exiting." - ) - sys.exit(1) - -# Fetch an optional environment variable to determine the branch suffix -branch_suffix = os.environ.get("CPR_BRANCH_SUFFIX") -if branch_suffix is not None: - if branch_suffix == "short-commit-hash": - # Suffix with the short SHA1 hash - branch = "{}-{}".format(branch, repo.git.rev_parse("--short", "HEAD")) - elif branch_suffix == "timestamp": - # Suffix with the current timestamp - branch = "{}-{}".format(branch, int(time.time())) - elif branch_suffix == "random": - # Suffix with a 7 character random string - branch = "{}-{}".format(branch, cmn.get_random_string()) - else: - print( - f"::error::Branch suffix '{branch_suffix}' is not a valid value. " - + "Unable to continue. Exiting." - ) - sys.exit(1) - -# Output head branch -print(f"Pull request branch to create or update set to '{branch}'") - -# Set the committer and author -try: - set_committer_author(repo, committer, author) -except ValueError as e: - print(f"::error::{e} " + "Unable to continue. Exiting.") - sys.exit(1) - -# Create or update the pull request branch -result = coub.create_or_update_branch(repo, repo_url, commit_message, base, branch) - -if result["action"] in ["created", "updated"]: - # The branch was created or updated - print(f"Pushing pull request branch to 'origin/{branch}'") - repo.git.push("--force", repo_url, f"HEAD:refs/heads/{branch}") - - # Set the base. It would have been 'None' if not specified as an input - base = result["base"] - - # If there is no longer a diff with the base delete the branch and exit - if not result["diff"]: - print(f"Branch '{branch}' no longer differs from base branch '{base}'") - print(f"Closing pull request and deleting branch '{branch}'") - repo.git.push("--delete", "--force", repo_url, f"refs/heads/{branch}") - sys.exit() - - # Fetch optional environment variables with default values - title = os.getenv("CPR_TITLE", DEFAULT_TITLE) - body = os.getenv("CPR_BODY", DEFAULT_BODY) - - # Create or update the pull request - coupr.create_or_update_pull_request( - github_token, - github_repository, - branch, - base, - title, - body, - os.environ.get("CPR_LABELS"), - os.environ.get("CPR_ASSIGNEES"), - os.environ.get("CPR_MILESTONE"), - os.environ.get("CPR_REVIEWERS"), - os.environ.get("CPR_TEAM_REVIEWERS"), - os.environ.get("CPR_PROJECT_NAME"), - os.environ.get("CPR_PROJECT_COLUMN_NAME"), - os.environ.get("CPR_DRAFT"), - os.environ.get("CPR_REQUEST_TO_PARENT"), - ) diff --git a/src/cpr/requirements.txt b/src/cpr/requirements.txt deleted file mode 100644 index 5afd424..0000000 --- a/src/cpr/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -setuptools==46.4.0 -wheel==0.34.2 -GitPython==3.1.2 -PyGithub==1.51 diff --git a/src/cpr/test_common.py b/src/cpr/test_common.py deleted file mode 100644 index 9409258..0000000 --- a/src/cpr/test_common.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -""" Test Common """ -import common as cmn -import pytest - - -def test_get_random_string(): - assert len(cmn.get_random_string()) == 7 - assert len(cmn.get_random_string(length=20)) == 20 - - -def test_parse_github_repository_success(): - protocol, repository = cmn.parse_github_repository( - "https://github.com/peter-evans/create-pull-request" - ) - assert protocol == "HTTPS" - assert repository == "peter-evans/create-pull-request" - - protocol, repository = cmn.parse_github_repository( - "https://xxx:x-oauth-basic@github.com/peter-evans/create-pull-request" - ) - assert protocol == "HTTPS" - assert repository == "peter-evans/create-pull-request" - - protocol, repository = cmn.parse_github_repository( - "git@github.com:peter-evans/create-pull-request.git" - ) - assert protocol == "SSH" - assert repository == "peter-evans/create-pull-request" - - -def test_parse_github_repository_failure(): - url = "https://github.com/peter-evans" - with pytest.raises(ValueError) as e_info: - cmn.parse_github_repository(url) - assert ( - e_info.value.args[0] - == f"The format of '{url}' is not a valid GitHub repository URL" - ) - - -def test_parse_display_name_email_success(): - name, email = cmn.parse_display_name_email("abc def ") - assert name == "abc def" - assert email == "abc@def.com" - - name, email = cmn.parse_display_name_email( - "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" - ) - assert name == "github-actions[bot]" - assert email == "41898282+github-actions[bot]@users.noreply.github.com" - - -def test_parse_display_name_email_failure(): - display_name_email = "abc@def.com" - with pytest.raises(ValueError) as e_info: - cmn.parse_display_name_email(display_name_email) - assert ( - e_info.value.args[0] - == f"The format of '{display_name_email}' is not a valid email address with display name" - ) - - display_name_email = " < >" - with pytest.raises(ValueError) as e_info: - cmn.parse_display_name_email(display_name_email) - assert ( - e_info.value.args[0] - == f"The format of '{display_name_email}' is not a valid email address with display name" - ) diff --git a/src/cpr/test_create_or_update_branch.py b/src/cpr/test_create_or_update_branch.py deleted file mode 100644 index a0f400c..0000000 --- a/src/cpr/test_create_or_update_branch.py +++ /dev/null @@ -1,757 +0,0 @@ -#!/usr/bin/env python3 -""" Test Create or Update Branch """ -import create_or_update_branch as coub -from git import Repo -import os -import pytest -import sys -import time - - -# Set git repo -REPO_PATH = os.getenv("COUB_REPO_PATH", os.getcwd()) -repo = Repo(REPO_PATH) - -# Set git environment -author_name = "github-actions[bot]" -author_email = "41898282+github-actions[bot]@users.noreply.github.com" -committer_name = "GitHub" -committer_email = "noreply@github.com" -repo.git.update_environment( - GIT_AUTHOR_NAME=author_name, - GIT_AUTHOR_EMAIL=author_email, - GIT_COMMITTER_NAME=committer_name, - GIT_COMMITTER_EMAIL=committer_email, -) - -REPO_URL = repo.git.config("--get", "remote.origin.url") - -TRACKED_FILE = "tracked-file.txt" -UNTRACKED_FILE = "untracked-file.txt" - -DEFAULT_BRANCH = "tests/master" -NOT_BASE_BRANCH = "tests/branch-that-is-not-the-base" -NOT_EXIST_BRANCH = "tests/branch-that-does-not-exist" - -COMMIT_MESSAGE = "[create-pull-request] automated change" -BRANCH = "tests/create-pull-request/patch" -BASE = DEFAULT_BRANCH - - -def create_tracked_change(content=None): - if content is None: - content = str(time.time()) - # Create a tracked file change - with open(os.path.join(REPO_PATH, TRACKED_FILE), "w") as f: - f.write(content) - return content - - -def create_untracked_change(content=None): - if content is None: - content = str(time.time()) - # Create an untracked file change - with open(os.path.join(REPO_PATH, UNTRACKED_FILE), "w") as f: - f.write(content) - return content - - -def get_tracked_content(): - # Read the content of the tracked file - with open(os.path.join(REPO_PATH, TRACKED_FILE), "r") as f: - return f.read() - - -def get_untracked_content(): - # Read the content of the untracked file - with open(os.path.join(REPO_PATH, UNTRACKED_FILE), "r") as f: - return f.read() - - -def create_changes(tracked_content=None, untracked_content=None): - tracked_content = create_tracked_change(tracked_content) - untracked_content = create_untracked_change(untracked_content) - return tracked_content, untracked_content - - -def create_commits(number=2, final_tracked_content=None, final_untracked_content=None): - for i in range(number): - commit_number = i + 1 - if commit_number == number: - tracked_content, untracked_content = create_changes( - final_tracked_content, final_untracked_content - ) - else: - tracked_content, untracked_content = create_changes() - repo.git.add("-A") - repo.git.commit(m=f"Commit {commit_number}") - return tracked_content, untracked_content - - -@pytest.fixture(scope="module", autouse=True) -def before_after_all(): - print("Before all tests") - # Check there are no local changes that might be - # destroyed by running these tests - assert not repo.is_dirty(untracked_files=True) - - # Create a new default branch for the test run - repo.remotes.origin.fetch() - repo.git.checkout("master") - repo.git.checkout("HEAD", b=NOT_BASE_BRANCH) - create_tracked_change() - repo.git.add("-A") - repo.git.commit(m="This commit should not appear in pr branches") - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{NOT_BASE_BRANCH}") - # Create a new default branch for the test run - repo.git.checkout("master") - repo.git.checkout("HEAD", b=DEFAULT_BRANCH) - create_tracked_change() - repo.git.add("-A") - repo.git.commit(m="Add file to be a tracked file for tests") - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - - yield - - print("After all tests") - repo.git.checkout("master") - # Delete the "not base branch" created for the test run - repo.git.branch("--delete", "--force", NOT_BASE_BRANCH) - repo.git.push("--delete", "--force", REPO_URL, f"refs/heads/{NOT_BASE_BRANCH}") - # Delete the default branch created for the test run - repo.git.branch("--delete", "--force", DEFAULT_BRANCH) - repo.git.push("--delete", "--force", REPO_URL, f"refs/heads/{DEFAULT_BRANCH}") - - -def before_test(): - print("Before test") - # Checkout the default branch - repo.git.checkout(DEFAULT_BRANCH) - - -def after_test(delete_remote=True): - print("After test") - # Output git log - print(repo.git.log("-5", pretty="oneline")) - # Delete the pull request branch if it exists - repo.git.checkout(DEFAULT_BRANCH) - print(f"Deleting {BRANCH}") - for branch in repo.branches: - if branch.name == BRANCH: - repo.git.branch("--delete", "--force", BRANCH) - break - if delete_remote: - print(f"Deleting origin/{BRANCH}") - for ref in repo.remotes.origin.refs: - if ref.name == f"origin/{BRANCH}": - repo.git.push("--delete", "--force", REPO_URL, f"refs/heads/{BRANCH}") - repo.remotes.origin.fetch("--prune") - break - - -@pytest.fixture(autouse=True) -def before_after_tests(): - before_test() - yield - after_test() - - -# Tests if a branch exists and can be fetched -def coub_fetch_successful(): - assert coub.fetch_successful(repo, REPO_URL, NOT_BASE_BRANCH) - assert not coub.fetch_successful(repo, REPO_URL, NOT_EXIST_BRANCH) - - -# Tests no changes resulting in no new branch being created -def coub_no_changes_on_create(): - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "none" - - -# Tests create and update with a tracked file change -def coub_tracked_changes(): - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - - -# Tests create and update with an untracked file change -def coub_untracked_changes(): - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_untracked_content() == untracked_content - - -# Tests create and update with identical changes -# The pull request branch will not be updated -def coub_identical_changes(): - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create identical tracked and untracked file changes - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "none" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with commits on the base inbetween -def coub_commits_on_base(): - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and then an update with no changes -# This effectively reverts the branch back to match the base and results in no diff -def coub_changes_no_diff(): - # Save the default branch tracked content - default_tracked_content = get_tracked_content() - - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Running with no update effectively reverts the branch back to match the base - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == default_tracked_content - - -# Tests create and update with commits on the base inbetween -# The changes on base effectively revert the branch back to match the base and results in no diff -def coub_commits_on_base_no_diff(): - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - tracked_content, untracked_content = create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Create the same tracked and untracked file changes that were made to the base - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with commits on the working base (during the workflow) -def coub_commits_on_working_base(): - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with changes and commits on the working base (during the workflow) -def coub_changes_and_commits_on_working_base(): - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Tests create and update with changes and commits on the working base (during the workflow) -# with commits on the base inbetween -def coub_changes_and_commits_on_base_and_working_base(): - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, None, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests no changes resulting in no new branch being created -def coub_wbnb_no_changes_on_create(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "none" - - -# Working Base is Not Base (WBNB) -# Tests create and update with a tracked file change -def coub_wbnb_tracked_changes(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create a tracked file change - tracked_content = create_tracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with an untracked file change -def coub_wbnb_untracked_changes(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create an untracked file change - untracked_content = create_untracked_change() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with identical changes -# The pull request branch will not be updated -def coub_wbnb_identical_changes(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create identical tracked and untracked file changes - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "none" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with commits on the base inbetween -def coub_wbnb_commits_on_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and then an update with no changes -# This effectively reverts the branch back to match the base and results in no diff -def coub_wbnb_changes_no_diff(): - # Save the default branch tracked content - default_tracked_content = get_tracked_content() - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Running with no update effectively reverts the branch back to match the base - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == default_tracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with commits on the base inbetween -# The changes on base effectively revert the branch back to match the base and results in no diff -# This scenario will cause cherrypick to fail due to an empty commit. -# The commit is empty because the changes now exist on the base. -def coub_wbnb_commits_on_base_no_diff(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - tracked_content, untracked_content = create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create the same tracked and untracked file changes that were made to the base - create_changes(tracked_content, untracked_content) - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] == False - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with commits on the working base (during the workflow) -def coub_wbnb_commits_on_working_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - tracked_content, untracked_content = create_commits() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with changes and commits on the working base (during the workflow) -def coub_wbnb_changes_and_commits_on_working_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# Working Base is Not Base (WBNB) -# Tests create and update with changes and commits on the working base (during the workflow) -# with commits on the base inbetween -def coub_wbnb_changes_and_commits_on_base_and_working_base(): - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "created" - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - # Push pull request branch to remote - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{BRANCH}") - repo.remotes.origin.fetch() - - after_test(delete_remote=False) - before_test() - - # Create commits on the base - create_commits() - repo.git.push("--force", REPO_URL, f"HEAD:refs/heads/{DEFAULT_BRANCH}") - repo.remotes.origin.fetch() - - # Set the working base to a branch that is not the pull request base - repo.git.checkout(NOT_BASE_BRANCH) - # Create commits on the working base - create_commits() - # Create tracked and untracked file changes - tracked_content, untracked_content = create_changes() - result = coub.create_or_update_branch(repo, REPO_URL, COMMIT_MESSAGE, BASE, BRANCH) - assert result["action"] == "updated" - assert result["diff"] - assert get_tracked_content() == tracked_content - assert get_untracked_content() == untracked_content - - -# pytest -v -s ~/git/create-pull-request/src - -test_coub_fetch_successful = coub_fetch_successful - -test_coub_no_changes_on_create = coub_no_changes_on_create -test_coub_tracked_changes = coub_tracked_changes -test_coub_untracked_changes = coub_untracked_changes -test_coub_identical_changes = coub_identical_changes -test_coub_commits_on_base = coub_commits_on_base - -test_coub_changes_no_diff = coub_changes_no_diff -test_coub_commits_on_base_no_diff = coub_commits_on_base_no_diff - -test_coub_commits_on_working_base = coub_commits_on_working_base -test_coub_changes_and_commits_on_working_base = coub_changes_and_commits_on_working_base -test_coub_changes_and_commits_on_base_and_working_base = ( - coub_changes_and_commits_on_base_and_working_base -) - -# WBNB -test_coub_wbnb_no_changes_on_create = coub_wbnb_no_changes_on_create -test_coub_wbnb_tracked_changes = coub_wbnb_tracked_changes -test_coub_wbnb_untracked_changes = coub_wbnb_untracked_changes -test_coub_wbnb_identical_changes = coub_wbnb_identical_changes -test_coub_wbnb_commits_on_base = coub_wbnb_commits_on_base - -test_coub_wbnb_changes_no_diff = coub_wbnb_changes_no_diff -test_coub_wbnb_commits_on_base_no_diff = coub_wbnb_commits_on_base_no_diff - -test_coub_wbnb_commits_on_working_base = coub_wbnb_commits_on_working_base -test_coub_wbnb_changes_and_commits_on_working_base = ( - coub_wbnb_changes_and_commits_on_working_base -) -test_coub_wbnb_changes_and_commits_on_base_and_working_base = ( - coub_wbnb_changes_and_commits_on_base_and_working_base -) diff --git a/src/isDocker.ts b/src/isDocker.ts deleted file mode 100644 index 26724f7..0000000 --- a/src/isDocker.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as fs from 'fs' - -function hasDockerEnv(): boolean { - try { - fs.statSync('/.dockerenv') - return true - } catch (_) { - return false - } -} - -function hasDockerCGroup(): boolean { - try { - return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker') - } catch (_) { - return false - } -} - -export function isDocker(): boolean { - return hasDockerEnv() || hasDockerCGroup() -} diff --git a/src/setupPython.ts b/src/setupPython.ts deleted file mode 100644 index d6f1cb8..0000000 --- a/src/setupPython.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as core from '@actions/core' -import * as tc from '@actions/tool-cache' -import * as path from 'path' -import * as semver from 'semver' - -/** - * Setup for Python from the GitHub Actions tool cache - * Converted from https://github.com/actions/setup-python - * - * @param {string} versionSpec version of Python - * @param {string} arch architecture (x64|x32) - */ -export function setupPython(versionSpec: string, arch: string): Promise { - return new Promise(resolve => { - const IS_WINDOWS = process.platform === 'win32' - - // Find the version of Python we want in the tool cache - const installDir = tc.find('Python', versionSpec, arch) - core.debug(`installDir: ${installDir}`) - - // Set paths - core.exportVariable('pythonLocation', installDir) - core.addPath(installDir) - if (IS_WINDOWS) { - core.addPath(path.join(installDir, 'Scripts')) - } else { - core.addPath(path.join(installDir, 'bin')) - } - - if (IS_WINDOWS) { - // Add --user directory - // `installDir` from tool cache should look like $AGENT_TOOLSDIRECTORY/Python//x64/ - // So if `findLocalTool` succeeded above, we must have a conformant `installDir` - const version = path.basename(path.dirname(installDir)) - const major = semver.major(version) - const minor = semver.minor(version) - - const userScriptsDir = path.join( - process.env['APPDATA'] || '', - 'Python', - `Python${major}${minor}`, - 'Scripts' - ) - core.addPath(userScriptsDir) - } - // On Linux and macOS, pip will create the --user directory and add it to PATH as needed. - - resolve() - }) -}