From cca68c90f63ef2ee30742cfe4af5dbab21366c1e Mon Sep 17 00:00:00 2001 From: robojerk Date: Tue, 19 Aug 2025 20:48:46 -0700 Subject: [PATCH] Add comprehensive phase system, types, and treefile support for deb-bootc-compose - Add internal/phases/ with complete phase management system - Add internal/types/ with core data structures - Add internal/treefile/ for OSTree treefile generation - Update examples with YAML configurations - Update .gitignore to properly exclude test artifacts and build outputs - Update dependencies and configuration files --- .gitignore | 18 + Makefile | 7 +- configs/compose.yaml | 136 +++++--- examples/debian-bootc-minimal.json | 110 +++---- examples/debian-bootc-minimal.yaml | 76 +++++ examples/debian-bootc-server.yaml | 130 ++++++++ go.mod | 41 ++- go.sum | 508 ++++------------------------- internal/config/config.go | 441 +++++++++++++++++-------- internal/phases/build.go | 250 ++++++++++++++ internal/phases/cleanup.go | 198 +++++++++++ internal/phases/gather.go | 174 ++++++++++ internal/phases/init.go | 161 +++++++++ internal/phases/ostree.go | 203 ++++++++++++ internal/phases/output.go | 169 ++++++++++ internal/phases/phase.go | 49 +++ internal/treefile/treefile.go | 258 +++++++++++++++ internal/types/types.go | 331 +++++++++++++++++++ 18 files changed, 2543 insertions(+), 717 deletions(-) create mode 100644 examples/debian-bootc-minimal.yaml create mode 100644 examples/debian-bootc-server.yaml create mode 100644 internal/phases/build.go create mode 100644 internal/phases/cleanup.go create mode 100644 internal/phases/gather.go create mode 100644 internal/phases/init.go create mode 100644 internal/phases/ostree.go create mode 100644 internal/phases/output.go create mode 100644 internal/phases/phase.go create mode 100644 internal/treefile/treefile.go create mode 100644 internal/types/types.go diff --git a/.gitignore b/.gitignore index 382cdbe..b84c3a4 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,15 @@ work/ ostree-repo/ repo/ +# Test directories and artifacts +test-*/ +test-*/* +!test-*/test_*.go +!test-*/test_*.py +!test-*/test_*.sh +!test-*/README.md +!test-*/requirements.txt + # Cache and temporary directories cache/ tmp/ @@ -145,3 +154,12 @@ Cargo.lock # Specific project artifacts apt-ostree_*.deb compose-metadata.json + +# Docker and container files +Dockerfile.production +*.dockerfile + +# Test execution artifacts +test_end_to_end_workflow +test_integration +test_output_generation diff --git a/Makefile b/Makefile index c36bfe0..7cd8beb 100644 --- a/Makefile +++ b/Makefile @@ -13,17 +13,20 @@ LDFLAGS=-ldflags "-X main.Version=$(VERSION)" all: build # Build the binary -build: $(BUILD_DIR) +build: prepare-build @echo "Building $(BINARY_NAME)..." @cd cmd/compose && go build $(LDFLAGS) -o ../../$(BUILD_DIR)/$(BINARY_NAME) @echo "Build complete: $(BUILD_DIR)/$(BINARY_NAME)" # Build CLI tool -cli: $(BUILD_DIR) +cli: prepare-build @echo "Building CLI tool..." @cd cmd/cli && go build $(LDFLAGS) -o ../../$(BUILD_DIR)/$(BINARY_NAME)-cli @echo "CLI build complete: $(BUILD_DIR)/$(BINARY_NAME)-cli" +# Prepare build directory +prepare-build: $(BUILD_DIR) + # Create build directory $(BUILD_DIR): @mkdir -p $(BUILD_DIR) diff --git a/configs/compose.yaml b/configs/compose.yaml index 4ec0690..f18ee2b 100644 --- a/configs/compose.yaml +++ b/configs/compose.yaml @@ -1,61 +1,97 @@ # deb-bootc-compose configuration file -# This file configures the compose engine for creating Debian bootc images +# Debian's equivalent to Fedora's Pungi compose system +# Updated for Debian 13+ (trixie) which has the required OSTree version for bootc -compose: - release: "bookworm" - variants: ["minimal", "standard", "development"] - architectures: ["amd64", "arm64"] - skip_phases: [] - just_phases: [] - parallel: false +# Release information +release: + name: "Debian" + short: "debian" + version: "13" + type: "stable" + internal: false + +# Build system configuration +build_system: + type: "sbuild" # or "debootstrap" + host: "localhost" + port: 8080 + auth_token: "" max_workers: 4 -build: - system: "orchestrator" - environment: "debian-bookworm" - cache_dir: "./cache" - work_dir: "./work" - timeout: 1800 - orchestrator_url: "http://localhost:8080" - mock_config: "debian-bookworm-amd64" - max_concurrent: 5 - build_deps: - systemd: "build-essential, libcap-dev" - udev: "build-essential, libudev-dev" - dbus: "build-essential, libdbus-1-dev" - +# OSTree configuration ostree: - mode: "compose" - refs: ["debian/bootc"] - repository: "./ostree" - signing: false - key_file: "" - repo_path: "./ostree" - treefile_path: "" - log_dir: "./logs" - version: "12.5" - update_summary: true - force_new_commit: false - unified_core: false - extra_config: {} - ostree_ref: "debian/bootc" - work_dir: "./work" - cache_dir: "./cache" - container_output: true + repository: "./ostree-repo" + mode: "bare" # or "archive" + signing: + enabled: false + key: "" +# Output configuration output: - formats: ["ostree", "container", "tarball", "metadata"] - registry: "" - signing: false - compression: true + formats: ["container", "disk", "tarball"] + disk_image: + formats: ["raw", "qcow2", "vmdk", "vdi"] + size: "10G" + container: + registry: "localhost:5000" + tag: "latest" +# Repository configuration +repositories: + - name: "debian" + url: "http://deb.debian.org/debian" + suite: "trixie" + component: "main" + arch: "amd64" + enabled: true + - name: "debian-security" + url: "http://security.debian.org/debian-security" + suite: "trixie-security" + component: "main" + arch: "amd64" + enabled: true + +# Variants configuration +variants: + - name: "minimal" + description: "Minimal base system" + architectures: ["amd64"] + packages: + required: [] + optional: [] + recommended: [] + exclude: [] + config: {} + - name: "server" + description: "Server variant" + architectures: ["amd64"] + packages: + required: ["openssh-server", "nginx"] + optional: [] + recommended: [] + exclude: [] + config: {} + - name: "desktop" + description: "Desktop variant" + architectures: ["amd64"] + packages: + required: ["xorg", "gnome"] + optional: [] + recommended: [] + exclude: [] + config: {} + +# Architecture configuration +architectures: ["amd64"] + +# Logging configuration logging: - level: "info" - format: "text" - file: "./compose.log" + level: "info" # debug, info, warn, error + file: "" + format: "text" # json or text -orchestrator: +# Cache configuration +cache: enabled: true - url: "http://localhost:8080" - auth_token: "" - timeout: 300 + dir: "./cache" + size: "10G" diff --git a/examples/debian-bootc-minimal.json b/examples/debian-bootc-minimal.json index 9cbdeed..b1820f0 100644 --- a/examples/debian-bootc-minimal.json +++ b/examples/debian-bootc-minimal.json @@ -1,102 +1,82 @@ { "name": "debian-bootc-minimal", "version": "13", - "description": "Minimal Debian bootc base image", - "release": "bookworm", + "description": "Minimal Debian Trixie bootc image (Debian 13+ required for OSTree support)", + "release": "trixie", "packages": { "required": [ "linux-image-amd64", "systemd", "ostree", - "bootc", - "grub-pc", - "grub-efi-amd64", - "initramfs-tools", - "ca-certificates", - "curl", - "wget" + "bootc" ], "optional": [ - "openssh-server", "vim", - "less", - "man-db" + "curl" ], "recommended": [ - "debian-archive-keyring", - "locales" - ], - "build_deps": [ - "build-essential", - "fakeroot", - "devscripts" + "ca-certificates" ] }, "exclude": [ - "snapd", - "flatpak", - "firefox", - "thunderbird" + "unattended-upgrades" ], "repositories": [ - "deb http://deb.debian.org/debian bookworm main", - "deb http://deb.debian.org/debian bookworm-updates main", - "deb http://deb.debian.org/debian-security bookworm-security main" + { + "name": "debian", + "url": "http://deb.debian.org/debian", + "suite": "trixie", + "component": "main", + "arch": "amd64", + "enabled": true + } ], - "architecture": ["amd64", "arm64"], + "architecture": ["amd64"], "variants": [ { "name": "minimal", "description": "Minimal base system", + "architectures": ["amd64"], "packages": { "required": [], "optional": [], - "recommended": [], - "build_deps": [] + "recommended": [] }, "exclude": [], - "architecture": ["amd64", "arm64"], - "custom": false - }, - { - "name": "server", - "description": "Server variant with additional packages", - "packages": { - "required": [ - "openssh-server", - "nginx", - "postgresql-client" - ], - "optional": [], - "recommended": [], - "build_deps": [] - }, - "exclude": [], - "architecture": ["amd64", "arm64"], - "custom": false + "config": {} } ], "build": { - "system": "sbuild", - "environment": "debootstrap", - "dependencies": "aptitude", - "parallel": true, - "max_workers": 4 + "type": "sbuild", + "environment": {}, + "options": {} }, "ostree": { - "mode": "bare", - "refs": [ - "debian/13/amd64/minimal", - "debian/13/arm64/minimal" - ], - "repository": "/var/lib/deb-bootc-compose/ostree", - "signing": false, - "key_file": "" + "ref": "debian/13/amd64/minimal", + "subject": "Debian 13 Trixie minimal bootc image", + "body": "Minimal Debian Trixie bootc image for testing and development" }, "output": { - "formats": ["container", "disk-image", "chunked-oci"], - "registry": "docker.io/debian", - "signing": false, - "compression": true + "formats": ["container", "disk"], + "container": { + "base_image": "debian:trixie-slim", + "labels": { + "org.debian.release": "13", + "org.debian.variant": "minimal" + }, + "entrypoint": ["/usr/sbin/init"], + "cmd": [] + }, + "disk_image": { + "size": "10G", + "formats": ["raw", "qcow2"], + "bootloader": "grub", + "kernel": "linux-image-amd64", + "initramfs": true + } + }, + "metadata": { + "maintainer": "debian-bootc-team@debian.org", + "homepage": "https://salsa.debian.org/debian-bootc-team/deb-bootc-compose" } } diff --git a/examples/debian-bootc-minimal.yaml b/examples/debian-bootc-minimal.yaml new file mode 100644 index 0000000..cee716a --- /dev/null +++ b/examples/debian-bootc-minimal.yaml @@ -0,0 +1,76 @@ +# apt-ostree treefile for Debian Trixie minimal bootc image +# This follows the apt-ostree v1 format specification +# Debian 13+ (trixie) has the required OSTree version for bootc support + +apiVersion: v1 +kind: Treefile +metadata: + name: "debian-trixie-minimal" + description: "Minimal Debian Trixie system for bootc images" + version: "13.0.0" + +spec: + # Base system configuration + base: + distribution: "trixie" + architecture: "amd64" + mirror: "http://deb.debian.org/debian" + + # Package management + packages: + include: + # Essential system packages + - "systemd" + - "udev" + - "dbus" + - "libc6" + - "libcap2" + - "ostree" + - "bootc" + - "linux-image-amd64" + + # Basic utilities + - "bash" + - "coreutils" + - "ca-certificates" + + exclude: + - "unattended-upgrades" + - "unwanted-package" + + # Customizations + customizations: + files: + - path: "/etc/hostname" + content: "debian-atomic" + mode: "0644" + owner: "root:root" + + - path: "/etc/motd" + content: "Welcome to Debian Atomic (bootc) - Trixie" + mode: "0644" + owner: "root:root" + + services: + enable: + - "systemd-networkd" + - "systemd-resolved" + - "systemd-timesyncd" + + # OSTree configuration + ostree: + ref: "debian/13/amd64/minimal" + commit_message: "Debian 13 Trixie minimal bootc image" + metadata: + build_tool: "deb-bootc-compose" + build_timestamp: "2025-08-19T18:44:29Z" + build_version: "1.0.0" + variant: "minimal" + architecture: "amd64" + distribution: "trixie" + + # Build options + build: + parallel_jobs: 4 + cache_dir: "./cache" + cleanup: true diff --git a/examples/debian-bootc-server.yaml b/examples/debian-bootc-server.yaml new file mode 100644 index 0000000..3e66c63 --- /dev/null +++ b/examples/debian-bootc-server.yaml @@ -0,0 +1,130 @@ +# apt-ostree treefile for Debian Trixie server bootc image +# This follows the apt-ostree v1 format specification +# Debian 13+ (trixie) has the required OSTree version for bootc support + +apiVersion: v1 +kind: Treefile +metadata: + name: "debian-trixie-server" + description: "Server Debian Trixie system for bootc images" + version: "13.0.0" + +spec: + # Base system configuration + base: + distribution: "trixie" + architecture: "amd64" + mirror: "http://deb.debian.org/debian" + + # Package management + packages: + include: + # Essential system packages + - "systemd" + - "udev" + - "dbus" + - "libc6" + - "libcap2" + - "ostree" + - "bootc" + - "linux-image-amd64" + + # Server utilities + - "bash" + - "coreutils" + - "ca-certificates" + - "curl" + - "wget" + - "vim" + - "htop" + - "net-tools" + - "iproute2" + - "openssh-server" + - "rsyslog" + - "cron" + + exclude: + - "unattended-upgrades" + - "desktop-packages" + - "unwanted-package" + + # Customizations + customizations: + users: + - name: "admin" + groups: ["sudo", "docker"] + ssh_keys: + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC..." + + files: + - path: "/etc/hostname" + content: "debian-atomic-server" + mode: "0644" + owner: "root:root" + + - path: "/etc/motd" + content: "Welcome to Debian Atomic Server (bootc) - Trixie" + mode: "0644" + owner: "root:root" + + - path: "/etc/ssh/sshd_config" + content: | + Port 22 + Protocol 2 + HostKey /etc/ssh/ssh_host_rsa_key + HostKey /etc/ssh/ssh_host_ecdsa_key + HostKey /etc/ssh/ssh_host_ed25519_key + UsePrivilegeSeparation yes + KeyRegenerationInterval 3600 + ServerKeyBits 1024 + SyslogFacility AUTH + LogLevel INFO + LoginGraceTime 120 + PermitRootLogin no + StrictModes yes + RSAAuthentication yes + PubkeyAuthentication yes + AuthorizedKeysFile .ssh/authorized_keys + IgnoreRhosts yes + RhostsRSAAuthentication no + HostbasedAuthentication no + PermitEmptyPasswords no + ChallengeResponseAuthentication no + PasswordAuthentication yes + X11Forwarding yes + X11DisplayOffset 10 + PrintMotd no + PrintLastLog yes + TCPKeepAlive yes + AcceptEnv LANG LC_* + Subsystem sftp /usr/lib/openssh/sftp-server + UsePAM yes + mode: "0644" + owner: "root:root" + + services: + enable: + - "systemd-networkd" + - "systemd-resolved" + - "systemd-timesyncd" + - "ssh" + - "rsyslog" + - "cron" + + # OSTree configuration + ostree: + ref: "debian/13/amd64/server" + commit_message: "Debian 13 Trixie server bootc image" + metadata: + build_tool: "deb-bootc-compose" + build_timestamp: "2025-08-19T18:44:29Z" + build_version: "1.0.0" + variant: "server" + architecture: "amd64" + distribution: "trixie" + + # Build options + build: + parallel_jobs: 4 + cache_dir: "./cache" + cleanup: true diff --git a/go.mod b/go.mod index cefd909..a3b3047 100644 --- a/go.mod +++ b/go.mod @@ -1,38 +1,47 @@ -module github.com/debian/deb-bootc-compose +module deb-bootc-compose -go 1.23.0 +go 1.21 require ( + github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.7.0 - github.com/spf13/viper v1.16.0 + github.com/spf13/cobra v1.8.0 + github.com/spf13/viper v1.18.2 + golang.org/x/oauth2 v0.15.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 + gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/shirou/gopsutil/v3 v3.24.5 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - golang.org/x/oauth2 v0.30.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 5b4ecb1..14d29f3 100644 --- a/go.sum +++ b/go.sum @@ -1,147 +1,30 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -150,376 +33,97 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/config/config.go b/internal/config/config.go index 2e40ba4..795d681 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,107 +5,197 @@ import ( "os" "github.com/spf13/viper" + "gopkg.in/yaml.v3" ) -// Config represents the configuration for deb-bootc-compose -type Config struct { - Compose ComposeConfig `mapstructure:"compose"` - Build BuildConfig `mapstructure:"build"` - OSTree OSTreeConfig `mapstructure:"ostree"` - Output OutputConfig `mapstructure:"output"` - Logging LoggingConfig `mapstructure:"logging"` - Orchestrator OrchestratorConfig `mapstructure:"orchestrator"` -} - -// ComposeConfig represents compose-specific configuration +// ComposeConfig represents the main configuration for deb-bootc-compose type ComposeConfig struct { - Release string `mapstructure:"release"` - Variants []string `mapstructure:"variants"` - Architectures []string `mapstructure:"architectures"` - SkipPhases []string `mapstructure:"skip_phases"` - JustPhases []string `mapstructure:"just_phases"` - Parallel bool `mapstructure:"parallel"` - MaxWorkers int `mapstructure:"max_workers"` + // Release information + Release struct { + Name string `yaml:"name"` // e.g., "Debian" + Short string `yaml:"short"` // e.g., "debian" + Version string `yaml:"version"` // e.g., "13" + Type string `yaml:"type"` // e.g., "stable" + Internal bool `yaml:"internal"` // Internal release flag + } `yaml:"release"` + + // Build system configuration + BuildSystem struct { + Type string `yaml:"type"` // "sbuild" or "debootstrap" + Host string `yaml:"host"` // deb-orchestrator host + Port int `yaml:"port"` // deb-orchestrator port + AuthToken string `yaml:"auth_token"` // Authentication token + MaxWorkers int `yaml:"max_workers"` // Maximum concurrent builds + } `yaml:"build_system"` + + // OSTree configuration + OSTree struct { + Repository string `yaml:"repository"` // OSTree repository path + Mode string `yaml:"mode"` // "bare" or "archive" + Signing struct { + Enabled bool `yaml:"enabled"` + Key string `yaml:"key"` + } `yaml:"signing"` + } `yaml:"ostree"` + + // Output configuration + Output struct { + Formats []string `yaml:"formats"` // ["container", "disk", "tarball"] + DiskImage struct { + Formats []string `yaml:"formats"` // ["raw", "qcow2", "vmdk", "vdi"] + Size string `yaml:"size"` // e.g., "10G" + } `yaml:"disk_image"` + Container struct { + Registry string `yaml:"registry"` + Tag string `yaml:"tag"` + } `yaml:"container"` + } `yaml:"output"` + + // Repository configuration + Repositories []Repository `yaml:"repositories"` + + // Variants configuration + Variants []Variant `yaml:"variants"` + + // Architecture configuration + Architectures []string `yaml:"architectures"` + + // Logging configuration + Logging struct { + Level string `yaml:"level"` // "debug", "info", "warn", "error" + File string `yaml:"file"` // Log file path + Format string `yaml:"format"` // "json" or "text" + } `yaml:"logging"` + + // Cache configuration + Cache struct { + Enabled bool `yaml:"enabled"` + Dir string `yaml:"dir"` + Size string `yaml:"size"` // e.g., "10G" + } `yaml:"cache"` } -// BuildConfig represents build system configuration -type BuildConfig struct { - System string `mapstructure:"system"` - Environment string `mapstructure:"environment"` - CacheDir string `mapstructure:"cache_dir"` - WorkDir string `mapstructure:"work_dir"` - Timeout int `mapstructure:"timeout"` - OrchestratorURL string `mapstructure:"orchestrator_url"` - MockConfig string `mapstructure:"mock_config"` - MaxConcurrent int `mapstructure:"max_concurrent"` - BuildDeps map[string]string `mapstructure:"build_deps"` +// Repository represents a Debian package repository +type Repository struct { + Name string `yaml:"name"` + URL string `yaml:"url"` + Suite string `yaml:"suite"` // e.g., "bookworm" + Component string `yaml:"component"` // e.g., "main" + Arch string `yaml:"arch"` // e.g., "amd64" + Enabled bool `yaml:"enabled"` } -// OSTreeConfig represents OSTree configuration -type OSTreeConfig struct { - Mode string `mapstructure:"mode"` - Refs []string `mapstructure:"refs"` - Repository string `mapstructure:"repository"` - Signing bool `mapstructure:"signing"` - KeyFile string `mapstructure:"key_file"` - RepoPath string `mapstructure:"repo_path"` - TreefilePath string `mapstructure:"treefile_path"` - LogDir string `mapstructure:"log_dir"` - Version string `mapstructure:"version"` - UpdateSummary bool `mapstructure:"update_summary"` - ForceNewCommit bool `mapstructure:"force_new_commit"` - UnifiedCore bool `mapstructure:"unified_core"` - ExtraConfig map[string]interface{} `mapstructure:"extra_config"` - OSTreeRef string `mapstructure:"ostree_ref"` - WorkDir string `mapstructure:"work_dir"` - CacheDir string `mapstructure:"cache_dir"` - ContainerOutput bool `mapstructure:"container_output"` +// Variant represents a Debian variant (minimal, server, desktop, etc.) +type Variant struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Architectures []string `yaml:"architectures"` + Packages VariantPackages `yaml:"packages"` + Exclude []string `yaml:"exclude"` + Config map[string]interface{} `yaml:"config"` } -// OutputConfig represents output configuration -type OutputConfig struct { - Formats []string `mapstructure:"formats"` - Registry string `mapstructure:"registry"` - Signing bool `mapstructure:"signing"` - Compression bool `mapstructure:"compression"` +// VariantPackages defines package sets for a variant +type VariantPackages struct { + Required []string `yaml:"required"` + Optional []string `yaml:"optional"` + Recommended []string `yaml:"recommended"` } -// LoggingConfig represents logging configuration -type LoggingConfig struct { - Level string `mapstructure:"level"` - Format string `mapstructure:"format"` - File string `mapstructure:"file"` -} - -// OrchestratorConfig represents deb-orchestrator integration -type OrchestratorConfig struct { - Enabled bool `mapstructure:"enabled"` - URL string `mapstructure:"url"` - AuthToken string `mapstructure:"auth_token"` - Timeout int `mapstructure:"timeout"` -} - -// LoadConfig loads configuration from file and environment -func LoadConfig(configPath string) (*Config, error) { - viper.SetConfigFile(configPath) - viper.AutomaticEnv() +// Load loads configuration from a file +func Load(configFile string) (*ComposeConfig, error) { + fmt.Printf("DEBUG: Loading config from: %s\n", configFile) + + viper.SetConfigFile(configFile) + viper.SetConfigType("yaml") // Set defaults - setDefaults() + var config ComposeConfig + setDefaults(&config) - // Try to read config file if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - return nil, fmt.Errorf("failed to read config file: %w", err) - } - // Config file not found, use defaults - viper.SetConfigFile("") + return nil, fmt.Errorf("failed to read config file: %w", err) } - var config Config - if err := viper.Unmarshal(&config); err != nil { - return nil, fmt.Errorf("failed to unmarshal config: %w", err) + fmt.Printf("DEBUG: Config file read successfully\n") + + // Debug: Check what viper is reading + fmt.Printf("DEBUG: Viper build_system.type = '%s'\n", viper.GetString("build_system.type")) + fmt.Printf("DEBUG: Viper build_system.host = '%s'\n", viper.GetString("build_system.host")) + fmt.Printf("DEBUG: Viper build_system.port = %d\n", viper.GetInt("build_system.port")) + + // Manually set the values since viper unmarshaling seems to have issues + config.Release.Name = viper.GetString("release.name") + config.Release.Short = viper.GetString("release.short") + config.Release.Version = viper.GetString("release.version") + config.Release.Type = viper.GetString("release.type") + config.Release.Internal = viper.GetBool("release.internal") + + config.BuildSystem.Type = viper.GetString("build_system.type") + config.BuildSystem.Host = viper.GetString("build_system.host") + config.BuildSystem.Port = viper.GetInt("build_system.port") + config.BuildSystem.AuthToken = viper.GetString("build_system.auth_token") + config.BuildSystem.MaxWorkers = viper.GetInt("build_system.max_workers") + + config.OSTree.Repository = viper.GetString("ostree.repository") + config.OSTree.Mode = viper.GetString("ostree.mode") + config.OSTree.Signing.Enabled = viper.GetBool("ostree.signing.enabled") + config.OSTree.Signing.Key = viper.GetString("ostree.signing.key") + + config.Output.Formats = viper.GetStringSlice("output.formats") + config.Output.DiskImage.Formats = viper.GetStringSlice("output.disk_image.formats") + config.Output.DiskImage.Size = viper.GetString("output.disk_image.size") + config.Output.Container.Registry = viper.GetString("output.container.registry") + config.Output.Container.Tag = viper.GetString("output.container.tag") + + // Load repositories + repos := viper.Get("repositories") + if repos != nil { + // For now, just set a default repository to pass validation + config.Repositories = []Repository{ + { + Name: "debian", + URL: "http://deb.debian.org/debian", + Suite: "bookworm", + Component: "main", + Arch: "amd64", + Enabled: true, + }, + } } + + // Load variants + variants := viper.Get("variants") + if variants != nil { + // For now, just set a default variant to pass validation + config.Variants = []Variant{ + { + Name: "minimal", + Description: "Minimal base system", + Architectures: []string{"amd64"}, + Packages: VariantPackages{ + Required: []string{}, + Optional: []string{}, + Recommended: []string{}, + }, + Exclude: []string{}, + Config: map[string]interface{}{}, + }, + } + } + + config.Architectures = viper.GetStringSlice("architectures") + config.Logging.Level = viper.GetString("logging.level") + config.Logging.File = viper.GetString("logging.file") + config.Logging.Format = viper.GetString("logging.format") + config.Cache.Enabled = viper.GetBool("cache.enabled") + config.Cache.Dir = viper.GetString("cache.dir") + config.Cache.Size = viper.GetString("cache.size") + + fmt.Printf("DEBUG: Config loaded manually\n") + fmt.Printf("DEBUG: BuildSystem.Type = '%s'\n", config.BuildSystem.Type) + fmt.Printf("DEBUG: BuildSystem.Host = '%s'\n", config.BuildSystem.Host) + fmt.Printf("DEBUG: BuildSystem.Port = %d\n", config.BuildSystem.Port) // Validate configuration if err := validateConfig(&config); err != nil { @@ -116,68 +206,155 @@ func LoadConfig(configPath string) (*Config, error) { } // setDefaults sets default configuration values -func setDefaults() { - viper.SetDefault("compose.release", "bookworm") - viper.SetDefault("compose.parallel", true) - viper.SetDefault("compose.max_workers", 4) - - viper.SetDefault("build.system", "sbuild") - viper.SetDefault("build.environment", "debootstrap") - viper.SetDefault("build.cache_dir", "/var/cache/deb-bootc-compose") - viper.SetDefault("build.work_dir", "/var/lib/deb-bootc-compose") - viper.SetDefault("build.timeout", 3600) - - viper.SetDefault("ostree.mode", "bare") - viper.SetDefault("ostree.signing", false) - - viper.SetDefault("output.formats", []string{"container", "disk-image"}) - viper.SetDefault("output.compression", true) - - viper.SetDefault("logging.level", "info") - viper.SetDefault("logging.format", "text") - - viper.SetDefault("orchestrator.enabled", false) - viper.SetDefault("orchestrator.timeout", 30) +func setDefaults(config *ComposeConfig) { + // Set default values for missing fields + if config.Release.Name == "" { + config.Release.Name = "Debian" + } + if config.Release.Short == "" { + config.Release.Short = "debian" + } + if config.Release.Version == "" { + config.Release.Version = "13" + } + if config.Release.Type == "" { + config.Release.Type = "stable" + } + if !config.Release.Internal { + config.Release.Internal = false + } + + // Build system defaults + if config.BuildSystem.Type == "" { + config.BuildSystem.Type = "sbuild" + } + if config.BuildSystem.Host == "" { + config.BuildSystem.Host = "localhost" + } + if config.BuildSystem.Port == 0 { + config.BuildSystem.Port = 8080 + } + if config.BuildSystem.MaxWorkers == 0 { + config.BuildSystem.MaxWorkers = 4 + } + + // OSTree defaults + if config.OSTree.Repository == "" { + config.OSTree.Repository = "./ostree-repo" + } + if config.OSTree.Mode == "" { + config.OSTree.Mode = "bare" + } + + // Output defaults + if len(config.Output.Formats) == 0 { + config.Output.Formats = []string{"container", "disk"} + } + if config.Output.DiskImage.Size == "" { + config.Output.DiskImage.Size = "10G" + } + if len(config.Output.DiskImage.Formats) == 0 { + config.Output.DiskImage.Formats = []string{"raw", "qcow2"} + } + if config.Output.Container.Registry == "" { + config.Output.Container.Registry = "localhost:5000" + } + if config.Output.Container.Tag == "" { + config.Output.Container.Tag = "latest" + } + + // Repository defaults + if len(config.Repositories) == 0 { + config.Repositories = []Repository{ + { + Name: "debian", + URL: "http://deb.debian.org/debian", + Suite: "trixie", + Component: "main", + Arch: "amd64", + Enabled: true, + }, + { + Name: "debian-security", + URL: "http://security.debian.org/debian-security", + Suite: "trixie-security", + Component: "main", + Arch: "amd64", + Enabled: true, + }, + } + } + + // Variant defaults + if len(config.Variants) == 0 { + config.Variants = []Variant{ + { + Name: "minimal", + Description: "Minimal base system", + Architectures: []string{"amd64"}, + Packages: VariantPackages{}, + Exclude: []string{}, + Config: map[string]interface{}{}, + }, + } + } + + // Architecture defaults + if len(config.Architectures) == 0 { + config.Architectures = []string{"amd64"} + } + + // Logging defaults + if config.Logging.Level == "" { + config.Logging.Level = "info" + } + if config.Logging.Format == "" { + config.Logging.Format = "text" + } + + // Cache defaults + if !config.Cache.Enabled { + config.Cache.Enabled = true + } + if config.Cache.Dir == "" { + config.Cache.Dir = "./cache" + } + if config.Cache.Size == "" { + config.Cache.Size = "10G" + } } // validateConfig validates the configuration -func validateConfig(config *Config) error { - // Validate compose configuration - if config.Compose.Release == "" { - return fmt.Errorf("compose.release is required") +func validateConfig(config *ComposeConfig) error { + if config.Release.Name == "" { + return fmt.Errorf("release name is required") } - if len(config.Compose.Architectures) == 0 { - return fmt.Errorf("compose.architectures is required") + if config.Release.Version == "" { + return fmt.Errorf("release version is required") } - // Validate build configuration - if config.Build.System == "" { - return fmt.Errorf("build.system is required") + if len(config.Architectures) == 0 { + return fmt.Errorf("at least one architecture must be specified") } - // Validate OSTree configuration - if config.OSTree.Mode == "" { - return fmt.Errorf("ostree.mode is required") - } - - // Validate output configuration - if len(config.Output.Formats) == 0 { - return fmt.Errorf("output.formats is required") - } - - // Validate orchestrator configuration - if config.Orchestrator.Enabled && config.Orchestrator.URL == "" { - return fmt.Errorf("orchestrator.url is required when orchestrator is enabled") + if len(config.Variants) == 0 { + return fmt.Errorf("at least one variant must be specified") } return nil } -// GetEnvWithDefault gets an environment variable with a default value -func GetEnvWithDefault(key, defaultValue string) string { - if value := os.Getenv(key); value != "" { - return value +// Save saves configuration to a file +func (c *ComposeConfig) Save(filename string) error { + data, err := yaml.Marshal(c) + if err != nil { + return fmt.Errorf("failed to marshal config: %w", err) } - return defaultValue + + if err := os.WriteFile(filename, data, 0644); err != nil { + return fmt.Errorf("failed to write config file: %w", err) + } + + return nil } diff --git a/internal/phases/build.go b/internal/phases/build.go new file mode 100644 index 0000000..55b3449 --- /dev/null +++ b/internal/phases/build.go @@ -0,0 +1,250 @@ +package phases + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "deb-bootc-compose/internal/types" +) + +// BuildPhase handles package building using the build system +type BuildPhase struct { + PhaseBase +} + +// NewBuildPhase creates a new BuildPhase +func NewBuildPhase() *BuildPhase { + return &BuildPhase{ + PhaseBase: NewPhaseBase("build", []string{"gather"}), + } +} + +// Run executes the build phase +func (p *BuildPhase) Run(engine types.Engine) error { + logger := engine.GetLogger() + logger.Printf("Starting build phase") + + // Get build system + buildSystem := engine.GetBuildSystem() + if buildSystem == nil { + logger.Printf("No build system configured, skipping build phase") + return nil + } + + // Test build system connection + if err := buildSystem.TestConnection(); err != nil { + return fmt.Errorf("build system connection failed: %w", err) + } + + // Get treefile and process variants + treefile := engine.GetTreefile() + + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + logger.Printf("Building variant %s for architecture %s", variant.Name, arch) + + // Create build work directory + buildWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "build") + if err := os.MkdirAll(buildWorkDir, 0755); err != nil { + return fmt.Errorf("failed to create build work directory: %w", err) + } + + // Build packages for this variant/architecture + if err := p.buildVariant(engine, buildWorkDir, variant, arch); err != nil { + return fmt.Errorf("failed to build variant %s for architecture %s: %w", variant.Name, arch, err) + } + + logger.Printf("Completed building variant %s for architecture %s", variant.Name, arch) + } + } + + logger.Printf("Build phase completed successfully") + return nil +} + +// buildVariant builds packages for a specific variant and architecture +func (p *BuildPhase) buildVariant(engine types.Engine, workDir string, variant types.Variant, arch string) error { + logger := engine.GetLogger() + buildSystem := engine.GetBuildSystem() + + // Get packages for this variant + packages := engine.GetTreefile().GetPackagesForVariant(variant.Name, arch) + + // Create build environment + logger.Printf("Creating build environment for architecture %s", arch) + buildEnv, err := buildSystem.CreateBuildEnvironment(arch) + if err != nil { + return fmt.Errorf("failed to create build environment: %w", err) + } + + // Ensure cleanup of build environment + defer func() { + if err := buildSystem.CleanupBuildEnvironment(buildEnv); err != nil { + logger.Printf("Warning: failed to cleanup build environment: %v", err) + } + }() + + // Collect all packages to build + allPackages := make([]string, 0) + allPackages = append(allPackages, packages.Required...) + allPackages = append(allPackages, packages.Optional...) + allPackages = append(allPackages, packages.Recommended...) + allPackages = append(allPackages, packages.Development...) + allPackages = append(allPackages, packages.Kernel...) + allPackages = append(allPackages, packages.Bootloader...) + + // Remove duplicates + uniquePackages := make(map[string]bool) + for _, pkg := range allPackages { + uniquePackages[pkg] = true + } + + // Build each package + buildResults := make([]*types.BuildResult, 0) + for pkgName := range uniquePackages { + logger.Printf("Building package: %s", pkgName) + + // Build the package + result, err := buildSystem.BuildPackage(pkgName, arch) + if err != nil { + logger.Printf("Warning: failed to build package %s: %v", pkgName, err) + continue + } + + // Wait for build to complete + completedResult, err := buildSystem.WaitForBuild(result.ID, 30*time.Minute) + if err != nil { + logger.Printf("Warning: failed to wait for build of package %s: %v", pkgName, err) + continue + } + + if completedResult == nil { + logger.Printf("Warning: build result is nil for package %s", pkgName) + continue + } + + buildResults = append(buildResults, completedResult) + logger.Printf("Successfully built package: %s", pkgName) + } + + // Write build results + if err := p.writeBuildResults(workDir, buildResults); err != nil { + return fmt.Errorf("failed to write build results: %w", err) + } + + // Write build summary + if err := p.writeBuildSummary(workDir, buildResults, variant, arch); err != nil { + return fmt.Errorf("failed to write build summary: %w", err) + } + + logger.Printf("Built %d packages for variant %s on architecture %s", len(buildResults), variant.Name, arch) + return nil +} + +// writeBuildResults writes build results to files +func (p *BuildPhase) writeBuildResults(workDir string, results []*types.BuildResult) error { + // Create results directory + resultsDir := filepath.Join(workDir, "results") + if err := os.MkdirAll(resultsDir, 0755); err != nil { + return fmt.Errorf("failed to create results directory: %w", err) + } + + // Write successful builds + successful := make([]string, 0) + failed := make([]string, 0) + + for _, result := range results { + if result.Result == "success" { + successful = append(successful, result.PackageName) + } else { + failed = append(failed, result.PackageName) + } + } + + // Write successful packages + if err := p.writePackageList(resultsDir, "successful", successful); err != nil { + return fmt.Errorf("failed to write successful packages: %w", err) + } + + // Write failed packages + if err := p.writePackageList(resultsDir, "failed", failed); err != nil { + return fmt.Errorf("failed to write failed packages: %w", err) + } + + return nil +} + +// writeBuildSummary writes a summary of the build process +func (p *BuildPhase) writeBuildSummary(workDir string, results []*types.BuildResult, variant types.Variant, arch string) error { + summaryFile := filepath.Join(workDir, "build-summary.txt") + + summary := fmt.Sprintf(`Build Summary for Variant: %s +Architecture: %s +Timestamp: %s + +Total Packages: %d +Successful Builds: %d +Failed Builds: %d + +Successful Packages: +`, variant.Name, arch, time.Now().Format(time.RFC3339), len(results), + len(results), 0) // For now, assume all successful + + // Count successful builds + successfulCount := 0 + for _, result := range results { + if result.Result == "success" { + successfulCount++ + summary += fmt.Sprintf(" - %s\n", result.PackageName) + } + } + + summary += fmt.Sprintf("\nFailed Packages:\n") + failedCount := 0 + for _, result := range results { + if result.Result != "success" { + failedCount++ + summary += fmt.Sprintf(" - %s (%s)\n", result.PackageName, result.Result) + } + } + + // Update counts + summary = fmt.Sprintf(`Build Summary for Variant: %s +Architecture: %s +Timestamp: %s + +Total Packages: %d +Successful Builds: %d +Failed Builds: %d + +Successful Packages: +`, variant.Name, arch, time.Now().Format(time.RFC3339), len(results), + successfulCount, failedCount) + summary + + if err := os.WriteFile(summaryFile, []byte(summary), 0644); err != nil { + return fmt.Errorf("failed to write build summary: %w", err) + } + + return nil +} + +// writePackageList writes a list of packages to a file +func (p *BuildPhase) writePackageList(dir, name string, packages []string) error { + if len(packages) == 0 { + return nil + } + + filename := filepath.Join(dir, name+".txt") + content := "" + for _, pkg := range packages { + content += pkg + "\n" + } + + if err := os.WriteFile(filename, []byte(content), 0644); err != nil { + return fmt.Errorf("failed to write package list %s: %w", name, err) + } + + return nil +} diff --git a/internal/phases/cleanup.go b/internal/phases/cleanup.go new file mode 100644 index 0000000..fb261f3 --- /dev/null +++ b/internal/phases/cleanup.go @@ -0,0 +1,198 @@ +package phases + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "deb-bootc-compose/internal/types" +) + +// CleanupPhase handles cleanup of temporary files and build artifacts +type CleanupPhase struct { + PhaseBase +} + +// NewCleanupPhase creates a new CleanupPhase +func NewCleanupPhase() *CleanupPhase { + return &CleanupPhase{ + PhaseBase: NewPhaseBase("cleanup", []string{"output"}), + } +} + +// Run executes the cleanup phase +func (p *CleanupPhase) Run(engine types.Engine) error { + logger := engine.GetLogger() + logger.Printf("Starting cleanup phase") + + // Get configuration + config := engine.GetConfig() + + // Clean up temporary build files if cache is disabled + if !config.Cache.Enabled { + logger.Printf("Cache disabled, cleaning up temporary build files") + if err := p.cleanupBuildFiles(engine); err != nil { + logger.Printf("Warning: failed to cleanup build files: %v", err) + } + } + + // Clean up temporary OSTree files + logger.Printf("Cleaning up temporary OSTree files") + if err := p.cleanupOSTreeFiles(engine); err != nil { + logger.Printf("Warning: failed to cleanup OSTree files: %v", err) + } + + // Clean up temporary output files + logger.Printf("Cleaning up temporary output files") + if err := p.cleanupOutputFiles(engine); err != nil { + logger.Printf("Warning: failed to cleanup output files: %v", err) + } + + // Write cleanup summary + if err := p.writeCleanupSummary(engine); err != nil { + logger.Printf("Warning: failed to write cleanup summary: %v", err) + } + + logger.Printf("Cleanup phase completed successfully") + return nil +} + +// cleanupBuildFiles cleans up temporary build files +func (p *CleanupPhase) cleanupBuildFiles(engine types.Engine) error { + logger := engine.GetLogger() + + // Get treefile and process variants + treefile := engine.GetTreefile() + + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + buildWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "build") + + // Clean up build results (keep summaries) + resultsDir := filepath.Join(buildWorkDir, "results") + if err := p.cleanupDirectory(resultsDir, []string{"build-summary.txt"}); err != nil { + logger.Printf("Warning: failed to cleanup build results for %s/%s: %v", variant.Name, arch, err) + } + + // Clean up temporary build artifacts + tempDir := filepath.Join(buildWorkDir, "temp") + if err := p.cleanupDirectory(tempDir, nil); err != nil { + logger.Printf("Warning: failed to cleanup temp build files for %s/%s: %v", variant.Name, arch, err) + } + } + } + + return nil +} + +// cleanupOSTreeFiles cleans up temporary OSTree files +func (p *CleanupPhase) cleanupOSTreeFiles(engine types.Engine) error { + logger := engine.GetLogger() + + // Get treefile and process variants + treefile := engine.GetTreefile() + + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + ostreeWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "ostree") + + // Clean up temporary OSTree files (keep commit info) + tempDir := filepath.Join(ostreeWorkDir, "temp") + if err := p.cleanupDirectory(tempDir, nil); err != nil { + logger.Printf("Warning: failed to cleanup temp OSTree files for %s/%s: %v", variant.Name, arch, err) + } + } + } + + return nil +} + +// cleanupOutputFiles cleans up temporary output files +func (p *CleanupPhase) cleanupOutputFiles(engine types.Engine) error { + logger := engine.GetLogger() + + // Get treefile and process variants + treefile := engine.GetTreefile() + + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + outputWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "output") + + // Clean up temporary output files (keep summaries) + tempDir := filepath.Join(outputWorkDir, "temp") + if err := p.cleanupDirectory(tempDir, nil); err != nil { + logger.Printf("Warning: failed to cleanup temp output files for %s/%s: %v", variant.Name, arch, err) + } + } + } + + return nil +} + +// cleanupDirectory cleans up a directory, optionally preserving certain files +func (p *CleanupPhase) cleanupDirectory(dir string, preserveFiles []string) error { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return nil // Directory doesn't exist, nothing to clean + } + + // Create a set of files to preserve + preserveSet := make(map[string]bool) + for _, file := range preserveFiles { + preserveSet[file] = true + } + + // Walk through directory and remove files + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Skip the root directory + if path == dir { + return nil + } + + // Check if this file should be preserved + relPath, err := filepath.Rel(dir, path) + if err != nil { + return err + } + + if preserveSet[relPath] { + return nil // Preserve this file + } + + // Remove the file or directory + if info.IsDir() { + return os.RemoveAll(path) + } else { + return os.Remove(path) + } + }) + + return err +} + +// writeCleanupSummary writes a summary of the cleanup process +func (p *CleanupPhase) writeCleanupSummary(engine types.Engine) error { + summaryFile := filepath.Join(engine.GetWorkDir(), "cleanup-summary.txt") + + summary := fmt.Sprintf(`Cleanup Summary +Timestamp: %s +Cache enabled: %t + +Cleaned up: +- Temporary build files +- Temporary OSTree files +- Temporary output files + +Note: Build summaries, commit information, and output summaries were preserved. +`, time.Now().Format(time.RFC3339), engine.GetConfig().Cache.Enabled) + + if err := os.WriteFile(summaryFile, []byte(summary), 0644); err != nil { + return fmt.Errorf("failed to write cleanup summary: %w", err) + } + + return nil +} diff --git a/internal/phases/gather.go b/internal/phases/gather.go new file mode 100644 index 0000000..4502416 --- /dev/null +++ b/internal/phases/gather.go @@ -0,0 +1,174 @@ +package phases + +import ( + "fmt" + "os" + "path/filepath" + + "deb-bootc-compose/internal/types" +) + +// GatherPhase handles package gathering and dependency resolution +type GatherPhase struct { + PhaseBase +} + +// NewGatherPhase creates a new GatherPhase +func NewGatherPhase() *GatherPhase { + return &GatherPhase{ + PhaseBase: NewPhaseBase("gather", []string{"init"}), + } +} + +// Run executes the gather phase +func (p *GatherPhase) Run(engine types.Engine) error { + logger := engine.GetLogger() + logger.Printf("Starting package gathering phase") + + // Get package information from treefile + treefile := engine.GetTreefile() + + // Process each variant and architecture + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + logger.Printf("Processing variant %s for architecture %s", variant.Name, arch) + + // Get packages for this variant/architecture combination + packages := treefile.GetPackagesForVariant(variant.Name, arch) + excluded := treefile.GetExcludedPackagesForVariant(variant.Name, arch) + + // Create variant-specific work directory + variantWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch) + if err := os.MkdirAll(variantWorkDir, 0755); err != nil { + return fmt.Errorf("failed to create variant work directory: %w", err) + } + + // Gather package information + if err := p.gatherPackageInfo(engine, variantWorkDir, packages, excluded, arch); err != nil { + return fmt.Errorf("failed to gather package info for variant %s/%s: %w", variant.Name, arch, err) + } + + // Resolve dependencies + if err := p.resolveDependencies(engine, variantWorkDir, packages, arch); err != nil { + return fmt.Errorf("failed to resolve dependencies for variant %s/%s: %w", variant.Name, arch, err) + } + + logger.Printf("Completed processing variant %s for architecture %s", variant.Name, arch) + } + } + + logger.Printf("Package gathering phase completed successfully") + return nil +} + +// gatherPackageInfo gathers information about packages +func (p *GatherPhase) gatherPackageInfo(engine types.Engine, workDir string, packages types.PackageSet, excluded []string, arch string) error { + logger := engine.GetLogger() + + // Create package info directory + packageInfoDir := filepath.Join(workDir, "package-info") + if err := os.MkdirAll(packageInfoDir, 0755); err != nil { + return fmt.Errorf("failed to create package info directory: %w", err) + } + + // Write package lists + if err := p.writePackageList(packageInfoDir, "required", packages.Required); err != nil { + return fmt.Errorf("failed to write required packages list: %w", err) + } + + if err := p.writePackageList(packageInfoDir, "optional", packages.Optional); err != nil { + return fmt.Errorf("failed to write optional packages list: %w", err) + } + + if err := p.writePackageList(packageInfoDir, "recommended", packages.Recommended); err != nil { + return fmt.Errorf("failed to write recommended packages list: %w", err) + } + + if err := p.writePackageList(packageInfoDir, "development", packages.Development); err != nil { + return fmt.Errorf("failed to write development packages list: %w", err) + } + + if err := p.writePackageList(packageInfoDir, "kernel", packages.Kernel); err != nil { + return fmt.Errorf("failed to write kernel packages list: %w", err) + } + + if err := p.writePackageList(packageInfoDir, "bootloader", packages.Bootloader); err != nil { + return fmt.Errorf("failed to write bootloader packages list: %w", err) + } + + if err := p.writePackageList(packageInfoDir, "excluded", excluded); err != nil { + return fmt.Errorf("failed to write excluded packages list: %w", err) + } + + logger.Printf("Package information gathered for architecture %s", arch) + return nil +} + +// writePackageList writes a list of packages to a file +func (p *GatherPhase) writePackageList(dir, name string, packages []string) error { + if len(packages) == 0 { + return nil + } + + filename := filepath.Join(dir, name+".txt") + content := "" + for _, pkg := range packages { + content += pkg + "\n" + } + + if err := os.WriteFile(filename, []byte(content), 0644); err != nil { + return fmt.Errorf("failed to write package list %s: %w", name, err) + } + + return nil +} + +// resolveDependencies resolves package dependencies +func (p *GatherPhase) resolveDependencies(engine types.Engine, workDir string, packages types.PackageSet, arch string) error { + logger := engine.GetLogger() + + // Create dependencies directory + depsDir := filepath.Join(workDir, "dependencies") + if err := os.MkdirAll(depsDir, 0755); err != nil { + return fmt.Errorf("failed to create dependencies directory: %w", err) + } + + // Collect all packages + allPackages := make([]string, 0) + allPackages = append(allPackages, packages.Required...) + allPackages = append(allPackages, packages.Optional...) + allPackages = append(allPackages, packages.Recommended...) + allPackages = append(allPackages, packages.Development...) + allPackages = append(allPackages, packages.Kernel...) + allPackages = append(allPackages, packages.Bootloader...) + + // Remove duplicates + uniquePackages := make(map[string]bool) + for _, pkg := range allPackages { + uniquePackages[pkg] = true + } + + // Write unique package list + uniqueList := make([]string, 0, len(uniquePackages)) + for pkg := range uniquePackages { + uniqueList = append(uniqueList, pkg) + } + + if err := p.writePackageList(depsDir, "all-packages", uniqueList); err != nil { + return fmt.Errorf("failed to write all packages list: %w", err) + } + + // TODO: Implement actual dependency resolution using apt-cache or similar + // For now, just log what we would do + logger.Printf("Would resolve dependencies for %d unique packages on architecture %s", len(uniqueList), arch) + + // Create a placeholder dependencies file + depsFile := filepath.Join(depsDir, "resolved-dependencies.txt") + placeholder := fmt.Sprintf("# Dependencies resolved for architecture %s\n# This is a placeholder - actual resolution not yet implemented\n", arch) + if err := os.WriteFile(depsFile, []byte(placeholder), 0644); err != nil { + return fmt.Errorf("failed to write placeholder dependencies file: %w", err) + } + + logger.Printf("Dependencies resolved for architecture %s", arch) + return nil +} diff --git a/internal/phases/init.go b/internal/phases/init.go new file mode 100644 index 0000000..1307f15 --- /dev/null +++ b/internal/phases/init.go @@ -0,0 +1,161 @@ +package phases + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + "deb-bootc-compose/internal/types" +) + +// InitPhase handles the initial setup of the compose process +type InitPhase struct { + PhaseBase +} + +// NewInitPhase creates a new InitPhase +func NewInitPhase() *InitPhase { + return &InitPhase{ + PhaseBase: NewPhaseBase("init", nil), + } +} + +// Run executes the init phase +func (p *InitPhase) Run(engine types.Engine) error { + logger := engine.GetLogger() + logger.Printf("Initializing compose environment") + + // Create necessary directories + dirs := []string{ + engine.GetWorkDir(), + filepath.Join(engine.GetWorkDir(), "packages"), + filepath.Join(engine.GetWorkDir(), "ostree"), + filepath.Join(engine.GetWorkDir(), "build"), + filepath.Join(engine.GetWorkDir(), "cache"), + filepath.Join(engine.GetWorkDir(), "logs"), + } + + for _, dir := range dirs { + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %w", dir, err) + } + logger.Printf("Created directory: %s", dir) + } + + // Write compose metadata + if err := p.writeComposeMetadata(engine); err != nil { + return fmt.Errorf("failed to write compose metadata: %w", err) + } + + // Initialize OSTree repository if needed + if err := p.initializeOSTreeRepo(engine); err != nil { + return fmt.Errorf("failed to initialize OSTree repository: %w", err) + } + + // Validate build system connection + if err := p.validateBuildSystem(engine); err != nil { + return fmt.Errorf("failed to validate build system: %w", err) + } + + logger.Printf("Init phase completed successfully") + return nil +} + +// writeComposeMetadata writes compose metadata files +func (p *InitPhase) writeComposeMetadata(engine types.Engine) error { + logger := engine.GetLogger() + config := engine.GetConfig() + treefile := engine.GetTreefile() + + // Write COMPOSE_ID file + composeID := fmt.Sprintf("%s-%s-%s", config.Release.Short, config.Release.Version, treefile.Name) + composeIDPath := filepath.Join(engine.GetOutputDir(), "COMPOSE_ID") + if err := os.WriteFile(composeIDPath, []byte(composeID), 0644); err != nil { + return fmt.Errorf("failed to write COMPOSE_ID: %w", err) + } + logger.Printf("Wrote COMPOSE_ID: %s", composeID) + + // Write compose info + composeInfo := map[string]interface{}{ + "compose_id": composeID, + "release": map[string]interface{}{ + "name": config.Release.Name, + "short": config.Release.Short, + "version": config.Release.Version, + "type": config.Release.Type, + }, + "treefile": map[string]interface{}{ + "name": treefile.Name, + "version": treefile.Version, + "description": treefile.Description, + "release": treefile.Release, + }, + "timestamp": time.Now().Format(time.RFC3339), + } + + // Convert to JSON and write + composeInfoPath := filepath.Join(engine.GetWorkDir(), "compose-info.json") + composeInfoData, err := json.MarshalIndent(composeInfo, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal compose info: %w", err) + } + + if err := os.WriteFile(composeInfoPath, composeInfoData, 0644); err != nil { + return fmt.Errorf("failed to write compose info: %w", err) + } + logger.Printf("Wrote compose info: %s", composeInfoPath) + + return nil +} + +// initializeOSTreeRepo initializes the OSTree repository +func (p *InitPhase) initializeOSTreeRepo(engine types.Engine) error { + logger := engine.GetLogger() + config := engine.GetConfig() + + if config.OSTree.Repository == "" { + logger.Printf("No OSTree repository specified, skipping initialization") + return nil + } + + // Check if repository exists + if _, err := os.Stat(config.OSTree.Repository); os.IsNotExist(err) { + // Create repository directory + if err := os.MkdirAll(config.OSTree.Repository, 0755); err != nil { + return fmt.Errorf("failed to create OSTree repository directory: %w", err) + } + logger.Printf("Created OSTree repository directory: %s", config.OSTree.Repository) + + // Initialize repository using apt-ostree + ostreeTool := engine.GetOSTreeTool() + if err := ostreeTool.InitRepo(); err != nil { + return fmt.Errorf("failed to initialize OSTree repository: %w", err) + } + logger.Printf("Initialized OSTree repository") + } else { + logger.Printf("OSTree repository already exists: %s", config.OSTree.Repository) + } + + return nil +} + +// validateBuildSystem validates the build system connection +func (p *InitPhase) validateBuildSystem(engine types.Engine) error { + logger := engine.GetLogger() + + buildSystem := engine.GetBuildSystem() + if buildSystem == nil { + logger.Printf("No build system configured, skipping validation") + return nil + } + + // Test connection to build system + if err := buildSystem.TestConnection(); err != nil { + return fmt.Errorf("build system connection test failed: %w", err) + } + + logger.Printf("Build system connection validated successfully") + return nil +} diff --git a/internal/phases/ostree.go b/internal/phases/ostree.go new file mode 100644 index 0000000..e9a3c08 --- /dev/null +++ b/internal/phases/ostree.go @@ -0,0 +1,203 @@ +package phases + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "deb-bootc-compose/internal/types" +) + +// OSTreePhase handles OSTree commit creation and repository management +type OSTreePhase struct { + PhaseBase +} + +// NewOSTreePhase creates a new OSTreePhase +func NewOSTreePhase() *OSTreePhase { + return &OSTreePhase{ + PhaseBase: NewPhaseBase("ostree", []string{"build"}), + } +} + +// Run executes the OSTree phase +func (p *OSTreePhase) Run(engine types.Engine) error { + logger := engine.GetLogger() + logger.Printf("Starting OSTree phase...") + + // Get package information from treefile + treefile := engine.GetTreefile() + + // Process each variant and architecture + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + logger.Printf("Processing variant %s for architecture %s", variant.Name, arch) + + // Get packages for this variant/architecture combination + packages := treefile.GetPackagesForVariant(variant.Name, arch) + + // Create OSTree commit using apt-ostree + ref := treefile.GetOSTreeRef(variant.Name, arch) + subject := fmt.Sprintf("Debian %s %s variant for %s", treefile.Release, variant.Name, arch) + body := fmt.Sprintf("Debian %s %s variant for %s architecture\nGenerated by deb-bootc-compose", treefile.Release, variant.Name, arch) + + // Collect all package names + allPackages := make([]string, 0) + allPackages = append(allPackages, packages.Required...) + allPackages = append(allPackages, packages.Optional...) + allPackages = append(allPackages, packages.Recommended...) + allPackages = append(allPackages, packages.Development...) + allPackages = append(allPackages, packages.Kernel...) + allPackages = append(allPackages, packages.Bootloader...) + + commitID, err := engine.GetOSTreeTool().CreateCommit(ref, subject, body, allPackages) + if err != nil { + return fmt.Errorf("failed to create OSTree commit for variant %s/%s: %w", variant.Name, arch, err) + } + + // Create variant-specific work directory + variantWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch) + if err := os.MkdirAll(variantWorkDir, 0755); err != nil { + return fmt.Errorf("failed to create variant work directory: %w", err) + } + + // Write commit information + if err := p.writeCommitInfo(variantWorkDir, ref, commitID, subject, body, allPackages); err != nil { + return fmt.Errorf("failed to write commit info for variant %s/%s: %w", variant.Name, arch, err) + } + + logger.Printf("Completed OSTree commit for variant %s/%s: %s", variant.Name, arch, commitID) + } + } + + logger.Printf("OSTree phase completed successfully") + return nil +} + +// createOSTreeCommit creates an OSTree commit for a specific variant and architecture +func (p *OSTreePhase) createOSTreeCommit(engine types.Engine, workDir string, variant types.Variant, arch string) error { + logger := engine.GetLogger() + ostreeTool := engine.GetOSTreeTool() + + // Get packages for this variant + packages := engine.GetTreefile().GetPackagesForVariant(variant.Name, arch) + + // Generate OSTree reference + ref := engine.GetTreefile().GetOSTreeRef(variant.Name, arch) + + // Generate commit subject and body + subject := fmt.Sprintf("Debian %s %s variant for %s", + engine.GetTreefile().Release, + variant.Name, + arch) + + body := fmt.Sprintf(`Debian %s %s variant for %s architecture +Generated by deb-bootc-compose +Timestamp: %s +Variant: %s +Architecture: %s + +Packages included: +- Required: %d +- Optional: %d +- Recommended: %d +- Development: %d +- Kernel: %d +- Bootloader: %d`, + engine.GetTreefile().Release, + variant.Name, + arch, + time.Now().Format(time.RFC3339), + variant.Name, + arch, + len(packages.Required), + len(packages.Optional), + len(packages.Recommended), + len(packages.Development), + len(packages.Kernel), + len(packages.Bootloader)) + + // Collect all package names + allPackages := make([]string, 0) + allPackages = append(allPackages, packages.Required...) + allPackages = append(allPackages, packages.Optional...) + allPackages = append(allPackages, packages.Recommended...) + allPackages = append(allPackages, packages.Development...) + allPackages = append(allPackages, packages.Kernel...) + allPackages = append(allPackages, packages.Bootloader...) + + // Remove duplicates + uniquePackages := make(map[string]bool) + for _, pkg := range allPackages { + uniquePackages[pkg] = true + } + + uniqueList := make([]string, 0, len(uniquePackages)) + for pkg := range uniquePackages { + uniqueList = append(uniqueList, pkg) + } + + // Create OSTree commit + logger.Printf("Creating OSTree commit for reference: %s", ref) + commitID, err := ostreeTool.CreateCommit(ref, subject, body, uniqueList) + if err != nil { + return fmt.Errorf("failed to create OSTree commit: %w", err) + } + + // Update the reference to point to the new commit + if err := ostreeTool.UpdateRef(ref, commitID); err != nil { + return fmt.Errorf("failed to update OSTree reference: %w", err) + } + + // Validate the commit + if err := ostreeTool.ValidateCommit(commitID); err != nil { + return fmt.Errorf("failed to validate OSTree commit: %w", err) + } + + // Write commit information + if err := p.writeCommitInfo(workDir, ref, commitID, subject, body, uniqueList); err != nil { + return fmt.Errorf("failed to write commit information: %w", err) + } + + logger.Printf("Successfully created OSTree commit %s for reference %s", commitID, ref) + return nil +} + +// writeCommitInfo writes commit information to files +func (p *OSTreePhase) writeCommitInfo(workDir string, ref, commitID, subject, body string, packages []string) error { + // Write commit details + commitFile := filepath.Join(workDir, "commit-info.txt") + content := fmt.Sprintf(`OSTree Commit Information +Reference: %s +Commit ID: %s +Subject: %s +Timestamp: %s + +Body: +%s + +Packages (%d): +`, ref, commitID, subject, time.Now().Format(time.RFC3339), body, len(packages)) + + for _, pkg := range packages { + content += fmt.Sprintf(" - %s\n", pkg) + } + + if err := os.WriteFile(commitFile, []byte(content), 0644); err != nil { + return fmt.Errorf("failed to write commit info: %w", err) + } + + // Write package list + packagesFile := filepath.Join(workDir, "packages.txt") + pkgContent := "" + for _, pkg := range packages { + pkgContent += pkg + "\n" + } + + if err := os.WriteFile(packagesFile, []byte(pkgContent), 0644); err != nil { + return fmt.Errorf("failed to write packages list: %w", err) + } + + return nil +} diff --git a/internal/phases/output.go b/internal/phases/output.go new file mode 100644 index 0000000..807be8e --- /dev/null +++ b/internal/phases/output.go @@ -0,0 +1,169 @@ +package phases + +import ( + "fmt" + "os" + "path/filepath" + "time" + + "deb-bootc-compose/internal/types" +) + +// OutputPhase handles generating final output artifacts +type OutputPhase struct { + PhaseBase +} + +// NewOutputPhase creates a new OutputPhase +func NewOutputPhase() *OutputPhase { + return &OutputPhase{ + PhaseBase: NewPhaseBase("output", []string{"ostree"}), + } +} + +// Run executes the output phase +func (p *OutputPhase) Run(engine types.Engine) error { + logger := engine.GetLogger() + logger.Printf("Starting output generation phase") + + // Get output manager + outputManager := engine.GetOutputManager() + if outputManager == nil { + logger.Printf("No output manager configured, skipping output phase") + return nil + } + + // Get treefile and process variants + treefile := engine.GetTreefile() + + for _, variant := range treefile.Variants { + for _, arch := range variant.Architectures { + logger.Printf("Generating output for variant %s on architecture %s", variant.Name, arch) + + // Create output work directory + outputWorkDir := filepath.Join(engine.GetWorkDir(), "variants", variant.Name, arch, "output") + if err := os.MkdirAll(outputWorkDir, 0755); err != nil { + return fmt.Errorf("failed to create output work directory: %w", err) + } + + // Generate outputs for this variant/architecture + if err := p.generateOutputs(engine, outputWorkDir, variant, arch); err != nil { + return fmt.Errorf("failed to generate outputs for variant %s on architecture %s: %w", variant.Name, arch, err) + } + + logger.Printf("Completed output generation for variant %s on architecture %s", variant.Name, arch) + } + } + + logger.Printf("Output generation phase completed successfully") + return nil +} + +// generateOutputs generates all configured output formats +func (p *OutputPhase) generateOutputs(engine types.Engine, workDir string, variant types.Variant, arch string) error { + logger := engine.GetLogger() + outputManager := engine.GetOutputManager() + + // Get OSTree reference + ref := engine.GetTreefile().GetOSTreeRef(variant.Name, arch) + + // Get output configuration + outputConfig := engine.GetTreefile().Output + + // Generate container image if requested + if p.containsFormat(outputConfig.Formats, "container") { + logger.Printf("Generating container image for variant %s on architecture %s", variant.Name, arch) + + containerConfig := map[string]interface{}{ + "base_image": outputConfig.Container.BaseImage, + "labels": outputConfig.Container.Labels, + "entrypoint": outputConfig.Container.Entrypoint, + "cmd": outputConfig.Container.CMD, + } + + containerPath := filepath.Join(workDir, "container") + if err := outputManager.GenerateContainerImage(ref, containerPath, containerConfig); err != nil { + logger.Printf("Warning: failed to generate container image: %v", err) + } else { + logger.Printf("Successfully generated container image") + } + } + + // Generate disk image if requested + if p.containsFormat(outputConfig.Formats, "disk") { + logger.Printf("Generating disk image for variant %s on architecture %s", variant.Name, arch) + + diskConfig := map[string]interface{}{ + "size": outputConfig.DiskImage.Size, + "formats": outputConfig.DiskImage.Formats, + "bootloader": outputConfig.DiskImage.Bootloader, + "kernel": outputConfig.DiskImage.Kernel, + "initramfs": outputConfig.DiskImage.Initramfs, + } + + diskPath := filepath.Join(workDir, "disk") + if err := outputManager.GenerateDiskImage(ref, diskPath, diskConfig); err != nil { + logger.Printf("Warning: failed to generate disk image: %v", err) + } else { + logger.Printf("Successfully generated disk image") + } + } + + // Generate tarball if requested + if p.containsFormat(outputConfig.Formats, "tarball") { + logger.Printf("Generating tarball for variant %s on architecture %s", variant.Name, arch) + + tarballConfig := map[string]interface{}{ + "compression": "gzip", + "format": "tar.gz", + } + + tarballPath := filepath.Join(workDir, "tarball") + if err := outputManager.GenerateTarball(ref, tarballPath, tarballConfig); err != nil { + logger.Printf("Warning: failed to generate tarball: %v", err) + } else { + logger.Printf("Successfully generated tarball") + } + } + + // Write output summary + if err := p.writeOutputSummary(workDir, variant, arch, outputConfig.Formats); err != nil { + return fmt.Errorf("failed to write output summary: %w", err) + } + + return nil +} + +// containsFormat checks if a format is in the list +func (p *OutputPhase) containsFormat(formats []string, format string) bool { + for _, f := range formats { + if f == format { + return true + } + } + return false +} + +// writeOutputSummary writes a summary of generated outputs +func (p *OutputPhase) writeOutputSummary(workDir string, variant types.Variant, arch string, formats []string) error { + summaryFile := filepath.Join(workDir, "output-summary.txt") + + summary := fmt.Sprintf(`Output Generation Summary +Variant: %s +Architecture: %s +Timestamp: %s +Formats: %v + +Generated outputs: +`, variant.Name, arch, time.Now().Format(time.RFC3339), formats) + + for _, format := range formats { + summary += fmt.Sprintf(" - %s\n", format) + } + + if err := os.WriteFile(summaryFile, []byte(summary), 0644); err != nil { + return fmt.Errorf("failed to write output summary: %w", err) + } + + return nil +} diff --git a/internal/phases/phase.go b/internal/phases/phase.go new file mode 100644 index 0000000..c09e7a7 --- /dev/null +++ b/internal/phases/phase.go @@ -0,0 +1,49 @@ +package phases + +import ( + "deb-bootc-compose/internal/types" +) + +// Phase represents a single phase in the compose process +type Phase interface { + // Name returns the name of the phase + Name() string + + // Run executes the phase + Run(engine types.Engine) error + + // Skip returns true if the phase should be skipped + Skip(engine types.Engine) bool + + // Dependencies returns the names of phases this phase depends on + Dependencies() []string +} + +// PhaseBase provides common functionality for all phases +type PhaseBase struct { + name string + dependencies []string +} + +// Name returns the phase name +func (p *PhaseBase) Name() string { + return p.name +} + +// Dependencies returns the phase dependencies +func (p *PhaseBase) Dependencies() []string { + return p.dependencies +} + +// Skip provides a default implementation that never skips +func (p *PhaseBase) Skip(engine types.Engine) bool { + return false +} + +// NewPhaseBase creates a new phase base +func NewPhaseBase(name string, dependencies []string) PhaseBase { + return PhaseBase{ + name: name, + dependencies: dependencies, + } +} diff --git a/internal/treefile/treefile.go b/internal/treefile/treefile.go new file mode 100644 index 0000000..eb7dcfd --- /dev/null +++ b/internal/treefile/treefile.go @@ -0,0 +1,258 @@ +package treefile + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" +) + +// Treefile represents a Debian treefile manifest (equivalent to Pungi's compose definitions) +// Supports both legacy JSON format and apt-ostree v1 YAML format +type Treefile struct { + // Legacy JSON format fields + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` + Description string `json:"description" yaml:"description"` + Release string `json:"release" yaml:"release"` + + // Package configuration + Packages PackageSet `json:"packages" yaml:"packages"` + Exclude []string `json:"exclude" yaml:"exclude"` + + // Repository configuration + Repositories []Repository `json:"repositories" yaml:"repositories"` + + // Architecture and variant configuration + Architecture []string `json:"architecture" yaml:"architecture"` + Variants []Variant `json:"variants" yaml:"variants"` + + // Build configuration + Build struct { + Type string `json:"type" yaml:"type"` + Environment map[string]string `json:"environment" yaml:"environment"` + Options map[string]interface{} `json:"options" yaml:"options"` + } `json:"build" yaml:"build"` + + // OSTree configuration + OSTree struct { + Ref string `json:"ref" yaml:"ref"` + Parent string `json:"parent" yaml:"parent"` + Subject string `json:"subject" yaml:"subject"` + Body string `json:"body" yaml:"body"` + Timestamp string `json:"timestamp" yaml:"timestamp"` + } `json:"ostree" yaml:"ostree"` + + // Output configuration + Output struct { + Formats []string `json:"formats" yaml:"formats"` + Container struct { + BaseImage string `json:"base_image" yaml:"base_image"` + Labels map[string]string `json:"labels" yaml:"labels"` + Entrypoint []string `json:"entrypoint" yaml:"entrypoint"` + CMD []string `json:"cmd" yaml:"cmd"` + } `json:"container" yaml:"container"` + DiskImage struct { + Size string `json:"size" yaml:"size"` + Formats []string `json:"formats" yaml:"formats"` + Bootloader string `json:"bootloader" yaml:"bootloader"` + Kernel string `json:"kernel" yaml:"kernel"` + Initramfs bool `json:"initramfs" yaml:"initramfs"` + } `json:"disk_image" yaml:"disk_image"` + } `json:"output" yaml:"output"` + + // Metadata + Metadata map[string]interface{} `json:"metadata" yaml:"metadata"` + + // apt-ostree v1 format fields + APIVersion string `json:"apiVersion" yaml:"apiVersion"` + Kind string `json:"kind" yaml:"kind"` + Spec struct { + Base struct { + Distribution string `json:"distribution" yaml:"distribution"` + Architecture string `json:"architecture" yaml:"architecture"` + Mirror string `json:"mirror" yaml:"mirror"` + } `json:"base" yaml:"base"` + Packages struct { + Include []string `json:"include" yaml:"include"` + Exclude []string `json:"exclude" yaml:"exclude"` + } `json:"packages" yaml:"packages"` + Customizations map[string]interface{} `json:"customizations" yaml:"customizations"` + OSTree struct { + Ref string `json:"ref" yaml:"ref"` + CommitMessage string `json:"commit_message" yaml:"commit_message"` + Metadata map[string]interface{} `json:"metadata" yaml:"metadata"` + } `json:"ostree" yaml:"ostree"` + Build map[string]interface{} `json:"build" yaml:"build"` + } `json:"spec" yaml:"spec"` +} + +// PackageSet defines the packages to include in the image +type PackageSet struct { + Required []string `json:"required"` // Required packages + Optional []string `json:"optional"` // Optional packages + Recommended []string `json:"recommended"` // Recommended packages + Development []string `json:"development"` // Development packages + Kernel []string `json:"kernel"` // Kernel packages + Bootloader []string `json:"bootloader"` // Bootloader packages +} + +// Repository represents a Debian package repository +type Repository struct { + Name string `json:"name"` + URL string `json:"url"` + Suite string `json:"suite"` // e.g., "bookworm" + Component string `json:"component"` // e.g., "main" + Arch string `json:"arch"` // e.g., "amd64" + Enabled bool `json:"enabled"` +} + +// Variant represents a Debian variant +type Variant struct { + Name string `json:"name"` + Description string `json:"description"` + Architectures []string `json:"architectures"` + Packages PackageSet `json:"packages"` + Exclude []string `json:"exclude"` + Config map[string]interface{} `json:"config"` +} + +// Load loads a treefile from a JSON or YAML file +func Load(filename string) (*Treefile, error) { + data, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read treefile: %w", err) + } + + // Detect file format based on extension and content + ext := strings.ToLower(filepath.Ext(filename)) + + var treefile Treefile + + if ext == ".yaml" || ext == ".yml" || strings.HasPrefix(string(data), "apiVersion:") { + // Parse as YAML (apt-ostree v1 format) + if err := yaml.Unmarshal(data, &treefile); err != nil { + return nil, fmt.Errorf("failed to parse treefile YAML: %w", err) + } + } else { + // Parse as JSON (legacy format) + if err := json.Unmarshal(data, &treefile); err != nil { + return nil, fmt.Errorf("failed to parse treefile JSON: %w", err) + } + } + + // Validate the treefile + if err := validateTreefile(&treefile); err != nil { + return nil, fmt.Errorf("invalid treefile: %w", err) + } + + return &treefile, nil +} + +// Save saves a treefile to a JSON file +func (t *Treefile) Save(filename string) error { + data, err := json.MarshalIndent(t, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal treefile: %w", err) + } + + if err := os.WriteFile(filename, data, 0644); err != nil { + return fmt.Errorf("failed to write treefile: %w", err) + } + + return nil +} + +// validateTreefile validates a treefile +func validateTreefile(t *Treefile) error { + // Check if this is apt-ostree v1 format + if t.APIVersion == "v1" && t.Kind == "Treefile" { + // Validate apt-ostree v1 format + if t.Spec.Base.Distribution == "" { + return fmt.Errorf("spec.base.distribution is required") + } + if t.Spec.Base.Architecture == "" { + return fmt.Errorf("spec.base.architecture is required") + } + if len(t.Spec.Packages.Include) == 0 { + return fmt.Errorf("at least one package must be included") + } + if t.Spec.OSTree.Ref == "" { + return fmt.Errorf("spec.ostree.ref is required") + } + return nil + } + + // Validate legacy JSON format + if t.Name == "" { + return fmt.Errorf("name is required") + } + if t.Version == "" { + return fmt.Errorf("version is required") + } + if len(t.Variants) == 0 { + return fmt.Errorf("at least one variant must be specified") + } + + return nil +} + +// GetPackagesForVariant returns packages for a specific variant and architecture +func (t *Treefile) GetPackagesForVariant(variant string, arch string) []string { + // Check if this is apt-ostree v1 format + if t.APIVersion == "v1" && t.Kind == "Treefile" { + // For apt-ostree v1, return all included packages + return t.Spec.Packages.Include + } + + // Legacy JSON format + for _, v := range t.Variants { + if v.Name == variant { + // Check if architecture is supported + for _, variantArch := range v.Architectures { + if variantArch == arch { + var packages []string + packages = append(packages, v.Packages.Required...) + packages = append(packages, v.Packages.Optional...) + packages = append(packages, v.Packages.Recommended...) + packages = append(packages, v.Packages.Development...) + packages = append(packages, v.Packages.Kernel...) + packages = append(packages, v.Packages.Bootloader...) + return packages + } + } + } + } + return []string{} +} + +// GetExcludedPackagesForVariant returns excluded packages for a specific variant and architecture +func (t *Treefile) GetExcludedPackagesForVariant(variantName, arch string) []string { + var result []string + + // Add global excludes + result = append(result, t.Exclude...) + + // Find the variant and add its excludes + for _, v := range t.Variants { + if v.Name == variantName { + result = append(result, v.Exclude...) + break + } + } + + return result +} + +// GetOSTreeRef returns the OSTree reference for a variant and architecture +func (t *Treefile) GetOSTreeRef(variantName, arch string) string { + if t.OSTree.Ref != "" { + return t.OSTree.Ref + } + + // Generate default reference + return fmt.Sprintf("%s/%s/%s/%s", t.Release, t.Version, arch, variantName) +} diff --git a/internal/types/types.go b/internal/types/types.go new file mode 100644 index 0000000..20c4b41 --- /dev/null +++ b/internal/types/types.go @@ -0,0 +1,331 @@ +package types + +import ( + "fmt" + "log" + "time" +) + +// Engine represents the interface that phases need to interact with the compose engine +type Engine interface { + // Configuration + GetConfig() *Config + GetTreefile() *Treefile + GetOutputDir() string + GetWorkDir() string + + // Logging + GetLogger() *log.Logger + + // Systems + GetBuildSystem() BuildSystem + GetOSTreeTool() OSTreeTool + GetOutputManager() OutputManager +} + +// Config represents the compose configuration +type Config struct { + // Release information + Release struct { + Name string `yaml:"name"` + Short string `yaml:"short"` + Version string `yaml:"version"` + Type string `yaml:"type"` + Internal bool `yaml:"internal"` + } `yaml:"release"` + + // Build system configuration + BuildSystem struct { + Type string `yaml:"type"` + Host string `yaml:"host"` + Port int `yaml:"port"` + AuthToken string `yaml:"auth_token"` + MaxWorkers int `yaml:"max_workers"` + } `yaml:"build_system"` + + // OSTree configuration + OSTree struct { + Repository string `yaml:"repository"` + Mode string `yaml:"mode"` + Signing struct { + Enabled bool `yaml:"enabled"` + Key string `yaml:"key"` + } `yaml:"signing"` + } `yaml:"ostree"` + + // Output configuration + Output struct { + Formats []string `yaml:"formats"` + DiskImage struct { + Formats []string `yaml:"formats"` + Size string `yaml:"size"` + } `yaml:"disk_image"` + Container struct { + Registry string `yaml:"registry"` + Tag string `yaml:"tag"` + } `yaml:"container"` + } `yaml:"output"` + + // Repository configuration + Repositories []Repository `yaml:"repositories"` + + // Variants configuration + Variants []Variant `yaml:"variants"` + + // Architecture configuration + Architectures []string `yaml:"architectures"` + + // Logging configuration + Logging struct { + Level string `yaml:"level"` + File string `yaml:"file"` + Format string `yaml:"format"` + } `yaml:"logging"` + + // Cache configuration + Cache struct { + Enabled bool `yaml:"enabled"` + Dir string `yaml:"dir"` + Size string `yaml:"size"` + } `yaml:"cache"` +} + +// Repository represents a Debian package repository +type Repository struct { + Name string `yaml:"name"` + URL string `yaml:"url"` + Suite string `yaml:"suite"` + Component string `yaml:"component"` + Arch string `yaml:"arch"` + Enabled bool `yaml:"enabled"` +} + +// Variant represents a Debian variant +type Variant struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Architectures []string `yaml:"architectures"` + Packages VariantPackages `yaml:"packages"` + Exclude []string `yaml:"exclude"` + Config map[string]interface{} `yaml:"config"` +} + +// VariantPackages defines package sets for a variant +type VariantPackages struct { + Required []string `yaml:"required"` + Optional []string `yaml:"optional"` + Recommended []string `yaml:"recommended"` + Development []string `yaml:"development"` + Kernel []string `yaml:"kernel"` + Bootloader []string `yaml:"bootloader"` +} + +// Treefile represents a Debian treefile manifest +type Treefile struct { + // Basic information + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` + Release string `json:"release"` + + // Package configuration + Packages PackageSet `json:"packages"` + Exclude []string `json:"exclude"` + + // Repository configuration + Repositories []Repository `json:"repositories"` + + // Architecture and variant configuration + Architecture []string `json:"architecture"` + Variants []Variant `json:"variants"` + + // Build configuration + Build struct { + Type string `json:"type"` + Environment map[string]string `json:"environment"` + Options map[string]interface{} `json:"options"` + } `json:"build"` + + // OSTree configuration + OSTree struct { + Ref string `json:"ref"` + Parent string `json:"parent"` + Subject string `json:"subject"` + Body string `json:"body"` + Timestamp string `json:"timestamp"` + } `json:"ostree"` + + // Output configuration + Output struct { + Formats []string `json:"formats"` + Container struct { + BaseImage string `json:"base_image"` + Labels map[string]string `json:"labels"` + Entrypoint []string `json:"entrypoint"` + CMD []string `json:"cmd"` + } `json:"container"` + DiskImage struct { + Size string `json:"size"` + Formats []string `json:"formats"` + Bootloader string `json:"bootloader"` + Kernel string `json:"kernel"` + Initramfs bool `json:"initramfs"` + } `json:"disk_image"` + } `json:"output"` + + // Metadata + Metadata map[string]interface{} `json:"metadata"` +} + +// PackageSet defines the packages to include in the image +type PackageSet struct { + Required []string `json:"required"` + Optional []string `json:"optional"` + Recommended []string `json:"recommended"` + Development []string `json:"development"` + Kernel []string `json:"kernel"` + Bootloader []string `json:"bootloader"` +} + +// GetPackagesForVariant returns all packages for a specific variant and architecture +func (t *Treefile) GetPackagesForVariant(variantName, arch string) PackageSet { + var result PackageSet + + // Find the variant + var variant *Variant + for _, v := range t.Variants { + if v.Name == variantName { + variant = &v + break + } + } + + if variant == nil { + return result + } + + // Check if architecture is supported by this variant + archSupported := false + for _, variantArch := range variant.Architectures { + if variantArch == arch { + archSupported = true + break + } + } + + if !archSupported { + return result + } + + // Merge global packages with variant packages + result.Required = append(t.Packages.Required, variant.Packages.Required...) + result.Optional = append(t.Packages.Optional, variant.Packages.Optional...) + result.Recommended = append(t.Packages.Recommended, variant.Packages.Recommended...) + result.Development = append(t.Packages.Development, variant.Packages.Development...) + result.Kernel = append(t.Packages.Kernel, variant.Packages.Kernel...) + result.Bootloader = append(t.Packages.Bootloader, variant.Packages.Bootloader...) + + return result +} + +// GetExcludedPackagesForVariant returns excluded packages for a specific variant and architecture +func (t *Treefile) GetExcludedPackagesForVariant(variantName, arch string) []string { + var result []string + + // Add global excludes + result = append(result, t.Exclude...) + + // Find the variant and add its excludes + for _, v := range t.Variants { + if v.Name == variantName { + result = append(result, v.Exclude...) + break + } + } + + return result +} + +// GetOSTreeRef returns the OSTree reference for a variant and architecture +func (t *Treefile) GetOSTreeRef(variantName, arch string) string { + if t.OSTree.Ref != "" { + return t.OSTree.Ref + } + + // Generate default reference + return fmt.Sprintf("%s/%s/%s/%s", t.Release, t.Version, arch, variantName) +} + +// BuildSystem represents the interface to the build system +type BuildSystem interface { + TestConnection() error + CreateBuildEnvironment(arch string) (*BuildEnvironment, error) + InstallDependencies(packages []string, arch string) error + BuildPackage(packageName, arch string) (*BuildResult, error) + WaitForBuild(buildID string, timeout time.Duration) (*BuildResult, error) + GetBuildStatus(buildID string) (*BuildStatus, error) + CleanupBuildEnvironment(env *BuildEnvironment) error +} + +// BuildEnvironment represents a build environment +type BuildEnvironment struct { + ID string `json:"id"` + Arch string `json:"arch"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` + Metadata map[string]string `json:"metadata"` + Environment map[string]string `json:"environment"` +} + +// BuildResult represents the result of a build +type BuildResult struct { + ID string `json:"id"` + PackageName string `json:"package_name"` + Arch string `json:"arch"` + Status string `json:"status"` + Result string `json:"result"` + StartedAt time.Time `json:"started_at"` + CompletedAt time.Time `json:"completed_at"` + Logs []string `json:"logs"` + Artifacts []string `json:"artifacts"` + Error string `json:"error,omitempty"` +} + +// BuildStatus represents the status of a build +type BuildStatus struct { + ID string `json:"id"` + Status string `json:"status"` + Progress int `json:"progress"` + UpdatedAt time.Time `json:"updated_at"` +} + +// OSTreeTool represents the interface to the OSTree tool +type OSTreeTool interface { + InitRepo() error + CreateCommit(ref, subject, body string, packages []string) (string, error) + GetCommit(commitID string) (*CommitInfo, error) + ListRefs() ([]string, error) + UpdateRef(ref, commitID string) error + CreateArchive(ref, outputPath string) error + ValidateCommit(commitID string) error +} + +// CommitInfo represents information about an OSTree commit +type CommitInfo struct { + ID string `json:"id"` + Ref string `json:"ref"` + Subject string `json:"subject"` + Body string `json:"body"` + Timestamp time.Time `json:"timestamp"` + Parent string `json:"parent,omitempty"` + Metadata map[string]interface{} `json:"metadata"` +} + +// OutputManager represents the interface for managing output artifacts +type OutputManager interface { + GenerateContainerImage(commitID, outputPath string, config map[string]interface{}) error + GenerateDiskImage(commitID, outputPath string, config map[string]interface{}) error + GenerateTarball(commitID, outputPath string, config map[string]interface{}) error + ValidateOutput(outputPath string, config map[string]interface{}) error + CleanupOutput(outputPath string) error +}