lr-lumenlabel-cli 3.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.
Files changed (62) hide show
  1. lr_lumenlabel_cli-3.0.0/LICENSE +21 -0
  2. lr_lumenlabel_cli-3.0.0/Makefile +98 -0
  3. lr_lumenlabel_cli-3.0.0/PKG-INFO +121 -0
  4. lr_lumenlabel_cli-3.0.0/README.md +90 -0
  5. lr_lumenlabel_cli-3.0.0/lumenlabel.spec +45 -0
  6. lr_lumenlabel_cli-3.0.0/pyproject.toml +87 -0
  7. lr_lumenlabel_cli-3.0.0/setup.cfg +4 -0
  8. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/PKG-INFO +121 -0
  9. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/SOURCES.txt +60 -0
  10. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/dependency_links.txt +1 -0
  11. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/entry_points.txt +2 -0
  12. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/requires.txt +15 -0
  13. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/scm_file_list.json +56 -0
  14. lr_lumenlabel_cli-3.0.0/src/lr_lumenlabel_cli.egg-info/top_level.txt +1 -0
  15. lr_lumenlabel_cli-3.0.0/src/lumenlabel/__init__.py +6 -0
  16. lr_lumenlabel_cli-3.0.0/src/lumenlabel/__main__.py +3 -0
  17. lr_lumenlabel_cli-3.0.0/src/lumenlabel/_version.py +24 -0
  18. lr_lumenlabel_cli-3.0.0/src/lumenlabel/build_words.py +75 -0
  19. lr_lumenlabel_cli-3.0.0/src/lumenlabel/cli.py +60 -0
  20. lr_lumenlabel_cli-3.0.0/src/lumenlabel/client.py +362 -0
  21. lr_lumenlabel_cli-3.0.0/src/lumenlabel/commands/__init__.py +1 -0
  22. lr_lumenlabel_cli-3.0.0/src/lumenlabel/commands/api.py +64 -0
  23. lr_lumenlabel_cli-3.0.0/src/lumenlabel/commands/auth.py +59 -0
  24. lr_lumenlabel_cli-3.0.0/src/lumenlabel/commands/label.py +253 -0
  25. lr_lumenlabel_cli-3.0.0/src/lumenlabel/commands/station.py +105 -0
  26. lr_lumenlabel_cli-3.0.0/src/lumenlabel/config.py +132 -0
  27. lr_lumenlabel_cli-3.0.0/src/lumenlabel/config_rules.py +92 -0
  28. lr_lumenlabel_cli-3.0.0/src/lumenlabel/importer.py +47 -0
  29. lr_lumenlabel_cli-3.0.0/src/lumenlabel/manifest_gen.py +232 -0
  30. lr_lumenlabel_cli-3.0.0/src/lumenlabel/media_check.py +73 -0
  31. lr_lumenlabel_cli-3.0.0/src/lumenlabel/package.py +294 -0
  32. lr_lumenlabel_cli-3.0.0/src/lumenlabel/shared.py +131 -0
  33. lr_lumenlabel_cli-3.0.0/src/lumenlabel/wordlist/bip39-english.txt +2048 -0
  34. lr_lumenlabel_cli-3.0.0/tests/conftest.py +238 -0
  35. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1114-arena/.arena.json +35 -0
  36. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1114-arena/109-1114 109-1115 v5.pdf +0 -0
  37. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1135/.arena.json +53 -0
  38. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1135/109-1135.txt +4 -0
  39. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1135/111-1002/.arena.json +12 -0
  40. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1135/Galileo MAX box label.pdf +3 -0
  41. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1135/Galileo MAX.nlbl +1 -0
  42. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1135/configuration.json +12 -0
  43. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1182/.arena.json +43 -0
  44. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1182/109-1182.txt +4 -0
  45. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1182/Reel label_TiMo_2021_.pdf +3 -0
  46. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1182/configuration.json +12 -0
  47. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1183/.arena.json +63 -0
  48. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1183/109-1183.txt +4 -0
  49. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1183/800-8203_MWA-N2.nlbl +1 -0
  50. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1183/800-8203_MWA-N2_Landscape.nlbl +1 -0
  51. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1183/Reel label_TimoTwo .pdf +3 -0
  52. lr_lumenlabel_cli-3.0.0/tests/fixtures/109-1183/configuration.json +12 -0
  53. lr_lumenlabel_cli-3.0.0/tests/test_build_words.py +119 -0
  54. lr_lumenlabel_cli-3.0.0/tests/test_client.py +237 -0
  55. lr_lumenlabel_cli-3.0.0/tests/test_config_rules.py +79 -0
  56. lr_lumenlabel_cli-3.0.0/tests/test_label_commands.py +190 -0
  57. lr_lumenlabel_cli-3.0.0/tests/test_manifest_gen.py +192 -0
  58. lr_lumenlabel_cli-3.0.0/tests/test_media_check.py +98 -0
  59. lr_lumenlabel_cli-3.0.0/tests/test_other_commands.py +199 -0
  60. lr_lumenlabel_cli-3.0.0/tests/test_package.py +184 -0
  61. lr_lumenlabel_cli-3.0.0/tests/test_real_sidecar.py +32 -0
  62. lr_lumenlabel_cli-3.0.0/tests/test_station_data_dir.py +70 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LumenRadio AB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,98 @@
1
+ VENV := .venv
2
+ PY := $(VENV)/bin/python
3
+ PIP := $(VENV)/bin/pip
4
+
5
+ # setuptools_scm version, resolved on the host. Passed in through
6
+ # SETUPTOOLS_SCM_PRETEND_VERSION_FOR_* so the sdist build skips the VCS file
7
+ # finder (root=".." would pull in the whole repository) and builds without
8
+ # git (the containers) get it too.
9
+ DIST_ENV := SETUPTOOLS_SCM_PRETEND_VERSION_FOR_LR_LUMENLABEL_CLI
10
+ SCM_VERSION ?= $(shell $(PY) -m setuptools_scm 2>/dev/null)
11
+
12
+ # dist/ also holds the PyInstaller binary; only these are PyPI distributions.
13
+ DIST_FILES := dist/*.whl dist/*.tar.gz
14
+
15
+ PYPI_REPOSITORY ?= https://upload.pypi.org/legacy/
16
+ TEST_PYPI_REPOSITORY ?= https://test.pypi.org/legacy/
17
+
18
+ # PyPI keeps every version number forever and rejects a local (+...) segment,
19
+ # so an upload is refused unless the version is a clean lumenlabel-X.Y.Z tag.
20
+ CHECK_RELEASE_VERSION = case "$(SCM_VERSION)" in *dev*|*+*|"") echo "error: refusing to upload '$(SCM_VERSION)': build from a clean 'lumenlabel-X.Y.Z' tag"; exit 2 ;; esac
21
+
22
+ .DEFAULT_GOAL := install
23
+
24
+ .PHONY: install dev test test-only dist check publish publish-test clean package build-linux-docker build-windows-docker
25
+
26
+ $(VENV)/bin/activate:
27
+ python3.12 -m venv $(VENV)
28
+ $(PIP) install --upgrade pip
29
+
30
+ install: $(VENV)/bin/activate
31
+ $(PIP) install .
32
+
33
+ dev: $(VENV)/bin/activate
34
+ $(PIP) install -e ".[dev,package]"
35
+
36
+ test: dev test-only
37
+
38
+ test-only:
39
+ $(VENV)/bin/pytest \
40
+ --cov=lumenlabel \
41
+ --cov-report=term-missing \
42
+ --junit-xml=build/reports/junit.xml \
43
+ tests/
44
+
45
+ # Clearing the previous sdist and wheel keeps $(DIST_FILES) to this build.
46
+ dist: dev
47
+ rm -f dist/*.whl dist/*.tar.gz
48
+ $(DIST_ENV)="$(SCM_VERSION)" $(PY) -m build
49
+
50
+ check: dist
51
+ $(VENV)/bin/twine check $(DIST_FILES)
52
+
53
+ # twine reads TWINE_USERNAME/TWINE_PASSWORD itself, so the token never shows
54
+ # in the echoed recipe or in the upload's argv.
55
+ publish-test: check
56
+ @$(CHECK_RELEASE_VERSION)
57
+ @if [ -z "$$TEST_PYPI_TOKEN" ]; then echo "error: TEST_PYPI_TOKEN is required"; exit 2; fi
58
+ @TWINE_USERNAME=__token__ TWINE_PASSWORD="$$TEST_PYPI_TOKEN" $(VENV)/bin/twine upload --repository-url $(TEST_PYPI_REPOSITORY) $(DIST_FILES)
59
+
60
+ publish: check
61
+ @$(CHECK_RELEASE_VERSION)
62
+ @if [ -z "$$PYPI_TOKEN" ]; then echo "error: PYPI_TOKEN is required"; exit 2; fi
63
+ @TWINE_USERNAME=__token__ TWINE_PASSWORD="$$PYPI_TOKEN" $(VENV)/bin/twine upload --repository-url $(PYPI_REPOSITORY) $(DIST_FILES)
64
+
65
+ clean:
66
+ rm -rf $(VENV) dist/ build/ .coverage
67
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; true
68
+ find . -name "*.egg-info" -type d -exec rm -rf {} + 2>/dev/null; true
69
+
70
+ # dist/lumenlabel for this host.
71
+ package: dev
72
+ $(VENV)/bin/pyinstaller -y lumenlabel.spec
73
+
74
+ # dist/lumenlabel in python:3.12-slim-bookworm, the image of the app's build-linux
75
+ # job. Debian 12 sets the glibc floor of the .deb (packaging/deb/README.md).
76
+ build-linux-docker: dev
77
+ docker run --rm \
78
+ -e $(DIST_ENV)="$(SCM_VERSION)" \
79
+ -v "$(CURDIR):/src" \
80
+ -w /src \
81
+ python:3.12-slim-bookworm \
82
+ bash -c "apt-get update -qq && apt-get install -y -qq --no-install-recommends binutils >/dev/null && \
83
+ python3 -m venv /build-venv && \
84
+ /build-venv/bin/pip install --no-cache-dir --upgrade pip setuptools wheel && \
85
+ /build-venv/bin/pip install --no-cache-dir -e '.[package]' && \
86
+ /build-venv/bin/pyinstaller -y lumenlabel.spec; \
87
+ rc=\$$?; chown -R $(shell id -u):$(shell id -g) build dist src 2>/dev/null; exit \$$rc"
88
+
89
+ # dist/lumenlabel.exe in the Wine image the app's Windows build uses.
90
+ build-windows-docker: dev
91
+ docker run --rm \
92
+ --entrypoint bash \
93
+ -e $(DIST_ENV)="$(SCM_VERSION)" \
94
+ -v "$(CURDIR):/src" \
95
+ -w /src \
96
+ batonogov/pyinstaller-windows:v5.0.0 \
97
+ -c "pip install --no-cache-dir --upgrade -e '.[package]' && pyinstaller -y lumenlabel.spec; \
98
+ rc=\$$?; chown -R $(shell id -u):$(shell id -g) build dist src 2>/dev/null; exit \$$rc"
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: lr-lumenlabel-cli
3
+ Version: 3.0.0
4
+ Summary: CLI for the LumenRadio LumenLabel label printing station: import label packages fetched from Arena with gladiator, list labels and inspect the station, locally, on the LAN or through the Fleet relay.
5
+ Author-email: Jonas Estberger <jonas.estberger@lumenradio.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://gitlab.com/lumenradio/production/lumenlabel
8
+ Project-URL: Changelog, https://gitlab.com/lumenradio/production/lumenlabel/-/blob/main/CHANGELOG.md
9
+ Keywords: LumenRadio,LumenLabel,label printing,Arena,CLI
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Environment :: Console
14
+ Requires-Python: >=3.12
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: typer>=0.12
18
+ Requires-Dist: rich>=13.7
19
+ Requires-Dist: httpx>=0.27
20
+ Requires-Dist: lr-fleet>=0.7
21
+ Provides-Extra: dev
22
+ Requires-Dist: build>=1.2.1; extra == "dev"
23
+ Requires-Dist: twine>=6.1; extra == "dev"
24
+ Requires-Dist: pytest>=8.4; extra == "dev"
25
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
26
+ Requires-Dist: black>=25.9; extra == "dev"
27
+ Requires-Dist: setuptools_scm>=8; extra == "dev"
28
+ Provides-Extra: package
29
+ Requires-Dist: pyinstaller==6.21.0; extra == "package"
30
+ Dynamic: license-file
31
+
32
+ # lr-lumenlabel-cli
33
+
34
+ The `lumenlabel` command for LumenRadio LumenLabel label printing stations.
35
+ It turns a label fetched from Arena with gladiator into a label package and
36
+ imports it into a station, locally, on the LAN or through the Fleet relay.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install lr-lumenlabel-cli
42
+ ```
43
+
44
+ The LumenLabel installers also put it on every station: the `.msi` as
45
+ `lumenlabel.exe` on the system `PATH`, the `.deb` as `/usr/bin/lumenlabel`.
46
+ There it imports from a USB stick without a network (airgapped mode).
47
+
48
+ ## Quick start
49
+
50
+ ```bash
51
+ # Fetch the label with its Arena metadata and BOM children
52
+ gladiator get-files 109-1182 --recursive --meta-data --out 109-1182
53
+
54
+ # Write the manifest and show Build Words and media warnings, offline
55
+ lumenlabel label import 109-1182 --dry-run
56
+
57
+ # Import into the LumenLabel running on this computer
58
+ lumenlabel label import 109-1182
59
+
60
+ # Or remember a station once, then import into it
61
+ lumenlabel login --station https://fleet.cloud.lumenradio.com/v1/s/<uid>
62
+ lumenlabel label import 109-1182
63
+ lumenlabel label list
64
+ ```
65
+
66
+ ## Commands
67
+
68
+ ```
69
+ lumenlabel [--station URL] [--token TOKEN] COMMAND # global options first
70
+
71
+ lumenlabel login --station URL [--token TOKEN]
72
+ # Checks the station and remembers it, with its token, in
73
+ # ~/.config/lumenlabel/login.json (0600). Every later command then talks
74
+ # to that station as if it were the local one. --base-url is an alias of
75
+ # --station, as in lumentest.
76
+ lumenlabel logout
77
+
78
+ lumenlabel label import DIR [--dry-run] [--invalidate] [--output rich|json]
79
+ lumenlabel label list [--output rich|json]
80
+ lumenlabel label remove ARTICLE [--yes]
81
+
82
+ lumenlabel station [--output rich|json] # info
83
+ lumenlabel station history [--limit N] [--before ID]
84
+ lumenlabel station logs [--tail N]
85
+
86
+ lumenlabel api METHOD PATH [--data JSON|@FILE] # any /api/ endpoint
87
+ lumenlabel --version
88
+ ```
89
+
90
+ ## Which station
91
+
92
+ 1. `--station URL` (alias `--base-url`), or `LUMENLABEL_STATION` /
93
+ `LUMENLABEL_URL`.
94
+ 2. The station saved by `lumenlabel login`.
95
+ 3. The LumenLabel on this computer, `http://127.0.0.1:<port>`. The port is
96
+ read from the station's `lumenlabel.json` (default 17081).
97
+
98
+ | URL | Authentication |
99
+ |---|---|
100
+ | `https://<fleet>/v1/s/<uid>` (Fleet relay) | Your Fleet sign-in (`fleet login`; the first command opens the browser if needed) |
101
+ | `http://127.0.0.1:<port>` (on the station) | None |
102
+ | `http://<station>:<port>` (LAN, `instrumentation.host` `0.0.0.0` on the station) | The station token, shown in admin mode on the station |
103
+
104
+ ## `label import`
105
+
106
+ - The manifest (`artifact-manifest.json`) is built from the `.arena.json`
107
+ sidecars gladiator writes with `--meta-data`. A folder with a manifest and no
108
+ sidecars is imported as it is.
109
+ - Private attachments (NiceLabel `.nlbl` sources), web links and BOM child
110
+ folders are left out. Only the declared files are uploaded.
111
+ - The media in `configuration.json` (`media_article`, `media_name`) is checked
112
+ against the BOM's 111 item. A mismatch is a warning; fix it with a new
113
+ revision in Arena. The CLI never changes `configuration.json`.
114
+ - A read-only folder (a write-protected USB stick) is imported through a
115
+ temporary copy.
116
+ - An interrupted import resumes: run it again and only the files that did not
117
+ arrive are sent. `--invalidate` discards what the station has staged.
118
+ - The output shows the revision, the Build Words, the media and any warnings.
119
+
120
+ Specification: `doc/label-package.md` and `doc/api-reference.md` in the
121
+ [LumenLabel repository](https://gitlab.com/lumenradio/production/lumenlabel).
@@ -0,0 +1,90 @@
1
+ # lr-lumenlabel-cli
2
+
3
+ The `lumenlabel` command for LumenRadio LumenLabel label printing stations.
4
+ It turns a label fetched from Arena with gladiator into a label package and
5
+ imports it into a station, locally, on the LAN or through the Fleet relay.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install lr-lumenlabel-cli
11
+ ```
12
+
13
+ The LumenLabel installers also put it on every station: the `.msi` as
14
+ `lumenlabel.exe` on the system `PATH`, the `.deb` as `/usr/bin/lumenlabel`.
15
+ There it imports from a USB stick without a network (airgapped mode).
16
+
17
+ ## Quick start
18
+
19
+ ```bash
20
+ # Fetch the label with its Arena metadata and BOM children
21
+ gladiator get-files 109-1182 --recursive --meta-data --out 109-1182
22
+
23
+ # Write the manifest and show Build Words and media warnings, offline
24
+ lumenlabel label import 109-1182 --dry-run
25
+
26
+ # Import into the LumenLabel running on this computer
27
+ lumenlabel label import 109-1182
28
+
29
+ # Or remember a station once, then import into it
30
+ lumenlabel login --station https://fleet.cloud.lumenradio.com/v1/s/<uid>
31
+ lumenlabel label import 109-1182
32
+ lumenlabel label list
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ ```
38
+ lumenlabel [--station URL] [--token TOKEN] COMMAND # global options first
39
+
40
+ lumenlabel login --station URL [--token TOKEN]
41
+ # Checks the station and remembers it, with its token, in
42
+ # ~/.config/lumenlabel/login.json (0600). Every later command then talks
43
+ # to that station as if it were the local one. --base-url is an alias of
44
+ # --station, as in lumentest.
45
+ lumenlabel logout
46
+
47
+ lumenlabel label import DIR [--dry-run] [--invalidate] [--output rich|json]
48
+ lumenlabel label list [--output rich|json]
49
+ lumenlabel label remove ARTICLE [--yes]
50
+
51
+ lumenlabel station [--output rich|json] # info
52
+ lumenlabel station history [--limit N] [--before ID]
53
+ lumenlabel station logs [--tail N]
54
+
55
+ lumenlabel api METHOD PATH [--data JSON|@FILE] # any /api/ endpoint
56
+ lumenlabel --version
57
+ ```
58
+
59
+ ## Which station
60
+
61
+ 1. `--station URL` (alias `--base-url`), or `LUMENLABEL_STATION` /
62
+ `LUMENLABEL_URL`.
63
+ 2. The station saved by `lumenlabel login`.
64
+ 3. The LumenLabel on this computer, `http://127.0.0.1:<port>`. The port is
65
+ read from the station's `lumenlabel.json` (default 17081).
66
+
67
+ | URL | Authentication |
68
+ |---|---|
69
+ | `https://<fleet>/v1/s/<uid>` (Fleet relay) | Your Fleet sign-in (`fleet login`; the first command opens the browser if needed) |
70
+ | `http://127.0.0.1:<port>` (on the station) | None |
71
+ | `http://<station>:<port>` (LAN, `instrumentation.host` `0.0.0.0` on the station) | The station token, shown in admin mode on the station |
72
+
73
+ ## `label import`
74
+
75
+ - The manifest (`artifact-manifest.json`) is built from the `.arena.json`
76
+ sidecars gladiator writes with `--meta-data`. A folder with a manifest and no
77
+ sidecars is imported as it is.
78
+ - Private attachments (NiceLabel `.nlbl` sources), web links and BOM child
79
+ folders are left out. Only the declared files are uploaded.
80
+ - The media in `configuration.json` (`media_article`, `media_name`) is checked
81
+ against the BOM's 111 item. A mismatch is a warning; fix it with a new
82
+ revision in Arena. The CLI never changes `configuration.json`.
83
+ - A read-only folder (a write-protected USB stick) is imported through a
84
+ temporary copy.
85
+ - An interrupted import resumes: run it again and only the files that did not
86
+ arrive are sent. `--invalidate` discards what the station has staged.
87
+ - The output shows the revision, the Build Words, the media and any warnings.
88
+
89
+ Specification: `doc/label-package.md` and `doc/api-reference.md` in the
90
+ [LumenLabel repository](https://gitlab.com/lumenradio/production/lumenlabel).
@@ -0,0 +1,45 @@
1
+ # -*- mode: python ; coding: utf-8 -*-
2
+ # One-file build of the lumenlabel CLI: cli/dist/lumenlabel(.exe).
3
+ from PyInstaller.utils.hooks import collect_data_files, collect_submodules, copy_metadata
4
+
5
+ a = Analysis(
6
+ ["src/lumenlabel/__main__.py"],
7
+ pathex=[],
8
+ binaries=[],
9
+ datas=[
10
+ *copy_metadata("lr-lumenlabel-cli"),
11
+ *collect_data_files("rich"),
12
+ ("src/lumenlabel/wordlist", "lumenlabel/wordlist"),
13
+ ],
14
+ # rich imports a Unicode-version submodule by a name computed at run
15
+ # time, which static analysis cannot see.
16
+ hiddenimports=[*collect_submodules("rich._unicode_data")],
17
+ hookspath=[],
18
+ hooksconfig={},
19
+ runtime_hooks=[],
20
+ excludes=[],
21
+ noarchive=False,
22
+ )
23
+
24
+ pyz = PYZ(a.pure)
25
+
26
+ exe = EXE(
27
+ pyz,
28
+ a.scripts,
29
+ a.binaries,
30
+ a.datas,
31
+ [],
32
+ name="lumenlabel",
33
+ debug=False,
34
+ bootloader_ignore_signals=False,
35
+ strip=False,
36
+ upx=True,
37
+ upx_exclude=[],
38
+ runtime_tmpdir=None,
39
+ console=True,
40
+ disable_windowed_traceback=False,
41
+ argv_emulation=False,
42
+ target_arch=None,
43
+ codesign_identity=None,
44
+ entitlements_file=None,
45
+ )
@@ -0,0 +1,87 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "setuptools_scm>=8", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lr-lumenlabel-cli"
7
+ dynamic = ["version"]
8
+ description = "CLI for the LumenRadio LumenLabel label printing station: import label packages fetched from Arena with gladiator, list labels and inspect the station, locally, on the LAN or through the Fleet relay."
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ { name = "Jonas Estberger", email = "jonas.estberger@lumenradio.com" }
15
+ ]
16
+ dependencies = [
17
+ "typer>=0.12",
18
+ "rich>=13.7",
19
+ "httpx>=0.27",
20
+ "lr-fleet>=0.7",
21
+ ]
22
+ keywords = ["LumenRadio", "LumenLabel", "label printing", "Arena", "CLI"]
23
+ classifiers = [
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Operating System :: OS Independent",
27
+ "Environment :: Console",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "build>=1.2.1",
33
+ "twine>=6.1",
34
+ "pytest>=8.4",
35
+ "pytest-cov>=5.0",
36
+ "black>=25.9",
37
+ "setuptools_scm>=8",
38
+ ]
39
+
40
+ # The one place the CLI's PyInstaller version is declared. Held equal to the
41
+ # app's requirements-build.txt (tests/test_build_pins.py), so both executables
42
+ # in the .msi and the .deb come from one toolchain. The Wine build installs it
43
+ # over the version batonogov/pyinstaller-windows ships.
44
+ package = [
45
+ "pyinstaller==6.21.0",
46
+ ]
47
+
48
+ [project.scripts]
49
+ lumenlabel = "lumenlabel.cli:app"
50
+
51
+ [project.urls]
52
+ Homepage = "https://gitlab.com/lumenradio/production/lumenlabel"
53
+ Changelog = "https://gitlab.com/lumenradio/production/lumenlabel/-/blob/main/CHANGELOG.md"
54
+
55
+ [tool.setuptools]
56
+ package-dir = {"" = "src"}
57
+ include-package-data = true
58
+
59
+ [tool.setuptools.packages.find]
60
+ where = ["src"]
61
+
62
+ [tool.setuptools.package-data]
63
+ lumenlabel = ["wordlist/*.txt"]
64
+
65
+ # The version is the repository's lumenlabel-X.Y.Z tag, as for the app.
66
+ # `make dist` and the CI jobs set SETUPTOOLS_SCM_PRETEND_VERSION_FOR_LR_LUMENLABEL_CLI,
67
+ # so the sdist build does not pull the whole repository in through root="..",
68
+ # and builds without git (the Wine image) get the version too.
69
+ [tool.setuptools_scm]
70
+ root = ".."
71
+ tag_regex = "^lumenlabel-(?P<version>[0-9].+)$"
72
+ git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "lumenlabel-*", "--exclude", "*-rc*"]
73
+ version_file = "src/lumenlabel/_version.py"
74
+ fallback_version = "0.0.0"
75
+
76
+ [tool.pytest.ini_options]
77
+ addopts = "-ra"
78
+ testpaths = ["tests"]
79
+
80
+ [tool.coverage.run]
81
+ branch = true
82
+ source = ["lumenlabel"]
83
+ omit = ["*/_version.py"]
84
+
85
+ [tool.coverage.report]
86
+ fail_under = 80
87
+ show_missing = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: lr-lumenlabel-cli
3
+ Version: 3.0.0
4
+ Summary: CLI for the LumenRadio LumenLabel label printing station: import label packages fetched from Arena with gladiator, list labels and inspect the station, locally, on the LAN or through the Fleet relay.
5
+ Author-email: Jonas Estberger <jonas.estberger@lumenradio.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://gitlab.com/lumenradio/production/lumenlabel
8
+ Project-URL: Changelog, https://gitlab.com/lumenradio/production/lumenlabel/-/blob/main/CHANGELOG.md
9
+ Keywords: LumenRadio,LumenLabel,label printing,Arena,CLI
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Environment :: Console
14
+ Requires-Python: >=3.12
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: typer>=0.12
18
+ Requires-Dist: rich>=13.7
19
+ Requires-Dist: httpx>=0.27
20
+ Requires-Dist: lr-fleet>=0.7
21
+ Provides-Extra: dev
22
+ Requires-Dist: build>=1.2.1; extra == "dev"
23
+ Requires-Dist: twine>=6.1; extra == "dev"
24
+ Requires-Dist: pytest>=8.4; extra == "dev"
25
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
26
+ Requires-Dist: black>=25.9; extra == "dev"
27
+ Requires-Dist: setuptools_scm>=8; extra == "dev"
28
+ Provides-Extra: package
29
+ Requires-Dist: pyinstaller==6.21.0; extra == "package"
30
+ Dynamic: license-file
31
+
32
+ # lr-lumenlabel-cli
33
+
34
+ The `lumenlabel` command for LumenRadio LumenLabel label printing stations.
35
+ It turns a label fetched from Arena with gladiator into a label package and
36
+ imports it into a station, locally, on the LAN or through the Fleet relay.
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install lr-lumenlabel-cli
42
+ ```
43
+
44
+ The LumenLabel installers also put it on every station: the `.msi` as
45
+ `lumenlabel.exe` on the system `PATH`, the `.deb` as `/usr/bin/lumenlabel`.
46
+ There it imports from a USB stick without a network (airgapped mode).
47
+
48
+ ## Quick start
49
+
50
+ ```bash
51
+ # Fetch the label with its Arena metadata and BOM children
52
+ gladiator get-files 109-1182 --recursive --meta-data --out 109-1182
53
+
54
+ # Write the manifest and show Build Words and media warnings, offline
55
+ lumenlabel label import 109-1182 --dry-run
56
+
57
+ # Import into the LumenLabel running on this computer
58
+ lumenlabel label import 109-1182
59
+
60
+ # Or remember a station once, then import into it
61
+ lumenlabel login --station https://fleet.cloud.lumenradio.com/v1/s/<uid>
62
+ lumenlabel label import 109-1182
63
+ lumenlabel label list
64
+ ```
65
+
66
+ ## Commands
67
+
68
+ ```
69
+ lumenlabel [--station URL] [--token TOKEN] COMMAND # global options first
70
+
71
+ lumenlabel login --station URL [--token TOKEN]
72
+ # Checks the station and remembers it, with its token, in
73
+ # ~/.config/lumenlabel/login.json (0600). Every later command then talks
74
+ # to that station as if it were the local one. --base-url is an alias of
75
+ # --station, as in lumentest.
76
+ lumenlabel logout
77
+
78
+ lumenlabel label import DIR [--dry-run] [--invalidate] [--output rich|json]
79
+ lumenlabel label list [--output rich|json]
80
+ lumenlabel label remove ARTICLE [--yes]
81
+
82
+ lumenlabel station [--output rich|json] # info
83
+ lumenlabel station history [--limit N] [--before ID]
84
+ lumenlabel station logs [--tail N]
85
+
86
+ lumenlabel api METHOD PATH [--data JSON|@FILE] # any /api/ endpoint
87
+ lumenlabel --version
88
+ ```
89
+
90
+ ## Which station
91
+
92
+ 1. `--station URL` (alias `--base-url`), or `LUMENLABEL_STATION` /
93
+ `LUMENLABEL_URL`.
94
+ 2. The station saved by `lumenlabel login`.
95
+ 3. The LumenLabel on this computer, `http://127.0.0.1:<port>`. The port is
96
+ read from the station's `lumenlabel.json` (default 17081).
97
+
98
+ | URL | Authentication |
99
+ |---|---|
100
+ | `https://<fleet>/v1/s/<uid>` (Fleet relay) | Your Fleet sign-in (`fleet login`; the first command opens the browser if needed) |
101
+ | `http://127.0.0.1:<port>` (on the station) | None |
102
+ | `http://<station>:<port>` (LAN, `instrumentation.host` `0.0.0.0` on the station) | The station token, shown in admin mode on the station |
103
+
104
+ ## `label import`
105
+
106
+ - The manifest (`artifact-manifest.json`) is built from the `.arena.json`
107
+ sidecars gladiator writes with `--meta-data`. A folder with a manifest and no
108
+ sidecars is imported as it is.
109
+ - Private attachments (NiceLabel `.nlbl` sources), web links and BOM child
110
+ folders are left out. Only the declared files are uploaded.
111
+ - The media in `configuration.json` (`media_article`, `media_name`) is checked
112
+ against the BOM's 111 item. A mismatch is a warning; fix it with a new
113
+ revision in Arena. The CLI never changes `configuration.json`.
114
+ - A read-only folder (a write-protected USB stick) is imported through a
115
+ temporary copy.
116
+ - An interrupted import resumes: run it again and only the files that did not
117
+ arrive are sent. `--invalidate` discards what the station has staged.
118
+ - The output shows the revision, the Build Words, the media and any warnings.
119
+
120
+ Specification: `doc/label-package.md` and `doc/api-reference.md` in the
121
+ [LumenLabel repository](https://gitlab.com/lumenradio/production/lumenlabel).
@@ -0,0 +1,60 @@
1
+ LICENSE
2
+ Makefile
3
+ README.md
4
+ lumenlabel.spec
5
+ pyproject.toml
6
+ src/lr_lumenlabel_cli.egg-info/PKG-INFO
7
+ src/lr_lumenlabel_cli.egg-info/SOURCES.txt
8
+ src/lr_lumenlabel_cli.egg-info/dependency_links.txt
9
+ src/lr_lumenlabel_cli.egg-info/entry_points.txt
10
+ src/lr_lumenlabel_cli.egg-info/requires.txt
11
+ src/lr_lumenlabel_cli.egg-info/scm_file_list.json
12
+ src/lr_lumenlabel_cli.egg-info/top_level.txt
13
+ src/lumenlabel/__init__.py
14
+ src/lumenlabel/__main__.py
15
+ src/lumenlabel/_version.py
16
+ src/lumenlabel/build_words.py
17
+ src/lumenlabel/cli.py
18
+ src/lumenlabel/client.py
19
+ src/lumenlabel/config.py
20
+ src/lumenlabel/config_rules.py
21
+ src/lumenlabel/importer.py
22
+ src/lumenlabel/manifest_gen.py
23
+ src/lumenlabel/media_check.py
24
+ src/lumenlabel/package.py
25
+ src/lumenlabel/shared.py
26
+ src/lumenlabel/commands/__init__.py
27
+ src/lumenlabel/commands/api.py
28
+ src/lumenlabel/commands/auth.py
29
+ src/lumenlabel/commands/label.py
30
+ src/lumenlabel/commands/station.py
31
+ src/lumenlabel/wordlist/bip39-english.txt
32
+ tests/conftest.py
33
+ tests/test_build_words.py
34
+ tests/test_client.py
35
+ tests/test_config_rules.py
36
+ tests/test_label_commands.py
37
+ tests/test_manifest_gen.py
38
+ tests/test_media_check.py
39
+ tests/test_other_commands.py
40
+ tests/test_package.py
41
+ tests/test_real_sidecar.py
42
+ tests/test_station_data_dir.py
43
+ tests/fixtures/109-1114-arena/.arena.json
44
+ tests/fixtures/109-1114-arena/109-1114 109-1115 v5.pdf
45
+ tests/fixtures/109-1135/.arena.json
46
+ tests/fixtures/109-1135/109-1135.txt
47
+ tests/fixtures/109-1135/Galileo MAX box label.pdf
48
+ tests/fixtures/109-1135/Galileo MAX.nlbl
49
+ tests/fixtures/109-1135/configuration.json
50
+ tests/fixtures/109-1135/111-1002/.arena.json
51
+ tests/fixtures/109-1182/.arena.json
52
+ tests/fixtures/109-1182/109-1182.txt
53
+ tests/fixtures/109-1182/Reel label_TiMo_2021_.pdf
54
+ tests/fixtures/109-1182/configuration.json
55
+ tests/fixtures/109-1183/.arena.json
56
+ tests/fixtures/109-1183/109-1183.txt
57
+ tests/fixtures/109-1183/800-8203_MWA-N2.nlbl
58
+ tests/fixtures/109-1183/800-8203_MWA-N2_Landscape.nlbl
59
+ tests/fixtures/109-1183/Reel label_TimoTwo .pdf
60
+ tests/fixtures/109-1183/configuration.json
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ lumenlabel = lumenlabel.cli:app
@@ -0,0 +1,15 @@
1
+ typer>=0.12
2
+ rich>=13.7
3
+ httpx>=0.27
4
+ lr-fleet>=0.7
5
+
6
+ [dev]
7
+ build>=1.2.1
8
+ twine>=6.1
9
+ pytest>=8.4
10
+ pytest-cov>=5.0
11
+ black>=25.9
12
+ setuptools_scm>=8
13
+
14
+ [package]
15
+ pyinstaller==6.21.0