diff --git a/alts/shared/utils/git_utils.py b/alts/shared/utils/git_utils.py index 9cb23e6..ee8c75e 100644 --- a/alts/shared/utils/git_utils.py +++ b/alts/shared/utils/git_utils.py @@ -135,11 +135,18 @@ def clone_git_repo( def prepare_gerrit_command(git_ref: str) -> str: + # The repo is reused between runs, so a previous checkout (or the test + # run itself) can leave the working tree dirty with modified tracked + # files and untracked leftovers. Reset and clean first, otherwise the + # subsequent `git checkout` aborts with "local changes/untracked working + # tree files would be overwritten by checkout". + cleanup = 'git reset --hard && git clean -fdx' command = '' if git_ref == 'master': - command = 'git checkout master && git pull' + command = f'{cleanup} && git checkout master && git pull' elif '/' not in git_ref and not git_ref.isdigit(): command = ( + f'{cleanup} && ' f'git reset --hard origin/{git_ref} && ' f'git checkout {git_ref} && git pull' ) @@ -147,6 +154,7 @@ def prepare_gerrit_command(git_ref: str) -> str: review, patchset = git_ref.split('/') sm = review[-2:] command = ( + f'{cleanup} && ' 'git checkout master && git pull && ' f"git fetch origin 'refs/changes/{sm}/{review}/{patchset}' " '--force --update-head-ok --progress && ' diff --git a/alts/worker/runners/base.py b/alts/worker/runners/base.py index fb6832e..86ce561 100644 --- a/alts/worker/runners/base.py +++ b/alts/worker/runners/base.py @@ -1916,7 +1916,14 @@ def clone_third_party_repo( repo_path = Path(self._tests_dir, subpath) result = None for attempt in range(1, 6): + # The repo persists between runs on the VM, so a previous + # test run can leave the working tree dirty with modified + # tracked files and untracked leftovers. Clean before + # switching refs, otherwise a later `git checkout` aborts + # with "local changes/untracked working tree files would be + # overwritten by checkout". cmd = (f'if [ -e {repo_path} ]; then cd {repo_path} && ' + f'git reset --hard && git clean -fdx && ' f'git reset --hard origin/master && git checkout master && git pull; ' f'else mkdir -p {repo_path.parent} && ' f'git clone {repo_url} {repo_path}; fi') diff --git a/resources/roles/preparation/tasks/alpine_extra_packages.yml b/resources/roles/preparation/tasks/alpine_extra_packages.yml index d540cc1..b2dc56d 100644 --- a/resources/roles/preparation/tasks/alpine_extra_packages.yml +++ b/resources/roles/preparation/tasks/alpine_extra_packages.yml @@ -13,7 +13,7 @@ curl -fSL -o "/tmp/{{ package_name }}-{{ package_version.stdout }}.apk" "{{ extra_alpine_repo }}/{{ ansible_architecture }}/{{ package_name }}-{{ package_version.stdout }}.apk" - name: "Install {{ package_name }} from downloaded package" - command: "apk add --allow-untrusted --no-network /tmp/{{ package_name }}-{{ package_version.stdout }}.apk" + command: "apk add --allow-untrusted /tmp/{{ package_name }}-{{ package_version.stdout }}.apk" - name: "Verify {{ package_name }} version was installed" shell: apk list -I {{ package_name }} | head -1 | sed 's/^{{ package_name }}-\([^ ]*\).*/\1/' diff --git a/tests/shared/test_git_utils.py b/tests/shared/test_git_utils.py index 2c8b5d0..e23694d 100644 --- a/tests/shared/test_git_utils.py +++ b/tests/shared/test_git_utils.py @@ -1,7 +1,10 @@ -"""Tests for alts.shared.utils.git_utils.repo_reference_subpath.""" +"""Tests for alts.shared.utils.git_utils.""" import pytest -from alts.shared.utils.git_utils import repo_reference_subpath +from alts.shared.utils.git_utils import ( + prepare_gerrit_command, + repo_reference_subpath, +) class TestRepoReferenceSubpath: @@ -62,3 +65,28 @@ def test_host_is_lowercased(self): def test_unparseable_url_falls_back_to_basename(self): # No scheme, no SCP-style colon — degenerate input. assert repo_reference_subpath('just-a-name') == 'just-a-name.git' + + +class TestPrepareGerritCommand: + CLEANUP = 'git reset --hard && git clean -fdx' + + def test_master_cleans_before_checkout(self): + cmd = prepare_gerrit_command('master') + assert cmd.startswith(self.CLEANUP) + assert cmd == f'{self.CLEANUP} && git checkout master && git pull' + + def test_named_branch_cleans_before_checkout(self): + cmd = prepare_gerrit_command('some-feature-branch') + assert cmd.startswith(self.CLEANUP) + assert 'git checkout some-feature-branch' in cmd + + def test_changeset_cleans_before_checkout(self): + # git_ref "review/patchset" -> refs/changes//... + cmd = prepare_gerrit_command('256840/2') + assert cmd.startswith(self.CLEANUP) + assert "git fetch origin 'refs/changes/40/256840/2'" in cmd + assert cmd.endswith('git checkout FETCH_HEAD') + + def test_incomplete_changeset_returns_empty(self): + # A ref with an empty segment isn't a valid review/patchset pair. + assert prepare_gerrit_command('256840/') == ''