From cb8d04981c76951872e323ab1250b06ccd0897cc Mon Sep 17 00:00:00 2001 From: Hannes Schmidt Date: Thu, 30 Jan 2025 22:09:06 -0800 Subject: [PATCH] [2/2] Fix: GitHub CI workflow selects dev when building prod branch (#5428) --- scripts/check_branch.py | 29 +++++++++++- test/test_check_branch.py | 93 ++++++++++++++++++++++++++++++++------- 2 files changed, 105 insertions(+), 17 deletions(-) diff --git a/scripts/check_branch.py b/scripts/check_branch.py index bc2e490ece..0342f453ed 100644 --- a/scripts/check_branch.py +++ b/scripts/check_branch.py @@ -52,7 +52,32 @@ def target_branch() -> str | None: built or, if the build is for a feature branch involving a pull request, the base branch of that feature branch. """ - for variable in 'CI_COMMIT_REF_NAME', 'GITHUB_BASE_REF', 'GITHUB_HEAD_REF': + # The comments on the environment variable names below are taken from + # + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables + # + # and + # + # https://docs.gitlab.com/ee/ci/variables/predefined_variables.html + # + for variable in [ + # The name of the base ref or target branch of the pull request in a + # workflow run. This is only set when the event that triggers a workflow + # run is either pull_request or pull_request_target. For example, main. + # + 'GITHUB_BASE_REF', + + # The short ref name of the branch or tag that triggered the workflow + # run. This value matches the branch or tag name shown on GitHub. For + # example, feature-branch-1. For pull requests, the format is + # /merge. + # + 'GITHUB_REF_NAME', + + # The branch or tag name for which project is built. + # + 'CI_COMMIT_REF_NAME', + ]: try: branch = os.environ[variable] except KeyError: @@ -60,7 +85,7 @@ def target_branch() -> str | None: else: return branch repo = git.Repo(config.project_root) - return None if repo.head.is_detached else repo.active_branch.name + return None if repo.head.is_detached else repo.head.reference.name def main(argv): diff --git a/test/test_check_branch.py b/test/test_check_branch.py index 2f044042f3..3a80e6ca83 100644 --- a/test/test_check_branch.py +++ b/test/test_check_branch.py @@ -1,9 +1,12 @@ import json import os from unittest.mock import ( + PropertyMock, patch, ) +import git + from azul.modules import ( load_script, ) @@ -14,7 +17,7 @@ class TestCheckBranch(AzulUnitTestCase): - def test(self): + def test_check_branch(self): script = load_script('check_branch') check_branch = script.check_branch @@ -71,17 +74,77 @@ def expect_exception(branch, deployment, message): "Branch 'feature/foo' cannot be deployed to 'prod', " "only one of {'sandbox'} or personal deployments.") - variables = ['CI_COMMIT_REF_NAME', 'GITHUB_BASE_REF', 'GITHUB_HEAD_REF'] - - for variable in variables: - if variable == 'GITHUB_HEAD_REF': - branches = ['foo/bar'] - else: - branches = ['develop', 'prod'] - for branch in branches: - with patch.dict(os.environ) as env: - for var in variables: - env.pop(var, None) - env[var] = branch - with self.subTest(branch=branch, variable=variable): - self.assertEqual(branch, script.target_branch()) + def test_target_branch(self): + script = load_script('check_branch') + + develop, prod = 'develop', 'prod' + feature, merge = 'issues/foo/1234-bar', '2345/merge' + cases = [ + ( + 'Local build', + feature, + {}, + feature + ), + ( + 'Local build with detached head', + None, + {}, + None + ), + ( + 'GitHub building develop', + develop, + {'GITHUB_REF_NAME': develop}, + develop + ), + ( + 'GitHub building prod', + prod, + {'GITHUB_REF_NAME': prod}, + prod + ), + ( + 'GitHub PR against develop', + merge, + { + 'GITHUB_REF_NAME': merge, + 'GITHUB_HEAD_REF': feature, + 'GITHUB_BASE_REF': develop + }, + develop + ), + ( + 'GitHub PR against prod', + merge, + { + 'GITHUB_REF_NAME': merge, + 'GITHUB_HEAD_REF': feature, + 'GITHUB_BASE_REF': prod + }, + prod + ), + ( + 'Sandbox build on GitLab', + None, + {'CI_COMMIT_REF_NAME': feature}, + feature + ), + ( + 'Non-sandbox build on GitLab', + None, + {'CI_COMMIT_REF_NAME': develop}, + develop + ), + ] + variables = {v for case in cases for v in case[2]} + for sub_test, current_branch, new_env, target_branch in cases: + with self.subTest(sub_test): + with patch.object(git.Repo, 'head', new_callable=PropertyMock) as head: + head.return_value.is_detached = current_branch is None + head.return_value.reference.name = current_branch + with patch.dict(os.environ) as env: + for variable in variables: + env.pop(variable, None) + env.update(new_env) + self.assertEqual(target_branch, script.target_branch())