From 30e9d4d376c8519d8d1496abd7f062d02f915c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Wed, 9 Sep 2026 12:11:46 -0400 Subject: [PATCH 1/4] feat: verify this installer against the contracts of the repos it clones The containers method builds nothing: it clones ovos-docker and hivemind-docker at a pinned ref and runs the compose files out of them. Four things are therefore an interface between those repositories and this one, and each has broken an install: the compose file names, the container names docker_container_exec targets, the variables the compose reads, and the images pulled. The third stays quiet. A compose file that starts reading a variable with an inline default keeps working and uses the fallback, so a value collected here is silently replaced - HIVEMIND_SITEID did exactly that and every satellite reported the wrong site with nothing failing. The contracts mark such a variable `owner: installer`, and this refuses to read "has a default" as "nobody needs to set it". Offline by default against the snapshots in tests/contracts/, so it runs in every pull request through code_quality.bats. --online re-fetches each contract at the ref pinned here and notices a snapshot that has drifted; --fail-on-stale additionally fails when a pin is behind a release, which is how an install sat on hivemind-docker v2.0.0 for weeks after the fix it needed had shipped. A scheduled job runs both, because a scheduled run that exits 0 tells nobody anything. Written against main first, where it immediately reported HIVEMIND_SITEID as unset - the bug this stack fixes - which is the case it exists for. Negative-tested by dropping an installer-owned variable, dropping a required variable, and renaming an expected compose file; each is reported by name, and the missing compose file correctly cascades into the container that is no longer reachable. The snapshots are derived from the compose files at the pinned tags, since contract.yml itself only lands in the next release of each repository; --online falls back to them and says so until then. Co-Authored-By: Claude Opus 5 --- .github/workflows/contracts.yml | 36 ++++ scripts/check_contracts.py | 182 +++++++++++++++++ tests/bats/code_quality.bats | 16 ++ tests/contracts/hivemind-docker.yml | 144 ++++++++++++++ tests/contracts/ovos-docker.yml | 298 ++++++++++++++++++++++++++++ 5 files changed, 676 insertions(+) create mode 100644 .github/workflows/contracts.yml create mode 100755 scripts/check_contracts.py create mode 100644 tests/contracts/hivemind-docker.yml create mode 100644 tests/contracts/ovos-docker.yml diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml new file mode 100644 index 00000000..eb0a4809 --- /dev/null +++ b/.github/workflows/contracts.yml @@ -0,0 +1,36 @@ +--- +name: Upstream contracts + +# The offline half of this runs in every pull request via code_quality.bats, against the snapshots +# in tests/contracts/. This is the half that needs the network: it re-fetches each contract at the +# ref this installer pins, so a snapshot that has drifted is caught, and reports a pin that has +# fallen behind a release - which is how an install sat on a hivemind-docker tag for weeks after +# the fix it needed had shipped. + +on: + schedule: + - cron: "17 6 * * 1" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: upstream-contracts + cancel-in-progress: false + +jobs: + contracts: + name: Verify pinned contracts + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + + - run: pip install --disable-pip-version-check pyyaml + + - name: Check the installer against the contracts at the pinned refs + env: + GH_TOKEN: ${{ github.token }} + run: python3 scripts/check_contracts.py --online --fail-on-stale diff --git a/scripts/check_contracts.py b/scripts/check_contracts.py new file mode 100755 index 00000000..2a0d5ff9 --- /dev/null +++ b/scripts/check_contracts.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +"""Check this installer against the contracts published by the repositories it clones. + +The containers method does not build anything: it clones ovos-docker and hivemind-docker at a +pinned ref and runs the compose files out of them. Four things are therefore an interface between +those repositories and this one, and all four have broken an install: + + compose file names named exactly here; a rename upstream fails the install + container names docker_container_exec targets them by name; a rename fails late, after the + stack is already up and pulled + environment every variable the compose reads must come from env.j2 - or, for a variable + with an inline default, must come from env.j2 anyway when the installer is + the one that owns the value + images the compose is pinned by tag, the images move with a channel tag, so an + image can be older than the compose that expects it + +The third is the one that stays quiet. A compose file that starts reading HIVEMIND_SITEID keeps +working: it just uses the literal string "default" instead of the site id collected here. Nothing +fails, and every satellite reports the wrong site. That is why a contract marks such a variable +`owner: installer` and why this refuses to treat "has a default" as "nobody needs to set it". + +Offline by default, against the snapshots in tests/contracts/, so this runs in any pull request. +--online re-fetches them at the pinned refs, reports a pin that has fallen behind a release, and +checks that the published images are not older than the compose pinned here. +""" +import argparse +import json +import re +import subprocess +import sys +import urllib.request +from pathlib import Path + +import yaml + +ROOT = Path(__file__).resolve().parent.parent +CONTAINERS = ROOT / "ansible/roles/ovos_containers/defaults/main.yml" +INSTALLER = ROOT / "ansible/roles/ovos_installer/defaults/main.yml" +ENV_TEMPLATE = ROOT / "ansible/roles/ovos_containers/templates/docker/env.j2" +SNAPSHOTS = ROOT / "tests/contracts" + +# The satellite profile is the only one that runs out of hivemind-docker; everything else comes +# from ovos-docker. ovos_containers_composition_directory switches on exactly that condition. +HIVEMIND_COMPOSE_VARS = {"ovos_containers_compose_file_satellite"} + +REPOS = { + "ovos-docker": "JarbasHiveMind/ovos-docker", # corrected below from the installer defaults + "hivemind-docker": "JarbasHiveMind/hivemind-docker", +} + + +def installer_facts() -> dict: + containers = CONTAINERS.read_text() + installer = INSTALLER.read_text() + + compose = dict(re.findall(r"^(ovos_containers_compose_file_[a-z_]+):\s*(\S+)$", containers, re.M)) + containers_named = dict(re.findall(r"^(ovos_containers_container_[a-z_]+):\s*(\S+)$", containers, re.M)) + pins = dict(re.findall(r"^ovos_installer_(ovos|hivemind)_docker_repo_branch:\s*(\S+)$", installer, re.M)) + urls = dict(re.findall(r"^ovos_installer_(ovos|hivemind)_docker_repo_url:\s*(\S+)$", installer, re.M)) + + files = {"hivemind-docker": set(), "ovos-docker": set()} + for var, name in compose.items(): + target = "hivemind-docker" if var in HIVEMIND_COMPOSE_VARS else "ovos-docker" + files[target].add(name) + + return { + "compose_files": files, + "container_names": set(containers_named.values()), + "provided_env": set(re.findall(r"^([A-Z][A-Z0-9_]*)=", ENV_TEMPLATE.read_text(), re.M)), + "pins": {"ovos-docker": pins.get("ovos"), "hivemind-docker": pins.get("hivemind")}, + "slugs": {k: re.sub(r"^https://github\.com/|\.git$", "", v) + for k, v in {"ovos-docker": urls.get("ovos", ""), + "hivemind-docker": urls.get("hivemind", "")}.items()}, + } + + +def check(repo: str, contract: dict, facts: dict) -> list: + problems = [] + wanted_files = facts["compose_files"][repo] + declared_files = set(contract.get("compose_files") or []) + + for name in sorted(wanted_files - declared_files): + problems.append(f"{repo}: compose file {name} is used here but not declared by the contract") + + # Only the files this installer actually runs matter; a satellite install must not be asked + # for variables that only the matrix-bot compose reads. + used = wanted_files & declared_files + + declared_containers = { + name for compose_file, services in (contract.get("services") or {}).items() + if compose_file in used for name in services.values() + } + all_containers = { + name for services in (contract.get("services") or {}).values() for name in services.values() + } + for name in sorted(facts["container_names"] & all_containers - declared_containers): + problems.append(f"{repo}: container {name} is referenced here but is not in any compose " + f"file this installer runs") + + for variable, spec in sorted((contract.get("env") or {}).items()): + if not (set(spec.get("compose_files") or []) & used): + continue + if variable in facts["provided_env"]: + continue + if spec.get("required"): + problems.append(f"{repo}: {variable} is required by {', '.join(sorted(set(spec['compose_files']) & used))} " + f"and is not set in env.j2") + elif spec.get("owner") == "installer": + problems.append(f"{repo}: {variable} is owned by the installer but is not set in env.j2 - " + f"the compose has a default, so this fails silently rather than loudly") + return problems + + +def fetch(slug: str, ref: str, path: str) -> str | None: + url = f"https://raw.githubusercontent.com/{slug}/{ref}/{path}" + try: + with urllib.request.urlopen(url, timeout=30) as response: + return response.read().decode() + except Exception: + return None + + +def latest_release(slug: str) -> str | None: + try: + out = subprocess.run(["gh", "release", "view", "--repo", slug, "--json", "tagName", "-q", ".tagName"], + capture_output=True, text=True, check=True) + return out.stdout.strip() or None + except Exception: + return None + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--online", action="store_true", + help="re-fetch the contracts at the pinned refs and check pin freshness") + parser.add_argument("--fail-on-stale", action="store_true", + help="treat a pin behind the latest release as a failure. A scheduled run " + "that exits 0 tells nobody anything, so the job that watches for " + "this uses it; the pull request check does not.") + args = parser.parse_args() + + facts = installer_facts() + problems, notes = [], [] + + for repo in ("ovos-docker", "hivemind-docker"): + snapshot = SNAPSHOTS / f"{repo}.yml" + if not snapshot.exists(): + problems.append(f"{repo}: no contract snapshot at {snapshot.relative_to(ROOT)}") + continue + contract = yaml.safe_load(snapshot.read_text()) + + if args.online: + slug, ref = facts["slugs"][repo], facts["pins"][repo] + live = fetch(slug, ref, "contract.yml") + if live is None: + notes.append(f"{repo}: {ref} publishes no contract.yml yet; using the snapshot") + elif yaml.safe_load(live) != contract: + problems.append(f"{repo}: the snapshot differs from contract.yml at {ref} - " + f"refresh tests/contracts/{repo}.yml") + newest = latest_release(slug) + if newest and newest != ref: + message = f"{repo}: pinned {ref}, latest release is {newest}" + (problems if args.fail_on_stale else notes).append(message) + + problems.extend(check(repo, contract, facts)) + + for note in notes: + print(f"note: {note}") + sys.stdout.flush() # keep notes above the violations in a CI log + + if problems: + print("\ncontract violations:", file=sys.stderr) + for problem in problems: + print(f" {problem}", file=sys.stderr) + return 1 + + print(f"contracts satisfied: {', '.join(f'{r} {facts['pins'][r]}' for r in facts['pins'])}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index fa4883e0..31e4a386 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -3730,3 +3730,19 @@ function teardown() { run grep -q "^HIVEMIND_SITEID={{ ovos_installer_site_id" "$env_template" assert_success } + +@test "installer_satisfies_the_contracts_of_the_repositories_it_clones" { + # The containers method builds nothing: it clones ovos-docker and hivemind-docker at a + # pinned ref and runs their compose files. The compose file names, the container names + # docker_container_exec targets, and the variables the compose reads are therefore an + # interface, and each has broken an install. The quietest is a variable with an inline + # default that the installer is nonetheless meant to own - HIVEMIND_SITEID kept working + # and silently reported the wrong site id - so the contracts mark those `owner: installer` + # and this refuses to read "has a default" as "nobody needs to set it". + run python3 scripts/check_contracts.py + if [ "$status" -ne 0 ]; then + echo "$output" >&2 + echo "refresh tests/contracts/ or fix env.j2; see scripts/check_contracts.py" >&2 + fi + assert_success +} diff --git a/tests/contracts/hivemind-docker.yml b/tests/contracts/hivemind-docker.yml new file mode 100644 index 00000000..c5ee1257 --- /dev/null +++ b/tests/contracts/hivemind-docker.yml @@ -0,0 +1,144 @@ +version: 1 +compose_files: +- docker-compose.chatroom.yml +- docker-compose.matrix-bot.yml +- docker-compose.satellite.yml +- docker-compose.webchat.yml +- docker-compose.yml +services: + docker-compose.chatroom.yml: + hivemind_chatroom: hivemind_chatroom + docker-compose.matrix-bot.yml: + hivemind_matrix_bot: hivemind_matrix_bot + docker-compose.satellite.yml: + hivemind_cli: hivemind_cli + hivemind_satellite: hivemind_satellite + docker-compose.webchat.yml: + hivemind_webchat: hivemind_webchat + docker-compose.yml: + hivemind_cli: hivemind_cli + hivemind_listener: hivemind_listener +env: + HIVEMIND_CONFIG_FOLDER: + required: true + compose_files: + - docker-compose.satellite.yml + - docker-compose.yml + HIVEMIND_SHARE_FOLDER: + required: true + compose_files: + - docker-compose.satellite.yml + - docker-compose.yml + HIVEMIND_SITEID: + required: false + compose_files: + - docker-compose.satellite.yml + owner: installer + description: Site id reported in message.context. The compose falls back to the + literal "default", so an installer that does not set it produces a working satellite + reporting the wrong site - silently. Not optional for a consumer that collects + a site id. + HIVEMIND_USER: + required: true + compose_files: + - docker-compose.satellite.yml + - docker-compose.yml + LIGHT_MEMORY_LIMIT: + required: false + compose_files: + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml + MATRIX_BOT_NAME: + required: true + compose_files: + - docker-compose.matrix-bot.yml + MATRIX_HOST: + required: true + compose_files: + - docker-compose.matrix-bot.yml + MATRIX_ROOM: + required: true + compose_files: + - docker-compose.matrix-bot.yml + MATRIX_TOKEN: + required: true + compose_files: + - docker-compose.matrix-bot.yml + OVOS_CONFIG_FOLDER: + required: true + compose_files: + - docker-compose.satellite.yml + SAT_HOST: + required: true + compose_files: + - docker-compose.chatroom.yml + SAT_KEY: + required: true + compose_files: + - docker-compose.chatroom.yml + SAT_PASSWORD: + required: true + compose_files: + - docker-compose.chatroom.yml + SAT_PORT: + required: true + compose_files: + - docker-compose.chatroom.yml + STANDARD_MEMORY_LIMIT: + required: false + compose_files: + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml + TZ: + required: true + compose_files: + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml + VERSION: + required: true + compose_files: + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml + VOICE_SAT_HOST: + required: true + compose_files: + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + VOICE_SAT_KEY: + required: true + compose_files: + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + VOICE_SAT_PASSWORD: + required: true + compose_files: + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + VOICE_SAT_PORT: + required: true + compose_files: + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + XDG_RUNTIME_DIR: + required: true + compose_files: + - docker-compose.satellite.yml +images: +- docker.io/smartgic/hivemind-chatroom +- docker.io/smartgic/hivemind-cli +- docker.io/smartgic/hivemind-listener +- docker.io/smartgic/hivemind-matrix-bot +- docker.io/smartgic/hivemind-satellite +- docker.io/smartgic/hivemind-webchat diff --git a/tests/contracts/ovos-docker.yml b/tests/contracts/ovos-docker.yml new file mode 100644 index 00000000..293b4029 --- /dev/null +++ b/tests/contracts/ovos-docker.yml @@ -0,0 +1,298 @@ +version: 1 +compose_files: +- docker-compose.gui.yml +- docker-compose.hivemind.yml +- docker-compose.macos.yml +- docker-compose.raspberrypi.gui.yml +- docker-compose.raspberrypi.yml +- docker-compose.server.yml +- docker-compose.skills-extra.yml +- docker-compose.skills.yml +- docker-compose.windows.yml +- docker-compose.yml +services: + docker-compose.gui.yml: + ovos_gui: ovos_gui + ovos_gui_websocket: ovos_gui_websocket + ovos_skill_homescreen: ovos_skill_homescreen + docker-compose.hivemind.yml: + hivemind_cli: hivemind_cli + hivemind_listener: hivemind_listener + docker-compose.macos.yml: + ovos_audio: ovos_audio + ovos_cli: ovos_cli + ovos_core: ovos_core + ovos_listener: ovos_listener + ovos_messagebus: ovos_messagebus + ovos_phal: ovos_phal + ovos_phal_admin: ovos_phal_admin + ovos_plugin_ggwave: ovos_plugin_ggwave + docker-compose.server.yml: + hivemind_cli: hivemind_cli + hivemind_listener: hivemind_listener + ovos_core: ovos_core + ovos_messagebus: ovos_messagebus + docker-compose.skills-extra.yml: + ovos_skill_camera: ovos_skill_camera + ovos_skill_easter_eggs: ovos_skill_easter_eggs + ovos_skill_homeassistant: ovos_skill_homeassistant + ovos_skill_jokes: ovos_skill_jokes + ovos_skill_parrot: ovos_skill_parrot + ovos_skill_randomness: ovos_skill_randomness + ovos_skill_wikihow: ovos_skill_wikihow + ovos_skill_wolfie: ovos_skill_wolfie + docker-compose.skills.yml: + ovos_skill_alerts: ovos_skill_alerts + ovos_skill_date_time: ovos_skill_date_time + ovos_skill_duckduckgo: ovos_skill_duckduckgo + ovos_skill_fallback_unknown: ovos_skill_fallback_unknown + ovos_skill_ggwave: ovos_skill_ggwave + ovos_skill_hello_world: ovos_skill_hello_world + ovos_skill_personal: ovos_skill_personal + ovos_skill_volume: ovos_skill_volume + ovos_skill_weather: ovos_skill_weather + ovos_skill_wikipedia: ovos_skill_wikipedia + ovos_skill_wordnet: ovos_skill_wordnet + docker-compose.windows.yml: + ovos_audio: ovos_audio + ovos_cli: ovos_cli + ovos_core: ovos_core + ovos_listener: ovos_listener + ovos_messagebus: ovos_messagebus + ovos_phal: ovos_phal + ovos_phal_admin: ovos_phal_admin + ovos_plugin_ggwave: ovos_plugin_ggwave + docker-compose.yml: + ovos_audio: ovos_audio + ovos_cli: ovos_cli + ovos_core: ovos_core + ovos_listener: ovos_listener + ovos_messagebus: ovos_messagebus + ovos_phal: ovos_phal + ovos_phal_admin: ovos_phal_admin + ovos_plugin_ggwave: ovos_plugin_ggwave +env: + CORE_MEMORY_LIMIT: + required: false + compose_files: + - docker-compose.macos.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml + DISPLAY: + required: true + compose_files: + - docker-compose.gui.yml + GPIO_GID: + required: true + compose_files: + - docker-compose.raspberrypi.yml + GUI_MEMORY_LIMIT: + required: false + compose_files: + - docker-compose.gui.yml + HIVEMIND_CONFIG_FOLDER: + required: true + compose_files: + - docker-compose.hivemind.yml + - docker-compose.server.yml + HIVEMIND_SHARE_FOLDER: + required: true + compose_files: + - docker-compose.hivemind.yml + - docker-compose.server.yml + HIVEMIND_USER: + required: true + compose_files: + - docker-compose.hivemind.yml + - docker-compose.server.yml + I2C_GID: + required: true + compose_files: + - docker-compose.raspberrypi.yml + INPUT_GID: + required: true + compose_files: + - docker-compose.gui.yml + LIGHT_MEMORY_LIMIT: + required: false + compose_files: + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.yml + OVOS_CONFIG_FOLDER: + required: true + compose_files: + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml + OVOS_CONFIG_PHAL_FOLDER: + required: true + compose_files: + - docker-compose.yml + OVOS_PERSONA_FOLDER: + required: true + compose_files: + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml + OVOS_SHARE_FOLDER: + required: true + compose_files: + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml + OVOS_USER: + required: true + compose_files: + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml + PULL_POLICY: + required: false + compose_files: + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml + owner: installer + description: Image pull policy. The compose has a fallback, so an unset value + silently overrides the policy the installer was configured with rather than + failing. + PULSE_SERVER: + required: false + compose_files: + - docker-compose.macos.yml + - docker-compose.windows.yml + QT_QPA_EGLFS_INTEGRATION: + required: true + compose_files: + - docker-compose.gui.yml + QT_QPA_EGLFS_KMS_CONFIG: + required: true + compose_files: + - docker-compose.gui.yml + QT_QPA_PLATFORM: + required: true + compose_files: + - docker-compose.gui.yml + RENDER_GID: + required: true + compose_files: + - docker-compose.gui.yml + SPI_GID: + required: true + compose_files: + - docker-compose.raspberrypi.yml + STANDARD_MEMORY_LIMIT: + required: false + compose_files: + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml + TMP_FOLDER: + required: true + compose_files: + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml + TZ: + required: false + compose_files: + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml + owner: installer + description: Timezone for every service. The compose falls back to a default, + so an installer that does not set it produces a working stack in the wrong timezone + - silently. + VERSION: + required: true + compose_files: + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml + VIDEO_GID: + required: true + compose_files: + - docker-compose.gui.yml + WAYLAND_DISPLAY: + required: true + compose_files: + - docker-compose.gui.yml + XDG_RUNTIME_DIR: + required: true + compose_files: + - docker-compose.gui.yml + - docker-compose.windows.yml + - docker-compose.yml +images: +- docker.io/smartgic/hivemind-cli +- docker.io/smartgic/hivemind-listener +- docker.io/smartgic/ovos-audio +- docker.io/smartgic/ovos-cli +- docker.io/smartgic/ovos-core +- docker.io/smartgic/ovos-gui-shell +- docker.io/smartgic/ovos-gui-websocket +- docker.io/smartgic/ovos-listener +- docker.io/smartgic/ovos-messagebus +- docker.io/smartgic/ovos-phal +- docker.io/smartgic/ovos-phal-admin +- docker.io/smartgic/ovos-plugin-ggwave +- docker.io/smartgic/ovos-skill-alerts +- docker.io/smartgic/ovos-skill-camera +- docker.io/smartgic/ovos-skill-date-time +- docker.io/smartgic/ovos-skill-duckduckgo +- docker.io/smartgic/ovos-skill-easter-eggs +- docker.io/smartgic/ovos-skill-fallback-unknown +- docker.io/smartgic/ovos-skill-ggwave +- docker.io/smartgic/ovos-skill-hello-world +- docker.io/smartgic/ovos-skill-homeassistant +- docker.io/smartgic/ovos-skill-homescreen +- docker.io/smartgic/ovos-skill-jokes +- docker.io/smartgic/ovos-skill-parrot +- docker.io/smartgic/ovos-skill-personal +- docker.io/smartgic/ovos-skill-randomness +- docker.io/smartgic/ovos-skill-volume +- docker.io/smartgic/ovos-skill-weather +- docker.io/smartgic/ovos-skill-wikihow +- docker.io/smartgic/ovos-skill-wikipedia +- docker.io/smartgic/ovos-skill-wolfie +- docker.io/smartgic/ovos-skill-wordnet From 09bd91ca5ffa2a562a35cc0211b4e372c7e35636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Wed, 9 Sep 2026 12:20:01 -0400 Subject: [PATCH 2/4] fix: regenerate contract snapshots with indented sequences ansible-lint runs over the whole tree, yamllint included, and rejected the sequence indentation PyYAML emits by default. The generator in both upstream repositories now indents sequences and writes a document start; these snapshots are regenerated with it. Caught only in CI because ansible-lint 26.8.0 enforces this and the 26.1.1 I had did not - and because I had run it scoped to ansible/ rather than at the root the way CI does. Co-Authored-By: Claude Opus 5 --- tests/contracts/hivemind-docker.yml | 113 ++++++------ tests/contracts/ovos-docker.yml | 277 ++++++++++++++-------------- 2 files changed, 196 insertions(+), 194 deletions(-) diff --git a/tests/contracts/hivemind-docker.yml b/tests/contracts/hivemind-docker.yml index c5ee1257..f4c8be0f 100644 --- a/tests/contracts/hivemind-docker.yml +++ b/tests/contracts/hivemind-docker.yml @@ -1,10 +1,11 @@ +--- version: 1 compose_files: -- docker-compose.chatroom.yml -- docker-compose.matrix-bot.yml -- docker-compose.satellite.yml -- docker-compose.webchat.yml -- docker-compose.yml + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml services: docker-compose.chatroom.yml: hivemind_chatroom: hivemind_chatroom @@ -22,17 +23,17 @@ env: HIVEMIND_CONFIG_FOLDER: required: true compose_files: - - docker-compose.satellite.yml - - docker-compose.yml + - docker-compose.satellite.yml + - docker-compose.yml HIVEMIND_SHARE_FOLDER: required: true compose_files: - - docker-compose.satellite.yml - - docker-compose.yml + - docker-compose.satellite.yml + - docker-compose.yml HIVEMIND_SITEID: required: false compose_files: - - docker-compose.satellite.yml + - docker-compose.satellite.yml owner: installer description: Site id reported in message.context. The compose falls back to the literal "default", so an installer that does not set it produces a working satellite @@ -41,104 +42,104 @@ env: HIVEMIND_USER: required: true compose_files: - - docker-compose.satellite.yml - - docker-compose.yml + - docker-compose.satellite.yml + - docker-compose.yml LIGHT_MEMORY_LIMIT: required: false compose_files: - - docker-compose.chatroom.yml - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml - - docker-compose.webchat.yml - - docker-compose.yml + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml MATRIX_BOT_NAME: required: true compose_files: - - docker-compose.matrix-bot.yml + - docker-compose.matrix-bot.yml MATRIX_HOST: required: true compose_files: - - docker-compose.matrix-bot.yml + - docker-compose.matrix-bot.yml MATRIX_ROOM: required: true compose_files: - - docker-compose.matrix-bot.yml + - docker-compose.matrix-bot.yml MATRIX_TOKEN: required: true compose_files: - - docker-compose.matrix-bot.yml + - docker-compose.matrix-bot.yml OVOS_CONFIG_FOLDER: required: true compose_files: - - docker-compose.satellite.yml + - docker-compose.satellite.yml SAT_HOST: required: true compose_files: - - docker-compose.chatroom.yml + - docker-compose.chatroom.yml SAT_KEY: required: true compose_files: - - docker-compose.chatroom.yml + - docker-compose.chatroom.yml SAT_PASSWORD: required: true compose_files: - - docker-compose.chatroom.yml + - docker-compose.chatroom.yml SAT_PORT: required: true compose_files: - - docker-compose.chatroom.yml + - docker-compose.chatroom.yml STANDARD_MEMORY_LIMIT: required: false compose_files: - - docker-compose.chatroom.yml - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml - - docker-compose.webchat.yml - - docker-compose.yml + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml TZ: required: true compose_files: - - docker-compose.chatroom.yml - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml - - docker-compose.webchat.yml - - docker-compose.yml + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml VERSION: required: true compose_files: - - docker-compose.chatroom.yml - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml - - docker-compose.webchat.yml - - docker-compose.yml + - docker-compose.chatroom.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml + - docker-compose.webchat.yml + - docker-compose.yml VOICE_SAT_HOST: required: true compose_files: - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml VOICE_SAT_KEY: required: true compose_files: - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml VOICE_SAT_PASSWORD: required: true compose_files: - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml VOICE_SAT_PORT: required: true compose_files: - - docker-compose.matrix-bot.yml - - docker-compose.satellite.yml + - docker-compose.matrix-bot.yml + - docker-compose.satellite.yml XDG_RUNTIME_DIR: required: true compose_files: - - docker-compose.satellite.yml + - docker-compose.satellite.yml images: -- docker.io/smartgic/hivemind-chatroom -- docker.io/smartgic/hivemind-cli -- docker.io/smartgic/hivemind-listener -- docker.io/smartgic/hivemind-matrix-bot -- docker.io/smartgic/hivemind-satellite -- docker.io/smartgic/hivemind-webchat + - docker.io/smartgic/hivemind-chatroom + - docker.io/smartgic/hivemind-cli + - docker.io/smartgic/hivemind-listener + - docker.io/smartgic/hivemind-matrix-bot + - docker.io/smartgic/hivemind-satellite + - docker.io/smartgic/hivemind-webchat diff --git a/tests/contracts/ovos-docker.yml b/tests/contracts/ovos-docker.yml index 293b4029..458fb266 100644 --- a/tests/contracts/ovos-docker.yml +++ b/tests/contracts/ovos-docker.yml @@ -1,15 +1,16 @@ +--- version: 1 compose_files: -- docker-compose.gui.yml -- docker-compose.hivemind.yml -- docker-compose.macos.yml -- docker-compose.raspberrypi.gui.yml -- docker-compose.raspberrypi.yml -- docker-compose.server.yml -- docker-compose.skills-extra.yml -- docker-compose.skills.yml -- docker-compose.windows.yml -- docker-compose.yml + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.raspberrypi.gui.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml services: docker-compose.gui.yml: ovos_gui: ovos_gui @@ -75,106 +76,106 @@ env: CORE_MEMORY_LIMIT: required: false compose_files: - - docker-compose.macos.yml - - docker-compose.raspberrypi.yml - - docker-compose.server.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.macos.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml DISPLAY: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml GPIO_GID: required: true compose_files: - - docker-compose.raspberrypi.yml + - docker-compose.raspberrypi.yml GUI_MEMORY_LIMIT: required: false compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml HIVEMIND_CONFIG_FOLDER: required: true compose_files: - - docker-compose.hivemind.yml - - docker-compose.server.yml + - docker-compose.hivemind.yml + - docker-compose.server.yml HIVEMIND_SHARE_FOLDER: required: true compose_files: - - docker-compose.hivemind.yml - - docker-compose.server.yml + - docker-compose.hivemind.yml + - docker-compose.server.yml HIVEMIND_USER: required: true compose_files: - - docker-compose.hivemind.yml - - docker-compose.server.yml + - docker-compose.hivemind.yml + - docker-compose.server.yml I2C_GID: required: true compose_files: - - docker-compose.raspberrypi.yml + - docker-compose.raspberrypi.yml INPUT_GID: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml LIGHT_MEMORY_LIMIT: required: false compose_files: - - docker-compose.gui.yml - - docker-compose.hivemind.yml - - docker-compose.raspberrypi.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.yml OVOS_CONFIG_FOLDER: required: true compose_files: - - docker-compose.gui.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml OVOS_CONFIG_PHAL_FOLDER: required: true compose_files: - - docker-compose.yml + - docker-compose.yml OVOS_PERSONA_FOLDER: required: true compose_files: - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml OVOS_SHARE_FOLDER: required: true compose_files: - - docker-compose.gui.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml OVOS_USER: required: true compose_files: - - docker-compose.gui.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml PULL_POLICY: required: false compose_files: - - docker-compose.gui.yml - - docker-compose.hivemind.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml owner: installer description: Image pull policy. The compose has a fallback, so an unset value silently overrides the policy the installer was configured with rather than @@ -182,58 +183,58 @@ env: PULSE_SERVER: required: false compose_files: - - docker-compose.macos.yml - - docker-compose.windows.yml + - docker-compose.macos.yml + - docker-compose.windows.yml QT_QPA_EGLFS_INTEGRATION: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml QT_QPA_EGLFS_KMS_CONFIG: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml QT_QPA_PLATFORM: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml RENDER_GID: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml SPI_GID: required: true compose_files: - - docker-compose.raspberrypi.yml + - docker-compose.raspberrypi.yml STANDARD_MEMORY_LIMIT: required: false compose_files: - - docker-compose.gui.yml - - docker-compose.macos.yml - - docker-compose.raspberrypi.yml - - docker-compose.server.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.raspberrypi.yml + - docker-compose.server.yml + - docker-compose.windows.yml + - docker-compose.yml TMP_FOLDER: required: true compose_files: - - docker-compose.gui.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml TZ: required: false compose_files: - - docker-compose.gui.yml - - docker-compose.hivemind.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml owner: installer description: Timezone for every service. The compose falls back to a default, so an installer that does not set it produces a working stack in the wrong timezone @@ -241,58 +242,58 @@ env: VERSION: required: true compose_files: - - docker-compose.gui.yml - - docker-compose.hivemind.yml - - docker-compose.macos.yml - - docker-compose.server.yml - - docker-compose.skills-extra.yml - - docker-compose.skills.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.hivemind.yml + - docker-compose.macos.yml + - docker-compose.server.yml + - docker-compose.skills-extra.yml + - docker-compose.skills.yml + - docker-compose.windows.yml + - docker-compose.yml VIDEO_GID: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml WAYLAND_DISPLAY: required: true compose_files: - - docker-compose.gui.yml + - docker-compose.gui.yml XDG_RUNTIME_DIR: required: true compose_files: - - docker-compose.gui.yml - - docker-compose.windows.yml - - docker-compose.yml + - docker-compose.gui.yml + - docker-compose.windows.yml + - docker-compose.yml images: -- docker.io/smartgic/hivemind-cli -- docker.io/smartgic/hivemind-listener -- docker.io/smartgic/ovos-audio -- docker.io/smartgic/ovos-cli -- docker.io/smartgic/ovos-core -- docker.io/smartgic/ovos-gui-shell -- docker.io/smartgic/ovos-gui-websocket -- docker.io/smartgic/ovos-listener -- docker.io/smartgic/ovos-messagebus -- docker.io/smartgic/ovos-phal -- docker.io/smartgic/ovos-phal-admin -- docker.io/smartgic/ovos-plugin-ggwave -- docker.io/smartgic/ovos-skill-alerts -- docker.io/smartgic/ovos-skill-camera -- docker.io/smartgic/ovos-skill-date-time -- docker.io/smartgic/ovos-skill-duckduckgo -- docker.io/smartgic/ovos-skill-easter-eggs -- docker.io/smartgic/ovos-skill-fallback-unknown -- docker.io/smartgic/ovos-skill-ggwave -- docker.io/smartgic/ovos-skill-hello-world -- docker.io/smartgic/ovos-skill-homeassistant -- docker.io/smartgic/ovos-skill-homescreen -- docker.io/smartgic/ovos-skill-jokes -- docker.io/smartgic/ovos-skill-parrot -- docker.io/smartgic/ovos-skill-personal -- docker.io/smartgic/ovos-skill-randomness -- docker.io/smartgic/ovos-skill-volume -- docker.io/smartgic/ovos-skill-weather -- docker.io/smartgic/ovos-skill-wikihow -- docker.io/smartgic/ovos-skill-wikipedia -- docker.io/smartgic/ovos-skill-wolfie -- docker.io/smartgic/ovos-skill-wordnet + - docker.io/smartgic/hivemind-cli + - docker.io/smartgic/hivemind-listener + - docker.io/smartgic/ovos-audio + - docker.io/smartgic/ovos-cli + - docker.io/smartgic/ovos-core + - docker.io/smartgic/ovos-gui-shell + - docker.io/smartgic/ovos-gui-websocket + - docker.io/smartgic/ovos-listener + - docker.io/smartgic/ovos-messagebus + - docker.io/smartgic/ovos-phal + - docker.io/smartgic/ovos-phal-admin + - docker.io/smartgic/ovos-plugin-ggwave + - docker.io/smartgic/ovos-skill-alerts + - docker.io/smartgic/ovos-skill-camera + - docker.io/smartgic/ovos-skill-date-time + - docker.io/smartgic/ovos-skill-duckduckgo + - docker.io/smartgic/ovos-skill-easter-eggs + - docker.io/smartgic/ovos-skill-fallback-unknown + - docker.io/smartgic/ovos-skill-ggwave + - docker.io/smartgic/ovos-skill-hello-world + - docker.io/smartgic/ovos-skill-homeassistant + - docker.io/smartgic/ovos-skill-homescreen + - docker.io/smartgic/ovos-skill-jokes + - docker.io/smartgic/ovos-skill-parrot + - docker.io/smartgic/ovos-skill-personal + - docker.io/smartgic/ovos-skill-randomness + - docker.io/smartgic/ovos-skill-volume + - docker.io/smartgic/ovos-skill-weather + - docker.io/smartgic/ovos-skill-wikihow + - docker.io/smartgic/ovos-skill-wikipedia + - docker.io/smartgic/ovos-skill-wolfie + - docker.io/smartgic/ovos-skill-wordnet From 7c62dad77f74edfca517470c998098db7e0c382d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Wed, 9 Sep 2026 12:23:30 -0400 Subject: [PATCH 3/4] feat: report fixes that are merged upstream but in no release Comparing the pin against the newest release cannot see the failure that matters. Both can read v2.0.2 while ten days of fixes sit unreleased on the default branch - which is how the fann2 gating, the intent-engine assertion and the terminal client all failed to reach a single install while every check here was green. --online now also reports how far the newest release trails the branch it is cut from, and --fail-on-stale makes it a failure: ovos-docker: v2.0.2 is 24 commit(s) behind dev - those fixes are in no release, so no install has them Co-Authored-By: Claude Opus 5 --- scripts/check_contracts.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/check_contracts.py b/scripts/check_contracts.py index 2a0d5ff9..f0d1cff1 100755 --- a/scripts/check_contracts.py +++ b/scripts/check_contracts.py @@ -120,6 +120,25 @@ def fetch(slug: str, ref: str, path: str) -> str | None: return None +def release_lag(slug: str, tag: str) -> tuple[int, str] | None: + """How far the newest release trails the branch it is cut from. + + Pinning a release is what makes an install reproducible, but a fix only reaches anyone once a + release carries it. Comparing the pin against the newest release cannot see this: both can say + v2.0.2 while ten days of fixes sit unreleased on the default branch, which is exactly how the + fann2 gating and the intent-engine assertion failed to reach a single install. + """ + try: + branch = subprocess.run(["gh", "repo", "view", slug, "--json", "defaultBranchRef", + "-q", ".defaultBranchRef.name"], + capture_output=True, text=True, check=True).stdout.strip() + out = subprocess.run(["gh", "api", f"repos/{slug}/compare/{tag}...{branch}", + "-q", ".ahead_by"], capture_output=True, text=True, check=True) + return int(out.stdout.strip()), branch + except Exception: + return None + + def latest_release(slug: str) -> str | None: try: out = subprocess.run(["gh", "release", "view", "--repo", slug, "--json", "tagName", "-q", ".tagName"], @@ -162,6 +181,13 @@ def main() -> int: message = f"{repo}: pinned {ref}, latest release is {newest}" (problems if args.fail_on_stale else notes).append(message) + lag = release_lag(slug, newest or ref) + if lag and lag[0]: + behind, branch = lag + message = (f"{repo}: {newest or ref} is {behind} commit(s) behind {branch} - " + f"those fixes are in no release, so no install has them") + (problems if args.fail_on_stale else notes).append(message) + problems.extend(check(repo, contract, facts)) for note in notes: From 99a45230a7f5ac61c9c39db061d883dd36651f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Wed, 9 Sep 2026 12:32:38 -0400 Subject: [PATCH 4/4] chore: refresh the snapshots from the published contracts v2.1.0 and v2.1.1 ship contract.yml, so the snapshots stop being derived from the compose files at those tags and become what --online verifies them against. With the pins current, strict mode is quiet: no drift, no pin behind a release, and no release behind the branch it is cut from. Co-Authored-By: Claude Opus 5 --- tests/contracts/hivemind-docker.yml | 3 +++ tests/contracts/ovos-docker.yml | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/contracts/hivemind-docker.yml b/tests/contracts/hivemind-docker.yml index f4c8be0f..d9ea3c5c 100644 --- a/tests/contracts/hivemind-docker.yml +++ b/tests/contracts/hivemind-docker.yml @@ -1,4 +1,7 @@ --- +# Generated by scripts/contract.py - run it after changing compose/. +# What ovos-installer may rely on from this repository. `owner` and `description` +# are hand-written and preserved; everything else is derived from the compose files. version: 1 compose_files: - docker-compose.chatroom.yml diff --git a/tests/contracts/ovos-docker.yml b/tests/contracts/ovos-docker.yml index 458fb266..119aabec 100644 --- a/tests/contracts/ovos-docker.yml +++ b/tests/contracts/ovos-docker.yml @@ -1,4 +1,7 @@ --- +# Generated by scripts/contract.py - run it after changing compose/. +# What ovos-installer may rely on from this repository. `owner` and `description` +# are hand-written and preserved; everything else is derived from the compose files. version: 1 compose_files: - docker-compose.gui.yml @@ -27,7 +30,6 @@ services: ovos_messagebus: ovos_messagebus ovos_phal: ovos_phal ovos_phal_admin: ovos_phal_admin - ovos_plugin_ggwave: ovos_plugin_ggwave docker-compose.server.yml: hivemind_cli: hivemind_cli hivemind_listener: hivemind_listener @@ -62,7 +64,6 @@ services: ovos_messagebus: ovos_messagebus ovos_phal: ovos_phal ovos_phal_admin: ovos_phal_admin - ovos_plugin_ggwave: ovos_plugin_ggwave docker-compose.yml: ovos_audio: ovos_audio ovos_cli: ovos_cli @@ -71,7 +72,6 @@ services: ovos_messagebus: ovos_messagebus ovos_phal: ovos_phal ovos_phal_admin: ovos_phal_admin - ovos_plugin_ggwave: ovos_plugin_ggwave env: CORE_MEMORY_LIMIT: required: false @@ -276,7 +276,6 @@ images: - docker.io/smartgic/ovos-messagebus - docker.io/smartgic/ovos-phal - docker.io/smartgic/ovos-phal-admin - - docker.io/smartgic/ovos-plugin-ggwave - docker.io/smartgic/ovos-skill-alerts - docker.io/smartgic/ovos-skill-camera - docker.io/smartgic/ovos-skill-date-time