Bug Description
stacker sync does not persist the config_contract section from stacker.yml to the project_app.config_contract database column. The field remains NULL for all project apps after sync.
Steps to Reproduce
-
Create a stacker.yml with a populated config_contract section:
config_contract:
services:
app:
fields:
SECRET_KEY:
mutability: generated
type: alphanumeric
length: 32
display: password
-
Run stacker sync
-
Query the database:
SELECT app_code, config_contract FROM project_app WHERE project_id = <id>;
-
Observe that config_contract IS NULL for all rows
Expected Behavior
Per the STACKER_SYNC_PLAN.md, config_contract should be persisted:
Project app configuration is stored in:
- project_app.environment
- project_app.ports
- project_app.volumes
- project_app.config_files
- project_app.config_contract ← Should be written
The regression tests also specify:
"config contracts not being lost during sync"
Actual Behavior
config_contract is NULL in the project_app table after stacker sync.
Root Cause
In src/project_app/sync.rs, the build_project_app() function constructs a ProjectApp but never sets the config_contract field:
fn build_project_app(...) -> models::ProjectApp {
let mut project_app = models::ProjectApp::new(...);
project_app.environment = ...;
project_app.ports = ...;
project_app.volumes = ...;
project_app.domain = ...;
project_app.restart_policy = ...;
project_app.command = ...;
project_app.entrypoint = ...;
project_app.networks = ...;
// config_contract is NEVER SET ← BUG
project_app
}
Workaround
stacker submit (marketplace submission) DOES persist config_contract as part of the template payload. The relevant code in src/console/commands/cli/submit.rs:
let config_contract = serde_json::to_value(&config.config_contract)?;
if config_contract
.get("services")
.and_then(|services| services.as_object())
.is_some_and(|services| !services.is_empty())
{
body["config_contract"] = config_contract;
}
Impact
- Stack Builder UI cannot display config contract fields for synced projects
- Developers must use
stacker submit to persist config contracts
- The sync plan's acceptance criteria is not met
Environment
- Stacker CLI version: 0.3.3
- Command:
stacker sync --json
Additional Context
The STACKER_SYNC_PLAN.md explicitly lists config_contract as a field that should be synchronized:
Store in PostgreSQL
Store non-secret, declarative configuration:
- app image;
- app name and code;
- non-secret environment values;
- ports;
- volumes;
- domains;
- restart policy;
- config contract; ← Listed as required
The plan also includes this regression test:
"config contracts not being lost during sync"
Bug Description
stacker syncdoes not persist theconfig_contractsection fromstacker.ymlto theproject_app.config_contractdatabase column. The field remains NULL for all project apps after sync.Steps to Reproduce
Create a
stacker.ymlwith a populatedconfig_contractsection:Run
stacker syncQuery the database:
Observe that
config_contractIS NULL for all rowsExpected Behavior
Per the STACKER_SYNC_PLAN.md,
config_contractshould be persisted:The regression tests also specify:
Actual Behavior
config_contractis NULL in theproject_apptable afterstacker sync.Root Cause
In
src/project_app/sync.rs, thebuild_project_app()function constructs aProjectAppbut never sets theconfig_contractfield:Workaround
stacker submit(marketplace submission) DOES persistconfig_contractas part of the template payload. The relevant code insrc/console/commands/cli/submit.rs:Impact
stacker submitto persist config contractsEnvironment
stacker sync --jsonAdditional Context
The STACKER_SYNC_PLAN.md explicitly lists
config_contractas a field that should be synchronized:The plan also includes this regression test: