Compare commits
	
		
			34 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f5e7d43cf3 | |||
| d80faa044a | |||
| 64062e5c43 | |||
| bd22844280 | |||
| 366c4a0bc2 | |||
| 0d3b10bd00 | |||
| a41e3d5d2c | |||
| c45cff89de | |||
| 7bb43ad478 | |||
| 8dcaf1c631 | |||
| 422761806d | |||
| 31360240a9 | |||
| e338ee584f | |||
| 31d2e18830 | |||
| a162ddabbb | |||
| 5dfa1d72aa | |||
| 7074a19a7f | |||
| 5774fb4da2 | |||
| be45ce765d | |||
| 2a250b8823 | |||
| 9a436cb4be | |||
| 86782c39dd | |||
| fba3e9d2b0 | |||
| cc37f70185 | |||
| dbc1a1ba18 | |||
| ff57f8a322 | |||
| 968e67330d | |||
| 935ee20e83 | |||
| c205180991 | |||
| 4a53bc4abc | |||
| a86fb3bb8e | |||
| b187000ae4 | |||
| c715adfd6c | |||
| 7b9ebfdacb | 
@@ -19,4 +19,8 @@ node_modules/
 | 
				
			|||||||
dist/
 | 
					dist/
 | 
				
			||||||
dist_*/
 | 
					dist_*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# AI
 | 
				
			||||||
 | 
					.claude/
 | 
				
			||||||
 | 
					.serena/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#------# custom
 | 
					#------# custom
 | 
				
			||||||
							
								
								
									
										135
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								changelog.md
									
									
									
									
									
								
							@@ -1,5 +1,140 @@
 | 
				
			|||||||
# Changelog
 | 
					# Changelog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-11-03 - 1.19.2 - fix(tsdoc)
 | 
				
			||||||
 | 
					Bump @git.zone/tsdoc to ^1.6.1 and add .claude/settings.local.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Update dependency @git.zone/tsdoc from ^1.6.0 to ^1.6.1
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json to include local Claude settings/permissions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-11-02 - 1.19.1 - fix(dependencies)
 | 
				
			||||||
 | 
					Bump dependencies and add local Claude settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Bump devDependencies: @git.zone/tsbuild -> ^2.7.1, @git.zone/tsrun -> ^1.6.2, @git.zone/tstest -> ^2.7.0
 | 
				
			||||||
 | 
					- Upgrade runtime dependencies: @git.zone/tsdoc -> ^1.6.0; update @push.rocks packages (smartcli ^4.0.19, smartjson ^5.2.0, smartlog ^3.1.10, smartnetwork ^4.4.0, etc.)
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json (local project permissions/settings file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-10-23 - 1.19.0 - feat(mod_commit)
 | 
				
			||||||
 | 
					Add CLI UI helpers and improve commit workflow with progress, recommendations and summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Introduce ts/mod_commit/mod.ui.ts: reusable CLI UI helpers (pretty headers, sections, AI recommendation box, step printer, commit summary and helpers for consistent messaging).
 | 
				
			||||||
 | 
					- Refactor ts/mod_commit/index.ts: use new UI functions to display AI recommendations, show step-by-step progress for baking commit info, generating changelog, staging, committing, bumping version and optional push; include commit SHA in final summary.
 | 
				
			||||||
 | 
					- Enhance ts/mod_commit/mod.helpers.ts: bumpProjectVersion now accepts currentStep/totalSteps to report progress and returns a consistent newVersion after handling npm/deno/both cases.
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json: local permissions configuration for development tooling.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-10-23 - 1.18.9 - fix(mod_commit)
 | 
				
			||||||
 | 
					Stage and commit deno.json when bumping/syncing versions and create/update git tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- bumpDenoVersion now creates a Smartshell instance and runs git add deno.json, git commit -m "v<newVersion>", and git tag v<newVersion> to persist the version bump
 | 
				
			||||||
 | 
					- syncVersionToDenoJson now stages deno.json, amends the npm version commit with --no-edit, and recreates the tag with -fa to keep package.json and deno.json in sync
 | 
				
			||||||
 | 
					- Added informative logger messages after creating commits and tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-10-23 - 1.18.8 - fix(mod_commit)
 | 
				
			||||||
 | 
					Improve commit workflow: detect project type and current branch; add robust version bump helpers for npm/deno
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Add mod_commit/mod.helpers.ts with utilities: detectCurrentBranch(), detectProjectType(), bumpProjectVersion(), bumpDenoVersion(), bumpNpmVersion(), syncVersionToDenoJson(), and calculateNewVersion()
 | 
				
			||||||
 | 
					- Refactor ts/mod_commit/index.ts to use the new helpers: bumpProjectVersion(projectType, ... ) instead of a hard npm version call and push the actual current branch instead of hardcoding 'master'
 | 
				
			||||||
 | 
					- Support bumping versions for npm-only, deno-only, and hybrid (both) projects and synchronize versions from package.json to deno.json when applicable
 | 
				
			||||||
 | 
					- Improve branch detection with a fallback to 'master' and informative logging on detection failures
 | 
				
			||||||
 | 
					- Add local Claude settings file (.claude/settings.local.json) (editor/CI config) — no code behavior change but included in diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-07 - 1.18.7 - fix(claude)
 | 
				
			||||||
 | 
					Add .claude local settings to whitelist dev tool permissions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json to configure allowed permissions for local AI/tooling helpers (Bash commands, WebFetch, and mcp_serena actions).
 | 
				
			||||||
 | 
					- Disable enableAllProjectMcpServers (set to false) to limit automatic project MCP server usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-07 - 1.18.6 - fix(deps)
 | 
				
			||||||
 | 
					Bump dependency versions and add local Claude settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Updated devDependencies: @git.zone/tsbuild ^2.6.4 → ^2.6.8, @git.zone/tstest ^2.3.4 → ^2.3.6, @push.rocks/smartfile ^11.2.5 → ^11.2.7
 | 
				
			||||||
 | 
					- Updated dependencies: @git.zone/tsdoc ^1.5.1 → ^1.5.2, @git.zone/tspublish ^1.10.1 → ^1.10.3, @push.rocks/smartlog ^3.1.8 → ^3.1.9, @push.rocks/smartnpm ^2.0.4 → ^2.0.6, @push.rocks/smartscaf ^4.0.17 → ^4.0.19
 | 
				
			||||||
 | 
					- Added .claude/settings.local.json to configure local Claude permissions/settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-17 - 1.18.5 - fix(dependencies)
 | 
				
			||||||
 | 
					Bump smartshell and smartscaf versions; add .claude local settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Update @push.rocks/smartshell from ^3.2.4 to ^3.3.0 in package.json
 | 
				
			||||||
 | 
					- Update @push.rocks/smartscaf from ^4.0.16 to ^4.0.17 in package.json
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json for local assistant permissions/configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-17 - 1.18.4 - fix(cli)
 | 
				
			||||||
 | 
					Update dependencies, add local Claude settings, and update gitignore template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Bump several dependencies: @git.zone/tsbuild -> ^2.6.4, @git.zone/tspublish -> ^1.10.1, @git.zone/tstest -> ^2.3.4, @push.rocks/smartfile -> ^11.2.5, @push.rocks/npmextra -> ^5.3.3, @push.rocks/smartchok -> ^1.1.1, @push.rocks/smartlog -> ^3.1.8, @push.rocks/smartpath -> ^6.0.0, prettier -> ^3.6.2
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json with local permissions configuration for AI tooling
 | 
				
			||||||
 | 
					- Update assets/templates/gitignore to ignore .claude/ and .serena/ directories
 | 
				
			||||||
 | 
					- Add pnpm onlyBuiltDependencies entries: esbuild and mongodb-memory-server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-16 - 1.18.3 - fix(services)
 | 
				
			||||||
 | 
					Simplify S3 endpoint handling in ServiceConfiguration to store host only
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- S3_ENDPOINT now stores the raw host (e.g. 'localhost') instead of a full URL with protocol and port.
 | 
				
			||||||
 | 
					- Default .nogit/env.json creation uses the host-only S3_ENDPOINT.
 | 
				
			||||||
 | 
					- Sync/update logic (when syncing with Docker or reconfiguring ports) sets S3_ENDPOINT to the host only.
 | 
				
			||||||
 | 
					- Consumers that previously relied on S3_ENDPOINT containing protocol and port should now construct the full endpoint URL using S3_USESSL, S3_HOST and S3_PORT.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-16 - 1.18.1 - fix(services)
 | 
				
			||||||
 | 
					Improve services and commit flow: stop AiDoc, use silent docker inspect, sync ports with logging, fix config loading, and bump deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Ensure AiDoc is stopped after building commit recommendation to avoid resource leaks
 | 
				
			||||||
 | 
					- Use execSilent for `docker inspect` in DockerContainer to avoid shell noise and improve JSON parsing
 | 
				
			||||||
 | 
					- Sync Docker-exposed ports into service configuration with explicit notes (logs) when MongoDB / S3 ports are updated
 | 
				
			||||||
 | 
					- Fix synchronous config loading by removing an unnecessary await in ServiceConfiguration.loadConfig
 | 
				
			||||||
 | 
					- Bump dependencies: @push.rocks/smartshell -> ^3.2.4, @git.zone/tsdoc -> ^1.5.1
 | 
				
			||||||
 | 
					- Add pnpm.onlyBuiltDependencies for puppeteer and sharp to package.json
 | 
				
			||||||
 | 
					- Add local Claude settings file (.claude/settings.local.json) with development permissions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-16 - 1.18.0 - feat(services)
 | 
				
			||||||
 | 
					Add Docker port mapping sync and reconfigure workflow for local services
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Add getPortMappings to DockerContainer to extract port bindings from docker inspect output
 | 
				
			||||||
 | 
					- Sync existing container port mappings into .nogit/env.json when loading/creating service configuration
 | 
				
			||||||
 | 
					- Validate and automatically update ports only when containers are not present; preserve container ports when containers exist
 | 
				
			||||||
 | 
					- Recreate containers automatically if detected container port mappings differ from configuration (MongoDB and MinIO)
 | 
				
			||||||
 | 
					- Add reconfigure method and new CLI command to reassign ports and optionally restart services
 | 
				
			||||||
 | 
					- Improve status output to show configured ports and port availability information
 | 
				
			||||||
 | 
					- Minor helpers and imports updated (DockerContainer injected into ServiceConfiguration)
 | 
				
			||||||
 | 
					- Add .claude/settings.local.json (local permissions config) to repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-15 - 1.17.5 - fix(services)
 | 
				
			||||||
 | 
					Update S3 credentials naming and add S3_ENDPOINT/S3_USESSL support for improved MinIO integration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Replaced S3_USER/S3_PASS with S3_ACCESSKEY/S3_SECRETKEY in ServiceConfiguration
 | 
				
			||||||
 | 
					- Added S3_ENDPOINT field with automatic protocol selection based on S3_USESSL
 | 
				
			||||||
 | 
					- Introduced S3_USESSL boolean field for SSL/TLS configuration
 | 
				
			||||||
 | 
					- Updated ServiceManager logging to display new S3_USESSL configuration
 | 
				
			||||||
 | 
					- Added .claude/settings.local.json for local permission settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-15 - 1.17.4 - fix(services)
 | 
				
			||||||
 | 
					Update S3 credentials naming and add S3_ENDPOINT/S3_USESSL support for improved MinIO integration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Replaced S3_USER/S3_PASS with S3_ACCESSKEY/S3_SECRETKEY in ServiceConfiguration
 | 
				
			||||||
 | 
					- Added S3_ENDPOINT field with automatic protocol selection based on S3_USESSL
 | 
				
			||||||
 | 
					- Added S3_USESSL boolean field for SSL/TLS configuration support
 | 
				
			||||||
 | 
					- Updated ServiceManager to use new credential names in container setup and logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-08-15 - 1.17.3 - fix(serviceconfig)
 | 
				
			||||||
 | 
					Update service configuration to include dynamic MongoDB connection string and add local permissions settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-08-14 - 1.17.0 - feat(services)
 | 
					## 2025-08-14 - 1.17.0 - feat(services)
 | 
				
			||||||
Add comprehensive development services management for MongoDB and MinIO containers
 | 
					Add comprehensive development services management for MongoDB and MinIO containers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								package.json
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "@git.zone/cli",
 | 
					  "name": "@git.zone/cli",
 | 
				
			||||||
  "private": false,
 | 
					  "private": false,
 | 
				
			||||||
  "version": "1.17.0",
 | 
					  "version": "1.19.2",
 | 
				
			||||||
  "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.",
 | 
					  "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",
 | 
				
			||||||
@@ -57,45 +57,45 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "homepage": "https://gitlab.com/gitzone/private/gitzone#readme",
 | 
					  "homepage": "https://gitlab.com/gitzone/private/gitzone#readme",
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@git.zone/tsbuild": "^2.3.2",
 | 
					    "@git.zone/tsbuild": "^2.7.1",
 | 
				
			||||||
    "@git.zone/tsrun": "^1.3.3",
 | 
					    "@git.zone/tsrun": "^1.6.2",
 | 
				
			||||||
    "@git.zone/tstest": "^1.0.96",
 | 
					    "@git.zone/tstest": "^2.7.0",
 | 
				
			||||||
    "@push.rocks/smartdelay": "^3.0.5",
 | 
					    "@push.rocks/smartdelay": "^3.0.5",
 | 
				
			||||||
    "@push.rocks/smartfile": "^11.2.0",
 | 
					    "@push.rocks/smartfile": "^11.2.7",
 | 
				
			||||||
    "@push.rocks/smartinteract": "^2.0.16",
 | 
					    "@push.rocks/smartinteract": "^2.0.16",
 | 
				
			||||||
    "@push.rocks/smartnetwork": "^4.1.2",
 | 
					    "@push.rocks/smartnetwork": "^4.4.0",
 | 
				
			||||||
    "@push.rocks/smartshell": "^3.2.3",
 | 
					    "@push.rocks/smartshell": "^3.3.0",
 | 
				
			||||||
    "@types/node": "^22.15.18"
 | 
					    "@types/node": "^22.15.18"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@git.zone/tsdoc": "^1.5.0",
 | 
					    "@git.zone/tsdoc": "^1.6.1",
 | 
				
			||||||
    "@git.zone/tspublish": "^1.9.1",
 | 
					    "@git.zone/tspublish": "^1.10.3",
 | 
				
			||||||
    "@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.2.2",
 | 
					    "@push.rocks/lik": "^6.2.2",
 | 
				
			||||||
    "@push.rocks/npmextra": "^5.1.2",
 | 
					    "@push.rocks/npmextra": "^5.3.3",
 | 
				
			||||||
    "@push.rocks/projectinfo": "^5.0.2",
 | 
					    "@push.rocks/projectinfo": "^5.0.2",
 | 
				
			||||||
    "@push.rocks/smartchok": "^1.0.34",
 | 
					    "@push.rocks/smartchok": "^1.1.1",
 | 
				
			||||||
    "@push.rocks/smartcli": "^4.0.11",
 | 
					    "@push.rocks/smartcli": "^4.0.19",
 | 
				
			||||||
    "@push.rocks/smartdiff": "^1.0.3",
 | 
					    "@push.rocks/smartdiff": "^1.0.3",
 | 
				
			||||||
    "@push.rocks/smartgulp": "^3.0.4",
 | 
					    "@push.rocks/smartgulp": "^3.0.4",
 | 
				
			||||||
    "@push.rocks/smartjson": "^5.0.20",
 | 
					    "@push.rocks/smartjson": "^5.2.0",
 | 
				
			||||||
    "@push.rocks/smartlegal": "^1.0.27",
 | 
					    "@push.rocks/smartlegal": "^1.0.27",
 | 
				
			||||||
    "@push.rocks/smartlog": "^3.0.9",
 | 
					    "@push.rocks/smartlog": "^3.1.10",
 | 
				
			||||||
    "@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.6",
 | 
				
			||||||
    "@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": "^6.0.0",
 | 
				
			||||||
    "@push.rocks/smartpromise": "^4.2.3",
 | 
					    "@push.rocks/smartpromise": "^4.2.3",
 | 
				
			||||||
    "@push.rocks/smartscaf": "^4.0.16",
 | 
					    "@push.rocks/smartscaf": "^4.0.19",
 | 
				
			||||||
    "@push.rocks/smartstream": "^3.2.5",
 | 
					    "@push.rocks/smartstream": "^3.2.5",
 | 
				
			||||||
    "@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.5.3",
 | 
					    "prettier": "^3.6.2",
 | 
				
			||||||
    "through2": "^4.0.2"
 | 
					    "through2": "^4.0.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "files": [
 | 
					  "files": [
 | 
				
			||||||
@@ -114,7 +114,13 @@
 | 
				
			|||||||
    "last 1 chrome versions"
 | 
					    "last 1 chrome versions"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "pnpm": {
 | 
					  "pnpm": {
 | 
				
			||||||
    "overrides": {}
 | 
					    "overrides": {},
 | 
				
			||||||
 | 
					    "onlyBuiltDependencies": [
 | 
				
			||||||
 | 
					      "esbuild",
 | 
				
			||||||
 | 
					      "mongodb-memory-server",
 | 
				
			||||||
 | 
					      "puppeteer",
 | 
				
			||||||
 | 
					      "sharp"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
 | 
					  "packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8782
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8782
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								test
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								test
									
									
									
									
									
										Submodule
									
								
							 Submodule test added at 0b89443584
									
								
							@@ -3,6 +3,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
export const commitinfo = {
 | 
					export const commitinfo = {
 | 
				
			||||||
  name: '@git.zone/cli',
 | 
					  name: '@git.zone/cli',
 | 
				
			||||||
  version: '1.16.10',
 | 
					  version: '1.19.2',
 | 
				
			||||||
  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.'
 | 
					  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.'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,8 @@
 | 
				
			|||||||
import * as plugins from './mod.plugins.js';
 | 
					import * as plugins from './mod.plugins.js';
 | 
				
			||||||
import * as paths from '../paths.js';
 | 
					import * as paths from '../paths.js';
 | 
				
			||||||
import { logger } from '../gitzone.logging.js';
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					import * as helpers from './mod.helpers.js';
 | 
				
			||||||
 | 
					import * as ui from './mod.ui.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const run = async (argvArg: any) => {
 | 
					export const run = async (argvArg: any) => {
 | 
				
			||||||
  if (argvArg.format) {
 | 
					  if (argvArg.format) {
 | 
				
			||||||
@@ -10,22 +12,21 @@ export const run = async (argvArg: any) => {
 | 
				
			|||||||
    await formatMod.run();
 | 
					    await formatMod.run();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logger.log('info', `gathering facts...`);
 | 
					  ui.printHeader('🔍 Analyzing repository changes...');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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(
 | 
					  await aidoc.stop();
 | 
				
			||||||
    'info',
 | 
					
 | 
				
			||||||
    `---------
 | 
					  ui.printRecommendation({
 | 
				
			||||||
    Next recommended commit would be:
 | 
					    recommendedNextVersion: nextCommitObject.recommendedNextVersion,
 | 
				
			||||||
    ===========
 | 
					    recommendedNextVersionLevel: nextCommitObject.recommendedNextVersionLevel,
 | 
				
			||||||
    -> ${nextCommitObject.recommendedNextVersion}:
 | 
					    recommendedNextVersionScope: nextCommitObject.recommendedNextVersionScope,
 | 
				
			||||||
    -> ${nextCommitObject.recommendedNextVersionLevel}(${nextCommitObject.recommendedNextVersionScope}): ${nextCommitObject.recommendedNextVersionMessage}
 | 
					    recommendedNextVersionMessage: nextCommitObject.recommendedNextVersionMessage,
 | 
				
			||||||
    ===========
 | 
					  });
 | 
				
			||||||
  `,
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  const commitInteract = new plugins.smartinteract.SmartInteract();
 | 
					  const commitInteract = new plugins.smartinteract.SmartInteract();
 | 
				
			||||||
  commitInteract.addQuestions([
 | 
					  commitInteract.addQuestions([
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -67,20 +68,30 @@ export const run = async (argvArg: any) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  })();
 | 
					  })();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logger.log('info', `OK! Creating commit with message '${commitString}'`);
 | 
					  ui.printHeader('✨ Creating Semantic Commit');
 | 
				
			||||||
 | 
					  ui.printCommitMessage(commitString);
 | 
				
			||||||
  const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
					  const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
				
			||||||
    executor: 'bash',
 | 
					    executor: 'bash',
 | 
				
			||||||
    sourceFilePaths: [],
 | 
					    sourceFilePaths: [],
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logger.log('info', `Baking commitinfo into code ...`);
 | 
					  // Determine total steps (6 if pushing, 5 if not)
 | 
				
			||||||
 | 
					  const totalSteps = answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true') ? 6 : 5;
 | 
				
			||||||
 | 
					  let currentStep = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Step 1: Baking commitinfo
 | 
				
			||||||
 | 
					  currentStep++;
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '🔧 Baking commit info into code', 'in-progress');
 | 
				
			||||||
  const commitInfo = new plugins.commitinfo.CommitInfo(
 | 
					  const commitInfo = new plugins.commitinfo.CommitInfo(
 | 
				
			||||||
    paths.cwd,
 | 
					    paths.cwd,
 | 
				
			||||||
    commitVersionType,
 | 
					    commitVersionType,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  await commitInfo.writeIntoPotentialDirs();
 | 
					  await commitInfo.writeIntoPotentialDirs();
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '🔧 Baking commit info into code', 'done');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logger.log('info', `Writing changelog.md ...`);
 | 
					  // Step 2: Writing changelog
 | 
				
			||||||
 | 
					  currentStep++;
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '📄 Generating changelog.md', 'in-progress');
 | 
				
			||||||
  let changelog = nextCommitObject.changelog;
 | 
					  let changelog = nextCommitObject.changelog;
 | 
				
			||||||
  changelog = changelog.replaceAll(
 | 
					  changelog = changelog.replaceAll(
 | 
				
			||||||
    '{{nextVersion}}',
 | 
					    '{{nextVersion}}',
 | 
				
			||||||
@@ -107,17 +118,54 @@ export const run = async (argvArg: any) => {
 | 
				
			|||||||
    changelog,
 | 
					    changelog,
 | 
				
			||||||
    plugins.path.join(paths.cwd, `changelog.md`),
 | 
					    plugins.path.join(paths.cwd, `changelog.md`),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '📄 Generating changelog.md', 'done');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  logger.log('info', `Staging files for commit:`);
 | 
					  // Step 3: Staging files
 | 
				
			||||||
 | 
					  currentStep++;
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '📦 Staging files', 'in-progress');
 | 
				
			||||||
  await smartshellInstance.exec(`git add -A`);
 | 
					  await smartshellInstance.exec(`git add -A`);
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '📦 Staging files', 'done');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Step 4: Creating commit
 | 
				
			||||||
 | 
					  currentStep++;
 | 
				
			||||||
 | 
					  ui.printStep(currentStep, totalSteps, '💾 Creating git commit', 'in-progress');
 | 
				
			||||||
  await smartshellInstance.exec(`git commit -m "${commitString}"`);
 | 
					  await smartshellInstance.exec(`git commit -m "${commitString}"`);
 | 
				
			||||||
  await smartshellInstance.exec(`npm version ${commitVersionType}`);
 | 
					  ui.printStep(currentStep, totalSteps, '💾 Creating git commit', 'done');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Step 5: Bumping version
 | 
				
			||||||
 | 
					  currentStep++;
 | 
				
			||||||
 | 
					  const projectType = await helpers.detectProjectType();
 | 
				
			||||||
 | 
					  const newVersion = await helpers.bumpProjectVersion(projectType, commitVersionType, currentStep, totalSteps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Step 6: Push to remote (optional)
 | 
				
			||||||
 | 
					  const currentBranch = await helpers.detectCurrentBranch();
 | 
				
			||||||
  if (
 | 
					  if (
 | 
				
			||||||
    answerBucket.getAnswerFor('pushToOrigin') &&
 | 
					    answerBucket.getAnswerFor('pushToOrigin') &&
 | 
				
			||||||
    !(process.env.CI === 'true')
 | 
					    !(process.env.CI === 'true')
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    await smartshellInstance.exec(`git push origin master --follow-tags`);
 | 
					    currentStep++;
 | 
				
			||||||
 | 
					    ui.printStep(currentStep, totalSteps, `🚀 Pushing to origin/${currentBranch}`, 'in-progress');
 | 
				
			||||||
 | 
					    await smartshellInstance.exec(`git push origin ${currentBranch} --follow-tags`);
 | 
				
			||||||
 | 
					    ui.printStep(currentStep, totalSteps, `🚀 Pushing to origin/${currentBranch}`, 'done');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log(''); // Add spacing before summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Get commit SHA for summary
 | 
				
			||||||
 | 
					  const commitShaResult = await smartshellInstance.exec('git rev-parse --short HEAD');
 | 
				
			||||||
 | 
					  const commitSha = commitShaResult.stdout.trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Print final summary
 | 
				
			||||||
 | 
					  ui.printSummary({
 | 
				
			||||||
 | 
					    projectType,
 | 
				
			||||||
 | 
					    branch: currentBranch,
 | 
				
			||||||
 | 
					    commitType: answerBucket.getAnswerFor('commitType'),
 | 
				
			||||||
 | 
					    commitScope: answerBucket.getAnswerFor('commitScope'),
 | 
				
			||||||
 | 
					    commitMessage: answerBucket.getAnswerFor('commitDescription'),
 | 
				
			||||||
 | 
					    newVersion: newVersion,
 | 
				
			||||||
 | 
					    commitSha: commitSha,
 | 
				
			||||||
 | 
					    pushed: answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true'),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createCommitStringFromAnswerBucket = (
 | 
					const createCommitStringFromAnswerBucket = (
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										256
									
								
								ts/mod_commit/mod.helpers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								ts/mod_commit/mod.helpers.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,256 @@
 | 
				
			|||||||
 | 
					import * as plugins from './mod.plugins.js';
 | 
				
			||||||
 | 
					import * as paths from '../paths.js';
 | 
				
			||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					import * as ui from './mod.ui.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ProjectType = 'npm' | 'deno' | 'both' | 'none';
 | 
				
			||||||
 | 
					export type VersionType = 'patch' | 'minor' | 'major';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Detects the current git branch
 | 
				
			||||||
 | 
					 * @returns The current branch name, defaults to 'master' if detection fails
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function detectCurrentBranch(): Promise<string> {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
				
			||||||
 | 
					      executor: 'bash',
 | 
				
			||||||
 | 
					      sourceFilePaths: [],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    const result = await smartshellInstance.exec('git branch --show-current');
 | 
				
			||||||
 | 
					    const branchName = result.stdout.trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!branchName) {
 | 
				
			||||||
 | 
					      logger.log('warn', 'Could not detect current branch, falling back to "master"');
 | 
				
			||||||
 | 
					      return 'master';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger.log('info', `Detected current branch: ${branchName}`);
 | 
				
			||||||
 | 
					    return branchName;
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    logger.log('warn', `Failed to detect branch: ${error.message}, falling back to "master"`);
 | 
				
			||||||
 | 
					    return 'master';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Detects the project type based on presence of package.json and/or deno.json
 | 
				
			||||||
 | 
					 * @returns The project type
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function detectProjectType(): Promise<ProjectType> {
 | 
				
			||||||
 | 
					  const packageJsonPath = plugins.path.join(paths.cwd, 'package.json');
 | 
				
			||||||
 | 
					  const denoJsonPath = plugins.path.join(paths.cwd, 'deno.json');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const hasPackageJson = await plugins.smartfile.fs.fileExists(packageJsonPath);
 | 
				
			||||||
 | 
					  const hasDenoJson = await plugins.smartfile.fs.fileExists(denoJsonPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (hasPackageJson && hasDenoJson) {
 | 
				
			||||||
 | 
					    logger.log('info', 'Detected dual project (npm + deno)');
 | 
				
			||||||
 | 
					    return 'both';
 | 
				
			||||||
 | 
					  } else if (hasPackageJson) {
 | 
				
			||||||
 | 
					    logger.log('info', 'Detected npm project');
 | 
				
			||||||
 | 
					    return 'npm';
 | 
				
			||||||
 | 
					  } else if (hasDenoJson) {
 | 
				
			||||||
 | 
					    logger.log('info', 'Detected deno project');
 | 
				
			||||||
 | 
					    return 'deno';
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    throw new Error('No package.json or deno.json found in current directory');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Parses a semantic version string and bumps it according to the version type
 | 
				
			||||||
 | 
					 * @param currentVersion Current version string (e.g., "1.2.3")
 | 
				
			||||||
 | 
					 * @param versionType Type of version bump
 | 
				
			||||||
 | 
					 * @returns New version string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function calculateNewVersion(currentVersion: string, versionType: VersionType): string {
 | 
				
			||||||
 | 
					  const versionMatch = currentVersion.match(/^(\d+)\.(\d+)\.(\d+)/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!versionMatch) {
 | 
				
			||||||
 | 
					    throw new Error(`Invalid version format: ${currentVersion}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let [, major, minor, patch] = versionMatch.map(Number);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (versionType) {
 | 
				
			||||||
 | 
					    case 'major':
 | 
				
			||||||
 | 
					      major += 1;
 | 
				
			||||||
 | 
					      minor = 0;
 | 
				
			||||||
 | 
					      patch = 0;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'minor':
 | 
				
			||||||
 | 
					      minor += 1;
 | 
				
			||||||
 | 
					      patch = 0;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'patch':
 | 
				
			||||||
 | 
					      patch += 1;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return `${major}.${minor}.${patch}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Bumps the version in deno.json, commits the change, and creates a tag
 | 
				
			||||||
 | 
					 * @param versionType Type of version bump
 | 
				
			||||||
 | 
					 * @returns The new version string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function bumpDenoVersion(versionType: VersionType): Promise<string> {
 | 
				
			||||||
 | 
					  const denoJsonPath = plugins.path.join(paths.cwd, 'deno.json');
 | 
				
			||||||
 | 
					  const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
				
			||||||
 | 
					    executor: 'bash',
 | 
				
			||||||
 | 
					    sourceFilePaths: [],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    // Read deno.json
 | 
				
			||||||
 | 
					    const denoConfig = plugins.smartfile.fs.toObjectSync(
 | 
				
			||||||
 | 
					      denoJsonPath
 | 
				
			||||||
 | 
					    ) as { version?: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!denoConfig.version) {
 | 
				
			||||||
 | 
					      throw new Error('deno.json does not contain a version field');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const currentVersion = denoConfig.version;
 | 
				
			||||||
 | 
					    const newVersion = calculateNewVersion(currentVersion, versionType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger.log('info', `Bumping deno.json version: ${currentVersion} → ${newVersion}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update version
 | 
				
			||||||
 | 
					    denoConfig.version = newVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Write back to disk
 | 
				
			||||||
 | 
					    await plugins.smartfile.memory.toFs(
 | 
				
			||||||
 | 
					      JSON.stringify(denoConfig, null, 2) + '\n',
 | 
				
			||||||
 | 
					      denoJsonPath
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Stage the deno.json file
 | 
				
			||||||
 | 
					    await smartshellInstance.exec('git add deno.json');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Commit the version bump
 | 
				
			||||||
 | 
					    await smartshellInstance.exec(`git commit -m "v${newVersion}"`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create the version tag
 | 
				
			||||||
 | 
					    await smartshellInstance.exec(`git tag v${newVersion} -m "v${newVersion}"`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger.log('info', `Created commit and tag v${newVersion}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return newVersion;
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    throw new Error(`Failed to bump deno.json version: ${error.message}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Bumps the version in package.json using npm version command
 | 
				
			||||||
 | 
					 * @param versionType Type of version bump
 | 
				
			||||||
 | 
					 * @returns The new version string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function bumpNpmVersion(versionType: VersionType): Promise<string> {
 | 
				
			||||||
 | 
					  const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
				
			||||||
 | 
					    executor: 'bash',
 | 
				
			||||||
 | 
					    sourceFilePaths: [],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  logger.log('info', `Bumping package.json version using npm version ${versionType}`);
 | 
				
			||||||
 | 
					  const result = await smartshellInstance.exec(`npm version ${versionType}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // npm version returns the new version with a 'v' prefix, e.g., "v1.2.3"
 | 
				
			||||||
 | 
					  const newVersion = result.stdout.trim().replace(/^v/, '');
 | 
				
			||||||
 | 
					  return newVersion;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Syncs the version from package.json to deno.json and amends the npm commit
 | 
				
			||||||
 | 
					 * @param version The version to sync
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function syncVersionToDenoJson(version: string): Promise<void> {
 | 
				
			||||||
 | 
					  const denoJsonPath = plugins.path.join(paths.cwd, 'deno.json');
 | 
				
			||||||
 | 
					  const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
				
			||||||
 | 
					    executor: 'bash',
 | 
				
			||||||
 | 
					    sourceFilePaths: [],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    const denoConfig = plugins.smartfile.fs.toObjectSync(
 | 
				
			||||||
 | 
					      denoJsonPath
 | 
				
			||||||
 | 
					    ) as { version?: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger.log('info', `Syncing version to deno.json: ${version}`);
 | 
				
			||||||
 | 
					    denoConfig.version = version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await plugins.smartfile.memory.toFs(
 | 
				
			||||||
 | 
					      JSON.stringify(denoConfig, null, 2) + '\n',
 | 
				
			||||||
 | 
					      denoJsonPath
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Stage the deno.json file
 | 
				
			||||||
 | 
					    await smartshellInstance.exec('git add deno.json');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Amend the npm version commit to include deno.json
 | 
				
			||||||
 | 
					    await smartshellInstance.exec('git commit --amend --no-edit');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Re-create the tag with force to update it
 | 
				
			||||||
 | 
					    await smartshellInstance.exec(`git tag -fa v${version} -m "v${version}"`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logger.log('info', `Amended commit to include deno.json and updated tag v${version}`);
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    throw new Error(`Failed to sync version to deno.json: ${error.message}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Bumps the project version based on project type
 | 
				
			||||||
 | 
					 * @param projectType The detected project type
 | 
				
			||||||
 | 
					 * @param versionType The type of version bump
 | 
				
			||||||
 | 
					 * @param currentStep The current step number for progress display
 | 
				
			||||||
 | 
					 * @param totalSteps The total number of steps for progress display
 | 
				
			||||||
 | 
					 * @returns The new version string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export async function bumpProjectVersion(
 | 
				
			||||||
 | 
					  projectType: ProjectType,
 | 
				
			||||||
 | 
					  versionType: VersionType,
 | 
				
			||||||
 | 
					  currentStep?: number,
 | 
				
			||||||
 | 
					  totalSteps?: number
 | 
				
			||||||
 | 
					): Promise<string> {
 | 
				
			||||||
 | 
					  const projectEmoji = projectType === 'npm' ? '📦' : projectType === 'deno' ? '🦕' : '🔀';
 | 
				
			||||||
 | 
					  const description = `🏷️  Bumping version (${projectEmoji} ${projectType})`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currentStep && totalSteps) {
 | 
				
			||||||
 | 
					    ui.printStep(currentStep, totalSteps, description, 'in-progress');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let newVersion: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (projectType) {
 | 
				
			||||||
 | 
					    case 'npm':
 | 
				
			||||||
 | 
					      newVersion = await bumpNpmVersion(versionType);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 'deno':
 | 
				
			||||||
 | 
					      newVersion = await bumpDenoVersion(versionType);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 'both': {
 | 
				
			||||||
 | 
					      // Bump npm version first (it handles git tags)
 | 
				
			||||||
 | 
					      newVersion = await bumpNpmVersion(versionType);
 | 
				
			||||||
 | 
					      // Then sync to deno.json
 | 
				
			||||||
 | 
					      await syncVersionToDenoJson(newVersion);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 'none':
 | 
				
			||||||
 | 
					      throw new Error('Cannot bump version: no package.json or deno.json found');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      throw new Error(`Unknown project type: ${projectType}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currentStep && totalSteps) {
 | 
				
			||||||
 | 
					    ui.printStep(currentStep, totalSteps, description, 'done');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return newVersion;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										196
									
								
								ts/mod_commit/mod.ui.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								ts/mod_commit/mod.ui.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
				
			|||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * UI helper module for beautiful CLI output
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ICommitSummary {
 | 
				
			||||||
 | 
					  projectType: string;
 | 
				
			||||||
 | 
					  branch: string;
 | 
				
			||||||
 | 
					  commitType: string;
 | 
				
			||||||
 | 
					  commitScope: string;
 | 
				
			||||||
 | 
					  commitMessage: string;
 | 
				
			||||||
 | 
					  newVersion: string;
 | 
				
			||||||
 | 
					  commitSha?: string;
 | 
				
			||||||
 | 
					  pushed: boolean;
 | 
				
			||||||
 | 
					  repoUrl?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IRecommendation {
 | 
				
			||||||
 | 
					  recommendedNextVersion: string;
 | 
				
			||||||
 | 
					  recommendedNextVersionLevel: string;
 | 
				
			||||||
 | 
					  recommendedNextVersionScope: string;
 | 
				
			||||||
 | 
					  recommendedNextVersionMessage: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print a header with a box around it
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printHeader(title: string): void {
 | 
				
			||||||
 | 
					  const width = 57;
 | 
				
			||||||
 | 
					  const padding = Math.max(0, width - title.length - 2);
 | 
				
			||||||
 | 
					  const leftPad = Math.floor(padding / 2);
 | 
				
			||||||
 | 
					  const rightPad = padding - leftPad;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log('');
 | 
				
			||||||
 | 
					  console.log('╭─' + '─'.repeat(width) + '─╮');
 | 
				
			||||||
 | 
					  console.log('│  ' + title + ' '.repeat(rightPad + leftPad) + '  │');
 | 
				
			||||||
 | 
					  console.log('╰─' + '─'.repeat(width) + '─╯');
 | 
				
			||||||
 | 
					  console.log('');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print a section with a border
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printSection(title: string, lines: string[]): void {
 | 
				
			||||||
 | 
					  const width = 59;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log('┌─ ' + title + ' ' + '─'.repeat(Math.max(0, width - title.length - 3)) + '┐');
 | 
				
			||||||
 | 
					  console.log('│' + ' '.repeat(width) + '│');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const line of lines) {
 | 
				
			||||||
 | 
					    const padding = width - line.length;
 | 
				
			||||||
 | 
					    console.log('│  ' + line + ' '.repeat(Math.max(0, padding - 2)) + '│');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log('│' + ' '.repeat(width) + '│');
 | 
				
			||||||
 | 
					  console.log('└─' + '─'.repeat(width) + '─┘');
 | 
				
			||||||
 | 
					  console.log('');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print AI recommendations in a nice box
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printRecommendation(recommendation: IRecommendation): void {
 | 
				
			||||||
 | 
					  const lines = [
 | 
				
			||||||
 | 
					    `Suggested Version:  v${recommendation.recommendedNextVersion}`,
 | 
				
			||||||
 | 
					    `Suggested Type:     ${recommendation.recommendedNextVersionLevel}`,
 | 
				
			||||||
 | 
					    `Suggested Scope:    ${recommendation.recommendedNextVersionScope}`,
 | 
				
			||||||
 | 
					    `Suggested Message:  ${recommendation.recommendedNextVersionMessage}`,
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  printSection('📊 AI Recommendations', lines);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print a progress step
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printStep(
 | 
				
			||||||
 | 
					  current: number,
 | 
				
			||||||
 | 
					  total: number,
 | 
				
			||||||
 | 
					  description: string,
 | 
				
			||||||
 | 
					  status: 'in-progress' | 'done' | 'error'
 | 
				
			||||||
 | 
					): void {
 | 
				
			||||||
 | 
					  const statusIcon = status === 'done' ? '✓' : status === 'error' ? '✗' : '⏳';
 | 
				
			||||||
 | 
					  const dots = '.'.repeat(Math.max(0, 40 - description.length));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log(`  [${current}/${total}] ${description}${dots} ${statusIcon}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Clear the line on next update if in progress
 | 
				
			||||||
 | 
					  if (status === 'in-progress') {
 | 
				
			||||||
 | 
					    process.stdout.write('\x1b[1A'); // Move cursor up one line
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get emoji for project type
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getProjectTypeEmoji(projectType: string): string {
 | 
				
			||||||
 | 
					  switch (projectType) {
 | 
				
			||||||
 | 
					    case 'npm':
 | 
				
			||||||
 | 
					      return '📦 npm';
 | 
				
			||||||
 | 
					    case 'deno':
 | 
				
			||||||
 | 
					      return '🦕 Deno';
 | 
				
			||||||
 | 
					    case 'both':
 | 
				
			||||||
 | 
					      return '🔀 npm + Deno';
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return '❓ Unknown';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get emoji for commit type
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getCommitTypeEmoji(commitType: string): string {
 | 
				
			||||||
 | 
					  switch (commitType) {
 | 
				
			||||||
 | 
					    case 'fix':
 | 
				
			||||||
 | 
					      return '🔧 fix';
 | 
				
			||||||
 | 
					    case 'feat':
 | 
				
			||||||
 | 
					      return '✨ feat';
 | 
				
			||||||
 | 
					    case 'BREAKING CHANGE':
 | 
				
			||||||
 | 
					      return '💥 BREAKING CHANGE';
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return commitType;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print final commit summary
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printSummary(summary: ICommitSummary): void {
 | 
				
			||||||
 | 
					  const lines = [
 | 
				
			||||||
 | 
					    `Project Type:   ${getProjectTypeEmoji(summary.projectType)}`,
 | 
				
			||||||
 | 
					    `Branch:         🌿 ${summary.branch}`,
 | 
				
			||||||
 | 
					    `Commit Type:    ${getCommitTypeEmoji(summary.commitType)}`,
 | 
				
			||||||
 | 
					    `Scope:          📍 ${summary.commitScope}`,
 | 
				
			||||||
 | 
					    `New Version:    🏷️  v${summary.newVersion}`,
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (summary.commitSha) {
 | 
				
			||||||
 | 
					    lines.push(`Commit SHA:     📌 ${summary.commitSha}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (summary.pushed) {
 | 
				
			||||||
 | 
					    lines.push(`Remote:         ✓ Pushed successfully`);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    lines.push(`Remote:         ⊘ Not pushed (local only)`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (summary.repoUrl && summary.commitSha) {
 | 
				
			||||||
 | 
					    lines.push('');
 | 
				
			||||||
 | 
					    lines.push(`View at: ${summary.repoUrl}/commit/${summary.commitSha}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  printSection('✅ Commit Summary', lines);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (summary.pushed) {
 | 
				
			||||||
 | 
					    console.log('🎉 All done! Your changes are committed and pushed.\n');
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    console.log('✓ Commit created successfully.\n');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print an info message with consistent formatting
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printInfo(message: string): void {
 | 
				
			||||||
 | 
					  console.log(`  ℹ️  ${message}`);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print a success message
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printSuccess(message: string): void {
 | 
				
			||||||
 | 
					  console.log(`  ✓ ${message}`);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print a warning message
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printWarning(message: string): void {
 | 
				
			||||||
 | 
					  logger.log('warn', `⚠️  ${message}`);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print an error message
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printError(message: string): void {
 | 
				
			||||||
 | 
					  logger.log('error', `✗ ${message}`);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Print commit message being created
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function printCommitMessage(commitString: string): void {
 | 
				
			||||||
 | 
					  console.log(`\n  📝 Commit: ${commitString}\n`);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import * as plugins from './mod.plugins.js';
 | 
					import * as plugins from './mod.plugins.js';
 | 
				
			||||||
import * as helpers from './helpers.js';
 | 
					import * as helpers from './helpers.js';
 | 
				
			||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ContainerStatus = 'running' | 'stopped' | 'not_exists';
 | 
					export type ContainerStatus = 'running' | 'stopped' | 'not_exists';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -147,7 +148,7 @@ export class DockerContainer {
 | 
				
			|||||||
      const result = await this.smartshell.exec(command);
 | 
					      const result = await this.smartshell.exec(command);
 | 
				
			||||||
      return result.exitCode === 0;
 | 
					      return result.exitCode === 0;
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      helpers.printMessage(`Failed to run container: ${error.message}`, 'red');
 | 
					      logger.log('error', `Failed to run container: ${error.message}`);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -214,7 +215,7 @@ export class DockerContainer {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  public async inspect(containerName: string): Promise<any> {
 | 
					  public async inspect(containerName: string): Promise<any> {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const result = await this.smartshell.exec(`docker inspect ${containerName}`);
 | 
					      const result = await this.smartshell.execSilent(`docker inspect ${containerName}`);
 | 
				
			||||||
      if (result.exitCode === 0) {
 | 
					      if (result.exitCode === 0) {
 | 
				
			||||||
        return JSON.parse(result.stdout);
 | 
					        return JSON.parse(result.stdout);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -223,4 +224,38 @@ export class DockerContainer {
 | 
				
			|||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get port mappings for a container
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async getPortMappings(containerName: string): Promise<{ [key: string]: string } | null> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      // Use docker inspect without format to get full JSON, then extract PortBindings
 | 
				
			||||||
 | 
					      const result = await this.smartshell.execSilent(`docker inspect ${containerName}`);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if (result.exitCode === 0 && result.stdout) {
 | 
				
			||||||
 | 
					        const inspectData = JSON.parse(result.stdout);
 | 
				
			||||||
 | 
					        if (inspectData && inspectData[0] && inspectData[0].HostConfig && inspectData[0].HostConfig.PortBindings) {
 | 
				
			||||||
 | 
					          const portBindings = inspectData[0].HostConfig.PortBindings;
 | 
				
			||||||
 | 
					          const mappings: { [key: string]: string } = {};
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          // Convert Docker's port binding format to simple host:container mapping
 | 
				
			||||||
 | 
					          for (const [containerPort, hostBindings] of Object.entries(portBindings)) {
 | 
				
			||||||
 | 
					            if (Array.isArray(hostBindings) && hostBindings.length > 0) {
 | 
				
			||||||
 | 
					              const hostPort = (hostBindings[0] as any).HostPort;
 | 
				
			||||||
 | 
					              if (hostPort) {
 | 
				
			||||||
 | 
					                mappings[containerPort.replace('/tcp', '').replace('/udp', '')] = hostPort;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          return mappings;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      // Silently fail - container might not exist
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
import * as plugins from './mod.plugins.js';
 | 
					import * as plugins from './mod.plugins.js';
 | 
				
			||||||
import * as helpers from './helpers.js';
 | 
					import * as helpers from './helpers.js';
 | 
				
			||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					import { DockerContainer } from './classes.dockercontainer.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IServiceConfig {
 | 
					export interface IServiceConfig {
 | 
				
			||||||
  PROJECT_NAME: string;
 | 
					  PROJECT_NAME: string;
 | 
				
			||||||
@@ -8,20 +10,25 @@ export interface IServiceConfig {
 | 
				
			|||||||
  MONGODB_PORT: string;
 | 
					  MONGODB_PORT: string;
 | 
				
			||||||
  MONGODB_USER: string;
 | 
					  MONGODB_USER: string;
 | 
				
			||||||
  MONGODB_PASS: string;
 | 
					  MONGODB_PASS: string;
 | 
				
			||||||
 | 
					  MONGODB_URL: string;
 | 
				
			||||||
  S3_HOST: string;
 | 
					  S3_HOST: string;
 | 
				
			||||||
  S3_PORT: string;
 | 
					  S3_PORT: string;
 | 
				
			||||||
  S3_CONSOLE_PORT: string;
 | 
					  S3_CONSOLE_PORT: string;
 | 
				
			||||||
  S3_USER: string;
 | 
					  S3_ACCESSKEY: string;
 | 
				
			||||||
  S3_PASS: string;
 | 
					  S3_SECRETKEY: string;
 | 
				
			||||||
  S3_BUCKET: string;
 | 
					  S3_BUCKET: string;
 | 
				
			||||||
 | 
					  S3_ENDPOINT: string;
 | 
				
			||||||
 | 
					  S3_USESSL: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ServiceConfiguration {
 | 
					export class ServiceConfiguration {
 | 
				
			||||||
  private configPath: string;
 | 
					  private configPath: string;
 | 
				
			||||||
  private config: IServiceConfig;
 | 
					  private config: IServiceConfig;
 | 
				
			||||||
 | 
					  private docker: DockerContainer;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    this.configPath = plugins.path.join(process.cwd(), '.nogit', 'env.json');
 | 
					    this.configPath = plugins.path.join(process.cwd(), '.nogit', 'env.json');
 | 
				
			||||||
 | 
					    this.docker = new DockerContainer();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -37,6 +44,9 @@ export class ServiceConfiguration {
 | 
				
			|||||||
      await this.createDefaultConfig();
 | 
					      await this.createDefaultConfig();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // Sync ports from existing Docker containers if they exist
 | 
				
			||||||
 | 
					    await this.syncPortsFromDocker();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    return this.config;
 | 
					    return this.config;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -76,7 +86,7 @@ export class ServiceConfiguration {
 | 
				
			|||||||
   * Load configuration from file
 | 
					   * Load configuration from file
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  private async loadConfig(): Promise<void> {
 | 
					  private async loadConfig(): Promise<void> {
 | 
				
			||||||
    const configContent = await plugins.smartfile.fs.toStringSync(this.configPath);
 | 
					    const configContent = plugins.smartfile.fs.toStringSync(this.configPath);
 | 
				
			||||||
    this.config = JSON.parse(configContent);
 | 
					    this.config = JSON.parse(configContent);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -94,27 +104,38 @@ export class ServiceConfiguration {
 | 
				
			|||||||
      s3ConsolePort++;
 | 
					      s3ConsolePort++;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    const mongoUser = 'defaultadmin';
 | 
				
			||||||
 | 
					    const mongoPass = 'defaultpass';
 | 
				
			||||||
 | 
					    const mongoHost = 'localhost';
 | 
				
			||||||
 | 
					    const mongoName = projectName;
 | 
				
			||||||
 | 
					    const mongoPortStr = mongoPort.toString();
 | 
				
			||||||
 | 
					    const s3Host = 'localhost';
 | 
				
			||||||
 | 
					    const s3PortStr = s3Port.toString();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    this.config = {
 | 
					    this.config = {
 | 
				
			||||||
      PROJECT_NAME: projectName,
 | 
					      PROJECT_NAME: projectName,
 | 
				
			||||||
      MONGODB_HOST: 'localhost',
 | 
					      MONGODB_HOST: mongoHost,
 | 
				
			||||||
      MONGODB_NAME: projectName,
 | 
					      MONGODB_NAME: mongoName,
 | 
				
			||||||
      MONGODB_PORT: mongoPort.toString(),
 | 
					      MONGODB_PORT: mongoPortStr,
 | 
				
			||||||
      MONGODB_USER: 'defaultadmin',
 | 
					      MONGODB_USER: mongoUser,
 | 
				
			||||||
      MONGODB_PASS: 'defaultpass',
 | 
					      MONGODB_PASS: mongoPass,
 | 
				
			||||||
      S3_HOST: 'localhost',
 | 
					      MONGODB_URL: `mongodb://${mongoUser}:${mongoPass}@${mongoHost}:${mongoPortStr}/${mongoName}?authSource=admin`,
 | 
				
			||||||
      S3_PORT: s3Port.toString(),
 | 
					      S3_HOST: s3Host,
 | 
				
			||||||
 | 
					      S3_PORT: s3PortStr,
 | 
				
			||||||
      S3_CONSOLE_PORT: s3ConsolePort.toString(),
 | 
					      S3_CONSOLE_PORT: s3ConsolePort.toString(),
 | 
				
			||||||
      S3_USER: 'defaultadmin',
 | 
					      S3_ACCESSKEY: 'defaultadmin',
 | 
				
			||||||
      S3_PASS: 'defaultpass',
 | 
					      S3_SECRETKEY: 'defaultpass',
 | 
				
			||||||
      S3_BUCKET: `${projectName}-documents`
 | 
					      S3_BUCKET: `${projectName}-documents`,
 | 
				
			||||||
 | 
					      S3_ENDPOINT: s3Host,
 | 
				
			||||||
 | 
					      S3_USESSL: false
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    await this.saveConfig();
 | 
					    await this.saveConfig();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage('✅ Created .nogit/env.json with project defaults', 'green');
 | 
					    logger.log('ok', '✅ Created .nogit/env.json with project defaults');
 | 
				
			||||||
    helpers.printMessage(`📍 MongoDB port: ${mongoPort}`, 'blue');
 | 
					    logger.log('info', `📍 MongoDB port: ${mongoPort}`);
 | 
				
			||||||
    helpers.printMessage(`📍 S3 API port: ${s3Port}`, 'blue');
 | 
					    logger.log('info', `📍 S3 API port: ${s3Port}`);
 | 
				
			||||||
    helpers.printMessage(`📍 S3 Console port: ${s3ConsolePort}`, 'blue');
 | 
					    logger.log('info', `📍 S3 Console port: ${s3ConsolePort}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -163,6 +184,14 @@ export class ServiceConfiguration {
 | 
				
			|||||||
      updated = true;
 | 
					      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) {
 | 
					    if (!this.config.S3_HOST) {
 | 
				
			||||||
      this.config.S3_HOST = 'localhost';
 | 
					      this.config.S3_HOST = 'localhost';
 | 
				
			||||||
      fieldsAdded.push('S3_HOST');
 | 
					      fieldsAdded.push('S3_HOST');
 | 
				
			||||||
@@ -189,15 +218,15 @@ export class ServiceConfiguration {
 | 
				
			|||||||
      updated = true;
 | 
					      updated = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (!this.config.S3_USER) {
 | 
					    if (!this.config.S3_ACCESSKEY) {
 | 
				
			||||||
      this.config.S3_USER = 'defaultadmin';
 | 
					      this.config.S3_ACCESSKEY = 'defaultadmin';
 | 
				
			||||||
      fieldsAdded.push('S3_USER');
 | 
					      fieldsAdded.push('S3_ACCESSKEY');
 | 
				
			||||||
      updated = true;
 | 
					      updated = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (!this.config.S3_PASS) {
 | 
					    if (!this.config.S3_SECRETKEY) {
 | 
				
			||||||
      this.config.S3_PASS = 'defaultpass';
 | 
					      this.config.S3_SECRETKEY = 'defaultpass';
 | 
				
			||||||
      fieldsAdded.push('S3_PASS');
 | 
					      fieldsAdded.push('S3_SECRETKEY');
 | 
				
			||||||
      updated = true;
 | 
					      updated = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -207,11 +236,25 @@ export class ServiceConfiguration {
 | 
				
			|||||||
      updated = true;
 | 
					      updated = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    if (!this.config.S3_USESSL) {
 | 
				
			||||||
 | 
					      this.config.S3_USESSL = false;
 | 
				
			||||||
 | 
					      fieldsAdded.push('S3_USESSL');
 | 
				
			||||||
 | 
					      updated = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Always update S3_ENDPOINT based on current settings
 | 
				
			||||||
 | 
					    const oldEndpoint = this.config.S3_ENDPOINT;
 | 
				
			||||||
 | 
					    this.config.S3_ENDPOINT = this.config.S3_HOST;
 | 
				
			||||||
 | 
					    if (oldEndpoint !== this.config.S3_ENDPOINT) {
 | 
				
			||||||
 | 
					      fieldsAdded.push('S3_ENDPOINT');
 | 
				
			||||||
 | 
					      updated = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    if (updated) {
 | 
					    if (updated) {
 | 
				
			||||||
      await this.saveConfig();
 | 
					      await this.saveConfig();
 | 
				
			||||||
      helpers.printMessage(`✅ Added missing fields: ${fieldsAdded.join(', ')}`, 'green');
 | 
					      logger.log('ok', `✅ Added missing fields: ${fieldsAdded.join(', ')}`);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      helpers.printMessage('✅ Configuration complete', 'green');
 | 
					      logger.log('ok', '✅ Configuration complete');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -242,4 +285,148 @@ export class ServiceConfiguration {
 | 
				
			|||||||
      minio: plugins.path.join(process.cwd(), '.nogit', 'miniodata')
 | 
					      minio: plugins.path.join(process.cwd(), '.nogit', 'miniodata')
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Sync port configuration from existing Docker containers
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private async syncPortsFromDocker(): Promise<void> {
 | 
				
			||||||
 | 
					    const containers = this.getContainerNames();
 | 
				
			||||||
 | 
					    let updated = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Check MongoDB container
 | 
				
			||||||
 | 
					    const mongoStatus = await this.docker.getStatus(containers.mongo);
 | 
				
			||||||
 | 
					    if (mongoStatus !== 'not_exists') {
 | 
				
			||||||
 | 
					      const portMappings = await this.docker.getPortMappings(containers.mongo);
 | 
				
			||||||
 | 
					      if (portMappings && portMappings['27017']) {
 | 
				
			||||||
 | 
					        const dockerPort = portMappings['27017'];
 | 
				
			||||||
 | 
					        if (this.config.MONGODB_PORT !== dockerPort) {
 | 
				
			||||||
 | 
					          logger.log('note', `📍 Syncing MongoDB port from Docker: ${dockerPort}`);
 | 
				
			||||||
 | 
					          this.config.MONGODB_PORT = dockerPort;
 | 
				
			||||||
 | 
					          updated = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Check MinIO container
 | 
				
			||||||
 | 
					    const minioStatus = await this.docker.getStatus(containers.minio);
 | 
				
			||||||
 | 
					    if (minioStatus !== 'not_exists') {
 | 
				
			||||||
 | 
					      const portMappings = await this.docker.getPortMappings(containers.minio);
 | 
				
			||||||
 | 
					      if (portMappings) {
 | 
				
			||||||
 | 
					        if (portMappings['9000']) {
 | 
				
			||||||
 | 
					          const dockerPort = portMappings['9000'];
 | 
				
			||||||
 | 
					          if (this.config.S3_PORT !== dockerPort) {
 | 
				
			||||||
 | 
					            logger.log('note', `📍 Syncing S3 API port from Docker: ${dockerPort}`);
 | 
				
			||||||
 | 
					            this.config.S3_PORT = dockerPort;
 | 
				
			||||||
 | 
					            updated = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (portMappings['9001']) {
 | 
				
			||||||
 | 
					          const dockerPort = portMappings['9001'];
 | 
				
			||||||
 | 
					          if (this.config.S3_CONSOLE_PORT !== dockerPort) {
 | 
				
			||||||
 | 
					            logger.log('note', `📍 Syncing S3 Console port from Docker: ${dockerPort}`);
 | 
				
			||||||
 | 
					            this.config.S3_CONSOLE_PORT = dockerPort;
 | 
				
			||||||
 | 
					            updated = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (updated) {
 | 
				
			||||||
 | 
					      // Update derived fields
 | 
				
			||||||
 | 
					      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`;
 | 
				
			||||||
 | 
					      this.config.S3_ENDPOINT = this.config.S3_HOST;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      await this.saveConfig();
 | 
				
			||||||
 | 
					      logger.log('ok', '✅ Configuration synced with Docker containers');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Validate and update ports if they're not available
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async validateAndUpdatePorts(): Promise<boolean> {
 | 
				
			||||||
 | 
					    let updated = false;
 | 
				
			||||||
 | 
					    const containers = this.getContainerNames();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Check if containers exist - if they do, ports are fine
 | 
				
			||||||
 | 
					    const mongoExists = await this.docker.exists(containers.mongo);
 | 
				
			||||||
 | 
					    const minioExists = await this.docker.exists(containers.minio);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Only check port availability if containers don't exist
 | 
				
			||||||
 | 
					    if (!mongoExists) {
 | 
				
			||||||
 | 
					      const mongoPort = parseInt(this.config.MONGODB_PORT);
 | 
				
			||||||
 | 
					      if (!(await helpers.isPortAvailable(mongoPort))) {
 | 
				
			||||||
 | 
					        logger.log('note', `⚠️  MongoDB port ${mongoPort} is in use, finding new port...`);
 | 
				
			||||||
 | 
					        const newPort = await helpers.getRandomAvailablePort();
 | 
				
			||||||
 | 
					        this.config.MONGODB_PORT = newPort.toString();
 | 
				
			||||||
 | 
					        logger.log('ok', `✅ New MongoDB port: ${newPort}`);
 | 
				
			||||||
 | 
					        updated = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (!minioExists) {
 | 
				
			||||||
 | 
					      const s3Port = parseInt(this.config.S3_PORT);
 | 
				
			||||||
 | 
					      const s3ConsolePort = parseInt(this.config.S3_CONSOLE_PORT);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if (!(await helpers.isPortAvailable(s3Port))) {
 | 
				
			||||||
 | 
					        logger.log('note', `⚠️  S3 API port ${s3Port} is in use, finding new port...`);
 | 
				
			||||||
 | 
					        const newPort = await helpers.getRandomAvailablePort();
 | 
				
			||||||
 | 
					        this.config.S3_PORT = newPort.toString();
 | 
				
			||||||
 | 
					        logger.log('ok', `✅ New S3 API port: ${newPort}`);
 | 
				
			||||||
 | 
					        updated = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if (!(await helpers.isPortAvailable(s3ConsolePort))) {
 | 
				
			||||||
 | 
					        logger.log('note', `⚠️  S3 Console port ${s3ConsolePort} is in use, finding new port...`);
 | 
				
			||||||
 | 
					        let newPort = parseInt(this.config.S3_PORT) + 1;
 | 
				
			||||||
 | 
					        while (!(await helpers.isPortAvailable(newPort))) {
 | 
				
			||||||
 | 
					          newPort++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.config.S3_CONSOLE_PORT = newPort.toString();
 | 
				
			||||||
 | 
					        logger.log('ok', `✅ New S3 Console port: ${newPort}`);
 | 
				
			||||||
 | 
					        updated = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (updated) {
 | 
				
			||||||
 | 
					      // Update derived fields
 | 
				
			||||||
 | 
					      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`;
 | 
				
			||||||
 | 
					      this.config.S3_ENDPOINT = this.config.S3_HOST;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      await this.saveConfig();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return updated;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Force reconfigure all ports with new available ones
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async reconfigurePorts(): Promise<void> {
 | 
				
			||||||
 | 
					    logger.log('note', '🔄 Finding new available ports...');
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    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++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.config.MONGODB_PORT = mongoPort.toString();
 | 
				
			||||||
 | 
					    this.config.S3_PORT = s3Port.toString();
 | 
				
			||||||
 | 
					    this.config.S3_CONSOLE_PORT = s3ConsolePort.toString();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Update derived fields
 | 
				
			||||||
 | 
					    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`;
 | 
				
			||||||
 | 
					    this.config.S3_ENDPOINT = this.config.S3_HOST;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    await this.saveConfig();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    logger.log('ok', '✅ New port configuration:');
 | 
				
			||||||
 | 
					    logger.log('info', `  📍 MongoDB: ${mongoPort}`);
 | 
				
			||||||
 | 
					    logger.log('info', `  📍 S3 API: ${s3Port}`);
 | 
				
			||||||
 | 
					    logger.log('info', `  📍 S3 Console: ${s3ConsolePort}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@ import * as plugins from './mod.plugins.js';
 | 
				
			|||||||
import * as helpers from './helpers.js';
 | 
					import * as helpers from './helpers.js';
 | 
				
			||||||
import { ServiceConfiguration } from './classes.serviceconfiguration.js';
 | 
					import { ServiceConfiguration } from './classes.serviceconfiguration.js';
 | 
				
			||||||
import { DockerContainer } from './classes.dockercontainer.js';
 | 
					import { DockerContainer } from './classes.dockercontainer.js';
 | 
				
			||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ServiceManager {
 | 
					export class ServiceManager {
 | 
				
			||||||
  private config: ServiceConfiguration;
 | 
					  private config: ServiceConfiguration;
 | 
				
			||||||
@@ -18,20 +19,23 @@ export class ServiceManager {
 | 
				
			|||||||
  public async init(): Promise<void> {
 | 
					  public async init(): Promise<void> {
 | 
				
			||||||
    // Check Docker availability
 | 
					    // Check Docker availability
 | 
				
			||||||
    if (!(await this.docker.checkDocker())) {
 | 
					    if (!(await this.docker.checkDocker())) {
 | 
				
			||||||
      helpers.printMessage('Error: Docker is not installed. Please install Docker first.', 'red');
 | 
					      logger.log('error', 'Error: Docker is not installed. Please install Docker first.');
 | 
				
			||||||
      process.exit(1);
 | 
					      process.exit(1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Load or create configuration
 | 
					    // Load or create configuration
 | 
				
			||||||
    await this.config.loadOrCreate();
 | 
					    await this.config.loadOrCreate();
 | 
				
			||||||
    helpers.printMessage(`📋 Project: ${this.config.getConfig().PROJECT_NAME}`, 'magenta');
 | 
					    logger.log('info', `📋 Project: ${this.config.getConfig().PROJECT_NAME}`);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Validate and update ports if needed
 | 
				
			||||||
 | 
					    await this.config.validateAndUpdatePorts();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Start MongoDB service
 | 
					   * Start MongoDB service
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async startMongoDB(): Promise<void> {
 | 
					  public async startMongoDB(): Promise<void> {
 | 
				
			||||||
    helpers.printMessage('📦 MongoDB:', 'yellow');
 | 
					    logger.log('note', '📦 MongoDB:');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    const config = this.config.getConfig();
 | 
					    const config = this.config.getConfig();
 | 
				
			||||||
    const containers = this.config.getContainerNames();
 | 
					    const containers = this.config.getContainerNames();
 | 
				
			||||||
@@ -44,19 +48,51 @@ export class ServiceManager {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    switch (status) {
 | 
					    switch (status) {
 | 
				
			||||||
      case 'running':
 | 
					      case 'running':
 | 
				
			||||||
        helpers.printMessage('  Already running ✓', 'green');
 | 
					        logger.log('ok', '  Already running ✓');
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      case 'stopped':
 | 
					      case 'stopped':
 | 
				
			||||||
        if (await this.docker.start(containers.mongo)) {
 | 
					        // Check if port mapping matches config
 | 
				
			||||||
          helpers.printMessage('  Started ✓', 'green');
 | 
					        const mongoPortMappings = await this.docker.getPortMappings(containers.mongo);
 | 
				
			||||||
 | 
					        if (mongoPortMappings && mongoPortMappings['27017'] !== config.MONGODB_PORT) {
 | 
				
			||||||
 | 
					          logger.log('note', '  Port configuration changed, recreating container...');
 | 
				
			||||||
 | 
					          await this.docker.remove(containers.mongo, true);
 | 
				
			||||||
 | 
					          // Fall through to create new 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', '  Recreated with new port ✓');
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            logger.log('error', '  Failed to recreate container');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          helpers.printMessage('  Failed to start', 'red');
 | 
					          // Ports match, just start the container
 | 
				
			||||||
 | 
					          if (await this.docker.start(containers.mongo)) {
 | 
				
			||||||
 | 
					            logger.log('ok', '  Started ✓');
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            logger.log('error', '  Failed to start');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      case 'not_exists':
 | 
					      case 'not_exists':
 | 
				
			||||||
        helpers.printMessage('  Creating container...', 'yellow');
 | 
					        logger.log('note', '  Creating container...');
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        const success = await this.docker.run({
 | 
					        const success = await this.docker.run({
 | 
				
			||||||
          name: containers.mongo,
 | 
					          name: containers.mongo,
 | 
				
			||||||
@@ -72,27 +108,33 @@ export class ServiceManager {
 | 
				
			|||||||
            MONGO_INITDB_ROOT_PASSWORD: config.MONGODB_PASS,
 | 
					            MONGO_INITDB_ROOT_PASSWORD: config.MONGODB_PASS,
 | 
				
			||||||
            MONGO_INITDB_DATABASE: config.MONGODB_NAME
 | 
					            MONGO_INITDB_DATABASE: config.MONGODB_NAME
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          restart: 'unless-stopped'
 | 
					          restart: 'unless-stopped',
 | 
				
			||||||
 | 
					          command: '--bind_ip_all'
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (success) {
 | 
					        if (success) {
 | 
				
			||||||
          helpers.printMessage('  Created and started ✓', 'green');
 | 
					          logger.log('ok', '  Created and started ✓');
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          helpers.printMessage('  Failed to create container', 'red');
 | 
					          logger.log('error', '  Failed to create container');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage(`  Container: ${containers.mongo}`, 'cyan');
 | 
					    logger.log('info', `  Container: ${containers.mongo}`);
 | 
				
			||||||
    helpers.printMessage(`  Port: ${config.MONGODB_PORT}`, 'cyan');
 | 
					    logger.log('info', `  Port: ${config.MONGODB_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  Connection: ${this.config.getMongoConnectionString()}`, 'blue');
 | 
					    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
 | 
					   * Start MinIO service
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async startMinIO(): Promise<void> {
 | 
					  public async startMinIO(): Promise<void> {
 | 
				
			||||||
    helpers.printMessage('📦 S3/MinIO:', 'yellow');
 | 
					    logger.log('note', '📦 S3/MinIO:');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    const config = this.config.getConfig();
 | 
					    const config = this.config.getConfig();
 | 
				
			||||||
    const containers = this.config.getContainerNames();
 | 
					    const containers = this.config.getContainerNames();
 | 
				
			||||||
@@ -105,19 +147,69 @@ export class ServiceManager {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    switch (status) {
 | 
					    switch (status) {
 | 
				
			||||||
      case 'running':
 | 
					      case 'running':
 | 
				
			||||||
        helpers.printMessage('  Already running ✓', 'green');
 | 
					        logger.log('ok', '  Already running ✓');
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      case 'stopped':
 | 
					      case 'stopped':
 | 
				
			||||||
        if (await this.docker.start(containers.minio)) {
 | 
					        // Check if port mapping matches config
 | 
				
			||||||
          helpers.printMessage('  Started ✓', 'green');
 | 
					        const minioPortMappings = await this.docker.getPortMappings(containers.minio);
 | 
				
			||||||
 | 
					        if (minioPortMappings && 
 | 
				
			||||||
 | 
					            (minioPortMappings['9000'] !== config.S3_PORT || 
 | 
				
			||||||
 | 
					             minioPortMappings['9001'] !== config.S3_CONSOLE_PORT)) {
 | 
				
			||||||
 | 
					          logger.log('note', '  Port configuration changed, recreating container...');
 | 
				
			||||||
 | 
					          await this.docker.remove(containers.minio, true);
 | 
				
			||||||
 | 
					          // Fall through to create new 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_ACCESSKEY,
 | 
				
			||||||
 | 
					              MINIO_ROOT_PASSWORD: config.S3_SECRETKEY
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            restart: 'unless-stopped',
 | 
				
			||||||
 | 
					            command: 'server /data --console-address ":9001"'
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          if (success) {
 | 
				
			||||||
 | 
					            logger.log('ok', '  Recreated with new ports ✓');
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 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_ACCESSKEY} ${config.S3_SECRETKEY}`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            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 recreate container');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          helpers.printMessage('  Failed to start', 'red');
 | 
					          // Ports match, just start the container
 | 
				
			||||||
 | 
					          if (await this.docker.start(containers.minio)) {
 | 
				
			||||||
 | 
					            logger.log('ok', '  Started ✓');
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            logger.log('error', '  Failed to start');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      case 'not_exists':
 | 
					      case 'not_exists':
 | 
				
			||||||
        helpers.printMessage('  Creating container...', 'yellow');
 | 
					        logger.log('note', '  Creating container...');
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        const success = await this.docker.run({
 | 
					        const success = await this.docker.run({
 | 
				
			||||||
          name: containers.minio,
 | 
					          name: containers.minio,
 | 
				
			||||||
@@ -130,15 +222,15 @@ export class ServiceManager {
 | 
				
			|||||||
            [directories.minio]: '/data'
 | 
					            [directories.minio]: '/data'
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          environment: {
 | 
					          environment: {
 | 
				
			||||||
            MINIO_ROOT_USER: config.S3_USER,
 | 
					            MINIO_ROOT_USER: config.S3_ACCESSKEY,
 | 
				
			||||||
            MINIO_ROOT_PASSWORD: config.S3_PASS
 | 
					            MINIO_ROOT_PASSWORD: config.S3_SECRETKEY
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          restart: 'unless-stopped',
 | 
					          restart: 'unless-stopped',
 | 
				
			||||||
          command: 'server /data --console-address ":9001"'
 | 
					          command: 'server /data --console-address ":9001"'
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (success) {
 | 
					        if (success) {
 | 
				
			||||||
          helpers.printMessage('  Created and started ✓', 'green');
 | 
					          logger.log('ok', '  Created and started ✓');
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
          // Wait for MinIO to be ready
 | 
					          // Wait for MinIO to be ready
 | 
				
			||||||
          await plugins.smartdelay.delayFor(3000);
 | 
					          await plugins.smartdelay.delayFor(3000);
 | 
				
			||||||
@@ -146,7 +238,7 @@ export class ServiceManager {
 | 
				
			|||||||
          // Create default bucket
 | 
					          // Create default bucket
 | 
				
			||||||
          await this.docker.exec(
 | 
					          await this.docker.exec(
 | 
				
			||||||
            containers.minio,
 | 
					            containers.minio,
 | 
				
			||||||
            `mc alias set local http://localhost:9000 ${config.S3_USER} ${config.S3_PASS}`
 | 
					            `mc alias set local http://localhost:9000 ${config.S3_ACCESSKEY} ${config.S3_SECRETKEY}`
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
          await this.docker.exec(
 | 
					          await this.docker.exec(
 | 
				
			||||||
@@ -154,37 +246,37 @@ export class ServiceManager {
 | 
				
			|||||||
            `mc mb local/${config.S3_BUCKET}`
 | 
					            `mc mb local/${config.S3_BUCKET}`
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          
 | 
					          
 | 
				
			||||||
          helpers.printMessage(`  Bucket '${config.S3_BUCKET}' created ✓`, 'green');
 | 
					          logger.log('ok', `  Bucket '${config.S3_BUCKET}' created ✓`);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          helpers.printMessage('  Failed to create container', 'red');
 | 
					          logger.log('error', '  Failed to create container');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage(`  Container: ${containers.minio}`, 'cyan');
 | 
					    logger.log('info', `  Container: ${containers.minio}`);
 | 
				
			||||||
    helpers.printMessage(`  Port: ${config.S3_PORT}`, 'cyan');
 | 
					    logger.log('info', `  Port: ${config.S3_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  Bucket: ${config.S3_BUCKET}`, 'cyan');
 | 
					    logger.log('info', `  Bucket: ${config.S3_BUCKET}`);
 | 
				
			||||||
    helpers.printMessage(`  API: http://${config.S3_HOST}:${config.S3_PORT}`, 'blue');
 | 
					    logger.log('info', `  API: http://${config.S3_HOST}:${config.S3_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT} (login: ${config.S3_USER}/***)`, 'blue');
 | 
					    logger.log('info', `  Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT} (login: ${config.S3_ACCESSKEY}/***)`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Stop MongoDB service
 | 
					   * Stop MongoDB service
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async stopMongoDB(): Promise<void> {
 | 
					  public async stopMongoDB(): Promise<void> {
 | 
				
			||||||
    helpers.printMessage('📦 MongoDB:', 'yellow');
 | 
					    logger.log('note', '📦 MongoDB:');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    const containers = this.config.getContainerNames();
 | 
					    const containers = this.config.getContainerNames();
 | 
				
			||||||
    const status = await this.docker.getStatus(containers.mongo);
 | 
					    const status = await this.docker.getStatus(containers.mongo);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (status === 'running') {
 | 
					    if (status === 'running') {
 | 
				
			||||||
      if (await this.docker.stop(containers.mongo)) {
 | 
					      if (await this.docker.stop(containers.mongo)) {
 | 
				
			||||||
        helpers.printMessage('  Stopped ✓', 'green');
 | 
					        logger.log('ok', '  Stopped ✓');
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        helpers.printMessage('  Failed to stop', 'red');
 | 
					        logger.log('error', '  Failed to stop');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      helpers.printMessage('  Not running', 'yellow');
 | 
					      logger.log('note', '  Not running');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -192,19 +284,19 @@ export class ServiceManager {
 | 
				
			|||||||
   * Stop MinIO service
 | 
					   * Stop MinIO service
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public async stopMinIO(): Promise<void> {
 | 
					  public async stopMinIO(): Promise<void> {
 | 
				
			||||||
    helpers.printMessage('📦 S3/MinIO:', 'yellow');
 | 
					    logger.log('note', '📦 S3/MinIO:');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    const containers = this.config.getContainerNames();
 | 
					    const containers = this.config.getContainerNames();
 | 
				
			||||||
    const status = await this.docker.getStatus(containers.minio);
 | 
					    const status = await this.docker.getStatus(containers.minio);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (status === 'running') {
 | 
					    if (status === 'running') {
 | 
				
			||||||
      if (await this.docker.stop(containers.minio)) {
 | 
					      if (await this.docker.stop(containers.minio)) {
 | 
				
			||||||
        helpers.printMessage('  Stopped ✓', 'green');
 | 
					        logger.log('ok', '  Stopped ✓');
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        helpers.printMessage('  Failed to stop', 'red');
 | 
					        logger.log('error', '  Failed to stop');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      helpers.printMessage('  Not running', 'yellow');
 | 
					      logger.log('note', '  Not running');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -217,23 +309,38 @@ export class ServiceManager {
 | 
				
			|||||||
    const config = this.config.getConfig();
 | 
					    const config = this.config.getConfig();
 | 
				
			||||||
    const containers = this.config.getContainerNames();
 | 
					    const containers = this.config.getContainerNames();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage(`Project: ${config.PROJECT_NAME}`, 'magenta');
 | 
					    logger.log('info', `Project: ${config.PROJECT_NAME}`);
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // MongoDB status
 | 
					    // MongoDB status
 | 
				
			||||||
    const mongoStatus = await this.docker.getStatus(containers.mongo);
 | 
					    const mongoStatus = await this.docker.getStatus(containers.mongo);
 | 
				
			||||||
    switch (mongoStatus) {
 | 
					    switch (mongoStatus) {
 | 
				
			||||||
      case 'running':
 | 
					      case 'running':
 | 
				
			||||||
        helpers.printMessage('📦 MongoDB: 🟢 Running', 'green');
 | 
					        logger.log('ok', '📦 MongoDB: 🟢 Running');
 | 
				
			||||||
        helpers.printMessage(`   ├─ Container: ${containers.mongo}`, 'cyan');
 | 
					        logger.log('info', `   ├─ Container: ${containers.mongo}`);
 | 
				
			||||||
        helpers.printMessage(`   └─ ${this.config.getMongoConnectionString()}`, 'cyan');
 | 
					        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}`);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'stopped':
 | 
					      case 'stopped':
 | 
				
			||||||
        helpers.printMessage('📦 MongoDB: 🟡 Stopped', 'yellow');
 | 
					        logger.log('note', '📦 MongoDB: 🟡 Stopped');
 | 
				
			||||||
        helpers.printMessage(`   └─ Container: ${containers.mongo}`, 'cyan');
 | 
					        logger.log('info', `   ├─ Container: ${containers.mongo}`);
 | 
				
			||||||
 | 
					        logger.log('info', `   └─ Port: ${config.MONGODB_PORT}`);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'not_exists':
 | 
					      case 'not_exists':
 | 
				
			||||||
        helpers.printMessage('📦 MongoDB: ⚪ Not installed', 'magenta');
 | 
					        logger.log('info', '📦 MongoDB: ⚪ Not installed');
 | 
				
			||||||
 | 
					        // Check port availability
 | 
				
			||||||
 | 
					        const mongoPort = parseInt(config.MONGODB_PORT);
 | 
				
			||||||
 | 
					        const mongoAvailable = await helpers.isPortAvailable(mongoPort);
 | 
				
			||||||
 | 
					        if (!mongoAvailable) {
 | 
				
			||||||
 | 
					          logger.log('error', `   └─ ⚠️  Port ${mongoPort} is in use by another process`);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          logger.log('info', `   └─ Port ${mongoPort} is available`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -241,18 +348,41 @@ export class ServiceManager {
 | 
				
			|||||||
    const minioStatus = await this.docker.getStatus(containers.minio);
 | 
					    const minioStatus = await this.docker.getStatus(containers.minio);
 | 
				
			||||||
    switch (minioStatus) {
 | 
					    switch (minioStatus) {
 | 
				
			||||||
      case 'running':
 | 
					      case 'running':
 | 
				
			||||||
        helpers.printMessage('📦 S3/MinIO: 🟢 Running', 'green');
 | 
					        logger.log('ok', '📦 S3/MinIO: 🟢 Running');
 | 
				
			||||||
        helpers.printMessage(`   ├─ Container: ${containers.minio}`, 'cyan');
 | 
					        logger.log('info', `   ├─ Container: ${containers.minio}`);
 | 
				
			||||||
        helpers.printMessage(`   ├─ API: http://${config.S3_HOST}:${config.S3_PORT}`, 'cyan');
 | 
					        logger.log('info', `   ├─ API: http://${config.S3_HOST}:${config.S3_PORT}`);
 | 
				
			||||||
        helpers.printMessage(`   ├─ Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`, 'cyan');
 | 
					        logger.log('info', `   ├─ Console: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`);
 | 
				
			||||||
        helpers.printMessage(`   └─ Bucket: ${config.S3_BUCKET}`, 'cyan');
 | 
					        logger.log('info', `   └─ Bucket: ${config.S3_BUCKET}`);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'stopped':
 | 
					      case 'stopped':
 | 
				
			||||||
        helpers.printMessage('📦 S3/MinIO: 🟡 Stopped', 'yellow');
 | 
					        logger.log('note', '📦 S3/MinIO: 🟡 Stopped');
 | 
				
			||||||
        helpers.printMessage(`   └─ Container: ${containers.minio}`, 'cyan');
 | 
					        logger.log('info', `   ├─ Container: ${containers.minio}`);
 | 
				
			||||||
 | 
					        logger.log('info', `   ├─ API Port: ${config.S3_PORT}`);
 | 
				
			||||||
 | 
					        logger.log('info', `   └─ Console Port: ${config.S3_CONSOLE_PORT}`);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'not_exists':
 | 
					      case 'not_exists':
 | 
				
			||||||
        helpers.printMessage('📦 S3/MinIO: ⚪ Not installed', 'magenta');
 | 
					        logger.log('info', '📦 S3/MinIO: ⚪ Not installed');
 | 
				
			||||||
 | 
					        // Check port availability
 | 
				
			||||||
 | 
					        const s3Port = parseInt(config.S3_PORT);
 | 
				
			||||||
 | 
					        const s3ConsolePort = parseInt(config.S3_CONSOLE_PORT);
 | 
				
			||||||
 | 
					        const s3Available = await helpers.isPortAvailable(s3Port);
 | 
				
			||||||
 | 
					        const consoleAvailable = await helpers.isPortAvailable(s3ConsolePort);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (!s3Available || !consoleAvailable) {
 | 
				
			||||||
 | 
					          if (!s3Available) {
 | 
				
			||||||
 | 
					            logger.log('error', `   ├─ ⚠️  API Port ${s3Port} is in use`);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            logger.log('info', `   ├─ API Port ${s3Port} is available`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (!consoleAvailable) {
 | 
				
			||||||
 | 
					            logger.log('error', `   └─ ⚠️  Console Port ${s3ConsolePort} is in use`);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            logger.log('info', `   └─ Console Port ${s3ConsolePort} is available`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          logger.log('info', `   ├─ API Port ${s3Port} is available`);
 | 
				
			||||||
 | 
					          logger.log('info', `   └─ Console Port ${s3ConsolePort} is available`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -265,30 +395,31 @@ export class ServiceManager {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    const config = this.config.getConfig();
 | 
					    const config = this.config.getConfig();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage(`Project: ${config.PROJECT_NAME}`, 'magenta');
 | 
					    logger.log('info', `Project: ${config.PROJECT_NAME}`);
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage('MongoDB:', 'yellow');
 | 
					    logger.log('note', 'MongoDB:');
 | 
				
			||||||
    helpers.printMessage(`  Host: ${config.MONGODB_HOST}:${config.MONGODB_PORT}`, undefined);
 | 
					    logger.log('info', `  Host: ${config.MONGODB_HOST}:${config.MONGODB_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  Database: ${config.MONGODB_NAME}`, undefined);
 | 
					    logger.log('info', `  Database: ${config.MONGODB_NAME}`);
 | 
				
			||||||
    helpers.printMessage(`  User: ${config.MONGODB_USER}`, undefined);
 | 
					    logger.log('info', `  User: ${config.MONGODB_USER}`);
 | 
				
			||||||
    helpers.printMessage('  Password: ***', undefined);
 | 
					    logger.log('info', '  Password: ***');
 | 
				
			||||||
    helpers.printMessage(`  Container: ${this.config.getContainerNames().mongo}`, undefined);
 | 
					    logger.log('info', `  Container: ${this.config.getContainerNames().mongo}`);
 | 
				
			||||||
    helpers.printMessage(`  Data: ${this.config.getDataDirectories().mongo}`, undefined);
 | 
					    logger.log('info', `  Data: ${this.config.getDataDirectories().mongo}`);
 | 
				
			||||||
    helpers.printMessage(`  Connection: ${this.config.getMongoConnectionString()}`, 'blue');
 | 
					    logger.log('info', `  Connection: ${this.config.getMongoConnectionString()}`);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    helpers.printMessage('S3/MinIO:', 'yellow');
 | 
					    logger.log('note', 'S3/MinIO:');
 | 
				
			||||||
    helpers.printMessage(`  Host: ${config.S3_HOST}`, undefined);
 | 
					    logger.log('info', `  Host: ${config.S3_HOST}`);
 | 
				
			||||||
    helpers.printMessage(`  API Port: ${config.S3_PORT}`, undefined);
 | 
					    logger.log('info', `  API Port: ${config.S3_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  Console Port: ${config.S3_CONSOLE_PORT}`, undefined);
 | 
					    logger.log('info', `  Console Port: ${config.S3_CONSOLE_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  User: ${config.S3_USER}`, undefined);
 | 
					    logger.log('info', `  Access Key: ${config.S3_ACCESSKEY}`);
 | 
				
			||||||
    helpers.printMessage('  Password: ***', undefined);
 | 
					    logger.log('info', '  Secret Key: ***');
 | 
				
			||||||
    helpers.printMessage(`  Bucket: ${config.S3_BUCKET}`, undefined);
 | 
					    logger.log('info', `  Bucket: ${config.S3_BUCKET}`);
 | 
				
			||||||
    helpers.printMessage(`  Container: ${this.config.getContainerNames().minio}`, undefined);
 | 
					    logger.log('info', `  Use SSL: ${config.S3_USESSL}`);
 | 
				
			||||||
    helpers.printMessage(`  Data: ${this.config.getDataDirectories().minio}`, undefined);
 | 
					    logger.log('info', `  Container: ${this.config.getContainerNames().minio}`);
 | 
				
			||||||
    helpers.printMessage(`  API URL: http://${config.S3_HOST}:${config.S3_PORT}`, 'blue');
 | 
					    logger.log('info', `  Data: ${this.config.getDataDirectories().minio}`);
 | 
				
			||||||
    helpers.printMessage(`  Console URL: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`, 'blue');
 | 
					    logger.log('info', `  Endpoint: ${config.S3_ENDPOINT}`);
 | 
				
			||||||
 | 
					    logger.log('info', `  Console URL: http://${config.S3_HOST}:${config.S3_CONSOLE_PORT}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -302,20 +433,20 @@ export class ServiceManager {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    const connectionString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`;
 | 
					    const connectionString = `mongodb://${config.MONGODB_USER}:${config.MONGODB_PASS}@${networkIp}:${config.MONGODB_PORT}/${config.MONGODB_NAME}?authSource=admin`;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    helpers.printMessage('MongoDB Compass is a GUI tool for MongoDB. To connect:', 'cyan');
 | 
					    logger.log('info', 'MongoDB Compass is a GUI tool for MongoDB. To connect:');
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    helpers.printMessage('1. Download MongoDB Compass from:', undefined);
 | 
					    logger.log('info', '1. Download MongoDB Compass from:');
 | 
				
			||||||
    helpers.printMessage('   https://www.mongodb.com/products/compass', 'blue');
 | 
					    logger.log('info', '   https://www.mongodb.com/products/compass');
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    helpers.printMessage('2. Open Compass and paste this connection string:', undefined);
 | 
					    logger.log('info', '2. Open Compass and paste this connection string:');
 | 
				
			||||||
    helpers.printMessage(`   ${connectionString}`, 'green');
 | 
					    logger.log('ok', `   ${connectionString}`);
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    helpers.printMessage('Connection Details:', 'yellow');
 | 
					    logger.log('note', 'Connection Details:');
 | 
				
			||||||
    helpers.printMessage(`  Network IP: ${networkIp}`, undefined);
 | 
					    logger.log('info', `  Network IP: ${networkIp}`);
 | 
				
			||||||
    helpers.printMessage(`  Port: ${config.MONGODB_PORT}`, undefined);
 | 
					    logger.log('info', `  Port: ${config.MONGODB_PORT}`);
 | 
				
			||||||
    helpers.printMessage(`  Database: ${config.MONGODB_NAME}`, undefined);
 | 
					    logger.log('info', `  Database: ${config.MONGODB_NAME}`);
 | 
				
			||||||
    helpers.printMessage(`  Username: ${config.MONGODB_USER}`, undefined);
 | 
					    logger.log('info', `  Username: ${config.MONGODB_USER}`);
 | 
				
			||||||
    helpers.printMessage(`  Auth Source: admin`, undefined);
 | 
					    logger.log('info', `  Auth Source: admin`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -332,7 +463,7 @@ export class ServiceManager {
 | 
				
			|||||||
          const logs = await this.docker.logs(containers.mongo, lines);
 | 
					          const logs = await this.docker.logs(containers.mongo, lines);
 | 
				
			||||||
          console.log(logs);
 | 
					          console.log(logs);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          helpers.printMessage('MongoDB container is not running', 'yellow');
 | 
					          logger.log('note', 'MongoDB container is not running');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@@ -343,7 +474,7 @@ export class ServiceManager {
 | 
				
			|||||||
          const logs = await this.docker.logs(containers.minio, lines);
 | 
					          const logs = await this.docker.logs(containers.minio, lines);
 | 
				
			||||||
          console.log(logs);
 | 
					          console.log(logs);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          helpers.printMessage('S3/MinIO container is not running', 'yellow');
 | 
					          logger.log('note', 'S3/MinIO container is not running');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@@ -355,7 +486,7 @@ export class ServiceManager {
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        helpers.printMessage('Usage: gitzone services logs [mongo|s3|all] [lines]', 'yellow');
 | 
					        logger.log('note', 'Usage: gitzone services logs [mongo|s3|all] [lines]');
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -369,20 +500,20 @@ export class ServiceManager {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    if (await this.docker.exists(containers.mongo)) {
 | 
					    if (await this.docker.exists(containers.mongo)) {
 | 
				
			||||||
      if (await this.docker.remove(containers.mongo, true)) {
 | 
					      if (await this.docker.remove(containers.mongo, true)) {
 | 
				
			||||||
        helpers.printMessage('  MongoDB container removed ✓', 'green');
 | 
					        logger.log('ok', '  MongoDB container removed ✓');
 | 
				
			||||||
        removed = true;
 | 
					        removed = true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (await this.docker.exists(containers.minio)) {
 | 
					    if (await this.docker.exists(containers.minio)) {
 | 
				
			||||||
      if (await this.docker.remove(containers.minio, true)) {
 | 
					      if (await this.docker.remove(containers.minio, true)) {
 | 
				
			||||||
        helpers.printMessage('  S3/MinIO container removed ✓', 'green');
 | 
					        logger.log('ok', '  S3/MinIO container removed ✓');
 | 
				
			||||||
        removed = true;
 | 
					        removed = true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (!removed) {
 | 
					    if (!removed) {
 | 
				
			||||||
      helpers.printMessage('  No containers to remove', 'yellow');
 | 
					      logger.log('note', '  No containers to remove');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -395,18 +526,58 @@ export class ServiceManager {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    if (await plugins.smartfile.fs.fileExists(directories.mongo)) {
 | 
					    if (await plugins.smartfile.fs.fileExists(directories.mongo)) {
 | 
				
			||||||
      await plugins.smartfile.fs.remove(directories.mongo);
 | 
					      await plugins.smartfile.fs.remove(directories.mongo);
 | 
				
			||||||
      helpers.printMessage('  MongoDB data removed ✓', 'green');
 | 
					      logger.log('ok', '  MongoDB data removed ✓');
 | 
				
			||||||
      cleaned = true;
 | 
					      cleaned = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (await plugins.smartfile.fs.fileExists(directories.minio)) {
 | 
					    if (await plugins.smartfile.fs.fileExists(directories.minio)) {
 | 
				
			||||||
      await plugins.smartfile.fs.remove(directories.minio);
 | 
					      await plugins.smartfile.fs.remove(directories.minio);
 | 
				
			||||||
      helpers.printMessage('  S3/MinIO data removed ✓', 'green');
 | 
					      logger.log('ok', '  S3/MinIO data removed ✓');
 | 
				
			||||||
      cleaned = true;
 | 
					      cleaned = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (!cleaned) {
 | 
					    if (!cleaned) {
 | 
				
			||||||
      helpers.printMessage('  No data to clean', 'yellow');
 | 
					      logger.log('note', '  No data to clean');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Reconfigure services with new ports
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public async reconfigure(): Promise<void> {
 | 
				
			||||||
 | 
					    helpers.printHeader('Reconfiguring Services');
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    const containers = this.config.getContainerNames();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Stop existing containers
 | 
				
			||||||
 | 
					    logger.log('note', '🛑 Stopping existing containers...');
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (await this.docker.exists(containers.mongo)) {
 | 
				
			||||||
 | 
					      await this.docker.stop(containers.mongo);
 | 
				
			||||||
 | 
					      logger.log('ok', '  MongoDB stopped ✓');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (await this.docker.exists(containers.minio)) {
 | 
				
			||||||
 | 
					      await this.docker.stop(containers.minio);
 | 
				
			||||||
 | 
					      logger.log('ok', '  S3/MinIO stopped ✓');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Reconfigure ports
 | 
				
			||||||
 | 
					    await this.config.reconfigurePorts();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Ask if user wants to restart services
 | 
				
			||||||
 | 
					    const smartinteract = new plugins.smartinteract.SmartInteract();
 | 
				
			||||||
 | 
					    const response = await smartinteract.askQuestion({
 | 
				
			||||||
 | 
					      name: 'restart',
 | 
				
			||||||
 | 
					      type: 'confirm',
 | 
				
			||||||
 | 
					      message: 'Do you want to start services with new ports?',
 | 
				
			||||||
 | 
					      default: true
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if (response.value) {
 | 
				
			||||||
 | 
					      console.log();
 | 
				
			||||||
 | 
					      await this.startMongoDB();
 | 
				
			||||||
 | 
					      await this.startMinIO();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import * as plugins from './mod.plugins.js';
 | 
					import * as plugins from './mod.plugins.js';
 | 
				
			||||||
import * as net from 'net';
 | 
					import * as net from 'net';
 | 
				
			||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Check if a port is available
 | 
					 * Check if a port is available
 | 
				
			||||||
@@ -58,40 +59,14 @@ export const getProjectName = (): string => {
 | 
				
			|||||||
  return plugins.path.basename(process.cwd());
 | 
					  return plugins.path.basename(process.cwd());
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Print colored message to console
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export const printMessage = (message: string, color?: 'green' | 'yellow' | 'red' | 'blue' | 'magenta' | 'cyan') => {
 | 
					 | 
				
			||||||
  const logger = new plugins.smartlog.ConsoleLog();
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  switch (color) {
 | 
					 | 
				
			||||||
    case 'green':
 | 
					 | 
				
			||||||
      logger.log('ok', message);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'yellow':
 | 
					 | 
				
			||||||
      logger.log('note', message);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'red':
 | 
					 | 
				
			||||||
      logger.log('error', message);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case 'blue':
 | 
					 | 
				
			||||||
    case 'magenta':
 | 
					 | 
				
			||||||
    case 'cyan':
 | 
					 | 
				
			||||||
      logger.log('info', message);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
      logger.log('info', message);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Print a header with decorative lines
 | 
					 * Print a header with decorative lines
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const printHeader = (title: string) => {
 | 
					export const printHeader = (title: string) => {
 | 
				
			||||||
  console.log();
 | 
					  console.log();
 | 
				
			||||||
  printMessage('═══════════════════════════════════════════════════════════════', 'cyan');
 | 
					  logger.log('info', '═══════════════════════════════════════════════════════════════');
 | 
				
			||||||
  printMessage(`  ${title}`, 'cyan');
 | 
					  logger.log('info', `  ${title}`);
 | 
				
			||||||
  printMessage('═══════════════════════════════════════════════════════════════', 'cyan');
 | 
					  logger.log('info', '═══════════════════════════════════════════════════════════════');
 | 
				
			||||||
  console.log();
 | 
					  console.log();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import * as plugins from './mod.plugins.js';
 | 
					import * as plugins from './mod.plugins.js';
 | 
				
			||||||
import * as helpers from './helpers.js';
 | 
					import * as helpers from './helpers.js';
 | 
				
			||||||
import { ServiceManager } from './classes.servicemanager.js';
 | 
					import { ServiceManager } from './classes.servicemanager.js';
 | 
				
			||||||
 | 
					import { logger } from '../gitzone.logging.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const run = async (argvArg: any) => {
 | 
					export const run = async (argvArg: any) => {
 | 
				
			||||||
  const serviceManager = new ServiceManager();
 | 
					  const serviceManager = new ServiceManager();
 | 
				
			||||||
@@ -47,6 +48,10 @@ export const run = async (argvArg: any) => {
 | 
				
			|||||||
      await handleClean(serviceManager);
 | 
					      await handleClean(serviceManager);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
 | 
					    case 'reconfigure':
 | 
				
			||||||
 | 
					      await serviceManager.reconfigure();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
    case 'help':
 | 
					    case 'help':
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      showHelp();
 | 
					      showHelp();
 | 
				
			||||||
@@ -76,8 +81,8 @@ async function handleStart(serviceManager: ServiceManager, service: string) {
 | 
				
			|||||||
      break;
 | 
					      break;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      helpers.printMessage(`Unknown service: ${service}`, 'red');
 | 
					      logger.log('error', `Unknown service: ${service}`);
 | 
				
			||||||
      helpers.printMessage('Use: mongo, s3, or all', 'yellow');
 | 
					      logger.log('note', 'Use: mongo, s3, or all');
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -104,8 +109,8 @@ async function handleStop(serviceManager: ServiceManager, service: string) {
 | 
				
			|||||||
      break;
 | 
					      break;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      helpers.printMessage(`Unknown service: ${service}`, 'red');
 | 
					      logger.log('error', `Unknown service: ${service}`);
 | 
				
			||||||
      helpers.printMessage('Use: mongo, s3, or all', 'yellow');
 | 
					      logger.log('note', 'Use: mongo, s3, or all');
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -139,28 +144,28 @@ async function handleRestart(serviceManager: ServiceManager, service: string) {
 | 
				
			|||||||
      break;
 | 
					      break;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      helpers.printMessage(`Unknown service: ${service}`, 'red');
 | 
					      logger.log('error', `Unknown service: ${service}`);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function handleRemove(serviceManager: ServiceManager) {
 | 
					async function handleRemove(serviceManager: ServiceManager) {
 | 
				
			||||||
  helpers.printHeader('Removing Containers');
 | 
					  helpers.printHeader('Removing Containers');
 | 
				
			||||||
  helpers.printMessage('⚠️  This will remove containers but preserve data', 'yellow');
 | 
					  logger.log('note', '⚠️  This will remove containers but preserve data');
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  const shouldContinue = await plugins.smartinteract.SmartInteract.getCliConfirmation('Continue?', false);
 | 
					  const shouldContinue = await plugins.smartinteract.SmartInteract.getCliConfirmation('Continue?', false);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  if (shouldContinue) {
 | 
					  if (shouldContinue) {
 | 
				
			||||||
    await serviceManager.removeContainers();
 | 
					    await serviceManager.removeContainers();
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    helpers.printMessage('Cancelled', 'yellow');
 | 
					    logger.log('note', 'Cancelled');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function handleClean(serviceManager: ServiceManager) {
 | 
					async function handleClean(serviceManager: ServiceManager) {
 | 
				
			||||||
  helpers.printHeader('Clean All');
 | 
					  helpers.printHeader('Clean All');
 | 
				
			||||||
  helpers.printMessage('⚠️  WARNING: This will remove all containers and data!', 'red');
 | 
					  logger.log('error', '⚠️  WARNING: This will remove all containers and data!');
 | 
				
			||||||
  helpers.printMessage('This action cannot be undone!', 'red');
 | 
					  logger.log('error', 'This action cannot be undone!');
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  const smartinteraction = new plugins.smartinteract.SmartInteract();
 | 
					  const smartinteraction = new plugins.smartinteract.SmartInteract();
 | 
				
			||||||
  const confirmAnswer = await smartinteraction.askQuestion({
 | 
					  const confirmAnswer = await smartinteraction.askQuestion({
 | 
				
			||||||
@@ -174,45 +179,46 @@ async function handleClean(serviceManager: ServiceManager) {
 | 
				
			|||||||
    await serviceManager.removeContainers();
 | 
					    await serviceManager.removeContainers();
 | 
				
			||||||
    console.log();
 | 
					    console.log();
 | 
				
			||||||
    await serviceManager.cleanData();
 | 
					    await serviceManager.cleanData();
 | 
				
			||||||
    helpers.printMessage('All cleaned ✓', 'green');
 | 
					    logger.log('ok', 'All cleaned ✓');
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    helpers.printMessage('Cancelled', 'yellow');
 | 
					    logger.log('note', 'Cancelled');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showHelp() {
 | 
					function showHelp() {
 | 
				
			||||||
  helpers.printHeader('GitZone Services Manager');
 | 
					  helpers.printHeader('GitZone Services Manager');
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  helpers.printMessage('Usage: gitzone services [command] [options]', 'green');
 | 
					  logger.log('ok', 'Usage: gitzone services [command] [options]');
 | 
				
			||||||
  console.log();
 | 
					  console.log();
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  helpers.printMessage('Commands:', 'yellow');
 | 
					  logger.log('note', 'Commands:');
 | 
				
			||||||
  helpers.printMessage('  start [service]   Start services (mongo|s3|all)', undefined);
 | 
					  logger.log('info', '  start [service]   Start services (mongo|s3|all)');
 | 
				
			||||||
  helpers.printMessage('  stop [service]    Stop services (mongo|s3|all)', undefined);
 | 
					  logger.log('info', '  stop [service]    Stop services (mongo|s3|all)');
 | 
				
			||||||
  helpers.printMessage('  restart [service] Restart services (mongo|s3|all)', undefined);
 | 
					  logger.log('info', '  restart [service] Restart services (mongo|s3|all)');
 | 
				
			||||||
  helpers.printMessage('  status            Show service status', undefined);
 | 
					  logger.log('info', '  status            Show service status');
 | 
				
			||||||
  helpers.printMessage('  config            Show current configuration', undefined);
 | 
					  logger.log('info', '  config            Show current configuration');
 | 
				
			||||||
  helpers.printMessage('  compass           Show MongoDB Compass connection string', undefined);
 | 
					  logger.log('info', '  compass           Show MongoDB Compass connection string');
 | 
				
			||||||
  helpers.printMessage('  logs [service]    Show logs (mongo|s3|all) [lines]', undefined);
 | 
					  logger.log('info', '  logs [service]    Show logs (mongo|s3|all) [lines]');
 | 
				
			||||||
  helpers.printMessage('  remove            Remove all containers', undefined);
 | 
					  logger.log('info', '  reconfigure       Reassign ports and restart services');
 | 
				
			||||||
  helpers.printMessage('  clean             Remove all containers and data ⚠️', undefined);
 | 
					  logger.log('info', '  remove            Remove all containers');
 | 
				
			||||||
  helpers.printMessage('  help              Show this help message', undefined);
 | 
					  logger.log('info', '  clean             Remove all containers and data ⚠️');
 | 
				
			||||||
 | 
					  logger.log('info', '  help              Show this help message');
 | 
				
			||||||
  console.log();
 | 
					  console.log();
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  helpers.printMessage('Features:', 'yellow');
 | 
					  logger.log('note', 'Features:');
 | 
				
			||||||
  helpers.printMessage('  • Auto-creates .nogit/env.json with smart defaults', undefined);
 | 
					  logger.log('info', '  • Auto-creates .nogit/env.json with smart defaults');
 | 
				
			||||||
  helpers.printMessage('  • Random ports (20000-30000) to avoid conflicts', undefined);
 | 
					  logger.log('info', '  • Random ports (20000-30000) to avoid conflicts');
 | 
				
			||||||
  helpers.printMessage('  • Project-specific containers for multi-project support', undefined);
 | 
					  logger.log('info', '  • Project-specific containers for multi-project support');
 | 
				
			||||||
  helpers.printMessage('  • Preserves custom configuration values', undefined);
 | 
					  logger.log('info', '  • Preserves custom configuration values');
 | 
				
			||||||
  helpers.printMessage('  • MongoDB Compass connection support', undefined);
 | 
					  logger.log('info', '  • MongoDB Compass connection support');
 | 
				
			||||||
  console.log();
 | 
					  console.log();
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  helpers.printMessage('Examples:', 'yellow');
 | 
					  logger.log('note', 'Examples:');
 | 
				
			||||||
  helpers.printMessage('  gitzone services start          # Start all services', undefined);
 | 
					  logger.log('info', '  gitzone services start          # Start all services');
 | 
				
			||||||
  helpers.printMessage('  gitzone services start mongo    # Start only MongoDB', undefined);
 | 
					  logger.log('info', '  gitzone services start mongo    # Start only MongoDB');
 | 
				
			||||||
  helpers.printMessage('  gitzone services stop           # Stop all services', undefined);
 | 
					  logger.log('info', '  gitzone services stop           # Stop all services');
 | 
				
			||||||
  helpers.printMessage('  gitzone services status         # Check service status', undefined);
 | 
					  logger.log('info', '  gitzone services status         # Check service status');
 | 
				
			||||||
  helpers.printMessage('  gitzone services config         # Show configuration', undefined);
 | 
					  logger.log('info', '  gitzone services config         # Show configuration');
 | 
				
			||||||
  helpers.printMessage('  gitzone services compass        # Get MongoDB Compass connection', undefined);
 | 
					  logger.log('info', '  gitzone services compass        # Get MongoDB Compass connection');
 | 
				
			||||||
  helpers.printMessage('  gitzone services logs mongo 50  # Show last 50 lines of MongoDB logs', undefined);
 | 
					  logger.log('info', '  gitzone services logs mongo 50  # Show last 50 lines of MongoDB logs');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user