CI Migration: Backport pkg/build (daggerbuild) and GHAs to release-12.0.3 (#107997)
* CI: move grafana-build into pkg/build (#105640) * move grafana-build into pkg/build * Update go.mod/go.sum files after cherry-pick build - Updates from workspace sync after cherry-picking daggerbuild package - Resolves dependency conflicts from pkg/build integration - Required for successful build after CI migration backport * CI Migration: Replace .github with main branch version - Complete replacement of .github directory from main branch - Includes updated workflows, actions, and configuration files - Ensures all CI improvements from main are included - Next step: fix branch-specific issues in separate commits * Fix branch triggers: Update workflows to target release branches - Update 8 workflows to trigger on release-* instead of main - Ensures workflows run on pull requests to release branches - Workflows updated: documentation-ci, pr-codeql-analysis-*, pr-dependabot-update-go-workspace, pr-go-workspace-check, pr-k8s-codegen-check, verify-kinds, codeowners-validator * Fix branch triggers: Update workflows to target both main and release branches - CORRECTED: Include both main and release-* branch patterns - Ensures workflows run on both main branch (original functionality) and release branches (needed for backport) - Previous commit incorrectly removed main branch support - Workflows updated: documentation-ci, pr-codeql-analysis-*, pr-dependabot-update-go-workspace, pr-go-workspace-check, pr-k8s-codegen-check, verify-kinds, codeowners-validator * CI Migration: Update .gitignore to allow OSS wire file to be committed - Remove **/wire_gen.go (allow pkg/server/wire_gen.go to be committed) - Add /pkg/server/enterprise_wire_gen.go (keep enterprise wire file ignored) - This enables the new committed wire files architecture for CI * CI Migration: Add enhanced wire tool with automatic build constraints - Backport enhanced wire tool from main branch with -gen_tags flag - This enables automatic regeneration with proper constraints to prevent conflicts - Wire file contains all required functions: Initialize, InitializeDocumentBuilders, etc. - Solves the missing wire functions issue causing CI failures - Enhanced tool ensures build constraints persist on regeneration * CI Migration: Update Makefile to use enhanced wire tool with build constraints - Update gen-go target to use -gen_tags flag for automatic build constraints - Ensures future engineers get proper build constraints when running 'make gen-go' - Matches main branch approach for consistent wire file generation - Removes dependency on WIRE_TAGS variable and GO_RACE_FLAG for simplicity * Add gen-enterprise-go target to Makefile for release-12.0.3 - Backports gen-enterprise-go target from main branch - Enables enterprise wire file generation in release branches - Part of CI migration backport wire infrastructure fixes - Resolves CI issues where enterprise wire functions were missing * Fix wire tool golden file format for Drone CI compatibility - Update all 43 golden test files to match main branch format - Change go:generate command from github.com/google/wire/cmd/wire to ./pkg/build/wire/cmd/wire/main.go - Remove legacy '// +build !wireinject' constraints - Fixes Drone CI test failures that were blocking OSS PR merge - All wire tests now pass successfully This aligns the backport branch with the golden file format updates that were made in main branch when the wire tool was enhanced. * Update swagger specs to include enterprise APIs - Regenerated api-enterprise-spec.json with enterprise API definitions - Updated api-merged.json with merged OSS and enterprise specs - Regenerated openapi3.json with complete API specifications - Fixes enterprise CI swagger generation validation failures - Enterprise APIs now properly detected and documented * Fix OSS integration tests missing enterprise build tags in Drone CI - Add -tags=enterprise to all integration test steps in lib.star - Fixes test-backend-integration, postgres-integration-tests, and mysql-integration-tests - Regenerate .drone.yml with proper enterprise build tags - Resolves 'server could not find the requested resource' errors for enterprise APIs - Ensures enterprise APIs (querylibrary, reporting, banners, scim) are registered during tests - Aligns OSS Drone CI with GitHub Actions behavior and enterprise repository * Fix CODEOWNERS for release-12.0.3 compatibility - Remove 21 entries that reference files/directories not present in release branch - Resolves File Exist Checker failures in codeowners-validator workflow - Maintains team ownership assignments from main branch for existing files - Lines removed: .air.toml, apps/secret/, apps/iam/, e2e-playwright/, packages/grafana-alerting/, etc. * baldm0mma/ update releasefinder * Add i18n-extract script for release branch compatibility - Add 'i18n-extract': 'make i18n-extract' to package.json scripts - Resolves i18n-verify workflow failure that expects this script - The make target already exists and handles OSS + enterprise i18n extraction - Maintains i18n verification functionality from main branch workflows - Simple safe addition that calls existing make infrastructure * Phase 4: Fix GitHub Actions workflow branch triggers - Add release-*.*.* pattern to 8 workflows missing it - Fix duplicate release branch patterns in 3 workflows - Ensure consistent branch trigger format across all workflows - Critical workflows now properly trigger on release branches Fixes workflows: actionlint, backend-code-checks, go-lint, reject-gh-secrets, run-schema-v2-e2e, shellcheck, swagger-gen, trivy-scan * Fix: Backport E2E runner infrastructure for Enterprise CI - Add e2e/main.go and e2e/internal/ directory from main branch - Add urfave/cli/v3 dependency required by E2E runner - Fixes Enterprise CI failure: 'Build E2E test runner' and 'Build & package Grafana for e2e tests' - Root cause: E2E runner infrastructure was added to main after release-12.0.3 branch creation - Solution: Backport missing E2E runner files to enable Enterprise CI completion Resolves: no Go files in /opt/actions-runner/_work/grafana-enterprise/grafana-enterprise/grafana/e2e * Fix: Update go.mod dependency classification for urfave/cli/v3 - Change urfave/cli/v3 from indirect to direct dependency - Fixes Go Workspace Check failure in CI - Required after adding E2E runner infrastructure that directly imports urfave/cli/v3 - Resolves: make update-workspace corrects dependency classification * Fix: Add team ownership for urfave/cli/v3 dependency - Assign @grafana/grafana-backend-group as owner for github.com/urfave/cli/v3@v3.3.8 - Follows existing pattern: urfave/cli v1 and v2 also owned by grafana-backend-group - Resolves Backend Code Checks / Validate Backend Configs CI failure - Required for E2E runner infrastructure dependency ownership compliance Fixes: modowners check requiring team assignment for newly added dependencies * Fix: Revert experimental E2E playwright infrastructure to stable version - Revert pr-e2e-tests.yml to stable Cypress-based E2E testing - Remove experimental storybook-verification-playwright.yml workflow - Revert run-dashboard-search-e2e.yml and release-pr.yml to use e2e/test-plugins/ - Keep stable E2E runner infrastructure (e2e/main.go + e2e/internal/) - Remove experimental playwright features per team recommendation Team feedback: Playwright tests are experimental and shouldn't be backported to stable release branches * Fix: Complete cleanup of experimental playwright dependencies - Revert package.json to stable version (remove e2e-playwright scripts and path references) - Revert playwright.config.ts to stable plugin-e2e configuration - Remove all experimental playwright infrastructure dependencies - Ensure clean stable E2E testing environment All experimental features removed per team recommendation for stable release branches * Fix: Restore working package.json configuration - Revert package.json to version 12.0.3 (working release branch version) - Fix workspace dependency resolution issues caused by incorrect revert to main branch version - FE tests, betterer, and linting should now work correctly Issue was caused by reverting package.json to main branch (12.1.0-pre) instead of keeping the working release branch configuration (12.0.3) * CI: mirror some CI dependencies (#106148) * mirror some CI dependencies * remove -v from go build * Fix: Backport missing e2e/run-suite script for daggerbuild E2E system The daggerbuild E2E system (used by OSS workflows) expects ./e2e/run-suite to exist, but we only backported the new E2E runner infrastructure. This script is needed for: - OSS workflow: 'go run ./pkg/build/e2e --suite=dashboards-suite' - Legacy Cypress configuration with video support - Integration between daggerbuild and existing Cypress test suites Resolves E2E test failures in OSS workflows where videos directory cannot be found because run-suite script was missing. * Fix: Resolve daggerbuild E2E path doubling issue - Extract just suite name from full path using filepath.Base() - Prevents doubled paths like './e2e/e2e/dashboards-suite/videos' - Resolves 'no spec files found' and 'no such file or directory' errors - GitHub Actions passes full paths like 'e2e/dashboards-suite' but run-suite script expects just 'dashboards-suite' * Infrastructure: Wholesale copy pkg/build/ from main COMPLETE DAGGERBUILD SYSTEM UPDATE: - Modern E2E system with CLI framework and --flags support - New accessibility testing system (a11y/) - New Playwright E2E testing system (e2e-playwright/) - External infrastructure improvements (musl.cc → dl.grafana.com/ci) - Updated daggerbuild core components with latest fixes - Updated Go dependencies and wire modules ARCHITECTURAL COMPATIBILITY VERIFIED: - Modern pkg/build calls: ./e2e-runner cypress --start-grafana=false --cypress-video - Our e2e runner supports: All required parameters including --suite, --env flags - GitHub Actions workflows: Pass compatible arguments - Binary builds: Successfully creates working e2e-runner RESOLVES ISSUES: - Fixes --flags parameter compatibility with GitHub Actions - Includes all follow-up infrastructure improvements - Provides complete, tested system with modern CLI framework - Eliminates external dependency failures (musl.cc timeouts) - Removes need for path doubling workarounds (modern system handles full paths correctly) Replaces incremental cherry-picking with complete, tested system from main. * Dependencies: Update Go modules after pkg/build wholesale copy DEPENDENCY UPDATES: - Updated go.work.sum with new dependencies from modern pkg/build system - Updated all workspace module dependencies (go.mod/go.sum files) - Removed pkg/build/cmd/enterprise.go (enterprise-only file, gets copied during enterprise development) ENTERPRISE INTEGRATION FIX: - enterprise.go file doesn't belong in OSS repository - Gets copied from grafana-enterprise during development mode - Main branch doesn't have this file, explaining module compatibility All Go modules now properly resolved and compatible with modern pkg/build architecture. * Dependencies: Update workspace Go module checksums after pkg/build wholesale copy - Synchronizes all go.sum files across workspace modules - Adds missing .mod.h1 entries that were required after infrastructure update - Resolves Go Workspace Check CI failures - Updates 22 modules: .citools/, apps/, pkg/ subdirectories plus go.work.sum Addresses CI error: 'Please run make update-workspace and commit the changes' * Infrastructure: Revert to pre-Playwright E2E system for release branch compatibility Strategic combination of modern daggerbuild with stable E2E infrastructure: **Modern Daggerbuild (from main):** - Latest CLI framework (github.com/urfave/cli/v3) - External infrastructure fixes (musl.cc → dl.grafana.com/ci) - Complete pkg/build/ system with all enhancements - Updated dependencies and architecture improvements **Stable E2E Infrastructure (pre-Playwright):** - pkg/build/e2e/service.go: Reverted to version before commitb6580ccb10- pkg/build/a11y/: Reverted to stable version compatible with existing infrastructure - Removed experimental pkg/build/e2e-playwright/ system - Removed experimental e2e-playwright/ test infrastructure **Why This Approach:** - GitHub Actions workflows call 'go run ./pkg/build/e2e' (modern system) - Modern system now expects only standard e2e/ directory (compatible) - Release branches have standard e2e/ infrastructure (dashboards-suite, various-suite, etc.) - Eliminates dependency on experimental Playwright infrastructure **Key Dependencies Met:** - e2e/pa11yci.conf.js: Added for a11y test compatibility - /src/e2e directory: Standard directory structure (no e2e-playwright needed) - Existing test plugins: Uses stable e2e/test-plugins/ **Result:** Best of both worlds - modern, reliable daggerbuild infrastructure with proven E2E testing that works across all release branches without requiring experimental dependencies. * Fix: Remove unused embed.go file causing golangci-lint failure - Removes pkg/build/daggerbuild/msi/embed.go with unused embed.FS variable - MSI build process uses directory mounting instead of embedded filesystem - Resolves 'var resources is unused (unused)' linting error * Fix: Update Node.js version to match working E2E configuration - Changes from v22.11.0 to v22.16.0 to match commit17952d45e4- Working commit had all E2E tests passing with same daggerbuild infrastructure - Node.js version differences can affect frontend builds and UI rendering - Ensures consistent environment between working reference and current branch * Fix: Use conditional container base for E2E compatibility - Alpine for OSS tests (better old arch dashboardScene=false compatibility) - Ubuntu for Enterprise tests (when image renderer needed) - Restores original release-12.0.3 Alpine behavior for OSS - Should resolve old arch E2E test failures * Fix: Go fmt formatting for conditional container logic * Revert: Remove unnecessary Alpine/Ubuntu conditional logic - Real issue was wrong environment variable name (dashboardScene vs DISABLE_SCENES) - Keep Ubuntu base (original main branch behavior) for simplicity - Container base was not the root cause of old arch failures * Experiment: Copy scripts/ from pre-Playwright commit (ea0ddb3fc9) - Copied scripts directory from commitea0ddb3fc9(just before Playwright migration) - This overwrites some CI migration fixes but testing if it's actually needed - Can revert back tob9b4602dbdif CI breaks Test commit to see real impact on GitHub Actions workflows. * trigger ci 01 * Fix: Copy .drone.star from main to match scripts structure - Resolves Starlark evaluation error for missing integration_test_pipelines - Aligns .drone.star with main branch scripts structure - Same fix applied to both OSS and Enterprise repositories * make drone * Fix ARM/v7 Docker build failures by using dagger run for cross-compilation - Change artifacts_cmd() in scripts/drone/steps/rgm.star to use 'dagger run go run' instead of 'go run' - Add --max-execution-steps 100000 to make drone target in Makefile to properly regenerate .drone.yml - This fixes CGO cross-compilation issues with SQLite for ARM/v7 Docker builds - Matches the approach already used successfully in Enterprise builds * Fix Docker base image references in rgm build steps - Change from symbolic names (alpine-base, ubuntu-base) to actual Docker images - alpine-base → alpine:3.21.3 - ubuntu-base → ubuntu:22.04 - Add ubuntu and alpine parameters to rgm_artifacts_step and rgm_build_docker_step functions - Fixes Docker pull errors: 'repository does not exist or may require authorization' - Aligns OSS configuration with Enterprise approach --------- Co-authored-by: Kevin Minehart <5140827+kminehart@users.noreply.github.com>
This commit is contained in:
co-authored by
Kevin Minehart
parent
aee6b20c7f
commit
dc6bd2a20a
+19
-19
@@ -9,7 +9,7 @@ require (
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel v1.36.0
|
||||
k8s.io/api v0.32.3
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/apiserver v0.32.3
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
|
||||
@@ -126,33 +126,33 @@ require (
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
|
||||
+40
-40
@@ -25,8 +25,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -358,30 +358,30 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q=
|
||||
go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -397,8 +397,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
@@ -408,8 +408,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -419,18 +419,18 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -451,8 +451,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -466,8 +466,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
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=
|
||||
@@ -481,20 +481,20 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -33,16 +33,16 @@ require (
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
+22
-22
@@ -74,23 +74,23 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@@ -103,15 +103,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -135,8 +135,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -147,10 +147,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
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/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
+16
-16
@@ -5,7 +5,7 @@ go 1.24.4
|
||||
require (
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250314071911-14e2784e6979
|
||||
github.com/stretchr/testify v1.10.0
|
||||
google.golang.org/grpc v1.72.1
|
||||
google.golang.org/grpc v1.73.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apimachinery v0.32.3
|
||||
@@ -16,7 +16,7 @@ require (
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
@@ -69,26 +69,26 @@ require (
|
||||
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/api v0.32.3 // indirect
|
||||
|
||||
+36
-36
@@ -6,8 +6,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -194,22 +194,22 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -225,8 +225,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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=
|
||||
@@ -243,11 +243,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -265,8 +265,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -279,8 +279,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
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=
|
||||
@@ -290,20 +290,20 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
+16
-16
@@ -10,8 +10,8 @@ require (
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
go.opentelemetry.io/otel v1.36.0
|
||||
go.opentelemetry.io/otel/trace v1.36.0
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/apiserver v0.32.3
|
||||
k8s.io/component-base v0.32.3
|
||||
@@ -23,7 +23,7 @@ require (
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
@@ -75,25 +75,25 @@ require (
|
||||
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
||||
+36
-36
@@ -6,8 +6,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
|
||||
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -200,24 +200,24 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
|
||||
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -233,8 +233,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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=
|
||||
@@ -251,11 +251,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -273,8 +273,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -287,8 +287,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
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=
|
||||
@@ -298,20 +298,20 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Pa11y accessability tests
|
||||
|
||||
We use pa11y to run some automated simple accessability tests. They're ran with dagger to help orchestrate starting server + tests in a reproducable manner.
|
||||
|
||||
To run the tests locally:
|
||||
|
||||
1. Install dagger locally https://docs.dagger.io/install/
|
||||
2. Grab the grafana.tar.gz artifact by either
|
||||
1. Downloading it from the Github Action artifact from your PR
|
||||
1. Build it locally with:
|
||||
```sh
|
||||
dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="$PWD" > dist/files.txt
|
||||
cat dist/files.txt # Will output the path to the grafana.tar.gz
|
||||
```
|
||||
3. Run the dagger pipeline with:
|
||||
```sh
|
||||
dagger -v run go run ./pkg/build/a11y --package=(full path to .tar.gz) --results=./pa11y-ci-results.json
|
||||
```
|
||||
The JSON results file will be saved to the file from the `--results` arg
|
||||
4. If they fail and you want to see the full output
|
||||
1. Run the dagger command with `dagger -vE [...]`
|
||||
2. At the end, arrow up to the exec pa11y-ci segment and hit Enter
|
||||
@@ -0,0 +1,179 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
grafanaHost = "grafana"
|
||||
grafanaPort = 3001
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
|
||||
if err := NewApp().Run(ctx, os.Args); err != nil {
|
||||
cancel()
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func NewApp() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "a11y",
|
||||
Usage: "Run Grafana accessibility tests",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-dir",
|
||||
Usage: "Path to the grafana/grafana clone directory",
|
||||
Value: ".",
|
||||
Validator: mustBeDir("grafana-dir"),
|
||||
TakesFile: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "package",
|
||||
Usage: "Path to the grafana tar.gz package",
|
||||
Value: "grafana.tar.gz",
|
||||
Validator: mustBeFile("package", false),
|
||||
TakesFile: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "license",
|
||||
Usage: "Path to the Grafana Enterprise license file (optional)",
|
||||
Validator: mustBeFile("license", true),
|
||||
TakesFile: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "Path to the pa11y config file to use",
|
||||
Value: "e2e/pa11yci.conf.js",
|
||||
Validator: mustBeFile("config", true),
|
||||
TakesFile: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "results",
|
||||
Usage: "Path to the pa11y results file to export",
|
||||
TakesFile: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "no-threshold-fail",
|
||||
Usage: "Don't fail the task if any of the tests fail. Use this in combination with --results to list all violations even if they're within thresholds",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
Action: run,
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, cmd *cli.Command) error {
|
||||
grafanaDir := cmd.String("grafana-dir")
|
||||
targzPath := cmd.String("package")
|
||||
licensePath := cmd.String("license")
|
||||
pa11yConfigPath := cmd.String("config")
|
||||
pa11yResultsPath := cmd.String("results")
|
||||
noThresholdFail := cmd.Bool("no-threshold-fail")
|
||||
|
||||
d, err := dagger.Connect(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to Dagger: %w", err)
|
||||
}
|
||||
|
||||
// Explicitly only the files used by the grafana-server service
|
||||
hostSrc := d.Host().Directory(grafanaDir, dagger.HostDirectoryOpts{
|
||||
Include: []string{
|
||||
"./devenv",
|
||||
"./e2e/test-plugins", // Directory is included so provisioning works, but they're not actually build
|
||||
"./scripts/grafana-server/custom.ini",
|
||||
"./scripts/grafana-server/start-server",
|
||||
"./scripts/grafana-server/kill-server",
|
||||
"./scripts/grafana-server/variables",
|
||||
},
|
||||
})
|
||||
|
||||
targz := d.Host().File(targzPath)
|
||||
pa11yConfig := d.Host().File(pa11yConfigPath)
|
||||
|
||||
var license *dagger.File
|
||||
if licensePath != "" {
|
||||
license = d.Host().File(licensePath)
|
||||
}
|
||||
|
||||
svc, err := GrafanaService(ctx, d, GrafanaServiceOpts{
|
||||
HostSrc: hostSrc,
|
||||
GrafanaTarGz: targz,
|
||||
License: license,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Grafana service: %w", err)
|
||||
}
|
||||
|
||||
c, runErr := RunTest(ctx, d, svc, pa11yConfig, noThresholdFail, pa11yResultsPath)
|
||||
if runErr != nil {
|
||||
return fmt.Errorf("failed to run a11y test suite: %w", runErr)
|
||||
}
|
||||
|
||||
c, syncErr := c.Sync(ctx)
|
||||
if syncErr != nil {
|
||||
return fmt.Errorf("failed to sync a11y test suite: %w", syncErr)
|
||||
}
|
||||
|
||||
code, codeErr := c.ExitCode(ctx)
|
||||
if codeErr != nil {
|
||||
return fmt.Errorf("failed to get exit code of a11y test suite: %w", codeErr)
|
||||
}
|
||||
|
||||
if code == 0 {
|
||||
log.Printf("a11y tests passed with exit code %d", code)
|
||||
} else if noThresholdFail {
|
||||
log.Printf("a11y tests failed with exit code %d, but noFail is true", code)
|
||||
} else {
|
||||
return fmt.Errorf("a11y tests failed with exit code %d", code)
|
||||
}
|
||||
|
||||
log.Println("a11y tests completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustBeFile(arg string, emptyOk bool) func(string) error {
|
||||
return func(s string) error {
|
||||
if s == "" {
|
||||
if emptyOk {
|
||||
return nil
|
||||
}
|
||||
return cli.Exit(arg+" cannot be empty", 1)
|
||||
}
|
||||
stat, err := os.Stat(s)
|
||||
if err != nil {
|
||||
return cli.Exit(arg+" does not exist or cannot be read: "+s, 1)
|
||||
}
|
||||
if stat.IsDir() {
|
||||
return cli.Exit(arg+" must be a file, not a directory: "+s, 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func mustBeDir(arg string) func(string) error {
|
||||
return func(s string) error {
|
||||
if s == "" {
|
||||
return cli.Exit(arg+" cannot be empty", 1)
|
||||
}
|
||||
stat, err := os.Stat(s)
|
||||
if err != nil {
|
||||
return cli.Exit(arg+" does not exist or cannot be read: "+s, 1)
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
return cli.Exit(arg+" must be a directory: "+s, 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
func RunTest(
|
||||
ctx context.Context,
|
||||
d *dagger.Client,
|
||||
grafanaService *dagger.Service,
|
||||
pa11yConfig *dagger.File,
|
||||
noThresholdFail bool,
|
||||
pa11yResultsPath string,
|
||||
) (*dagger.Container, error) {
|
||||
// docker-puppeteer container already has Chrome and Pa11y installed in it
|
||||
pa11yContainer := d.Container().From("grafana/docker-puppeteer:1.1.0").
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"mkdir", "-p", "./screenshots"}). // not yet exported
|
||||
WithEnvVariable("HOST", grafanaHost).
|
||||
WithEnvVariable("PORT", fmt.Sprint(grafanaPort))
|
||||
|
||||
if noThresholdFail {
|
||||
// This logic is non-intuitive - --no-threshold-fail will make pa11y fail (by removing thresholds from the config)
|
||||
// so it can write all violations to the results file. This failure is then ignored by the caller in main.go.
|
||||
// Otherwise, pa11y ignores violations if they're within the thresholds and doesn't include them in the results file
|
||||
pa11yContainer = pa11yContainer.
|
||||
WithEnvVariable("NO_THRESHOLDS", "true")
|
||||
}
|
||||
|
||||
pa11yContainer = pa11yContainer.
|
||||
WithServiceBinding(grafanaHost, grafanaService).
|
||||
WithMountedFile("pa11yci-config.js", pa11yConfig).
|
||||
WithExec([]string{"pa11y-ci", "--config", "pa11yci-config.js"}, dagger.ContainerWithExecOpts{
|
||||
Expect: dagger.ReturnTypeAny, // allow this to fail here so we can handle non-zero exit codes at the caller
|
||||
})
|
||||
|
||||
if pa11yResultsPath != "" {
|
||||
_, err := pa11yContainer.File("/src/pa11y-ci-results.json").Export(ctx, pa11yResultsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get pa11y results: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return pa11yContainer, nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
type GrafanaServiceOpts struct {
|
||||
HostSrc *dagger.Directory
|
||||
GrafanaTarGz *dagger.File
|
||||
License *dagger.File
|
||||
}
|
||||
|
||||
func GrafanaService(ctx context.Context, d *dagger.Client, opts GrafanaServiceOpts) (*dagger.Service, error) {
|
||||
container := d.Container().From("alpine:3").
|
||||
WithExec([]string{"apk", "add", "--no-cache", "bash", "tar", "netcat-openbsd"}).
|
||||
WithMountedFile("/src/grafana.tar.gz", opts.GrafanaTarGz).
|
||||
WithExec([]string{"mkdir", "-p", "/src/grafana"}).
|
||||
WithExec([]string{"tar", "--strip-components=1", "-xzf", "/src/grafana.tar.gz", "-C", "/src/grafana"}).
|
||||
WithDirectory("/src/grafana/devenv", opts.HostSrc.Directory("./devenv")).
|
||||
WithDirectory("/src/grafana/e2e/test-plugins", opts.HostSrc.Directory("./e2e/test-plugins")).
|
||||
WithDirectory("/src/grafana/scripts", opts.HostSrc.Directory("./scripts")).
|
||||
WithWorkdir("/src/grafana").
|
||||
WithEnvVariable("GF_APP_MODE", "development").
|
||||
WithEnvVariable("GF_SERVER_HTTP_PORT", fmt.Sprint(grafanaPort)).
|
||||
WithEnvVariable("GF_SERVER_ROUTER_LOGGING", "1").
|
||||
WithExposedPort(grafanaPort)
|
||||
|
||||
var licenseArg string
|
||||
if opts.License != nil {
|
||||
licenseArg = "/src/license.jwt"
|
||||
container = container.WithMountedFile(licenseArg, opts.License)
|
||||
}
|
||||
|
||||
// We add all GF_ environment variables to allow for overriding Grafana configuration.
|
||||
// It is unlikely the runner has any such otherwise.
|
||||
for _, env := range os.Environ() {
|
||||
if strings.HasPrefix(env, "GF_") {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
container = container.WithEnvVariable(parts[0], parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
svc := container.AsService(dagger.ContainerAsServiceOpts{Args: []string{"bash", "-x", "scripts/grafana-server/start-server", licenseArg}})
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
@@ -12,9 +12,12 @@ runs:
|
||||
go-version-file: go.mod
|
||||
- name: Bump versions
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
env:
|
||||
GO_MOD_DIR: ${{ inputs.go-mod-dir }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
with:
|
||||
verb: run
|
||||
args: go run ./pkg/build/actions/bump-version -version=${{ inputs.version }}
|
||||
args: go run ./pkg/build/actions/bump-version -version=${VERSION}
|
||||
- name: make gen-cue
|
||||
shell: bash
|
||||
run: make gen-cue
|
||||
|
||||
@@ -2,10 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -26,6 +28,23 @@ import (
|
||||
|
||||
const grafanaAPI = "https://grafana.com/api"
|
||||
|
||||
var httpClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return dialer.DialContext
|
||||
}(&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}),
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
// GrafanaCom implements the sub-command "grafana-com".
|
||||
func GrafanaCom(c *cli.Context) error {
|
||||
bucketStr := c.String("src-bucket")
|
||||
@@ -330,7 +349,7 @@ func postRequest(cfg packaging.PublishConfig, pth string, body any, descr string
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed posting to %s (%s): %s", u, descr, err)
|
||||
}
|
||||
|
||||
+131
-135
@@ -4,8 +4,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build"
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cmd"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -17,15 +17,131 @@ func registerAppCommand(c *cli.Command) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Commands = cli.Commands{
|
||||
// TODO change the registerer if the user is running using a JSON file etc
|
||||
for k, v := range cmd.Artifacts {
|
||||
if err := cmd.GlobalCLI.Register(k, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
app := cmd.GlobalCLI.App()
|
||||
artifactsCommand := cmd.GlobalCLI.ArtifactsCommand()
|
||||
artifactsCommand.Subcommands = cli.Commands{
|
||||
{
|
||||
Name: "build",
|
||||
Action: build.RunCmdCLI,
|
||||
Name: "storybook",
|
||||
Usage: "[ARCHIVED] Publish Grafana storybook",
|
||||
Action: PublishStorybookAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "storybook-bucket",
|
||||
Value: "grafana-storybook",
|
||||
Usage: "Google Cloud Storage bucket for storybooks",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "static-assets",
|
||||
Usage: "[ARCHIVED] Publish Grafana static assets",
|
||||
Action: PublishStaticAssetsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "static-assets-bucket",
|
||||
Value: "grafana-static-assets",
|
||||
Usage: "Google Cloud Storage bucket for static assets",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "static-asset-editions",
|
||||
Usage: "All the editions of the static assets (or $STATIC_ASSET_EDITIONS)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "packages",
|
||||
Usage: "[ARCHIVED] Publish Grafana packages",
|
||||
Action: PublishArtifactsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringSliceFlag{
|
||||
Name: "artifacts-editions",
|
||||
Value: cli.NewStringSlice("oss", "enterprise", "enterprise2"),
|
||||
Usage: "Editions for which the artifacts should be delivered (oss,enterprise,enterprise2), (or $ARTIFACTS_EDITIONS)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-dest-bucket",
|
||||
Value: "grafana-downloads-enterprise2",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-security-prefix",
|
||||
Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "docker",
|
||||
Usage: "[ARCHIVED] Handle Grafana Docker images",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "fetch",
|
||||
Usage: "Fetch Grafana Docker images",
|
||||
ArgsUsage: "[version]",
|
||||
Action: util.MaxArgCountWrapper(1, FetchImages),
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "npm",
|
||||
Usage: "[ARCHIVED] Handle Grafana npm packages",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "release",
|
||||
Usage: "Release npm packages",
|
||||
ArgsUsage: "[version]",
|
||||
Action: NpmReleaseAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "store",
|
||||
Usage: "Store npm packages tarball",
|
||||
Action: NpmStoreAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "retrieve",
|
||||
Usage: "Retrieve npm packages tarball",
|
||||
Action: NpmRetrieveAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Commands = append(app.Commands, []*cli.Command{
|
||||
{
|
||||
Name: "e2e-tests",
|
||||
Usage: "Run Grafana e2e tests",
|
||||
Usage: "[ARCHIVED] Run Grafana e2e tests",
|
||||
Action: EndToEndTests,
|
||||
Flags: []cli.Flag{
|
||||
&triesFlag,
|
||||
@@ -50,14 +166,9 @@ func main() {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "whatsnew-checker",
|
||||
Usage: "Checks whatsNewUrl in package.json for differences between the tag and the docs version",
|
||||
Action: WhatsNewChecker,
|
||||
},
|
||||
{
|
||||
Name: "upload-cdn",
|
||||
Usage: "Upload public/* to a cdn bucket",
|
||||
Usage: "[ARCHIVED] Upload public/* to a cdn bucket",
|
||||
Action: UploadCDN,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
@@ -65,18 +176,18 @@ func main() {
|
||||
},
|
||||
{
|
||||
Name: "publish-metrics",
|
||||
Usage: "Publish a set of metrics from stdin",
|
||||
Usage: "[ARCHIVED] Publish a set of metrics from stdin",
|
||||
ArgsUsage: "<api-key>",
|
||||
Action: util.MaxArgCountWrapper(1, PublishMetrics),
|
||||
},
|
||||
{
|
||||
Name: "verify-drone",
|
||||
Usage: "Verify Drone configuration",
|
||||
Usage: "[ARCHIVED] Verify Drone configuration",
|
||||
Action: VerifyDrone,
|
||||
},
|
||||
{
|
||||
Name: "store-storybook",
|
||||
Usage: "Stores storybook to GCS buckets",
|
||||
Usage: "[ARCHIVED] Stores storybook to GCS buckets",
|
||||
Action: StoreStorybook,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -87,12 +198,12 @@ func main() {
|
||||
},
|
||||
{
|
||||
Name: "verify-storybook",
|
||||
Usage: "Integrity check for storybook build",
|
||||
Usage: "[ARCHIVED] Integrity check for storybook build",
|
||||
Action: VerifyStorybook,
|
||||
},
|
||||
{
|
||||
Name: "upload-packages",
|
||||
Usage: "Upload Grafana packages",
|
||||
Usage: "[ARCHIVED] Upload Grafana packages",
|
||||
Action: UploadPackages,
|
||||
Flags: []cli.Flag{
|
||||
&jobsFlag,
|
||||
@@ -103,125 +214,10 @@ func main() {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "artifacts",
|
||||
Usage: "Handle Grafana artifacts",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "storybook",
|
||||
Usage: "Publish Grafana storybook",
|
||||
Action: PublishStorybookAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "storybook-bucket",
|
||||
Value: "grafana-storybook",
|
||||
Usage: "Google Cloud Storage bucket for storybooks",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "static-assets",
|
||||
Usage: "Publish Grafana static assets",
|
||||
Action: PublishStaticAssetsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "static-assets-bucket",
|
||||
Value: "grafana-static-assets",
|
||||
Usage: "Google Cloud Storage bucket for static assets",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "static-asset-editions",
|
||||
Usage: "All the editions of the static assets (or $STATIC_ASSET_EDITIONS)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "packages",
|
||||
Usage: "Publish Grafana packages",
|
||||
Action: PublishArtifactsAction,
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&securityFlag,
|
||||
&securityDestBucketFlag,
|
||||
&tagFlag,
|
||||
&srcFlag,
|
||||
&destFlag,
|
||||
&cli.StringSliceFlag{
|
||||
Name: "artifacts-editions",
|
||||
Value: cli.NewStringSlice("oss", "enterprise", "enterprise2"),
|
||||
Usage: "Editions for which the artifacts should be delivered (oss,enterprise,enterprise2), (or $ARTIFACTS_EDITIONS)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-dest-bucket",
|
||||
Value: "grafana-downloads-enterprise2",
|
||||
Usage: "Google Cloud Storage bucket for published packages",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise2-security-prefix",
|
||||
Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "docker",
|
||||
Usage: "Handle Grafana Docker images",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "fetch",
|
||||
Usage: "Fetch Grafana Docker images",
|
||||
ArgsUsage: "[version]",
|
||||
Action: util.MaxArgCountWrapper(1, FetchImages),
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "npm",
|
||||
Usage: "Handle Grafana npm packages",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "release",
|
||||
Usage: "Release npm packages",
|
||||
ArgsUsage: "[version]",
|
||||
Action: NpmReleaseAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "store",
|
||||
Usage: "Store npm packages tarball",
|
||||
Action: NpmStoreAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "retrieve",
|
||||
Usage: "Retrieve npm packages tarball",
|
||||
Action: NpmRetrieveAction,
|
||||
Flags: []cli.Flag{
|
||||
&tagFlag,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
artifactsCommand,
|
||||
{
|
||||
Name: "publish",
|
||||
Usage: "Publish packages to Grafana com and repositories",
|
||||
Usage: "[ARCHIVED] Publish packages to Grafana com and repositories",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "grafana-com",
|
||||
@@ -292,7 +288,7 @@ func main() {
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}...)
|
||||
|
||||
app.Commands = append(app.Commands, additionalCommands...)
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const GrafanaDir = "."
|
||||
|
||||
var whatsNewRegex = regexp.MustCompile(`^.*whats-new-in-(v\d*-[\d+]*)`)
|
||||
|
||||
type PackageJSON struct {
|
||||
Grafana Grafana `json:"grafana"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type Grafana struct {
|
||||
WhatsNewUrl string `json:"whatsNewUrl"`
|
||||
}
|
||||
|
||||
func WhatsNewChecker(c *cli.Context) error {
|
||||
metadata, err := config.GenerateMetadata(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if metadata.ReleaseMode.IsTest {
|
||||
fmt.Println("test mode, skipping check")
|
||||
return nil
|
||||
}
|
||||
if metadata.ReleaseMode.Mode != config.TagMode {
|
||||
return fmt.Errorf("non-tag pipeline, exiting")
|
||||
}
|
||||
|
||||
tag := fmt.Sprintf("v%s", metadata.GrafanaVersion)
|
||||
|
||||
if !semver.IsValid(tag) {
|
||||
return fmt.Errorf("non-semver compatible version %s, exiting", tag)
|
||||
}
|
||||
|
||||
majorMinorDigits := strings.Replace(semver.MajorMinor(tag), ".", "-", 1)
|
||||
|
||||
pkgJSONPath := filepath.Join(GrafanaDir, "package.json")
|
||||
//nolint:gosec
|
||||
pkgJSONB, err := os.ReadFile(pkgJSONPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read %q: %w", pkgJSONPath, err)
|
||||
}
|
||||
|
||||
var pkgObj PackageJSON
|
||||
if err := json.Unmarshal(pkgJSONB, &pkgObj); err != nil {
|
||||
return fmt.Errorf("failed decoding %q: %w", pkgJSONPath, err)
|
||||
}
|
||||
|
||||
whatsNewSplit := whatsNewRegex.FindStringSubmatch(pkgObj.Grafana.WhatsNewUrl)
|
||||
whatsNewVersion := whatsNewSplit[1]
|
||||
|
||||
if whatsNewVersion != majorMinorDigits {
|
||||
return fmt.Errorf("whatsNewUrl in package.json needs to be updated to %s/", strings.Replace(whatsNewSplit[0], whatsNewVersion, majorMinorDigits, 1))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
DroneBuildEvent = "DRONE_BUILD_EVENT"
|
||||
DroneTag = "DRONE_TAG"
|
||||
DroneSemverPrerelease = "DRONE_SEMVER_PRERELEASE"
|
||||
)
|
||||
|
||||
const whatsNewUrl = "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-"
|
||||
|
||||
func TestWhatsNewChecker(t *testing.T) {
|
||||
tests := []struct {
|
||||
envMap map[string]string
|
||||
packageJsonVersion string
|
||||
name string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{envMap: map[string]string{DroneBuildEvent: config.PullRequest}, packageJsonVersion: "", name: "non-tag event", wantErr: true, errMsg: "non-tag pipeline, exiting"},
|
||||
{envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "abcd123"}, packageJsonVersion: "", name: "non-semver compatible", wantErr: true, errMsg: "non-semver compatible version vabcd123, exiting"},
|
||||
{envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "v0.0.0", DroneSemverPrerelease: "test"}, packageJsonVersion: "v10-0", name: "skip check for test tags", wantErr: false},
|
||||
{envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "v10.0.0"}, packageJsonVersion: "v10-0", name: "package.json version matches tag", wantErr: false},
|
||||
{envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "v10.0.0"}, packageJsonVersion: "v9-5", name: "package.json doesn't match tag", wantErr: true, errMsg: "whatsNewUrl in package.json needs to be updated to https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v10-0/"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
app := cli.NewApp()
|
||||
app.Version = "1.0.0"
|
||||
context := cli.NewContext(app, &flag.FlagSet{}, nil)
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
setUpEnv(t, tt.envMap)
|
||||
err := createTempPackageJson(t, tt.packageJsonVersion)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = WhatsNewChecker(context)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.errMsg, err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setUpEnv(t *testing.T, envMap map[string]string) {
|
||||
t.Helper()
|
||||
|
||||
os.Clearenv()
|
||||
t.Setenv("DRONE_BUILD_NUMBER", "12345")
|
||||
t.Setenv("DRONE_COMMIT", "abcd12345")
|
||||
for k, v := range envMap {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func createTempPackageJson(t *testing.T, version string) error {
|
||||
t.Helper()
|
||||
|
||||
grafanaData := Grafana{WhatsNewUrl: fmt.Sprintf("%s%s/", whatsNewUrl, version)}
|
||||
data := PackageJSON{Grafana: grafanaData, Version: "1.2.3"}
|
||||
file, _ := json.MarshalIndent(data, "", " ")
|
||||
|
||||
err := os.WriteFile("package.json", file, 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
err := os.RemoveAll("package.json")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.grafana/
|
||||
bin/
|
||||
*.tar.gz
|
||||
*.txt
|
||||
.idea/
|
||||
dist/
|
||||
grafana/
|
||||
.DS_Store
|
||||
@@ -0,0 +1,4 @@
|
||||
# daggerbuild
|
||||
|
||||
This folder was copied from the repository [grafana-build](https://github.com/grafana/grafana-build). If anything looks
|
||||
out-of-place, then that's probably why.
|
||||
@@ -0,0 +1,102 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/docker"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
DockerRegistryFlag = &cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "Prefix the image name with the registry provided",
|
||||
Value: "docker.io",
|
||||
}
|
||||
DockerOrgFlag = &cli.StringFlag{
|
||||
Name: "org",
|
||||
Usage: "Overrides the organization of the images",
|
||||
Value: "grafana",
|
||||
}
|
||||
AlpineImageFlag = &cli.StringFlag{
|
||||
Name: "alpine-base",
|
||||
Usage: "The image or image alias specified in the Dockerfile to be used as the base image when building the Alpine version of the Grafana docker image.",
|
||||
Value: "alpine-base",
|
||||
}
|
||||
UbuntuImageFlag = &cli.StringFlag{
|
||||
Name: "ubuntu-base",
|
||||
Usage: "The image or image alias specified in the Dockerfile to be used as the base image when building the Ubuntu version of the Grafana docker image",
|
||||
Value: "ubuntu-base",
|
||||
}
|
||||
TagFormatFlag = &cli.StringFlag{
|
||||
Name: "tag-format",
|
||||
Usage: "Provide a go template for formatting the docker tag(s) for images with an Alpine base",
|
||||
Value: docker.DefaultTagFormat,
|
||||
}
|
||||
UbuntuTagFormatFlag = &cli.StringFlag{
|
||||
Name: "ubuntu-tag-format",
|
||||
Usage: "Provide a go template for formatting the docker tag(s) for images with a ubuntu base",
|
||||
Value: docker.DefaultUbuntuTagFormat,
|
||||
}
|
||||
BoringTagFormatFlag = &cli.StringFlag{
|
||||
Name: "boring-tag-format",
|
||||
Usage: "Provide a go template for formatting the docker tag(s) for the boringcrypto build of Grafana Enterprise",
|
||||
Value: docker.DefaultBoringTagFormat,
|
||||
}
|
||||
|
||||
ProDockerRegistryFlag = &cli.StringFlag{
|
||||
Name: "pro-registry",
|
||||
Usage: "Prefix the image name with the registry provided",
|
||||
Value: "docker.io",
|
||||
}
|
||||
ProDockerOrgFlag = &cli.StringFlag{
|
||||
Name: "pro-org",
|
||||
Usage: "Overrides the organization of the images",
|
||||
Value: "grafana",
|
||||
}
|
||||
ProDockerRepoFlag = &cli.StringFlag{
|
||||
Name: "pro-repo",
|
||||
Usage: "Overrides the docker repository of the built images",
|
||||
Value: "grafana-pro",
|
||||
}
|
||||
|
||||
EntDockerRegistryFlag = &cli.StringFlag{
|
||||
Name: "docker-enterprise-registry",
|
||||
Usage: "Prefix the image name with the registry provided",
|
||||
Value: "docker.io",
|
||||
}
|
||||
EntDockerOrgFlag = &cli.StringFlag{
|
||||
Name: "docker-enterprise-org",
|
||||
Usage: "Overrides the organization of the images",
|
||||
Value: "grafana",
|
||||
}
|
||||
EntDockerRepoFlag = &cli.StringFlag{
|
||||
Name: "docker-enterprise-repo",
|
||||
Usage: "Overrides the docker repository of the built images",
|
||||
Value: "grafana-enterprise",
|
||||
}
|
||||
|
||||
HGTagFormatFlag = &cli.StringFlag{
|
||||
Name: "hg-tag-format",
|
||||
Usage: "Provide a go template for formatting the docker tag(s) for Hosted Grafana images",
|
||||
Value: docker.DefaultHGTagFormat,
|
||||
}
|
||||
|
||||
DockerRegistry = pipeline.NewStringFlagArgument(DockerRegistryFlag)
|
||||
DockerOrg = pipeline.NewStringFlagArgument(DockerOrgFlag)
|
||||
AlpineImage = pipeline.NewStringFlagArgument(AlpineImageFlag)
|
||||
UbuntuImage = pipeline.NewStringFlagArgument(UbuntuImageFlag)
|
||||
TagFormat = pipeline.NewStringFlagArgument(TagFormatFlag)
|
||||
UbuntuTagFormat = pipeline.NewStringFlagArgument(UbuntuTagFormatFlag)
|
||||
BoringTagFormat = pipeline.NewStringFlagArgument(BoringTagFormatFlag)
|
||||
|
||||
// The docker registry for Grafana Pro is often different than the one for Grafana & Enterprise
|
||||
ProDockerRegistry = pipeline.NewStringFlagArgument(ProDockerRegistryFlag)
|
||||
ProDockerOrg = pipeline.NewStringFlagArgument(ProDockerOrgFlag)
|
||||
ProDockerRepo = pipeline.NewStringFlagArgument(ProDockerRepoFlag)
|
||||
|
||||
EntDockerRegistry = pipeline.NewStringFlagArgument(EntDockerRegistryFlag)
|
||||
EntDockerOrg = pipeline.NewStringFlagArgument(EntDockerOrgFlag)
|
||||
EntDockerRepo = pipeline.NewStringFlagArgument(EntDockerRepoFlag)
|
||||
|
||||
HGTagFormat = pipeline.NewStringFlagArgument(HGTagFormatFlag)
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
// Package arguments holds globally-defined arguments that are used throughout the program for shared data.
|
||||
// A good candidate for an argument is a directory whose contents that may be used in the creation of multiple artifacts, like the Grafana source directory.
|
||||
// Arguments are different than flags; a flag is a boolean argument in an artifact string which can set one or multiple preset values.
|
||||
package arguments
|
||||
@@ -0,0 +1 @@
|
||||
package arguments
|
||||
@@ -0,0 +1,53 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/golang"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var GoBuildCache = pipeline.Argument{
|
||||
Name: "go-cache-volume",
|
||||
Description: "Mounted at GOCACHE when building Go backends",
|
||||
ArgumentType: pipeline.ArgumentTypeCacheVolume,
|
||||
Flags: []cli.Flag{},
|
||||
ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
return opts.Client.CacheVolume("go-build-cache"), nil
|
||||
},
|
||||
}
|
||||
|
||||
var GoModCache = pipeline.Argument{
|
||||
Name: "go-mod-volume",
|
||||
Description: "Stores downloaded Go modules when building Go backends",
|
||||
ArgumentType: pipeline.ArgumentTypeCacheVolume,
|
||||
Flags: []cli.Flag{},
|
||||
ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
vol := opts.Client.CacheVolume("go-mod-cache")
|
||||
goVersion, err := opts.State.String(ctx, GoVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src, err := opts.State.Directory(ctx, GrafanaDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := golang.Container(opts.Client, opts.Platform, goVersion).
|
||||
WithEnvVariable("GOMODCACHE", "/go/pkg/mod").
|
||||
WithMountedCache("/go/pkg/mod", vol).
|
||||
WithDirectory("/src", src, dagger.ContainerWithDirectoryOpts{
|
||||
Include: []string{"**/*.mod", "**/*.sum", "**/*.work"},
|
||||
}).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"go", "mod", "download"})
|
||||
|
||||
if _, err := c.Sync(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vol, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultViceroyVersion = "v0.4.0"
|
||||
)
|
||||
|
||||
var GoVersion = pipeline.Argument{
|
||||
Name: "go-version",
|
||||
Description: "The Go version to use when compiling Grafana",
|
||||
ArgumentType: pipeline.ArgumentTypeString,
|
||||
ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
src, err := opts.State.Directory(ctx, GrafanaDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdout, err := opts.Client.Container().From("alpine").
|
||||
WithMountedFile("/src/go.mod", src.File("go.mod")).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"/bin/sh", "-c", `grep '^go ' go.mod | awk '{print $2}'`}).
|
||||
Stdout(ctx)
|
||||
|
||||
return strings.TrimSpace(stdout), err
|
||||
},
|
||||
}
|
||||
|
||||
var ViceroyVersionFlag = &cli.StringFlag{
|
||||
Name: "viceroy-version",
|
||||
Usage: "This flag sets the base image of the container used to build the Grafana backend binaries for non-Linux distributions",
|
||||
Value: DefaultViceroyVersion,
|
||||
}
|
||||
|
||||
var ViceroyVersion = pipeline.NewStringFlagArgument(ViceroyVersionFlag)
|
||||
@@ -0,0 +1,28 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
GPGPublicKeyFlag = &cli.StringFlag{
|
||||
Name: "gpg-public-key-base64",
|
||||
Usage: "Provides a public key encoded in base64 for GPG signing",
|
||||
EnvVars: []string{"GPG_PUBLIC_KEY"},
|
||||
}
|
||||
GPGPrivateKeyFlag = &cli.StringFlag{
|
||||
Name: "gpg-private-key-base64",
|
||||
Usage: "Provides a private key encoded in base64 for GPG signing",
|
||||
EnvVars: []string{"GPG_PRIVATE_KEY"},
|
||||
}
|
||||
GPGPassphraseFlag = &cli.StringFlag{
|
||||
Name: "gpg-passphrase",
|
||||
Usage: "Provides a private key passphrase encoded in base64 for GPG signing",
|
||||
EnvVars: []string{"GPG_PASSPHRASE"},
|
||||
}
|
||||
|
||||
GPGPublicKey = pipeline.NewStringFlagArgument(GPGPublicKeyFlag)
|
||||
GPGPrivateKey = pipeline.NewStringFlagArgument(GPGPrivateKeyFlag)
|
||||
GPGPassphrase = pipeline.NewStringFlagArgument(GPGPassphraseFlag)
|
||||
)
|
||||
@@ -0,0 +1,299 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/containers"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/daggerutil"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/frontend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/git"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const BusyboxImage = "busybox:1.36"
|
||||
|
||||
func InitializeEnterprise(d *dagger.Client, grafana *dagger.Directory, enterprise *dagger.Directory) *dagger.Directory {
|
||||
hash := d.Container().From("alpine/git").
|
||||
WithDirectory("/src/grafana-enterprise", enterprise).
|
||||
WithWorkdir("/src/grafana-enterprise").
|
||||
WithEntrypoint([]string{}).
|
||||
WithExec([]string{"/bin/sh", "-c", "git rev-parse HEAD > .buildinfo.enterprise-commit"}).
|
||||
File("/src/grafana-enterprise/.buildinfo.enterprise-commit")
|
||||
|
||||
return d.Container().From(BusyboxImage).
|
||||
WithDirectory("/src/grafana", grafana).
|
||||
WithDirectory("/src/grafana-enterprise", enterprise).
|
||||
WithWorkdir("/src/grafana-enterprise").
|
||||
WithFile("/src/grafana/.buildinfo.enterprise-commit", hash).
|
||||
WithExec([]string{"/bin/sh", "build.sh"}).
|
||||
WithExec([]string{"cp", "LICENSE", "../grafana"}).
|
||||
Directory("/src/grafana")
|
||||
}
|
||||
|
||||
// GrafnaaOpts are populated by the 'GrafanaFlags' flags.
|
||||
// These options define how to mount or clone the grafana/enterprise source code.
|
||||
type GrafanaDirectoryOpts struct {
|
||||
// GrafanaDir is the path to the Grafana source tree.
|
||||
// If GrafanaDir is empty, then we're most likely cloning Grafana and using that as a directory.
|
||||
GrafanaDir string
|
||||
EnterpriseDir string
|
||||
// GrafanaRepo will clone Grafana from a different repository when cloning Grafana.
|
||||
GrafanaRepo string
|
||||
EnterpriseRepo string
|
||||
// GrafanaRef will checkout a specific tag, branch, or commit when cloning Grafana.
|
||||
GrafanaRef string
|
||||
EnterpriseRef string
|
||||
// GitHubToken is used when cloning Grafana/Grafana Enterprise.
|
||||
GitHubToken string
|
||||
|
||||
PatchesRepo string
|
||||
PatchesPath string
|
||||
PatchesRef string
|
||||
}
|
||||
|
||||
func githubToken(ctx context.Context, token string) (string, error) {
|
||||
// Since GrafanaDir was not provided, we must clone it.
|
||||
ght := token
|
||||
|
||||
// If GitHubToken was not set from flag
|
||||
if ght != "" {
|
||||
return ght, nil
|
||||
}
|
||||
|
||||
token, err := git.LookupGitHubToken(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if token == "" {
|
||||
return "", fmt.Errorf("unable to acquire github token")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func GrafanaDirectoryOptsFromFlags(c cliutil.CLIContext) *GrafanaDirectoryOpts {
|
||||
return &GrafanaDirectoryOpts{
|
||||
GrafanaRepo: c.String("grafana-repo"),
|
||||
EnterpriseRepo: c.String("enterprise-repo"),
|
||||
GrafanaDir: c.String("grafana-dir"),
|
||||
EnterpriseDir: c.String("enterprise-dir"),
|
||||
GrafanaRef: c.String("grafana-ref"),
|
||||
EnterpriseRef: c.String("enterprise-ref"),
|
||||
GitHubToken: c.String("github-token"),
|
||||
PatchesRepo: c.String("patches-repo"),
|
||||
PatchesPath: c.String("patches-path"),
|
||||
PatchesRef: c.String("patches-ref"),
|
||||
}
|
||||
}
|
||||
|
||||
func cloneOrMount(ctx context.Context, client *dagger.Client, localPath, repo, ref string, ght string) (*dagger.Directory, error) {
|
||||
if localPath != "" {
|
||||
absolute, err := filepath.Abs(localPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting absolute path for local dir: %w", err)
|
||||
}
|
||||
localPath = absolute
|
||||
slog.Info("Using local directory for repository", "path", localPath, "repo", repo)
|
||||
return daggerutil.HostDir(client, localPath)
|
||||
}
|
||||
|
||||
ght, err := githubToken(ctx, ght)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error acquiring GitHub token: %w", err)
|
||||
}
|
||||
|
||||
return git.CloneWithGitHubToken(client, ght, repo, ref)
|
||||
}
|
||||
|
||||
func applyPatches(ctx context.Context, client *dagger.Client, src *dagger.Directory, repo, patchesPath, ref, ght string) (*dagger.Directory, error) {
|
||||
ght, err := githubToken(ctx, ght)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error acquiring GitHub token: %w", err)
|
||||
}
|
||||
|
||||
// Clone the patches repository on 'main'
|
||||
dir, err := git.CloneWithGitHubToken(client, ght, repo, ref)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error cloning patches repository: %w", err)
|
||||
}
|
||||
|
||||
entries, err := dir.Entries(ctx, dagger.DirectoryEntriesOpts{
|
||||
Path: patchesPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing entries in repository: %w", err)
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
return nil, fmt.Errorf("no patches in the given path")
|
||||
}
|
||||
|
||||
container := client.Container().From(git.GitImage).
|
||||
WithEntrypoint([]string{}).
|
||||
WithMountedDirectory("/src", src).
|
||||
WithMountedDirectory("/patches", dir).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"git", "config", "--local", "user.name", "grafana"}).
|
||||
WithExec([]string{"git", "config", "--local", "user.email", "engineering@grafana.com"})
|
||||
|
||||
for _, v := range entries {
|
||||
if filepath.Ext(v) != ".patch" {
|
||||
continue
|
||||
}
|
||||
|
||||
container = container.WithExec([]string{"/bin/sh", "-c", fmt.Sprintf(`git am --3way --ignore-whitespace --ignore-space-change --committer-date-is-author-date %s > /dev/null 2>&1`, path.Join("/patches", patchesPath, v))})
|
||||
}
|
||||
|
||||
return container.Directory("/src"), nil
|
||||
}
|
||||
|
||||
func grafanaDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
o := GrafanaDirectoryOptsFromFlags(opts.CLIContext)
|
||||
|
||||
src, err := cloneOrMount(ctx, opts.Client, o.GrafanaDir, o.GrafanaRepo, o.GrafanaRef, o.GitHubToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gitContainer := opts.Client.Container().From("alpine/git").
|
||||
WithWorkdir("/src").
|
||||
WithMountedDirectory("/src/.git", src.Directory(".git")).
|
||||
WithEntrypoint([]string{})
|
||||
|
||||
commitFile := gitContainer.
|
||||
WithExec([]string{"/bin/sh", "-c", "git rev-parse HEAD > .buildinfo.grafana-commit"}).
|
||||
File("/src/.buildinfo.grafana-commit")
|
||||
|
||||
branchFile := gitContainer.
|
||||
WithExec([]string{"/bin/sh", "-c", "git rev-parse --abbrev-ref HEAD > .buildinfo.grafana-branch"}).
|
||||
File("/src/.buildinfo.grafana-branch")
|
||||
|
||||
src = src.
|
||||
WithFile(".buildinfo.commit", commitFile).
|
||||
WithFile(".buildinfo.branch", branchFile)
|
||||
|
||||
if o.PatchesRepo != "" {
|
||||
withPatches, err := applyPatches(ctx, opts.Client, src, o.PatchesRepo, o.PatchesPath, o.PatchesRef, o.GitHubToken)
|
||||
if err != nil {
|
||||
opts.Log.Debug("patch application skipped", "error", err)
|
||||
} else {
|
||||
// Only replace src when there was no error.
|
||||
src = withPatches
|
||||
}
|
||||
}
|
||||
|
||||
nodeVersion, err := frontend.NodeVersion(opts.Client, src).Stdout(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get node version from source code: %w", err)
|
||||
}
|
||||
|
||||
yarnCache, err := opts.State.CacheVolume(ctx, YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
container := frontend.YarnInstall(opts.Client, src, nodeVersion, yarnCache, opts.Platform)
|
||||
|
||||
if _, err := containers.ExitError(ctx, container); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return container.Directory("/src"), nil
|
||||
}
|
||||
|
||||
func enterpriseDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
// Get the Grafana directory...
|
||||
o := GrafanaDirectoryOptsFromFlags(opts.CLIContext)
|
||||
|
||||
grafanaDir, err := grafanaDirectory(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing grafana directory: %w", err)
|
||||
}
|
||||
|
||||
clone, err := cloneOrMount(ctx, opts.Client, o.EnterpriseDir, o.EnterpriseRepo, o.EnterpriseRef, o.GitHubToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error cloning or mounting Grafana Enterprise directory: %w", err)
|
||||
}
|
||||
|
||||
return InitializeEnterprise(opts.Client, grafanaDir.(*dagger.Directory), clone), nil
|
||||
}
|
||||
|
||||
var GrafanaDirectoryFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-dir",
|
||||
Usage: "Local Grafana dir to use, instead of git clone",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise-dir",
|
||||
Usage: "Local Grafana Enterprise dir to use, instead of git clone",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-repo",
|
||||
Usage: "Grafana repo to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "https://github.com/grafana/grafana.git",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise-repo",
|
||||
Usage: "Grafana Enterprise repo to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "https://github.com/grafana/grafana-enterprise.git",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-ref",
|
||||
Usage: "Grafana ref to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "main",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise-ref",
|
||||
Usage: "Grafana Enterprise ref to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "main",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "github-token",
|
||||
Usage: "GitHub token to use for git cloning, by default will be pulled from GitHub",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "patches-repo",
|
||||
Usage: "GitHub repository that contains git patches to apply to the Grafana source code. Must be an https git URL",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "patches-path",
|
||||
Usage: "Path to folder containing '.patch' files to apply",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "patches-ref",
|
||||
Usage: "Ref to checkout in the patches repository",
|
||||
Value: "main",
|
||||
},
|
||||
}
|
||||
|
||||
// GrafanaDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has Grafana in it.
|
||||
// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning.
|
||||
var GrafanaDirectory = pipeline.Argument{
|
||||
Name: "grafana-dir",
|
||||
Description: "The source tree of the Grafana repository",
|
||||
Flags: GrafanaDirectoryFlags,
|
||||
ValueFunc: grafanaDirectory,
|
||||
}
|
||||
|
||||
// EnterpriseDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has Grafana Enterprise initialized it.
|
||||
// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning.
|
||||
var EnterpriseDirectory = pipeline.Argument{
|
||||
Name: "enterprise-dir",
|
||||
Description: "The source tree of Grafana Enterprise",
|
||||
Flags: GrafanaDirectoryFlags,
|
||||
ValueFunc: enterpriseDirectory,
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var HGDirectoryFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "hosted-grafana-dir",
|
||||
Usage: "Local clone of HG to use, instead of git cloning",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "hosted-grafana-repo",
|
||||
Usage: "https `.git` repository to use for hosted-grafana",
|
||||
Required: false,
|
||||
Value: "https://github.com/grafana/hosted-grafana.git",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "hosted-grafana-ref",
|
||||
Usage: "git ref to checkout",
|
||||
Required: false,
|
||||
Value: "main",
|
||||
},
|
||||
}
|
||||
|
||||
// HGDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has a repository that has the Grafana Pro/Enterprise docker image.
|
||||
// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning.
|
||||
var HGDirectory = pipeline.Argument{
|
||||
Name: "hg-dir",
|
||||
Description: "The source tree of that has the Dockerfile for Grafana Pro/Enterprise",
|
||||
Flags: HGDirectoryFlags,
|
||||
ValueFunc: hgDirectory,
|
||||
}
|
||||
|
||||
type HGDirectoryOpts struct {
|
||||
GitHubToken string
|
||||
HGDir string
|
||||
HGRepo string
|
||||
HGRef string
|
||||
}
|
||||
|
||||
func HGDirectoryOptsFromFlags(c cliutil.CLIContext) *HGDirectoryOpts {
|
||||
return &HGDirectoryOpts{
|
||||
GitHubToken: c.String("github-token"),
|
||||
HGDir: c.String("hosted-grafana-dir"),
|
||||
HGRepo: c.String("hosted-grafana-repo"),
|
||||
HGRef: c.String("hosted-grafana-ref"),
|
||||
}
|
||||
}
|
||||
|
||||
func hgDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
o := HGDirectoryOptsFromFlags(opts.CLIContext)
|
||||
ght, err := githubToken(ctx, o.GitHubToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get GitHub token: %w", err)
|
||||
}
|
||||
|
||||
src, err := cloneOrMount(ctx, opts.Client, o.HGDir, o.HGRepo, o.HGRef, ght)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return src, nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package arguments
|
||||
|
||||
import "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
|
||||
func Join(f ...[]pipeline.Argument) []pipeline.Argument {
|
||||
r := []pipeline.Argument{}
|
||||
for _, v := range f {
|
||||
r = append(r, v...)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/containers"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/stringutil"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var flagBuildID = &cli.StringFlag{
|
||||
Name: "build-id",
|
||||
Usage: "Build ID to use in package names",
|
||||
Value: "local",
|
||||
}
|
||||
|
||||
var BuildID = pipeline.Argument{
|
||||
Name: "build-id",
|
||||
Description: "The grafana backend binaries ('grafana', 'grafana-cli', 'grafana-server') in a directory",
|
||||
Flags: []cli.Flag{
|
||||
flagBuildID,
|
||||
},
|
||||
ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
v := opts.CLIContext.String("build-id")
|
||||
if v == "" {
|
||||
v = stringutil.RandomString(8)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
},
|
||||
}
|
||||
|
||||
var flagVersion = &cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Explicit version number. If this is not set then one with will auto-detected based on the source repository",
|
||||
}
|
||||
|
||||
var Version = pipeline.Argument{
|
||||
Name: "version",
|
||||
Description: "The version string that is shown in the UI, in the CLI, and in package metadata",
|
||||
Flags: []cli.Flag{
|
||||
flagVersion,
|
||||
},
|
||||
Requires: []pipeline.Argument{
|
||||
GrafanaDirectory,
|
||||
},
|
||||
ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
v := opts.CLIContext.String("version")
|
||||
if v != "" {
|
||||
return v, nil
|
||||
}
|
||||
src, err := opts.State.Directory(ctx, GrafanaDirectory)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buildID, err := opts.State.String(ctx, BuildID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version, err := containers.GetJSONValue(ctx, opts.Client, src, "package.json", "version")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.ReplaceAll(version, "pre", buildID), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var YarnCacheDirFlag = &cli.StringFlag{
|
||||
Name: "yarn-cache-dir",
|
||||
Aliases: []string{"yarn-cache"},
|
||||
Usage: "Path to the yarn cache directory to mount during 'yarn install' commands (if there is one)",
|
||||
EnvVars: []string{"YARN_CACHE_FOLDER", "YARN_CACHE_DIR"},
|
||||
Value: "",
|
||||
}
|
||||
|
||||
var YarnCacheDirectory = pipeline.Argument{
|
||||
Name: "yarn-cache-dir",
|
||||
Description: YarnCacheDirFlag.Usage,
|
||||
ArgumentType: pipeline.ArgumentTypeCacheVolume,
|
||||
Flags: []cli.Flag{
|
||||
YarnCacheDirFlag,
|
||||
},
|
||||
ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
|
||||
vol := opts.CLIContext.String(YarnCacheDirFlag.Name)
|
||||
|
||||
// Prepopulate the cache with what's defined in YARN_CACHE_FOLDER
|
||||
// or in the CLI
|
||||
if val, ok := os.LookupEnv("YARN_CACHE_FOLDER"); ok {
|
||||
vol = val
|
||||
}
|
||||
|
||||
cache := opts.Client.CacheVolume("yarn-cache-dir")
|
||||
if vol == "" {
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
dir := opts.Client.Host().Directory(vol)
|
||||
_, err := opts.Client.Container().
|
||||
From("alpine").
|
||||
WithMountedCache("/cache", cache).
|
||||
WithMountedDirectory("/data", dir).
|
||||
WithExec([]string{"/bin/sh", "-c", "cp -r /data/* /cache || return 0"}).
|
||||
Sync(ctx)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cache, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
func Action(r Registerer, c *cli.Context) error {
|
||||
// ArtifactStrings represent an artifact with a list of boolean options, like
|
||||
// targz:linux/amd64:enterprise
|
||||
artifactStrings := c.StringSlice("artifacts")
|
||||
|
||||
logLevel := slog.LevelInfo
|
||||
if c.Bool("verbose") {
|
||||
logLevel = slog.LevelDebug
|
||||
}
|
||||
|
||||
var (
|
||||
ctx = c.Context
|
||||
log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||
Level: logLevel,
|
||||
}))
|
||||
parallel = c.Int64("parallel")
|
||||
destination = c.String("destination")
|
||||
platform = dagger.Platform(c.String("platform"))
|
||||
verify = c.Bool("verify")
|
||||
checksum = c.Bool("checksum")
|
||||
)
|
||||
|
||||
if len(artifactStrings) == 0 {
|
||||
return errors.New("no artifacts specified. At least 1 artifact is required using the '--artifact' or '-a' flag")
|
||||
}
|
||||
|
||||
log.Debug("Connecting to dagger daemon...")
|
||||
daggerOpts := []dagger.ClientOpt{}
|
||||
if logLevel == slog.LevelDebug {
|
||||
daggerOpts = append(daggerOpts, dagger.WithLogOutput(os.Stderr))
|
||||
}
|
||||
client, err := dagger.Connect(ctx, daggerOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Connected to dagger daemon")
|
||||
|
||||
var state pipeline.StateHandler = &pipeline.State{
|
||||
Log: log,
|
||||
Client: client,
|
||||
CLIContext: c,
|
||||
Platform: platform,
|
||||
}
|
||||
|
||||
registered := r.Initializers()
|
||||
|
||||
log.Debug("Generating artifacts from artifact strings...")
|
||||
// Initialize the artifacts that were specified by the artifacts commands.
|
||||
// These are specified by using artifact strings, or comma-delimited lists of flags.
|
||||
artifacts, err := ArtifactsFromStrings(ctx, log, artifactStrings, registered, state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Done generating artifact metadata")
|
||||
|
||||
state = pipeline.StateWithLogger(
|
||||
log.With("service", "state"),
|
||||
state,
|
||||
)
|
||||
|
||||
// The artifact store is responsible for storing built artifacts and issuing them to artifacts that use them as dependencies using the artifact's filename as the key.
|
||||
store := pipeline.NewArtifactStore(log)
|
||||
|
||||
opts := &pipeline.ArtifactContainerOpts{
|
||||
Client: client,
|
||||
Log: log,
|
||||
State: state,
|
||||
Platform: platform,
|
||||
Store: store,
|
||||
}
|
||||
|
||||
// Build each artifact and their dependencies, essentially constructing a dag using Dagger.
|
||||
for i, v := range artifacts {
|
||||
filename, err := v.Handler.Filename(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error processing artifact string '%s': %w", artifactStrings[i], err)
|
||||
}
|
||||
log := log.With("filename", filename, "artifact", v.ArtifactString)
|
||||
log.Info("Adding artifact to dag...")
|
||||
if err := BuildArtifact(ctx, log, v, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Done adding artifact")
|
||||
}
|
||||
|
||||
wg := &errgroup.Group{}
|
||||
sm := semaphore.NewWeighted(parallel)
|
||||
log.Info("Exporting artifacts...")
|
||||
// Export the files from the dag, causing the containers to trigger.
|
||||
for _, v := range artifacts {
|
||||
log := log.With("artifact", v.ArtifactString, "action", "export")
|
||||
wg.Go(ExportArtifactFunc(ctx, client, sm, log, v, store, destination, checksum))
|
||||
}
|
||||
if verify {
|
||||
// Export the files from the dag, causing the containers to trigger.
|
||||
for _, v := range artifacts {
|
||||
log := log.With("artifact", v.ArtifactString, "action", "validate")
|
||||
wg.Go(VerifyArtifactFunc(ctx, client, sm, log, v, store, destination))
|
||||
}
|
||||
}
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func BuildArtifact(ctx context.Context, log *slog.Logger, a *pipeline.Artifact, opts *pipeline.ArtifactContainerOpts) error {
|
||||
store := opts.Store
|
||||
exists, err := store.Exists(ctx, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// populate the dependency list
|
||||
dependencies, err := a.Handler.Dependencies(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the files / directories that the dependencies define,
|
||||
// and store the result for re-use.
|
||||
for _, v := range dependencies {
|
||||
f, err := v.Handler.Filename(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log := log.With("artifact", v.ArtifactString, "filename", f)
|
||||
if err := BuildArtifact(ctx, log, v, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch a.Type {
|
||||
case pipeline.ArtifactTypeDirectory:
|
||||
dir, err := BuildArtifactDirectory(ctx, a, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.StoreDirectory(ctx, a, dir)
|
||||
case pipeline.ArtifactTypeFile:
|
||||
file, err := BuildArtifactFile(ctx, a, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.StoreFile(ctx, a, file)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Command(r Registerer) func(c *cli.Context) error {
|
||||
return func(c *cli.Context) error {
|
||||
if err := Action(r, c); err != nil {
|
||||
return cli.Exit(err, 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func BuildArtifactFile(ctx context.Context, a *pipeline.Artifact, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
builder, err := a.Handler.Builder(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.Handler.BuildFile(ctx, builder, opts)
|
||||
}
|
||||
|
||||
func BuildArtifactDirectory(ctx context.Context, a *pipeline.Artifact, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
builder, err := a.Handler.Builder(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.Handler.BuildDir(ctx, builder, opts)
|
||||
}
|
||||
|
||||
func ExportArtifactFunc(ctx context.Context, d *dagger.Client, sm *semaphore.Weighted, log *slog.Logger, v *pipeline.Artifact, store pipeline.ArtifactStore, dst string, checksum bool) func() error {
|
||||
return func() error {
|
||||
log.Info("Started exporting artifact...")
|
||||
|
||||
log.Info("Acquiring semaphore")
|
||||
if err := sm.Acquire(ctx, 1); err != nil {
|
||||
log.Info("Error acquiring semaphore", "error", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Acquired semaphore")
|
||||
|
||||
defer sm.Release(1)
|
||||
|
||||
filename, err := v.Handler.Filename(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error processing artifact string '%s': %w", v.ArtifactString, err)
|
||||
}
|
||||
|
||||
log.Info("Exporting artifact")
|
||||
paths, err := store.Export(ctx, d, v, dst, checksum)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error exporting artifact '%s': %w", filename, err)
|
||||
}
|
||||
|
||||
for _, v := range paths {
|
||||
if _, err := fmt.Fprintf(Stdout, "%s\n", v); err != nil {
|
||||
return fmt.Errorf("error writing to stdout: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Done exporting artifact")
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func verifyArtifact(ctx context.Context, client *dagger.Client, v *pipeline.Artifact, store pipeline.ArtifactStore) error {
|
||||
switch v.Type {
|
||||
case pipeline.ArtifactTypeDirectory:
|
||||
file, err := store.Directory(ctx, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.Handler.VerifyDirectory(ctx, client, file); err != nil {
|
||||
return err
|
||||
}
|
||||
case pipeline.ArtifactTypeFile:
|
||||
file, err := store.File(ctx, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.Handler.VerifyFile(ctx, client, file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyArtifactFunc(ctx context.Context, d *dagger.Client, sm *semaphore.Weighted, log *slog.Logger, v *pipeline.Artifact, store pipeline.ArtifactStore, dst string) func() error {
|
||||
return func() error {
|
||||
log.Info("Started verifying artifact...")
|
||||
|
||||
log.Info("Acquiring semaphore")
|
||||
if err := sm.Acquire(ctx, 1); err != nil {
|
||||
log.Info("Error acquiring semaphore", "error", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Acquired semaphore")
|
||||
defer sm.Release(1)
|
||||
|
||||
if err := verifyArtifact(ctx, d, v, store); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
BackendArguments = []pipeline.Argument{
|
||||
arguments.GrafanaDirectory,
|
||||
arguments.EnterpriseDirectory,
|
||||
arguments.GoVersion,
|
||||
arguments.ViceroyVersion,
|
||||
}
|
||||
|
||||
BackendFlags = flags.JoinFlags(
|
||||
flags.PackageNameFlags,
|
||||
flags.DistroFlags(),
|
||||
)
|
||||
)
|
||||
|
||||
var BackendInitializer = Initializer{
|
||||
InitializerFunc: NewBackendFromString,
|
||||
Arguments: BackendArguments,
|
||||
}
|
||||
|
||||
type Backend struct {
|
||||
// Name allows different backend compilations to be different even if all other factors are the same.
|
||||
// For example, Grafana Enterprise, Grafana, and Grafana Pro may be built using the same options,
|
||||
// but are fundamentally different because of the source code of the binary.
|
||||
Name packages.Name
|
||||
Src *dagger.Directory
|
||||
Distribution backend.Distribution
|
||||
BuildOpts *backend.BuildOpts
|
||||
GoVersion string
|
||||
ViceroyVersion string
|
||||
|
||||
GoBuildCache *dagger.CacheVolume
|
||||
GoModCache *dagger.CacheVolume
|
||||
// Version is embedded in the binary at build-time
|
||||
Version string
|
||||
}
|
||||
|
||||
func (b *Backend) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return backend.Builder(
|
||||
opts.Client,
|
||||
opts.Log,
|
||||
b.Distribution,
|
||||
b.BuildOpts,
|
||||
opts.Platform,
|
||||
b.Src,
|
||||
b.GoVersion,
|
||||
b.ViceroyVersion,
|
||||
b.GoBuildCache,
|
||||
b.GoModCache,
|
||||
)
|
||||
}
|
||||
|
||||
func (b *Backend) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *Backend) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (b *Backend) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
f, err := b.Filename(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return backend.Build(
|
||||
opts.Client,
|
||||
builder,
|
||||
b.Src,
|
||||
b.Distribution,
|
||||
f,
|
||||
b.BuildOpts,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (b *Backend) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (b *Backend) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (b *Backend) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (b *Backend) Filename(ctx context.Context) (string, error) {
|
||||
return filepath.Join("bin", string(b.Name), string(b.Distribution)), nil
|
||||
}
|
||||
|
||||
func (b *Backend) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Not a file
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
// Nothing to do (yet)
|
||||
return nil
|
||||
}
|
||||
|
||||
type NewBackendOpts struct {
|
||||
Name packages.Name
|
||||
Enterprise bool
|
||||
Src *dagger.Directory
|
||||
Distribution backend.Distribution
|
||||
GoVersion string
|
||||
ViceroyVersion string
|
||||
Version string
|
||||
Experiments []string
|
||||
Tags []string
|
||||
Static bool
|
||||
WireTag string
|
||||
GoBuildCache *dagger.CacheVolume
|
||||
GoModCache *dagger.CacheVolume
|
||||
}
|
||||
|
||||
func NewBackendFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
goVersion, err := state.String(ctx, arguments.GoVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
viceroyVersion, err := state.String(ctx, arguments.ViceroyVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goModCache, err := state.CacheVolume(ctx, arguments.GoModCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goBuildCache, err := state.CacheVolume(ctx, arguments.GoBuildCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1. Figure out the options that were provided as part of the artifact string.
|
||||
// For example, `linux/amd64:grafana`.
|
||||
options, err := pipeline.ParseFlags(artifact, TargzFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
static, err := options.Bool(flags.Static)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wireTag, err := options.String(flags.WireTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
experiments, err := options.StringSlice(flags.GoExperiments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tags, err := options.StringSlice(flags.GoTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src, err := GrafanaDir(ctx, state, p.Enterprise)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goCacheProg := ""
|
||||
// If the caller has GOCACHEPROG set, then reuse it
|
||||
if val, ok := os.LookupEnv("GOCACHEPROG"); ok {
|
||||
goCacheProg = val
|
||||
}
|
||||
|
||||
bopts := &backend.BuildOpts{
|
||||
Version: p.Version,
|
||||
Enterprise: p.Enterprise,
|
||||
ExperimentalFlags: experiments,
|
||||
GoCacheProg: goCacheProg,
|
||||
Static: static,
|
||||
WireTag: wireTag,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeDirectory,
|
||||
Flags: BackendFlags,
|
||||
Handler: &Backend{
|
||||
Name: p.Name,
|
||||
Distribution: p.Distribution,
|
||||
BuildOpts: bopts,
|
||||
GoVersion: goVersion,
|
||||
ViceroyVersion: viceroyVersion,
|
||||
Src: src,
|
||||
GoModCache: goModCache,
|
||||
GoBuildCache: goBuildCache,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func NewBackend(ctx context.Context, log *slog.Logger, artifact string, opts *NewBackendOpts) (*pipeline.Artifact, error) {
|
||||
bopts := &backend.BuildOpts{
|
||||
Version: opts.Version,
|
||||
Enterprise: opts.Enterprise,
|
||||
ExperimentalFlags: opts.Experiments,
|
||||
Tags: opts.Tags,
|
||||
Static: opts.Static,
|
||||
WireTag: opts.WireTag,
|
||||
}
|
||||
|
||||
log.Info("Initializing backend artifact with options", "static", opts.Static, "version", opts.Version, "name", opts.Name, "distro", opts.Distribution)
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeDirectory,
|
||||
Flags: BackendFlags,
|
||||
Handler: &Backend{
|
||||
Name: opts.Name,
|
||||
Distribution: opts.Distribution,
|
||||
BuildOpts: bopts,
|
||||
GoVersion: opts.GoVersion,
|
||||
ViceroyVersion: opts.ViceroyVersion,
|
||||
Src: opts.Src,
|
||||
GoModCache: opts.GoModCache,
|
||||
GoBuildCache: opts.GoBuildCache,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"log/slog"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cmd/flags"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func ArtifactFlags(r Registerer) []cli.Flag {
|
||||
artifactsFlag := &cli.StringSliceFlag{
|
||||
Name: "artifacts",
|
||||
Aliases: []string{"a"},
|
||||
}
|
||||
|
||||
buildFlag := &cli.BoolFlag{
|
||||
Name: "build",
|
||||
Value: true,
|
||||
}
|
||||
publishFlag := &cli.BoolFlag{
|
||||
Name: "publish",
|
||||
Usage: "If true, then the artifacts that are built will be published. If `--build=false` and the artifacts are found in the --destination, then those artifacts are not built and are published instead.",
|
||||
Value: true,
|
||||
}
|
||||
|
||||
verifyFlag := &cli.BoolFlag{
|
||||
Name: "verify",
|
||||
Usage: "If true, then the artifacts that are built will be verified with e2e tests or similar after being exported, depending on the artifact",
|
||||
Value: false,
|
||||
}
|
||||
|
||||
flags := flags.Join(
|
||||
[]cli.Flag{
|
||||
artifactsFlag,
|
||||
buildFlag,
|
||||
publishFlag,
|
||||
verifyFlag,
|
||||
flags.Platform,
|
||||
},
|
||||
flags.PublishFlags,
|
||||
flags.ConcurrencyFlags,
|
||||
[]cli.Flag{
|
||||
flags.Verbose,
|
||||
},
|
||||
)
|
||||
|
||||
// All of these artifacts are the registered artifacts. These should mostly stay the same no matter what.
|
||||
initializers := r.Initializers()
|
||||
|
||||
// Add all of the CLI flags that are defined by each artifact's arguments.
|
||||
m := map[string]cli.Flag{}
|
||||
|
||||
// For artifact arguments that specify flags, we'll coalesce them here and add them to the list of flags.
|
||||
for _, n := range initializers {
|
||||
for _, arg := range n.Arguments {
|
||||
for _, f := range arg.Flags {
|
||||
fn := strings.Join(f.Names(), ",")
|
||||
m[fn] = f
|
||||
slog.Debug("global flag added by argument in artifact", "flag", fn, "arg", arg.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range m {
|
||||
flags = append(flags, v)
|
||||
}
|
||||
|
||||
sort.Slice(flags, func(i, j int) bool {
|
||||
return strings.Compare(flags[i].Names()[0], flags[j].Names()[0]) <= 0
|
||||
})
|
||||
|
||||
return flags
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/frontend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
FrontendFlags = flags.PackageNameFlags
|
||||
FrontendArguments = []pipeline.Argument{
|
||||
arguments.YarnCacheDirectory,
|
||||
}
|
||||
)
|
||||
|
||||
var FrontendInitializer = Initializer{
|
||||
InitializerFunc: NewFrontendFromString,
|
||||
Arguments: FrontendArguments,
|
||||
}
|
||||
|
||||
type Frontend struct {
|
||||
Enterprise bool
|
||||
Version string
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
}
|
||||
|
||||
// The frontend does not have any artifact dependencies.
|
||||
func (f *Frontend) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository
|
||||
func (f *Frontend) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return FrontendBuilder(ctx, f.Src, f.YarnCache, opts)
|
||||
}
|
||||
|
||||
func (f *Frontend) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
panic("not implemented") // Frontend doesn't return a file
|
||||
}
|
||||
|
||||
func (f *Frontend) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
return frontend.Build(builder, f.Version), nil
|
||||
}
|
||||
|
||||
func (f *Frontend) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *Frontend) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *Frontend) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (f *Frontend) Filename(ctx context.Context) (string, error) {
|
||||
n := "grafana"
|
||||
if f.Enterprise {
|
||||
n = "grafana-enterprise"
|
||||
}
|
||||
|
||||
// Important note: this path is only used in two ways:
|
||||
// 1. When requesting an artifact be built and exported, this is the path where it will be exported to
|
||||
// 2. In a map to distinguish when the same artifact is being built more than once
|
||||
return filepath.Join(f.Version, n, "public"), nil
|
||||
}
|
||||
|
||||
func (f *Frontend) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Should never be called since this isn't a File.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Frontend) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
// Nothing to do to verify these (for now?)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFrontendFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
options, err := pipeline.ParseFlags(artifact, FrontendFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enterprise, err := options.Bool(flags.Enterprise)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src, err := GrafanaDir(ctx, state, enterprise)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version, err := state.String(ctx, arguments.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewFrontend(ctx, log, artifact, version, enterprise, src, cache)
|
||||
}
|
||||
|
||||
func NewFrontend(ctx context.Context, log *slog.Logger, artifact, version string, enterprise bool, src *dagger.Directory, cache *dagger.CacheVolume) (*pipeline.Artifact, error) {
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeDirectory,
|
||||
Flags: FrontendFlags,
|
||||
Handler: &Frontend{
|
||||
Enterprise: enterprise,
|
||||
Version: version,
|
||||
Src: src,
|
||||
YarnCache: cache,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func FrontendBuilder(
|
||||
ctx context.Context,
|
||||
src *dagger.Directory,
|
||||
cache *dagger.CacheVolume,
|
||||
opts *pipeline.ArtifactContainerOpts,
|
||||
) (*dagger.Container, error) {
|
||||
nodeVersion, err := frontend.NodeVersion(opts.Client, src).Stdout(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get node version from source code: %w", err)
|
||||
}
|
||||
|
||||
return frontend.Builder(opts.Client, opts.Platform, src, nodeVersion, cache), nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
func GrafanaDir(ctx context.Context, state pipeline.StateHandler, enterprise bool) (*dagger.Directory, error) {
|
||||
if enterprise {
|
||||
return state.Directory(ctx, arguments.EnterpriseDirectory)
|
||||
}
|
||||
return state.Directory(ctx, arguments.GrafanaDirectory)
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/frontend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
NPMPackagesFlags = flags.PackageNameFlags
|
||||
NPMPackagesArguments = []pipeline.Argument{
|
||||
arguments.YarnCacheDirectory,
|
||||
}
|
||||
)
|
||||
|
||||
var NPMPackagesInitializer = Initializer{
|
||||
InitializerFunc: NewNPMPackagesFromString,
|
||||
Arguments: NPMPackagesArguments,
|
||||
}
|
||||
|
||||
type NPMPackages struct {
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
Version string
|
||||
}
|
||||
|
||||
// The frontend does not have any artifact dependencies.
|
||||
func (f *NPMPackages) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository
|
||||
func (f *NPMPackages) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return FrontendBuilder(ctx, f.Src, f.YarnCache, opts)
|
||||
}
|
||||
|
||||
func (f *NPMPackages) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
panic("not implemented") // NPMPackages doesn't return a file
|
||||
}
|
||||
|
||||
func (f *NPMPackages) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
return frontend.NPMPackages(builder, opts.Client, opts.Log, f.Src, strings.TrimPrefix(f.Version, "v"))
|
||||
}
|
||||
|
||||
func (f *NPMPackages) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *NPMPackages) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *NPMPackages) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *NPMPackages) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Not a file
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *NPMPackages) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
// Nothing to verify (yet?)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (f *NPMPackages) Filename(ctx context.Context) (string, error) {
|
||||
// Important note: this path is only used in two ways:
|
||||
// 1. When requesting an artifact be built and exported, this is the path where it will be exported to
|
||||
// 2. In a map to distinguish when the same artifact is being built more than once
|
||||
return filepath.Join(f.Version, "npm-packages"), nil
|
||||
}
|
||||
|
||||
func NewNPMPackagesFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
grafanaDir, err := GrafanaDir(ctx, state, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version, err := state.String(ctx, arguments.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewNPMPackages(ctx, log, artifact, grafanaDir, version, cache)
|
||||
}
|
||||
|
||||
func NewNPMPackages(ctx context.Context, log *slog.Logger, artifact string, src *dagger.Directory, version string, cache *dagger.CacheVolume) (*pipeline.Artifact, error) {
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeDirectory,
|
||||
Flags: NPMPackagesFlags,
|
||||
Handler: &NPMPackages{
|
||||
Src: src,
|
||||
YarnCache: cache,
|
||||
Version: version,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/fpm"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
DebArguments = TargzArguments
|
||||
DebFlags = flags.JoinFlags(
|
||||
TargzFlags,
|
||||
[]pipeline.Flag{
|
||||
flags.NightlyFlag,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
var DebInitializer = Initializer{
|
||||
InitializerFunc: NewDebFromString,
|
||||
Arguments: TargzArguments,
|
||||
}
|
||||
|
||||
// PacakgeDeb uses a built tar.gz package to create a .deb installer for debian based Linux distributions.
|
||||
type Deb struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distribution backend.Distribution
|
||||
Enterprise bool
|
||||
NameOverride string
|
||||
|
||||
Tarball *pipeline.Artifact
|
||||
|
||||
// Src is the source tree of Grafana. This should only be used in the verify function.
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
}
|
||||
|
||||
func (d *Deb) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Tarball,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Deb) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return fpm.Builder(opts.Client), nil
|
||||
}
|
||||
|
||||
func debVersion(version string) string {
|
||||
// If there is a `+security-` modifier to the version, simply use `-`
|
||||
return strings.ReplaceAll(version, "+security-", "-")
|
||||
}
|
||||
|
||||
func (d *Deb) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
targz, err := opts.Store.File(ctx, d.Tarball)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fpm.Build(builder, fpm.BuildOpts{
|
||||
Name: d.Name,
|
||||
Enterprise: d.Enterprise,
|
||||
Version: debVersion(d.Version),
|
||||
BuildID: d.BuildID,
|
||||
Distribution: d.Distribution,
|
||||
PackageType: fpm.PackageTypeDeb,
|
||||
NameOverride: d.NameOverride,
|
||||
ConfigFiles: [][]string{
|
||||
{"/src/packaging/deb/default/grafana-server", "/pkg/etc/default/grafana-server"},
|
||||
{"/src/packaging/deb/init.d/grafana-server", "/pkg/etc/init.d/grafana-server"},
|
||||
{"/src/packaging/deb/systemd/grafana-server.service", "/pkg/usr/lib/systemd/system/grafana-server.service"},
|
||||
},
|
||||
AfterInstall: "/src/packaging/deb/control/postinst",
|
||||
BeforeRemove: "/src/packaging/deb/control/prerm",
|
||||
Depends: []string{
|
||||
"adduser",
|
||||
"musl",
|
||||
},
|
||||
EnvFolder: "/pkg/etc/default",
|
||||
ExtraArgs: []string{
|
||||
"--deb-no-default-config-files",
|
||||
},
|
||||
}, targz), nil
|
||||
}
|
||||
|
||||
func (d *Deb) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *Deb) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *Deb) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *Deb) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *Deb) Filename(ctx context.Context) (string, error) {
|
||||
name := d.Name
|
||||
if d.NameOverride != "" {
|
||||
name = packages.Name(d.NameOverride)
|
||||
}
|
||||
|
||||
return packages.FileName(name, d.Version, d.BuildID, d.Distribution, "deb")
|
||||
}
|
||||
|
||||
func (d *Deb) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return fpm.VerifyDeb(ctx, client, file, d.Src, d.YarnCache, d.Distribution, d.Enterprise)
|
||||
}
|
||||
|
||||
func (d *Deb) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewDebFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
tarball, err := NewTarballFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err := pipeline.ParseFlags(artifact, DebFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src, err := state.Directory(ctx, arguments.GrafanaDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debname := string(p.Name)
|
||||
if nightly, _ := options.Bool(flags.Nightly); nightly {
|
||||
debname += "-nightly"
|
||||
}
|
||||
if rpi, _ := options.Bool(flags.RPI); rpi {
|
||||
debname += "-rpi"
|
||||
}
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &Deb{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distribution: p.Distribution,
|
||||
Enterprise: p.Enterprise,
|
||||
Tarball: tarball,
|
||||
Src: src,
|
||||
YarnCache: yarnCache,
|
||||
NameOverride: debname,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: TargzFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/docker"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
DockerArguments = arguments.Join(
|
||||
TargzArguments,
|
||||
[]pipeline.Argument{
|
||||
arguments.DockerRegistry,
|
||||
arguments.DockerOrg,
|
||||
arguments.AlpineImage,
|
||||
arguments.UbuntuImage,
|
||||
arguments.TagFormat,
|
||||
arguments.UbuntuTagFormat,
|
||||
arguments.BoringTagFormat,
|
||||
},
|
||||
)
|
||||
DockerFlags = flags.JoinFlags(
|
||||
TargzFlags,
|
||||
flags.DockerFlags,
|
||||
)
|
||||
)
|
||||
|
||||
var DockerInitializer = Initializer{
|
||||
InitializerFunc: NewDockerFromString,
|
||||
Arguments: DockerArguments,
|
||||
}
|
||||
|
||||
// PacakgeDocker uses a built tar.gz package to create a docker image from the Dockerfile in the tar.gz
|
||||
type Docker struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distro backend.Distribution
|
||||
Enterprise bool
|
||||
|
||||
Ubuntu bool
|
||||
Registry string
|
||||
Repositories []string
|
||||
Org string
|
||||
BaseImage string
|
||||
TagFormat string
|
||||
|
||||
Tarball *pipeline.Artifact
|
||||
|
||||
// Src is the Grafana source code for running e2e tests when validating.
|
||||
// The grafana source should not be used for anything else when building a docker image. All files in the Docker image, including the Dockerfile, should be
|
||||
// from the tar.gz file.
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
}
|
||||
|
||||
func (d *Docker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Tarball,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Docker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
targz, err := opts.Store.File(ctx, d.Tarball)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return docker.Builder(opts.Client, opts.Client.Host().UnixSocket("/var/run/docker.sock"), targz), nil
|
||||
}
|
||||
|
||||
func (d *Docker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
// Unlike most other things we push to, docker image tags do not support all characters.
|
||||
// Specifically, the `+` character used in the `buildmetadata` section of semver.
|
||||
version := strings.ReplaceAll(d.Version, "+", "-")
|
||||
|
||||
tags, err := docker.Tags(d.Org, d.Registry, d.Repositories, d.TagFormat, packages.NameOpts{
|
||||
Name: d.Name,
|
||||
Version: version,
|
||||
BuildID: d.BuildID,
|
||||
Distro: d.Distro,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buildOpts := &docker.BuildOpts{
|
||||
// Tags are provided as the '-t' argument, and can include the registry domain as well as the repository.
|
||||
// Docker build supports building the same image with multiple tags.
|
||||
// You might want to also include a 'latest' version of the tag.
|
||||
Tags: tags,
|
||||
Platform: backend.Platform(d.Distro),
|
||||
BuildArgs: []string{
|
||||
"GRAFANA_TGZ=grafana.tar.gz",
|
||||
"GO_SRC=tgz-builder",
|
||||
"JS_SRC=tgz-builder",
|
||||
fmt.Sprintf("BASE_IMAGE=%s", d.BaseImage),
|
||||
},
|
||||
}
|
||||
|
||||
b := docker.Build(opts.Client, builder, buildOpts)
|
||||
|
||||
return docker.Save(b, buildOpts), nil
|
||||
}
|
||||
|
||||
func (d *Docker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
func (d *Docker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
socket := opts.Client.Host().UnixSocket("/var/run/docker.sock")
|
||||
return opts.Client.Container().From("docker").WithUnixSocket("/var/run/docker.sock", socket), nil
|
||||
}
|
||||
|
||||
func (d *Docker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (d *Docker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *Docker) Filename(ctx context.Context) (string, error) {
|
||||
ext := "docker.tar.gz"
|
||||
if d.Ubuntu {
|
||||
ext = "ubuntu.docker.tar.gz"
|
||||
}
|
||||
|
||||
return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext)
|
||||
}
|
||||
|
||||
func (d *Docker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Currently verifying riscv64 is unsupported (because alpine and ubuntu don't have riscv64 images yet)
|
||||
if _, arch := backend.OSAndArch(d.Distro); arch == "riscv64" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return docker.Verify(ctx, client, file, d.Src, d.YarnCache, d.Distro)
|
||||
}
|
||||
|
||||
func (d *Docker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
options, err := pipeline.ParseFlags(artifact, DockerFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tarball, err := NewTarballFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ubuntu, err := options.Bool(flags.Ubuntu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ubuntu Version to use as the base for the Grafana docker image (if this is a ubuntu artifact)
|
||||
// This shouldn't fail if it's not set by the user, instead it'll default to 22.04 or something.
|
||||
ubuntuImage, err := state.String(ctx, arguments.UbuntuImage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Same for Alpine
|
||||
alpineImage, err := state.String(ctx, arguments.AlpineImage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registry, err := state.String(ctx, arguments.DockerRegistry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org, err := state.String(ctx, arguments.DockerOrg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repos, err := options.StringSlice(flags.DockerRepositories)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, err := state.String(ctx, arguments.TagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ubuntuFormat, err := state.String(ctx, arguments.UbuntuTagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
boringFormat, err := state.String(ctx, arguments.BoringTagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base := alpineImage
|
||||
if ubuntu {
|
||||
format = ubuntuFormat
|
||||
base = ubuntuImage
|
||||
}
|
||||
|
||||
if p.Name == packages.PackageEnterpriseBoring {
|
||||
format = boringFormat
|
||||
}
|
||||
|
||||
src, err := state.Directory(ctx, arguments.GrafanaDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("initializing Docker artifact", "Org", org, "registry", registry, "repos", repos, "tag", format)
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &Docker{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distro: p.Distribution,
|
||||
Enterprise: p.Enterprise,
|
||||
Tarball: tarball,
|
||||
|
||||
Ubuntu: ubuntu,
|
||||
BaseImage: base,
|
||||
Registry: registry,
|
||||
Org: org,
|
||||
Repositories: repos,
|
||||
TagFormat: format,
|
||||
|
||||
Src: src,
|
||||
YarnCache: yarnCache,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: DockerFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/docker"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
EntDockerArguments = arguments.Join(
|
||||
DebArguments,
|
||||
[]pipeline.Argument{
|
||||
arguments.HGDirectory,
|
||||
arguments.EntDockerRegistry,
|
||||
arguments.EntDockerOrg,
|
||||
arguments.EntDockerRepo,
|
||||
arguments.HGTagFormat,
|
||||
},
|
||||
)
|
||||
EntDockerFlags = flags.JoinFlags(
|
||||
DebFlags,
|
||||
flags.DockerFlags,
|
||||
)
|
||||
)
|
||||
|
||||
var EntDockerInitializer = Initializer{
|
||||
InitializerFunc: NewEntDockerFromString,
|
||||
Arguments: EntDockerArguments,
|
||||
}
|
||||
|
||||
// EntDocker uses a built deb installer to create a docker image
|
||||
type EntDocker struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distro backend.Distribution
|
||||
EntDir *dagger.Directory
|
||||
|
||||
// EntRegistry is the docker registry when using the `enterprise` name. (e.g. hub.docker.io)
|
||||
EntRegistry string
|
||||
// EntOrg is the docker org when using the `enterprise` name. (e.g. grafana)
|
||||
EntOrg string
|
||||
// EntOrg is the docker repo when using the `enterprise` name. (e.g. grafana-enterprise)
|
||||
EntRepo string
|
||||
// TagFormat is the docker tag format when using the `enterprise` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }})
|
||||
TagFormat string
|
||||
|
||||
Deb *pipeline.Artifact
|
||||
}
|
||||
|
||||
func (d *EntDocker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Deb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *EntDocker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
deb, err := opts.Store.File(ctx, d.Deb)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting deb from state: %w", err)
|
||||
}
|
||||
|
||||
socket := opts.Client.Host().UnixSocket("/var/run/docker.sock")
|
||||
|
||||
return opts.Client.Container().From("docker").
|
||||
WithUnixSocket("/var/run/docker.sock", socket).
|
||||
WithMountedDirectory("/src", d.EntDir).
|
||||
WithMountedFile("/src/grafana.deb", deb).
|
||||
WithWorkdir("/src"), nil
|
||||
}
|
||||
|
||||
func (d *EntDocker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
tags, err := docker.Tags(d.EntOrg, d.EntRegistry, []string{d.EntRepo}, d.TagFormat, packages.NameOpts{
|
||||
Name: d.Name,
|
||||
Version: d.Version,
|
||||
BuildID: d.BuildID,
|
||||
Distro: d.Distro,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builder = docker.Build(opts.Client, builder, &docker.BuildOpts{
|
||||
Dockerfile: "./docker/hosted-grafana-all/Dockerfile",
|
||||
Tags: tags,
|
||||
Target: "hosted-grafana-localenterprise",
|
||||
Platform: dagger.Platform("linux/amd64"),
|
||||
BuildArgs: []string{
|
||||
"RELEASE_TYPE=main",
|
||||
// I think because deb files use a ~ as a version delimiter of some kind, so the hg docker image uses that instead of a -
|
||||
fmt.Sprintf("GRAFANA_VERSION=%s", strings.Replace(d.Version, "-", "~", 1)),
|
||||
},
|
||||
})
|
||||
|
||||
// Save the resulting docker image to the local filesystem
|
||||
return builder.WithExec([]string{"docker", "save", tags[0], "-o", "enterprise.tar"}).File("enterprise.tar"), nil
|
||||
}
|
||||
|
||||
func (d *EntDocker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
func (d *EntDocker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (d *EntDocker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (d *EntDocker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *EntDocker) Filename(ctx context.Context) (string, error) {
|
||||
ext := "docker-enterprise.tar.gz"
|
||||
|
||||
return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext)
|
||||
}
|
||||
|
||||
func (d *EntDocker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *EntDocker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewEntDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
options, err := pipeline.ParseFlags(artifact, DockerFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deb, err := NewDebFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entRegistry, err := state.String(ctx, arguments.EntDockerRegistry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entOrg, err := state.String(ctx, arguments.EntDockerOrg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entRepo, err := state.String(ctx, arguments.EntDockerRepo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagFormat, err := state.String(ctx, arguments.HGTagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir, err := state.Directory(ctx, arguments.HGDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("initializing Enterprise Docker artifact", "Org", entOrg, "registry", entRegistry, "repo", entRepo, "tag", tagFormat)
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &EntDocker{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distro: p.Distribution,
|
||||
EntDir: dir,
|
||||
Deb: deb,
|
||||
|
||||
EntRegistry: entRegistry,
|
||||
EntOrg: entOrg,
|
||||
EntRepo: entRepo,
|
||||
TagFormat: tagFormat,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: DockerFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/docker"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
ProDockerArguments = arguments.Join(
|
||||
DebArguments,
|
||||
[]pipeline.Argument{
|
||||
arguments.HGDirectory,
|
||||
arguments.ProDockerRegistry,
|
||||
arguments.ProDockerOrg,
|
||||
arguments.ProDockerRepo,
|
||||
arguments.HGTagFormat,
|
||||
},
|
||||
)
|
||||
ProDockerFlags = flags.JoinFlags(
|
||||
DebFlags,
|
||||
flags.DockerFlags,
|
||||
)
|
||||
)
|
||||
|
||||
var ProDockerInitializer = Initializer{
|
||||
InitializerFunc: NewProDockerFromString,
|
||||
Arguments: ProDockerArguments,
|
||||
}
|
||||
|
||||
// ProDocker uses a built deb installer to create a docker image
|
||||
type ProDocker struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distro backend.Distribution
|
||||
ProDir *dagger.Directory
|
||||
|
||||
// ProRegistry is the docker registry when using the `pro` name. (e.g. hub.docker.io)
|
||||
ProRegistry string
|
||||
// ProOrg is the docker org when using the `pro` name. (e.g. grafana)
|
||||
ProOrg string
|
||||
// ProOrg is the docker repo when using the `pro` name. (e.g. grafana-pro)
|
||||
ProRepo string
|
||||
// TagFormat is the docker tag format when using the `pro` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }})
|
||||
TagFormat string
|
||||
|
||||
// Building the Pro image requires a Debian package instead of a tar.gz
|
||||
Deb *pipeline.Artifact
|
||||
}
|
||||
|
||||
func (d *ProDocker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Deb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *ProDocker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
deb, err := opts.Store.File(ctx, d.Deb)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting deb from state: %w", err)
|
||||
}
|
||||
|
||||
socket := opts.Client.Host().UnixSocket("/var/run/docker.sock")
|
||||
|
||||
return opts.Client.Container().From("docker").
|
||||
WithUnixSocket("/var/run/docker.sock", socket).
|
||||
WithMountedDirectory("/src", d.ProDir).
|
||||
WithMountedFile("/src/grafana.deb", deb).
|
||||
WithWorkdir("/src"), nil
|
||||
}
|
||||
|
||||
func (d *ProDocker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
tags, err := docker.Tags(d.ProOrg, d.ProRegistry, []string{d.ProRepo}, d.TagFormat, packages.NameOpts{
|
||||
Name: d.Name,
|
||||
Version: d.Version,
|
||||
BuildID: d.BuildID,
|
||||
Distro: d.Distro,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builder = docker.Build(opts.Client, builder, &docker.BuildOpts{
|
||||
Dockerfile: "./docker/hosted-grafana-all/Dockerfile",
|
||||
Tags: tags,
|
||||
Target: "hosted-grafana-localpro",
|
||||
Platform: dagger.Platform("linux/amd64"),
|
||||
BuildArgs: []string{
|
||||
"RELEASE_TYPE=main",
|
||||
// I think because deb files use a ~ as a version delimiter of some kind, so the hg docker image uses that instead of a -
|
||||
fmt.Sprintf("GRAFANA_VERSION=%s", strings.Replace(d.Version, "-", "~", 1)),
|
||||
},
|
||||
})
|
||||
|
||||
// Save the resulting docker image to the local filesystem
|
||||
return builder.WithExec([]string{"docker", "save", tags[0], "-o", "pro.tar"}).File("pro.tar"), nil
|
||||
}
|
||||
|
||||
func (d *ProDocker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
func (d *ProDocker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (d *ProDocker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (d *ProDocker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *ProDocker) Filename(ctx context.Context) (string, error) {
|
||||
ext := "docker-pro.tar.gz"
|
||||
|
||||
return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext)
|
||||
}
|
||||
|
||||
func (d *ProDocker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *ProDocker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewProDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
options, err := pipeline.ParseFlags(artifact, DockerFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deb, err := NewDebFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proRegistry, err := state.String(ctx, arguments.ProDockerRegistry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proOrg, err := state.String(ctx, arguments.ProDockerOrg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proRepo, err := state.String(ctx, arguments.ProDockerRepo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagFormat, err := state.String(ctx, arguments.HGTagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir, err := state.Directory(ctx, arguments.HGDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("initializing Pro Docker artifact", "Org", proOrg, "registry", proRegistry, "repo", proRepo, "tag", tagFormat)
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &ProDocker{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distro: p.Distribution,
|
||||
ProDir: dir,
|
||||
Deb: deb,
|
||||
|
||||
ProRegistry: proRegistry,
|
||||
ProOrg: proOrg,
|
||||
ProRepo: proRepo,
|
||||
TagFormat: tagFormat,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: DockerFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/msi"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
MSIArguments = TargzArguments
|
||||
MSIFlags = TargzFlags
|
||||
)
|
||||
|
||||
var MSIInitializer = Initializer{
|
||||
InitializerFunc: NewMSIFromString,
|
||||
Arguments: TargzArguments,
|
||||
}
|
||||
|
||||
// PacakgeMSI uses a built tar.gz package to create a .exe installer for exeian based Linux distributions.
|
||||
type MSI struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distribution backend.Distribution
|
||||
Enterprise bool
|
||||
Grafana *dagger.Directory
|
||||
|
||||
Tarball *pipeline.Artifact
|
||||
}
|
||||
|
||||
func (d *MSI) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Tarball,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *MSI) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return msi.Builder(opts.Client, d.Grafana), nil
|
||||
}
|
||||
|
||||
func (d *MSI) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
targz, err := opts.Store.File(ctx, d.Tarball)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msi.Build(opts.Client, builder, targz, d.Version, d.Enterprise)
|
||||
}
|
||||
|
||||
func (d *MSI) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
// Not a directory so this shouldn't be called
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *MSI) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *MSI) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *MSI) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
// Not a directory so this shouldn't be called
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *MSI) Filename(ctx context.Context) (string, error) {
|
||||
return packages.FileName(d.Name, d.Version, d.BuildID, d.Distribution, "msi")
|
||||
}
|
||||
|
||||
func (d *MSI) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *MSI) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewMSIFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
targz, err := NewTarballFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err := pipeline.ParseFlags(artifact, MSIFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !backend.IsWindows(p.Distribution) {
|
||||
return nil, fmt.Errorf("distribution ('%s') for exe '%s' is not a Windows distribution", string(p.Distribution), artifact)
|
||||
}
|
||||
|
||||
src, err := GrafanaDir(ctx, state, p.Enterprise)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &MSI{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distribution: p.Distribution,
|
||||
Enterprise: p.Enterprise,
|
||||
Tarball: targz,
|
||||
Grafana: src,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: ZipFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/fpm"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/gpg"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
RPMArguments = TargzArguments
|
||||
RPMFlags = flags.JoinFlags(
|
||||
TargzFlags,
|
||||
[]pipeline.Flag{
|
||||
flags.SignFlag,
|
||||
flags.NightlyFlag,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
var RPMInitializer = Initializer{
|
||||
InitializerFunc: NewRPMFromString,
|
||||
Arguments: arguments.Join(
|
||||
TargzArguments,
|
||||
[]pipeline.Argument{
|
||||
arguments.GPGPublicKey,
|
||||
arguments.GPGPrivateKey,
|
||||
arguments.GPGPassphrase,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
// PacakgeRPM uses a built tar.gz package to create a .rpm installer for RHEL-ish Linux distributions.
|
||||
type RPM struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distribution backend.Distribution
|
||||
Enterprise bool
|
||||
Sign bool
|
||||
NameOverride string
|
||||
|
||||
GPGPublicKey string
|
||||
GPGPrivateKey string
|
||||
GPGPassphrase string
|
||||
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
|
||||
Tarball *pipeline.Artifact
|
||||
}
|
||||
|
||||
func (d *RPM) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Tarball,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *RPM) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return fpm.Builder(opts.Client), nil
|
||||
}
|
||||
|
||||
func rpmVersion(version string) string {
|
||||
// https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/#_snapshots
|
||||
// If there's a buildmeta revision, then use that as a snapshot version
|
||||
return strings.ReplaceAll(version, "+", "^")
|
||||
}
|
||||
|
||||
func (d *RPM) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
targz, err := opts.Store.File(ctx, d.Tarball)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpm := fpm.Build(builder, fpm.BuildOpts{
|
||||
Name: d.Name,
|
||||
Enterprise: d.Enterprise,
|
||||
Version: rpmVersion(d.Version),
|
||||
BuildID: d.BuildID,
|
||||
Distribution: d.Distribution,
|
||||
PackageType: fpm.PackageTypeRPM,
|
||||
NameOverride: d.NameOverride,
|
||||
ConfigFiles: [][]string{
|
||||
{"/src/packaging/rpm/sysconfig/grafana-server", "/pkg/etc/sysconfig/grafana-server"},
|
||||
{"/src/packaging/rpm/systemd/grafana-server.service", "/pkg/usr/lib/systemd/system/grafana-server.service"},
|
||||
},
|
||||
AfterInstall: "/src/packaging/rpm/control/postinst",
|
||||
Depends: []string{
|
||||
"/sbin/service",
|
||||
},
|
||||
ExtraArgs: []string{
|
||||
"--rpm-posttrans=/src/packaging/rpm/control/posttrans",
|
||||
"--rpm-digest=sha256",
|
||||
},
|
||||
EnvFolder: "/pkg/etc/sysconfig",
|
||||
}, targz)
|
||||
|
||||
if !d.Sign {
|
||||
return rpm, nil
|
||||
}
|
||||
return gpg.Sign(opts.Client, rpm, gpg.GPGOpts{
|
||||
GPGPublicKey: d.GPGPublicKey,
|
||||
GPGPrivateKey: d.GPGPrivateKey,
|
||||
GPGPassphrase: d.GPGPassphrase,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (d *RPM) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *RPM) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *RPM) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *RPM) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *RPM) Filename(ctx context.Context) (string, error) {
|
||||
name := d.Name
|
||||
if d.NameOverride != "" {
|
||||
name = packages.Name(d.NameOverride)
|
||||
}
|
||||
|
||||
return packages.FileName(name, d.Version, d.BuildID, d.Distribution, "rpm")
|
||||
}
|
||||
|
||||
func (d *RPM) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
// return fpm.VerifyRpm(ctx, client, file, d.Src, d.YarnCache, d.Distribution, d.Enterprise, d.Sign, d.GPGPublicKey, d.GPGPrivateKey, d.GPGPassphrase)
|
||||
}
|
||||
|
||||
func (d *RPM) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewRPMFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
tarball, err := NewTarballFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err := pipeline.ParseFlags(artifact, RPMFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sign, err := options.Bool(flags.Sign)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src, err := state.Directory(ctx, arguments.GrafanaDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var gpgPublicKey, gpgPrivateKey, gpgPassphrase string
|
||||
|
||||
if sign {
|
||||
pubb64, err := state.String(ctx, arguments.GPGPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pub, err := base64.StdEncoding.DecodeString(pubb64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gpg-private-key-base64 cannot be decoded %w", err)
|
||||
}
|
||||
|
||||
privb64, err := state.String(ctx, arguments.GPGPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv, err := base64.StdEncoding.DecodeString(privb64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gpg-private-key-base64 cannot be decoded %w", err)
|
||||
}
|
||||
|
||||
pass, err := state.String(ctx, arguments.GPGPassphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gpgPublicKey = string(pub)
|
||||
gpgPrivateKey = string(priv)
|
||||
gpgPassphrase = pass
|
||||
}
|
||||
|
||||
rpmname := string(p.Name)
|
||||
if nightly, _ := options.Bool(flags.Nightly); nightly {
|
||||
rpmname += "-nightly"
|
||||
}
|
||||
if rpi, _ := options.Bool(flags.RPI); rpi {
|
||||
rpmname += "-rpi"
|
||||
}
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &RPM{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distribution: p.Distribution,
|
||||
Enterprise: p.Enterprise,
|
||||
Tarball: tarball,
|
||||
Sign: sign,
|
||||
Src: src,
|
||||
YarnCache: yarnCache,
|
||||
GPGPublicKey: gpgPublicKey,
|
||||
GPGPrivateKey: gpgPrivateKey,
|
||||
GPGPassphrase: gpgPassphrase,
|
||||
NameOverride: rpmname,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: TargzFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/containers"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/e2e"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/frontend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/targz"
|
||||
)
|
||||
|
||||
var (
|
||||
TargzArguments = []pipeline.Argument{
|
||||
// Tarballs need the Build ID and version for naming the package properly.
|
||||
arguments.BuildID,
|
||||
arguments.Version,
|
||||
|
||||
// The grafanadirectory has contents like the LICENSE.txt and such that need to be included in the package
|
||||
arguments.GrafanaDirectory,
|
||||
|
||||
// The go version used to build the backend
|
||||
arguments.GoVersion,
|
||||
arguments.ViceroyVersion,
|
||||
arguments.YarnCacheDirectory,
|
||||
}
|
||||
TargzFlags = flags.JoinFlags(
|
||||
flags.StdPackageFlags(),
|
||||
)
|
||||
)
|
||||
|
||||
var TargzInitializer = Initializer{
|
||||
InitializerFunc: NewTarballFromString,
|
||||
Arguments: TargzArguments,
|
||||
}
|
||||
|
||||
type Tarball struct {
|
||||
Distribution backend.Distribution
|
||||
Name packages.Name
|
||||
BuildID string
|
||||
Version string
|
||||
GoVersion string
|
||||
Enterprise bool
|
||||
|
||||
Grafana *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
|
||||
// Dependent artifacts
|
||||
Backend *pipeline.Artifact
|
||||
Frontend *pipeline.Artifact
|
||||
NPMPackages *pipeline.Artifact
|
||||
BundledPlugins *pipeline.Artifact
|
||||
Storybook *pipeline.Artifact
|
||||
}
|
||||
|
||||
func NewTarballFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
goVersion, err := state.String(ctx, arguments.GoVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
viceroyVersion, err := state.String(ctx, arguments.ViceroyVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1. Figure out the options that were provided as part of the artifact string.
|
||||
// For example, `linux/amd64:grafana`.
|
||||
options, err := pipeline.ParseFlags(artifact, TargzFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
static, err := options.Bool(flags.Static)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wireTag, err := options.String(flags.WireTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tags, err := options.StringSlice(flags.GoTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
experiments, err := options.StringSlice(flags.GoExperiments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goModCache, err := state.CacheVolume(ctx, arguments.GoModCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
goBuildCache, err := state.CacheVolume(ctx, arguments.GoBuildCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Initializing tar.gz artifact with options", "name", p.Name, "build ID", p.BuildID, "version", p.Version, "distro", p.Distribution, "static", static, "enterprise", p.Enterprise)
|
||||
|
||||
src, err := GrafanaDir(ctx, state, p.Enterprise)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTarball(ctx, log, artifact, p.Distribution, p.Enterprise, p.Name, p.Version, p.BuildID, src, yarnCache, goModCache, goBuildCache, static, wireTag, tags, goVersion, viceroyVersion, experiments)
|
||||
}
|
||||
|
||||
// NewTarball returns a properly initialized Tarball artifact.
|
||||
// There are a lot of options that can affect how a tarball is built; most of which define different ways for the backend to be built.
|
||||
func NewTarball(
|
||||
ctx context.Context,
|
||||
log *slog.Logger,
|
||||
artifact string,
|
||||
distro backend.Distribution,
|
||||
enterprise bool,
|
||||
name packages.Name,
|
||||
version string,
|
||||
buildID string,
|
||||
src *dagger.Directory,
|
||||
cache *dagger.CacheVolume,
|
||||
goModCache *dagger.CacheVolume,
|
||||
goBuildCache *dagger.CacheVolume,
|
||||
static bool,
|
||||
wireTag string,
|
||||
tags []string,
|
||||
goVersion string,
|
||||
viceroyVersion string,
|
||||
experiments []string,
|
||||
) (*pipeline.Artifact, error) {
|
||||
backendArtifact, err := NewBackend(ctx, log, artifact, &NewBackendOpts{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Distribution: distro,
|
||||
Src: src,
|
||||
Static: static,
|
||||
WireTag: wireTag,
|
||||
Tags: tags,
|
||||
GoVersion: goVersion,
|
||||
ViceroyVersion: viceroyVersion,
|
||||
Experiments: experiments,
|
||||
Enterprise: enterprise,
|
||||
GoBuildCache: goBuildCache,
|
||||
GoModCache: goModCache,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frontendArtifact, err := NewFrontend(ctx, log, artifact, version, enterprise, src, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bundledPluginsArtifact, err := NewBundledPlugins(ctx, log, artifact, src, version, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
npmArtifact, err := NewNPMPackages(ctx, log, artifact, src, version, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storybookArtifact, err := NewStorybook(ctx, log, artifact, src, version, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tarball := &Tarball{
|
||||
Name: name,
|
||||
Distribution: distro,
|
||||
Version: version,
|
||||
GoVersion: goVersion,
|
||||
BuildID: buildID,
|
||||
Grafana: src,
|
||||
Enterprise: enterprise,
|
||||
YarnCache: cache,
|
||||
|
||||
Backend: backendArtifact,
|
||||
Frontend: frontendArtifact,
|
||||
NPMPackages: npmArtifact,
|
||||
BundledPlugins: bundledPluginsArtifact,
|
||||
Storybook: storybookArtifact,
|
||||
}
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: tarball,
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: TargzFlags,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Tarball) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
version := t.Version
|
||||
|
||||
container := opts.Client.Container().
|
||||
From("alpine:3.18.4").
|
||||
WithExec([]string{"apk", "add", "--update", "tar"}).
|
||||
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("echo %s > VERSION", version)})
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (t *Tarball) BuildFile(ctx context.Context, b *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
var (
|
||||
state = opts.State
|
||||
log = opts.Log
|
||||
)
|
||||
|
||||
log.Debug("Getting grafana dir from state...")
|
||||
// The Grafana directory is used for other packaged data like Dockerfile, license.txt, etc.
|
||||
grafanaDir := t.Grafana
|
||||
|
||||
backendDir, err := opts.Store.Directory(ctx, t.Backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
frontendDir, err := opts.Store.Directory(ctx, t.Frontend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
npmDir, err := opts.Store.Directory(ctx, t.NPMPackages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storybookDir, err := opts.Store.Directory(ctx, t.Storybook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginsDir, err := opts.Store.Directory(ctx, t.BundledPlugins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version, err := state.String(ctx, arguments.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files := []targz.MappedFile{
|
||||
targz.NewMappedFile("VERSION", b.File("VERSION")),
|
||||
targz.NewMappedFile("LICENSE", grafanaDir.File("LICENSE")),
|
||||
targz.NewMappedFile("NOTICE.md", grafanaDir.File("NOTICE.md")),
|
||||
targz.NewMappedFile("README.md", grafanaDir.File("README.md")),
|
||||
targz.NewMappedFile("Dockerfile", grafanaDir.File("Dockerfile")),
|
||||
targz.NewMappedFile("tools/zoneinfo.zip", opts.Client.Container().From(fmt.Sprintf("golang:%s", t.GoVersion)).File("/usr/local/go/lib/time/zoneinfo.zip")),
|
||||
}
|
||||
|
||||
directories := []targz.MappedDirectory{
|
||||
targz.NewMappedDir("conf", grafanaDir.Directory("conf")),
|
||||
targz.NewMappedDir("docs/sources", grafanaDir.Directory("docs/sources")),
|
||||
targz.NewMappedDir("packaging/deb", grafanaDir.Directory("packaging/deb")),
|
||||
targz.NewMappedDir("packaging/rpm", grafanaDir.Directory("packaging/rpm")),
|
||||
targz.NewMappedDir("packaging/docker", grafanaDir.Directory("packaging/docker")),
|
||||
targz.NewMappedDir("packaging/wrappers", grafanaDir.Directory("packaging/wrappers")),
|
||||
targz.NewMappedDir("bin", backendDir),
|
||||
targz.NewMappedDir("public", frontendDir),
|
||||
targz.NewMappedDir("npm-artifacts", npmDir),
|
||||
targz.NewMappedDir("storybook", storybookDir),
|
||||
targz.NewMappedDir("plugins-bundled", pluginsDir),
|
||||
}
|
||||
|
||||
root := fmt.Sprintf("grafana-%s", version)
|
||||
|
||||
return targz.Build(
|
||||
b,
|
||||
&targz.Opts{
|
||||
Root: root,
|
||||
Files: files,
|
||||
Directories: directories,
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (t *Tarball) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (t *Tarball) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (t *Tarball) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tarball) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (t *Tarball) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Currently verifying riscv64 is unsupported (because alpine and ubuntu don't have riscv64 images yet)
|
||||
// windows/darwin verification may never be supported.
|
||||
os, arch := backend.OSAndArch(t.Distribution)
|
||||
if os != "linux" || arch == "riscv64" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return verifyTarball(ctx, client, file, t.Grafana, t.YarnCache, t.Distribution, t.Enterprise)
|
||||
}
|
||||
|
||||
func (t *Tarball) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (t *Tarball) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
t.Backend,
|
||||
t.Frontend,
|
||||
t.NPMPackages,
|
||||
t.BundledPlugins,
|
||||
t.Storybook,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Tarball) Filename(ctx context.Context) (string, error) {
|
||||
return packages.FileName(t.Name, t.Version, t.BuildID, t.Distribution, "tar.gz")
|
||||
}
|
||||
|
||||
func verifyTarball(
|
||||
ctx context.Context,
|
||||
d *dagger.Client,
|
||||
pkg *dagger.File,
|
||||
src *dagger.Directory,
|
||||
yarnCache *dagger.CacheVolume,
|
||||
distro backend.Distribution,
|
||||
enterprise bool,
|
||||
) error {
|
||||
nodeVersion, err := frontend.NodeVersion(d, src).Stdout(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get node version from source code: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
platform = backend.Platform(distro)
|
||||
archive = containers.ExtractedArchive(d, pkg)
|
||||
)
|
||||
|
||||
// This grafana service runs in the background for the e2e tests
|
||||
service := d.Container(dagger.ContainerOpts{
|
||||
Platform: platform,
|
||||
}).From("ubuntu:22.04").
|
||||
WithExec([]string{"apt-get", "update", "-yq"}).
|
||||
WithExec([]string{"apt-get", "install", "-yq", "ca-certificates"}).
|
||||
WithDirectory("/src", archive).
|
||||
WithMountedTemp("/tmp").
|
||||
WithWorkdir("/src")
|
||||
|
||||
if err := e2e.ValidateLicense(ctx, service, "/src/LICENSE", enterprise); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svc := service.
|
||||
WithEnvVariable("GF_PATHS_PLUGINS", "/tmp").
|
||||
WithEnvVariable("GF_LOG_LEVEL", "error").
|
||||
WithExposedPort(3000).AsService(dagger.ContainerAsServiceOpts{
|
||||
Args: []string{"./bin/grafana", "server"},
|
||||
})
|
||||
|
||||
if _, err := containers.ExitError(ctx, e2e.ValidatePackage(d, svc, src, yarnCache, nodeVersion)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/zip"
|
||||
)
|
||||
|
||||
var (
|
||||
ZipArguments = TargzArguments
|
||||
ZipFlags = TargzFlags
|
||||
)
|
||||
|
||||
var ZipInitializer = Initializer{
|
||||
InitializerFunc: NewZipFromString,
|
||||
Arguments: TargzArguments,
|
||||
}
|
||||
|
||||
// PacakgeZip uses a built tar.gz package to create a .zip package for zipian based Linux distributions.
|
||||
type Zip struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distribution backend.Distribution
|
||||
Enterprise bool
|
||||
|
||||
Tarball *pipeline.Artifact
|
||||
}
|
||||
|
||||
func (d *Zip) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Tarball,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Zip) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return zip.Builder(opts.Client), nil
|
||||
}
|
||||
|
||||
func (d *Zip) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
targz, err := opts.Store.File(ctx, d.Tarball)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return zip.Build(builder, targz), nil
|
||||
}
|
||||
|
||||
func (d *Zip) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *Zip) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *Zip) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Zip) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (d *Zip) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Zip) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *Zip) Filename(ctx context.Context) (string, error) {
|
||||
return packages.FileName(d.Name, d.Version, d.BuildID, d.Distribution, "zip")
|
||||
}
|
||||
|
||||
func NewZipFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
tarball, err := NewTarballFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err := pipeline.ParseFlags(artifact, ZipFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &Zip{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distribution: p.Distribution,
|
||||
Enterprise: p.Enterprise,
|
||||
Tarball: tarball,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: TargzFlags,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
type PackageDetails struct {
|
||||
Name packages.Name
|
||||
Enterprise bool
|
||||
Version string
|
||||
BuildID string
|
||||
Distribution backend.Distribution
|
||||
}
|
||||
|
||||
func GetPackageDetails(ctx context.Context, options *pipeline.OptionsHandler, state pipeline.StateHandler) (PackageDetails, error) {
|
||||
distro, err := options.String(flags.Distribution)
|
||||
if err != nil {
|
||||
return PackageDetails{}, err
|
||||
}
|
||||
version, err := state.String(ctx, arguments.Version)
|
||||
if err != nil {
|
||||
return PackageDetails{}, err
|
||||
}
|
||||
buildID, err := state.String(ctx, arguments.BuildID)
|
||||
if err != nil {
|
||||
return PackageDetails{}, err
|
||||
}
|
||||
|
||||
name, err := options.String(flags.PackageName)
|
||||
if err != nil {
|
||||
return PackageDetails{}, err
|
||||
}
|
||||
|
||||
enterprise, err := options.Bool(flags.Enterprise)
|
||||
if err != nil {
|
||||
return PackageDetails{}, err
|
||||
}
|
||||
|
||||
return PackageDetails{
|
||||
Name: packages.Name(name),
|
||||
Version: version,
|
||||
BuildID: buildID,
|
||||
Distribution: backend.Distribution(distro),
|
||||
Enterprise: enterprise,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorArtifactCollision = errors.New("artifact argument specifies two different artifacts")
|
||||
ErrorDuplicateArgument = errors.New("artifact argument specifies duplicate or incompatible arguments")
|
||||
ErrorNoArtifact = errors.New("could not find compatible artifact for argument string")
|
||||
|
||||
ErrorFlagNotFound = errors.New("no option available for the given flag")
|
||||
)
|
||||
|
||||
func findInitializer(val string, initializers map[string]Initializer) (Initializer, error) {
|
||||
c := strings.Split(val, ":")
|
||||
var initializer *Initializer
|
||||
|
||||
// Find the artifact that is requested by `val`.
|
||||
// The artifact can be defined anywhere in the artifact string. Example: `linux/amd64:grafana:targz` or `linux/amd64:grafana:targz` are the same, where targz is the artifact.
|
||||
for _, v := range c {
|
||||
n, ok := initializers[v]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if initializer != nil {
|
||||
return Initializer{}, fmt.Errorf("%s: %w", val, ErrorArtifactCollision)
|
||||
}
|
||||
|
||||
initializer = &n
|
||||
}
|
||||
|
||||
if initializer == nil {
|
||||
return Initializer{}, fmt.Errorf("%s: %w", val, ErrorNoArtifact)
|
||||
}
|
||||
|
||||
return *initializer, nil
|
||||
}
|
||||
|
||||
// The ArtifactsFromStrings function should provide all of the necessary arguments to produce each artifact
|
||||
// dleimited by colons. It's a repeated flag, so all permutations are stored in 1 instance of the ArtifactsFlag struct.
|
||||
// Examples:
|
||||
// * targz:linux/amd64 -- Will produce a "Grafana" tar.gz for "linux/amd64".
|
||||
// * targz:enterprise:linux/amd64 -- Will produce a "Grafana" tar.gz for "linux/amd64".
|
||||
func ArtifactsFromStrings(ctx context.Context, log *slog.Logger, a []string, registered map[string]Initializer, state pipeline.StateHandler) ([]*pipeline.Artifact, error) {
|
||||
artifacts := make([]*pipeline.Artifact, len(a))
|
||||
for i, v := range a {
|
||||
n, err := Parse(ctx, log, v, registered, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
artifacts[i] = n
|
||||
}
|
||||
|
||||
return artifacts, nil
|
||||
}
|
||||
|
||||
// Parse parses the artifact string `artifact` and finds the matching initializer.
|
||||
func Parse(ctx context.Context, log *slog.Logger, artifact string, initializers map[string]Initializer, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
artifact = strings.TrimSpace(artifact)
|
||||
initializer, err := findInitializer(artifact, initializers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initializerFunc := initializer.InitializerFunc
|
||||
// TODO soon, the initializer might need more info about flags
|
||||
return initializerFunc(ctx, log, artifact, state)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package artifacts_test
|
||||
|
||||
// var TestArtifact struct {
|
||||
// }
|
||||
//
|
||||
// func TestParse(t *testing.T) {
|
||||
// v := "artifact:flag1:flag2"
|
||||
//
|
||||
// exampleArtifact := &pipeline.Artifact{
|
||||
// Name: "example",
|
||||
// }
|
||||
//
|
||||
// argument1 := &pipeline.Argument{
|
||||
// Name: "argument1",
|
||||
// }
|
||||
//
|
||||
// argument2 := &pipeline.Argument{
|
||||
// Name: "argument2",
|
||||
// }
|
||||
//
|
||||
// res, err := artifacts.Parse(v, map[string]artifacts.ArgumentOption{
|
||||
// "artifact": {Artifact: exampleArtifact},
|
||||
// "argument1": {Arguments: []*pipeline.Argument{argument1}},
|
||||
// "argument2": {Arguments: []*pipeline.Argument{argument2}},
|
||||
// })
|
||||
//
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// if res.Artifact.Name != exampleArtifact.Name {
|
||||
// t.Fatal("Parse should return the example artifact")
|
||||
// }
|
||||
//
|
||||
// if len(res.Arguments) != 2 {
|
||||
// t.Fatal("Parse should return 2 Arguments")
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,91 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"path"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/frontend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
BundledPluginsFlags = flags.PackageNameFlags
|
||||
BundledPluginsArguments = []pipeline.Argument{
|
||||
arguments.YarnCacheDirectory,
|
||||
}
|
||||
)
|
||||
|
||||
type BundledPlugins struct {
|
||||
Name packages.Name
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
Version string
|
||||
}
|
||||
|
||||
// The frontend does not have any artifact dependencies.
|
||||
func (f *BundledPlugins) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository
|
||||
func (f *BundledPlugins) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return FrontendBuilder(ctx, f.Src, f.YarnCache, opts)
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
panic("not implemented") // BundledPlugins doesn't return a file
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
return frontend.BuildPlugins(builder), nil
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *BundledPlugins) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (f *BundledPlugins) Filename(ctx context.Context) (string, error) {
|
||||
// Important note: this path is only used in two ways:
|
||||
// 1. When requesting an artifact be built and exported, this is the path where it will be exported to
|
||||
// 2. In a map to distinguish when the same artifact is being built more than once
|
||||
return path.Join("bin", "bundled-plugins"), nil
|
||||
}
|
||||
|
||||
func NewBundledPlugins(ctx context.Context, log *slog.Logger, artifact string, src *dagger.Directory, version string, cacheVolume *dagger.CacheVolume) (*pipeline.Artifact, error) {
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeDirectory,
|
||||
Flags: BundledPluginsFlags,
|
||||
Handler: &BundledPlugins{
|
||||
Src: src,
|
||||
YarnCache: cacheVolume,
|
||||
Version: version,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package artifacts
|
||||
|
||||
import "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
|
||||
type Initializer struct {
|
||||
InitializerFunc pipeline.ArtifactInitializer
|
||||
Arguments []pipeline.Argument
|
||||
}
|
||||
|
||||
type Registerer interface {
|
||||
Register(string, Initializer) error
|
||||
Initializers() map[string]Initializer
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package artifacts
|
||||
@@ -0,0 +1,113 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/frontend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
StorybookFlags = flags.PackageNameFlags
|
||||
StorybookArguments = []pipeline.Argument{
|
||||
arguments.YarnCacheDirectory,
|
||||
}
|
||||
)
|
||||
|
||||
var StorybookInitializer = Initializer{
|
||||
InitializerFunc: NewStorybookFromString,
|
||||
Arguments: StorybookArguments,
|
||||
}
|
||||
|
||||
type Storybook struct {
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
Version string
|
||||
}
|
||||
|
||||
// The frontend does not have any artifact dependencies.
|
||||
func (f *Storybook) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository
|
||||
func (f *Storybook) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return FrontendBuilder(ctx, f.Src, f.YarnCache, opts)
|
||||
}
|
||||
|
||||
func (f *Storybook) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
// Not a file
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *Storybook) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
return frontend.Storybook(builder, f.Src, f.Version), nil
|
||||
}
|
||||
|
||||
func (f *Storybook) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *Storybook) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *Storybook) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (f *Storybook) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Not a file
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Storybook) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (f *Storybook) Filename(ctx context.Context) (string, error) {
|
||||
// Important note: this path is only used in two ways:
|
||||
// 1. When requesting an artifact be built and exported, this is the path where it will be exported to
|
||||
// 2. In a map to distinguish when the same artifact is being built more than once
|
||||
return filepath.Join(f.Version, "storybook"), nil
|
||||
}
|
||||
|
||||
func NewStorybookFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
grafanaDir, err := GrafanaDir(ctx, state, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheDir, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version, err := state.String(ctx, arguments.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStorybook(ctx, log, artifact, grafanaDir, version, cacheDir)
|
||||
}
|
||||
|
||||
func NewStorybook(ctx context.Context, log *slog.Logger, artifact string, src *dagger.Directory, version string, cache *dagger.CacheVolume) (*pipeline.Artifact, error) {
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeDirectory,
|
||||
Flags: StorybookFlags,
|
||||
Handler: &Storybook{
|
||||
Src: src,
|
||||
YarnCache: cache,
|
||||
Version: version,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SyncWriter wraps a writer and makes its writes synchronous, preventing multiple threads writing to the same writer
|
||||
// from creating wacky looking output.
|
||||
type SyncWriter struct {
|
||||
Writer io.Writer
|
||||
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
func NewSyncWriter(w io.Writer) *SyncWriter {
|
||||
return &SyncWriter{
|
||||
Writer: w,
|
||||
mutex: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *SyncWriter) Write(b []byte) (int, error) {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
var Stdout = NewSyncWriter(os.Stdout)
|
||||
@@ -0,0 +1,94 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
VersionArguments = []pipeline.Argument{
|
||||
arguments.GrafanaDirectory,
|
||||
arguments.Version,
|
||||
}
|
||||
|
||||
VersionFlags = TargzFlags
|
||||
)
|
||||
|
||||
var VersionInitializer = Initializer{
|
||||
InitializerFunc: NewVersionFromString,
|
||||
Arguments: VersionArguments,
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
// Version is embedded in the binary at build-time
|
||||
Version string
|
||||
}
|
||||
|
||||
func (b *Version) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return opts.Client.Container().WithNewFile("/VERSION", b.Version), nil
|
||||
}
|
||||
|
||||
func (b *Version) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{}, nil
|
||||
}
|
||||
|
||||
func (b *Version) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
return builder.File("/VERSION"), nil
|
||||
}
|
||||
|
||||
func (b *Version) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *Version) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *Version) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Version) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (b *Version) Filename(ctx context.Context) (string, error) {
|
||||
return "VERSION", nil
|
||||
}
|
||||
|
||||
func (b *Version) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Version) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewVersionFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
version, err := state.String(ctx, arguments.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewVersion(ctx, log, artifact, version)
|
||||
}
|
||||
|
||||
func NewVersion(ctx context.Context, log *slog.Logger, artifact, version string) (*pipeline.Artifact, error) {
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: VersionFlags,
|
||||
Handler: &Version{
|
||||
Version: version,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
type LDFlag struct {
|
||||
Name string
|
||||
Values []string
|
||||
}
|
||||
|
||||
func GoLDFlags(flags []LDFlag) string {
|
||||
ldflags := strings.Builder{}
|
||||
for _, v := range flags {
|
||||
if v.Values == nil {
|
||||
ldflags.WriteString(v.Name + " ")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, value := range v.Values {
|
||||
// For example, "-X 'main.version=v1.0.0'"
|
||||
ldflags.WriteString(fmt.Sprintf(`%s \"%s\" `, v.Name, value))
|
||||
}
|
||||
}
|
||||
|
||||
return ldflags.String()
|
||||
}
|
||||
|
||||
// GoBuildCommand returns the arguments for go build to be used in 'WithExec'.
|
||||
func GoBuildCommand(output string, ldflags []LDFlag, tags []string, main string) []string {
|
||||
args := []string{"go", "build",
|
||||
fmt.Sprintf("-ldflags=\"%s\"", GoLDFlags(ldflags)),
|
||||
fmt.Sprintf("-o=%s", output),
|
||||
"-trimpath",
|
||||
fmt.Sprintf("-tags=%s", strings.Join(tags, ",")),
|
||||
// Go is weird and paths referring to packages within a module to be prefixed with "./".
|
||||
// Otherwise, the path is assumed to be relative to $GOROOT
|
||||
"./" + main,
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func Build(
|
||||
d *dagger.Client,
|
||||
builder *dagger.Container,
|
||||
src *dagger.Directory,
|
||||
distro Distribution,
|
||||
out string,
|
||||
opts *BuildOpts,
|
||||
) *dagger.Directory {
|
||||
vcsinfo := GetVCSInfo(src, opts.Version, opts.Enterprise)
|
||||
builder = WithVCSInfo(builder, vcsinfo, opts.Enterprise)
|
||||
|
||||
ldflags := LDFlagsDynamic(vcsinfo)
|
||||
|
||||
if opts.Static {
|
||||
ldflags = LDFlagsStatic(vcsinfo)
|
||||
}
|
||||
|
||||
cmd := []string{
|
||||
"grafana",
|
||||
"grafana-server",
|
||||
"grafana-cli",
|
||||
"grafana-example-apiserver",
|
||||
}
|
||||
|
||||
os, _ := OSAndArch(distro)
|
||||
|
||||
for _, v := range cmd {
|
||||
// Some CLI packages such as grafana-example-apiserver don't exist in earlier Grafana Versions <10.3
|
||||
// Below check skips building them as needed
|
||||
pkgPath := path.Join("pkg", "cmd", v)
|
||||
out := path.Join(out, v)
|
||||
if os == "windows" {
|
||||
out += ".exe"
|
||||
}
|
||||
|
||||
cmd := GoBuildCommand(out, ldflags, opts.Tags, pkgPath)
|
||||
|
||||
script := fmt.Sprintf(`if [ -d %s ]; then %s; fi`, pkgPath, strings.Join(cmd, " "))
|
||||
log.Printf("Building with command '%s'", script)
|
||||
|
||||
builder = builder.
|
||||
WithExec([]string{"/bin/sh", "-c", script})
|
||||
}
|
||||
|
||||
return builder.Directory(out)
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/containers"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/golang"
|
||||
)
|
||||
|
||||
// BuildOpts are general options that can change the way Grafana is compiled regardless of distribution.
|
||||
type BuildOpts struct {
|
||||
Version string
|
||||
ExperimentalFlags []string
|
||||
Tags []string
|
||||
WireTag string
|
||||
GoCacheProg string
|
||||
Static bool
|
||||
Enterprise bool
|
||||
}
|
||||
|
||||
func distroOptsFunc(log *slog.Logger, distro Distribution) (DistroBuildOptsFunc, error) {
|
||||
if val, ok := DistributionGoOpts[distro]; ok {
|
||||
return DistroOptsLogger(log, val), nil
|
||||
}
|
||||
return nil, errors.New("unrecognized distribution")
|
||||
}
|
||||
|
||||
func WithGoEnv(log *slog.Logger, container *dagger.Container, distro Distribution, opts *BuildOpts) (*dagger.Container, error) {
|
||||
fn, err := distroOptsFunc(log, distro)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bopts := fn(distro, opts.ExperimentalFlags, opts.Tags)
|
||||
|
||||
return containers.WithEnv(container, GoBuildEnv(bopts)), nil
|
||||
}
|
||||
|
||||
func WithViceroyEnv(log *slog.Logger, container *dagger.Container, distro Distribution, opts *BuildOpts) (*dagger.Container, error) {
|
||||
fn, err := distroOptsFunc(log, distro)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bopts := fn(distro, opts.ExperimentalFlags, opts.Tags)
|
||||
|
||||
return containers.WithEnv(container, ViceroyEnv(bopts)), nil
|
||||
}
|
||||
|
||||
func ViceroyContainer(
|
||||
d *dagger.Client,
|
||||
log *slog.Logger,
|
||||
distro Distribution,
|
||||
goVersion string,
|
||||
viceroyVersion string,
|
||||
opts *BuildOpts,
|
||||
) (*dagger.Container, error) {
|
||||
containerOpts := dagger.ContainerOpts{
|
||||
Platform: "linux/amd64",
|
||||
}
|
||||
|
||||
// Instead of directly using the `arch` variable here to substitute in the GoURL, we have to be careful with the Go releases.
|
||||
// Supported releases (in the names):
|
||||
// * amd64
|
||||
// * armv6l
|
||||
// * arm64
|
||||
goURL := golang.DownloadURL(goVersion, "amd64")
|
||||
container := d.Container(containerOpts).From(fmt.Sprintf("rfratto/viceroy:%s", viceroyVersion))
|
||||
|
||||
// Install Go manually, and install make, git, and curl from the package manager.
|
||||
container = container.WithExec([]string{"apt-get", "update"}).
|
||||
WithExec([]string{"apt-get", "install", "-yq", "curl", "make", "git"}).
|
||||
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("curl -L %s | tar -C /usr/local -xzf -", goURL)}).
|
||||
WithEnvVariable("PATH", "/bin:/usr/bin:/usr/local/bin:/usr/local/go/bin:/usr/osxcross/bin")
|
||||
|
||||
return WithViceroyEnv(log, container, distro, opts)
|
||||
}
|
||||
|
||||
func GolangContainer(
|
||||
d *dagger.Client,
|
||||
log *slog.Logger,
|
||||
goVersion string,
|
||||
viceroyVersion string,
|
||||
platform dagger.Platform,
|
||||
distro Distribution,
|
||||
opts *BuildOpts,
|
||||
) (*dagger.Container, error) {
|
||||
os, _ := OSAndArch(distro)
|
||||
// Only use viceroy for all darwin and only windows/amd64
|
||||
if os == "darwin" || distro == DistWindowsAMD64 {
|
||||
return ViceroyContainer(d, log, distro, goVersion, viceroyVersion, opts)
|
||||
}
|
||||
|
||||
container := golang.Container(d, platform, goVersion).
|
||||
WithExec([]string{"apk", "add", "--update", "wget", "build-base", "alpine-sdk", "musl", "musl-dev", "xz"}).
|
||||
WithExec([]string{"wget", "-q", "https://dl.grafana.com/ci/zig-linux-x86_64-0.11.0.tar.xz"}).
|
||||
WithExec([]string{"tar", "--strip-components=1", "-C", "/", "-xf", "zig-linux-x86_64-0.11.0.tar.xz"}).
|
||||
WithExec([]string{"mv", "/zig", "/bin/zig"}).
|
||||
// Install the toolchain specifically for armv7 until we figure out why it's crashing w/ zig container = container.
|
||||
WithExec([]string{"mkdir", "/toolchain"}).
|
||||
WithExec([]string{"wget", "-q", "http://dl.grafana.com/ci/arm-linux-musleabihf-cross.tgz", "-P", "/toolchain"}).
|
||||
WithExec([]string{"tar", "-xf", "/toolchain/arm-linux-musleabihf-cross.tgz", "-C", "/toolchain"}).
|
||||
WithExec([]string{"wget", "-q", "https://dl.grafana.com/ci/s390x-linux-musl-cross.tgz", "-P", "/toolchain"}).
|
||||
WithExec([]string{"tar", "-xf", "/toolchain/s390x-linux-musl-cross.tgz", "-C", "/toolchain"})
|
||||
|
||||
return WithGoEnv(log, container, distro, opts)
|
||||
}
|
||||
|
||||
func withCue(c *dagger.Container, src *dagger.Directory) *dagger.Container {
|
||||
return c.
|
||||
WithDirectory("/src/cue.mod", src.Directory("cue.mod")).
|
||||
WithDirectory("/src/kinds", src.Directory("kinds")).
|
||||
WithDirectory("/src/packages/grafana-schema", src.Directory("packages/grafana-schema"), dagger.ContainerWithDirectoryOpts{
|
||||
Include: []string{"**/*.cue"},
|
||||
}).
|
||||
WithDirectory("/src/public/app/plugins", src.Directory("public/app/plugins"), dagger.ContainerWithDirectoryOpts{
|
||||
Include: []string{"**/*.cue", "**/plugin.json"},
|
||||
}).
|
||||
WithFile("/src/embed.go", src.File("embed.go"))
|
||||
}
|
||||
|
||||
// Builder returns the container that is used to build the Grafana backend binaries.
|
||||
// The build container:
|
||||
// * Will be based on rfratto/viceroy for Darwin or Windows
|
||||
// * Will be based on golang:x.y.z-alpine for all other ditsros
|
||||
// * Will download & cache the downloaded Go modules
|
||||
// * Will run `make gen-go` on the provided Grafana source
|
||||
// - With the linux/amd64 arch/os combination, regardless of what the requested distro is.
|
||||
//
|
||||
// * And will have all of the environment variables necessary to run `go build`.
|
||||
func Builder(
|
||||
d *dagger.Client,
|
||||
log *slog.Logger,
|
||||
distro Distribution,
|
||||
opts *BuildOpts,
|
||||
platform dagger.Platform,
|
||||
src *dagger.Directory,
|
||||
goVersion string,
|
||||
viceroyVersion string,
|
||||
goBuildCache *dagger.CacheVolume,
|
||||
goModCache *dagger.CacheVolume,
|
||||
) (*dagger.Container, error) {
|
||||
var (
|
||||
version = opts.Version
|
||||
)
|
||||
|
||||
// for some distros we use the golang official iamge. For others, we use viceroy.
|
||||
builder, err := GolangContainer(d, log, goVersion, viceroyVersion, platform, distro, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builder = builder.
|
||||
WithMountedCache("/root/.cache/go", goBuildCache).
|
||||
WithEnvVariable("GOCACHE", "/root/.cache/go")
|
||||
|
||||
if prog := opts.GoCacheProg; prog != "" {
|
||||
builder = builder.WithEnvVariable("GOCACHEPROG", prog)
|
||||
}
|
||||
|
||||
commitInfo := GetVCSInfo(src, version, opts.Enterprise)
|
||||
|
||||
builder = withCue(builder, src).
|
||||
WithDirectory("/src/", src, dagger.ContainerWithDirectoryOpts{
|
||||
Include: []string{"**/*.mod", "**/*.sum", "**/*.work", ".git"},
|
||||
}).
|
||||
WithDirectory("/src/pkg", src.WithoutDirectory("pkg/build").Directory("pkg")).
|
||||
WithDirectory("/src/apps", src.Directory("apps")).
|
||||
WithDirectory("/src/emails", src.Directory("emails")).
|
||||
WithFile("/src/pkg/server/wire_gen.go", Wire(d, src, platform, goVersion, opts.WireTag)).
|
||||
WithFile("/src/.buildinfo.commit", commitInfo.Commit).
|
||||
WithWorkdir("/src")
|
||||
|
||||
if opts.Enterprise {
|
||||
builder = builder.WithFile("/src/.buildinfo.enterprise-commit", commitInfo.EnterpriseCommit)
|
||||
}
|
||||
|
||||
builder = golang.WithCachedGoDependencies(
|
||||
builder,
|
||||
goModCache,
|
||||
)
|
||||
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
func Wire(d *dagger.Client, src *dagger.Directory, platform dagger.Platform, goVersion string, wireTag string) *dagger.File {
|
||||
// withCue is only required during `make gen-go` in 9.5.x or older.
|
||||
return withCue(golang.Container(d, platform, goVersion), src).
|
||||
WithExec([]string{"apk", "add", "make"}).
|
||||
WithDirectory("/src/", src, dagger.ContainerWithDirectoryOpts{
|
||||
Include: []string{"**/*.mod", "**/*.sum", "**/*.work", ".git"},
|
||||
}).
|
||||
WithDirectory("/src/pkg", src.Directory("pkg")).
|
||||
WithDirectory("/src/apps", src.Directory("apps")).
|
||||
WithDirectory("/src/.bingo", src.Directory(".bingo")).
|
||||
WithDirectory("/src/.citools", src.Directory(".citools")).
|
||||
WithFile("/src/Makefile", src.File("Makefile")).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"make", "gen-go", fmt.Sprintf("WIRE_TAGS=%s", wireTag)}).
|
||||
File("/src/pkg/server/wire_gen.go")
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
// Distribution is a string that represents the GOOS and GOARCH environment variables joined by a "/".
|
||||
// Optionally, if there is an extra argument specific to that architecture, it will be the last segment of the string.
|
||||
// Examples:
|
||||
// - "linux/arm/v6" = GOOS=linux, GOARCH=arm, GOARM=6
|
||||
// - "linux/arm/v7" = GOOS=linux, GOARCH=arm, GOARM=7
|
||||
// - "linux/amd64/v7" = GOOS=linux, GOARCH=arm, GOARM=7
|
||||
// - "linux/amd64/v2" = GOOS=linux, GOARCH=amd64, GOAMD64=v2
|
||||
// The list of distributions is built from the command "go tool dist list".
|
||||
// While not all are used, it at least represents the possible combinations.
|
||||
type Distribution string
|
||||
|
||||
const (
|
||||
DistDarwinAMD64 Distribution = "darwin/amd64"
|
||||
DistDarwinAMD64v1 Distribution = "darwin/amd64/v1"
|
||||
DistDarwinAMD64v2 Distribution = "darwin/amd64/v2"
|
||||
DistDarwinAMD64v3 Distribution = "darwin/amd64/v3"
|
||||
DistDarwinAMD64v4 Distribution = "darwin/amd64/v4"
|
||||
DistDarwinARM64 Distribution = "darwin/arm64"
|
||||
)
|
||||
|
||||
const (
|
||||
DistFreeBSD386 Distribution = "freebsd/386"
|
||||
DistFreeBSD386SSE2 Distribution = "freebsd/386/sse2"
|
||||
DistFreeBSD386SoftFloat Distribution = "freebsd/386/softfloat"
|
||||
DistFreeBSDAMD64 Distribution = "freebsd/amd64"
|
||||
DistFreeBSDAMD64v1 Distribution = "freebsd/amd64/v1"
|
||||
DistFreeBSDAMD64v2 Distribution = "freebsd/amd64/v2"
|
||||
DistFreeBSDAMD64v3 Distribution = "freebsd/amd64/v3"
|
||||
DistFreeBSDAMD64v4 Distribution = "freebsd/amd64/v4"
|
||||
DistFreeBSDARM Distribution = "freebsd/arm"
|
||||
DistFreeBSDARM64 Distribution = "freebsd/arm64"
|
||||
DistFreeBSDRISCV Distribution = "freebsd/riscv64"
|
||||
)
|
||||
|
||||
const (
|
||||
DistIllumosAMD64 Distribution = "illumos/amd64"
|
||||
DistIllumosAMD64v1 Distribution = "illumos/amd64/v1"
|
||||
DistIllumosAMD64v2 Distribution = "illumos/amd64/v2"
|
||||
DistIllumosAMD64v3 Distribution = "illumos/amd64/v3"
|
||||
DistIllumosAMD64v4 Distribution = "illumos/amd64/v4"
|
||||
)
|
||||
const (
|
||||
DistLinux386 Distribution = "linux/386"
|
||||
DistLinux386SSE2 Distribution = "linux/386/sse2"
|
||||
DistLinux386SoftFloat Distribution = "linux/386/softfloat"
|
||||
DistLinuxAMD64 Distribution = "linux/amd64"
|
||||
DistLinuxAMD64v1 Distribution = "linux/amd64/v1"
|
||||
DistLinuxAMD64v2 Distribution = "linux/amd64/v2"
|
||||
DistLinuxAMD64v3 Distribution = "linux/amd64/v3"
|
||||
DistLinuxAMD64v4 Distribution = "linux/amd64/v4"
|
||||
DistLinuxAMD64Dynamic Distribution = "linux/amd64/dynamic"
|
||||
DistLinuxAMD64DynamicMusl Distribution = "linux/amd64/dynamic-musl"
|
||||
DistLinuxARM Distribution = "linux/arm"
|
||||
DistLinuxARMv6 Distribution = "linux/arm/v6"
|
||||
DistLinuxARMv7 Distribution = "linux/arm/v7"
|
||||
DistLinuxARM64 Distribution = "linux/arm64"
|
||||
DistLinuxARM64Dynamic Distribution = "linux/arm64/dynamic"
|
||||
DistLinuxLoong64 Distribution = "linux/loong64"
|
||||
DistLinuxMips Distribution = "linux/mips"
|
||||
DistLinuxMips64 Distribution = "linux/mips64"
|
||||
DistLinuxMips64le Distribution = "linux/mips64le"
|
||||
DistLinuxMipsle Distribution = "linux/mipsle"
|
||||
DistLinuxPPC64 Distribution = "linux/ppc64"
|
||||
DistLinuxPPC64le Distribution = "linux/ppc64le"
|
||||
DistLinuxRISCV64 Distribution = "linux/riscv64"
|
||||
DistLinuxS390X Distribution = "linux/s390x"
|
||||
)
|
||||
|
||||
const (
|
||||
DistOpenBSD386 Distribution = "openbsd/386"
|
||||
DistOpenBSD386SSE2 Distribution = "openbsd/386/sse2"
|
||||
DistOpenBSD386SoftFLoat Distribution = "openbsd/386/softfloat"
|
||||
DistOpenBSDAMD64 Distribution = "openbsd/amd64"
|
||||
DistOpenBSDAMD64v1 Distribution = "openbsd/amd64/v1"
|
||||
DistOpenBSDAMD64v2 Distribution = "openbsd/amd64/v2"
|
||||
DistOpenBSDAMD64v3 Distribution = "openbsd/amd64/v3"
|
||||
DistOpenBSDAMD64v4 Distribution = "openbsd/amd64/v4"
|
||||
DistOpenBSDARM Distribution = "openbsd/arm"
|
||||
DistOpenBSDARMv6 Distribution = "openbsd/arm/v6"
|
||||
DistOpenBSDARMv7 Distribution = "openbsd/arm/v7"
|
||||
DistOpenBSDARM64 Distribution = "openbsd/arm64"
|
||||
DistOpenBSDMips64 Distribution = "openbsd/mips64"
|
||||
)
|
||||
|
||||
const (
|
||||
DistPlan9386 Distribution = "plan9/386"
|
||||
DistPlan9386SSE2 Distribution = "plan9/386/sse2"
|
||||
DistPlan9386SoftFloat Distribution = "plan9/386/softfloat"
|
||||
DistPlan9AMD64 Distribution = "plan9/amd64"
|
||||
DistPlan9AMD64v1 Distribution = "plan9/amd64/v1"
|
||||
DistPlan9AMD64v2 Distribution = "plan9/amd64/v2"
|
||||
DistPlan9AMD64v3 Distribution = "plan9/amd64/v3"
|
||||
DistPlan9AMD64v4 Distribution = "plan9/amd64/v4"
|
||||
DistPlan9ARM Distribution = "plan9/arm/v6"
|
||||
DistPlan9ARMv6 Distribution = "plan9/arm/v6"
|
||||
DistPlan9ARMv7 Distribution = "plan9/arm/v7"
|
||||
)
|
||||
|
||||
const (
|
||||
DistSolarisAMD64 Distribution = "solaris/amd64"
|
||||
DistSolarisAMD64v1 Distribution = "solaris/amd64/v1"
|
||||
DistSolarisAMD64v2 Distribution = "solaris/amd64/v2"
|
||||
DistSolarisAMD64v3 Distribution = "solaris/amd64/v3"
|
||||
DistSolarisAMD64v4 Distribution = "solaris/amd64/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
DistWindows386 Distribution = "windows/386"
|
||||
DistWindows386SSE2 Distribution = "windows/386/sse2"
|
||||
DistWindows386SoftFloat Distribution = "windows/386/softfloat"
|
||||
DistWindowsAMD64 Distribution = "windows/amd64"
|
||||
DistWindowsAMD64v1 Distribution = "windows/amd64/v1"
|
||||
DistWindowsAMD64v2 Distribution = "windows/amd64/v2"
|
||||
DistWindowsAMD64v3 Distribution = "windows/amd64/v3"
|
||||
DistWindowsAMD64v4 Distribution = "windows/amd64/v4"
|
||||
DistWindowsARM Distribution = "windows/arm"
|
||||
DistWindowsARMv6 Distribution = "windows/arm/v6"
|
||||
DistWindowsARMv7 Distribution = "windows/arm/v7"
|
||||
DistWindowsARM64 Distribution = "windows/arm64"
|
||||
)
|
||||
|
||||
func IsWindows(d Distribution) bool {
|
||||
return strings.Split(string(d), "/")[0] == "windows"
|
||||
}
|
||||
|
||||
func OSAndArch(d Distribution) (string, string) {
|
||||
p := strings.Split(string(d), "/")
|
||||
if len(p) < 2 {
|
||||
return string(d), ""
|
||||
}
|
||||
return p[0], p[1]
|
||||
}
|
||||
|
||||
func FullArch(d Distribution) string {
|
||||
p := strings.Split(string(d), "/")
|
||||
return strings.Join(p[1:], "/")
|
||||
}
|
||||
|
||||
func ArchVersion(d Distribution) string {
|
||||
p := strings.Split(string(d), "/")
|
||||
if len(p) < 3 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// ARM specifically must be specified without a 'v' prefix.
|
||||
// GOAMD64, however, expects a 'v' prefix.
|
||||
// Specifying the ARM version with the 'v' prefix and without is supported in Docker's platform argument, however.
|
||||
if arch := p[1]; arch == "arm" {
|
||||
return strings.TrimPrefix(p[2], "v")
|
||||
}
|
||||
|
||||
return p[2]
|
||||
}
|
||||
|
||||
func PackageArch(d Distribution) string {
|
||||
_, arch := OSAndArch(d)
|
||||
|
||||
if arch == "arm" {
|
||||
return "armhf"
|
||||
}
|
||||
|
||||
return arch
|
||||
}
|
||||
|
||||
// From the distribution, try to assume the docker platform (used in Docker's --platform argument or the (dagger.ContainerOpts).Platform field
|
||||
func Platform(d Distribution) dagger.Platform {
|
||||
p := strings.ReplaceAll(string(d), "/dynamic-musl", "")
|
||||
p = strings.ReplaceAll(p, "/dynamic", "")
|
||||
p = strings.ReplaceAll(p, "arm/v6", "arm/v7")
|
||||
// for now let's just try to use the distro name as the platform and see if that works...
|
||||
return dagger.Platform(p)
|
||||
}
|
||||
|
||||
type DistroBuildOptsFunc func(distro Distribution, experiments []string, tags []string) *GoBuildOpts
|
||||
|
||||
func LDFlagsStatic(info *VCSInfo) []LDFlag {
|
||||
return []LDFlag{
|
||||
{"-w", nil},
|
||||
{"-s", nil},
|
||||
{"-X", info.X()},
|
||||
{"-linkmode=external", nil},
|
||||
{"-extldflags=-static", nil},
|
||||
}
|
||||
}
|
||||
|
||||
func LDFlagsDynamic(info *VCSInfo) []LDFlag {
|
||||
return []LDFlag{
|
||||
{"-X", info.X()},
|
||||
}
|
||||
}
|
||||
|
||||
func ZigCC(distro Distribution) string {
|
||||
target, ok := ZigTargets[distro]
|
||||
if !ok {
|
||||
target = "x86_64-linux-musl" // best guess? should probably retun an error but i don't want to
|
||||
}
|
||||
|
||||
return fmt.Sprintf("zig cc -target %s", target)
|
||||
}
|
||||
|
||||
func ZigCXX(distro Distribution) string {
|
||||
target, ok := ZigTargets[distro]
|
||||
if !ok {
|
||||
target = "x86_64-linux-musl" // best guess? should probably retun an error but i don't want to
|
||||
}
|
||||
|
||||
return fmt.Sprintf("zig c++ -target %s", target)
|
||||
}
|
||||
|
||||
var DefaultBuildOpts = func(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
os, arch := OSAndArch(distro)
|
||||
|
||||
return &GoBuildOpts{
|
||||
CC: ZigCC(distro),
|
||||
CXX: ZigCXX(distro),
|
||||
ExperimentalFlags: experiments,
|
||||
OS: os,
|
||||
Arch: arch,
|
||||
CGOEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildOptsStaticARM builds Grafana statically for the armv6/v7 architectures (not aarch64/arm64)
|
||||
func BuildOptsStaticARM(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
var (
|
||||
os, _ = OSAndArch(distro)
|
||||
arm = ArchVersion(distro)
|
||||
)
|
||||
|
||||
return &GoBuildOpts{
|
||||
CC: "/toolchain/arm-linux-musleabihf-cross/bin/arm-linux-musleabihf-gcc",
|
||||
CXX: "/toolchain/arm-linux-musleabihf-cross/bin/arm-linux-musleabihf-cpp",
|
||||
ExperimentalFlags: experiments,
|
||||
OS: os,
|
||||
Arch: "arm",
|
||||
GoARM: GoARM(arm),
|
||||
CGOEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildOptsStaticS390X builds Grafana statically for the s390x arch
|
||||
func BuildOptsStaticS390X(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
var (
|
||||
os, _ = OSAndArch(distro)
|
||||
)
|
||||
|
||||
return &GoBuildOpts{
|
||||
CC: "/toolchain/s390x-linux-musl-cross/bin/s390x-linux-musl-gcc",
|
||||
CXX: "/toolchain/s390x-linux-musl-cross/bin/s390x-linux-musl-cpp",
|
||||
ExperimentalFlags: experiments,
|
||||
OS: os,
|
||||
Arch: "s390x",
|
||||
CGOEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
func StdZigBuildOpts(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
var (
|
||||
os, arch = OSAndArch(distro)
|
||||
)
|
||||
|
||||
return &GoBuildOpts{
|
||||
CC: ZigCC(distro),
|
||||
CXX: ZigCXX(distro),
|
||||
ExperimentalFlags: experiments,
|
||||
OS: os,
|
||||
Arch: arch,
|
||||
CGOEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
func BuildOptsWithoutZig(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
var (
|
||||
os, arch = OSAndArch(distro)
|
||||
)
|
||||
|
||||
return &GoBuildOpts{
|
||||
ExperimentalFlags: experiments,
|
||||
OS: os,
|
||||
Arch: arch,
|
||||
CGOEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
func ViceroyBuildOpts(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
var (
|
||||
os, arch = OSAndArch(distro)
|
||||
)
|
||||
|
||||
return &GoBuildOpts{
|
||||
CC: "viceroycc",
|
||||
ExperimentalFlags: experiments,
|
||||
OS: os,
|
||||
Arch: arch,
|
||||
CGOEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
var ZigTargets = map[Distribution]string{
|
||||
DistLinuxAMD64: "x86_64-linux-musl",
|
||||
DistLinuxAMD64Dynamic: "x86_64-linux-gnu",
|
||||
DistLinuxAMD64DynamicMusl: "x86_64-linux-musl",
|
||||
DistLinuxARM64: "aarch64-linux-musl",
|
||||
DistLinuxARM64Dynamic: "aarch64-linux-musl",
|
||||
DistLinuxARM: "arm-linux-musleabihf",
|
||||
DistLinuxARMv6: "arm-linux-musleabihf",
|
||||
DistLinuxARMv7: "arm-linux-musleabihf",
|
||||
DistLinuxRISCV64: "riscv64-linux-musl",
|
||||
DistWindowsAMD64: "x86_64-windows-gnu",
|
||||
DistWindowsARM64: "aarch64-windows-gnu",
|
||||
}
|
||||
|
||||
var DistributionGoOpts = map[Distribution]DistroBuildOptsFunc{
|
||||
// The Linux distros should all have an equivalent zig target in the ZigTargets map
|
||||
DistLinuxARM: BuildOptsStaticARM,
|
||||
DistLinuxARMv6: BuildOptsStaticARM,
|
||||
DistLinuxARMv7: BuildOptsStaticARM,
|
||||
DistLinuxS390X: BuildOptsStaticS390X,
|
||||
DistLinuxARM64: StdZigBuildOpts,
|
||||
DistLinuxARM64Dynamic: StdZigBuildOpts,
|
||||
DistLinuxAMD64: StdZigBuildOpts,
|
||||
DistLinuxAMD64Dynamic: StdZigBuildOpts,
|
||||
DistPlan9AMD64: StdZigBuildOpts,
|
||||
DistLinuxRISCV64: StdZigBuildOpts,
|
||||
|
||||
// Non-Linux distros can have whatever they want in CC and CXX; it'll get overridden
|
||||
// but it's probably not best to rely on that.
|
||||
DistWindowsAMD64: ViceroyBuildOpts,
|
||||
DistWindowsARM64: StdZigBuildOpts,
|
||||
DistDarwinAMD64: ViceroyBuildOpts,
|
||||
DistDarwinARM64: ViceroyBuildOpts,
|
||||
|
||||
DistLinuxAMD64DynamicMusl: BuildOptsWithoutZig,
|
||||
}
|
||||
|
||||
func DistroOptsLogger(log *slog.Logger, fn DistroBuildOptsFunc) func(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
return func(distro Distribution, experiments []string, tags []string) *GoBuildOpts {
|
||||
opts := fn(distro, experiments, tags)
|
||||
log.Debug("Building with options", "distribution", distro, "experiments", experiments, "tags", tags, "os", opts.OS, "arch", opts.Arch, "arm", opts.GoARM, "CGO", opts.CGOEnabled, "386", opts.Go386, "CC", opts.CC, "CXX", opts.CXX)
|
||||
return opts
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Package backend holds the functions that create containers, files, and directories for building Grafana's backend binaries.
|
||||
package backend
|
||||
@@ -0,0 +1,140 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/containers"
|
||||
)
|
||||
|
||||
type (
|
||||
BuildMode string
|
||||
GoARM string
|
||||
GoAMD64 string
|
||||
Go386 string
|
||||
LibC int
|
||||
)
|
||||
|
||||
const (
|
||||
BuildModeDefault BuildMode = "default"
|
||||
BuildModeExe BuildMode = "exe"
|
||||
)
|
||||
|
||||
const (
|
||||
GOARM5 GoARM = "5"
|
||||
GOARM6 GoARM = "6"
|
||||
GOARM7 GoARM = "7"
|
||||
)
|
||||
|
||||
const (
|
||||
Go386SSE2 Go386 = "sse2"
|
||||
Go386SoftFloat Go386 = "softfloat"
|
||||
)
|
||||
|
||||
const (
|
||||
Musl LibC = iota
|
||||
GLibC
|
||||
)
|
||||
|
||||
type GoBuildOpts struct {
|
||||
// OS is value supplied to the GOOS environment variable
|
||||
OS string
|
||||
|
||||
// Arch is value supplied to the GOARCH environment variable
|
||||
Arch string
|
||||
|
||||
// ExperimentalFlags are Go build-time feature flags in the "GOEXPERIMENT" environment variable that enable experimental features.
|
||||
ExperimentalFlags []string
|
||||
|
||||
// CGOEnabled defines whether or not the CGO_ENABLED flag is set.
|
||||
CGOEnabled bool
|
||||
|
||||
// GOARM: For GOARCH=arm, the ARM architecture for which to compile.
|
||||
// Valid values are 5, 6, 7.
|
||||
GoARM GoARM
|
||||
|
||||
// GO386: For GOARCH=386, how to implement floating point instructions.
|
||||
// Valid values are sse2 (default), softfloat.
|
||||
Go386 Go386
|
||||
|
||||
// CC is the command to use to compile C code when CGO is enabled. (Sets the "CC" environment variable)
|
||||
CC string
|
||||
|
||||
// CXX is the command to use to compile C++ code when CGO is enabled. (Sets the "CXX" environment variable)
|
||||
CXX string
|
||||
}
|
||||
|
||||
// GoBuildEnv returns the environment variables that must be set for a 'go build' command given the provided 'GoBuildOpts'.
|
||||
func GoBuildEnv(opts *GoBuildOpts) []containers.Env {
|
||||
var (
|
||||
os = opts.OS
|
||||
arch = opts.Arch
|
||||
)
|
||||
|
||||
env := []containers.Env{containers.EnvVar("GOOS", os), containers.EnvVar("GOARCH", arch)}
|
||||
|
||||
if arch == "arm" {
|
||||
env = append(env, containers.EnvVar("GOARM", string(opts.GoARM)))
|
||||
}
|
||||
|
||||
if opts.CGOEnabled {
|
||||
env = append(env, containers.EnvVar("GOARM", string(opts.GoARM)))
|
||||
env = append(env, containers.EnvVar("CGO_ENABLED", "1"))
|
||||
|
||||
// https://github.com/mattn/go-sqlite3/issues/1164#issuecomment-1635253695
|
||||
env = append(env, containers.EnvVar("CGO_CFLAGS", "-D_LARGEFILE64_SOURCE"))
|
||||
} else {
|
||||
env = append(env, containers.EnvVar("CGO_ENABLED", "0"))
|
||||
}
|
||||
|
||||
if opts.ExperimentalFlags != nil {
|
||||
env = append(env, containers.EnvVar("GOEXPERIMENT", strings.Join(opts.ExperimentalFlags, ",")))
|
||||
}
|
||||
|
||||
if opts.CC != "" {
|
||||
env = append(env, containers.EnvVar("CC", opts.CC))
|
||||
}
|
||||
|
||||
if opts.CXX != "" {
|
||||
env = append(env, containers.EnvVar("CXX", opts.CXX))
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// ViceroyEnv returns the environment variables that must be set for a 'go build' command given the provided 'GoBuildOpts'.
|
||||
func ViceroyEnv(opts *GoBuildOpts) []containers.Env {
|
||||
var (
|
||||
os = opts.OS
|
||||
arch = opts.Arch
|
||||
)
|
||||
|
||||
env := []containers.Env{
|
||||
containers.EnvVar("VICEROYOS", os),
|
||||
containers.EnvVar("GOOS", os),
|
||||
containers.EnvVar("VICEROYARCH", arch),
|
||||
containers.EnvVar("GOARCH", arch),
|
||||
}
|
||||
|
||||
if arch == "arm" {
|
||||
env = append(env, containers.EnvVar("VICEROYARM", string(opts.GoARM)))
|
||||
}
|
||||
|
||||
if opts.CGOEnabled {
|
||||
env = append(env, containers.EnvVar("CGO_ENABLED", "1"))
|
||||
|
||||
// https://github.com/mattn/go-sqlite3/issues/1164#issuecomment-1635253695
|
||||
env = append(env, containers.EnvVar("CGO_CFLAGS", "-D_LARGEFILE64_SOURCE"))
|
||||
} else {
|
||||
env = append(env, containers.EnvVar("CGO_ENABLED", "0"))
|
||||
}
|
||||
|
||||
if opts.ExperimentalFlags != nil {
|
||||
env = append(env, containers.EnvVar("GOEXPERIMENT", strings.Join(opts.ExperimentalFlags, ",")))
|
||||
}
|
||||
|
||||
if opts.CC != "" {
|
||||
env = append(env, containers.EnvVar("CC", "viceroycc"))
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
type VCSInfo struct {
|
||||
Version string
|
||||
Commit *dagger.File
|
||||
EnterpriseCommit *dagger.File
|
||||
Branch *dagger.File
|
||||
}
|
||||
|
||||
func WithVCSInfo(c *dagger.Container, info *VCSInfo, enterprise bool) *dagger.Container {
|
||||
c = c.
|
||||
WithFile(".buildinfo.commit", info.Commit).
|
||||
WithFile(".buildinfo.branch", info.Branch)
|
||||
|
||||
if enterprise {
|
||||
return c.WithFile(".buildinfo.enterprise-commit", info.EnterpriseCommit)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// VCSInfo gets the VCS data from the directory 'src', writes them to a file on the given container, and returns the files which can be used in other containers.
|
||||
func GetVCSInfo(src *dagger.Directory, version string, enterprise bool) *VCSInfo {
|
||||
info := &VCSInfo{
|
||||
Version: version,
|
||||
Commit: src.File(".buildinfo.commit"),
|
||||
Branch: src.File(".buildinfo.branch"),
|
||||
}
|
||||
|
||||
if enterprise {
|
||||
info.EnterpriseCommit = src.File(".buildinfo.enterprise-commit")
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func (v *VCSInfo) X() []string {
|
||||
flags := []string{
|
||||
fmt.Sprintf("main.version=%s", strings.TrimPrefix(v.Version, "v")),
|
||||
`main.commit=$(cat ./.buildinfo.commit)`,
|
||||
`main.buildBranch=$(cat ./.buildinfo.branch)`,
|
||||
}
|
||||
|
||||
if v.EnterpriseCommit != nil {
|
||||
flags = append(flags, `main.enterpriseCommit=$(cat ./.buildinfo.enterprise-commit)`)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package cliutil
|
||||
|
||||
type CLIContext interface {
|
||||
Bool(string) bool
|
||||
String(string) string
|
||||
Set(string, string) error
|
||||
StringSlice(string) []string
|
||||
Path(string) string
|
||||
Int64(string) int64
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/artifacts"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type CLI struct {
|
||||
artifacts map[string]artifacts.Initializer
|
||||
}
|
||||
|
||||
func (c *CLI) ArtifactsCommand() *cli.Command {
|
||||
f := artifacts.ArtifactFlags(c)
|
||||
flags := make([]cli.Flag, len(f))
|
||||
copy(flags, f)
|
||||
return &cli.Command{
|
||||
Name: "artifacts",
|
||||
Usage: "Use this command to declare a list of artifacts to be built and/or published",
|
||||
Flags: flags,
|
||||
Action: artifacts.Command(c),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CLI) App() *cli.App {
|
||||
return &cli.App{
|
||||
Name: "grafana-build",
|
||||
Usage: "A build tool for Grafana",
|
||||
Commands: []*cli.Command{
|
||||
// Legacy commands, should eventually be completely replaced by what's in "artifacts"
|
||||
{
|
||||
Name: "package",
|
||||
Subcommands: []*cli.Command{
|
||||
PackagePublishCommand,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "docker",
|
||||
Subcommands: []*cli.Command{
|
||||
DockerPublishCommand,
|
||||
},
|
||||
},
|
||||
ProImageCommand,
|
||||
{
|
||||
Name: "npm",
|
||||
Subcommands: []*cli.Command{
|
||||
PublishNPMCommand,
|
||||
},
|
||||
},
|
||||
GCOMCommand,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CLI) Register(flag string, a artifacts.Initializer) error {
|
||||
c.artifacts[flag] = a
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CLI) Initializers() map[string]artifacts.Initializer {
|
||||
return c.artifacts
|
||||
}
|
||||
|
||||
var GlobalCLI = &CLI{
|
||||
artifacts: map[string]artifacts.Initializer{},
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/artifacts"
|
||||
)
|
||||
|
||||
var Artifacts = map[string]artifacts.Initializer{
|
||||
"backend": artifacts.BackendInitializer,
|
||||
"frontend": artifacts.FrontendInitializer,
|
||||
"npm": artifacts.NPMPackagesInitializer,
|
||||
"targz": artifacts.TargzInitializer,
|
||||
"zip": artifacts.ZipInitializer,
|
||||
"deb": artifacts.DebInitializer,
|
||||
"rpm": artifacts.RPMInitializer,
|
||||
"docker": artifacts.DockerInitializer,
|
||||
"docker-pro": artifacts.ProDockerInitializer,
|
||||
"docker-enterprise": artifacts.EntDockerInitializer,
|
||||
"storybook": artifacts.StorybookInitializer,
|
||||
"msi": artifacts.MSIInitializer,
|
||||
"version": artifacts.VersionInitializer,
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipelines"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var DockerPublishCommand = &cli.Command{
|
||||
Name: "publish",
|
||||
Action: PipelineActionWithPackageInput(pipelines.PublishDocker),
|
||||
Usage: "Using a grafana.docker.tar.gz as input (ideally one built using the 'package' command), publish a docker image and manifest",
|
||||
Flags: JoinFlagsWithDefault(
|
||||
PackageInputFlags,
|
||||
DockerFlags,
|
||||
DockerPublishFlags,
|
||||
GCPFlags,
|
||||
ConcurrencyFlags,
|
||||
),
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cmd/flags"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var FlagPackage = &cli.StringSliceFlag{
|
||||
Name: "package",
|
||||
Usage: "Path to a grafana.tar.gz package used as input. This command will process each package provided separately and produce an equal number of applicable outputs",
|
||||
}
|
||||
var FlagNameOverride = &cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "Overrides any calculation for name in the package with the value provided here",
|
||||
}
|
||||
|
||||
// PackageInputFlags are used for commands that require a grafana package as input.
|
||||
// These commands are exclusively used outside of the CI process and are typically used in the CD process where a grafana.tar.gz has already been created.
|
||||
var PackageInputFlags = []cli.Flag{
|
||||
FlagPackage,
|
||||
FlagNameOverride,
|
||||
}
|
||||
|
||||
// GCPFlags are used in commands that need to authenticate with Google Cloud platform using the Google Cloud SDK
|
||||
var GCPFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "gcp-service-account-key-base64",
|
||||
Usage: "Provides a service-account key encoded in base64 to use to authenticate with the Google Cloud SDK",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "gcp-service-account-key",
|
||||
Usage: "Provides a service-account keyfile to use to authenticate with the Google Cloud SDK. If not provided or is empty, then $XDG_CONFIG_HOME/gcloud will be mounted in the container",
|
||||
},
|
||||
}
|
||||
|
||||
// NPMFlags are used in commands that need to authenticate with package registries to publish NPM packages
|
||||
var NPMFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "The package registry to publish packages",
|
||||
Value: "registry.npmjs.org",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "Provides a token to use to authenticate with the package registry",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "tag",
|
||||
Usage: "Provides the tags to use when publishing packages",
|
||||
Required: true,
|
||||
},
|
||||
}
|
||||
|
||||
// PublishFlags are flags that are used in commands that create artifacts.
|
||||
// Anything that creates an artifact should have the option to specify a local folder destination or a remote destination.
|
||||
var PublishFlags = flags.PublishFlags
|
||||
|
||||
// GrafanaFlags are flags that are required when working with the grafana source code.
|
||||
var GrafanaFlags = []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "grafana",
|
||||
Usage: "If set, initialize Grafana",
|
||||
Required: false,
|
||||
Value: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-dir",
|
||||
Usage: "Local Grafana dir to use, instead of git clone",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-repo",
|
||||
Usage: "Grafana repo to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "https://github.com/grafana/grafana.git",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-ref",
|
||||
Usage: "Grafana ref to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "main",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "enterprise",
|
||||
Usage: "If set, initialize Grafana Enterprise",
|
||||
Value: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise-dir",
|
||||
Usage: "Local Grafana Enterprise dir to use, instead of git clone",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise-repo",
|
||||
Usage: "Grafana Enterprise repo to clone, not valid if --grafana-dir is set",
|
||||
Required: false,
|
||||
Value: "https://github.com/grafana/grafana-enterprise.git",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "enterprise-ref",
|
||||
Usage: "Grafana Enterprise ref to clone, not valid if --enterprise-dir is set",
|
||||
Required: false,
|
||||
Value: "main",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "github-token",
|
||||
Usage: "Github token to use for git cloning, by default will be pulled from GitHub",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "env",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Set a build-time environment variable using the same syntax as 'docker run'. Example: `--env=GOOS=linux --env=GOARCH=amd64`",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "go-tags",
|
||||
Usage: "Sets the go `-tags` flag when compiling the backend",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "go-version",
|
||||
Usage: "The version of Go to be used for building the Grafana backend",
|
||||
Required: false,
|
||||
Value: "1.21.8",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "yarn-cache",
|
||||
Usage: "If there is a yarn cache directory, then mount that when running 'yarn install' instead of creating a cache directory",
|
||||
},
|
||||
}
|
||||
|
||||
// DockerFlags are used when producing docker images.
|
||||
var DockerFlags = []cli.Flag{
|
||||
arguments.DockerRegistryFlag,
|
||||
arguments.AlpineImageFlag,
|
||||
arguments.UbuntuImageFlag,
|
||||
arguments.TagFormatFlag,
|
||||
arguments.UbuntuTagFormatFlag,
|
||||
arguments.DockerOrgFlag,
|
||||
}
|
||||
|
||||
var DockerPublishFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "username",
|
||||
Usage: "The username to login to the docker registry when publishing images",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "The password to login to the docker registry when publishing images",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Usage: "Overrides the repository of the images",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "latest",
|
||||
Usage: "Tags the published images as latest",
|
||||
},
|
||||
}
|
||||
|
||||
var FlagDistros = &cli.StringSliceFlag{
|
||||
Name: "distro",
|
||||
Usage: "See the list of distributions with 'go tool dist list'. For variations of the same distribution, like 'armv6' or 'armv7', append an extra path part. Example: 'linux/arm/v6', or 'linux/amd64/v3'",
|
||||
Value: cli.NewStringSlice(flags.DefaultDistros...),
|
||||
}
|
||||
|
||||
var ConcurrencyFlags = flags.ConcurrencyFlags
|
||||
|
||||
// PackageFlags are flags that are used when building packages or similar artifacts (like binaries) for different distributions
|
||||
// from the grafana source code.
|
||||
var PackageFlags = []cli.Flag{
|
||||
FlagDistros,
|
||||
&cli.StringFlag{
|
||||
Name: "edition",
|
||||
Usage: "Simply alters the naming of the '.tar.gz' package. The string set will override the '-{flavor}' part of the package name",
|
||||
},
|
||||
}
|
||||
|
||||
var ProImageFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "github-token",
|
||||
Usage: "Github token to use for git cloning, by default will be pulled from GitHub",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-repo",
|
||||
Usage: "The Grafana repository",
|
||||
Required: false,
|
||||
Value: "https://github.com/grafana/grafana",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-version",
|
||||
Usage: "The Grafana version",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Usage: "The docker image repo",
|
||||
Value: "hosted-grafana-pro",
|
||||
Required: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "image-tag",
|
||||
Usage: "The docker image tag",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "release-type",
|
||||
Usage: "The Grafana release type",
|
||||
Value: "prerelease",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "push",
|
||||
Usage: "Push the built image to the container registry",
|
||||
Value: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "registry",
|
||||
Usage: "The container registry that the image should be pushed to. Required if --push is set.",
|
||||
Value: "docker.io",
|
||||
},
|
||||
}
|
||||
|
||||
var GCOMFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "api-url",
|
||||
Usage: "API URL used in requests to grafana.com",
|
||||
Value: "https://grafana.com/api/grafana",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "api-key",
|
||||
Usage: "API Key used in requests to grafana.com",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "download-url",
|
||||
Usage: "URL used to download packages from grafana.com",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "beta",
|
||||
Usage: "Use when publishing a beta version",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "nightly",
|
||||
Usage: "Use when publishing a nightly version",
|
||||
},
|
||||
}
|
||||
|
||||
// JoinFlags combines several slices of flags into one slice of flags.
|
||||
var JoinFlags = flags.Join
|
||||
|
||||
func JoinFlagsWithDefault(f ...[]cli.Flag) []cli.Flag {
|
||||
// Kind of gross but ensures that DefaultFlags are registered before any others.
|
||||
return JoinFlags(append([][]cli.Flag{flags.DefaultFlags}, f...)...)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package flags
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var ConcurrencyFlags = []cli.Flag{
|
||||
&cli.Int64Flag{
|
||||
Name: "parallel",
|
||||
Usage: "The number of parallel pipelines to run. This can be particularly useful for building for multiple distributions at the same time",
|
||||
DefaultText: "Just like with 'go test', this defaults to GOMAXPROCS",
|
||||
Value: int64(runtime.GOMAXPROCS(0)),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package flags
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var Verbose = &cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Increase log verbosity. WARNING: This setting could potentially log sensitive data",
|
||||
Value: false,
|
||||
}
|
||||
|
||||
var DefaultFlags = []cli.Flag{
|
||||
Platform,
|
||||
Verbose,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag.
|
||||
var DefaultDistros = []string{"linux/amd64"}
|
||||
|
||||
// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag.
|
||||
var DefaultPlatform = "linux/amd64"
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag.
|
||||
var DefaultDistros = []string{"linux/arm64"}
|
||||
|
||||
// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag.
|
||||
var DefaultPlatform = "linux/arm64"
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag.
|
||||
var DefaultDistros = []string{"linux/amd64"}
|
||||
|
||||
// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag.
|
||||
var DefaultPlatform = "linux/amd64"
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag.
|
||||
var DefaultDistros = []string{"linux/arm64"}
|
||||
|
||||
// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag.
|
||||
var DefaultPlatform = "linux/arm64"
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag.
|
||||
var DefaultDistros = []string{"linux/amd64"}
|
||||
|
||||
// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag.
|
||||
var DefaultPlatform = "linux/amd64"
|
||||
@@ -0,0 +1,7 @@
|
||||
package flags
|
||||
|
||||
// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag.
|
||||
var DefaultDistros = []string{"linux/arm64"}
|
||||
|
||||
// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag.
|
||||
var DefaultPlatform = "linux/arm64"
|
||||
@@ -0,0 +1,12 @@
|
||||
package flags
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
func Join(f ...[]cli.Flag) []cli.Flag {
|
||||
flags := []cli.Flag{}
|
||||
for _, v := range f {
|
||||
flags = append(flags, v...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package flags
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var Platform = &cli.StringFlag{
|
||||
Name: "platform",
|
||||
Usage: "The buildkit / dagger platform to run containers when building the backend",
|
||||
Value: DefaultPlatform,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package flags
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var PublishFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "destination",
|
||||
Usage: "full URL to upload the artifacts to (examples: '/tmp/package.tar.gz', 'file://package.tar.gz', 'file:///tmp/package.tar.gz', 'gs://bucket/grafana/')",
|
||||
Aliases: []string{"d"},
|
||||
Value: "dist",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "checksum",
|
||||
Usage: "When enabled, also creates a `.sha256' checksum file in the destination that matches the checksum of the artifact(s) produced",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var GCOMCommand = &cli.Command{
|
||||
Name: "gcom",
|
||||
Description: "Executes requests to grafana.com",
|
||||
Subcommands: []*cli.Command{GCOMPublishCommand},
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipelines"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var GCOMPublishCommand = &cli.Command{
|
||||
Name: "publish",
|
||||
Action: PipelineActionWithPackageInput(pipelines.PublishGCOM),
|
||||
Description: "Publishes a grafana.tar.gz (ideally one built using the 'package' command) to grafana.com (--destination will be the download path)",
|
||||
Flags: JoinFlagsWithDefault(
|
||||
GCOMFlags,
|
||||
PackageInputFlags,
|
||||
PublishFlags,
|
||||
ConcurrencyFlags,
|
||||
),
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipelines"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Deprecated: use the Artifact type instead
|
||||
func PipelineActionWithPackageInput(pf pipelines.PipelineFuncWithPackageInput) cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
var (
|
||||
ctx = c.Context
|
||||
opts = []dagger.ClientOpt{}
|
||||
)
|
||||
if c.Bool("verbose") {
|
||||
opts = append(opts, dagger.WithLogOutput(os.Stderr))
|
||||
}
|
||||
client, err := dagger.Connect(ctx, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(c *dagger.Client) {
|
||||
if err := c.Close(); err != nil {
|
||||
fmt.Println("error closing dagger client:", err)
|
||||
}
|
||||
}(client)
|
||||
|
||||
args, err := pipelines.PipelineArgsFromContext(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args.PackageInputOpts.Packages) == 0 {
|
||||
return errors.New("expected at least one package from a '--package' flag")
|
||||
}
|
||||
|
||||
if err := pf(ctx, client, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipelines"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var PublishNPMCommand = &cli.Command{
|
||||
Name: "publish",
|
||||
Action: PipelineActionWithPackageInput(pipelines.PublishNPM),
|
||||
Usage: "Using a grafana.tar.gz as input (ideally one built using the 'package' command), take the npm artifacts and publish them on NPM.",
|
||||
Flags: JoinFlagsWithDefault(
|
||||
PackageInputFlags,
|
||||
NPMFlags,
|
||||
GCPFlags,
|
||||
ConcurrencyFlags,
|
||||
),
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipelines"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var PackagePublishCommand = &cli.Command{
|
||||
Name: "publish",
|
||||
Action: PipelineActionWithPackageInput(pipelines.PublishPackage),
|
||||
Description: "Publishes a grafana.tar.gz (ideally one built using the 'package' command) in the destination directory (--destination)",
|
||||
Flags: JoinFlagsWithDefault(
|
||||
PackageInputFlags,
|
||||
PublishFlags,
|
||||
GCPFlags,
|
||||
ConcurrencyFlags,
|
||||
),
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipelines"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var ProImageCommand = &cli.Command{
|
||||
Name: "pro-image",
|
||||
Action: PipelineActionWithPackageInput(pipelines.ProImage),
|
||||
Description: "Creates a hosted grafana pro image",
|
||||
Flags: JoinFlagsWithDefault(ProImageFlags, GCPFlags, PackageInputFlags),
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// package containers holds functions to make it easier to work with dagger containers.
|
||||
package containers
|
||||
@@ -0,0 +1,29 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorNonZero = errors.New("container exited with non-zero exit code")
|
||||
)
|
||||
|
||||
// ExitError functionally replaces '(*container).ExitCode' in a more usable way.
|
||||
// It will return an error with the container's stderr and stdout if the exit code is not zero.
|
||||
func ExitError(ctx context.Context, container *dagger.Container) (*dagger.Container, error) {
|
||||
container, err := container.Sync(ctx)
|
||||
if err == nil {
|
||||
return container, nil
|
||||
}
|
||||
|
||||
var e *dagger.ExecError
|
||||
if errors.As(err, &e) {
|
||||
return container, fmt.Errorf("%w\nstdout: %s\nstderr: %s", ErrorNonZero, e.Stdout, e.Stderr)
|
||||
}
|
||||
|
||||
return container, err
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package containers
|
||||
|
||||
import "dagger.io/dagger"
|
||||
|
||||
// ExtractedActive returns a directory that holds an extracted tar.gz
|
||||
func ExtractedArchive(d *dagger.Client, f *dagger.File) *dagger.Directory {
|
||||
return d.Container().From("busybox").
|
||||
// Workaround for now (maybe unnecessary?): set a FILE environment variable so that we don't accidentally cache
|
||||
WithFile("/src/archive.tar.gz", f).
|
||||
WithExec([]string{"mkdir", "-p", "/src/archive"}).
|
||||
WithExec([]string{"tar", "--strip-components=1", "-xzf", "/src/archive.tar.gz", "-C", "/src/archive"}).
|
||||
Directory("/src/archive")
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package containers
|
||||
@@ -0,0 +1,132 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
const GoogleCloudImage = "google/cloud-sdk:alpine"
|
||||
|
||||
// GCPAuthenticator injects authentication information into the provided container.
|
||||
type GCPAuthenticator interface {
|
||||
Authenticate(*dagger.Client, *dagger.Container) (*dagger.Container, error)
|
||||
}
|
||||
|
||||
// GCPServiceAccount satisfies GCPAuthenticator and injects the provided ServiceAccount into the filesystem and adds a 'gcloud auth activate-service-account'
|
||||
type GCPServiceAccount struct {
|
||||
DaggerFile *dagger.File
|
||||
JSONFile string
|
||||
}
|
||||
|
||||
func (a *GCPServiceAccount) Authenticate(d *dagger.Client, c *dagger.Container) (*dagger.Container, error) {
|
||||
if a.DaggerFile == nil && a.JSONFile == "" {
|
||||
return nil, fmt.Errorf("GCPServiceAccount authentication missed JSONFile AND DaggerFile")
|
||||
}
|
||||
var container *dagger.Container
|
||||
|
||||
if a.JSONFile != "" {
|
||||
container = c.WithMountedFile(
|
||||
"/opt/service_account.json",
|
||||
d.Host().Directory(filepath.Dir(a.JSONFile)).File(filepath.Base(a.JSONFile)),
|
||||
)
|
||||
}
|
||||
|
||||
if a.DaggerFile != nil {
|
||||
container = c.WithMountedFile("/opt/service_account.json", a.DaggerFile)
|
||||
}
|
||||
|
||||
return container.WithExec([]string{"gcloud", "auth", "activate-service-account", "--key-file", "/opt/service_account.json"}), nil
|
||||
}
|
||||
|
||||
func NewGCPServiceAccount(filepath string) *GCPServiceAccount {
|
||||
return &GCPServiceAccount{
|
||||
JSONFile: filepath,
|
||||
}
|
||||
}
|
||||
|
||||
func NewGCPServiceAccountWithFile(file *dagger.File) *GCPServiceAccount {
|
||||
return &GCPServiceAccount{
|
||||
DaggerFile: file,
|
||||
}
|
||||
}
|
||||
|
||||
// InheritedServiceAccount uses `gcloud` command in the current shell to get the GCS credentials.
|
||||
// This type should really only be used when running locally.
|
||||
type GCPInheritedAuth struct{}
|
||||
|
||||
func (a *GCPInheritedAuth) Authenticate(d *dagger.Client, c *dagger.Container) (*dagger.Container, error) {
|
||||
if val, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); ok {
|
||||
return c.WithMountedDirectory("/auth/credentials.json", d.Host().Directory(val)).WithEnvVariable("GOOGLE_APPLICATION_CREDENTIALS", "/auth/credentials.json"), nil
|
||||
}
|
||||
|
||||
cfg, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.WithMountedDirectory("/root/.config/gcloud", d.Host().Directory(filepath.Join(cfg, ".config", "gcloud"))), nil
|
||||
}
|
||||
|
||||
func GCSUploadDirectory(d *dagger.Client, image string, auth GCPAuthenticator, dir *dagger.Directory, dst string) (*dagger.Container, error) {
|
||||
container := d.Container().From(image).
|
||||
WithMountedDirectory("/src", dir)
|
||||
|
||||
var err error
|
||||
container, err = auth.Authenticate(d, container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secret := d.SetSecret("gcs-destination", dst)
|
||||
container = container.WithSecretVariable("GCS_DESTINATION", secret)
|
||||
|
||||
return container.WithExec([]string{"/bin/sh", "-c", "gcloud storage cp -r /src/* ${GCS_DESTINATION}"}), nil
|
||||
}
|
||||
|
||||
func GCSDownloadFile(d *dagger.Client, image string, auth GCPAuthenticator, url string) (*dagger.File, error) {
|
||||
var (
|
||||
container = d.Container().From(image)
|
||||
err error
|
||||
r = rand.Int()
|
||||
)
|
||||
|
||||
container, err = auth.Authenticate(d, container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret := d.SetSecret("gcs-download-url", url)
|
||||
file := container.
|
||||
WithEnvVariable("RAND", strconv.Itoa(r)).
|
||||
WithSecretVariable("GCS_DOWNLOAD_URL", secret).
|
||||
WithExec([]string{"/bin/sh", "-c", "gcloud storage cp ${GCS_DOWNLOAD_URL} /src/file"}).
|
||||
File("/src/file")
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func GCSAuth(d *dagger.Client, opts *GCPOpts) GCPAuthenticator {
|
||||
var auth GCPAuthenticator = &GCPInheritedAuth{}
|
||||
// The order of operations:
|
||||
// 1. Try to use base64 key.
|
||||
// 2. Try to use gcp-service-account-key (path to a file).
|
||||
// 3. Try mounting $XDG_CONFIG_HOME/gcloud
|
||||
if key := opts.ServiceAccountKeyBase64; key != "" {
|
||||
secret := d.SetSecret("gcp-sa-key-base64", key)
|
||||
// Write key to a file in an alpine container...
|
||||
file := d.Container().From("alpine").
|
||||
WithSecretVariable("GCP_SERVICE_ACCOUNT_KEY_BASE64", secret).
|
||||
WithExec([]string{"/bin/sh", "-c", "echo $GCP_SERVICE_ACCOUNT_KEY_BASE64 | base64 -d > /key.json"}).
|
||||
File("/key.json")
|
||||
|
||||
auth = NewGCPServiceAccountWithFile(file)
|
||||
} else if key := opts.ServiceAccountKey; key != "" {
|
||||
auth = NewGCPServiceAccount(key)
|
||||
}
|
||||
|
||||
return auth
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package containers
|
||||
|
||||
import "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
|
||||
|
||||
// GCPOpts are options used when using Google Cloud Platform / the Google Cloud SDK.
|
||||
type GCPOpts struct {
|
||||
ServiceAccountKey string
|
||||
ServiceAccountKeyBase64 string
|
||||
}
|
||||
|
||||
func GCPOptsFromFlags(c cliutil.CLIContext) *GCPOpts {
|
||||
return &GCPOpts{
|
||||
ServiceAccountKeyBase64: c.String("gcp-service-account-key-base64"),
|
||||
ServiceAccountKey: c.String("gcp-service-account-key"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package containers
|
||||
|
||||
import "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
|
||||
|
||||
type ProImageOpts struct {
|
||||
// Github token used to clone private repositories.
|
||||
GitHubToken string
|
||||
|
||||
// The path to a Grafana debian package.
|
||||
Deb string
|
||||
|
||||
// The Grafana version.
|
||||
GrafanaVersion string
|
||||
|
||||
// The docker image tag.
|
||||
ImageTag string
|
||||
|
||||
// The docker image repo.
|
||||
Repo string
|
||||
|
||||
// The release type.
|
||||
ReleaseType string
|
||||
|
||||
// True if the pro image should be pushed to the container registry.
|
||||
Push bool
|
||||
|
||||
// The container registry that the image should be pushed to. Required if Push is true.
|
||||
ContainerRegistry string
|
||||
}
|
||||
|
||||
func ProImageOptsFromFlags(c cliutil.CLIContext) *ProImageOpts {
|
||||
return &ProImageOpts{
|
||||
GitHubToken: c.String("github-token"),
|
||||
Deb: c.String("deb"),
|
||||
GrafanaVersion: c.String("grafana-version"),
|
||||
ImageTag: c.String("image-tag"),
|
||||
Repo: c.String("repo"),
|
||||
ReleaseType: c.String("release-type"),
|
||||
Push: c.Bool("push"),
|
||||
ContainerRegistry: c.String("registry"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
|
||||
)
|
||||
|
||||
type PackageInputOpts struct {
|
||||
// Name is used when overriding the artifact that is being produced. This is used in very specific scenarios where
|
||||
// the source package's name does not match the package's metadata name.
|
||||
Name string
|
||||
Packages []string
|
||||
}
|
||||
|
||||
func PackageInputOptsFromFlags(c cliutil.CLIContext) *PackageInputOpts {
|
||||
return &PackageInputOpts{
|
||||
Name: c.String("name"),
|
||||
Packages: c.StringSlice("package"),
|
||||
}
|
||||
}
|
||||
|
||||
// GetPackage uses the PackageInputOpts to get a Grafana package, either from the local filesystem (if the package is of type 'file://...')
|
||||
// or Google Cloud Storage if the package is a 'gs://' URL.
|
||||
func GetPackages(ctx context.Context, d *dagger.Client, packageOpts *PackageInputOpts, gcpOpts *GCPOpts) ([]*dagger.File, error) {
|
||||
files := make([]*dagger.File, len(packageOpts.Packages))
|
||||
for i, pkg := range packageOpts.Packages {
|
||||
u, err := url.Parse(pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var file *dagger.File
|
||||
switch u.Scheme {
|
||||
case "file", "fs":
|
||||
p := strings.TrimPrefix(u.String(), u.Scheme+"://")
|
||||
f, err := getLocalPackage(ctx, d, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file = f
|
||||
case "gs":
|
||||
f, err := getGCSPackage(ctx, d, gcpOpts, u.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file = f
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %s", ErrorUnrecognizedScheme, u.Scheme)
|
||||
}
|
||||
|
||||
files[i] = file
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func getLocalPackage(ctx context.Context, d *dagger.Client, file string) (*dagger.File, error) {
|
||||
// pending https://github.com/dagger/dagger/issues/4745
|
||||
return d.Host().Directory(filepath.Dir(file)).File(filepath.Base(file)), nil
|
||||
}
|
||||
|
||||
func getGCSPackage(ctx context.Context, d *dagger.Client, opts *GCPOpts, gcsURL string) (*dagger.File, error) {
|
||||
auth := GCSAuth(d, opts)
|
||||
return GCSDownloadFile(d, GoogleCloudImage, auth, gcsURL)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package containers
|
||||
@@ -0,0 +1,38 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/cliutil"
|
||||
)
|
||||
|
||||
// PublishOpts fields are selectively used based on the protocol field of the destination.
|
||||
// Be sure to fill out the applicable fields (or all of them) when calling a 'Publish' func.
|
||||
type PublishOpts struct {
|
||||
// Destination is any URL to publish an artifact(s) to.
|
||||
// Examples:
|
||||
// * '/tmp/package.tar.gz'
|
||||
// * 'file:///tmp/package.tar.gz'
|
||||
// * 'gcs://bucket/package.tar.gz'
|
||||
Destination string
|
||||
|
||||
// Checksum defines if the PublishFile function should also produce / publish a checksum of the given `*dagger.File'
|
||||
Checksum bool
|
||||
}
|
||||
|
||||
func PublishOptsFromFlags(c cliutil.CLIContext) *PublishOpts {
|
||||
return &PublishOpts{
|
||||
Destination: c.String("destination"),
|
||||
Checksum: c.Bool("checksum"),
|
||||
}
|
||||
}
|
||||
|
||||
var ErrorUnrecognizedScheme = errors.New("unrecognized scheme")
|
||||
|
||||
type PublishFileOpts struct {
|
||||
File *dagger.File
|
||||
PublishOpts *PublishOpts
|
||||
GCPOpts *GCPOpts
|
||||
Destination string
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
func publishLocalDir(ctx context.Context, dir *dagger.Directory, dst string) error {
|
||||
if _, err := dir.Export(ctx, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishGCSDir(ctx context.Context, d *dagger.Client, dir *dagger.Directory, opts *GCPOpts, dst string) error {
|
||||
auth := GCSAuth(d, opts)
|
||||
uploader, err := GCSUploadDirectory(d, GoogleCloudImage, auth, dir, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := ExitError(ctx, uploader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PublishDirectory publishes a directory to the given destination.
|
||||
func PublishDirectory(ctx context.Context, d *dagger.Client, dir *dagger.Directory, opts *GCPOpts, dst string) (string, error) {
|
||||
log.Println("Publishing directory", dst)
|
||||
u, err := url.Parse(dst)
|
||||
if err != nil {
|
||||
// If the destination URL is not a URL then we can assume that it's just a filepath.
|
||||
if err := publishLocalDir(ctx, dir, dst); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "file", "fs":
|
||||
dst := strings.TrimPrefix(u.String(), u.Scheme+"://")
|
||||
if err := publishLocalDir(ctx, dir, dst); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case "gs":
|
||||
if err := publishGCSDir(ctx, d, dir, opts, dst); err != nil {
|
||||
return "", err
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("%w: '%s'", ErrorUnrecognizedScheme, u.Scheme)
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
// Sha256 returns a dagger.File which contains the sha256 for the provided file.
|
||||
func Sha256(d *dagger.Client, file *dagger.File) *dagger.File {
|
||||
return d.Container().From("busybox").
|
||||
WithFile("/src/file", file).
|
||||
WithExec([]string{"/bin/sh", "-c", "sha256sum /src/file | awk '{print $1}' > /src/file.sha256"}).
|
||||
File("/src/file.sha256")
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package containers
|
||||
|
||||
// func BackendTestShort(d *dagger.Client, platform dagger.Platform, dir *dagger.Directory) *dagger.Container {
|
||||
// return GrafanaContainer(d, platform, GetGoImageAlpine("1.21.0"), dir).
|
||||
// WithExec([]string{"go", "test", "-tags", "requires_buildifer", "-short", "-covermode", "atomic", "-timeout", "5m", "./pkg/..."})
|
||||
// }
|
||||
//
|
||||
// func BackendTestIntegration(d *dagger.Client, platform dagger.Platform, dir *dagger.Directory) *dagger.Container {
|
||||
// return GrafanaContainer(d, platform, GetGoImageAlpine("1.21.0"), dir).
|
||||
// WithExec([]string{"go", "test", "-run", "Integration", "-covermode", "atomic", "-timeout", "5m", "./pkg/..."})
|
||||
// }
|
||||
@@ -0,0 +1,24 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
// GetJSONValue gets the value of a JSON field from a JSON file in the 'src' directory.
|
||||
func GetJSONValue(ctx context.Context, d *dagger.Client, src *dagger.Directory, file string, field string) (string, error) {
|
||||
c := d.Container().From("alpine").
|
||||
WithExec([]string{"apk", "--update", "add", "jq"}).
|
||||
WithMountedDirectory("/src", src).
|
||||
WithWorkdir("/src").
|
||||
WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("cat %s | jq -r .%s", file, field)})
|
||||
|
||||
if stdout, err := c.Stdout(ctx); err == nil {
|
||||
return strings.TrimSpace(stdout), nil
|
||||
}
|
||||
|
||||
return c.Stderr(ctx)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package containers
|
||||
@@ -0,0 +1,23 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func EnvVar(name, value string) Env {
|
||||
return Env{Name: name, Value: value}
|
||||
}
|
||||
|
||||
func WithEnv(c *dagger.Container, env []Env) *dagger.Container {
|
||||
container := c
|
||||
for _, v := range env {
|
||||
container = container.WithEnvVariable(v.Name, v.Value)
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package daggerutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"dagger.io/dagger"
|
||||
)
|
||||
|
||||
// HostDir checks that the directory at 'path' exists and returns the dagger.Directory at 'path'.
|
||||
func HostDir(d *dagger.Client, path string) (*dagger.Directory, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil, errors.New("given hostdir is not a directory")
|
||||
}
|
||||
|
||||
return d.Host().Directory(path), nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/containers"
|
||||
)
|
||||
|
||||
type BuildOpts struct {
|
||||
// Dockerfile is the path to the dockerfile with the '-f' command.
|
||||
// If it's not provided, then the docker command will default to 'Dockerfile' in `pwd`.
|
||||
Dockerfile string
|
||||
|
||||
// Tags are provided as the '-t' argument, and can include the registry domain as well as the repository.
|
||||
// Docker build supports building the same image with multiple tags.
|
||||
// You might want to also include a 'latest' version of the tag.
|
||||
Tags []string
|
||||
// BuildArgs are provided to the docker command as '--build-arg'
|
||||
BuildArgs []string
|
||||
// Set the target build stage to build as '--target'
|
||||
Target string
|
||||
|
||||
// Platform, if set to the non-default value, will use buildkit's emulation to build the docker image. This can be useful if building a docker image for a platform that doesn't match the host platform.
|
||||
Platform dagger.Platform
|
||||
}
|
||||
|
||||
func Builder(d *dagger.Client, socket *dagger.Socket, targz *dagger.File) *dagger.Container {
|
||||
extracted := containers.ExtractedArchive(d, targz)
|
||||
|
||||
// Instead of supplying the Platform argument here, we need to tell the host docker socket that it needs to build with the given platform.
|
||||
return d.Container().From("docker").
|
||||
WithUnixSocket("/var/run/docker.sock", socket).
|
||||
WithWorkdir("/src").
|
||||
WithMountedFile("/src/Dockerfile", extracted.File("Dockerfile")).
|
||||
WithMountedFile("/src/packaging/docker/run.sh", extracted.File("packaging/docker/run.sh")).
|
||||
WithMountedFile("/src/grafana.tar.gz", targz)
|
||||
}
|
||||
|
||||
func Build(d *dagger.Client, builder *dagger.Container, opts *BuildOpts) *dagger.Container {
|
||||
args := []string{"docker", "buildx", "build"}
|
||||
if p := opts.Platform; p != "" {
|
||||
args = append(args, fmt.Sprintf("--platform=%s", string(p)))
|
||||
}
|
||||
dockerfile := opts.Dockerfile
|
||||
if dockerfile == "" {
|
||||
dockerfile = "Dockerfile"
|
||||
}
|
||||
|
||||
args = append(args, ".", "-f", dockerfile)
|
||||
|
||||
for _, v := range opts.BuildArgs {
|
||||
args = append(args, fmt.Sprintf("--build-arg=%s", v))
|
||||
}
|
||||
|
||||
for _, v := range opts.Tags {
|
||||
args = append(args, "-t", v)
|
||||
}
|
||||
|
||||
if opts.Target != "" {
|
||||
args = append(args, "--target", opts.Target)
|
||||
}
|
||||
|
||||
return builder.WithExec(args)
|
||||
}
|
||||
|
||||
func Save(builder *dagger.Container, opts *BuildOpts) *dagger.File {
|
||||
return builder.WithExec([]string{"docker", "save", opts.Tags[0], "-o", "image.tar.gz"}).File("image.tar.gz")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package docker
|
||||
|
||||
type DockerOpts struct {
|
||||
// Registry is the docker Registry for the image.
|
||||
// If using '--save', then this will have no effect.
|
||||
// Uses docker hub by default.
|
||||
// Example: us.gcr.io/12345
|
||||
Registry string
|
||||
|
||||
// AlpineBase is supplied as a build-arg when building the Grafana docker image.
|
||||
// When building alpine versions of Grafana it uses this image as its base.
|
||||
AlpineBase string
|
||||
|
||||
// UbuntuBase is supplied as a build-arg when building the Grafana docker image.
|
||||
// When building ubuntu versions of Grafana it uses this image as its base.
|
||||
UbuntuBase string
|
||||
|
||||
// Username is supplied to login to the docker registry when publishing images.
|
||||
Username string
|
||||
|
||||
// Password is supplied to login to the docker registry when publishing images.
|
||||
Password string
|
||||
|
||||
// Org overrides the organization when when publishing images.
|
||||
Org string
|
||||
|
||||
// Repository overrides the repository when when publishing images.
|
||||
Repository string
|
||||
|
||||
// Latest is supplied to also tag as latest when publishing images.
|
||||
Latest bool
|
||||
|
||||
// TagFormat and UbuntuTagFormat should be formatted using go template tags.
|
||||
TagFormat string
|
||||
UbuntuTagFormat string
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user