name: ci on: push: branches: [main] pull_request: branches: [main] jobs: vet-and-test: runs-on: ubuntu-latest env: GOPROXY: https://goproxy.cn,direct GOTOOLCHAIN: local GOWORK: "off" GOPRIVATE: git-ssh.u1680832.nyat.app/weichen steps: - name: Checkout env: TOKEN: ${{ secrets.GITHUB_TOKEN }} SUBMODULE_TOKEN: ${{ secrets.SUBMODULE_TOKEN }} run: &trusted_acquisition_1 | set -euo pipefail python3 - <<'PY_TRUSTED' # Trusted workflow code only: never import or execute checkout scripts here. # Token scopes, workflow provenance and runner isolation are platform NOT_RUN. import json import os from pathlib import Path import re import shlex import subprocess import sys import tempfile from urllib.parse import urlsplit SERVER = os.environ.get("GITHUB_SERVER_URL", "").rstrip("/") parsed = urlsplit(SERVER) if (parsed.scheme not in {"http", "https"} or not parsed.netloc or parsed.username or parsed.password or parsed.query or parsed.fragment): sys.exit("Acquisition requires the platform-injected http(s) server") SSH = "ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/" GO_PREFIX = "git-ssh.u1680832.nyat.app/weichen/" REPOS = {"Agent-System-Dev", "Logging-System", "Error-Code-System", "Agent-System-Core", "Agent-Native", "Agent-Intent", "Agent-Command", "Agent-Squad", "Agent-System-Backend", "Agent-System-Frontend", "Agent-System-UI", "Agent-System-RDM", "Development-Guidelines", "Agent-System-Documents"} LEGACY = { "modules/core": "Agent-System-Core", "modules/backend": "Agent-System-Backend", "modules/frontend": "Agent-System-Frontend", "modules/ui": "Agent-System-UI", "modules/documents": "Agent-System-Documents", "deps/agent-system-core": "Agent-System-Core", "deps/development-guidelines": "Development-Guidelines", "deps/agent-native": "Agent-Native", "deps/agent-squad": "Agent-Squad", "deps/agent-intent": "Agent-Intent", "deps/agent-command": "Agent-Command", "deps/error-code-system": "Error-Code-System", "deps/logging-system": "Logging-System", "pins/agent-system-backend": "Agent-System-Backend", "pins/agent-system-core": "Agent-System-Core", "pins/agent-system-frontend": "Agent-System-Frontend", "pins/agent-system-ui": "Agent-System-UI", } GO_REPOS = REPOS - {"Agent-System-Dev", "Agent-System-UI", "Agent-System-Frontend", "Development-Guidelines", "Agent-System-Documents"} GO_MODULES = {GO_PREFIX + repo + ".git" for repo in GO_REPOS} GO_MODULES |= {GO_PREFIX + "Agent-Squad.git/" + suffix for suffix in ("adapters/native", "adapters/intent", "adapters/sqlite", "adapters/a2a", "cmd/agent-squad")} root_token = os.environ.pop("TOKEN", "") component_token = os.environ.pop("SUBMODULE_TOKEN", "") # Go environment files and inherited Git tracing/config must not change acquisition. clean = {k: v for k, v in os.environ.items() if not k.startswith(("GIT_", "NPM_CONFIG_", "npm_config_")) and not any(word in k.upper() for word in ("TOKEN", "PASSWORD", "SECRET"))} clean.update(GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_SYSTEM="/dev/null", GIT_CONFIG_GLOBAL="/dev/null", GIT_TERMINAL_PROMPT="0", GOENV="off", GOTOOLCHAIN="local", GOWORK="off", GOFLAGS="-mod=readonly", GOPRIVATE=GO_PREFIX.rstrip("/"), GONOPROXY=GO_PREFIX.rstrip("/"), GONOSUMDB=GO_PREFIX.rstrip("/")) if os.environ.get("npm_config_cache"): clean["npm_config_cache"] = os.environ["npm_config_cache"] config = {"core.hooksPath": "/dev/null", "core.fsmonitor": "false", "init.templateDir": "", "credential.helper": "", "fetch.recurseSubmodules": "false", "submodule.recurse": "false", "http.followRedirects": "false", "protocol.allow": "never", "protocol." + parsed.scheme + ".allow": "always"} root = Path.cwd() mirrors = root / ".ci-mirrors" def process_env(values): env = clean | {"GIT_CONFIG_COUNT": str(len(values))} for index, (key, value) in enumerate(values.items()): env[f"GIT_CONFIG_KEY_{index}"] = key env[f"GIT_CONFIG_VALUE_{index}"] = value return env def git(directory, *args, token="", repo=""): values = config.copy() if token: if repo not in REPOS: raise ValueError("Credential destination is outside the reviewed repository allowlist") values[f"http.{SERVER}/weichen/{repo}.git.extraheader"] = "AUTHORIZATION: token " + token result = subprocess.run(["git", "-C", str(directory), *args], env=process_env(values), check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return result.stdout def fixed_sha(value): if not isinstance(value, str) or not re.fullmatch(r"[0-9a-fA-F]{40}", value): raise ValueError("Source acquisition requires a fixed commit SHA") return value.lower() def mirror(repo, sha, token): sha = fixed_sha(sha) destination = mirrors / (repo + ".git") if destination.is_symlink() or mirrors.is_symlink(): raise ValueError("Mirror directory cannot be a symlink") mirrors.mkdir(exist_ok=True) if not destination.exists(): destination.mkdir() git(destination, "init", "--bare", "-q") url = f"{SERVER}/weichen/{repo}.git" try: git(destination, "cat-file", "-e", sha + "^{commit}") except subprocess.CalledProcessError: git(destination, "fetch", "--depth=200", url, sha, token=token, repo=repo) git(destination, "update-ref", "refs/heads/main", sha) git(destination, "symbolic-ref", "HEAD", "refs/heads/main") git(destination, "update-ref", "refs/ci/" + sha, sha) return destination def mirror_env(): values = {"protocol.file.allow": "always", "core.hooksPath": "/dev/null", "credential.helper": ""} # Distinct keys use repeated .insteadOf values, represented as a sequence below. pairs = list(values.items()) for repo in sorted(REPOS): destination = mirrors / (repo + ".git") if not destination.exists(): continue key = "url." + destination.as_uri() + ".insteadOf" pairs.extend((key, url) for url in (SSH + repo + ".git", "https://" + GO_PREFIX + repo + ".git", SERVER + "/weichen/" + repo + ".git", "https://" + GO_PREFIX + repo)) env = clean | {"CI_DEPENDENCY_MIRRORS": str(mirrors), "GIT_CONFIG_COUNT": str(len(pairs))} for index, (key, value) in enumerate(pairs): env[f"GIT_CONFIG_KEY_{index}"] = key env[f"GIT_CONFIG_VALUE_{index}"] = value exports = {k: v for k, v in env.items() if k.startswith("GIT_CONFIG_") or k == "CI_DEPENDENCY_MIRRORS"} (root / ".ci-mirrors.env").write_text("".join("export " + k + "=" + shlex.quote(v) + "\n" for k, v in exports.items())) return env def materialize_legacy(directory, depth=0): if depth > 16: raise ValueError("Historical submodule depth exceeds the checkout limit") links = {} for entry in git(directory, "ls-tree", "-rz", "HEAD").split(b"\0"): if entry: metadata, path = entry.split(b"\t", 1) mode, kind, commit = metadata.decode().split() if mode == "160000": links[path.decode()] = commit if not links: return raw = git(directory, "config", "--no-includes", "--blob", "HEAD:.gitmodules", "--null", "--list") entries = {} for item in raw.split(b"\0"): if not item: continue key, value = item.decode().split("\n", 1) match = re.fullmatch(r"submodule\.(.+)\.(path|url)", key) if not match: raise ValueError("Unsupported candidate .gitmodules setting") name, field = match.groups() if field in entries.setdefault(name, {}): raise ValueError("Duplicate candidate .gitmodules setting") entries[name][field] = value if set(entries) != set(links): raise ValueError("Submodule names must match fixed gitlink paths") for name, fields in entries.items(): repo = LEGACY.get(name) if not repo or fields.get("path") != name or fields.get("url") not in { SSH + repo + ".git", f"{SERVER}/weichen/{repo}.git"}: raise ValueError("Historical submodule source is outside the reviewed allowlist") for name in entries: repo = LEGACY[name] sha = links[name] url = f"{SERVER}/weichen/{repo}.git" git(directory, "config", f"submodule.{name}.url", url) # Trusted explicit checkout prevents candidate update commands from running. git(directory, "submodule", "update", "--init", "--checkout", "--", name, token=component_token, repo=repo) child = directory / name if git(child, "rev-parse", "HEAD").decode().strip() != sha: raise ValueError("Historical submodule does not match its fixed SHA") mirror(repo, sha, component_token) materialize_legacy(child, depth + 1) def materialize_manifest(directory): manifest = directory / "components.json" lock = directory / "components.lock.json" if not manifest.exists() and not lock.exists(): materialize_legacy(directory) return data = json.loads(manifest.read_text()) locked = json.loads(lock.read_text()) if data.get("schema") != 1 or locked.get("schema") != 1: raise ValueError("Unsupported source manifest schema") entries = {} for item in data["components"]: repo = item["id"] path = ".workspace/" + repo if (repo not in REPOS or repo in entries or item["path"] != path or item["repo"] not in {SSH + repo + ".git", f"{SERVER}/weichen/{repo}.git"} or item.get("role") not in {"go-library", "npm-library", "application", "guidelines", "documentation"}): raise ValueError("Candidate source manifest is outside the reviewed allowlist") for module in item.get("modules", []): if module.get("path") not in GO_MODULES or not module["path"].startswith(GO_PREFIX + repo + ".git"): raise ValueError("Candidate module mapping is outside the reviewed allowlist") entries[repo] = item selected = locked["components"] if set(selected) - set(entries): raise ValueError("Source lock refers to an unregistered repository") for repo, item in entries.items(): if item["role"] in {"application", "guidelines", "documentation"} and repo not in selected: raise ValueError("Application and documentation source needs a fixed lock") for repo, pin in selected.items(): fixed_sha(pin["commit"]) destination = directory / entries[repo]["path"] if destination.exists() or destination.is_symlink() or destination.parent.is_symlink(): raise ValueError("Source materialization requires a fresh destination") # Validate the complete lock before any authenticated component fetch. for repo, pin in selected.items(): sha = fixed_sha(pin["commit"]) destination = directory / entries[repo]["path"] destination.mkdir(parents=True) url = f"{SERVER}/weichen/{repo}.git" git(destination, "init", "-q") git(destination, "remote", "add", "origin", url) git(destination, "fetch", "--depth=1", "origin", sha, token=component_token, repo=repo) git(destination, "checkout", "-q", "--detach", "FETCH_HEAD") if git(destination, "rev-parse", "HEAD").decode().strip() != sha: raise ValueError("Materialized source differs from the fixed lock") # Persist the registered identity; process config chooses the transport. git(destination, "remote", "set-url", "origin", entries[repo]["repo"]) mirror(repo, sha, component_token) materialize_legacy(destination) try: if os.environ.get("GITHUB_REPOSITORY") != "weichen/Agent-Squad": raise ValueError("Checkout repository is outside the reviewed allowlist") sha = fixed_sha(os.environ.get("GITHUB_SHA", "")) if not root_token: raise ValueError("Checkout requires TOKEN") if (root / ".git").exists(): raise ValueError("Checkout requires a fresh workspace") git(root, "init", "-q") git(root, "remote", "add", "origin", SERVER + "/weichen/Agent-Squad.git") git(root, "fetch", "--depth=1", "origin", sha, token=root_token, repo="Agent-Squad") git(root, "checkout", "-q", "--detach", "FETCH_HEAD") if git(root, "rev-parse", "HEAD").decode().strip() != sha: raise ValueError("Checked-out commit differs from the platform commit") if (root / ".ci-mirrors").exists() or (root / ".ci-mirrors.env").exists(): raise ValueError("Candidate cannot provide trusted prefetch state") materialize_manifest(root) mirror_env() except (ValueError, KeyError, OSError, subprocess.CalledProcessError) as error: # Do not print child stderr or environment: a failed credential fetch stays redacted. sys.exit("Checkout refused: " + str(error)) print("Fixed source checkout complete; credentials were not persisted") PY_TRUSTED unset TOKEN SUBMODULE_TOKEN - name: Install Go 1.27.1 run: | set -euo pipefail curl -fsSL -o "$RUNNER_TEMP/go.tgz" https://mirrors.aliyun.com/golang/go1.27.1.linux-amd64.tar.gz tar -C /usr/local -xzf "$RUNNER_TEMP/go.tgz" ln -sfn /usr/local/go/bin/go /usr/local/bin/go ln -sfn /usr/local/go/bin/gofmt /usr/local/bin/gofmt - name: Prefetch Go modules without executing candidate code env: SUBMODULE_TOKEN: ${{ secrets.SUBMODULE_TOKEN }} run: &trusted_acquisition_2 | set -euo pipefail python3 - <<'PY_TRUSTED' # Trusted workflow code only: never import or execute checkout scripts here. # Token scopes, workflow provenance and runner isolation are platform NOT_RUN. import json import os from pathlib import Path import re import shlex import subprocess import sys import tempfile from urllib.parse import urlsplit SERVER = os.environ.get("GITHUB_SERVER_URL", "").rstrip("/") parsed = urlsplit(SERVER) if (parsed.scheme not in {"http", "https"} or not parsed.netloc or parsed.username or parsed.password or parsed.query or parsed.fragment): sys.exit("Acquisition requires the platform-injected http(s) server") SSH = "ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/" GO_PREFIX = "git-ssh.u1680832.nyat.app/weichen/" REPOS = {"Agent-System-Dev", "Logging-System", "Error-Code-System", "Agent-System-Core", "Agent-Native", "Agent-Intent", "Agent-Command", "Agent-Squad", "Agent-System-Backend", "Agent-System-Frontend", "Agent-System-UI", "Agent-System-RDM", "Development-Guidelines", "Agent-System-Documents"} LEGACY = { "modules/core": "Agent-System-Core", "modules/backend": "Agent-System-Backend", "modules/frontend": "Agent-System-Frontend", "modules/ui": "Agent-System-UI", "modules/documents": "Agent-System-Documents", "deps/agent-system-core": "Agent-System-Core", "deps/development-guidelines": "Development-Guidelines", "deps/agent-native": "Agent-Native", "deps/agent-squad": "Agent-Squad", "deps/agent-intent": "Agent-Intent", "deps/agent-command": "Agent-Command", "deps/error-code-system": "Error-Code-System", "deps/logging-system": "Logging-System", "pins/agent-system-backend": "Agent-System-Backend", "pins/agent-system-core": "Agent-System-Core", "pins/agent-system-frontend": "Agent-System-Frontend", "pins/agent-system-ui": "Agent-System-UI", } GO_REPOS = REPOS - {"Agent-System-Dev", "Agent-System-UI", "Agent-System-Frontend", "Development-Guidelines", "Agent-System-Documents"} GO_MODULES = {GO_PREFIX + repo + ".git" for repo in GO_REPOS} GO_MODULES |= {GO_PREFIX + "Agent-Squad.git/" + suffix for suffix in ("adapters/native", "adapters/intent", "adapters/sqlite", "adapters/a2a", "cmd/agent-squad")} root_token = os.environ.pop("TOKEN", "") component_token = os.environ.pop("SUBMODULE_TOKEN", "") # Go environment files and inherited Git tracing/config must not change acquisition. clean = {k: v for k, v in os.environ.items() if not k.startswith(("GIT_", "NPM_CONFIG_", "npm_config_")) and not any(word in k.upper() for word in ("TOKEN", "PASSWORD", "SECRET"))} clean.update(GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_SYSTEM="/dev/null", GIT_CONFIG_GLOBAL="/dev/null", GIT_TERMINAL_PROMPT="0", GOENV="off", GOTOOLCHAIN="local", GOWORK="off", GOFLAGS="-mod=readonly", GOPRIVATE=GO_PREFIX.rstrip("/"), GONOPROXY=GO_PREFIX.rstrip("/"), GONOSUMDB=GO_PREFIX.rstrip("/")) if os.environ.get("npm_config_cache"): clean["npm_config_cache"] = os.environ["npm_config_cache"] config = {"core.hooksPath": "/dev/null", "core.fsmonitor": "false", "init.templateDir": "", "credential.helper": "", "fetch.recurseSubmodules": "false", "submodule.recurse": "false", "http.followRedirects": "false", "protocol.allow": "never", "protocol." + parsed.scheme + ".allow": "always"} root = Path.cwd() mirrors = root / ".ci-mirrors" def process_env(values): env = clean | {"GIT_CONFIG_COUNT": str(len(values))} for index, (key, value) in enumerate(values.items()): env[f"GIT_CONFIG_KEY_{index}"] = key env[f"GIT_CONFIG_VALUE_{index}"] = value return env def git(directory, *args, token="", repo=""): values = config.copy() if token: if repo not in REPOS: raise ValueError("Credential destination is outside the reviewed repository allowlist") values[f"http.{SERVER}/weichen/{repo}.git.extraheader"] = "AUTHORIZATION: token " + token result = subprocess.run(["git", "-C", str(directory), *args], env=process_env(values), check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return result.stdout def fixed_sha(value): if not isinstance(value, str) or not re.fullmatch(r"[0-9a-fA-F]{40}", value): raise ValueError("Source acquisition requires a fixed commit SHA") return value.lower() def mirror(repo, sha, token): sha = fixed_sha(sha) destination = mirrors / (repo + ".git") if destination.is_symlink() or mirrors.is_symlink(): raise ValueError("Mirror directory cannot be a symlink") mirrors.mkdir(exist_ok=True) if not destination.exists(): destination.mkdir() git(destination, "init", "--bare", "-q") url = f"{SERVER}/weichen/{repo}.git" try: git(destination, "cat-file", "-e", sha + "^{commit}") except subprocess.CalledProcessError: git(destination, "fetch", "--depth=200", url, sha, token=token, repo=repo) git(destination, "update-ref", "refs/heads/main", sha) git(destination, "symbolic-ref", "HEAD", "refs/heads/main") git(destination, "update-ref", "refs/ci/" + sha, sha) return destination def mirror_env(): values = {"protocol.file.allow": "always", "core.hooksPath": "/dev/null", "credential.helper": ""} # Distinct keys use repeated .insteadOf values, represented as a sequence below. pairs = list(values.items()) for repo in sorted(REPOS): destination = mirrors / (repo + ".git") if not destination.exists(): continue key = "url." + destination.as_uri() + ".insteadOf" pairs.extend((key, url) for url in (SSH + repo + ".git", "https://" + GO_PREFIX + repo + ".git", SERVER + "/weichen/" + repo + ".git", "https://" + GO_PREFIX + repo)) env = clean | {"CI_DEPENDENCY_MIRRORS": str(mirrors), "GIT_CONFIG_COUNT": str(len(pairs))} for index, (key, value) in enumerate(pairs): env[f"GIT_CONFIG_KEY_{index}"] = key env[f"GIT_CONFIG_VALUE_{index}"] = value exports = {k: v for k, v in env.items() if k.startswith("GIT_CONFIG_") or k == "CI_DEPENDENCY_MIRRORS"} (root / ".ci-mirrors.env").write_text("".join("export " + k + "=" + shlex.quote(v) + "\n" for k, v in exports.items())) return env def source_roots(): bases = [root] composition = root / ".ci-composition-dev" if composition.is_dir() and not composition.is_symlink(): bases.append(composition) paths = [] for base in bases: paths += [base, base / "rdm"] + [base / path for path in LEGACY] + [base / ".workspace" / repo for repo in sorted(REPOS)] return [path for path in paths if path.is_dir() and not path.is_symlink()] try: targets = [] for source in source_roots(): for suffix in (".", "examples/trace", "adapters/native", "adapters/intent", "adapters/sqlite", "adapters/a2a", "cmd/agent-squad"): directory = source / suffix if (directory / "go.mod").is_file() and directory.resolve() not in targets: targets.append(directory.resolve()) # Parse using the installed local Go tool without credentials or candidate execution. original_files = {} for directory in targets: parsed_mod = json.loads(subprocess.check_output(["go", "mod", "edit", "-json"], cwd=directory, env=clean)) canonical = parsed_mod["Module"]["Path"].startswith(GO_PREFIX) for name in ("go.mod", "go.sum"): dependency_file = directory / name if dependency_file.is_file(): original_files[(directory, name)] = dependency_file.read_bytes() elif canonical and name == "go.sum": original_files[(directory, name)] = None for dep in (parsed_mod.get("Require") or []): path = dep["Path"] if path.startswith(GO_PREFIX) and path not in GO_MODULES: raise ValueError("Go dependency is outside the reviewed module mapping") for replacement in (parsed_mod.get("Replace") or []): path = replacement["New"]["Path"] if canonical and not replacement["New"].get("Version"): raise ValueError("Canonical module cannot acquire a local path replacement") if path.startswith(GO_PREFIX) and path not in GO_MODULES: raise ValueError("Go replacement is outside the reviewed module mapping") auth = config.copy() for repo in sorted(GO_REPOS): url = f"{SERVER}/weichen/{repo}.git" auth[f"url.{url}.insteadOf"] = "https://" + GO_PREFIX + repo auth[f"http.{url}.extraheader"] = "AUTHORIZATION: token " + component_token def objects(output): decoder = json.JSONDecoder() remaining = output.strip() while remaining: item, end = decoder.raw_decode(remaining) remaining = remaining[end:].lstrip() yield item def unchanged(): for (owner, name), contents in original_files.items(): dependency_file = owner / name current = dependency_file.read_bytes() if dependency_file.exists() else None if current != contents: raise ValueError("Go acquisition modified a committed Go dependency file: " + str(dependency_file)) for directory in targets: # Resolve private go.mod metadata only through exact repository headers; # validate the complete selected graph before acquiring module zip files. graph = subprocess.run(["go", "list", "-mod=readonly", "-m", "-json", "all"], cwd=directory, env=process_env(auth), capture_output=True, text=True) if graph.returncode: raise ValueError("Go selected graph resolution failed: " + graph.stderr.strip()) for item in objects(graph.stdout): if item.get("Main"): continue selected = item.get("Replace") or item for path in (item.get("Path", ""), selected.get("Path", "")): if path.startswith(GO_PREFIX) and path not in GO_MODULES: raise ValueError("Selected Go module is outside the reviewed mapping") unchanged() # Go 1.27 can prune go.sum even under -mod=readonly for this # metadata-only command. Use a disposable copy, then validate actual # package loading against the unchanged committed source locks. local_replacements = json.loads(subprocess.check_output( ["go", "mod", "edit", "-json"], cwd=directory, env=clean)).get("Replace") or [] downloaded = "" if not any(not item["New"].get("Version") for item in local_replacements): with tempfile.TemporaryDirectory(prefix="ci-go-prefetch-") as temporary: download_root = Path(temporary) for name in ("go.mod", "go.sum"): if (directory / name).is_file(): (download_root / name).write_bytes((directory / name).read_bytes()) result = subprocess.run(["go", "mod", "download", "-json"], cwd=download_root, env=process_env(auth), capture_output=True, text=True) if result.returncode: raise ValueError("Go module acquisition failed: " + result.stderr.strip()) downloaded = result.stdout unchanged() for item in objects(downloaded): path = item.get("Path", "") if path.startswith(GO_PREFIX): if path not in GO_MODULES: raise ValueError("Resolved Go module is outside the reviewed mapping") repo = path[len(GO_PREFIX):].split(".git", 1)[0] origin = item.get("Origin") or json.loads(Path(item["Info"]).read_text()).get("Origin") or {} mirror(repo, fixed_sha(origin.get("Hash")), component_token) # Package metadata loads actual test/build dependencies without running # vet, compilers, package init functions or candidate tests. No CGO tools. packages = subprocess.run(["go", "list", "-mod=readonly", "-deps", "-test", "./..."], cwd=directory, env=process_env(auth) | {"CGO_ENABLED": "0"}, capture_output=True, text=True) if packages.returncode: raise ValueError("Go test/build dependency acquisition failed: " + packages.stderr.strip()) unchanged() mirror_env() except (ValueError, OSError, subprocess.CalledProcessError) as error: sys.exit("Go prefetch refused: " + str(error)) print("Go modules cached with GOTOOLCHAIN=local; no candidate code executed") PY_TRUSTED unset TOKEN SUBMODULE_TOKEN - name: Vet, test, and race run: | set -euo pipefail source .ci-mirrors.env for module in . adapters/native adapters/intent adapters/sqlite adapters/a2a cmd/agent-squad; do test -f "$module/go.mod" (cd "$module" && go vet -mod=readonly ./... && go test -mod=readonly -count=1 ./... && go test -mod=readonly -count=1 -race ./...) done docker-checkout-hostgateway: runs-on: ubuntu-latest options: --add-host gitea:host-gateway env: GITHUB_SERVER_URL: http://gitea:3000 steps: - name: Checkout env: TOKEN: ${{ secrets.GITHUB_TOKEN }} SUBMODULE_TOKEN: ${{ secrets.SUBMODULE_TOKEN }} run: *trusted_acquisition_1 - run: test -f go.mod && echo "docker-mode checkout via host-gateway OK"