circfirm 3.0.0__tar.gz → 4.0.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.
- {circfirm-3.0.0 → circfirm-4.0.0}/.github/workflows/push.yml +22 -5
- {circfirm-3.0.0 → circfirm-4.0.0}/.pre-commit-config.yaml +3 -3
- {circfirm-3.0.0 → circfirm-4.0.0}/Makefile +2 -2
- circfirm-4.0.0/NOTICE +64 -0
- circfirm-4.0.0/NOTICE.license +2 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/PKG-INFO +18 -16
- circfirm-4.0.0/VERSION +1 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/backend/__init__.py +18 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/backend/cache.py +11 -27
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/backend/device.py +2 -2
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/backend/github.py +4 -4
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/backend/s3.py +10 -8
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/__init__.py +23 -8
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/cache.py +65 -9
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/current.py +1 -3
- circfirm-4.0.0/circfirm/cli/detect.py +38 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/install.py +13 -2
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/query.py +7 -3
- circfirm-4.0.0/circfirm/cli/update.py +122 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/startup.py +3 -4
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm.egg-info/PKG-INFO +18 -16
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm.egg-info/SOURCES.txt +7 -0
- circfirm-4.0.0/circfirm.egg-info/requires.txt +17 -0
- circfirm-4.0.0/docs/commands/cache.rst +74 -0
- circfirm-4.0.0/docs/commands/detect.rst +30 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/commands/install.rst +8 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/commands/update.rst +12 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/conf.py +0 -1
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/index.rst +2 -0
- circfirm-4.0.0/docs/infrastructure/notice.rst +8 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/pyproject.toml +4 -3
- {circfirm-3.0.0 → circfirm-4.0.0}/requirements-dev.txt +6 -6
- {circfirm-3.0.0 → circfirm-4.0.0}/requirements.txt +6 -6
- {circfirm-3.0.0 → circfirm-4.0.0}/scripts/rmdir.py +8 -0
- circfirm-4.0.0/tests/backend/test_backend.py +41 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/backend/test_backend_cache.py +0 -27
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/backend/test_backend_github.py +0 -2
- circfirm-4.0.0/tests/backend/test_backend_s3.py +64 -0
- circfirm-4.0.0/tests/cli/test_cli_cache.py +282 -0
- circfirm-4.0.0/tests/cli/test_cli_detect.py +56 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/cli/test_cli_install.py +36 -1
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/cli/test_cli_query.py +26 -12
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/cli/test_cli_update.py +66 -3
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/helpers.py +59 -11
- circfirm-3.0.0/VERSION +0 -1
- circfirm-3.0.0/circfirm/cli/update.py +0 -63
- circfirm-3.0.0/circfirm.egg-info/requires.txt +0 -17
- circfirm-3.0.0/docs/commands/cache.rst +0 -48
- circfirm-3.0.0/tests/backend/test_backend_s3.py +0 -26
- circfirm-3.0.0/tests/cli/test_cli_cache.py +0 -139
- {circfirm-3.0.0 → circfirm-4.0.0}/.github/workflows/codeql.yml +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/.github/workflows/publish.yml +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/.gitignore +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/.readthedocs.yaml +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/.reuse/dep5 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/LICENSE +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/LICENSES/MIT.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/LICENSES/Unlicense.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/README.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/VERSION.license +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/__init__.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/about.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/cli/config.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/py.typed +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/templates/settings.yaml +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm/templates/settings.yaml.license +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm.egg-info/dependency_links.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm.egg-info/entry_points.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/circfirm.egg-info/top_level.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/commands/config.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/commands/current.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/commands/query.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/examples/update_many.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/examples/weekly_cache.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/index.rst.license +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/infrastructure/ci.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/infrastructure/codecheck.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/infrastructure/docs.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/infrastructure/license.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/docs/infrastructure/tests.rst +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/examples/bash/update_many.sh +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/examples/bash/weekly_cache.sh +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/examples/powershell/update_many.ps1 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/examples/powershell/weekly_cache.ps1 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/setup.cfg +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/boot_out.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-en_US-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-fr-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m0_express/adafruit-circuitpython-feather_m0_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-en_US-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-fr-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/feather_m4_express/adafruit-circuitpython-feather_m4_express-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-en_US-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-fr-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.0.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.1.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/firmwares/pygamer/adafruit-circuitpython-pygamer-zh_Latn_pinyin-7.2.0.uf2 +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/info_uf2.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/responses/full_list.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/responses/full_list.txt.license +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/responses/specific_board.txt +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/assets/responses/specific_board.txt.license +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/backend/test_backend_device.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/cli/test_cli_about.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/cli/test_cli_config.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/cli/test_cli_current.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/conftest.py +0 -0
- {circfirm-3.0.0 → circfirm-4.0.0}/tests/test_startup.py +0 -0
|
@@ -14,20 +14,37 @@ jobs:
|
|
|
14
14
|
runs-on: ${{ matrix.os }}
|
|
15
15
|
strategy:
|
|
16
16
|
fail-fast: false
|
|
17
|
-
max-parallel: 3
|
|
18
17
|
matrix:
|
|
19
18
|
py-version: [
|
|
20
|
-
"3.8",
|
|
21
19
|
"3.9",
|
|
22
20
|
"3.10",
|
|
23
21
|
"3.11",
|
|
24
22
|
"3.12",
|
|
23
|
+
"3.13",
|
|
25
24
|
]
|
|
26
25
|
os: [
|
|
27
|
-
ubuntu-latest,
|
|
28
|
-
windows-latest,
|
|
29
|
-
macos-latest
|
|
26
|
+
"ubuntu-latest",
|
|
27
|
+
"windows-latest",
|
|
28
|
+
"macos-latest",
|
|
30
29
|
]
|
|
30
|
+
exclude:
|
|
31
|
+
- os: windows-latest
|
|
32
|
+
py-version: "3.10"
|
|
33
|
+
- os: windows-latest
|
|
34
|
+
py-version: "3.11"
|
|
35
|
+
- os: windows-latest
|
|
36
|
+
py-version: "3.12"
|
|
37
|
+
- os: windows-latest
|
|
38
|
+
py-version: "3.13"
|
|
39
|
+
- os: macos-latest
|
|
40
|
+
py-version: "3.10"
|
|
41
|
+
- os: macos-latest
|
|
42
|
+
py-version: "3.11"
|
|
43
|
+
- os: macos-latest
|
|
44
|
+
py-version: "3.12"
|
|
45
|
+
- os: macos-latest
|
|
46
|
+
py-version: "3.13"
|
|
47
|
+
|
|
31
48
|
steps:
|
|
32
49
|
- name: Setup Python 3.x
|
|
33
50
|
uses: actions/setup-python@v5
|
|
@@ -14,7 +14,7 @@ repos:
|
|
|
14
14
|
- id: reuse
|
|
15
15
|
name: Check REUSE compatibility
|
|
16
16
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
17
|
-
rev:
|
|
17
|
+
rev: v5.0.0
|
|
18
18
|
hooks:
|
|
19
19
|
- id: check-yaml
|
|
20
20
|
name: Check YAML
|
|
@@ -31,8 +31,8 @@ repos:
|
|
|
31
31
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
32
32
|
rev: v0.2.2
|
|
33
33
|
hooks:
|
|
34
|
+
- id: ruff-format
|
|
35
|
+
name: Format via ruff
|
|
34
36
|
- id: ruff
|
|
35
37
|
name: Lint via ruff
|
|
36
38
|
args: [--fix]
|
|
37
|
-
- id: ruff-format
|
|
38
|
-
name: Format via ruff
|
|
@@ -59,8 +59,8 @@ test-run:
|
|
|
59
59
|
test-clean:
|
|
60
60
|
ifeq "$(OS)" "Windows_NT"
|
|
61
61
|
-@subst T: /d
|
|
62
|
-
-@python scripts
|
|
63
|
-
-@python scripts
|
|
62
|
+
-@python scripts\rmdir.py testmount
|
|
63
|
+
-@python scripts\rmdir.py tests\sandbox\circuitpython
|
|
64
64
|
else ifeq "$(shell uname -s)" "Linux"
|
|
65
65
|
-@sudo umount testmount
|
|
66
66
|
-@sudo rm -rf testmount
|
circfirm-4.0.0/NOTICE
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Required Dependencies
|
|
2
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
3
|
+
|
|
4
|
+
This software interacts with the CircuitPython AWS S3 bucket using boto3 by
|
|
5
|
+
Amazon.com Inc or its affiliates (https://github.com/boto/boto3) [Apache-2.0 license].
|
|
6
|
+
|
|
7
|
+
This software uses type annotations for boto3 provided by boto3-stubs by Vlad
|
|
8
|
+
Emelinaov and maintained by youtype (https://github.com/youtype/mypy_boto3_builder)
|
|
9
|
+
[MIT license].
|
|
10
|
+
|
|
11
|
+
This software creates its command line interface using click by the pallets
|
|
12
|
+
project (https://palletsprojects.com/p/click/) [BSD-3-Clause license].
|
|
13
|
+
|
|
14
|
+
This software uses a CLI spinner add-on click-spinner to clink to display loading
|
|
15
|
+
animation created by Yoav Ram (https://github.com/click-contrib/click-spinner)
|
|
16
|
+
[MIT license].
|
|
17
|
+
|
|
18
|
+
This software compares and orders CircuitPython versions according to PEP 440
|
|
19
|
+
using the packaging library by Donald Stufft and maintained by the Python Packaging
|
|
20
|
+
Authority (https://packaging.pypa.io/en/stable/) [Apache-2.0 or BSD-2-Clause
|
|
21
|
+
licenses].
|
|
22
|
+
|
|
23
|
+
This software discovers connected CircuitPython boards using the system utilization
|
|
24
|
+
features of psutil, created by Jay Loden, Dave Daeschler, and Giampaolo Rodola
|
|
25
|
+
(https://github.com/giampaolo/psutil) [BSD-3-Clause license].
|
|
26
|
+
|
|
27
|
+
This software reads and modifies the YAML configuration settings using PyYAML,
|
|
28
|
+
created by Kirill Simonov and Ingy dot Net, and maintained by the YAML community
|
|
29
|
+
(https://pyyaml.org/) [MIT license].
|
|
30
|
+
|
|
31
|
+
requests~=2.31
|
|
32
|
+
This software makes HTTP requests, such as communicating with the CircuitPython
|
|
33
|
+
GitHub repository and downloading CircuitPython firmware files, using requests,
|
|
34
|
+
created by Kenneth Reitz and maintained by the Python Software Foundation
|
|
35
|
+
(https://requests.readthedocs.io/en/latest/) [Apache-2.0 license].
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Development Dependencies
|
|
39
|
+
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
40
|
+
|
|
41
|
+
This software uses the build package for its build frontend, created by Filipe Lains
|
|
42
|
+
and maintained by the Python Packaging Authority (https://build.pypa.io/en/stable/)
|
|
43
|
+
[MIT license].
|
|
44
|
+
|
|
45
|
+
This software measures code coverage metrics using Coverage.py, authored by and
|
|
46
|
+
copyright Ned Batchelder (https://github.com/nedbat/coveragepy) [Apache-2.0 license].
|
|
47
|
+
|
|
48
|
+
This software manages code checks such as linting and formatting using pre-commit,
|
|
49
|
+
copyright Anythony Sottile and Ken Struys and (https://github.com/pre-commit/pre-commit)
|
|
50
|
+
[MIT license].
|
|
51
|
+
|
|
52
|
+
This software performs software tests using pytest, copyright Holger Krekel and others
|
|
53
|
+
(https://docs.pytest.org/en/latest/) [MIT license].
|
|
54
|
+
|
|
55
|
+
Ths software creates its documentation using Sphinx, copyright the Sphinx team
|
|
56
|
+
(https://www.sphinx-doc.org/en/master/) [BSD license].
|
|
57
|
+
|
|
58
|
+
This software uses tabs within its documentation provided by sphinx-tabs, copyright
|
|
59
|
+
djungelorm (https://github.com/executablebooks/sphinx-tabs) [MIT license].
|
|
60
|
+
|
|
61
|
+
sphinx-rtd-theme~=1.0
|
|
62
|
+
This software displays its documentation using the ReadTheDocs Sphinx theme, copyright Dave
|
|
63
|
+
Snider, Read the Docs, Inc. & contributors (https://sphinx-rtd-theme.readthedocs.io/en/stable/)
|
|
64
|
+
[MIT license].
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: circfirm
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: CLI tool for install firmware for CircuitPython boards
|
|
5
5
|
Author-email: Alec Delaney <tekktrik@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -15,35 +15,37 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
15
15
|
Classifier: Development Status :: 5 - Production/Stable
|
|
16
16
|
Classifier: Environment :: Console
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
24
|
Classifier: Operating System :: Unix
|
|
25
25
|
Classifier: Operating System :: Microsoft :: Windows
|
|
26
26
|
Classifier: Operating System :: MacOS
|
|
27
27
|
Classifier: Typing :: Typed
|
|
28
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.9.0
|
|
29
29
|
Description-Content-Type: text/x-rst
|
|
30
30
|
License-File: LICENSE
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
License-File: NOTICE
|
|
32
|
+
License-File: NOTICE.license
|
|
33
|
+
Requires-Dist: boto3~=1.35
|
|
34
|
+
Requires-Dist: click~=8.1
|
|
33
35
|
Requires-Dist: click-spinner~=0.1
|
|
34
|
-
Requires-Dist: packaging~=
|
|
35
|
-
Requires-Dist: psutil~=
|
|
36
|
+
Requires-Dist: packaging~=24.2
|
|
37
|
+
Requires-Dist: psutil~=6.1
|
|
36
38
|
Requires-Dist: pyyaml~=6.0
|
|
37
|
-
Requires-Dist: requests~=2.
|
|
38
|
-
Requires-Dist: boto3-stubs[essential]~=1.
|
|
39
|
+
Requires-Dist: requests~=2.32
|
|
40
|
+
Requires-Dist: boto3-stubs[essential]~=1.35
|
|
39
41
|
Provides-Extra: dev
|
|
40
|
-
Requires-Dist: build~=1.
|
|
41
|
-
Requires-Dist: coverage~=7.
|
|
42
|
-
Requires-Dist: pre-commit~=
|
|
43
|
-
Requires-Dist: pytest~=8.
|
|
44
|
-
Requires-Dist: sphinx~=
|
|
42
|
+
Requires-Dist: build~=1.2; extra == "dev"
|
|
43
|
+
Requires-Dist: coverage~=7.6; extra == "dev"
|
|
44
|
+
Requires-Dist: pre-commit~=4.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest~=8.3; extra == "dev"
|
|
46
|
+
Requires-Dist: sphinx~=7.4; extra == "dev"
|
|
45
47
|
Requires-Dist: sphinx-tabs~=3.4; extra == "dev"
|
|
46
|
-
Requires-Dist: sphinx-rtd-theme~=
|
|
48
|
+
Requires-Dist: sphinx-rtd-theme~=3.0; extra == "dev"
|
|
47
49
|
|
|
48
50
|
..
|
|
49
51
|
SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
|
circfirm-4.0.0/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0.0
|
|
@@ -8,6 +8,7 @@ Author(s): Alec Delaney
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import enum
|
|
11
|
+
import re
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class Language(enum.Enum):
|
|
@@ -51,3 +52,20 @@ FIRMWARE_REGEX = (
|
|
|
51
52
|
.replace(r"[language]", f"({_ALL_LANGUAGES_REGEX})")
|
|
52
53
|
.replace(r"[version]", _VALID_VERSIONS_CAPTURE)
|
|
53
54
|
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_uf2_filename(board_id: str, version: str, language: str = "en_US") -> str:
|
|
58
|
+
"""Get the structured name for a specific board/version CircuitPython."""
|
|
59
|
+
return f"adafruit-circuitpython-{board_id}-{language}-{version}.uf2"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse_firmware_info(uf2_filename: str) -> tuple[str, str]:
|
|
63
|
+
"""Get firmware info."""
|
|
64
|
+
regex_match = re.match(FIRMWARE_REGEX, uf2_filename)
|
|
65
|
+
if regex_match is None:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Firmware information could not be determined from the filename"
|
|
68
|
+
)
|
|
69
|
+
version = regex_match[3]
|
|
70
|
+
language = regex_match[2]
|
|
71
|
+
return version, language
|
|
@@ -9,8 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
|
-
import
|
|
13
|
-
from typing import Dict, List, Optional, Set, Tuple
|
|
12
|
+
from typing import Optional
|
|
14
13
|
|
|
15
14
|
import packaging.version
|
|
16
15
|
import requests
|
|
@@ -19,16 +18,11 @@ import circfirm.backend
|
|
|
19
18
|
import circfirm.startup
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def get_uf2_filename(board_id: str, version: str, language: str = "en_US") -> str:
|
|
23
|
-
"""Get the structured name for a specific board/version CircuitPython."""
|
|
24
|
-
return f"adafruit-circuitpython-{board_id}-{language}-{version}.uf2"
|
|
25
|
-
|
|
26
|
-
|
|
27
21
|
def get_uf2_filepath(
|
|
28
22
|
board_id: str, version: str, language: str = "en_US"
|
|
29
23
|
) -> pathlib.Path:
|
|
30
24
|
"""Get the path to a downloaded UF2 file."""
|
|
31
|
-
file = get_uf2_filename(board_id, version, language)
|
|
25
|
+
file = circfirm.backend.get_uf2_filename(board_id, version, language)
|
|
32
26
|
uf2_folder = get_board_folder(board_id)
|
|
33
27
|
return uf2_folder / file
|
|
34
28
|
|
|
@@ -46,31 +40,33 @@ def is_downloaded(board_id: str, version: str, language: str = "en_US") -> bool:
|
|
|
46
40
|
|
|
47
41
|
def download_uf2(board_id: str, version: str, language: str = "en_US") -> None:
|
|
48
42
|
"""Download a version of CircuitPython for a specific board."""
|
|
49
|
-
file = get_uf2_filename(board_id, version, language=language)
|
|
43
|
+
file = circfirm.backend.get_uf2_filename(board_id, version, language=language)
|
|
50
44
|
uf2_file = get_uf2_filepath(board_id, version, language=language)
|
|
51
45
|
url = f"https://downloads.circuitpython.org/bin/{board_id}/{language}/{file}"
|
|
52
46
|
response = requests.get(url)
|
|
53
47
|
|
|
54
48
|
SUCCESS = 200
|
|
55
49
|
if response.status_code != SUCCESS:
|
|
56
|
-
raise ConnectionError(
|
|
50
|
+
raise ConnectionError(
|
|
51
|
+
f"Could not download the specified UF2 file:\n{url}\nAre the board ID, version, and language correct?"
|
|
52
|
+
)
|
|
57
53
|
|
|
58
54
|
uf2_file.parent.mkdir(parents=True, exist_ok=True)
|
|
59
55
|
with open(uf2_file, mode="wb") as uf2file:
|
|
60
56
|
uf2file.write(response.content)
|
|
61
57
|
|
|
62
58
|
|
|
63
|
-
def get_sorted_boards(board_id: Optional[str]) ->
|
|
59
|
+
def get_sorted_boards(board_id: Optional[str]) -> dict[str, dict[str, set[str]]]:
|
|
64
60
|
"""Get a sorted collection of boards, versions, and languages."""
|
|
65
|
-
boards:
|
|
61
|
+
boards: dict[str, dict[str, set[str]]] = {}
|
|
66
62
|
for board_folder in sorted(os.listdir(circfirm.UF2_ARCHIVE)):
|
|
67
|
-
versions:
|
|
68
|
-
sorted_versions:
|
|
63
|
+
versions: dict[str, list[str]] = {}
|
|
64
|
+
sorted_versions: dict[str, set[str]] = {}
|
|
69
65
|
if board_id is not None and board_id != board_folder:
|
|
70
66
|
continue
|
|
71
67
|
board_folder_full = get_board_folder(board_folder)
|
|
72
68
|
for item in os.listdir(board_folder_full):
|
|
73
|
-
version, language = parse_firmware_info(item)
|
|
69
|
+
version, language = circfirm.backend.parse_firmware_info(item)
|
|
74
70
|
try:
|
|
75
71
|
version_set = set(versions[version])
|
|
76
72
|
version_set.add(language)
|
|
@@ -83,15 +79,3 @@ def get_sorted_boards(board_id: Optional[str]) -> Dict[str, Dict[str, Set[str]]]
|
|
|
83
79
|
sorted_versions[sorted_version] = versions[sorted_version]
|
|
84
80
|
boards[board_folder] = sorted_versions
|
|
85
81
|
return boards
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def parse_firmware_info(uf2_filename: str) -> Tuple[str, str]:
|
|
89
|
-
"""Get firmware info."""
|
|
90
|
-
regex_match = re.match(circfirm.backend.FIRMWARE_REGEX, uf2_filename)
|
|
91
|
-
if regex_match is None:
|
|
92
|
-
raise ValueError(
|
|
93
|
-
"Firmware information could not be determined from the filename"
|
|
94
|
-
)
|
|
95
|
-
version = regex_match[3]
|
|
96
|
-
language = regex_match[2]
|
|
97
|
-
return version, language
|
|
@@ -9,7 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import pathlib
|
|
11
11
|
import re
|
|
12
|
-
from typing import Optional
|
|
12
|
+
from typing import Optional
|
|
13
13
|
|
|
14
14
|
import psutil
|
|
15
15
|
|
|
@@ -21,7 +21,7 @@ BOARD_VER_REGEX = (
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def get_board_info(device_path: str) ->
|
|
24
|
+
def get_board_info(device_path: str) -> tuple[str, str]:
|
|
25
25
|
"""Get the attached CircuitPytho board's name and version."""
|
|
26
26
|
bootout_file = pathlib.Path(device_path) / circfirm.BOOTOUT_FILE
|
|
27
27
|
with open(bootout_file, encoding="utf-8") as infofile:
|
|
@@ -9,7 +9,7 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import datetime
|
|
11
11
|
import re
|
|
12
|
-
from typing import
|
|
12
|
+
from typing import TypedDict
|
|
13
13
|
|
|
14
14
|
import requests
|
|
15
15
|
|
|
@@ -42,7 +42,7 @@ class GitTreeItem(TypedDict):
|
|
|
42
42
|
url: str
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def get_rate_limit() ->
|
|
45
|
+
def get_rate_limit() -> tuple[int, int, datetime.datetime]:
|
|
46
46
|
"""Get the rate limit for the GitHub REST endpoint."""
|
|
47
47
|
response = requests.get(
|
|
48
48
|
url="https://api.github.com/rate_limit",
|
|
@@ -55,7 +55,7 @@ def get_rate_limit() -> Tuple[int, int, datetime.datetime]:
|
|
|
55
55
|
return available, total, reset_time
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def get_board_id_list(token: str) ->
|
|
58
|
+
def get_board_id_list(token: str) -> list[str]:
|
|
59
59
|
"""Get a list of CircuitPython boards."""
|
|
60
60
|
boards = set()
|
|
61
61
|
headers = BASE_REQUESTS_HEADERS.copy()
|
|
@@ -69,7 +69,7 @@ def get_board_id_list(token: str) -> List[str]:
|
|
|
69
69
|
headers=headers,
|
|
70
70
|
)
|
|
71
71
|
try:
|
|
72
|
-
tree_items:
|
|
72
|
+
tree_items: list[GitTreeItem] = response.json()["tree"]
|
|
73
73
|
except KeyError as err:
|
|
74
74
|
raise ValueError("Could not parse JSON response, check token") from err
|
|
75
75
|
for tree_item in tree_items:
|
|
@@ -8,7 +8,7 @@ Author(s): Alec Delaney
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import re
|
|
11
|
-
from typing import
|
|
11
|
+
from typing import Optional
|
|
12
12
|
|
|
13
13
|
import boto3
|
|
14
14
|
import botocore
|
|
@@ -17,6 +17,7 @@ import packaging.version
|
|
|
17
17
|
from mypy_boto3_s3 import S3ServiceResource
|
|
18
18
|
|
|
19
19
|
import circfirm.backend
|
|
20
|
+
import circfirm.backend.cache
|
|
20
21
|
|
|
21
22
|
S3_CONFIG = botocore.client.Config(signature_version=botocore.UNSIGNED)
|
|
22
23
|
S3_RESOURCE: S3ServiceResource = boto3.resource("s3", config=S3_CONFIG)
|
|
@@ -26,24 +27,25 @@ BUCKET = S3_RESOURCE.Bucket(BUCKET_NAME)
|
|
|
26
27
|
|
|
27
28
|
def get_board_versions(
|
|
28
29
|
board_id: str, language: str = "en_US", *, regex: Optional[str] = None
|
|
29
|
-
) ->
|
|
30
|
+
) -> list[str]:
|
|
30
31
|
"""Get a list of CircuitPython versions for a given board."""
|
|
31
32
|
prefix = f"bin/{board_id}/{language}"
|
|
32
33
|
firmware_regex = circfirm.backend.FIRMWARE_REGEX_PATTERN.replace(
|
|
33
34
|
r"[board]", board_id
|
|
34
35
|
).replace(r"[language]", language)
|
|
35
|
-
version_regex =
|
|
36
|
+
version_regex = circfirm.backend._VALID_VERSIONS_CAPTURE
|
|
36
37
|
firmware_regex = firmware_regex.replace(r"[version]", version_regex)
|
|
37
38
|
s3_objects = BUCKET.objects.filter(Prefix=prefix)
|
|
38
39
|
versions = set()
|
|
39
40
|
for s3_object in s3_objects:
|
|
40
41
|
result = re.match(f"{prefix}/{firmware_regex}", s3_object.key)
|
|
41
42
|
if result:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
if regex:
|
|
44
|
+
firmware_filename = s3_object.key.split("/")[-1]
|
|
45
|
+
version, _ = circfirm.backend.parse_firmware_info(firmware_filename)
|
|
46
|
+
if not re.match(regex, version):
|
|
47
|
+
continue
|
|
48
|
+
versions.add(result[1])
|
|
47
49
|
return sorted(versions, key=packaging.version.Version, reverse=True)
|
|
48
50
|
|
|
49
51
|
|
|
@@ -13,7 +13,8 @@ import pkgutil
|
|
|
13
13
|
import shutil
|
|
14
14
|
import sys
|
|
15
15
|
import time
|
|
16
|
-
from
|
|
16
|
+
from collections.abc import Iterable
|
|
17
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
17
18
|
|
|
18
19
|
import click
|
|
19
20
|
import click_spinner
|
|
@@ -43,8 +44,11 @@ def maybe_support(msg: str) -> None:
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
def get_board_id(
|
|
46
|
-
circuitpy: Optional[str],
|
|
47
|
-
|
|
47
|
+
circuitpy: Optional[str],
|
|
48
|
+
bootloader: Optional[str],
|
|
49
|
+
board: Optional[str],
|
|
50
|
+
timeout: int = -1,
|
|
51
|
+
) -> tuple[str, str]:
|
|
48
52
|
"""Get the board ID of a device via CLI."""
|
|
49
53
|
if not board:
|
|
50
54
|
if not circuitpy and bootloader:
|
|
@@ -56,12 +60,22 @@ def get_board_id(
|
|
|
56
60
|
board = circfirm.backend.device.get_board_info(circuitpy)[0]
|
|
57
61
|
|
|
58
62
|
click.echo("Board ID detected, please switch the device to bootloader mode.")
|
|
63
|
+
if timeout == -1:
|
|
64
|
+
skip_timeout = True
|
|
65
|
+
else:
|
|
66
|
+
skip_timeout = False
|
|
67
|
+
start_time = time.time()
|
|
68
|
+
|
|
59
69
|
while not (bootloader := circfirm.backend.device.find_bootloader()):
|
|
60
|
-
time.
|
|
70
|
+
if not skip_timeout and time.time() >= start_time + timeout:
|
|
71
|
+
raise OSError(
|
|
72
|
+
"Bootloader mode device not found within the timeout period"
|
|
73
|
+
)
|
|
74
|
+
time.sleep(0.05)
|
|
61
75
|
return bootloader, board
|
|
62
76
|
|
|
63
77
|
|
|
64
|
-
def get_connection_status() ->
|
|
78
|
+
def get_connection_status() -> tuple[Optional[str], Optional[str]]:
|
|
65
79
|
"""Get the status of a connectted CircuitPython device as a CIRCUITPY and bootloader location."""
|
|
66
80
|
circuitpy = circfirm.backend.device.find_circuitpy()
|
|
67
81
|
bootloader = circfirm.backend.device.find_bootloader()
|
|
@@ -108,14 +122,15 @@ def copy_cache_firmware(
|
|
|
108
122
|
announce_and_await(
|
|
109
123
|
f"Copying UF2 to {board}", shutil.copyfile, args=(uf2file, uf2_path)
|
|
110
124
|
)
|
|
111
|
-
click.echo("
|
|
125
|
+
click.echo(f"CircuitPython version now upgraded to {version}")
|
|
126
|
+
click.echo("Device should reboot momentarily")
|
|
112
127
|
|
|
113
128
|
|
|
114
129
|
def announce_and_await(
|
|
115
130
|
msg: str,
|
|
116
131
|
func: Callable[..., _T],
|
|
117
132
|
args: Iterable = (),
|
|
118
|
-
kwargs: Optional[
|
|
133
|
+
kwargs: Optional[dict[str, Any]] = None,
|
|
119
134
|
*,
|
|
120
135
|
use_spinner: bool = True,
|
|
121
136
|
) -> _T:
|
|
@@ -140,7 +155,7 @@ def announce_and_await(
|
|
|
140
155
|
raise err
|
|
141
156
|
|
|
142
157
|
|
|
143
|
-
def get_settings() ->
|
|
158
|
+
def get_settings() -> dict[str, Any]:
|
|
144
159
|
"""Get the contents of the settings file."""
|
|
145
160
|
with open(circfirm.SETTINGS_FILE, encoding="utf-8") as yamlfile:
|
|
146
161
|
return yaml.safe_load(yamlfile)
|
|
@@ -9,14 +9,15 @@ Author(s): Alec Delaney
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
import pathlib
|
|
12
|
+
import re
|
|
12
13
|
import shutil
|
|
13
|
-
import sys
|
|
14
14
|
from typing import Optional
|
|
15
15
|
|
|
16
16
|
import click
|
|
17
17
|
|
|
18
18
|
import circfirm
|
|
19
19
|
import circfirm.backend.cache
|
|
20
|
+
import circfirm.backend.s3
|
|
20
21
|
import circfirm.cli
|
|
21
22
|
import circfirm.startup
|
|
22
23
|
|
|
@@ -30,8 +31,18 @@ def cli():
|
|
|
30
31
|
@click.option("-b", "--board-id", default=None, help="CircuitPython board ID")
|
|
31
32
|
@click.option("-v", "--version", default=None, help="CircuitPython version")
|
|
32
33
|
@click.option("-l", "--language", default=None, help="CircuitPython language/locale")
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
@click.option(
|
|
35
|
+
"-r",
|
|
36
|
+
"--regex",
|
|
37
|
+
is_flag=True,
|
|
38
|
+
default=False,
|
|
39
|
+
help="The board ID, version, and language options represent regex patterns",
|
|
40
|
+
)
|
|
41
|
+
def clear( # noqa: PLR0913
|
|
42
|
+
board_id: Optional[str],
|
|
43
|
+
version: Optional[str],
|
|
44
|
+
language: Optional[str],
|
|
45
|
+
regex: bool,
|
|
35
46
|
) -> None:
|
|
36
47
|
"""Clear the cache, either entirely or for a specific board/version."""
|
|
37
48
|
if board_id is None and version is None and language is None:
|
|
@@ -40,13 +51,33 @@ def clear(
|
|
|
40
51
|
click.echo("Cache cleared!")
|
|
41
52
|
return
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
if regex:
|
|
55
|
+
glob_pattern = "*-*-*-*"
|
|
56
|
+
else:
|
|
57
|
+
glob_pattern = "*-*" if board_id is None else f"*-{board_id}"
|
|
58
|
+
language_pattern = "-*" if language is None else f"-{language}"
|
|
59
|
+
glob_pattern += language_pattern
|
|
60
|
+
version_pattern = "-*" if version is None else f"-{version}.uf2"
|
|
61
|
+
glob_pattern += version_pattern
|
|
62
|
+
|
|
48
63
|
matching_files = pathlib.Path(circfirm.UF2_ARCHIVE).rglob(glob_pattern)
|
|
64
|
+
|
|
49
65
|
for matching_file in matching_files:
|
|
66
|
+
if regex:
|
|
67
|
+
board_id = ".*" if board_id is None else board_id
|
|
68
|
+
version = ".*" if version is None else version
|
|
69
|
+
language = ".*" if language is None else language
|
|
70
|
+
|
|
71
|
+
current_board_id = matching_file.parent.name
|
|
72
|
+
current_version, current_language = circfirm.backend.parse_firmware_info(
|
|
73
|
+
matching_file.name
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
board_id_matches = re.search(board_id, current_board_id)
|
|
77
|
+
version_matches = re.match(version, current_version)
|
|
78
|
+
language_matches = re.match(language, current_language)
|
|
79
|
+
if not all([board_id_matches, version_matches, language_matches]):
|
|
80
|
+
continue
|
|
50
81
|
matching_file.unlink()
|
|
51
82
|
|
|
52
83
|
# Delete board folder if empty
|
|
@@ -88,7 +119,7 @@ def cache_list(board_id: Optional[str]) -> None:
|
|
|
88
119
|
@click.argument("version")
|
|
89
120
|
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
90
121
|
def cache_save(board_id: str, version: str, language: str) -> None:
|
|
91
|
-
"""
|
|
122
|
+
"""Download a version of CircuitPython to the cache."""
|
|
92
123
|
try:
|
|
93
124
|
circfirm.cli.announce_and_await(
|
|
94
125
|
f"Caching firmware version {version} for {board_id}",
|
|
@@ -97,3 +128,28 @@ def cache_save(board_id: str, version: str, language: str) -> None:
|
|
|
97
128
|
)
|
|
98
129
|
except ConnectionError as err:
|
|
99
130
|
raise click.exceptions.ClickException(err.args[0])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@cli.command(name="latest")
|
|
134
|
+
@click.argument("board-id")
|
|
135
|
+
@click.option("-l", "--language", default="en_US", help="CircuitPython language/locale")
|
|
136
|
+
@click.option(
|
|
137
|
+
"-p",
|
|
138
|
+
"--pre-release",
|
|
139
|
+
is_flag=True,
|
|
140
|
+
default=False,
|
|
141
|
+
help="Whether pre-release versions should be considered",
|
|
142
|
+
)
|
|
143
|
+
def cache_latest(board_id: str, language: str, pre_release: bool) -> None:
|
|
144
|
+
"""Download the latest version of CircuitPython to the cache."""
|
|
145
|
+
try:
|
|
146
|
+
version = circfirm.backend.s3.get_latest_board_version(
|
|
147
|
+
board_id, language, pre_release
|
|
148
|
+
)
|
|
149
|
+
circfirm.cli.announce_and_await(
|
|
150
|
+
f"Caching firmware version {version} for {board_id}",
|
|
151
|
+
circfirm.backend.cache.download_uf2,
|
|
152
|
+
args=(board_id, version, language),
|
|
153
|
+
)
|
|
154
|
+
except ConnectionError as err:
|
|
155
|
+
raise click.exceptions.ClickException(err.args[0])
|
|
@@ -7,15 +7,13 @@
|
|
|
7
7
|
Author(s): Alec Delaney
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from typing import Tuple
|
|
11
|
-
|
|
12
10
|
import click
|
|
13
11
|
|
|
14
12
|
import circfirm.backend.device
|
|
15
13
|
import circfirm.cli
|
|
16
14
|
|
|
17
15
|
|
|
18
|
-
def get_board_info() ->
|
|
16
|
+
def get_board_info() -> tuple[str, str]:
|
|
19
17
|
"""Get board info via the CLI."""
|
|
20
18
|
circuitpy, _ = circfirm.cli.get_connection_status()
|
|
21
19
|
if not circuitpy:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Alec Delaney, for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
"""CLI functionality for the detect subcommand.
|
|
6
|
+
|
|
7
|
+
Author(s): Alec Delaney
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
import circfirm.backend.device
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def cli() -> None:
|
|
18
|
+
"""Detect connected CircuitPython boards."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@cli.command(name="circuitpy")
|
|
22
|
+
def detect_circuitpy() -> None:
|
|
23
|
+
"""Detect a connected board in CIRCUITPY or equivalent mode."""
|
|
24
|
+
circuitpy = circfirm.backend.device.find_circuitpy()
|
|
25
|
+
if not circuitpy:
|
|
26
|
+
click.echo("No board connected in CIRCUITPY or equivalent mode")
|
|
27
|
+
return
|
|
28
|
+
click.echo(circuitpy)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@cli.command(name="bootloader")
|
|
32
|
+
def detect_bootloader() -> None:
|
|
33
|
+
"""Detect a connected board in bootloader mode."""
|
|
34
|
+
bootloader = circfirm.backend.device.find_bootloader()
|
|
35
|
+
if not bootloader:
|
|
36
|
+
click.echo("No board connected in bootloader mode")
|
|
37
|
+
return
|
|
38
|
+
click.echo(bootloader)
|