Skip to content

Commit 0b19c11

Browse files
committed
Bug 1876573 - Allow 'bitrisescript' to specify multiple scope prefixes
It turns out that we need to use consistent scope prefixes as the other scriptworkers as Taskgraph configures a single prefix across all scriptworkers. This means that the repo name can appear in the prefix, which in turn means we'll need to support multiple valid prefixes per trust domain. This is how the other scriptworkers work, so it's a good change to make anyway.
1 parent aa4c5ad commit 0b19c11

File tree

8 files changed

+81
-43
lines changed

8 files changed

+81
-43
lines changed

bitrisescript/docker.d/worker.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@ verbose: { "$eval": "VERBOSE == 'true'" }
33
bitrise:
44
access_token: { "$eval": "BITRISE_ACCESS_TOKEN" }
55

6-
taskcluster_scope_prefix: "project:${COT_PRODUCT}:bitrise:"
76
trust_domain: "${COT_PRODUCT}"
7+
taskcluster_scope_prefixes:
8+
$switch:
9+
'COT_PRODUCT == "mobile"':
10+
["project:mobile:firefox-ios:releng:bitrise:"]

bitrisescript/examples/config.example.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"bitrise": {
44
"access_token": "<token>"
55
},
6-
"taskcluster_scope_prefix": ["project:mobile:firefox-ios:bitrise:"],
6+
"taskcluster_scope_prefixes": ["project:mobile:firefox-ios:bitrise:"],
77

88
"verbose": true
99
}

bitrisescript/src/bitrisescript/data/config_schema.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": "object",
44
"required": [
55
"bitrise",
6-
"taskcluster_scope_prefix",
6+
"taskcluster_scope_prefixes",
77
"trust_domain"
88
],
99
"properties": {
@@ -16,8 +16,11 @@
1616
}
1717
}
1818
},
19-
"taskcluster_scope_prefix": {
20-
"type": "string"
19+
"taskcluster_scope_prefixes": {
20+
"type": "array",
21+
"items": {
22+
"type": "string"
23+
}
2124
},
2225
"trust_domain": {
2326
"type": "string"

bitrisescript/src/bitrisescript/script.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
#!/usr/bin/env python3
2-
""" Github main script
2+
""" Bitrise main script
33
"""
44
import asyncio
55
import logging
66
import os
77

88
from bitrisescript.bitrise import BitriseClient, run_build
9-
from bitrisescript.task import get_bitrise_app, get_bitrise_workflows, get_build_params, validate_scope_prefixes
9+
from bitrisescript.task import get_bitrise_app, get_bitrise_workflows, get_build_params
1010
from scriptworker_client.client import sync_main
1111

1212
log = logging.getLogger(__name__)
1313

1414

1515
async def async_main(config, task):
16-
validate_scope_prefixes(config, task)
17-
1816
artifact_dir = os.path.join(config["work_dir"], "artifacts")
1917

2018
app = get_bitrise_app(config, task)

bitrisescript/src/bitrisescript/task.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
from textwrap import dedent
21
from typing import Any
32

43
from scriptworker_client.exceptions import TaskVerificationError
54
from scriptworker_client.utils import get_single_item_from_sequence
65

76

8-
def validate_scope_prefixes(config, task):
9-
"""Validates scope prefixes.
7+
def _get_allowed_scope_prefixes(config):
8+
prefixes = config["taskcluster_scope_prefixes"]
9+
return [prefix if prefix.endswith(":") else "{}:".format(prefix) for prefix in prefixes]
10+
11+
12+
def extract_common_scope_prefix(config: dict[str, Any], task: dict[str, Any]) -> str:
13+
"""Extract common scope prefix.
1014
1115
Args:
1216
config (dict): The bitrisescript config.
1317
task (dict): The task definition.
1418
"""
15-
prefix = config["taskcluster_scope_prefix"]
19+
prefixes = _get_allowed_scope_prefixes(config)
20+
scopes = task["scopes"]
1621

17-
invalid_scopes = {s for s in task["scopes"] if not s.startswith(prefix)}
18-
if invalid_scopes:
19-
invalid_scopes = "\n".join(sorted(invalid_scopes))
20-
raise TaskVerificationError(
21-
dedent(
22-
f"""
23-
The following scopes have an invalid prefix:
24-
{invalid_scopes}
22+
found_prefixes = {prefix for prefix in prefixes for scope in scopes if scope.startswith(prefix)}
2523

26-
Expected prefix:
27-
{prefix}
28-
""".lstrip()
29-
)
30-
)
24+
return get_single_item_from_sequence(
25+
sequence=found_prefixes,
26+
condition=lambda _: True,
27+
ErrorClass=TaskVerificationError,
28+
no_item_error_message=f"No scope starting with any of these prefixes {prefixes} found",
29+
too_many_item_error_message="Too many prefixes found",
30+
)
3131

3232

3333
def _extract_last_chunk_of_scope(scope: str, prefix: str) -> str:
@@ -51,7 +51,7 @@ def get_bitrise_app(config: dict[str, Any], task: dict[str, Any]) -> str:
5151
TaskVerificationError: If task is missing the app scope or has more
5252
than one app scope.
5353
"""
54-
prefix = config["taskcluster_scope_prefix"]
54+
prefix = extract_common_scope_prefix(config, task)
5555
app_prefix = f"{prefix}app:"
5656
scope = get_single_item_from_sequence(
5757
sequence=task["scopes"],
@@ -76,9 +76,13 @@ def get_bitrise_workflows(config: dict[str, Any], task: dict[str, Any]) -> list[
7676
Returns:
7777
list: A list of bitrise workflows that should be scheduled.
7878
"""
79-
prefix = config["taskcluster_scope_prefix"]
79+
prefix = extract_common_scope_prefix(config, task)
8080
workflow_prefix = f"{prefix}workflow:"
81-
return [_extract_last_chunk_of_scope(scope, workflow_prefix) for scope in task["scopes"] if scope.startswith(workflow_prefix)]
81+
workflows = [_extract_last_chunk_of_scope(scope, workflow_prefix) for scope in task["scopes"] if scope.startswith(workflow_prefix)]
82+
83+
if not workflows:
84+
raise TaskVerificationError(f"No workflow scopes starting with '{prefix}' found")
85+
return workflows
8286

8387

8488
def get_build_params(task: dict[str, Any]) -> dict[str, Any]:

bitrisescript/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ def responses():
1010

1111
@pytest.fixture
1212
def config():
13-
return {"bitrise": {"token": "abc"}, "taskcluster_scope_prefix": "test:prefix:", "work_dir": "work"}
13+
return {"bitrise": {"token": "abc"}, "taskcluster_scope_prefixes": ["test:prefix:"], "work_dir": "work"}

bitrisescript/tests/test_script.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
@pytest.mark.parametrize(
1414
"task, expectation, expected_num_futures",
1515
(
16-
pytest.param({"scopes": ["test:prefix:app:bar"]}, does_not_raise(), 0, id="no_workflow"),
16+
pytest.param({"scopes": ["test:prefix:app:bar"]}, pytest.raises(TaskVerificationError), 0, id="no_workflow"),
1717
pytest.param({"scopes": ["test:prefix:app:bar", "test:prefix:workflow:baz"]}, does_not_raise(), 1, id="single_workflow"),
1818
pytest.param({"scopes": ["test:prefix:app:bar", "test:prefix:workflow:baz", "test:prefix:workflow:other"]}, does_not_raise(), 2, id="two_workflows"),
19-
pytest.param({"scopes": ["bad:app:bar"]}, pytest.raises(TaskVerificationError), 1, id="invalid_prefix_app"),
20-
pytest.param({"scopes": ["test:prefix:app:bar", "bad:workflow:baz"]}, pytest.raises(TaskVerificationError), 1, id="invalid_prefix_workflow"),
19+
pytest.param({"scopes": ["bad:app:bar"]}, pytest.raises(TaskVerificationError), 0, id="invalid_prefix_app"),
20+
pytest.param({"scopes": ["test:prefix:app:bar", "bad:workflow:baz"]}, pytest.raises(TaskVerificationError), 0, id="invalid_prefix_workflow"),
2121
),
2222
)
2323
async def test_async_main(mocker, config, task, expectation, expected_num_futures):

bitrisescript/tests/test_task.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,43 @@
88

99

1010
@pytest.mark.parametrize(
11-
"task, expectation",
11+
"config, task, expectation, expected_result",
1212
(
13-
pytest.param({"scopes": ["test:prefix:app:foo", "test:prefix:workflow:bar"]}, does_not_raise(), id="valid"),
14-
pytest.param({"scopes": ["bad:prefix:app:foo", "test:prefix:workflow:bar"]}, pytest.raises(TaskVerificationError), id="invalid"),
15-
pytest.param({"scopes": ["test:prefix:app:foo", "bad:prefix:workflow:bar"]}, pytest.raises(TaskVerificationError), id="invalid"),
13+
(
14+
{"taskcluster_scope_prefixes": ["some:prefix"]},
15+
{"scopes": ["some:prefix:project:someproject"]},
16+
does_not_raise(),
17+
"some:prefix:",
18+
),
19+
(
20+
{"taskcluster_scope_prefixes": ["some:prefix:"]},
21+
{"scopes": ["some:prefix:project:someproject"]},
22+
does_not_raise(),
23+
"some:prefix:",
24+
),
25+
(
26+
{"taskcluster_scope_prefixes": ["some:prefix"]},
27+
{"scopes": ["some:prefix:project:someproject", "some:prefix:action:someaction"]},
28+
does_not_raise(),
29+
"some:prefix:",
30+
),
31+
(
32+
{"taskcluster_scope_prefixes": ["another:prefix"]},
33+
{"scopes": ["some:prefix:project:someproject", "some:prefix:action:someaction"]},
34+
pytest.raises(TaskVerificationError),
35+
None,
36+
),
37+
(
38+
{"taskcluster_scope_prefixes": ["some:prefix", "another:prefix"]},
39+
{"scopes": ["some:prefix:project:someproject", "another:prefix:action:someaction"]},
40+
pytest.raises(TaskVerificationError),
41+
None,
42+
),
1643
),
1744
)
18-
def test_validate_scope_prefixes(config, task, expectation):
45+
def test_extract_common_scope_prefix(config, task, expectation, expected_result):
1946
with expectation:
20-
task_mod.validate_scope_prefixes(config, task)
47+
assert task_mod.extract_common_scope_prefix(config, task) == expected_result
2148

2249

2350
@pytest.mark.parametrize(
@@ -65,20 +92,23 @@ def test_get_bitrise_app(config, task, expected):
6592

6693

6794
@pytest.mark.parametrize(
68-
"task, expected",
95+
"task, expectation, expected",
6996
(
7097
(
7198
{"scopes": ["test:prefix:app:foo", "test:prefix:workflow:bar", "test:prefix:workflow:baz"]},
99+
does_not_raise(),
72100
["bar", "baz"],
73101
),
74102
(
75103
{"scopes": ["test:prefix:app:foo"]},
76-
[],
104+
pytest.raises(TaskVerificationError),
105+
None,
77106
),
78107
),
79108
)
80-
def test_get_bitrise_workflows(config, task, expected):
81-
assert task_mod.get_bitrise_workflows(config, task) == expected
109+
def test_get_bitrise_workflows(config, task, expectation, expected):
110+
with expectation:
111+
assert task_mod.get_bitrise_workflows(config, task) == expected
82112

83113

84114
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)