#2 Pulumi IaC scaffolding: Worker, R2 bucket, Google Cloud DNS
Add an infra/ Pulumi (Go) program in its own module (github.com/JMR-dev/LibreMail-Bug-Report-Ingest/infra) that declares the three pieces of edge/DNS infrastructure for the bug-report ingest pipeline: - Cloudflare Worker script (libremail-bug-report-ingest, built in #1) - Cloudflare R2 bucket (libremail-bug-reports) for encrypted reports (ADR 0001) - Google Cloud DNS record (CNAME) pointing the ingest hostname at the Worker, referencing an existing managed zone by name Per-environment stacks (dev/prod) via Pulumi.<stack>.yaml + pulumi.Config; account id, zone, domain, etc. are parameterized through config and secrets are kept out of git (documented in infra/README.md). Worker content is a documented placeholder because the real TinyGo->Wasm artifact is produced by the build pipeline. Mock-based unit tests (pulumi.RunErr + pulumi.WithMocks) assert the registered resources and their inputs; go build + go vet + go test all pass without the Pulumi CLI. Structured so the #7 Cloudflare Rate Limiting ruleset can be added later (reserved cloudflareZoneId config + insertion point in deploy.go). Providers: pulumi-cloudflare v6.17.0, pulumi-gcp v8.41.1, pulumi/sdk v3.250.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
# Dev stack configuration.
|
||||
#
|
||||
# NON-SECRET values only. Secrets (Cloudflare API token, GCP credentials) are set
|
||||
# with `pulumi config set --secret ...` (they land here ENCRYPTED) or supplied via
|
||||
# provider environment variables. NEVER commit a plaintext secret. See README.md.
|
||||
#
|
||||
# Replace every REPLACE_ME_* value below with your real dev account/zone/domain
|
||||
# before running `pulumi up`.
|
||||
config:
|
||||
# --- GCP provider (see also GOOGLE_CREDENTIALS / gcp:credentials) ---
|
||||
gcp:project: REPLACE_ME_GCP_PROJECT_ID
|
||||
|
||||
# --- Program config (namespace = project name from Pulumi.yaml) ---
|
||||
libremail-bug-report-ingest-infra:cloudflareAccountId: REPLACE_ME_CLOUDFLARE_ACCOUNT_ID
|
||||
libremail-bug-report-ingest-infra:workerName: libremail-bug-report-ingest
|
||||
libremail-bug-report-ingest-infra:workerCompatibilityDate: "2025-06-01"
|
||||
libremail-bug-report-ingest-infra:r2BucketName: libremail-bug-reports-dev
|
||||
# R2 location hint (optional). One of: apac, eeur, enam, weur, wnam, oc.
|
||||
libremail-bug-report-ingest-infra:r2BucketLocation: enam
|
||||
# Google Cloud DNS managed-zone NAME (the zone already exists; it is referenced,
|
||||
# not created, by this stack).
|
||||
libremail-bug-report-ingest-infra:dnsManagedZone: REPLACE_ME_GCLOUD_DNS_MANAGED_ZONE_NAME
|
||||
# Ingest hostname (FQDN, trailing dot) and the Worker route/custom-domain it
|
||||
# points at (CNAME target, FQDN, trailing dot).
|
||||
libremail-bug-report-ingest-infra:dnsRecordName: bugreport.dev.libremail.example.
|
||||
libremail-bug-report-ingest-infra:dnsRecordType: CNAME
|
||||
libremail-bug-report-ingest-infra:dnsRecordTarget: libremail-bug-report-ingest.REPLACE_ME_SUBDOMAIN.workers.dev.
|
||||
libremail-bug-report-ingest-infra:dnsTtlSeconds: "300"
|
||||
# Optional, reserved for #7 (rate-limit ruleset) and Worker routes/custom domain:
|
||||
# libremail-bug-report-ingest-infra:cloudflareZoneId: REPLACE_ME_CLOUDFLARE_ZONE_ID
|
||||
@@ -0,0 +1,30 @@
|
||||
# Prod stack configuration.
|
||||
#
|
||||
# NON-SECRET values only. Secrets (Cloudflare API token, GCP credentials) are set
|
||||
# with `pulumi config set --secret ...` (they land here ENCRYPTED) or supplied via
|
||||
# provider environment variables. NEVER commit a plaintext secret. See README.md.
|
||||
#
|
||||
# Replace every REPLACE_ME_* value below with your real prod account/zone/domain
|
||||
# before running `pulumi up`.
|
||||
config:
|
||||
# --- GCP provider (see also GOOGLE_CREDENTIALS / gcp:credentials) ---
|
||||
gcp:project: REPLACE_ME_GCP_PROJECT_ID
|
||||
|
||||
# --- Program config (namespace = project name from Pulumi.yaml) ---
|
||||
libremail-bug-report-ingest-infra:cloudflareAccountId: REPLACE_ME_CLOUDFLARE_ACCOUNT_ID
|
||||
libremail-bug-report-ingest-infra:workerName: libremail-bug-report-ingest
|
||||
libremail-bug-report-ingest-infra:workerCompatibilityDate: "2025-06-01"
|
||||
libremail-bug-report-ingest-infra:r2BucketName: libremail-bug-reports
|
||||
# R2 location hint (optional). One of: apac, eeur, enam, weur, wnam, oc.
|
||||
libremail-bug-report-ingest-infra:r2BucketLocation: enam
|
||||
# Google Cloud DNS managed-zone NAME (the zone already exists; it is referenced,
|
||||
# not created, by this stack).
|
||||
libremail-bug-report-ingest-infra:dnsManagedZone: REPLACE_ME_GCLOUD_DNS_MANAGED_ZONE_NAME
|
||||
# Ingest hostname (FQDN, trailing dot) and the Worker route/custom-domain it
|
||||
# points at (CNAME target, FQDN, trailing dot).
|
||||
libremail-bug-report-ingest-infra:dnsRecordName: bugreport.libremail.example.
|
||||
libremail-bug-report-ingest-infra:dnsRecordType: CNAME
|
||||
libremail-bug-report-ingest-infra:dnsRecordTarget: libremail-bug-report-ingest.REPLACE_ME_SUBDOMAIN.workers.dev.
|
||||
libremail-bug-report-ingest-infra:dnsTtlSeconds: "300"
|
||||
# Optional, reserved for #7 (rate-limit ruleset) and Worker routes/custom domain:
|
||||
# libremail-bug-report-ingest-infra:cloudflareZoneId: REPLACE_ME_CLOUDFLARE_ZONE_ID
|
||||
@@ -0,0 +1,9 @@
|
||||
name: libremail-bug-report-ingest-infra
|
||||
runtime: go
|
||||
description: >-
|
||||
Pulumi (Go) infrastructure for the LibreMail bug-report ingest pipeline:
|
||||
the Cloudflare Worker, the encrypted-report Cloudflare R2 bucket, and the
|
||||
Google Cloud DNS record that points the ingest hostname at the Worker.
|
||||
Secrets (Cloudflare API token, GCP credentials) live in the stack config as
|
||||
Pulumi secrets / in the provider environment and are never committed. See
|
||||
README.md for the full config + secret contract.
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
# Infrastructure (Pulumi, Go)
|
||||
|
||||
Infrastructure-as-code for the LibreMail bug-report ingest pipeline. This is its
|
||||
own Go module (`github.com/JMR-dev/LibreMail-Bug-Report-Ingest/infra`) so the
|
||||
Worker module at the repo root stays lean.
|
||||
|
||||
It provisions, with the Pulumi Go SDK and the
|
||||
[`pulumi-cloudflare`](https://www.pulumi.com/registry/packages/cloudflare/) and
|
||||
[`pulumi-gcp`](https://www.pulumi.com/registry/packages/gcp/) providers:
|
||||
|
||||
| Resource | Type | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ingest-worker` | `cloudflare.WorkersScript` | The ingest Worker (`libremail-bug-report-ingest`, built in #1). |
|
||||
| `reports-bucket` | `cloudflare.R2Bucket` | Encrypted bug-report storage (`libremail-bug-reports`). Per [ADR 0001](../docs/decisions/encryption.md) only ciphertext is written. |
|
||||
| `ingest-dns-record` | `gcp.dns.RecordSet` | Google Cloud DNS record pointing the ingest hostname at the Worker's route/custom domain. |
|
||||
|
||||
DNS authority is **Google Cloud DNS**. The managed zone is **referenced by name**
|
||||
(it already exists / is managed elsewhere), and this stack only adds a record to
|
||||
it.
|
||||
|
||||
> **Worker content is a placeholder.** The deployed Worker is Go compiled by
|
||||
> TinyGo to Wasm plus the `syumai/workers` ES-module shim, emitted by
|
||||
> `pnpm run build` into `../build/` (git-ignored, produced by CI). This program
|
||||
> ships a documented placeholder module body so the resource is fully described
|
||||
> and unit-testable without the artifact. Wire the real artifact at deploy time
|
||||
> via the `workerScriptContent` config, or by setting `ContentFile` /
|
||||
> `ContentSha256` on the Worker resource to `../build/worker.mjs` in the deploy
|
||||
> pipeline.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Go (matching the repo toolchain) — `go build ./...` / `go test ./...` work
|
||||
without the Pulumi CLI.
|
||||
- The [Pulumi CLI](https://www.pulumi.com/docs/install/) — required only to
|
||||
`pulumi preview` / `pulumi up`. It is **not** installed in CI yet, so a real
|
||||
`pulumi preview` is a follow-up (see "Deploying").
|
||||
|
||||
## Configuration
|
||||
|
||||
Config is per-stack (`Pulumi.<stack>.yaml`). Program keys are namespaced by the
|
||||
project name, `libremail-bug-report-ingest-infra`. Provider keys use the
|
||||
`cloudflare:` / `gcp:` namespaces.
|
||||
|
||||
### Program config (namespace `libremail-bug-report-ingest-infra`)
|
||||
|
||||
| Key | Required | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `cloudflareAccountId` | yes | — | Cloudflare account that owns the Worker + R2 bucket. |
|
||||
| `dnsManagedZone` | yes | — | **Name** of the existing Google Cloud DNS managed zone. |
|
||||
| `dnsRecordName` | yes | — | Ingest hostname as an FQDN with a trailing dot (e.g. `bugreport.libremail.example.`). |
|
||||
| `dnsRecordTarget` | yes | — | CNAME target = the Worker route/custom domain (FQDN, trailing dot). |
|
||||
| `workerName` | no | `libremail-bug-report-ingest` | Worker script name (matches `wrangler.jsonc`). |
|
||||
| `workerCompatibilityDate` | no | `2025-06-01` | Worker runtime compatibility date. |
|
||||
| `workerScriptContent` | no | placeholder | Override the Worker module body (normally supplied by the build pipeline). |
|
||||
| `r2BucketName` | no | `libremail-bug-reports` | R2 bucket name. |
|
||||
| `r2BucketLocation` | no | (provider default) | R2 location hint: `apac`, `eeur`, `enam`, `weur`, `wnam`, `oc`. |
|
||||
| `dnsRecordType` | no | `CNAME` | DNS record type. |
|
||||
| `dnsTtlSeconds` | no | `300` | DNS record TTL (seconds). |
|
||||
| `gcpProject` | no | (from `gcp:project`) | Project the record belongs to, if different from the provider project. |
|
||||
| `cloudflareZoneId` | no | — | **Reserved** for #7 (rate-limit ruleset) and Worker routes/custom domain. Unused today. |
|
||||
|
||||
Set a non-secret value with, e.g.:
|
||||
|
||||
```console
|
||||
pulumi config set libremail-bug-report-ingest-infra:cloudflareAccountId <account-id>
|
||||
```
|
||||
|
||||
### Secrets and credentials — DO NOT COMMIT
|
||||
|
||||
Set these as **encrypted** Pulumi secrets (`--secret`) or via the provider's
|
||||
environment variables. Never place plaintext secrets in `Pulumi.<stack>.yaml`.
|
||||
|
||||
| What | How | Notes |
|
||||
| --- | --- | --- |
|
||||
| Cloudflare API token | `pulumi config set --secret cloudflare:apiToken <token>` (or env `CLOUDFLARE_API_TOKEN`) | Scope it to Workers Scripts + R2 admin for the account. |
|
||||
| GCP project | `pulumi config set gcp:project <project-id>` | Not secret; identifies the GCP project. |
|
||||
| GCP credentials | `pulumi config set --secret gcp:credentials "$(cat key.json)"` (or env `GOOGLE_CREDENTIALS` / Application Default Credentials) | Service account with Cloud DNS admin on the zone. |
|
||||
|
||||
The **bug-report encryption keyring** (`BUGREPORT_ENC_KEYRING`, see
|
||||
[ADR 0001](../docs/decisions/encryption.md)) lives in **Cloudflare Secrets
|
||||
Store**, bound to the Worker — it is managed there, not committed here.
|
||||
|
||||
## Deploying
|
||||
|
||||
```console
|
||||
pulumi stack select dev # or: pulumi stack init dev
|
||||
# set the REPLACE_ME_* config values + secrets above, then:
|
||||
pulumi preview
|
||||
pulumi up
|
||||
```
|
||||
|
||||
> Deployment is gated behind a maintainer check-in and the Pulumi CLI is not yet
|
||||
> available in this environment, so this change ships **compile- and
|
||||
> test-verified only**. Running a real `pulumi preview` against the accounts is a
|
||||
> follow-up.
|
||||
|
||||
## Testing (no Pulumi CLI required)
|
||||
|
||||
The program is exercised with the Pulumi Go SDK's mocking
|
||||
(`pulumi.RunErr` + `pulumi.WithMocks`), which registers resources against an
|
||||
in-memory monitor — no cloud calls, no CLI. The tests assert that the expected
|
||||
resources are registered with the expected inputs (Worker name/account, R2 bucket
|
||||
name/location, DNS type/name/target/ttl).
|
||||
|
||||
```console
|
||||
go vet ./...
|
||||
go build ./...
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Adding rate limiting later (#7)
|
||||
|
||||
[The abuse/rate-limit ADR](../docs/decisions/labels-and-abuse.md) chose to
|
||||
implement ingest rate limiting as **Cloudflare Rate Limiting rules via Pulumi**
|
||||
(`cloudflare.NewRuleset`, phase `http_ratelimit`, scoped to a zone). That is out
|
||||
of scope for this ticket. The `cloudflareZoneId` config key and the structure of
|
||||
`deploy.go` leave a clean insertion point; #7 adds the ruleset resource there.
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pulumi/pulumi-cloudflare/sdk/v6/go/cloudflare"
|
||||
"github.com/pulumi/pulumi-gcp/sdk/v8/go/gcp/dns"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
|
||||
)
|
||||
|
||||
// Logical (Pulumi) resource names. Stable across deploys and asserted by
|
||||
// deploy_test.go, so treat them as part of the program's contract.
|
||||
const (
|
||||
resWorker = "ingest-worker"
|
||||
resR2Bucket = "reports-bucket"
|
||||
resDNSRecord = "ingest-dns-record"
|
||||
)
|
||||
|
||||
// Defaults for config values that have a sensible project-wide default. Values
|
||||
// that identify a specific Cloudflare account, GCP project, DNS zone, or
|
||||
// hostname have no safe default and are required (see loadConfig).
|
||||
const (
|
||||
// defaultWorkerName matches wrangler.jsonc "name" and the Worker built in #1.
|
||||
defaultWorkerName = "libremail-bug-report-ingest"
|
||||
// defaultR2BucketName is the encrypted-report bucket (ADR 0001).
|
||||
defaultR2BucketName = "libremail-bug-reports"
|
||||
// defaultCompatibilityDate matches wrangler.jsonc "compatibility_date".
|
||||
defaultCompatibilityDate = "2025-06-01"
|
||||
// mainModule is the ES-module entry emitted alongside the Wasm build.
|
||||
mainModule = "worker.mjs"
|
||||
|
||||
defaultDNSRecordType = "CNAME"
|
||||
defaultDNSTTLSeconds = 300
|
||||
)
|
||||
|
||||
// placeholderWorkerScript is a stand-in module body for the Worker.
|
||||
//
|
||||
// The deployed Worker is Go compiled by TinyGo to Wasm plus the syumai/workers
|
||||
// ES-module shim, emitted by `pnpm run build` into ../build/ (git-ignored,
|
||||
// produced by CI). Uploading that multipart Wasm artifact is the build/deploy
|
||||
// pipeline's job; this program ships a documented placeholder so the resource is
|
||||
// fully described and unit-testable without the artifact present. Override it at
|
||||
// deploy time with the `workerScriptContent` config, or wire the real artifact
|
||||
// via ContentFile/ContentSha256 in the deploy pipeline. See infra/README.md.
|
||||
const placeholderWorkerScript = `// PLACEHOLDER - replaced at deploy time by the TinyGo -> Wasm build (see repo README).
|
||||
export default {
|
||||
async fetch() {
|
||||
return new Response("libremail-bug-report-ingest: placeholder worker; real build is produced by TinyGo.\n", { status: 501 });
|
||||
},
|
||||
};
|
||||
`
|
||||
|
||||
// infraConfig is the fully-resolved configuration for one stack. It is loaded
|
||||
// from pulumi.Config (the project-namespaced keys) plus provider config
|
||||
// (cloudflare:*, gcp:*) that the SDK reads directly.
|
||||
type infraConfig struct {
|
||||
// cloudflareAccountId is the Cloudflare account that owns the Worker + bucket.
|
||||
cloudflareAccountId string
|
||||
// cloudflareZoneId is optional and unused today. It is reserved so that
|
||||
// Worker routes / custom domains and the ingest rate-limit ruleset (#7) can
|
||||
// be added later without a config change. See the forward note in deploy.
|
||||
cloudflareZoneId string
|
||||
|
||||
workerName string
|
||||
workerContent string
|
||||
compatibilityDate string
|
||||
|
||||
r2BucketName string
|
||||
r2BucketLocation string // optional; Cloudflare picks a location when empty
|
||||
|
||||
// gcpProject is optional; when empty the record inherits the gcp:project
|
||||
// provider config.
|
||||
gcpProject string
|
||||
dnsManagedZone string
|
||||
dnsRecordName string
|
||||
dnsRecordType string
|
||||
dnsRecordTarget string
|
||||
dnsTTLSeconds int
|
||||
}
|
||||
|
||||
// loadConfig resolves the stack configuration, returning an error (rather than
|
||||
// exiting) when a required key is missing so the program is testable.
|
||||
func loadConfig(ctx *pulumi.Context) (*infraConfig, error) {
|
||||
cfg := config.New(ctx, "")
|
||||
|
||||
accountID, err := cfg.Try("cloudflareAccountId")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config cloudflareAccountId: %w", err)
|
||||
}
|
||||
managedZone, err := cfg.Try("dnsManagedZone")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config dnsManagedZone: %w", err)
|
||||
}
|
||||
recordName, err := cfg.Try("dnsRecordName")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config dnsRecordName: %w", err)
|
||||
}
|
||||
recordTarget, err := cfg.Try("dnsRecordTarget")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config dnsRecordTarget: %w", err)
|
||||
}
|
||||
|
||||
ttl := defaultDNSTTLSeconds
|
||||
if v, err := cfg.TryInt("dnsTtlSeconds"); err == nil {
|
||||
ttl = v
|
||||
} else if !errors.Is(err, config.ErrMissingVar) {
|
||||
return nil, fmt.Errorf("config dnsTtlSeconds: %w", err)
|
||||
}
|
||||
|
||||
return &infraConfig{
|
||||
cloudflareAccountId: accountID,
|
||||
cloudflareZoneId: cfg.Get("cloudflareZoneId"),
|
||||
workerName: getOr(cfg, "workerName", defaultWorkerName),
|
||||
workerContent: getOr(cfg, "workerScriptContent", placeholderWorkerScript),
|
||||
compatibilityDate: getOr(cfg, "workerCompatibilityDate", defaultCompatibilityDate),
|
||||
r2BucketName: getOr(cfg, "r2BucketName", defaultR2BucketName),
|
||||
r2BucketLocation: cfg.Get("r2BucketLocation"),
|
||||
gcpProject: cfg.Get("gcpProject"),
|
||||
dnsManagedZone: managedZone,
|
||||
dnsRecordName: recordName,
|
||||
dnsRecordType: getOr(cfg, "dnsRecordType", defaultDNSRecordType),
|
||||
dnsRecordTarget: recordTarget,
|
||||
dnsTTLSeconds: ttl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getOr returns the config value for key, or def when it is unset/empty.
|
||||
func getOr(cfg *config.Config, key, def string) string {
|
||||
if v := cfg.Get(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// deploy registers all resources for the bug-report ingest stack.
|
||||
func deploy(ctx *pulumi.Context) error {
|
||||
cfg, err := loadConfig(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 1. Cloudflare Worker script - the ingest Worker built in #1. Content is a
|
||||
// documented placeholder; the real Wasm artifact is uploaded by the build
|
||||
// pipeline (see placeholderWorkerScript).
|
||||
worker, err := cloudflare.NewWorkersScript(ctx, resWorker, &cloudflare.WorkersScriptArgs{
|
||||
AccountId: pulumi.String(cfg.cloudflareAccountId),
|
||||
ScriptName: pulumi.String(cfg.workerName),
|
||||
Content: pulumi.String(cfg.workerContent),
|
||||
MainModule: pulumi.String(mainModule),
|
||||
CompatibilityDate: pulumi.String(cfg.compatibilityDate),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("worker script: %w", err)
|
||||
}
|
||||
|
||||
// 2. Cloudflare R2 bucket for the encrypted bug-report objects. Per ADR 0001
|
||||
// the Worker encrypts each report with AES-256-GCM before writing, so only
|
||||
// ciphertext is ever stored here.
|
||||
bucketArgs := &cloudflare.R2BucketArgs{
|
||||
AccountId: pulumi.String(cfg.cloudflareAccountId),
|
||||
Name: pulumi.String(cfg.r2BucketName),
|
||||
}
|
||||
if cfg.r2BucketLocation != "" {
|
||||
bucketArgs.Location = pulumi.String(cfg.r2BucketLocation)
|
||||
}
|
||||
bucket, err := cloudflare.NewR2Bucket(ctx, resR2Bucket, bucketArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("r2 bucket: %w", err)
|
||||
}
|
||||
|
||||
// 3. Google Cloud DNS record pointing the ingest hostname at the Worker's
|
||||
// route/custom domain. DNS authority is Google Cloud DNS; the managed zone
|
||||
// is referenced by name (it is managed outside this stack) and a record is
|
||||
// added to it.
|
||||
recordArgs := &dns.RecordSetArgs{
|
||||
ManagedZone: pulumi.String(cfg.dnsManagedZone),
|
||||
Name: pulumi.String(cfg.dnsRecordName),
|
||||
Type: pulumi.String(cfg.dnsRecordType),
|
||||
Ttl: pulumi.Int(cfg.dnsTTLSeconds),
|
||||
Rrdatas: pulumi.StringArray{pulumi.String(cfg.dnsRecordTarget)},
|
||||
}
|
||||
if cfg.gcpProject != "" {
|
||||
recordArgs.Project = pulumi.String(cfg.gcpProject)
|
||||
}
|
||||
record, err := dns.NewRecordSet(ctx, resDNSRecord, recordArgs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dns record: %w", err)
|
||||
}
|
||||
|
||||
// Forward note (#7 / docs/decisions/labels-and-abuse.md): the accepted ADR
|
||||
// implements ingest rate limiting as Cloudflare Rate Limiting rules via
|
||||
// Pulumi (cloudflare.NewRuleset with Phase "http_ratelimit", scoped to
|
||||
// cloudflareZoneId). That is out of scope for #2, but the config
|
||||
// (cloudflareZoneId) and this structure leave a clean insertion point:
|
||||
// add the ruleset resource here. Worker routes / a custom domain would
|
||||
// attach the same way via cloudflareZoneId.
|
||||
|
||||
ctx.Export("workerName", worker.ScriptName)
|
||||
ctx.Export("workerAccountId", worker.AccountId)
|
||||
ctx.Export("r2BucketName", bucket.Name)
|
||||
ctx.Export("dnsRecordFqdn", record.Name)
|
||||
ctx.Export("dnsRecordTargets", record.Rrdatas)
|
||||
if cfg.cloudflareZoneId != "" {
|
||||
ctx.Export("cloudflareZoneId", pulumi.String(cfg.cloudflareZoneId))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
)
|
||||
|
||||
// testProject is the Pulumi project name used for the mock runs. Program config
|
||||
// keys are namespaced by it (matches Pulumi.yaml "name").
|
||||
const testProject = "libremail-bug-report-ingest-infra"
|
||||
|
||||
// Resource type tokens registered by the program.
|
||||
const (
|
||||
tokWorkersScript = "cloudflare:index/workersScript:WorkersScript"
|
||||
tokR2Bucket = "cloudflare:index/r2Bucket:R2Bucket"
|
||||
tokDNSRecordSet = "gcp:dns/recordSet:RecordSet"
|
||||
)
|
||||
|
||||
// recordingMocks implements pulumi.MockResourceMonitor, capturing every
|
||||
// RegisterResource call so tests can assert on the inputs the program sends.
|
||||
// No provider is ever contacted, so these tests run without the Pulumi CLI.
|
||||
type recordingMocks struct {
|
||||
resources []pulumi.MockResourceArgs
|
||||
}
|
||||
|
||||
func (m *recordingMocks) NewResource(args pulumi.MockResourceArgs) (string, resource.PropertyMap, error) {
|
||||
m.resources = append(m.resources, args)
|
||||
// Echo inputs back as the resource's state and synthesize a physical ID.
|
||||
return args.Name + "-id", args.Inputs, nil
|
||||
}
|
||||
|
||||
func (m *recordingMocks) Call(args pulumi.MockCallArgs) (resource.PropertyMap, error) {
|
||||
return resource.PropertyMap{}, nil
|
||||
}
|
||||
|
||||
// find returns the first registered resource with the given type token.
|
||||
func (m *recordingMocks) find(t *testing.T, typeToken string) pulumi.MockResourceArgs {
|
||||
t.Helper()
|
||||
for _, r := range m.resources {
|
||||
if r.TypeToken == typeToken {
|
||||
return r
|
||||
}
|
||||
}
|
||||
t.Fatalf("no resource registered with type token %q", typeToken)
|
||||
return pulumi.MockResourceArgs{}
|
||||
}
|
||||
|
||||
func key(k string) string { return testProject + ":" + k }
|
||||
|
||||
// fullConfig is a complete, valid stack config for the happy-path tests.
|
||||
func fullConfig() map[string]string {
|
||||
return map[string]string{
|
||||
key("cloudflareAccountId"): "cf-acct-123",
|
||||
key("dnsManagedZone"): "libremail-zone",
|
||||
key("dnsRecordName"): "bugreport.example.com.",
|
||||
key("dnsRecordTarget"): "libremail-bug-report-ingest.acme.workers.dev.",
|
||||
key("r2BucketLocation"): "enam",
|
||||
key("gcpProject"): "libremail-proj",
|
||||
}
|
||||
}
|
||||
|
||||
// runProgram runs deploy under mocks with cfg supplied via PULUMI_CONFIG (the
|
||||
// same channel the Pulumi CLI uses to pass config to a Go program).
|
||||
func runProgram(t *testing.T, cfg map[string]string) *recordingMocks {
|
||||
t.Helper()
|
||||
blob, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal config: %v", err)
|
||||
}
|
||||
t.Setenv("PULUMI_CONFIG", string(blob))
|
||||
|
||||
m := &recordingMocks{}
|
||||
if err := pulumi.RunErr(deploy, pulumi.WithMocks(testProject, "test", m)); err != nil {
|
||||
t.Fatalf("pulumi.RunErr: %v", err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// strProp returns pm[k] as a string, failing the test if it is absent or not a
|
||||
// string.
|
||||
func strProp(t *testing.T, pm resource.PropertyMap, k string) string {
|
||||
t.Helper()
|
||||
v, ok := pm[resource.PropertyKey(k)]
|
||||
if !ok {
|
||||
t.Fatalf("property %q missing; have %v", k, pm.Mappable())
|
||||
}
|
||||
if !v.IsString() {
|
||||
t.Fatalf("property %q is not a string: %v", k, v)
|
||||
}
|
||||
return v.StringValue()
|
||||
}
|
||||
|
||||
func TestWorkerScriptRegistered(t *testing.T) {
|
||||
m := runProgram(t, fullConfig())
|
||||
in := m.find(t, tokWorkersScript).Inputs
|
||||
|
||||
if got, want := strProp(t, in, "scriptName"), "libremail-bug-report-ingest"; got != want {
|
||||
t.Errorf("scriptName = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "accountId"), "cf-acct-123"; got != want {
|
||||
t.Errorf("accountId = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "mainModule"), "worker.mjs"; got != want {
|
||||
t.Errorf("mainModule = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "compatibilityDate"), "2025-06-01"; got != want {
|
||||
t.Errorf("compatibilityDate = %q, want %q", got, want)
|
||||
}
|
||||
if got := strProp(t, in, "content"); got == "" {
|
||||
t.Error("content is empty; want a non-empty (placeholder) Worker module body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestR2BucketRegistered(t *testing.T) {
|
||||
m := runProgram(t, fullConfig())
|
||||
in := m.find(t, tokR2Bucket).Inputs
|
||||
|
||||
if got, want := strProp(t, in, "name"), "libremail-bug-reports"; got != want {
|
||||
t.Errorf("name = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "accountId"), "cf-acct-123"; got != want {
|
||||
t.Errorf("accountId = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "location"), "enam"; got != want {
|
||||
t.Errorf("location = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSRecordRegistered(t *testing.T) {
|
||||
m := runProgram(t, fullConfig())
|
||||
in := m.find(t, tokDNSRecordSet).Inputs
|
||||
|
||||
if got, want := strProp(t, in, "managedZone"), "libremail-zone"; got != want {
|
||||
t.Errorf("managedZone = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "name"), "bugreport.example.com."; got != want {
|
||||
t.Errorf("name = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "type"), "CNAME"; got != want {
|
||||
t.Errorf("type = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strProp(t, in, "project"), "libremail-proj"; got != want {
|
||||
t.Errorf("project = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
// Default TTL (dnsTtlSeconds unset in fullConfig) should be 300.
|
||||
ttl, ok := in[resource.PropertyKey("ttl")]
|
||||
if !ok || !ttl.IsNumber() {
|
||||
t.Fatalf("ttl missing or not a number: %v", in.Mappable())
|
||||
}
|
||||
if got := int(ttl.NumberValue()); got != defaultDNSTTLSeconds {
|
||||
t.Errorf("ttl = %d, want %d", got, defaultDNSTTLSeconds)
|
||||
}
|
||||
|
||||
// Rrdatas should point at the configured Worker target.
|
||||
rr, ok := in[resource.PropertyKey("rrdatas")]
|
||||
if !ok || !rr.IsArray() {
|
||||
t.Fatalf("rrdatas missing or not an array: %v", in.Mappable())
|
||||
}
|
||||
arr := rr.ArrayValue()
|
||||
if len(arr) != 1 || !arr[0].IsString() {
|
||||
t.Fatalf("rrdatas = %v, want one string element", rr)
|
||||
}
|
||||
if got, want := arr[0].StringValue(), "libremail-bug-report-ingest.acme.workers.dev."; got != want {
|
||||
t.Errorf("rrdatas[0] = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExactlyThreeManagedResources(t *testing.T) {
|
||||
m := runProgram(t, fullConfig())
|
||||
counts := map[string]int{}
|
||||
for _, r := range m.resources {
|
||||
counts[r.TypeToken]++
|
||||
}
|
||||
for _, tok := range []string{tokWorkersScript, tokR2Bucket, tokDNSRecordSet} {
|
||||
if counts[tok] != 1 {
|
||||
t.Errorf("expected exactly 1 %s, got %d", tok, counts[tok])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissingRequiredConfigIsAnError(t *testing.T) {
|
||||
// Omit all required keys: loadConfig must return an error and RunErr must
|
||||
// surface it rather than registering resources.
|
||||
t.Setenv("PULUMI_CONFIG", "{}")
|
||||
m := &recordingMocks{}
|
||||
err := pulumi.RunErr(deploy, pulumi.WithMocks(testProject, "test", m))
|
||||
if err == nil {
|
||||
t.Fatal("expected an error for missing required config, got nil")
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
module github.com/JMR-dev/LibreMail-Bug-Report-Ingest/infra
|
||||
|
||||
go 1.26.2
|
||||
|
||||
require (
|
||||
github.com/pulumi/pulumi-cloudflare/sdk/v6 v6.17.0
|
||||
github.com/pulumi/pulumi-gcp/sdk/v8 v8.41.1
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.250.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/bubbles v1.0.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v1.3.10 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.2 // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/cheggaaa/pb v1.0.29 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/djherbis/times v1.5.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/go-git/gcfg/v2 v2.0.2 // indirect
|
||||
github.com/go-git/go-billy/v6 v6.0.0-alpha.1 // indirect
|
||||
github.com/go-git/go-git/v6 v6.0.0-alpha.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.5 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-version v1.8.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.22.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.20 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/opentracing/basictracer-go v1.1.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pgavlin/fx v0.1.6 // indirect
|
||||
github.com/pgavlin/fx/v2 v2.0.12 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/term v1.1.0 // indirect
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
|
||||
github.com/pulumi/esc v0.24.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.10.2 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/texttheater/golang-levenshtein v1.0.1 // indirect
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/zclconf/go-cty v1.13.2 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/collector/featuregate v1.53.0 // indirect
|
||||
go.opentelemetry.io/collector/pdata v1.53.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.18.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/term v0.43.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/frand v1.4.2 // indirect
|
||||
)
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
|
||||
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY=
|
||||
github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
|
||||
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/djherbis/times v1.5.0 h1:79myA211VwPhFTqUk8xehWrsEO+zcIZj0zT8mXPVARU=
|
||||
github.com/djherbis/times v1.5.0/go.mod h1:5q7FDLvbNg1L/KaBmPcWlVR9NmoKo3+ucqUA3ijQhA0=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
|
||||
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
|
||||
github.com/go-git/go-billy/v6 v6.0.0-alpha.1 h1:xVjAR4oUvrKy7/Xuw/lLlV3gkxR3KO2H8W+MamuVVsQ=
|
||||
github.com/go-git/go-billy/v6 v6.0.0-alpha.1/go.mod h1:eaCUpHbedW7//EwcYmUDfJe2N6sJC9O12AT0OTqJR1E=
|
||||
github.com/go-git/go-git-fixtures/v6 v6.0.0-alpha.1 h1:gmqi2jvsreu0s8JMLylYDFq4sbjHwwlhktMw0DUg3mA=
|
||||
github.com/go-git/go-git-fixtures/v6 v6.0.0-alpha.1/go.mod h1:ECf1MqJlBdYpKggBrOXjo/0EnvRZx6D++I86UYjPgAQ=
|
||||
github.com/go-git/go-git/v6 v6.0.0-alpha.4 h1:aDTc2UGanmaE7FkGLSlBEB9nohMnQ+RKXcfq/D+esDQ=
|
||||
github.com/go-git/go-git/v6 v6.0.0-alpha.4/go.mod h1:4ODa/G7hPWrh4Y+7lmt59Ij3zW38IEfvRoAZxLYYBhc=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
|
||||
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
|
||||
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0=
|
||||
github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
|
||||
github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
|
||||
github.com/pgavlin/fx/v2 v2.0.12 h1:SjjaJ68Dt8Z4zHwOpY/RPijd7lShs6xYupJbF9ra00M=
|
||||
github.com/pgavlin/fx/v2 v2.0.12/go.mod h1:M/nF/ooAOy+NUBooYYXl2REARzJ/giPJxfMs8fINfKc=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
|
||||
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
|
||||
github.com/pulumi/esc v0.24.0 h1:sCtiB0qbyrlU1ZNzJn4dTLYiChl8xeCBFbHWl1YoXJg=
|
||||
github.com/pulumi/esc v0.24.0/go.mod h1:eCOOkcDJS6eooGwdE4/E0+pOsvUWG254+KBmPCFwJpA=
|
||||
github.com/pulumi/pulumi-cloudflare/sdk/v6 v6.17.0 h1:EAgb6T2aQ6GHMTyq2zAU1P7ysBgAS+sF3jW8TYy20QU=
|
||||
github.com/pulumi/pulumi-cloudflare/sdk/v6 v6.17.0/go.mod h1:xj1wh7fz3MnClHlaE1LZuJ70QktVCqechvH6n6WGI8Q=
|
||||
github.com/pulumi/pulumi-gcp/sdk/v8 v8.41.1 h1:w6OnO3d4j5yVf2vpm8OzXFC/xHOEGqt+9FjWCUBCq6U=
|
||||
github.com/pulumi/pulumi-gcp/sdk/v8 v8.41.1/go.mod h1:UyZyv7hz4knpFx6/Sh+SkZe6hT6sJHtDvw9A0TbvEsk=
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.250.0 h1:J9KDqsFS5rknqym7uVNjtcBF3M+9laOvpDAtY4xmaEg=
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.250.0/go.mod h1:lzcc9vPbkWn3TLD4ZJTiz0lixYM3a/9pSNwsBLLmUEE=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U=
|
||||
github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0=
|
||||
github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/collector/featuregate v1.53.0 h1:cgjXdtl7jezWxq6V0eohe/JqjY4PBotZGb5+bTR2OJw=
|
||||
go.opentelemetry.io/collector/featuregate v1.53.0/go.mod h1:PS7zY/zaCb28EqciePVwRHVhc3oKortTFXsi3I6ee4g=
|
||||
go.opentelemetry.io/collector/internal/testutil v0.147.0 h1:DFlRxBRp23/sZnpTITK25yqe0d56yNvK+63IaWc6OsU=
|
||||
go.opentelemetry.io/collector/internal/testutil v0.147.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE=
|
||||
go.opentelemetry.io/collector/pdata v1.53.0 h1:DlYDbRwammEZaxDZHINx5v0n8SEOVNniPbi6FRTlVkA=
|
||||
go.opentelemetry.io/collector/pdata v1.53.0/go.mod h1:LRSYGNjKXaUrZEwZv3Yl+8/zV2HmRGKXW62zB2bysms=
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.18.0 h1:hhPGP3zvvy1xWT9RTy970wlniSxFttBIsAK1gvMguJM=
|
||||
go.opentelemetry.io/contrib/bridges/otelslog v0.18.0/go.mod h1:twJF7inoMza6kxMcF8JOdL3mPmtOZu7GEr34CUNE6Dg=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0=
|
||||
go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4=
|
||||
go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko=
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE=
|
||||
go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI=
|
||||
go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8=
|
||||
go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA=
|
||||
go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk=
|
||||
go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/sync v0.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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
|
||||
lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
@@ -0,0 +1,19 @@
|
||||
// Command infra is the Pulumi (Go) entrypoint for the LibreMail bug-report
|
||||
// ingest infrastructure.
|
||||
//
|
||||
// It provisions the three pieces of edge/DNS infrastructure the pipeline needs:
|
||||
//
|
||||
// - the Cloudflare Worker script that receives bug reports (built in #1),
|
||||
// - the Cloudflare R2 bucket that stores the encrypted reports (ADR 0001),
|
||||
// - the Google Cloud DNS record that points the ingest hostname at the Worker.
|
||||
//
|
||||
// The program is a thin wrapper: all resource wiring lives in deploy, which is
|
||||
// exercised directly by the mock-based unit tests in deploy_test.go (no Pulumi
|
||||
// CLI required). See infra/README.md for the config/secrets a deployer supplies.
|
||||
package main
|
||||
|
||||
import "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
|
||||
func main() {
|
||||
pulumi.Run(deploy)
|
||||
}
|
||||
Reference in New Issue
Block a user