Conversation
π· ci(release): publish stable releases with gh
β¦ngelog-dupes # Conflicts: # CHANGELOG.md
Add the `aliases` module backing issue #86 β string-substitution CLI aliases expanded before clap parses argv. - `src/aliases.rs` β `ResolvedAliases` (built-in / repo / user chain), `load()`, `expand_argv()`, `validate_aliases()`. Static `BUILT_IN_ALIASES` slice mirrors every clap `visible_alias` declared on `Command` so the shadow gate stays in lockstep with `src/cli.rs`. - `src/config.rs` β new `Config.aliases: BTreeMap<String, String>` field; `Config::load_for_repo` now calls `validate_aliases()` so a shadowed or shell-piped declaration fails at load time. - `tests/aliases_tests.rs` β 21 unit tests covering: empty default, TOML round-trip, refusal of built-in shadow (subcommand + visible alias), refusal of shell metachars (`&&`, `||`, `|`, `;`, backticks), refusal of empty value, repo > user precedence, user-only fallback, missing user file is no-op, expansion respects trailing args, positional vs subcommand slot, no recursion, global flags pre-alias preserved, built-in-subcommand-always-wins defence in depth. No CLI surface changes yet β the next commit wires expansion into `main.rs` and adds the `gwm aliases list` subcommand. refs #86
π· ci(release): reject duplicate rc changelog entries
Implement issue #86's user-visible surface on top of the `aliases` module landed in the previous commit. - `src/main.rs` β expand argv via `aliases::load()` + `aliases::expand_argv()` BEFORE `Cli::parse_from(...)`. A failure to load the alias chain prints a warning and falls back to the raw argv, so a typo in `[aliases]` doesn't lock the user out of `gwm doctor` / `gwm aliases list`. - `src/cli.rs` β new `Aliases { action: AliasesAction }` subcommand with a single `List` action. `cmd_aliases_list()` discovers the current repo (gracefully degrades outside a git tree), loads the resolved chain, and prints three grouped sections (`built-in`, `repo (.gwm.toml)`, `user (~/.config/gwm/aliases.toml)`). Entries in the user section that are shadowed by a repo declaration are flagged inline `(shadowed by repo)`. - `tests/cli_binary.rs` β extend the `help_prints_subcommands` canary with the `aliases` row, add 6 `aliases_list_*` end-to-end tests (built-in section outside a repo, repo section, user section, repo-overrides-user precedence, shadow error surfaced), and 3 `alias_*` expansion tests (built-in subcommand reached through alias, trailing args preserved, shadow refusal at runtime). All tests are env-isolated (XDG_CONFIG_HOME + HOME pointed at the TempDir) so a contributor's real `~/.config/gwm/aliases.toml` never contaminates the suite. refs #86
β¦ce rules Add user-facing documentation for issue #86, closing the changeset. - `CHANGELOG.md` β Unreleased entry under Added describing the alias chain, expansion semantics, and the `gwm aliases list` subcommand. - `examples/gwm.toml.example` β commented-out `[aliases]` block with the resolution chain, the no-shell-pipeline rule, the canonical examples (`wip`, `ll`, `sync`), and a pointer to `gwm aliases list`. - `docs/4.configuration/1.gwm-toml.md` β full `[aliases]` section with the validation table (shadow + shell metachar rules) and a row added to the defaults summary table. - `docs/3.cli/1.reference.md` β `gwm aliases list` subcommand reference with sample output, resolution order, and the inline shadowing flag. closes #86
Fixes the windows-latest CI compile failure: NoSymlink is only used by Unix-gated bootstrap tests.\n\nrefs #112
# Conflicts: # CHANGELOG.md
Fixes the windows-latest CI TOML parse failures when test TempDir paths contain backslashes.\n\nrefs #112
Fixes windows-latest config tests by using Windows absolute path literals when the suite runs on Windows.\n\nrefs #112
The `aliases_list_prints_built_in_section_outside_repo` test asserted
`contains("s")` for the built-in `s β switch` row, which matched
incidental letters in `aliases`, `users`, `built-ins`, and other prose
in the output. The test would have passed even if the `s` row was
missing or malformed.
Assert the full formatted row ` s β switch` (width-2 padding from
`cmd_aliases_list`) and ` cd β path` instead, so the predicate
actually pins the built-in alias rendering. Also tighten `built-in:`
(colon-terminated) so the section header is anchored to its full
label rather than the substring shared with `built-ins` prose.
Addresses Copilot review comment on PR #151.
Refs #86
Preserve Windows backslashes for {path} and {diff} placeholders before shell_words tokenizes command templates.
Refs #112
`main` collected argv via `std::env::args()` and handed a `Vec<String>` to clap, which panics on the first non-UTF-8 argv entry. This was a regression vs. clap's default `args_os` handling and could abort the binary on a perfectly valid OS argv (Unix and Windows both allow non-UTF-8 bytes in argv). Switch to `std::env::args_os()` + `Cli::parse_from(Vec<OsString>)`, and add an OsString-native alias expander `aliases::expand_argv_os` that only attempts UTF-8 decoding on the alias-slot token (alias keys are `String` by construction, so a non-UTF-8 token cannot match β argv flows through unchanged and clap surfaces the unknown subcommand verbatim). Flag detection uses `OsStr::as_encoded_bytes` and looks for a leading ASCII `-`, which is unambiguous in both UTF-8 (Unix) and WTF-8 (Windows) and avoids decoding the rest of the token. The existing `expand_argv` (String surface) is preserved for unit tests and as a stable internal API β `expand_argv_os` is the new entry point from `main`. Tests added: - `binary_tolerates_non_utf8_argv` (Unix integration) β pins that the binary does NOT die on a signal (SIGABRT) when argv contains invalid UTF-8 bytes (`0xff 0xfe 0x80`). Without the fix this reproduces the libstd panic `called Result::unwrap() on an Err value: "\xFF\xFE\x80"`. - `expand_argv_os_matches_expand_argv_for_utf8_inputs` β sanity check that the OsString and String surfaces stay in lockstep on valid UTF-8 input. - `expand_argv_os_passes_non_utf8_token_through_unchanged` β documents the contract: non-UTF-8 tokens in the alias slot flow through, no panic, no coercion. - `expand_argv_os_expands_alias_after_non_utf8_flag_value` β pins the conservative behaviour when a non-UTF-8 byte sequence lands in the would-be alias slot. Addresses Copilot review comment on PR #151. Refs #86
Avoid hard-coded Unix separators in naming path assertions now that the suite runs on windows-latest. Refs #112
Keep RC changelog duplicate tests portable on windows-latest by invoking the shell script through bash instead of as a native executable. Refs #112
Copy the RC changelog duplicate checker into the temp fixture and execute it by relative path so Git Bash does not need to translate a Windows drive path. Refs #112
The RC changelog checker is a Bash workflow helper exercised by Unix CI jobs; keep Windows focused on workflow structure so windows-latest is not blocked on Git Bash path semantics. Refs #112
Avoid dead-code failures on windows-latest after gating the Bash-only RC changelog duplicate tests. Refs #112
Keep TUI bootstrap-gate fixtures parseable on windows-latest by escaping backslashes in temporary paths. Refs #112
π· ci(test): add windows test matrix row
β¨ feat(cli): CLI aliases ([aliases] in .gwm.toml + user fallback)
Red phase of the TDD loop for issue #85. Adds: - `tests/gitmoji_tests.rs` β built-in branch_type β shortcode map, `.gwm.toml` `[gitmoji]` overrides merged on top of defaults, prefix resolution in both `:shortcode:` and unicode forms, unknown-type fallback (`:question:` / β), and a sweep that every default shortcode has a matching unicode entry. - `tests/hooks_tests.rs` β `install_commit_msg(repo, force)` creates an executable `.git/hooks/commit-msg`, refuses to clobber an existing hook without `--force`, replaces on `--force`, and the generated script carries the gwm marker + shell-out to `gwm commit-prefix --unicode`. - `tests/cli_binary.rs` β end-to-end coverage for `gwm commit-prefix [--branch <name>] [--unicode]`, the new `--gitmoji` flag on `gwm types`, and `gwm hooks install commit-msg` (with / without `--force`). `help_prints_subcommands` is updated with the two new subcommand rows β the canary contract from CONTRIBUTING.md. refs #85
Internal plumbing for issue #85 (turns the new tests green; the CLI surface lands in the next commit). src/gitmoji.rs - `DEFAULT_GITMOJI` β the ten built-in `branch_type β :shortcode:` pairs baked into the binary so a stock repo (no `[gitmoji]` block) works out of the box. - `GitmojiMap` wrapper around a `BTreeMap` for deterministic iteration (load-bearing for `gwm types --gitmoji` byte stability). - `default_map()` materialises the built-in table. - `load(repo_root)` reads `.gwm.toml`'s `[gitmoji]` block via a local deserialisation envelope and merges it on top of the defaults β overriding `feat` doesn't wipe the other nine built-ins. - `resolve_prefix(map, branch, unicode)` renders `β¨ feat(#41):` (or `β¨ feat(#41):` with `unicode=true`), falling back to `:question:` / β for unknown branch types so the surface is safe to call on any branch. - `shortcode_to_unicode` is a small `match` (eleven entries: ten built-ins + `:question:`) β no `gh-emoji`-style dep for so few keys; user-defined shortcodes round-trip verbatim. src/hooks.rs - `install_commit_msg(repo_root, force)` writes `.git/hooks/commit-msg` (chmod 0o755 on Unix), refuses to overwrite an existing hook without `--force`, and rejects non-git directories. - `commit_msg_script()` returns a self-contained POSIX `sh` script that shells out to `gwm commit-prefix --unicode` and prepends the resolved prefix when the first line of the commit message doesn't already start with either a `:shortcode:` or a unicode emoji glyph. Degrades to no-op when `gwm` isn't on `$PATH` so the hook never blocks a commit because of itself. refs #85
Surfaces the gitmoji + hooks plumbing through three CLI entry points (issue #85). Diff on src/main.rs deliberately empty β the new subcommands ride the existing `cli::run` dispatch, so the merge surface against PR #151 (issue #86 aliases) stays minimal. `gwm types [--gitmoji]` Extends the existing two-column branch-type list with two more columns (unicode emoji + `:shortcode:`) when `--gitmoji` is set. Without the flag the output is byte-identical to the pre-#85 surface β scripted consumers don't regress. Shortcode width is aligned on the widest entry so the description column stays put. `gwm commit-prefix [--branch <name>] [--unicode]` Resolves to `:sparkles: feat(#41):` (or `β¨ feat(#41):` under `--unicode`). Two resolution paths: - `--branch <name>` β no repo required; useful for scripted contexts and AI assistants composing a commit message out-of-band. - default β reads HEAD from the current repo via libgit2. A branch that doesn't match `<type>/#<N>-<slug>` exits non-zero with a pointer to the convention rather than silently rendering a bogus prefix. `gwm hooks install commit-msg [--force]` Installs `.git/hooks/commit-msg` via `hooks::install_commit_msg`. Opt-in (off by default), non-destructive (refuses to overwrite without `--force`), executable on Unix (mode 0o755). Wired through `HooksAction::Install` + `HookKind::CommitMsg` so future hooks drop in without breaking the CLI surface. tests/cli_binary.rs Adds end-to-end coverage for all three surfaces (9 new tests), including the canary update to `help_prints_subcommands` per CONTRIBUTING.md. closes #85
β¨ feat(tui): polish confirm + help modals (buttons, loader, key badges)
β¨ feat(config): user-level global config merged under .gwm.toml (#190)
`KeyStroke::from_event` kept the SHIFT modifier verbatim, so on terminals
that report a shifted letter as `Char('V') + SHIFT` (Ghostty / Kitty /
WezTerm) β or as the base key `Char('v') + SHIFT` under the kitty keyboard
protocol β the runtime keystroke never compared equal to a bound chord like
`"V"` (`Char('V')`, no modifier). The lookup missed and the action silently
did nothing, not even a status-bar update. This affected every uppercase
binding (`G`, `R`, `F`, `O`, `L`), but only surfaced now because #188's
`V` / `H` were the first uppercase keys actively exercised on such a terminal.
Canonicalise any `Char` keystroke carrying SHIFT to its uppercase form with
the SHIFT bit dropped, in `KeyStroke::new` β so `from_event` and the config
parser (`"V"`, `"Shift+v"`) all converge on the same `Char('V')` with no
modifier, independent of how the terminal encodes shift. Non-`Char` codes
(`Shift+Tab` β `BackTab`) are untouched.
refs #188
# Conflicts: # CHANGELOG.md # src/tui/mod.rs
β¨ feat(tui): borderless styled header + claude-dark preset (#185)
β¦onsive # Conflicts: # CHANGELOG.md # src/tui/ui.rs
β¦ global config After #190 added the user-level global config layer, every test that loads config through `Config::load_for_repo` β directly, or via `aliases::load` / `App::new_at` β merged the runner's real `~/.config/gwm/config.toml`. On any machine whose global config diverges from the built-in defaults the suite failed: a global `[theme] preset` unknown to the checked-out build (e.g. `claude-dark`, only added on an unmerged branch) made `theme.resolve()` reject the merged config, panicking `aliases_tests.rs` (10 tests) and `tui_theme_integration_tests.rs` (2 tests) on `.unwrap()`. CI stayed green only because it exports `GWM_NO_GLOBAL_CONFIG=1` (#191); local `cargo test` had no such guard. Root cause: #190 added the `Config::load_layered(repo, global)` seam for hermetic tests but did not propagate it to the call sites built on top of `load_for_repo`. Complete the pattern: - `aliases::load_layered(repo, global, user)` β repo step reads via `Config::load_layered`, user step takes the path literally (no implicit `default_user_path` fallback). `aliases::load` resolves the real global/user paths then delegates, so runtime behaviour is unchanged. - `App::new_at_layered(start, global)` β same seam for the TUI app builder; `App::new_at` delegates with the real `global_config_path()`. - The leaking tests now inject `None` global (and explicit/`None` user paths), so the whole suite passes deterministically without `GWM_NO_GLOBAL_CONFIG`, regardless of the contributor's `~/.config/gwm/`. closes #194
π fix(config): hermetic config-loading seams so tests ignore the real global config
β¨ feat(tui): responsive sidebar β stacked fallback + left/right position
β¦red seams Completes the hermeticity work from #194 / #195. Those added the `Config::load_layered` / `aliases::load_layered` / `App::new_at_layered` seams and converted `aliases_tests.rs` + `tui_theme_integration_tests.rs`, but the rest of the suite still called the environment-reading entry points (`Config::load_for_repo`, `App::new_at`, `App::new_picker_at`), so `cargo test` without `GWM_NO_GLOBAL_CONFIG=1` still failed on a machine with a divergent global config (e.g. `[tui] sidebar_position = "left"` broke `config_tests::tui_sidebar_position_absent_keeps_right`). - Add `App::new_picker_at_layered(start, global)` (mirrors `new_at_layered`; `new_picker_at` delegates with the real global path). - Convert every remaining test site to inject `None` global: `config_tests.rs` (68), `bootstrap_tests.rs` (2), `doctor_tests.rs` (1), `tui_app_tests.rs` (5), `tui_chord_tests.rs` (2), `tui_state_palette_tests.rs` (1). The whole suite now passes **without** `GWM_NO_GLOBAL_CONFIG=1`, verified against a real `~/.config/gwm/config.toml` carrying `claude-dark` + `sidebar_position = "left"`. No runtime behaviour change β production entry points still read the real global config. closes #196
π fix(config): sweep all config-loading test sites onto the load_layered seams
Largest RC of the 0.8.0 series β the personalisation + polish wave on top of rc.4's quick wins (global config #190, responsive sidebar #188, claude-dark + header #185, modal polish #187, statusline #180, working-tree colours #179, PR auto-detection #181, {repo_path}/{repo_parent} #175), plus the test- hermeticity sweep (#194 / #196). - Cargo.toml / Cargo.lock: 0.8.0-rc.4 -> 0.8.0-rc.5. - changelogs/pre-releases/0.8.0-rc.5.md: new per-RC file with the delta against rc.4 (merged PRs #176, #178, #182, #183, #184, #186, #189, #191, #192, #195, #197). - CHANGELOG.md: reset [Unreleased] to the placeholder (bullets move into the rc.5 file) and add 0.8.0-rc.5 above rc.4 in the index. - ROADMAP.md: add the rc.5 shipped-highlights row. Reconciliation: open PRs accounted for (only #157 git2 0.21, deferred to #169, left open). Local guards green: cargo build (Cargo.lock synced to rc.5) cargo fmt --check cargo clippy --all-targets -- -D warnings cargo clippy --all-targets -- -W clippy::incompatible_msrv ./.github/scripts/check-rc-changelog-dupes.sh v0.8.0-rc.5 cargo test (green without GWM_NO_GLOBAL_CONFIG=1 after the #194/#196 sweep) refs #188
- Version line: note 0.7.0 stable + the 0.8.0-rc.5 feature set on dev (global config, aliases/gitmoji, config CLI + hooks, templates, sync, binstall, TUI personalisation incl. claude-dark + responsive sidebar). - Keymap table: correct the `v` sidebar row (narrow terminal stacks, not hidden) and add `V` (cycle layout) / `H` (toggle position). - Details sidebar section: document the responsive orientation + left/right position and the `[tui] sidebar_position` knob. refs #188
Copilot review on PR #198: the `V` sidebar-layout cycle is `auto β side-by-side β stacked β auto`, but SKILL.md's keymap table and Details sidebar section omitted the wrap back to `auto`. Align both with the actual behaviour (and with the CHANGELOG / rc.5 notes, which already showed the wrap). refs #188
ποΈ build: cut v0.8.0-rc.5
Add the subcommands that shipped across the 0.8.0 RC series but were still missing from the reference: commit-prefix, hooks install commit-msg, undo, history, theme list/show, tui keys. Extend types with --gitmoji and remove/prune with --dry-run, complete the create/bootstrap flag tables (--reuse-branch / --skip-hooks / --force), and add sync to the dynamic completers. Fix a stale completions anchor. refs #199
Add the s (sidebar stashes mode) and : (command palette) list-view keys, note that the keymap is now configurable via [tui.keys] with chord support, and fix the focused-border colour wording (theme focus role, default Cyan). Add two pages β Themes (roles, presets, value formats) and Keymap & command palette ([tui.keys] remap, gwm tui keys, palette flow) β plus an rc.5 chrome section (single-line statusline, borderless header, shared modal frame). refs #199
Add the [theme] (presets, roles, named/indexed/hex value formats) and
[tui.keys] (chord grammar, rebindable action table, validation) blocks
with #theme / #tui-keys anchors, the [tui].sidebar_position knob, and
the {repo_path} / {repo_parent} placeholders. Add a User-level global
config page (~/.config/gwm/config.toml deep-merge, GWM_NO_GLOBAL_CONFIG)
and document when: predicates for [[hooks.*]] phases.
refs #199
Document the 8th gwm doctor check ([tui.keys] keymap resolves), add a cargo-binstall packaging section, and flesh out the PR auto-detection provenance (LinkSource::Detected, the (detected) TUI marker). refs #199
Add cargo binstall to the install channels and fix the MSRV to 1.82; refresh the testing story (ubuntu/macos/windows matrix, ~990 tests, the new integration files, the mandatory TDD loop) and the contributing commit helpers (commit-prefix / commit-msg hook). Add the v0.8.0-rc.5 personalisation+polish bullet to the roadmap and refresh the landing page feature bullets + 30-second tour. refs #199
Complete French mirror of the docs/ tree (33 pages incl. the three new v0.8.0 pages), structured for Nuxt Content i18n. English under /docs stays canonical; prose is translated with full diacritics while code, commands, flags, TOML keys, identifiers and proper nouns are kept verbatim, and internal links are rewritten to the /fr/ prefix. refs #199
Addresses the review on the docs v0.8.0 PR:
- gwm doctor runs 8 checks, not 7 (EN + FR CLI reference; doctor.md and
the integrations index already said 8).
- Reword the "no git CLI dependency" claim (EN landing + getting-started,
FR landing): worktree ops run on vendored libgit2, but a few features
(gwm sync, the review-diff launcher, the sidebar git status/log) do
shell out to the user's own git.
- i18n: translate the leftover English section headings on the FR pages
(rΓ΄les, prΓ©rΓ©glages, rΓ©solution de la base, en-tΓͺte, mode stashes, β¦)
and localize the getting-started navigation.title to "DΓ©marrage".
- Align every internal anchor link with the real heading slug on both
trees: drop the {#tui-keys} / {#theme} id pins (they render literally
on GitHub) in favour of the natural slugs (#tuikeys, #tuiopen,
#bootstrapcommand), and fix the FR anchors to the French heading slugs
(incl. the double-hyphen "issue / pr" cases). Verified: 0 dangling
page or anchor link across docs/ and docs/fr/.
refs #199
π docs: refresh docs/ to the v0.8.0 surface + French i18n mirror
Promote 0.8.0-rc.5 to the 0.8.0 stable release. - Bump Cargo.toml / Cargo.lock to 0.8.0. - Add changelogs/0.8.0.md consolidating the rc.1βrc.5 deltas plus the stable-only docs refresh (#199 / #200), per the release-notes-are- per-version house rule. - Reset CHANGELOG.md [Unreleased] and index 0.8.0 under Past releases. - Refresh ROADMAP.md Current state to v0.8.0 (config CLI, hooks, aliases, gitmoji, templates, TUI personalisation, global config, sync, binstall, 8 doctor checks, gh-CLI publish with #146 resolved). git2 0.21 (#157) stays deferred to #169.
ποΈ build: cut v0.8.0 stable
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.
Description
Release merge: promote
devintomainfor the v0.8.0 stable release.Bundles the full 0.8.0 train (rc.1 β rc.5) plus the stable cut (#201): version bump to
0.8.0, consolidatedchangelogs/0.8.0.md, reset[Unreleased], and the ROADMAP refresh to v0.8.0.Type of change
Changes
dev.Cargo.toml/Cargo.lockβ0.8.0,changelogs/0.8.0.md, ROADMAP v0.8.0.Tests
cargo testgreen locally (1080 tests, 0 failures) and on the CI matrix (ubuntu/macos/windows).cargo fmt --check+cargo clippy --all-targets -- -D warningspass.gwm doctorgreen (8/8).Notes for reviewers
After this merges,
v0.8.0is tagged onmainto triggerrelease.yml(gh-CLI publish, #146 path).git20.21 (#157) stays deferred to #169.