fix(setup): UI mode now re-packs .gtbundle after successful save#91
Open
BimaPangestu28 wants to merge 2 commits into
Open
fix(setup): UI mode now re-packs .gtbundle after successful save#91BimaPangestu28 wants to merge 2 commits into
BimaPangestu28 wants to merge 2 commits into
Conversation
`run_simple_setup` calls `gtbundle::create_gtbundle` after `engine.execute` to write the configured bundle back to its `.gtbundle` archive. `run_ui_mode` did the same engine work — answers, secrets, tenant config all written under `bundle_dir` — but **never re-packed** the extracted dir back to the user's input file. Result: every `gtc setup` (default UI mode) on a `.gtbundle` left the original archive untouched, and the next `gtc start` saw none of the just-saved secrets and panicked with `secret error: not-found`. Fix: - Add `output_target: Option<SetupOutputTarget>` to `UiState` and the `ui::launch` signature so the UI flow knows whether the user passed a `.gtbundle` archive (re-pack) or a directory (copy). - `run_ui_mode` now computes `setup_output_target(&bundle_path)` up front (same helper the simple-mode flow uses) and passes it through. - The `/api/execute` UI handler runs the existing `execute_setup` blocking task; on `result.success` it dispatches a follow-up `spawn_blocking` that calls `gtbundle::create_gtbundle` (Archive) or `copy_dir_recursive` (Directory) and appends a "Configured bundle written to: …" line to the result. Re-pack failure flips `success` to false and surfaces the error in `stderr` so the UI can show it instead of silently lying about success. - Derive `Clone` on `SetupOutputTarget` since the value is now stored in `UiState` (`Arc`-shared) and consumed inside the spawn_blocking closure. Verified: a `.gtbundle` run through `gtc setup` now contains the populated `state/config/<provider>/setup-answers.json` and `.greentic/dev/.dev.secrets.env` after extraction, where it was empty before this change.
BimaPangestu28
added a commit
that referenced
this pull request
May 2, 2026
Carries the four PRs in the setup-roundtrip family up onto a stable release tag so `cargo install greentic-setup` (and therefore the `gtc setup` router target) picks up the fixes: - #89/#90: squashfs perms (re-packed bundle now extracts as 0o755/0644 instead of 0o000), `--non-interactive` no longer hangs on tunnel prompt, `emit-answers` template now includes `platform_setup.tunnel`. - #91/#92: UI mode (default `gtc setup`) now re-packs the .gtbundle after engine.execute — answers + secrets + tenant config actually land inside the squashfs instead of being dropped on temp-dir cleanup. Tag `v0.5.8` after merge to trigger crates.io publish via release-binaries.yml.
…ms (cramped 7-col tables)
BimaPangestu28
added a commit
that referenced
this pull request
May 2, 2026
) * fix(setup): UI mode now re-packs .gtbundle after successful save `run_simple_setup` calls `gtbundle::create_gtbundle` after `engine.execute` to write the configured bundle back to its `.gtbundle` archive. `run_ui_mode` did the same engine work — answers, secrets, tenant config all written under `bundle_dir` — but **never re-packed** the extracted dir back to the user's input file. Result: every `gtc setup` (default UI mode) on a `.gtbundle` left the original archive untouched, and the next `gtc start` saw none of the just-saved secrets and panicked with `secret error: not-found`. Fix: - Add `output_target: Option<SetupOutputTarget>` to `UiState` and the `ui::launch` signature so the UI flow knows whether the user passed a `.gtbundle` archive (re-pack) or a directory (copy). - `run_ui_mode` now computes `setup_output_target(&bundle_path)` up front (same helper the simple-mode flow uses) and passes it through. - The `/api/execute` UI handler runs the existing `execute_setup` blocking task; on `result.success` it dispatches a follow-up `spawn_blocking` that calls `gtbundle::create_gtbundle` (Archive) or `copy_dir_recursive` (Directory) and appends a "Configured bundle written to: …" line to the result. Re-pack failure flips `success` to false and surfaces the error in `stderr` so the UI can show it instead of silently lying about success. - Derive `Clone` on `SetupOutputTarget` since the value is now stored in `UiState` (`Arc`-shared) and consumed inside the spawn_blocking closure. Verified: a `.gtbundle` run through `gtc setup` now contains the populated `state/config/<provider>/setup-answers.json` and `.greentic/dev/.dev.secrets.env` after extraction, where it was empty before this change. * feat(setup): support QuestionKind::Table for repeating-row wizard answers Phases 2–3 of the table-kind RFC (greenticai/greentic-types#112). Wizard operators can now answer repeating-row questions row-by-row instead of typing JSON arrays into a single textarea. Changes: - `setup_input::SetupQuestion` accepts `kind: table` plus `columns: [...]`, `min_rows`, `max_rows`. New `SetupTableColumn` struct mirrors the per-row schema (key/title/kind/required/help/placeholder/choices/default). - `setup_to_formspec::convert::convert_table_question` bridges the setup.yaml shape into qa-spec's existing `QuestionType::List` + `ListSpec.fields`. Each column becomes a nested `QuestionSpec` keyed by its `key` field. Nested tables are not supported — column kind collapses to scalar. - `qa::prompts::ask_list_question` implements the CLI prompt loop: "Add a row? [y/N]", per-column prompts, drop rows with empty required columns, enforce `min_items`/`max_items`. Returns a `Value::Array` of per-row JSON objects. - `tenant_config::sync_nav_links_to_tenant_config` now accepts the native array shape (`nav_links`) produced by the table wizard, in addition to the existing `nav_links_json` string fallback. New helper `sanitize_nav_link_array` deduplicates the validation logic and adds i18n-keyed label support (locale-keyed object passes through). Tests: 23 tenant_config (1 new — table-shape answer) + 22 setup_to_formspec (1 new — table → list bridge). 223 lib total. Phase 4 (canary migration of `nav_links_json` to Table kind in messaging-webchat-gui setup.yaml) lands in greenticai/greentic-messaging-providers PR #149 alongside this. * chore(release): bump to 0.5.8 to ship setup .gtbundle round-trip fixes Carries the four PRs in the setup-roundtrip family up onto a stable release tag so `cargo install greentic-setup` (and therefore the `gtc setup` router target) picks up the fixes: - #89/#90: squashfs perms (re-packed bundle now extracts as 0o755/0644 instead of 0o000), `--non-interactive` no longer hangs on tunnel prompt, `emit-answers` template now includes `platform_setup.tunnel`. - #91/#92: UI mode (default `gtc setup`) now re-packs the .gtbundle after engine.execute — answers + secrets + tenant config actually land inside the squashfs instead of being dropped on temp-dir cleanup. Tag `v0.5.8` after merge to trigger crates.io publish via release-binaries.yml. * feat(setup-ui): render kind:table as add/remove-row table input Phase 5 of the table-kind RFC. The web wizard now renders QuestionType::List questions as a real table with one input per column, an Add row button, and trash-icon row removal — instead of falling through to the empty Back button stub. Wire-up: - QuestionInfo gains list_columns plus min_rows/max_rows. - app.js: renderQuestion handles kind==='List', appendTableRow builds one tr of inputs, setupTableQuestions wires the Add button and seeds saved rows. restoreFormValues skips List, collectFormValues walks tbody rows and builds per-row JSON objects. - style.css: minimal table styling. End-to-end: operator sees a real table at gtc setup --ui, fills per-row, submit posts { nav_links: [{label, url, external?}, ...] }, sync function writes the array to tenants/<tenant>.json, SPA renderTopbarNav consumes. * feat(setup-ui): multilingual cell + uniform input sizing for table columns * feat(setup-ui): expand multilingual locale picker to full 65-entry SUPPORTED_LOCALES set * feat(setup,ui): tooltip+num columns in nav_links table; nested tooltip emit * chore: cargo fmt — apply to tenant_config.rs CI fmt check failed after the i18n nav-links work landed; running `cargo fmt --all` reformats the affected blocks in sanitize_nav_links / build_tooltip_obj. No semantic change — picked up on the hotfix branch so CI on PR #92 (and the matching #91 sibling) goes green. * fix(setup-ui): widen container + has-wide-form toggle for kind:table forms * fix(setup): always prompt kind:List even when optional in normal (non-advanced) mode * feat(setup-ui): row-level i18n picker, table hydration, legacy migration - Replace per-cell "+ language" pickers with a single row-level toolbar that adds locale sub-rows to every multilingual cell at once and removes them symmetrically via chip ✕. Saves four clicks per row and keeps the pre-mount layout from cramming pickers into 7 columns. - Pre-populate the nav_links table on wizard re-run by reading the bundle's persisted tenant.json::nav_links via a new read_existing_nav_links helper that flattens the on-disk nested tooltip {eyebrow,title,lede} shape back to the wizard's flat columns. saved_rows is sent through the new QuestionInfo field. - Migrate legacy nav_links_json string answers to nav_links arrays in /api/scopes prefill, then drop the legacy key. Without this the ghost overrides the table edits the user just made on next sync. - Fix collectFormValues silently dropping URL/Boolean/scalar columns: td.dataset.col (added for CSS column-width targeting) made the bare [data-col] selector match the parent <td> first; cell.value read back undefined and required-column rows got filtered out, leaving nav_links absent from the answers payload. Selector now scopes to input/select. - Switch form-area listeners to event delegation so dynamically-added locale-row inputs participate in autosave. - Per-column min-widths + container widening to 1380px for the 7-column nav_links table; row-table__lang-row chips match brand. - Verbose [hydrate] / [nav_links] logging for round-trip diagnosis. - Bump 0.5.8 → 0.5.9. * chore: cargo fmt + drop single-element legacy-key loop CI fmt found two long lines in tenant_config.rs / ui/mod.rs; clippy flagged the migration loop as single_element_loop. Open-code the single legacy_key migration with const strings — when we add more `_json` legacy answer shapes we can re-introduce the loop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
Bima reported `secret error: not-found` at runtime even after running `gtc setup` and seeing 'persisted N keys' for every provider. Extract of his `test.gtbundle` post-setup confirmed the bundle had no `state/`, `tenants/`, or `.greentic/` inside the squashfs — even though `/tmp/gtbundle-test/` (the extracted working dir) had everything correctly populated.
Root cause
`run_simple_setup` (greentic_setup.rs:267-296) calls `gtbundle::create_gtbundle` after `engine.execute` to write the configured bundle back to its `.gtbundle` archive. `run_ui_mode` (greentic_setup.rs:312-378) did the same engine work — answers, secrets, tenant config all written under `bundle_dir` — but never re-packed the extracted dir back to the user's input file.
Default `gtc setup` flow lands on UI mode (`cli.ui && !cli.no_ui && !cli.non_interactive`), so the typical user gets the broken path. The original `.gtbundle` stays unchanged, and the next `gtc start` sees none of the just-saved secrets.
Fix
Verification
Related