Skip to content

feat: config-driven templates with testnet as staging - #282

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

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

Conversation

@zachfedor

Copy link
Copy Markdown
Contributor

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.ai/code/session_01QhmaWLv4yExX8LahP5UtUb

zachfedor and others added 5 commits October 15, 2026 23:12
The frontend's network now comes from the passphrase and RPC URL the CLI
writes into app-lib/clients, so it can't drift from the network the
contracts were built for. An unknown passphrase throws instead of
falling back to local. Balances come from RPC instead of Horizon;
`MappedBalances` is now `{ xlm?: { balance } }` and `horizonUrl` is
no longer exported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
Testnet no longer deploys from `build` or `watch`; contracts point at
deployments made deliberately. A commented `testnet-dev` network shows
how to opt in to redeploying on testnet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
`npm start` runs `stellar scaffold watch` without `--build-clients`,
which a version 2 scaffold.yml no longer needs. .env.example drops the
PUBLIC_STELLAR_* values and the per-project XDG_CONFIG_HOME, keeping an
optional STELLAR_NETWORK and a slot for secrets. CI keeps the flag for
released CLIs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb
`stellar scaffold build` records deployments in .scaffold/state.json. The
directory ignores itself for git, but Prettier only reads the root
.gitignore, so `prettier --check .` flagged the generated file.

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
zachfedor and others added 3 commits October 16, 2026 14:31
The generated clients index also exports a shared `options` object, so
counting every `export const` overcounted by one and failed the check
even when every client was generated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* If the defaults only apply to one network, just put them in that network
* If the comments for the `testnet` network below make it hard to tell
  where the indent level should go, leave a `testnet` example in, even
  if the contract ID is invalid (it still typechecks, but will fail on
  build; there's a comment explaining what to do)
chadoh
chadoh previously approved these changes 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.

A couple small comments & suggestions, but overall it looks great! No need for re-review after (optionally) addressing the suggestions below.

Comment thread app-lib/wallet.ts Resolved
Comment thread templates/react/.env.example Resolved
Comment thread templates/svelte/.env.example Resolved
Comment thread .gitignore Resolved
Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com>
@zachfedor
zachfedor merged commit 5053544 into main Oct 17, 2026
3 checks passed
@zachfedor
zachfedor deleted the feat/config-driven-network branch October 17, 2026 11:46
zachfedor added a commit to stellar-scaffold/cli that referenced this pull request Oct 17, 2026
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.com/claude-code)

https://claude.ai/code/session_01QhmaWLv4yExX8LahP5UtUb

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.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