devobs 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {devobs-0.1.0 → devobs-0.2.0}/.devcontainer/devcontainer.json +1 -1
  2. {devobs-0.1.0 → devobs-0.2.0}/.devcontainer/postAttachCommand.sh +1 -1
  3. {devobs-0.1.0 → devobs-0.2.0}/.github/workflows/ci.yaml +2 -2
  4. {devobs-0.1.0 → devobs-0.2.0}/.github/workflows/release.yaml +4 -4
  5. {devobs-0.1.0 → devobs-0.2.0}/.pre-commit-config.yaml +14 -0
  6. {devobs-0.1.0 → devobs-0.2.0}/Cargo.lock +61 -87
  7. {devobs-0.1.0 → devobs-0.2.0}/Cargo.toml +6 -5
  8. {devobs-0.1.0 → devobs-0.2.0}/Makefile +2 -2
  9. {devobs-0.1.0 → devobs-0.2.0}/PKG-INFO +3 -3
  10. devobs-0.2.0/README.md +7 -0
  11. devobs-0.2.0/src/commands/assert_diff.rs +158 -0
  12. {devobs-0.1.0 → devobs-0.2.0}/src/commands/check_file_pair.rs +2 -1
  13. {devobs-0.1.0 → devobs-0.2.0}/src/commands.rs +1 -0
  14. {devobs-0.1.0 → devobs-0.2.0}/src/main.rs +5 -3
  15. {devobs-0.1.0 → devobs-0.2.0}/src/utils/fs.rs +19 -4
  16. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/mod.rs +1 -0
  17. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__changes_create-2.snap +5 -0
  18. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__changes_create.snap +7 -0
  19. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__changes_delete-2.snap +5 -0
  20. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__changes_delete.snap +7 -0
  21. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__changes_modify-2.snap +5 -0
  22. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__changes_modify.snap +7 -0
  23. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__empty_directory.snap +9 -0
  24. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__no_changes.snap +9 -0
  25. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__no_command_to_run.snap +5 -0
  26. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__nonexistent_directory.snap +5 -0
  27. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__on_command_error_ignore.snap +9 -0
  28. devobs-0.2.0/tests/commands/snapshots/integration_test__commands__test_assert_diff__on_command_error_propagate.snap +7 -0
  29. devobs-0.2.0/tests/commands/test_assert_diff.rs +334 -0
  30. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/test_check_file_pair.rs +43 -38
  31. {devobs-0.1.0 → devobs-0.2.0}/tests/helpers.rs +19 -8
  32. devobs-0.1.0/README.md +0 -7
  33. {devobs-0.1.0 → devobs-0.2.0}/.config/nextest.toml +0 -0
  34. {devobs-0.1.0 → devobs-0.2.0}/.devcontainer/onCreateCommand.sh +0 -0
  35. {devobs-0.1.0 → devobs-0.2.0}/.editorconfig +0 -0
  36. {devobs-0.1.0 → devobs-0.2.0}/.gitattributes +0 -0
  37. {devobs-0.1.0 → devobs-0.2.0}/.github/dependabot.yaml +0 -0
  38. {devobs-0.1.0 → devobs-0.2.0}/.gitignore +0 -0
  39. {devobs-0.1.0 → devobs-0.2.0}/.pre-commit-hooks.yaml +0 -0
  40. {devobs-0.1.0 → devobs-0.2.0}/.rustfmt.toml +0 -0
  41. {devobs-0.1.0 → devobs-0.2.0}/.vscode/extensions.json +0 -0
  42. {devobs-0.1.0 → devobs-0.2.0}/.vscode/launch.json +0 -0
  43. {devobs-0.1.0 → devobs-0.2.0}/.vscode/settings.json +0 -0
  44. {devobs-0.1.0 → devobs-0.2.0}/LICENSE +0 -0
  45. {devobs-0.1.0 → devobs-0.2.0}/pyproject.toml +0 -0
  46. {devobs-0.1.0 → devobs-0.2.0}/rust-toolchain.toml +0 -0
  47. {devobs-0.1.0 → devobs-0.2.0}/src/utils.rs +0 -0
  48. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/snapshots/integration_test__commands__test_check_file_pair__backward_matching.snap +0 -0
  49. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/snapshots/integration_test__commands__test_check_file_pair__create_if_not_exists.snap +0 -0
  50. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/snapshots/integration_test__commands__test_check_file_pair__create_if_not_exists_dry_run.snap +0 -0
  51. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/snapshots/integration_test__commands__test_check_file_pair__empty_directory_no_error_no_output.snap +0 -0
  52. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/snapshots/integration_test__commands__test_check_file_pair__forward_matching.snap +0 -0
  53. {devobs-0.1.0 → devobs-0.2.0}/tests/commands/snapshots/integration_test__commands__test_check_file_pair__on_fully_populated_directory.snap +0 -0
  54. {devobs-0.1.0 → devobs-0.2.0}/tests/integration_test.rs +0 -0
@@ -2,7 +2,7 @@
2
2
  "name": "lasuillard/devobs",
3
3
  "image": "mcr.microsoft.com/devcontainers/rust:1-bookworm",
4
4
  "features": {
5
- "ghcr.io/devcontainers-contrib/features/pre-commit:2": {}
5
+ "ghcr.io/devcontainers-extra/features/pre-commit:2": {}
6
6
  },
7
7
  "onCreateCommand": "./.devcontainer/onCreateCommand.sh",
8
8
  "postAttachCommand": "./.devcontainer/postAttachCommand.sh",
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
3
  # Convenience alias for this project
4
- echo 'alias dvo="cargo run --"' >> ~/.bashrc
4
+ echo 'alias devobs="cargo run --"' >> ~/.bashrc
5
5
 
6
6
  make install
@@ -15,7 +15,7 @@ jobs:
15
15
  timeout-minutes: 10
16
16
  steps:
17
17
  - name: Checkout
18
- uses: actions/checkout@v4
18
+ uses: actions/checkout@v5
19
19
 
20
20
  - name: Set up Rust
21
21
  run: rustup show
@@ -38,7 +38,7 @@ jobs:
38
38
  id-token: write
39
39
  steps:
40
40
  - name: Checkout
41
- uses: actions/checkout@v4
41
+ uses: actions/checkout@v5
42
42
 
43
43
  - name: Set up Rust
44
44
  run: rustup show
@@ -23,7 +23,7 @@ jobs:
23
23
  timeout-minutes: 15
24
24
  steps:
25
25
  - name: Checkout
26
- uses: actions/checkout@v4
26
+ uses: actions/checkout@v5
27
27
 
28
28
  - name: Build wheels
29
29
  uses: PyO3/maturin-action@v1
@@ -42,7 +42,7 @@ jobs:
42
42
  timeout-minutes: 15
43
43
  steps:
44
44
  - name: Checkout
45
- uses: actions/checkout@v4
45
+ uses: actions/checkout@v5
46
46
 
47
47
  - name: Build sdist
48
48
  uses: PyO3/maturin-action@v1
@@ -68,7 +68,7 @@ jobs:
68
68
  contents: write
69
69
  steps:
70
70
  - name: Download binary artifacts
71
- uses: actions/download-artifact@v4
71
+ uses: actions/download-artifact@v5
72
72
  with:
73
73
  path: dist
74
74
 
@@ -91,7 +91,7 @@ jobs:
91
91
  id-token: write
92
92
  steps:
93
93
  - name: Download binary artifacts
94
- uses: actions/download-artifact@v4
94
+ uses: actions/download-artifact@v5
95
95
  with:
96
96
  path: dist
97
97
  merge-multiple: true
@@ -11,6 +11,20 @@ repos:
11
11
  - id: trailing-whitespace
12
12
  - id: end-of-file-fixer
13
13
 
14
+ # TODO(lasuillard): For testing. Later need to be improved with config file support.
15
+ - repo: https://github.com/lasuillard/devobs
16
+ rev: v0.1.0
17
+ hooks:
18
+ # Require each commands to have a matching integration tests.
19
+ - id: devobs
20
+ pass_filenames: false
21
+ args:
22
+ - check-file-pair
23
+ - --from=src/commands
24
+ - --to=tests/commands
25
+ - --include='*.rs'
26
+ - --expect='{to}/{relative_from}/test_{filename}'
27
+
14
28
  - repo: local
15
29
  hooks:
16
30
  - id: cargo-fmt
@@ -2,21 +2,6 @@
2
2
  # It is not intended for manual editing.
3
3
  version = 4
4
4
 
5
- [[package]]
6
- name = "addr2line"
7
- version = "0.24.2"
8
- source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
10
- dependencies = [
11
- "gimli",
12
- ]
13
-
14
- [[package]]
15
- name = "adler2"
16
- version = "2.0.0"
17
- source = "registry+https://github.com/rust-lang/crates.io-index"
18
- checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19
-
20
5
  [[package]]
21
6
  name = "aho-corasick"
22
7
  version = "1.1.3"
@@ -110,21 +95,6 @@ version = "1.4.0"
110
95
  source = "registry+https://github.com/rust-lang/crates.io-index"
111
96
  checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
112
97
 
113
- [[package]]
114
- name = "backtrace"
115
- version = "0.3.75"
116
- source = "registry+https://github.com/rust-lang/crates.io-index"
117
- checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
118
- dependencies = [
119
- "addr2line",
120
- "cfg-if",
121
- "libc",
122
- "miniz_oxide",
123
- "object",
124
- "rustc-demangle",
125
- "windows-targets 0.52.6",
126
- ]
127
-
128
98
  [[package]]
129
99
  name = "base64"
130
100
  version = "0.22.1"
@@ -260,7 +230,7 @@ dependencies = [
260
230
 
261
231
  [[package]]
262
232
  name = "devobs"
263
- version = "0.1.0"
233
+ version = "0.2.0"
264
234
  dependencies = [
265
235
  "anyhow",
266
236
  "assert_cmd",
@@ -272,6 +242,7 @@ dependencies = [
272
242
  "regex",
273
243
  "reqwest",
274
244
  "rstest",
245
+ "serde",
275
246
  "simplelog",
276
247
  "strfmt",
277
248
  "sugars",
@@ -462,12 +433,6 @@ dependencies = [
462
433
  "wasi 0.14.2+wasi-0.2.4",
463
434
  ]
464
435
 
465
- [[package]]
466
- name = "gimli"
467
- version = "0.31.1"
468
- source = "registry+https://github.com/rust-lang/crates.io-index"
469
- checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
470
-
471
436
  [[package]]
472
437
  name = "glob"
473
438
  version = "0.3.2"
@@ -611,7 +576,7 @@ dependencies = [
611
576
  "hyper",
612
577
  "libc",
613
578
  "pin-project-lite",
614
- "socket2",
579
+ "socket2 0.5.9",
615
580
  "tokio",
616
581
  "tower-service",
617
582
  "tracing",
@@ -819,15 +784,6 @@ version = "0.3.17"
819
784
  source = "registry+https://github.com/rust-lang/crates.io-index"
820
785
  checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
821
786
 
822
- [[package]]
823
- name = "miniz_oxide"
824
- version = "0.8.8"
825
- source = "registry+https://github.com/rust-lang/crates.io-index"
826
- checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
827
- dependencies = [
828
- "adler2",
829
- ]
830
-
831
787
  [[package]]
832
788
  name = "mio"
833
789
  version = "1.0.4"
@@ -897,15 +853,6 @@ dependencies = [
897
853
  "libc",
898
854
  ]
899
855
 
900
- [[package]]
901
- name = "object"
902
- version = "0.36.7"
903
- source = "registry+https://github.com/rust-lang/crates.io-index"
904
- checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
905
- dependencies = [
906
- "memchr",
907
- ]
908
-
909
856
  [[package]]
910
857
  name = "once_cell"
911
858
  version = "1.21.3"
@@ -1095,9 +1042,9 @@ dependencies = [
1095
1042
 
1096
1043
  [[package]]
1097
1044
  name = "regex"
1098
- version = "1.11.1"
1045
+ version = "1.12.2"
1099
1046
  source = "registry+https://github.com/rust-lang/crates.io-index"
1100
- checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
1047
+ checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
1101
1048
  dependencies = [
1102
1049
  "aho-corasick",
1103
1050
  "memchr",
@@ -1107,9 +1054,9 @@ dependencies = [
1107
1054
 
1108
1055
  [[package]]
1109
1056
  name = "regex-automata"
1110
- version = "0.4.9"
1057
+ version = "0.4.13"
1111
1058
  source = "registry+https://github.com/rust-lang/crates.io-index"
1112
- checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
1059
+ checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
1113
1060
  dependencies = [
1114
1061
  "aho-corasick",
1115
1062
  "memchr",
@@ -1188,21 +1135,20 @@ dependencies = [
1188
1135
 
1189
1136
  [[package]]
1190
1137
  name = "rstest"
1191
- version = "0.25.0"
1138
+ version = "0.26.1"
1192
1139
  source = "registry+https://github.com/rust-lang/crates.io-index"
1193
- checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d"
1140
+ checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
1194
1141
  dependencies = [
1195
1142
  "futures-timer",
1196
1143
  "futures-util",
1197
1144
  "rstest_macros",
1198
- "rustc_version",
1199
1145
  ]
1200
1146
 
1201
1147
  [[package]]
1202
1148
  name = "rstest_macros"
1203
- version = "0.25.0"
1149
+ version = "0.26.1"
1204
1150
  source = "registry+https://github.com/rust-lang/crates.io-index"
1205
- checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746"
1151
+ checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
1206
1152
  dependencies = [
1207
1153
  "cfg-if",
1208
1154
  "glob",
@@ -1216,12 +1162,6 @@ dependencies = [
1216
1162
  "unicode-ident",
1217
1163
  ]
1218
1164
 
1219
- [[package]]
1220
- name = "rustc-demangle"
1221
- version = "0.1.24"
1222
- source = "registry+https://github.com/rust-lang/crates.io-index"
1223
- checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
1224
-
1225
1165
  [[package]]
1226
1166
  name = "rustc_version"
1227
1167
  version = "0.4.1"
@@ -1344,18 +1284,28 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
1344
1284
 
1345
1285
  [[package]]
1346
1286
  name = "serde"
1347
- version = "1.0.219"
1287
+ version = "1.0.228"
1348
1288
  source = "registry+https://github.com/rust-lang/crates.io-index"
1349
- checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
1289
+ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1290
+ dependencies = [
1291
+ "serde_core",
1292
+ "serde_derive",
1293
+ ]
1294
+
1295
+ [[package]]
1296
+ name = "serde_core"
1297
+ version = "1.0.228"
1298
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1299
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1350
1300
  dependencies = [
1351
1301
  "serde_derive",
1352
1302
  ]
1353
1303
 
1354
1304
  [[package]]
1355
1305
  name = "serde_derive"
1356
- version = "1.0.219"
1306
+ version = "1.0.228"
1357
1307
  source = "registry+https://github.com/rust-lang/crates.io-index"
1358
- checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
1308
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1359
1309
  dependencies = [
1360
1310
  "proc-macro2",
1361
1311
  "quote",
@@ -1443,6 +1393,16 @@ dependencies = [
1443
1393
  "windows-sys 0.52.0",
1444
1394
  ]
1445
1395
 
1396
+ [[package]]
1397
+ name = "socket2"
1398
+ version = "0.6.0"
1399
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1400
+ checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
1401
+ dependencies = [
1402
+ "libc",
1403
+ "windows-sys 0.59.0",
1404
+ ]
1405
+
1446
1406
  [[package]]
1447
1407
  name = "stable_deref_trait"
1448
1408
  version = "1.2.0"
@@ -1527,15 +1487,15 @@ dependencies = [
1527
1487
 
1528
1488
  [[package]]
1529
1489
  name = "tempfile"
1530
- version = "3.20.0"
1490
+ version = "3.23.0"
1531
1491
  source = "registry+https://github.com/rust-lang/crates.io-index"
1532
- checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
1492
+ checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
1533
1493
  dependencies = [
1534
1494
  "fastrand",
1535
1495
  "getrandom 0.3.3",
1536
1496
  "once_cell",
1537
1497
  "rustix",
1538
- "windows-sys 0.59.0",
1498
+ "windows-sys 0.61.2",
1539
1499
  ]
1540
1500
 
1541
1501
  [[package]]
@@ -1598,27 +1558,26 @@ dependencies = [
1598
1558
 
1599
1559
  [[package]]
1600
1560
  name = "tokio"
1601
- version = "1.45.1"
1561
+ version = "1.48.0"
1602
1562
  source = "registry+https://github.com/rust-lang/crates.io-index"
1603
- checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
1563
+ checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
1604
1564
  dependencies = [
1605
- "backtrace",
1606
1565
  "bytes",
1607
1566
  "libc",
1608
1567
  "mio",
1609
1568
  "parking_lot",
1610
1569
  "pin-project-lite",
1611
1570
  "signal-hook-registry",
1612
- "socket2",
1571
+ "socket2 0.6.0",
1613
1572
  "tokio-macros",
1614
- "windows-sys 0.52.0",
1573
+ "windows-sys 0.61.2",
1615
1574
  ]
1616
1575
 
1617
1576
  [[package]]
1618
1577
  name = "tokio-macros"
1619
- version = "2.5.0"
1578
+ version = "2.6.0"
1620
1579
  source = "registry+https://github.com/rust-lang/crates.io-index"
1621
- checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
1580
+ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
1622
1581
  dependencies = [
1623
1582
  "proc-macro2",
1624
1583
  "quote",
@@ -1897,6 +1856,12 @@ version = "0.1.1"
1897
1856
  source = "registry+https://github.com/rust-lang/crates.io-index"
1898
1857
  checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
1899
1858
 
1859
+ [[package]]
1860
+ name = "windows-link"
1861
+ version = "0.2.1"
1862
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1863
+ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
1864
+
1900
1865
  [[package]]
1901
1866
  name = "windows-registry"
1902
1867
  version = "0.4.0"
@@ -1914,7 +1879,7 @@ version = "0.3.4"
1914
1879
  source = "registry+https://github.com/rust-lang/crates.io-index"
1915
1880
  checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
1916
1881
  dependencies = [
1917
- "windows-link",
1882
+ "windows-link 0.1.1",
1918
1883
  ]
1919
1884
 
1920
1885
  [[package]]
@@ -1923,7 +1888,7 @@ version = "0.3.1"
1923
1888
  source = "registry+https://github.com/rust-lang/crates.io-index"
1924
1889
  checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
1925
1890
  dependencies = [
1926
- "windows-link",
1891
+ "windows-link 0.1.1",
1927
1892
  ]
1928
1893
 
1929
1894
  [[package]]
@@ -1944,6 +1909,15 @@ dependencies = [
1944
1909
  "windows-targets 0.52.6",
1945
1910
  ]
1946
1911
 
1912
+ [[package]]
1913
+ name = "windows-sys"
1914
+ version = "0.61.2"
1915
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1916
+ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
1917
+ dependencies = [
1918
+ "windows-link 0.2.1",
1919
+ ]
1920
+
1947
1921
  [[package]]
1948
1922
  name = "windows-targets"
1949
1923
  version = "0.52.6"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "devobs"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  edition = "2024"
5
5
  description = "CLI for obsessed developers."
6
6
  readme = "README.md"
@@ -12,17 +12,18 @@ license = "MIT"
12
12
  clap = { version = "4.5.38", features = ["derive"] }
13
13
  glob = "0.3.2"
14
14
  log = "0.4.27"
15
- regex = "1.11.1"
15
+ regex = "1.12.2"
16
16
  simplelog = "0.12.2"
17
17
  strfmt = "0.2.4"
18
- tokio = { version = "1.45.1", features = ["full"] }
18
+ tokio = { version = "1.48.0", features = ["full"] }
19
19
  anyhow = "1.0.86"
20
20
  sugars = "3.0.1"
21
+ serde = { version = "1.0.228", features = ["derive"] }
21
22
 
22
23
  [dev-dependencies]
23
24
  assert_cmd = "2.0.17"
24
25
  insta = "1.43.1"
25
26
  mockall = "0.13.0"
26
27
  reqwest = "0.12.5"
27
- rstest = "0.25.0"
28
- tempfile = "3.20.0"
28
+ rstest = "0.26.1"
29
+ tempfile = "3.23.0"
@@ -28,7 +28,8 @@ update: ## Update deps and tools
28
28
  .PHONY: update
29
29
 
30
30
  run: ## Run development application
31
- cargo watch -x 'run -- --log-level debug'
31
+ read -p "Enter command line arguments: " args
32
+ cargo watch --exec "run -- --log-level debug $$args"
32
33
  .PHONY: run
33
34
 
34
35
 
@@ -51,6 +52,5 @@ lint: ## Run linters
51
52
  test: ## Run tests
52
53
  cargo llvm-cov nextest --workspace --lcov --output-path lcov.info \
53
54
  && cargo llvm-cov report --summary-only
54
-
55
55
  cargo insta accept
56
56
  .PHONY: test
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devobs
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: CLI for obsessed developers.
5
5
  Home-Page: https://github.com/lasuillard/devobs
6
6
  Author: Yuchan Lee <lasuillard@gmail.com>
@@ -11,8 +11,8 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
11
11
  # devobs
12
12
 
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
- [![CI](https://github.com/lasuillard/devobs/actions/workflows/ci.yaml/badge.svg)](https://github.com/lasuillard/devobs/actions/workflows/ci.yaml)
15
- [![codecov](https://codecov.io/gh/lasuillard/devobs/graph/badge.svg?token=VlANvU6qUC)](https://codecov.io/gh/lasuillard/devobs)
14
+ [![codecov](https://codecov.io/gh/lasuillard-s/devobs/graph/badge.svg?token=VlANvU6qUC)](https://codecov.io/gh/lasuillard-s/devobs)
15
+ [![PyPI - Version](https://img.shields.io/pypi/v/devobs)](https://pypi.org/project/devobs/)
16
16
 
17
17
  CLI for obsessed developers.
18
18
 
devobs-0.2.0/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # devobs
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![codecov](https://codecov.io/gh/lasuillard-s/devobs/graph/badge.svg?token=VlANvU6qUC)](https://codecov.io/gh/lasuillard-s/devobs)
5
+ [![PyPI - Version](https://img.shields.io/pypi/v/devobs)](https://pypi.org/project/devobs/)
6
+
7
+ CLI for obsessed developers.
@@ -0,0 +1,158 @@
1
+ use std::{fs::File,
2
+ hash::{DefaultHasher, Hash, Hasher},
3
+ io::Read,
4
+ path::{PathBuf, absolute}};
5
+
6
+ use anyhow::{Result, bail};
7
+ use clap::{Args, ValueEnum};
8
+ use serde::Serialize;
9
+
10
+ use crate::{GlobalOpts, utils::fs::list_files};
11
+
12
+ const BUFFER_SIZE: usize = 8192;
13
+
14
+ #[derive(ValueEnum, Clone, Debug, Serialize, Default)]
15
+ #[serde(rename_all = "kebab-case")]
16
+ enum OnCommandError {
17
+ /// Exit the program with the original error
18
+ #[default]
19
+ Propagate,
20
+
21
+ /// Ignore the error and continue
22
+ Ignore,
23
+ }
24
+
25
+ // NOTE: This command does not support dry-run mode, as there is no state change involved (except hash file).
26
+ /// Detects changes in the target directory by comparing file hashes before and after running a command.
27
+ /// Raises an error if any changes are detected.
28
+ #[derive(Args, Debug, Clone)]
29
+ pub(crate) struct CommandArgs {
30
+ /// Target directory to watch for changes.
31
+ #[arg(long)]
32
+ target: String,
33
+
34
+ /// List of glob patterns to include files from the `target` directory.
35
+ ///
36
+ /// This option can be specified multiple times or as a comma-separated list.
37
+ #[arg(long, num_args = 1.., value_delimiter = ',', default_value = "**/*")]
38
+ include: Vec<String>,
39
+
40
+ /// List of glob patterns to exclude files from the `target` directory.
41
+ ///
42
+ /// This option can be specified multiple times or as a comma-separated list.
43
+ #[arg(long, num_args = 1.., value_delimiter = ',')]
44
+ exclude: Vec<String>,
45
+
46
+ /// Error handling strategy for the command.
47
+ #[arg(long, default_value_t, value_enum)]
48
+ on_command_error: OnCommandError,
49
+
50
+ /// Command to run. First hash is computed before running the command, second hash after.
51
+ /// If the hashes differ, an error is raised.
52
+ #[arg(trailing_var_arg = true)]
53
+ command: Vec<String>,
54
+ }
55
+
56
+ pub(crate) fn command(args: CommandArgs, _global_opts: GlobalOpts) -> Result<()> {
57
+ // Prepare arguments
58
+ let target = absolute(PathBuf::from(&args.target))?;
59
+ if !target.exists() {
60
+ bail!("Target path does not exist: {}", target.display());
61
+ }
62
+ if args.command.is_empty() {
63
+ bail!("No command specified to run.");
64
+ }
65
+
66
+ // Calculate hash
67
+ log::debug!("Calculating hash for: {}", target.display());
68
+ let before_hash = calculate_directory_hash(&target, &args.include, &args.exclude)?;
69
+ log::info!("Hash before command run: {}", before_hash);
70
+
71
+ // Run command
72
+ log::info!("Running command as child process: {:?}", args.command);
73
+ let mut child = std::process::Command::new(&args.command[0])
74
+ .args(&args.command[1..])
75
+ .spawn()?;
76
+
77
+ let status = child.wait()?;
78
+ log::debug!("Command exited with status: {:?}", status);
79
+
80
+ // Check for exit code
81
+ if !status.success() {
82
+ match args.on_command_error {
83
+ OnCommandError::Ignore => {
84
+ log::warn!(
85
+ "Command exited with non-zero status: {}, but ignoring as per configuration.",
86
+ status
87
+ );
88
+ }
89
+ OnCommandError::Propagate => {
90
+ if let Some(code) = status.code() {
91
+ log::warn!(
92
+ "Command exited with non-zero status: {}, propagating exit code.",
93
+ code
94
+ );
95
+ std::process::exit(code);
96
+ } else {
97
+ bail!("Command terminated by signal");
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ // Calculate hash again
104
+ let after_hash = calculate_directory_hash(&target, &args.include, &args.exclude)?;
105
+ log::info!("Hash after command run: {}", after_hash);
106
+
107
+ // Compare hashes
108
+ if before_hash != after_hash {
109
+ bail!(
110
+ "Hash has changed after running command: {} != {}",
111
+ before_hash,
112
+ after_hash
113
+ );
114
+ }
115
+
116
+ // No changes detected
117
+ log::info!("Target hash matches, no changes detected.");
118
+ Ok(())
119
+ }
120
+
121
+ // NOTE: There is more performant library [merkle_hash](https://github.com/hristogochev/merkle_hash) exists,
122
+ // but using our version here for more control over hashing process (hasher, include/exclude patterns, etc.)
123
+ // TODO(lasuillard): `DefaultHasher` may change between Rust versions, consider replacing it with more stable hasher
124
+ // IF speed becomes an issue, for large file handling (BLAKE3 or xxHash)
125
+ fn calculate_directory_hash(
126
+ path: &PathBuf,
127
+ include: &[String],
128
+ exclude: &[String],
129
+ ) -> Result<String> {
130
+ log::debug!(
131
+ "Calculating hash for directory: {}; include: {:?}, exclude: {:?}",
132
+ path.display(),
133
+ include,
134
+ exclude
135
+ );
136
+ let mut hasher = DefaultHasher::new();
137
+ let mut buffer = [0; BUFFER_SIZE];
138
+ for path in list_files(&path, &include, &exclude) {
139
+ // ? Should take account directory structure in the hash?
140
+ if path.is_dir() {
141
+ log::debug!("Skipping directory: {}", path.display());
142
+ continue;
143
+ }
144
+
145
+ log::debug!("Calculating hash for file: {}", path.display());
146
+ let mut file = File::open(path)?;
147
+ loop {
148
+ let bytes_read = file.read(&mut buffer)?;
149
+ if bytes_read == 0 {
150
+ break;
151
+ }
152
+ buffer[..bytes_read].hash(&mut hasher);
153
+ }
154
+ }
155
+ let hash = hasher.finish();
156
+ let hash_as_hex = format!("{:x}", hash);
157
+ Ok(hash_as_hex)
158
+ }
@@ -12,7 +12,7 @@ use crate::{GlobalOpts,
12
12
 
13
13
  /// Check for matching file exists.
14
14
  #[derive(Args, Debug, Clone)]
15
- pub struct CommandArgs {
15
+ pub(crate) struct CommandArgs {
16
16
  /// Directory to check for matching files.
17
17
  #[arg(long)]
18
18
  from: String,
@@ -33,6 +33,7 @@ pub struct CommandArgs {
33
33
  #[arg(long, num_args = 1.., value_delimiter = ',')]
34
34
  exclude: Vec<String>,
35
35
 
36
+ // * Don't forget to update below doc when modifying available variables
36
37
  /// Expected pattern for the file in the `to` directory.
37
38
  ///
38
39
  /// Variables available for substitution:
@@ -1 +1,2 @@
1
+ pub(crate) mod assert_diff;
1
2
  pub(crate) mod check_file_pair;