serverless

package
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2026 License: MIT Imports: 69 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DNSProviderRules = map[string]ProviderRules{
	"namecheap": {
		Name:       "Namecheap",
		Icon:       "🧢",
		RootFormat: "@",
		WwwFormat:  "www",
		SSLFormat: func(record dns.ValidationRecord) string {
			if strings.Contains(record.Name, ".www") {
				return record.Name
			}
			return strings.Split(record.Name, ".")[0]
		},
		Warning: "⚠️ CRITICAL: NEVER include your domain name in the Host field! Use only the hash or '@'.",
		ProTip:  "For www SSL records, the Host must include '.www' (e.g., '_5ab8c33b39a.www')",
	},
	"cloudflare": {
		Name:       "Cloudflare",
		Icon:       "☁️",
		RootFormat: "@",
		WwwFormat:  "www",
		SSLFormat: func(record dns.ValidationRecord) string {
			return strings.TrimSuffix(record.Name, ".")
		},
		Warning:      "⚠️ IMPORTANT: Set proxy status to DNS only (gray cloud) for SSL validation records!",
		ProTip:       "After SSL is issued, you can enable the orange cloud (proxied) for better performance.",
		ProxyWarning: "🔴 SSL validation WILL FAIL if the cloud is orange! Keep it gray until certificate is issued.",
	},
	"godaddy": {
		Name:       "GoDaddy",
		Icon:       "🇬",
		RootFormat: "@",
		WwwFormat:  "www",
		SSLFormat: func(record dns.ValidationRecord) string {
			return strings.TrimSuffix(record.Name, ".")
		},
		Warning: "⚠️ Do not include trailing dots in the 'Points to' field.",
		ProTip:  "Use '@' for root domain, leave TTL as 1 hour.",
	},
	"route53": {
		Name:       "Route 53",
		Icon:       "📡",
		RootFormat: "@",
		WwwFormat:  "www",
		SSLFormat: func(record dns.ValidationRecord) string {
			return record.Name
		},
		Warning: "✅ Use Alias records (A type) for better performance!",
		ProTip:  "Route 53 handles validation automatically if domain is hosted here.",
	},
	"other": {
		Name:       "Other Provider",
		Icon:       "🌐",
		RootFormat: "@",
		WwwFormat:  "www",
		SSLFormat: func(record dns.ValidationRecord) string {
			return strings.TrimSuffix(record.Name, ".")
		},
		Warning: "⚠️ Check your provider's documentation for exact field names.",
		ProTip:  "Common field names: Host, Name, Alias, Points to.",
	},
}

DNSProviderRules maps provider names to their specific instructions

Functions

func BuildWorkerBundle

func BuildWorkerBundle(
	ctx context.Context,
	standaloneDir string,
	meta *nextcore.NextCorePayload,
	cfg *config.NextDeployConfig,
	log *shared.Logger,
) (string, error)

BuildWorkerBundle adapts a Next.js standalone build into a single Cloudflare Worker module bundle.

Pipeline:

  1. Run nextcompile.Compile against the standalone tree + nextcore payload. This produces <standaloneDir>/.nextdeploy-cf/_nextdeploy/{manifest.json, dispatch.mjs, action_manifest.json, worker_entry.mjs}, extracts the embedded JS runtime, and (when RSC is used) vendors react-server-dom-webpack.
  2. Log the capability summary so operators see what the bundle supports before it deploys.
  3. Invoke `npx esbuild` to bundle worker_entry.mjs + all compiled Next modules it transitively imports into a single ESM Worker bundle.
  4. Return the final bundle path.

Requires Node + npx on PATH for step 3. Every other step is pure Go.

`meta` may be nil for the static-export path (DeployCompute skips early in that case), but every Worker-deploy call path supplies it.

func Deploy

func Deploy(ctx context.Context, cfg *config.NextDeployConfig, meta *nextcore.NextCorePayload, verbose bool) error

Deploy orchestrates the full serverless deployment pipeline:

  1. Discovers the build artifact (app.tar.gz)
  2. Fetches local secrets via SecretManager and pushes them to the cloud secret store
  3. Uploads static assets to CDN/Storage
  4. Deploys the compute layer (Lambda, Workers, etc.)
  5. Invalidates the CDN cache

func GenerateQuickReference

func GenerateQuickReference(resMap ServerlessResourceMap) string

GenerateQuickReference creates a markdown quick reference

func GenerateResourceView

func GenerateResourceView(appCfg *config.AppConfig, resMap ServerlessResourceMap) (string, error)

GenerateResourceView creates a premium HTML report of the provisioned resources

func LoadLocalSecrets

func LoadLocalSecrets(cfg *config.NextDeployConfig) (map[string]string, error)

LoadLocalSecrets is the exported alias for loadLocalSecrets, used by CLI commands outside this package (e.g. `nextdeploy secrets prune`) that need to compute the canonical local secret set.

func Rollback

func Rollback(ctx context.Context, cfg *config.NextDeployConfig, opts RollbackOptions) error

Rollback orchestrates the serverless rollback process. Opts forwards --steps / --to <commit> from the CLI down to the provider implementation.

Types

type AWSProvider

type AWSProvider struct {
	// contains filtered or unexported fields
}

func NewAWSProvider

func NewAWSProvider(verbose bool) *AWSProvider

func (*AWSProvider) CheckServiceQuotas

func (p *AWSProvider) CheckServiceQuotas(ctx context.Context) error

func (*AWSProvider) DeployCompute

func (*AWSProvider) DeployStatic

func (*AWSProvider) Destroy

func (p *AWSProvider) Destroy(ctx context.Context, appCfg *cfgTypes.NextDeployConfig) error

func (*AWSProvider) GetResourceMap

func (p *AWSProvider) GetResourceMap(ctx context.Context, appCfg *cfgTypes.NextDeployConfig) (ServerlessResourceMap, error)

func (*AWSProvider) GetSecrets

func (p *AWSProvider) GetSecrets(ctx context.Context, appName string) (map[string]string, error)

func (*AWSProvider) Initialize

func (p *AWSProvider) Initialize(ctx context.Context, appCfg *cfgTypes.NextDeployConfig) error

func (*AWSProvider) InvalidateCache

func (p *AWSProvider) InvalidateCache(ctx context.Context, appCfg *cfgTypes.NextDeployConfig) error

func (*AWSProvider) Rollback

func (p *AWSProvider) Rollback(ctx context.Context, appCfg *cfgTypes.NextDeployConfig, opts RollbackOptions) error

Rollback reverts the Lambda function to a previous deployed zip using the S3 deployment history. Target selection:

  • opts.ToCommit: prefix-match against history GitCommit (errors if not found within retention).
  • opts.Steps (default 1): walk N entries back from the current (last) one.

This is instant — no HTTP download required.

func (*AWSProvider) SetSecret

func (p *AWSProvider) SetSecret(ctx context.Context, appName string, key, value string) error

SetSecret performs an optimistic read-modify-write that survives concurrent writers. See mutateSecrets for the concurrency model.

func (*AWSProvider) UnsetSecret

func (p *AWSProvider) UnsetSecret(ctx context.Context, appName string, key string) error

UnsetSecret performs an optimistic read-modify-write that survives concurrent writers. Returns nil if the key was already absent.

func (*AWSProvider) UpdateSecrets

func (p *AWSProvider) UpdateSecrets(ctx context.Context, appName string, secrets map[string]string) error

UpdateSecrets is the public write entry point. It diffs against the remote blob first to avoid polluting version history and save API calls, then delegates to writeSecretBlob. Callers that already hold the pre-image (e.g. mutateSecrets) should call writeSecretBlob directly to skip the diff.

type CloudflareProvider

type CloudflareProvider struct {
	// contains filtered or unexported fields
}

CloudflareProvider implements Provider for Cloudflare Workers + R2.

IMPORTANT — Next.js compatibility status:

Cloudflare Workers do not run vanilla Node.js, so a Next.js standalone build cannot be uploaded as-is. Production deployments require the build to be adapted into a Worker-compatible bundle (see the cloudflare adapter step in the packager). Until that lands, DeployCompute will log a loud warning when given a non-static-export build.

SDK usage:

  • Management plane (workers, secrets, routes, R2 buckets, zone, cache): github.com/cloudflare/cloudflare-go/v6
  • R2 object plane (PUT/GET/DELETE objects): the SDK does not cover this; we use the AWS S3 SDK pointed at R2's S3-compatible endpoint (https://<account>.r2.cloudflarestorage.com).

Credentials (resolved in this order — first non-empty wins):

  1. Environment variables (CI-friendly, ephemeral)
  2. Encrypted credstore at ~/.nextdeploy/credstore (per-machine, mode 0600)
  3. Plaintext nextdeploy.yml (LEGACY — emits a loud WARN; prefer 1 or 2)

Field map:

  • CF API token: CLOUDFLARE_API_TOKEN | credstore[cloudflare].api_token | cloudprovider.access_key
  • CF account ID: CLOUDFLARE_ACCOUNT_ID | credstore[cloudflare].account_id | cloudprovider.account_id
  • R2 access key: R2_ACCESS_KEY_ID | credstore[cloudflare].r2_access_key_id | (no yaml fallback)
  • R2 secret key: R2_SECRET_ACCESS_KEY | credstore[cloudflare].r2_secret_key | (no yaml fallback)

Every resolved value is registered with the sensitive scrubber so it never leaks into log lines or error messages.

func NewCloudflareProvider

func NewCloudflareProvider() *CloudflareProvider

func (*CloudflareProvider) DeployCompute

DeployCompute adapts the Next.js standalone build into a Worker bundle (using esbuild + the embedded shim) and uploads it via the SDK.

For static-export sites, no compute deploy is needed — DeployStatic + a catch-all R2 worker is sufficient. We skip in that case.

func (*CloudflareProvider) DeployStatic

DeployStatic uploads the package's static assets to an R2 bucket via the S3-compatible endpoint. R2 management (bucket creation) goes through the official SDK.

R2 object PUTs use S3-protocol HMAC auth, not Bearer; cloudflare-go doesn't expose object operations. Credential resolution order, designed so the zero-config path is the default:

  1. Explicit long-lived pair (R2_ACCESS_KEY_ID + R2_SECRET_ACCESS_KEY in env / credstore). Used as-is. Preserves existing CI configs.
  2. **Auto-derived from the CF API token.** Per CF's R2 docs, the access key id is the API token's `id` (captured in verifyToken via /user/tokens/verify) and the secret access key is SHA-256 of the token's `value`. No dashboard click, no extra scope beyond "Workers R2 Storage: Edit", no minting endpoint. This is the intended one-token-end-to-end path.
  3. Parent-key fallback (R2_PARENT_ACCESS_KEY_ID set): mint a 1-hour scoped child via /accounts/:id/r2/temp-access-credentials. Useful when the API token deliberately doesn't have R2 scope but a dedicated R2 parent key does.
  4. None of the above — fail with dashboard URL.

func (*CloudflareProvider) Destroy

Destroy removes the Worker and the R2 bucket. Bucket delete will fail if the bucket still has objects; we don't sweep them yet.

func (*CloudflareProvider) GetResourceMap

func (*CloudflareProvider) GetSecrets

func (p *CloudflareProvider) GetSecrets(ctx context.Context, appName string) (map[string]string, error)

GetSecrets lists secret names. The CF API never returns secret values.

func (*CloudflareProvider) Initialize

Initialize wires up the Cloudflare SDK client and verifies the API token.

The deploy is designed around a single-token UX: the user sets CLOUDFLARE_API_TOKEN and everything else (account ID, optionally R2 credentials) is derived. If they want explicit overrides, env or credstore wins.

func (*CloudflareProvider) InvalidateCache

func (p *CloudflareProvider) InvalidateCache(ctx context.Context, cfg *config.NextDeployConfig) error

InvalidateCache purges the Cloudflare zone cache for the configured domain.

func (*CloudflareProvider) Plan

Plan computes a dry-run diff for every IaC resource declared under cfg.Serverless.Cloudflare.Resources. Read-only: makes GET / List calls against the CF API but no mutations. Caller must have already called Initialize() so p.cf is wired.

Scope: hyperdrive, queues, vectorize, ai_gateway, dns. Custom domains and routes are tied to worker upload (not standalone resources) and so are not planned here — they're computed during DeployCompute.

func (*CloudflareProvider) ProvisionResources

func (p *CloudflareProvider) ProvisionResources(ctx context.Context, cfg *config.NextDeployConfig) error

ProvisionResources walks cfg.Serverless.Cloudflare.Resources and calls the matching ensure* helper for each declared resource. Stops on first error. Results are stashed on p.provisioned for binding ref resolution at upload time. Safe to call before DeployCompute.

func (*CloudflareProvider) Rollback

Rollback reverts the Worker to a previous deployment version. Cloudflare's deployment API does not surface git commit metadata, so --to <commit> is unsupported and falls back to step-based rollback.

func (*CloudflareProvider) SetSecret

func (p *CloudflareProvider) SetSecret(ctx context.Context, appName, key, value string) error

func (*CloudflareProvider) UnsetSecret

func (p *CloudflareProvider) UnsetSecret(ctx context.Context, appName, key string) error

func (*CloudflareProvider) UpdateSecrets

func (p *CloudflareProvider) UpdateSecrets(ctx context.Context, appName string, secrets map[string]string) error

UpdateSecrets stashes a batch of secrets onto the provider so DeployCompute can fold them into the next Workers.Scripts.Update call as secret_text bindings. This bypasses CF's per-secret PUT endpoint (rate-limited at ~13 req/s, error 10013) and lands every secret atomically in a single upload.

Trade-off: secret rotation requires a Worker re-upload. That re-upload is cheap because the bundle is content-hashed and reused — only the metadata changes — but it does mean `nextdeploy secrets set FOO=bar` outside of a full deploy uses the per-secret path (SetSecret/UnsetSecret) instead of going through here.

Calling UpdateSecrets with an empty map clears the staged set.

type DeploymentEntry

type DeploymentEntry struct {
	S3Key       string `json:"s3_key"`
	GitCommit   string `json:"git_commit,omitempty"`
	GitDirty    bool   `json:"git_dirty,omitempty"`
	GeneratedAt string `json:"generated_at,omitempty"`
}

DeploymentEntry records a single Lambda deployment with the git commit it was built from, so rollbacks can be addressed by commit hash rather than opaque S3 keys.

type ImageOptConfig

type ImageOptConfig struct {
	AllowedDomains []string `json:"allowed_domains"`
	DeviceSizes    []int    `json:"device_sizes"`
	ImageSizes     []int    `json:"image_sizes"`
	Formats        []string `json:"formats"`
}

ImageOptConfig is the JSON contract consumed by the imgopt Lambda binary. AllowedDomains is the flattened list of every Next.js image source — both the legacy `images.domains[]` and the hostnames extracted from `images.remotePatterns[]`. The nextcore feature detector merges both into HasExternalImages, so the imgopt lambda only needs the flat list.

type PlanAction

type PlanAction string

PlanAction is the verdict the diff engine emits for each declared resource. "immutable-drift" is its own action (vs. error) so the renderer can surface every problem in one pass instead of bailing on the first immutable mismatch.

const (
	PlanCreate         PlanAction = "create"
	PlanUpdate         PlanAction = "update"
	PlanNoOp           PlanAction = "no-op"
	PlanImmutableDrift PlanAction = "immutable-drift"
)

type PlanItem

type PlanItem struct {
	Kind   string
	Name   string
	Action PlanAction
	Detail string
}

PlanItem is one row in the plan output. Detail is human-readable; for NoOp it is empty, for Update it describes the field-level diff, for Drift it explains the immutable mismatch.

type PlanResult

type PlanResult struct {
	Items []PlanItem
}

PlanResult is the full diff bundle. HasDrift() flags whether any item is ImmutableDrift so the cmd can exit non-zero (CI-friendly).

func (*PlanResult) HasDrift

func (r *PlanResult) HasDrift() bool

type ProbeOutcome

type ProbeOutcome struct {
	URL        string
	StatusCode int
	Duration   time.Duration
	Attempts   int
	Err        string
}

type Provider

type Provider interface {
	// Initialize validates credentials and prepares the environment.
	Initialize(ctx context.Context, cfg *config.NextDeployConfig) error

	// DeployStatic uploads static assets (public/, .next/static/) to a CDN/Storage bucket.
	DeployStatic(ctx context.Context, pkg *packaging.PackageResult, cfg *config.NextDeployConfig, meta *nextcore.NextCorePayload) error

	// GetSecrets retrieves all secrets for the application.
	GetSecrets(ctx context.Context, appName string) (map[string]string, error)

	// SetSecret sets a single secret for the application.
	SetSecret(ctx context.Context, appName string, key, value string) error

	// UnsetSecret removes a single secret from the application.
	UnsetSecret(ctx context.Context, appName string, key string) error

	// UpdateSecrets securely injects/syncs a batch of secrets.
	UpdateSecrets(ctx context.Context, appName string, secrets map[string]string) error

	// DeployCompute packages the standalone build and updates the compute layer
	// (e.g., AWS Lambda + Web Adapter, Cloudflare Workers).
	DeployCompute(ctx context.Context, pkg *packaging.PackageResult, cfg *config.NextDeployConfig, meta *nextcore.NextCorePayload) error

	// InvalidateCache clears the CDN cache to ensure fresh assets are served.
	InvalidateCache(ctx context.Context, cfg *config.NextDeployConfig) error

	// Rollback reverts the compute layer to a previous version and
	// invalidates the CDN cache so the old version is served immediately.
	// Opts.Steps (default 1) walks N deployments back; Opts.ToCommit pins
	// rollback to a specific git commit (prefix match supported).
	Rollback(ctx context.Context, cfg *config.NextDeployConfig, opts RollbackOptions) error

	// Destroy removes all application resources from the cloud provider.
	Destroy(ctx context.Context, cfg *config.NextDeployConfig) error

	// GetResourceMap returns a summary of all provisioned cloud resources.
	GetResourceMap(ctx context.Context, cfg *config.NextDeployConfig) (ServerlessResourceMap, error)
}

Provider defines the interface for deploying to various serverless platforms (e.g., AWS, Cloudflare, GCP, Azure).

func New

func New(providerName string, verbose bool) (Provider, error)

New returns a new serverless provider based on the provider name.

type ProviderRules

type ProviderRules struct {
	Name         string
	Icon         string
	RootFormat   string
	WwwFormat    string
	SSLFormat    func(record dns.ValidationRecord) string
	Warning      string
	ProTip       string
	ProxyWarning string
}

ProviderRules holds DNS provider-specific display instructions

type RollbackOptions

type RollbackOptions struct {
	// Steps is the number of deployments to walk back from the current
	// active one (1 = previous deployment). Defaults to 1 when zero.
	Steps int
	// ToCommit is a git commit hash (full or short prefix) to roll back to.
	// Resolved against the deployment history; errors if not found within
	// the retention window.
	ToCommit string
}

RollbackOptions controls how a rollback selects its target deployment. Steps and ToCommit are mutually exclusive; ToCommit wins if both are set.

type ServerlessResourceMap

type ServerlessResourceMap struct {
	AppName           string
	Environment       string
	Region            string
	LambdaARN         string
	FunctionURL       string
	S3BucketName      string
	CloudFrontID      string
	CloudFrontDomain  string
	CustomDomain      string
	CertificateARN    string
	CertificateStatus string // "PENDING_VALIDATION", "ISSUED", "FAILED"
	ValidationRecords []dns.ValidationRecord
	DeploymentTime    time.Time
	DNSProvider       string // "namecheap", "cloudflare", "godaddy", "route53", "other"
}

ServerlessResourceMap holds the metadata for the visual report

type SmokeOpts

type SmokeOpts struct {
	// MaxAttempts per URL. DNS + CF cache propagation can take a few
	// seconds; 3 attempts spaced by Delay covers cold paths.
	MaxAttempts int
	// Delay between attempts for a single URL.
	Delay time.Duration
	// Timeout per individual HTTP request.
	Timeout time.Duration
	// FailOnError makes the smoke check fail the deploy on any non-2xx.
	// Default false (warn-only) — deploys sometimes race with CF caching.
	FailOnError bool
}

SmokeOpts tunes the post-deploy smoke check. Defaults are correct for typical deploys; tighten via the caller when fronting a CI workflow.

type SmokeResult

type SmokeResult struct {
	Probed []ProbeOutcome
	Passed int
	Failed int
}

SmokeResult is the summary a caller logs or surfaces in CI output.

func SmokeVerify

func SmokeVerify(
	ctx context.Context,
	log *shared.Logger,
	cfg *config.NextDeployConfig,
	meta *nextcore.NextCorePayload,
	opts SmokeOpts,
) (*SmokeResult, error)

SmokeVerify probes a set of URLs derived from the app config + route manifest after a deploy completes. It's load-bearing for "did the deploy actually work" — the rest of the pipeline only proves the upload succeeded, not that the Worker responds.

Non-fatal by default. Operators opting into FailOnError make the deploy gate on this check.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL