Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f43f88a3cb | |||
| 4c86ad62fb | |||
| 4214a1fdf1 | |||
| 1c33735799 | |||
| 274405e364 | |||
| bf858c8650 | |||
| b257c82bd6 | |||
| 5a1f6d8c76 | |||
| d44ad6e4e4 | |||
| 142adfd396 | |||
| b55e75d169 | |||
| d0d922e53b | |||
| eda67395fe | |||
| 470e87eb79 | |||
| 3358a0eacc | |||
| b65fac6257 | |||
| 4ab59609e6 | |||
| 32f106291f | |||
| b8aa5d61f6 | |||
| 71759c276e | |||
| 7938f12d43 | |||
| 3722258d69 | |||
| 68859d0e97 | |||
| ecadbc7a86 | |||
| 0243bc5ec7 | |||
| 92e618104f | |||
| c089c1f80d | |||
| 10a394c7d8 | |||
| 5980308bb8 | |||
| 398e36bdf7 | |||
| 1e78517547 | |||
| 55700ad87e | |||
| 773df5268b | |||
| b51fa88283 | |||
| cb9f717d54 | |||
| 70be11894c | |||
| 89ab63b153 | |||
| 44c193d4a8 | |||
| 44d259a0ae | |||
| f0adff8784 | |||
| fb453e62c3 | |||
| 001721a8e9 | |||
| b191464ff9 | |||
| 4d7eaa238f | |||
| 601e0d1063 | |||
| 4bb1a2f8c7 | |||
| b506bf8785 |
@@ -6,19 +6,19 @@ on:
|
|||||||
- '**'
|
- '**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
NPMCI_URL_CLOUDLY: ${-{secrets.NPMCI_URL_CLOUDLY}-}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
security:
|
security:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
container:
|
container:
|
||||||
image: ${{ env.IMAGE }}
|
image: ${-{ env.IMAGE }-}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -44,11 +44,11 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
test:
|
test:
|
||||||
if: ${{ always() }}
|
if: ${-{ always() }-}
|
||||||
needs: security
|
needs: security
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ${{ env.IMAGE }}
|
image: ${-{ env.IMAGE }-}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ on:
|
|||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
NPMCI_URL_CLOUDLY: ${-{secrets.NPMCI_URL_CLOUDLY}-}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
security:
|
security:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
container:
|
container:
|
||||||
image: ${{ env.IMAGE }}
|
image: ${-{ env.IMAGE }-}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -42,11 +42,11 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
test:
|
test:
|
||||||
if: ${{ always() }}
|
if: ${-{ always() }-}
|
||||||
needs: security
|
needs: security
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ${{ env.IMAGE }}
|
image: ${-{ env.IMAGE }-}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ${{ env.IMAGE }}
|
image: ${-{ env.IMAGE }-}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -95,7 +95,7 @@ jobs:
|
|||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ${{ env.IMAGE }}
|
image: ${-{ env.IMAGE }-}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+4
-2
@@ -3,8 +3,6 @@
|
|||||||
# artifacts
|
# artifacts
|
||||||
coverage/
|
coverage/
|
||||||
public/
|
public/
|
||||||
test/
|
|
||||||
test2/
|
|
||||||
|
|
||||||
# installs
|
# installs
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -18,6 +16,10 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
|
# AI
|
||||||
|
.claude/
|
||||||
|
.serena/
|
||||||
|
|
||||||
#------# custom
|
#------# custom
|
||||||
.serena
|
.serena
|
||||||
test-output.json
|
test-output.json
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
|
# rust
|
||||||
|
rust/target/
|
||||||
|
dist_rust/
|
||||||
|
|
||||||
# AI
|
# AI
|
||||||
.claude/
|
.claude/
|
||||||
.serena/
|
.serena/
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
|
||||||
"lib": ["ES2022", "DOM"],
|
"lib": ["ES2022", "DOM"],
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"checkJs": true
|
"checkJs": true
|
||||||
|
|||||||
@@ -12,15 +12,17 @@ fileName: package.json
|
|||||||
"author": "{{module.author}}",
|
"author": "{{module.author}}",
|
||||||
"license": "{{module.license}}",
|
"license": "{{module.license}}",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "(tstest test/ --web)",
|
"test": "(tstest test/ --verbose --logfile --timeout 60)",
|
||||||
"build": "(tsbuild --web --allowimplicitany)",
|
"build": "(tsbuild tsfolders --allowimplicitany)",
|
||||||
"buildDocs": "(tsdoc)"
|
"buildDocs": "(tsdoc)"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^3.1.2",
|
"@git.zone/tsbuild": "^4.1.2",
|
||||||
"@git.zone/tsrun": "^2.0.0",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@git.zone/tstest": "^3.1.3",
|
"@git.zone/tstest": "^3.1.8",
|
||||||
"@types/node": "^24.10.1"
|
"@types/node": "^25.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"@push.rocks/smartpath": "^6.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import * as plugins from './{{module.name}}.plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
export let demoExport = 'Hi there! :) This is an exported string';
|
export let demoExport = 'Hi there! :) This is an exported string';
|
||||||
|
|||||||
+3
@@ -1,3 +1,6 @@
|
|||||||
|
---
|
||||||
|
fileName: .smartconfig.json
|
||||||
|
---
|
||||||
{
|
{
|
||||||
"@git.zone/cli": {
|
"@git.zone/cli": {
|
||||||
"projectType": "{{projectType}}",
|
"projectType": "{{projectType}}",
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"json.schemas": [
|
"json.schemas": [
|
||||||
{
|
{
|
||||||
"fileMatch": ["/npmextra.json"],
|
"fileMatch": ["/.smartconfig.json"],
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
+199
-2
@@ -1,6 +1,160 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-04-16 - 2.13.16 - fix(mod_format)
|
||||||
|
stop package.json formatter from modifying buildDocs and dependency entries
|
||||||
|
|
||||||
|
- removes automatic buildDocs script injection from the package.json formatter
|
||||||
|
- removes dependency include/exclude and latest-version update logic from package.json formatting
|
||||||
|
- drops the unused smartnpm plugin import after removing registry lookups
|
||||||
|
|
||||||
|
## 2026-03-24 - 2.13.15 - fix(repo)
|
||||||
|
no changes to commit
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-03-24 - 2.13.14 - fix(mod_format)
|
||||||
|
move smartconfig file renaming into the formatter orchestrator
|
||||||
|
|
||||||
|
- Renames smartconfig.json or npmextra.json to .smartconfig.json before formatters run
|
||||||
|
- Simplifies the smartconfig formatter to only read and modify .smartconfig.json
|
||||||
|
- Removes create/delete change planning for config renames and applies only content updates within the formatter
|
||||||
|
|
||||||
|
## 2026-03-24 - 2.13.13 - fix(vscode-template)
|
||||||
|
update VS Code schema matching to use .smartconfig.json
|
||||||
|
|
||||||
|
- Changes the VS Code template settings so the JSON schema applies to /.smartconfig.json instead of /npmextra.json.
|
||||||
|
|
||||||
|
## 2026-03-24 - 2.13.12 - fix(mod_format)
|
||||||
|
render format templates through smartscaf before comparing generated files
|
||||||
|
|
||||||
|
- adds smartscaf-based in-memory template rendering so supplied variables are applied before detecting changes
|
||||||
|
- supports release.accessLevel as a fallback when selecting public vs private CI templates
|
||||||
|
- matches rendered output by template or destination path to handle renamed files from template frontmatter
|
||||||
|
|
||||||
|
## 2026-03-24 - 2.13.10 - fix(config)
|
||||||
|
migrate configuration handling from npmextra to smartconfig
|
||||||
|
|
||||||
|
- replace @push.rocks/npmextra with @push.rocks/smartconfig across config, commit, format, and service modules
|
||||||
|
- switch managed project config file references from npmextra.json to smartconfig.json
|
||||||
|
- update formatting and package metadata checks to include smartconfig.json
|
||||||
|
- extend the gitignore template with Rust build output directories
|
||||||
|
|
||||||
|
## 2026-03-11 - 2.13.9 - fix(deps,readme)
|
||||||
|
bump dependencies and update README to prefer pnpm and document semantic commit flags
|
||||||
|
|
||||||
|
- Dev dependency updates: @git.zone/tsbuild -> ^4.3.0, @git.zone/tstest -> ^3.3.2, @types/node -> ^25.4.0
|
||||||
|
- Dependency upgrades: @git.zone/tsdoc -> ^2.0.0, @git.zone/tspublish -> ^1.11.2, @push.rocks/lik -> ^6.3.1, @push.rocks/smartfs -> ^1.5.0, @push.rocks/smartlog -> ^3.2.1, @push.rocks/smartstream -> ^3.4.0, prettier -> ^3.8.1 (and other minor/patch bumps)
|
||||||
|
- README changes: prefer pnpm for global install, clarify format command dry-run behavior and --write flag, add and document gitzone commit flags (-y/--yes, -p/--push, -t/--test, -b/--build, -r/--release) and AI-powered commit workflow
|
||||||
|
- No source code changes; this is a documentation and dependency refresh, recommend a patch release
|
||||||
|
|
||||||
|
## 2026-03-05 - 2.13.8 - fix(dependencies)
|
||||||
|
move runtime tooling packages from devDependencies to dependencies
|
||||||
|
|
||||||
|
- Removed @push.rocks/smartdelay, @push.rocks/smartinteract, @push.rocks/smartnetwork, and @push.rocks/smartshell from devDependencies and added them to dependencies
|
||||||
|
- No package version numbers were changed; this ensures the moved packages are installed for consumers at runtime
|
||||||
|
|
||||||
|
## 2026-03-05 - 2.13.7 - fix(deps)
|
||||||
|
bump devDependencies: @git.zone/tsbuild to ^4.1.4 and @push.rocks/smartshell to ^3.3.7
|
||||||
|
|
||||||
|
- Updated @git.zone/tsbuild from ^4.1.2 to ^4.1.4 (patch)
|
||||||
|
- Updated @push.rocks/smartshell from ^3.3.0 to ^3.3.7 (patch)
|
||||||
|
|
||||||
|
## 2026-02-01 - 2.13.6 - fix(templates/npm)
|
||||||
|
use tsbuild tsfolders instead of --web flag in npm template build script
|
||||||
|
|
||||||
|
- Changed build script in assets/templates/npm/.package.json from "(tsbuild --web --allowimplicitany)" to "(tsbuild tsfolders --allowimplicitany)"
|
||||||
|
- Replaces --web flag with explicit tsfolders argument to correctly target project folders during build
|
||||||
|
|
||||||
|
## 2026-02-01 - 2.13.5 - fix(templates/npm)
|
||||||
|
update npm template: tweak test script, bump devDependencies, add smartpath dependency, and fix ts import path
|
||||||
|
|
||||||
|
- test script updated: '(tstest test/ --web)' -> '(tstest test/ --verbose --logfile --timeout 60)'
|
||||||
|
- devDependencies bumped: @git.zone/tsbuild ^3.1.2 -> ^4.1.2, @git.zone/tsrun ^2.0.0 -> ^2.0.1, @git.zone/tstest ^3.1.3 -> ^3.1.8, @types/node ^24.10.1 -> ^25.2.0
|
||||||
|
- dependencies: added @push.rocks/smartpath ^6.0.0
|
||||||
|
- TypeScript template import fixed: './{{module.name}}.plugins.js' -> './plugins.js'
|
||||||
|
|
||||||
|
## 2025-12-18 - 2.13.3 - fix(tsconfig)
|
||||||
|
remove experimentalDecorators and useDefineForClassFields from TypeScript configuration files
|
||||||
|
|
||||||
|
- Removed "experimentalDecorators": true from assets/templates/multienv/deno.json and tsconfig.json
|
||||||
|
- Removed "useDefineForClassFields": false from tsconfig.json
|
||||||
|
- This change alters TypeScript/Deno compiler behavior: decorator support and legacy class-field initialization semantics may be affected; code relying on those may need updates
|
||||||
|
|
||||||
|
## 2025-12-16 - 2.13.2 - fix(deps)
|
||||||
|
bump @git.zone/tspublish to ^1.11.0
|
||||||
|
|
||||||
|
- Updated dependency @git.zone/tspublish from ^1.10.3 to ^1.11.0 in package.json
|
||||||
|
|
||||||
|
## 2025-12-16 - 2.13.1 - fix(npmextra)
|
||||||
|
merge old npmextra keys into new keys during migration, preserving existing new values
|
||||||
|
|
||||||
|
- Changed migration logic to merge data when both old and new keys exist instead of skipping the merge.
|
||||||
|
- Merge preserves existing new-key values (old values do not overwrite new ones) and still deletes the old key after migration.
|
||||||
|
- Applied the fix in both ts/mod_format/format.npmextra.ts and ts/mod_format/formatters/npmextra.formatter.ts.
|
||||||
|
- Adds a console log for successful migrations; behavior for single-key rename remains unchanged.
|
||||||
|
|
||||||
|
## 2025-12-16 - 2.13.0 - feat(tests)
|
||||||
|
feat(tests): add sandbox test fixture, CI and editor configs; bump deps
|
||||||
|
|
||||||
|
- Added comprehensive test/ fixture (sandbox-npmts) including package.json, npmextra.json, readme, GitLab CI (.gitlab-ci.yml), .npmrc, VSCode launch/settings and qenv.yml
|
||||||
|
- Added test sources and helper files under test/test and test/ts (browser and node tests, commitinfo data, simple library code)
|
||||||
|
- Updated dependencies in package.json: @git.zone/tsdoc -> ^1.11.4, @push.rocks/smartfs -> ^1.3.1
|
||||||
|
|
||||||
|
## 2025-12-15 - 2.12.2 - fix(cli)
|
||||||
|
noop: no changes
|
||||||
|
|
||||||
|
- No source or documentation changes detected in the diff; nothing to release.
|
||||||
|
- Package version remains unchanged at 2.12.1.
|
||||||
|
|
||||||
|
## 2025-12-15 - 2.12.1 - fix(cli)
|
||||||
|
No changes detected — no version bump required
|
||||||
|
|
||||||
|
- Current package version: 2.12.0
|
||||||
|
- No files changed in this commit
|
||||||
|
- No release or version bump necessary
|
||||||
|
|
||||||
|
## 2025-12-15 - 2.12.0 - feat(ci,test)
|
||||||
|
feat(ci/test): add test scaffold, GitLab CI, update gitea workflows and .gitignore
|
||||||
|
|
||||||
|
- Add comprehensive test/ scaffold including sample tests, test package.json, npmextra.json, test fixtures and TypeScript test sources
|
||||||
|
- Add GitLab CI pipeline (test/.gitlab-ci.yml), test-specific .npmrc, VSCode launch/settings and supporting qenv/readme files for CI/local test runs
|
||||||
|
- Update .gitea workflow YAML files to use templated placeholders and corrected container image path for CI execution
|
||||||
|
- Update .gitignore to exclude AI assistant and tooling dirs (.claude/, .serena/) and add test/.gitignore to ignore test artifacts
|
||||||
|
- Update changelog and documentation files to reflect recent formatter/commit/service changes and the new test/CI additions
|
||||||
|
|
||||||
|
## 2025-12-15 - 2.11.1 - fix(mod_format/formatters)
|
||||||
|
|
||||||
|
fix(packagejson.formatter): correctly parse scoped package dependency arguments and default to latest
|
||||||
|
|
||||||
|
- Handle scoped packages (e.g. @scope/name@version) by detecting the last '@' after the scope slash so package name and version are split correctly.
|
||||||
|
- Fallback to 'latest' when no version is provided.
|
||||||
|
- Fixes earlier incorrect splitting on every '@' which broke scoped package names.
|
||||||
|
|
||||||
|
## 2025-12-15 - 2.11.0 - feat(mod_format)
|
||||||
|
|
||||||
|
feat(mod_format): use unified diff formatter with filenames and context in BaseFormatter.displayDiff
|
||||||
|
|
||||||
|
- Replaced plugins.smartdiff.formatLineDiffForConsole(...) with plugins.smartdiff.formatUnifiedDiffForConsole(...) when both before and after are present.
|
||||||
|
- Passes originalFileName and revisedFileName as diff.path and sets context to 3 to show a unified diff with surrounding lines.
|
||||||
|
- Improves console output for multi-line diffs by using unified diff format and including file names.
|
||||||
|
|
||||||
|
## 2025-12-15 - 2.10.0 - feat(mod_format)
|
||||||
|
|
||||||
|
Refactor formatting modules to new BaseFormatter and implement concrete analyze/apply logic
|
||||||
|
|
||||||
|
- Replace generic LegacyFormatter with explicit BaseFormatter implementations for formatters: copy, gitignore, license, npmextra, packagejson, prettier, readme, templates, tsconfig (legacy.formatter.ts removed).
|
||||||
|
- Copy formatter: implemented pattern-based copying, template-preserve path handling, content equality check and planned change generation/apply.
|
||||||
|
- Gitignore formatter: canonical template with preservation of custom section when updating/creating .gitignore.
|
||||||
|
- License formatter: added runtime license check against node_modules for incompatible licenses and reporting (no file changes).
|
||||||
|
- Npmextra formatter: automatic migrations for old namespace keys to package-scoped keys and migration of npmAccessLevel -> @git.zone/cli.release.accessLevel; reformatting and interactive prompting to fill missing repo metadata.
|
||||||
|
- Package.json formatter: enforces repository/metadata, sets module type/private/license/scripts/files, ensures/updates dependencies (including fetching latest via registry), and applies pnpm overrides from assets.
|
||||||
|
- Prettier formatter: added check() to compute diffs by running Prettier and returning per-file before/after diffs.
|
||||||
|
- Readme formatter: create readme.md and readme.hints.md when missing with default content.
|
||||||
|
- Templates formatter: apply templates from templatesDir based on project type (vscode, CI, docker, website/service/wcc), compare template vs destination and create/modify files as needed; ensures dest directories exist.
|
||||||
|
- Tsconfig formatter: sets compilerOptions.baseUrl and computes path mappings from @git.zone/tspublish modules.
|
||||||
|
- General: extensive use of plugins (smartfs, path, smartnpm, smartinteract, smartobject, smartlegal), improved logging and verbose messages.
|
||||||
|
|
||||||
## 2025-12-15 - 2.9.0 - feat(format)
|
## 2025-12-15 - 2.9.0 - feat(format)
|
||||||
|
|
||||||
Add --diff option to format command to display file diffs; pass flag through CLI and show formatter diffs. Bump @git.zone/tsdoc to ^1.11.0.
|
Add --diff option to format command to display file diffs; pass flag through CLI and show formatter diffs. Bump @git.zone/tsdoc to ^1.11.0.
|
||||||
|
|
||||||
- Add a diff boolean option to mod_format to enable showing file diffs during format operations.
|
- Add a diff boolean option to mod_format to enable showing file diffs during format operations.
|
||||||
@@ -9,6 +163,7 @@ Add --diff option to format command to display file diffs; pass flag through CLI
|
|||||||
- Update dependency @git.zone/tsdoc from ^1.10.2 to ^1.11.0.
|
- Update dependency @git.zone/tsdoc from ^1.10.2 to ^1.11.0.
|
||||||
|
|
||||||
## 2025-12-15 - 2.8.0 - feat(commit)
|
## 2025-12-15 - 2.8.0 - feat(commit)
|
||||||
|
|
||||||
Add commit configuration and automatic pre-commit tests
|
Add commit configuration and automatic pre-commit tests
|
||||||
|
|
||||||
- Add CommitConfig class to manage @git.zone/cli.commit settings in npmextra.json (alwaysTest, alwaysBuild).
|
- Add CommitConfig class to manage @git.zone/cli.commit settings in npmextra.json (alwaysTest, alwaysBuild).
|
||||||
@@ -20,6 +175,7 @@ Add commit configuration and automatic pre-commit tests
|
|||||||
- Add 'gitzone config services' entry to configure services via ServiceManager.
|
- Add 'gitzone config services' entry to configure services via ServiceManager.
|
||||||
|
|
||||||
## 2025-12-14 - 2.7.0 - feat(mod_format)
|
## 2025-12-14 - 2.7.0 - feat(mod_format)
|
||||||
|
|
||||||
Add check-only formatting with interactive diff preview; make formatting default to dry-run and extend formatting API
|
Add check-only formatting with interactive diff preview; make formatting default to dry-run and extend formatting API
|
||||||
|
|
||||||
- Add BaseFormatter.check(), displayDiff() and displayAllDiffs() to compute and render diffs without applying changes.
|
- Add BaseFormatter.check(), displayDiff() and displayAllDiffs() to compute and render diffs without applying changes.
|
||||||
@@ -32,6 +188,7 @@ Add check-only formatting with interactive diff preview; make formatting default
|
|||||||
- Bump dependency @push.rocks/smartdiff to ^1.1.0.
|
- Bump dependency @push.rocks/smartdiff to ^1.1.0.
|
||||||
|
|
||||||
## 2025-12-14 - 2.6.1 - fix(npmextra)
|
## 2025-12-14 - 2.6.1 - fix(npmextra)
|
||||||
|
|
||||||
Normalize npmextra.json: move tsdoc legal entry and reposition @git.zone/cli configuration
|
Normalize npmextra.json: move tsdoc legal entry and reposition @git.zone/cli configuration
|
||||||
|
|
||||||
- Move TSDoc legal text into a top-level "tsdoc.legal" property in npmextra.json
|
- Move TSDoc legal text into a top-level "tsdoc.legal" property in npmextra.json
|
||||||
@@ -40,6 +197,7 @@ Normalize npmextra.json: move tsdoc legal entry and reposition @git.zone/cli con
|
|||||||
- Pure configuration change (JSON structure) — no functional code changes
|
- Pure configuration change (JSON structure) — no functional code changes
|
||||||
|
|
||||||
## 2025-12-14 - 2.6.0 - feat(mod_commit)
|
## 2025-12-14 - 2.6.0 - feat(mod_commit)
|
||||||
|
|
||||||
Add execution plan output to commit command
|
Add execution plan output to commit command
|
||||||
|
|
||||||
- Print an execution plan at the start of the commit flow (shows active options and planned steps)
|
- Print an execution plan at the start of the commit flow (shows active options and planned steps)
|
||||||
@@ -48,6 +206,7 @@ Add execution plan output to commit command
|
|||||||
- Execution plan reflects flags: auto-accept (-y), push (-p), build (-b), release (-r), --format, and target registries
|
- Execution plan reflects flags: auto-accept (-y), push (-p), build (-b), release (-r), --format, and target registries
|
||||||
|
|
||||||
## 2025-12-14 - 2.5.0 - feat(mod_standard)
|
## 2025-12-14 - 2.5.0 - feat(mod_standard)
|
||||||
|
|
||||||
Add interactive main menu and help to standard CLI module; route commands via dynamic imports
|
Add interactive main menu and help to standard CLI module; route commands via dynamic imports
|
||||||
|
|
||||||
- Introduce interactive CLI menu using @push.rocks/smartinteract to prompt user for actions.
|
- Introduce interactive CLI menu using @push.rocks/smartinteract to prompt user for actions.
|
||||||
@@ -56,6 +215,7 @@ Add interactive main menu and help to standard CLI module; route commands via dy
|
|||||||
- Remove previous static template listing and logger.warn placeholder.
|
- Remove previous static template listing and logger.warn placeholder.
|
||||||
|
|
||||||
## 2025-12-14 - 2.4.0 - feat(cli)
|
## 2025-12-14 - 2.4.0 - feat(cli)
|
||||||
|
|
||||||
Add optional build step to release flow and auto-format npmextra config when registries change
|
Add optional build step to release flow and auto-format npmextra config when registries change
|
||||||
|
|
||||||
- Introduce a --build/-b flag in the commit/release flow to run 'pnpm build' before pushing/releases
|
- Introduce a --build/-b flag in the commit/release flow to run 'pnpm build' before pushing/releases
|
||||||
@@ -66,6 +226,7 @@ Add optional build step to release flow and auto-format npmextra config when reg
|
|||||||
- Add npmextra registry config entry (https://verdaccio.lossless.digital) to npmextra.json
|
- Add npmextra registry config entry (https://verdaccio.lossless.digital) to npmextra.json
|
||||||
|
|
||||||
## 2025-12-14 - 2.3.0 - feat(config)
|
## 2025-12-14 - 2.3.0 - feat(config)
|
||||||
|
|
||||||
Add interactive menu and help to config command, handle unknown commands, and bump dependencies
|
Add interactive menu and help to config command, handle unknown commands, and bump dependencies
|
||||||
|
|
||||||
- When running the 'config' command with no arguments, show an interactive menu (via SmartInteract) to choose actions (show, add, remove, clear, access, help) instead of defaulting to 'show'.
|
- When running the 'config' command with no arguments, show an interactive menu (via SmartInteract) to choose actions (show, add, remove, clear, access, help) instead of defaulting to 'show'.
|
||||||
@@ -74,6 +235,7 @@ Add interactive menu and help to config command, handle unknown commands, and bu
|
|||||||
- Update dependency: @push.rocks/smartjson -> ^6.0.0.
|
- Update dependency: @push.rocks/smartjson -> ^6.0.0.
|
||||||
|
|
||||||
## 2025-12-04 - 2.2.1 - fix(commit)
|
## 2025-12-04 - 2.2.1 - fix(commit)
|
||||||
|
|
||||||
Prevent auto-accept for BREAKING CHANGE commits; require manual confirmation and warn when --yes is used
|
Prevent auto-accept for BREAKING CHANGE commits; require manual confirmation and warn when --yes is used
|
||||||
|
|
||||||
- Do not auto-accept AI commit recommendations when the suggested change is a BREAKING CHANGE (major bump).
|
- Do not auto-accept AI commit recommendations when the suggested change is a BREAKING CHANGE (major bump).
|
||||||
@@ -82,6 +244,7 @@ Prevent auto-accept for BREAKING CHANGE commits; require manual confirmation and
|
|||||||
- Introduced isBreakingChange and canAutoAccept flags to centralize the auto-accept logic.
|
- Introduced isBreakingChange and canAutoAccept flags to centralize the auto-accept logic.
|
||||||
|
|
||||||
## 2025-12-02 - 2.2.0 - feat(services)
|
## 2025-12-02 - 2.2.0 - feat(services)
|
||||||
|
|
||||||
Improve services manager and configuration; switch test templates to @git.zone/tstest; bump dev dependencies and update docs
|
Improve services manager and configuration; switch test templates to @git.zone/tstest; bump dev dependencies and update docs
|
||||||
|
|
||||||
- services: Add robust ServiceConfiguration (creates .nogit/env.json with sane defaults, syncs ports from existing Docker containers, validates and can reconfigure ports)
|
- services: Add robust ServiceConfiguration (creates .nogit/env.json with sane defaults, syncs ports from existing Docker containers, validates and can reconfigure ports)
|
||||||
@@ -92,6 +255,7 @@ Improve services manager and configuration; switch test templates to @git.zone/t
|
|||||||
- docs: README updates — add issue reporting/security section, AI-powered commit recommendation notes, and clarify trademark/legal wording
|
- docs: README updates — add issue reporting/security section, AI-powered commit recommendation notes, and clarify trademark/legal wording
|
||||||
|
|
||||||
## 2025-11-29 - 2.1.0 - feat(mod_services)
|
## 2025-11-29 - 2.1.0 - feat(mod_services)
|
||||||
|
|
||||||
Add global service registry and global commands for managing project containers
|
Add global service registry and global commands for managing project containers
|
||||||
|
|
||||||
- Introduce GlobalRegistry class to track registered projects, their containers, ports and last activity (ts/mod_services/classes.globalregistry.ts)
|
- Introduce GlobalRegistry class to track registered projects, their containers, ports and last activity (ts/mod_services/classes.globalregistry.ts)
|
||||||
@@ -101,30 +265,34 @@ Add global service registry and global commands for managing project containers
|
|||||||
- Bump dependency @push.rocks/smartfile to ^13.1.0 in package.json
|
- Bump dependency @push.rocks/smartfile to ^13.1.0 in package.json
|
||||||
|
|
||||||
## 2025-11-27 - 2.0.0 - BREAKING CHANGE(core)
|
## 2025-11-27 - 2.0.0 - BREAKING CHANGE(core)
|
||||||
|
|
||||||
Migrate filesystem to smartfs (async) and add Elasticsearch service support; refactor format/commit/meta modules
|
Migrate filesystem to smartfs (async) and add Elasticsearch service support; refactor format/commit/meta modules
|
||||||
|
|
||||||
- Replace @push.rocks/smartfile usage with @push.rocks/smartfs across the codebase; all filesystem operations are now async (SmartFs.file(...).read()/write(), SmartFs.directory(...).list()/create()/delete(), etc.)
|
- Replace @push.rocks/smartfile usage with @push.rocks/smartfs across the codebase; all filesystem operations are now async (SmartFs.file(...).read()/write(), SmartFs.directory(...).list()/create()/delete(), etc.)
|
||||||
- Convert formerly synchronous helpers and APIs to async (notable: detectProjectType, getProjectName, readCurrentVersion and related version bumping logic). Callers updated accordingly.
|
- Convert formerly synchronous helpers and APIs to async (notable: detectProjectType, getProjectName, readCurrentVersion and related version bumping logic). Callers updated accordingly.
|
||||||
- Add Elasticsearch support to services: new config fields (ELASTICSEARCH_*), Docker run/start/stop/logs/status handling, and ELASTICSEARCH_URL in service configuration.
|
- Add Elasticsearch support to services: new config fields (ELASTICSEARCH\_\*), Docker run/start/stop/logs/status handling, and ELASTICSEARCH_URL in service configuration.
|
||||||
- Refactor formatting subsystem: cache and rollback/backup systems removed/disabled for stability, format planner execution simplified (sequential), diff/stats reporting updated to use smartfs.
|
- Refactor formatting subsystem: cache and rollback/backup systems removed/disabled for stability, format planner execution simplified (sequential), diff/stats reporting updated to use smartfs.
|
||||||
- Update package.json dependencies: bump @git.zone/tsbuild, tsrun, tstest; upgrade @push.rocks/smartfile to v13 and add @push.rocks/smartfs dependency; update @types/node.
|
- Update package.json dependencies: bump @git.zone/tsbuild, tsrun, tstest; upgrade @push.rocks/smartfile to v13 and add @push.rocks/smartfs dependency; update @types/node.
|
||||||
- Update commit flow and changelog generation to use smartfs for reading/writing files and to await version/branch detection where necessary.
|
- Update commit flow and changelog generation to use smartfs for reading/writing files and to await version/branch detection where necessary.
|
||||||
- Expose a SmartFs instance via plugins and adjust all mod.* plugin files to import/use smartfs where required.
|
- Expose a SmartFs instance via plugins and adjust all mod.\* plugin files to import/use smartfs where required.
|
||||||
- Breaking change: Public and internal APIs that previously used synchronous smartfile APIs are now asynchronous. Consumers and scripts must await these functions and use the new smartfs API.
|
- Breaking change: Public and internal APIs that previously used synchronous smartfile APIs are now asynchronous. Consumers and scripts must await these functions and use the new smartfs API.
|
||||||
|
|
||||||
## 2025-11-17 - 1.21.5 - fix(tsconfig)
|
## 2025-11-17 - 1.21.5 - fix(tsconfig)
|
||||||
|
|
||||||
Remove emitDecoratorMetadata from tsconfig template
|
Remove emitDecoratorMetadata from tsconfig template
|
||||||
|
|
||||||
- Removed the "emitDecoratorMetadata" compiler option from assets/templates/tsconfig_update/tsconfig.json
|
- Removed the "emitDecoratorMetadata" compiler option from assets/templates/tsconfig_update/tsconfig.json
|
||||||
- This updates the tsconfig template to avoid emitting decorator metadata when targeting ES2022
|
- This updates the tsconfig template to avoid emitting decorator metadata when targeting ES2022
|
||||||
|
|
||||||
## 2025-11-17 - 1.21.4 - fix(tsconfig template)
|
## 2025-11-17 - 1.21.4 - fix(tsconfig template)
|
||||||
|
|
||||||
Remove experimentalDecorators and useDefineForClassFields from tsconfig template
|
Remove experimentalDecorators and useDefineForClassFields from tsconfig template
|
||||||
|
|
||||||
- Removed experimentalDecorators option from assets/templates/tsconfig_update/tsconfig.json
|
- Removed experimentalDecorators option from assets/templates/tsconfig_update/tsconfig.json
|
||||||
- Removed useDefineForClassFields option from assets/templates/tsconfig_update/tsconfig.json
|
- Removed useDefineForClassFields option from assets/templates/tsconfig_update/tsconfig.json
|
||||||
|
|
||||||
## 2025-11-17 - 1.21.3 - fix(assets/templates/multienv)
|
## 2025-11-17 - 1.21.3 - fix(assets/templates/multienv)
|
||||||
|
|
||||||
Remove unused Bun configuration template (assets/templates/multienv/bunfig.toml)
|
Remove unused Bun configuration template (assets/templates/multienv/bunfig.toml)
|
||||||
|
|
||||||
- Deleted assets/templates/multienv/bunfig.toml which previously provided Bun TypeScript decorator configuration
|
- Deleted assets/templates/multienv/bunfig.toml which previously provided Bun TypeScript decorator configuration
|
||||||
@@ -132,17 +300,20 @@ Remove unused Bun configuration template (assets/templates/multienv/bunfig.toml)
|
|||||||
- No functional code changes; removes an unused asset file
|
- No functional code changes; removes an unused asset file
|
||||||
|
|
||||||
## 2025-11-17 - 1.21.2 - fix(templates/multienv)
|
## 2025-11-17 - 1.21.2 - fix(templates/multienv)
|
||||||
|
|
||||||
Disable useDefineForClassFields in multienv TypeScript configs to ensure decorator compatibility
|
Disable useDefineForClassFields in multienv TypeScript configs to ensure decorator compatibility
|
||||||
|
|
||||||
- Set useDefineForClassFields = false in assets/templates/multienv/bunfig.toml to keep Bun's transpiler compatible with decorator usage
|
- Set useDefineForClassFields = false in assets/templates/multienv/bunfig.toml to keep Bun's transpiler compatible with decorator usage
|
||||||
- Set "useDefineForClassFields": false in assets/templates/multienv/deno.json to ensure Deno/TypeScript compiler emits class fields compatible with decorators
|
- Set "useDefineForClassFields": false in assets/templates/multienv/deno.json to ensure Deno/TypeScript compiler emits class fields compatible with decorators
|
||||||
|
|
||||||
## 2025-11-17 - 1.21.1 - fix(templates.multienv)
|
## 2025-11-17 - 1.21.1 - fix(templates.multienv)
|
||||||
|
|
||||||
Enable checkJs in multienv Deno template to enable JS type checking
|
Enable checkJs in multienv Deno template to enable JS type checking
|
||||||
|
|
||||||
- Added "checkJs": true to compilerOptions in assets/templates/multienv/deno.json to enable JavaScript type checking for the Deno multienv template
|
- Added "checkJs": true to compilerOptions in assets/templates/multienv/deno.json to enable JavaScript type checking for the Deno multienv template
|
||||||
|
|
||||||
## 2025-11-17 - 1.21.0 - feat(multienv)
|
## 2025-11-17 - 1.21.0 - feat(multienv)
|
||||||
|
|
||||||
Add multi-env templates enabling TypeScript decorators for Bun and Deno; rename npmextra config key to szci
|
Add multi-env templates enabling TypeScript decorators for Bun and Deno; rename npmextra config key to szci
|
||||||
|
|
||||||
- Added assets/templates/multienv/bunfig.toml to enable Bun TypeScript transpiler experimentalDecorators
|
- Added assets/templates/multienv/bunfig.toml to enable Bun TypeScript transpiler experimentalDecorators
|
||||||
@@ -150,6 +321,7 @@ Add multi-env templates enabling TypeScript decorators for Bun and Deno; rename
|
|||||||
- Updated npmextra.json: renamed top-level config key from "npmci" to "szci" (keeps npmGlobalTools, npmAccessLevel and npmRegistryUrl unchanged)
|
- Updated npmextra.json: renamed top-level config key from "npmci" to "szci" (keeps npmGlobalTools, npmAccessLevel and npmRegistryUrl unchanged)
|
||||||
|
|
||||||
## 2025-11-06 - 1.20.0 - feat(commit)
|
## 2025-11-06 - 1.20.0 - feat(commit)
|
||||||
|
|
||||||
Add non-interactive --yes (-y) flag to commit command to auto-accept AI recommendations and optionally push with -p
|
Add non-interactive --yes (-y) flag to commit command to auto-accept AI recommendations and optionally push with -p
|
||||||
|
|
||||||
- Add -y / --yes flag to gitzone commit to auto-accept AI-generated commit recommendations without interactive prompts
|
- Add -y / --yes flag to gitzone commit to auto-accept AI-generated commit recommendations without interactive prompts
|
||||||
@@ -159,6 +331,7 @@ Add non-interactive --yes (-y) flag to commit command to auto-accept AI recommen
|
|||||||
- Updated CLI usage and documentation (readme.hints.md) to document the new flags
|
- Updated CLI usage and documentation (readme.hints.md) to document the new flags
|
||||||
|
|
||||||
## 2025-11-05 - 1.19.9 - fix(mod_commit)
|
## 2025-11-05 - 1.19.9 - fix(mod_commit)
|
||||||
|
|
||||||
Refactor version bumping to a unified implementation for npm and Deno; remove npm-exec based helpers and add file-based version readers/updaters to avoid npm warning pollution
|
Refactor version bumping to a unified implementation for npm and Deno; remove npm-exec based helpers and add file-based version readers/updaters to avoid npm warning pollution
|
||||||
|
|
||||||
- Removed legacy npm/deno-specific helpers (bumpNpmVersion, syncVersionToDenoJson, bumpDenoVersion) that relied on executing npm and caused warning pollution
|
- Removed legacy npm/deno-specific helpers (bumpNpmVersion, syncVersionToDenoJson, bumpDenoVersion) that relied on executing npm and caused warning pollution
|
||||||
@@ -169,39 +342,46 @@ Refactor version bumping to a unified implementation for npm and Deno; remove np
|
|||||||
- Benefits: no npm warning pollution in deno.json, simpler git history, consistent behavior across project types
|
- Benefits: no npm warning pollution in deno.json, simpler git history, consistent behavior across project types
|
||||||
|
|
||||||
## 2025-11-04 - 1.19.8 - fix(package.json)
|
## 2025-11-04 - 1.19.8 - fix(package.json)
|
||||||
|
|
||||||
Bump @git.zone/tsdoc dependency to ^1.9.2
|
Bump @git.zone/tsdoc dependency to ^1.9.2
|
||||||
|
|
||||||
- Updated dependency @git.zone/tsdoc from ^1.9.1 to ^1.9.2 in package.json
|
- Updated dependency @git.zone/tsdoc from ^1.9.1 to ^1.9.2 in package.json
|
||||||
|
|
||||||
## 2025-11-04 - 1.19.7 - fix(dependencies)
|
## 2025-11-04 - 1.19.7 - fix(dependencies)
|
||||||
|
|
||||||
Bump @git.zone/tsdoc to ^1.9.1
|
Bump @git.zone/tsdoc to ^1.9.1
|
||||||
|
|
||||||
- Updated package.json dependency @git.zone/tsdoc from ^1.9.0 to ^1.9.1
|
- Updated package.json dependency @git.zone/tsdoc from ^1.9.0 to ^1.9.1
|
||||||
|
|
||||||
## 2025-11-04 - 1.19.6 - fix(cli)
|
## 2025-11-04 - 1.19.6 - fix(cli)
|
||||||
|
|
||||||
Bump @git.zone/tsdoc dependency to ^1.9.0
|
Bump @git.zone/tsdoc dependency to ^1.9.0
|
||||||
|
|
||||||
- Updated dependency @git.zone/tsdoc from ^1.8.3 to ^1.9.0 in package.json
|
- Updated dependency @git.zone/tsdoc from ^1.8.3 to ^1.9.0 in package.json
|
||||||
|
|
||||||
## 2025-11-04 - 1.19.5 - fix(cli)
|
## 2025-11-04 - 1.19.5 - fix(cli)
|
||||||
|
|
||||||
Bump @git.zone/tsdoc to ^1.8.3 and add local .claude settings for allowed permissions
|
Bump @git.zone/tsdoc to ^1.8.3 and add local .claude settings for allowed permissions
|
||||||
|
|
||||||
- Updated dependency @git.zone/tsdoc from ^1.8.2 to ^1.8.3
|
- Updated dependency @git.zone/tsdoc from ^1.8.2 to ^1.8.3
|
||||||
- Added .claude/settings.local.json to declare allowed permissions for local tooling (Bash commands, Docker, npm, WebFetch and MCP actions)
|
- Added .claude/settings.local.json to declare allowed permissions for local tooling (Bash commands, Docker, npm, WebFetch and MCP actions)
|
||||||
|
|
||||||
## 2025-11-03 - 1.19.3 - fix(tsdoc)
|
## 2025-11-03 - 1.19.3 - fix(tsdoc)
|
||||||
|
|
||||||
Bump @git.zone/tsdoc to ^1.8.0 and add .claude local settings
|
Bump @git.zone/tsdoc to ^1.8.0 and add .claude local settings
|
||||||
|
|
||||||
- Upgrade dependency @git.zone/tsdoc from ^1.6.1 to ^1.8.0 in package.json
|
- Upgrade dependency @git.zone/tsdoc from ^1.6.1 to ^1.8.0 in package.json
|
||||||
- Add .claude/settings.local.json for local assistant permissions/configuration
|
- Add .claude/settings.local.json for local assistant permissions/configuration
|
||||||
|
|
||||||
## 2025-11-03 - 1.19.2 - fix(tsdoc)
|
## 2025-11-03 - 1.19.2 - fix(tsdoc)
|
||||||
|
|
||||||
Bump @git.zone/tsdoc to ^1.6.1 and add .claude/settings.local.json
|
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
|
- Update dependency @git.zone/tsdoc from ^1.6.0 to ^1.6.1
|
||||||
- Add .claude/settings.local.json to include local Claude settings/permissions
|
- Add .claude/settings.local.json to include local Claude settings/permissions
|
||||||
|
|
||||||
## 2025-11-02 - 1.19.1 - fix(dependencies)
|
## 2025-11-02 - 1.19.1 - fix(dependencies)
|
||||||
|
|
||||||
Bump dependencies and add local Claude settings
|
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
|
- Bump devDependencies: @git.zone/tsbuild -> ^2.7.1, @git.zone/tsrun -> ^1.6.2, @git.zone/tstest -> ^2.7.0
|
||||||
@@ -209,6 +389,7 @@ Bump dependencies and add local Claude settings
|
|||||||
- Add .claude/settings.local.json (local project permissions/settings file)
|
- Add .claude/settings.local.json (local project permissions/settings file)
|
||||||
|
|
||||||
## 2025-10-23 - 1.19.0 - feat(mod_commit)
|
## 2025-10-23 - 1.19.0 - feat(mod_commit)
|
||||||
|
|
||||||
Add CLI UI helpers and improve commit workflow with progress, recommendations and summary
|
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).
|
- 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).
|
||||||
@@ -217,6 +398,7 @@ Add CLI UI helpers and improve commit workflow with progress, recommendations an
|
|||||||
- Add .claude/settings.local.json: local permissions configuration for development tooling.
|
- Add .claude/settings.local.json: local permissions configuration for development tooling.
|
||||||
|
|
||||||
## 2025-10-23 - 1.18.9 - fix(mod_commit)
|
## 2025-10-23 - 1.18.9 - fix(mod_commit)
|
||||||
|
|
||||||
Stage and commit deno.json when bumping/syncing versions and create/update git tags
|
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
|
- 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
|
||||||
@@ -224,6 +406,7 @@ Stage and commit deno.json when bumping/syncing versions and create/update git t
|
|||||||
- Added informative logger messages after creating commits and tags
|
- Added informative logger messages after creating commits and tags
|
||||||
|
|
||||||
## 2025-10-23 - 1.18.8 - fix(mod_commit)
|
## 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
|
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()
|
- Add mod_commit/mod.helpers.ts with utilities: detectCurrentBranch(), detectProjectType(), bumpProjectVersion(), bumpDenoVersion(), bumpNpmVersion(), syncVersionToDenoJson(), and calculateNewVersion()
|
||||||
@@ -233,12 +416,14 @@ Improve commit workflow: detect project type and current branch; add robust vers
|
|||||||
- Add local Claude settings file (.claude/settings.local.json) (editor/CI config) — no code behavior change but included in diff
|
- 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)
|
## 2025-09-07 - 1.18.7 - fix(claude)
|
||||||
|
|
||||||
Add .claude local settings to whitelist dev tool permissions
|
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).
|
- 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.
|
- Disable enableAllProjectMcpServers (set to false) to limit automatic project MCP server usage.
|
||||||
|
|
||||||
## 2025-09-07 - 1.18.6 - fix(deps)
|
## 2025-09-07 - 1.18.6 - fix(deps)
|
||||||
|
|
||||||
Bump dependency versions and add local Claude settings
|
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 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
|
||||||
@@ -246,6 +431,7 @@ Bump dependency versions and add local Claude settings
|
|||||||
- Added .claude/settings.local.json to configure local Claude permissions/settings
|
- Added .claude/settings.local.json to configure local Claude permissions/settings
|
||||||
|
|
||||||
## 2025-08-17 - 1.18.5 - fix(dependencies)
|
## 2025-08-17 - 1.18.5 - fix(dependencies)
|
||||||
|
|
||||||
Bump smartshell and smartscaf versions; add .claude local settings
|
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/smartshell from ^3.2.4 to ^3.3.0 in package.json
|
||||||
@@ -253,6 +439,7 @@ Bump smartshell and smartscaf versions; add .claude local settings
|
|||||||
- Add .claude/settings.local.json for local assistant permissions/configuration
|
- Add .claude/settings.local.json for local assistant permissions/configuration
|
||||||
|
|
||||||
## 2025-08-17 - 1.18.4 - fix(cli)
|
## 2025-08-17 - 1.18.4 - fix(cli)
|
||||||
|
|
||||||
Update dependencies, add local Claude settings, and update gitignore template
|
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
|
- 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
|
||||||
@@ -261,6 +448,7 @@ Update dependencies, add local Claude settings, and update gitignore template
|
|||||||
- Add pnpm onlyBuiltDependencies entries: esbuild and mongodb-memory-server
|
- Add pnpm onlyBuiltDependencies entries: esbuild and mongodb-memory-server
|
||||||
|
|
||||||
## 2025-08-16 - 1.18.3 - fix(services)
|
## 2025-08-16 - 1.18.3 - fix(services)
|
||||||
|
|
||||||
Simplify S3 endpoint handling in ServiceConfiguration to store host only
|
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.
|
- S3_ENDPOINT now stores the raw host (e.g. 'localhost') instead of a full URL with protocol and port.
|
||||||
@@ -269,6 +457,7 @@ Simplify S3 endpoint handling in ServiceConfiguration to store 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.
|
- 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)
|
## 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
|
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
|
- Ensure AiDoc is stopped after building commit recommendation to avoid resource leaks
|
||||||
@@ -280,6 +469,7 @@ Improve services and commit flow: stop AiDoc, use silent docker inspect, sync po
|
|||||||
- Add local Claude settings file (.claude/settings.local.json) with development permissions
|
- Add local Claude settings file (.claude/settings.local.json) with development permissions
|
||||||
|
|
||||||
## 2025-08-16 - 1.18.0 - feat(services)
|
## 2025-08-16 - 1.18.0 - feat(services)
|
||||||
|
|
||||||
Add Docker port mapping sync and reconfigure workflow for local services
|
Add Docker port mapping sync and reconfigure workflow for local services
|
||||||
|
|
||||||
- Add getPortMappings to DockerContainer to extract port bindings from docker inspect output
|
- Add getPortMappings to DockerContainer to extract port bindings from docker inspect output
|
||||||
@@ -292,6 +482,7 @@ Add Docker port mapping sync and reconfigure workflow for local services
|
|||||||
- Add .claude/settings.local.json (local permissions config) to repository
|
- Add .claude/settings.local.json (local permissions config) to repository
|
||||||
|
|
||||||
## 2025-08-15 - 1.17.5 - fix(services)
|
## 2025-08-15 - 1.17.5 - fix(services)
|
||||||
|
|
||||||
Update S3 credentials naming and add S3_ENDPOINT/S3_USESSL support for improved MinIO integration
|
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
|
- Replaced S3_USER/S3_PASS with S3_ACCESSKEY/S3_SECRETKEY in ServiceConfiguration
|
||||||
@@ -301,6 +492,7 @@ Update S3 credentials naming and add S3_ENDPOINT/S3_USESSL support for improved
|
|||||||
- Added .claude/settings.local.json for local permission settings
|
- Added .claude/settings.local.json for local permission settings
|
||||||
|
|
||||||
## 2025-08-15 - 1.17.4 - fix(services)
|
## 2025-08-15 - 1.17.4 - fix(services)
|
||||||
|
|
||||||
Update S3 credentials naming and add S3_ENDPOINT/S3_USESSL support for improved MinIO integration
|
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
|
- Replaced S3_USER/S3_PASS with S3_ACCESSKEY/S3_SECRETKEY in ServiceConfiguration
|
||||||
@@ -309,12 +501,14 @@ Update S3 credentials naming and add S3_ENDPOINT/S3_USESSL support for improved
|
|||||||
- Updated ServiceManager to use new credential names in container setup and logging
|
- Updated ServiceManager to use new credential names in container setup and logging
|
||||||
|
|
||||||
## 2025-08-15 - 1.17.3 - fix(serviceconfig)
|
## 2025-08-15 - 1.17.3 - fix(serviceconfig)
|
||||||
|
|
||||||
Update service configuration to include dynamic MongoDB connection string and add local permissions settings
|
Update service configuration to include dynamic MongoDB connection string and add local permissions settings
|
||||||
|
|
||||||
- Added .claude/settings.local.json for local permissions configuration
|
- Added .claude/settings.local.json for local permissions configuration
|
||||||
- Updated ServiceConfiguration to compute and update MONGODB_URL based on current config values
|
- Updated ServiceConfiguration to compute and update MONGODB_URL based on current config values
|
||||||
|
|
||||||
## 2025-08-15 - 1.17.2 - fix(ci-test-services)
|
## 2025-08-15 - 1.17.2 - fix(ci-test-services)
|
||||||
|
|
||||||
Update CI/CD configurations, test settings, and Docker service for MongoDB.
|
Update CI/CD configurations, test settings, and Docker service for MongoDB.
|
||||||
|
|
||||||
- Add .claude/settings.local.json with updated permission settings
|
- Add .claude/settings.local.json with updated permission settings
|
||||||
@@ -323,6 +517,7 @@ Update CI/CD configurations, test settings, and Docker service for MongoDB.
|
|||||||
- Fix MongoDB Docker container command by adding '--bind_ip_all' for proper network binding
|
- Fix MongoDB Docker container command by adding '--bind_ip_all' for proper network binding
|
||||||
|
|
||||||
## 2025-08-15 - 1.17.1 - fix(services)
|
## 2025-08-15 - 1.17.1 - fix(services)
|
||||||
|
|
||||||
Improve services module logging and enhance MongoDB Compass integration
|
Improve services module logging and enhance MongoDB Compass integration
|
||||||
|
|
||||||
- Refactored services module to use centralized logger from gitzone.logging.ts
|
- Refactored services module to use centralized logger from gitzone.logging.ts
|
||||||
@@ -331,6 +526,7 @@ Improve services module logging and enhance MongoDB Compass integration
|
|||||||
- Consistent logging across all service commands
|
- 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
|
||||||
|
|
||||||
- Implemented `gitzone services` command for managing local development services
|
- Implemented `gitzone services` command for managing local development services
|
||||||
@@ -344,6 +540,7 @@ Add comprehensive development services management for MongoDB and MinIO containe
|
|||||||
- Interactive confirmations for destructive operations
|
- Interactive confirmations for destructive operations
|
||||||
|
|
||||||
## 2025-08-08 - 1.16.10 - fix(format)
|
## 2025-08-08 - 1.16.10 - fix(format)
|
||||||
|
|
||||||
Improve concurrency control in caching and rollback modules, refine gitignore custom section handling, and enhance Prettier file processing.
|
Improve concurrency control in caching and rollback modules, refine gitignore custom section handling, and enhance Prettier file processing.
|
||||||
|
|
||||||
- Added mutex locking in ChangeCache and RollbackManager to prevent race conditions during manifest updates
|
- Added mutex locking in ChangeCache and RollbackManager to prevent race conditions during manifest updates
|
||||||
|
|||||||
+17
-23
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/cli",
|
"name": "@git.zone/cli",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "2.9.2",
|
"version": "2.13.16",
|
||||||
"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,46 +57,40 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/gitzone/private/gitzone#readme",
|
"homepage": "https://gitlab.com/gitzone/private/gitzone#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^4.0.2",
|
"@git.zone/tsbuild": "^4.3.0",
|
||||||
"@git.zone/tsrun": "^2.0.1",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@git.zone/tstest": "^3.1.3",
|
"@git.zone/tstest": "^3.3.2",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@types/node": "^25.4.0"
|
||||||
"@push.rocks/smartinteract": "^2.0.16",
|
|
||||||
"@push.rocks/smartnetwork": "^4.4.0",
|
|
||||||
"@push.rocks/smartshell": "^3.3.0",
|
|
||||||
"@types/node": "^25.0.2"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@git.zone/tsdoc": "^1.11.3",
|
"@git.zone/tsdoc": "^2.0.0",
|
||||||
"@git.zone/tspublish": "^1.10.3",
|
"@git.zone/tspublish": "^1.11.2",
|
||||||
"@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/lik": "^6.2.2",
|
|
||||||
"@push.rocks/npmextra": "^5.3.3",
|
|
||||||
"@push.rocks/projectinfo": "^5.0.2",
|
"@push.rocks/projectinfo": "^5.0.2",
|
||||||
"@push.rocks/smartcli": "^4.0.19",
|
"@push.rocks/smartcli": "^4.0.20",
|
||||||
|
"@push.rocks/smartconfig": "^6.0.1",
|
||||||
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartdiff": "^1.1.0",
|
"@push.rocks/smartdiff": "^1.1.0",
|
||||||
"@push.rocks/smartfile": "^13.1.2",
|
"@push.rocks/smartfile": "^13.1.2",
|
||||||
"@push.rocks/smartfs": "^1.2.0",
|
"@push.rocks/smartfs": "^1.5.0",
|
||||||
"@push.rocks/smartgulp": "^3.0.4",
|
"@push.rocks/smartinteract": "^2.0.16",
|
||||||
"@push.rocks/smartjson": "^6.0.0",
|
"@push.rocks/smartjson": "^6.0.0",
|
||||||
"@push.rocks/smartlegal": "^1.0.27",
|
"@push.rocks/smartlegal": "^1.0.27",
|
||||||
"@push.rocks/smartlog": "^3.1.10",
|
"@push.rocks/smartlog": "^3.2.1",
|
||||||
"@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/smartnetwork": "^4.4.0",
|
||||||
"@push.rocks/smartnpm": "^2.0.6",
|
"@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": "^6.0.0",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartscaf": "^4.0.19",
|
"@push.rocks/smartscaf": "^4.0.21",
|
||||||
"@push.rocks/smartstream": "^3.2.5",
|
"@push.rocks/smartshell": "^3.3.7",
|
||||||
"@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",
|
"prettier": "^3.8.1"
|
||||||
"prettier": "^3.7.4",
|
|
||||||
"through2": "^4.0.2"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"ts/**/*",
|
"ts/**/*",
|
||||||
@@ -107,7 +101,7 @@
|
|||||||
"dist_ts_web/**/*",
|
"dist_ts_web/**/*",
|
||||||
"assets/**/*",
|
"assets/**/*",
|
||||||
"cli.js",
|
"cli.js",
|
||||||
"npmextra.json",
|
".smartconfig.json",
|
||||||
"readme.md"
|
"readme.md"
|
||||||
],
|
],
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|||||||
Generated
+865
-1611
File diff suppressed because it is too large
Load Diff
+14
-6
@@ -96,6 +96,7 @@ The format module is responsible for project standardization:
|
|||||||
The commit module's version bumping has been refactored to eliminate npm command dependencies:
|
The commit module's version bumping has been refactored to eliminate npm command dependencies:
|
||||||
|
|
||||||
**Changes:**
|
**Changes:**
|
||||||
|
|
||||||
- Removed `bumpNpmVersion()` - was causing npm warnings to pollute deno.json
|
- Removed `bumpNpmVersion()` - was causing npm warnings to pollute deno.json
|
||||||
- Removed `syncVersionToDenoJson()` - no longer needed with unified approach
|
- Removed `syncVersionToDenoJson()` - no longer needed with unified approach
|
||||||
- Removed separate `bumpDenoVersion()` - replaced by unified implementation
|
- Removed separate `bumpDenoVersion()` - replaced by unified implementation
|
||||||
@@ -104,6 +105,7 @@ The commit module's version bumping has been refactored to eliminate npm command
|
|||||||
- Unified `bumpProjectVersion()` - handles npm/deno/both with single clean code path
|
- Unified `bumpProjectVersion()` - handles npm/deno/both with single clean code path
|
||||||
|
|
||||||
**Benefits:**
|
**Benefits:**
|
||||||
|
|
||||||
- No npm warning pollution in version fields
|
- No npm warning pollution in version fields
|
||||||
- Full control over version bumping process
|
- Full control over version bumping process
|
||||||
- Simpler git history (no amending, no force-tagging)
|
- Simpler git history (no amending, no force-tagging)
|
||||||
@@ -115,11 +117,13 @@ The commit module's version bumping has been refactored to eliminate npm command
|
|||||||
The commit module now supports `-y/--yes` flag for non-interactive commits:
|
The commit module now supports `-y/--yes` flag for non-interactive commits:
|
||||||
|
|
||||||
**Usage:**
|
**Usage:**
|
||||||
|
|
||||||
- `gitzone commit -y` - Auto-accepts AI recommendations without prompts
|
- `gitzone commit -y` - Auto-accepts AI recommendations without prompts
|
||||||
- `gitzone commit -yp` - Auto-accepts and pushes to origin
|
- `gitzone commit -yp` - Auto-accepts and pushes to origin
|
||||||
- Separate `-p/--push` flag controls push behavior
|
- Separate `-p/--push` flag controls push behavior
|
||||||
|
|
||||||
**Implementation:**
|
**Implementation:**
|
||||||
|
|
||||||
- Creates AnswerBucket programmatically when `-y` flag detected
|
- Creates AnswerBucket programmatically when `-y` flag detected
|
||||||
- Preserves all UI output for transparency
|
- Preserves all UI output for transparency
|
||||||
- Fully backward compatible with interactive mode
|
- Fully backward compatible with interactive mode
|
||||||
@@ -248,10 +252,12 @@ gitzone format --clean-backups
|
|||||||
The project has been fully migrated from @push.rocks/smartfile v11 to v13, which introduced a major breaking change where filesystem operations were split into two separate packages:
|
The project has been fully migrated from @push.rocks/smartfile v11 to v13, which introduced a major breaking change where filesystem operations were split into two separate packages:
|
||||||
|
|
||||||
**Packages:**
|
**Packages:**
|
||||||
|
|
||||||
- `@push.rocks/smartfile` v13.0.1 - File representation classes (SmartFile, StreamFile, VirtualDirectory)
|
- `@push.rocks/smartfile` v13.0.1 - File representation classes (SmartFile, StreamFile, VirtualDirectory)
|
||||||
- `@push.rocks/smartfs` v1.1.0 - Filesystem operations (read, write, exists, stat, etc.)
|
- `@push.rocks/smartfs` v1.1.0 - Filesystem operations (read, write, exists, stat, etc.)
|
||||||
|
|
||||||
**Key API Changes:**
|
**Key API Changes:**
|
||||||
|
|
||||||
1. **File Reading**:
|
1. **File Reading**:
|
||||||
- Old: `plugins.smartfile.fs.toStringSync(path)` or `plugins.smartfile.fs.toObjectSync(path)`
|
- Old: `plugins.smartfile.fs.toStringSync(path)` or `plugins.smartfile.fs.toObjectSync(path)`
|
||||||
- New: `await plugins.smartfs.file(path).encoding('utf8').read()` + JSON.parse if needed
|
- New: `await plugins.smartfs.file(path).encoding('utf8').read()` + JSON.parse if needed
|
||||||
@@ -290,13 +296,15 @@ The project has been fully migrated from @push.rocks/smartfile v11 to v13, which
|
|||||||
All sync methods must become async. Functions that were previously synchronous (like `getProjectName()`) now return `Promise<T>` and must be awaited.
|
All sync methods must become async. Functions that were previously synchronous (like `getProjectName()`) now return `Promise<T>` and must be awaited.
|
||||||
|
|
||||||
**Affected Modules:**
|
**Affected Modules:**
|
||||||
- ts/mod_format/* (largest area - 15+ files)
|
|
||||||
- ts/mod_commit/* (version bumping)
|
- ts/mod_format/\* (largest area - 15+ files)
|
||||||
- ts/mod_services/* (configuration management)
|
- ts/mod_commit/\* (version bumping)
|
||||||
- ts/mod_meta/* (meta repository management)
|
- ts/mod_services/\* (configuration management)
|
||||||
- ts/mod_standard/* (template listing)
|
- ts/mod_meta/\* (meta repository management)
|
||||||
- ts/mod_template/* (template operations)
|
- ts/mod_standard/\* (template listing)
|
||||||
|
- ts/mod_template/\* (template operations)
|
||||||
|
|
||||||
**Previous API Changes:**
|
**Previous API Changes:**
|
||||||
|
|
||||||
- smartnpm requires instance creation: `new NpmRegistry()`
|
- smartnpm requires instance creation: `new NpmRegistry()`
|
||||||
- Type imports use `import type` for proper verbatim module syntax
|
- Type imports use `import type` for proper verbatim module syntax
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install globally via npm
|
# Install globally via pnpm (recommended)
|
||||||
npm install -g @git.zone/cli
|
|
||||||
|
|
||||||
# Or with pnpm (recommended)
|
|
||||||
pnpm add -g @git.zone/cli
|
pnpm add -g @git.zone/cli
|
||||||
|
|
||||||
|
# Or with npm
|
||||||
|
npm install -g @git.zone/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
Once installed, you can use either `gitzone` or the shorter `gzone` command from anywhere in your terminal.
|
Once installed, you can use either `gitzone` or the shorter `gzone` command from anywhere in your terminal.
|
||||||
@@ -33,9 +33,12 @@ Once installed, you can use either `gitzone` or the shorter `gzone` command from
|
|||||||
# Create a new TypeScript npm package
|
# Create a new TypeScript npm package
|
||||||
gitzone template npm
|
gitzone template npm
|
||||||
|
|
||||||
# Format your entire codebase
|
# Format your entire codebase (dry-run by default)
|
||||||
gitzone format
|
gitzone format
|
||||||
|
|
||||||
|
# Apply formatting changes
|
||||||
|
gitzone format --write
|
||||||
|
|
||||||
# Start local MongoDB and MinIO services
|
# Start local MongoDB and MinIO services
|
||||||
gitzone services start
|
gitzone services start
|
||||||
|
|
||||||
@@ -45,34 +48,146 @@ gitzone commit
|
|||||||
|
|
||||||
## 🛠️ Core Features
|
## 🛠️ Core Features
|
||||||
|
|
||||||
|
### 🔀 Semantic Commits & Versioning
|
||||||
|
|
||||||
|
Create standardized commits with AI-powered suggestions that automatically handle versioning:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive commit with AI recommendations
|
||||||
|
gitzone commit
|
||||||
|
|
||||||
|
# Auto-accept AI recommendations (skipped for BREAKING CHANGEs)
|
||||||
|
gitzone commit -y
|
||||||
|
|
||||||
|
# Auto-accept, push, build, and release
|
||||||
|
gitzone commit -ypbr
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flags:**
|
||||||
|
|
||||||
|
| Flag | Long Form | Description |
|
||||||
|
|------|-----------|-------------|
|
||||||
|
| `-y` | `--yes` | Auto-accept AI recommendations |
|
||||||
|
| `-p` | `--push` | Push to remote after commit |
|
||||||
|
| `-t` | `--test` | Run tests before committing |
|
||||||
|
| `-b` | `--build` | Build after commit, verify clean tree |
|
||||||
|
| `-r` | `--release` | Publish to configured npm registries |
|
||||||
|
| | `--format` | Run format before committing |
|
||||||
|
|
||||||
|
**Workflow steps:**
|
||||||
|
|
||||||
|
1. 🤖 **AI-powered analysis** — analyzes your changes and suggests commit type, scope, and message
|
||||||
|
2. 📝 Interactive commit message builder (type: `fix`/`feat`/`BREAKING CHANGE`, scope, description)
|
||||||
|
3. 📜 Automatic changelog generation
|
||||||
|
4. 🏷️ Automatic version bumping (major/minor/patch) with git tag creation
|
||||||
|
5. 🔨 Optional build & verification
|
||||||
|
6. 🚀 Optional push to origin
|
||||||
|
7. 📦 Optional publish to npm registries
|
||||||
|
|
||||||
|
Supports both npm (`package.json`) and Deno (`deno.json`) projects, including dual-type projects.
|
||||||
|
|
||||||
|
### 🎨 Intelligent Code Formatting
|
||||||
|
|
||||||
|
Automatically format and standardize your entire codebase. **Dry-run by default** — nothing changes until you explicitly use `--write`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Preview what would change (default behavior)
|
||||||
|
gitzone format
|
||||||
|
|
||||||
|
# Apply changes
|
||||||
|
gitzone format --write
|
||||||
|
|
||||||
|
# Auto-approve without prompts
|
||||||
|
gitzone format --yes --write
|
||||||
|
|
||||||
|
# Show detailed diffs
|
||||||
|
gitzone format --diff
|
||||||
|
|
||||||
|
# Enable verbose logging
|
||||||
|
gitzone format --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flags:**
|
||||||
|
|
||||||
|
| Flag | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `--write` / `-w` | Apply changes (default is dry-run) |
|
||||||
|
| `--yes` | Auto-approve without interactive confirmation |
|
||||||
|
| `--plan-only` | Only show what would be done |
|
||||||
|
| `--save-plan <file>` | Save the format plan to a file |
|
||||||
|
| `--from-plan <file>` | Load and execute a saved plan |
|
||||||
|
| `--detailed` | Show detailed stats and save report |
|
||||||
|
| `--parallel` / `--no-parallel` | Toggle parallel execution |
|
||||||
|
| `--verbose` | Enable verbose logging |
|
||||||
|
| `--diff` | Show file diffs |
|
||||||
|
|
||||||
|
**Formatters (executed in order):**
|
||||||
|
|
||||||
|
1. 🧹 **Cleanup** — removes obsolete files (yarn.lock, package-lock.json, tslint.json, etc.)
|
||||||
|
2. ⚙️ **Npmextra** — formats and standardizes `npmextra.json`
|
||||||
|
3. 📜 **License** — ensures proper licensing and checks dependency licenses
|
||||||
|
4. 📦 **Package.json** — standardizes package configuration
|
||||||
|
5. 📋 **Templates** — applies project template updates
|
||||||
|
6. 🙈 **Gitignore** — updates repository ignore rules
|
||||||
|
7. 🔧 **Tsconfig** — optimizes TypeScript configuration
|
||||||
|
8. ✨ **Prettier** — applies code formatting
|
||||||
|
9. 📖 **Readme** — ensures readme files exist
|
||||||
|
10. 📂 **Copy** — copies configured files
|
||||||
|
|
||||||
### 🐳 Development Services Management
|
### 🐳 Development Services Management
|
||||||
|
|
||||||
Effortlessly manage local MongoDB and MinIO (S3-compatible) services for your development environment:
|
Effortlessly manage local development services (MongoDB, MinIO S3, Elasticsearch) with Docker:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitzone services [command]
|
gitzone services [command]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Available commands:**
|
**Commands:**
|
||||||
|
|
||||||
- **`start [service]`** - Start services (mongo|s3|all)
|
| Command | Description |
|
||||||
- **`stop [service]`** - Stop services (mongo|s3|all)
|
|---------|-------------|
|
||||||
- **`restart [service]`** - Restart services
|
| `start [service]` | Start services (`mongo`\|`s3`\|`elasticsearch`\|`all`) |
|
||||||
- **`status`** - Show current service status
|
| `stop [service]` | Stop services |
|
||||||
- **`config`** - Display configuration details
|
| `restart [service]` | Restart services |
|
||||||
- **`compass`** - Get MongoDB Compass connection string with network IP
|
| `status` | Show current service status |
|
||||||
- **`logs [service] [lines]`** - View service logs
|
| `config` | Display configuration details |
|
||||||
- **`remove`** - Remove containers (preserves data)
|
| `compass` | Get MongoDB Compass connection string with network IP |
|
||||||
- **`clean`** - Remove containers AND data (⚠️ destructive)
|
| `logs [service] [lines]` | View service logs (default: 20 lines) |
|
||||||
|
| `reconfigure` | Reassign ports and restart all services |
|
||||||
|
| `remove` | Remove containers (preserves data) |
|
||||||
|
| `clean` | Remove containers AND data (⚠️ destructive) |
|
||||||
|
|
||||||
|
**Service aliases:**
|
||||||
|
|
||||||
|
- `mongo` / `mongodb` — MongoDB
|
||||||
|
- `minio` / `s3` — MinIO (S3-compatible storage)
|
||||||
|
- `elasticsearch` / `es` — Elasticsearch
|
||||||
|
- `all` — All services (default)
|
||||||
|
|
||||||
**Key features:**
|
**Key features:**
|
||||||
|
|
||||||
- 🎲 **Smart port assignment** - Automatically assigns random ports (20000-30000) to avoid conflicts
|
- 🎲 **Smart port assignment** — automatically assigns random ports (20000–30000) to avoid conflicts
|
||||||
- 📦 **Project isolation** - Each project gets its own containers with unique names
|
- 📦 **Project isolation** — each project gets its own containers with unique names
|
||||||
- 💾 **Data persistence** - Data stored in `.nogit/` directories survives container restarts
|
- 💾 **Data persistence** — data stored in `.nogit/` survives container restarts
|
||||||
- 🔗 **MongoDB Compass support** - Instantly get connection strings for GUI access
|
- 🔗 **MongoDB Compass support** — instantly get connection strings for GUI access
|
||||||
- 🌐 **Network IP detection** - Automatically detects your local network IP for remote connections
|
- 🌐 **Network IP detection** — detects your local network IP for remote connections
|
||||||
- ⚙️ **Auto-configuration** - Creates `.nogit/env.json` with smart defaults
|
- ⚙️ **Auto-configuration** — creates `.nogit/env.json` with smart defaults
|
||||||
|
|
||||||
|
**Global operations (`-g` flag):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all registered projects
|
||||||
|
gitzone services list -g
|
||||||
|
|
||||||
|
# Show status across all projects
|
||||||
|
gitzone services status -g
|
||||||
|
|
||||||
|
# Stop all containers across all projects
|
||||||
|
gitzone services stop -g
|
||||||
|
|
||||||
|
# Remove stale registry entries
|
||||||
|
gitzone services cleanup -g
|
||||||
|
```
|
||||||
|
|
||||||
**Example workflow:**
|
**Example workflow:**
|
||||||
|
|
||||||
@@ -94,7 +209,26 @@ gitzone services logs mongo 50
|
|||||||
gitzone services stop
|
gitzone services stop
|
||||||
```
|
```
|
||||||
|
|
||||||
The services are configured via `.nogit/env.json` which is automatically created with secure defaults and random ports for each project.
|
### ⚙️ Release & Commit Configuration
|
||||||
|
|
||||||
|
Manage release registries and commit settings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gitzone config [subcommand]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `show` | Display current release config (registries, access level) |
|
||||||
|
| `add [url]` | Add a registry URL (default: `https://registry.npmjs.org`) |
|
||||||
|
| `remove [url]` | Remove a registry URL (interactive selection if no URL) |
|
||||||
|
| `clear` | Clear all registries (with confirmation) |
|
||||||
|
| `access [public\|private]` | Set npm access level for publishing |
|
||||||
|
| `commit alwaysTest [true\|false]` | Always run tests before commit |
|
||||||
|
| `commit alwaysBuild [true\|false]` | Always build after commit |
|
||||||
|
| `services` | Configure which services are enabled |
|
||||||
|
|
||||||
|
Configuration is stored in `npmextra.json` under the `@git.zone/cli` key.
|
||||||
|
|
||||||
### 📦 Project Templates
|
### 📦 Project Templates
|
||||||
|
|
||||||
@@ -104,12 +238,12 @@ Instantly scaffold production-ready projects with best practices built-in:
|
|||||||
gitzone template [template-name]
|
gitzone template [template-name]
|
||||||
```
|
```
|
||||||
|
|
||||||
**Available templates:**
|
**Interactive templates:**
|
||||||
|
|
||||||
- **`npm`** - TypeScript npm package with testing, CI/CD, and full tooling
|
- **`npm`** — TypeScript npm package with testing, CI/CD, and full tooling
|
||||||
- **`service`** - Microservice architecture with Docker support
|
- **`service`** — Microservice architecture with Docker support
|
||||||
- **`website`** - Modern web application with LitElement and service workers
|
- **`website`** — Modern web application with LitElement and service workers
|
||||||
- **`wcc`** - Web Component Collection for reusable UI components
|
- **`wcc`** — Web Component Collection for reusable UI components
|
||||||
|
|
||||||
Each template comes pre-configured with:
|
Each template comes pre-configured with:
|
||||||
|
|
||||||
@@ -119,94 +253,6 @@ Each template comes pre-configured with:
|
|||||||
- ✅ Code formatting and linting
|
- ✅ Code formatting and linting
|
||||||
- ✅ Documentation structure
|
- ✅ Documentation structure
|
||||||
|
|
||||||
### 🎨 Intelligent Code Formatting
|
|
||||||
|
|
||||||
The most powerful feature of gitzone - automatically format and standardize your entire codebase:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Preview changes without applying them
|
|
||||||
gitzone format --dry-run
|
|
||||||
|
|
||||||
# Format with automatic approval
|
|
||||||
gitzone format --yes
|
|
||||||
|
|
||||||
# Save formatting plan for later execution
|
|
||||||
gitzone format --save-plan format-plan.json
|
|
||||||
|
|
||||||
# Execute a saved plan
|
|
||||||
gitzone format --from-plan format-plan.json
|
|
||||||
|
|
||||||
# Enable verbose output for debugging
|
|
||||||
gitzone format --verbose
|
|
||||||
```
|
|
||||||
|
|
||||||
**Format features:**
|
|
||||||
|
|
||||||
- 🔄 **Smart caching** - Only processes changed files
|
|
||||||
- 🛡️ **Rollback support** - Undo formatting changes if needed
|
|
||||||
- 📊 **Detailed reporting** - See exactly what changed
|
|
||||||
- ⚡ **Parallel execution** - Format multiple files simultaneously
|
|
||||||
- 🎯 **Module-specific formatting** - Target specific formatters
|
|
||||||
|
|
||||||
**Rollback capabilities:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# List all available backups
|
|
||||||
gitzone format --list-backups
|
|
||||||
|
|
||||||
# Rollback to the last operation
|
|
||||||
gitzone format --rollback
|
|
||||||
|
|
||||||
# Rollback to a specific operation
|
|
||||||
gitzone format --rollback [operation-id]
|
|
||||||
|
|
||||||
# Clean old backups
|
|
||||||
gitzone format --clean-backups
|
|
||||||
```
|
|
||||||
|
|
||||||
**Formatters included:**
|
|
||||||
|
|
||||||
- **Prettier** - JavaScript/TypeScript code formatting
|
|
||||||
- **License** - Ensure proper licensing
|
|
||||||
- **Package.json** - Standardize package configurations
|
|
||||||
- **Tsconfig** - TypeScript configuration optimization
|
|
||||||
- **Readme** - Documentation formatting
|
|
||||||
- **Gitignore** - Repository ignore rules
|
|
||||||
- **Templates** - Project template updates
|
|
||||||
- **Npmextra** - Extended npm configurations
|
|
||||||
- **Cleanup** - Removes obsolete files (yarn.lock, package-lock.json, tslint.json, etc.)
|
|
||||||
|
|
||||||
### 🔀 Semantic Commits & Versioning
|
|
||||||
|
|
||||||
Create standardized commits with AI-powered suggestions that automatically handle versioning:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Interactive commit with AI recommendations
|
|
||||||
gitzone commit
|
|
||||||
|
|
||||||
# Auto-accept AI recommendations
|
|
||||||
gitzone commit -y
|
|
||||||
|
|
||||||
# Auto-accept and push
|
|
||||||
gitzone commit -y -p
|
|
||||||
```
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
- 🤖 **AI-powered analysis** - Analyzes your changes and suggests commit type, scope, and message
|
|
||||||
- 📝 Interactive commit message builder with smart defaults
|
|
||||||
- 🏷️ Automatic version bumping (major/minor/patch)
|
|
||||||
- 📜 Changelog generation
|
|
||||||
- 🚀 Optional auto-push to origin
|
|
||||||
- 🎯 Conventional commit compliance
|
|
||||||
|
|
||||||
The commit wizard guides you through:
|
|
||||||
|
|
||||||
1. **Type selection** (fix/feat/BREAKING CHANGE) with AI recommendation
|
|
||||||
2. **Scope definition** (component/module affected)
|
|
||||||
3. **Description crafting**
|
|
||||||
4. **Version bump determination**
|
|
||||||
|
|
||||||
### 🏗️ Meta Repository Management
|
### 🏗️ Meta Repository Management
|
||||||
|
|
||||||
Manage multiple related repositories as a cohesive unit:
|
Manage multiple related repositories as a cohesive unit:
|
||||||
@@ -218,36 +264,22 @@ gitzone meta init
|
|||||||
# Add a sub-project
|
# Add a sub-project
|
||||||
gitzone meta add [name] [git-url]
|
gitzone meta add [name] [git-url]
|
||||||
|
|
||||||
# Update all sub-projects
|
# Update all sub-projects (clone missing, clean superfluous)
|
||||||
gitzone meta update
|
gitzone meta update
|
||||||
|
|
||||||
# Remove a sub-project
|
# Remove a sub-project
|
||||||
gitzone meta remove [name]
|
gitzone meta remove [name]
|
||||||
```
|
```
|
||||||
|
|
||||||
Perfect for:
|
|
||||||
|
|
||||||
- Monorepo management
|
|
||||||
- Multi-package projects
|
|
||||||
- Coordinated deployments
|
|
||||||
- Synchronized versioning
|
|
||||||
|
|
||||||
### 🐳 Docker Management
|
### 🐳 Docker Management
|
||||||
|
|
||||||
Streamline your Docker workflow:
|
Streamline your Docker workflow:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clean up all Docker resources
|
# Clean up all Docker resources (containers, images, volumes, networks)
|
||||||
gitzone docker prune
|
gitzone docker prune
|
||||||
```
|
```
|
||||||
|
|
||||||
This command removes:
|
|
||||||
|
|
||||||
- Stopped containers
|
|
||||||
- Unused images
|
|
||||||
- Dangling volumes
|
|
||||||
- Unused networks
|
|
||||||
|
|
||||||
### 🔗 Quick CI/CD Access
|
### 🔗 Quick CI/CD Access
|
||||||
|
|
||||||
Jump directly to your CI/CD configurations:
|
Jump directly to your CI/CD configurations:
|
||||||
@@ -270,12 +302,7 @@ Smoothly transition users from old to new packages:
|
|||||||
gitzone deprecate
|
gitzone deprecate
|
||||||
```
|
```
|
||||||
|
|
||||||
Interactive wizard for:
|
Interactive wizard that prompts for registry URLs, old package name, and new package name — then runs `npm deprecate` across all specified registries.
|
||||||
|
|
||||||
- Setting deprecation notices
|
|
||||||
- Guiding users to replacements
|
|
||||||
- Updating registry metadata
|
|
||||||
- Coordinating migration paths
|
|
||||||
|
|
||||||
### 🚦 Project Initialization
|
### 🚦 Project Initialization
|
||||||
|
|
||||||
@@ -285,17 +312,10 @@ Prepare existing projects for development:
|
|||||||
gitzone start
|
gitzone start
|
||||||
```
|
```
|
||||||
|
|
||||||
Automatically:
|
Automatically checks out master, pulls latest changes, and installs dependencies.
|
||||||
|
|
||||||
- Checks out master branch
|
|
||||||
- Pulls latest changes
|
|
||||||
- Installs dependencies
|
|
||||||
- Sets up development environment
|
|
||||||
|
|
||||||
### 🔧 Helper Utilities
|
### 🔧 Helper Utilities
|
||||||
|
|
||||||
Quick utilities for common tasks:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate a unique short ID
|
# Generate a unique short ID
|
||||||
gitzone helpers shortid
|
gitzone helpers shortid
|
||||||
@@ -303,31 +323,44 @@ gitzone helpers shortid
|
|||||||
|
|
||||||
## 📋 Configuration
|
## 📋 Configuration
|
||||||
|
|
||||||
### npmextra.json Configuration
|
### npmextra.json
|
||||||
|
|
||||||
Customize gitzone behavior through `npmextra.json`:
|
Customize gitzone behavior through `npmextra.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"@git.zone/cli": {
|
||||||
|
"projectType": "npm",
|
||||||
|
"release": {
|
||||||
|
"registries": [
|
||||||
|
"https://registry.npmjs.org"
|
||||||
|
],
|
||||||
|
"accessLevel": "public"
|
||||||
|
},
|
||||||
|
"commit": {
|
||||||
|
"alwaysTest": false,
|
||||||
|
"alwaysBuild": false
|
||||||
|
}
|
||||||
|
},
|
||||||
"gitzone": {
|
"gitzone": {
|
||||||
"format": {
|
"format": {
|
||||||
"interactive": true,
|
"interactive": true,
|
||||||
"showDiffs": false,
|
|
||||||
"autoApprove": false,
|
|
||||||
"parallel": true,
|
"parallel": true,
|
||||||
"rollback": {
|
"showStats": true,
|
||||||
|
"cache": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"autoRollbackOnError": true,
|
"clean": true
|
||||||
"backupRetentionDays": 7
|
|
||||||
},
|
},
|
||||||
"modules": {
|
"modules": {
|
||||||
"skip": ["prettier"],
|
"skip": ["prettier"],
|
||||||
"only": [],
|
"only": [],
|
||||||
"order": []
|
"order": []
|
||||||
},
|
},
|
||||||
"cache": {
|
"licenses": {
|
||||||
"enabled": true,
|
"allowed": ["MIT", "Apache-2.0"],
|
||||||
"clean": true
|
"exceptions": {
|
||||||
|
"some-package": "GPL-3.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,35 +369,9 @@ Customize gitzone behavior through `npmextra.json`:
|
|||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
- `CI` - Detect CI environment for automated workflows
|
- `CI` — Detect CI environment for automated workflows
|
||||||
- `DEBUG` - Enable debug output
|
- `DEBUG` — Enable debug output
|
||||||
- `GITZONE_FORMAT_PARALLEL` - Control parallel formatting
|
- `GITZONE_FORMAT_PARALLEL` — Control parallel formatting
|
||||||
|
|
||||||
## 🏆 Best Practices
|
|
||||||
|
|
||||||
### For New Projects
|
|
||||||
|
|
||||||
1. Start with a template: `gitzone template npm`
|
|
||||||
2. Set up local services: `gitzone services start`
|
|
||||||
3. Customize the generated structure
|
|
||||||
4. Run initial format: `gitzone format`
|
|
||||||
5. Set up CI/CD: `gitzone open ci`
|
|
||||||
|
|
||||||
### For Existing Projects
|
|
||||||
|
|
||||||
1. Initialize: `gitzone start`
|
|
||||||
2. Format codebase: `gitzone format --dry-run` (preview first!)
|
|
||||||
3. Apply formatting: `gitzone format --yes`
|
|
||||||
4. Set up services: `gitzone services start`
|
|
||||||
5. Commit changes: `gitzone commit`
|
|
||||||
|
|
||||||
### For Teams
|
|
||||||
|
|
||||||
1. Document format preferences in `npmextra.json`
|
|
||||||
2. Share `.nogit/env.json` template for consistent service setup
|
|
||||||
3. Use `--save-plan` for reviewable format changes
|
|
||||||
4. Enable rollback for safety
|
|
||||||
5. Standardize commit conventions
|
|
||||||
|
|
||||||
## 🎯 Common Workflows
|
## 🎯 Common Workflows
|
||||||
|
|
||||||
@@ -383,8 +390,9 @@ gitzone services start
|
|||||||
# 4. Check service logs if needed
|
# 4. Check service logs if needed
|
||||||
gitzone services logs mongo
|
gitzone services logs mongo
|
||||||
|
|
||||||
# 5. Format code
|
# 5. Preview format changes, then apply
|
||||||
gitzone format
|
gitzone format
|
||||||
|
gitzone format --write
|
||||||
|
|
||||||
# 6. Commit with semantic versioning
|
# 6. Commit with semantic versioning
|
||||||
gitzone commit
|
gitzone commit
|
||||||
@@ -393,6 +401,13 @@ gitzone commit
|
|||||||
gitzone services stop
|
gitzone services stop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Automated CI/CD Commit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auto-accept, test, build, push, and release in one command
|
||||||
|
gitzone commit -ytbpr
|
||||||
|
```
|
||||||
|
|
||||||
### Multi-Repository Management
|
### Multi-Repository Management
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -408,29 +423,26 @@ gitzone meta add shared https://github.com/org/shared.git
|
|||||||
gitzone meta update
|
gitzone meta update
|
||||||
```
|
```
|
||||||
|
|
||||||
### Safe Formatting with Rollback
|
### Safe Formatting with Plan Review
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Preview changes
|
# 1. Preview changes (default)
|
||||||
gitzone format --dry-run
|
gitzone format
|
||||||
|
|
||||||
# 2. Save plan for review
|
# 2. Save plan for review
|
||||||
gitzone format --save-plan format-changes.json
|
gitzone format --save-plan format-changes.json
|
||||||
|
|
||||||
# 3. Apply formatting
|
# 3. Apply from saved plan
|
||||||
gitzone format --from-plan format-changes.json
|
gitzone format --from-plan format-changes.json --write
|
||||||
|
|
||||||
# 4. If something goes wrong, rollback
|
|
||||||
gitzone format --rollback
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database-Driven Development
|
### Database-Driven Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Start MongoDB and MinIO
|
# 1. Start MongoDB, MinIO, and Elasticsearch
|
||||||
gitzone services start
|
gitzone services start
|
||||||
|
|
||||||
# 2. Get connection string for your app
|
# 2. Get connection details
|
||||||
gitzone services config
|
gitzone services config
|
||||||
|
|
||||||
# 3. Connect with MongoDB Compass
|
# 3. Connect with MongoDB Compass
|
||||||
@@ -447,43 +459,44 @@ gitzone services clean # ⚠️ Warning: deletes data
|
|||||||
|
|
||||||
### CI/CD Platforms
|
### CI/CD Platforms
|
||||||
|
|
||||||
- **GitLab CI** - Full pipeline support with templates
|
- **GitLab CI** — full pipeline support with templates
|
||||||
- **GitHub Actions** - Automated workflows
|
- **GitHub Actions** — automated workflows
|
||||||
- **Docker** - Container-based deployments
|
- **Docker** — container-based deployments
|
||||||
|
|
||||||
### Development Tools
|
### Development Tools
|
||||||
|
|
||||||
- **TypeScript** - First-class support
|
- **TypeScript** — first-class support
|
||||||
- **Prettier** - Code formatting
|
- **Prettier** — code formatting
|
||||||
- **npm/pnpm** - Package management
|
- **pnpm** — package management
|
||||||
- **MongoDB** - Local database service
|
- **MongoDB** — local database service
|
||||||
- **MinIO** - S3-compatible object storage
|
- **MinIO** — S3-compatible object storage
|
||||||
- **MongoDB Compass** - Database GUI integration
|
- **Elasticsearch** — search and analytics
|
||||||
|
- **MongoDB Compass** — database GUI integration
|
||||||
|
|
||||||
### Version Control
|
### Version Control
|
||||||
|
|
||||||
- **Git** - Deep integration
|
- **Git** — deep integration
|
||||||
- **Semantic Versioning** - Automatic version bumping
|
- **Semantic Versioning** — automatic version bumping
|
||||||
- **Conventional Commits** - Standardized commit messages
|
- **Conventional Commits** — standardized commit messages
|
||||||
|
- **AI-Powered Analysis** — intelligent commit suggestions via `@git.zone/tsdoc`
|
||||||
|
|
||||||
## 💡 Pro Tips
|
## 💡 Pro Tips
|
||||||
|
|
||||||
1. **Use aliases**: Add `alias gz='gitzone'` to your shell profile
|
1. **Use aliases**: Add `alias gz='gitzone'` to your shell profile
|
||||||
2. **Combine commands**: `gitzone format --yes && gitzone commit`
|
2. **Combine flags**: `gitzone commit -ypbr` for the full auto workflow
|
||||||
3. **Leverage templates**: Start projects right with proven structures
|
3. **Leverage templates**: Start projects right with proven structures
|
||||||
4. **Enable caching**: Dramatically speeds up formatting operations
|
4. **Enable caching**: Dramatically speeds up formatting operations
|
||||||
5. **Save format plans**: Review changes before applying in production
|
5. **Save format plans**: Review changes before applying
|
||||||
6. **Port management**: Let services auto-assign ports to avoid conflicts
|
6. **Port management**: Let services auto-assign ports to avoid conflicts
|
||||||
7. **Use MongoDB Compass**: `gitzone services compass` for visual DB management
|
7. **Use MongoDB Compass**: `gitzone services compass` for visual DB management
|
||||||
|
8. **Global service management**: `gitzone services status -g` to see all projects' services at once
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
### Format Command Shows "Cancelled"
|
### Format Command Shows "Cancelled"
|
||||||
|
|
||||||
If the format command shows cancelled even after confirming:
|
|
||||||
|
|
||||||
- Check your `npmextra.json` configuration
|
- Check your `npmextra.json` configuration
|
||||||
- Try with `--yes` flag to skip confirmation
|
- Try with `--yes --write` flags
|
||||||
- Use `--verbose` for detailed output
|
- Use `--verbose` for detailed output
|
||||||
|
|
||||||
### Docker Commands Fail
|
### Docker Commands Fail
|
||||||
@@ -496,19 +509,20 @@ docker info
|
|||||||
|
|
||||||
### Services Won't Start
|
### Services Won't Start
|
||||||
|
|
||||||
Check for port conflicts:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Services auto-assign ports, but you can check the config
|
# Services auto-assign ports, but you can check the config
|
||||||
cat .nogit/env.json
|
cat .nogit/env.json
|
||||||
|
|
||||||
# Verify Docker is running
|
# Verify Docker is running
|
||||||
docker ps
|
docker ps
|
||||||
|
|
||||||
|
# Reassign ports if there are conflicts
|
||||||
|
gitzone services reconfigure
|
||||||
```
|
```
|
||||||
|
|
||||||
### Template Creation Issues
|
### Template Creation Issues
|
||||||
|
|
||||||
Verify npm/pnpm is properly configured:
|
Verify pnpm/npm is properly configured:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm config get registry
|
npm config get registry
|
||||||
@@ -524,12 +538,11 @@ npm config get registry
|
|||||||
|
|
||||||
gitzone is optimized for speed:
|
gitzone is optimized for speed:
|
||||||
|
|
||||||
- **Parallel processing** for format operations
|
- ⚡ **Parallel processing** for format operations
|
||||||
- **Smart caching** to avoid redundant work
|
- 🧠 **Smart caching** to avoid redundant work
|
||||||
- **Incremental updates** for meta repositories
|
- 📊 **Incremental updates** for meta repositories
|
||||||
- **Minimal dependencies** for fast installation
|
- 🐳 **Isolated services** prevent resource conflicts
|
||||||
- **Isolated services** prevent resource conflicts
|
- 🎲 **Auto port assignment** eliminates manual configuration
|
||||||
- **Auto port assignment** eliminates manual configuration
|
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# GitZone Services Command Implementation Plan
|
# GitZone Services Command Implementation Plan
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Implement the `gitzone services` command to manage MongoDB and MinIO containers for development projects.
|
Implement the `gitzone services` command to manage MongoDB and MinIO containers for development projects.
|
||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
### Module Structure Setup
|
### Module Structure Setup
|
||||||
|
|
||||||
- [x] Create `ts/mod_services/` directory
|
- [x] Create `ts/mod_services/` directory
|
||||||
- [x] Create `mod.plugins.ts` with required imports
|
- [x] Create `mod.plugins.ts` with required imports
|
||||||
- [x] Create `helpers.ts` with utility functions
|
- [x] Create `helpers.ts` with utility functions
|
||||||
@@ -15,6 +17,7 @@ Implement the `gitzone services` command to manage MongoDB and MinIO containers
|
|||||||
- [x] Create `index.ts` with main command logic
|
- [x] Create `index.ts` with main command logic
|
||||||
|
|
||||||
### Core Functionality
|
### Core Functionality
|
||||||
|
|
||||||
- [x] Implement ServiceConfiguration class
|
- [x] Implement ServiceConfiguration class
|
||||||
- [x] Load/create `.nogit/env.json` configuration
|
- [x] Load/create `.nogit/env.json` configuration
|
||||||
- [x] Generate random available ports (20000-30000 range)
|
- [x] Generate random available ports (20000-30000 range)
|
||||||
@@ -37,6 +40,7 @@ Implement the `gitzone services` command to manage MongoDB and MinIO containers
|
|||||||
- [x] Generate MongoDB Compass connection strings
|
- [x] Generate MongoDB Compass connection strings
|
||||||
|
|
||||||
### Commands Implementation
|
### Commands Implementation
|
||||||
|
|
||||||
- [x] `start` command - Start services (mongo|s3|all)
|
- [x] `start` command - Start services (mongo|s3|all)
|
||||||
- [x] `stop` command - Stop services (mongo|s3|all)
|
- [x] `stop` command - Stop services (mongo|s3|all)
|
||||||
- [x] `restart` command - Restart services (mongo|s3|all)
|
- [x] `restart` command - Restart services (mongo|s3|all)
|
||||||
@@ -48,12 +52,14 @@ Implement the `gitzone services` command to manage MongoDB and MinIO containers
|
|||||||
- [x] `clean` command - Remove containers and data
|
- [x] `clean` command - Remove containers and data
|
||||||
|
|
||||||
### Integration
|
### Integration
|
||||||
|
|
||||||
- [x] Add `@push.rocks/smartshell` to main plugins.ts
|
- [x] Add `@push.rocks/smartshell` to main plugins.ts
|
||||||
- [x] Add `@push.rocks/smartnetwork` to main plugins.ts
|
- [x] Add `@push.rocks/smartnetwork` to main plugins.ts
|
||||||
- [x] Add `@push.rocks/smartinteraction` to main plugins.ts
|
- [x] Add `@push.rocks/smartinteraction` to main plugins.ts
|
||||||
- [x] Register services command in `gitzone.cli.ts`
|
- [x] Register services command in `gitzone.cli.ts`
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- [x] Auto-configuration with smart defaults
|
- [x] Auto-configuration with smart defaults
|
||||||
- [x] Random port assignment to avoid conflicts
|
- [x] Random port assignment to avoid conflicts
|
||||||
- [x] Project isolation with unique container names
|
- [x] Project isolation with unique container names
|
||||||
@@ -65,6 +71,7 @@ Implement the `gitzone services` command to manage MongoDB and MinIO containers
|
|||||||
- [x] MongoDB Compass connection string with network IP
|
- [x] MongoDB Compass connection string with network IP
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
- [ ] Test service start/stop operations
|
- [ ] Test service start/stop operations
|
||||||
- [ ] Test configuration creation and updates
|
- [ ] Test configuration creation and updates
|
||||||
- [ ] Test port collision handling
|
- [ ] Test port collision handling
|
||||||
@@ -73,6 +80,7 @@ Implement the `gitzone services` command to manage MongoDB and MinIO containers
|
|||||||
- [ ] Test all command variations
|
- [ ] Test all command variations
|
||||||
|
|
||||||
## Configuration Format
|
## Configuration Format
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"PROJECT_NAME": "derived-from-package-name",
|
"PROJECT_NAME": "derived-from-package-name",
|
||||||
@@ -91,6 +99,7 @@ Implement the `gitzone services` command to manage MongoDB and MinIO containers
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Command Examples
|
## Command Examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gitzone services start # Start all services
|
gitzone services start # Start all services
|
||||||
gitzone services start mongo # Start only MongoDB
|
gitzone services start mongo # Start only MongoDB
|
||||||
@@ -104,10 +113,12 @@ gitzone services clean # Remove containers and data
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Progress Notes
|
## Progress Notes
|
||||||
|
|
||||||
Implementation started: 2025-08-14
|
Implementation started: 2025-08-14
|
||||||
Implementation completed: 2025-08-14
|
Implementation completed: 2025-08-14
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Successfully implemented the `gitzone services` command in TypeScript, providing a complete replacement for the `services.sh` shell script. The implementation includes:
|
Successfully implemented the `gitzone services` command in TypeScript, providing a complete replacement for the `services.sh` shell script. The implementation includes:
|
||||||
|
|
||||||
1. **Complete Docker service management** for MongoDB and MinIO containers
|
1. **Complete Docker service management** for MongoDB and MinIO containers
|
||||||
|
|||||||
Submodule
+1
Submodule test added at 0b89443584
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/cli',
|
name: '@git.zone/cli',
|
||||||
version: '2.9.0',
|
version: '2.13.16',
|
||||||
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.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ export class GitzoneConfig {
|
|||||||
public data: IGitzoneConfigData;
|
public data: IGitzoneConfigData;
|
||||||
|
|
||||||
public async readConfigFromCwd() {
|
public async readConfigFromCwd() {
|
||||||
const npmextraInstance = new plugins.npmextra.Npmextra(paths.cwd);
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig(paths.cwd);
|
||||||
this.data = npmextraInstance.dataFor<IGitzoneConfigData>('@git.zone/cli', {});
|
this.data = smartconfigInstance.dataFor<IGitzoneConfigData>('@git.zone/cli', {});
|
||||||
|
|
||||||
// Read szci config for backward compatibility
|
// Read szci config for backward compatibility
|
||||||
const szciConfig = npmextraInstance.dataFor<any>('@ship.zone/szci', {});
|
const szciConfig = smartconfigInstance.dataFor<any>('@ship.zone/szci', {});
|
||||||
|
|
||||||
// Prefer accessLevel from @git.zone/cli.release, fallback to @ship.zone/szci.npmAccessLevel
|
// Prefer accessLevel from @git.zone/cli.release, fallback to @ship.zone/szci.npmAccessLevel
|
||||||
const accessLevel =
|
const accessLevel =
|
||||||
|
|||||||
@@ -63,22 +63,6 @@ export let run = async () => {
|
|||||||
const config = GitzoneConfig.fromCwd();
|
const config = GitzoneConfig.fromCwd();
|
||||||
const modFormat = await import('./mod_format/index.js');
|
const modFormat = await import('./mod_format/index.js');
|
||||||
|
|
||||||
// Handle rollback commands
|
|
||||||
if (argvArg.rollback) {
|
|
||||||
await modFormat.handleRollback(argvArg.rollback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argvArg['list-backups']) {
|
|
||||||
await modFormat.handleListBackups();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argvArg['clean-backups']) {
|
|
||||||
await modFormat.handleCleanBackups();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle format with options
|
// Handle format with options
|
||||||
// Default is dry-mode, use --write/-w to apply changes
|
// Default is dry-mode, use --write/-w to apply changes
|
||||||
await modFormat.run({
|
await modFormat.run({
|
||||||
@@ -90,7 +74,6 @@ export let run = async () => {
|
|||||||
fromPlan: argvArg['from-plan'],
|
fromPlan: argvArg['from-plan'],
|
||||||
detailed: argvArg.detailed,
|
detailed: argvArg.detailed,
|
||||||
interactive: argvArg.interactive !== false,
|
interactive: argvArg.interactive !== false,
|
||||||
parallel: argvArg.parallel !== false,
|
|
||||||
verbose: argvArg.verbose,
|
verbose: argvArg.verbose,
|
||||||
diff: argvArg.diff,
|
diff: argvArg.diff,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import * as ui from './mod.ui.js';
|
|||||||
import { ReleaseConfig } from '../mod_config/classes.releaseconfig.js';
|
import { ReleaseConfig } from '../mod_config/classes.releaseconfig.js';
|
||||||
|
|
||||||
export const run = async (argvArg: any) => {
|
export const run = async (argvArg: any) => {
|
||||||
// Read commit config from npmextra.json
|
// Read commit config from .smartconfig.json
|
||||||
const npmextraConfig = new plugins.npmextra.Npmextra();
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig();
|
||||||
const gitzoneConfig = npmextraConfig.dataFor<{
|
const gitzoneConfig = smartconfigInstance.dataFor<{
|
||||||
commit?: {
|
commit?: {
|
||||||
alwaysTest?: boolean;
|
alwaysTest?: boolean;
|
||||||
alwaysBuild?: boolean;
|
alwaysBuild?: boolean;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface ICommitConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages commit configuration stored in npmextra.json
|
* Manages commit configuration stored in .smartconfig.json
|
||||||
* under @git.zone/cli.commit namespace
|
* under @git.zone/cli.commit namespace
|
||||||
*/
|
*/
|
||||||
export class CommitConfig {
|
export class CommitConfig {
|
||||||
@@ -28,11 +28,11 @@ export class CommitConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load configuration from npmextra.json
|
* Load configuration from .smartconfig.json
|
||||||
*/
|
*/
|
||||||
public async load(): Promise<void> {
|
public async load(): Promise<void> {
|
||||||
const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig(this.cwd);
|
||||||
const gitzoneConfig = npmextraInstance.dataFor<any>('@git.zone/cli', {});
|
const gitzoneConfig = smartconfigInstance.dataFor<any>('@git.zone/cli', {});
|
||||||
|
|
||||||
this.config = {
|
this.config = {
|
||||||
alwaysTest: gitzoneConfig?.commit?.alwaysTest ?? false,
|
alwaysTest: gitzoneConfig?.commit?.alwaysTest ?? false,
|
||||||
@@ -41,37 +41,37 @@ export class CommitConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save configuration to npmextra.json
|
* Save configuration to .smartconfig.json
|
||||||
*/
|
*/
|
||||||
public async save(): Promise<void> {
|
public async save(): Promise<void> {
|
||||||
const npmextraPath = plugins.path.join(this.cwd, 'npmextra.json');
|
const smartconfigPath = plugins.path.join(this.cwd, '.smartconfig.json');
|
||||||
let npmextraData: any = {};
|
let smartconfigData: any = {};
|
||||||
|
|
||||||
// Read existing npmextra.json
|
// Read existing .smartconfig.json
|
||||||
if (await plugins.smartfs.file(npmextraPath).exists()) {
|
if (await plugins.smartfs.file(smartconfigPath).exists()) {
|
||||||
const content = await plugins.smartfs.file(npmextraPath).encoding('utf8').read();
|
const content = await plugins.smartfs.file(smartconfigPath).encoding('utf8').read();
|
||||||
npmextraData = JSON.parse(content as string);
|
smartconfigData = JSON.parse(content as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure @git.zone/cli namespace exists
|
// Ensure @git.zone/cli namespace exists
|
||||||
if (!npmextraData['@git.zone/cli']) {
|
if (!smartconfigData['@git.zone/cli']) {
|
||||||
npmextraData['@git.zone/cli'] = {};
|
smartconfigData['@git.zone/cli'] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure commit object exists
|
// Ensure commit object exists
|
||||||
if (!npmextraData['@git.zone/cli'].commit) {
|
if (!smartconfigData['@git.zone/cli'].commit) {
|
||||||
npmextraData['@git.zone/cli'].commit = {};
|
smartconfigData['@git.zone/cli'].commit = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update commit settings
|
// Update commit settings
|
||||||
npmextraData['@git.zone/cli'].commit.alwaysTest = this.config.alwaysTest;
|
smartconfigData['@git.zone/cli'].commit.alwaysTest = this.config.alwaysTest;
|
||||||
npmextraData['@git.zone/cli'].commit.alwaysBuild = this.config.alwaysBuild;
|
smartconfigData['@git.zone/cli'].commit.alwaysBuild = this.config.alwaysBuild;
|
||||||
|
|
||||||
// Write back to file
|
// Write back to file
|
||||||
await plugins.smartfs
|
await plugins.smartfs
|
||||||
.file(npmextraPath)
|
.file(smartconfigPath)
|
||||||
.encoding('utf8')
|
.encoding('utf8')
|
||||||
.write(JSON.stringify(npmextraData, null, 2));
|
.write(JSON.stringify(smartconfigData, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export interface IReleaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages release configuration stored in npmextra.json
|
* Manages release configuration stored in .smartconfig.json
|
||||||
* under @git.zone/cli.release namespace
|
* under @git.zone/cli.release namespace
|
||||||
*/
|
*/
|
||||||
export class ReleaseConfig {
|
export class ReleaseConfig {
|
||||||
@@ -30,14 +30,14 @@ export class ReleaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load configuration from npmextra.json
|
* Load configuration from .smartconfig.json
|
||||||
*/
|
*/
|
||||||
public async load(): Promise<void> {
|
public async load(): Promise<void> {
|
||||||
const npmextraInstance = new plugins.npmextra.Npmextra(this.cwd);
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig(this.cwd);
|
||||||
const gitzoneConfig = npmextraInstance.dataFor<any>('@git.zone/cli', {});
|
const gitzoneConfig = smartconfigInstance.dataFor<any>('@git.zone/cli', {});
|
||||||
|
|
||||||
// Also check szci for backward compatibility
|
// Also check szci for backward compatibility
|
||||||
const szciConfig = npmextraInstance.dataFor<any>('@ship.zone/szci', {});
|
const szciConfig = smartconfigInstance.dataFor<any>('@ship.zone/szci', {});
|
||||||
|
|
||||||
this.config = {
|
this.config = {
|
||||||
registries: gitzoneConfig?.release?.registries || [],
|
registries: gitzoneConfig?.release?.registries || [],
|
||||||
@@ -46,37 +46,37 @@ export class ReleaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save configuration to npmextra.json
|
* Save configuration to .smartconfig.json
|
||||||
*/
|
*/
|
||||||
public async save(): Promise<void> {
|
public async save(): Promise<void> {
|
||||||
const npmextraPath = plugins.path.join(this.cwd, 'npmextra.json');
|
const smartconfigPath = plugins.path.join(this.cwd, '.smartconfig.json');
|
||||||
let npmextraData: any = {};
|
let smartconfigData: any = {};
|
||||||
|
|
||||||
// Read existing npmextra.json
|
// Read existing .smartconfig.json
|
||||||
if (await plugins.smartfs.file(npmextraPath).exists()) {
|
if (await plugins.smartfs.file(smartconfigPath).exists()) {
|
||||||
const content = await plugins.smartfs.file(npmextraPath).encoding('utf8').read();
|
const content = await plugins.smartfs.file(smartconfigPath).encoding('utf8').read();
|
||||||
npmextraData = JSON.parse(content as string);
|
smartconfigData = JSON.parse(content as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure @git.zone/cli namespace exists
|
// Ensure @git.zone/cli namespace exists
|
||||||
if (!npmextraData['@git.zone/cli']) {
|
if (!smartconfigData['@git.zone/cli']) {
|
||||||
npmextraData['@git.zone/cli'] = {};
|
smartconfigData['@git.zone/cli'] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure release object exists
|
// Ensure release object exists
|
||||||
if (!npmextraData['@git.zone/cli'].release) {
|
if (!smartconfigData['@git.zone/cli'].release) {
|
||||||
npmextraData['@git.zone/cli'].release = {};
|
smartconfigData['@git.zone/cli'].release = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update registries and accessLevel
|
// Update registries and accessLevel
|
||||||
npmextraData['@git.zone/cli'].release.registries = this.config.registries;
|
smartconfigData['@git.zone/cli'].release.registries = this.config.registries;
|
||||||
npmextraData['@git.zone/cli'].release.accessLevel = this.config.accessLevel;
|
smartconfigData['@git.zone/cli'].release.accessLevel = this.config.accessLevel;
|
||||||
|
|
||||||
// Write back to file
|
// Write back to file
|
||||||
await plugins.smartfs
|
await plugins.smartfs
|
||||||
.file(npmextraPath)
|
.file(smartconfigPath)
|
||||||
.encoding('utf8')
|
.encoding('utf8')
|
||||||
.write(JSON.stringify(npmextraData, null, 2));
|
.write(JSON.stringify(smartconfigData, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+11
-11
@@ -8,23 +8,23 @@ import { runFormatter, type ICheckResult } from '../mod_format/index.js';
|
|||||||
export { ReleaseConfig, CommitConfig };
|
export { ReleaseConfig, CommitConfig };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format npmextra.json with diff preview
|
* Format .smartconfig.json with diff preview
|
||||||
* Shows diff first, asks for confirmation, then applies
|
* Shows diff first, asks for confirmation, then applies
|
||||||
*/
|
*/
|
||||||
async function formatNpmextraWithDiff(): Promise<void> {
|
async function formatSmartconfigWithDiff(): Promise<void> {
|
||||||
// Check for diffs first
|
// Check for diffs first
|
||||||
const checkResult = await runFormatter('npmextra', {
|
const checkResult = await runFormatter('smartconfig', {
|
||||||
checkOnly: true,
|
checkOnly: true,
|
||||||
showDiff: true,
|
showDiff: true,
|
||||||
}) as ICheckResult | void;
|
}) as ICheckResult | void;
|
||||||
|
|
||||||
if (checkResult && checkResult.hasDiff) {
|
if (checkResult && checkResult.hasDiff) {
|
||||||
const shouldApply = await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
const shouldApply = await plugins.smartinteract.SmartInteract.getCliConfirmation(
|
||||||
'Apply formatting changes to npmextra.json?',
|
'Apply formatting changes to .smartconfig.json?',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
if (shouldApply) {
|
if (shouldApply) {
|
||||||
await runFormatter('npmextra', { silent: true });
|
await runFormatter('smartconfig', { silent: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ async function handleAdd(url?: string): Promise<void> {
|
|||||||
if (added) {
|
if (added) {
|
||||||
await config.save();
|
await config.save();
|
||||||
plugins.logger.log('success', `Added registry: ${url}`);
|
plugins.logger.log('success', `Added registry: ${url}`);
|
||||||
await formatNpmextraWithDiff();
|
await formatSmartconfigWithDiff();
|
||||||
} else {
|
} else {
|
||||||
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
plugins.logger.log('warn', `Registry already exists: ${url}`);
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ async function handleRemove(url?: string): Promise<void> {
|
|||||||
if (removed) {
|
if (removed) {
|
||||||
await config.save();
|
await config.save();
|
||||||
plugins.logger.log('success', `Removed registry: ${url}`);
|
plugins.logger.log('success', `Removed registry: ${url}`);
|
||||||
await formatNpmextraWithDiff();
|
await formatSmartconfigWithDiff();
|
||||||
} else {
|
} else {
|
||||||
plugins.logger.log('warn', `Registry not found: ${url}`);
|
plugins.logger.log('warn', `Registry not found: ${url}`);
|
||||||
}
|
}
|
||||||
@@ -250,7 +250,7 @@ async function handleClear(): Promise<void> {
|
|||||||
config.clearRegistries();
|
config.clearRegistries();
|
||||||
await config.save();
|
await config.save();
|
||||||
plugins.logger.log('success', 'All registries cleared.');
|
plugins.logger.log('success', 'All registries cleared.');
|
||||||
await formatNpmextraWithDiff();
|
await formatSmartconfigWithDiff();
|
||||||
} else {
|
} else {
|
||||||
plugins.logger.log('info', 'Operation cancelled.');
|
plugins.logger.log('info', 'Operation cancelled.');
|
||||||
}
|
}
|
||||||
@@ -290,7 +290,7 @@ async function handleAccessLevel(level?: string): Promise<void> {
|
|||||||
config.setAccessLevel(level as 'public' | 'private');
|
config.setAccessLevel(level as 'public' | 'private');
|
||||||
await config.save();
|
await config.save();
|
||||||
plugins.logger.log('success', `Access level set to: ${level}`);
|
plugins.logger.log('success', `Access level set to: ${level}`);
|
||||||
await formatNpmextraWithDiff();
|
await formatSmartconfigWithDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -350,7 +350,7 @@ async function handleCommitInteractive(config: CommitConfig): Promise<void> {
|
|||||||
await config.save();
|
await config.save();
|
||||||
|
|
||||||
plugins.logger.log('success', 'Commit configuration updated');
|
plugins.logger.log('success', 'Commit configuration updated');
|
||||||
await formatNpmextraWithDiff();
|
await formatSmartconfigWithDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -368,7 +368,7 @@ async function handleCommitSetting(config: CommitConfig, setting: string, value?
|
|||||||
|
|
||||||
await config.save();
|
await config.save();
|
||||||
plugins.logger.log('success', `Set ${setting} to ${boolValue}`);
|
plugins.logger.log('success', `Set ${setting} to ${boolValue}`);
|
||||||
await formatNpmextraWithDiff();
|
await formatSmartconfigWithDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import * as plugins from './mod.plugins.js';
|
|||||||
import { FormatContext } from './classes.formatcontext.js';
|
import { FormatContext } from './classes.formatcontext.js';
|
||||||
import type { IPlannedChange, ICheckResult } from './interfaces.format.js';
|
import type { IPlannedChange, ICheckResult } from './interfaces.format.js';
|
||||||
import { Project } from '../classes.project.js';
|
import { Project } from '../classes.project.js';
|
||||||
|
import { FormatStats } from './classes.formatstats.js';
|
||||||
|
|
||||||
export abstract class BaseFormatter {
|
export abstract class BaseFormatter {
|
||||||
protected context: FormatContext;
|
protected context: FormatContext;
|
||||||
protected project: Project;
|
protected project: Project;
|
||||||
protected stats: any; // Will be FormatStats from context
|
protected stats: FormatStats;
|
||||||
|
|
||||||
constructor(context: FormatContext, project: Project) {
|
constructor(context: FormatContext, project: Project) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -36,9 +37,6 @@ export abstract class BaseFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.postExecute();
|
await this.postExecute();
|
||||||
} catch (error) {
|
|
||||||
// Don't rollback here - let the FormatPlanner handle it
|
|
||||||
throw error;
|
|
||||||
} finally {
|
} finally {
|
||||||
this.stats.endModule(this.name, startTime);
|
this.stats.endModule(this.name, startTime);
|
||||||
}
|
}
|
||||||
@@ -53,13 +51,10 @@ export abstract class BaseFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async modifyFile(filepath: string, content: string): Promise<void> {
|
protected async modifyFile(filepath: string, content: string): Promise<void> {
|
||||||
// Validate filepath before writing
|
|
||||||
if (!filepath || filepath.trim() === '') {
|
if (!filepath || filepath.trim() === '') {
|
||||||
throw new Error(`Invalid empty filepath in modifyFile`);
|
throw new Error(`Invalid empty filepath in modifyFile`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have a proper path with directory component
|
|
||||||
// If the path has no directory component (e.g., "package.json"), prepend "./"
|
|
||||||
let normalizedPath = filepath;
|
let normalizedPath = filepath;
|
||||||
if (!plugins.path.parse(filepath).dir) {
|
if (!plugins.path.parse(filepath).dir) {
|
||||||
normalizedPath = './' + filepath;
|
normalizedPath = './' + filepath;
|
||||||
@@ -69,44 +64,46 @@ export abstract class BaseFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async createFile(filepath: string, content: string): Promise<void> {
|
protected async createFile(filepath: string, content: string): Promise<void> {
|
||||||
await plugins.smartfs.file(filepath).encoding('utf8').write(content);
|
let normalizedPath = filepath;
|
||||||
|
if (!plugins.path.parse(filepath).dir) {
|
||||||
|
normalizedPath = './' + filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure parent directory exists
|
||||||
|
const dir = plugins.path.dirname(normalizedPath);
|
||||||
|
if (dir && dir !== '.') {
|
||||||
|
await plugins.smartfs.directory(dir).recursive().create();
|
||||||
|
}
|
||||||
|
|
||||||
|
await plugins.smartfs.file(normalizedPath).encoding('utf8').write(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async deleteFile(filepath: string): Promise<void> {
|
protected async deleteFile(filepath: string): Promise<void> {
|
||||||
await plugins.smartfs.file(filepath).delete();
|
await plugins.smartfs.file(filepath).delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async shouldProcessFile(filepath: string): Promise<boolean> {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for diffs without applying changes
|
* Check for diffs without applying changes
|
||||||
* Returns information about what would change
|
|
||||||
*/
|
*/
|
||||||
async check(): Promise<ICheckResult> {
|
async check(): Promise<ICheckResult> {
|
||||||
const changes = await this.analyze();
|
const changes = await this.analyze();
|
||||||
const diffs: ICheckResult['diffs'] = [];
|
const diffs: ICheckResult['diffs'] = [];
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
// Skip generic changes that don't have actual content
|
|
||||||
if (change.path === '<various files>') {
|
if (change.path === '<various files>') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change.type === 'modify' || change.type === 'create') {
|
if (change.type === 'modify' || change.type === 'create') {
|
||||||
// Read current content if file exists
|
|
||||||
let currentContent: string | undefined;
|
let currentContent: string | undefined;
|
||||||
try {
|
try {
|
||||||
currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
|
currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
|
||||||
} catch {
|
} catch {
|
||||||
// File doesn't exist yet
|
|
||||||
currentContent = undefined;
|
currentContent = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newContent = change.content;
|
const newContent = change.content;
|
||||||
|
|
||||||
// Check if there's an actual diff
|
|
||||||
if (currentContent !== newContent && newContent !== undefined) {
|
if (currentContent !== newContent && newContent !== undefined) {
|
||||||
diffs.push({
|
diffs.push({
|
||||||
path: change.path,
|
path: change.path,
|
||||||
@@ -116,7 +113,6 @@ export abstract class BaseFormatter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (change.type === 'delete') {
|
} else if (change.type === 'delete') {
|
||||||
// Check if file exists before marking for deletion
|
|
||||||
try {
|
try {
|
||||||
const currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
|
const currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
|
||||||
diffs.push({
|
diffs.push({
|
||||||
@@ -137,16 +133,16 @@ export abstract class BaseFormatter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a single diff using smartdiff
|
|
||||||
*/
|
|
||||||
displayDiff(diff: ICheckResult['diffs'][0]): void {
|
displayDiff(diff: ICheckResult['diffs'][0]): void {
|
||||||
console.log(`\n--- ${diff.path}`);
|
console.log(`\n--- ${diff.path}`);
|
||||||
if (diff.before && diff.after) {
|
if (diff.before && diff.after) {
|
||||||
console.log(plugins.smartdiff.formatLineDiffForConsole(diff.before, diff.after));
|
console.log(plugins.smartdiff.formatUnifiedDiffForConsole(diff.before, diff.after, {
|
||||||
|
originalFileName: diff.path,
|
||||||
|
revisedFileName: diff.path,
|
||||||
|
context: 3,
|
||||||
|
}));
|
||||||
} else if (diff.after && !diff.before) {
|
} else if (diff.after && !diff.before) {
|
||||||
console.log(' (new file)');
|
console.log(' (new file)');
|
||||||
// Show first few lines of new content
|
|
||||||
const lines = diff.after.split('\n').slice(0, 10);
|
const lines = diff.after.split('\n').slice(0, 10);
|
||||||
lines.forEach(line => console.log(` + ${line}`));
|
lines.forEach(line => console.log(` + ${line}`));
|
||||||
if (diff.after.split('\n').length > 10) {
|
if (diff.after.split('\n').length > 10) {
|
||||||
@@ -157,9 +153,6 @@ export abstract class BaseFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Display all diffs from a check result
|
|
||||||
*/
|
|
||||||
displayAllDiffs(result: ICheckResult): void {
|
displayAllDiffs(result: ICheckResult): void {
|
||||||
if (!result.hasDiff) {
|
if (!result.hasDiff) {
|
||||||
console.log(' No changes detected');
|
console.log(' No changes detected');
|
||||||
|
|||||||
@@ -1,235 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
|
|
||||||
export interface IFileCache {
|
|
||||||
path: string;
|
|
||||||
checksum: string;
|
|
||||||
modified: number;
|
|
||||||
size: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICacheManifest {
|
|
||||||
version: string;
|
|
||||||
lastFormat: number;
|
|
||||||
files: IFileCache[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChangeCache {
|
|
||||||
private cacheDir: string;
|
|
||||||
private manifestPath: string;
|
|
||||||
private cacheVersion = '1.0.0';
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.cacheDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-cache');
|
|
||||||
this.manifestPath = plugins.path.join(this.cacheDir, 'manifest.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
|
||||||
await plugins.smartfs.directory(this.cacheDir).recursive().create();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getManifest(): Promise<ICacheManifest> {
|
|
||||||
const defaultManifest: ICacheManifest = {
|
|
||||||
version: this.cacheVersion,
|
|
||||||
lastFormat: 0,
|
|
||||||
files: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const exists = await plugins.smartfs.file(this.manifestPath).exists();
|
|
||||||
if (!exists) {
|
|
||||||
return defaultManifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = (await plugins.smartfs
|
|
||||||
.file(this.manifestPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
const manifest = JSON.parse(content);
|
|
||||||
|
|
||||||
// Validate the manifest structure
|
|
||||||
if (this.isValidManifest(manifest)) {
|
|
||||||
return manifest;
|
|
||||||
} else {
|
|
||||||
console.warn('Invalid manifest structure, returning default manifest');
|
|
||||||
return defaultManifest;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(
|
|
||||||
`Failed to read cache manifest: ${error.message}, returning default manifest`,
|
|
||||||
);
|
|
||||||
// Try to delete the corrupted file
|
|
||||||
try {
|
|
||||||
await plugins.smartfs.file(this.manifestPath).delete();
|
|
||||||
} catch (removeError) {
|
|
||||||
// Ignore removal errors
|
|
||||||
}
|
|
||||||
return defaultManifest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveManifest(manifest: ICacheManifest): Promise<void> {
|
|
||||||
// Validate before saving
|
|
||||||
if (!this.isValidManifest(manifest)) {
|
|
||||||
throw new Error('Invalid manifest structure, cannot save');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure directory exists
|
|
||||||
await plugins.smartfs.directory(this.cacheDir).recursive().create();
|
|
||||||
|
|
||||||
// Write directly with proper JSON stringification
|
|
||||||
const jsonContent = JSON.stringify(manifest, null, 2);
|
|
||||||
await plugins.smartfs
|
|
||||||
.file(this.manifestPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.write(jsonContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async hasFileChanged(filePath: string): Promise<boolean> {
|
|
||||||
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
||||||
? filePath
|
|
||||||
: plugins.path.join(paths.cwd, filePath);
|
|
||||||
|
|
||||||
// Check if file exists
|
|
||||||
const exists = await plugins.smartfs.file(absolutePath).exists();
|
|
||||||
if (!exists) {
|
|
||||||
return true; // File doesn't exist, so it's "changed" (will be created)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current file stats
|
|
||||||
const stats = await plugins.smartfs.file(absolutePath).stat();
|
|
||||||
|
|
||||||
// Skip directories
|
|
||||||
if (stats.isDirectory) {
|
|
||||||
return false; // Directories are not processed
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = (await plugins.smartfs
|
|
||||||
.file(absolutePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
const currentChecksum = this.calculateChecksum(content);
|
|
||||||
|
|
||||||
// Get cached info
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
const cachedFile = manifest.files.find((f) => f.path === filePath);
|
|
||||||
|
|
||||||
if (!cachedFile) {
|
|
||||||
return true; // Not in cache, so it's changed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare checksums
|
|
||||||
return (
|
|
||||||
cachedFile.checksum !== currentChecksum ||
|
|
||||||
cachedFile.size !== stats.size ||
|
|
||||||
cachedFile.modified !== stats.mtime.getTime()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateFileCache(filePath: string): Promise<void> {
|
|
||||||
const absolutePath = plugins.path.isAbsolute(filePath)
|
|
||||||
? filePath
|
|
||||||
: plugins.path.join(paths.cwd, filePath);
|
|
||||||
|
|
||||||
// Get current file stats
|
|
||||||
const stats = await plugins.smartfs.file(absolutePath).stat();
|
|
||||||
|
|
||||||
// Skip directories
|
|
||||||
if (stats.isDirectory) {
|
|
||||||
return; // Don't cache directories
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = (await plugins.smartfs
|
|
||||||
.file(absolutePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
const checksum = this.calculateChecksum(content);
|
|
||||||
|
|
||||||
// Update manifest
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
const existingIndex = manifest.files.findIndex((f) => f.path === filePath);
|
|
||||||
|
|
||||||
const cacheEntry: IFileCache = {
|
|
||||||
path: filePath,
|
|
||||||
checksum,
|
|
||||||
modified: stats.mtime.getTime(),
|
|
||||||
size: stats.size,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (existingIndex !== -1) {
|
|
||||||
manifest.files[existingIndex] = cacheEntry;
|
|
||||||
} else {
|
|
||||||
manifest.files.push(cacheEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest.lastFormat = Date.now();
|
|
||||||
await this.saveManifest(manifest);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getChangedFiles(filePaths: string[]): Promise<string[]> {
|
|
||||||
const changedFiles: string[] = [];
|
|
||||||
|
|
||||||
for (const filePath of filePaths) {
|
|
||||||
if (await this.hasFileChanged(filePath)) {
|
|
||||||
changedFiles.push(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changedFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
async clean(): Promise<void> {
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
const validFiles: IFileCache[] = [];
|
|
||||||
|
|
||||||
// Remove entries for files that no longer exist
|
|
||||||
for (const file of manifest.files) {
|
|
||||||
const absolutePath = plugins.path.isAbsolute(file.path)
|
|
||||||
? file.path
|
|
||||||
: plugins.path.join(paths.cwd, file.path);
|
|
||||||
|
|
||||||
if (await plugins.smartfs.file(absolutePath).exists()) {
|
|
||||||
validFiles.push(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest.files = validFiles;
|
|
||||||
await this.saveManifest(manifest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateChecksum(content: string | Buffer): string {
|
|
||||||
return plugins.crypto.createHash('sha256').update(content).digest('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
private isValidManifest(manifest: any): manifest is ICacheManifest {
|
|
||||||
// Check if manifest has the required structure
|
|
||||||
if (!manifest || typeof manifest !== 'object') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check required fields
|
|
||||||
if (
|
|
||||||
typeof manifest.version !== 'string' ||
|
|
||||||
typeof manifest.lastFormat !== 'number' ||
|
|
||||||
!Array.isArray(manifest.files)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each file entry
|
|
||||||
for (const file of manifest.files) {
|
|
||||||
if (
|
|
||||||
!file ||
|
|
||||||
typeof file !== 'object' ||
|
|
||||||
typeof file.path !== 'string' ||
|
|
||||||
typeof file.checksum !== 'string' ||
|
|
||||||
typeof file.modified !== 'number' ||
|
|
||||||
typeof file.size !== 'number'
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import { BaseFormatter } from './classes.baseformatter.js';
|
|
||||||
|
|
||||||
export interface IModuleDependency {
|
|
||||||
module: string;
|
|
||||||
dependencies: Set<string>;
|
|
||||||
dependents: Set<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DependencyAnalyzer {
|
|
||||||
private moduleDependencies: Map<string, IModuleDependency> = new Map();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.initializeDependencies();
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeDependencies(): void {
|
|
||||||
// Define dependencies between format modules
|
|
||||||
const dependencies = {
|
|
||||||
cleanup: [], // No dependencies
|
|
||||||
npmextra: [], // No dependencies
|
|
||||||
license: ['npmextra'], // Depends on npmextra for config
|
|
||||||
packagejson: ['npmextra'], // Depends on npmextra for config
|
|
||||||
templates: ['npmextra', 'packagejson'], // Depends on both
|
|
||||||
gitignore: ['templates'], // Depends on templates
|
|
||||||
tsconfig: ['packagejson'], // Depends on package.json
|
|
||||||
prettier: [
|
|
||||||
'cleanup',
|
|
||||||
'npmextra',
|
|
||||||
'packagejson',
|
|
||||||
'templates',
|
|
||||||
'gitignore',
|
|
||||||
'tsconfig',
|
|
||||||
], // Runs after most others
|
|
||||||
readme: ['npmextra', 'packagejson'], // Depends on project metadata
|
|
||||||
copy: ['npmextra'], // Depends on config
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize all modules
|
|
||||||
for (const [module, deps] of Object.entries(dependencies)) {
|
|
||||||
this.moduleDependencies.set(module, {
|
|
||||||
module,
|
|
||||||
dependencies: new Set(deps),
|
|
||||||
dependents: new Set(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build reverse dependencies (dependents)
|
|
||||||
for (const [module, deps] of Object.entries(dependencies)) {
|
|
||||||
for (const dep of deps) {
|
|
||||||
const depModule = this.moduleDependencies.get(dep);
|
|
||||||
if (depModule) {
|
|
||||||
depModule.dependents.add(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getExecutionGroups(modules: BaseFormatter[]): BaseFormatter[][] {
|
|
||||||
const modulesMap = new Map(modules.map((m) => [m.name, m]));
|
|
||||||
const executed = new Set<string>();
|
|
||||||
const groups: BaseFormatter[][] = [];
|
|
||||||
|
|
||||||
while (executed.size < modules.length) {
|
|
||||||
const currentGroup: BaseFormatter[] = [];
|
|
||||||
|
|
||||||
for (const module of modules) {
|
|
||||||
if (executed.has(module.name)) continue;
|
|
||||||
|
|
||||||
const dependency = this.moduleDependencies.get(module.name);
|
|
||||||
if (!dependency) {
|
|
||||||
// Unknown module, execute in isolation
|
|
||||||
currentGroup.push(module);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if all dependencies have been executed
|
|
||||||
const allDepsExecuted = Array.from(dependency.dependencies).every(
|
|
||||||
(dep) => executed.has(dep) || !modulesMap.has(dep),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (allDepsExecuted) {
|
|
||||||
currentGroup.push(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentGroup.length === 0) {
|
|
||||||
// Circular dependency or error - execute remaining modules
|
|
||||||
for (const module of modules) {
|
|
||||||
if (!executed.has(module.name)) {
|
|
||||||
currentGroup.push(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentGroup.forEach((m) => executed.add(m.name));
|
|
||||||
groups.push(currentGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
canRunInParallel(module1: string, module2: string): boolean {
|
|
||||||
const dep1 = this.moduleDependencies.get(module1);
|
|
||||||
const dep2 = this.moduleDependencies.get(module2);
|
|
||||||
|
|
||||||
if (!dep1 || !dep2) return false;
|
|
||||||
|
|
||||||
// Check if module1 depends on module2 or vice versa
|
|
||||||
return (
|
|
||||||
!dep1.dependencies.has(module2) &&
|
|
||||||
!dep2.dependencies.has(module1) &&
|
|
||||||
!dep1.dependents.has(module2) &&
|
|
||||||
!dep2.dependents.has(module1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,12 @@ import * as plugins from './mod.plugins.js';
|
|||||||
import { FormatContext } from './classes.formatcontext.js';
|
import { FormatContext } from './classes.formatcontext.js';
|
||||||
import { BaseFormatter } from './classes.baseformatter.js';
|
import { BaseFormatter } from './classes.baseformatter.js';
|
||||||
import type { IFormatPlan, IPlannedChange } from './interfaces.format.js';
|
import type { IFormatPlan, IPlannedChange } from './interfaces.format.js';
|
||||||
|
import { getModuleIcon } from './interfaces.format.js';
|
||||||
import { logger } from '../gitzone.logging.js';
|
import { logger } from '../gitzone.logging.js';
|
||||||
import { DependencyAnalyzer } from './classes.dependency-analyzer.js';
|
|
||||||
import { DiffReporter } from './classes.diffreporter.js';
|
import { DiffReporter } from './classes.diffreporter.js';
|
||||||
|
|
||||||
export class FormatPlanner {
|
export class FormatPlanner {
|
||||||
private plannedChanges: Map<string, IPlannedChange[]> = new Map();
|
private plannedChanges: Map<string, IPlannedChange[]> = new Map();
|
||||||
private dependencyAnalyzer = new DependencyAnalyzer();
|
|
||||||
private diffReporter = new DiffReporter();
|
private diffReporter = new DiffReporter();
|
||||||
|
|
||||||
async planFormat(modules: BaseFormatter[]): Promise<IFormatPlan> {
|
async planFormat(modules: BaseFormatter[]): Promise<IFormatPlan> {
|
||||||
@@ -18,7 +17,6 @@ export class FormatPlanner {
|
|||||||
filesAdded: 0,
|
filesAdded: 0,
|
||||||
filesModified: 0,
|
filesModified: 0,
|
||||||
filesRemoved: 0,
|
filesRemoved: 0,
|
||||||
estimatedTime: 0,
|
|
||||||
},
|
},
|
||||||
changes: [],
|
changes: [],
|
||||||
warnings: [],
|
warnings: [],
|
||||||
@@ -32,7 +30,6 @@ export class FormatPlanner {
|
|||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
plan.changes.push(change);
|
plan.changes.push(change);
|
||||||
|
|
||||||
// Update summary
|
|
||||||
switch (change.type) {
|
switch (change.type) {
|
||||||
case 'create':
|
case 'create':
|
||||||
plan.summary.filesAdded++;
|
plan.summary.filesAdded++;
|
||||||
@@ -58,7 +55,6 @@ export class FormatPlanner {
|
|||||||
plan.summary.filesAdded +
|
plan.summary.filesAdded +
|
||||||
plan.summary.filesModified +
|
plan.summary.filesModified +
|
||||||
plan.summary.filesRemoved;
|
plan.summary.filesRemoved;
|
||||||
plan.summary.estimatedTime = plan.summary.totalFiles * 100; // 100ms per file estimate
|
|
||||||
|
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
@@ -67,27 +63,20 @@ export class FormatPlanner {
|
|||||||
plan: IFormatPlan,
|
plan: IFormatPlan,
|
||||||
modules: BaseFormatter[],
|
modules: BaseFormatter[],
|
||||||
context: FormatContext,
|
context: FormatContext,
|
||||||
parallel: boolean = false,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
try {
|
for (const module of modules) {
|
||||||
// Always use sequential execution to avoid race conditions
|
const changes = this.plannedChanges.get(module.name) || [];
|
||||||
for (const module of modules) {
|
|
||||||
const changes = this.plannedChanges.get(module.name) || [];
|
|
||||||
|
|
||||||
if (changes.length > 0) {
|
if (changes.length > 0) {
|
||||||
logger.log('info', `Executing ${module.name} formatter...`);
|
logger.log('info', `Executing ${module.name} formatter...`);
|
||||||
await module.execute(changes);
|
await module.execute(changes);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const endTime = Date.now();
|
|
||||||
const duration = endTime - startTime;
|
|
||||||
logger.log('info', `Format operations completed in ${duration}ms`);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const duration = Date.now() - startTime;
|
||||||
|
logger.log('info', `Format operations completed in ${duration}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async displayPlan(
|
async displayPlan(
|
||||||
@@ -103,7 +92,6 @@ export class FormatPlanner {
|
|||||||
console.log('');
|
console.log('');
|
||||||
console.log('Changes by module:');
|
console.log('Changes by module:');
|
||||||
|
|
||||||
// Group changes by module
|
|
||||||
const changesByModule = new Map<string, IPlannedChange[]>();
|
const changesByModule = new Map<string, IPlannedChange[]>();
|
||||||
for (const change of plan.changes) {
|
for (const change of plan.changes) {
|
||||||
const moduleChanges = changesByModule.get(change.module) || [];
|
const moduleChanges = changesByModule.get(change.module) || [];
|
||||||
@@ -113,14 +101,13 @@ export class FormatPlanner {
|
|||||||
|
|
||||||
for (const [module, changes] of changesByModule) {
|
for (const [module, changes] of changesByModule) {
|
||||||
console.log(
|
console.log(
|
||||||
`\n${this.getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`,
|
`\n${getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
const icon = this.getChangeIcon(change.type);
|
const icon = this.getChangeIcon(change.type);
|
||||||
console.log(` ${icon} ${change.path} - ${change.description}`);
|
console.log(` ${icon} ${change.path} - ${change.description}`);
|
||||||
|
|
||||||
// Show diff for modified files if detailed view is requested
|
|
||||||
if (detailed && change.type === 'modify') {
|
if (detailed && change.type === 'modify') {
|
||||||
const diff = await this.diffReporter.generateDiffForChange(change);
|
const diff = await this.diffReporter.generateDiffForChange(change);
|
||||||
if (diff) {
|
if (diff) {
|
||||||
@@ -141,22 +128,6 @@ export class FormatPlanner {
|
|||||||
console.log('\n' + '━'.repeat(50));
|
console.log('\n' + '━'.repeat(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getModuleIcon(module: string): string {
|
|
||||||
const icons: Record<string, string> = {
|
|
||||||
packagejson: '📦',
|
|
||||||
license: '📝',
|
|
||||||
tsconfig: '🔧',
|
|
||||||
cleanup: '🚮',
|
|
||||||
gitignore: '🔒',
|
|
||||||
prettier: '✨',
|
|
||||||
readme: '📖',
|
|
||||||
templates: '📄',
|
|
||||||
npmextra: '⚙️',
|
|
||||||
copy: '📋',
|
|
||||||
};
|
|
||||||
return icons[module] || '📁';
|
|
||||||
}
|
|
||||||
|
|
||||||
private getChangeIcon(type: 'create' | 'modify' | 'delete'): string {
|
private getChangeIcon(type: 'create' | 'modify' | 'delete'): string {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'create':
|
case 'create':
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.js';
|
||||||
import { logger } from '../gitzone.logging.js';
|
import { logger } from '../gitzone.logging.js';
|
||||||
|
import { getModuleIcon } from './interfaces.format.js';
|
||||||
|
|
||||||
export interface IModuleStats {
|
export interface IModuleStats {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -23,8 +24,6 @@ export interface IFormatStats {
|
|||||||
totalModified: number;
|
totalModified: number;
|
||||||
totalDeleted: number;
|
totalDeleted: number;
|
||||||
totalErrors: number;
|
totalErrors: number;
|
||||||
cacheHits: number;
|
|
||||||
cacheMisses: number;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +42,6 @@ export class FormatStats {
|
|||||||
totalModified: 0,
|
totalModified: 0,
|
||||||
totalDeleted: 0,
|
totalDeleted: 0,
|
||||||
totalErrors: 0,
|
totalErrors: 0,
|
||||||
cacheHits: 0,
|
|
||||||
cacheMisses: 0,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -107,14 +104,6 @@ export class FormatStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recordCacheHit(): void {
|
|
||||||
this.stats.overallStats.cacheHits++;
|
|
||||||
}
|
|
||||||
|
|
||||||
recordCacheMiss(): void {
|
|
||||||
this.stats.overallStats.cacheMisses++;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish(): void {
|
finish(): void {
|
||||||
this.stats.endTime = Date.now();
|
this.stats.endTime = Date.now();
|
||||||
this.stats.totalExecutionTime = this.stats.endTime - this.stats.startTime;
|
this.stats.totalExecutionTime = this.stats.endTime - this.stats.startTime;
|
||||||
@@ -135,20 +124,6 @@ export class FormatStats {
|
|||||||
console.log(` • Deleted: ${this.stats.overallStats.totalDeleted}`);
|
console.log(` • Deleted: ${this.stats.overallStats.totalDeleted}`);
|
||||||
console.log(` Errors: ${this.stats.overallStats.totalErrors}`);
|
console.log(` Errors: ${this.stats.overallStats.totalErrors}`);
|
||||||
|
|
||||||
if (
|
|
||||||
this.stats.overallStats.cacheHits > 0 ||
|
|
||||||
this.stats.overallStats.cacheMisses > 0
|
|
||||||
) {
|
|
||||||
const cacheHitRate =
|
|
||||||
(this.stats.overallStats.cacheHits /
|
|
||||||
(this.stats.overallStats.cacheHits +
|
|
||||||
this.stats.overallStats.cacheMisses)) *
|
|
||||||
100;
|
|
||||||
console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);
|
|
||||||
console.log(` • Hits: ${this.stats.overallStats.cacheHits}`);
|
|
||||||
console.log(` • Misses: ${this.stats.overallStats.cacheMisses}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Module stats
|
// Module stats
|
||||||
console.log('\nModule Breakdown:');
|
console.log('\nModule Breakdown:');
|
||||||
console.log('─'.repeat(50));
|
console.log('─'.repeat(50));
|
||||||
@@ -159,7 +134,7 @@ export class FormatStats {
|
|||||||
|
|
||||||
for (const moduleStats of sortedModules) {
|
for (const moduleStats of sortedModules) {
|
||||||
console.log(
|
console.log(
|
||||||
`\n${this.getModuleIcon(moduleStats.name)} ${moduleStats.name}:`,
|
`\n${getModuleIcon(moduleStats.name)} ${moduleStats.name}:`,
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
` Execution Time: ${this.formatDuration(moduleStats.executionTime)}`,
|
` Execution Time: ${this.formatDuration(moduleStats.executionTime)}`,
|
||||||
@@ -211,19 +186,4 @@ export class FormatStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getModuleIcon(module: string): string {
|
|
||||||
const icons: Record<string, string> = {
|
|
||||||
packagejson: '📦',
|
|
||||||
license: '📝',
|
|
||||||
tsconfig: '🔧',
|
|
||||||
cleanup: '🚮',
|
|
||||||
gitignore: '🔒',
|
|
||||||
prettier: '✨',
|
|
||||||
readme: '📖',
|
|
||||||
templates: '📄',
|
|
||||||
npmextra: '⚙️',
|
|
||||||
copy: '📋',
|
|
||||||
};
|
|
||||||
return icons[module] || '📁';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,340 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
import type { IFormatOperation } from './interfaces.format.js';
|
|
||||||
|
|
||||||
export class RollbackManager {
|
|
||||||
private backupDir: string;
|
|
||||||
private manifestPath: string;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.backupDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-backups');
|
|
||||||
this.manifestPath = plugins.path.join(this.backupDir, 'manifest.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
async createOperation(): Promise<IFormatOperation> {
|
|
||||||
await this.ensureBackupDir();
|
|
||||||
|
|
||||||
const operation: IFormatOperation = {
|
|
||||||
id: this.generateOperationId(),
|
|
||||||
timestamp: Date.now(),
|
|
||||||
files: [],
|
|
||||||
status: 'pending',
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.updateManifest(operation);
|
|
||||||
return operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
async backupFile(filepath: string, operationId: string): Promise<void> {
|
|
||||||
const operation = await this.getOperation(operationId);
|
|
||||||
if (!operation) {
|
|
||||||
throw new Error(`Operation ${operationId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const absolutePath = plugins.path.isAbsolute(filepath)
|
|
||||||
? filepath
|
|
||||||
: plugins.path.join(paths.cwd, filepath);
|
|
||||||
|
|
||||||
// Check if file exists
|
|
||||||
const exists = await plugins.smartfs.file(absolutePath).exists();
|
|
||||||
if (!exists) {
|
|
||||||
// File doesn't exist yet (will be created), so we skip backup
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read file content and metadata
|
|
||||||
const content = (await plugins.smartfs
|
|
||||||
.file(absolutePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
const stats = await plugins.smartfs.file(absolutePath).stat();
|
|
||||||
const checksum = this.calculateChecksum(content);
|
|
||||||
|
|
||||||
// Create backup
|
|
||||||
const backupPath = this.getBackupPath(operationId, filepath);
|
|
||||||
await plugins.smartfs
|
|
||||||
.directory(plugins.path.dirname(backupPath))
|
|
||||||
.recursive()
|
|
||||||
.create();
|
|
||||||
await plugins.smartfs.file(backupPath).encoding('utf8').write(content);
|
|
||||||
|
|
||||||
// Update operation
|
|
||||||
operation.files.push({
|
|
||||||
path: filepath,
|
|
||||||
originalContent: content,
|
|
||||||
checksum,
|
|
||||||
permissions: stats.mode.toString(8),
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.updateManifest(operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
async rollback(operationId: string): Promise<void> {
|
|
||||||
const operation = await this.getOperation(operationId);
|
|
||||||
if (!operation) {
|
|
||||||
// Operation doesn't exist, might have already been rolled back or never created
|
|
||||||
console.warn(`Operation ${operationId} not found for rollback, skipping`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operation.status === 'rolled-back') {
|
|
||||||
throw new Error(`Operation ${operationId} has already been rolled back`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore files in reverse order
|
|
||||||
for (let i = operation.files.length - 1; i >= 0; i--) {
|
|
||||||
const file = operation.files[i];
|
|
||||||
const absolutePath = plugins.path.isAbsolute(file.path)
|
|
||||||
? file.path
|
|
||||||
: plugins.path.join(paths.cwd, file.path);
|
|
||||||
|
|
||||||
// Verify backup integrity
|
|
||||||
const backupPath = this.getBackupPath(operationId, file.path);
|
|
||||||
const backupContent = await plugins.smartfs
|
|
||||||
.file(backupPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read();
|
|
||||||
const backupChecksum = this.calculateChecksum(backupContent);
|
|
||||||
|
|
||||||
if (backupChecksum !== file.checksum) {
|
|
||||||
throw new Error(`Backup integrity check failed for ${file.path}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore file
|
|
||||||
await plugins.smartfs
|
|
||||||
.file(absolutePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.write(file.originalContent);
|
|
||||||
|
|
||||||
// Restore permissions
|
|
||||||
const mode = parseInt(file.permissions, 8);
|
|
||||||
// Note: Permissions restoration may not work on all platforms
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update operation status
|
|
||||||
operation.status = 'rolled-back';
|
|
||||||
await this.updateManifest(operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
async markComplete(operationId: string): Promise<void> {
|
|
||||||
const operation = await this.getOperation(operationId);
|
|
||||||
if (!operation) {
|
|
||||||
throw new Error(`Operation ${operationId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
operation.status = 'completed';
|
|
||||||
await this.updateManifest(operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
async cleanOldBackups(retentionDays: number): Promise<void> {
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
const cutoffTime = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
const operationsToDelete = manifest.operations.filter(
|
|
||||||
(op) => op.timestamp < cutoffTime && op.status === 'completed',
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const operation of operationsToDelete) {
|
|
||||||
// Remove backup files
|
|
||||||
const operationDir = plugins.path.join(
|
|
||||||
this.backupDir,
|
|
||||||
'operations',
|
|
||||||
operation.id,
|
|
||||||
);
|
|
||||||
await plugins.smartfs.directory(operationDir).recursive().delete();
|
|
||||||
|
|
||||||
// Remove from manifest
|
|
||||||
manifest.operations = manifest.operations.filter(
|
|
||||||
(op) => op.id !== operation.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.saveManifest(manifest);
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyBackup(operationId: string): Promise<boolean> {
|
|
||||||
const operation = await this.getOperation(operationId);
|
|
||||||
if (!operation) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const file of operation.files) {
|
|
||||||
const backupPath = this.getBackupPath(operationId, file.path);
|
|
||||||
const exists = await plugins.smartfs.file(backupPath).exists();
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = await plugins.smartfs
|
|
||||||
.file(backupPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read();
|
|
||||||
const checksum = this.calculateChecksum(content);
|
|
||||||
|
|
||||||
if (checksum !== file.checksum) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async listBackups(): Promise<IFormatOperation[]> {
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
return manifest.operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async ensureBackupDir(): Promise<void> {
|
|
||||||
await plugins.smartfs.directory(this.backupDir).recursive().create();
|
|
||||||
await plugins.smartfs
|
|
||||||
.directory(plugins.path.join(this.backupDir, 'operations'))
|
|
||||||
.recursive()
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
private generateOperationId(): string {
|
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
||||||
const random = Math.random().toString(36).substring(2, 8);
|
|
||||||
return `${timestamp}-${random}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getBackupPath(operationId: string, filepath: string): string {
|
|
||||||
const filename = plugins.path.basename(filepath);
|
|
||||||
const dir = plugins.path.dirname(filepath);
|
|
||||||
const safeDir = dir.replace(/[/\\]/g, '__');
|
|
||||||
return plugins.path.join(
|
|
||||||
this.backupDir,
|
|
||||||
'operations',
|
|
||||||
operationId,
|
|
||||||
'files',
|
|
||||||
safeDir,
|
|
||||||
`${filename}.backup`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateChecksum(content: string | Buffer): string {
|
|
||||||
return plugins.crypto.createHash('sha256').update(content).digest('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getManifest(): Promise<{ operations: IFormatOperation[] }> {
|
|
||||||
const defaultManifest = { operations: [] };
|
|
||||||
|
|
||||||
const exists = await plugins.smartfs.file(this.manifestPath).exists();
|
|
||||||
if (!exists) {
|
|
||||||
return defaultManifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = (await plugins.smartfs
|
|
||||||
.file(this.manifestPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
const manifest = JSON.parse(content);
|
|
||||||
|
|
||||||
// Validate the manifest structure
|
|
||||||
if (this.isValidManifest(manifest)) {
|
|
||||||
return manifest;
|
|
||||||
} else {
|
|
||||||
console.warn(
|
|
||||||
'Invalid rollback manifest structure, returning default manifest',
|
|
||||||
);
|
|
||||||
return defaultManifest;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(
|
|
||||||
`Failed to read rollback manifest: ${error.message}, returning default manifest`,
|
|
||||||
);
|
|
||||||
// Try to delete the corrupted file
|
|
||||||
try {
|
|
||||||
await plugins.smartfs.file(this.manifestPath).delete();
|
|
||||||
} catch (removeError) {
|
|
||||||
// Ignore removal errors
|
|
||||||
}
|
|
||||||
return defaultManifest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveManifest(manifest: {
|
|
||||||
operations: IFormatOperation[];
|
|
||||||
}): Promise<void> {
|
|
||||||
// Validate before saving
|
|
||||||
if (!this.isValidManifest(manifest)) {
|
|
||||||
throw new Error('Invalid rollback manifest structure, cannot save');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure directory exists
|
|
||||||
await this.ensureBackupDir();
|
|
||||||
|
|
||||||
// Write directly with proper JSON stringification
|
|
||||||
const jsonContent = JSON.stringify(manifest, null, 2);
|
|
||||||
await plugins.smartfs
|
|
||||||
.file(this.manifestPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.write(jsonContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getOperation(
|
|
||||||
operationId: string,
|
|
||||||
): Promise<IFormatOperation | null> {
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
return manifest.operations.find((op) => op.id === operationId) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateManifest(operation: IFormatOperation): Promise<void> {
|
|
||||||
const manifest = await this.getManifest();
|
|
||||||
const existingIndex = manifest.operations.findIndex(
|
|
||||||
(op) => op.id === operation.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingIndex !== -1) {
|
|
||||||
manifest.operations[existingIndex] = operation;
|
|
||||||
} else {
|
|
||||||
manifest.operations.push(operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.saveManifest(manifest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isValidManifest(
|
|
||||||
manifest: any,
|
|
||||||
): manifest is { operations: IFormatOperation[] } {
|
|
||||||
// Check if manifest has the required structure
|
|
||||||
if (!manifest || typeof manifest !== 'object') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check required fields
|
|
||||||
if (!Array.isArray(manifest.operations)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each operation entry
|
|
||||||
for (const operation of manifest.operations) {
|
|
||||||
if (
|
|
||||||
!operation ||
|
|
||||||
typeof operation !== 'object' ||
|
|
||||||
typeof operation.id !== 'string' ||
|
|
||||||
typeof operation.timestamp !== 'number' ||
|
|
||||||
typeof operation.status !== 'string' ||
|
|
||||||
!Array.isArray(operation.files)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each file in the operation
|
|
||||||
for (const file of operation.files) {
|
|
||||||
if (
|
|
||||||
!file ||
|
|
||||||
typeof file !== 'object' ||
|
|
||||||
typeof file.path !== 'string' ||
|
|
||||||
typeof file.checksum !== 'string'
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
const filesToDelete = [
|
|
||||||
'defaults.yml',
|
|
||||||
'yarn.lock',
|
|
||||||
'package-lock.json',
|
|
||||||
'tslint.json',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
for (const relativeFilePath of filesToDelete) {
|
|
||||||
const fileExists = await plugins.smartfs.file(relativeFilePath).exists();
|
|
||||||
if (fileExists) {
|
|
||||||
logger.log('info', `Found ${relativeFilePath}! Removing it!`);
|
|
||||||
await plugins.smartfs
|
|
||||||
.file(plugins.path.join(paths.cwd, relativeFilePath))
|
|
||||||
.delete();
|
|
||||||
} else {
|
|
||||||
logger.log('info', `Project is free of ${relativeFilePath}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import type { Project } from '../classes.project.js';
|
|
||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
const gitzoneConfig = await projectArg.gitzoneConfig;
|
|
||||||
|
|
||||||
// Get copy configuration from npmextra.json
|
|
||||||
const npmextraConfig = new plugins.npmextra.Npmextra();
|
|
||||||
const copyConfig = npmextraConfig.dataFor<any>('gitzone.format.copy', {
|
|
||||||
patterns: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!copyConfig.patterns || copyConfig.patterns.length === 0) {
|
|
||||||
logger.log('info', 'No copy patterns configured in npmextra.json');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const pattern of copyConfig.patterns) {
|
|
||||||
if (!pattern.from || !pattern.to) {
|
|
||||||
logger.log('warn', 'Invalid copy pattern - missing "from" or "to" field');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Handle glob patterns
|
|
||||||
const entries = await plugins.smartfs
|
|
||||||
.directory('.')
|
|
||||||
.recursive()
|
|
||||||
.filter(pattern.from)
|
|
||||||
.list();
|
|
||||||
const files = entries.map((entry) => entry.path);
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
const sourcePath = file;
|
|
||||||
let destPath = pattern.to;
|
|
||||||
|
|
||||||
// If destination is a directory, preserve filename
|
|
||||||
if (pattern.to.endsWith('/')) {
|
|
||||||
const filename = plugins.path.basename(file);
|
|
||||||
destPath = plugins.path.join(pattern.to, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle template variables in destination path
|
|
||||||
if (pattern.preservePath) {
|
|
||||||
const relativePath = plugins.path.relative(
|
|
||||||
plugins.path.dirname(pattern.from.replace(/\*/g, '')),
|
|
||||||
file,
|
|
||||||
);
|
|
||||||
destPath = plugins.path.join(pattern.to, relativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure destination directory exists
|
|
||||||
await plugins.smartfs
|
|
||||||
.directory(plugins.path.dirname(destPath))
|
|
||||||
.recursive()
|
|
||||||
.create();
|
|
||||||
|
|
||||||
// Copy file
|
|
||||||
await plugins.smartfs.file(sourcePath).copy(destPath);
|
|
||||||
logger.log('info', `Copied ${sourcePath} to ${destPath}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.log(
|
|
||||||
'error',
|
|
||||||
`Failed to copy pattern ${pattern.from}: ${error.message}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example npmextra.json configuration:
|
|
||||||
* {
|
|
||||||
* "gitzone": {
|
|
||||||
* "format": {
|
|
||||||
* "copy": {
|
|
||||||
* "patterns": [
|
|
||||||
* {
|
|
||||||
* "from": "src/assets/*",
|
|
||||||
* "to": "dist/assets/",
|
|
||||||
* "preservePath": true
|
|
||||||
* },
|
|
||||||
* {
|
|
||||||
* "from": "config/*.json",
|
|
||||||
* "to": "dist/"
|
|
||||||
* }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
const gitignorePath = plugins.path.join(paths.cwd, './.gitignore');
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
const gitignoreExists = await plugins.smartfs.file(gitignorePath).exists();
|
|
||||||
let customContent = '';
|
|
||||||
|
|
||||||
if (gitignoreExists) {
|
|
||||||
// lets get the existing gitignore file
|
|
||||||
const existingGitIgnoreString = (await plugins.smartfs
|
|
||||||
.file(gitignorePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
|
|
||||||
// Check for different custom section markers
|
|
||||||
const customMarkers = ['#------# custom', '# custom'];
|
|
||||||
for (const marker of customMarkers) {
|
|
||||||
const splitResult = existingGitIgnoreString.split(marker);
|
|
||||||
if (splitResult.length > 1) {
|
|
||||||
// Get everything after the marker (excluding the marker itself)
|
|
||||||
customContent = splitResult[1].trim();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the template
|
|
||||||
const templateModule = await import('../mod_template/index.js');
|
|
||||||
const ciTemplate = await templateModule.getTemplate('gitignore');
|
|
||||||
await ciTemplate.writeToDisk(paths.cwd);
|
|
||||||
|
|
||||||
// Append the custom content if it exists
|
|
||||||
if (customContent) {
|
|
||||||
const newGitignoreContent = (await plugins.smartfs
|
|
||||||
.file(gitignorePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
// The template already ends with "#------# custom", so just append the content
|
|
||||||
const finalContent =
|
|
||||||
newGitignoreContent.trimEnd() + '\n' + customContent + '\n';
|
|
||||||
await plugins.smartfs
|
|
||||||
.file(gitignorePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.write(finalContent);
|
|
||||||
logger.log('info', 'Updated .gitignore while preserving custom section!');
|
|
||||||
} else {
|
|
||||||
logger.log('info', 'Added a .gitignore!');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
|
|
||||||
const incompatibleLicenses: string[] = ['AGPL', 'GPL', 'SSPL'];
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
const nodeModulesInstalled = await plugins.smartfs
|
|
||||||
.directory(plugins.path.join(paths.cwd, 'node_modules'))
|
|
||||||
.exists();
|
|
||||||
if (!nodeModulesInstalled) {
|
|
||||||
logger.log('warn', 'No node_modules found. Skipping license check');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const licenseChecker = await plugins.smartlegal.createLicenseChecker();
|
|
||||||
const licenseCheckResult = await licenseChecker.excludeLicenseWithinPath(
|
|
||||||
paths.cwd,
|
|
||||||
incompatibleLicenses,
|
|
||||||
);
|
|
||||||
if (licenseCheckResult.failingModules.length === 0) {
|
|
||||||
logger.log('info', 'Success -> licenses passed!');
|
|
||||||
} else {
|
|
||||||
logger.log('error', 'Error -> licenses failed. Here is why:');
|
|
||||||
for (const failedModule of licenseCheckResult.failingModules) {
|
|
||||||
console.log(
|
|
||||||
`${failedModule.name} fails with license ${failedModule.license}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
import * as gulpFunction from '@push.rocks/gulp-function';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Migrates npmextra.json from old namespace keys to new package-scoped keys
|
|
||||||
*/
|
|
||||||
const migrateNamespaceKeys = (npmextraJson: any): boolean => {
|
|
||||||
let migrated = false;
|
|
||||||
const migrations = [
|
|
||||||
{ oldKey: 'gitzone', newKey: '@git.zone/cli' },
|
|
||||||
{ oldKey: 'tsdoc', newKey: '@git.zone/tsdoc' },
|
|
||||||
{ oldKey: 'npmdocker', newKey: '@git.zone/tsdocker' },
|
|
||||||
{ oldKey: 'npmci', newKey: '@ship.zone/szci' },
|
|
||||||
{ oldKey: 'szci', newKey: '@ship.zone/szci' },
|
|
||||||
];
|
|
||||||
for (const { oldKey, newKey } of migrations) {
|
|
||||||
if (npmextraJson[oldKey] && !npmextraJson[newKey]) {
|
|
||||||
npmextraJson[newKey] = npmextraJson[oldKey];
|
|
||||||
delete npmextraJson[oldKey];
|
|
||||||
migrated = true;
|
|
||||||
console.log(`Migrated npmextra.json: ${oldKey} -> ${newKey}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return migrated;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Migrates npmAccessLevel from @ship.zone/szci to @git.zone/cli.release.accessLevel
|
|
||||||
* This is a one-time migration for projects using the old location
|
|
||||||
*/
|
|
||||||
const migrateAccessLevel = (npmextraJson: any): boolean => {
|
|
||||||
const szciConfig = npmextraJson['@ship.zone/szci'];
|
|
||||||
|
|
||||||
// Check if szci has npmAccessLevel that needs to be migrated
|
|
||||||
if (!szciConfig?.npmAccessLevel) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we already have the new location
|
|
||||||
const gitzoneConfig = npmextraJson['@git.zone/cli'] || {};
|
|
||||||
if (gitzoneConfig?.release?.accessLevel) {
|
|
||||||
// Already migrated, just remove from szci
|
|
||||||
delete szciConfig.npmAccessLevel;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure @git.zone/cli and release exist
|
|
||||||
if (!npmextraJson['@git.zone/cli']) {
|
|
||||||
npmextraJson['@git.zone/cli'] = {};
|
|
||||||
}
|
|
||||||
if (!npmextraJson['@git.zone/cli'].release) {
|
|
||||||
npmextraJson['@git.zone/cli'].release = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate the value
|
|
||||||
npmextraJson['@git.zone/cli'].release.accessLevel = szciConfig.npmAccessLevel;
|
|
||||||
delete szciConfig.npmAccessLevel;
|
|
||||||
|
|
||||||
console.log(`Migrated npmAccessLevel to @git.zone/cli.release.accessLevel`);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* runs the npmextra file checking
|
|
||||||
*/
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
const formatSmartstream = new plugins.smartstream.StreamWrapper([
|
|
||||||
plugins.smartgulp.src([`npmextra.json`]),
|
|
||||||
gulpFunction.forEach(async (fileArg: plugins.smartfile.SmartFile) => {
|
|
||||||
const fileString = fileArg.contents.toString();
|
|
||||||
const npmextraJson = JSON.parse(fileString);
|
|
||||||
|
|
||||||
// Migrate old namespace keys to new package-scoped keys
|
|
||||||
migrateNamespaceKeys(npmextraJson);
|
|
||||||
|
|
||||||
// Migrate npmAccessLevel from szci to @git.zone/cli.release.accessLevel
|
|
||||||
migrateAccessLevel(npmextraJson);
|
|
||||||
|
|
||||||
if (!npmextraJson['@git.zone/cli']) {
|
|
||||||
npmextraJson['@git.zone/cli'] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const expectedRepoInformation: string[] = [
|
|
||||||
'projectType',
|
|
||||||
'module.githost',
|
|
||||||
'module.gitscope',
|
|
||||||
'module.gitrepo',
|
|
||||||
'module.description',
|
|
||||||
'module.npmPackagename',
|
|
||||||
'module.license',
|
|
||||||
];
|
|
||||||
|
|
||||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
|
||||||
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
|
||||||
if (
|
|
||||||
!plugins.smartobject.smartGet(
|
|
||||||
npmextraJson['@git.zone/cli'],
|
|
||||||
expectedRepoInformationItem,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
interactInstance.addQuestions([
|
|
||||||
{
|
|
||||||
message: `What is the value of ${expectedRepoInformationItem}`,
|
|
||||||
name: expectedRepoInformationItem,
|
|
||||||
type: 'input',
|
|
||||||
default: 'undefined variable',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const answerbucket = await interactInstance.runQueue();
|
|
||||||
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
|
||||||
const cliProvidedValue = answerbucket.getAnswerFor(
|
|
||||||
expectedRepoInformationItem,
|
|
||||||
);
|
|
||||||
if (cliProvidedValue) {
|
|
||||||
plugins.smartobject.smartAdd(
|
|
||||||
npmextraJson['@git.zone/cli'],
|
|
||||||
expectedRepoInformationItem,
|
|
||||||
cliProvidedValue,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete obsolete
|
|
||||||
// tbd
|
|
||||||
|
|
||||||
if (!npmextraJson['@ship.zone/szci']) {
|
|
||||||
npmextraJson['@ship.zone/szci'] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
fileArg.setContentsFromString(JSON.stringify(npmextraJson, null, 2));
|
|
||||||
}),
|
|
||||||
plugins.smartgulp.replace(),
|
|
||||||
]);
|
|
||||||
await formatSmartstream.run().catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
import * as gulpFunction from '@push.rocks/gulp-function';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ensures a certain dependency
|
|
||||||
*/
|
|
||||||
const ensureDependency = async (
|
|
||||||
packageJsonObjectArg: any,
|
|
||||||
position: 'dep' | 'devDep' | 'everywhere',
|
|
||||||
constraint: 'exclude' | 'include' | 'latest',
|
|
||||||
dependencyArg: string,
|
|
||||||
) => {
|
|
||||||
const [packageName, version] = dependencyArg.includes('@')
|
|
||||||
? dependencyArg.split('@').filter(Boolean)
|
|
||||||
: [dependencyArg, 'latest'];
|
|
||||||
|
|
||||||
const targetSections: string[] = [];
|
|
||||||
|
|
||||||
switch (position) {
|
|
||||||
case 'dep':
|
|
||||||
targetSections.push('dependencies');
|
|
||||||
break;
|
|
||||||
case 'devDep':
|
|
||||||
targetSections.push('devDependencies');
|
|
||||||
break;
|
|
||||||
case 'everywhere':
|
|
||||||
targetSections.push('dependencies', 'devDependencies');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const section of targetSections) {
|
|
||||||
if (!packageJsonObjectArg[section]) {
|
|
||||||
packageJsonObjectArg[section] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (constraint) {
|
|
||||||
case 'exclude':
|
|
||||||
delete packageJsonObjectArg[section][packageName];
|
|
||||||
break;
|
|
||||||
case 'include':
|
|
||||||
if (!packageJsonObjectArg[section][packageName]) {
|
|
||||||
packageJsonObjectArg[section][packageName] =
|
|
||||||
version === 'latest' ? '^1.0.0' : version;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'latest':
|
|
||||||
// Fetch latest version from npm
|
|
||||||
try {
|
|
||||||
const registry = new plugins.smartnpm.NpmRegistry();
|
|
||||||
const packageInfo = await registry.getPackageInfo(packageName);
|
|
||||||
const latestVersion = packageInfo['dist-tags'].latest;
|
|
||||||
packageJsonObjectArg[section][packageName] = `^${latestVersion}`;
|
|
||||||
} catch (error) {
|
|
||||||
logger.log(
|
|
||||||
'warn',
|
|
||||||
`Could not fetch latest version for ${packageName}, using existing or default`,
|
|
||||||
);
|
|
||||||
if (!packageJsonObjectArg[section][packageName]) {
|
|
||||||
packageJsonObjectArg[section][packageName] =
|
|
||||||
version === 'latest' ? '^1.0.0' : version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
const formatStreamWrapper = new plugins.smartstream.StreamWrapper([
|
|
||||||
plugins.smartgulp.src([`package.json`]),
|
|
||||||
gulpFunction.forEach(async (fileArg: plugins.smartfile.SmartFile) => {
|
|
||||||
const npmextraConfig = new plugins.npmextra.Npmextra(paths.cwd);
|
|
||||||
const gitzoneData: any = npmextraConfig.dataFor('@git.zone/cli', {});
|
|
||||||
const fileString = fileArg.contents.toString();
|
|
||||||
const packageJson = JSON.parse(fileString);
|
|
||||||
|
|
||||||
// metadata
|
|
||||||
packageJson.repository = {
|
|
||||||
type: 'git',
|
|
||||||
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
|
|
||||||
};
|
|
||||||
((packageJson.bugs = {
|
|
||||||
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
|
|
||||||
}),
|
|
||||||
(packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`));
|
|
||||||
|
|
||||||
// Check for module type
|
|
||||||
if (!packageJson.type) {
|
|
||||||
logger.log('info', `setting packageJson.type to "module"`);
|
|
||||||
packageJson.type = 'module';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for private or public
|
|
||||||
if (packageJson.private !== undefined) {
|
|
||||||
logger.log(
|
|
||||||
'info',
|
|
||||||
'Success -> found private/public info in package.json!',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logger.log(
|
|
||||||
'error',
|
|
||||||
'found no private boolean! Setting it to private for now!',
|
|
||||||
);
|
|
||||||
packageJson.private = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for license
|
|
||||||
if (packageJson.license) {
|
|
||||||
logger.log('info', 'Success -> found license in package.json!');
|
|
||||||
} else {
|
|
||||||
logger.log(
|
|
||||||
'error',
|
|
||||||
'found no license! Setting it to UNLICENSED for now!',
|
|
||||||
);
|
|
||||||
packageJson.license = 'UNLICENSED';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for build script
|
|
||||||
if (packageJson.scripts.build) {
|
|
||||||
logger.log('info', 'Success -> found build script in package.json!');
|
|
||||||
} else {
|
|
||||||
logger.log(
|
|
||||||
'error',
|
|
||||||
'found no build script! Putting a placeholder there for now!',
|
|
||||||
);
|
|
||||||
packageJson.scripts.build = `echo "Not needed for now"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for buildDocs script
|
|
||||||
if (!packageJson.scripts.buildDocs) {
|
|
||||||
logger.log(
|
|
||||||
'info',
|
|
||||||
'found no buildDocs script! Putting tsdoc script there now.',
|
|
||||||
);
|
|
||||||
packageJson.scripts.buildDocs = `tsdoc`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for files
|
|
||||||
packageJson.files = [
|
|
||||||
'ts/**/*',
|
|
||||||
'ts_web/**/*',
|
|
||||||
'dist/**/*',
|
|
||||||
'dist_*/**/*',
|
|
||||||
'dist_ts/**/*',
|
|
||||||
'dist_ts_web/**/*',
|
|
||||||
'assets/**/*',
|
|
||||||
'cli.js',
|
|
||||||
'npmextra.json',
|
|
||||||
'readme.md',
|
|
||||||
];
|
|
||||||
|
|
||||||
// check for dependencies
|
|
||||||
// Note: @push.rocks/tapbundle is deprecated - use @git.zone/tstest/tapbundle instead
|
|
||||||
await ensureDependency(
|
|
||||||
packageJson,
|
|
||||||
'devDep',
|
|
||||||
'exclude',
|
|
||||||
'@push.rocks/tapbundle',
|
|
||||||
);
|
|
||||||
await ensureDependency(
|
|
||||||
packageJson,
|
|
||||||
'devDep',
|
|
||||||
'latest',
|
|
||||||
'@git.zone/tstest',
|
|
||||||
);
|
|
||||||
await ensureDependency(
|
|
||||||
packageJson,
|
|
||||||
'devDep',
|
|
||||||
'latest',
|
|
||||||
'@git.zone/tsbuild',
|
|
||||||
);
|
|
||||||
|
|
||||||
// set overrides
|
|
||||||
const overridesContent = (await plugins.smartfs
|
|
||||||
.file(plugins.path.join(paths.assetsDir, 'overrides.json'))
|
|
||||||
.encoding('utf8')
|
|
||||||
.read()) as string;
|
|
||||||
const overrides = JSON.parse(overridesContent);
|
|
||||||
packageJson.pnpm = packageJson.pnpm || {};
|
|
||||||
packageJson.pnpm.overrides = overrides;
|
|
||||||
|
|
||||||
// exclude
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
fileArg.setContentsFromString(JSON.stringify(packageJson, null, 2));
|
|
||||||
}),
|
|
||||||
plugins.smartgulp.replace(),
|
|
||||||
]);
|
|
||||||
await formatStreamWrapper.run().catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import prettier from 'prettier';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
|
|
||||||
const prettierDefaultTypeScriptConfig: prettier.Options = {
|
|
||||||
printWidth: 100,
|
|
||||||
parser: 'typescript',
|
|
||||||
singleQuote: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const prettierDefaultMarkdownConfig: prettier.Options = {
|
|
||||||
singleQuote: true,
|
|
||||||
printWidth: 100,
|
|
||||||
parser: 'markdown',
|
|
||||||
};
|
|
||||||
|
|
||||||
const filesToFormat = [
|
|
||||||
`ts/**/*.ts`,
|
|
||||||
`test/**/*.ts`,
|
|
||||||
`readme.md`,
|
|
||||||
`docs/**/*.md`,
|
|
||||||
];
|
|
||||||
|
|
||||||
const choosePrettierConfig = (fileArg: plugins.smartfile.SmartFile) => {
|
|
||||||
switch (fileArg.parsedPath.ext) {
|
|
||||||
case '.ts':
|
|
||||||
return prettierDefaultTypeScriptConfig;
|
|
||||||
case '.md':
|
|
||||||
return prettierDefaultMarkdownConfig;
|
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const prettierTypeScriptPipestop = plugins.through2.obj(
|
|
||||||
async (fileArg: plugins.smartfile.SmartFile, enc, cb) => {
|
|
||||||
const fileString = fileArg.contentBuffer.toString();
|
|
||||||
const chosenConfig = choosePrettierConfig(fileArg);
|
|
||||||
const filePasses = await prettier.check(fileString, chosenConfig);
|
|
||||||
if (filePasses) {
|
|
||||||
logger.log('info', `OK! -> ${fileArg.path} passes!`);
|
|
||||||
cb(null);
|
|
||||||
} else {
|
|
||||||
logger.log('info', `${fileArg.path} is being reformated!`);
|
|
||||||
const formatedFileString = await prettier.format(
|
|
||||||
fileString,
|
|
||||||
chosenConfig,
|
|
||||||
);
|
|
||||||
fileArg.setContentsFromString(formatedFileString);
|
|
||||||
cb(null, fileArg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
const formatStreamWrapper = new plugins.smartstream.StreamWrapper([
|
|
||||||
plugins.smartgulp.src(filesToFormat),
|
|
||||||
prettierTypeScriptPipestop,
|
|
||||||
plugins.smartgulp.replace(),
|
|
||||||
]);
|
|
||||||
await formatStreamWrapper.run().catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
|
|
||||||
export const run = async () => {
|
|
||||||
const readmePath = plugins.path.join(paths.cwd, 'readme.md');
|
|
||||||
const readmeHintsPath = plugins.path.join(paths.cwd, 'readme.hints.md');
|
|
||||||
|
|
||||||
// Check and initialize readme.md if it doesn't exist
|
|
||||||
const readmeExists = await plugins.smartfs.file(readmePath).exists();
|
|
||||||
if (!readmeExists) {
|
|
||||||
await plugins.smartfs.file(readmePath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.write('# Project Readme\n\nThis is the initial readme file.');
|
|
||||||
console.log('Initialized readme.md');
|
|
||||||
} else {
|
|
||||||
console.log('readme.md already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check and initialize readme.hints.md if it doesn't exist
|
|
||||||
const readmeHintsExists = await plugins.smartfs.file(readmeHintsPath).exists();
|
|
||||||
if (!readmeHintsExists) {
|
|
||||||
await plugins.smartfs.file(readmeHintsPath)
|
|
||||||
.encoding('utf8')
|
|
||||||
.write('# Project Readme Hints\n\nThis is the initial readme hints file.');
|
|
||||||
console.log('Initialized readme.hints.md');
|
|
||||||
} else {
|
|
||||||
console.log('readme.hints.md already exists');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* takes care of updating files from templates
|
|
||||||
*/
|
|
||||||
export const run = async (project: Project) => {
|
|
||||||
const templateModule = await import('../mod_template/index.js');
|
|
||||||
|
|
||||||
// update vscode
|
|
||||||
const vscodeTemplate = await templateModule.getTemplate('vscode');
|
|
||||||
await vscodeTemplate.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', `Updated vscode template!`);
|
|
||||||
|
|
||||||
// update gitlab ci and Dockerfile
|
|
||||||
switch (project.gitzoneConfig.data.projectType) {
|
|
||||||
case 'npm':
|
|
||||||
case 'wcc':
|
|
||||||
if (project.gitzoneConfig.data.npmciOptions.npmAccessLevel === 'public') {
|
|
||||||
const ciTemplateDefault =
|
|
||||||
await templateModule.getTemplate('ci_default');
|
|
||||||
ciTemplateDefault.writeToDisk(paths.cwd);
|
|
||||||
} else {
|
|
||||||
const ciTemplateDefault =
|
|
||||||
await templateModule.getTemplate('ci_default_private');
|
|
||||||
ciTemplateDefault.writeToDisk(paths.cwd);
|
|
||||||
}
|
|
||||||
logger.log('info', 'Updated .gitlabci.yml!');
|
|
||||||
break;
|
|
||||||
case 'service':
|
|
||||||
case 'website':
|
|
||||||
const ciTemplateDocker = await templateModule.getTemplate('ci_docker');
|
|
||||||
await ciTemplateDocker.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', 'Updated CI/CD config files!');
|
|
||||||
|
|
||||||
// lets care about docker
|
|
||||||
const dockerTemplate =
|
|
||||||
await templateModule.getTemplate('dockerfile_service');
|
|
||||||
dockerTemplate.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', 'Updated Dockerfile!');
|
|
||||||
|
|
||||||
// lets care about cli
|
|
||||||
const cliTemplate = await templateModule.getTemplate('cli');
|
|
||||||
await cliTemplate.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', 'Updated cli.ts.js and cli.js!');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update html
|
|
||||||
if (project.gitzoneConfig.data.projectType === 'website') {
|
|
||||||
const websiteUpdateTemplate =
|
|
||||||
await templateModule.getTemplate('website_update');
|
|
||||||
const variables = {
|
|
||||||
assetbrokerUrl: project.gitzoneConfig.data.module.assetbrokerUrl,
|
|
||||||
legalUrl: project.gitzoneConfig.data.module.legalUrl,
|
|
||||||
};
|
|
||||||
console.log(
|
|
||||||
'updating website template with variables\n',
|
|
||||||
JSON.stringify(variables, null, 2),
|
|
||||||
);
|
|
||||||
websiteUpdateTemplate.supplyVariables(variables);
|
|
||||||
await websiteUpdateTemplate.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', `Updated html for website!`);
|
|
||||||
} else if (project.gitzoneConfig.data.projectType === 'service') {
|
|
||||||
const websiteUpdateTemplate =
|
|
||||||
await templateModule.getTemplate('service_update');
|
|
||||||
await websiteUpdateTemplate.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', `Updated html for element template!`);
|
|
||||||
} else if (project.gitzoneConfig.data.projectType === 'wcc') {
|
|
||||||
const wccUpdateTemplate = await templateModule.getTemplate('wcc_update');
|
|
||||||
await wccUpdateTemplate.writeToDisk(paths.cwd);
|
|
||||||
logger.log('info', `Updated html for wcc template!`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
|
||||||
import * as paths from '../paths.js';
|
|
||||||
|
|
||||||
import { logger } from '../gitzone.logging.js';
|
|
||||||
import { Project } from '../classes.project.js';
|
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
|
||||||
// lets care about tsconfig.json
|
|
||||||
logger.log('info', 'Formatting tsconfig.json...');
|
|
||||||
const factory = plugins.smartfile.SmartFileFactory.nodeFs();
|
|
||||||
const tsconfigSmartfile = await factory.fromFilePath(
|
|
||||||
plugins.path.join(paths.cwd, 'tsconfig.json'),
|
|
||||||
);
|
|
||||||
const tsconfigObject = JSON.parse(tsconfigSmartfile.parseContentAsString());
|
|
||||||
tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
|
|
||||||
tsconfigObject.compilerOptions.baseUrl = '.';
|
|
||||||
tsconfigObject.compilerOptions.paths = {};
|
|
||||||
const tsPublishMod = await import('@git.zone/tspublish');
|
|
||||||
const tsPublishInstance = new tsPublishMod.TsPublish();
|
|
||||||
const publishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
|
|
||||||
for (const publishModule of Object.keys(publishModules)) {
|
|
||||||
const publishConfig = publishModules[publishModule];
|
|
||||||
tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [
|
|
||||||
`./${publishModule}/index.js`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
await tsconfigSmartfile.editContentAsString(async () => {
|
|
||||||
return JSON.stringify(tsconfigObject, null, 2);
|
|
||||||
});
|
|
||||||
await tsconfigSmartfile.write();
|
|
||||||
};
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { BaseFormatter } from '../classes.baseformatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import type { IPlannedChange } from '../interfaces.format.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
import * as plugins from '../mod.plugins.js';
|
import * as plugins from '../mod.plugins.js';
|
||||||
import * as cleanupFormatter from '../format.cleanup.js';
|
|
||||||
|
|
||||||
export class CleanupFormatter extends BaseFormatter {
|
export class CleanupFormatter extends BaseFormatter {
|
||||||
get name(): string {
|
get name(): string {
|
||||||
|
|||||||
@@ -1,8 +1,113 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import * as formatCopy from '../format.copy.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
export class CopyFormatter extends LegacyFormatter {
|
interface ICopyPattern {
|
||||||
constructor(context: any, project: any) {
|
from: string;
|
||||||
super(context, project, 'copy', formatCopy);
|
to: string;
|
||||||
|
preservePath?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CopyFormatter extends BaseFormatter {
|
||||||
|
get name(): string {
|
||||||
|
return 'copy';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
|
||||||
|
// Get copy configuration from .smartconfig.json
|
||||||
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig();
|
||||||
|
const copyConfig = smartconfigInstance.dataFor<{ patterns: ICopyPattern[] }>(
|
||||||
|
'gitzone.format.copy',
|
||||||
|
{ patterns: [] },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!copyConfig.patterns || copyConfig.patterns.length === 0) {
|
||||||
|
logVerbose('No copy patterns configured in .smartconfig.json');
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pattern of copyConfig.patterns) {
|
||||||
|
if (!pattern.from || !pattern.to) {
|
||||||
|
logVerbose('Invalid copy pattern - missing "from" or "to" field');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Handle glob patterns
|
||||||
|
const entries = await plugins.smartfs
|
||||||
|
.directory('.')
|
||||||
|
.recursive()
|
||||||
|
.filter(pattern.from)
|
||||||
|
.list();
|
||||||
|
const files = entries.map((entry) => entry.path);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const sourcePath = file;
|
||||||
|
let destPath = pattern.to;
|
||||||
|
|
||||||
|
// If destination is a directory, preserve filename
|
||||||
|
if (pattern.to.endsWith('/')) {
|
||||||
|
const filename = plugins.path.basename(file);
|
||||||
|
destPath = plugins.path.join(pattern.to, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle template variables in destination path
|
||||||
|
if (pattern.preservePath) {
|
||||||
|
const relativePath = plugins.path.relative(
|
||||||
|
plugins.path.dirname(pattern.from.replace(/\*/g, '')),
|
||||||
|
file,
|
||||||
|
);
|
||||||
|
destPath = plugins.path.join(pattern.to, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read source content
|
||||||
|
const content = (await plugins.smartfs
|
||||||
|
.file(sourcePath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
// Check if destination exists and has same content
|
||||||
|
let needsCopy = true;
|
||||||
|
const destExists = await plugins.smartfs.file(destPath).exists();
|
||||||
|
if (destExists) {
|
||||||
|
const existingContent = (await plugins.smartfs
|
||||||
|
.file(destPath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
if (existingContent === content) {
|
||||||
|
needsCopy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsCopy) {
|
||||||
|
changes.push({
|
||||||
|
type: destExists ? 'modify' : 'create',
|
||||||
|
path: destPath,
|
||||||
|
module: this.name,
|
||||||
|
description: `Copy from ${sourcePath}`,
|
||||||
|
content: content,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logVerbose(`Failed to process pattern ${pattern.from}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (!change.content) return;
|
||||||
|
|
||||||
|
if (change.type === 'create') {
|
||||||
|
await this.createFile(change.path, change.content);
|
||||||
|
} else {
|
||||||
|
await this.modifyFile(change.path, change.content);
|
||||||
|
}
|
||||||
|
logger.log('info', `Copied to ${change.path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,107 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import * as formatGitignore from '../format.gitignore.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import * as paths from '../../paths.js';
|
||||||
|
import { logger } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
export class GitignoreFormatter extends LegacyFormatter {
|
export class GitignoreFormatter extends BaseFormatter {
|
||||||
constructor(context: any, project: any) {
|
get name(): string {
|
||||||
super(context, project, 'gitignore', formatGitignore);
|
return 'gitignore';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the standard gitignore template from the asset file,
|
||||||
|
* stripping the YAML frontmatter.
|
||||||
|
*/
|
||||||
|
private async getStandardTemplate(): Promise<string> {
|
||||||
|
const templatePath = plugins.path.join(paths.templatesDir, 'gitignore', '_gitignore');
|
||||||
|
const raw = (await plugins.smartfs
|
||||||
|
.file(templatePath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
// Strip YAML frontmatter (---\n...\n---)
|
||||||
|
const frontmatterEnd = raw.indexOf('---', 3);
|
||||||
|
if (frontmatterEnd !== -1) {
|
||||||
|
return raw.slice(frontmatterEnd + 3).trimStart();
|
||||||
|
}
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
const gitignorePath = '.gitignore';
|
||||||
|
|
||||||
|
const standardTemplate = await this.getStandardTemplate();
|
||||||
|
|
||||||
|
// Check if file exists and extract custom content
|
||||||
|
let customContent = '';
|
||||||
|
const exists = await plugins.smartfs.file(gitignorePath).exists();
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
const existingContent = (await plugins.smartfs
|
||||||
|
.file(gitignorePath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
// Extract custom section content
|
||||||
|
const customMarkers = ['#------# custom', '# custom'];
|
||||||
|
for (const marker of customMarkers) {
|
||||||
|
const splitResult = existingContent.split(marker);
|
||||||
|
if (splitResult.length > 1) {
|
||||||
|
customContent = splitResult[1].trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute new content
|
||||||
|
let newContent = standardTemplate;
|
||||||
|
if (customContent) {
|
||||||
|
newContent = standardTemplate + '\n' + customContent + '\n';
|
||||||
|
} else {
|
||||||
|
newContent = standardTemplate + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current content to compare
|
||||||
|
let currentContent = '';
|
||||||
|
if (exists) {
|
||||||
|
currentContent = (await plugins.smartfs
|
||||||
|
.file(gitignorePath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
changes.push({
|
||||||
|
type: 'create',
|
||||||
|
path: gitignorePath,
|
||||||
|
module: this.name,
|
||||||
|
description: 'Create .gitignore',
|
||||||
|
content: newContent,
|
||||||
|
});
|
||||||
|
} else if (newContent !== currentContent) {
|
||||||
|
changes.push({
|
||||||
|
type: 'modify',
|
||||||
|
path: gitignorePath,
|
||||||
|
module: this.name,
|
||||||
|
description: 'Update .gitignore (preserving custom section)',
|
||||||
|
content: newContent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (!change.content) return;
|
||||||
|
|
||||||
|
if (change.type === 'create') {
|
||||||
|
await this.createFile(change.path, change.content);
|
||||||
|
logger.log('info', 'Created .gitignore');
|
||||||
|
} else if (change.type === 'modify') {
|
||||||
|
await this.modifyFile(change.path, change.content);
|
||||||
|
logger.log('info', 'Updated .gitignore (preserved custom section)');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import { BaseFormatter } from '../classes.baseformatter.js';
|
|
||||||
import type { IPlannedChange } from '../interfaces.format.js';
|
|
||||||
import { Project } from '../../classes.project.js';
|
|
||||||
import * as plugins from '../mod.plugins.js';
|
|
||||||
|
|
||||||
// This is a wrapper for existing format modules
|
|
||||||
export class LegacyFormatter extends BaseFormatter {
|
|
||||||
private moduleName: string;
|
|
||||||
private formatModule: any;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
context: any,
|
|
||||||
project: Project,
|
|
||||||
moduleName: string,
|
|
||||||
formatModule: any,
|
|
||||||
) {
|
|
||||||
super(context, project);
|
|
||||||
this.moduleName = moduleName;
|
|
||||||
this.formatModule = formatModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name(): string {
|
|
||||||
return this.moduleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
async analyze(): Promise<IPlannedChange[]> {
|
|
||||||
// For legacy modules, we can't easily predict changes
|
|
||||||
// So we'll return a generic change that indicates the module will run
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
type: 'modify',
|
|
||||||
path: '<various files>',
|
|
||||||
module: this.name,
|
|
||||||
description: `Run ${this.name} formatter`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
async applyChange(change: IPlannedChange): Promise<void> {
|
|
||||||
// Run the legacy format module
|
|
||||||
await this.formatModule.run(this.project);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,62 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import * as formatLicense from '../format.license.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import * as paths from '../../paths.js';
|
||||||
|
import { logger } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
export class LicenseFormatter extends LegacyFormatter {
|
const INCOMPATIBLE_LICENSES: string[] = ['AGPL', 'GPL', 'SSPL'];
|
||||||
constructor(context: any, project: any) {
|
|
||||||
super(context, project, 'license', formatLicense);
|
export class LicenseFormatter extends BaseFormatter {
|
||||||
|
get name(): string {
|
||||||
|
return 'license';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
// License formatter only checks for incompatible licenses
|
||||||
|
// It does not modify any files, so return empty array
|
||||||
|
// The actual check happens in execute() for reporting purposes
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(changes: IPlannedChange[]): Promise<void> {
|
||||||
|
const startTime = this.stats.moduleStartTime(this.name);
|
||||||
|
this.stats.startModule(this.name);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if node_modules exists
|
||||||
|
const nodeModulesPath = plugins.path.join(paths.cwd, 'node_modules');
|
||||||
|
const nodeModulesExists = await plugins.smartfs
|
||||||
|
.directory(nodeModulesPath)
|
||||||
|
.exists();
|
||||||
|
|
||||||
|
if (!nodeModulesExists) {
|
||||||
|
logger.log('warn', 'No node_modules found. Skipping license check');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run license check
|
||||||
|
const licenseChecker = await plugins.smartlegal.createLicenseChecker();
|
||||||
|
const licenseCheckResult = await licenseChecker.excludeLicenseWithinPath(
|
||||||
|
paths.cwd,
|
||||||
|
INCOMPATIBLE_LICENSES,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (licenseCheckResult.failingModules.length === 0) {
|
||||||
|
logger.log('info', 'License check passed - no incompatible licenses found');
|
||||||
|
} else {
|
||||||
|
logger.log('error', 'License check failed - incompatible licenses found:');
|
||||||
|
for (const failedModule of licenseCheckResult.failingModules) {
|
||||||
|
console.log(
|
||||||
|
` ${failedModule.name} has license ${failedModule.license}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.stats.endModule(this.name, startTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
// No file changes for license formatter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
|
||||||
import * as formatNpmextra from '../format.npmextra.js';
|
|
||||||
|
|
||||||
export class NpmextraFormatter extends LegacyFormatter {
|
|
||||||
constructor(context: any, project: any) {
|
|
||||||
super(context, project, 'npmextra', formatNpmextra);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,122 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import * as formatPackageJson from '../format.packagejson.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import * as paths from '../../paths.js';
|
||||||
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
export class PackageJsonFormatter extends LegacyFormatter {
|
export class PackageJsonFormatter extends BaseFormatter {
|
||||||
constructor(context: any, project: any) {
|
get name(): string {
|
||||||
super(context, project, 'packagejson', formatPackageJson);
|
return 'packagejson';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
const packageJsonPath = 'package.json';
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
const exists = await plugins.smartfs.file(packageJsonPath).exists();
|
||||||
|
if (!exists) {
|
||||||
|
logVerbose('package.json does not exist, skipping');
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current content
|
||||||
|
const currentContent = (await plugins.smartfs
|
||||||
|
.file(packageJsonPath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
// Parse and compute new content
|
||||||
|
const packageJson = JSON.parse(currentContent);
|
||||||
|
|
||||||
|
// Get gitzone config from smartconfig
|
||||||
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig(paths.cwd);
|
||||||
|
const gitzoneData: any = smartconfigInstance.dataFor('@git.zone/cli', {});
|
||||||
|
|
||||||
|
// Set metadata from gitzone config
|
||||||
|
if (gitzoneData.module) {
|
||||||
|
packageJson.repository = {
|
||||||
|
type: 'git',
|
||||||
|
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
|
||||||
|
};
|
||||||
|
packageJson.bugs = {
|
||||||
|
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
|
||||||
|
};
|
||||||
|
packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure module type
|
||||||
|
if (!packageJson.type) {
|
||||||
|
packageJson.type = 'module';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure private field exists
|
||||||
|
if (packageJson.private === undefined) {
|
||||||
|
packageJson.private = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure license field exists
|
||||||
|
if (!packageJson.license) {
|
||||||
|
packageJson.license = 'UNLICENSED';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure scripts object exists
|
||||||
|
if (!packageJson.scripts) {
|
||||||
|
packageJson.scripts = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure build script exists
|
||||||
|
if (!packageJson.scripts.build) {
|
||||||
|
packageJson.scripts.build = `echo "Not needed for now"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set files array
|
||||||
|
packageJson.files = [
|
||||||
|
'ts/**/*',
|
||||||
|
'ts_web/**/*',
|
||||||
|
'dist/**/*',
|
||||||
|
'dist_*/**/*',
|
||||||
|
'dist_ts/**/*',
|
||||||
|
'dist_ts_web/**/*',
|
||||||
|
'assets/**/*',
|
||||||
|
'cli.js',
|
||||||
|
'.smartconfig.json',
|
||||||
|
'readme.md',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Set pnpm overrides from assets
|
||||||
|
try {
|
||||||
|
const overridesContent = (await plugins.smartfs
|
||||||
|
.file(plugins.path.join(paths.assetsDir, 'overrides.json'))
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
const overrides = JSON.parse(overridesContent);
|
||||||
|
packageJson.pnpm = packageJson.pnpm || {};
|
||||||
|
packageJson.pnpm.overrides = overrides;
|
||||||
|
} catch (error) {
|
||||||
|
logVerbose(`Could not read overrides.json: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newContent = JSON.stringify(packageJson, null, 2);
|
||||||
|
|
||||||
|
// Only add change if content differs
|
||||||
|
if (newContent !== currentContent) {
|
||||||
|
changes.push({
|
||||||
|
type: 'modify',
|
||||||
|
path: packageJsonPath,
|
||||||
|
module: this.name,
|
||||||
|
description: 'Format package.json',
|
||||||
|
content: newContent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (change.type !== 'modify' || !change.content) return;
|
||||||
|
|
||||||
|
await this.modifyFile(change.path, change.content);
|
||||||
|
logger.log('info', 'Updated package.json');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BaseFormatter } from '../classes.baseformatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import type { IPlannedChange } from '../interfaces.format.js';
|
import type { IPlannedChange, ICheckResult } from '../interfaces.format.js';
|
||||||
import * as plugins from '../mod.plugins.js';
|
import * as plugins from '../mod.plugins.js';
|
||||||
import { logger, logVerbose } from '../../gitzone.logging.js';
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export class PrettierFormatter extends BaseFormatter {
|
|||||||
const rootConfigFiles = [
|
const rootConfigFiles = [
|
||||||
'package.json',
|
'package.json',
|
||||||
'tsconfig.json',
|
'tsconfig.json',
|
||||||
'npmextra.json',
|
'.smartconfig.json',
|
||||||
'.prettierrc',
|
'.prettierrc',
|
||||||
'.prettierrc.json',
|
'.prettierrc.json',
|
||||||
'.prettierrc.js',
|
'.prettierrc.js',
|
||||||
@@ -79,12 +79,9 @@ export class PrettierFormatter extends BaseFormatter {
|
|||||||
// Remove duplicates
|
// Remove duplicates
|
||||||
const uniqueFiles = [...new Set(allFiles)];
|
const uniqueFiles = [...new Set(allFiles)];
|
||||||
|
|
||||||
// Get all files that match the pattern
|
|
||||||
const files = uniqueFiles;
|
|
||||||
|
|
||||||
// Ensure we only process actual files (not directories)
|
// Ensure we only process actual files (not directories)
|
||||||
const validFiles: string[] = [];
|
const validFiles: string[] = [];
|
||||||
for (const file of files) {
|
for (const file of uniqueFiles) {
|
||||||
try {
|
try {
|
||||||
const stats = await plugins.smartfs.file(file).stat();
|
const stats = await plugins.smartfs.file(file).stat();
|
||||||
if (!stats.isDirectory) {
|
if (!stats.isDirectory) {
|
||||||
@@ -96,14 +93,7 @@ export class PrettierFormatter extends BaseFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check which files need formatting
|
|
||||||
for (const file of validFiles) {
|
for (const file of validFiles) {
|
||||||
// Skip files that haven't changed
|
|
||||||
if (!(await this.shouldProcessFile(file))) {
|
|
||||||
logVerbose(`Skipping ${file} - no changes detected`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
changes.push({
|
changes.push({
|
||||||
type: 'modify',
|
type: 'modify',
|
||||||
path: file,
|
path: file,
|
||||||
@@ -232,7 +222,7 @@ export class PrettierFormatter extends BaseFormatter {
|
|||||||
|
|
||||||
private async getPrettierConfig(): Promise<any> {
|
private async getPrettierConfig(): Promise<any> {
|
||||||
// Try to load prettier config from the project
|
// Try to load prettier config from the project
|
||||||
const prettierConfig = new plugins.npmextra.Npmextra();
|
const prettierConfig = new plugins.smartconfig.Smartconfig();
|
||||||
return prettierConfig.dataFor('prettier', {
|
return prettierConfig.dataFor('prettier', {
|
||||||
// Default prettier config
|
// Default prettier config
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
@@ -243,4 +233,53 @@ export class PrettierFormatter extends BaseFormatter {
|
|||||||
arrowParens: 'always',
|
arrowParens: 'always',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override check() to compute diffs on-the-fly by running prettier
|
||||||
|
*/
|
||||||
|
async check(): Promise<ICheckResult> {
|
||||||
|
const changes = await this.analyze();
|
||||||
|
const diffs: ICheckResult['diffs'] = [];
|
||||||
|
|
||||||
|
for (const change of changes) {
|
||||||
|
if (change.type !== 'modify') continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read current content
|
||||||
|
const currentContent = (await plugins.smartfs
|
||||||
|
.file(change.path)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
// Skip files without extension (prettier can't infer parser)
|
||||||
|
const fileExt = plugins.path.extname(change.path).toLowerCase();
|
||||||
|
if (!fileExt) continue;
|
||||||
|
|
||||||
|
// Format with prettier to get what it would produce
|
||||||
|
const prettier = await import('prettier');
|
||||||
|
const formatted = await prettier.format(currentContent, {
|
||||||
|
filepath: change.path,
|
||||||
|
...(await this.getPrettierConfig()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only add to diffs if content differs
|
||||||
|
if (formatted !== currentContent) {
|
||||||
|
diffs.push({
|
||||||
|
path: change.path,
|
||||||
|
type: 'modify',
|
||||||
|
before: currentContent,
|
||||||
|
after: formatted,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Skip files that can't be processed
|
||||||
|
logVerbose(`Skipping diff for ${change.path}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasDiff: diffs.length > 0,
|
||||||
|
diffs,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { BaseFormatter } from '../classes.baseformatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import type { IPlannedChange } from '../interfaces.format.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
import * as formatReadme from '../format.readme.js';
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import { logger } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
|
const DEFAULT_README_CONTENT = `# Project Readme
|
||||||
|
|
||||||
|
This is the initial readme file.`;
|
||||||
|
|
||||||
|
const DEFAULT_README_HINTS_CONTENT = `# Project Readme Hints
|
||||||
|
|
||||||
|
This is the initial readme hints file.`;
|
||||||
|
|
||||||
export class ReadmeFormatter extends BaseFormatter {
|
export class ReadmeFormatter extends BaseFormatter {
|
||||||
get name(): string {
|
get name(): string {
|
||||||
@@ -8,17 +17,39 @@ export class ReadmeFormatter extends BaseFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async analyze(): Promise<IPlannedChange[]> {
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
return [
|
const changes: IPlannedChange[] = [];
|
||||||
{
|
|
||||||
type: 'modify',
|
// Check readme.md
|
||||||
|
const readmeExists = await plugins.smartfs.file('readme.md').exists();
|
||||||
|
if (!readmeExists) {
|
||||||
|
changes.push({
|
||||||
|
type: 'create',
|
||||||
path: 'readme.md',
|
path: 'readme.md',
|
||||||
module: this.name,
|
module: this.name,
|
||||||
description: 'Ensure readme files exist',
|
description: 'Create readme.md',
|
||||||
},
|
content: DEFAULT_README_CONTENT,
|
||||||
];
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check readme.hints.md
|
||||||
|
const hintsExists = await plugins.smartfs.file('readme.hints.md').exists();
|
||||||
|
if (!hintsExists) {
|
||||||
|
changes.push({
|
||||||
|
type: 'create',
|
||||||
|
path: 'readme.hints.md',
|
||||||
|
module: this.name,
|
||||||
|
description: 'Create readme.hints.md',
|
||||||
|
content: DEFAULT_README_HINTS_CONTENT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyChange(change: IPlannedChange): Promise<void> {
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
await formatReadme.run();
|
if (change.type !== 'create' || !change.content) return;
|
||||||
|
|
||||||
|
await this.createFile(change.path, change.content);
|
||||||
|
logger.log('info', `Created ${change.path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrates .smartconfig.json from old namespace keys to new package-scoped keys
|
||||||
|
*/
|
||||||
|
const migrateNamespaceKeys = (smartconfigJson: any): boolean => {
|
||||||
|
let migrated = false;
|
||||||
|
const migrations = [
|
||||||
|
{ oldKey: 'gitzone', newKey: '@git.zone/cli' },
|
||||||
|
{ oldKey: 'tsdoc', newKey: '@git.zone/tsdoc' },
|
||||||
|
{ oldKey: 'npmdocker', newKey: '@git.zone/tsdocker' },
|
||||||
|
{ oldKey: 'npmci', newKey: '@ship.zone/szci' },
|
||||||
|
{ oldKey: 'szci', newKey: '@ship.zone/szci' },
|
||||||
|
];
|
||||||
|
for (const { oldKey, newKey } of migrations) {
|
||||||
|
if (smartconfigJson[oldKey]) {
|
||||||
|
if (!smartconfigJson[newKey]) {
|
||||||
|
smartconfigJson[newKey] = smartconfigJson[oldKey];
|
||||||
|
} else {
|
||||||
|
smartconfigJson[newKey] = {
|
||||||
|
...smartconfigJson[oldKey],
|
||||||
|
...smartconfigJson[newKey],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
delete smartconfigJson[oldKey];
|
||||||
|
migrated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return migrated;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrates npmAccessLevel from @ship.zone/szci to @git.zone/cli.release.accessLevel
|
||||||
|
*/
|
||||||
|
const migrateAccessLevel = (smartconfigJson: any): boolean => {
|
||||||
|
const szciConfig = smartconfigJson['@ship.zone/szci'];
|
||||||
|
|
||||||
|
if (!szciConfig?.npmAccessLevel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gitzoneConfig = smartconfigJson['@git.zone/cli'] || {};
|
||||||
|
if (gitzoneConfig?.release?.accessLevel) {
|
||||||
|
delete szciConfig.npmAccessLevel;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!smartconfigJson['@git.zone/cli']) {
|
||||||
|
smartconfigJson['@git.zone/cli'] = {};
|
||||||
|
}
|
||||||
|
if (!smartconfigJson['@git.zone/cli'].release) {
|
||||||
|
smartconfigJson['@git.zone/cli'].release = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
smartconfigJson['@git.zone/cli'].release.accessLevel = szciConfig.npmAccessLevel;
|
||||||
|
delete szciConfig.npmAccessLevel;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CONFIG_FILE = '.smartconfig.json';
|
||||||
|
|
||||||
|
export class SmartconfigFormatter extends BaseFormatter {
|
||||||
|
get name(): string {
|
||||||
|
return 'smartconfig';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
|
||||||
|
// File rename (npmextra.json/smartconfig.json → .smartconfig.json)
|
||||||
|
// is handled by the orchestrator before analysis.
|
||||||
|
// This formatter only operates on .smartconfig.json.
|
||||||
|
const exists = await plugins.smartfs.file(CONFIG_FILE).exists();
|
||||||
|
if (!exists) {
|
||||||
|
logVerbose('.smartconfig.json does not exist, skipping');
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentContent = (await plugins.smartfs
|
||||||
|
.file(CONFIG_FILE)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
const smartconfigJson = JSON.parse(currentContent);
|
||||||
|
|
||||||
|
// Apply key migrations
|
||||||
|
migrateNamespaceKeys(smartconfigJson);
|
||||||
|
migrateAccessLevel(smartconfigJson);
|
||||||
|
|
||||||
|
// Ensure namespaces exist
|
||||||
|
if (!smartconfigJson['@git.zone/cli']) {
|
||||||
|
smartconfigJson['@git.zone/cli'] = {};
|
||||||
|
}
|
||||||
|
if (!smartconfigJson['@ship.zone/szci']) {
|
||||||
|
smartconfigJson['@ship.zone/szci'] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const newContent = JSON.stringify(smartconfigJson, null, 2);
|
||||||
|
|
||||||
|
if (newContent !== currentContent) {
|
||||||
|
changes.push({
|
||||||
|
type: 'modify',
|
||||||
|
path: CONFIG_FILE,
|
||||||
|
module: this.name,
|
||||||
|
description: 'Migrate and format .smartconfig.json',
|
||||||
|
content: newContent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (change.type !== 'modify' || !change.content) return;
|
||||||
|
|
||||||
|
const smartconfigJson = JSON.parse(change.content);
|
||||||
|
|
||||||
|
// Check for missing required module information
|
||||||
|
const expectedRepoInformation: string[] = [
|
||||||
|
'projectType',
|
||||||
|
'module.githost',
|
||||||
|
'module.gitscope',
|
||||||
|
'module.gitrepo',
|
||||||
|
'module.description',
|
||||||
|
'module.npmPackagename',
|
||||||
|
'module.license',
|
||||||
|
];
|
||||||
|
|
||||||
|
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||||
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
||||||
|
if (
|
||||||
|
!plugins.smartobject.smartGet(
|
||||||
|
smartconfigJson['@git.zone/cli'],
|
||||||
|
expectedRepoInformationItem,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
interactInstance.addQuestions([
|
||||||
|
{
|
||||||
|
message: `What is the value of ${expectedRepoInformationItem}`,
|
||||||
|
name: expectedRepoInformationItem,
|
||||||
|
type: 'input',
|
||||||
|
default: 'undefined variable',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const answerbucket = await interactInstance.runQueue();
|
||||||
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
||||||
|
const cliProvidedValue = answerbucket.getAnswerFor(
|
||||||
|
expectedRepoInformationItem,
|
||||||
|
);
|
||||||
|
if (cliProvidedValue) {
|
||||||
|
plugins.smartobject.smartAdd(
|
||||||
|
smartconfigJson['@git.zone/cli'],
|
||||||
|
expectedRepoInformationItem,
|
||||||
|
cliProvidedValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalContent = JSON.stringify(smartconfigJson, null, 2);
|
||||||
|
await this.modifyFile(change.path, finalContent);
|
||||||
|
logger.log('info', 'Updated .smartconfig.json');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,167 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import * as formatTemplates from '../format.templates.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import * as paths from '../../paths.js';
|
||||||
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
export class TemplatesFormatter extends LegacyFormatter {
|
export class TemplatesFormatter extends BaseFormatter {
|
||||||
constructor(context: any, project: any) {
|
get name(): string {
|
||||||
super(context, project, 'templates', formatTemplates);
|
return 'templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a template directory through smartscaf and return a map of path → content.
|
||||||
|
*/
|
||||||
|
private async renderTemplate(templateName: string): Promise<Map<string, string>> {
|
||||||
|
const templateDir = plugins.path.join(paths.templatesDir, templateName);
|
||||||
|
|
||||||
|
const scafTemplate = new plugins.smartscaf.ScafTemplate(templateDir);
|
||||||
|
await scafTemplate.readTemplateFromDir();
|
||||||
|
|
||||||
|
const gitzoneData = this.project.gitzoneConfig?.data;
|
||||||
|
if (gitzoneData) {
|
||||||
|
await scafTemplate.supplyVariables({
|
||||||
|
module: gitzoneData.module,
|
||||||
|
projectType: gitzoneData.projectType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderedFiles = await scafTemplate.renderToMemory();
|
||||||
|
|
||||||
|
const fileMap = new Map<string, string>();
|
||||||
|
for (const file of renderedFiles) {
|
||||||
|
fileMap.set(file.path, file.contents.toString());
|
||||||
|
}
|
||||||
|
return fileMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
const project = this.project;
|
||||||
|
const projectType = project.gitzoneConfig?.data?.projectType;
|
||||||
|
|
||||||
|
// VSCode template - for all projects
|
||||||
|
const vscodeChanges = await this.analyzeTemplate('vscode', [
|
||||||
|
{ templatePath: '.vscode/settings.json', destPath: '.vscode/settings.json' },
|
||||||
|
{ templatePath: '.vscode/launch.json', destPath: '.vscode/launch.json' },
|
||||||
|
]);
|
||||||
|
changes.push(...vscodeChanges);
|
||||||
|
|
||||||
|
// CI and other templates based on projectType
|
||||||
|
switch (projectType) {
|
||||||
|
case 'npm':
|
||||||
|
case 'wcc':
|
||||||
|
const accessLevel = (project.gitzoneConfig?.data as any)?.release?.accessLevel
|
||||||
|
|| project.gitzoneConfig?.data?.npmciOptions?.npmAccessLevel;
|
||||||
|
const ciTemplate = accessLevel === 'public' ? 'ci_default' : 'ci_default_private';
|
||||||
|
const ciChanges = await this.analyzeTemplate(ciTemplate, [
|
||||||
|
{ templatePath: '.gitea/workflows/default_nottags.yaml', destPath: '.gitea/workflows/default_nottags.yaml' },
|
||||||
|
{ templatePath: '.gitea/workflows/default_tags.yaml', destPath: '.gitea/workflows/default_tags.yaml' },
|
||||||
|
]);
|
||||||
|
changes.push(...ciChanges);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'service':
|
||||||
|
case 'website':
|
||||||
|
const dockerCiChanges = await this.analyzeTemplate('ci_docker', [
|
||||||
|
{ templatePath: '.gitea/workflows/docker_nottags.yaml', destPath: '.gitea/workflows/docker_nottags.yaml' },
|
||||||
|
{ templatePath: '.gitea/workflows/docker_tags.yaml', destPath: '.gitea/workflows/docker_tags.yaml' },
|
||||||
|
]);
|
||||||
|
changes.push(...dockerCiChanges);
|
||||||
|
|
||||||
|
const dockerfileChanges = await this.analyzeTemplate('dockerfile_service', [
|
||||||
|
{ templatePath: 'Dockerfile', destPath: 'Dockerfile' },
|
||||||
|
{ templatePath: 'dockerignore', destPath: '.dockerignore' },
|
||||||
|
]);
|
||||||
|
changes.push(...dockerfileChanges);
|
||||||
|
|
||||||
|
const cliChanges = await this.analyzeTemplate('cli', [
|
||||||
|
{ templatePath: 'cli.js', destPath: 'cli.js' },
|
||||||
|
{ templatePath: 'cli.ts.js', destPath: 'cli.ts.js' },
|
||||||
|
]);
|
||||||
|
changes.push(...cliChanges);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update templates based on projectType
|
||||||
|
if (projectType === 'website') {
|
||||||
|
const websiteChanges = await this.analyzeTemplate('website_update', [
|
||||||
|
{ templatePath: 'html/index.html', destPath: 'html/index.html' },
|
||||||
|
]);
|
||||||
|
changes.push(...websiteChanges);
|
||||||
|
} else if (projectType === 'wcc') {
|
||||||
|
const wccChanges = await this.analyzeTemplate('wcc_update', [
|
||||||
|
{ templatePath: 'html/index.html', destPath: 'html/index.html' },
|
||||||
|
{ templatePath: 'html/index.ts', destPath: 'html/index.ts' },
|
||||||
|
]);
|
||||||
|
changes.push(...wccChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async analyzeTemplate(
|
||||||
|
templateName: string,
|
||||||
|
files: Array<{ templatePath: string; destPath: string }>,
|
||||||
|
): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
const templateDir = plugins.path.join(paths.templatesDir, templateName);
|
||||||
|
|
||||||
|
const templateExists = await plugins.smartfs.directory(templateDir).exists();
|
||||||
|
if (!templateExists) {
|
||||||
|
logVerbose(`Template ${templateName} not found`);
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
let renderedFiles: Map<string, string>;
|
||||||
|
try {
|
||||||
|
renderedFiles = await this.renderTemplate(templateName);
|
||||||
|
} catch (error) {
|
||||||
|
logVerbose(`Failed to render template ${templateName}: ${error.message}`);
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
// Look up by templatePath first, then destPath (frontmatter may rename files)
|
||||||
|
const processedContent = renderedFiles.get(file.templatePath)
|
||||||
|
|| renderedFiles.get(file.destPath);
|
||||||
|
|
||||||
|
if (!processedContent) {
|
||||||
|
logVerbose(`Template file ${file.templatePath} not found in rendered output`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const destExists = await plugins.smartfs.file(file.destPath).exists();
|
||||||
|
let currentContent = '';
|
||||||
|
if (destExists) {
|
||||||
|
currentContent = (await plugins.smartfs
|
||||||
|
.file(file.destPath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processedContent !== currentContent) {
|
||||||
|
changes.push({
|
||||||
|
type: destExists ? 'modify' : 'create',
|
||||||
|
path: file.destPath,
|
||||||
|
module: this.name,
|
||||||
|
description: `Apply template ${templateName}/${file.templatePath}`,
|
||||||
|
content: processedContent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (!change.content) return;
|
||||||
|
|
||||||
|
if (change.type === 'create') {
|
||||||
|
await this.createFile(change.path, change.content);
|
||||||
|
} else {
|
||||||
|
await this.modifyFile(change.path, change.content);
|
||||||
|
}
|
||||||
|
logger.log('info', `Applied template to ${change.path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,76 @@
|
|||||||
import { LegacyFormatter } from './legacy.formatter.js';
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
import * as formatTsconfig from '../format.tsconfig.js';
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import * as paths from '../../paths.js';
|
||||||
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
export class TsconfigFormatter extends LegacyFormatter {
|
export class TsconfigFormatter extends BaseFormatter {
|
||||||
constructor(context: any, project: any) {
|
get name(): string {
|
||||||
super(context, project, 'tsconfig', formatTsconfig);
|
return 'tsconfig';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
const tsconfigPath = 'tsconfig.json';
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
const exists = await plugins.smartfs.file(tsconfigPath).exists();
|
||||||
|
if (!exists) {
|
||||||
|
logVerbose('tsconfig.json does not exist, skipping');
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current content
|
||||||
|
const currentContent = (await plugins.smartfs
|
||||||
|
.file(tsconfigPath)
|
||||||
|
.encoding('utf8')
|
||||||
|
.read()) as string;
|
||||||
|
|
||||||
|
// Parse and compute new content
|
||||||
|
const tsconfigObject = JSON.parse(currentContent);
|
||||||
|
tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
|
||||||
|
tsconfigObject.compilerOptions.baseUrl = '.';
|
||||||
|
const existingPaths = tsconfigObject.compilerOptions.paths || {};
|
||||||
|
|
||||||
|
// Get module paths from tspublish, merging with existing custom paths
|
||||||
|
const tspublishPaths: Record<string, string[]> = {};
|
||||||
|
try {
|
||||||
|
const tsPublishMod = await import('@git.zone/tspublish');
|
||||||
|
const tsPublishInstance = new tsPublishMod.TsPublish();
|
||||||
|
const publishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
|
||||||
|
|
||||||
|
for (const publishModule of Object.keys(publishModules)) {
|
||||||
|
const publishConfig = publishModules[publishModule];
|
||||||
|
tspublishPaths[`${publishConfig.name}`] = [
|
||||||
|
`./${publishModule}/index.js`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logVerbose(`Could not get tspublish modules: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
tsconfigObject.compilerOptions.paths = { ...existingPaths, ...tspublishPaths };
|
||||||
|
|
||||||
|
const newContent = JSON.stringify(tsconfigObject, null, 2);
|
||||||
|
|
||||||
|
// Only add change if content differs
|
||||||
|
if (newContent !== currentContent) {
|
||||||
|
changes.push({
|
||||||
|
type: 'modify',
|
||||||
|
path: tsconfigPath,
|
||||||
|
module: this.name,
|
||||||
|
description: 'Format tsconfig.json with path mappings',
|
||||||
|
content: newContent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (change.type !== 'modify' || !change.content) return;
|
||||||
|
|
||||||
|
await this.modifyFile(change.path, change.content);
|
||||||
|
logger.log('info', 'Updated tsconfig.json');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-98
@@ -5,9 +5,8 @@ import { FormatPlanner } from './classes.formatplanner.js';
|
|||||||
import { BaseFormatter } from './classes.baseformatter.js';
|
import { BaseFormatter } from './classes.baseformatter.js';
|
||||||
import { logger, setVerboseMode } from '../gitzone.logging.js';
|
import { logger, setVerboseMode } from '../gitzone.logging.js';
|
||||||
|
|
||||||
// Import wrapper classes for formatters
|
|
||||||
import { CleanupFormatter } from './formatters/cleanup.formatter.js';
|
import { CleanupFormatter } from './formatters/cleanup.formatter.js';
|
||||||
import { NpmextraFormatter } from './formatters/npmextra.formatter.js';
|
import { SmartconfigFormatter } from './formatters/smartconfig.formatter.js';
|
||||||
import { LicenseFormatter } from './formatters/license.formatter.js';
|
import { LicenseFormatter } from './formatters/license.formatter.js';
|
||||||
import { PackageJsonFormatter } from './formatters/packagejson.formatter.js';
|
import { PackageJsonFormatter } from './formatters/packagejson.formatter.js';
|
||||||
import { TemplatesFormatter } from './formatters/templates.formatter.js';
|
import { TemplatesFormatter } from './formatters/templates.formatter.js';
|
||||||
@@ -17,82 +16,90 @@ import { PrettierFormatter } from './formatters/prettier.formatter.js';
|
|||||||
import { ReadmeFormatter } from './formatters/readme.formatter.js';
|
import { ReadmeFormatter } from './formatters/readme.formatter.js';
|
||||||
import { CopyFormatter } from './formatters/copy.formatter.js';
|
import { CopyFormatter } from './formatters/copy.formatter.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename npmextra.json or smartconfig.json to .smartconfig.json
|
||||||
|
* before any formatter tries to read config.
|
||||||
|
*/
|
||||||
|
async function migrateConfigFile(): Promise<void> {
|
||||||
|
const target = '.smartconfig.json';
|
||||||
|
const targetExists = await plugins.smartfs.file(target).exists();
|
||||||
|
if (targetExists) return;
|
||||||
|
|
||||||
|
for (const oldName of ['smartconfig.json', 'npmextra.json']) {
|
||||||
|
const exists = await plugins.smartfs.file(oldName).exists();
|
||||||
|
if (exists) {
|
||||||
|
const content = await plugins.smartfs.file(oldName).encoding('utf8').read() as string;
|
||||||
|
await plugins.smartfs.file(`./${target}`).encoding('utf8').write(content);
|
||||||
|
await plugins.smartfs.file(oldName).delete();
|
||||||
|
logger.log('info', `Migrated ${oldName} to ${target}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared formatter class map used by both run() and runFormatter()
|
||||||
|
const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
|
||||||
|
cleanup: CleanupFormatter,
|
||||||
|
smartconfig: SmartconfigFormatter,
|
||||||
|
license: LicenseFormatter,
|
||||||
|
packagejson: PackageJsonFormatter,
|
||||||
|
templates: TemplatesFormatter,
|
||||||
|
gitignore: GitignoreFormatter,
|
||||||
|
tsconfig: TsconfigFormatter,
|
||||||
|
prettier: PrettierFormatter,
|
||||||
|
readme: ReadmeFormatter,
|
||||||
|
copy: CopyFormatter,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Formatters that don't require projectType to be set
|
||||||
|
const formattersNotRequiringProjectType = ['smartconfig', 'prettier', 'cleanup', 'packagejson'];
|
||||||
|
|
||||||
export let run = async (
|
export let run = async (
|
||||||
options: {
|
options: {
|
||||||
write?: boolean; // Explicitly write changes (default: false, dry-mode)
|
write?: boolean;
|
||||||
dryRun?: boolean; // Deprecated, kept for compatibility
|
dryRun?: boolean; // Deprecated, kept for compatibility
|
||||||
yes?: boolean;
|
yes?: boolean;
|
||||||
planOnly?: boolean;
|
planOnly?: boolean;
|
||||||
savePlan?: string;
|
savePlan?: string;
|
||||||
fromPlan?: string;
|
fromPlan?: string;
|
||||||
detailed?: boolean;
|
detailed?: boolean;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
parallel?: boolean;
|
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
diff?: boolean; // Show file diffs
|
diff?: boolean;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<any> => {
|
): Promise<any> => {
|
||||||
// Set verbose mode if requested
|
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
setVerboseMode(true);
|
setVerboseMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if we should write changes
|
|
||||||
// Default is dry-mode (no writing) unless --write/-w is specified
|
|
||||||
const shouldWrite = options.write ?? (options.dryRun === false);
|
const shouldWrite = options.write ?? (options.dryRun === false);
|
||||||
|
|
||||||
|
// Migrate config file before anything reads it
|
||||||
|
await migrateConfigFile();
|
||||||
|
|
||||||
const project = await Project.fromCwd({ requireProjectType: false });
|
const project = await Project.fromCwd({ requireProjectType: false });
|
||||||
const context = new FormatContext();
|
const context = new FormatContext();
|
||||||
// Cache system removed - no longer needed
|
|
||||||
const planner = new FormatPlanner();
|
const planner = new FormatPlanner();
|
||||||
|
|
||||||
// Get configuration from npmextra
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig();
|
||||||
const npmextraConfig = new plugins.npmextra.Npmextra();
|
const formatConfig = smartconfigInstance.dataFor<any>('@git.zone/cli.format', {
|
||||||
const formatConfig = npmextraConfig.dataFor<any>('@git.zone/cli.format', {
|
|
||||||
interactive: true,
|
interactive: true,
|
||||||
showDiffs: false,
|
showDiffs: false,
|
||||||
autoApprove: false,
|
autoApprove: false,
|
||||||
planTimeout: 30000,
|
|
||||||
rollback: {
|
|
||||||
enabled: true,
|
|
||||||
autoRollbackOnError: true,
|
|
||||||
backupRetentionDays: 7,
|
|
||||||
maxBackupSize: '100MB',
|
|
||||||
excludePatterns: ['node_modules/**', '.git/**'],
|
|
||||||
},
|
|
||||||
modules: {
|
modules: {
|
||||||
skip: [],
|
skip: [],
|
||||||
only: [],
|
only: [],
|
||||||
order: [],
|
|
||||||
},
|
|
||||||
parallel: true,
|
|
||||||
cache: {
|
|
||||||
enabled: true,
|
|
||||||
clean: true, // Clean invalid entries from cache
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cache cleaning removed - no longer using cache system
|
|
||||||
|
|
||||||
// Override config with command options
|
|
||||||
const interactive = options.interactive ?? formatConfig.interactive;
|
const interactive = options.interactive ?? formatConfig.interactive;
|
||||||
const autoApprove = options.yes ?? formatConfig.autoApprove;
|
const autoApprove = options.yes ?? formatConfig.autoApprove;
|
||||||
const parallel = options.parallel ?? formatConfig.parallel;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize formatters
|
// Initialize formatters in execution order
|
||||||
const formatters = [
|
const formatters = Object.entries(formatterMap).map(
|
||||||
new CleanupFormatter(context, project),
|
([, FormatterClass]) => new FormatterClass(context, project),
|
||||||
new NpmextraFormatter(context, project),
|
);
|
||||||
new LicenseFormatter(context, project),
|
|
||||||
new PackageJsonFormatter(context, project),
|
|
||||||
new TemplatesFormatter(context, project),
|
|
||||||
new GitignoreFormatter(context, project),
|
|
||||||
new TsconfigFormatter(context, project),
|
|
||||||
new PrettierFormatter(context, project),
|
|
||||||
new ReadmeFormatter(context, project),
|
|
||||||
new CopyFormatter(context, project),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Filter formatters based on configuration
|
// Filter formatters based on configuration
|
||||||
const activeFormatters = formatters.filter((formatter) => {
|
const activeFormatters = formatters.filter((formatter) => {
|
||||||
@@ -128,13 +135,13 @@ export let run = async (
|
|||||||
logger.log('info', `Plan saved to ${options.savePlan}`);
|
logger.log('info', `Plan saved to ${options.savePlan}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit if plan-only mode
|
|
||||||
if (options.planOnly) {
|
if (options.planOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show diffs if requested (works in both dry-run and write modes)
|
// Show diffs if explicitly requested or before interactive write confirmation
|
||||||
if (options.diff) {
|
const showDiffs = options.diff || (shouldWrite && interactive && !autoApprove);
|
||||||
|
if (showDiffs) {
|
||||||
logger.log('info', 'Showing file diffs:');
|
logger.log('info', 'Showing file diffs:');
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
@@ -171,22 +178,16 @@ export let run = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute phase
|
// Execute phase
|
||||||
logger.log(
|
logger.log('info', 'Executing format operations...');
|
||||||
'info',
|
await planner.executePlan(plan, activeFormatters, context);
|
||||||
`Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`,
|
|
||||||
);
|
|
||||||
await planner.executePlan(plan, activeFormatters, context, parallel);
|
|
||||||
|
|
||||||
// Finish statistics tracking
|
|
||||||
context.getFormatStats().finish();
|
context.getFormatStats().finish();
|
||||||
|
|
||||||
// Display statistics
|
const showStats = smartconfigInstance.dataFor('gitzone.format.showStats', true);
|
||||||
const showStats = npmextraConfig.dataFor('gitzone.format.showStats', true);
|
|
||||||
if (showStats) {
|
if (showStats) {
|
||||||
context.getFormatStats().displayStats();
|
context.getFormatStats().displayStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save stats if requested
|
|
||||||
if (options.detailed) {
|
if (options.detailed) {
|
||||||
const statsPath = `.nogit/format-stats-${Date.now()}.json`;
|
const statsPath = `.nogit/format-stats-${Date.now()}.json`;
|
||||||
await context.getFormatStats().saveReport(statsPath);
|
await context.getFormatStats().saveReport(statsPath);
|
||||||
@@ -195,36 +196,13 @@ export let run = async (
|
|||||||
logger.log('success', 'Format operations completed successfully!');
|
logger.log('success', 'Format operations completed successfully!');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Format operation failed: ${error.message}`);
|
logger.log('error', `Format operation failed: ${error.message}`);
|
||||||
|
|
||||||
// Rollback system has been removed for stability
|
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export CLI command handlers
|
|
||||||
export const handleRollback = async (operationId?: string): Promise<void> => {
|
|
||||||
logger.log('info', 'Rollback system has been disabled for stability');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleListBackups = async (): Promise<void> => {
|
|
||||||
logger.log('info', 'Backup system has been disabled for stability');
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleCleanBackups = async (): Promise<void> => {
|
|
||||||
logger.log(
|
|
||||||
'info',
|
|
||||||
'Backup cleaning has been disabled - backup system removed',
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Import the ICheckResult type for external use
|
|
||||||
import type { ICheckResult } from './interfaces.format.js';
|
import type { ICheckResult } from './interfaces.format.js';
|
||||||
export type { ICheckResult };
|
export type { ICheckResult };
|
||||||
|
|
||||||
// Formatters that don't require projectType to be set
|
|
||||||
const formattersNotRequiringProjectType = ['npmextra', 'prettier', 'cleanup', 'packagejson'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a single formatter by name (for use by other modules)
|
* Run a single formatter by name (for use by other modules)
|
||||||
*/
|
*/
|
||||||
@@ -232,29 +210,14 @@ export const runFormatter = async (
|
|||||||
formatterName: string,
|
formatterName: string,
|
||||||
options: {
|
options: {
|
||||||
silent?: boolean;
|
silent?: boolean;
|
||||||
checkOnly?: boolean; // Only check for diffs, don't apply
|
checkOnly?: boolean;
|
||||||
showDiff?: boolean; // Show the diff output
|
showDiff?: boolean;
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<ICheckResult | void> => {
|
): Promise<ICheckResult | void> => {
|
||||||
// Determine if this formatter requires projectType
|
|
||||||
const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName);
|
const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName);
|
||||||
const project = await Project.fromCwd({ requireProjectType });
|
const project = await Project.fromCwd({ requireProjectType });
|
||||||
const context = new FormatContext();
|
const context = new FormatContext();
|
||||||
|
|
||||||
// Map formatter names to classes
|
|
||||||
const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
|
|
||||||
cleanup: CleanupFormatter,
|
|
||||||
npmextra: NpmextraFormatter,
|
|
||||||
license: LicenseFormatter,
|
|
||||||
packagejson: PackageJsonFormatter,
|
|
||||||
templates: TemplatesFormatter,
|
|
||||||
gitignore: GitignoreFormatter,
|
|
||||||
tsconfig: TsconfigFormatter,
|
|
||||||
prettier: PrettierFormatter,
|
|
||||||
readme: ReadmeFormatter,
|
|
||||||
copy: CopyFormatter,
|
|
||||||
};
|
|
||||||
|
|
||||||
const FormatterClass = formatterMap[formatterName];
|
const FormatterClass = formatterMap[formatterName];
|
||||||
if (!FormatterClass) {
|
if (!FormatterClass) {
|
||||||
throw new Error(`Unknown formatter: ${formatterName}`);
|
throw new Error(`Unknown formatter: ${formatterName}`);
|
||||||
@@ -262,7 +225,6 @@ export const runFormatter = async (
|
|||||||
|
|
||||||
const formatter = new FormatterClass(context, project);
|
const formatter = new FormatterClass(context, project);
|
||||||
|
|
||||||
// Check-only mode: just check for diffs and optionally display them
|
|
||||||
if (options.checkOnly) {
|
if (options.checkOnly) {
|
||||||
const result = await formatter.check();
|
const result = await formatter.check();
|
||||||
if (result.hasDiff && options.showDiff) {
|
if (result.hasDiff && options.showDiff) {
|
||||||
@@ -271,7 +233,6 @@ export const runFormatter = async (
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal mode: analyze and apply changes
|
|
||||||
const changes = await formatter.analyze();
|
const changes = await formatter.analyze();
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
|
|||||||
@@ -1,31 +1,15 @@
|
|||||||
export type IFormatOperation = {
|
|
||||||
id: string;
|
|
||||||
timestamp: number;
|
|
||||||
files: Array<{
|
|
||||||
path: string;
|
|
||||||
originalContent: string;
|
|
||||||
checksum: string;
|
|
||||||
permissions: string;
|
|
||||||
}>;
|
|
||||||
status: 'pending' | 'in-progress' | 'completed' | 'failed' | 'rolled-back';
|
|
||||||
error?: Error;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type IFormatPlan = {
|
export type IFormatPlan = {
|
||||||
summary: {
|
summary: {
|
||||||
totalFiles: number;
|
totalFiles: number;
|
||||||
filesAdded: number;
|
filesAdded: number;
|
||||||
filesModified: number;
|
filesModified: number;
|
||||||
filesRemoved: number;
|
filesRemoved: number;
|
||||||
estimatedTime: number;
|
|
||||||
};
|
};
|
||||||
changes: Array<{
|
changes: Array<{
|
||||||
type: 'create' | 'modify' | 'delete';
|
type: 'create' | 'modify' | 'delete';
|
||||||
path: string;
|
path: string;
|
||||||
module: string;
|
module: string;
|
||||||
description: string;
|
description: string;
|
||||||
diff?: string;
|
|
||||||
size?: number;
|
|
||||||
}>;
|
}>;
|
||||||
warnings: Array<{
|
warnings: Array<{
|
||||||
level: 'info' | 'warning' | 'error';
|
level: 'info' | 'warning' | 'error';
|
||||||
@@ -40,9 +24,6 @@ export type IPlannedChange = {
|
|||||||
module: string;
|
module: string;
|
||||||
description: string;
|
description: string;
|
||||||
content?: string; // New content for create/modify operations
|
content?: string; // New content for create/modify operations
|
||||||
originalContent?: string; // Original content for comparison
|
|
||||||
diff?: string;
|
|
||||||
size?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ICheckResult {
|
export interface ICheckResult {
|
||||||
@@ -54,3 +35,19 @@ export interface ICheckResult {
|
|||||||
after?: string;
|
after?: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getModuleIcon(module: string): string {
|
||||||
|
const icons: Record<string, string> = {
|
||||||
|
packagejson: '📦',
|
||||||
|
license: '📝',
|
||||||
|
tsconfig: '🔧',
|
||||||
|
cleanup: '🚮',
|
||||||
|
gitignore: '🔒',
|
||||||
|
prettier: '✨',
|
||||||
|
readme: '📖',
|
||||||
|
templates: '📄',
|
||||||
|
smartconfig: '⚙️',
|
||||||
|
copy: '📋',
|
||||||
|
};
|
||||||
|
return icons[module] || '📁';
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +1,21 @@
|
|||||||
export * from '../plugins.js';
|
export * from '../plugins.js';
|
||||||
|
|
||||||
import * as crypto from 'crypto';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as lik from '@push.rocks/lik';
|
|
||||||
import * as smartfile from '@push.rocks/smartfile';
|
import * as smartfile from '@push.rocks/smartfile';
|
||||||
import * as smartgulp from '@push.rocks/smartgulp';
|
|
||||||
import * as smartinteract from '@push.rocks/smartinteract';
|
import * as smartinteract from '@push.rocks/smartinteract';
|
||||||
import * as smartlegal from '@push.rocks/smartlegal';
|
import * as smartlegal from '@push.rocks/smartlegal';
|
||||||
import * as smartobject from '@push.rocks/smartobject';
|
import * as smartobject from '@push.rocks/smartobject';
|
||||||
import * as smartnpm from '@push.rocks/smartnpm';
|
import * as smartconfig from '@push.rocks/smartconfig';
|
||||||
import * as smartstream from '@push.rocks/smartstream';
|
|
||||||
import * as through2 from 'through2';
|
|
||||||
import * as npmextra from '@push.rocks/npmextra';
|
|
||||||
import * as smartdiff from '@push.rocks/smartdiff';
|
import * as smartdiff from '@push.rocks/smartdiff';
|
||||||
|
import * as smartscaf from '@push.rocks/smartscaf';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
crypto,
|
|
||||||
path,
|
path,
|
||||||
lik,
|
|
||||||
smartfile,
|
smartfile,
|
||||||
smartgulp,
|
|
||||||
smartinteract,
|
smartinteract,
|
||||||
smartlegal,
|
smartlegal,
|
||||||
smartobject,
|
smartobject,
|
||||||
smartnpm,
|
smartconfig,
|
||||||
smartstream,
|
|
||||||
through2,
|
|
||||||
npmextra,
|
|
||||||
smartdiff,
|
smartdiff,
|
||||||
|
smartscaf,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ export interface IGlobalRegistryData {
|
|||||||
|
|
||||||
export class GlobalRegistry {
|
export class GlobalRegistry {
|
||||||
private static instance: GlobalRegistry | null = null;
|
private static instance: GlobalRegistry | null = null;
|
||||||
private kvStore: plugins.npmextra.KeyValueStore<IGlobalRegistryData>;
|
private kvStore: plugins.smartconfig.KeyValueStore<IGlobalRegistryData>;
|
||||||
private docker: DockerContainer;
|
private docker: DockerContainer;
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
this.kvStore = new plugins.npmextra.KeyValueStore({
|
this.kvStore = new plugins.smartconfig.KeyValueStore({
|
||||||
typeArg: 'userHomeDir',
|
typeArg: 'userHomeDir',
|
||||||
identityArg: 'gitzone-services',
|
identityArg: 'gitzone-services',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class ServiceManager {
|
|||||||
await this.config.loadOrCreate();
|
await this.config.loadOrCreate();
|
||||||
logger.log('info', `📋 Project: ${this.config.getConfig().PROJECT_NAME}`);
|
logger.log('info', `📋 Project: ${this.config.getConfig().PROJECT_NAME}`);
|
||||||
|
|
||||||
// Load service selection from npmextra.json
|
// Load service selection from .smartconfig.json
|
||||||
await this.loadServiceConfiguration();
|
await this.loadServiceConfiguration();
|
||||||
|
|
||||||
// Validate and update ports if needed
|
// Validate and update ports if needed
|
||||||
@@ -39,11 +39,11 @@ export class ServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load service configuration from npmextra.json
|
* Load service configuration from .smartconfig.json
|
||||||
*/
|
*/
|
||||||
private async loadServiceConfiguration(): Promise<void> {
|
private async loadServiceConfiguration(): Promise<void> {
|
||||||
const npmextraConfig = new plugins.npmextra.Npmextra(process.cwd());
|
const smartconfigInstance = new plugins.smartconfig.Smartconfig(process.cwd());
|
||||||
const gitzoneConfig = npmextraConfig.dataFor<any>('@git.zone/cli', {});
|
const gitzoneConfig = smartconfigInstance.dataFor<any>('@git.zone/cli', {});
|
||||||
|
|
||||||
// Check if services array exists
|
// Check if services array exists
|
||||||
if (!gitzoneConfig.services || !Array.isArray(gitzoneConfig.services) || gitzoneConfig.services.length === 0) {
|
if (!gitzoneConfig.services || !Array.isArray(gitzoneConfig.services) || gitzoneConfig.services.length === 0) {
|
||||||
@@ -63,7 +63,7 @@ export class ServiceManager {
|
|||||||
|
|
||||||
this.enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
this.enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
||||||
|
|
||||||
// Save to npmextra.json
|
// Save to .smartconfig.json
|
||||||
await this.saveServiceConfiguration(this.enabledServices);
|
await this.saveServiceConfiguration(this.enabledServices);
|
||||||
} else {
|
} else {
|
||||||
this.enabledServices = gitzoneConfig.services;
|
this.enabledServices = gitzoneConfig.services;
|
||||||
@@ -72,31 +72,31 @@ export class ServiceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save service configuration to npmextra.json
|
* Save service configuration to .smartconfig.json
|
||||||
*/
|
*/
|
||||||
private async saveServiceConfiguration(services: string[]): Promise<void> {
|
private async saveServiceConfiguration(services: string[]): Promise<void> {
|
||||||
const npmextraPath = plugins.path.join(process.cwd(), 'npmextra.json');
|
const smartconfigPath = plugins.path.join(process.cwd(), '.smartconfig.json');
|
||||||
let npmextraData: any = {};
|
let smartconfigData: any = {};
|
||||||
|
|
||||||
// Read existing npmextra.json if it exists
|
// Read existing .smartconfig.json if it exists
|
||||||
if (await plugins.smartfs.file(npmextraPath).exists()) {
|
if (await plugins.smartfs.file(smartconfigPath).exists()) {
|
||||||
const content = await plugins.smartfs.file(npmextraPath).encoding('utf8').read();
|
const content = await plugins.smartfs.file(smartconfigPath).encoding('utf8').read();
|
||||||
npmextraData = JSON.parse(content as string);
|
smartconfigData = JSON.parse(content as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update @git.zone/cli.services
|
// Update @git.zone/cli.services
|
||||||
if (!npmextraData['@git.zone/cli']) {
|
if (!smartconfigData['@git.zone/cli']) {
|
||||||
npmextraData['@git.zone/cli'] = {};
|
smartconfigData['@git.zone/cli'] = {};
|
||||||
}
|
}
|
||||||
npmextraData['@git.zone/cli'].services = services;
|
smartconfigData['@git.zone/cli'].services = services;
|
||||||
|
|
||||||
// Write back to npmextra.json
|
// Write back to .smartconfig.json
|
||||||
await plugins.smartfs
|
await plugins.smartfs
|
||||||
.file(npmextraPath)
|
.file(smartconfigPath)
|
||||||
.encoding('utf8')
|
.encoding('utf8')
|
||||||
.write(JSON.stringify(npmextraData, null, 2));
|
.write(JSON.stringify(smartconfigData, null, 2));
|
||||||
|
|
||||||
logger.log('ok', `✅ Saved service configuration to npmextra.json`);
|
logger.log('ok', `✅ Saved service configuration to .smartconfig.json`);
|
||||||
logger.log('info', `🔧 Enabled services: ${services.join(', ')}`);
|
logger.log('info', `🔧 Enabled services: ${services.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,7 +904,7 @@ export class ServiceManager {
|
|||||||
|
|
||||||
this.enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
this.enabledServices = response.value || ['mongodb', 'minio', 'elasticsearch'];
|
||||||
|
|
||||||
// Save to npmextra.json
|
// Save to .smartconfig.json
|
||||||
await this.saveServiceConfiguration(this.enabledServices);
|
await this.saveServiceConfiguration(this.enabledServices);
|
||||||
|
|
||||||
logger.log('ok', '✅ Service configuration updated');
|
logger.log('ok', '✅ Service configuration updated');
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import * as smartlog from '@push.rocks/smartlog';
|
import * as smartlog from '@push.rocks/smartlog';
|
||||||
import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local';
|
import * as smartlogDestinationLocal from '@push.rocks/smartlog-destination-local';
|
||||||
import * as npmextra from '@push.rocks/npmextra';
|
import * as smartconfig from '@push.rocks/smartconfig';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as projectinfo from '@push.rocks/projectinfo';
|
import * as projectinfo from '@push.rocks/projectinfo';
|
||||||
import * as smartcli from '@push.rocks/smartcli';
|
import * as smartcli from '@push.rocks/smartcli';
|
||||||
@@ -20,7 +20,7 @@ export const smartfs = new SmartFs(new SmartFsProviderNode());
|
|||||||
export {
|
export {
|
||||||
smartlog,
|
smartlog,
|
||||||
smartlogDestinationLocal,
|
smartlogDestinationLocal,
|
||||||
npmextra,
|
smartconfig,
|
||||||
path,
|
path,
|
||||||
projectinfo,
|
projectinfo,
|
||||||
smartcli,
|
smartcli,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
|
||||||
"useDefineForClassFields": false,
|
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
|
|||||||
Reference in New Issue
Block a user