Documentation
¶
Overview ¶
Package cloudflare implements the Cloudflare deploy plugin (Cloudflare Pages direct-upload in PR1; R2 and Worker arrive in later PRs).
See docs/adr/0002-cloudflare-deploy-plugin.md for protocol details and docs/adr/0003-unified-plugin-system.md for the plugin host abstraction.
Index ¶
- Constants
- func Backoff(attempt int) time.Duration
- func Batch(assets []Asset) [][]Asset
- func Hash(content []byte, ext string) string
- func MD5Hex(content []byte) string
- type Asset
- type Client
- func (c *Client) APITokenAuth() string
- func (c *Client) GetJSON(ctx context.Context, path, auth string, out any) error
- func (c *Client) InvalidateJWT(project string)
- func (c *Client) JWTViaProject(ctx context.Context, project string) (string, error)
- func (c *Client) PostForm(ctx context.Context, path, auth string, fields map[string]formField, out any) error
- func (c *Client) PostJSON(ctx context.Context, path, auth string, body, out any) error
- func (c *Client) PutForm(ctx context.Context, path, auth string, fields map[string]formField, out any) error
- func (c *Client) UploadToken(ctx context.Context, project string) (string, error)
- func (c *Client) WithBaseURL(url string) *Client
- func (c *Client) WithHTTPClient(h HTTPClient) *Client
- type CommitMeta
- type Config
- type DeployPagesOptions
- type DeployWorkerOptions
- type DeploymentResult
- type HTTPClient
- type Limiter
- type ManifestError
- type PagesConfig
- type PagesDeployer
- type Plugin
- type R2Config
- type R2FileFailure
- type R2Object
- type R2SyncOptions
- type R2SyncResult
- type R2Syncer
- type RetryDecision
- type SyncMapping
- type WorkerBinding
- type WorkerConfig
- type WorkerDeployResult
- type WorkerDeployer
- type WorkerRoute
Constants ¶
const ( MaxFileCount = 20000 // total files per deployment MaxFileSize = 25 * 1024 * 1024 // 25 MiB per file MaxFilesPerBatch = 2000 // files per upload bucket (POST request) MaxBatchSize = 40 * 1024 * 1024 // bytes per upload bucket (POST request) )
Hard limits enforced by Cloudflare Pages (per wrangler constants.ts). Manifest build rejects inputs exceeding these so we fail fast in the manifest stage rather than mid-upload.
"bucket" here = one POST /pages/assets/upload request body, NOT per- deployment total. A 20,000-file deployment is split into many buckets of at most MaxFilesPerBatch files or MaxBatchSize bytes (whichever hits first) per ADR §14.4.
const DefaultBaseURL = "https://api.cloudflare.com/client/v4"
DefaultBaseURL is the Cloudflare API root.
const DefaultWorkerCompatibilityDate = "2024-01-01"
DefaultWorkerCompatibilityDate is used when WorkerConfig.CompatibilityDate is empty. CF requires this field; we default to the same date wrangler uses.
const HTTPDegradedParallel = 1
HTTPDegradedParallel is the cap after a gateway 5xx triggers auto-degrade per ADR 0002 §9. Degrade is one-way (this constant never grows back during a single deploy invocation).
const HTTPMaxParallel = 3
HTTPMaxParallel is the hard cap on concurrent in-flight HTTP POST requests to Cloudflare (Pages assets/upload + R2 PutObject). Per ADR 0002 §14.3 this is not user-tunable — exceeding 3 typically trips CF gateway 5xx.
const MaxRetries = 3
MaxRetries is the number of retry attempts per request. The total number of attempts is MaxRetries+1 (initial + retries). Per ADR 0002 §9.
const R2EndpointHostPattern = "%s.r2.cloudflarestorage.com"
R2EndpointHostPattern is the hostname pattern for Cloudflare R2's S3- compatible API. minio.New takes a bare hostname (no scheme); the scheme is controlled by Options.Secure (set to true for R2).
const WorkerMIMEType = "application/javascript+module"
WorkerMIMEType is the Content-Type CF Workers API expects for ES module scripts. Not the standard application/javascript — CF distinguishes service-worker (legacy) from module (current) via this MIME type.
Variables ¶
This section is empty.
Functions ¶
func Backoff ¶
Backoff returns the wait duration before the (attempt+1)-th retry. attempt is 0-indexed: Backoff(0) is the wait before the 1st retry.
Returns zero when attempt exceeds the schedule length, indicating the caller should not retry further. Adds up to 20% jitter to avoid thundering-herd on simultaneous retries against the same CF edge.
Tests that need deterministic timing should call backoffSchedule[attempt] directly.
func Batch ¶
Batch splits assets into chunks suitable for POST /pages/assets/upload. Each chunk satisfies BOTH constraints:
- len(chunk) <= MaxFilesPerBatch (2000)
- sum(chunk[i].Size) <= MaxBatchSize (40 MiB)
Whichever constraint hits first triggers a new bucket. Per ADR §14.4 / wrangler constants.ts: "bucket" = one POST request body, not per- deployment total.
Returns nil if input is empty.
func Hash ¶
Hash computes a Cloudflare Pages asset hash.
Per wrangler source (cloudflare/workers-sdk packages/deploy-helpers/src/ deploy/helpers/hash.ts):
hash = blake3(base64(content) + ext).hex()[:32]
where:
- content is the raw file bytes
- base64 is standard base64 encoding (with padding)
- ext is the file extension WITHOUT leading dot ("html" for "index.html", "" for "Makefile" or any extensionless file)
Output is the first 32 hex characters of the blake3 hex digest.
Correctness is verified against python blake3 reference vectors in hash_test.go. A single byte error here invalidates the entire dedup mechanism.
Types ¶
type Asset ¶
type Asset struct {
// Path is the deployment-relative path with leading slash, e.g. "/index.html".
// Cloudflare requires leading slash on manifest keys; BuildManifest enforces this.
Path string
// Hash is the 32-hex blake3 hash (see hash.go).
Hash string
// Size is the file size in bytes.
Size int64
// ContentType is the MIME type guessed from extension (e.g. "text/html; charset=utf-8").
// Empty if no mapping found; Cloudflare accepts empty content-type.
ContentType string
// Content is the raw file bytes, loaded at manifest build time. For large
// deployments this can be memory-heavy; if memory becomes a concern, switch
// to lazy loading in pages.go upload step.
Content []byte
}
Asset describes one file in a Pages deployment manifest.
func BuildManifest ¶
BuildManifest walks publishDir and returns Asset entries ready for upload.
Path conventions:
- publishDir itself becomes the deployment root; e.g. publishDir/index.html becomes Asset.Path "/index.html".
- Subdirectories preserved: publishDir/blog/2024/post.html -> "/blog/2024/post.html".
- All paths use forward slashes regardless of host OS (Cloudflare expects /).
Hard limits enforced (see constants). Returned error unwraps to *ManifestError for the first violation encountered.
Symbolic links are skipped (not followed) to avoid cycles and unexpected out-of-tree content. Hidden files (leading dot) are included — Pages typically wants .well-known/, robots.txt, etc. Callers can post-filter if needed.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps Cloudflare API access. It handles:
- Bearer auth (apiToken at account scope, project JWT at assets scope)
- JSON envelope unwrapping ({result, success, errors, messages})
- Retry with exponential backoff (delegated to retry.go)
Client is safe for concurrent use.
func NewClient ¶
func NewClient(accountID, apiToken string, logger *observability.Logger) *Client
NewClient returns a Client configured for the Cloudflare API.
func (*Client) APITokenAuth ¶
APITokenAuth returns the Authorization header value for apiToken requests. Exposed so callers (pages.go) can pass it explicitly to GetJSON/PostJSON.
func (*Client) GetJSON ¶
GetJSON sends a GET request and decodes the envelope result into out. auth is the full Authorization header value (use APITokenAuth() or JWTViaProject()).
func (*Client) InvalidateJWT ¶
InvalidateJWT clears the cached JWT for a project. Call after a 401 on an assets/* endpoint to force a refresh on the next UploadToken call.
func (*Client) JWTViaProject ¶
JWTViaProject returns the Authorization header value for JWT-authenticated assets/* requests, fetching a fresh JWT via UploadToken first.
func (*Client) PostForm ¶
func (c *Client) PostForm(ctx context.Context, path, auth string, fields map[string]formField, out any) error
PostForm sends multipart/form-data. fields is name -> {contentType, value}. Used for POST deployment (manifest + branch + commit_*).
func (*Client) PostJSON ¶
PostJSON sends a POST request with JSON body and decodes the envelope result.
func (*Client) PutForm ¶
func (c *Client) PutForm(ctx context.Context, path, auth string, fields map[string]formField, out any) error
PutForm sends multipart/form-data via PUT. Used for Workers script upload (PUT /accounts/{id}/workers/scripts/{name}).
func (*Client) UploadToken ¶
UploadToken returns a JWT for the given project. Cached per-project; auto- refreshed when within jwtRefreshMargin of expiry.
func (*Client) WithBaseURL ¶
WithBaseURL substitutes the API base URL. Used by tests.
func (*Client) WithHTTPClient ¶
func (c *Client) WithHTTPClient(h HTTPClient) *Client
WithHTTPClient substitutes the default http.Client. Used by tests to inject httptest mock clients.
type CommitMeta ¶
CommitMeta attaches git metadata to a deployment. Empty values are allowed; Cloudflare accepts deployments without commit metadata.
func InferCommitMetadata ¶
func InferCommitMetadata(flagSHA, flagMessage string) CommitMeta
InferCommitMetadata resolves commit metadata via three-layer fallback per ADR 0002 §14.2:
- Explicit flag values (--commit-sha / --commit-message) win if non-empty.
- Otherwise, infer from the current git repo via `git rev-parse HEAD` and `git log -1 --pretty=%s`.
- If git inference fails (not a repo, no commits, git not installed), return CommitMeta with empty values; Cloudflare accepts deployments without commit metadata.
flagSHA / flagMessage are typically passed from CLI flags. The working directory for git inference is the process's current working directory — callers should chdir to the project root before calling (or pass the project root via cwd parameter in future).
type Config ¶
type Config struct {
AccountID string `yaml:"accountId" json:"accountId"`
APIToken string `yaml:"apiToken" json:"apiToken"`
Pages PagesConfig `yaml:"pages" json:"pages"`
R2 R2Config `yaml:"r2" json:"r2"`
Worker WorkerConfig `yaml:"worker" json:"worker"` // singular (legacy)
Workers []WorkerConfig `yaml:"workers" json:"workers"` // plural (preferred for 2+ workers)
}
Config is the typed Cloudflare plugin configuration, parsed from cfg.Plugins["cloudflare"] (the map[string]any from yaml). The map is already ${VAR}-interpolated by the config layer (see internal/config/interpolate.go).
func LoadConfigFromFile ¶
LoadConfigFromFile is a convenience for tests that want to parse a yaml file directly without going through the full huan config pipeline.
func ParseConfig ¶
ParseConfig decodes raw (already-interpolated) yaml map into typed Config and validates required fields. Returns error on missing account_id, token, pages.project, or pages.branch.
func (Config) AllWorkers ¶ added in v0.3.0
func (c Config) AllWorkers() []WorkerConfig
AllWorkers returns the full list of workers to deploy, combining the plural `workers:` list (preferred) with the singular `worker:` block (legacy). Singular entries are appended AFTER plural entries so plural takes precedence on conflicts (same Name).
Used by deployWorker to iterate without caring which yaml form was used. Returns empty slice when neither form is configured.
func (Config) HasR2Configured ¶
HasR2Configured returns true if the R2 block looks intentional (any field set). Used by Plugin.Deploy to decide whether to error or skip when target="r2".
func (Config) HasWorkerConfigured ¶
HasWorkerConfigured returns true if EITHER the singular Worker block OR the plural Workers list has at least one entry with Name/Script set. Callers that need to know which form was used should call AllWorkers.
type DeployPagesOptions ¶
type DeployPagesOptions struct {
Project string
Branch string
Commit *CommitMeta
Assets []Asset
HTTPParallel int // hard-capped at 3 internally per ADR 0002 §14.3
}
DeployPagesOptions captures deploy-time parameters for the Pages protocol.
type DeployWorkerOptions ¶
type DeployWorkerOptions struct {
// SourceDir is the project root for resolving relative Script paths.
SourceDir string
// DryRun skips the actual PUT; returns success if local file reads OK.
DryRun bool
}
DeployWorkerOptions captures deploy-time parameters.
type DeploymentResult ¶
type DeploymentResult struct {
ID string `json:"id"`
URL string `json:"url"`
Aliases []string `json:"aliases"`
Env string `json:"environment"`
Stage string `json:"latest_stage"`
Status string `json:"latest_stage_status"`
}
DeploymentResult models the response from POST .../deployments.
type HTTPClient ¶
HTTPClient is the subset of *http.Client this package uses, extracted as an interface for test substitution.
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter caps concurrent HTTP requests per ADR §14.3 (3 normal, 1 degraded).
Behavior:
- Initially allows up to HTTPMaxParallel concurrent Acquire calls.
- Degrade() switches the cap to HTTPDegradedParallel permanently (atomic store; not reversible). In-flight Acquire holders continue; future Acquires use the smaller semaphore.
- Acquire respects ctx.Done() — goroutine can be cancelled while waiting.
- Release is the caller's responsibility; pair with defer.
Limiter is safe for concurrent use.
func NewLimiter ¶
func NewLimiter() *Limiter
NewLimiter returns a fresh Limiter in non-degraded state.
func (*Limiter) Acquire ¶
Acquire blocks until a slot is available or ctx is cancelled. Callers MUST call Release when done with the slot.
func (*Limiter) Degrade ¶
func (l *Limiter) Degrade()
Degrade permanently lowers the concurrency cap to HTTPDegradedParallel. Subsequent Acquire calls use the smaller semaphore. Idempotent — calling multiple times is a no-op after the first.
Per ADR §9 this fires on any 5xx response (gateway / app error); the conservative over-reaction cost is "deploy gets slower", the under-reaction cost is "continuous 5xx storms waste upload attempts".
func (*Limiter) IsDegraded ¶
IsDegraded returns whether Degrade has been called. Useful for tests.
func (*Limiter) Release ¶
func (l *Limiter) Release()
Release frees one slot. Must be called exactly once per successful Acquire. Releases into whichever semaphore is current (post-Degrade, tokens go back to degradedSem even if originally acquired from normalSem — the slot is simply freed; over-counting in normalSem is benign because future acquires use degradedSem).
type ManifestError ¶
type ManifestError struct {
Path string
Limit string // "MaxFileSize" / "MaxFileCount" / "MaxFilesPerBatch"
Details string
}
ManifestError reports a structured error from manifest building, including the file path and limit name so the user can fix the input.
func (*ManifestError) Error ¶
func (e *ManifestError) Error() string
type PagesConfig ¶
type PagesConfig struct {
// Project is the CF Pages project name (created in dashboard). Required.
Project string `yaml:"project" json:"project"`
// Branch is the default deployment branch (typically "main"). Required.
// Override at deploy time via --branch=preview or similar.
Branch string `yaml:"branch" json:"branch"`
}
PagesConfig captures Cloudflare Pages project settings.
type PagesDeployer ¶
type PagesDeployer struct {
// contains filtered or unexported fields
}
PagesDeployer orchestrates the 5-endpoint Cloudflare Pages direct-upload protocol per ADR 0002 §7.
func NewPagesDeployer ¶
func NewPagesDeployer(client *Client, logger *observability.Logger) *PagesDeployer
NewPagesDeployer returns a deployer using the given client. logger carries the trace_id used to correlate all log lines for this deploy invocation.
func (*PagesDeployer) DeployPages ¶
func (p *PagesDeployer) DeployPages(ctx context.Context, opts DeployPagesOptions) (*deploy.Report, error)
DeployPages runs the 5-endpoint Pages direct-upload protocol and returns a deploy.Report describing the outcome.
The 5 endpoints (see ADR 0002 §7 for protocol details):
- GET /accounts/{id}/pages/projects/{project}/upload-token → JWT
- POST /pages/assets/check-missing {hashes:[...]} → missing hashes
- POST /pages/assets/upload [{key,value,metadata,base64}] (batched ≤2000)
- POST /pages/assets/upsert-hashes {hashes:[...]} → ack
- POST /accounts/{id}/pages/projects/{project}/deployments multipart
Per ADR 0002 §9, individual file failures are collected into Report.Failures rather than aborting the deploy.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the Cloudflare deploy plugin. It implements both plugin.Plugin (via Name) and deploy.Deployer (via Deploy), so it is discoverable both as a base plugin and as a deployer via plugin.Find[*deploy.Deployer].
func New ¶
New constructs a Plugin from parsed Config. The cfg should come from ParseConfig; pass its output directly here.
func (*Plugin) Config ¶
Config returns the parsed configuration. Used by the CLI to display effective config in `huan plugin info`.
func (*Plugin) Deploy ¶
Deploy implements deploy.Deployer. It dispatches to the appropriate sub- target based on opts.Targets.
Supported targets:
- "pages" — Cloudflare Pages direct-upload (PR1)
- "r2" — R2 bucket sync via S3-compatible API (PR2)
- "worker" — Worker modules upload (PR3, not yet implemented)
Mixed targets like ["pages", "r2"] return an error — invoke each separately.
type R2Config ¶
type R2Config struct {
// AccountID is used to construct the S3 endpoint URL
// (<accountID>.r2.cloudflarestorage.com). Required unless Endpoint is set.
AccountID string `yaml:"accountId" json:"accountId"`
// AccessKeyID and SecretAccessKey are S3-style credentials for R2.
// Generate in CF dashboard under R2 > Manage R2 API Tokens.
AccessKeyID string `yaml:"accessKeyId" json:"accessKeyId"`
SecretAccessKey string `yaml:"secretAccessKey" json:"secretAccessKey"`
// Bucket is the R2 bucket name (pre-created in CF dashboard per ADR 0002 §10).
Bucket string `yaml:"bucket" json:"bucket"`
// Endpoint overrides the default R2 URL pattern (for testing).
Endpoint string `yaml:"endpoint" json:"endpoint"`
// Sync is the list of local-to-remote path mappings. Each entry uploads
// files from local `From` directory to remote `To` key prefix.
// Example: {from: "static/images", to: "images"} uploads static/images/a.jpg
// to bucket key "images/a.jpg".
Sync []SyncMapping `yaml:"sync" json:"sync"`
}
R2Config captures Cloudflare R2 (S3-compatible) settings.
type R2FileFailure ¶
type R2FileFailure struct {
LocalPath string
Key string
Stage string // "walk" / "list" / "hash" / "upload" / "prune"
Error string
}
R2FileFailure describes a single file that failed during R2 sync.
type R2SyncOptions ¶
type R2SyncOptions struct {
// Prune deletes remote objects whose keys aren't present in the local
// set. Default false (keep orphans) per ADR 0002 §6.
Prune bool
// DryRun computes the diff but performs no network calls (upload/delete).
DryRun bool
// Concurrency caps CPU-bound work (file walk, MD5 hashing). HTTP upload
// parallelism is governed separately (default 3).
Concurrency int
}
R2SyncOptions configures a single sync invocation.
type R2SyncResult ¶
type R2SyncResult struct {
Attempted int // local files considered
Succeeded int // uploads completed
Skipped int // remote already had matching MD5
Failed int // uploads that exhausted retries
Pruned int // remote objects deleted (--prune)
Failures []R2FileFailure
}
R2SyncResult is the per-invocation outcome. Counts mirror deploy.Report semantics but are scoped to R2 only.
type R2Syncer ¶
type R2Syncer struct {
// contains filtered or unexported fields
}
R2Syncer uploads local files to a Cloudflare R2 bucket via S3-compatible API. See ADR 0002 §6 for the strategy.
func NewR2Syncer ¶
func NewR2Syncer(cfg R2Config, logger *observability.Logger) (*R2Syncer, error)
NewR2Syncer constructs an R2Syncer with a minio-go-backed client. accountID and access keys come from cloudflare.Config.R2; for test substitution use NewR2SyncerWithClient.
func NewR2SyncerWithClient ¶
func NewR2SyncerWithClient(client r2ObjectClient, bucket string, logger *observability.Logger) *R2Syncer
NewR2SyncerWithClient lets tests inject a mock r2ObjectClient.
func (*R2Syncer) Sync ¶
func (s *R2Syncer) Sync(ctx context.Context, mappings []SyncMapping, opts R2SyncOptions) (*R2SyncResult, error)
Sync runs the R2 sync algorithm against the given local-to-remote mappings.
Algorithm:
- Verify bucket exists (fail fast if not — ADR 0002 §10).
- Walk local paths → build {key → localPath} map for each sync.from→to.
- For each unique "to" prefix, list remote objects in one call.
- Compare: skip if remote exists with matching MD5; upload otherwise.
- If Prune: delete remote keys not in local set.
Per ADR 0002 §9 collection-not-interruption: per-file failures are collected into Failures; the sync continues.
type RetryDecision ¶
RetryDecision classifies an HTTP response/error as retryable or fatal.
func ClassifyError ¶
func ClassifyError(resp *http.Response, err error) RetryDecision
ClassifyError inspects the HTTP response (or underlying error) and decides whether the caller should retry.
Retryable conditions (per ADR 0002 §9):
- HTTP 5xx (server errors, includes 500 gateway errors)
- HTTP 429 (rate limit)
- Network errors (DNS, connection refused, timeout) — but NOT context cancellation, which propagates immediately
Fatal conditions:
- HTTP 4xx (other than 429) — auth failures, malformed requests, etc.
- HTTP 2xx — success, no retry needed
- nil response AND nil err — programmer error
- Context cancellation / deadline exceeded
type SyncMapping ¶
type SyncMapping struct {
// From is the local directory (or single file) to upload.
From string `yaml:"from" json:"from"`
// To is the remote key prefix (without trailing slash).
// For directory mappings, files become <To>/<relative-path>.
// For single-file mappings, To becomes the key directly.
To string `yaml:"to" json:"to"`
}
SyncMapping declares one local-to-remote path mapping for R2 sync.
type WorkerBinding ¶
type WorkerBinding struct {
Type string `yaml:"type" json:"type"`
Name string `yaml:"name" json:"name"` // env var name in Worker (e.g. "R2_BUCKET")
Bucket string `yaml:"bucket" json:"bucket,omitempty"`
NamespaceID string `yaml:"namespaceId" json:"namespace_id,omitempty"`
ID string `yaml:"id" json:"id,omitempty"`
Value string `yaml:"value" json:"value,omitempty"`
}
WorkerBinding declares one resource binding for a Worker.
Type values supported by CF Workers modules API:
- "r2_bucket" — R2 bucket binding (requires Bucket)
- "kv_namespace" — KV namespace (requires NamespaceID)
- "vars" — plain-text env var (requires Value)
- "secret_text" — secret env var (requires Value; this is the non-Wrangler-managed variant)
- "d1" — D1 database (requires ID)
huan does NOT validate binding types — it serializes what you declare and lets CF reject unknown types. This keeps the surface minimal as CF adds new binding kinds.
type WorkerConfig ¶
type WorkerConfig struct {
// Name is the Worker script name (must match across deploys; renames are
// not supported by the CF API). Required.
Name string `yaml:"name" json:"name"`
// Script is the local path (relative to huan.yaml dir) of the single-file
// ES module .js source. Required.
Script string `yaml:"script" json:"script"`
// CompatibilityDate defaults to "2024-01-01" if empty.
CompatibilityDate string `yaml:"compatibilityDate" json:"compatibilityDate"`
// Bindings declares resources the Worker can access (R2 buckets, KV
// namespaces, env vars, etc.). huan serializes them into the upload
// metadata JSON. See WorkerBinding for supported types.
Bindings []WorkerBinding `yaml:"bindings" json:"bindings"`
// Routes declares route patterns the Worker handles. Each entry must
// include Pattern and Zone (zone name like "zhurongshuo.com").
Routes []WorkerRoute `yaml:"routes" json:"routes"`
}
WorkerConfig captures Cloudflare Workers settings for the modules API (PUT /accounts/{id}/workers/scripts/{name}).
type WorkerDeployResult ¶
type WorkerDeployResult struct {
// ScriptName (echoes request).
ScriptName string `json:"-"`
// ModifiedOn is the raw timestamp string from CF (typically RFC3339).
ModifiedOn string `json:"modified_on"`
// UsageModel (default "bundled").
UsageModel string `json:"usage_model"`
// Handler is the entrypoint (typically "default" for ES modules).
Handler string `json:"handler"`
}
WorkerDeployResult models the response from PUT .../workers/scripts/{name}.
Audit M3: ModifiedOn is a string (not time.Time) so we always log what CF actually returned, even if their format shifts (sub-microsecond precision, non-RFC3339 variants). Go's default time.Time JSON unmarshal requires strict RFC3339 — a parse failure leaves a zero time which is misleading. Strings preserve the raw value; callers can time.Parse on the string if needed.
type WorkerDeployer ¶
type WorkerDeployer struct {
// contains filtered or unexported fields
}
WorkerDeployer uploads a Worker script via the CF Workers modules API.
func NewWorkerDeployer ¶
func NewWorkerDeployer(client *Client, logger *observability.Logger) *WorkerDeployer
NewWorkerDeployer returns a deployer using the given client.
func (*WorkerDeployer) Deploy ¶
func (d *WorkerDeployer) Deploy(ctx context.Context, cfg WorkerConfig, opts DeployWorkerOptions) (*deploy.Report, error)
Deploy uploads the Worker script + metadata. The metadata JSON includes main_module (script basename), compatibility_date, bindings, and routes.
Algorithm per ADR 0002 §8:
- Read Script file relative to opts.SourceDir.
- Build metadata JSON.
- PUT multipart/form-data to /accounts/{id}/workers/scripts/{name}: - part "metadata": application/json - part "<basename>": application/javascript+module with filename set
type WorkerRoute ¶
type WorkerRoute struct {
Pattern string `yaml:"pattern" json:"pattern"` // e.g. "r2.zhurongshuo.com/*"
Zone string `yaml:"zone" json:"zone,omitempty"` // zone name (e.g. "zhurongshuo.com")
}
WorkerRoute declares one route pattern + zone for the Worker.