Skip to content

Preserve script order in generated README#2875

Open
mikeland73 wants to merge 4 commits into
mainfrom
claude/focused-goldberg-txihvu
Open

Preserve script order in generated README#2875
mikeland73 wants to merge 4 commits into
mainfrom
claude/focused-goldberg-txihvu

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Summary

Fixes #1991.

devbox generate readme listed scripts in alphabetical order instead of the order they are defined in devbox.json. For example, given scripts defined as step-one, step-two, step-three, step-four, the generated README showed them as step-four, step-one, step-three, step-two — losing the meaningful ordering authors use to communicate "do this first".

Root cause

Scripts are stored in a map[string]*script, and the README template ranged over that map directly:

{{ range $name, $_ := .Scripts }}
* [{{ $name }}](#devbox-run-{{ $name }})
{{- end }}

Go's text/template (like Go map iteration in general) visits map keys in sorted order, so the user's devbox.json ordering was discarded.

Fix

Add order-preserving APIs driven by the existing hujson AST (which already retains source order for comment lookups):

  • configAST.objectKeysInOrder(path...) — walks to an object and returns its member names in source order.
  • ConfigFile.ScriptOrder() / Config.ScriptOrder() — expose script names in definition order (included plugins first, then the root config, matching Scripts' existing merge precedence).
  • Scripts.InOrder(order) — converts the script map into an ordered []ScriptWithName, appending any scripts not present in order alphabetically so output stays deterministic even when the AST is unavailable (e.g. a config not parsed from a file).

docgen now builds the ordered slice (cfg.Scripts().WithRelativePaths(...).InOrder(cfg.ScriptOrder())) and the README template ranges over it.

Testing

  • New unit tests in internal/devconfig/configfile/scripts_test.go:
    • source-order preservation from a parsed devbox.json
    • alphabetical fallback when no order is available
    • de-duplication / alphabetical append of scripts missing from the order slice
    • commands are carried through to the ordered slice
  • go build ./internal/..., go vet, and gofmt are clean.
  • End-to-end: built devbox and ran devbox generate readme on a project whose scripts are defined step-one/step-two/step-three/step-four; the generated README now lists them in that exact order (both the script list and the "Script Details" section), instead of alphabetically.

Note: two unrelated TestFindError permission subtests fail in the CI/sandbox environment because it runs as root (uid 0 bypasses file-permission bits); they fail identically on main without this change.

cc @ametad (issue reporter) — thanks for the clear repro.

🤖 Generated with Claude Code

https://claude.ai/code/session_012kMnjYNx5Y5YzyqLPzeroQ


Generated by Claude Code

`devbox generate readme` listed scripts in alphabetical order instead of
the order they are defined in devbox.json. This happened because scripts
are stored in a map and the README template ranged over it directly;
Go's text/template (like map iteration generally) visits keys in sorted
order, so user-defined ordering was lost.

Add order-preserving APIs driven by the existing hujson AST:

- configAST.objectKeysInOrder walks to an object and returns its member
  names in source order.
- ConfigFile.ScriptOrder / Config.ScriptOrder expose the script names in
  the order they appear (included plugins first, then the root config,
  matching Scripts' merge precedence).
- Scripts.InOrder turns the script map into an ordered []ScriptWithName,
  appending any scripts not covered by the order slice alphabetically so
  output stays deterministic.

docgen now renders scripts via this ordered slice, and the README
template ranges over it. Adds unit tests covering source-order
preservation, the alphabetical fallback, and de-duplication of unknown
scripts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kMnjYNx5Y5YzyqLPzeroQ
Copilot AI review requested due to automatic review settings June 19, 2026 14:14

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 updates devbox generate readme so scripts are rendered in the same order they appear in devbox.json (instead of the deterministic-but-unhelpful ordering produced when ranging over a map in Go templates). It does this by extracting script-name order from the existing hujson AST and by converting the Scripts map into an explicitly ordered slice for templating.

Changes:

  • Add AST-backed script order APIs (configAST.objectKeysInOrder, ConfigFile.ScriptOrder, Config.ScriptOrder) and an ordering helper (Scripts.InOrder).
  • Update docgen to pass ordered scripts into the README template and update the template to range over that ordered slice.
  • Add unit tests covering source-order preservation, alphabetical fallback, append behavior, and command propagation.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/devconfig/configfile/scripts.go Adds ordered iteration support (ScriptWithName, ScriptOrder, InOrder) for scripts stored in a map.
internal/devconfig/configfile/scripts_test.go Adds unit tests for the new script ordering behavior.
internal/devconfig/configfile/ast.go Adds objectKeysInOrder to extract object-member key order from the hujson AST.
internal/devconfig/config.go Exposes merged script-order across included configs and the root config.
internal/devbox/docgen/readme.tmpl Updates template to iterate over an ordered script slice rather than a map.
internal/devbox/docgen/docgen.go Builds ordered scripts (InOrder(cfg.ScriptOrder())) before executing the template.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/devconfig/configfile/scripts.go
Comment thread internal/devconfig/configfile/scripts.go
Comment thread internal/devconfig/configfile/scripts_test.go
claude added 3 commits June 19, 2026 14:19
…rence

- Reword the ScriptWithName comment: only text/template sorts map keys;
  plain Go map iteration is randomized, not alphabetical.
- Scripts.InOrder now keeps the last occurrence of a duplicated name in
  order, so a script overridden by a later (root) definition appears at
  that definition's position, matching merge precedence.
- Add a unit test covering the duplicate-name / keep-last-occurrence case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kMnjYNx5Y5YzyqLPzeroQ
The add_platforms_flakeref testscript fetches and nix-builds
github:F1bonacc1/process-compose from GitHub; it timed out on the
previous run. The failure is unrelated to this PR's changes (README
script ordering). Empty commit to re-run CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kMnjYNx5Y5YzyqLPzeroQ
Run on c0a718f flaked on add_platforms_flakeref.test (plugin.test passed);
run on 15b1dcb flaked on plugin.test (add_platforms_flakeref passed). A
different heavy/network integration testscript fails each run. This PR only
changes README generation and additive config script-ordering APIs, which
these testscripts do not exercise; build, vet, lint and unit tests all pass.
Empty commit to re-run CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kMnjYNx5Y5YzyqLPzeroQ

Copy link
Copy Markdown
Collaborator Author

The remaining red check is a flaky integration test unrelated to this change, which only touches README generation and additive config script-ordering APIs.

A different heavy/network testscript failed on each of three runs:

Run Failed job Notes
c0a718f project-tests-offadd_platforms_flakeref.test plugin.test passed
15b1dcb project-tests-offplugin.test add_platforms_flakeref.test passed
a70478f project-tests-only (examples) → 2.30.2 all three project-tests-off jobs passed

On the latest run, 22/24 checks are green, including build-devbox, golangci-lint, Test Flake Build, Spell Check, every test-nix-versions/auto-nix-install job, and all project-tests-off jobs. The failing project-tests-only job exercises the examples/ projects, which this PR doesn't modify.

The two empty ci: re-trigger commits were attempts to re-run CI (the rerun-jobs API returns 403 for this integration, so a maintainer re-running the failed job — or merging past the flake — should be all that's needed). Deterministic checks (build, lint, unit tests, flake build) pass on every run.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

The order of appearance of scripts in generated readme should have same order as defined in devbox.json

3 participants