diff --git a/.github/workflows/l10n-write-mappings.yml b/.github/workflows/l10n-write-mappings.yml new file mode 100644 index 00000000..41256fdf --- /dev/null +++ b/.github/workflows/l10n-write-mappings.yml @@ -0,0 +1,31 @@ +--- +name: L10N output mapping splits + +run-name: ${{ github.actor }} is writing L10N mapping splits +on: + schedule: + - cron: "0 0 * * 1" + +permissions: + contents: "write" + +jobs: + Select-Channels: + runs-on: ubuntu-latest + outputs: + channels: ${{ steps.splits.outputs.channels }} + steps: + - name: Create app token + uses: actions/create-github-app-token@v1 + id: app-token + with: + app-id: ${{ secrets.BOT_CLIENT_ID }} + private-key: ${{ secrets.BOT_PRIVATE_KEY }} + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + - name: Select test channels + id: splits + run: | + python3 choose_l10n_beta_split.py \ No newline at end of file diff --git a/choose_l10n_beta_split.py b/choose_l10n_beta_split.py new file mode 100644 index 00000000..de7078ad --- /dev/null +++ b/choose_l10n_beta_split.py @@ -0,0 +1,38 @@ +import json +import logging +import sys +from collections import defaultdict + +from choose_l10n_ci_set import valid_l10n_mappings + + +def distribute_mappings_evenly(mappings): + """ + Distribute the selected mappings into 3 splits and return the container. + + Args: + mappings (dict): A dictionary of mappings, where the keys are sites and the values are sets of regions. + """ + if not mappings: + return {} + # sort the mappings by the length of the regions per site + mappings = dict(sorted(mappings.items(), key=lambda val: len(val[1]), reverse=True)) + # place the mappings into 3 containers evenly according to the load + loads = [0, 0, 0] + balanced_splits = [defaultdict(list) for _ in range(3)] + for site, regions in mappings.items(): + min_idx = loads.index(min(loads)) + balanced_splits[min_idx][site] = list(regions) + loads[min_idx] += len(regions) + return balanced_splits + + +if __name__ == "__main__": + l10n_mappings = valid_l10n_mappings() + all_splits = distribute_mappings_evenly(l10n_mappings) + if not all_splits: + logging.warning("No valid l10n mappings") + sys.exit(1) + for idx, split in enumerate(all_splits, start=1): + with open(f"l10n_CM/beta_run_splits/l10n_split_{idx}.json", "w") as f: + json.dump(split, f) diff --git a/choose_l10n_ci_set.py b/choose_l10n_ci_set.py index 3da0c456..37ced5a4 100644 --- a/choose_l10n_ci_set.py +++ b/choose_l10n_ci_set.py @@ -34,35 +34,6 @@ def valid_l10n_mappings(): return mapping -def distribute_mappings_evenly(mappings, version): - """ - Distribute the selected mappings if its a reportable run. - - Args: - mappings (dict): A dictionary of mappings, where the keys are sites and the values are sets of regions. - version (int): The beta_version of the beta. - """ - if not mappings: - return {} - if os.environ.get("TESTRAIL_REPORT"): - # sort the mappings by the length of the regions per site - mappings = dict( - sorted(mappings.items(), key=lambda val: len(val[1]), reverse=True) - ) - # place the mappings into 3 containers evenly according to the load - loads = [0, 0, 0] - containers = [defaultdict(set) for _ in range(3)] - for key, value in mappings.items(): - min_idx = loads.index(min(loads)) - containers[min_idx][key] = value - loads[min_idx] += len(value) - # get container index according to beta beta_version - run_idx = version % 3 - return containers[run_idx] - else: - return mappings - - def process_changed_file(f, selected_mappings): """ process the changed file to add the site/region mappings. @@ -108,6 +79,21 @@ def save_mappings(selected_container): f.writelines(current_running_mappings) +def select_l10n_mappings(beta_version): + """ + Select the correct l10n mappings. + + Args: + beta_version: the current beta version. + """ + beta_split = (beta_version % 3) + 1 + if os.path.exists(f"l10n_CM/beta_run_splits/l10n_split_{beta_split}.json"): + with open(f"l10n_CM/beta_run_splits/l10n_split_{beta_split}.json", "r") as f: + return json.load(f) + else: + return valid_l10n_mappings() + + if __name__ == "__main__": if os.path.exists(".env"): with open(".env") as fh: @@ -134,11 +120,12 @@ def save_mappings(selected_container): except ValueError: # failsafe beta_version beta_version = 0 - l10n_mappings = valid_l10n_mappings() + # choose split number + l10n_mappings = select_l10n_mappings(beta_version) sample_mappings = {k: v for k, v in l10n_mappings.items() if k.startswith("demo")} if os.environ.get("TESTRAIL_REPORT") or os.environ.get("MANUAL"): # Run all tests if this is a scheduled beta or a manual run - save_mappings(distribute_mappings_evenly(l10n_mappings, beta_version)) + save_mappings(l10n_mappings) sys.exit(0) re_set_all = [ @@ -199,5 +186,5 @@ def save_mappings(selected_container): selected_mappings |= sample_mappings break - save_mappings(distribute_mappings_evenly(selected_mappings, beta_version)) + save_mappings(selected_mappings) sys.exit(0) diff --git a/l10n_CM/beta_run_splits/l10n_split_1.json b/l10n_CM/beta_run_splits/l10n_split_1.json new file mode 100644 index 00000000..66e42444 --- /dev/null +++ b/l10n_CM/beta_run_splits/l10n_split_1.json @@ -0,0 +1,32 @@ +{ + "demo": [ + "US", + "GB", + "DE", + "FR", + "PL", + "ES", + "CA" + ], + "bijoubrigitte": [ + "DE" + ], + "calvinklein": [ + "US" + ], + "aldoshoes": [ + "US" + ], + "peacocks": [ + "GB" + ], + "assos": [ + "GB" + ], + "fnac": [ + "FR" + ], + "canadatire": [ + "CA" + ] +} \ No newline at end of file diff --git a/l10n_CM/beta_run_splits/l10n_split_2.json b/l10n_CM/beta_run_splits/l10n_split_2.json new file mode 100644 index 00000000..e8133f46 --- /dev/null +++ b/l10n_CM/beta_run_splits/l10n_split_2.json @@ -0,0 +1 @@ +{"amazon": ["US", "CA", "DE", "FR"], "walmart": ["US", "CA"], "mediamarkt": ["DE"], "zalando": ["PL"], "bestbuy": ["US"], "apple": ["US"], "whittard": ["GB"], "tiffany": ["IT"], "yellowkorner": ["FR"], "burtsbees": ["CA"]} \ No newline at end of file diff --git a/l10n_CM/beta_run_splits/l10n_split_3.json b/l10n_CM/beta_run_splits/l10n_split_3.json new file mode 100644 index 00000000..ca4ede59 --- /dev/null +++ b/l10n_CM/beta_run_splits/l10n_split_3.json @@ -0,0 +1 @@ +{"ebay": ["ES", "IT", "CA", "GB"], "etsy": ["US", "CA"], "justspices": ["DE"], "lowes": ["US"], "newegg": ["US"], "wish": ["US"], "diy": ["GB"], "artsper": ["FR"], "newbalance": ["CA"]} \ No newline at end of file diff --git a/modules/testrail_integration.py b/modules/testrail_integration.py index 28d5fd8d..9fff2c17 100644 --- a/modules/testrail_integration.py +++ b/modules/testrail_integration.py @@ -4,7 +4,7 @@ import subprocess import sys -from choose_l10n_ci_set import distribute_mappings_evenly, valid_l10n_mappings +from choose_l10n_ci_set import select_l10n_mappings from modules import taskcluster as tc from modules import testrail as tr from modules.testrail import TestRail @@ -187,11 +187,9 @@ def reportable(platform_to_test=None): plan_entries = this_plan.get("entries") if os.environ.get("FX_L10N"): - report = True beta_version = int(minor_num.split("b")[-1]) - distributed_mappings = distribute_mappings_evenly( - valid_l10n_mappings(), beta_version - ) + distributed_mappings = select_l10n_mappings(beta_version) + expected_mappings = sum(map(lambda x: len(x), distributed_mappings.values())) covered_mappings = 0 # keeping this logic to still see how many mappings are reported. for entry in plan_entries: @@ -205,13 +203,11 @@ def reportable(platform_to_test=None): and platform in run_platform ): covered_mappings += 1 - report = False logging.warning( - f"Potentially matching run found for {platform}, may be reportable. (Found {covered_mappings} site/region mappings reported.)" + f"Potentially matching run found for {platform}, may be reportable. (Found {covered_mappings} site/region mappings reported, expected {expected_mappings}.)" ) - logging.warning(f"Run is reportable: {report}") - # Only report when there is a new beta and no other site/region mappings are reported. - return report + # Only report when there is a new beta without a reported plan or if the selected split is not completely reported. + return covered_mappings < expected_mappings else: covered_suites = 0 for entry in plan_entries: