diff --git a/recipes/recipe_modules/gitiles/api.py b/recipes/recipe_modules/gitiles/api.py index 42c4086da..84b20495f 100644 --- a/recipes/recipe_modules/gitiles/api.py +++ b/recipes/recipe_modules/gitiles/api.py @@ -3,6 +3,7 @@ # found in the LICENSE file. import base64 +import urlparse from recipe_engine import recipe_api @@ -200,3 +201,40 @@ class Gitiles(recipe_api.RecipeApi): ex = self.m.step.StepFailure(step_name) ex.gitiles_skipped_files = stat['names'] raise ex + + def parse_repo_url(self, repo_url): + """Returns (host, project) pair. + + Returns (None, None) if repo_url is not recognized. + """ + return parse_repo_url(repo_url) + + +def parse_http_host_and_path(url): + # Copied from https://chromium.googlesource.com/infra/luci/recipes-py/+/809e57935211b3fcb802f74a7844d4f36eff6b87/recipe_modules/buildbucket/util.py + parsed = urlparse.urlparse(url) + if not parsed.scheme: + parsed = urlparse.urlparse('https://' + url) + if (parsed.scheme in ('http', 'https') and + not parsed.params and + not parsed.query and + not parsed.fragment): + return parsed.netloc, parsed.path + return None, None + + +def parse_repo_url(repo_url): + """Returns (host, project) pair. + + Returns (None, None) if repo_url is not recognized. + """ + # Adapted from https://chromium.googlesource.com/infra/luci/recipes-py/+/809e57935211b3fcb802f74a7844d4f36eff6b87/recipe_modules/buildbucket/util.py + host, project = parse_http_host_and_path(repo_url) + if not host or not project or '+' in project.split('/'): + return None, None + project = project.strip('/') + if project.startswith('a/'): + project = project[len('a/'):] + if project.endswith('.git'): + project = project[:-len('.git')] + return host, project diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/a prefix.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/a prefix.json new file mode 100644 index 000000000..0a04299de --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/a prefix.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "host", + "path/to/project" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/basic.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/basic.json new file mode 100644 index 000000000..0a04299de --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/basic.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "host", + "path/to/project" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/git suffix.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/git suffix.json new file mode 100644 index 000000000..0a04299de --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/git suffix.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "host", + "path/to/project" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/http and a prefix.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/http and a prefix.json new file mode 100644 index 000000000..0a04299de --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/http and a prefix.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "host", + "path/to/project" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/http.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/http.json new file mode 100644 index 000000000..0a04299de --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/http.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "host", + "path/to/project" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/no scheme.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/no scheme.json new file mode 100644 index 000000000..0a04299de --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/no scheme.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "host", + "path/to/project" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/plus.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/plus.json new file mode 100644 index 000000000..6ad75e3ea --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/plus.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "None", + "None" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/query string param.json b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/query string param.json new file mode 100644 index 000000000..6ad75e3ea --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.expected/query string param.json @@ -0,0 +1,15 @@ +[ + { + "cmd": [ + "echo", + "None", + "None" + ], + "name": "build" + }, + { + "name": "$result", + "recipe_result": null, + "status_code": 0 + } +] \ No newline at end of file diff --git a/recipes/recipe_modules/gitiles/tests/parse_repo_url.py b/recipes/recipe_modules/gitiles/tests/parse_repo_url.py new file mode 100644 index 000000000..570dc06fe --- /dev/null +++ b/recipes/recipe_modules/gitiles/tests/parse_repo_url.py @@ -0,0 +1,30 @@ +# Copyright 2018 The LUCI Authors. All rights reserved. +# Use of this source code is governed under the Apache License, Version 2.0 +# that can be found in the LICENSE file. + +DEPS = [ + 'gitiles', + 'recipe_engine/properties', + 'recipe_engine/step', +] + + +def RunSteps(api): + repo_url = api.properties['repo_url'] + host, project = api.gitiles.parse_repo_url(repo_url) + api.step('build', ['echo', str(host), str(project)]) + + +def GenTests(api): + + def case(name, repo_url): + return api.test(name) + api.properties(repo_url=repo_url) + + yield case('basic', 'https://host/path/to/project') + yield case('http', 'http://host/path/to/project') + yield case('a prefix', 'https://host/a/path/to/project') + yield case('git suffix', 'https://host/path/to/project.git') + yield case('http and a prefix', 'http://host/a/path/to/project') + yield case('no scheme', 'host/a/path/to/project') + yield case('query string param', 'https://host/a/path/to/project?a=b') + yield case('plus', 'https://host/path/to/project/+/master')