From 9c67c3c235c1a073bde99a82a906ac4cee64314d Mon Sep 17 00:00:00 2001 From: Daniel McIlvaney Date: Tue, 21 Jul 2026 16:04:43 -0700 Subject: [PATCH] chore(azldev): rework alzdev version pinning to use go modules --- .github/dependabot.yml | 15 + .../pr-check-workflows.instructions.md | 6 +- .../ado/templates/steps/install-deps.yml | 14 +- .github/workflows/azldev-smoke.yml | 39 +-- .github/workflows/check-rendered-specs.yml | 37 ++- .../containers/azldev-runner.Dockerfile | 12 +- .github/workflows/lint.yaml | 11 +- DEVELOPING.md | 21 +- .../resolve_azldev_version.py | 209 +++++++++--- .../tests/test_resolve_azldev_version.py | 261 ++++++++++++--- tools/azldev/go.mod | 109 +++++++ tools/azldev/go.sum | 303 ++++++++++++++++++ tools/azldev/tools.go | 9 + 13 files changed, 893 insertions(+), 153 deletions(-) create mode 100644 tools/azldev/go.mod create mode 100644 tools/azldev/go.sum create mode 100644 tools/azldev/tools.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b0e1cfa3aef..78eec9ff236 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -41,3 +41,18 @@ updates: actions: patterns: - "*" + + # Dependabot does not yet update dependencies declared only with Go tool + # directives: https://github.com/dependabot/dependabot-core/issues/12050. + # Keep the direct Azure Linux tool current without opening standalone PRs for + # its transitive dependencies. Add another dependency-name here for each + # additional repository-managed Go tool. + - package-ecosystem: gomod + directory: /tools/azldev + schedule: + interval: daily + open-pull-requests-limit: 5 + labels: + - dependencies + allow: + - dependency-name: github.com/microsoft/azure-linux-dev-tools diff --git a/.github/instructions/pr-check-workflows.instructions.md b/.github/instructions/pr-check-workflows.instructions.md index 41fe37d9efa..0fd4a4d8ea2 100644 --- a/.github/instructions/pr-check-workflows.instructions.md +++ b/.github/instructions/pr-check-workflows.instructions.md @@ -31,16 +31,18 @@ If the check builds, renders, or runs PR code, do the whole thing inside the bui The shared runner image is [`.github/workflows/containers/azldev-runner.Dockerfile`](../workflows/containers/azldev-runner.Dockerfile). It's a minimal Azure Linux base with `mock`, `git`, `python3`, `sudo`, and `azldev` itself (installed to `/usr/local/bin` during image build) — enough to run any `azldev` subcommand. Reuse it rather than building a per-check image; add extras via a derived `FROM localhost/azldev-runner` stage if a check genuinely needs more. -`azldev` is baked in via `go install` during image build. The version is pinned in `.azldev-version` at the repo root and passed to the Dockerfile as `--build-arg AZLDEV_VERSION=…`. All CI workflows (GH Actions, ADO, Dockerfile) read from the same file. Image build context is `.github/workflows/containers/` only — keep it that way so the build can never see PR-controlled files. +`azldev` is baked in via `go install` during image build. The version is declared in `tools/azldev/go.mod`; callers resolve it to a validated full Git hash before passing it to the Dockerfile as `--build-arg AZLDEV_HASH=…`. Image build context is `.github/workflows/containers/` only — keep it that way so the build can never see PR-controlled files. Build it with the caller's UID so bind-mounted writes don't end up root-owned: ```yaml - name: Build azldev runner run: | + set -euo pipefail + AZLDEV_HASH="$(python3 scripts/ci/render-specs-check/resolve_azldev_version.py hash tools/azldev/go.mod)" docker build \ --build-arg UID=$(id -u) \ - --build-arg AZLDEV_VERSION="$(cat .azldev-version)" \ + --build-arg AZLDEV_HASH="$AZLDEV_HASH" \ -t localhost/azldev-runner \ -f .github/workflows/containers/azldev-runner.Dockerfile \ .github/workflows/containers/ diff --git a/.github/workflows/ado/templates/steps/install-deps.yml b/.github/workflows/ado/templates/steps/install-deps.yml index 17380e131f7..93aaf65d32c 100644 --- a/.github/workflows/ado/templates/steps/install-deps.yml +++ b/.github/workflows/ado/templates/steps/install-deps.yml @@ -3,7 +3,7 @@ # # Step template: authenticate to the internal pip feed, then install the # requested host tools. Each tool is opt-in via a boolean parameter. azldev's -# version comes from the committed .azldev-version and is validated before use. +# immutable hash is resolved from the committed tools/azldev/go.mod before installation. parameters: # azure-devops SDK (scripts/ci/ado/requirements.txt). @@ -62,15 +62,13 @@ steps: - script: | set -euo pipefail echo "##[group]Azldev (host)" - # Only the version string comes from the checkout; reject a - # malformed/garbage value before it reaches `go install`. - AZLDEV_VERSION="$(tr -d '\n' < .azldev-version)" - if ! printf '%s' "$AZLDEV_VERSION" | grep -Eq '^[0-9A-Za-z._+-]+$'; then - echo "##[error].azldev-version is empty or has unexpected characters" + AZLDEV_HASH="$(python3 scripts/ci/render-specs-check/resolve_azldev_version.py hash tools/azldev/go.mod)" + if ! printf '%s' "$AZLDEV_HASH" | grep -Eq '^[0-9a-f]{40}$'; then + echo "##[error]Resolver emitted an invalid azldev hash" exit 1 fi - echo "Installing azldev@${AZLDEV_VERSION}..." - go install "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@${AZLDEV_VERSION}" + echo "Installing azldev@${AZLDEV_HASH}..." + go install "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@${AZLDEV_HASH}" go_bin_path="$(go env GOPATH)/bin" echo "##vso[task.prependpath]$go_bin_path" diff --git a/.github/workflows/azldev-smoke.yml b/.github/workflows/azldev-smoke.yml index e67ac2fe143..e7e11a7fd4e 100644 --- a/.github/workflows/azldev-smoke.yml +++ b/.github/workflows/azldev-smoke.yml @@ -1,19 +1,21 @@ -# Smoke-test the azldev version pinned in .azldev-version. +# Smoke-test the azldev module version pinned in tools/azldev/go.mod. # -# When a PR bumps .azldev-version (or touches the runner image / this workflow), -# build the runner container with that exact pin and confirm the resulting -# binary can (a) run and (b) parse every component definition in the repo via -# `azldev component list`. This catches the two failure modes of a version bump: -# the pin doesn't `go install`, or the new version breaks on the repo's TOMLs. +# When a PR updates the module pin or its resolver (or touches the runner +# image / this workflow), build the runner container with its resolved immutable +# hash and confirm the resulting binary can (a) run and (b) parse every component +# definition in the repo via `azldev component list`. This catches resolver/Go +# integration regressions, pins that do not install, and incompatible TOMLs. name: "azldev Smoke Test" on: pull_request: branches: ["4.0"] paths: - - ".azldev-version" + - "tools/azldev/go.mod" + - "tools/azldev/go.sum" - ".github/workflows/containers/azldev-runner.Dockerfile" - ".github/workflows/azldev-smoke.yml" + - "scripts/ci/render-specs-check/resolve_azldev_version.py" workflow_dispatch: # Cancel in-progress runs of this workflow if a new run is triggered. @@ -36,25 +38,24 @@ jobs: with: persist-credentials: false + - name: Set up Go + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version-file: tools/azldev/go.mod + cache: false + - name: Build azldev runner container run: | set -euo pipefail - version="$(tr -d '\n' < .azldev-version)" - if [ -z "$version" ]; then - echo "::error::.azldev-version is empty" - exit 1 - fi - # Restrict to the charset Go module versions use (commit SHAs, tags, - # pseudo-versions). This blocks shell metacharacters so the value is - # safe to pass straight through to `docker build --build-arg` below. - if ! printf '%s' "$version" | grep -Eq '^[0-9A-Za-z._+-]+$'; then - echo "::error::.azldev-version contains unexpected characters: '$version'" + azldev_hash="$(python3 scripts/ci/render-specs-check/resolve_azldev_version.py hash tools/azldev/go.mod)" + if ! [[ "$azldev_hash" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::resolver emitted an invalid azldev hash" exit 1 fi - echo "azldev version pin: $version" + echo "azldev hash: $azldev_hash" docker build \ --build-arg UID="$(id -u)" \ - --build-arg AZLDEV_VERSION="$version" \ + --build-arg AZLDEV_HASH="$azldev_hash" \ -t localhost/azldev-runner \ -f .github/workflows/containers/azldev-runner.Dockerfile \ .github/workflows/containers/ diff --git a/.github/workflows/check-rendered-specs.yml b/.github/workflows/check-rendered-specs.yml index 87398e93480..41989f2d352 100644 --- a/.github/workflows/check-rendered-specs.yml +++ b/.github/workflows/check-rendered-specs.yml @@ -16,7 +16,7 @@ # render is skipped and only the locks comment is posted. # # Security: the PR checkout is data-only. Trusted base code resolves the -# post-merge azldev pin and passes only a validated version token to the trusted +# post-merge azldev pin and passes only a validated full hash to the trusted # runner Dockerfile, which hardcodes the Microsoft-owned module path. PR-derived # azldev runs only inside sandboxed jobs with a read-only token and no secrets. # A selected ref can still contain code introduced through the module's fork @@ -69,7 +69,7 @@ jobs: outputs: patch-url: ${{ steps.upload-locks-patch.outputs.artifact-url }} # Resolve once so lock and render checks use the same binary. - azldev-version: ${{ steps.resolve.outputs.azldev-version }} + azldev-hash: ${{ steps.resolve.outputs.azldev-hash }} render-all: ${{ steps.resolve.outputs.render-all }} steps: # --- Trusted base branch (tools, scripts, container config) --- @@ -80,6 +80,12 @@ jobs: ref: "4.0" persist-credentials: false + - name: Set up Go + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version-file: tools/azldev/go.mod + cache: false + # --- PR head (untrusted data — TOML configs, overlays, locks) --- - name: Checkout PR head (data) uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -97,9 +103,9 @@ jobs: # sandboxed container — so the fork head is never executed with trust. allow-unsafe-pr-checkout: true - # The trusted helper reads PR data but emits only a validated version and + # The trusted helper reads PR data but emits only a validated hash and # boolean. Equal pins need one freshness check but no mergeability polling. - - name: Resolve post-merge azldev version + - name: Resolve post-merge azldev hash id: resolve env: REPO: ${{ inputs.repo }} @@ -109,18 +115,19 @@ jobs: run: | set -euo pipefail resolve_output="$(python3 scripts/ci/render-specs-check/resolve_azldev_version.py \ + post-merge \ --repo "$REPO" \ --pr-number "$PR_NUMBER" \ --head-sha "$PR_HEAD_SHA" \ --base-sha "$(git rev-parse HEAD)" \ - --base-version-file .azldev-version \ - --head-version-file pr-head/.azldev-version)" + --base-modfile tools/azldev/go.mod \ + --head-modfile pr-head/tools/azldev/go.mod)" echo "$resolve_output" - azldev_version="$(sed -n 's/^azldev-version=//p' <<< "$resolve_output")" + azldev_hash="$(sed -n 's/^azldev-hash=//p' <<< "$resolve_output")" render_all="$(sed -n 's/^render-all=//p' <<< "$resolve_output")" - if ! [[ "$azldev_version" =~ ^[0-9A-Za-z._+-]+$ ]]; then - echo "::error::resolver emitted an invalid azldev version" + if ! [[ "$azldev_hash" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::resolver emitted an invalid azldev hash" exit 1 fi if [[ "$render_all" != "true" && "$render_all" != "false" ]]; then @@ -128,18 +135,18 @@ jobs: exit 1 fi { - printf 'azldev-version=%s\n' "$azldev_version" + printf 'azldev-hash=%s\n' "$azldev_hash" printf 'render-all=%s\n' "$render_all" } >> "$GITHUB_OUTPUT" - name: Build azldev runner container env: - AZLDEV_VERSION: ${{ steps.resolve.outputs.azldev-version }} + AZLDEV_HASH: ${{ steps.resolve.outputs.azldev-hash }} run: | set -euo pipefail docker build \ --build-arg UID="$(id -u)" \ - --build-arg AZLDEV_VERSION="$AZLDEV_VERSION" \ + --build-arg AZLDEV_HASH="$AZLDEV_HASH" \ -t localhost/azldev-runner \ -f .github/workflows/containers/azldev-runner.Dockerfile \ .github/workflows/containers/ @@ -360,14 +367,14 @@ jobs: allow-unsafe-pr-checkout: true - name: Build azldev runner container - # Reuse the version that update_locks already resolved. + # Reuse the hash that update_locks already resolved. env: - AZLDEV_VERSION: ${{ needs.update_locks.outputs.azldev-version }} + AZLDEV_HASH: ${{ needs.update_locks.outputs.azldev-hash }} run: | set -euo pipefail docker build \ --build-arg UID="$(id -u)" \ - --build-arg AZLDEV_VERSION="$AZLDEV_VERSION" \ + --build-arg AZLDEV_HASH="$AZLDEV_HASH" \ -t localhost/azldev-runner \ -f .github/workflows/containers/azldev-runner.Dockerfile \ .github/workflows/containers/ diff --git a/.github/workflows/containers/azldev-runner.Dockerfile b/.github/workflows/containers/azldev-runner.Dockerfile index fe2fe36be8b..a0bcb5c39ca 100644 --- a/.github/workflows/containers/azldev-runner.Dockerfile +++ b/.github/workflows/containers/azldev-runner.Dockerfile @@ -35,9 +35,9 @@ RUN tdnf -y install \ symcrypt-openssl \ && tdnf clean all -# The version is passed in as a build arg from .azldev-version in the repo -# root. Callers (check-rendered-specs.yml, etc.) read the file and pass it -# via --build-arg so the Dockerfile never needs repo-root build context. +# The azldev Git hash is resolved from tools/azldev/go.mod. Callers +# (check-rendered-specs.yml, etc.) pass it via --build-arg so the Dockerfile +# never needs repo-root build context. # No default — omitting --build-arg will fail the build loudly. # Optional Go module proxy for the `go install` below. Callers that build # behind an internal-only proxy forward it via --build-arg GOPROXY=...; Docker @@ -50,10 +50,10 @@ RUN tdnf -y install \ # direct) and would break the install below. The ADO/OneBranch PR build # forwards the host's internal proxy. ARG GOPROXY -ARG AZLDEV_VERSION -RUN test -n "${AZLDEV_VERSION}" || { echo "ERROR: AZLDEV_VERSION build-arg is required (read from .azldev-version)" >&2; exit 1; } \ +ARG AZLDEV_HASH +RUN test -n "${AZLDEV_HASH}" || { echo "ERROR: AZLDEV_HASH build-arg is required" >&2; exit 1; } \ && GOBIN=/usr/local/bin go install \ - "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@${AZLDEV_VERSION}" \ + "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@${AZLDEV_HASH}" \ && rm -rf /root/go /root/.cache ARG UID=1000 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 9a877dd842c..f3a8510cff2 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -26,12 +26,11 @@ jobs: - name: Set up Go uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: stable + go-version-file: tools/azldev/go.mod cache: false - - name: Install azldev - run: | - go install "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@$(cat .azldev-version)" - - name: "Validate config (strict)" - run: azldev config dump > /dev/null + run: | + set -euo pipefail + azldev_hash="$(python3 scripts/ci/render-specs-check/resolve_azldev_version.py hash tools/azldev/go.mod)" + go run "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@${azldev_hash}" config dump > /dev/null diff --git a/DEVELOPING.md b/DEVELOPING.md index 6a2edbbfde6..b1928dc1388 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -4,13 +4,26 @@ ### Install azldev -The [`azldev`](https://github.com/microsoft/azure-linux-dev-tools) CLI tool drives all component, image, and build workflows. Install it from source (requires Go): +The [`azldev`](https://github.com/microsoft/azure-linux-dev-tools) CLI tool drives all component, image, and build workflows. Its version is declared in `tools/azldev/go.mod`. + +From the repository root, install the selected version with: + +```bash +go -C tools/azldev install github.com/microsoft/azure-linux-dev-tools/cmd/azldev +azldev --version +``` + +Go installs the command in `$(go env GOPATH)/bin`; add that directory to `PATH` if needed. Installation is recommended for normal development and shell completion. For one-off use without installing: ```bash -go install "github.com/microsoft/azure-linux-dev-tools/cmd/azldev@$(cat .azldev-version)" +go -C tools/azldev run github.com/microsoft/azure-linux-dev-tools/cmd/azldev --version ``` -> **Note:** azldev is still in active development, using the latest commit from the `main` branch is recommended for the most up-to-date features and fixes. +For unpinned development, you can also run the latest version of the tool: + +```bash +go install github.com/microsoft/azure-linux-dev-tools/cmd/azldev@latest +``` ### Render specs @@ -33,9 +46,11 @@ If the workspace is opened correctly, the agent will automatically gain access t The `azl-diagnose` agent and Koji-related tools require: 1. **MCP Python packages** — the MCP servers won't start without them: + ```bash pip3 install --user -r scripts/mcps/requirements.txt ``` + 2. **Network access to the internal Koji instance** — The internal Koji is only accessible via VPN or the corporate network. If the agent reports connection errors, verify you are connected before retrying. 3. **(Optional) `.env` configuration** — Create a `.env` file (in the workspace root or `scripts/mcps/`) to pre-configure MCP server settings like the Koji base URL and pre-approved insecure URLs. This avoids the agent having to set the URL or approve self-signed certificates every session. See [scripts/mcps/.env.example](scripts/mcps/.env.example) for available variables. diff --git a/scripts/ci/render-specs-check/resolve_azldev_version.py b/scripts/ci/render-specs-check/resolve_azldev_version.py index 26cd2d2c073..c13448bcb3e 100644 --- a/scripts/ci/render-specs-check/resolve_azldev_version.py +++ b/scripts/ci/render-specs-check/resolve_azldev_version.py @@ -1,14 +1,18 @@ -"""Resolve the azldev version that will be present after a PR merges. +"""Resolve the selected azldev Go module to an immutable Git hash. Resolved values are printed as ``key=value`` records; diagnostics go to stderr. +The ``hash`` command prints only the hash for local and non-PR callers. """ from __future__ import annotations import argparse +import json +import os import re import subprocess import sys +import tempfile import time from dataclasses import dataclass from pathlib import Path @@ -16,12 +20,15 @@ VERSION_RE = re.compile(r"[0-9A-Za-z._+-]+") REPO_RE = re.compile(r"[A-Za-z0-9._-]+/[A-Za-z0-9._-]+") SHA_RE = re.compile(r"[0-9a-f]{40}") +AZLDEV_MODULE = "github.com/microsoft/azure-linux-dev-tools" +AZLDEV_REPOSITORY = f"https://{AZLDEV_MODULE}" MERGE_ATTEMPTS = 30 CONTENT_ATTEMPTS = 5 RETRY_SECONDS = 4 GH_API_TIMEOUT_SECONDS = 15 +GO_COMMAND_TIMEOUT_SECONDS = 60 GITHUB_API_VERSION = "2026-03-10" -PR_FIELD_COUNT = 3 +PR_FIELD_COUNT = 4 @dataclass(frozen=True, slots=True) @@ -47,7 +54,7 @@ def _log(message: str) -> None: print(message, file=sys.stderr) -def _validate_version(value: str, source: str) -> str: +def validate_version(value: str, source: str) -> str: """Return a validated, single-token azldev version.""" version = value.rstrip("\r\n") if not VERSION_RE.fullmatch(version): @@ -56,20 +63,106 @@ def _validate_version(value: str, source: str) -> str: return version -def _read_version(path: Path) -> str: - """Read a version file without following an untrusted symlink.""" - if path.is_symlink(): - message = f"{path} must be a regular file, not a symlink" +def _run_go(*args: str, cwd: Path | None = None) -> str: + """Run Go without ambient workspace or toolchain selection and return stdout.""" + environment = os.environ.copy() + environment["GOFLAGS"] = "" + environment["GOTOOLCHAIN"] = "local" + environment["GOWORK"] = "off" + try: + result = subprocess.run( + ["go", *args], + capture_output=True, + text=True, + check=False, + cwd=cwd, + timeout=GO_COMMAND_TIMEOUT_SECONDS, + env=environment, + ) + except (OSError, subprocess.TimeoutExpired) as error: + message = f"could not run go {' '.join(args)}: {error}" + raise ResolutionError(message) from error + if result.returncode != 0: + detail = "\n".join(output for output in (result.stdout.strip(), result.stderr.strip()) if output) + detail = detail or f"go exited with {result.returncode}" + message = f"go {' '.join(args)} failed: {detail}" + raise ResolutionError(message) + return result.stdout + + +def _validated_module_path(path: Path) -> Path: + """Return an absolute regular module file without symlinked components.""" + absolute_path = path.absolute() + for component in (absolute_path, *absolute_path.parents): + if component.is_symlink(): + message = f"{path} must not use a symlinked path component, but {component} is a symlink" + raise ResolutionError(message) + if not absolute_path.is_file(): + message = f"{path} must be a regular file" + raise ResolutionError(message) + return absolute_path + + +def read_version(path: Path) -> str: + """Read the direct azldev requirement from one validated module file.""" + module_path = _validated_module_path(path) + try: + module = json.loads(_run_go("mod", "edit", "-json", str(module_path))) + except (json.JSONDecodeError, ResolutionError) as error: + message = f"could not parse {path}: {error}" + raise ResolutionError(message) from error + requires = module.get("Require", []) if isinstance(module, dict) else [] + direct_requirements = [ + requirement + for requirement in requires + if isinstance(requirement, dict) + and requirement.get("Path") == AZLDEV_MODULE + and requirement.get("Indirect", False) is False + ] + if len(direct_requirements) != 1: + message = f"{path} must contain exactly one direct azldev requirement, but has {len(direct_requirements)}" raise ResolutionError(message) + version = direct_requirements[0].get("Version") + if not isinstance(version, str): + message = ( + f"{path} direct azldev requirement has version with type {type(version).__name__} and value {version!r}" + ) + raise ResolutionError(message) + return validate_version(version, f"{path} azldev version") + + +def _version_from_content(content: str, source: str) -> str: + """Parse an API-provided go.mod through Go and return its azldev version.""" try: - value = path.read_text(encoding="ascii") - except (OSError, UnicodeError) as error: - message = f"could not read {path}: {error}" + with tempfile.TemporaryDirectory() as directory: + modfile = Path(directory, "go.mod") + modfile.write_text(content, encoding="utf-8") + return read_version(modfile) + except OSError as error: + message = f"could not parse {source}: {error}" raise ResolutionError(message) from error - return _validate_version(value, str(path)) -def _validate_inputs(pull_request: PullRequest) -> None: +def resolve_hash(version: str) -> str: + """Resolve an azldev module version to its full Microsoft Git revision.""" + version = validate_version(version, "azldev version") + try: + module = json.loads(_run_go("mod", "download", "-json", f"{AZLDEV_MODULE}@{version}")) + except json.JSONDecodeError as error: + message = f"go returned invalid JSON while resolving {AZLDEV_MODULE}@{version}" + raise ResolutionError(message) from error + origin = module.get("Origin", {}) if isinstance(module, dict) else {} + if not isinstance(origin, dict) or origin.get("VCS") != "git" or origin.get("URL") != AZLDEV_REPOSITORY: + message = f"Go resolved {AZLDEV_MODULE}@{version} from an unexpected source" + raise ResolutionError(message) + revision = origin.get("Hash", "") + if not isinstance(revision, str) or not SHA_RE.fullmatch(revision): + message = f"Go did not resolve {AZLDEV_MODULE}@{version} to a full Git hash" + raise ResolutionError(message) + return revision + + +def validate_inputs(pull_request: PullRequest) -> None: """Validate values used in API paths and workflow output.""" if not REPO_RE.fullmatch(pull_request.repo): message = f"repo is not a valid owner/repo: {pull_request.repo!r}" @@ -85,7 +178,7 @@ def _validate_inputs(pull_request: PullRequest) -> None: raise ResolutionError(message) -def _github_api(endpoint: str, *options: str) -> str: +def github_api(endpoint: str, *options: str) -> str: """Call GitHub through the authenticated gh CLI and return stdout.""" command = ["gh", "api", "-H", f"X-GitHub-Api-Version: {GITHUB_API_VERSION}", endpoint, *options] try: @@ -105,72 +198,71 @@ def _github_api(endpoint: str, *options: str) -> str: return result.stdout -def _validate_pr_snapshot(fields: list[str], pull_request: PullRequest) -> str: +def _validate_pr_snapshot(fields: list[str], pull_request: PullRequest) -> tuple[str, str]: """Validate live PR fields against the trusted workflow snapshot.""" if len(fields) != PR_FIELD_COUNT: message = "GitHub returned an invalid PR response" raise ResolutionError(message) - polled_head, polled_base, mergeable = fields + polled_head, polled_base, mergeable, merge_commit_sha = fields if polled_head != pull_request.head_sha: message = f"PR head advanced from {pull_request.head_sha!r} to {polled_head!r}; a newer run supersedes this one" raise ResolutionError(message) if polled_base != pull_request.base_sha: message = f"PR base advanced from {pull_request.base_sha!r} to {polled_base!r}; a newer run must validate it" raise ResolutionError(message) - return mergeable + return mergeable, merge_commit_sha -def _wait_for_merge_ref(pull_request: PullRequest, *, wait_for_merge: bool) -> None: - """Validate the live PR snapshot and optionally wait for its merge ref.""" +def _wait_for_merge_commit(pull_request: PullRequest, *, wait_for_merge: bool) -> str: + """Validate the live PR snapshot and return its immutable test-merge SHA.""" endpoint = f"repos/{pull_request.repo}/pulls/{pull_request.number}" - query = "[.head.sha, .base.sha, (.mergeable | tostring)] | @tsv" + query = '[.head.sha, .base.sha, (.mergeable | tostring), (.merge_commit_sha // "")] | @tsv' last_error: GitHubApiError | None = None for attempt in range(1, MERGE_ATTEMPTS + 1): try: - fields = _github_api(endpoint, "--jq", query).rstrip("\r\n").split("\t") + fields = github_api(endpoint, "--jq", query).rstrip("\r\n").split("\t") except GitHubApiError as error: last_error = error _log(f"PR API call failed ({attempt}/{MERGE_ATTEMPTS}): {error}; retrying...") else: last_error = None - mergeable = _validate_pr_snapshot(fields, pull_request) + mergeable, merge_commit_sha = _validate_pr_snapshot(fields, pull_request) if not wait_for_merge: - return + return merge_commit_sha if mergeable == "false": message = "PR conflicts with the base branch" raise ResolutionError(message) - if mergeable == "true": - return - _log(f"Waiting for GitHub to publish the merge ref ({attempt}/{MERGE_ATTEMPTS})...") + if mergeable == "true" and SHA_RE.fullmatch(merge_commit_sha): + return merge_commit_sha + _log(f"Waiting for GitHub to publish the test-merge commit ({attempt}/{MERGE_ATTEMPTS})...") if attempt < MERGE_ATTEMPTS: time.sleep(RETRY_SECONDS) message = ( f"GitHub API failed after {MERGE_ATTEMPTS} attempts: {last_error}" if last_error - else f"GitHub did not publish the merge ref after {MERGE_ATTEMPTS} attempts" + else f"GitHub did not publish a valid test-merge commit after {MERGE_ATTEMPTS} attempts" ) raise ResolutionError(message) -def _read_merged_version(pull_request: PullRequest) -> str: - """Read the azldev pin from the pull request merge ref.""" - # A moving merge ref matters only if another pin change lands; that supersedes this run or conflicts. - merge_ref = f"refs/pull/{pull_request.number}/merge" - endpoint = f"repos/{pull_request.repo}/contents/.azldev-version?ref={merge_ref}" +def _read_merged_version(pull_request: PullRequest, modfile: Path, merge_commit_sha: str) -> str: + """Read the azldev pin from an immutable test-merge commit.""" + modfile_path = modfile.as_posix() + endpoint = f"repos/{pull_request.repo}/contents/{modfile_path}?ref={merge_commit_sha}" last_error: GitHubApiError | None = None for attempt in range(1, CONTENT_ATTEMPTS + 1): try: - content = _github_api(endpoint, "-H", "Accept: application/vnd.github.raw") + content = github_api(endpoint, "-H", "Accept: application/vnd.github.raw") except GitHubApiError as error: last_error = error - _log(f"Could not read the post-merge .azldev-version ({attempt}/{CONTENT_ATTEMPTS}): {error}; retrying...") + _log(f"Could not read post-merge {modfile_path} ({attempt}/{CONTENT_ATTEMPTS}): {error}; retrying...") if attempt < CONTENT_ATTEMPTS: time.sleep(RETRY_SECONDS) else: - return _validate_version(content, "post-merge .azldev-version") + return _version_from_content(content, f"post-merge {modfile_path}") - message = f"could not read .azldev-version from the pull request merge ref: {last_error}" + message = f"could not read {modfile_path} from test-merge commit {merge_commit_sha}: {last_error}" raise ResolutionError(message) @@ -179,55 +271,68 @@ def resolve_version( head_version: str, *, pull_request: PullRequest, + merged_modfile: Path = Path("go.mod"), ) -> tuple[str, bool]: """Return the post-merge version and whether all specs must be rendered.""" - _validate_inputs(pull_request) - base_version = _validate_version(base_version, "base version") - head_version = _validate_version(head_version, "PR-head version") + validate_inputs(pull_request) + base_version = validate_version(base_version, "base version") + head_version = validate_version(head_version, "PR-head version") versions_differ = head_version != base_version - _wait_for_merge_ref(pull_request, wait_for_merge=versions_differ) + merge_commit_sha = _wait_for_merge_commit(pull_request, wait_for_merge=versions_differ) if not versions_differ: return base_version, False - merged_version = _read_merged_version(pull_request) + merged_version = _read_merged_version(pull_request, merged_modfile, merge_commit_sha) return merged_version, merged_version != base_version -def _parse_args() -> argparse.Namespace: +def parse_args() -> argparse.Namespace: """Parse command-line arguments.""" parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--repo", required=True, help="GitHub owner/repository") - parser.add_argument("--pr-number", required=True, type=int, help="pull request number") - parser.add_argument("--head-sha", required=True, help="expected PR head commit") - parser.add_argument("--base-sha", required=True, help="trusted base commit") - parser.add_argument("--base-version-file", required=True, type=Path, help="base azldev version file") - parser.add_argument("--head-version-file", required=True, type=Path, help="PR-head azldev version file") + commands = parser.add_subparsers(dest="command", required=True) + + hash_parser = commands.add_parser("hash", help="resolve one go.mod to an azldev hash") + hash_parser.add_argument("modfile", type=Path) + + post_merge_parser = commands.add_parser("post-merge", help="resolve the hash after a pull request merges") + post_merge_parser.add_argument("--repo", required=True, help="GitHub owner/repository") + post_merge_parser.add_argument("--pr-number", required=True, type=int, help="pull request number") + post_merge_parser.add_argument("--head-sha", required=True, help="expected PR head commit") + post_merge_parser.add_argument("--base-sha", required=True, help="trusted base commit") + post_merge_parser.add_argument("--base-modfile", required=True, type=Path, help="base Go module file") + post_merge_parser.add_argument("--head-modfile", required=True, type=Path, help="PR-head Go module file") return parser.parse_args() def main() -> int: """Resolve the version and emit machine-readable output.""" - args = _parse_args() + args = parse_args() try: - base_version = _read_version(args.base_version_file) - head_version = _read_version(args.head_version_file) + if args.command == "hash": + print(resolve_hash(read_version(args.modfile))) + return 0 + pull_request = PullRequest( repo=args.repo, number=args.pr_number, head_sha=args.head_sha, base_sha=args.base_sha, ) + base_version = read_version(args.base_modfile) + head_version = read_version(args.head_modfile) version, render_all = resolve_version( base_version=base_version, head_version=head_version, pull_request=pull_request, + merged_modfile=args.base_modfile, ) + revision = resolve_hash(version) _log( - f"Resolved azldev version: {version}; render all: {str(render_all).lower()} " + f"Resolved azldev version: {version} ({revision}); render all: {str(render_all).lower()} " f"(base: {base_version}, PR head: {head_version})" ) - print(f"azldev-version={version}") + print(f"azldev-hash={revision}") print(f"render-all={str(render_all).lower()}") except ResolutionError as error: _log(f"Error: {error}") diff --git a/scripts/ci/render-specs-check/tests/test_resolve_azldev_version.py b/scripts/ci/render-specs-check/tests/test_resolve_azldev_version.py index 5a56ff02273..f56da308ac3 100644 --- a/scripts/ci/render-specs-check/tests/test_resolve_azldev_version.py +++ b/scripts/ci/render-specs-check/tests/test_resolve_azldev_version.py @@ -4,17 +4,14 @@ import subprocess import sys -from typing import TYPE_CHECKING +from pathlib import Path from unittest.mock import Mock import pytest import resolve_azldev_version as resolver -if TYPE_CHECKING: - from pathlib import Path - -def _pull_request(*, number: int = 1, head_sha: str = "a" * 40, base_sha: str = "b" * 40) -> resolver.PullRequest: +def pull_request(*, number: int = 1, head_sha: str = "a" * 40, base_sha: str = "b" * 40) -> resolver.PullRequest: """Build a test pull request snapshot.""" return resolver.PullRequest( repo="microsoft/azurelinux", @@ -26,13 +23,13 @@ def _pull_request(*, number: int = 1, head_sha: str = "a" * 40, base_sha: str = def test_equal_versions_skip_merge_poll(monkeypatch: pytest.MonkeyPatch) -> None: """Validate one PR snapshot without polling for a test-merge commit.""" - github_api = Mock(return_value=f"{'a' * 40}\t{'b' * 40}\ttrue\n") - monkeypatch.setattr(resolver, "_github_api", github_api) + github_api = Mock(return_value=f"{'a' * 40}\t{'b' * 40}\ttrue\t{'d' * 40}\n") + monkeypatch.setattr(resolver, "github_api", github_api) version, render_all = resolver.resolve_version( "v1.2.3", "v1.2.3", - pull_request=_pull_request(), + pull_request=pull_request(), ) if version != "v1.2.3" or render_all: @@ -41,40 +38,206 @@ def test_equal_versions_skip_merge_poll(monkeypatch: pytest.MonkeyPatch) -> None pytest.fail(f"expected one PR snapshot request, got {github_api.call_count}") +def test_read_version_uses_selected_azldev_module( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + """Read the direct azldev requirement from the supplied module file.""" + modfile = tmp_path / "go.mod" + modfile.write_text("module example.com/test\n", encoding="ascii") + run = Mock( + return_value=subprocess.CompletedProcess( + [], + 0, + stdout=('{"Require":[{"Path":"github.com/microsoft/azure-linux-dev-tools","Version":"v1.2.3"}]}'), + stderr="", + ) + ) + monkeypatch.setattr(resolver.subprocess, "run", run) + + if resolver.read_version(modfile) != "v1.2.3": + pytest.fail("unexpected azldev tool version") + command = run.call_args.args[0] + if command != ["go", "mod", "edit", "-json", str(modfile.absolute())]: + pytest.fail(f"unexpected Go command: {command}") + environment = run.call_args.kwargs.get("env", {}) + if environment.get("GOFLAGS") or environment.get("GOWORK") != "off": + pytest.fail(f"Go did not isolate the selected module file: {environment}") + + +def test_run_go_failure_includes_stdout_and_stderr( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + """Preserve diagnostics from both output streams when Go fails.""" + modfile = tmp_path / "go.mod" + modfile.write_text("module example.com/test\n", encoding="ascii") + run = Mock( + return_value=subprocess.CompletedProcess( + [], + 1, + stdout="helpful stdout\n", + stderr="fatal stderr\n", + ) + ) + monkeypatch.setattr(resolver.subprocess, "run", run) + + with pytest.raises(resolver.ResolutionError, match="helpful stdout") as error: + resolver.read_version(modfile) + if "fatal stderr" not in str(error.value): + pytest.fail(f"Go stderr missing from error: {error.value}") + + +def test_read_version_rejects_indirect_azldev_requirement( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + """Reject azldev requirements that do not directly select the tool.""" + modfile = tmp_path / "go.mod" + modfile.write_text("module example.com/test\n", encoding="ascii") + run = Mock( + return_value=subprocess.CompletedProcess( + [], + 0, + stdout=( + '{"Require":[{"Path":"github.com/microsoft/azure-linux-dev-tools","Version":"v1.2.3","Indirect":true}]}' + ), + stderr="", + ) + ) + monkeypatch.setattr(resolver.subprocess, "run", run) + + with pytest.raises(resolver.ResolutionError, match="but has 0"): + resolver.read_version(modfile) + + +def test_read_version_reports_malformed_version( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + """Describe a malformed direct requirement without misreporting it as absent.""" + modfile = tmp_path / "go.mod" + modfile.write_text("module example.com/test\n", encoding="ascii") + run = Mock( + return_value=subprocess.CompletedProcess( + [], + 0, + stdout=('{"Require":[{"Path":"github.com/microsoft/azure-linux-dev-tools","Version":123}]}'), + stderr="", + ) + ) + monkeypatch.setattr(resolver.subprocess, "run", run) + + with pytest.raises(resolver.ResolutionError, match="type int and value 123"): + resolver.read_version(modfile) + + +def test_resolve_hash_validates_module_origin(monkeypatch: pytest.MonkeyPatch) -> None: + """Resolve a selected module version to its full Microsoft repository hash.""" + revision = "c" * 40 + run = Mock( + return_value=subprocess.CompletedProcess( + [], + 0, + stdout=( + '{"Origin":{"VCS":"git",' + '"URL":"https://github.com/microsoft/azure-linux-dev-tools",' + f'"Hash":"{revision}"}}}}' + ), + stderr="", + ) + ) + monkeypatch.setattr(resolver.subprocess, "run", run) + + if resolver.resolve_hash("v1.2.3") != revision: + pytest.fail("unexpected resolved hash") + + +@pytest.mark.parametrize( + "origin", + [ + "{}", + '{"Origin":null}', + '{"Origin":{"VCS":"hg","URL":"https://github.com/microsoft/azure-linux-dev-tools","Hash":"c"}}', + '{"Origin":{"VCS":"git","URL":"https://example.com/azure-linux-dev-tools","Hash":"c"}}', + '{"Origin":{"VCS":"git","URL":"https://github.com/microsoft/azure-linux-dev-tools","Hash":"bad"}}', + ], +) +def test_resolve_hash_rejects_unexpected_module_origin( + monkeypatch: pytest.MonkeyPatch, + origin: str, +) -> None: + """Reject missing or malformed provenance for the selected module.""" + run = Mock(return_value=subprocess.CompletedProcess([], 0, stdout=origin, stderr="")) + monkeypatch.setattr(resolver.subprocess, "run", run) + + with pytest.raises(resolver.ResolutionError, match=r"unexpected source|full Git hash"): + resolver.resolve_hash("v1.2.3") + + +def test_hash_cli_emits_resolved_hash( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """Print the hash resolved from the module tool requirement.""" + revision = "d" * 40 + monkeypatch.setattr(resolver, "read_version", Mock(return_value="v1.2.3")) + monkeypatch.setattr(resolver, "resolve_hash", Mock(return_value=revision)) + monkeypatch.setattr( + sys, + "argv", + ["resolve_azldev_version.py", "hash", str(tmp_path / "go.mod")], + ) + + if resolver.main() != 0: + pytest.fail("hash resolver unexpectedly failed") + if capsys.readouterr().out != f"{revision}\n": + pytest.fail("hash resolver emitted unexpected output") + + def test_differing_versions_use_test_merge_version(monkeypatch: pytest.MonkeyPatch) -> None: """Use the test-merge pin when the base and PR-head pins differ.""" + merge_commit_sha = "d" * 40 github_api = Mock( side_effect=[ - f"{'a' * 40}\t{'c' * 40}\tnull\n", - f"{'a' * 40}\t{'c' * 40}\ttrue\n", + f"{'a' * 40}\t{'c' * 40}\tnull\t\n", + f"{'a' * 40}\t{'c' * 40}\ttrue\t{merge_commit_sha}\n", "v2.0.0\n", ] ) - monkeypatch.setattr(resolver, "_github_api", github_api) + monkeypatch.setattr(resolver, "github_api", github_api) + monkeypatch.setattr(resolver, "_version_from_content", Mock(return_value="v2.0.0")) monkeypatch.setattr(resolver.time, "sleep", Mock()) version, render_all = resolver.resolve_version( "v1.2.3", "v2.0.0", - pull_request=_pull_request(base_sha="c" * 40), + merged_modfile=Path("tools/azldev/go.mod"), + pull_request=pull_request(base_sha="c" * 40), ) if version != "v2.0.0" or not render_all: pytest.fail(f"unexpected resolution: version={version!r}, render_all={render_all!r}") + snapshot_query = github_api.call_args_list[0].args[-1] + if ".merge_commit_sha" not in snapshot_query: + pytest.fail(f"test-merge SHA missing from PR snapshot query: {snapshot_query}") content_endpoint = github_api.call_args_list[-1].args[0] - if content_endpoint != "repos/microsoft/azurelinux/contents/.azldev-version?ref=refs/pull/1/merge": - pytest.fail(f"unexpected merge-ref endpoint: {content_endpoint}") + expected_endpoint = f"repos/microsoft/azurelinux/contents/tools/azldev/go.mod?ref={merge_commit_sha}" + if content_endpoint != expected_endpoint: + pytest.fail(f"unexpected test-merge endpoint: {content_endpoint}") def test_behind_base_uses_base_version_without_full_render(monkeypatch: pytest.MonkeyPatch) -> None: """Avoid a full render when the PR merely has an older pin than base.""" - github_api = Mock(side_effect=[f"{'a' * 40}\t{'c' * 40}\ttrue\n", "v2.0.0\n"]) - monkeypatch.setattr(resolver, "_github_api", github_api) + github_api = Mock(side_effect=[f"{'a' * 40}\t{'c' * 40}\ttrue\t{'d' * 40}\n", "v2.0.0\n"]) + monkeypatch.setattr(resolver, "github_api", github_api) + monkeypatch.setattr(resolver, "_version_from_content", Mock(return_value="v2.0.0")) version, render_all = resolver.resolve_version( "v2.0.0", "v1.2.3", - pull_request=_pull_request(base_sha="c" * 40), + pull_request=pull_request(base_sha="c" * 40), ) if version != "v2.0.0" or render_all: @@ -98,13 +261,13 @@ def test_invalid_inputs_fail_before_github( ) -> None: """Reject malformed reusable-workflow inputs before calling GitHub.""" github_api = Mock() - monkeypatch.setattr(resolver, "_github_api", github_api) + monkeypatch.setattr(resolver, "github_api", github_api) with pytest.raises(resolver.ResolutionError, match=message): resolver.resolve_version( "v1.2.3", "v1.2.3", - pull_request=_pull_request( + pull_request=pull_request( number=pr_number, head_sha=expected_head, base_sha=expected_base, @@ -116,14 +279,14 @@ def test_invalid_inputs_fail_before_github( def test_stale_base_fails_before_equal_pin_result(monkeypatch: pytest.MonkeyPatch) -> None: """Reject a live PR base that differs from the trusted checkout.""" - github_api = Mock(return_value=f"{'a' * 40}\t{'c' * 40}\ttrue\n") - monkeypatch.setattr(resolver, "_github_api", github_api) + github_api = Mock(return_value=f"{'a' * 40}\t{'c' * 40}\ttrue\t{'d' * 40}\n") + monkeypatch.setattr(resolver, "github_api", github_api) with pytest.raises(resolver.ResolutionError, match="PR base advanced"): resolver.resolve_version( "v1.2.3", "v1.2.3", - pull_request=_pull_request(), + pull_request=pull_request(), ) @@ -133,21 +296,16 @@ def test_github_api_timeout_is_retryable(monkeypatch: pytest.MonkeyPatch) -> Non monkeypatch.setattr(resolver.subprocess, "run", run) with pytest.raises(resolver.GitHubApiError, match="timed out"): - resolver._github_api("repos/microsoft/azurelinux/pulls/1") # noqa: SLF001 - test timeout boundary directly + resolver.github_api("repos/microsoft/azurelinux/pulls/1") def test_github_api_pins_current_rest_version(monkeypatch: pytest.MonkeyPatch) -> None: - """Request the REST contract used by the merge-ref implementation.""" + """Request the REST contract used by the test-merge implementation.""" result = subprocess.CompletedProcess([], 0, stdout="ok", stderr="") run = Mock(return_value=result) monkeypatch.setattr(resolver.subprocess, "run", run) - if ( - resolver._github_api( # noqa: SLF001 - test API header boundary directly - "repos/microsoft/azurelinux/pulls/1" - ) - != "ok" - ): + if resolver.github_api("repos/microsoft/azurelinux/pulls/1") != "ok": pytest.fail("unexpected API output") command = run.call_args.args[0] expected_header = f"X-GitHub-Api-Version: {resolver.GITHUB_API_VERSION}" @@ -155,23 +313,29 @@ def test_github_api_pins_current_rest_version(monkeypatch: pytest.MonkeyPatch) - pytest.fail(f"API version header missing from command: {command}") -def test_main_rejects_multiline_output_injection( +def test_main_rejects_module_without_azldev_requirement( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str], ) -> None: - """Reject a multiline pin without emitting attacker-controlled records.""" - base_file = tmp_path / "base-version" - head_file = tmp_path / "head-version" - base_file.write_text("v1.2.3\n", encoding="ascii") - head_file.write_text("safe\nazldev-version=attacker-value\n", encoding="ascii") + """Reject a module without an azldev requirement before emitting output.""" + base_file = tmp_path / "base.mod" + head_file = tmp_path / "head.mod" + base_file.write_text("module example.com/base\n", encoding="ascii") + head_file.write_text("module example.com/head\n", encoding="ascii") github_api = Mock() - monkeypatch.setattr(resolver, "_github_api", github_api) + monkeypatch.setattr(resolver, "github_api", github_api) + monkeypatch.setattr( + resolver, + "_run_go", + Mock(side_effect=resolver.ResolutionError("azldev is not a known dependency")), + ) monkeypatch.setattr( sys, "argv", [ "resolve_azldev_version.py", + "post-merge", "--repo", "microsoft/azurelinux", "--pr-number", @@ -180,26 +344,39 @@ def test_main_rejects_multiline_output_injection( "a" * 40, "--base-sha", "b" * 40, - "--base-version-file", + "--base-modfile", str(base_file), - "--head-version-file", + "--head-modfile", str(head_file), ], ) if resolver.main() == 0: - pytest.fail("multiline version unexpectedly succeeded") + pytest.fail("module without azldev requirement unexpectedly succeeded") if capsys.readouterr().out: pytest.fail("resolver emitted machine-readable output after rejecting the version") github_api.assert_not_called() def test_read_version_rejects_symlink(tmp_path: Path) -> None: - """Reject a version-file symlink before reading its target.""" + """Reject a go.mod symlink before parsing its target.""" target = tmp_path / "target" link = tmp_path / "version" target.write_text("v1.2.3\n", encoding="ascii") link.symlink_to(target) with pytest.raises(resolver.ResolutionError, match="symlink"): - resolver._read_version(link) # noqa: SLF001 - test symlink boundary directly + resolver.read_version(link) + + +def test_read_version_rejects_symlinked_parent(tmp_path: Path) -> None: + """Reject a module accessed through an untrusted symlinked directory.""" + tools_directory = tmp_path / "tools" + tools_directory.mkdir() + (tools_directory / "go.mod").write_text("module example.com/test\n", encoding="ascii") + linked_directory = tmp_path / "linked-tools" + linked_directory.symlink_to(tools_directory, target_is_directory=True) + + message = f"{linked_directory} is a symlink" + with pytest.raises(resolver.ResolutionError, match=message): + resolver.read_version(linked_directory / "go.mod") diff --git a/tools/azldev/go.mod b/tools/azldev/go.mod new file mode 100644 index 00000000000..64c67e8393f --- /dev/null +++ b/tools/azldev/go.mod @@ -0,0 +1,109 @@ +module github.com/microsoft/azurelinux/tools/azldev + +go 1.25.6 + +require github.com/microsoft/azure-linux-dev-tools v0.1.1-0.20260715220518-661c69b6c5b1 + +require ( + cyphar.com/go-pathrs v0.2.4 // indirect + dario.cat/mergo v1.0.2 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect + github.com/acobaugh/osrelease v0.1.0 // indirect + github.com/auribuo/stylishcobra v1.0.1 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/binalyze/ctxio v0.0.0-20210917063359-64c39a15e009 // indirect + github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect + github.com/briandowns/spinner v1.23.2 // indirect + github.com/brunoga/deep v1.3.1 // indirect + github.com/buger/jsonparser v1.1.2 // indirect + github.com/cavaliergopher/cpio v1.0.1 // indirect + github.com/cavaliergopher/rpm v1.3.0 // indirect + github.com/charmbracelet/bubbles v1.0.0 // indirect + github.com/charmbracelet/bubbletea v1.3.10 // indirect + github.com/charmbracelet/colorprofile v0.4.3 // indirect + github.com/charmbracelet/gum v0.17.0 // indirect + github.com/charmbracelet/harmonica v0.2.0 // indirect + github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect + github.com/charmbracelet/x/ansi v0.11.6 // indirect + github.com/charmbracelet/x/cellbuf v0.0.15 // indirect + github.com/charmbracelet/x/term v0.2.2 // indirect + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/cloudflare/circl v1.6.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect + github.com/creachadair/tomledit v0.0.29 // indirect + github.com/cyphar/filepath-securejoin v0.6.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/fatih/color v1.19.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.9.0 // indirect + github.com/go-git/go-git/v5 v5.19.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.3 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/jsonschema-go v0.4.2 // indirect + github.com/google/renameio v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/jsonschema v0.14.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jedib0t/go-pretty/v6 v6.8.2 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.19.0 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lmittmann/tint v1.1.3 // indirect + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect + github.com/mark3labs/mcp-go v0.55.1 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.21 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/nxadm/tail v1.4.11 // indirect + github.com/opencontainers/selinux v1.15.1 // indirect + github.com/pb33f/ordered-map/v2 v2.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 // indirect + github.com/pjbgf/sha1cd v0.6.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/samber/lo v1.53.0 // indirect + github.com/samber/slog-common v0.21.0 // indirect + github.com/samber/slog-multi v1.8.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + github.com/sergi/go-diff v1.4.0 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + go.szostok.io/version v1.2.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/tools/azldev/go.sum b/tools/azldev/go.sum new file mode 100644 index 00000000000..dececa6df58 --- /dev/null +++ b/tools/azldev/go.sum @@ -0,0 +1,303 @@ +cyphar.com/go-pathrs v0.2.4 h1:iD/mge36swa1UFKdINkr1Frkpp6wZsy3YYEildj9cLY= +cyphar.com/go-pathrs v0.2.4/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= +github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/auribuo/stylishcobra v1.0.1 h1:bKGeCcLCW1Pzk5ZiT2Ubijdccn+unGrB5SBf3TQBGeA= +github.com/auribuo/stylishcobra v1.0.1/go.mod h1:NRNkmflkRM1078o68o3pFpPFZeb7wtD6EscbqLZmDsU= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= +github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/binalyze/ctxio v0.0.0-20210917063359-64c39a15e009 h1:rfYIdUKTmGdK3Dmxz2mp8z4LV8X7N+Yawbg0GASu/UM= +github.com/binalyze/ctxio v0.0.0-20210917063359-64c39a15e009/go.mod h1:YuOZ+lC/JejWMd9pp6GYQiGlW5jlsyVsDH0IPq+Lo/E= +github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= +github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w= +github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= +github.com/brunoga/deep v1.3.1 h1:bSrL6FhAZa6JlVv4vsi7Hg8SLwroDb1kgDERRVipBCo= +github.com/brunoga/deep v1.3.1/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= +github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= +github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= +github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= +github.com/cavaliergopher/rpm v1.3.0 h1:UHX46sasX8MesUXXQ+UbkFLUX4eUWTlEcX8jcnRBIgI= +github.com/cavaliergopher/rpm v1.3.0/go.mod h1:vEumo1vvtrHM1Ov86f6+k8j7zNKOxQfHDCAIcR/36ZI= +github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= +github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= +github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= +github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= +github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= +github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= +github.com/charmbracelet/gum v0.17.0 h1:zlBYATCSG128gdZcCx0BfCsaADk3g9d0QyD19jjQrx0= +github.com/charmbracelet/gum v0.17.0/go.mod h1:All7PGZUc3z0ALtAhp1oLw8r+25sSKCkNdg5FjtOi5I= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.13.1/go.mod h1:zaYVJ2xKSKEnTEEbX6uAHabh2d975RJ+0yfkFpRBz5U= +github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE= +github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA= +github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.4.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= +github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= +github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= +github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= +github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creachadair/tomledit v0.0.29 h1:dB5CbdwJMpn/fmfAPTAAleXF/KJwY0Ggc1eL/zvZRgk= +github.com/creachadair/tomledit v0.0.29/go.mod h1:4SoTXxzHgvzHRMIJPw+o6zK/yXii4VjLrb6/3gCQnyA= +github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= +github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= +github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= +github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= +github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= +github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg= +github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jedib0t/go-pretty/v6 v6.8.2 h1:FmKNr1GOyot/zqNQplE8HLhFguJaeHJTCArntnI4uxE= +github.com/jedib0t/go-pretty/v6 v6.8.2/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ= +github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g= +github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lmittmann/tint v1.1.3 h1:Hv4EaHWXQr+GTFnOU4VKf8UvAtZgn0VuKT+G0wFlO3I= +github.com/lmittmann/tint v1.1.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= +github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mark3labs/mcp-go v0.55.1 h1:GLYqNm9qdMGPhCtK4g1t1y1vhAPfayOBuaibDi4mrSA= +github.com/mark3labs/mcp-go v0.55.1/go.mod h1:+8WclSK1ZUweCP3hvktSji8n8ABG/95QaEkeVE/Uwas= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= +github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/microsoft/azure-linux-dev-tools v0.1.1-0.20260715220518-661c69b6c5b1 h1:jd2gsjXqZCthbBlBdHLx62XGxZ8+bJBPvpaedtH0zp8= +github.com/microsoft/azure-linux-dev-tools v0.1.1-0.20260715220518-661c69b6c5b1/go.mod h1:fbBeEWs22cm4Wr66tXKmVN0E2PVMB5H6DoGT/Eh132c= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/opencontainers/selinux v1.15.1 h1:ERxeh5caJvCzNAKdI8WQbJmB1LDTn4BuaAg8wihLBpA= +github.com/opencontainers/selinux v1.15.1/go.mod h1:LenyElirjUHszfxrjuFqC85HIeXZKumHcKMQtnaDlQQ= +github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY= +github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= +github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= +github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/samber/slog-common v0.21.0 h1:Wo2hTly1Br5RjYqX/BTWJJeDnTE85oWk/7vqlpZuAUc= +github.com/samber/slog-common v0.21.0/go.mod h1:d/6OaSlzdkl9PFpfRLgn8FwY1OW6EFmPtBpsHX4MrU0= +github.com/samber/slog-multi v1.8.0 h1:E05c1wnQ+8M58oQDBABlJ4TEIJWssNgtckso3zlaLlI= +github.com/samber/slog-multi v1.8.0/go.mod h1:6+3j/ILxDvAcLD75YdQAm6iKWu6AmwlohLgQxL/2aiI= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM= +github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574GlUxHD4rs8= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +go.szostok.io/version v1.2.0 h1:8eMMdfsonjbibwZRLJ8TnrErY8bThFTQsZYV16mcXms= +go.szostok.io/version v1.2.0/go.mod h1:EiU0gPxaXb6MZ+apSN0WgDO6F4JXyC99k9PIXf2k2E8= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s= +go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/azldev/tools.go b/tools/azldev/tools.go new file mode 100644 index 00000000000..00c92f21495 --- /dev/null +++ b/tools/azldev/tools.go @@ -0,0 +1,9 @@ +// Dependabot does not update dependencies declared only with Go tool directives. +// Keep this blank import until https://github.com/dependabot/dependabot-core/issues/12050 is resolved. +//go:build tools + +package azldev + +import ( + _ "github.com/microsoft/azure-linux-dev-tools/pkg/app/azldev_cli" +)