Skip to content

Dev#199

Merged
vsilent merged 33 commits into
mainfrom
dev
Jul 25, 2026
Merged

Dev#199
vsilent merged 33 commits into
mainfrom
dev

Conversation

@vsilent

@vsilent vsilent commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

This pull request introduces several significant improvements to the stacker CLI and configuration system, focusing on better handling of Docker Compose networks for agent deployments, more accurate synthesis of the app service in Compose files, and enhanced support for marketplace installs. The changes also improve test coverage and add new migration files for marketplace authorization tracking.

Docker Compose network handling:

  • Added inject_shared_network_all_services to compose_service_sync.rs, ensuring all services in a Compose file join a shared external network (e.g., default_network), which is critical for agent-based deployments. This function is idempotent and covered by a new unit test. [1] [2]
  • Updated build_config_bundle to optionally attach the agent network to all services when requested, with corresponding tests to verify this behavior. [1] [2] [3]

Marketplace install command enhancements:

  • Extended the marketplace install CLI command to accept additional arguments for cloud credential selection, region, size, and provider, improving flexibility for deployments. [1] [2]

Compose/app service synthesis improvements:

  • Added an app_present flag to StackerConfig to distinguish between configs that explicitly declare an app: section and those where it is defaulted. The Compose generator now only materializes the app service when appropriate, preventing phantom services and related deploy errors. [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • The app service's runtime label now uses the project code, ensuring the agent can resolve containers correctly.

Database migrations:

  • Added migrations to create and drop a new marketplace_install_authorization table, with appropriate indexes and constraints for tracking authorizations. [1] [2]

Test and default value updates:

  • Updated test defaults for cloud server size from cpx11 to cx23 for consistency. [1] [2]

These changes collectively improve deployment reliability, configuration accuracy, and user experience for both agent-based and marketplace-driven workflows.

robotizeit and others added 30 commits July 17, 2026 12:07
… fetches via HTTP, no curl needed in container)
… config.origin == MarketplaceGenerated (the file carries the # @stacker-origin: marketplace marker) unless the app section declares an explicit source (image/dockerfile/build). User-authored configs are completely unaffected — their app is always kept.
robotizeit and others added 3 commits July 23, 2026 14:29
install_catalog_application enriches the search summary from
/applications/catalog via merge_catalog_enrichment, but that merge only
overlaid single-app fields (docker_image, default_port(s), default_env,
default_config_files) and dropped the stack's services[] and kind. The
service-less summary then reached synthesize_catalog_compose, which took the
single-app path and failed loud with "missing a dockerhub_image" for real
multi-service stacks (e.g. LAMP) -- even though fetch_app_catalog fetched the
13-member payload successfully (no fallback).

Overlay services[] and kind too. Add a regression test that runs the real
flow (production LAMP fixture -> merge -> synthesize) and asserts a
multi-service compose, plus a full-payload deserialize/synthesize test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 25, 2026 07:10
@vsilent
vsilent merged commit 508eab2 into main Jul 25, 2026
17 of 18 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands Stacker’s marketplace + CLI deployment workflows by adding per-install billing primitives (authorization ledger, capture/void hooks, and a background sweeper), improving catalog/marketplace install robustness (compose synthesis + embedded compose extraction), and enhancing CLI ergonomics (cloud override flags, service catalog inputs/healthchecks, and agent-network injection). It also adds Google Analytics tagging to the website HTML.

Changes:

  • Add per-install billing flow: new DB table + user_service connector methods, install-time authorization persistence, deploy-complete capture hook, and an expiry sweeper.
  • Improve install/deploy reliability for marketplace/catalog templates by synthesizing/locating compose content and avoiding phantom app services for services-only configs.
  • Enhance CLI: marketplace install cloud overrides + idempotency key threading, service catalog secrets-to-.env flow, healthchecks, and shared-network injection for agent deployments.

Reviewed changes

Copilot reviewed 47 out of 48 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
website/src/index.html Adds Google tag (gtag.js) snippet to site source HTML.
website/dist/index.html Adds the same Google tag snippet to built/dist HTML.
tests/marketplace_integration.rs Updates webhook payload tests to use Default expansion.
tests/features/marketplace_per_install_billing.feature.pending Adds pending BDD spec for per-install billing scenarios.
src/startup.rs Spawns per-install billing sweeper during server startup (flag-gated).
src/services/mod.rs Exposes install_authorization_sweeper module.
src/services/marketplace_access.rs Adds per-install access gate via can_charge and a new NoPaymentMethod error variant + tests.
src/services/install_authorization_sweeper.rs Implements background void/reconcile loop for expired authorizations.
src/routes/project/deploy.rs Maps NoPaymentMethod to HTTP 402 and adds embedded compose extraction helper + tests.
src/routes/marketplace/search.rs Enriches marketplace search results with creator_name.
src/routes/marketplace/public.rs Captures per-install authorizations on deploy-complete; injects user_service into handler.
src/routes/marketplace/lamp_catalog_fixture.json Adds production fixture for LAMP catalog payload round-trip tests.
src/routes/marketplace/install.rs Adds idempotency + authorization fields to install API, per-install authorize/capture/void flow, and catalog compose synthesis + enrichment helpers + tests.
src/routes/marketplace/admin.rs Fetches latest template version for approval webhook federation (best effort).
src/helpers/stacker_labels.rs Adds sanitize_service_code helper for stable agent-facing service codes.
src/helpers/json.rs Adds payment_required() helper for HTTP 402 responses.
src/db/mod.rs Registers new marketplace_billing DB module.
src/db/marketplace.rs Adds get_latest_version() helper for template approval/publish webhook federation.
src/db/marketplace_billing.rs Implements per-install authorization persistence and sweeper lookup helpers.
src/console/commands/cli/service.rs Adds service catalog input prompting + .env update helpers and tests.
src/console/commands/cli/secrets.rs Updates test fixture for new StackerConfig.app_present field.
src/console/commands/cli/pipe.rs Adds “direct container” fallback probing logic + tests, uses sanitize_service_code.
src/console/commands/cli/marketplace.rs Adds vendor column, per-install idempotency key threading, cloud override flags, and plan display updates + tests.
src/console/commands/cli/init.rs Updates default Hetzner size and marks init-generated configs as app_present=true.
src/console/commands/cli/deploy.rs Enables agent shared-network injection when building deploy bundles for cloud/server deploys.
src/console/commands/cli/config.rs Extends cloud setup wizard to optionally show/select saved cloud credential names.
src/console/commands/cli/agent.rs Threads new build_config_bundle parameter for agent deploy flow.
src/connectors/user_service/types.rs Adds per-install billing types (AuthorizationHandle, BillingCapability).
src/connectors/user_service/stack.rs Avoids misusing stack_view OS images as container images; resolves stack docker images safely.
src/connectors/user_service/mod.rs Re-exports new billing types.
src/connectors/user_service/mock.rs Adds mock implementations for new connector methods.
src/connectors/user_service/marketplace_webhook.rs Extends webhook payload with federated stack definition/config files/version + tests.
src/connectors/user_service/connector.rs Extends trait with catalog enrichment + per-install billing methods.
src/connectors/user_service/client.rs Implements catalog enrichment + per-install billing HTTP calls and shared error mapping.
src/connectors/user_service/app.rs Adds typed services[] for stack catalog entries.
src/connectors/errors.rs Adds PaymentRequired and Conflict variants and maps them to HTTP status codes.
src/configuration.rs Adds per_install_billing_enabled global kill switch (default false).
src/cli/stacker_client.rs Threads install idempotency key + adds response authorization summary + creator_name field.
src/cli/service_catalog.rs Adds catalog inputs + secret placeholders, plus healthchecks + tests.
src/cli/install_runner.rs Ensures docker_registry key is always sent; updates default server size + tests.
src/cli/generator/compose.rs Avoids synthesizing phantom app service; updates runtime labels to use project code; adds tests.
src/cli/config_parser.rs Tracks whether app: was explicitly present (app_present) and updates defaults/tests.
src/cli/config_bundle.rs Adds optional injection of shared agent network into all compose services + tests.
src/cli/compose_service_sync.rs Adds inject_shared_network_all_services helper + tests.
src/cli/ai_scenarios.rs Updates default cloud size used in tests.
src/bin/stacker.rs Adds marketplace install flags for cloud key/region/size/provider.
migrations/20260718130000_marketplace_install_authorization.up.sql Creates marketplace_install_authorization table + indexes.
migrations/20260718130000_marketplace_install_authorization.down.sql Drops marketplace_install_authorization table.

Comment on lines +331 to +379
// Per-install billing: capture the previously-authorized charge now that
// the deploy has confirmed successful. Lookup is by deployment_hash on the
// authorization row (populated at install time); non-per_install rows
// simply won't match and this becomes a no-op. Non-fatal on capture
// failure — the sweeper reconciles stragglers.
match db::marketplace_billing::find_by_deployment_hash(
pg_pool.get_ref(),
&payload.deployment_hash,
)
.await
{
Ok(Some(auth_row)) if auth_row.status == "authorized" => {
let service_token = std::env::var("STACKER_SERVICE_TOKEN").unwrap_or_default();
match user_service
.capture_install_charge(
&service_token,
&auth_row.authorization_id,
&payload.deployment_hash,
)
.await
{
Ok(_handle) => {
if let Err(err) = db::marketplace_billing::mark_captured(
pg_pool.get_ref(),
&auth_row.authorization_id,
)
.await
{
tracing::warn!(
"mark_captured DB write failed for {}: {}",
auth_row.authorization_id,
err
);
}
}
Err(err) => tracing::warn!(
"capture_install_charge failed for {} (deploy already succeeded, sweeper will retry): {}",
auth_row.authorization_id,
err
),
}
}
Ok(_) => {} // no per_install authorization for this deployment
Err(err) => tracing::warn!(
"find_by_deployment_hash failed for {}: {}",
payload.deployment_hash,
err
),
}
Comment on lines +68 to +73
let service_token = std::env::var("STACKER_SERVICE_TOKEN").unwrap_or_default();
for row in expired {
match user_service
.void_install_charge(&service_token, &row.authorization_id, "expired")
.await
{
Comment on lines +1942 to +1947
t.price = Some(10.005);
assert_eq!(
amount_minor_for(&t).unwrap(),
1001,
"banker rounding to nearest cent"
);
Comment on lines +509 to +513
let reason = if is_stack {
"none of its member apps resolved a container image (each member app needs its own dockerhub_image)"
} else {
"the catalog entry is missing a dockerhub_image"
};
Comment on lines +120 to +142
let is_per_install = template
.billing_cycle
.as_deref()
.map(|c| c.trim().eq_ignore_ascii_case("per_install"))
.unwrap_or(false);

// Per-install templates skip the ownership check entirely — there is no
// permanent purchase for these. Instead we probe the user's payment
// capability so `stacker install` fails fast with a clear 402 if the
// user has no card on file, rather than deep inside the install handler
// after the actual authorize attempt.
if is_per_install {
let capability = user_service
.can_charge(user_token)
.await
.map_err(validation_failed)?;
if !capability.can_charge {
return Err(MarketplaceAccessError::NoPaymentMethod {
reason: capability.reason.unwrap_or_else(|| "unknown".to_string()),
});
}
return Ok(());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants