From 7c8c8d8cb4401c2aef7f5e7abc60dfa1bf9e779c Mon Sep 17 00:00:00 2001 From: dennismeister93 Date: Thu, 23 May 2024 14:46:08 +0200 Subject: [PATCH 1/3] fix: correctly fetch all tags of remote repository during init (#271) * fix: correctly fetch all tags of remote repository during init * fix: prune also branches when fetching --------- Signed-off-by: Dennis Meister --- src/modules/package-downloader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/package-downloader.ts b/src/modules/package-downloader.ts index 5f62533..0b1b42b 100644 --- a/src/modules/package-downloader.ts +++ b/src/modules/package-downloader.ts @@ -34,7 +34,7 @@ export class PackageDownloader { const localRepoExists = await this.git.checkIsRepo(checkRepoAction); if (localRepoExists) { - await this.git.fetch(['--all']); + await this.git.fetch(['--force', '--tags', '--prune', '--prune-tags']); } else { await this.git.clone(this.packageConfig.getPackageRepo(), packageDir, cloneOpts); } From 6d39c847b7820793350ff1822fe807802112d6ac Mon Sep 17 00:00:00 2001 From: Dominic Sudy <99014187+doosuu@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:20:14 +0200 Subject: [PATCH 2/3] feat: Add component base path (#270) --- src/commands/package/index.ts | 4 +- src/commands/sync/index.ts | 8 ++-- src/modules/component.ts | 12 +++++ src/modules/exec.ts | 2 +- src/modules/package.ts | 2 +- src/modules/setup.ts | 10 ++-- test/system-test/exec.stest.ts | 7 +++ test/system-test/sync.stest.ts | 7 +++ test/unit/component.test.ts | 44 ++++++++++++++++++ .../components/test-component2/nested.py | 15 ++++++ .../test-package/test-version/manifest.json | 46 +++++++++++++++---- testbench/test-sync/.velocitas.json | 3 +- .../test-version/manifest.json | 1 - .../test-version/manifest.json | 11 ++++- .../test-packageTwo/test-version/nested/dummy | 1 + 15 files changed, 148 insertions(+), 25 deletions(-) create mode 100644 test/unit/component.test.ts create mode 100644 testbench/test-exec/packages/test-package/test-version/components/test-component2/nested.py create mode 100644 testbench/test-sync/packages/test-packageTwo/test-version/nested/dummy diff --git a/src/commands/package/index.ts b/src/commands/package/index.ts index 86e2881..10530e3 100644 --- a/src/commands/package/index.ts +++ b/src/commands/package/index.ts @@ -58,10 +58,8 @@ export default class Package extends Command { if (args.name) { packagesToPrint.push(projectConfig.getPackages().find((pkgCfg: PackageConfig) => pkgCfg.getPackageName() === args.name)!); - const componentDir = join(packagesToPrint[0].getPackageDirectory(), packagesToPrint[0].version); - if (flags.getPath) { - this.log(componentDir); + this.log(packagesToPrint[0].getPackageDirectoryWithVersion()); return; } } else { diff --git a/src/commands/sync/index.ts b/src/commands/sync/index.ts index 83e3b9b..38631fb 100644 --- a/src/commands/sync/index.ts +++ b/src/commands/sync/index.ts @@ -30,13 +30,13 @@ Syncing Velocitas components! this.log(`Syncing Velocitas components!`); const projectConfig = ProjectConfigIO.read(`v${this.config.version}`); - for (const component of projectConfig.getComponentContexts()) { - if (!component.manifest.files || component.manifest.files.length === 0) { + for (const componentContext of projectConfig.getComponentContexts()) { + if (!componentContext.manifest.files || componentContext.manifest.files.length === 0) { continue; } - this.log(`... syncing '${component.manifest.id}'`); - installComponent(component.packageConfig, component.manifest, projectConfig.getVariableCollection(component)); + this.log(`... syncing '${componentContext.manifest.id}'`); + installComponent(componentContext, projectConfig.getVariableCollection(componentContext)); } } } diff --git a/src/modules/component.ts b/src/modules/component.ts index 9417223..72188ca 100644 --- a/src/modules/component.ts +++ b/src/modules/component.ts @@ -12,6 +12,7 @@ // // SPDX-License-Identifier: Apache-2.0 +import { join } from 'node:path'; import { PackageConfig } from './package'; import { VariableDefinition } from './variables'; @@ -75,6 +76,9 @@ export interface ComponentManifest { // Human readable description of the component, if any. description?: string; + // A base path within the package where all files and programs are located to relatively. + basePath?: string; + // A list of files that need to be copied from source to target when running `velocitas sync`. files?: FileSpec[]; @@ -114,4 +118,12 @@ export class ComponentContext { this.config = config; this.usedInProject = usedInProject; } + + getComponentPath(): string { + const componentPath = this.packageConfig.getPackageDirectoryWithVersion(); + if (this.manifest.basePath) { + return join(componentPath, this.manifest.basePath); + } + return componentPath; + } } diff --git a/src/modules/exec.ts b/src/modules/exec.ts index c859e6b..b2a48d0 100644 --- a/src/modules/exec.ts +++ b/src/modules/exec.ts @@ -112,7 +112,7 @@ export async function runExecSpec( throw new Error(`No program found for item '${execSpec.ref}' referenced in program list of '${componentId}'`); } - const cwd = join(componentContext.packageConfig.getPackageDirectory(), componentContext.packageConfig.version); + const cwd = componentContext.getComponentPath(); let programArgs = programSpec.args ? programSpec.args : []; if (execSpec.args && execSpec.args.length > 0) { diff --git a/src/modules/package.ts b/src/modules/package.ts index 8a62a16..b20a15a 100644 --- a/src/modules/package.ts +++ b/src/modules/package.ts @@ -138,7 +138,7 @@ export function getVelocitasRoot(): string { return join(process.env.VELOCITAS_HOME ? process.env.VELOCITAS_HOME : homedir(), '.velocitas'); } -function getPackageFolderPath(): string { +export function getPackageFolderPath(): string { return join(getVelocitasRoot(), 'packages'); } diff --git a/src/modules/setup.ts b/src/modules/setup.ts index 6b2fe4f..852a196 100644 --- a/src/modules/setup.ts +++ b/src/modules/setup.ts @@ -18,7 +18,7 @@ import { cwd } from 'node:process'; import { Transform, TransformCallback, TransformOptions } from 'node:stream'; import copy from 'recursive-copy'; import { CliFileSystem } from '../utils/fs-bridge'; -import { ComponentManifest } from './component'; +import { ComponentContext, ComponentManifest } from './component'; import { PackageConfig } from './package'; import { VariableCollection } from './variables'; @@ -80,15 +80,15 @@ class ReplaceVariablesStream extends Transform { } } -export function installComponent(packageConfig: PackageConfig, component: ComponentManifest, variables: VariableCollection) { - if (component.files) { - for (const spec of component.files) { +export function installComponent(component: ComponentContext, variables: VariableCollection) { + if (component.manifest.files) { + for (const spec of component.manifest.files) { const src = variables.substitute(spec.src); const dst = variables.substitute(spec.dst); let ifCondition = spec.condition ? variables.substitute(spec.condition) : 'true'; if (eval(ifCondition)) { - const sourceFileOrDir = join(packageConfig.getPackageDirectory(), packageConfig.version, src); + const sourceFileOrDir = join(component.getComponentPath(), src); const destFileOrDir = join(cwd(), dst); try { if (CliFileSystem.existsSync(sourceFileOrDir)) { diff --git a/test/system-test/exec.stest.ts b/test/system-test/exec.stest.ts index 38b4922..9c61d31 100644 --- a/test/system-test/exec.stest.ts +++ b/test/system-test/exec.stest.ts @@ -179,6 +179,13 @@ describe('CLI command', () => { }); expect(result.status).to.be.equal(1); }); + + it('should execute programs with relative paths', async () => { + const result = spawnSync(VELOCITAS_PROCESS, ['exec', 'test-component2', 'nested'], { + encoding: DEFAULT_BUFFER_ENCODING, + }); + expect(result.stdout.trim()).to.be.equal("hello nested"); + }); }); }); diff --git a/test/system-test/sync.stest.ts b/test/system-test/sync.stest.ts index 90bb5f6..96172a1 100644 --- a/test/system-test/sync.stest.ts +++ b/test/system-test/sync.stest.ts @@ -28,6 +28,7 @@ const packageManifestTwo = JSON.parse( const fileOneDestination = packageManifestOne.components[0].files[0].dst; const fileTwoDestination = packageManifestTwo.components[0].files[0].dst; +const fileThreeDestination = packageManifestTwo.components[1].files[0].dst; describe('CLI command', () => { describe('sync', () => { @@ -38,6 +39,7 @@ describe('CLI command', () => { afterEach(() => { removeSync(`./${fileOneDestination}`); removeSync(`./${fileTwoDestination}`); + removeSync(`./${fileThreeDestination}`); }); it('should sync configured setup components and replace variables accordingly', async () => { const syncOutput = spawnSync(VELOCITAS_PROCESS, ['sync'], { encoding: DEFAULT_BUFFER_ENCODING }); @@ -56,6 +58,11 @@ describe('CLI command', () => { expect(resultTwo.stdout).to.contain('projectTest'); expect(resultTwo.stdout).to.contain('packageTestTwo'); expect(resultTwo.stdout).to.contain(2); + + const resultThree = readFileSync(`./${fileThreeDestination}`, { + encoding: DEFAULT_BUFFER_ENCODING, + }); + expect(resultThree).to.equal('A nested file\n'); }); }); }); diff --git a/test/unit/component.test.ts b/test/unit/component.test.ts new file mode 100644 index 0000000..87d83ef --- /dev/null +++ b/test/unit/component.test.ts @@ -0,0 +1,44 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// This program and the accompanying materials are made available under the +// terms of the Apache License, Version 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { expect } from 'chai'; +import 'mocha'; +import { ComponentConfig, ComponentContext, ComponentManifest } from '../../src/modules/component'; +import { PackageConfig, getPackageFolderPath } from '../../src/modules/package'; +import { join } from 'path'; + +function createDefaultComponentContext(basePath?: string): ComponentContext { + const componentId = "componentUnderTest"; + const pkgConfig = new PackageConfig({ repo: "packageUnderTest", version: "main" }); + const componentManifest: ComponentManifest = { + id: componentId, + basePath: basePath + }; + const componentConfig = new ComponentConfig(componentId); + return new ComponentContext(pkgConfig, componentManifest, componentConfig, true); +} + +describe('component - module', () => { + describe('ComponentContext`s getComponentPath function', () => { + it('should return the package path as the component path, if no base path is given', () => { + const componentContext = createDefaultComponentContext(); + expect(componentContext.getComponentPath()).to.equal(join(getPackageFolderPath(), "packageUnderTest", "main")); + }); + + it('should return the package path extended by the basePath as the component path, if a base path is given', () => { + const componentContext = createDefaultComponentContext("foo/bar"); + expect(componentContext.getComponentPath()).to.equal(join(getPackageFolderPath(), "packageUnderTest", "main", "foo", "bar")); + }); + }); +}); diff --git a/testbench/test-exec/packages/test-package/test-version/components/test-component2/nested.py b/testbench/test-exec/packages/test-package/test-version/components/test-component2/nested.py new file mode 100644 index 0000000..bd8f266 --- /dev/null +++ b/testbench/test-exec/packages/test-package/test-version/components/test-component2/nested.py @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +print("hello nested") diff --git a/testbench/test-exec/packages/test-package/test-version/manifest.json b/testbench/test-exec/packages/test-package/test-version/manifest.json index 3a1f959..79ca7b6 100644 --- a/testbench/test-exec/packages/test-package/test-version/manifest.json +++ b/testbench/test-exec/packages/test-package/test-version/manifest.json @@ -2,7 +2,6 @@ "components": [ { "id": "test-component", - "type": "setup", "programs": [ { "id": "echo-env", @@ -11,37 +10,68 @@ { "id": "executable-on-path", "executable": "python3", - "args": ["./hello-world.py"] + "args": [ + "./hello-world.py" + ] }, { "id": "set-cache", "executable": "python3", - "args": ["./set-cache.py"] + "args": [ + "./set-cache.py" + ] }, { "id": "get-cache", "executable": "python3", - "args": ["./get-cache.py"] + "args": [ + "./get-cache.py" + ] }, { "id": "tty", "executable": "docker", - "args": ["run", "-it", "hello-world"] + "args": [ + "run", + "-it", + "hello-world" + ] }, { "id": "print-args", "executable": "python3", - "args": ["./print-args.py", "default", "foo"] + "args": [ + "./print-args.py", + "default", + "foo" + ] }, { "id": "print-args-no-default", "executable": "python3", - "args": ["./print-args.py"] + "args": [ + "./print-args.py" + ] }, { "id": "exit", "executable": "python3", - "args": ["./exit.py"] + "args": [ + "./exit.py" + ] + } + ] + }, + { + "id": "test-component2", + "basePath": "components/test-component2", + "programs": [ + { + "id": "nested", + "executable": "python3", + "args": [ + "./nested.py" + ] } ] } diff --git a/testbench/test-sync/.velocitas.json b/testbench/test-sync/.velocitas.json index fe49b08..a3e41ef 100644 --- a/testbench/test-sync/.velocitas.json +++ b/testbench/test-sync/.velocitas.json @@ -5,7 +5,8 @@ }, "components": [ "test-componentOne", - "test-componentTwo" + "test-componentTwo", + "test-componentThree" ], "variables": { "projectVariable": "projectTest", diff --git a/testbench/test-sync/packages/test-packageOne/test-version/manifest.json b/testbench/test-sync/packages/test-packageOne/test-version/manifest.json index 1b0cf23..ffef835 100644 --- a/testbench/test-sync/packages/test-packageOne/test-version/manifest.json +++ b/testbench/test-sync/packages/test-packageOne/test-version/manifest.json @@ -2,7 +2,6 @@ "components": [ { "id": "test-componentOne", - "type": "setup", "files": [ { "src": "testFile.sh", diff --git a/testbench/test-sync/packages/test-packageTwo/test-version/manifest.json b/testbench/test-sync/packages/test-packageTwo/test-version/manifest.json index a7f726c..bd21532 100644 --- a/testbench/test-sync/packages/test-packageTwo/test-version/manifest.json +++ b/testbench/test-sync/packages/test-packageTwo/test-version/manifest.json @@ -2,13 +2,22 @@ "components": [ { "id": "test-componentTwo", - "type": "setup", "files": [ { "src": "testFile.sh", "dst": "testFile2.sh" } ] + }, + { + "id": "test-componentThree", + "basePath": "nested", + "files": [ + { + "src": "dummy", + "dst": "dummy" + } + ] } ] } diff --git a/testbench/test-sync/packages/test-packageTwo/test-version/nested/dummy b/testbench/test-sync/packages/test-packageTwo/test-version/nested/dummy new file mode 100644 index 0000000..f99600b --- /dev/null +++ b/testbench/test-sync/packages/test-packageTwo/test-version/nested/dummy @@ -0,0 +1 @@ +A nested file From 60e6ce01e5772a5976c157c44f0bf9cae0d7ee6b Mon Sep 17 00:00:00 2001 From: dennismeister93 Date: Fri, 7 Jun 2024 15:26:37 +0200 Subject: [PATCH 3/3] fix: fix detection of failed package download (#276) * fix: detection of failed package download Signed-off-by: Dennis Meister --- NOTICE-3RD-PARTY-CONTENT.md | 21 +-- package-lock.json | 232 ++++++++++++++---------------- package.json | 21 +-- src/commands/init/index.ts | 2 +- src/modules/package-downloader.ts | 52 ++++--- src/modules/package.ts | 11 +- src/utils/fs-bridge.ts | 24 ++++ test/commands/init/init.test.ts | 24 +++- test/helpers/simpleGit.ts | 4 +- 9 files changed, 219 insertions(+), 172 deletions(-) diff --git a/NOTICE-3RD-PARTY-CONTENT.md b/NOTICE-3RD-PARTY-CONTENT.md index d567c6c..bcb2227 100644 --- a/NOTICE-3RD-PARTY-CONTENT.md +++ b/NOTICE-3RD-PARTY-CONTENT.md @@ -3,14 +3,15 @@ ## JavaScript | Dependency | Version | License | |:-----------|:-------:|--------:| -|@oclif/core|3.26.2|MIT| -|@oclif/test|3.2.8|MIT| -|@types/chai|4.3.14|MIT| +|@oclif/core|3.27.0|MIT| +|@oclif/test|3.2.15|MIT| +|@types/chai|4.3.16|MIT| |@types/fs-extra|11.0.4|MIT| |@types/mocha|10.0.6|MIT| -|@types/node|20.12.7|MIT| -|@typescript-eslint/eslint-plugin|7.6.0|MIT| -|@typescript-eslint/parser|7.6.0|unknown| +|@types/node|20.14.2|MIT| +|@types/semver|7.5.8|MIT| +|@typescript-eslint/eslint-plugin|7.12.0|MIT| +|@typescript-eslint/parser|7.12.0|unknown| |@yao-pkg/pkg|5.11.5|MIT| |chai|4.4.1|MIT| |eslint|8.57.0|ISC
MIT| @@ -19,17 +20,17 @@ |mocha|10.4.0|ISC
MIT| |node-pty|1.0.0|MIT| |nyc|15.1.0|ISC
MIT| -|prettier|3.2.5|Apache 2.0| +|prettier|3.3.1|Apache 2.0| |prettier-plugin-organize-imports|3.2.4|MIT| |recursive-copy|2.0.14|ISC
MIT| -|semver|7.6.0|ISC| +|semver|7.6.2|ISC| |shx|0.3.4|MIT| |simple-git|3.24.0|unknown| |sinon|17.0.1|MIT
New BSD| |ts-node|10.9.2|MIT| -|typescript|5.4.4|Apache 2.0| +|typescript|5.4.5|Apache 2.0| |velocitas-cli|0.0.0|Apache 2.0| -|yaml|2.4.1|ISC| +|yaml|2.4.3|ISC| ## Workflows | Dependency | Version | License | |:-----------|:-------:|--------:| diff --git a/package-lock.json b/package-lock.json index c02a726..a06e1aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,37 +9,38 @@ "version": "0.0.0", "license": "Apache-2.0", "dependencies": { - "@oclif/core": "3.26.2", + "@oclif/core": "3.27.0", "fs-extra": "11.2.0", "inquirer": "8.2.6", "node-pty": "1.0.0", "recursive-copy": "2.0.14", - "semver": "7.6.0", + "semver": "7.6.2", "simple-git": "3.24.0" }, "bin": { "velocitas": "bin/run" }, "devDependencies": { - "@oclif/test": "3.2.8", - "@types/chai": "4.3.14", + "@oclif/test": "3.2.15", + "@types/chai": "4.3.16", "@types/fs-extra": "11.0.4", "@types/mocha": "10.0.6", - "@types/node": "20.12.7", - "@typescript-eslint/eslint-plugin": "7.6.0", - "@typescript-eslint/parser": "7.6.0", + "@types/node": "20.14.2", + "@types/semver": "^7.5.8", + "@typescript-eslint/eslint-plugin": "7.12.0", + "@typescript-eslint/parser": "7.12.0", "@yao-pkg/pkg": "5.11.5", "chai": "4.4.1", "eslint": "8.57.0", "mocha": "10.4.0", "nyc": "15.1.0", - "prettier": "3.2.5", + "prettier": "3.3.1", "prettier-plugin-organize-imports": "3.2.4", "shx": "0.3.4", "sinon": "^17.0.1", "ts-node": "10.9.2", - "typescript": "5.4.4", - "yaml": "2.4.1" + "typescript": "5.4.5", + "yaml": "2.4.3" }, "engines": { "node": ">=20.0.0" @@ -949,9 +950,9 @@ } }, "node_modules/@oclif/core": { - "version": "3.26.2", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-3.26.2.tgz", - "integrity": "sha512-Gpn21jKjcOx0TecI1wLJrY/65jtgJx5f1GzTc81oKvEpKes1b3Li2SMZygRaWRpcQ3wjN0d7lTPi8WwLsmTBjA==", + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-3.27.0.tgz", + "integrity": "sha512-Fg93aNFvXzBq5L7ztVHFP2nYwWU1oTCq48G0TjF/qC1UN36KWa2H5Hsm72kERd5x/sjy2M2Tn4kDEorUlpXOlw==", "dependencies": { "@types/cli-progress": "^3.11.5", "ansi-escapes": "^4.3.2", @@ -961,8 +962,8 @@ "clean-stack": "^3.0.1", "cli-progress": "^3.12.0", "color": "^4.2.3", - "debug": "^4.3.4", - "ejs": "^3.1.9", + "debug": "^4.3.5", + "ejs": "^3.1.10", "get-package-type": "^0.1.0", "globby": "^11.1.0", "hyperlinker": "^1.0.0", @@ -986,15 +987,31 @@ "node": ">=18.0.0" } }, + "node_modules/@oclif/core/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@oclif/test": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@oclif/test/-/test-3.2.8.tgz", - "integrity": "sha512-aC523gJxzRxzL1P1m2Mrdu/xl0FG+ho8GAcZtdUKNOMM6te2iwR481GRzs8B0xDtAPNi0jZWiPFetbyR7mh/kA==", + "version": "3.2.15", + "resolved": "https://registry.npmjs.org/@oclif/test/-/test-3.2.15.tgz", + "integrity": "sha512-XqG3RosozNqySkxSXInU12Xec2sPSOkqYHJDfdFZiWG3a8Cxu4dnPiAQvms+BJsOlLQmfEQlSHqiyVUKOMHhXA==", "dev": true, "dependencies": { - "@oclif/core": "^3.26.0", + "@oclif/core": "^3.26.6", "chai": "^4.4.1", - "fancy-test": "^3.0.14" + "fancy-test": "^3.0.15" }, "engines": { "node": ">=18.0.0" @@ -1069,9 +1086,9 @@ "dev": true }, "node_modules/@types/chai": { - "version": "4.3.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", - "integrity": "sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==", + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true }, "node_modules/@types/cli-progress": { @@ -1092,12 +1109,6 @@ "@types/node": "*" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, "node_modules/@types/jsonfile": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", @@ -1108,9 +1119,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", - "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==", "dev": true }, "node_modules/@types/mocha": { @@ -1120,9 +1131,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", + "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", "dependencies": { "undici-types": "~5.26.4" } @@ -1149,21 +1160,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", - "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.12.0.tgz", + "integrity": "sha512-7F91fcbuDf/d3S8o21+r3ZncGIke/+eWk0EpO21LXhDfLahriZF9CGj4fbAetEjlaBdjdSm9a6VeXbpbT6Z40Q==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/type-utils": "7.6.0", - "@typescript-eslint/utils": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", - "debug": "^4.3.4", + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/type-utils": "7.12.0", + "@typescript-eslint/utils": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -1184,15 +1193,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.12.0.tgz", + "integrity": "sha512-dm/J2UDY3oV3TKius2OUZIFHsomQmpHtsV0FTh1WO8EKgHLQ1QCADUqscPgTpU+ih1e21FQSRjXckHn3txn6kQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/typescript-estree": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "debug": "^4.3.4" }, "engines": { @@ -1212,13 +1221,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", - "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz", + "integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0" + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1229,13 +1238,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", - "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.12.0.tgz", + "integrity": "sha512-lib96tyRtMhLxwauDWUp/uW3FMhLA6D0rJ8T7HmH7x23Gk1Gwwu8UZ94NMXBvOELn6flSPiBrCKlehkiXyaqwA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.6.0", - "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/typescript-estree": "7.12.0", + "@typescript-eslint/utils": "7.12.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1256,9 +1265,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", - "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz", + "integrity": "sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1269,13 +1278,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", - "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz", + "integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/visitor-keys": "7.6.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1297,18 +1306,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", - "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz", + "integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.6.0", - "@typescript-eslint/types": "7.6.0", - "@typescript-eslint/typescript-estree": "7.6.0", - "semver": "^7.6.0" + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/typescript-estree": "7.12.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1322,12 +1328,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", - "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz", + "integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/types": "7.12.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2259,9 +2265,9 @@ } }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": { "jake": "^10.8.5" }, @@ -2546,9 +2552,10 @@ } }, "node_modules/fancy-test": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-3.0.14.tgz", - "integrity": "sha512-FkiDltQA8PBZzw5tUnMrP1QtwIdNQWxxbZK5C22M9wu6HfFNciwkG3D9siT4l4s1fBTDaEG+Fdkbpi9FDTxtrg==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-3.0.16.tgz", + "integrity": "sha512-y1xZFpyYbE2TMiT+agOW2Emv8gr73zvDrKKbcXc8L+gMyIVJFn71cc4ICfzu2zEXjHirpHpdDJN0JBX99wwDXQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { "@types/chai": "*", @@ -3561,9 +3568,9 @@ } }, "node_modules/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", + "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -4742,9 +4749,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz", + "integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -5209,12 +5216,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -5222,22 +5226,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -5906,9 +5894,9 @@ } }, "node_modules/typescript": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", - "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6105,9 +6093,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", - "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz", + "integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==", "dev": true, "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index ef0484e..5c9d4e8 100644 --- a/package.json +++ b/package.json @@ -17,34 +17,35 @@ ], "repository": "eclipse-velocitas/cli", "dependencies": { - "@oclif/core": "3.26.2", + "@oclif/core": "3.27.0", "fs-extra": "11.2.0", "inquirer": "8.2.6", "node-pty": "1.0.0", "recursive-copy": "2.0.14", - "semver": "7.6.0", + "semver": "7.6.2", "simple-git": "3.24.0" }, "devDependencies": { - "@oclif/test": "3.2.8", - "@types/chai": "4.3.14", + "@oclif/test": "3.2.15", + "@types/chai": "4.3.16", "@types/fs-extra": "11.0.4", "@types/mocha": "10.0.6", - "@types/node": "20.12.7", - "@typescript-eslint/eslint-plugin": "7.6.0", - "@typescript-eslint/parser": "7.6.0", + "@types/node": "20.14.2", + "@types/semver": "^7.5.8", + "@typescript-eslint/eslint-plugin": "7.12.0", + "@typescript-eslint/parser": "7.12.0", "@yao-pkg/pkg": "5.11.5", "chai": "4.4.1", "eslint": "8.57.0", "mocha": "10.4.0", "nyc": "15.1.0", - "prettier": "3.2.5", + "prettier": "3.3.1", "prettier-plugin-organize-imports": "3.2.4", "shx": "0.3.4", "sinon": "^17.0.1", "ts-node": "10.9.2", - "typescript": "5.4.4", - "yaml": "2.4.1" + "typescript": "5.4.5", + "yaml": "2.4.3" }, "oclif": { "bin": "velocitas", diff --git a/src/commands/init/index.ts b/src/commands/init/index.ts index c95f491..75bef98 100644 --- a/src/commands/init/index.ts +++ b/src/commands/init/index.ts @@ -179,7 +179,7 @@ export default class Init extends Command { for (const packageConfig of packageConfigs) { await this._resolveVersion(packageConfig, flags.verbose); - if (!flags.force && packageConfig.isPackageInstalled()) { + if (!flags.force && packageConfig.isPackageInstalled() && (await packageConfig.isPackageValidRepo(flags.verbose))) { this.log(`... '${packageConfig.getPackageName()}:${packageConfig.version}' already installed.`); continue; } diff --git a/src/modules/package-downloader.ts b/src/modules/package-downloader.ts index 0b1b42b..47bfb43 100644 --- a/src/modules/package-downloader.ts +++ b/src/modules/package-downloader.ts @@ -12,7 +12,7 @@ // // SPDX-License-Identifier: Apache-2.0 -import { posix as pathPosix } from 'node:path'; +import { join } from 'node:path'; import { CheckRepoActions, SimpleGit, simpleGit } from 'simple-git'; import { CliFileSystem } from '../utils/fs-bridge'; import { PackageConfig } from './package'; @@ -26,57 +26,65 @@ export class PackageDownloader { this.packageConfig = packageConfig; } - async cloneRepository(packageDir: string, cloneOpts: string[]): Promise { + private async _cloneRepository(packageDir: string, cloneOpts: string[]): Promise { await this.git.clone(this.packageConfig.getPackageRepo(), packageDir, cloneOpts); } - async updateRepository(checkRepoAction: CheckRepoActions, packageDir: string, cloneOpts: string[]): Promise { + private async _updateRepository(checkRepoAction: CheckRepoActions): Promise { const localRepoExists = await this.git.checkIsRepo(checkRepoAction); if (localRepoExists) { await this.git.fetch(['--force', '--tags', '--prune', '--prune-tags']); - } else { - await this.git.clone(this.packageConfig.getPackageRepo(), packageDir, cloneOpts); } } - async checkoutVersion(): Promise { - await this.git.checkout( - this.packageConfig.version.startsWith(BRANCH_PREFIX) - ? this.packageConfig.version.substring(BRANCH_PREFIX.length) - : this.packageConfig.version, - ); + private async _checkoutVersion(version: string): Promise { + const branchOrTag = version.startsWith(BRANCH_PREFIX) ? version.substring(BRANCH_PREFIX.length) : version; + await this.git.checkout(branchOrTag); + } + + private async _checkForValidRepo(packageDir: string, cloneOpts: string[], checkRepoAction: CheckRepoActions): Promise { + let directoryExists = CliFileSystem.existsSync(packageDir); + if (directoryExists && !(await this.isValidRepo(packageDir, checkRepoAction))) { + CliFileSystem.removeSync(packageDir); + directoryExists = false; + } + + if (!directoryExists) { + await this._cloneRepository(packageDir, cloneOpts); + } + } + + public async isValidRepo(packageDir: string, checkRepoAction?: CheckRepoActions): Promise { + return await simpleGit(packageDir).checkIsRepo(checkRepoAction); } - async downloadPackage(option: { checkVersionOnly: boolean }): Promise { + public async downloadPackage(option: { checkVersionOnly: boolean }): Promise { let packageDir: string = this.packageConfig.getPackageDirectory(); - let cloneOpts: string[] = []; let checkRepoAction: CheckRepoActions; + const cloneOpts: string[] = []; if (option.checkVersionOnly) { - packageDir = pathPosix.join(packageDir, '_cache'); + packageDir = join(packageDir, '_cache'); cloneOpts.push('--bare'); checkRepoAction = CheckRepoActions.BARE; } else { - packageDir = pathPosix.join(packageDir, this.packageConfig.version); + packageDir = join(packageDir, this.packageConfig.version); checkRepoAction = CheckRepoActions.IS_REPO_ROOT; } - if (!CliFileSystem.existsSync(packageDir)) { - await this.cloneRepository(packageDir, cloneOpts); - } - + await this._checkForValidRepo(packageDir, cloneOpts, checkRepoAction); this.git = simpleGit(packageDir); - await this.updateRepository(checkRepoAction, packageDir, cloneOpts); + await this._updateRepository(checkRepoAction); if (!option.checkVersionOnly) { - await this.checkoutVersion(); + await this._checkoutVersion(this.packageConfig.version); } return this.git; } } -export const packageDownloader = (packageConfig: PackageConfig) => { +export const packageDownloader = (packageConfig: PackageConfig): PackageDownloader => { return new PackageDownloader(packageConfig); }; diff --git a/src/modules/package.ts b/src/modules/package.ts index b20a15a..830d168 100644 --- a/src/modules/package.ts +++ b/src/modules/package.ts @@ -116,10 +116,15 @@ export class PackageConfig { } isPackageInstalled(): boolean { - if (!CliFileSystem.existsSync(this.getPackageDirectoryWithVersion())) { - return false; + return CliFileSystem.existsSync(this.getPackageDirectoryWithVersion()); + } + + async isPackageValidRepo(verbose?: boolean): Promise { + const isValid = await packageDownloader(this).isValidRepo(this.getPackageDirectoryWithVersion()); + if (!isValid && verbose) { + console.log(`... Corrupted .git directory found for: '${this.getPackageName()}:${this.version}'`); } - return true; + return isValid; } readPackageManifest(): PackageManifest { diff --git a/src/utils/fs-bridge.ts b/src/utils/fs-bridge.ts index cd0da33..cc5cd78 100644 --- a/src/utils/fs-bridge.ts +++ b/src/utils/fs-bridge.ts @@ -43,6 +43,13 @@ interface IFileSystem { */ mkdirSync(path: fs.PathLike): string | undefined; + /** + * Removes a file or directory. The directory can have contents. + * If the path does not exist, silently does nothing. + * @param path A path to the file or directory as string. + */ + removeSync(path: string): void; + /** * Synchronously writes data to a file, replacing the file if it already exists. * @param path A path to the file to write to. @@ -100,6 +107,10 @@ class RealFileSystem implements IFileSystem { return fs.mkdirSync(path, { recursive: true }); } + removeSync(path: string): void { + fse.removeSync(path); + } + writeFileSync(path: fs.PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView): void { fs.writeFileSync(path, data, { encoding: DEFAULT_BUFFER_ENCODING }); } @@ -143,6 +154,10 @@ export class MockFileSystem implements IFileSystem, IFileSystemTests { return (this._fileSystemObj[path.toString()] = ''); } + removeSync(path: string): void { + delete this._fileSystemObj[path]; + } + writeFileSync(path: fs.PathOrFileDescriptor, data: any): void { this._fileSystemObj[path.toString()] = data; } @@ -203,6 +218,15 @@ export class CliFileSystem { return this._impl.mkdirSync(path); } + /** + * Removes a file or directory. The directory can have contents. + * If the path does not exist, silently does nothing. + * @param path A path to the file or directory as string. + */ + static removeSync(path: string): void { + return this._impl.removeSync(path); + } + /** * Synchronously writes data to a file, replacing the file if it already exists. * @param path A path to the file to write to. diff --git a/test/commands/init/init.test.ts b/test/commands/init/init.test.ts index 7f49294..83878a4 100644 --- a/test/commands/init/init.test.ts +++ b/test/commands/init/init.test.ts @@ -91,6 +91,26 @@ describe('init', () => { expect(ProjectConfigIO.isLockAvailable()).to.be.true; }); + test.do(() => { + mockFolders({ velocitasConfig: true, packageIndex: true, installedComponents: true }); + }) + .stdout() + .stub(gitModule, 'simpleGit', (stub) => stub.returns(simpleGitInstanceMock(undefined, false))) + .stub(exec, 'runExecSpec', (stub) => stub.returns({})) + .command(['init', '-v']) + .it('downloads corrupted packages again', (ctx) => { + expect(ctx.stdout).to.contain('Initializing Velocitas packages ...'); + expect(ctx.stdout).to.contain( + `... Corrupted .git directory found for: '${corePackageInfoMock.repo}:${corePackageInfoMock.resolvedVersion}'`, + ); + expect(ctx.stdout).to.contain(`... Downloading package: '${corePackageInfoMock.repo}:${corePackageInfoMock.resolvedVersion}'`); + expect( + CliFileSystem.existsSync( + `${userHomeDir}/.velocitas/packages/${corePackageInfoMock.repo}/${corePackageInfoMock.resolvedVersion}`, + ), + ).to.be.true; + }); + test.do(() => { mockFolders({ velocitasConfig: true, velocitasConfigLock: true, installedComponents: true, appManifest: false }); }) @@ -144,9 +164,9 @@ describe('init', () => { .stdout() .stub(gitModule, 'simpleGit', (stub) => stub.returns(simpleGitInstanceMock())) .stub(exec, 'runExecSpec', (stub) => stub.returns({})) - .command(['init', '--package', 'devenv-runtime']) + .command(['init', '--package', 'devenv-runtimes']) .it('adds a new entry to .velocitas.json if the package does not exist', (ctx) => { - expect(ctx.stdout).to.contain(`... Package 'devenv-runtime:v1.1.1' added to .velocitas.json`); + expect(ctx.stdout).to.contain(`... Package 'devenv-runtimes:v1.1.1' added to .velocitas.json`); }); test.do(() => { diff --git a/test/helpers/simpleGit.ts b/test/helpers/simpleGit.ts index 5d9cd87..64aaf97 100644 --- a/test/helpers/simpleGit.ts +++ b/test/helpers/simpleGit.ts @@ -15,7 +15,7 @@ import { CliFileSystem } from '../../src/utils/fs-bridge'; import { corePackageManifestMock, runtimePackageManifestMock, setupPackageManifestMock } from '../utils/mockConfig'; -export const simpleGitInstanceMock = (mockedNewVersionTag?: string) => { +export const simpleGitInstanceMock = (mockedNewVersionTag?: string, checkRepo: boolean = true) => { return { clone: async (repoPath: string, localPath: string, options?: any) => { await CliFileSystem.promisesMkdir(localPath); @@ -30,7 +30,7 @@ export const simpleGitInstanceMock = (mockedNewVersionTag?: string) => { } }, checkIsRepo: () => { - return true; + return checkRepo; }, fetch: () => {}, checkout: () => {