bt-dualboot-sync 0.1.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 (151) hide show
  1. bt_dualboot_sync-0.1.0/.dockerignore +1 -0
  2. bt_dualboot_sync-0.1.0/.github/dependabot.yml +23 -0
  3. bt_dualboot_sync-0.1.0/.github/workflows/build-dist.yml +55 -0
  4. bt_dualboot_sync-0.1.0/.github/workflows/linter.yml +88 -0
  5. bt_dualboot_sync-0.1.0/.github/workflows/publish-pypi.yml +34 -0
  6. bt_dualboot_sync-0.1.0/.github/workflows/release.yml +157 -0
  7. bt_dualboot_sync-0.1.0/.gitignore +11 -0
  8. bt_dualboot_sync-0.1.0/.kacl.yml +13 -0
  9. bt_dualboot_sync-0.1.0/.python-version +1 -0
  10. bt_dualboot_sync-0.1.0/CHANGELOG.md +33 -0
  11. bt_dualboot_sync-0.1.0/LICENSE +22 -0
  12. bt_dualboot_sync-0.1.0/PKG-INFO +79 -0
  13. bt_dualboot_sync-0.1.0/README-dev.md +387 -0
  14. bt_dualboot_sync-0.1.0/README.md +244 -0
  15. bt_dualboot_sync-0.1.0/bt_dualboot/README.md +49 -0
  16. bt_dualboot_sync-0.1.0/bt_dualboot/__init__.py +4 -0
  17. bt_dualboot_sync-0.1.0/bt_dualboot/__main__.py +4 -0
  18. bt_dualboot_sync-0.1.0/bt_dualboot/__meta__.py +5 -0
  19. bt_dualboot_sync-0.1.0/bt_dualboot/bluetooth_device.py +120 -0
  20. bt_dualboot_sync-0.1.0/bt_dualboot/bt_linux/__init__.py +0 -0
  21. bt_dualboot_sync-0.1.0/bt_dualboot/bt_linux/bluetooth_device_factory.py +76 -0
  22. bt_dualboot_sync-0.1.0/bt_dualboot/bt_linux/devices.py +41 -0
  23. bt_dualboot_sync-0.1.0/bt_dualboot/bt_sync_manager/__init__.py +1 -0
  24. bt_dualboot_sync-0.1.0/bt_dualboot/bt_sync_manager/bt_sync_manager.py +255 -0
  25. bt_dualboot_sync-0.1.0/bt_dualboot/bt_windows/__init__.py +0 -0
  26. bt_dualboot_sync-0.1.0/bt_dualboot/bt_windows/convert.py +142 -0
  27. bt_dualboot_sync-0.1.0/bt_dualboot/bt_windows/devices.py +95 -0
  28. bt_dualboot_sync-0.1.0/bt_dualboot/cli/__init__.py +0 -0
  29. bt_dualboot_sync-0.1.0/bt_dualboot/cli/__main__.py +4 -0
  30. bt_dualboot_sync-0.1.0/bt_dualboot/cli/app.py +350 -0
  31. bt_dualboot_sync-0.1.0/bt_dualboot/cli/tools.py +112 -0
  32. bt_dualboot_sync-0.1.0/bt_dualboot/win_mount.py +27 -0
  33. bt_dualboot_sync-0.1.0/bt_dualboot/windows_registry/__init__.py +1 -0
  34. bt_dualboot_sync-0.1.0/bt_dualboot/windows_registry/windows_registry.py +203 -0
  35. bt_dualboot_sync-0.1.0/dev/bootstrap +4 -0
  36. bt_dualboot_sync-0.1.0/dev/git-diffadd.sh +23 -0
  37. bt_dualboot_sync-0.1.0/dev/helpers.sh +165 -0
  38. bt_dualboot_sync-0.1.0/dev/lists-intersection +33 -0
  39. bt_dualboot_sync-0.1.0/dev/pre-release/lint-all +7 -0
  40. bt_dualboot_sync-0.1.0/dev/pre-release/lint-autopep8 +7 -0
  41. bt_dualboot_sync-0.1.0/dev/pre-release/lint-black +4 -0
  42. bt_dualboot_sync-0.1.0/dev/pre-release/lint-flake8 +14 -0
  43. bt_dualboot_sync-0.1.0/dev/pre-release/update-version +45 -0
  44. bt_dualboot_sync-0.1.0/dev/pre-release-all +11 -0
  45. bt_dualboot_sync-0.1.0/dev/start-tests +7 -0
  46. bt_dualboot_sync-0.1.0/dev/start-tests-all +4 -0
  47. bt_dualboot_sync-0.1.0/dev/start-tests-bug +3 -0
  48. bt_dualboot_sync-0.1.0/dev/start-tests-integration +140 -0
  49. bt_dualboot_sync-0.1.0/dev/start-tests-manual +5 -0
  50. bt_dualboot_sync-0.1.0/dev/start-windows-vm +253 -0
  51. bt_dualboot_sync-0.1.0/docker/add-user +11 -0
  52. bt_dualboot_sync-0.1.0/docker/devpi-bootstrap +17 -0
  53. bt_dualboot_sync-0.1.0/docker/devpi-stop +5 -0
  54. bt_dualboot_sync-0.1.0/docker/prebuild-dependencies +9 -0
  55. bt_dualboot_sync-0.1.0/docker/wait-http +9 -0
  56. bt_dualboot_sync-0.1.0/pyproject.toml +108 -0
  57. bt_dualboot_sync-0.1.0/tests/__init__.py +0 -0
  58. bt_dualboot_sync-0.1.0/tests/bt_linux/__init__.py +0 -0
  59. bt_dualboot_sync-0.1.0/tests/bt_linux/bluetooth_device_factory_test.py +63 -0
  60. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/22:94:90:56:EE:38/info +7 -0
  61. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/A4:BF:C6:D0:E5:FF/info +17 -0
  62. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/B6:C2:D3:E5:F2:0D/info +12 -0
  63. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/C2:9E:1D:E2:3D:A5/info +12 -0
  64. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/A4:6B:6C:9D:E2:FB/D1:8A:4E:71:5D:C1/info +12 -0
  65. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/B4:6B:6C:9D:E2:FB/A4:80:1D:C5:4F:7E/info +18 -0
  66. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/B4:6B:6C:9D:E2:FB/B8:94:A5:FD:F1:0A/info +12 -0
  67. bt_dualboot_sync-0.1.0/tests/bt_linux/data_samples/bt_sample_01/B4:6B:6C:9D:E2:FB/C4:72:B3:6F:82:42/info +12 -0
  68. bt_dualboot_sync-0.1.0/tests/bt_linux/devices_test.py +56 -0
  69. bt_dualboot_sync-0.1.0/tests/bt_linux/shared_fixtures.py +22 -0
  70. bt_dualboot_sync-0.1.0/tests/bt_sync_manager/__init__.py +0 -0
  71. bt_dualboot_sync-0.1.0/tests/bt_sync_manager/bt_sync_manager_test.py +125 -0
  72. bt_dualboot_sync-0.1.0/tests/bt_windows/__init__.py +0 -0
  73. bt_dualboot_sync-0.1.0/tests/bt_windows/convert_test.py +57 -0
  74. bt_dualboot_sync-0.1.0/tests/bt_windows/devices_test.py +29 -0
  75. bt_dualboot_sync-0.1.0/tests/bt_windows/shared_fixtures.py +97 -0
  76. bt_dualboot_sync-0.1.0/tests/cli/snapshots/tools_test/test_none/stdout +5 -0
  77. bt_dualboot_sync-0.1.0/tests/cli/snapshots/tools_test/test_two_devices/stdout +8 -0
  78. bt_dualboot_sync-0.1.0/tests/cli/tools_test.py +62 -0
  79. bt_dualboot_sync-0.1.0/tests/helpers.py +5 -0
  80. bt_dualboot_sync-0.1.0/tests/windows_registry/__init__.py +0 -0
  81. bt_dualboot_sync-0.1.0/tests/windows_registry/data_samples/SYSTEM_BLANK +0 -0
  82. bt_dualboot_sync-0.1.0/tests/windows_registry/data_samples/export_sample_01.reg +18 -0
  83. bt_dualboot_sync-0.1.0/tests/windows_registry/shared_fixtures.py +42 -0
  84. bt_dualboot_sync-0.1.0/tests/windows_registry/windows_registry_test.py +80 -0
  85. bt_dualboot_sync-0.1.0/tests_integration/__init__.py +1 -0
  86. bt_dualboot_sync-0.1.0/tests_integration/cli/__init__.py +0 -0
  87. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/Dockerfile +12 -0
  88. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/__init__.py +0 -0
  89. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/snapshots/test_cli/test_list/output +16 -0
  90. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/snapshots/test_cli/test_no_args/output +43 -0
  91. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/snapshots/test_cli/test_version/output +11 -0
  92. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/start +9 -0
  93. bt_dualboot_sync-0.1.0/tests_integration/cli/env_blank_linux/test_cli.py +48 -0
  94. bt_dualboot_sync-0.1.0/tests_integration/cli/env_no_windows/__init__.py +0 -0
  95. bt_dualboot_sync-0.1.0/tests_integration/cli/env_no_windows/start +3 -0
  96. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/Dockerfile +9 -0
  97. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/Dockerfile.base +24 -0
  98. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/Dockerfile.build_pip +26 -0
  99. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/__init__.py +0 -0
  100. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/opts-Dockerfile +1 -0
  101. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/opts-Dockerfile.base +1 -0
  102. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/opts-Dockerfile.build_pip +3 -0
  103. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test__when_no_devices__sync_single/output +12 -0
  104. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_backup/output +12 -0
  105. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_backup_default/output +12 -0
  106. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_case_insensive/output +11 -0
  107. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_missing_mac/output +14 -0
  108. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_multipe_macs/output +11 -0
  109. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_require_backup/output +28 -0
  110. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_single_mac/output +11 -0
  111. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_valid_and_wrong_mac/output +12 -0
  112. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_wrong_mac/output +12 -0
  113. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSync/test_wrong_separator/output +14 -0
  114. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test__when_no_devices__sync_all/output +11 -0
  115. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test_backup/output +17 -0
  116. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test_backup_default/output +17 -0
  117. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test_require_backup/output +28 -0
  118. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test_sync_all/output +16 -0
  119. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test_sync_all_and_sync/output +14 -0
  120. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAll/test_sync_all_bot/output +13 -0
  121. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test__when_no_devices__sync_all/output +17 -0
  122. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test_backup/output +18 -0
  123. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test_backup_default/output +18 -0
  124. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test_require_backup/output +28 -0
  125. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test_sync_all/output +17 -0
  126. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test_sync_all_and_sync/output +14 -0
  127. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncAllDryRun/test_sync_all_bot/output +14 -0
  128. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test__when_no_devices__sync_single/output +12 -0
  129. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_backup/output +13 -0
  130. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_backup_default/output +13 -0
  131. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_case_insensive/output +12 -0
  132. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_missing_mac/output +14 -0
  133. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_multipe_macs/output +12 -0
  134. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_require_backup/output +28 -0
  135. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_single_mac/output +12 -0
  136. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_valid_and_wrong_mac/output +12 -0
  137. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_wrong_mac/output +12 -0
  138. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/TestSyncDryRun/test_wrong_separator/output +14 -0
  139. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_backup_without_sync/output +14 -0
  140. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_list/output +12 -0
  141. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_list_as_module_bot_sudo/output +18 -0
  142. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_list_bot_sudo/output +18 -0
  143. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_list_sudo/output +39 -0
  144. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_no_args/output +37 -0
  145. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_no_args_but_win/output +14 -0
  146. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_no_backup_and_backup/output +14 -0
  147. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/snapshots/test_cli/test_no_backup_without_sync/output +14 -0
  148. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/start +8 -0
  149. bt_dualboot_sync-0.1.0/tests_integration/cli/env_single_windows/test_cli.py +605 -0
  150. bt_dualboot_sync-0.1.0/tests_integration/helpers.py +173 -0
  151. bt_dualboot_sync-0.1.0/uv.lock +191 -0
@@ -0,0 +1 @@
1
+ dist/
@@ -0,0 +1,23 @@
1
+ version: 2
2
+ updates:
3
+ # Python dependencies
4
+ - package-ecosystem: "uv"
5
+ directory: "/"
6
+ target-branch: "dev"
7
+ schedule:
8
+ interval: "weekly"
9
+ open-pull-requests-limit: 5
10
+ groups:
11
+ python:
12
+ patterns: ["*"]
13
+
14
+ # GitHub Actions
15
+ - package-ecosystem: "github-actions"
16
+ directory: "/"
17
+ target-branch: "dev"
18
+ schedule:
19
+ interval: "weekly"
20
+ open-pull-requests-limit: 5
21
+ groups:
22
+ actions:
23
+ patterns: ["*"]
@@ -0,0 +1,55 @@
1
+ name: Build Release Distributions
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ verify:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+
17
+ - name: Check tag against project.version
18
+ if: github.event_name == 'release'
19
+ env:
20
+ TAG: ${{ github.event.release.tag_name }}
21
+ run: |
22
+ set -euo pipefail
23
+ VERSION="$(python3 -c 'import pathlib, tomllib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
24
+ if [ "$TAG" != "v${VERSION}" ]; then
25
+ echo "::error::release tag '$TAG' does not match project.version '$VERSION' in pyproject.toml"
26
+ exit 1
27
+ fi
28
+
29
+ build:
30
+ needs: [verify]
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v7
34
+
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v7
37
+ with:
38
+ python-version: "3.11"
39
+
40
+ - name: Build sdist and wheel
41
+ run: uv build --out-dir dist
42
+
43
+ - name: Check distribution metadata
44
+ run: uvx twine check dist/*
45
+
46
+ - uses: actions/upload-artifact@v7
47
+ with:
48
+ name: dist
49
+ path: dist/*
50
+
51
+ - name: Attach distributions to the GitHub Release
52
+ if: github.event_name == 'release'
53
+ env:
54
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
55
+ run: gh release upload "${{ github.event.release.tag_name }}" dist/* --clobber --repo "${{ github.repository }}"
@@ -0,0 +1,88 @@
1
+ name: Lint & Test
2
+
3
+ on:
4
+ push:
5
+ branches: [dev]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ lint:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v7
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v7
21
+ with:
22
+ python-version: "3.11"
23
+ enable-cache: true
24
+ cache-dependency-glob: "uv.lock"
25
+
26
+ - name: Install dev dependencies
27
+ run: uv sync --group dev
28
+
29
+ - name: ruff (lint)
30
+ run: uv run -- ruff check --output-format github bt_dualboot/
31
+
32
+ - name: ruff (format)
33
+ run: uv run -- ruff format --check --diff bt_dualboot/
34
+
35
+ test:
36
+ runs-on: ubuntu-latest
37
+ strategy:
38
+ fail-fast: false
39
+ matrix:
40
+ python: ["3.11", "3.12", "3.13"]
41
+ steps:
42
+ - name: Checkout repository
43
+ uses: actions/checkout@v7
44
+
45
+ - name: Install uv
46
+ uses: astral-sh/setup-uv@v7
47
+ with:
48
+ python-version: ${{ matrix.python }}
49
+ enable-cache: true
50
+ cache-dependency-glob: "uv.lock"
51
+
52
+ - name: Install chntpw
53
+ run: sudo apt-get update && sudo apt-get install -y --no-install-recommends chntpw
54
+
55
+ - name: pytest
56
+ run: uv run --python ${{ matrix.python }} -- pytest tests/
57
+
58
+ changelog:
59
+ runs-on: ubuntu-latest
60
+ steps:
61
+ - name: Checkout repository
62
+ uses: actions/checkout@v7
63
+
64
+ - name: Install uv
65
+ uses: astral-sh/setup-uv@v7
66
+
67
+ - name: Verify CHANGELOG.md
68
+ run: uvx --from python-kacl==0.7.3 kacl-cli verify
69
+
70
+ - name: Require changelog notes if this PR bumps the version
71
+ if: github.event_name == 'pull_request'
72
+ run: |
73
+ set -euo pipefail
74
+ git fetch origin main
75
+ BASE_VERSION="$(git show origin/main:pyproject.toml | python3 -c 'import sys, tomllib; print(tomllib.load(sys.stdin.buffer)["project"]["version"])')"
76
+ HEAD_VERSION="$(python3 -c 'import pathlib, tomllib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
77
+ if [ "$BASE_VERSION" = "$HEAD_VERSION" ]; then
78
+ echo "Version unchanged ($HEAD_VERSION); this PR won't trigger a release."
79
+ exit 0
80
+ fi
81
+ echo "Version bumped $BASE_VERSION -> $HEAD_VERSION; checking for release notes..."
82
+ # kacl-cli's 'get' errors out (rather than printing nothing) when the
83
+ # section has no subsections at all, so treat any failure as "empty".
84
+ if ! NOTES="$(uvx --from python-kacl==0.7.3 kacl-cli get unreleased --no-header 2>/dev/null)" || [ -z "$NOTES" ]; then
85
+ echo "::error::pyproject.toml is bumped to ${HEAD_VERSION}, but CHANGELOG.md has no content under '## [Unreleased]'. Merging as-is will bump the version with an empty changelog and fail release.yml after merge. Add release notes under '## [Unreleased]' first."
86
+ exit 1
87
+ fi
88
+ echo "Notes found under [Unreleased]."
@@ -0,0 +1,34 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Build Release Distributions"]
6
+ types: [completed]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ publish:
13
+ # Only for successful runs triggered by a published release.
14
+ if: >
15
+ github.event.workflow_run.conclusion == 'success' &&
16
+ github.event.workflow_run.event == 'release'
17
+ runs-on: ubuntu-latest
18
+ environment: pypi
19
+ permissions:
20
+ id-token: write
21
+ steps:
22
+ - name: Download build artifacts
23
+ uses: actions/download-artifact@v8
24
+ with:
25
+ run-id: ${{ github.event.workflow_run.id }}
26
+ github-token: ${{ secrets.GITHUB_TOKEN }}
27
+ path: dist
28
+ pattern: dist
29
+ merge-multiple: true
30
+
31
+ - name: Publish
32
+ uses: pypa/gh-action-pypi-publish@release/v1
33
+ with:
34
+ packages-dir: dist
@@ -0,0 +1,157 @@
1
+ name: Release
2
+
3
+ # Fires automatically whenever a merge to main bumps pyproject.toml's version:
4
+ #
5
+ # read VERSION from pyproject.toml -> promote CHANGELOG.md's [Unreleased] section
6
+ # -> sync bt_dualboot/__meta__.py -> commit + tag on main -> DRAFT the release
7
+ # -> sync the promotion back onto dev
8
+
9
+
10
+ on:
11
+ push:
12
+ branches: [main]
13
+ paths:
14
+ - 'pyproject.toml'
15
+
16
+ permissions:
17
+ contents: write
18
+
19
+ concurrency:
20
+ group: release
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ release:
25
+ runs-on: ubuntu-latest
26
+ permissions:
27
+ contents: write
28
+ pull-requests: write
29
+ issues: write
30
+ steps:
31
+ - uses: actions/checkout@v7
32
+ with:
33
+ ref: main
34
+ fetch-depth: 0
35
+
36
+ - name: Install uv
37
+ uses: astral-sh/setup-uv@v7
38
+
39
+ - name: Read the version from pyproject.toml
40
+ id: version
41
+ run: |
42
+ set -euo pipefail
43
+ VERSION="$(python3 -c 'import pathlib, tomllib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
44
+ case "$VERSION" in
45
+ [0-9]*) ;;
46
+ *) echo "::error::Version '$VERSION' in pyproject.toml must start with a digit."; exit 1 ;;
47
+ esac
48
+ {
49
+ echo "version=$VERSION"
50
+ echo "tag=v$VERSION"
51
+ } >> "$GITHUB_OUTPUT"
52
+
53
+ - name: Skip if this version is already released
54
+ id: gate
55
+ env:
56
+ TAG: ${{ steps.version.outputs.tag }}
57
+ run: |
58
+ set -euo pipefail
59
+ if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
60
+ echo "::notice::${TAG} is already tagged - this push didn't bump the version (or it shipped already). Nothing to do."
61
+ echo "skip=true" >> "$GITHUB_OUTPUT"
62
+ else
63
+ echo "skip=false" >> "$GITHUB_OUTPUT"
64
+ fi
65
+
66
+ - name: Require notes under [Unreleased]
67
+ if: steps.gate.outputs.skip != 'true'
68
+ run: |
69
+ set -euo pipefail
70
+ # kacl-cli's 'get' errors out (rather than printing nothing) when the
71
+ # section has no subsections at all, so treat any failure as "empty".
72
+ if ! NOTES="$(uvx --from python-kacl==0.7.3 kacl-cli get unreleased --no-header 2>/dev/null)" || [ -z "$NOTES" ]; then
73
+ echo "::error::pyproject.toml was bumped to ${{ steps.version.outputs.version }}, but CHANGELOG.md has no content under '## [Unreleased]'. Add the release notes, then push a follow-up commit to main."
74
+ exit 1
75
+ fi
76
+ echo "Notes found under [Unreleased]:"
77
+ echo "$NOTES"
78
+
79
+ - name: Promote [Unreleased] to [${{ steps.version.outputs.version }}] in CHANGELOG.md
80
+ if: steps.gate.outputs.skip != 'true'
81
+ env:
82
+ VERSION: ${{ steps.version.outputs.version }}
83
+ run: uvx --from python-kacl==0.7.3 kacl-cli release "$VERSION" --modify --auto-link
84
+
85
+ - name: Sync bt_dualboot/__meta__.py with pyproject.toml
86
+ if: steps.gate.outputs.skip != 'true'
87
+ run: python3 dev/pre-release/update-version
88
+
89
+ - name: Commit and tag
90
+ if: steps.gate.outputs.skip != 'true'
91
+ id: commit
92
+ env:
93
+ VERSION: ${{ steps.version.outputs.version }}
94
+ TAG: ${{ steps.version.outputs.tag }}
95
+ run: |
96
+ set -euo pipefail
97
+ git config user.name "github-actions[bot]"
98
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
99
+ # Not pyproject.toml: it is already correct from the merge that triggered
100
+ # this run, and this push must not touch it, or the path filter above would
101
+ # re-trigger this workflow on the commit below.
102
+ git add CHANGELOG.md bt_dualboot/__meta__.py
103
+ if git diff --cached --quiet; then
104
+ echo "::error::Nothing changed; is CHANGELOG.md already at ${VERSION}?"
105
+ exit 1
106
+ fi
107
+ git commit -m "docs(changelog): promote to ${TAG}"
108
+ git push origin HEAD:main
109
+ git tag -a "$TAG" -m "bt-dualboot-sync ${VERSION}"
110
+ git push origin "refs/tags/${TAG}"
111
+ echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
112
+
113
+ - name: Draft the release from CHANGELOG.md
114
+ if: steps.gate.outputs.skip != 'true'
115
+ env:
116
+ GH_TOKEN: ${{ github.token }}
117
+ VERSION: ${{ steps.version.outputs.version }}
118
+ TAG: ${{ steps.version.outputs.tag }}
119
+ run: |
120
+ set -euo pipefail
121
+ uvx --from python-kacl==0.7.3 kacl-cli get "$VERSION" --no-header > /tmp/notes.md
122
+ gh release create "$TAG" \
123
+ --draft \
124
+ --title "$TAG" \
125
+ --notes-file /tmp/notes.md \
126
+ --target "${{ steps.commit.outputs.sha }}"
127
+ url="$(gh release view "$TAG" --json url --jq .url)"
128
+ {
129
+ echo "### Draft release \`${TAG}\` is ready"
130
+ } >> "$GITHUB_STEP_SUMMARY"
131
+
132
+ - name: Sync the release commit back to dev
133
+ if: steps.gate.outputs.skip != 'true'
134
+ env:
135
+ GH_TOKEN: ${{ github.token }}
136
+ TAG: ${{ steps.version.outputs.tag }}
137
+ run: |
138
+ set -euo pipefail
139
+ git fetch origin dev:refs/remotes/origin/dev
140
+ BRANCH="sync-main-${TAG}"
141
+ git checkout -b "$BRANCH" origin/dev
142
+ git merge --no-edit origin/main
143
+
144
+ if [ "$(git rev-parse HEAD)" = "$(git rev-parse origin/dev)" ]; then
145
+ echo "dev is already up to date with main; nothing to sync."
146
+ exit 0
147
+ fi
148
+
149
+ git push --set-upstream origin "$BRANCH"
150
+
151
+ gh label create "release changelog" --description "Pull requests with a release changelog update" --force || true
152
+ gh pr create \
153
+ --title "Sync ${TAG} back to dev" \
154
+ --body "Merges the \`${TAG}\` version bump and changelog promotion from \`main\` back into \`dev\`, so \`dev\` picks up a fresh \`[Unreleased]\` section." \
155
+ --label "release changelog" \
156
+ --base dev \
157
+ --head "$BRANCH"
@@ -0,0 +1,11 @@
1
+ # Python
2
+ __pycache__
3
+ dist/
4
+ .venv/
5
+
6
+ # OS files
7
+ .DS_Store
8
+
9
+ # IDE files
10
+ .vscode/
11
+ .idea/
@@ -0,0 +1,13 @@
1
+ kacl:
2
+ file: CHANGELOG.md
3
+
4
+ links:
5
+ auto_generate: True
6
+ host_url: https://github.com/vhrabar/bt-dualboot
7
+ compare_versions_template: '{host}/compare/v{previous_version}...v{version}'
8
+ unreleased_changes_template: '{host}/compare/v{latest_version}...HEAD'
9
+ initial_version_template: '{host}/releases/tag/v{version}'
10
+
11
+ git:
12
+ commit: False
13
+ tag: False
@@ -0,0 +1 @@
1
+ 3.11.15
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-09-12
10
+
11
+ ### Added
12
+
13
+ - The first release of this fork of [x2es/bt-dualboot](https://github.com/x2es/bt-dualboot): it carries the community fixes that were merged upstream but never published, and modernises packaging, tooling and CI around `uv` package manager.
14
+ - Published to PyPI as `bt-dualboot-sync` (the command is still `bt-dualboot`).
15
+ - Bluetooth 5.1 / LE device support: LTK, ERand and EDIV keys are read from the Linux `info` file and written to `ControlSet001\Services\BTHPORT\Parameters\Keys` in the Windows registry. Contributed by [@Simon128](https://github.com/Simon128); never released upstream.
16
+ - `--list` reports a *Missing pairing key* group for devices that have no key to sync, instead of leaving them unexplained. Contributed by [@asarium](https://github.com/asarium) in [#1](https://github.com/vhrabar/bt-dualboot/pull/1); never released upstream.
17
+ - `dev/start-windows-vm`, which boots the installed Windows partition in a QEMU VM with the Bluetooth adapter passed through, so a sync can be verified without rebooting. Autodetects the Windows disk and the Bluetooth USB device, with manual overrides.
18
+
19
+ ### Changed
20
+
21
+ - Requires Python 3.11 or newer; tested against 3.11, 3.12 and 3.13.
22
+ - Packaging moved from Poetry to `uv` with the `hatchling` build backend; `uv.lock` replaces `poetry.lock`.
23
+ - Docker images and the `dev/` scripts install and run the project through `uv`.
24
+
25
+ ### Fixed
26
+
27
+ - No longer crashes on devices without a `LinkKey` in the Windows registry. Contributed by [@asarium](https://github.com/asarium) in [#1](https://github.com/vhrabar/bt-dualboot/pull/1); never released upstream.
28
+ - `[General]/Class=` in `/var/lib/bluetooth/ADAPTER_MAC/DEVICE_MAC/info` is treated as optional, instead of failing when it is absent. Fixed upstream by [@x2es](https://github.com/x2es) in [x2es/bt-dualboot#4](https://github.com/x2es/bt-dualboot/pull/4); never released.
29
+ - The "paired for multiple BT-adapters" warnings print the device MACs again; they were literal text because of a missing f-string prefix. Fixed upstream by [@J3RN](https://github.com/J3RN) in [x2es/bt-dualboot#9](https://github.com/x2es/bt-dualboot/pull/9); never released.
30
+ - `raise ... from err` on the device-not-found path, so the underlying error is no longer reported as an unexpected exception during handling.
31
+
32
+ [Unreleased]: https://github.com/vhrabar/bt-dualboot/compare/v0.1.0...HEAD
33
+ [0.1.0]: https://github.com/vhrabar/bt-dualboot/releases/tag/v0.1.0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Konstantin Ivanov
4
+ Copyright (c) 2026 Vedran Hrabar
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.5
2
+ Name: bt-dualboot-sync
3
+ Version: 0.1.0
4
+ Summary: Sync Bluetooth for dualboot Linux and Windows
5
+ Project-URL: Homepage, https://github.com/vhrabar/bt-dualboot
6
+ Project-URL: Repository, https://github.com/vhrabar/bt-dualboot
7
+ Project-URL: Documentation, https://github.com/vhrabar/bt-dualboot/wiki
8
+ Project-URL: Issues, https://github.com/vhrabar/bt-dualboot/issues
9
+ Project-URL: Changelog, https://github.com/vhrabar/bt-dualboot/blob/main/CHANGELOG.md
10
+ Author-email: Vedran Hrabar <vedran.hrabar@outlook.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: bluetooth,dualboot,keys,pairing,sync
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Intended Audience :: System Administrators
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Home Automation
24
+ Classifier: Topic :: Multimedia
25
+ Classifier: Topic :: System
26
+ Classifier: Topic :: System :: Operating System
27
+ Classifier: Topic :: System :: Systems Administration
28
+ Requires-Python: >=3.11
29
+ Description-Content-Type: text/markdown
30
+
31
+ Sync Bluetooth for dualboot Linux and Windows
32
+ =============================================
33
+
34
+ User-friendly CLI that makes your Bluetooth devices work in both Windows and
35
+ Linux without the re-pairing chore. It copies the pairing keys from Linux into
36
+ the Windows registry, so you don't have to reboot back and forth.
37
+
38
+ * doesn't require rebooting multiple times
39
+ * discovers the mounted Windows partition automatically
40
+ * safe, size-preserving Windows Registry update via `chntpw/reged`
41
+ * backs up the Windows Registry before writing
42
+ * `--dry-run` to preview any command
43
+
44
+ ## Requirements
45
+
46
+ * [uv](https://docs.astral.sh/uv/)
47
+ * `chntpw` (`sudo apt install chntpw` on Ubuntu/Mint)
48
+
49
+ ## Quick start
50
+
51
+ Pair your devices on Windows, boot to Linux, pair them there too, then:
52
+
53
+ ```console
54
+ # mount the Windows partition read-write, then:
55
+ $ sudo uvx --from bt-dualboot-sync bt-dualboot --sync-all
56
+ ```
57
+
58
+ List devices and sync a single one:
59
+
60
+ ```console
61
+ $ sudo uvx --from bt-dualboot-sync bt-dualboot -l
62
+ $ sudo uvx --from bt-dualboot-sync bt-dualboot --sync C2:9E:1D:E2:3D:A5
63
+ ```
64
+
65
+ For a persistent command: `uv tool install bt-dualboot-sync`
66
+
67
+ ## Documentation
68
+
69
+ Full usage, troubleshooting, advanced options and CLI reference:
70
+ https://github.com/vhrabar/bt-dualboot
71
+
72
+ ## License
73
+
74
+ Released under the MIT License. See [`LICENSE`](https://github.com/vhrabar/bt-dualboot/blob/main/LICENSE).
75
+
76
+ Fork of [x2es/bt-dualboot](https://github.com/x2es/bt-dualboot) by Konstantin Ivanov.
77
+
78
+ Copyright © 2022 Konstantin Ivanov
79
+ Copyright © 2026 Vedran Hrabar