cleepcli 1.45.1__tar.gz → 1.48.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 (41) hide show
  1. {cleepcli-1.45.1 → cleepcli-1.48.0}/MANIFEST.in +1 -0
  2. {cleepcli-1.45.1/cleepcli.egg-info → cleepcli-1.48.0}/PKG-INFO +22 -6
  3. {cleepcli-1.45.1 → cleepcli-1.48.0}/README.md +21 -5
  4. {cleepcli-1.45.1 → cleepcli-1.48.0}/bin/ccli +65 -8
  5. {cleepcli-1.45.1 → cleepcli-1.48.0}/bin/cleep-cli +65 -8
  6. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/check.py +44 -0
  7. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/file.py +45 -0
  8. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/module.py +2 -0
  9. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/package.py +10 -0
  10. cleepcli-1.48.0/cleepcli/systemd/cleep-watch.service +18 -0
  11. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/test.py +50 -15
  12. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/version.py +1 -1
  13. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/watch.py +24 -2
  14. cleepcli-1.48.0/cleepcli/watchsvc.py +214 -0
  15. {cleepcli-1.45.1 → cleepcli-1.48.0/cleepcli.egg-info}/PKG-INFO +22 -6
  16. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli.egg-info/SOURCES.txt +5 -1
  17. {cleepcli-1.45.1 → cleepcli-1.48.0}/setup.py +1 -0
  18. cleepcli-1.48.0/tests/test_file_sync_all.py +91 -0
  19. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_watch.py +7 -0
  20. cleepcli-1.48.0/tests/test_watchsvc.py +57 -0
  21. {cleepcli-1.45.1 → cleepcli-1.48.0}/LICENSE +0 -0
  22. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/__init__.py +0 -0
  23. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/ci.py +0 -0
  24. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/cleepapi.py +0 -0
  25. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/config.py +0 -0
  26. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/console.py +0 -0
  27. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/docs.py +0 -0
  28. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/git.py +0 -0
  29. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli/tools.py +0 -0
  30. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli.egg-info/dependency_links.txt +0 -0
  31. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli.egg-info/requires.txt +0 -0
  32. {cleepcli-1.45.1 → cleepcli-1.48.0}/cleepcli.egg-info/top_level.txt +0 -0
  33. {cleepcli-1.45.1 → cleepcli-1.48.0}/pyproject.toml +0 -0
  34. {cleepcli-1.45.1 → cleepcli-1.48.0}/requirements.txt +0 -0
  35. {cleepcli-1.45.1 → cleepcli-1.48.0}/setup.cfg +0 -0
  36. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_check.py +0 -0
  37. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_config.py +0 -0
  38. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_console.py +0 -0
  39. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_coverage.py +0 -0
  40. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_docs_breaking.py +0 -0
  41. {cleepcli-1.45.1 → cleepcli-1.48.0}/tests/test_module_scaffold.py +0 -0
@@ -7,3 +7,4 @@ include setup.cfg
7
7
  include MANIFEST.in
8
8
  recursive-include bin *
9
9
  recursive-include cleepcli *.py
10
+ recursive-include cleepcli/systemd *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cleepcli
3
- Version: 1.45.1
3
+ Version: 1.48.0
4
4
  Summary: Cleep-cli helps developers to build great Cleep applications from command line.
5
5
  Home-page: https://github.com/CleepDevice/cleep-cli
6
6
  Author: Tanguy Bonneau
@@ -42,7 +42,7 @@ This utility helps developers to build Cleep applications providing some useful
42
42
  - `coreget` to clone or pull official Cleep repository.
43
43
  - `coredocs` to generate core documentation in HTML format.
44
44
  - `coresync` to synchronize sources from repository to execution folders.
45
- - `coretests` to perform core tests suite.
45
+ - `coretests` to perform core unit tests suite (`--functional` for real OS binary tests on device / OS migration).
46
46
  - `coretestscov` to display last core tests code coverage report.
47
47
  - `cpuprof` to run CPU profiler on application execution instance.
48
48
  - `memprof` to run memory profiler on application execution instance.
@@ -52,7 +52,7 @@ This utility helps developers to build Cleep applications providing some useful
52
52
  - `moddelete` to delete installed files of a module.
53
53
  - `moddocs` to generate module documentation.
54
54
  - `modsync` to synchronize sources from module folder to execution folders.
55
- - `modtests` to execute module tests.
55
+ - `modtests` to execute module unit tests (`--functional` for real OS/device tests).
56
56
  - `modtestscov` to display last module tests code coverage report.
57
57
  - `modbuild` to create application package (.zip)
58
58
  - `modcheckback` to check app backend (python files structure and content)
@@ -61,7 +61,9 @@ This utility helps developers to build Cleep applications providing some useful
61
61
  - `modcheckscripts` to check app scripts files
62
62
  - `modcheckcode` to check backend code quality (run pylint)
63
63
  - Watch commands:
64
- - `watch` to watch for repository filesystem changes and automatically update files on execution folders. It also restart backend or/and frontend according to changes.
64
+ - `watch` to watch for repository filesystem changes and automatically update files on execution folders. It also restart backend or/and frontend according to changes (catch-up sync on start).
65
+ - `watchsvc` to install/enable/disable/restart the durable `cleep-watch` systemd unit (runs `watch` outside `cleep.service`).
66
+ - `syncall` to force a one-shot sync of core + all modules (same catch-up as watch start).
65
67
  - Misc commands:
66
68
  - `version` to get cleep-cli version
67
69
 
@@ -85,9 +87,23 @@ Execute this command to get help on this cli:
85
87
 
86
88
  ## Watch usage
87
89
 
88
- Launch `watch` cli command to monitor changes on "/root/cleep-dev" directory after getting official Cleep repository using `coreget` command.
90
+ Preferred: let the CLI own the durable systemd unit:
89
91
 
90
- Then open your favorite editor on your development computer, configure a remote sync plugin to push local changes on your raspberry pi then cleep-cli will synchronize sources on Cleep execution environment and restart application automatically.
92
+ ```
93
+ cleep-cli watchsvc install
94
+ cleep-cli watchsvc enable
95
+ ```
96
+
97
+ The unit runs `cleep-cli watch`, survives `systemctl restart cleep`, and on each start performs a catch-up `syncall` so files written while the watcher was down are applied.
98
+
99
+ Manual one-shot resync (when the UI looks stale):
100
+
101
+ ```
102
+ cleep-cli syncall
103
+ cleep-cli watchsvc restart # catch-up again + resume watch
104
+ ```
105
+
106
+ You can still run `cleep-cli watch` in a terminal for debugging. Push sources to `/root/cleep-dev` (CleepDesktop developer sync or SFTP); the watcher copies them into the execution folders and restarts backend/frontend as needed.
91
107
 
92
108
  ### Paths
93
109
 
@@ -6,7 +6,7 @@ This utility helps developers to build Cleep applications providing some useful
6
6
  - `coreget` to clone or pull official Cleep repository.
7
7
  - `coredocs` to generate core documentation in HTML format.
8
8
  - `coresync` to synchronize sources from repository to execution folders.
9
- - `coretests` to perform core tests suite.
9
+ - `coretests` to perform core unit tests suite (`--functional` for real OS binary tests on device / OS migration).
10
10
  - `coretestscov` to display last core tests code coverage report.
11
11
  - `cpuprof` to run CPU profiler on application execution instance.
12
12
  - `memprof` to run memory profiler on application execution instance.
@@ -16,7 +16,7 @@ This utility helps developers to build Cleep applications providing some useful
16
16
  - `moddelete` to delete installed files of a module.
17
17
  - `moddocs` to generate module documentation.
18
18
  - `modsync` to synchronize sources from module folder to execution folders.
19
- - `modtests` to execute module tests.
19
+ - `modtests` to execute module unit tests (`--functional` for real OS/device tests).
20
20
  - `modtestscov` to display last module tests code coverage report.
21
21
  - `modbuild` to create application package (.zip)
22
22
  - `modcheckback` to check app backend (python files structure and content)
@@ -25,7 +25,9 @@ This utility helps developers to build Cleep applications providing some useful
25
25
  - `modcheckscripts` to check app scripts files
26
26
  - `modcheckcode` to check backend code quality (run pylint)
27
27
  - Watch commands:
28
- - `watch` to watch for repository filesystem changes and automatically update files on execution folders. It also restart backend or/and frontend according to changes.
28
+ - `watch` to watch for repository filesystem changes and automatically update files on execution folders. It also restart backend or/and frontend according to changes (catch-up sync on start).
29
+ - `watchsvc` to install/enable/disable/restart the durable `cleep-watch` systemd unit (runs `watch` outside `cleep.service`).
30
+ - `syncall` to force a one-shot sync of core + all modules (same catch-up as watch start).
29
31
  - Misc commands:
30
32
  - `version` to get cleep-cli version
31
33
 
@@ -49,9 +51,23 @@ Execute this command to get help on this cli:
49
51
 
50
52
  ## Watch usage
51
53
 
52
- Launch `watch` cli command to monitor changes on "/root/cleep-dev" directory after getting official Cleep repository using `coreget` command.
54
+ Preferred: let the CLI own the durable systemd unit:
53
55
 
54
- Then open your favorite editor on your development computer, configure a remote sync plugin to push local changes on your raspberry pi then cleep-cli will synchronize sources on Cleep execution environment and restart application automatically.
56
+ ```
57
+ cleep-cli watchsvc install
58
+ cleep-cli watchsvc enable
59
+ ```
60
+
61
+ The unit runs `cleep-cli watch`, survives `systemctl restart cleep`, and on each start performs a catch-up `syncall` so files written while the watcher was down are applied.
62
+
63
+ Manual one-shot resync (when the UI looks stale):
64
+
65
+ ```
66
+ cleep-cli syncall
67
+ cleep-cli watchsvc restart # catch-up again + resume watch
68
+ ```
69
+
70
+ You can still run `cleep-cli watch` in a terminal for debugging. Push sources to `/root/cleep-dev` (CleepDesktop developer sync or SFTP); the watcher copies them into the execution folders and restarts backend/frontend as needed.
55
71
 
56
72
  ### Paths
57
73
 
@@ -17,6 +17,7 @@ from cleepcli.git import Git
17
17
  from cleepcli.module import Module
18
18
  from cleepcli.file import File
19
19
  from cleepcli.watch import CleepWatchdog
20
+ from cleepcli.watchsvc import WatchService
20
21
  from cleepcli.test import Test, is_file_coverage_valid
21
22
  from cleepcli.package import Package
22
23
  from cleepcli.docs import Docs
@@ -108,18 +109,31 @@ def coresync():
108
109
  sys.exit(1)
109
110
  logging.info('Done')
110
111
 
112
+ @core.command()
113
+ def syncall():
114
+ """
115
+ Synchronize core and all modules from /root/cleep-dev to execution folders.
116
+
117
+ Same catch-up used when cleep-watch.service starts. Safe one-shot resync when
118
+ the UI looks stale: run this, then reload the device page.
119
+ """
120
+ f = File()
121
+ if not f.sync_all():
122
+ sys.exit(1)
123
+
111
124
  @core.command()
112
125
  @click.option('--coverage', is_flag=True, help='Display coverage report.')
113
126
  @click.option('--output', is_flag=True, help='Display tests output.')
114
127
  @click.option('--xml', is_flag=True, default=False, help='Use xml reporting instead of default one (report).')
115
128
  @click.option('--quiet', is_flag=True, default=True, help='Display or not coverage warnings.')
116
129
  @click.option('-p', '--pattern', default=None, help='Run tests on specified file pattern only')
117
- def coretests(coverage, output, xml, quiet, pattern):
130
+ @click.option('--functional', is_flag=True, help='Run functional tests (real OS binaries) instead of unit tests.')
131
+ def coretests(coverage, output, xml, quiet, pattern, functional):
118
132
  """
119
133
  Execute core tests
120
134
  """
121
135
  m = Test()
122
- res = m.core_tests(coverage, output, xml, quiet, pattern)
136
+ res = m.core_tests(coverage, output, xml, quiet, pattern, functional=functional)
123
137
 
124
138
  if not res:
125
139
  sys.exit(1)
@@ -357,9 +371,13 @@ def watchdog():
357
371
  @click.option('--quiet', is_flag=True, help='Disable logging.')
358
372
  @click.option('--loglevel', default=logging.INFO, help='Logging level (10=DEBUG, 20=INFO, 30=WARN, 40=ERROR).')
359
373
  @click.option('--rpc-url', default='https://127.0.0.1:443', help='Cleep RPC url (ex https://ip|host:port')
360
- def watch(quiet, loglevel, rpc_url):
374
+ @click.option('--skip-catchup', is_flag=True, help='Do not syncall before watching (not recommended).')
375
+ def watch(quiet, loglevel, rpc_url, skip_catchup):
361
376
  """
362
- Start watchdog that monitors filesystem changes on Cleep sources
377
+ Start watchdog that monitors filesystem changes on Cleep sources.
378
+
379
+ Prefer managing the durable unit with watchsvc (survives Cleep restarts).
380
+ On start, syncs core + all modules once unless --skip-catchup is set.
363
381
  """
364
382
  if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
365
383
  # do not overwrite root logger level if configured to DEBUG (dev mode)
@@ -368,13 +386,51 @@ def watch(quiet, loglevel, rpc_url):
368
386
  logging.disable(logging.CRITICAL)
369
387
  else:
370
388
  logging.getLogger().setLevel(loglevel)
371
-
372
- w = CleepWatchdog(rpc_url)
389
+
390
+ w = CleepWatchdog(rpc_url, catch_up=not skip_catchup)
373
391
  res = w.watch()
374
392
 
375
393
  if not res:
376
394
  sys.exit(1)
377
395
 
396
+ @watchdog.command('watchsvc')
397
+ @click.argument(
398
+ 'action',
399
+ type=click.Choice(
400
+ ['install', 'uninstall', 'enable', 'disable', 'start', 'stop', 'restart', 'status']
401
+ ),
402
+ )
403
+ def watchsvc_cmd(action):
404
+ """
405
+ Manage the durable cleep-watch systemd unit.
406
+
407
+ ACTION is one of: install, uninstall, enable, disable, start, stop, restart, status.
408
+
409
+ The unit runs ``cleep-cli watch`` outside of cleep.service. Typical setup::
410
+
411
+ cleep-cli watchsvc install
412
+ cleep-cli watchsvc enable
413
+ """
414
+ svc = WatchService()
415
+ handlers = {
416
+ 'install': svc.install,
417
+ 'uninstall': svc.uninstall,
418
+ 'enable': svc.enable,
419
+ 'disable': svc.disable,
420
+ 'start': svc.start,
421
+ 'stop': svc.stop,
422
+ 'restart': svc.restart,
423
+ }
424
+ if action == 'status':
425
+ status = svc.status()
426
+ logging.info(status)
427
+ if status != 'active':
428
+ sys.exit(1)
429
+ return
430
+
431
+ if not handlers[action]():
432
+ sys.exit(1)
433
+
378
434
  @click.group()
379
435
  def test():
380
436
  pass
@@ -385,7 +441,8 @@ def test():
385
441
  @click.option('--coverage', is_flag=True, help='Display coverage report.')
386
442
  @click.option('--copyto', help='Copy .coverage file to specified dir.')
387
443
  @click.option('-p', '--pattern', help='Execute tests matching pattern.')
388
- def modtests(ctx, module, coverage, copyto, pattern):
444
+ @click.option('--functional', is_flag=True, help='Run functional tests (real OS/device) instead of unit tests.')
445
+ def modtests(ctx, module, coverage, copyto, pattern, functional):
389
446
  """
390
447
  Execute module tests
391
448
  """
@@ -393,7 +450,7 @@ def modtests(ctx, module, coverage, copyto, pattern):
393
450
  ctx.invoke(modsync, module=module)
394
451
 
395
452
  m = Test()
396
- res = m.module_tests(module, coverage, copyto, pattern)
453
+ res = m.module_tests(module, coverage, copyto, pattern, functional=functional)
397
454
 
398
455
  if not res:
399
456
  sys.exit(1)
@@ -17,6 +17,7 @@ from cleepcli.git import Git
17
17
  from cleepcli.module import Module
18
18
  from cleepcli.file import File
19
19
  from cleepcli.watch import CleepWatchdog
20
+ from cleepcli.watchsvc import WatchService
20
21
  from cleepcli.test import Test, is_file_coverage_valid
21
22
  from cleepcli.package import Package
22
23
  from cleepcli.docs import Docs
@@ -108,18 +109,31 @@ def coresync():
108
109
  sys.exit(1)
109
110
  logging.info('Done')
110
111
 
112
+ @core.command()
113
+ def syncall():
114
+ """
115
+ Synchronize core and all modules from /root/cleep-dev to execution folders.
116
+
117
+ Same catch-up used when cleep-watch.service starts. Safe one-shot resync when
118
+ the UI looks stale: run this, then reload the device page.
119
+ """
120
+ f = File()
121
+ if not f.sync_all():
122
+ sys.exit(1)
123
+
111
124
  @core.command()
112
125
  @click.option('--coverage', is_flag=True, help='Display coverage report.')
113
126
  @click.option('--output', is_flag=True, help='Display tests output.')
114
127
  @click.option('--xml', is_flag=True, default=False, help='Use xml reporting instead of default one (report).')
115
128
  @click.option('--quiet', is_flag=True, default=True, help='Display or not coverage warnings.')
116
129
  @click.option('-p', '--pattern', default=None, help='Run tests on specified file pattern only')
117
- def coretests(coverage, output, xml, quiet, pattern):
130
+ @click.option('--functional', is_flag=True, help='Run functional tests (real OS binaries) instead of unit tests.')
131
+ def coretests(coverage, output, xml, quiet, pattern, functional):
118
132
  """
119
133
  Execute core tests
120
134
  """
121
135
  m = Test()
122
- res = m.core_tests(coverage, output, xml, quiet, pattern)
136
+ res = m.core_tests(coverage, output, xml, quiet, pattern, functional=functional)
123
137
 
124
138
  if not res:
125
139
  sys.exit(1)
@@ -357,9 +371,13 @@ def watchdog():
357
371
  @click.option('--quiet', is_flag=True, help='Disable logging.')
358
372
  @click.option('--loglevel', default=logging.INFO, help='Logging level (10=DEBUG, 20=INFO, 30=WARN, 40=ERROR).')
359
373
  @click.option('--rpc-url', default='https://127.0.0.1:443', help='Cleep RPC url (ex https://ip|host:port')
360
- def watch(quiet, loglevel, rpc_url):
374
+ @click.option('--skip-catchup', is_flag=True, help='Do not syncall before watching (not recommended).')
375
+ def watch(quiet, loglevel, rpc_url, skip_catchup):
361
376
  """
362
- Start watchdog that monitors filesystem changes on Cleep sources
377
+ Start watchdog that monitors filesystem changes on Cleep sources.
378
+
379
+ Prefer managing the durable unit with watchsvc (survives Cleep restarts).
380
+ On start, syncs core + all modules once unless --skip-catchup is set.
363
381
  """
364
382
  if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
365
383
  # do not overwrite root logger level if configured to DEBUG (dev mode)
@@ -368,13 +386,51 @@ def watch(quiet, loglevel, rpc_url):
368
386
  logging.disable(logging.CRITICAL)
369
387
  else:
370
388
  logging.getLogger().setLevel(loglevel)
371
-
372
- w = CleepWatchdog(rpc_url)
389
+
390
+ w = CleepWatchdog(rpc_url, catch_up=not skip_catchup)
373
391
  res = w.watch()
374
392
 
375
393
  if not res:
376
394
  sys.exit(1)
377
395
 
396
+ @watchdog.command('watchsvc')
397
+ @click.argument(
398
+ 'action',
399
+ type=click.Choice(
400
+ ['install', 'uninstall', 'enable', 'disable', 'start', 'stop', 'restart', 'status']
401
+ ),
402
+ )
403
+ def watchsvc_cmd(action):
404
+ """
405
+ Manage the durable cleep-watch systemd unit.
406
+
407
+ ACTION is one of: install, uninstall, enable, disable, start, stop, restart, status.
408
+
409
+ The unit runs ``cleep-cli watch`` outside of cleep.service. Typical setup::
410
+
411
+ cleep-cli watchsvc install
412
+ cleep-cli watchsvc enable
413
+ """
414
+ svc = WatchService()
415
+ handlers = {
416
+ 'install': svc.install,
417
+ 'uninstall': svc.uninstall,
418
+ 'enable': svc.enable,
419
+ 'disable': svc.disable,
420
+ 'start': svc.start,
421
+ 'stop': svc.stop,
422
+ 'restart': svc.restart,
423
+ }
424
+ if action == 'status':
425
+ status = svc.status()
426
+ logging.info(status)
427
+ if status != 'active':
428
+ sys.exit(1)
429
+ return
430
+
431
+ if not handlers[action]():
432
+ sys.exit(1)
433
+
378
434
  @click.group()
379
435
  def test():
380
436
  pass
@@ -385,7 +441,8 @@ def test():
385
441
  @click.option('--coverage', is_flag=True, help='Display coverage report.')
386
442
  @click.option('--copyto', help='Copy .coverage file to specified dir.')
387
443
  @click.option('-p', '--pattern', help='Execute tests matching pattern.')
388
- def modtests(ctx, module, coverage, copyto, pattern):
444
+ @click.option('--functional', is_flag=True, help='Run functional tests (real OS/device) instead of unit tests.')
445
+ def modtests(ctx, module, coverage, copyto, pattern, functional):
389
446
  """
390
447
  Execute module tests
391
448
  """
@@ -393,7 +450,7 @@ def modtests(ctx, module, coverage, copyto, pattern):
393
450
  ctx.invoke(modsync, module=module)
394
451
 
395
452
  m = Test()
396
- res = m.module_tests(module, coverage, copyto, pattern)
453
+ res = m.module_tests(module, coverage, copyto, pattern, functional=functional)
397
454
 
398
455
  if not res:
399
456
  sys.exit(1)
@@ -1497,6 +1497,50 @@ overgeneral-exceptions=Exception
1497
1497
  """
1498
1498
  return self._check_module_tree(module_name, "asset")
1499
1499
 
1500
+ def check_firmwares(self, module_name):
1501
+ """
1502
+ Validate optional ``firmwares/`` zip+sidecar ``.manifest`` pairs and list
1503
+ files for packaging (see ``cleep.libs.internals.firmware_manifest``).
1504
+
1505
+ Returns:
1506
+ dict: errors / warnings / files (same shape as check_scripts)
1507
+ """
1508
+ if not os.path.exists(os.path.join(config.MODULES_DST, module_name)):
1509
+ raise Exception('Module "%s" does not exist' % module_name)
1510
+
1511
+ firmwares_path = os.path.join(config.MODULES_SRC, module_name, "firmwares")
1512
+ out = {
1513
+ "errors": [],
1514
+ "warnings": [],
1515
+ "files": [],
1516
+ }
1517
+ if not os.path.isdir(firmwares_path):
1518
+ return out
1519
+
1520
+ try:
1521
+ from cleep.libs.internals.firmware_manifest import (
1522
+ FirmwareManifestError,
1523
+ scan_firmwares_dir,
1524
+ )
1525
+ except ImportError:
1526
+ out["errors"].append(
1527
+ "Unable to import cleep.libs.internals.firmware_manifest "
1528
+ "(is Cleep core available on PYTHONPATH?)"
1529
+ )
1530
+ return out
1531
+
1532
+ try:
1533
+ scan_firmwares_dir(firmwares_path)
1534
+ except FirmwareManifestError as error:
1535
+ out["errors"].append(str(error))
1536
+ return out
1537
+
1538
+ # Include valid pairs in the package file list
1539
+ tree = self._check_module_tree(module_name, "firmwares")
1540
+ out["files"] = tree["files"]
1541
+ out["warnings"] = tree["warnings"]
1542
+ return out
1543
+
1500
1544
  def check_tests(self, module_name):
1501
1545
  """
1502
1546
  Check test files
@@ -60,6 +60,51 @@ rsync -av "%(REPO_DIR)s/medias/sounds" "%(MEDIA_DST)s/sounds/" --delete
60
60
 
61
61
  return True
62
62
 
63
+ def list_modules(self):
64
+ """
65
+ List module folder names under MODULES_SRC (skips hidden dirs).
66
+
67
+ Returns:
68
+ list: sorted module names
69
+ """
70
+ if not os.path.isdir(config.MODULES_SRC):
71
+ return []
72
+ names = []
73
+ try:
74
+ for name in os.listdir(config.MODULES_SRC):
75
+ if name.startswith("."):
76
+ continue
77
+ path = os.path.join(config.MODULES_SRC, name)
78
+ if os.path.isdir(path):
79
+ names.append(name)
80
+ except OSError as error:
81
+ self.logger.error("Unable to list modules: %s", error)
82
+ return []
83
+ return sorted(names)
84
+
85
+ def sync_all(self):
86
+ """
87
+ Synchronize core and every module from REPO_DIR to execution folders.
88
+
89
+ Used as watch catch-up on start and by the ``syncall`` CLI command so a
90
+ full resync is always one explicit action away.
91
+
92
+ Returns:
93
+ bool: True if every sync succeeded
94
+ """
95
+ self.logger.info("Synchronizing core sources...")
96
+ if not self.core_sync():
97
+ return False
98
+
99
+ modules = self.list_modules()
100
+ for module_name in modules:
101
+ self.logger.info('Synchronizing module "%s" sources...', module_name)
102
+ if not self.module_sync(module_name):
103
+ return False
104
+
105
+ self.logger.info("Sync all done (%d module(s))", len(modules))
106
+ return True
107
+
63
108
  def module_sync(self, module_name, backend=True, frontend=True, scripts=True):
64
109
  """
65
110
  Copy specified module content to valid execution location
@@ -488,6 +488,8 @@ pytest_plugins = ('cleep.libs.tests.fixtures',)
488
488
  pythonpath = .
489
489
  testpaths = tests
490
490
  python_files = test_*.py
491
+ markers =
492
+ functional: real OS/device tests (CLEEP_FUNCTIONAL_TESTS / modtests --functional)
491
493
  """
492
494
  DOCS_CONF_PY = """# -*- coding: utf-8 -*-
493
495
  import os, sys
@@ -516,6 +516,12 @@ sha256sum $DEB > $SHA256
516
516
  "Error in asset/. Fix it before packaging application: %s"
517
517
  % data_asset["errors"]
518
518
  )
519
+ data_firmwares = check.check_firmwares(module_name)
520
+ if len(data_firmwares["errors"]) > 0:
521
+ raise Exception(
522
+ "Error in firmwares/. Fix it before packaging application: %s"
523
+ % data_firmwares["errors"]
524
+ )
519
525
  data_tests = check.check_tests(module_name)
520
526
  if len(data_tests["errors"]) > 0:
521
527
  raise Exception(
@@ -629,6 +635,10 @@ sha256sum $DEB > $SHA256
629
635
  for a_file in data_asset["files"]:
630
636
  archive.write(a_file["fullpath"], a_file["path"])
631
637
 
638
+ # add firmwares/ (zip + sidecar .manifest; installer copies to APP_FIRMWARE)
639
+ for a_file in data_firmwares["files"]:
640
+ archive.write(a_file["fullpath"], a_file["path"])
641
+
632
642
  # add module.json
633
643
  archive.write(module_json, "module.json")
634
644
 
@@ -0,0 +1,18 @@
1
+ [Unit]
2
+ Description=Cleep development source watcher (cleep-cli watch)
3
+ Documentation=https://github.com/CleepDevice/cleep-cli
4
+ # Independent from cleep.service so backend restarts do not kill the watcher
5
+ # mid-sync (Desktop writes would otherwise be missed until the next edit).
6
+ After=network-online.target
7
+ Wants=network-online.target
8
+
9
+ [Service]
10
+ Type=simple
11
+ ExecStart=/usr/local/bin/cleep-cli watch --loglevel=20
12
+ Restart=always
13
+ RestartSec=3
14
+ # Allow a long catch-up sync on start before the main loop
15
+ TimeoutStartSec=180
16
+
17
+ [Install]
18
+ WantedBy=multi-user.target