diff --git a/doc/config-reference.md b/doc/config-reference.md index b5e680edc..9024dc84c 100644 --- a/doc/config-reference.md +++ b/doc/config-reference.md @@ -228,3 +228,69 @@ kernel. It is used in `kselftest`-related jobs. The `dtbs_check` parameter is used to enable or disable the dtbs_check build. The dtbs_check build is additional kind of build that will verify the device tree blobs. + +## Build Configs configuration + +The `build_configs` section defines which kernel trees and branches to monitor +for new commits. Each build config specifies a tree/branch combination that the +trigger service will poll for changes. + +Build configs are typically defined in separate files under `config/trees/` +directory, one file per tree (e.g., `config/trees/chromiumos.yaml`). + +### Mandatory parameters + +- **tree**: string - Name of the tree (must match an entry in `trees` section) +- **branch**: string - Branch name to monitor + +### Optional parameters + +- **architectures**: list of strings - Filter to limit which architectures to build +- **frequency**: string - Limit how often checkout nodes are created + +### Parameter frequency (optional) + +- **Value**: [Nd][Nh][Nm] +- **Default**: none (no limit) + +The frequency parameter in `build_configs` limits how often the trigger service +creates new checkout nodes for a tree/branch combination. This is useful for +trees where all jobs have frequency limits - without this setting, empty +checkout nodes would be created that show as zeros in the dashboard. + +When set, the trigger service checks if a checkout node for the same tree/branch +was created within the specified time period. If so, it skips creating a new +checkout node even if a new commit is detected. + +This is different from the job-level `frequency` parameter which controls how +often individual jobs run. The build_config frequency controls checkout node +creation at the source, preventing empty checkouts entirely. + +**Recommendation**: Set this to match the minimum frequency of all jobs +scheduled for this tree/branch. For example, if all jobs for chromiumos have +`frequency: 1d` or higher, set `frequency: 1d` in the build_config. + +Example: +```yaml +build_configs: + chromiumos: + tree: chromiumos + branch: 'master' + frequency: 1d + architectures: + - x86_64 + - arm64 + - arm + + chromiumos-6.6: + tree: chromiumos + branch: 'chromeos-6.6' + frequency: 1d +``` + +In this example, new checkout nodes for chromiumos branches will only be created +once per day, even if multiple commits are pushed. This prevents empty checkout +nodes when all jobs have daily frequency limits. + +**Note**: The `--force` flag on the trigger service overrides the frequency +check, allowing checkout creation regardless of the frequency setting. diff --git a/src/trigger.py b/src/trigger.py index 4e3b1e3dd..9eea3626d 100755 --- a/src/trigger.py +++ b/src/trigger.py @@ -8,6 +8,7 @@ import copy import datetime +import re import sys import time @@ -21,6 +22,28 @@ from base import Service, validate_url +def translate_freq(freq): + """ + Translate the frequency string to seconds. + Format is: [Nd][Nh][Nm], where each field is optional. + Examples: "1d" = 1 day, "12h" = 12 hours, "1d12h" = 1.5 days + """ + if not freq: + return 0 + freq_sec = 0 + freq_re = re.compile(r'(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?') + freq_match = freq_re.match(freq) + if freq_match: + days, hours, minutes = freq_match.groups() + if days: + freq_sec += int(days) * 24 * 60 * 60 + if hours: + freq_sec += int(hours) * 60 * 60 + if minutes: + freq_sec += int(minutes) * 60 + return freq_sec + + class Trigger(Service): def __init__(self, configs, args): @@ -32,6 +55,43 @@ def __init__(self, configs, args): def _log_revision(self, message, build_config, head_commit): self.log.info(f"{message:32s} {build_config.name:32s} {head_commit}") + def _check_frequency(self, build_config): + """Check if a checkout can be created based on frequency limit. + + Returns True if a new checkout can be created, False if we should skip + due to frequency limit (a recent checkout already exists). + """ + frequency = build_config.frequency + if not frequency: + return True + + freq_sec = translate_freq(frequency) + if not freq_sec or freq_sec < 60: + self.log.warning(f"Invalid frequency '{frequency}' for {build_config.name}") + return True + + # Calculate the timestamp threshold + now = datetime.datetime.now(datetime.UTC) + tstamp = now - datetime.timedelta(seconds=freq_sec) + + # Search for checkouts with this tree/branch created after threshold + search_terms = { + "kind": "checkout", + "data.kernel_revision.tree": build_config.tree.name, + "data.kernel_revision.branch": build_config.branch, + "owner": self._current_user['username'], + "submitter": "service:pipeline", + "created__gte": tstamp.isoformat(), + } + node_count = self._api.node.count(search_terms) + + if node_count > 0: + self.log.info(f"Skipping {build_config.name}: checkout created within " + f"frequency limit ({frequency})") + return False + + return True + def _run_trigger(self, build_config, force, timeout, trees): if trees and len(trees) > 1: tree_condition = "not" if trees.startswith("!") else "only" @@ -41,6 +101,10 @@ def _run_trigger(self, build_config, force, timeout, trees): (not tree_in_list and tree_condition == "only"): return + # Check frequency limit before doing any git operations + if not force and not self._check_frequency(build_config): + return + try: current_config = copy.copy(build_config) if validate_url(current_config.branch):