Skip to content

feat: config-driven builds with per-checkout deployment state - #630

Merged
zachfedor merged 8 commits into
mainfrom
feat/config-driven-network
Oct 17, 2026
Merged

zachfedor merged 8 commits into
mainfrom
feat/config-driven-network

Conversation

@zachfedor

@zachfedor zachfedor commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Restores Scaffold's local-first model: local is for experimenting freely, testnet is staging and a deliberate dry run, and mainnet is copied over when it's ready. build follows scaffold.yml instead of flags, and each checkout of a project keeps its own record of what it deployed, so projects and worktrees that share stellar-cli's global config no longer reuse each other's contracts.

Companion UI PR: stellar-scaffold/ui#282 (same branch, feat/config-driven-network). Both PRs carry the cross-repo label so each one's CI builds against the other.

Changes

Network selection

  • Precedence is --network, then STELLAR_NETWORK, then stellar network use, then local.
  • build now says when the network came from stellar network use, and config show resolves the network the same way build does.
  • "Not deploying" messages name the network.

build with a version 2 scaffold.yml

  • Always deploys and generates clients for networks that allow deploys. --build-clients is accepted but only prints a note.
  • init without a frontend and upgrade only compile.

.scaffold/state.json (new state module)

  • Records, for each network, the contract ID and saved alias of each deployed contract. The format version is independent of scaffold.yml's.
  • Machine-local: .scaffold/ ignores itself with its own .gitignore. Writes are atomic.
  • build decides between deploying, upgrading and reusing from this record, not from stellar-cli's global aliases.
  • If a network name is redefined with a different passphrase, the entries recorded for it are dropped.

Aliases

  • Scaffold saves aliases itself as <directory>-<contract> (e.g. my-app-guess_the_number) and no longer passes --alias to stellar contract deploy. Separate checkouts and worktrees get separate aliases.
  • It only overwrites an alias that's free or already its own. Otherwise it warns and prints the stellar contract alias remove command.
  • After the project directory is renamed, the next deploy removes the old alias.

clean

  • Removes only the recorded aliases that still point at the recorded contracts, then deletes state.json.
  • No longer removes keys, which may be shared with other projects.

Docs

  • Configuration and CLI reference, the tutorial, and the agent skill are updated for all of the above.

Upgrading (release note)

Scaffold now records deployments in .scaffold/state.json and no longer finds them through contract-name aliases like guess_the_number. The first build after upgrading deploys every contract again, on every network you build for. Later builds upgrade them as usual. Old aliases are left in your stellar-cli config; remove them with stellar contract alias remove <name>. New aliases are prefixed with the project directory name (e.g. my-app-guess_the_number).

Known limitations

  • Two checkouts with the same directory name share an alias prefix. The second one gets the "Not saving alias" warning, and its contracts are still tracked correctly in its own state.json.
  • Spanish translations under docs/site/i18n/es still use the old paths and aliases.

Testing

  • cargo fmt, cargo clippy --workspace --all-targets --all-features, and the unit tests (239) pass, including new tests for the state file, alias naming, alias ownership, and clean.
  • Before rebasing onto 0.0.29, the full integration suite passed 269/269 against local quickstart (protocol 29). New test: checkouts_sharing_a_stellar_config_keep_their_own_contracts, which fails on main because the second checkout reports the first checkout's contract as up to date.
  • Manual run against the UI branch with stellar scaffold build:
    • The first build deployed all three contracts and saved ui-* aliases.
    • The second build reported them up to date.
    • contract invoke --id <alias> worked.
    • clean removed the aliases and the state file and kept the keys.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb

zachfedor and others added 8 commits October 16, 2026 13:33
Run as `stellar scaffold`, stellar-cli copies its `network use` default
into STELLAR_NETWORK, so builds labelled it as an explicit choice.
`config show` now resolves the network the same way `build` does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
A version 2 scaffold.yml declares what each network deploys and binds,
so `build` and `watch` always follow it; `--build-clients` is noted
as no longer needed. environments.toml projects still opt in with the
flag. `init` without a frontend and `upgrade` only compile, and
"Not deploying" now names the network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
Drop `--build-clients` from version 2 examples, explain the network
precedence, and stop pairing STELLAR_NETWORK with PUBLIC_* values now
that the frontend reads its network from the generated clients.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
Records the contract ID and alias Scaffold saved for each contract,
per network, with the network's passphrase so a name that now points at
another chain is treated as empty. The directory writes its own
.gitignore. Aliases are prefixed with the workspace directory's name so
separate checkouts, like git worktrees, don't share them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
…ases

stellar-cli's contract aliases are machine-wide, so two projects with a
contract of the same name shared one: the second build adopted, or
upgraded, the first project's contract. `build` now looks up what this
checkout deployed in .scaffold/state.json and records each deploy there.
It still saves a stellar-cli alias for hand-run commands, prefixed with
the directory name, and only when the alias is free or already
Scaffold's, so one made by hand is never overwritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
`clean` removes the aliases recorded in .scaffold/state.json, while they
still point at the recorded contracts, then the file itself. It no
longer deletes identities: keys are machine-wide, so another project may
depend on them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
Describe how build records deployments per checkout, the
`<directory>-<contract>` alias it saves for hand-run commands, and that
`clean` keeps keys. The tutorial's scaffold.yml now matches the
template: testnet points at a deliberate deployment instead of
deploying from the dev loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
Builds now find earlier deployments only through .scaffold/state.json,
so the first build after upgrading deploys every contract again and the
old contract-name aliases are left in the stellar-cli config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
@zachfedor zachfedor added the cross-repo Opt-in to cross-repo testing on matching branch names label Oct 16, 2026

@chadoh chadoh 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.

Goooood shyt.

Only thing I noticed that might be worth changing before merge is the "don't make the json pretty" suggestion below

zachfedor added a commit to stellar-scaffold/ui that referenced this pull request Oct 17, 2026
Companion to stellar-scaffold/cli#630. The templates now rely on
`scaffold.yml` to decide what gets deployed where, use stellar-cli's
global config, and treat testnet as staging.

**Merge after the CLI release that includes #630.** This PR carries the
`cross-repo` label, so its CI builds the CLI from
`feat/config-driven-network`.

## Changes

**`app-lib`**
- Reads the network (passphrase and RPC URL) from the generated clients
rather than from separate env settings.
- Fetches balances over RPC.
- **Breaking:** `MappedBalances` changes shape.

**`scaffold.yml`**
- Testnet is staging: it no longer deploys automatically on every build.
- A commented `testnet-dev` example shows how to opt in to automatic
testnet deploys.

**Templates**
- `start` runs `stellar scaffold watch` without `--build-clients`, since
`scaffold.yml` now decides. CI keeps the flag.
- `.env.example` drops `XDG_CONFIG_HOME`/`STELLAR_CONFIG_HOME`, so keys
and aliases live in stellar-cli's global config. It keeps a commented
`STELLAR_NETWORK`, a slot for secrets, and a note on `PUBLIC_`
variables.
- `.scaffold` (the CLI's machine-local deployment record) is added to
`.gitignore` so Prettier skips it.
- READMEs, CONTRIBUTING and `CLAUDE.md` are updated.

## Known follow-ups

- `STELLAR_NETWORK=testnet npm start` fails until a testnet `type:
contract` entry exists, because testnet no longer deploys.
- `build.yml` still writes `.config/stellar/identity/me.toml`, which
nothing reads now.
- `dapp-ipfs.yml` is out of date.
- The committed `Cargo.lock` doesn't match the contracts: a build bumps
the contract crates from 0.0.1 to 0.0.2. This predates this PR.

## Testing

- Against clients generated by the CLI branch, these pass:
  - `npm run lint` and `npm run format:check`
  - the `app-lib` typecheck
  - the React build
  - the Svelte typecheck and build
- End to end with the CLI branch:
  - The first build deployed and saved `ui-*` aliases.
  - The second build reported up to date.
  - Invoking by alias worked.
  - `clean` removed the aliases and the state file.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com>
@zachfedor
zachfedor merged commit b8e808a into main Oct 17, 2026
15 of 19 checks passed
@zachfedor
zachfedor deleted the feat/config-driven-network branch October 17, 2026 14:33
@aha-release-plz aha-release-plz Bot mentioned this pull request Oct 17, 2026
chadoh added a commit that referenced this pull request Oct 21, 2026
Note: needed because in #630 Zach and I made `--build-clients` the
default

Officially encouraged Stellar Scaffold deploy process:

1. Build Wasm binaries for testnet
2. Publish & deploy them with Stellar Registry CLI
3. Update `testnet` sections of `scaffold.yml` to point at deployed
   instances (using contract IDs or Registry names)

By default, version 2 builds also deploy contracts and generate clients.
That prevents compiling Wasm independently when testnet client
generation
depends on contracts that have not been deployed yet.

Expose the existing --compile-only flag so users can build Wasm before
publishing and deploying it through Stellar Registry CLI, without
triggering Scaffold deployment or client generation.

--compile-only still validates scaffold.yml, including settings for
all declared networks. Contract IDs and Registry names must be
syntactically valid, but their referenced contracts need not exist yet:
this build skips the deployment/client-generation steps that would
attempt to use them.
chadoh pushed a commit that referenced this pull request Oct 21, 2026
## 🤖 New release

* `stellar-scaffold-ext-types`: 0.0.4 -> 0.0.5 (✓ API compatible
changes)
* `stellar-scaffold-cli`: 0.0.30 -> 0.0.31 (⚠ API breaking changes)
* `stellar-scaffold-reporter`: 0.1.3 -> 0.1.4

### ⚠ `stellar-scaffold-cli` breaking changes

```text
--- failure constructible_struct_adds_field: struct exhaustively constructible through public API adds field ---

Description:
A pub struct that could be exhaustively constructed with a literal using only public API has a new pub field, breaking existing exhaustive literals.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.51.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field Command.compile_only in /tmp/.tmp7uanvi/cli/crates/stellar-scaffold-cli/src/commands/build/mod.rs:38

--- failure enum_variant_added: enum variant added on exhaustive enum ---

Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.51.0/src/lints/enum_variant_added.ron

Failed in:
  variant Error:State in /tmp/.tmp7uanvi/cli/crates/stellar-scaffold-cli/src/commands/build/clients.rs:153

--- failure enum_variant_missing: pub enum variant removed or renamed ---

Description:
A publicly-visible enum has at least one variant that is no longer available under its prior name. It may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.51.0/src/lints/enum_variant_missing.ron

Failed in:
  variant Error::NetworkConfig, previously in file /tmp/.tmpdxkV6L/stellar-scaffold-cli/src/commands/clean.rs:26
  variant Error::EnvToml, previously in file /tmp/.tmpdxkV6L/stellar-scaffold-cli/src/commands/clean.rs:29
```

<details><summary><i><b>Changelog</b></i></summary><p>

## `stellar-scaffold-ext-types`

<blockquote>

##
[0.0.5](stellar-scaffold-ext-types-v0.0.4...stellar-scaffold-ext-types-v0.0.5)
- 2026-10-21

### Added

- config-driven builds with per-checkout deployment state
([#630](#630))
</blockquote>

## `stellar-scaffold-cli`

<blockquote>

##
[0.0.31](stellar-scaffold-cli-v0.0.30...stellar-scaffold-cli-v0.0.31)
- 2026-10-21

### Added

- config-driven builds with per-checkout deployment state
([#630](#630))

### Fixed

- *(stellar-scaffold-cli)* expose `build --compile-only` flag
([#637](#637))
</blockquote>

## `stellar-scaffold-reporter`

<blockquote>

##
[0.1.4](stellar-scaffold-reporter-v0.1.3...stellar-scaffold-reporter-v0.1.4)
- 2026-10-21

### Other

- updated the following local packages: stellar-scaffold-ext-types
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: aha-release-plz[bot] <211513279+aha-release-plz[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cross-repo Opt-in to cross-repo testing on matching branch names

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants