Skip to content

Commit 977baab

Browse files
committed
Keep Git table headers valid during upserts
1 parent bb9ae46 commit 977baab

3 files changed

Lines changed: 30 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ EXPERIMENTAL, so on-disk formats and the CLI may change without notice.
1010

1111
- **Fabric commands no longer replace the formatting in `peers.toml`.** Add,
1212
remove, policy, and Git remote changes update only the affected entries.
13-
User comments, spacing, order, and an edited or removed file header remain.
13+
User comments, spacing, order, unknown fields, and an edited or removed file
14+
header remain. This preserves newer fields when an older Fabric rewrites a
15+
mixed-fleet configuration.
1416

1517
- **A failed tunnel no longer reuses one bad shared connection forever.**
1618
Fabric counts post-admission attach failures on the exact current connection.

src/config.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,17 @@ fn upsert_table_field(table: &mut Table, desired: &Table, key: &str) -> Result<(
10441044
};
10451045
let child_position = table.position().unwrap_or(0).saturating_add(1);
10461046
if let Some(current_item) = table.get_mut(key) {
1047+
let current_is_table = matches!(current_item, Item::Table(_) | Item::ArrayOfTables(_));
1048+
let desired_is_table = matches!(desired_item, Item::Table(_) | Item::ArrayOfTables(_));
10471049
replace_item_preserving_decor(current_item, desired_item, child_position);
1050+
if current_is_table != desired_is_table
1051+
&& let Some(mut current_key) = table.key_mut(key)
1052+
{
1053+
// A key-value line uses one space before `=`, while a table header
1054+
// uses no space before `]`. Clear the old context's key decor when
1055+
// a field changes between these two shapes.
1056+
current_key.fmt();
1057+
}
10481058
} else {
10491059
let mut desired_item = desired_item;
10501060
let mut next_position = child_position;
@@ -1651,6 +1661,19 @@ mod tests {
16511661
assert_eq!(mode, 0o640);
16521662
}
16531663

1664+
#[test]
1665+
fn first_peer_save_creates_the_document_header() {
1666+
let directory = tempfile::tempdir().unwrap();
1667+
let home = FabricHome::new(directory.path());
1668+
assert!(!home.peers_path().exists());
1669+
1670+
PeerBook::default().save(&home).unwrap();
1671+
1672+
let raw = fs::read_to_string(home.peers_path()).unwrap();
1673+
assert!(raw.starts_with("# fabric peers."));
1674+
PeerBook::load(&home).unwrap();
1675+
}
1676+
16541677
#[test]
16551678
fn peer_add_and_remove_preserve_human_formatting() {
16561679
let directory = tempfile::tempdir().unwrap();

tests/provisioning.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ fn git_shares_and_peer_grants_live_in_peers_toml() -> Result<()> {
192192
.output()?,
193193
)?;
194194
let raw = fs::read_to_string(home.join("peers.toml"))?;
195-
assert!(raw.contains("[[git_remotes]]"));
195+
assert!(
196+
raw.contains("[[git_remotes]]"),
197+
"the Git remote table header changed:\n{raw}"
198+
);
196199
assert!(raw.contains("git/mandat/write"));
197200
assert!(!raw.contains("git/mandat/read"));
198201
assert!(raw.contains("shell"));

0 commit comments

Comments
 (0)