Compare commits

...

139 Commits

Author SHA1 Message Date
4a53bc4abc 1.17.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-15 10:12:24 +00:00
a86fb3bb8e fix(serviceconfig): Update service configuration to include dynamic MongoDB connection string and add local permissions settings 2025-08-15 10:12:24 +00:00
b187000ae4 1.17.2
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-15 09:48:00 +00:00
c715adfd6c fix(ci-test-services): Update CI/CD configurations, test settings, and Docker service for MongoDB. 2025-08-15 09:48:00 +00:00
7b9ebfdacb fix(services): Improve logging and enhance MongoDB Compass integration
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-15 09:37:54 +00:00
05b170cbac feat(services): Add comprehensive development services management (v1.17.0)
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
- Implemented gitzone services command for managing MongoDB and MinIO containers
- Added smart port assignment (20000-30000 range) to avoid conflicts
- Project-specific container names for complete isolation
- Data persistence in .nogit/ directories
- MongoDB Compass connection string generation with network IP detection
- Auto-configuration via .nogit/env.json with secure defaults
- Commands: start, stop, restart, status, config, compass, logs, remove, clean
- Interactive confirmations for destructive operations
- Comprehensive documentation and Task Venture Capital GmbH legal update
2025-08-14 14:38:27 +00:00
b320af0b61 1.16.10
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 09:46:34 +00:00
49e1ee1f39 fix(format): Improve concurrency control in caching and rollback modules, refine gitignore custom section handling, and enhance Prettier file processing. 2025-08-08 09:46:34 +00:00
cef31cf1ff 1.16.9
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 06:50:58 +00:00
74ecdde1ac fix(format): Improve concurrency control in cache and rollback modules, refine gitignore custom section handling, and enhance Prettier file processing 2025-08-08 06:50:58 +00:00
74a8229e43 1.16.8
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 06:25:40 +00:00
859cbc733d fix(format): Improve concurrency control in cache and rollback management with mutex locking and refine formatting details 2025-08-08 06:25:40 +00:00
d32d47b706 1.16.7
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 05:48:41 +00:00
fd90cfe895 fix(core): Improve formatting, logging, and rollback integrity in core modules 2025-08-08 05:48:41 +00:00
c48f48fc8b 1.16.6
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 05:43:34 +00:00
e21e7f0850 fix(changecache): Improve cache manifest validation and atomic file writes; add local settings and overrides 2025-08-08 05:43:34 +00:00
5f561527f9 1.16.5
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 05:34:54 +00:00
9f5f568c3f fix(prettier): Improve file selection in Prettier formatter, remove legacy package overrides, and update CI template indentation 2025-08-08 05:34:54 +00:00
39a31a4304 1.16.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 05:28:02 +00:00
b629a7d70b fix(prettier): Improve file exclusion in the Prettier formatter to skip unnecessary files and directories. 2025-08-08 05:28:01 +00:00
4003944139 1.16.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-08 05:18:19 +00:00
83d374dffd fix(changecache/prettier): Skip directories during file processing to prevent errors in changecache and prettier formatting 2025-08-08 05:18:19 +00:00
49a5a66440 fix(format): Fix format command confirmation prompt to correctly check user response
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-07 21:13:12 +00:00
20a53d4d92 1.16.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-04 13:16:56 +00:00
fe02b990b3 fix(package/config): Move smartdiff dependency to runtime and add local bash permissions settings 2025-08-04 13:16:56 +00:00
c013fbf42e 1.16.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-19 13:34:23 +00:00
949f273317 feat(format): Enhance format module with rollback, diff reporting, and improved parallel execution 2025-05-19 13:34:23 +00:00
7b2ae01112 1.15.5
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-14 12:25:01 +00:00
53421e79d8 fix(dependencies): Update @git.zone/tsdoc to ^1.5.0 and @types/node to ^22.15.18 2025-05-14 12:25:01 +00:00
eec803e512 1.15.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-13 21:20:54 +00:00
6d11515b47 fix(package.json): Update dependency versions: bump @git.zone/tsdoc, @push.rocks/lik, @push.rocks/smartlog, and @types/node to their latest releases 2025-05-13 21:20:54 +00:00
6a7b4c8b7e 1.15.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-15 22:16:16 +00:00
25c0162c39 fix(deps): update dependency versions and improve website template variable handling 2025-04-15 22:16:16 +00:00
e66d1f05e4 1.15.2
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-15 16:06:28 +00:00
b1a8a5527e fix(website_update): Await supplyVariables call in website update template 2025-04-15 16:06:27 +00:00
0017781516 1.15.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-15 15:54:04 +00:00
da0cd0ed71 fix(cli): Refresh internal CLI tooling and configuration for consistency. 2025-04-15 15:54:03 +00:00
4890afb2f0 1.15.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-15 15:53:34 +00:00
77bb8bf22e feat(config/template): Add assetbrokerUrl and legalUrl fields to module config and update website template to supply these values 2025-04-15 15:53:34 +00:00
ff2ebc076d 1.14.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-15 15:36:51 +00:00
d71b082b75 fix(package.json): Add packageManager field to specify pnpm version for consistent package management 2025-04-15 15:36:51 +00:00
f6680dc45e 1.14.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-04-15 15:35:33 +00:00
cb4a02d1ef feat(tsconfig_update): Add runafter directive to trigger gitzone format after tsconfig update 2025-04-15 15:35:33 +00:00
9e39444daa 1.13.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-03-07 02:30:14 +00:00
6ec5d275bb fix(cli): Improve commit message logging 2025-03-07 02:30:14 +00:00
f5d78f3b40 1.13.0 2025-02-28 18:00:11 +00:00
68546939ca feat(templates): Updated and added new TypeScript template files for npm projects 2025-02-28 18:00:11 +00:00
08e9849116 1.12.8
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-02-25 18:47:41 +00:00
f25ade3e97 fix(metadata): Updated package and npmextra json description and keywords for enhanced development workflow clarity 2025-02-25 18:47:41 +00:00
6870c40e57 1.12.7
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-02-25 18:43:56 +00:00
5e99a37a55 fix(meta): Fix issues in project metadata and configuration. 2025-02-25 18:43:56 +00:00
44d1cefc9f fix(ci): Updated dependencies and added CI/CD workflows. 2025-02-25 18:43:29 +00:00
83bafbb6f6 1.12.6
Some checks failed
Default (tags) / security (push) Failing after 2s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-29 17:56:21 +01:00
b90234e226 fix(project): Minor fixes and cleanup 2025-01-29 17:56:21 +01:00
a039a58f8e 1.12.5
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-29 15:38:36 +01:00
d861fcb2d4 fix(cli): Initial implementation of CLI utility with project management features 2025-01-29 15:38:35 +01:00
3a1138da83 1.12.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-29 14:45:43 +01:00
bdbd2b6e08 update 2025-01-29 14:45:32 +01:00
43f5643ab1 1.12.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-29 14:42:30 +01:00
99e4dcb2e8 update 2025-01-29 14:42:24 +01:00
52f6afb69a 1.12.2
Some checks failed
Default (tags) / security (push) Failing after 2s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-29 14:39:31 +01:00
75c1e3d8f2 fix(format): Add overrides for peek-readable in package.json formatting 2025-01-29 14:39:31 +01:00
2a1ed874b2 1.12.1
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-18 12:38:15 +01:00
58fb0bc3d5 fix(dependencies): Update various package dependencies and Dockerfile base image 2025-01-18 12:38:14 +01:00
b1fbd38dba 1.12.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-17 17:48:56 +01:00
fad5e046d3 feat(build): Update TypeScript configuration to support emit decorator metadata 2025-01-17 17:48:56 +01:00
eb1498c242 1.11.0
Some checks failed
Default (tags) / security (push) Failing after 2s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-08 22:31:12 +01:00
6b3d4d1ecf feat(cli): Add Docker command for cleaning up Docker system and extend deprecation command for multiple registries 2025-01-08 22:31:11 +01:00
16ea9bb8bd 1.10.10
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-01 05:33:40 +01:00
7e7aa098f9 fix(templates): Corrected typo in template file comment 2025-01-01 05:33:40 +01:00
df4350d29d 1.10.9
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-01 05:32:10 +01:00
81eb713dee fix(templates): Correct template file paths and organization for service projects 2025-01-01 05:32:10 +01:00
90637b1466 1.10.8
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-01-01 05:02:37 +01:00
6c3b78d0fe fix(assets/templates): Update CI template configurations to use module.githost 2025-01-01 05:02:36 +01:00
f0c7cf34e6 1.10.7
Some checks failed
Default (tags) / security (push) Failing after 2s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-12-26 14:48:07 +01:00
b9b5736e5e fix(assets): Correct URLs in templates and fix TypeScript declaration 2024-12-26 14:48:07 +01:00
b73acf904a 1.10.6
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-12-08 17:47:45 +01:00
6614529122 fix(ci): Corrected Docker image URL in CI templates 2024-12-08 17:47:45 +01:00
9ff5e9a70f 1.10.5
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-12-02 16:14:36 +01:00
9dbf511d50 fix(assets): Update .gitignore template to remove pages directory 2024-12-02 16:14:35 +01:00
b7a239abca 1.10.4
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-11-05 01:57:40 +01:00
181fad27fd fix(mod_format): Correct file extension for TypeScript path configuration 2024-11-05 01:57:39 +01:00
59c3deb83a 1.10.3
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-10-27 02:18:13 +02:00
c76403a34c fix(mod_format): Reorder TypeScript formatting steps in mod_format module 2024-10-27 02:18:13 +02:00
23b361f26a 1.10.2
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-10-27 02:13:12 +02:00
9f7d447bc7 fix(format): Add logging for tsconfig.json formatting 2024-10-27 02:13:12 +02:00
8904bf9427 1.10.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-10-27 02:09:04 +02:00
3c5a8282ab fix(format): Fixed async issue in tsconfig module lookup and corrected property access 2024-10-27 02:09:04 +02:00
4f0fb26a15 1.10.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-10-27 01:51:17 +02:00
00f06b48f3 feat(mod_format): Add support for tsconfig.json formatting 2024-10-27 01:51:17 +02:00
74f4cb3034 1.9.126
Some checks failed
Default (tags) / security (push) Failing after 6s
Default (tags) / test (push) Failing after 2s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-10-23 22:11:19 +02:00
b98dc7a9fa fix(format): Remove redundant package.json property checks 2024-10-23 22:11:18 +02:00
924470407a 1.9.125
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-09-29 16:48:24 +02:00
f346a397d5 fix(cli): Fix package version configuration and formatting issues 2024-09-29 16:48:23 +02:00
c05f2d35e5 1.9.124
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-09-27 23:26:31 +02:00
c0a409bbc9 fix(cli): Ensured proper existence and initialization of readme files 2024-09-27 23:26:31 +02:00
7cb798077d 1.9.123
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-09-27 23:13:00 +02:00
fc4409bea4 fix(core): No changes detected 2024-09-27 23:13:00 +02:00
27ee8195e8 fix(core): Update dependencies and improve build configurations 2024-09-27 23:12:02 +02:00
2f80abafc2 1.9.122
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-24 00:03:54 +02:00
0d95cda8b5 fix(mod_commit): Update package.json dependencies: @git.zone/tsdoc and @push.rocks/smartpromise to latest versions. 2024-06-24 00:03:53 +02:00
4a9d98b3f9 1.9.121
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 23:52:49 +02:00
c99ff2556e fix(mod_commit): Fix changelog template rendering by removing extra new line when no version details are provided. 2024-06-23 23:52:48 +02:00
f1d75a3aba 1.9.120
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 23:41:20 +02:00
377fa83fe7 fix(mod_commit): Handle edge case for empty version details in changelog formatting 2024-06-23 23:41:19 +02:00
8199c6c2cc 1.9.119
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 23:35:34 +02:00
3ef785438e fix(dependencies): Update @git.zone/tsdoc to v1.3.8 2024-06-23 23:35:33 +02:00
d8fe8bae64 1.9.118
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 23:25:11 +02:00
ad4175b3f9 fix(dependencies): Update @git.zone/tsdoc to version 1.3.7 2024-06-23 23:25:11 +02:00
707c1718a3 1.9.117
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 23:21:20 +02:00
7e0df65de6 fix(dependencies): Update @git.zone/tsdoc dependency to v1.3.6 2024-06-23 23:21:19 +02:00
fe30629ff9 1.9.116
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 23:07:31 +02:00
05bf7aa9d6 fix(dependencies): Update @git.zone/tsdoc to version 1.3.5 2024-06-23 23:07:30 +02:00
847798e9c9 1.9.115
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 22:57:10 +02:00
b797fca1fc fix(format): Fixed formatting issues across multiple TypeScript files. 2024-06-23 22:57:09 +02:00
ae46afdee6 1.9.114
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 22:54:46 +02:00
ceddc23423 fix(mod_commit): Remove extra new lines in changelog 2024-06-23 22:54:45 +02:00
4f8ca3fe5e 1.9.113
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 22:54:12 +02:00
947fe781d8 fix(core): Update changelog formatting and remove outdated entries 2024-06-23 22:54:11 +02:00
1c7fbb073d 1.9.112
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 22:52:41 +02:00
9b05252045 fix(changelog): Remove outdated changelog entries and update formatting 2024-06-23 22:52:40 +02:00
f34718bfb3 1.9.111
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 22:49:13 +02:00
0db0b17e0d fix(dependencies): Update @git.zone/tsdoc to version 1.3.4 2024-06-23 22:49:12 +02:00
7c4dffd142 1.9.110
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 20:00:55 +02:00
d716364cb0 fix(changelog): Remove outdated entries and adjust formatting in changelog 2024-06-23 20:00:55 +02:00
53cb139277 1.9.109
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 19:58:19 +02:00
5c0834aa48 fix(dependencies): Update @git.zone/tsdoc dependency to version 1.3.2 2024-06-23 19:58:18 +02:00
a2df347ee3 1.9.108
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 19:53:30 +02:00
632bdd7847 fix(changelog): Remove placeholder entries and adjust formatting in changelog. 2024-06-23 19:53:29 +02:00
5135be478f 1.9.107
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 19:51:18 +02:00
5fc9bb0245 fix(dependencies): Updated @git.zone/tsdoc from version 1.3.0 to 1.3.1 2024-06-23 19:51:17 +02:00
a3b7a33bc2 1.9.106
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 18:41:16 +02:00
51fd699965 fix(dependencies): Updated @git.zone/tsdoc dependency from 1.2.2 to 1.3.0 in package.json and pnpm-lock.yaml. 2024-06-23 18:41:15 +02:00
45020805cb 1.9.105
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 17:45:06 +02:00
c9e384567e fix(changelog): Remove placeholder entries and adjust formatting in changelog. 2024-06-23 17:45:05 +02:00
152b436096 1.9.104
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 17:43:27 +02:00
99f88236cb fix(changelog): Fix changelog to remove placeholder entries and adjust formatting 2024-06-23 17:43:26 +02:00
af0cb494fd 1.9.103
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2024-06-23 17:41:51 +02:00
dc43deece9 fix(logging): Optimize logger instantiation and configuration. 2024-06-23 17:41:51 +02:00
93 changed files with 10158 additions and 2046 deletions

5
.gitignore vendored
View File

@@ -3,7 +3,6 @@
# artifacts # artifacts
coverage/ coverage/
public/ public/
pages/
# installs # installs
node_modules/ node_modules/
@@ -17,4 +16,6 @@ node_modules/
dist/ dist/
dist_*/ dist_*/
# custom #------# custom
.serena
test-output.json

1
assets/overrides.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -6,8 +6,8 @@ on:
- '**' - '**'
env: env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-} NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-} NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-} NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}

View File

@@ -6,8 +6,8 @@ on:
- '*' - '*'
env: env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-} NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-} NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-} NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}

View File

@@ -27,8 +27,8 @@ auditProductionDependencies:
image: code.foss.global/hosttoday/ht-docker-node:npmci image: code.foss.global/hosttoday/ht-docker-node:npmci
stage: security stage: security
script: script:
- npmci command npm config set registry https://registry.npmjs.org - npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --prod - npmci command pnpm audit --audit-level=high --prod
tags: tags:
- private - private
- docker - docker

View File

@@ -7,7 +7,7 @@ on:
env: env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-} NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-} NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-} NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}

View File

@@ -7,7 +7,7 @@ on:
env: env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-} NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-} NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-} NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}

View File

@@ -27,8 +27,8 @@ auditProductionDependencies:
image: code.foss.global/hosttoday/ht-docker-node:npmci image: code.foss.global/hosttoday/ht-docker-node:npmci
stage: security stage: security
script: script:
- npmci command npm config set registry https://registry.npmjs.org - npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --prod - npmci command pnpm audit --audit-level=high --prod
tags: tags:
- private - private
- docker - docker

View File

@@ -7,7 +7,7 @@ on:
env: env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{gi.host}}/${-{gitea.repository}-}.git NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-} NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-} NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-} NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}

View File

@@ -7,7 +7,7 @@ on:
env: env:
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-} NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-} NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-} NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
@@ -89,8 +89,8 @@ jobs:
npmci docker login npmci docker login
npmci docker build npmci docker build
npmci docker test npmci docker test
# npmci docker push {{git.host}} # npmci docker push {{module.githost}}
npmci docker push {{git.host}} npmci docker push {{module.githost}}
metadata: metadata:
needs: test needs: test

View File

@@ -25,8 +25,8 @@ auditProductionDependencies:
image: code.foss.global/hosttoday/ht-docker-node:npmci image: code.foss.global/hosttoday/ht-docker-node:npmci
stage: security stage: security
script: script:
- npmci command npm config set registry https://registry.npmjs.org - npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --prod - npmci command pnpm audit --audit-level=high --prod
tags: tags:
- private - private
- docker - docker

View File

@@ -1,6 +1,6 @@
# gitzone dockerfile_service # gitzone dockerfile_service
## STAGE 1 // BUILD ## STAGE 1 // BUILD
FROM code.foss.global/hosttoday/ht-docker-node:npmci as node1 FROM code.foss.global/host.today/ht-docker-node:npmci as node1
COPY ./ /app COPY ./ /app
WORKDIR /app WORKDIR /app
ARG NPMCI_TOKEN_NPM2 ARG NPMCI_TOKEN_NPM2
@@ -12,7 +12,7 @@ RUN pnpm run build
# gitzone dockerfile_service # gitzone dockerfile_service
## STAGE 2 // install production ## STAGE 2 // install production
FROM code.foss.global/hosttoday/ht-docker-node:npmci as node2 FROM code.foss.global/host.today/ht-docker-node:npmci as node2
WORKDIR /app WORKDIR /app
COPY --from=node1 /app /app COPY --from=node1 /app /app
RUN rm -rf .pnpm-store RUN rm -rf .pnpm-store
@@ -24,7 +24,7 @@ RUN rm -rf node_modules/ && pnpm install --prod
## STAGE 3 // rebuild dependencies for alpine ## STAGE 3 // rebuild dependencies for alpine
FROM code.foss.global/hosttoday/ht-docker-node:alpinenpmci as node3 FROM code.foss.global/host.today/ht-docker-node:alpine_npmci as node3
WORKDIR /app WORKDIR /app
COPY --from=node2 /app /app COPY --from=node2 /app /app
ARG NPMCI_TOKEN_NPM2 ARG NPMCI_TOKEN_NPM2
@@ -34,7 +34,7 @@ RUN pnpm config set store-dir .pnpm-store
RUN pnpm rebuild -r RUN pnpm rebuild -r
## STAGE 4 // the final production image with all dependencies in place ## STAGE 4 // the final production image with all dependencies in place
FROM code.foss.global/hosttoday/ht-docker-node:alpine as node4 FROM code.foss.global/host.today/ht-docker-node:alpine as node4
WORKDIR /app WORKDIR /app
COPY --from=node3 /app /app COPY --from=node3 /app /app

View File

@@ -6,7 +6,6 @@ fileName: .gitignore
# artifacts # artifacts
coverage/ coverage/
public/ public/
pages/
# installs # installs
node_modules/ node_modules/
@@ -20,4 +19,4 @@ node_modules/
dist/ dist/
dist_*/ dist_*/
# custom #------# custom

View File

@@ -2,4 +2,3 @@ runafter:
- git add -A && git commit -m initial - git add -A && git commit -m initial
- git push origin master - git push origin master
- gitzone meta update - gitzone meta update

View File

@@ -0,0 +1,8 @@
---
fileName: paths.ts
---
import * as plugins from './plugins.js';
export const packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../'
);

View File

@@ -0,0 +1,12 @@
---
fileName: plugins.ts
---
// native scope
import * as path from 'path';
export { path };
// @push.rocks scope
import * as smartpath from '@push.rocks/smartpath';
export { smartpath };

View File

@@ -1,7 +0,0 @@
---
fileName: {{module.name}}.plugins.ts
---
const removeme = {};
export {
removeme
}

View File

@@ -21,14 +21,14 @@ fileName: package.json
"@git.zone/tsrun": "^1.2.8", "@git.zone/tsrun": "^1.2.8",
"@git.zone/tstest": "^1.0.28", "@git.zone/tstest": "^1.0.28",
"@git.zone/tswatch": "^2.0.1", "@git.zone/tswatch": "^2.0.1",
"@push.rocks/tapbundle": "^5.0.3" "@push.rocks/tapbundle": "^5.5.4"
}, },
"dependencies": { "dependencies": {
"@api.global/typedserver": "^1.0.24", "@api.global/typedserver": "^3.0.53",
"@push.rocks/projectinfo": "^5.0.1", "@push.rocks/projectinfo": "^5.0.1",
"@push.rocks/qenv": "^4.0.10", "@push.rocks/qenv": "^6.1.0",
"@push.rocks/smartdata": "^5.0.7", "@push.rocks/smartdata": "^5.0.7",
"@push.rocks/smartpath": "^5.0.5", "@push.rocks/smartpath": "^5.0.5",
"@push.rocks/smartstate": "^2.0.0" "@push.rocks/smartstate": "^2.0.0"
} },
} }

View File

@@ -1,8 +1,8 @@
--- ---
fileName: {{module.name}}.classes.{{module.name}}db.ts fileName: classes.{{module.name}}db.ts
--- ---
import * as plugins from './{{module.name}}.plugins.js'; import * as plugins from './{{module.name}}.plugins.js';
import { {{module.name}} } from './{{module.name}}.classes.{{module.name}}.js'; import { {{module.name}} } from './classes.{{module.name}}.js';

View File

@@ -1,9 +1,9 @@
--- ---
fileName: {{module.name}}.classes.{{module.name}}.ts fileName: classes.{{module.name}}.ts
--- ---
import * as plugins from './{{module.name}}.plugins.js'; import * as plugins from './{{module.name}}.plugins.js';
import * as paths from './{{module.name}}.paths.js'; import * as paths from './{{module.name}}.paths.js';
import { {{module.name}}Db } from './{{module.name}}.db.js' import { {{module.name}}Db } from './classes.{{module.name}}db.js'
export class {{module.name}} { export class {{module.name}} {
public projectinfo: plugins.projectinfo.ProjectInfo; public projectinfo: plugins.projectinfo.ProjectInfo;

View File

@@ -15,8 +15,7 @@ export {
loleServiceserver loleServiceserver
} }
// pushrocks scope // @push.rocks scope
// pushrocks scope
import * as projectinfo from '@push.rocks/projectinfo'; import * as projectinfo from '@push.rocks/projectinfo';
import * as qenv from '@push.rocks/qenv'; import * as qenv from '@push.rocks/qenv';
import * as smartdata from '@push.rocks/smartdata'; import * as smartdata from '@push.rocks/smartdata';

View File

@@ -0,0 +1,2 @@
runafter:
- gitzone format

View File

@@ -1,6 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false, "useDefineForClassFields": false,
"target": "ES2022", "target": "ES2022",
"module": "NodeNext", "module": "NodeNext",

View File

@@ -0,0 +1,2 @@
export * from './page2.js';
export * from './page1.js';

View File

@@ -0,0 +1,5 @@
import { html } from "@design.estate/dees-element";
export const page1 = () => html`
<first-element></first-element>
`;

View File

@@ -0,0 +1,5 @@
import { html } from "@design.estate/dees-element";
export const page2 = () => html`
<first-element></first-element>
`;

View File

@@ -18,12 +18,12 @@ fileName: package.json
"author": "{{author.name}}", "author": "{{author.name}}",
"license": "{{module.license}}", "license": "{{module.license}}",
"dependencies": { "dependencies": {
"@api.global/typedserver": "^1.0.16", "@api.global/typedserver": "^3.0.51",
"@consentsoftware_private/catalog": "^1.0.73", "@consentsoftware_private/catalog": "^1.0.73",
"@design.estate/dees-domtools": "^2.0.23", "@design.estate/dees-domtools": "^2.0.23",
"@design.estate/dees-element": "^2.0.15", "@design.estate/dees-element": "^2.0.15",
"@push.rocks/smartlog": "^2.0.1", "@push.rocks/qenv": "^6.0.5",
"@push.rocks/qenv": "^5.0.2", "@push.rocks/smartlog": "^3.0.7",
"@push.rocks/smartpath": "^5.0.5", "@push.rocks/smartpath": "^5.0.5",
"@push.rocks/smartstate": "^2.0.0", "@push.rocks/smartstate": "^2.0.0",
"@push.rocks/websetup": "^3.0.15" "@push.rocks/websetup": "^3.0.15"

View File

@@ -3,10 +3,10 @@ import * as paths from './ffb.paths.js';
export const runCli = async () => { export const runCli = async () => {
const serviceQenv = new plugins.qenv.Qenv('./', './.nogit', false); const serviceQenv = new plugins.qenv.Qenv('./', './.nogit', false);
const websiteServer = new plugins.loleWebsiteserver.LoleWebsiteServer({ const websiteServer = new plugins.typedserver.utilityservers.UtilityWebsiteServer({
feedMetadata: null, feedMetadata: null,
domain: '{{module.projectDomain}}', domain: '{{module.projectDomain}}',
serveDir: paths.distWebDir serveDir: paths.distWebDir,
}); });
await websiteServer.start(); await websiteServer.start();
}; };

View File

@@ -6,7 +6,7 @@ import {
cssManager, cssManager,
unsafeCSS, unsafeCSS,
css, css,
TemplateResult, type TemplateResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
@customElement('default-header') @customElement('default-header')

View File

@@ -45,7 +45,7 @@ const run = async () => {
}, },
}); });
const serviceWorker = serviceworker.getServiceWorker(); const serviceWorker = serviceworker.getServiceworkerClient();
const mainTemplate = html` const mainTemplate = html`
<style> <style>

View File

@@ -1,16 +1,479 @@
# Changelog # Changelog
## 1.9.102 - fix(metadata): Ensure accurate project metadata in package.json ## 2025-08-15 - 1.17.3 - fix(serviceconfig)
Update service configuration to include dynamic MongoDB connection string and add local permissions settings
- Placeholder entry for the next version changes. - Added .claude/settings.local.json for local permissions configuration
- Updated ServiceConfiguration to compute and update MONGODB_URL based on current config values
## 2025-08-15 - 1.17.2 - fix(ci-test-services)
Update CI/CD configurations, test settings, and Docker service for MongoDB.
- Add .claude/settings.local.json with updated permission settings
- Introduce new GitLab CI, VSCode launch and settings, and updated test configuration files (.gitignore, .npmrc, npmextra.json, package.json, qenv.yml, readme.md)
- Update test scripts in test/test and test/ts to improve project validation
- Fix MongoDB Docker container command by adding '--bind_ip_all' for proper network binding
## 2025-08-15 - 1.17.1 - fix(services)
Improve services module logging and enhance MongoDB Compass integration
## 1.9.101 - fix(dependencies): Updated @git.zone/tsdoc dependency version to ^1.2.2 in package.json and pnpm-lock.yaml - Refactored services module to use centralized logger from gitzone.logging.ts
- Automatically display MongoDB Compass connection string when starting services or checking status
- Removed custom printMessage wrapper in favor of standard logger.log() calls
- Consistent logging across all service commands
- Fix: General core updates ## 2025-08-14 - 1.17.0 - feat(services)
Add comprehensive development services management for MongoDB and MinIO containers
## 1.9.100 - Implemented `gitzone services` command for managing local development services
- Added MongoDB and MinIO (S3-compatible) container orchestration
- Smart port assignment (20000-30000 range) to avoid conflicts between projects
- Project-specific container names for complete isolation
- Data persistence in `.nogit/` directories
- MongoDB Compass connection string generation with network IP detection
- Auto-configuration via `.nogit/env.json` with secure defaults
- Commands: start, stop, restart, status, config, compass, logs, remove, clean
- Interactive confirmations for destructive operations
- Fix: Fix variable reassignment issue in changelog writing step ## 2025-08-08 - 1.16.10 - fix(format)
Improve concurrency control in caching and rollback modules, refine gitignore custom section handling, and enhance Prettier file processing.
- Added mutex locking in ChangeCache and RollbackManager to prevent race conditions during manifest updates
- Updated gitignore logic to detect and preserve custom sections
- Enhanced Prettier batching and file formatting for better performance
## 2025-08-08 - 1.16.9 - fix(format)
Improve concurrency control in cache and rollback modules, refine gitignore custom section handling, and enhance Prettier file processing
- Added mutex locking in ChangeCache and RollbackManager to prevent race conditions during manifest updates
- Updated gitignore logic to detect and preserve existing custom sections from various markers
- Simplified Prettier formatter to process files sequentially, skip files without extensions, and log detailed status
- Minor refactoring in base formatter and tsconfig file updates for improved reliability
## 2025-08-08 - 1.16.8 - fix(format)
Improve concurrency control in cache and rollback management with mutex locking and refine formatting details
- Added 'withMutex' functions in ChangeCache and RollbackManager to synchronize file I/O operations
- Introduced static mutex maps to prevent race conditions during manifest updates
- Fixed minor formatting issues in commit info and package.json
## 2025-08-08 - 1.16.7 - fix(core)
Improve formatting, logging, and rollback integrity in core modules
- Add .claude/settings.local.json with defined permissions for allowed commands
- Standardize formatting in package.json, commit info, and configuration files
- Refactor rollback manager to use atomic manifest writes and validate manifest structure
- Enhance logging messages and overall code clarity in CLI and commit modules
## 2025-08-08 - 1.16.6 - fix(changecache)
Improve cache manifest validation and atomic file writes; add local settings and overrides
- Add manifest structure validation and default fallback in getManifest
- Implement atomic write in saveManifest using a temporary file and rename strategy
- Enhance error handling and cleanup for corrupted manifest files
- Introduce new .claude/settings.local.json for project-specific permission configuration
- Add an empty assets/overrides.json file for future overrides
## 2025-08-08 - 1.16.5 - fix(prettier)
Improve file selection in Prettier formatter, remove legacy package overrides, and update CI template indentation
- Added .claude/settings.local.json with updated permission settings for local commands
- Removed unnecessary overrides from assets/overrides.json and cleared packageManager overrides in package.json
- Adjusted CI template files (ci_default_gitlab, ci_default_private_gitlab, ci_docker_gitlab) for consistent indentation and formatting
- Refined Prettier formatter logic by defining include directories, root config files, and filtering duplicates instead of manual exclusion
## 2025-08-08 - 1.16.4 - fix(prettier)
Improve file exclusion in the Prettier formatter to skip unnecessary files and directories.
- Added exclusion patterns for node_modules, .git, dist, .nogit, coverage, .nyc_output, vendor, bower_components, jspm_packages, and minified files.
- Optimized filtering logic to ensure only valid files are processed.
## 2025-08-08 - 1.16.3 - fix(changecache/prettier)
Skip directories during file processing to prevent errors in changecache and prettier formatting
- Removed unnecessary await on synchronous file reads in changecache
- Added directory checks in changecache to immediately skip directories
- Filtered out directories in prettier formatter to avoid processing non-files
## 2025-08-07 - 1.16.2 - fix(format)
Fix format command confirmation prompt to correctly check user response
- Fixed bug where format command always showed "cancelled" even when user confirmed
- Changed response check from `response.proceed` to `response.value` for SmartInteract compatibility
## 2025-08-04 - 1.16.1 - fix(package/config)
Move smartdiff dependency to runtime and add local bash permissions settings
- Moved '@push.rocks/smartdiff' from devDependencies to dependencies in package.json
- Added .claude/settings.local.json with allowed bash commands (grep, mkdir, find, ls)
## 2025-05-19 - 1.16.0 - feat(format)
Enhance format module with rollback, diff reporting, and improved parallel execution
- Implemented rollback functionality with backup management and automatic rollback on error
- Added CLI commands for rollback, listing backups, and cleaning old backups
- Introduced DiffReporter for generating and displaying file diffs
- Improved file change caching via ChangeCache and expanded dependency analysis for parallel execution
- Updated logging to support verbose mode and enhanced user feedback
- Updated package.json to include new dependency '@push.rocks/smartdiff'
## 2025-05-14 - 1.15.5 - fix(dependencies)
Update @git.zone/tsdoc to ^1.5.0 and @types/node to ^22.15.18
- Bumped @git.zone/tsdoc from ^1.4.5 to ^1.5.0
- Bumped @types/node from ^22.15.17 to ^22.15.18
## 2025-05-13 - 1.15.4 - fix(package.json)
Update dependency versions: bump @git.zone/tsdoc, @push.rocks/lik, @push.rocks/smartlog, and @types/node to their latest releases
- Upgrade @git.zone/tsdoc from ^1.4.4 to ^1.4.5
- Upgrade @push.rocks/lik from ^6.0.15 to ^6.2.2
- Upgrade @push.rocks/smartlog from ^3.0.7 to ^3.0.9
- Upgrade @types/node from ^22.14.1 to ^22.15.17
## 2025-04-15 - 1.15.3 - fix(deps)
update dependency versions and improve website template variable handling
- Bumped @git.zone/tsbuild from ^2.2.1 to ^2.3.2 and @types/node to ^22.14.1
- Upgraded @push.rocks/smartscaf from ^4.0.15 to ^4.0.16 and prettier from ^3.5.2 to ^3.5.3
- Refactored website template update to correctly supply variables with added logging
## 2025-04-15 - 1.15.2 - fix(website_update)
Await supplyVariables call in website update template
- Changed website template update to properly await the supplyVariables method
- Ensured asynchronous consistency in updating website template variables
## 2025-04-15 - 1.15.1 - fix(cli)
Refresh internal CLI tooling and configuration for consistency.
## 2025-04-15 - 1.15.0 - feat(config/template)
Add assetbrokerUrl and legalUrl fields to module config and update website template to supply these values
- Added assetbrokerUrl and legalUrl properties in ts/classes.gitzoneconfig.ts
- Updated ts/mod_format/format.templates.ts to pass assetbrokerUrl and legalUrl to website template
## 2025-04-15 - 1.14.1 - fix(package.json)
Add packageManager field to specify pnpm version for consistent package management
- Inserted packageManager property in package.json with pnpm version info to ensure reproducible dependency installs
## 2025-04-15 - 1.14.0 - feat(tsconfig_update)
Add runafter directive to trigger gitzone format after tsconfig update
- Added runafter configuration in assets/templates/tsconfig_update/.smartscaf.yml to automate formatting task
## 2025-03-07 - 1.13.1 - fix(cli)
Improve commit message logging
- Updated logging to display recommended next commit details.
- Enabled interactive prompt for choosing commit type and scope.
## 2025-02-28 - 1.13.0 - feat(templates)
Updated and added new TypeScript template files for npm projects
- Added new paths.ts and plugins.ts template files for npm projects.
- Removed outdated some.plugins.ts template file.
## 2025-02-25 - 1.12.8 - fix(metadata)
Updated package and npmextra json description and keywords for enhanced development workflow clarity
- Updated the description in package.json to focus on project setup and management.
- Aligned the keywords in both package.json and npmextra.json to include more relevant terms such as gitzone utilities, template management, and CI/CD.
## 2025-02-25 - 1.12.7 - fix(meta)
Fix issues in project metadata and configuration.
- Updated package metadata to ensure accurate project description and licensing.
- Ensured npm access level configuration consistency within npmextra.json.
## 2025-02-25 - 1.12.7 - fix(ci)
Updated dependencies and added CI/CD workflows.
- Updated several dependencies in package.json for compatibility and security.
- Added default GitHub and GitLab CI/CD workflow files to the test directory.
- Introduced a .gitignore file to exclude certain directories and files.
- Ensured consistent formatting with Prettier and TypeScript configurations.
## 2025-01-29 - 1.12.6 - fix(project)
Minor fixes and cleanup
- Removed outdated pages/ directory entry in .gitignore.
- Adjusted Docker module files to include proper newlines.
- Updated TypeScript configuration for modular paths.
- Fixed formatting issues across various TypeScript files.
## 2025-01-29 - 1.12.5 - fix(cli)
Initial implementation of CLI utility with project management features
- Integration of various plugins for logging, command-line interactions, and project management.
- Setup CLI commands for project initialization, updating, formatting, and more.
- Implement commands for packaging, versioning, and deprecating npm packages.
## 2025-01-29 - 1.12.2 - fix(format)
Add overrides for peek-readable in package.json formatting
- Added a URL correction in the packageJson repository information.
- Introduced support for pnpm overrides by including an `overrides.json` file.
## 2025-01-18 - 1.12.1 - fix(dependencies)
Update various package dependencies and Dockerfile base image
- Updated Dockerfile base image from 'alpinenpmci' to 'alpine_npmci'.
- Upgraded @git.zone/tsbuild, @git.zone/tsrun, @git.zone/tsdoc, and other dependencies to their latest versions.
## 2025-01-17 - 1.12.0 - feat(build)
Update TypeScript configuration to support emit decorator metadata
- Added emitDecoratorMetadata to the tsconfig.json template in assets/templates/tsconfig_update.
## 2025-01-08 - 1.11.0 - feat(cli)
Add Docker command for cleaning up Docker system and extend deprecation command for multiple registries
- Added a new command 'docker' to handle Docker system cleanup operations.
- Improved the 'deprecate' command to support deprecating packages across multiple npm registry URLs.
## 2025-01-01 - 1.10.10 - fix(templates)
Corrected typo in template file comment
- Fixed repeated comment in the template file for services under 'assets/templates/service/ts/some.plugins.ts'.
## 2025-01-01 - 1.10.9 - fix(templates)
Correct template file paths and organization for service projects
- Moved 'some.classes.some.ts' to 'classes.some.ts'
- Moved 'some.classes.some.db.ts' to 'classes.some.db.ts'
- Resolved incorrect import paths in service templates
## 2025-01-01 - 1.10.8 - fix(assets/templates)
Update CI template configurations to use module.githost
- Replaced occurrences of {{git.host}} with {{module.githost}} in CI workflow files
- Updated package dependencies for service template
## 2024-12-26 - 1.10.7 - fix(assets)
Correct URLs in templates and fix TypeScript declaration
- Updated incorrect URLs in Dockerfile templates to 'host.today'.
- Fixed type declaration for 'TemplateResult' in header.ts file.
## 2024-12-08 - 1.10.6 - fix(ci)
Corrected Docker image URL in CI templates
- Updated Docker image URL from 'code.foss.global/hosttoday' to 'code.foss.global/host.today' in default_nottags.yaml and default_tags.yaml.
- Adjusted gitignore template to include a custom section delineation.
## 2024-12-02 - 1.10.5 - fix(assets)
Update .gitignore template to remove pages directory
- Removed 'pages/' from the ignored directories in the .gitignore template.
## 2024-11-05 - 1.10.4 - fix(mod_format)
Correct file extension for TypeScript path configuration
- Fixed the TypeScript configuration to use correct file extensions for module subdirectories.
## 2024-10-27 - 1.10.3 - fix(mod_format)
Reorder TypeScript formatting steps in mod_format module
- Moved TypeScript configuration formatting earlier in the sequence for better logical consistency.
## 2024-10-27 - 1.10.2 - fix(format)
Add logging for tsconfig.json formatting
- Added an info log message for tsconfig.json formatting in format.tsconfig.ts.
## 2024-10-27 - 1.10.1 - fix(format)
Fixed async issue in tsconfig module lookup and corrected property access
## 2024-10-27 - 1.10.0 - feat(mod_format)
Add support for tsconfig.json formatting
- Added a new script to format tsconfig.json.
- Updated package.json to include `@git.zone/tspublish` as a dependency.
## 2024-10-23 - 1.9.126 - fix(format)
Remove redundant package.json property checks
- Removed property checks for `main`, `typings`, and `browserslist` from format.packagejson.ts
- This change streamlines the formatting process by removing unnecessary exits
## 2024-09-29 - 1.9.125 - fix(cli)
Fix package version configuration and formatting issues
- Updated metadata fields in package.json (repository URL, bugs URL, and homepage).
- Ensured presence and correctness of essential fields in package.json (main, typings, files, scripts, etc.).
- Added missing Prettier default TypeScript and Markdown configurations.
## 2024-09-27 - 1.9.124 - fix(cli)
Ensured proper existence and initialization of readme files
- Ensured readme.md and readme.hints.md files are created and initialized if they do not exist.
## 2024-09-27 - 1.9.123 - fix(core)
No changes detected
## 2024-09-27 - 1.9.123 - fix(core)
Update dependencies and improve build configurations
- Updated several dependencies in package.json for better compatibility
- Changed typedserver dependency version in website template
- Adjusted website server and service worker in templates
- Added security and test workflows for Gitea repositories
- Added and configured .gitignore, .npmrc, .vscode settings for the project
- Provided initial structure for readme and readme hints
## 2024-06-24 - 1.9.122 - fix(mod_commit)
Update package.json dependencies: @git.zone/tsdoc and @push.rocks/smartpromise to latest versions.
- - Updated @git.zone/tsdoc to ^1.3.12
- - Updated @push.rocks/smartfile to ^11.0.21
## 2024-06-23 - 1.9.121 - fix(mod_commit)
Fix changelog template rendering by removing extra new line when no version details are provided.
- Update package.json dependencies: @git.zone/tsdoc and @push.rocks/smartpromise to latest versions.
## 2024-06-23 - 1.9.120 - fix(mod_commit)
Handle edge case for empty version details in changelog formatting
- Added check for the length of the recommendedNextVersionDetails array
- Ensure no extra newline in changelog if there are no version details
## 2024-06-23 - 1.9.119 - fix(dependencies)
Update @git.zone/tsdoc to v1.3.8
- Updated @git.zone/tsdoc from v1.3.7 to v1.3.8 in package.json
## 2024-06-23 - 1.9.118 - fix(dependencies)
Update @git.zone/tsdoc to version 1.3.7
- Bump @git.zone/tsdoc from 1.3.6 to 1.3.7 in both package.json and pnpm-lock.yaml
## 2024-06-23 - 1.9.117 - fix(dependencies)
Update @git.zone/tsdoc dependency to v1.3.6
- Updated @git.zone/tsdoc version from 1.3.5 to 1.3.6 in package.json
- Updated pnpm-lock.yaml to reflect the new version of @git.zone/tsdoc
## 2024-06-23 - 1.9.116 - fix(dependencies)
Update @git.zone/tsdoc to version 1.3.5
- Updated the @git.zone/tsdoc dependency in package.json and pnpm-lock.yaml from version 1.3.4 to 1.3.5
- Removed the outdated changelog.md file.
## 2024-06-23 - 1.9.114 - fix(format)
Fixed formatting issues across multiple TypeScript files.
## 2024-06-23 - 1.9.113 - fix(mod_commit)
Remove extra new lines in changelog.
## 2024-06-23 - 1.9.112 - fix(core)
Update changelog formatting and remove outdated entries.
## 2024-06-23 - 1.9.111 - fix(changelog)
Remove outdated changelog entries and update formatting.
## 2024-06-23 - 1.9.110 - fix(dependencies)
Update @git.zone/tsdoc to version 1.3.4.
## 2024-06-23 - 1.9.109 - fix(changelog)
Remove outdated entries and adjust formatting in changelog.
## 2024-06-23 - 1.9.108 - fix(dependencies)
Update @git.zone/tsdoc dependency to version 1.3.2.
## 2024-06-23 - 1.9.107 - fix(changelog)
Remove placeholder entries and adjust formatting in changelog.
## 2024-06-23 - 1.9.106 - fix(dependencies)
Updated @git.zone/tsdoc from version 1.3.0 to 1.3.1.
## 2024-06-23 - 1.9.105 - fix(dependencies)
Updated @git.zone/tsdoc dependency from 1.2.2 to 1.3.0 in package.json and pnpm-lock.yaml.
## 2024-06-23 - 1.9.104 - fix(changelog)
Remove placeholder entries and adjust formatting in changelog.
## 2024-06-23 - 1.9.103 - fix(changelog)
Fix changelog to remove placeholder entries and adjust formatting.
## 2024-06-23 - 1.9.102 - fix(logging)
Optimize logger instantiation and configuration.
## 2024-06-23 - 1.9.101 - fix(metadata)
Ensure accurate project metadata in package.json.
## 2024-06-23 - 1.9.100 - fix(dependencies)
Updated @git.zone/tsdoc dependency version to ^1.2.2 in package.json and pnpm-lock.yaml.
## 2024-06-23 - 1.9.99 - fix(mod_commit)
Fix variable reassignment issue in changelog writing step.

View File

@@ -10,25 +10,30 @@
"githost": "gitlab.com", "githost": "gitlab.com",
"gitscope": "gitzone/private", "gitscope": "gitzone/private",
"gitrepo": "gitzone", "gitrepo": "gitzone",
"description": "A CLI toolbelt to streamline local development cycles by using various gitzone utilities.", "description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.",
"npmPackagename": "@gitzone_private/gitzone", "npmPackagename": "@gitzone_private/gitzone",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"CLI", "CLI",
"development", "local development",
"git", "gitzone utilities",
"npm", "project setup",
"version control",
"code formatting",
"template management",
"npm packages",
"TypeScript", "TypeScript",
"automation", "automation",
"project setup", "commit management",
"code formatting",
"template creation", "template creation",
"logging", "meta project management",
"meta project management" "sync",
"Docker",
"CI/CD"
] ]
} }
}, },
"tsdoc": { "tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n" "legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
} }
} }

View File

@@ -1,8 +1,8 @@
{ {
"name": "@git.zone/cli", "name": "@git.zone/cli",
"private": false, "private": false,
"version": "1.9.102", "version": "1.17.3",
"description": "A CLI toolbelt to streamline local development cycles by using various gitzone utilities.", "description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.",
"main": "dist_ts/index.ts", "main": "dist_ts/index.ts",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
"type": "module", "type": "module",
@@ -30,64 +30,72 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://code.foss.global/git.zone/cli.git" "url": "https://gitlab.com/gitzone/private/gitzone.git"
}, },
"keywords": [ "keywords": [
"CLI", "CLI",
"development", "local development",
"git", "gitzone utilities",
"npm", "project setup",
"version control",
"code formatting",
"template management",
"npm packages",
"TypeScript", "TypeScript",
"automation", "automation",
"project setup", "commit management",
"code formatting",
"template creation", "template creation",
"logging", "meta project management",
"meta project management" "sync",
"Docker",
"CI/CD"
], ],
"author": "Task Venture Capital GmbH", "author": "Task Venture Capital GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://code.foss.global/git.zone/cli/issues" "url": "https://gitlab.com/gitzone/private/gitzone/issues"
}, },
"homepage": "https://pages.foss.global/git.zone/cli", "homepage": "https://gitlab.com/gitzone/private/gitzone#readme",
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^2.1.80", "@git.zone/tsbuild": "^2.3.2",
"@git.zone/tsrun": "^1.2.46", "@git.zone/tsrun": "^1.3.3",
"@git.zone/tstest": "^1.0.90", "@git.zone/tstest": "^1.0.96",
"@types/node": "^20.14.8" "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartfile": "^11.2.0",
"@push.rocks/smartinteract": "^2.0.16",
"@push.rocks/smartnetwork": "^4.1.2",
"@push.rocks/smartshell": "^3.2.3",
"@types/node": "^22.15.18"
}, },
"dependencies": { "dependencies": {
"@git.zone/tsdoc": "^1.2.2", "@git.zone/tsdoc": "^1.5.0",
"@git.zone/tspublish": "^1.9.1",
"@push.rocks/commitinfo": "^1.0.12", "@push.rocks/commitinfo": "^1.0.12",
"@push.rocks/early": "^4.0.4", "@push.rocks/early": "^4.0.4",
"@push.rocks/gulp-function": "^3.0.7", "@push.rocks/gulp-function": "^3.0.7",
"@push.rocks/lik": "^6.0.15", "@push.rocks/lik": "^6.2.2",
"@push.rocks/npmextra": "^5.0.23", "@push.rocks/npmextra": "^5.1.2",
"@push.rocks/projectinfo": "^5.0.2", "@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/smartchok": "^1.0.34", "@push.rocks/smartchok": "^1.0.34",
"@push.rocks/smartcli": "^4.0.11", "@push.rocks/smartcli": "^4.0.11",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdiff": "^1.0.3",
"@push.rocks/smartfile": "^11.0.20",
"@push.rocks/smartgulp": "^3.0.4", "@push.rocks/smartgulp": "^3.0.4",
"@push.rocks/smartinteract": "^2.0.15",
"@push.rocks/smartjson": "^5.0.20", "@push.rocks/smartjson": "^5.0.20",
"@push.rocks/smartlegal": "^1.0.27", "@push.rocks/smartlegal": "^1.0.27",
"@push.rocks/smartlog": "^3.0.7", "@push.rocks/smartlog": "^3.0.9",
"@push.rocks/smartlog-destination-local": "^9.0.2", "@push.rocks/smartlog-destination-local": "^9.0.2",
"@push.rocks/smartmustache": "^3.0.2", "@push.rocks/smartmustache": "^3.0.2",
"@push.rocks/smartnpm": "^2.0.4", "@push.rocks/smartnpm": "^2.0.4",
"@push.rocks/smartobject": "^1.0.12", "@push.rocks/smartobject": "^1.0.12",
"@push.rocks/smartopen": "^2.0.0", "@push.rocks/smartopen": "^2.0.0",
"@push.rocks/smartpath": "^5.0.18", "@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.3", "@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartscaf": "^4.0.15", "@push.rocks/smartscaf": "^4.0.16",
"@push.rocks/smartshell": "^3.0.5", "@push.rocks/smartstream": "^3.2.5",
"@push.rocks/smartstream": "^3.0.44",
"@push.rocks/smartunique": "^3.0.9", "@push.rocks/smartunique": "^3.0.9",
"@push.rocks/smartupdate": "^2.0.6", "@push.rocks/smartupdate": "^2.0.6",
"@types/through2": "^2.0.41", "@types/through2": "^2.0.41",
"prettier": "^3.3.2", "prettier": "^3.5.3",
"through2": "^4.0.2" "through2": "^4.0.2"
}, },
"files": [ "files": [
@@ -104,5 +112,9 @@
], ],
"browserslist": [ "browserslist": [
"last 1 chrome versions" "last 1 chrome versions"
] ],
"pnpm": {
"overrides": {}
},
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
} }

6687
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1,193 @@
* the cli of the git.zone project. # Gitzone CLI - Development Hints
- the cli of the git.zone project.
## Project Overview
Gitzone CLI (`@git.zone/cli`) is a comprehensive toolbelt for streamlining local development cycles. It provides utilities for:
- Project initialization and templating (via smartscaf)
- Code formatting and standardization
- Version control and commit management
- Docker and CI/CD integration
- Meta project management
## Architecture
### Core Structure
- Main CLI entry: `cli.ts` / `cli.child.ts`
- Modular architecture with separate modules in `ts/mod_*` directories
- Each module handles specific functionality (format, commit, docker, etc.)
- Extensive use of plugins pattern via `plugins.ts` files
### Configuration Management
- Uses `npmextra.json` for all tool configuration
- Configuration stored under `gitzone` key in npmextra
- No separate `.gitzonerc` file - everything in npmextra.json
- Project type and module metadata also stored in npmextra
### Format Module (`mod_format`) - SIGNIFICANTLY ENHANCED
The format module is responsible for project standardization:
#### Current Modules:
1. **cleanup** - Removes obsolete files (yarn.lock, tslint.json, etc.)
2. **copy** - File copying with glob patterns (fully implemented)
3. **gitignore** - Creates/updates .gitignore from templates
4. **license** - Checks dependency licenses for compatibility
5. **npmextra** - Manages project metadata and configuration
6. **packagejson** - Formats and updates package.json
7. **prettier** - Applies code formatting with batching
8. **readme** - Ensures readme files exist
9. **templates** - Updates project templates based on type
10. **tsconfig** - Formats TypeScript configuration
#### Execution Order (Dependency-Based):
- Modules are now executed in parallel groups based on dependencies
- Independent modules run concurrently for better performance
- Dependency analyzer ensures correct execution order
### New Architecture Features
1. **BaseFormatter Pattern**: All formatters extend abstract BaseFormatter class
2. **FormatContext**: Central state management across all modules
3. **FormatPlanner**: Implements plan → action workflow
4. **RollbackManager**: Full backup/restore capabilities
5. **ChangeCache**: Tracks file changes to optimize performance
6. **DependencyAnalyzer**: Manages module execution order
7. **DiffReporter**: Generates diff views for changes
8. **FormatStats**: Comprehensive execution statistics
### Key Patterns
1. **Plugin Architecture**: All dependencies imported through `plugins.ts` files
2. **Streaming**: Uses smartstream for file processing
3. **Interactive Prompts**: smartinteract for user input
4. **Enhanced Error Handling**: Comprehensive try-catch with automatic rollback
5. **Template System**: Templates handled by smartscaf, not directly by gitzone
6. **Type Safety**: Full TypeScript with interfaces and type definitions
### Important Notes
- `.nogit/` directory used for temporary/untracked files, backups, and cache
- `.nogit/gitzone-backups/` stores format operation backups
- `.nogit/gitzone-cache/` stores file change cache
- Templates are managed by smartscaf - improvements should be made there
- License checking configurable with exceptions support
- All features implemented: `ensureDependency`, copy module, etc.
## Recent Improvements (Completed)
1. **Plan → Action Workflow**: Shows changes before applying them
2. **Rollback Mechanism**: Full backup and restore on failures
3. **Enhanced Configuration**: Granular control via npmextra.json
4. **Better Error Handling**: Detailed errors with recovery options
5. **Performance Optimizations**: Parallel execution and caching
6. **Reporting**: Diff views, statistics, verbose logging
7. **Architecture**: Clean separation of concerns with new classes
## Development Tips
- Always check readme.plan.md for ongoing improvement plans
- Use npmextra.json for any new configuration options
- Keep modules focused and single-purpose
- Maintain the existing plugin pattern for dependencies
- Test format operations on sample projects before deploying
- Consider backward compatibility when changing configuration structure
- Use BaseFormatter pattern for new format modules
- Leverage FormatContext for cross-module state sharing
## Configuration Examples
```json
{
"gitzone": {
"format": {
"interactive": true,
"parallel": true,
"showStats": true,
"cache": {
"enabled": true,
"clean": true
},
"rollback": {
"enabled": true,
"autoRollbackOnError": true,
"backupRetentionDays": 7
},
"modules": {
"skip": ["prettier"],
"only": [],
"order": []
},
"licenses": {
"allowed": ["MIT", "Apache-2.0"],
"exceptions": {
"some-package": "GPL-3.0"
}
}
}
}
}
```
## CLI Usage
```bash
# Basic format
gitzone format
# Dry run to preview changes
gitzone format --dry-run
# Non-interactive mode
gitzone format --yes
# Plan only (no execution)
gitzone format --plan-only
# Save plan for later
gitzone format --save-plan format.json
# Execute saved plan
gitzone format --from-plan format.json
# Verbose mode
gitzone format --verbose
# Detailed diff views
gitzone format --detailed
# Rollback operations
gitzone format --rollback
gitzone format --rollback <operation-id>
gitzone format --list-backups
gitzone format --clean-backups
```
## Common Issues (Now Resolved)
1. ✅ Format operations are now reversible with rollback
2. ✅ Enhanced error messages with recovery suggestions
3. ✅ All modules fully implemented (including copy)
4. ✅ Dry-run capability available
5. ✅ Extensive configuration options available
## Future Considerations
- Plugin system for custom formatters
- Git hooks integration for pre-commit formatting
- Advanced UI with interactive configuration
- Format presets for common scenarios
- Performance benchmarking tools
## API Changes
- smartfile API updated to use fs._ and memory._ namespaces
- smartnpm requires instance creation: `new NpmRegistry()`
- All file operations now use updated APIs
- Type imports use `import type` for proper verbatim module syntax

589
readme.md
View File

@@ -1,195 +1,528 @@
# @git.zone/cli # @git.zone/cli 🚀
A CLI toolbelt to streamline local development cycles by using various gitzone utilities. **The ultimate CLI toolbelt for modern TypeScript development workflows**
## Install [![npm version](https://img.shields.io/npm/v/@git.zone/cli.svg)](https://www.npmjs.com/package/@git.zone/cli)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
To install the `@git.zone/cli` tool, you need to have Node.js and npm installed on your machine. Once you have those set up, you can install the CLI tool globally using the following command: ## 🎯 What is gitzone?
```shell gitzone is a powerful command-line interface that supercharges your development workflow with automated project management, intelligent code formatting, seamless version control, and development service orchestration. Whether you're bootstrapping a new TypeScript project, maintaining code quality, managing complex multi-repository setups, or spinning up local development databases, gitzone has got you covered.
## 🏃‍♂️ Quick Start
### Installation
```bash
# Install globally via npm
npm install -g @git.zone/cli npm install -g @git.zone/cli
# Or with pnpm (recommended)
pnpm add -g @git.zone/cli
``` ```
This will add the `gitzone` or `gzone` command to your PATH, allowing you to use the tool from any directory. Once installed, you can use either `gitzone` or the shorter `gzone` command from anywhere in your terminal.
## Usage ### Your First Commands
The `gitzone` CLI is designed to streamline various aspects of the local development cycle, including project setup, maintenance, and deployment. Below, we'll go through several scenarios that demonstrate the capabilities of `gitzone`. ```bash
# Create a new TypeScript npm package
gitzone template npm
### Getting Started # Format your entire codebase
gitzone format
To start using `gitzone`, you need to initialize a new project or use it with an existing one. This section will guide you through the initial setup and provide examples of the core commands. # Start local MongoDB and MinIO services
gitzone services start
#### Initialize a New Project # Create a semantic commit
`gitzone` can create several types of projects, including standard npm modules, websites using LitElement, and custom web components. To create a new project, you can use the following command:
```shell
gitzone template [templatename]
```
Replace `[templatename]` with one of the following:
- `npm`: A standard npm module with TypeScript support, testing, and CI/CD setup.
- `website`: A LitElement-based website with e2e testing, bundling, and service worker support.
- `element`: A LitElement standard setup for creating web components.
Example of starting a new npm project:
1. Open your terminal and navigate to the directory where you want to create your project.
2. Execute the following command:
```shell
gitzone template npm
```
3. Follow the interactive prompts to set up your project. You will be asked to provide information such as the project name, description, GitHub repository, etc.
#### Committing Changes
To standardize commit messages and increment versions based on change types (fix, feat, breaking change):
```shell
gitzone commit gitzone commit
``` ```
This command will prompt you with a series of questions to help construct a standardized commit message and create a new commit. ## 🛠️ Core Features
#### Deprecating a Package ### 🐳 Development Services Management (NEW!)
To deprecate an old package in favor of a new one: Effortlessly manage local MongoDB and MinIO (S3-compatible) services for your development environment:
```shell ```bash
gitzone services [command]
```
**Available commands:**
- **`start [service]`** - Start services (mongo|s3|all)
- **`stop [service]`** - Stop services (mongo|s3|all)
- **`restart [service]`** - Restart services
- **`status`** - Show current service status
- **`config`** - Display configuration details
- **`compass`** - Get MongoDB Compass connection string with network IP
- **`logs [service] [lines]`** - View service logs
- **`remove`** - Remove containers (preserves data)
- **`clean`** - Remove containers AND data (⚠️ destructive)
**Key features:**
- 🎲 **Smart port assignment** - Automatically assigns random ports (20000-30000) to avoid conflicts
- 📦 **Project isolation** - Each project gets its own containers with unique names
- 💾 **Data persistence** - Data stored in `.nogit/` directories survives container restarts
- 🔗 **MongoDB Compass support** - Instantly get connection strings for GUI access
- 🌐 **Network IP detection** - Automatically detects your local network IP for remote connections
- ⚙️ **Auto-configuration** - Creates `.nogit/env.json` with smart defaults
**Example workflow:**
```bash
# Start all services for your project
gitzone services start
# Check what's running
gitzone services status
# Get MongoDB Compass connection string
gitzone services compass
# Output: mongodb://defaultadmin:defaultpass@192.168.1.100:27018/myproject?authSource=admin
# View MongoDB logs
gitzone services logs mongo 50
# Stop services when done
gitzone services stop
```
The services are configured via `.nogit/env.json` which is automatically created with secure defaults and random ports for each project.
### 📦 Project Templates
Instantly scaffold production-ready projects with best practices built-in:
```bash
gitzone template [template-name]
```
**Available templates:**
- **`npm`** - TypeScript npm package with testing, CI/CD, and full tooling
- **`service`** - Microservice architecture with Docker support
- **`website`** - Modern web application with LitElement and service workers
- **`wcc`** - Web Component Collection for reusable UI components
Each template comes pre-configured with:
- ✅ TypeScript with modern configurations
- ✅ Automated testing setup
- ✅ CI/CD pipelines (GitLab/GitHub)
- ✅ Code formatting and linting
- ✅ Documentation structure
### 🎨 Intelligent Code Formatting
The most powerful feature of gitzone - automatically format and standardize your entire codebase:
```bash
# Preview changes without applying them
gitzone format --dry-run
# Format with automatic approval
gitzone format --yes
# Save formatting plan for later execution
gitzone format --save-plan format-plan.json
# Execute a saved plan
gitzone format --from-plan format-plan.json
# Enable verbose output for debugging
gitzone format --verbose
```
**Format features:**
- 🔄 **Smart caching** - Only processes changed files
- 🛡️ **Rollback support** - Undo formatting changes if needed
- 📊 **Detailed reporting** - See exactly what changed
-**Parallel execution** - Format multiple files simultaneously
- 🎯 **Module-specific formatting** - Target specific formatters
**Rollback capabilities:**
```bash
# List all available backups
gitzone format --list-backups
# Rollback to the last operation
gitzone format --rollback
# Rollback to a specific operation
gitzone format --rollback [operation-id]
# Clean old backups
gitzone format --clean-backups
```
**Formatters included:**
- **Prettier** - JavaScript/TypeScript code formatting
- **License** - Ensure proper licensing
- **Package.json** - Standardize package configurations
- **Tsconfig** - TypeScript configuration optimization
- **Readme** - Documentation formatting
- **Gitignore** - Repository ignore rules
- **Templates** - Project template updates
- **Npmextra** - Extended npm configurations
### 🔀 Semantic Commits & Versioning
Create standardized commits that automatically handle versioning:
```bash
gitzone commit
```
Features:
- 📝 Interactive commit message builder
- 🏷️ Automatic version bumping (major/minor/patch)
- 📜 Changelog generation
- 🚀 Optional auto-push to origin
- 🎯 Conventional commit compliance
The commit wizard guides you through:
1. **Type selection** (feat/fix/docs/style/refactor/perf/test/chore)
2. **Scope definition** (component/module affected)
3. **Description crafting**
4. **Breaking change detection**
5. **Version bump determination**
### 🏗️ Meta Repository Management
Manage multiple related repositories as a cohesive unit:
```bash
# Initialize a meta repository
gitzone meta init
# Add a sub-project
gitzone meta add [name] [git-url]
# Update all sub-projects
gitzone meta update
# Remove a sub-project
gitzone meta remove [name]
```
Perfect for:
- Monorepo management
- Multi-package projects
- Coordinated deployments
- Synchronized versioning
### 🐳 Docker Management
Streamline your Docker workflow:
```bash
# Clean up all Docker resources
gitzone docker prune
```
This command removes:
- Stopped containers
- Unused images
- Dangling volumes
- Unused networks
### 🔗 Quick CI/CD Access
Jump directly to your CI/CD configurations:
```bash
# Open CI/CD settings
gitzone open ci
# Open pipelines view
gitzone open pipelines
```
Works with GitLab repositories to provide instant access to your deployment configurations.
### 📝 Package Deprecation
Smoothly transition users from old to new packages:
```bash
gitzone deprecate gitzone deprecate
``` ```
This command will prompt you for the old and new package names and will automatically deprecate the old package on npm. Interactive wizard for:
#### Opening CI/CD Settings - Setting deprecation notices
- Guiding users to replacements
- Updating registry metadata
- Coordinating migration paths
Quickly open the CI/CD settings page of your project repository: ### 🚦 Project Initialization
```shell Prepare existing projects for development:
gitzone open ci
```bash
gitzone start
``` ```
### Example Commands Automatically:
#### Project Setup and Initialization - Checks out master branch
- Pulls latest changes
- Installs dependencies
- Sets up development environment
Let's go through how to scaffold a new web component project: ### 🔧 Helper Utilities
1. Navigate to your desired directory. Quick utilities for common tasks:
2. Run the following command:
```shell
gitzone template element
```
3. Follow the prompts to set up your web component project.
#### Managing Project Lifecycle ```bash
# Generate a unique short ID
gitzone helpers shortid
```
Commands to manage your project's lifecycle: ## 📋 Configuration
- **Commit Changes:** ### npmextra.json Configuration
```shell
gitzone commit
```
- **Deprecate a Package:**
```shell
gitzone deprecate
```
- **Format Project Files:**
```shell
gitzone format
```
- **Regenerate Readme:**
```shell
gitzone readme
```
- **Update Local Repositories:**
```shell
gitzone update
```
#### Advanced Scenarios Customize gitzone behavior through `npmextra.json`:
##### Formatting ```json
{
"gitzone": {
"format": {
"interactive": true,
"showDiffs": false,
"autoApprove": false,
"parallel": true,
"rollback": {
"enabled": true,
"autoRollbackOnError": true,
"backupRetentionDays": 7
},
"modules": {
"skip": ["prettier"],
"only": [],
"order": []
},
"cache": {
"enabled": true,
"clean": true
}
}
}
}
```
To ensure consistent formatting using Prettier: ### Environment Variables
```shell - `CI` - Detect CI environment for automated workflows
- `DEBUG` - Enable debug output
- `GITZONE_FORMAT_PARALLEL` - Control parallel formatting
## 🏆 Best Practices
### For New Projects
1. Start with a template: `gitzone template npm`
2. Set up local services: `gitzone services start`
3. Customize the generated structure
4. Run initial format: `gitzone format`
5. Set up CI/CD: `gitzone open ci`
### For Existing Projects
1. Initialize: `gitzone start`
2. Format codebase: `gitzone format --dry-run` (preview first!)
3. Apply formatting: `gitzone format --yes`
4. Set up services: `gitzone services start`
5. Commit changes: `gitzone commit`
### For Teams
1. Document format preferences in `npmextra.json`
2. Share `.nogit/env.json` template for consistent service setup
3. Use `--save-plan` for reviewable format changes
4. Enable rollback for safety
5. Standardize commit conventions
## 🎯 Common Workflows
### Full-Stack Development Cycle
```bash
# 1. Start fresh
gitzone start
# 2. Spin up databases and services
gitzone services start
# 3. Make changes
# ... your development work ...
# 4. Check service logs if needed
gitzone services logs mongo
# 5. Format code
gitzone format gitzone format
# 6. Commit with semantic versioning
gitzone commit
# 7. Stop services when done
gitzone services stop
``` ```
##### Building the Project ### Multi-Repository Management
To build your project as defined in your `package.json`: ```bash
# 1. Set up meta repository
```shell
npm run build
```
##### Git and Version Control
Check the current project version:
```shell
gitzone -v
```
To synchronize local repositories with remotes:
```shell
gitzone update
```
##### Metadata and Configuration
To initialize or update metadata:
```shell
gitzone meta init gitzone meta init
# 2. Add all related projects
gitzone meta add frontend https://github.com/org/frontend.git
gitzone meta add backend https://github.com/org/backend.git
gitzone meta add shared https://github.com/org/shared.git
# 3. Synchronize updates
gitzone meta update
``` ```
### Continuous Integration and Delivery (CI/CD) ### Safe Formatting with Rollback
#### Running Tests ```bash
# 1. Preview changes
gitzone format --dry-run
To execute tests defined in your `package.json`: # 2. Save plan for review
gitzone format --save-plan format-changes.json
```shell # 3. Apply formatting
npm test gitzone format --from-plan format-changes.json
# 4. If something goes wrong, rollback
gitzone format --rollback
``` ```
#### Building Documentation ### Database-Driven Development
Generate documentation: ```bash
# 1. Start MongoDB and MinIO
gitzone services start
```shell # 2. Get connection string for your app
npm run buildDocs gitzone services config
# 3. Connect with MongoDB Compass
gitzone services compass
# 4. Monitor services
gitzone services status
# 5. Clean everything when done
gitzone services clean # ⚠️ Warning: deletes data
``` ```
### Troubleshooting and Debugging ## 🔌 Integrations
#### Detailed Logs ### CI/CD Platforms
Enable detailed logging for troubleshooting: - **GitLab CI** - Full pipeline support with templates
- **GitHub Actions** - Automated workflows
- **Docker** - Container-based deployments
```shell ### Development Tools
gitzone --loglevel=debug
- **TypeScript** - First-class support
- **Prettier** - Code formatting
- **ESLint** - Linting (via format modules)
- **npm/pnpm** - Package management
- **MongoDB** - Local database service
- **MinIO** - S3-compatible object storage
- **MongoDB Compass** - Database GUI integration
### Version Control
- **Git** - Deep integration
- **Semantic Versioning** - Automatic version bumping
- **Conventional Commits** - Standardized commit messages
## 💡 Pro Tips
1. **Use aliases**: Add `alias gz='gitzone'` to your shell profile
2. **Combine commands**: `gitzone format --yes && gitzone commit`
3. **Leverage templates**: Start projects right with proven structures
4. **Enable caching**: Dramatically speeds up formatting operations
5. **Save format plans**: Review changes before applying in production
6. **Port management**: Let services auto-assign ports to avoid conflicts
7. **Use MongoDB Compass**: `gitzone services compass` for visual DB management
## 🐛 Troubleshooting
### Format Command Shows "Cancelled"
If the format command shows cancelled even after confirming:
- Check your `npmextra.json` configuration
- Try with `--yes` flag to skip confirmation
- Use `--verbose` for detailed output
### Docker Commands Fail
Ensure Docker daemon is running:
```bash
docker info
``` ```
#### Cleaning Up ### Services Won't Start
To clean up project artifacts: Check for port conflicts:
```shell ```bash
gitzone clean # Services auto-assign ports, but you can check the config
cat .nogit/env.json
# Verify Docker is running
docker ps
``` ```
### Summary ### Template Creation Issues
The `gitzone` CLI tool provides a comprehensive suite of commands that streamline project setup, lifecycle management, and deployment, which are indispensable for modern development workflows. By familiarizing yourself with the different commands, you can maximize your productivity and focus on what really matters—writing code. Whether you are starting a new project, maintaining an existing one, or deploying your work, `gitzone` is your toolbelt for efficient development cycles. Verify npm/pnpm is properly configured:
```bash
npm config get registry
```
### MongoDB Connection Issues
- Ensure services are running: `gitzone services status`
- Check firewall settings for the assigned ports
- Use `gitzone services compass` for the correct connection string
## 📈 Performance
gitzone is optimized for speed:
- **Parallel processing** for format operations
- **Smart caching** to avoid redundant work
- **Incremental updates** for meta repositories
- **Minimal dependencies** for fast installation
- **Isolated services** prevent resource conflicts
- **Auto port assignment** eliminates manual configuration
## License and Legal Information ## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
@@ -204,4 +537,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc. For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works. By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

121
readme.plan.md Normal file
View File

@@ -0,0 +1,121 @@
# GitZone Services Command Implementation Plan
## Overview
Implement the `gitzone services` command to manage MongoDB and MinIO containers for development projects.
## Tasks
### Module Structure Setup
- [x] Create `ts/mod_services/` directory
- [x] Create `mod.plugins.ts` with required imports
- [x] Create `helpers.ts` with utility functions
- [x] Create `classes.serviceconfiguration.ts` for config handling
- [x] Create `classes.dockercontainer.ts` for Docker operations
- [x] Create `classes.servicemanager.ts` for service management
- [x] Create `index.ts` with main command logic
### Core Functionality
- [x] Implement ServiceConfiguration class
- [x] Load/create `.nogit/env.json` configuration
- [x] Generate random available ports (20000-30000 range)
- [x] Preserve existing custom values
- [x] Provide default values for missing fields
- [x] Implement DockerContainer class
- [x] Check container status
- [x] Start/stop/restart containers
- [x] Execute Docker commands
- [x] Handle container logs
- [x] Manage volumes and port bindings
- [x] Implement ServiceManager class
- [x] Manage MongoDB containers
- [x] Manage MinIO containers
- [x] Handle container lifecycle
- [x] Generate project-specific container names
- [x] Manage data directories in `.nogit/`
- [x] Generate MongoDB Compass connection strings
### Commands Implementation
- [x] `start` command - Start services (mongo|s3|all)
- [x] `stop` command - Stop services (mongo|s3|all)
- [x] `restart` command - Restart services (mongo|s3|all)
- [x] `status` command - Show service status
- [x] `config` command - Show current configuration
- [x] `compass` command - Show MongoDB Compass connection string
- [x] `logs` command - Show service logs with line count
- [x] `remove` command - Remove containers (preserve data)
- [x] `clean` command - Remove containers and data
### Integration
- [x] Add `@push.rocks/smartshell` to main plugins.ts
- [x] Add `@push.rocks/smartnetwork` to main plugins.ts
- [x] Add `@push.rocks/smartinteraction` to main plugins.ts
- [x] Register services command in `gitzone.cli.ts`
### Features
- [x] Auto-configuration with smart defaults
- [x] Random port assignment to avoid conflicts
- [x] Project isolation with unique container names
- [x] Data persistence in `.nogit/` directories
- [x] Status display (running/stopped/not installed)
- [x] Interactive confirmations for destructive operations
- [x] Colored console output
- [x] MinIO bucket auto-creation
- [x] MongoDB Compass connection string with network IP
### Testing
- [ ] Test service start/stop operations
- [ ] Test configuration creation and updates
- [ ] Test port collision handling
- [ ] Test data persistence
- [ ] Test MongoDB Compass connection string generation
- [ ] Test all command variations
## Configuration Format
```json
{
"PROJECT_NAME": "derived-from-package-name",
"MONGODB_HOST": "localhost",
"MONGODB_NAME": "project-name",
"MONGODB_PORT": "random-port",
"MONGODB_USER": "defaultadmin",
"MONGODB_PASS": "defaultpass",
"S3_HOST": "localhost",
"S3_PORT": "random-port",
"S3_CONSOLE_PORT": "s3-port+1",
"S3_USER": "defaultadmin",
"S3_PASS": "defaultpass",
"S3_BUCKET": "project-name-documents"
}
```
## Command Examples
```bash
gitzone services start # Start all services
gitzone services start mongo # Start only MongoDB
gitzone services stop # Stop all services
gitzone services status # Check service status
gitzone services config # Show configuration
gitzone services compass # Show MongoDB Compass connection string
gitzone services logs mongo 50 # Show last 50 lines of MongoDB logs
gitzone services remove # Remove containers (preserve data)
gitzone services clean # Remove containers and data
```
## Progress Notes
Implementation started: 2025-08-14
Implementation completed: 2025-08-14
## Summary
Successfully implemented the `gitzone services` command in TypeScript, providing a complete replacement for the `services.sh` shell script. The implementation includes:
1. **Complete Docker service management** for MongoDB and MinIO containers
2. **Smart configuration management** with automatic port assignment and conflict avoidance
3. **MongoDB Compass support** with network IP detection for remote connections
4. **Project isolation** using project-specific container names
5. **Data persistence** in `.nogit/` directories
6. **Interactive confirmations** for destructive operations
7. **Comprehensive command set** including start, stop, restart, status, config, compass, logs, remove, and clean commands
The module is fully integrated into the gitzone CLI and ready for testing.

1
test Submodule

Submodule test added at 0b89443584

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/cli', name: '@git.zone/cli',
version: '1.9.102', version: '1.17.3',
description: 'A CLI toolbelt to streamline local development cycles by using various gitzone utilities.' description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
} }

View File

@@ -16,8 +16,10 @@ export interface IGitzoneConfigData {
npmPackageName: string; npmPackageName: string;
license: string; license: string;
projectDomain: string; projectDomain: string;
assetbrokerUrl: string;
legalUrl: string;
}; };
copy: {[key: string]: string} copy: { [key: string]: string };
npmciOptions: { npmciOptions: {
npmAccessLevel: 'public' | 'private'; npmAccessLevel: 'public' | 'private';
}; };
@@ -38,7 +40,9 @@ export class GitzoneConfig {
public async readConfigFromCwd() { public async readConfigFromCwd() {
const npmextraInstance = new plugins.npmextra.Npmextra(paths.cwd); const npmextraInstance = new plugins.npmextra.Npmextra(paths.cwd);
this.data = npmextraInstance.dataFor<IGitzoneConfigData>('gitzone', {}); this.data = npmextraInstance.dataFor<IGitzoneConfigData>('gitzone', {});
this.data.npmciOptions = npmextraInstance.dataFor<IGitzoneConfigData['npmciOptions']>('npmci', { this.data.npmciOptions = npmextraInstance.dataFor<
IGitzoneConfigData['npmciOptions']
>('npmci', {
npmAccessLevel: 'public', npmAccessLevel: 'public',
}); });
} }

View File

@@ -1,4 +1,4 @@
import * as plugins from './plugins.js' import * as plugins from './plugins.js';
import * as paths from './paths.js'; import * as paths from './paths.js';
import { GitzoneConfig } from './classes.gitzoneconfig.js'; import { GitzoneConfig } from './classes.gitzoneconfig.js';

View File

@@ -15,7 +15,7 @@ export let run = async () => {
await smartupdateInstance.check( await smartupdateInstance.check(
'gitzone', 'gitzone',
projectInfo.npm.version, projectInfo.npm.version,
'http://gitzone.gitlab.io/gitzone/changelog.html' 'http://gitzone.gitlab.io/gitzone/changelog.html',
); );
console.log('---------------------------------------------'); console.log('---------------------------------------------');
gitzoneSmartcli.addVersion(projectInfo.npm.version); gitzoneSmartcli.addVersion(projectInfo.npm.version);
@@ -48,13 +48,49 @@ export let run = async () => {
await modDeprecate.run(); await modDeprecate.run();
}); });
/**
* docker
*/
gitzoneSmartcli.addCommand('docker').subscribe(async (argvArg) => {
const modDocker = await import('./mod_docker/index.js');
await modDocker.run(argvArg);
});
/** /**
* Update all files that comply with the gitzone standard * Update all files that comply with the gitzone standard
*/ */
gitzoneSmartcli.addCommand('format').subscribe(async (argvArg) => { gitzoneSmartcli.addCommand('format').subscribe(async (argvArg) => {
const config = GitzoneConfig.fromCwd(); const config = GitzoneConfig.fromCwd();
const modFormat = await import('./mod_format/index.js'); const modFormat = await import('./mod_format/index.js');
await modFormat.run();
// Handle rollback commands
if (argvArg.rollback) {
await modFormat.handleRollback(argvArg.rollback);
return;
}
if (argvArg['list-backups']) {
await modFormat.handleListBackups();
return;
}
if (argvArg['clean-backups']) {
await modFormat.handleCleanBackups();
return;
}
// Handle format with options
await modFormat.run({
dryRun: argvArg['dry-run'],
yes: argvArg.yes,
planOnly: argvArg['plan-only'],
savePlan: argvArg['save-plan'],
fromPlan: argvArg['from-plan'],
detailed: argvArg.detailed,
interactive: argvArg.interactive !== false,
parallel: argvArg.parallel !== false,
verbose: argvArg.verbose,
});
}); });
/** /**
@@ -95,6 +131,14 @@ export let run = async () => {
modHelpers.run(argvArg); modHelpers.run(argvArg);
}); });
/**
* manage development services (MongoDB, S3/MinIO)
*/
gitzoneSmartcli.addCommand('services').subscribe(async (argvArg) => {
const modServices = await import('./mod_services/index.js');
await modServices.run(argvArg);
});
// start parsing of the cli // start parsing of the cli
gitzoneSmartcli.startParse(); gitzoneSmartcli.startParse();
return await done.promise; return await done.promise;

View File

@@ -1,15 +1,29 @@
import { commitinfo } from '@push.rocks/commitinfo';
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
export const logger = new plugins.smartlog.Smartlog({ // Create logger instance
minimumLogLevel: 'silly', export const logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
logContext: {
company: 'Task Venture Capital GmbH',
companyunit: 'Lossless Cloud',
containerName: 'local',
environment: 'local',
runtime: 'node',
zone: 'gitzone',
},
});
logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal()); // Add console destination
const consoleDestination =
new plugins.smartlogDestinationLocal.DestinationLocal();
logger.addLogDestination(consoleDestination);
// Verbose logging helper
let verboseMode = false;
export const setVerboseMode = (verbose: boolean): void => {
verboseMode = verbose;
logger.log('info', `Verbose mode ${verbose ? 'enabled' : 'disabled'}`);
};
export const isVerboseMode = (): boolean => {
return verboseMode;
};
// Custom log method with verbose support
export const logVerbose = (message: string): void => {
if (verboseMode) {
logger.log('info', `[VERBOSE] ${message}`);
}
};

View File

@@ -5,19 +5,27 @@ import * as paths from '../paths.js';
import { logger } from '../gitzone.logging.js'; import { logger } from '../gitzone.logging.js';
export const run = async (argvArg: any) => { export const run = async (argvArg: any) => {
if (argvArg.format) {
const formatMod = await import('../mod_format/index.js');
await formatMod.run();
}
logger.log('info', `gathering facts...`); logger.log('info', `gathering facts...`);
const aidoc = new plugins.tsdoc.AiDoc(); const aidoc = new plugins.tsdoc.AiDoc();
await aidoc.start(); await aidoc.start();
const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd); const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd);
logger.log('info', `--------- logger.log(
'info',
`---------
Next recommended commit would be: Next recommended commit would be:
=========== ===========
-> ${nextCommitObject.recommendedNextVersion}: -> ${nextCommitObject.recommendedNextVersion}:
-> ${nextCommitObject.recommendedNextVersionLevel}(${nextCommitObject.recommendedNextVersionScope}): ${nextCommitObject.recommendedNextVersionMessage} -> ${nextCommitObject.recommendedNextVersionLevel}(${nextCommitObject.recommendedNextVersionScope}): ${nextCommitObject.recommendedNextVersionMessage}
=========== ===========
`); `,
);
const commitInteract = new plugins.smartinteract.SmartInteract(); const commitInteract = new plugins.smartinteract.SmartInteract();
commitInteract.addQuestions([ commitInteract.addQuestions([
{ {
@@ -66,26 +74,55 @@ export const run = async (argvArg: any) => {
}); });
logger.log('info', `Baking commitinfo into code ...`); logger.log('info', `Baking commitinfo into code ...`);
const commitInfo = new plugins.commitinfo.CommitInfo(paths.cwd, commitVersionType); const commitInfo = new plugins.commitinfo.CommitInfo(
paths.cwd,
commitVersionType,
);
await commitInfo.writeIntoPotentialDirs(); await commitInfo.writeIntoPotentialDirs();
logger.log('info', `Writing changelog.md ...`); logger.log('info', `Writing changelog.md ...`);
let changelog = nextCommitObject.changelog; let changelog = nextCommitObject.changelog;
changelog = changelog.replaceAll('{{nextVersion}}', (await commitInfo.getNextPlannedVersion()).versionString); changelog = changelog.replaceAll(
changelog = changelog.replaceAll('{{nextVersionMessage}}', commitString); '{{nextVersion}}',
(await commitInfo.getNextPlannedVersion()).versionString,
);
changelog = changelog.replaceAll(
'{{nextVersionScope}}',
`${await answerBucket.getAnswerFor('commitType')}(${await answerBucket.getAnswerFor('commitScope')})`,
);
changelog = changelog.replaceAll(
'{{nextVersionMessage}}',
nextCommitObject.recommendedNextVersionMessage,
);
if (nextCommitObject.recommendedNextVersionDetails?.length > 0) {
changelog = changelog.replaceAll(
'{{nextVersionDetails}}',
'- ' + nextCommitObject.recommendedNextVersionDetails.join('\n- '),
);
} else {
changelog = changelog.replaceAll('\n{{nextVersionDetails}}', '');
}
await plugins.smartfile.memory.toFs(changelog, plugins.path.join(paths.cwd, `changelog.md`)); await plugins.smartfile.memory.toFs(
changelog,
plugins.path.join(paths.cwd, `changelog.md`),
);
logger.log('info', `Staging files for commit:`); logger.log('info', `Staging files for commit:`);
await smartshellInstance.exec(`git add -A`); await smartshellInstance.exec(`git add -A`);
await smartshellInstance.exec(`git commit -m "${commitString}"`); await smartshellInstance.exec(`git commit -m "${commitString}"`);
await smartshellInstance.exec(`npm version ${commitVersionType}`); await smartshellInstance.exec(`npm version ${commitVersionType}`);
if (answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true')) { if (
answerBucket.getAnswerFor('pushToOrigin') &&
!(process.env.CI === 'true')
) {
await smartshellInstance.exec(`git push origin master --follow-tags`); await smartshellInstance.exec(`git push origin master --follow-tags`);
} }
}; };
const createCommitStringFromAnswerBucket = (answerBucket: plugins.smartinteract.AnswerBucket) => { const createCommitStringFromAnswerBucket = (
answerBucket: plugins.smartinteract.AnswerBucket,
) => {
const commitType = answerBucket.getAnswerFor('commitType'); const commitType = answerBucket.getAnswerFor('commitType');
const commitScope = answerBucket.getAnswerFor('commitScope'); const commitScope = answerBucket.getAnswerFor('commitScope');
const commitDescription = answerBucket.getAnswerFor('commitDescription'); const commitDescription = answerBucket.getAnswerFor('commitDescription');

View File

@@ -4,6 +4,15 @@ import { logger } from '../gitzone.logging.js';
export const run = async () => { export const run = async () => {
const smartInteract = new plugins.smartinteract.SmartInteract([ const smartInteract = new plugins.smartinteract.SmartInteract([
{
name: `registryUrls`,
message: `What are the comma separated registry URLs?`,
type: `input`,
default: `https://registry.npmjs.org`,
validate: (stringInput) => {
return stringInput !== '' && !process.env.CI;
},
},
{ {
name: `oldPackageName`, name: `oldPackageName`,
message: `Whats the name of the OLD package?`, message: `Whats the name of the OLD package?`,
@@ -24,14 +33,20 @@ export const run = async () => {
}, },
]); ]);
const answerBucket = await smartInteract.runQueue(); const answerBucket = await smartInteract.runQueue();
const registryUrls = answerBucket.getAnswerFor(`registryUrls`).split(',');
const oldPackageName = answerBucket.getAnswerFor(`oldPackageName`); const oldPackageName = answerBucket.getAnswerFor(`oldPackageName`);
const newPackageName = answerBucket.getAnswerFor(`newPackageName`); const newPackageName = answerBucket.getAnswerFor(`newPackageName`);
logger.log('info', `Deprecating package ${oldPackageName} in favour of ${newPackageName}`); logger.log(
'info',
`Deprecating package ${oldPackageName} in favour of ${newPackageName}`,
);
const smartshellInstance = new plugins.smartshell.Smartshell({ const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash', executor: 'bash',
}); });
await smartshellInstance.exec( for (const registryUrl of registryUrls) {
`npm deprecate ${oldPackageName}@* ` + await smartshellInstance.exec(
`"${oldPackageName} has been deprecated in favour of ${newPackageName} - please upgrade asap!!!"` `npm deprecate ${oldPackageName}@* ` +
); `"${oldPackageName} has been deprecated in favour of ${newPackageName} - please upgrade asap!!!" --registry ${registryUrl}`,
);
}
}; };

12
ts/mod_docker/index.ts Normal file
View File

@@ -0,0 +1,12 @@
import * as plugins from './mod.plugins.js';
export const run = async (argvArg) => {
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash',
});
switch (argvArg._[1]) {
case 'prune':
await smartshellInstance.exec(`docker system prune -a -f --volumes`);
break;
}
};

View File

@@ -0,0 +1,4 @@
export * from '../plugins.js';
import * as smartshell from '@push.rocks/smartshell';
export { smartshell };

View File

@@ -0,0 +1,82 @@
import * as plugins from './mod.plugins.js';
import { FormatContext } from './classes.formatcontext.js';
import type { IPlannedChange } from './interfaces.format.js';
import { Project } from '../classes.project.js';
export abstract class BaseFormatter {
protected context: FormatContext;
protected project: Project;
protected stats: any; // Will be FormatStats from context
constructor(context: FormatContext, project: Project) {
this.context = context;
this.project = project;
this.stats = context.getFormatStats();
}
abstract get name(): string;
abstract analyze(): Promise<IPlannedChange[]>;
abstract applyChange(change: IPlannedChange): Promise<void>;
async execute(changes: IPlannedChange[]): Promise<void> {
const startTime = this.stats.moduleStartTime(this.name);
this.stats.startModule(this.name);
try {
await this.preExecute();
for (const change of changes) {
try {
await this.applyChange(change);
this.stats.recordFileOperation(this.name, change.type, true);
} catch (error) {
this.stats.recordFileOperation(this.name, change.type, false);
throw error;
}
}
await this.postExecute();
} catch (error) {
// Don't rollback here - let the FormatPlanner handle it
throw error;
} finally {
this.stats.endModule(this.name, startTime);
}
}
protected async preExecute(): Promise<void> {
// Override in subclasses if needed
}
protected async postExecute(): Promise<void> {
// Override in subclasses if needed
}
protected async modifyFile(filepath: string, content: string): Promise<void> {
// Validate filepath before writing
if (!filepath || filepath.trim() === '') {
throw new Error(`Invalid empty filepath in modifyFile`);
}
// Ensure we have a proper path with directory component
// If the path has no directory component (e.g., "package.json"), prepend "./"
let normalizedPath = filepath;
if (!plugins.path.parse(filepath).dir) {
normalizedPath = './' + filepath;
}
await plugins.smartfile.memory.toFs(content, normalizedPath);
}
protected async createFile(filepath: string, content: string): Promise<void> {
await plugins.smartfile.memory.toFs(content, filepath);
}
protected async deleteFile(filepath: string): Promise<void> {
await plugins.smartfile.fs.remove(filepath);
}
protected async shouldProcessFile(filepath: string): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,223 @@
import * as plugins from './mod.plugins.js';
import * as paths from '../paths.js';
export interface IFileCache {
path: string;
checksum: string;
modified: number;
size: number;
}
export interface ICacheManifest {
version: string;
lastFormat: number;
files: IFileCache[];
}
export class ChangeCache {
private cacheDir: string;
private manifestPath: string;
private cacheVersion = '1.0.0';
constructor() {
this.cacheDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-cache');
this.manifestPath = plugins.path.join(this.cacheDir, 'manifest.json');
}
async initialize(): Promise<void> {
await plugins.smartfile.fs.ensureDir(this.cacheDir);
}
async getManifest(): Promise<ICacheManifest> {
const defaultManifest: ICacheManifest = {
version: this.cacheVersion,
lastFormat: 0,
files: [],
};
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
if (!exists) {
return defaultManifest;
}
try {
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
const manifest = JSON.parse(content);
// Validate the manifest structure
if (this.isValidManifest(manifest)) {
return manifest;
} else {
console.warn('Invalid manifest structure, returning default manifest');
return defaultManifest;
}
} catch (error) {
console.warn(
`Failed to read cache manifest: ${error.message}, returning default manifest`,
);
// Try to delete the corrupted file
try {
await plugins.smartfile.fs.remove(this.manifestPath);
} catch (removeError) {
// Ignore removal errors
}
return defaultManifest;
}
}
async saveManifest(manifest: ICacheManifest): Promise<void> {
// Validate before saving
if (!this.isValidManifest(manifest)) {
throw new Error('Invalid manifest structure, cannot save');
}
// Ensure directory exists
await plugins.smartfile.fs.ensureDir(this.cacheDir);
// Write directly with proper JSON stringification
const jsonContent = JSON.stringify(manifest, null, 2);
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
}
async hasFileChanged(filePath: string): Promise<boolean> {
const absolutePath = plugins.path.isAbsolute(filePath)
? filePath
: plugins.path.join(paths.cwd, filePath);
// Check if file exists
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
if (!exists) {
return true; // File doesn't exist, so it's "changed" (will be created)
}
// Get current file stats
const stats = await plugins.smartfile.fs.stat(absolutePath);
// Skip directories
if (stats.isDirectory()) {
return false; // Directories are not processed
}
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const currentChecksum = this.calculateChecksum(content);
// Get cached info
const manifest = await this.getManifest();
const cachedFile = manifest.files.find((f) => f.path === filePath);
if (!cachedFile) {
return true; // Not in cache, so it's changed
}
// Compare checksums
return (
cachedFile.checksum !== currentChecksum ||
cachedFile.size !== stats.size ||
cachedFile.modified !== stats.mtimeMs
);
}
async updateFileCache(filePath: string): Promise<void> {
const absolutePath = plugins.path.isAbsolute(filePath)
? filePath
: plugins.path.join(paths.cwd, filePath);
// Get current file stats
const stats = await plugins.smartfile.fs.stat(absolutePath);
// Skip directories
if (stats.isDirectory()) {
return; // Don't cache directories
}
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const checksum = this.calculateChecksum(content);
// Update manifest
const manifest = await this.getManifest();
const existingIndex = manifest.files.findIndex((f) => f.path === filePath);
const cacheEntry: IFileCache = {
path: filePath,
checksum,
modified: stats.mtimeMs,
size: stats.size,
};
if (existingIndex !== -1) {
manifest.files[existingIndex] = cacheEntry;
} else {
manifest.files.push(cacheEntry);
}
manifest.lastFormat = Date.now();
await this.saveManifest(manifest);
}
async getChangedFiles(filePaths: string[]): Promise<string[]> {
const changedFiles: string[] = [];
for (const filePath of filePaths) {
if (await this.hasFileChanged(filePath)) {
changedFiles.push(filePath);
}
}
return changedFiles;
}
async clean(): Promise<void> {
const manifest = await this.getManifest();
const validFiles: IFileCache[] = [];
// Remove entries for files that no longer exist
for (const file of manifest.files) {
const absolutePath = plugins.path.isAbsolute(file.path)
? file.path
: plugins.path.join(paths.cwd, file.path);
if (await plugins.smartfile.fs.fileExists(absolutePath)) {
validFiles.push(file);
}
}
manifest.files = validFiles;
await this.saveManifest(manifest);
}
private calculateChecksum(content: string | Buffer): string {
return plugins.crypto.createHash('sha256').update(content).digest('hex');
}
private isValidManifest(manifest: any): manifest is ICacheManifest {
// Check if manifest has the required structure
if (!manifest || typeof manifest !== 'object') {
return false;
}
// Check required fields
if (
typeof manifest.version !== 'string' ||
typeof manifest.lastFormat !== 'number' ||
!Array.isArray(manifest.files)
) {
return false;
}
// Check each file entry
for (const file of manifest.files) {
if (
!file ||
typeof file !== 'object' ||
typeof file.path !== 'string' ||
typeof file.checksum !== 'string' ||
typeof file.modified !== 'number' ||
typeof file.size !== 'number'
) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,117 @@
import * as plugins from './mod.plugins.js';
import { BaseFormatter } from './classes.baseformatter.js';
export interface IModuleDependency {
module: string;
dependencies: Set<string>;
dependents: Set<string>;
}
export class DependencyAnalyzer {
private moduleDependencies: Map<string, IModuleDependency> = new Map();
constructor() {
this.initializeDependencies();
}
private initializeDependencies(): void {
// Define dependencies between format modules
const dependencies = {
cleanup: [], // No dependencies
npmextra: [], // No dependencies
license: ['npmextra'], // Depends on npmextra for config
packagejson: ['npmextra'], // Depends on npmextra for config
templates: ['npmextra', 'packagejson'], // Depends on both
gitignore: ['templates'], // Depends on templates
tsconfig: ['packagejson'], // Depends on package.json
prettier: [
'cleanup',
'npmextra',
'packagejson',
'templates',
'gitignore',
'tsconfig',
], // Runs after most others
readme: ['npmextra', 'packagejson'], // Depends on project metadata
copy: ['npmextra'], // Depends on config
};
// Initialize all modules
for (const [module, deps] of Object.entries(dependencies)) {
this.moduleDependencies.set(module, {
module,
dependencies: new Set(deps),
dependents: new Set(),
});
}
// Build reverse dependencies (dependents)
for (const [module, deps] of Object.entries(dependencies)) {
for (const dep of deps) {
const depModule = this.moduleDependencies.get(dep);
if (depModule) {
depModule.dependents.add(module);
}
}
}
}
getExecutionGroups(modules: BaseFormatter[]): BaseFormatter[][] {
const modulesMap = new Map(modules.map((m) => [m.name, m]));
const executed = new Set<string>();
const groups: BaseFormatter[][] = [];
while (executed.size < modules.length) {
const currentGroup: BaseFormatter[] = [];
for (const module of modules) {
if (executed.has(module.name)) continue;
const dependency = this.moduleDependencies.get(module.name);
if (!dependency) {
// Unknown module, execute in isolation
currentGroup.push(module);
continue;
}
// Check if all dependencies have been executed
const allDepsExecuted = Array.from(dependency.dependencies).every(
(dep) => executed.has(dep) || !modulesMap.has(dep),
);
if (allDepsExecuted) {
currentGroup.push(module);
}
}
if (currentGroup.length === 0) {
// Circular dependency or error - execute remaining modules
for (const module of modules) {
if (!executed.has(module.name)) {
currentGroup.push(module);
}
}
}
currentGroup.forEach((m) => executed.add(m.name));
groups.push(currentGroup);
}
return groups;
}
canRunInParallel(module1: string, module2: string): boolean {
const dep1 = this.moduleDependencies.get(module1);
const dep2 = this.moduleDependencies.get(module2);
if (!dep1 || !dep2) return false;
// Check if module1 depends on module2 or vice versa
return (
!dep1.dependencies.has(module2) &&
!dep2.dependencies.has(module1) &&
!dep1.dependents.has(module2) &&
!dep2.dependents.has(module1)
);
}
}

View File

@@ -0,0 +1,124 @@
import * as plugins from './mod.plugins.js';
import type { IPlannedChange } from './interfaces.format.js';
import { logger } from '../gitzone.logging.js';
export class DiffReporter {
private diffs: Map<string, string> = new Map();
async generateDiff(
filePath: string,
oldContent: string,
newContent: string,
): Promise<string> {
const diff = plugins.smartdiff.createDiff(oldContent, newContent);
this.diffs.set(filePath, diff);
return diff;
}
async generateDiffForChange(change: IPlannedChange): Promise<string | null> {
if (change.type !== 'modify') {
return null;
}
try {
const exists = await plugins.smartfile.fs.fileExists(change.path);
if (!exists) {
return null;
}
const currentContent = await plugins.smartfile.fs.toStringSync(
change.path,
);
// For planned changes, we need the new content
if (!change.content) {
return null;
}
return await this.generateDiff(
change.path,
currentContent,
change.content,
);
} catch (error) {
logger.log(
'error',
`Failed to generate diff for ${change.path}: ${error.message}`,
);
return null;
}
}
displayDiff(filePath: string, diff?: string): void {
const diffToShow = diff || this.diffs.get(filePath);
if (!diffToShow) {
logger.log('warn', `No diff available for ${filePath}`);
return;
}
console.log(`\n${this.formatDiffHeader(filePath)}`);
console.log(this.colorDiff(diffToShow));
console.log('━'.repeat(50));
}
displayAllDiffs(): void {
if (this.diffs.size === 0) {
logger.log('info', 'No diffs to display');
return;
}
console.log('\nFile Changes:');
console.log('═'.repeat(50));
for (const [filePath, diff] of this.diffs) {
this.displayDiff(filePath, diff);
}
}
private formatDiffHeader(filePath: string): string {
return `📄 ${filePath}`;
}
private colorDiff(diff: string): string {
const lines = diff.split('\n');
const coloredLines = lines.map((line) => {
if (line.startsWith('+') && !line.startsWith('+++')) {
return `\x1b[32m${line}\x1b[0m`; // Green for additions
} else if (line.startsWith('-') && !line.startsWith('---')) {
return `\x1b[31m${line}\x1b[0m`; // Red for deletions
} else if (line.startsWith('@')) {
return `\x1b[36m${line}\x1b[0m`; // Cyan for line numbers
} else {
return line;
}
});
return coloredLines.join('\n');
}
async saveDiffReport(outputPath: string): Promise<void> {
const report = {
timestamp: new Date().toISOString(),
totalFiles: this.diffs.size,
diffs: Array.from(this.diffs.entries()).map(([path, diff]) => ({
path,
diff,
})),
};
await plugins.smartfile.memory.toFs(
JSON.stringify(report, null, 2),
outputPath,
);
logger.log('info', `Diff report saved to ${outputPath}`);
}
hasAnyDiffs(): boolean {
return this.diffs.size > 0;
}
getDiffCount(): number {
return this.diffs.size;
}
}

View File

@@ -0,0 +1,14 @@
import * as plugins from './mod.plugins.js';
import { FormatStats } from './classes.formatstats.js';
export class FormatContext {
private formatStats: FormatStats;
constructor() {
this.formatStats = new FormatStats();
}
getFormatStats(): FormatStats {
return this.formatStats;
}
}

View File

@@ -0,0 +1,170 @@
import * as plugins from './mod.plugins.js';
import { FormatContext } from './classes.formatcontext.js';
import { BaseFormatter } from './classes.baseformatter.js';
import type { IFormatPlan, IPlannedChange } from './interfaces.format.js';
import { logger } from '../gitzone.logging.js';
import { DependencyAnalyzer } from './classes.dependency-analyzer.js';
import { DiffReporter } from './classes.diffreporter.js';
export class FormatPlanner {
private plannedChanges: Map<string, IPlannedChange[]> = new Map();
private dependencyAnalyzer = new DependencyAnalyzer();
private diffReporter = new DiffReporter();
async planFormat(modules: BaseFormatter[]): Promise<IFormatPlan> {
const plan: IFormatPlan = {
summary: {
totalFiles: 0,
filesAdded: 0,
filesModified: 0,
filesRemoved: 0,
estimatedTime: 0,
},
changes: [],
warnings: [],
};
for (const module of modules) {
try {
const changes = await module.analyze();
this.plannedChanges.set(module.name, changes);
for (const change of changes) {
plan.changes.push(change);
// Update summary
switch (change.type) {
case 'create':
plan.summary.filesAdded++;
break;
case 'modify':
plan.summary.filesModified++;
break;
case 'delete':
plan.summary.filesRemoved++;
break;
}
}
} catch (error) {
plan.warnings.push({
level: 'error',
message: `Failed to analyze module ${module.name}: ${error.message}`,
module: module.name,
});
}
}
plan.summary.totalFiles =
plan.summary.filesAdded +
plan.summary.filesModified +
plan.summary.filesRemoved;
plan.summary.estimatedTime = plan.summary.totalFiles * 100; // 100ms per file estimate
return plan;
}
async executePlan(
plan: IFormatPlan,
modules: BaseFormatter[],
context: FormatContext,
parallel: boolean = false,
): Promise<void> {
const startTime = Date.now();
try {
// Always use sequential execution to avoid race conditions
for (const module of modules) {
const changes = this.plannedChanges.get(module.name) || [];
if (changes.length > 0) {
logger.log('info', `Executing ${module.name} formatter...`);
await module.execute(changes);
}
}
const endTime = Date.now();
const duration = endTime - startTime;
logger.log('info', `Format operations completed in ${duration}ms`);
} catch (error) {
throw error;
}
}
async displayPlan(
plan: IFormatPlan,
detailed: boolean = false,
): Promise<void> {
console.log('\nFormat Plan:');
console.log('━'.repeat(50));
console.log(`Summary: ${plan.summary.totalFiles} files will be changed`);
console.log(`${plan.summary.filesAdded} new files`);
console.log(`${plan.summary.filesModified} modified files`);
console.log(`${plan.summary.filesRemoved} deleted files`);
console.log('');
console.log('Changes by module:');
// Group changes by module
const changesByModule = new Map<string, IPlannedChange[]>();
for (const change of plan.changes) {
const moduleChanges = changesByModule.get(change.module) || [];
moduleChanges.push(change);
changesByModule.set(change.module, moduleChanges);
}
for (const [module, changes] of changesByModule) {
console.log(
`\n${this.getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`,
);
for (const change of changes) {
const icon = this.getChangeIcon(change.type);
console.log(` ${icon} ${change.path} - ${change.description}`);
// Show diff for modified files if detailed view is requested
if (detailed && change.type === 'modify') {
const diff = await this.diffReporter.generateDiffForChange(change);
if (diff) {
this.diffReporter.displayDiff(change.path, diff);
}
}
}
}
if (plan.warnings.length > 0) {
console.log('\nWarnings:');
for (const warning of plan.warnings) {
const icon = warning.level === 'error' ? '❌' : '⚠️';
console.log(` ${icon} ${warning.message}`);
}
}
console.log('\n' + '━'.repeat(50));
}
private getModuleIcon(module: string): string {
const icons: Record<string, string> = {
packagejson: '📦',
license: '📝',
tsconfig: '🔧',
cleanup: '🚮',
gitignore: '🔒',
prettier: '✨',
readme: '📖',
templates: '📄',
npmextra: '⚙️',
copy: '📋',
};
return icons[module] || '📁';
}
private getChangeIcon(type: 'create' | 'modify' | 'delete'): string {
switch (type) {
case 'create':
return '✅';
case 'modify':
return '✏️';
case 'delete':
return '❌';
}
}
}

View File

@@ -0,0 +1,229 @@
import * as plugins from './mod.plugins.js';
import { logger } from '../gitzone.logging.js';
export interface IModuleStats {
name: string;
filesProcessed: number;
executionTime: number;
errors: number;
successes: number;
filesCreated: number;
filesModified: number;
filesDeleted: number;
}
export interface IFormatStats {
totalExecutionTime: number;
startTime: number;
endTime: number;
moduleStats: Map<string, IModuleStats>;
overallStats: {
totalFiles: number;
totalCreated: number;
totalModified: number;
totalDeleted: number;
totalErrors: number;
cacheHits: number;
cacheMisses: number;
};
}
export class FormatStats {
private stats: IFormatStats;
constructor() {
this.stats = {
totalExecutionTime: 0,
startTime: Date.now(),
endTime: 0,
moduleStats: new Map(),
overallStats: {
totalFiles: 0,
totalCreated: 0,
totalModified: 0,
totalDeleted: 0,
totalErrors: 0,
cacheHits: 0,
cacheMisses: 0,
},
};
}
startModule(moduleName: string): void {
this.stats.moduleStats.set(moduleName, {
name: moduleName,
filesProcessed: 0,
executionTime: 0,
errors: 0,
successes: 0,
filesCreated: 0,
filesModified: 0,
filesDeleted: 0,
});
}
moduleStartTime(moduleName: string): number {
return Date.now();
}
endModule(moduleName: string, startTime: number): void {
const moduleStats = this.stats.moduleStats.get(moduleName);
if (moduleStats) {
moduleStats.executionTime = Date.now() - startTime;
}
}
recordFileOperation(
moduleName: string,
operation: 'create' | 'modify' | 'delete',
success: boolean = true,
): void {
const moduleStats = this.stats.moduleStats.get(moduleName);
if (!moduleStats) return;
moduleStats.filesProcessed++;
if (success) {
moduleStats.successes++;
this.stats.overallStats.totalFiles++;
switch (operation) {
case 'create':
moduleStats.filesCreated++;
this.stats.overallStats.totalCreated++;
break;
case 'modify':
moduleStats.filesModified++;
this.stats.overallStats.totalModified++;
break;
case 'delete':
moduleStats.filesDeleted++;
this.stats.overallStats.totalDeleted++;
break;
}
} else {
moduleStats.errors++;
this.stats.overallStats.totalErrors++;
}
}
recordCacheHit(): void {
this.stats.overallStats.cacheHits++;
}
recordCacheMiss(): void {
this.stats.overallStats.cacheMisses++;
}
finish(): void {
this.stats.endTime = Date.now();
this.stats.totalExecutionTime = this.stats.endTime - this.stats.startTime;
}
displayStats(): void {
console.log('\n📊 Format Operation Statistics:');
console.log('═'.repeat(50));
// Overall stats
console.log('\nOverall Summary:');
console.log(
` Total Execution Time: ${this.formatDuration(this.stats.totalExecutionTime)}`,
);
console.log(` Files Processed: ${this.stats.overallStats.totalFiles}`);
console.log(` • Created: ${this.stats.overallStats.totalCreated}`);
console.log(` • Modified: ${this.stats.overallStats.totalModified}`);
console.log(` • Deleted: ${this.stats.overallStats.totalDeleted}`);
console.log(` Errors: ${this.stats.overallStats.totalErrors}`);
if (
this.stats.overallStats.cacheHits > 0 ||
this.stats.overallStats.cacheMisses > 0
) {
const cacheHitRate =
(this.stats.overallStats.cacheHits /
(this.stats.overallStats.cacheHits +
this.stats.overallStats.cacheMisses)) *
100;
console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);
console.log(` • Hits: ${this.stats.overallStats.cacheHits}`);
console.log(` • Misses: ${this.stats.overallStats.cacheMisses}`);
}
// Module stats
console.log('\nModule Breakdown:');
console.log('─'.repeat(50));
const sortedModules = Array.from(this.stats.moduleStats.values()).sort(
(a, b) => b.filesProcessed - a.filesProcessed,
);
for (const moduleStats of sortedModules) {
console.log(
`\n${this.getModuleIcon(moduleStats.name)} ${moduleStats.name}:`,
);
console.log(
` Execution Time: ${this.formatDuration(moduleStats.executionTime)}`,
);
console.log(` Files Processed: ${moduleStats.filesProcessed}`);
if (moduleStats.filesCreated > 0) {
console.log(` • Created: ${moduleStats.filesCreated}`);
}
if (moduleStats.filesModified > 0) {
console.log(` • Modified: ${moduleStats.filesModified}`);
}
if (moduleStats.filesDeleted > 0) {
console.log(` • Deleted: ${moduleStats.filesDeleted}`);
}
if (moduleStats.errors > 0) {
console.log(` ❌ Errors: ${moduleStats.errors}`);
}
}
console.log('\n' + '═'.repeat(50));
}
async saveReport(outputPath: string): Promise<void> {
const report = {
timestamp: new Date().toISOString(),
executionTime: this.stats.totalExecutionTime,
overallStats: this.stats.overallStats,
moduleStats: Array.from(this.stats.moduleStats.values()),
};
await plugins.smartfile.memory.toFs(
JSON.stringify(report, null, 2),
outputPath,
);
logger.log('info', `Statistics report saved to ${outputPath}`);
}
private formatDuration(ms: number): string {
if (ms < 1000) {
return `${ms}ms`;
} else if (ms < 60000) {
return `${(ms / 1000).toFixed(1)}s`;
} else {
const minutes = Math.floor(ms / 60000);
const seconds = Math.floor((ms % 60000) / 1000);
return `${minutes}m ${seconds}s`;
}
}
private getModuleIcon(module: string): string {
const icons: Record<string, string> = {
packagejson: '📦',
license: '📝',
tsconfig: '🔧',
cleanup: '🚮',
gitignore: '🔒',
prettier: '✨',
readme: '📖',
templates: '📄',
npmextra: '⚙️',
copy: '📋',
};
return icons[module] || '📁';
}
}

View File

@@ -0,0 +1,318 @@
import * as plugins from './mod.plugins.js';
import * as paths from '../paths.js';
import type { IFormatOperation } from './interfaces.format.js';
export class RollbackManager {
private backupDir: string;
private manifestPath: string;
constructor() {
this.backupDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-backups');
this.manifestPath = plugins.path.join(this.backupDir, 'manifest.json');
}
async createOperation(): Promise<IFormatOperation> {
await this.ensureBackupDir();
const operation: IFormatOperation = {
id: this.generateOperationId(),
timestamp: Date.now(),
files: [],
status: 'pending',
};
await this.updateManifest(operation);
return operation;
}
async backupFile(filepath: string, operationId: string): Promise<void> {
const operation = await this.getOperation(operationId);
if (!operation) {
throw new Error(`Operation ${operationId} not found`);
}
const absolutePath = plugins.path.isAbsolute(filepath)
? filepath
: plugins.path.join(paths.cwd, filepath);
// Check if file exists
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
if (!exists) {
// File doesn't exist yet (will be created), so we skip backup
return;
}
// Read file content and metadata
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const stats = await plugins.smartfile.fs.stat(absolutePath);
const checksum = this.calculateChecksum(content);
// Create backup
const backupPath = this.getBackupPath(operationId, filepath);
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(backupPath));
await plugins.smartfile.memory.toFs(content, backupPath);
// Update operation
operation.files.push({
path: filepath,
originalContent: content,
checksum,
permissions: stats.mode.toString(8),
});
await this.updateManifest(operation);
}
async rollback(operationId: string): Promise<void> {
const operation = await this.getOperation(operationId);
if (!operation) {
// Operation doesn't exist, might have already been rolled back or never created
console.warn(`Operation ${operationId} not found for rollback, skipping`);
return;
}
if (operation.status === 'rolled-back') {
throw new Error(`Operation ${operationId} has already been rolled back`);
}
// Restore files in reverse order
for (let i = operation.files.length - 1; i >= 0; i--) {
const file = operation.files[i];
const absolutePath = plugins.path.isAbsolute(file.path)
? file.path
: plugins.path.join(paths.cwd, file.path);
// Verify backup integrity
const backupPath = this.getBackupPath(operationId, file.path);
const backupContent = plugins.smartfile.fs.toStringSync(backupPath);
const backupChecksum = this.calculateChecksum(backupContent);
if (backupChecksum !== file.checksum) {
throw new Error(`Backup integrity check failed for ${file.path}`);
}
// Restore file
await plugins.smartfile.memory.toFs(file.originalContent, absolutePath);
// Restore permissions
const mode = parseInt(file.permissions, 8);
// Note: Permissions restoration may not work on all platforms
}
// Update operation status
operation.status = 'rolled-back';
await this.updateManifest(operation);
}
async markComplete(operationId: string): Promise<void> {
const operation = await this.getOperation(operationId);
if (!operation) {
throw new Error(`Operation ${operationId} not found`);
}
operation.status = 'completed';
await this.updateManifest(operation);
}
async cleanOldBackups(retentionDays: number): Promise<void> {
const manifest = await this.getManifest();
const cutoffTime = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
const operationsToDelete = manifest.operations.filter(
(op) => op.timestamp < cutoffTime && op.status === 'completed',
);
for (const operation of operationsToDelete) {
// Remove backup files
const operationDir = plugins.path.join(
this.backupDir,
'operations',
operation.id,
);
await plugins.smartfile.fs.remove(operationDir);
// Remove from manifest
manifest.operations = manifest.operations.filter(
(op) => op.id !== operation.id,
);
}
await this.saveManifest(manifest);
}
async verifyBackup(operationId: string): Promise<boolean> {
const operation = await this.getOperation(operationId);
if (!operation) {
return false;
}
for (const file of operation.files) {
const backupPath = this.getBackupPath(operationId, file.path);
const exists = await plugins.smartfile.fs.fileExists(backupPath);
if (!exists) {
return false;
}
const content = plugins.smartfile.fs.toStringSync(backupPath);
const checksum = this.calculateChecksum(content);
if (checksum !== file.checksum) {
return false;
}
}
return true;
}
async listBackups(): Promise<IFormatOperation[]> {
const manifest = await this.getManifest();
return manifest.operations;
}
private async ensureBackupDir(): Promise<void> {
await plugins.smartfile.fs.ensureDir(this.backupDir);
await plugins.smartfile.fs.ensureDir(
plugins.path.join(this.backupDir, 'operations'),
);
}
private generateOperationId(): string {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const random = Math.random().toString(36).substring(2, 8);
return `${timestamp}-${random}`;
}
private getBackupPath(operationId: string, filepath: string): string {
const filename = plugins.path.basename(filepath);
const dir = plugins.path.dirname(filepath);
const safeDir = dir.replace(/[/\\]/g, '__');
return plugins.path.join(
this.backupDir,
'operations',
operationId,
'files',
safeDir,
`${filename}.backup`,
);
}
private calculateChecksum(content: string | Buffer): string {
return plugins.crypto.createHash('sha256').update(content).digest('hex');
}
private async getManifest(): Promise<{ operations: IFormatOperation[] }> {
const defaultManifest = { operations: [] };
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
if (!exists) {
return defaultManifest;
}
try {
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
const manifest = JSON.parse(content);
// Validate the manifest structure
if (this.isValidManifest(manifest)) {
return manifest;
} else {
console.warn(
'Invalid rollback manifest structure, returning default manifest',
);
return defaultManifest;
}
} catch (error) {
console.warn(
`Failed to read rollback manifest: ${error.message}, returning default manifest`,
);
// Try to delete the corrupted file
try {
await plugins.smartfile.fs.remove(this.manifestPath);
} catch (removeError) {
// Ignore removal errors
}
return defaultManifest;
}
}
private async saveManifest(manifest: {
operations: IFormatOperation[];
}): Promise<void> {
// Validate before saving
if (!this.isValidManifest(manifest)) {
throw new Error('Invalid rollback manifest structure, cannot save');
}
// Ensure directory exists
await this.ensureBackupDir();
// Write directly with proper JSON stringification
const jsonContent = JSON.stringify(manifest, null, 2);
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
}
private async getOperation(
operationId: string,
): Promise<IFormatOperation | null> {
const manifest = await this.getManifest();
return manifest.operations.find((op) => op.id === operationId) || null;
}
private async updateManifest(operation: IFormatOperation): Promise<void> {
const manifest = await this.getManifest();
const existingIndex = manifest.operations.findIndex(
(op) => op.id === operation.id,
);
if (existingIndex !== -1) {
manifest.operations[existingIndex] = operation;
} else {
manifest.operations.push(operation);
}
await this.saveManifest(manifest);
}
private isValidManifest(
manifest: any,
): manifest is { operations: IFormatOperation[] } {
// Check if manifest has the required structure
if (!manifest || typeof manifest !== 'object') {
return false;
}
// Check required fields
if (!Array.isArray(manifest.operations)) {
return false;
}
// Check each operation entry
for (const operation of manifest.operations) {
if (
!operation ||
typeof operation !== 'object' ||
typeof operation.id !== 'string' ||
typeof operation.timestamp !== 'number' ||
typeof operation.status !== 'string' ||
!Array.isArray(operation.files)
) {
return false;
}
// Check each file in the operation
for (const file of operation.files) {
if (
!file ||
typeof file !== 'object' ||
typeof file.path !== 'string' ||
typeof file.checksum !== 'string'
) {
return false;
}
}
}
return true;
}
}

View File

@@ -4,14 +4,21 @@ import * as paths from '../paths.js';
import { logger } from '../gitzone.logging.js'; import { logger } from '../gitzone.logging.js';
import { Project } from '../classes.project.js'; import { Project } from '../classes.project.js';
const filesToDelete = ['defaults.yml', 'yarn.lock', 'package-lock.json', 'tslint.json']; const filesToDelete = [
'defaults.yml',
'yarn.lock',
'package-lock.json',
'tslint.json',
];
export const run = async (projectArg: Project) => { export const run = async (projectArg: Project) => {
for (const relativeFilePath of filesToDelete) { for (const relativeFilePath of filesToDelete) {
const fileExists = plugins.smartfile.fs.fileExistsSync(relativeFilePath); const fileExists = plugins.smartfile.fs.fileExistsSync(relativeFilePath);
if (fileExists) { if (fileExists) {
logger.log('info', `Found ${relativeFilePath}! Removing it!`); logger.log('info', `Found ${relativeFilePath}! Removing it!`);
plugins.smartfile.fs.removeSync(plugins.path.join(paths.cwd, relativeFilePath)); plugins.smartfile.fs.removeSync(
plugins.path.join(paths.cwd, relativeFilePath),
);
} else { } else {
logger.log('info', `Project is free of ${relativeFilePath}`); logger.log('info', `Project is free of ${relativeFilePath}`);
} }

View File

@@ -1,6 +1,85 @@
import type { Project } from '../classes.project.js'; import type { Project } from '../classes.project.js';
import * as plugins from '../plugins.js'; import * as plugins from './mod.plugins.js';
import { logger } from '../gitzone.logging.js';
export const run = async (projectArg: Project) => { export const run = async (projectArg: Project) => {
const gitzoneConfig = await projectArg.gitzoneConfig; const gitzoneConfig = await projectArg.gitzoneConfig;
}
// Get copy configuration from npmextra.json
const npmextraConfig = new plugins.npmextra.Npmextra();
const copyConfig = npmextraConfig.dataFor<any>('gitzone.format.copy', {
patterns: [],
});
if (!copyConfig.patterns || copyConfig.patterns.length === 0) {
logger.log('info', 'No copy patterns configured in npmextra.json');
return;
}
for (const pattern of copyConfig.patterns) {
if (!pattern.from || !pattern.to) {
logger.log('warn', 'Invalid copy pattern - missing "from" or "to" field');
continue;
}
try {
// Handle glob patterns
const files = await plugins.smartfile.fs.listFileTree('.', pattern.from);
for (const file of files) {
const sourcePath = file;
let destPath = pattern.to;
// If destination is a directory, preserve filename
if (pattern.to.endsWith('/')) {
const filename = plugins.path.basename(file);
destPath = plugins.path.join(pattern.to, filename);
}
// Handle template variables in destination path
if (pattern.preservePath) {
const relativePath = plugins.path.relative(
plugins.path.dirname(pattern.from.replace(/\*/g, '')),
file,
);
destPath = plugins.path.join(pattern.to, relativePath);
}
// Ensure destination directory exists
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(destPath));
// Copy file
await plugins.smartfile.fs.copy(sourcePath, destPath);
logger.log('info', `Copied ${sourcePath} to ${destPath}`);
}
} catch (error) {
logger.log(
'error',
`Failed to copy pattern ${pattern.from}: ${error.message}`,
);
}
}
};
/**
* Example npmextra.json configuration:
* {
* "gitzone": {
* "format": {
* "copy": {
* "patterns": [
* {
* "from": "src/assets/*",
* "to": "dist/assets/",
* "preservePath": true
* },
* {
* "from": "config/*.json",
* "to": "dist/"
* }
* ]
* }
* }
* }
* }
*/

View File

@@ -8,14 +8,40 @@ const gitignorePath = plugins.path.join(paths.cwd, './.gitignore');
export const run = async (projectArg: Project) => { export const run = async (projectArg: Project) => {
const gitignoreExists = await plugins.smartfile.fs.fileExists(gitignorePath); const gitignoreExists = await plugins.smartfile.fs.fileExists(gitignorePath);
const templateModule = await import('../mod_template/index.js'); let customContent = '';
const ciTemplate = await templateModule.getTemplate('gitignore');
if (gitignoreExists) { if (gitignoreExists) {
// lets get the existing gitignore file // lets get the existing gitignore file
const existingGitIgnoreString = plugins.smartfile.fs.toStringSync(gitignorePath); const existingGitIgnoreString =
let customPart = existingGitIgnoreString.split('# custom\n')[1]; plugins.smartfile.fs.toStringSync(gitignorePath);
customPart ? null : (customPart = '');
// Check for different custom section markers
const customMarkers = ['#------# custom', '# custom'];
for (const marker of customMarkers) {
const splitResult = existingGitIgnoreString.split(marker);
if (splitResult.length > 1) {
// Get everything after the marker (excluding the marker itself)
customContent = splitResult[1].trim();
break;
}
}
}
// Write the template
const templateModule = await import('../mod_template/index.js');
const ciTemplate = await templateModule.getTemplate('gitignore');
await ciTemplate.writeToDisk(paths.cwd);
// Append the custom content if it exists
if (customContent) {
const newGitignoreContent =
plugins.smartfile.fs.toStringSync(gitignorePath);
// The template already ends with "#------# custom", so just append the content
const finalContent =
newGitignoreContent.trimEnd() + '\n' + customContent + '\n';
await plugins.smartfile.fs.toFs(finalContent, gitignorePath);
logger.log('info', 'Updated .gitignore while preserving custom section!');
} else {
logger.log('info', 'Added a .gitignore!');
} }
ciTemplate.writeToDisk(paths.cwd);
logger.log('info', 'Added a .gitignore!');
}; };

View File

@@ -4,26 +4,29 @@ import { Project } from '../classes.project.js';
import { logger } from '../gitzone.logging.js'; import { logger } from '../gitzone.logging.js';
const incompatibleLicenses: string[] = [ const incompatibleLicenses: string[] = ['AGPL', 'GPL', 'SSPL'];
"AGPL",
"GPL",
"SSPL",
];
export const run = async (projectArg: Project) => { export const run = async (projectArg: Project) => {
const nodeModulesInstalled = await plugins.smartfile.fs.isDirectory(plugins.path.join(paths.cwd, 'node_modules')); const nodeModulesInstalled = await plugins.smartfile.fs.isDirectory(
plugins.path.join(paths.cwd, 'node_modules'),
);
if (!nodeModulesInstalled) { if (!nodeModulesInstalled) {
logger.log('warn', 'No node_modules found. Skipping license check'); logger.log('warn', 'No node_modules found. Skipping license check');
return; return;
} }
const licenseChecker = await plugins.smartlegal.createLicenseChecker(); const licenseChecker = await plugins.smartlegal.createLicenseChecker();
const licenseCheckResult = await licenseChecker.excludeLicenseWithinPath(paths.cwd, incompatibleLicenses); const licenseCheckResult = await licenseChecker.excludeLicenseWithinPath(
paths.cwd,
incompatibleLicenses,
);
if (licenseCheckResult.failingModules.length === 0) { if (licenseCheckResult.failingModules.length === 0) {
logger.log('info', 'Success -> licenses passed!'); logger.log('info', 'Success -> licenses passed!');
} else { } else {
logger.log('error', 'Error -> licenses failed. Here is why:'); logger.log('error', 'Error -> licenses failed. Here is why:');
for (const failedModule of licenseCheckResult.failingModules) { for (const failedModule of licenseCheckResult.failingModules) {
console.log(`${failedModule.name} fails with license ${failedModule.license}`); console.log(
`${failedModule.name} fails with license ${failedModule.license}`,
);
} }
} }
}; };

View File

@@ -29,7 +29,12 @@ export const run = async (projectArg: Project) => {
const interactInstance = new plugins.smartinteract.SmartInteract(); const interactInstance = new plugins.smartinteract.SmartInteract();
for (const expectedRepoInformationItem of expectedRepoInformation) { for (const expectedRepoInformationItem of expectedRepoInformation) {
if (!plugins.smartobject.smartGet(npmextraJson.gitzone, expectedRepoInformationItem)) { if (
!plugins.smartobject.smartGet(
npmextraJson.gitzone,
expectedRepoInformationItem,
)
) {
interactInstance.addQuestions([ interactInstance.addQuestions([
{ {
message: `What is the value of ${expectedRepoInformationItem}`, message: `What is the value of ${expectedRepoInformationItem}`,
@@ -43,12 +48,14 @@ export const run = async (projectArg: Project) => {
const answerbucket = await interactInstance.runQueue(); const answerbucket = await interactInstance.runQueue();
for (const expectedRepoInformationItem of expectedRepoInformation) { for (const expectedRepoInformationItem of expectedRepoInformation) {
const cliProvidedValue = answerbucket.getAnswerFor(expectedRepoInformationItem); const cliProvidedValue = answerbucket.getAnswerFor(
expectedRepoInformationItem,
);
if (cliProvidedValue) { if (cliProvidedValue) {
plugins.smartobject.smartAdd( plugins.smartobject.smartAdd(
npmextraJson.gitzone, npmextraJson.gitzone,
expectedRepoInformationItem, expectedRepoInformationItem,
cliProvidedValue cliProvidedValue,
); );
} }
} }

View File

@@ -12,8 +12,62 @@ const ensureDependency = async (
packageJsonObjectArg: any, packageJsonObjectArg: any,
position: 'dep' | 'devDep' | 'everywhere', position: 'dep' | 'devDep' | 'everywhere',
constraint: 'exclude' | 'include' | 'latest', constraint: 'exclude' | 'include' | 'latest',
dependencyArg: string dependencyArg: string,
) => {}; ) => {
const [packageName, version] = dependencyArg.includes('@')
? dependencyArg.split('@').filter(Boolean)
: [dependencyArg, 'latest'];
const targetSections: string[] = [];
switch (position) {
case 'dep':
targetSections.push('dependencies');
break;
case 'devDep':
targetSections.push('devDependencies');
break;
case 'everywhere':
targetSections.push('dependencies', 'devDependencies');
break;
}
for (const section of targetSections) {
if (!packageJsonObjectArg[section]) {
packageJsonObjectArg[section] = {};
}
switch (constraint) {
case 'exclude':
delete packageJsonObjectArg[section][packageName];
break;
case 'include':
if (!packageJsonObjectArg[section][packageName]) {
packageJsonObjectArg[section][packageName] =
version === 'latest' ? '^1.0.0' : version;
}
break;
case 'latest':
// Fetch latest version from npm
try {
const registry = new plugins.smartnpm.NpmRegistry();
const packageInfo = await registry.getPackageInfo(packageName);
const latestVersion = packageInfo['dist-tags'].latest;
packageJsonObjectArg[section][packageName] = `^${latestVersion}`;
} catch (error) {
logger.log(
'warn',
`Could not fetch latest version for ${packageName}, using existing or default`,
);
if (!packageJsonObjectArg[section][packageName]) {
packageJsonObjectArg[section][packageName] =
version === 'latest' ? '^1.0.0' : version;
}
}
break;
}
}
};
export const run = async (projectArg: Project) => { export const run = async (projectArg: Project) => {
const formatStreamWrapper = new plugins.smartstream.StreamWrapper([ const formatStreamWrapper = new plugins.smartstream.StreamWrapper([
@@ -26,14 +80,13 @@ export const run = async (projectArg: Project) => {
// metadata // metadata
packageJson.repository = { packageJson.repository = {
"type": "git", type: 'git',
"url": `git+https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git` url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
}; };
packageJson.bugs = { ((packageJson.bugs = {
"url": `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues` url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
}, }),
packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`; (packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`));
// Check for module type // Check for module type
if (!packageJson.type) { if (!packageJson.type) {
@@ -43,9 +96,15 @@ export const run = async (projectArg: Project) => {
// Check for private or public // Check for private or public
if (packageJson.private !== undefined) { if (packageJson.private !== undefined) {
logger.log('info', 'Success -> found private/public info in package.json!'); logger.log(
'info',
'Success -> found private/public info in package.json!',
);
} else { } else {
logger.log('error', 'found no private boolean! Setting it to private for now!'); logger.log(
'error',
'found no private boolean! Setting it to private for now!',
);
packageJson.private = true; packageJson.private = true;
} }
@@ -53,7 +112,10 @@ export const run = async (projectArg: Project) => {
if (packageJson.license) { if (packageJson.license) {
logger.log('info', 'Success -> found license in package.json!'); logger.log('info', 'Success -> found license in package.json!');
} else { } else {
logger.log('error', 'found no license! Setting it to UNLICENSED for now!'); logger.log(
'error',
'found no license! Setting it to UNLICENSED for now!',
);
packageJson.license = 'UNLICENSED'; packageJson.license = 'UNLICENSED';
} }
@@ -61,33 +123,22 @@ export const run = async (projectArg: Project) => {
if (packageJson.scripts.build) { if (packageJson.scripts.build) {
logger.log('info', 'Success -> found build script in package.json!'); logger.log('info', 'Success -> found build script in package.json!');
} else { } else {
logger.log('error', 'found no build script! Putting a placeholder there for now!'); logger.log(
'error',
'found no build script! Putting a placeholder there for now!',
);
packageJson.scripts.build = `echo "Not needed for now"`; packageJson.scripts.build = `echo "Not needed for now"`;
} }
// Check for buildDocs script // Check for buildDocs script
if (!packageJson.scripts.buildDocs) { if (!packageJson.scripts.buildDocs) {
logger.log('info', 'found no buildDocs script! Putting tsdoc script there now.'); logger.log(
'info',
'found no buildDocs script! Putting tsdoc script there now.',
);
packageJson.scripts.buildDocs = `tsdoc`; packageJson.scripts.buildDocs = `tsdoc`;
} }
// check package.json
if (!packageJson.main) {
logger.log('error', 'no "main" property');
process.exit(0);
}
if (!packageJson.typings) {
logger.log('error', 'no "typings" property');
process.exit(0);
}
if (!packageJson.browserslist) {
packageJson.browserslist = ['last 1 chrome versions'];
}
if (!packageJson.main.includes('dist_') || !packageJson.typings.includes('dist_')) {
logger.log('error', 'check packagesJson main and typings');
process.exit(0);
}
// check for files // check for files
packageJson.files = [ packageJson.files = [
'ts/**/*', 'ts/**/*',
@@ -103,9 +154,31 @@ export const run = async (projectArg: Project) => {
]; ];
// check for dependencies // check for dependencies
await ensureDependency(packageJson, 'devDep', 'latest', '@push.rocks/tapbundle'); await ensureDependency(
await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tstest'); packageJson,
await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tsbuild'); 'devDep',
'latest',
'@push.rocks/tapbundle',
);
await ensureDependency(
packageJson,
'devDep',
'latest',
'@git.zone/tstest',
);
await ensureDependency(
packageJson,
'devDep',
'latest',
'@git.zone/tsbuild',
);
// set overrides
const overrides = plugins.smartfile.fs.toObjectSync(
plugins.path.join(paths.assetsDir, 'overrides.json'),
);
packageJson.pnpm = packageJson.pnpm || {};
packageJson.pnpm.overrides = overrides;
// exclude // exclude
// TODO // TODO

View File

@@ -16,7 +16,12 @@ const prettierDefaultMarkdownConfig: prettier.Options = {
parser: 'markdown', parser: 'markdown',
}; };
const filesToFormat = [`ts/**/*.ts`, `test/**/*.ts`, `readme.md`, `docs/**/*.md`]; const filesToFormat = [
`ts/**/*.ts`,
`test/**/*.ts`,
`readme.md`,
`docs/**/*.md`,
];
const choosePrettierConfig = (fileArg: plugins.smartfile.SmartFile) => { const choosePrettierConfig = (fileArg: plugins.smartfile.SmartFile) => {
switch (fileArg.parsedPath.ext) { switch (fileArg.parsedPath.ext) {
@@ -39,11 +44,14 @@ const prettierTypeScriptPipestop = plugins.through2.obj(
cb(null); cb(null);
} else { } else {
logger.log('info', `${fileArg.path} is being reformated!`); logger.log('info', `${fileArg.path} is being reformated!`);
const formatedFileString = await prettier.format(fileString, chosenConfig); const formatedFileString = await prettier.format(
fileString,
chosenConfig,
);
fileArg.setContentsFromString(formatedFileString); fileArg.setContentsFromString(formatedFileString);
cb(null, fileArg); cb(null, fileArg);
} }
} },
); );
export const run = async (projectArg: Project) => { export const run = async (projectArg: Project) => {

View File

@@ -8,18 +8,25 @@ export const run = async () => {
// Check and initialize readme.md if it doesn't exist // Check and initialize readme.md if it doesn't exist
const readmeExists = await plugins.smartfile.fs.fileExists(readmePath); const readmeExists = await plugins.smartfile.fs.fileExists(readmePath);
if (!readmeExists) { if (!readmeExists) {
await plugins.smartfile.fs.toFs('# Project Readme\n\nThis is the initial readme file.', readmePath); await plugins.smartfile.fs.toFs(
'# Project Readme\n\nThis is the initial readme file.',
readmePath,
);
console.log('Initialized readme.md'); console.log('Initialized readme.md');
} else { } else {
console.log('readme.md already exists'); console.log('readme.md already exists');
} }
// Check and initialize readme.hints.md if it doesn't exist // Check and initialize readme.hints.md if it doesn't exist
const readmeHintsExists = await plugins.smartfile.fs.fileExists(readmeHintsPath); const readmeHintsExists =
await plugins.smartfile.fs.fileExists(readmeHintsPath);
if (!readmeHintsExists) { if (!readmeHintsExists) {
await plugins.smartfile.fs.toFs('# Project Readme Hints\n\nThis is the initial readme hints file.', readmeHintsPath); await plugins.smartfile.fs.toFs(
'# Project Readme Hints\n\nThis is the initial readme hints file.',
readmeHintsPath,
);
console.log('Initialized readme.hints.md'); console.log('Initialized readme.hints.md');
} else { } else {
console.log('readme.hints.md already exists'); console.log('readme.hints.md already exists');
} }
}; };

View File

@@ -26,10 +26,12 @@ export const run = async (project: Project) => {
case 'npm': case 'npm':
case 'wcc': case 'wcc':
if (project.gitzoneConfig.data.npmciOptions.npmAccessLevel === 'public') { if (project.gitzoneConfig.data.npmciOptions.npmAccessLevel === 'public') {
const ciTemplateDefault = await templateModule.getTemplate('ci_default'); const ciTemplateDefault =
await templateModule.getTemplate('ci_default');
ciTemplateDefault.writeToDisk(paths.cwd); ciTemplateDefault.writeToDisk(paths.cwd);
} else { } else {
const ciTemplateDefault = await templateModule.getTemplate('ci_default_private'); const ciTemplateDefault =
await templateModule.getTemplate('ci_default_private');
ciTemplateDefault.writeToDisk(paths.cwd); ciTemplateDefault.writeToDisk(paths.cwd);
} }
logger.log('info', 'Updated .gitlabci.yml!'); logger.log('info', 'Updated .gitlabci.yml!');
@@ -38,10 +40,11 @@ export const run = async (project: Project) => {
case 'website': case 'website':
const ciTemplateDocker = await templateModule.getTemplate('ci_docker'); const ciTemplateDocker = await templateModule.getTemplate('ci_docker');
await ciTemplateDocker.writeToDisk(paths.cwd); await ciTemplateDocker.writeToDisk(paths.cwd);
logger.log('info', 'Updated .gitlabci.yml!'); logger.log('info', 'Updated CI/CD config files!');
// lets care about docker // lets care about docker
const dockerTemplate = await templateModule.getTemplate('dockerfile_service'); const dockerTemplate =
await templateModule.getTemplate('dockerfile_service');
dockerTemplate.writeToDisk(paths.cwd); dockerTemplate.writeToDisk(paths.cwd);
logger.log('info', 'Updated Dockerfile!'); logger.log('info', 'Updated Dockerfile!');
@@ -56,11 +59,22 @@ export const run = async (project: Project) => {
// update html // update html
if (project.gitzoneConfig.data.projectType === 'website') { if (project.gitzoneConfig.data.projectType === 'website') {
const websiteUpdateTemplate = await templateModule.getTemplate('website_update'); const websiteUpdateTemplate =
await templateModule.getTemplate('website_update');
const variables = {
assetbrokerUrl: project.gitzoneConfig.data.module.assetbrokerUrl,
legalUrl: project.gitzoneConfig.data.module.legalUrl,
};
console.log(
'updating website template with variables\n',
JSON.stringify(variables, null, 2),
);
websiteUpdateTemplate.supplyVariables(variables);
await websiteUpdateTemplate.writeToDisk(paths.cwd); await websiteUpdateTemplate.writeToDisk(paths.cwd);
logger.log('info', `Updated html for website!`); logger.log('info', `Updated html for website!`);
} else if (project.gitzoneConfig.data.projectType === 'service') { } else if (project.gitzoneConfig.data.projectType === 'service') {
const websiteUpdateTemplate = await templateModule.getTemplate('service_update'); const websiteUpdateTemplate =
await templateModule.getTemplate('service_update');
await websiteUpdateTemplate.writeToDisk(paths.cwd); await websiteUpdateTemplate.writeToDisk(paths.cwd);
logger.log('info', `Updated html for element template!`); logger.log('info', `Updated html for element template!`);
} else if (project.gitzoneConfig.data.projectType === 'wcc') { } else if (project.gitzoneConfig.data.projectType === 'wcc') {

View File

@@ -0,0 +1,30 @@
import * as plugins from './mod.plugins.js';
import * as paths from '../paths.js';
import { logger } from '../gitzone.logging.js';
import { Project } from '../classes.project.js';
export const run = async (projectArg: Project) => {
// lets care about tsconfig.json
logger.log('info', 'Formatting tsconfig.json...');
const tsconfigSmartfile = await plugins.smartfile.SmartFile.fromFilePath(
plugins.path.join(paths.cwd, 'tsconfig.json'),
);
const tsconfigObject = JSON.parse(tsconfigSmartfile.contentBuffer.toString());
tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
tsconfigObject.compilerOptions.baseUrl = '.';
tsconfigObject.compilerOptions.paths = {};
const tsPublishMod = await import('@git.zone/tspublish');
const tsPublishInstance = new tsPublishMod.TsPublish();
const publishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
for (const publishModule of Object.keys(publishModules)) {
const publishConfig = publishModules[publishModule];
tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [
`./${publishModule}/index.js`,
];
}
tsconfigSmartfile.setContentsFromString(
JSON.stringify(tsconfigObject, null, 2),
);
await tsconfigSmartfile.write();
};

View File

@@ -0,0 +1,44 @@
import { BaseFormatter } from '../classes.baseformatter.js';
import type { IPlannedChange } from '../interfaces.format.js';
import * as plugins from '../mod.plugins.js';
import * as cleanupFormatter from '../format.cleanup.js';
export class CleanupFormatter extends BaseFormatter {
get name(): string {
return 'cleanup';
}
async analyze(): Promise<IPlannedChange[]> {
const changes: IPlannedChange[] = [];
// List of files to remove
const filesToRemove = [
'yarn.lock',
'package-lock.json',
'tslint.json',
'defaults.yml',
];
for (const file of filesToRemove) {
const exists = await plugins.smartfile.fs.fileExists(file);
if (exists) {
changes.push({
type: 'delete',
path: file,
module: this.name,
description: `Remove obsolete file`,
});
}
}
return changes;
}
async applyChange(change: IPlannedChange): Promise<void> {
switch (change.type) {
case 'delete':
await this.deleteFile(change.path);
break;
}
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatCopy from '../format.copy.js';
export class CopyFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'copy', formatCopy);
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatGitignore from '../format.gitignore.js';
export class GitignoreFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'gitignore', formatGitignore);
}
}

View File

@@ -0,0 +1,43 @@
import { BaseFormatter } from '../classes.baseformatter.js';
import type { IPlannedChange } from '../interfaces.format.js';
import { Project } from '../../classes.project.js';
import * as plugins from '../mod.plugins.js';
// This is a wrapper for existing format modules
export class LegacyFormatter extends BaseFormatter {
private moduleName: string;
private formatModule: any;
constructor(
context: any,
project: Project,
moduleName: string,
formatModule: any,
) {
super(context, project);
this.moduleName = moduleName;
this.formatModule = formatModule;
}
get name(): string {
return this.moduleName;
}
async analyze(): Promise<IPlannedChange[]> {
// For legacy modules, we can't easily predict changes
// So we'll return a generic change that indicates the module will run
return [
{
type: 'modify',
path: '<various files>',
module: this.name,
description: `Run ${this.name} formatter`,
},
];
}
async applyChange(change: IPlannedChange): Promise<void> {
// Run the legacy format module
await this.formatModule.run(this.project);
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatLicense from '../format.license.js';
export class LicenseFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'license', formatLicense);
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatNpmextra from '../format.npmextra.js';
export class NpmextraFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'npmextra', formatNpmextra);
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatPackageJson from '../format.packagejson.js';
export class PackageJsonFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'packagejson', formatPackageJson);
}
}

View File

@@ -0,0 +1,223 @@
import { BaseFormatter } from '../classes.baseformatter.js';
import type { IPlannedChange } from '../interfaces.format.js';
import * as plugins from '../mod.plugins.js';
import { logger, logVerbose } from '../../gitzone.logging.js';
export class PrettierFormatter extends BaseFormatter {
get name(): string {
return 'prettier';
}
async analyze(): Promise<IPlannedChange[]> {
const changes: IPlannedChange[] = [];
// Define directories to format (TypeScript directories by default)
const includeDirs = ['ts', 'ts_*', 'test', 'tests'];
// File extensions to format
const extensions = '{ts,tsx,js,jsx,json,md,css,scss,html,xml,yaml,yml}';
// Also format root-level config files
const rootConfigFiles = [
'package.json',
'tsconfig.json',
'npmextra.json',
'.prettierrc',
'.prettierrc.json',
'.prettierrc.js',
'readme.md',
'README.md',
'changelog.md',
'CHANGELOG.md',
// Skip files without extensions as prettier can't infer parser
// 'license',
// 'LICENSE',
'*.md',
];
// Collect all files to format
const allFiles: string[] = [];
// Add files from TypeScript directories
for (const dir of includeDirs) {
const globPattern = `${dir}/**/*.${extensions}`;
const dirFiles = await plugins.smartfile.fs.listFileTree(
'.',
globPattern,
);
allFiles.push(...dirFiles);
}
// Add root config files
for (const pattern of rootConfigFiles) {
const rootFiles = await plugins.smartfile.fs.listFileTree('.', pattern);
// Only include files at root level (no slashes in path)
const rootLevelFiles = rootFiles.filter((f) => !f.includes('/'));
allFiles.push(...rootLevelFiles);
}
// Remove duplicates
const uniqueFiles = [...new Set(allFiles)];
// Get all files that match the pattern
const files = uniqueFiles;
// Ensure we only process actual files (not directories)
const validFiles: string[] = [];
for (const file of files) {
try {
const stats = await plugins.smartfile.fs.stat(file);
if (!stats.isDirectory()) {
validFiles.push(file);
}
} catch (error) {
// Skip files that can't be accessed
logVerbose(`Skipping ${file} - cannot access: ${error.message}`);
}
}
// Check which files need formatting
for (const file of validFiles) {
// Skip files that haven't changed
if (!(await this.shouldProcessFile(file))) {
logVerbose(`Skipping ${file} - no changes detected`);
continue;
}
changes.push({
type: 'modify',
path: file,
module: this.name,
description: 'Format with Prettier',
});
}
logger.log('info', `Found ${changes.length} files to format with Prettier`);
return changes;
}
async execute(changes: IPlannedChange[]): Promise<void> {
const startTime = this.stats.moduleStartTime(this.name);
this.stats.startModule(this.name);
try {
await this.preExecute();
logVerbose(`Processing ${changes.length} files sequentially`);
// Process files sequentially to avoid prettier cache/state issues
for (let i = 0; i < changes.length; i++) {
const change = changes[i];
logVerbose(
`Processing file ${i + 1}/${changes.length}: ${change.path}`,
);
try {
await this.applyChange(change);
this.stats.recordFileOperation(this.name, change.type, true);
} catch (error) {
this.stats.recordFileOperation(this.name, change.type, false);
logger.log(
'error',
`Failed to format ${change.path}: ${error.message}`,
);
// Don't throw - continue with other files
}
}
await this.postExecute();
} catch (error) {
// Rollback removed - no longer tracking operations
throw error;
} finally {
this.stats.endModule(this.name, startTime);
}
}
async applyChange(change: IPlannedChange): Promise<void> {
if (change.type !== 'modify') return;
try {
// Validate the path before processing
if (!change.path || change.path.trim() === '') {
logger.log(
'error',
`Invalid empty path in change: ${JSON.stringify(change)}`,
);
throw new Error('Invalid empty path');
}
// Read current content
const content = plugins.smartfile.fs.toStringSync(change.path);
// Format with prettier
const prettier = await import('prettier');
// Skip files that prettier can't parse without explicit parser
const fileExt = plugins.path.extname(change.path).toLowerCase();
if (!fileExt || fileExt === '') {
// Files without extensions need explicit parser
logVerbose(
`Skipping ${change.path} - no file extension for parser inference`,
);
return;
}
try {
const formatted = await prettier.format(content, {
filepath: change.path,
...(await this.getPrettierConfig()),
});
// Only write if content actually changed
if (formatted !== content) {
// Debug: log the path being written
logVerbose(`Writing formatted content to: ${change.path}`);
await this.modifyFile(change.path, formatted);
logVerbose(`Formatted ${change.path}`);
} else {
logVerbose(`No formatting changes for ${change.path}`);
}
} catch (prettierError) {
// Check if it's a parser error
if (
prettierError.message &&
prettierError.message.includes('No parser could be inferred')
) {
logVerbose(`Skipping ${change.path} - ${prettierError.message}`);
return; // Skip this file silently
}
throw prettierError;
}
} catch (error) {
// Log the full error stack for debugging mkdir issues
if (error.message && error.message.includes('mkdir')) {
logger.log(
'error',
`Failed to format ${change.path}: ${error.message}`,
);
logger.log('error', `Error stack: ${error.stack}`);
} else {
logger.log(
'error',
`Failed to format ${change.path}: ${error.message}`,
);
}
throw error;
}
}
private async getPrettierConfig(): Promise<any> {
// Try to load prettier config from the project
const prettierConfig = new plugins.npmextra.Npmextra();
return prettierConfig.dataFor('prettier', {
// Default prettier config
singleQuote: true,
trailingComma: 'all',
printWidth: 80,
tabWidth: 2,
semi: true,
arrowParens: 'always',
});
}
}

View File

@@ -0,0 +1,24 @@
import { BaseFormatter } from '../classes.baseformatter.js';
import type { IPlannedChange } from '../interfaces.format.js';
import * as formatReadme from '../format.readme.js';
export class ReadmeFormatter extends BaseFormatter {
get name(): string {
return 'readme';
}
async analyze(): Promise<IPlannedChange[]> {
return [
{
type: 'modify',
path: 'readme.md',
module: this.name,
description: 'Ensure readme files exist',
},
];
}
async applyChange(change: IPlannedChange): Promise<void> {
await formatReadme.run();
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatTemplates from '../format.templates.js';
export class TemplatesFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'templates', formatTemplates);
}
}

View File

@@ -0,0 +1,8 @@
import { LegacyFormatter } from './legacy.formatter.js';
import * as formatTsconfig from '../format.tsconfig.js';
export class TsconfigFormatter extends LegacyFormatter {
constructor(context: any, project: any) {
super(context, project, 'tsconfig', formatTsconfig);
}
}

View File

@@ -1,38 +1,192 @@
import * as plugins from './mod.plugins.js'; import * as plugins from './mod.plugins.js';
import { Project } from '../classes.project.js'; import { Project } from '../classes.project.js';
import { FormatContext } from './classes.formatcontext.js';
import { FormatPlanner } from './classes.formatplanner.js';
import { logger, setVerboseMode } from '../gitzone.logging.js';
// Import wrapper classes for formatters
import { CleanupFormatter } from './formatters/cleanup.formatter.js';
import { NpmextraFormatter } from './formatters/npmextra.formatter.js';
import { LicenseFormatter } from './formatters/license.formatter.js';
import { PackageJsonFormatter } from './formatters/packagejson.formatter.js';
import { TemplatesFormatter } from './formatters/templates.formatter.js';
import { GitignoreFormatter } from './formatters/gitignore.formatter.js';
import { TsconfigFormatter } from './formatters/tsconfig.formatter.js';
import { PrettierFormatter } from './formatters/prettier.formatter.js';
import { ReadmeFormatter } from './formatters/readme.formatter.js';
import { CopyFormatter } from './formatters/copy.formatter.js';
export let run = async (
options: {
dryRun?: boolean;
yes?: boolean;
planOnly?: boolean;
savePlan?: string;
fromPlan?: string;
detailed?: boolean;
interactive?: boolean;
parallel?: boolean;
verbose?: boolean;
} = {},
): Promise<any> => {
// Set verbose mode if requested
if (options.verbose) {
setVerboseMode(true);
}
export let run = async (write: boolean = true): Promise<any> => {
const project = await Project.fromCwd(); const project = await Project.fromCwd();
const context = new FormatContext();
// Cache system removed - no longer needed
const planner = new FormatPlanner();
// cleanup // Get configuration from npmextra
const formatCleanup = await import('./format.cleanup.js'); const npmextraConfig = new plugins.npmextra.Npmextra();
await formatCleanup.run(project); const formatConfig = npmextraConfig.dataFor<any>('gitzone.format', {
interactive: true,
showDiffs: false,
autoApprove: false,
planTimeout: 30000,
rollback: {
enabled: true,
autoRollbackOnError: true,
backupRetentionDays: 7,
maxBackupSize: '100MB',
excludePatterns: ['node_modules/**', '.git/**'],
},
modules: {
skip: [],
only: [],
order: [],
},
parallel: true,
cache: {
enabled: true,
clean: true, // Clean invalid entries from cache
},
});
// npmextra // Cache cleaning removed - no longer using cache system
const formatNpmextra = await import('./format.npmextra.js');
await formatNpmextra.run(project);
// license // Override config with command options
const formatLicense = await import('./format.license.js'); const interactive = options.interactive ?? formatConfig.interactive;
await formatLicense.run(project); const autoApprove = options.yes ?? formatConfig.autoApprove;
const parallel = options.parallel ?? formatConfig.parallel;
// format package.json try {
const formatPackageJson = await import('./format.packagejson.js'); // Initialize formatters
await formatPackageJson.run(project); const formatters = [
new CleanupFormatter(context, project),
new NpmextraFormatter(context, project),
new LicenseFormatter(context, project),
new PackageJsonFormatter(context, project),
new TemplatesFormatter(context, project),
new GitignoreFormatter(context, project),
new TsconfigFormatter(context, project),
new PrettierFormatter(context, project),
new ReadmeFormatter(context, project),
new CopyFormatter(context, project),
];
// format .gitlab-ci.yml // Filter formatters based on configuration
const formatTemplates = await import('./format.templates.js'); const activeFormatters = formatters.filter((formatter) => {
await formatTemplates.run(project); if (formatConfig.modules.only.length > 0) {
return formatConfig.modules.only.includes(formatter.name);
}
if (formatConfig.modules.skip.includes(formatter.name)) {
return false;
}
return true;
});
// format .gitignore // Plan phase
const formatGitignore = await import('./format.gitignore.js'); logger.log('info', 'Analyzing project for format operations...');
await formatGitignore.run(project); let plan = options.fromPlan
? JSON.parse(await plugins.smartfile.fs.toStringSync(options.fromPlan))
: await planner.planFormat(activeFormatters);
// format TypeScript // Display plan
const formatPrettier = await import('./format.prettier.js'); await planner.displayPlan(plan, options.detailed);
await formatPrettier.run(project);
// format readme.md // Save plan if requested
const formatReadme = await import('./format.readme.js'); if (options.savePlan) {
await formatReadme.run(); await plugins.smartfile.memory.toFs(
JSON.stringify(plan, null, 2),
options.savePlan,
);
logger.log('info', `Plan saved to ${options.savePlan}`);
}
// Exit if plan-only mode
if (options.planOnly) {
return;
}
// Dry-run mode
if (options.dryRun) {
logger.log('info', 'Dry-run mode - no changes will be made');
return;
}
// Interactive confirmation
if (interactive && !autoApprove) {
const interactInstance = new plugins.smartinteract.SmartInteract();
const response = await interactInstance.askQuestion({
type: 'confirm',
name: 'proceed',
message: 'Proceed with formatting?',
default: true,
});
if (!(response as any).value) {
logger.log('info', 'Format operation cancelled by user');
return;
}
}
// Execute phase
logger.log(
'info',
`Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`,
);
await planner.executePlan(plan, activeFormatters, context, parallel);
// Finish statistics tracking
context.getFormatStats().finish();
// Display statistics
const showStats = npmextraConfig.dataFor('gitzone.format.showStats', true);
if (showStats) {
context.getFormatStats().displayStats();
}
// Save stats if requested
if (options.detailed) {
const statsPath = `.nogit/format-stats-${Date.now()}.json`;
await context.getFormatStats().saveReport(statsPath);
}
logger.log('success', 'Format operations completed successfully!');
} catch (error) {
logger.log('error', `Format operation failed: ${error.message}`);
// Rollback system has been removed for stability
throw error;
}
};
// Export CLI command handlers
export const handleRollback = async (operationId?: string): Promise<void> => {
logger.log('info', 'Rollback system has been disabled for stability');
};
export const handleListBackups = async (): Promise<void> => {
logger.log('info', 'Backup system has been disabled for stability');
};
export const handleCleanBackups = async (): Promise<void> => {
logger.log(
'info',
'Backup cleaning has been disabled - backup system removed',
);
}; };

View File

@@ -0,0 +1,45 @@
export type IFormatOperation = {
id: string;
timestamp: number;
files: Array<{
path: string;
originalContent: string;
checksum: string;
permissions: string;
}>;
status: 'pending' | 'in-progress' | 'completed' | 'failed' | 'rolled-back';
error?: Error;
};
export type IFormatPlan = {
summary: {
totalFiles: number;
filesAdded: number;
filesModified: number;
filesRemoved: number;
estimatedTime: number;
};
changes: Array<{
type: 'create' | 'modify' | 'delete';
path: string;
module: string;
description: string;
diff?: string;
size?: number;
}>;
warnings: Array<{
level: 'info' | 'warning' | 'error';
message: string;
module: string;
}>;
};
export type IPlannedChange = {
type: 'create' | 'modify' | 'delete';
path: string;
module: string;
description: string;
content?: string; // For create/modify operations
diff?: string;
size?: number;
};

View File

@@ -1,5 +1,7 @@
export * from '../plugins.js'; export * from '../plugins.js';
import * as crypto from 'crypto';
import * as path from 'path';
import * as lik from '@push.rocks/lik'; import * as lik from '@push.rocks/lik';
import * as smartfile from '@push.rocks/smartfile'; import * as smartfile from '@push.rocks/smartfile';
import * as smartgulp from '@push.rocks/smartgulp'; import * as smartgulp from '@push.rocks/smartgulp';
@@ -9,5 +11,21 @@ import * as smartobject from '@push.rocks/smartobject';
import * as smartnpm from '@push.rocks/smartnpm'; import * as smartnpm from '@push.rocks/smartnpm';
import * as smartstream from '@push.rocks/smartstream'; import * as smartstream from '@push.rocks/smartstream';
import * as through2 from 'through2'; import * as through2 from 'through2';
import * as npmextra from '@push.rocks/npmextra';
import * as smartdiff from '@push.rocks/smartdiff';
export { lik, smartfile, smartgulp, smartinteract, smartlegal, smartobject, smartnpm, smartstream, through2 }; export {
crypto,
path,
lik,
smartfile,
smartgulp,
smartinteract,
smartlegal,
smartobject,
smartnpm,
smartstream,
through2,
npmextra,
smartdiff,
};

View File

@@ -35,7 +35,10 @@ export class Meta {
* sorts the metaRepoData * sorts the metaRepoData
*/ */
public async sortMetaRepoData() { public async sortMetaRepoData() {
const stringifiedMetadata = plugins.smartjson.stringify(this.metaRepoData, []); const stringifiedMetadata = plugins.smartjson.stringify(
this.metaRepoData,
[],
);
this.metaRepoData = plugins.smartjson.parse(stringifiedMetadata); this.metaRepoData = plugins.smartjson.parse(stringifiedMetadata);
} }
@@ -45,11 +48,15 @@ export class Meta {
public async readDirectory() { public async readDirectory() {
await this.syncToRemote(true); await this.syncToRemote(true);
logger.log('info', `reading directory`); logger.log('info', `reading directory`);
const metaFileExists = plugins.smartfile.fs.fileExistsSync(this.filePaths.metaJson); const metaFileExists = plugins.smartfile.fs.fileExistsSync(
this.filePaths.metaJson,
);
if (!metaFileExists) { if (!metaFileExists) {
throw new Error(`meta file does not exist at ${this.filePaths.metaJson}`); throw new Error(`meta file does not exist at ${this.filePaths.metaJson}`);
} }
this.metaRepoData = plugins.smartfile.fs.toObjectSync(this.filePaths.metaJson); this.metaRepoData = plugins.smartfile.fs.toObjectSync(
this.filePaths.metaJson,
);
} }
/** /**
@@ -73,10 +80,13 @@ export class Meta {
// write .meta.json to disk // write .meta.json to disk
plugins.smartfile.memory.toFsSync( plugins.smartfile.memory.toFsSync(
JSON.stringify(this.metaRepoData, null, 2), JSON.stringify(this.metaRepoData, null, 2),
this.filePaths.metaJson this.filePaths.metaJson,
); );
// write .gitignore to disk // write .gitignore to disk
plugins.smartfile.memory.toFsSync(await this.generateGitignore(), this.filePaths.gitIgnore); plugins.smartfile.memory.toFsSync(
await this.generateGitignore(),
this.filePaths.gitIgnore,
);
} }
/** /**
@@ -84,13 +94,17 @@ export class Meta {
*/ */
public async syncToRemote(gitCleanArg = false) { public async syncToRemote(gitCleanArg = false) {
logger.log('info', `syncing from origin master`); logger.log('info', `syncing from origin master`);
await this.smartshellInstance.exec(`cd ${this.cwd} && git pull origin master`); await this.smartshellInstance.exec(
`cd ${this.cwd} && git pull origin master`,
);
if (gitCleanArg) { if (gitCleanArg) {
logger.log('info', `cleaning the repository from old directories`); logger.log('info', `cleaning the repository from old directories`);
await this.smartshellInstance.exec(`cd ${this.cwd} && git clean -fd`); await this.smartshellInstance.exec(`cd ${this.cwd} && git clean -fd`);
} }
logger.log('info', `syncing to remote origin master`); logger.log('info', `syncing to remote origin master`);
await this.smartshellInstance.exec(`cd ${this.cwd} && git push origin master`); await this.smartshellInstance.exec(
`cd ${this.cwd} && git push origin master`,
);
} }
/** /**
@@ -98,23 +112,29 @@ export class Meta {
*/ */
public async updateLocalRepos() { public async updateLocalRepos() {
await this.syncToRemote(); await this.syncToRemote();
const projects = plugins.smartfile.fs.toObjectSync(this.filePaths.metaJson).projects; const projects = plugins.smartfile.fs.toObjectSync(
this.filePaths.metaJson,
).projects;
const preExistingFolders = plugins.smartfile.fs.listFoldersSync(this.cwd); const preExistingFolders = plugins.smartfile.fs.listFoldersSync(this.cwd);
for (const preExistingFolderArg of preExistingFolders) { for (const preExistingFolderArg of preExistingFolders) {
if ( if (
preExistingFolderArg !== '.git' && preExistingFolderArg !== '.git' &&
!Object.keys(projects).find((projectFolder) => !Object.keys(projects).find((projectFolder) =>
projectFolder.startsWith(preExistingFolderArg) projectFolder.startsWith(preExistingFolderArg),
) )
) { ) {
const response = await plugins.smartinteraction.SmartInteract.getCliConfirmation( const response =
`Do you want to delete superfluous directory >>${preExistingFolderArg}<< ?`, await plugins.smartinteraction.SmartInteract.getCliConfirmation(
true `Do you want to delete superfluous directory >>${preExistingFolderArg}<< ?`,
); true,
);
if (response) { if (response) {
logger.log('warn', `Deleting >>${preExistingFolderArg}<<!`); logger.log('warn', `Deleting >>${preExistingFolderArg}<<!`);
} else { } else {
logger.log('warn', `Not deleting ${preExistingFolderArg} by request!`); logger.log(
'warn',
`Not deleting ${preExistingFolderArg} by request!`,
);
} }
} }
} }
@@ -131,7 +151,7 @@ export class Meta {
logger.log('info', `found ${missingRepos.length} missing repos`); logger.log('info', `found ${missingRepos.length} missing repos`);
for (const missingRepo of missingRepos) { for (const missingRepo of missingRepos) {
await this.smartshellInstance.exec( await this.smartshellInstance.exec(
`cd ${this.cwd} && git clone ${this.metaRepoData.projects[missingRepo]} ${missingRepo}` `cd ${this.cwd} && git clone ${this.metaRepoData.projects[missingRepo]} ${missingRepo}`,
); );
} }
@@ -140,7 +160,7 @@ export class Meta {
logger.log('info', `persist changes with a git commit`); logger.log('info', `persist changes with a git commit`);
await this.smartshellInstance.exec( await this.smartshellInstance.exec(
`cd ${this.cwd} && git add -A && git commit -m "updated structure"` `cd ${this.cwd} && git add -A && git commit -m "updated structure"`,
); );
await this.syncToRemote(); await this.syncToRemote();
@@ -160,28 +180,39 @@ export class Meta {
*/ */
public async initProject() { public async initProject() {
await this.syncToRemote(true); await this.syncToRemote(true);
const fileExists = await plugins.smartfile.fs.fileExists(this.filePaths.metaJson); const fileExists = await plugins.smartfile.fs.fileExists(
this.filePaths.metaJson,
);
if (!fileExists) { if (!fileExists) {
await plugins.smartfile.memory.toFs( await plugins.smartfile.memory.toFs(
JSON.stringify({ JSON.stringify({
projects: {}, projects: {},
}), }),
this.filePaths.metaJson this.filePaths.metaJson,
);
logger.log(
`success`,
`created a new .meta.json in directory ${this.cwd}`,
); );
logger.log(`success`, `created a new .meta.json in directory ${this.cwd}`);
await plugins.smartfile.memory.toFs( await plugins.smartfile.memory.toFs(
JSON.stringify({ JSON.stringify({
name: this.dirName, name: this.dirName,
version: '1.0.0', version: '1.0.0',
}), }),
this.filePaths.packageJson this.filePaths.packageJson,
);
logger.log(
`success`,
`created a new package.json in directory ${this.cwd}`,
); );
logger.log(`success`, `created a new package.json in directory ${this.cwd}`);
} else { } else {
logger.log(`error`, `directory ${this.cwd} already has a .metaJson file. Doing nothing.`); logger.log(
`error`,
`directory ${this.cwd} already has a .metaJson file. Doing nothing.`,
);
} }
await this.smartshellInstance.exec( await this.smartshellInstance.exec(
`cd ${this.cwd} && git add -A && git commit -m "feat(project): init meta project for ${this.dirName}"` `cd ${this.cwd} && git add -A && git commit -m "feat(project): init meta project for ${this.dirName}"`,
); );
await this.updateLocalRepos(); await this.updateLocalRepos();
} }
@@ -195,7 +226,9 @@ export class Meta {
const existingProject = this.metaRepoData.projects[projectNameArg]; const existingProject = this.metaRepoData.projects[projectNameArg];
if (existingProject) { if (existingProject) {
throw new Error('Project already exists! Please remove it first before adding it again.'); throw new Error(
'Project already exists! Please remove it first before adding it again.',
);
} }
this.metaRepoData.projects[projectNameArg] = gitUrlArg; this.metaRepoData.projects[projectNameArg] = gitUrlArg;
@@ -203,7 +236,7 @@ export class Meta {
await this.sortMetaRepoData(); await this.sortMetaRepoData();
await this.writeToDisk(); await this.writeToDisk();
await this.smartshellInstance.exec( await this.smartshellInstance.exec(
`cd ${this.cwd} && git add -A && git commit -m "feat(project): add ${projectNameArg}"` `cd ${this.cwd} && git add -A && git commit -m "feat(project): add ${projectNameArg}"`,
); );
await this.updateLocalRepos(); await this.updateLocalRepos();
} }
@@ -217,7 +250,10 @@ export class Meta {
const existingProject = this.metaRepoData.projects[projectNameArg]; const existingProject = this.metaRepoData.projects[projectNameArg];
if (!existingProject) { if (!existingProject) {
logger.log('error', `Project ${projectNameArg} does not exist! So it cannot be removed`); logger.log(
'error',
`Project ${projectNameArg} does not exist! So it cannot be removed`,
);
return; return;
} }
@@ -228,7 +264,9 @@ export class Meta {
await this.writeToDisk(); await this.writeToDisk();
logger.log('info', 'removing directory from cwd'); logger.log('info', 'removing directory from cwd');
await plugins.smartfile.fs.remove(plugins.path.join(paths.cwd, projectNameArg)); await plugins.smartfile.fs.remove(
plugins.path.join(paths.cwd, projectNameArg),
);
await this.updateLocalRepos(); await this.updateLocalRepos();
} }
} }

View File

@@ -5,11 +5,11 @@ export let run = (argvArg) => {
let projectInfo = new plugins.projectinfo.ProjectInfo(paths.cwd); let projectInfo = new plugins.projectinfo.ProjectInfo(paths.cwd);
if (argvArg._[1] === 'ci') { if (argvArg._[1] === 'ci') {
plugins.smartopen.openUrl( plugins.smartopen.openUrl(
`https://gitlab.com/${projectInfo.git.gituser}/${projectInfo.git.gitrepo}/settings/ci_cd` `https://gitlab.com/${projectInfo.git.gituser}/${projectInfo.git.gitrepo}/settings/ci_cd`,
); );
} else if (argvArg._[1] === 'pipelines') { } else if (argvArg._[1] === 'pipelines') {
plugins.smartopen.openUrl( plugins.smartopen.openUrl(
`https://gitlab.com/${projectInfo.git.gituser}/${projectInfo.git.gitrepo}/pipelines` `https://gitlab.com/${projectInfo.git.gituser}/${projectInfo.git.gitrepo}/pipelines`,
); );
} }
}; };

View File

@@ -0,0 +1,227 @@
import * as plugins from './mod.plugins.js';
import * as helpers from './helpers.js';
import { logger } from '../gitzone.logging.js';
export type ContainerStatus = 'running' | 'stopped' | 'not_exists';
export interface IDockerRunOptions {
name: string;
image: string;
ports?: { [key: string]: string };
volumes?: { [key: string]: string };
environment?: { [key: string]: string };
restart?: string;
command?: string;
}
export class DockerContainer {
private smartshell: plugins.smartshell.Smartshell;
constructor() {
this.smartshell = new plugins.smartshell.Smartshell({
executor: 'bash',
});
}
/**
* Check if Docker is installed and available
*/
public async checkDocker(): Promise<boolean> {
try {
const result = await this.smartshell.exec('docker --version');
return result.exitCode === 0;
} catch (error) {
return false;
}
}
/**
* Get container status
*/
public async getStatus(containerName: string): Promise<ContainerStatus> {
try {
// Check if running
const runningResult = await this.smartshell.exec(
`docker ps --format '{{.Names}}' | grep -q "^${containerName}$"`
);
if (runningResult.exitCode === 0) {
return 'running';
}
// Check if exists but stopped
const existsResult = await this.smartshell.exec(
`docker ps -a --format '{{.Names}}' | grep -q "^${containerName}$"`
);
if (existsResult.exitCode === 0) {
return 'stopped';
}
return 'not_exists';
} catch (error) {
return 'not_exists';
}
}
/**
* Start a container
*/
public async start(containerName: string): Promise<boolean> {
try {
const result = await this.smartshell.exec(`docker start ${containerName}`);
return result.exitCode === 0;
} catch (error) {
return false;
}
}
/**
* Stop a container
*/
public async stop(containerName: string): Promise<boolean> {
try {
const result = await this.smartshell.exec(`docker stop ${containerName}`);
return result.exitCode === 0;
} catch (error) {
return false;
}
}
/**
* Remove a container
*/
public async remove(containerName: string, force: boolean = false): Promise<boolean> {
try {
const forceFlag = force ? '-f' : '';
const result = await this.smartshell.exec(`docker rm ${forceFlag} ${containerName}`);
return result.exitCode === 0;
} catch (error) {
return false;
}
}
/**
* Run a new container
*/
public async run(options: IDockerRunOptions): Promise<boolean> {
let command = 'docker run -d';
// Add name
command += ` --name ${options.name}`;
// Add ports
if (options.ports) {
for (const [hostPort, containerPort] of Object.entries(options.ports)) {
command += ` -p ${hostPort}:${containerPort}`;
}
}
// Add volumes
if (options.volumes) {
for (const [hostPath, containerPath] of Object.entries(options.volumes)) {
command += ` -v "${hostPath}:${containerPath}"`;
}
}
// Add environment variables
if (options.environment) {
for (const [key, value] of Object.entries(options.environment)) {
command += ` -e ${key}="${value}"`;
}
}
// Add restart policy
if (options.restart) {
command += ` --restart ${options.restart}`;
}
// Add image
command += ` ${options.image}`;
// Add command if provided
if (options.command) {
command += ` ${options.command}`;
}
try {
const result = await this.smartshell.exec(command);
return result.exitCode === 0;
} catch (error) {
logger.log('error', `Failed to run container: ${error.message}`);
return false;
}
}
/**
* Execute a command in a running container
*/
public async exec(containerName: string, command: string): Promise<string> {
try {
const result = await this.smartshell.exec(`docker exec ${containerName} ${command}`);
if (result.exitCode === 0) {
return result.stdout;
}
return '';
} catch (error) {
return '';
}
}
/**
* Get container logs
*/
public async logs(containerName: string, lines?: number): Promise<string> {
try {
const tailFlag = lines ? `--tail ${lines}` : '';
const result = await this.smartshell.exec(`docker logs ${tailFlag} ${containerName}`);
return result.stdout;
} catch (error) {
return `Error getting logs: ${error.message}`;
}
}
/**
* Check if a container exists
*/
public async exists(containerName: string): Promise<boolean> {
const status = await this.getStatus(containerName);
return status !== 'not_exists';
}
/**
* Check if a container is running
*/
public async isRunning(containerName: string): Promise<boolean> {
const status = await this.getStatus(containerName);
return status === 'running';
}
/**
* Wait for a container to be ready
*/
public async waitForReady(containerName: string, maxAttempts: number = 30): Promise<boolean> {
for (let i = 0; i < maxAttempts; i++) {
if (await this.isRunning(containerName)) {
return true;
}
await plugins.smartdelay.delayFor(1000);
}
return false;
}
/**
* Get container information
*/
public async inspect(containerName: string): Promise<any> {
try {
const result = await this.smartshell.exec(`docker inspect ${containerName}`);
if (result.exitCode === 0) {
return JSON.parse(result.stdout);
}
return null;
} catch (error) {
return null;
}
}
}

View File

@@ -0,0 +1,262 @@
import * as plugins from './mod.plugins.js';
import * as helpers from './helpers.js';
import { logger } from '../gitzone.logging.js';
export interface IServiceConfig {
PROJECT_NAME: string;
MONGODB_HOST: string;
MONGODB_NAME: string;
MONGODB_PORT: string;
MONGODB_USER: string;
MONGODB_PASS: string;
MONGODB_URL: string;
S3_HOST: string;
S3_PORT: string;
S3_CONSOLE_PORT: string;
S3_USER: string;
S3_PASS: string;
S3_BUCKET: string;
}
export class ServiceConfiguration {
private configPath: string;
private config: IServiceConfig;
constructor() {
this.configPath = plugins.path.join(process.cwd(), '.nogit', 'env.json');
}
/**
* Load or create the configuration
*/
public async loadOrCreate(): Promise<IServiceConfig> {
await this.ensureNogitDirectory();
if (await this.configExists()) {
await this.loadConfig();
await this.updateMissingFields();
} else {
await this.createDefaultConfig();
}
return this.config;
}
/**
* Get the current configuration
*/
public getConfig(): IServiceConfig {
return this.config;
}
/**
* Save the configuration to file
*/
public async saveConfig(): Promise<void> {
await plugins.smartfile.memory.toFs(
JSON.stringify(this.config, null, 2),
this.configPath
);
}
/**
* Ensure .nogit directory exists
*/
private async ensureNogitDirectory(): Promise<void> {
const nogitPath = plugins.path.join(process.cwd(), '.nogit');
await plugins.smartfile.fs.ensureDir(nogitPath);
}
/**
* Check if configuration file exists
*/
private async configExists(): Promise<boolean> {
return plugins.smartfile.fs.fileExists(this.configPath);
}
/**
* Load configuration from file
*/
private async loadConfig(): Promise<void> {
const configContent = await plugins.smartfile.fs.toStringSync(this.configPath);
this.config = JSON.parse(configContent);
}
/**
* Create default configuration
*/
private async createDefaultConfig(): Promise<void> {
const projectName = helpers.getProjectName();
const mongoPort = await helpers.getRandomAvailablePort();
const s3Port = await helpers.getRandomAvailablePort();
let s3ConsolePort = s3Port + 1;
// Ensure console port is also available
while (!(await helpers.isPortAvailable(s3ConsolePort))) {
s3ConsolePort++;
}
const mongoUser = 'defaultadmin';
const mongoPass = 'defaultpass';
const mongoHost = 'localhost';
const mongoName = projectName;
const mongoPortStr = mongoPort.toString();
this.config = {
PROJECT_NAME: projectName,
MONGODB_HOST: mongoHost,
MONGODB_NAME: mongoName,
MONGODB_PORT: mongoPortStr,
MONGODB_USER: mongoUser,
MONGODB_PASS: mongoPass,
MONGODB_URL: `mongodb://${mongoUser}:${mongoPass}@${mongoHost}:${mongoPortStr}/${mongoName}?authSource=admin`,
S3_HOST: 'localhost',
S3_PORT: s3Port.toString(),
S3_CONSOLE_PORT: s3ConsolePort.toString(),
S3_USER: 'defaultadmin',
S3_PASS: 'defaultpass',
S3_BUCKET: `${projectName}-documents`
};
await this.saveConfig();
logger.log('ok', '✅ Created .nogit/env.json with project defaults');
logger.log('info', `📍 MongoDB port: ${mongoPort}`);
logger.log('info', `📍 S3 API port: ${s3Port}`);
logger.log('info', `📍 S3 Console port: ${s3ConsolePort}`);
}
/**
* Update missing fields in existing configuration
*/
private async updateMissingFields(): Promise<void> {
const projectName = helpers.getProjectName();
let updated = false;
const fieldsAdded: string[] = [];
// Check and add missing fields
if (!this.config.PROJECT_NAME) {
this.config.PROJECT_NAME = projectName;
fieldsAdded.push('PROJECT_NAME');
updated = true;
}
if (!this.config.MONGODB_HOST) {
this.config.MONGODB_HOST = 'localhost';
fieldsAdded.push('MONGODB_HOST');
updated = true;
}
if (!this.config.MONGODB_NAME) {
this.config.MONGODB_NAME = projectName;
fieldsAdded.push('MONGODB_NAME');
updated = true;
}
if (!this.config.MONGODB_PORT) {
const port = await helpers.getRandomAvailablePort();
this.config.MONGODB_PORT = port.toString();
fieldsAdded.push(`MONGODB_PORT(${port})`);
updated = true;
}
if (!this.config.MONGODB_USER) {
this.config.MONGODB_USER = 'defaultadmin';
fieldsAdded.push('MONGODB_USER');
updated = true;
}
if (!this.config.MONGODB_PASS) {
this.config.MONGODB_PASS = 'defaultpass';
fieldsAdded.push('MONGODB_PASS');
updated = true;
}
// Always update MONGODB_URL based on current settings
const oldUrl = this.config.MONGODB_URL;
this.config.MONGODB_URL = `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${this.config.MONGODB_HOST}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`;
if (oldUrl !== this.config.MONGODB_URL) {
fieldsAdded.push('MONGODB_URL');
updated = true;
}
if (!this.config.S3_HOST) {
this.config.S3_HOST = 'localhost';
fieldsAdded.push('S3_HOST');
updated = true;
}
if (!this.config.S3_PORT) {
const port = await helpers.getRandomAvailablePort();
this.config.S3_PORT = port.toString();
fieldsAdded.push(`S3_PORT(${port})`);
updated = true;
}
if (!this.config.S3_CONSOLE_PORT) {
const s3Port = parseInt(this.config.S3_PORT);
let consolePort = s3Port + 1;
while (!(await helpers.isPortAvailable(consolePort))) {
consolePort++;
}
this.config.S3_CONSOLE_PORT = consolePort.toString();
fieldsAdded.push(`S3_CONSOLE_PORT(${consolePort})`);
updated = true;
}
if (!this.config.S3_USER) {
this.config.S3_USER = 'defaultadmin';
fieldsAdded.push('S3_USER');
updated = true;
}
if (!this.config.S3_PASS) {
this.config.S3_PASS = 'defaultpass';
fieldsAdded.push('S3_PASS');
updated = true;
}
if (!this.config.S3_BUCKET) {
this.config.S3_BUCKET = `${projectName}-documents`;
fieldsAdded.push('S3_BUCKET');
updated = true;
}
if (updated) {
await this.saveConfig();
logger.log('ok', `✅ Added missing fields: ${fieldsAdded.join(', ')}`);
} else {
logger.log('ok', '✅ Configuration complete');
}
}
/**
* Get MongoDB connection string
*/
public getMongoConnectionString(useNetworkIp: boolean = false): string {
const host = useNetworkIp ? '${networkIp}' : this.config.MONGODB_HOST;
return `mongodb://${this.config.MONGODB_USER}:${this.config.MONGODB_PASS}@${host}:${this.config.MONGODB_PORT}/${this.config.MONGODB_NAME}?authSource=admin`;
}
/**
* Get container names
*/
public getContainerNames() {
return {
mongo: `${this.config.PROJECT_NAME}-mongodb`,
minio: `${this.config.PROJECT_NAME}-minio`
};
}
/**
* Get data directories
*/
public getDataDirectories() {
return {
mongo: plugins.path.join(process.cwd(), '.nogit', 'mongodata'),
minio: plugins.path.join(process.cwd(), '.nogit', 'miniodata')
};
}
}

View File

@@ -0,0 +1,424 @@
import * as plugins from './mod.plugins.js';
import * as helpers from './helpers.js';
import { ServiceConfiguration } from './classes.serviceconfiguration.js';
import { DockerContainer } from './classes.dockercontainer.js';
import { logger } from '../gitzone.logging.js';
export class ServiceManager {
private config: ServiceConfiguration;
private docker: DockerContainer;
constructor() {
this.config = new ServiceConfiguration();
this.docker = new DockerContainer();
}
/**
* Initialize the service manager
*/
public async init(): Promise<void> {
// Check Docker availability
if (!(await this.docker.checkDocker())) {
logger.log('error', 'Error: Docker is not installed. Please install Docker first.');
process.exit(1);
}
// Load or create configuration
await this.config.loadOrCreate();
logger.log('info', `📋 Project: ${this.config.getConfig().PROJECT_NAME}`);
}
/**
* Start MongoDB service
*/
public async startMongoDB(): Promise<void> {
logger.log('note', '📦 MongoDB:');
const config = this.config.getConfig();
const containers = this.config.getContainerNames();
const directories = this.config.getDataDirectories();
// Ensure data directory exists
await plugins.smartfile.fs.ensureDir(directories.mongo);
const status = await this.docker.getStatus(containers.mongo);
switch (status) {
case 'running':
logger.log('ok', ' Already running ✓');
break;
case 'stopped':
if (await this.docker.start(containers.mongo)) {
logger.log('ok', ' Started ✓');
} else {
logger.log('error', ' Failed to start');
}
break;
case 'not_exists':
logger.log('note', ' Creating container...');
const success = await this.docker.run({
name: containers.mongo,
image: 'mongo:7.0',
ports: {
[`0.0.0.0:${config.MONGODB_PORT}`]: '27017'
},
volumes: {
[directories.mongo]: '/data/db'
},
environment: {
MONGO_INITDB_ROOT_USERNAME: config.MONGODB_USER,
MONGO_INITDB_ROOT_PASSWORD: config.MONGODB_PASS,
MONGO_INITDB_DATABASE: config.MONGODB_NAME
},
restart: 'unless-stopped',
command: '--bind_ip_all'
});
if (success) {
logger.log('ok', ' Created and started ✓');
} else {
logger.log('error', ' Failed to create container');
}
break;
}
logger.log('info', ` Container: ${containers.mongo}`);
logger.log('info', ` Port: ${config.MONGODB_PORT}`);
logger.log('info', ` Connection: ${this.config.getMongoConnectionString()}`);
// Show Compass connection string
const networkIp = await helpers.getLocalNetworkIp();
const compassString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`;
logger.log('ok', ` Compass: ${compassString}`);
}
/**
* Start MinIO service
*/
public async startMinIO(): Promise<void> {
logger.log('note', '📦 S3/MinIO:');
const config = this.config.getConfig();
const containers = this.config.getContainerNames();
const directories = this.config.getDataDirectories();
// Ensure data directory exists
await plugins.smartfile.fs.ensureDir(directories.minio);
const status = await this.docker.getStatus(containers.minio);
switch (status) {
case 'running':
logger.log('ok', ' Already running ✓');
break;
case 'stopped':
if (await this.docker.start(containers.minio)) {
logger.log('ok', ' Started ✓');
} else {
logger.log('error', ' Failed to start');
}
break;
case 'not_exists':
logger.log('note', ' Creating container...');
const success = await this.docker.run({
name: containers.minio,
image: 'minio/minio',
ports: {
[config.S3_PORT]: '9000',
[config.S3_CONSOLE_PORT]: '9001'
},
volumes: {
[directories.minio]: '/data'
},
environment: {
MINIO_ROOT_USER: config.S3_USER,
MINIO_ROOT_PASSWORD: config.S3_PASS
},
restart: 'unless-stopped',
command: 'server /data --console-address ":9001"'
});
if (success) {
logger.log('ok', ' Created and started ✓');
// Wait for MinIO to be ready
await plugins.smartdelay.delayFor(3000);
// Create default bucket
await this.docker.exec(
containers.minio,
`mc alias set local http://localhost:9000 ${config.S3_USER} ${config.S3_PASS}`
);
await this.docker.exec(
containers.minio,
`mc mb local/${config.S3_BUCKET}`
);
logger.log('ok', ` Bucket '${config.S3_BUCKET}' created ✓`);
} else {
logger.log('error', ' Failed to create container');
}
break;
}
logger.log('info', ` Container: ${containers.minio}`);
logger.log('info', ` Port: ${config.S3_PORT}`);
logger.log('info', ` Bucket: ${config.S3_BUCKET}`);
logger.log('info', ` API: http://${config.S3_HOST}:${config.S3_PORT}`);
logger.log('info', ` Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT} (login: ${config.S3_USER}/***)`);
}
/**
* Stop MongoDB service
*/
public async stopMongoDB(): Promise<void> {
logger.log('note', '📦 MongoDB:');
const containers = this.config.getContainerNames();
const status = await this.docker.getStatus(containers.mongo);
if (status === 'running') {
if (await this.docker.stop(containers.mongo)) {
logger.log('ok', ' Stopped ✓');
} else {
logger.log('error', ' Failed to stop');
}
} else {
logger.log('note', ' Not running');
}
}
/**
* Stop MinIO service
*/
public async stopMinIO(): Promise<void> {
logger.log('note', '📦 S3/MinIO:');
const containers = this.config.getContainerNames();
const status = await this.docker.getStatus(containers.minio);
if (status === 'running') {
if (await this.docker.stop(containers.minio)) {
logger.log('ok', ' Stopped ✓');
} else {
logger.log('error', ' Failed to stop');
}
} else {
logger.log('note', ' Not running');
}
}
/**
* Show service status
*/
public async showStatus(): Promise<void> {
helpers.printHeader('Service Status');
const config = this.config.getConfig();
const containers = this.config.getContainerNames();
logger.log('info', `Project: ${config.PROJECT_NAME}`);
console.log();
// MongoDB status
const mongoStatus = await this.docker.getStatus(containers.mongo);
switch (mongoStatus) {
case 'running':
logger.log('ok', '📦 MongoDB: 🟢 Running');
logger.log('info', ` ├─ Container: ${containers.mongo}`);
logger.log('info', ` ├─ Connection: ${this.config.getMongoConnectionString()}`);
// Show Compass connection string
const networkIp = await helpers.getLocalNetworkIp();
const compassString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`;
logger.log('ok', ` └─ Compass: ${compassString}`);
break;
case 'stopped':
logger.log('note', '📦 MongoDB: 🟡 Stopped');
logger.log('info', ` └─ Container: ${containers.mongo}`);
break;
case 'not_exists':
logger.log('info', '📦 MongoDB: ⚪ Not installed');
break;
}
// MinIO status
const minioStatus = await this.docker.getStatus(containers.minio);
switch (minioStatus) {
case 'running':
logger.log('ok', '📦 S3/MinIO: 🟢 Running');
logger.log('info', ` ├─ Container: ${containers.minio}`);
logger.log('info', ` ├─ API: http://${config.S3_HOST}:${config.S3_PORT}`);
logger.log('info', ` ├─ Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`);
logger.log('info', ` └─ Bucket: ${config.S3_BUCKET}`);
break;
case 'stopped':
logger.log('note', '📦 S3/MinIO: 🟡 Stopped');
logger.log('info', ` └─ Container: ${containers.minio}`);
break;
case 'not_exists':
logger.log('info', '📦 S3/MinIO: ⚪ Not installed');
break;
}
}
/**
* Show configuration
*/
public async showConfig(): Promise<void> {
helpers.printHeader('Current Configuration');
const config = this.config.getConfig();
logger.log('info', `Project: ${config.PROJECT_NAME}`);
console.log();
logger.log('note', 'MongoDB:');
logger.log('info', ` Host: ${config.MONGODB_HOST}:${config.MONGODB_PORT}`);
logger.log('info', ` Database: ${config.MONGODB_NAME}`);
logger.log('info', ` User: ${config.MONGODB_USER}`);
logger.log('info', ' Password: ***');
logger.log('info', ` Container: ${this.config.getContainerNames().mongo}`);
logger.log('info', ` Data: ${this.config.getDataDirectories().mongo}`);
logger.log('info', ` Connection: ${this.config.getMongoConnectionString()}`);
console.log();
logger.log('note', 'S3/MinIO:');
logger.log('info', ` Host: ${config.S3_HOST}`);
logger.log('info', ` API Port: ${config.S3_PORT}`);
logger.log('info', ` Console Port: ${config.S3_CONSOLE_PORT}`);
logger.log('info', ` User: ${config.S3_USER}`);
logger.log('info', ' Password: ***');
logger.log('info', ` Bucket: ${config.S3_BUCKET}`);
logger.log('info', ` Container: ${this.config.getContainerNames().minio}`);
logger.log('info', ` Data: ${this.config.getDataDirectories().minio}`);
logger.log('info', ` API URL: http://${config.S3_HOST}:${config.S3_PORT}`);
logger.log('info', ` Console URL: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`);
}
/**
* Show MongoDB Compass connection string
*/
public async showCompassConnection(): Promise<void> {
helpers.printHeader('MongoDB Compass Connection');
const config = this.config.getConfig();
const networkIp = await helpers.getLocalNetworkIp();
const connectionString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`;
logger.log('info', 'MongoDB Compass is a GUI tool for MongoDB. To connect:');
console.log();
logger.log('info', '1. Download MongoDB Compass from:');
logger.log('info', ' https://www.mongodb.com/products/compass');
console.log();
logger.log('info', '2. Open Compass and paste this connection string:');
logger.log('ok', ` ${connectionString}`);
console.log();
logger.log('note', 'Connection Details:');
logger.log('info', ` Network IP: ${networkIp}`);
logger.log('info', ` Port: ${config.MONGODB_PORT}`);
logger.log('info', ` Database: ${config.MONGODB_NAME}`);
logger.log('info', ` Username: ${config.MONGODB_USER}`);
logger.log('info', ` Auth Source: admin`);
}
/**
* Show logs for a service
*/
public async showLogs(service: string, lines: number = 20): Promise<void> {
const containers = this.config.getContainerNames();
switch (service) {
case 'mongo':
case 'mongodb':
if (await this.docker.isRunning(containers.mongo)) {
helpers.printHeader(`MongoDB Logs (last ${lines} lines)`);
const logs = await this.docker.logs(containers.mongo, lines);
console.log(logs);
} else {
logger.log('note', 'MongoDB container is not running');
}
break;
case 'minio':
case 's3':
if (await this.docker.isRunning(containers.minio)) {
helpers.printHeader(`S3/MinIO Logs (last ${lines} lines)`);
const logs = await this.docker.logs(containers.minio, lines);
console.log(logs);
} else {
logger.log('note', 'S3/MinIO container is not running');
}
break;
case 'all':
case '':
await this.showLogs('mongo', lines);
console.log();
await this.showLogs('minio', lines);
break;
default:
logger.log('note', 'Usage: gitzone services logs [mongo|s3|all] [lines]');
break;
}
}
/**
* Remove containers
*/
public async removeContainers(): Promise<void> {
const containers = this.config.getContainerNames();
let removed = false;
if (await this.docker.exists(containers.mongo)) {
if (await this.docker.remove(containers.mongo, true)) {
logger.log('ok', ' MongoDB container removed ✓');
removed = true;
}
}
if (await this.docker.exists(containers.minio)) {
if (await this.docker.remove(containers.minio, true)) {
logger.log('ok', ' S3/MinIO container removed ✓');
removed = true;
}
}
if (!removed) {
logger.log('note', ' No containers to remove');
}
}
/**
* Clean data directories
*/
public async cleanData(): Promise<void> {
const directories = this.config.getDataDirectories();
let cleaned = false;
if (await plugins.smartfile.fs.fileExists(directories.mongo)) {
await plugins.smartfile.fs.remove(directories.mongo);
logger.log('ok', ' MongoDB data removed ✓');
cleaned = true;
}
if (await plugins.smartfile.fs.fileExists(directories.minio)) {
await plugins.smartfile.fs.remove(directories.minio);
logger.log('ok', ' S3/MinIO data removed ✓');
cleaned = true;
}
if (!cleaned) {
logger.log('note', ' No data to clean');
}
}
}

123
ts/mod_services/helpers.ts Normal file
View File

@@ -0,0 +1,123 @@
import * as plugins from './mod.plugins.js';
import * as net from 'net';
import { logger } from '../gitzone.logging.js';
/**
* Check if a port is available
*/
export const isPortAvailable = async (port: number): Promise<boolean> => {
return new Promise((resolve) => {
const server = net.createServer();
server.once('error', () => {
resolve(false);
});
server.once('listening', () => {
server.close();
resolve(true);
});
server.listen(port, '0.0.0.0');
});
};
/**
* Get a random available port between 20000 and 30000
*/
export const getRandomAvailablePort = async (): Promise<number> => {
const maxAttempts = 100;
for (let i = 0; i < maxAttempts; i++) {
const port = Math.floor(Math.random() * 10001) + 20000;
if (await isPortAvailable(port)) {
return port;
}
}
// Fallback: let the system assign a port
return 0;
};
/**
* Get the project name from package.json or directory
*/
export const getProjectName = (): string => {
try {
const packageJsonPath = plugins.path.join(process.cwd(), 'package.json');
if (plugins.smartfile.fs.fileExistsSync(packageJsonPath)) {
const packageJson = plugins.smartfile.fs.toObjectSync(packageJsonPath);
if (packageJson.name) {
// Sanitize: @fin.cx/skr → fin-cx-skr
return packageJson.name.replace(/@/g, '').replace(/[\/\.]/g, '-');
}
}
} catch (error) {
// Ignore errors and fall back to directory name
}
return plugins.path.basename(process.cwd());
};
/**
* Print a header with decorative lines
*/
export const printHeader = (title: string) => {
console.log();
logger.log('info', '═══════════════════════════════════════════════════════════════');
logger.log('info', ` ${title}`);
logger.log('info', '═══════════════════════════════════════════════════════════════');
console.log();
};
/**
* Format bytes to human readable string
*/
export const formatBytes = (bytes: number): string => {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
};
/**
* Get the local network IP address
*/
export const getLocalNetworkIp = async (): Promise<string> => {
const smartnetworkInstance = new plugins.smartnetwork.SmartNetwork();
const gateways = await smartnetworkInstance.getGateways();
// Find the best local IP from network interfaces
for (const interfaceName of Object.keys(gateways)) {
const interfaces = gateways[interfaceName];
for (const iface of interfaces) {
// Skip loopback and internal interfaces
if (!iface.internal && iface.family === 'IPv4') {
const address = iface.address;
// Prefer LAN IPs
if (address.startsWith('192.168.') || address.startsWith('10.') || address.startsWith('172.')) {
return address;
}
}
}
}
// Fallback: try to get any non-internal IPv4
for (const interfaceName of Object.keys(gateways)) {
const interfaces = gateways[interfaceName];
for (const iface of interfaces) {
if (!iface.internal && iface.family === 'IPv4') {
return iface.address;
}
}
}
// Last resort: localhost
return 'localhost';
};

219
ts/mod_services/index.ts Normal file
View File

@@ -0,0 +1,219 @@
import * as plugins from './mod.plugins.js';
import * as helpers from './helpers.js';
import { ServiceManager } from './classes.servicemanager.js';
import { logger } from '../gitzone.logging.js';
export const run = async (argvArg: any) => {
const serviceManager = new ServiceManager();
await serviceManager.init();
const command = argvArg._[1] || 'help';
const service = argvArg._[2] || 'all';
switch (command) {
case 'start':
await handleStart(serviceManager, service);
break;
case 'stop':
await handleStop(serviceManager, service);
break;
case 'restart':
await handleRestart(serviceManager, service);
break;
case 'status':
await serviceManager.showStatus();
break;
case 'config':
await serviceManager.showConfig();
break;
case 'compass':
await serviceManager.showCompassConnection();
break;
case 'logs':
const lines = parseInt(argvArg._[3]) || 20;
await serviceManager.showLogs(service, lines);
break;
case 'remove':
await handleRemove(serviceManager);
break;
case 'clean':
await handleClean(serviceManager);
break;
case 'help':
default:
showHelp();
break;
}
};
async function handleStart(serviceManager: ServiceManager, service: string) {
helpers.printHeader('Starting Services');
switch (service) {
case 'mongo':
case 'mongodb':
await serviceManager.startMongoDB();
break;
case 'minio':
case 's3':
await serviceManager.startMinIO();
break;
case 'all':
case '':
await serviceManager.startMongoDB();
console.log();
await serviceManager.startMinIO();
break;
default:
logger.log('error', `Unknown service: ${service}`);
logger.log('note', 'Use: mongo, s3, or all');
break;
}
}
async function handleStop(serviceManager: ServiceManager, service: string) {
helpers.printHeader('Stopping Services');
switch (service) {
case 'mongo':
case 'mongodb':
await serviceManager.stopMongoDB();
break;
case 'minio':
case 's3':
await serviceManager.stopMinIO();
break;
case 'all':
case '':
await serviceManager.stopMongoDB();
console.log();
await serviceManager.stopMinIO();
break;
default:
logger.log('error', `Unknown service: ${service}`);
logger.log('note', 'Use: mongo, s3, or all');
break;
}
}
async function handleRestart(serviceManager: ServiceManager, service: string) {
helpers.printHeader('Restarting Services');
switch (service) {
case 'mongo':
case 'mongodb':
await serviceManager.stopMongoDB();
await plugins.smartdelay.delayFor(2000);
await serviceManager.startMongoDB();
break;
case 'minio':
case 's3':
await serviceManager.stopMinIO();
await plugins.smartdelay.delayFor(2000);
await serviceManager.startMinIO();
break;
case 'all':
case '':
await serviceManager.stopMongoDB();
await serviceManager.stopMinIO();
await plugins.smartdelay.delayFor(2000);
await serviceManager.startMongoDB();
console.log();
await serviceManager.startMinIO();
break;
default:
logger.log('error', `Unknown service: ${service}`);
break;
}
}
async function handleRemove(serviceManager: ServiceManager) {
helpers.printHeader('Removing Containers');
logger.log('note', '⚠️ This will remove containers but preserve data');
const shouldContinue = await plugins.smartinteract.SmartInteract.getCliConfirmation('Continue?', false);
if (shouldContinue) {
await serviceManager.removeContainers();
} else {
logger.log('note', 'Cancelled');
}
}
async function handleClean(serviceManager: ServiceManager) {
helpers.printHeader('Clean All');
logger.log('error', '⚠️ WARNING: This will remove all containers and data!');
logger.log('error', 'This action cannot be undone!');
const smartinteraction = new plugins.smartinteract.SmartInteract();
const confirmAnswer = await smartinteraction.askQuestion({
name: 'confirm',
type: 'input',
message: 'Type "yes" to confirm:',
default: 'no'
});
if (confirmAnswer.value === 'yes') {
await serviceManager.removeContainers();
console.log();
await serviceManager.cleanData();
logger.log('ok', 'All cleaned ✓');
} else {
logger.log('note', 'Cancelled');
}
}
function showHelp() {
helpers.printHeader('GitZone Services Manager');
logger.log('ok', 'Usage: gitzone services [command] [options]');
console.log();
logger.log('note', 'Commands:');
logger.log('info', ' start [service] Start services (mongo|s3|all)');
logger.log('info', ' stop [service] Stop services (mongo|s3|all)');
logger.log('info', ' restart [service] Restart services (mongo|s3|all)');
logger.log('info', ' status Show service status');
logger.log('info', ' config Show current configuration');
logger.log('info', ' compass Show MongoDB Compass connection string');
logger.log('info', ' logs [service] Show logs (mongo|s3|all) [lines]');
logger.log('info', ' remove Remove all containers');
logger.log('info', ' clean Remove all containers and data ⚠️');
logger.log('info', ' help Show this help message');
console.log();
logger.log('note', 'Features:');
logger.log('info', ' • Auto-creates .nogit/env.json with smart defaults');
logger.log('info', ' • Random ports (20000-30000) to avoid conflicts');
logger.log('info', ' • Project-specific containers for multi-project support');
logger.log('info', ' • Preserves custom configuration values');
logger.log('info', ' • MongoDB Compass connection support');
console.log();
logger.log('note', 'Examples:');
logger.log('info', ' gitzone services start # Start all services');
logger.log('info', ' gitzone services start mongo # Start only MongoDB');
logger.log('info', ' gitzone services stop # Stop all services');
logger.log('info', ' gitzone services status # Check service status');
logger.log('info', ' gitzone services config # Show configuration');
logger.log('info', ' gitzone services compass # Get MongoDB Compass connection');
logger.log('info', ' gitzone services logs mongo 50 # Show last 50 lines of MongoDB logs');
}

View File

@@ -0,0 +1,9 @@
export * from '../plugins.js';
import * as smartshell from '@push.rocks/smartshell';
import * as smartfile from '@push.rocks/smartfile';
import * as smartinteract from '@push.rocks/smartinteract';
import * as smartnetwork from '@push.rocks/smartnetwork';
import * as smartdelay from '@push.rocks/smartdelay';
export { smartshell, smartfile, smartinteract, smartnetwork, smartdelay };

View File

@@ -16,13 +16,15 @@ export let run = () => {
* create a new project with 'gitzone template [template]' * create a new project with 'gitzone template [template]'
the following templates exist: ${(() => { the following templates exist: ${(() => {
let projects = `\n`; let projects = `\n`;
for (const template of plugins.smartfile.fs.listFoldersSync(paths.templatesDir)) { for (const template of plugins.smartfile.fs.listFoldersSync(
paths.templatesDir,
)) {
projects += ` - ${template}\n`; projects += ` - ${template}\n`;
} }
return projects; return projects;
})()} })()}
* format a project with 'gitzone format' * format a project with 'gitzone format'
` `,
); );
done.resolve(); done.resolve();
return done.promise; return done.promise;

View File

@@ -15,7 +15,9 @@ export const run = async (argvArg: any) => {
}); });
await smartshellInstance.execStrict(`cd ${paths.cwd} && git checkout master`); await smartshellInstance.execStrict(`cd ${paths.cwd} && git checkout master`);
await smartshellInstance.execStrict(`cd ${paths.cwd} && git pull origin master`); await smartshellInstance.execStrict(
`cd ${paths.cwd} && git pull origin master`,
);
await smartshellInstance.execStrict(`cd ${paths.cwd} && npm ci`); await smartshellInstance.execStrict(`cd ${paths.cwd} && npm ci`);
await provideNoGitFiles(); await provideNoGitFiles();

View File

@@ -16,7 +16,9 @@ export const isTemplate = async (templateNameArg: string) => {
export const getTemplate = async (templateNameArg: string) => { export const getTemplate = async (templateNameArg: string) => {
if (isTemplate(templateNameArg)) { if (isTemplate(templateNameArg)) {
const localScafTemplate = new plugins.smartscaf.ScafTemplate(getTemplatePath(templateNameArg)); const localScafTemplate = new plugins.smartscaf.ScafTemplate(
getTemplatePath(templateNameArg),
);
await localScafTemplate.readTemplateFromDir(); await localScafTemplate.readTemplateFromDir();
return localScafTemplate; return localScafTemplate;
} else { } else {
@@ -32,7 +34,8 @@ export const run = async (argvArg: any) => {
const answerBucket = await smartinteract.askQuestion({ const answerBucket = await smartinteract.askQuestion({
type: 'list', type: 'list',
default: 'npm', default: 'npm',
message: 'What template do you want to scaffold? (Only showing mpost common options)', message:
'What template do you want to scaffold? (Only showing mpost common options)',
name: 'templateName', name: 'templateName',
choices: ['npm', 'service', 'wcc', 'website'], choices: ['npm', 'service', 'wcc', 'website'],
}); });

View File

@@ -2,7 +2,7 @@ import * as plugins from './plugins.js';
export let packageDir = plugins.path.join( export let packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../' '../',
); );
export let assetsDir = plugins.path.join(packageDir, './assets'); export let assetsDir = plugins.path.join(packageDir, './assets');
export let templatesDir = plugins.path.join(assetsDir, 'templates'); export let templatesDir = plugins.path.join(assetsDir, 'templates');

View File

@@ -7,6 +7,11 @@ import * as smartcli from '@push.rocks/smartcli';
import * as smartpath from '@push.rocks/smartpath'; import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise'; import * as smartpromise from '@push.rocks/smartpromise';
import * as smartupdate from '@push.rocks/smartupdate'; import * as smartupdate from '@push.rocks/smartupdate';
import * as smartshell from '@push.rocks/smartshell';
import * as smartnetwork from '@push.rocks/smartnetwork';
import * as smartfile from '@push.rocks/smartfile';
import * as smartinteract from '@push.rocks/smartinteract';
import * as smartdelay from '@push.rocks/smartdelay';
export { export {
smartlog, smartlog,
@@ -18,4 +23,9 @@ export {
smartpath, smartpath,
smartpromise, smartpromise,
smartupdate, smartupdate,
smartshell,
smartnetwork,
smartfile,
smartinteract,
smartdelay,
}; };

View File

@@ -6,9 +6,9 @@
"module": "NodeNext", "module": "NodeNext",
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",
"esModuleInterop": true, "esModuleInterop": true,
"verbatimModuleSyntax": true "verbatimModuleSyntax": true,
"baseUrl": ".",
"paths": {}
}, },
"exclude": [ "exclude": ["dist_*/**/*.d.ts"]
"dist_*/**/*.d.ts"
]
} }