[git] Add ls_remote to git recipe module

Call `git ls-remote` to determine the head revision for a branch, or
the revision for a tag. The method is useful for bots which do not
require a checkout.

Bug: none
Change-Id: Ibc01d1fb9c2714bd4d719159dbdaf3d7684ea1e9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/6012563
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Commit-Queue: Alexander Schulze <alexschulze@chromium.org>
Reviewed-by: Gavin Mak <gavinmak@google.com>
changes/63/6012563/6
Alex Schulze 7 months ago committed by LUCI CQ
parent 1538c996af
commit c22f4dc74f

@ -30,6 +30,7 @@
* [gclient:tests/sync_failure](#recipes-gclient_tests_sync_failure)
* [gerrit:examples/full](#recipes-gerrit_examples_full)
* [git:examples/full](#recipes-git_examples_full)
* [git:tests/ls_remote](#recipes-git_tests_ls_remote)
* [git:tests/number](#recipes-git_tests_number)
* [git_cl:examples/full](#recipes-git_cl_examples_full)
* [gitiles:examples/full](#recipes-gitiles_examples_full)
@ -536,6 +537,20 @@ Returns: (str) The URL of the remote Git repository, or None.
Find and return the timestamp of the given commit.
&mdash; **def [ls\_remote](/recipes/recipe_modules/git/api.py#472)(self, url, ref, name=None, tags=True, branches=True, \*\*kwargs):**
Request the head revision for a given ref using ls-remote. Raise a
StepFailure if the ref does not exist, or more than one ref was found.
Args:
* url (str): url of remote repo to use as upstream.
* ref (str): ref to query head revision.
* name (str): Name of the infra step.
* tags (bool): Include tags.
* branches (bool): Include branches.
Returns: A git revision.
&mdash; **def [new\_branch](/recipes/recipe_modules/git/api.py#404)(self, branch, name=None, upstream=None, upstream_current=False, \*\*kwargs):**
Runs git new-branch on a Git repository, to be used before git cl
@ -1135,6 +1150,12 @@ Raises:
&mdash; **def [RunSteps](/recipes/recipe_modules/git/examples/full.py#20)(api):**
### *recipes* / [git:tests/ls\_remote](/recipes/recipe_modules/git/tests/ls_remote.py)
[DEPS](/recipes/recipe_modules/git/tests/ls_remote.py#7): [git](#recipe_modules-git), [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
&mdash; **def [RunSteps](/recipes/recipe_modules/git/tests/ls_remote.py#18)(api):**
### *recipes* / [git:tests/number](/recipes/recipe_modules/git/tests/number.py)
[DEPS](/recipes/recipe_modules/git/tests/number.py#9): [git](#recipe_modules-git), [recipe\_engine/assertions][recipe_engine/recipe_modules/assertions], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/properties][recipe_engine/recipe_modules/properties]

@ -468,3 +468,41 @@ class GitApi(recipe_api.RecipeApi):
stdout=self.m.raw_io.output_text(add_output_log=True),
step_test_data=step_test_data)
return [l.strip() for l in step_result.stdout.strip().splitlines()]
def ls_remote(self, url, ref, name=None, tags=True, branches=True, **kwargs):
"""Request the head revision for a given ref using ls-remote. Raise a
StepFailure if the ref does not exist, or more than one ref was found.
Args:
* url (str): url of remote repo to use as upstream.
* ref (str): ref to query head revision.
* name (str): Name of the infra step.
* tags (bool): Include tags.
* branches (bool): Include branches.
Returns: A git revision.
"""
cwd = self.m.context.cwd or self.m.path.start_dir
name = name or f'Retrieve revision for {ref}'
cmd = ['ls-remote']
if tags:
cmd.append('-t')
if branches:
cmd.append('-b')
cmd.extend([url, ref])
with self.m.context(cwd):
result = self(*cmd,
name=name,
stdout=self.m.raw_io.output_text(),
**kwargs)
lines = result.stdout.strip().splitlines()
if len(lines) > 1:
raise self.m.step.StepFailure(f'Multiple remote refs found for {ref}')
if not lines:
raise self.m.step.StepFailure(f'No remote ref found for {ref}')
return lines[0].split('\t')[0]

@ -0,0 +1,104 @@
# Copyright 2024 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from recipe_engine import post_process as post
DEPS = [
'recipe_engine/json',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/step',
'git',
]
REPO_URL = 'https://chromium.googlesource.com/v8/v8'
def RunSteps(api):
url = api.properties.get('url', REPO_URL)
ref = api.properties.get('ref', 'main')
tags = api.properties.get('tags', True)
branches = api.properties.get('branches', True)
result = api.git.ls_remote(url, ref, tags=tags, branches=branches)
api.step.empty('revision', step_text=result)
def GenTests(api):
def mock_ls_remote(ref, revision_refs, retcode=None):
lines = [f"{revision}\t{ref}" for revision, ref in revision_refs] + ['']
return api.step_data(
f'Retrieve revision for {ref}',
api.raw_io.stream_output_text(
'\n'.join(lines),
retcode=retcode or 0,
stream='stdout',
),
)
def test(name, cmd, *checks, **kwargs):
ref = cmd[-1]
return api.test(
name,
api.post_process(
post.StepCommandEquals,
f'Retrieve revision for {ref}',
cmd,
),
*checks,
api.post_process(post.DropExpectation),
**kwargs,
)
yield test(
'basic',
['git', 'ls-remote', '-t', '-b', REPO_URL, 'main'],
mock_ls_remote('main', [('badc0ffee0ded', 'refs/heads/main')]),
api.post_process(
post.StepTextEquals,
'revision',
'badc0ffee0ded',
),
)
yield test(
'no-tags',
['git', 'ls-remote', '-b', REPO_URL, 'main'],
api.properties(tags=False),
mock_ls_remote('main', [('badc0ffee0ded', 'refs/heads/main')]),
)
yield test(
'no-branches',
['git', 'ls-remote', '-t', REPO_URL, 'main'],
api.properties(branches=False),
mock_ls_remote('main', [('badc0ffee0ded', 'refs/heads/main')]),
)
yield test(
'multiple-refs',
['git', 'ls-remote', '-t', '-b', REPO_URL, '13.3.19'],
api.properties(ref='13.3.19'),
mock_ls_remote('13.3.19', [
('badc0ffee0ded', 'refs/heads/13.3.19'),
('c001c001c001d', 'refs/tags/13.3.19'),
]),
api.post_process(post.StatusFailure),
api.post_process(post.SummaryMarkdown,
"Multiple remote refs found for 13.3.19"),
status='FAILURE',
)
yield test(
'no-refs',
['git', 'ls-remote', '-t', '-b', REPO_URL, '13.3'],
api.properties(ref='13.3'),
mock_ls_remote('13.3', []),
api.post_process(post.StatusFailure),
api.post_process(post.SummaryMarkdown, "No remote ref found for 13.3"),
status='FAILURE',
)
Loading…
Cancel
Save