diff --git a/.cirrus.yml b/.cirrus.yml
index 8763c79..51c4b44 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -12,17 +12,35 @@ run_dev_task:
run_script:
- sleep 2000
-integration_test_dev_task:
+integration_test_dev_macos_task:
+ timeout_in: 120m
macos_instance:
image: codify-test-vm
+ cpu: 4
+ memory: 6124
+ storage: 100
node_modules_cache:
folder: node_modules
fingerprint_script: cat package-lock.json
populate_script: npm ci
test_script:
+ - zsh -i -c "npm run test:integration -- --disable-console-intercept $DEBUG --no-file-parallelism"
+
+integration_test_dev_linux_task:
+# trick cirrus CI to use tart here to run a linux instance
+ macos_instance:
+ image: codify-test-vm-linux
+# node_modules_cache:
+# folder: node_modules
+# fingerprint_script: cat package-lock.json
+# populate_script: npm ci
+ test_script:
+# - adduser --disabled-password --gecos '' newuser
+# - su - newuser
+ - npm ci
- npm run test:integration -- --disable-console-intercept $DEBUG --no-file-parallelism
-integration_individual_test_task:
+integration_individual_test_macos_task:
macos_instance:
image: codify-test-vm
node_modules_cache:
@@ -31,4 +49,19 @@ integration_individual_test_task:
populate_script: npm ci
test_script:
- echo $FILE_NAME
+ - zsh -i -c "npm run test -- $FILE_NAME --disable-console-intercept $DEBUG --no-file-parallelism"
+
+integration_individual_test_linux_task:
+ # trick cirrus CI to use tart here to run a linux instance
+ macos_instance:
+ image: codify-test-vm-linux
+# node_modules_cache:
+# folder: node_modules
+# fingerprint_script: cat package-lock.json
+# populate_script: npm ci
+ test_script:
+# - chown -R node /tmp/cirrus-ci/working-dir
+# - su node -c "npm install"
+ - echo $FILE_NAME
+ - npm ci
- npm run test -- $FILE_NAME --disable-console-intercept $DEBUG --no-file-parallelism
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 1b44dbd..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: Node.js CI
-
-on: [push]
-
-jobs:
- build:
-
- runs-on: macos-latest
-
- steps:
- - uses: actions/checkout@v4
- - name: Use Node.js
- uses: actions/setup-node@v3
- with:
- node-version: '18.x'
- - run: npm ci
- - run: npm run test:integration
diff --git a/.run/test_integration_dev -- $FilePathRelativeToProjectRoot$.run.xml b/.run/test_integration_dev -- $FilePathRelativeToProjectRoot$.run.xml
index a8fb251..b595360 100644
--- a/.run/test_integration_dev -- $FilePathRelativeToProjectRoot$.run.xml
+++ b/.run/test_integration_dev -- $FilePathRelativeToProjectRoot$.run.xml
@@ -8,6 +8,9 @@
+
+
+
\ No newline at end of file
diff --git a/.run/test_integration_dev.run.xml b/.run/test_integration_dev.run.xml
index ccc0aba..ea3b8a8 100644
--- a/.run/test_integration_dev.run.xml
+++ b/.run/test_integration_dev.run.xml
@@ -7,6 +7,9 @@
+
+
+
\ No newline at end of file
diff --git a/.run/test_integration_dev_linux.run.xml b/.run/test_integration_dev_linux.run.xml
new file mode 100644
index 0000000..06f946f
--- /dev/null
+++ b/.run/test_integration_dev_linux.run.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/esbuild.ts b/esbuild.ts
deleted file mode 100644
index 1584851..0000000
--- a/esbuild.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { build } from 'esbuild';
-
-const result = await build({
- entryPoints: ['src/index.ts'],
- bundle: true,
- minify: false,
- splitting: true,
- platform: 'node',
- outdir: 'dist',
- format: 'esm',
- loader: {
- '.node': 'file',
- '.cc': 'file',
- },
- // external: ['node-pty'],
- // packages: 'external',
- logLevel: 'debug',
-});
-
-console.log(result);
diff --git a/package-lock.json b/package-lock.json
index c927504..c74053b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,19 @@
{
"name": "default",
- "version": "0.16.1",
+ "version": "0.17.0-beta9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "default",
- "version": "0.16.1",
+ "version": "0.17.0-beta9",
"license": "ISC",
"dependencies": {
+ "@codifycli/plugin-core": "1.0.0-beta1",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"chalk": "^5.3.0",
- "codify-plugin-lib": "^1.0.180",
- "codify-schemas": "1.0.63",
+ "codify-schemas": "1.0.86-beta11",
"debug": "^4.3.4",
"lodash.isequal": "^4.5.0",
"nanoid": "^5.0.9",
@@ -41,7 +41,8 @@
"@types/node": "^18",
"@types/plist": "^3.0.5",
"@types/semver": "^7.5.4",
- "codify-plugin-test": "0.0.53",
+ "@types/uuid": "10.0.0",
+ "codify-plugin-test": "0.0.53-beta24",
"commander": "^12.1.0",
"eslint": "^8.51.0",
"eslint-config-oclif": "^5",
@@ -55,11 +56,14 @@
"ts-node": "^10.9.1",
"tslib": "^2.6.2",
"tsx": "^4.7.2",
- "typescript": "^5",
+ "typescript": "5.9.3",
"vitest": "^1.4.0"
},
"engines": {
"node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-linux-x64-musl": "4.9.5"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
@@ -105,6 +109,31 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@codifycli/plugin-core": {
+ "version": "1.0.0-beta1",
+ "resolved": "https://registry.npmjs.org/@codifycli/plugin-core/-/plugin-core-1.0.0-beta1.tgz",
+ "integrity": "sha512-BfSEYXC1n8U56szNfImOdvL9p3Qk7OwOrC8kGf2sikeBdYTWg0bwVcmItgpQfJNXTu0NO5hTob37puEqUwuYmQ==",
+ "license": "ISC",
+ "dependencies": {
+ "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
+ "@npmcli/promise-spawn": "^7.0.1",
+ "ajv": "^8.12.0",
+ "ajv-formats": "^2.1.1",
+ "clean-deep": "^3.4.0",
+ "codify-schemas": "1.0.86-beta11",
+ "lodash.isequal": "^4.5.0",
+ "nanoid": "^5.0.9",
+ "strip-ansi": "^7.1.0",
+ "uuid": "^10.0.0",
+ "zod": "4.1.13"
+ },
+ "bin": {
+ "codify-build": "bin/build.js"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -130,9 +159,9 @@
}
},
"node_modules/@emnapi/core": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz",
- "integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
+ "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -142,9 +171,9 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz",
- "integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
+ "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -164,9 +193,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
- "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
@@ -181,9 +210,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz",
- "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
@@ -198,9 +227,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz",
- "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
@@ -215,9 +244,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz",
- "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
@@ -232,9 +261,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz",
- "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
@@ -249,9 +278,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz",
- "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
@@ -266,9 +295,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz",
- "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
@@ -283,9 +312,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz",
- "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
@@ -300,9 +329,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz",
- "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
@@ -317,9 +346,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz",
- "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
@@ -334,9 +363,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz",
- "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
@@ -351,9 +380,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz",
- "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
@@ -368,9 +397,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz",
- "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
@@ -385,9 +414,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz",
- "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
@@ -402,9 +431,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz",
- "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
@@ -419,9 +448,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz",
- "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
@@ -436,9 +465,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz",
- "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
@@ -453,9 +482,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz",
- "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
@@ -470,9 +499,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz",
- "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
@@ -487,9 +516,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz",
- "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
@@ -504,9 +533,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz",
- "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
@@ -521,9 +550,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz",
- "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"cpu": [
"arm64"
],
@@ -538,9 +567,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz",
- "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
@@ -555,9 +584,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz",
- "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
@@ -572,9 +601,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz",
- "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
@@ -589,9 +618,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz",
- "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
@@ -737,14 +766,17 @@
}
},
"node_modules/@homebridge/node-pty-prebuilt-multiarch": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/@homebridge/node-pty-prebuilt-multiarch/-/node-pty-prebuilt-multiarch-0.12.0.tgz",
- "integrity": "sha512-hJCGcfOnMeRh2KUdWPlVN/1egnfqI4yxgpDhqHSkF2DLn5fiJNdjEHHlcM1K2w9+QBmRE2D/wfmM4zUOb8aMyQ==",
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/@homebridge/node-pty-prebuilt-multiarch/-/node-pty-prebuilt-multiarch-0.13.1.tgz",
+ "integrity": "sha512-ccQ60nMcbEGrQh0U9E6x0ajW9qJNeazpcM/9CH6J8leyNtJgb+gu24WTBAfBUVeO486ZhscnaxLEITI2HXwhow==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-addon-api": "^7.1.0",
"prebuild-install": "^7.1.2"
+ },
+ "engines": {
+ "node": ">=18.0.0 <25.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
@@ -1137,9 +1169,9 @@
}
},
"node_modules/@oclif/core/node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1292,9 +1324,9 @@
}
},
"node_modules/@rollup/plugin-replace": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz",
- "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",
+ "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1387,9 +1419,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
- "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
+ "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
"cpu": [
"arm"
],
@@ -1401,9 +1433,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
- "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
+ "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
"cpu": [
"arm64"
],
@@ -1415,9 +1447,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
- "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
+ "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
"cpu": [
"arm64"
],
@@ -1429,9 +1461,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
- "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
+ "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
"cpu": [
"x64"
],
@@ -1443,9 +1475,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
- "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
+ "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
"cpu": [
"arm64"
],
@@ -1457,9 +1489,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
- "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
+ "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
"cpu": [
"x64"
],
@@ -1471,9 +1503,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
- "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
+ "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
"cpu": [
"arm"
],
@@ -1485,9 +1517,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
- "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
+ "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
"cpu": [
"arm"
],
@@ -1499,9 +1531,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
- "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
+ "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
"cpu": [
"arm64"
],
@@ -1513,9 +1545,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
- "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
+ "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
"cpu": [
"arm64"
],
@@ -1527,9 +1559,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
- "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
+ "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
"cpu": [
"loong64"
],
@@ -1541,9 +1573,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
- "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
+ "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
"cpu": [
"ppc64"
],
@@ -1555,9 +1587,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
- "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
+ "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
"cpu": [
"riscv64"
],
@@ -1569,9 +1601,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
- "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
+ "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
"cpu": [
"riscv64"
],
@@ -1583,9 +1615,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
- "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
+ "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
"cpu": [
"s390x"
],
@@ -1597,9 +1629,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
- "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
+ "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
"cpu": [
"x64"
],
@@ -1611,13 +1643,12 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
- "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.5.tgz",
+ "integrity": "sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1625,9 +1656,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
- "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
+ "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
"cpu": [
"arm64"
],
@@ -1639,9 +1670,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
- "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
+ "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
"cpu": [
"arm64"
],
@@ -1653,9 +1684,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
- "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
+ "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
"cpu": [
"ia32"
],
@@ -1667,9 +1698,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
- "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
+ "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
"cpu": [
"x64"
],
@@ -1681,9 +1712,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
- "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
+ "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
"cpu": [
"x64"
],
@@ -1708,19 +1739,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@sindresorhus/chunkify": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/chunkify/-/chunkify-2.0.0.tgz",
- "integrity": "sha512-srajPSoMTC98FETCJIeXJhJqB77IRPJSu8g907jLuuioLORHZJ3YAOY2DsP5ebrZrjOrAwjqf+Cgkg/I8TGPpw==",
- "deprecated": "Renamed to chunkify: https://www.npmjs.com/package/chunkify",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/@sindresorhus/df": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/df/-/df-3.1.1.tgz",
@@ -1901,9 +1919,9 @@
}
},
"node_modules/@tsconfig/node10": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
- "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
+ "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
"dev": true,
"license": "MIT"
},
@@ -2031,9 +2049,9 @@
"license": "MIT"
},
"node_modules/@types/lodash": {
- "version": "4.17.20",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
- "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==",
"dev": true,
"license": "MIT"
},
@@ -2107,9 +2125,9 @@
"license": "MIT"
},
"node_modules/@types/sinon": {
- "version": "17.0.4",
- "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz",
- "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==",
+ "version": "21.0.0",
+ "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz",
+ "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2117,9 +2135,16 @@
}
},
"node_modules/@types/sinonjs__fake-timers": {
- "version": "15.0.0",
- "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.0.tgz",
- "integrity": "sha512-lqKG4X0fO3aJF7Bz590vuCkFt/inbDyL7FXaVjPEYO+LogMZ2fwSDUiP7bJvdYHaCgCQGNOPxquzSrrnVH3fGw==",
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz",
+ "integrity": "sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
"dev": true,
"license": "MIT"
},
@@ -2660,9 +2685,9 @@
}
},
"node_modules/@vitest/runner/node_modules/yocto-queue": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz",
- "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3302,6 +3327,18 @@
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"license": "ISC"
},
+ "node_modules/chunkify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chunkify/-/chunkify-5.0.0.tgz",
+ "integrity": "sha512-G8dj/3/Gm+1yL4oWSdwIxihZWFlgC4V2zYtIApacI0iPIRKBHlBGOGAiDUBZgrj4H8MBA8g8fPFwnJrWF3wl7Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -3384,51 +3421,23 @@
"node": ">=4"
}
},
- "node_modules/codify-plugin-lib": {
- "version": "1.0.180",
- "resolved": "https://registry.npmjs.org/codify-plugin-lib/-/codify-plugin-lib-1.0.180.tgz",
- "integrity": "sha512-1PGSBTT+iLT5eSb1igoTEAteklL2gICYCb2FaUmy8iO7a8gviZlhn0T7tqZ78PymeYHJLQQu0TRgNLi/tdfLyQ==",
- "license": "ISC",
- "dependencies": {
- "@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
- "@npmcli/promise-spawn": "^7.0.1",
- "ajv": "^8.12.0",
- "ajv-formats": "^2.1.1",
- "clean-deep": "^3.4.0",
- "codify-schemas": "1.0.83",
- "lodash.isequal": "^4.5.0",
- "nanoid": "^5.0.9",
- "strip-ansi": "^7.1.0",
- "uuid": "^10.0.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/codify-plugin-lib/node_modules/codify-schemas": {
- "version": "1.0.83",
- "resolved": "https://registry.npmjs.org/codify-schemas/-/codify-schemas-1.0.83.tgz",
- "integrity": "sha512-QNMYhOt/V78Z+F1eLsSodNcmn8dLhLGlWa3rEzrBl7Ah8E2hylmsK7dEjkBWytoqEB2DISt8RuULG/FmL32/0g==",
- "license": "ISC",
- "dependencies": {
- "ajv": "^8.12.0"
- }
- },
"node_modules/codify-plugin-test": {
- "version": "0.0.53",
- "resolved": "https://registry.npmjs.org/codify-plugin-test/-/codify-plugin-test-0.0.53.tgz",
- "integrity": "sha512-bHDvUgEJ6f/YpkWb3zzQWRLChYwq1HESQZ5QAL/ZcZtvSJiIYRGYWhj1RK0k7/EixXqoohybd9eMF7AGVd3XCw==",
+ "version": "0.0.53-beta24",
+ "resolved": "https://registry.npmjs.org/codify-plugin-test/-/codify-plugin-test-0.0.53-beta24.tgz",
+ "integrity": "sha512-ijY785cPNSpMYCjVY+K6n7X2pXzzNWKQufHmrVo5IVLd3I21b3UBYS0Z+op/7nUelD5bCcsAAW6JDes7VZ4/qA==",
"dev": true,
"license": "ISC",
"dependencies": {
+ "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
"ajv": "^8.12.0",
"ajv-formats": "^3.0.1",
"chalk": "^5.4.1",
- "codify-schemas": "1.0.77",
+ "codify-schemas": "1.0.86-beta7",
"lodash.differencewith": "4.5.0",
"lodash.matches": "^4.6.0",
"lodash.unionby": "^4.8.0",
- "nanoid": "^5.0.9"
+ "nanoid": "^5.0.9",
+ "strip-ansi": "^7.1.2"
},
"engines": {
"node": ">=18.0.0"
@@ -3453,9 +3462,9 @@
}
},
"node_modules/codify-plugin-test/node_modules/codify-schemas": {
- "version": "1.0.77",
- "resolved": "https://registry.npmjs.org/codify-schemas/-/codify-schemas-1.0.77.tgz",
- "integrity": "sha512-Xv4M/2k9e6pIbrI6NGDeUYsRjBHET45x0ygBxhpgLcIgGGeNnWVi5/Mh1mTn+TOwGmrAw3bTRsQCpDLmtfJeGA==",
+ "version": "1.0.86-beta7",
+ "resolved": "https://registry.npmjs.org/codify-schemas/-/codify-schemas-1.0.86-beta7.tgz",
+ "integrity": "sha512-axzw4JApbyoyZ70kOtc7k4gk/gO18JzfGuk7v5epFuNC8jcimgYwqz30uBwind+B2PZIsFXeMiAITZKn3urBog==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3463,9 +3472,9 @@
}
},
"node_modules/codify-schemas": {
- "version": "1.0.63",
- "resolved": "https://registry.npmjs.org/codify-schemas/-/codify-schemas-1.0.63.tgz",
- "integrity": "sha512-0khOFJOK7UPibAw8Dfsf9XDcK1ad5c+YbSBMGdpTv6L4lwkYiuEgJhgoxM3oL7fzywe96Woj1KdLVecwZDyaZQ==",
+ "version": "1.0.86-beta11",
+ "resolved": "https://registry.npmjs.org/codify-schemas/-/codify-schemas-1.0.86-beta11.tgz",
+ "integrity": "sha512-yvysOzSuZHxqlr94LCnAuJlENFd2+NYyyX7ZXQGoLL5j6pEPbxBjleBikn/kPtQ2YDXplH09X+CKbXuBuOEqsQ==",
"license": "ISC",
"dependencies": {
"ajv": "^8.12.0"
@@ -3887,9 +3896,9 @@
}
},
"node_modules/es-abstract": {
- "version": "1.24.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
- "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4036,9 +4045,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.25.11",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz",
- "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -4049,32 +4058,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.11",
- "@esbuild/android-arm": "0.25.11",
- "@esbuild/android-arm64": "0.25.11",
- "@esbuild/android-x64": "0.25.11",
- "@esbuild/darwin-arm64": "0.25.11",
- "@esbuild/darwin-x64": "0.25.11",
- "@esbuild/freebsd-arm64": "0.25.11",
- "@esbuild/freebsd-x64": "0.25.11",
- "@esbuild/linux-arm": "0.25.11",
- "@esbuild/linux-arm64": "0.25.11",
- "@esbuild/linux-ia32": "0.25.11",
- "@esbuild/linux-loong64": "0.25.11",
- "@esbuild/linux-mips64el": "0.25.11",
- "@esbuild/linux-ppc64": "0.25.11",
- "@esbuild/linux-riscv64": "0.25.11",
- "@esbuild/linux-s390x": "0.25.11",
- "@esbuild/linux-x64": "0.25.11",
- "@esbuild/netbsd-arm64": "0.25.11",
- "@esbuild/netbsd-x64": "0.25.11",
- "@esbuild/openbsd-arm64": "0.25.11",
- "@esbuild/openbsd-x64": "0.25.11",
- "@esbuild/openharmony-arm64": "0.25.11",
- "@esbuild/sunos-x64": "0.25.11",
- "@esbuild/win32-arm64": "0.25.11",
- "@esbuild/win32-ia32": "0.25.11",
- "@esbuild/win32-x64": "0.25.11"
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/escape-string-regexp": {
@@ -4956,9 +4965,9 @@
"license": "BSD-3-Clause"
},
"node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -5291,15 +5300,15 @@
"license": "MIT"
},
"node_modules/glob": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
- "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
+ "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
"foreground-child": "^3.3.1",
"jackspeak": "^4.1.1",
- "minimatch": "^10.0.3",
+ "minimatch": "^10.1.1",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^2.0.0"
@@ -5328,11 +5337,11 @@
}
},
"node_modules/glob/node_modules/minimatch": {
- "version": "10.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
- "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
+ "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/brace-expansion": "^5.0.0"
},
@@ -6226,9 +6235,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6469,11 +6478,11 @@
}
},
"node_modules/lru-cache": {
- "version": "11.2.2",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz",
- "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==",
+ "version": "11.2.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
+ "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
@@ -7120,9 +7129,9 @@
}
},
"node_modules/p-map": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz",
- "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==",
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
+ "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -7228,9 +7237,9 @@
"license": "MIT"
},
"node_modules/path-scurry": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
- "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz",
+ "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -7950,9 +7959,9 @@
}
},
"node_modules/rollup": {
- "version": "4.52.5",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
- "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
+ "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7966,31 +7975,45 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.52.5",
- "@rollup/rollup-android-arm64": "4.52.5",
- "@rollup/rollup-darwin-arm64": "4.52.5",
- "@rollup/rollup-darwin-x64": "4.52.5",
- "@rollup/rollup-freebsd-arm64": "4.52.5",
- "@rollup/rollup-freebsd-x64": "4.52.5",
- "@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
- "@rollup/rollup-linux-arm-musleabihf": "4.52.5",
- "@rollup/rollup-linux-arm64-gnu": "4.52.5",
- "@rollup/rollup-linux-arm64-musl": "4.52.5",
- "@rollup/rollup-linux-loong64-gnu": "4.52.5",
- "@rollup/rollup-linux-ppc64-gnu": "4.52.5",
- "@rollup/rollup-linux-riscv64-gnu": "4.52.5",
- "@rollup/rollup-linux-riscv64-musl": "4.52.5",
- "@rollup/rollup-linux-s390x-gnu": "4.52.5",
- "@rollup/rollup-linux-x64-gnu": "4.52.5",
- "@rollup/rollup-linux-x64-musl": "4.52.5",
- "@rollup/rollup-openharmony-arm64": "4.52.5",
- "@rollup/rollup-win32-arm64-msvc": "4.52.5",
- "@rollup/rollup-win32-ia32-msvc": "4.52.5",
- "@rollup/rollup-win32-x64-gnu": "4.52.5",
- "@rollup/rollup-win32-x64-msvc": "4.52.5",
+ "@rollup/rollup-android-arm-eabi": "4.54.0",
+ "@rollup/rollup-android-arm64": "4.54.0",
+ "@rollup/rollup-darwin-arm64": "4.54.0",
+ "@rollup/rollup-darwin-x64": "4.54.0",
+ "@rollup/rollup-freebsd-arm64": "4.54.0",
+ "@rollup/rollup-freebsd-x64": "4.54.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.54.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.54.0",
+ "@rollup/rollup-linux-arm64-musl": "4.54.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.54.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.54.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.54.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.54.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.54.0",
+ "@rollup/rollup-linux-x64-gnu": "4.54.0",
+ "@rollup/rollup-linux-x64-musl": "4.54.0",
+ "@rollup/rollup-openharmony-arm64": "4.54.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.54.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.54.0",
+ "@rollup/rollup-win32-x64-gnu": "4.54.0",
+ "@rollup/rollup-win32-x64-msvc": "4.54.0",
"fsevents": "~2.3.2"
}
},
+ "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.54.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
+ "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -8971,9 +8994,9 @@
}
},
"node_modules/terser": {
- "version": "5.44.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
- "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
+ "version": "5.44.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
+ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -9060,13 +9083,13 @@
}
},
"node_modules/trash": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/trash/-/trash-10.0.0.tgz",
- "integrity": "sha512-nyHQPJ7F4dYCfj1xN95DAkLkf9qlyRLDpT9yYwcR5SH16q+f7VA1L5VwsdEqWFUuGNpKwgLnbOS1QBvXMYnLfA==",
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/trash/-/trash-10.0.1.tgz",
+ "integrity": "sha512-WSh7WXBRkudzQMXRh61vyT/f3mjVnn+3conu5DdvMGzRPsc3mtviPLIwCK1OtwfgR2gr4+9+EE/eWwPlDj5NcA==",
"license": "MIT",
"dependencies": {
- "@sindresorhus/chunkify": "^2.0.0",
"@stroncium/procfs": "^1.2.1",
+ "chunkify": "^5.0.0",
"globby": "^14.1.0",
"is-path-inside": "^4.0.0",
"move-file": "^3.1.0",
@@ -9233,13 +9256,13 @@
"license": "0BSD"
},
"node_modules/tsx": {
- "version": "4.20.6",
- "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz",
- "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "~0.25.0",
+ "esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
@@ -10405,6 +10428,15 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
}
}
diff --git a/package.json b/package.json
index f575e5d..8c291f4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "default",
- "version": "0.16.1",
+ "version": "0.17.0-beta10",
"description": "",
"main": "dist/index.js",
"scripts": {
@@ -9,23 +9,25 @@
"start:dev": "tsc && cirrus run run_dev -o simple",
"test:unit": "vitest src/**/*.test.ts",
"test:integration:dev": "tsx scripts/run-tests.ts",
+ "test:integration:dev:linux": "tsx scripts/run-tests.ts",
"test:integration": "vitest test/**/*.test.ts",
"test": "vitest",
"rollup": "rollup -c",
"build": "tsx ./scripts/build.ts",
"deploy": "tsx ./scripts/deploy.ts",
- "deploy:beta": "BETA=true tsx ./scripts/deploy.ts"
+ "deploy:beta": "BETA=true tsx ./scripts/deploy.ts",
+ "testing": "codify-deploy"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
+ "@codifycli/plugin-core": "1.0.0-beta1",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"chalk": "^5.3.0",
- "codify-plugin-lib": "^1.0.180",
- "codify-schemas": "1.0.63",
+ "codify-schemas": "1.0.86-beta11",
"debug": "^4.3.4",
"lodash.isequal": "^4.5.0",
"nanoid": "^5.0.9",
@@ -50,10 +52,11 @@
"@types/debug": "4.1.12",
"@types/lodash.isequal": "^4.5.8",
"@types/mock-fs": "^4.13.4",
+ "@types/uuid": "10.0.0",
"@types/node": "^18",
"@types/plist": "^3.0.5",
"@types/semver": "^7.5.4",
- "codify-plugin-test": "0.0.53",
+ "codify-plugin-test": "0.0.53-beta24",
"commander": "^12.1.0",
"eslint": "^8.51.0",
"eslint-config-oclif": "^5",
@@ -67,9 +70,12 @@
"ts-node": "^10.9.1",
"tslib": "^2.6.2",
"tsx": "^4.7.2",
- "typescript": "^5",
+ "typescript": "5.9.3",
"vitest": "^1.4.0"
},
+ "optionalDependencies": {
+ "@rollup/rollup-linux-x64-musl": "4.9.5"
+ },
"engines": {
"node": ">=18.0.0"
}
diff --git a/rollup.config.js b/rollup.config.js
index 4eb944e..5e79f6d 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -8,13 +8,16 @@ export default {
input: 'src/index.ts',
output: {
dir:'dist',
- format: 'cjs'
+ format: 'cjs',
+ inlineDynamicImports: true,
},
external: ['@homebridge/node-pty-prebuilt-multiarch'],
plugins: [
json(),
nodeResolve({ exportConditions: ['node'] }),
- typescript(),
+ typescript({
+ exclude: ['**/*.test.ts', '**/*.d.ts', 'test']
+ }),
commonjs(),
terser()
]
diff --git a/scripts/build.ts b/scripts/build.ts
index 8c000bd..f0e1176 100644
--- a/scripts/build.ts
+++ b/scripts/build.ts
@@ -1,5 +1,7 @@
import { JSONSchema } from '@apidevtools/json-schema-ref-parser';
import { Ajv } from 'ajv';
+import { VerbosityLevel } from '@codifycli/plugin-core';
+import { SequentialPty } from '@codifycli/plugin-core/dist/pty/seqeuntial-pty';
import { IpcMessage, IpcMessageSchema, MessageStatus, ResourceSchema } from 'codify-schemas';
import mergeJsonSchemas from 'merge-json-schemas';
import { ChildProcess, fork } from 'node:child_process';
@@ -7,8 +9,6 @@ import fs from 'node:fs';
import path from 'node:path';
import * as url from 'node:url';
-import { codifySpawn } from '../src/utils/codify-spawn.js';
-
const ajv = new Ajv({
strict: true
});
@@ -36,8 +36,11 @@ function sendMessageAndAwaitResponse(process: ChildProcess, message: IpcMessage)
});
}
-await codifySpawn('rm -rf ./dist')
-await codifySpawn('npm run rollup -- -f es');
+VerbosityLevel.set(3);
+const $ = new SequentialPty();
+
+await $.spawn('rm -rf ./dist')
+await $.spawn('npm run rollup -- -f es', { interactive: true });
const plugin = fork(
'./dist/index.js',
@@ -89,8 +92,8 @@ const mergedSchemas = [...schemasMap.entries()].map(([type, schema]) => {
});
-await codifySpawn('rm -rf ./dist')
-await codifySpawn('npm run rollup'); // re-run rollup without building for es
+await $.spawn('rm -rf ./dist')
+await $.spawn('npm run rollup', { interactive: true }); // re-run rollup without building for es
console.log('Generated JSON Schemas for all resources')
@@ -100,8 +103,7 @@ fs.writeFileSync(schemaOutputPath, JSON.stringify(mergedSchemas, null, 2));
console.log('Successfully wrote schema to ./dist/schemas.json')
-// eslint-disable-next-line n/no-process-exit,unicorn/no-process-exit
-process.exit(0)
-
+plugin.kill(9);
+process.exit(0);
diff --git a/scripts/docker-files/debian-linux/Dockerfile b/scripts/docker-files/debian-linux/Dockerfile
new file mode 100644
index 0000000..496d69c
--- /dev/null
+++ b/scripts/docker-files/debian-linux/Dockerfile
@@ -0,0 +1,14 @@
+FROM node:22
+
+USER root
+
+# Install system dependencies without sudo
+RUN apt-get update && apt-get install -y \
+ build-essential \
+ curl \
+ git \
+ sudo \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN echo "node ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/node
+
diff --git a/scripts/docker-files/redhat-linux/Dockerfile b/scripts/docker-files/redhat-linux/Dockerfile
new file mode 100644
index 0000000..7377ff8
--- /dev/null
+++ b/scripts/docker-files/redhat-linux/Dockerfile
@@ -0,0 +1,46 @@
+# Use a CentOS base image
+FROM rockylinux:9
+
+# Install necessary packages to fetch the NodeSource script and Node.js
+#RUN dnf clean all \
+# && rm -rf /var/cache/dnf \
+# && dnf -y --refresh update
+#
+#RUN dnf install sudo -y \
+# && dnf install nodejs npm -y
+
+RUN dnf -y install which && dnf clean all
+RUN dnf -y install sudo && dnf clean all
+
+# Update base + install deps
+#RUN dnf clean all \
+# && rm -rf /var/cache/dnf \
+# && dnf -y --refresh update \
+# && dnf -y install ca-certificates \
+# && dnf clean all
+
+# Add NodeSource repo for Node.js 22
+RUN curl -fsSL https://rpm.nodesource.com/setup_22.x | bash -
+
+# Install Node.js
+RUN dnf -y install nodejs \
+ && dnf clean all
+
+# Verify
+RUN node --version && npm --version
+
+## Create node user with home directory and bash shell
+RUN useradd --create-home --shell /bin/bash node
+RUN usermod -aG wheel node
+
+## Ensure node owns its home directory
+RUN chown -R node:node /home/node
+
+RUN echo "node ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/node
+##
+### Switch to node user
+#USER node
+
+RUN dnf clean all \
+ && rm -rf /var/cache/dnf \
+ && dnf clean all
diff --git a/scripts/init.sh b/scripts/init.sh
index 3eebf4f..2f85a59 100755
--- a/scripts/init.sh
+++ b/scripts/init.sh
@@ -1,2 +1,4 @@
-tart clone ghcr.io/kevinwang5658/sonoma-codify:v0.0.3 codify-test-vm
+tart clone ghcr.io/cirruslabs/macos-tahoe-base:latest codify-test-vm
+tart set codify-test-vm --memory 6124 --cpu 4 --storage 128
+
# tart clone ghcr.io/kevinwang5658/sonoma-codify:v0.0.3 codify-sonoma
diff --git a/scripts/run-tests.ts b/scripts/run-tests.ts
index 9b734ff..4ca9652 100644
--- a/scripts/run-tests.ts
+++ b/scripts/run-tests.ts
@@ -1,47 +1,143 @@
+import { Shell, SpawnStatus, VerbosityLevel } from '@codifycli/plugin-core';
+import { testSpawn, TestUtils } from 'codify-plugin-test';
import { Command } from 'commander';
-import { glob } from 'glob';
-import { spawn, spawnSync } from 'node:child_process';
+import { spawn } from 'node:child_process';
import * as inspector from 'node:inspector';
+import os from 'node:os';
+import path from 'node:path';
+
+import { codifySpawn } from '../src/utils/codify-spawn';
const IP_REGEX = /VM was assigned with (.*) IP/;
const program = new Command();
program
+ .option('--launchPersistent', 'Launches a persistent VM for testing', false)
+ .option('--operatingSystem ', 'Operating system to run tests on', os.platform())
+ .option('--persistent', 'Runs tests on a persistent VM (reuse) to skip the overhead of launching a new VM each time', false)
.argument('[file]', 'File to run')
.action(main)
.parse()
-async function main(argument: string): Promise {
+async function main(argument: string, args: {
+ operatingSystem: string;
+ persistent: boolean;
+ launchPersistent: boolean
+}): Promise {
const debug = isInDebugMode();
if (debug) {
console.log('Running in debug mode!')
}
+ if (args.launchPersistent) {
+ await launchPersistentVm(args.operatingSystem);
+ return process.exit(0);
+ }
+
+ if (args.persistent) {
+ if (!argument) {
+ throw new Error('No test specified for persistent mode');
+ }
+
+ await launchPersistentTest(argument, debug, args.operatingSystem);
+ return process.exit(0);
+ }
+
if (!argument) {
- await launchTestAll(debug)
+ await launchTestAll(debug, args.operatingSystem)
return process.exit(0);
}
- await launchSingleTest(argument, debug);
+ await launchSingleTest(argument, debug, args.operatingSystem);
process.exit(0);
}
-async function launchTestAll(debug: boolean): Promise {
- const tests = await glob('./test/**/*.test.ts');
- for (const test of tests) {
- console.log(`Running test ${test}`)
- await run(`cirrus run --lazy-pull integration_individual_test -e FILE_NAME="${test}" ${ debug ? '-o simple' : ''}`, debug, false)
- }
+async function launchTestAll(debug: boolean, operatingSystem: string): Promise {
+ const image = operatingSystem === 'darwin' ? 'integration_test_dev_macos' : 'integration_test_dev_linux';
+
+ // const tests = await glob('./test/**/*.test.ts');
+ // for (const test of tests) {
+ // console.log(`Running test ${test}`)
+ // await run(`cirrus run --lazy-pull ${image} -e FILE_NAME="${test}" ${ debug ? '-o simple' : ''}`, debug, false)
+ // }
- // await run('cirrus run --lazy-pull integration_test_dev -o simple', debug, false);
+ await run(`cirrus run --lazy-pull ${image} -o simple`, debug, false);
}
-async function launchSingleTest(test: string, debug: boolean) {
+async function launchSingleTest(test: string, debug: boolean, operatingSystem: string) {
+ const image = operatingSystem === 'darwin' ? 'integration_individual_test_macos' : 'integration_individual_test_linux';
+
console.log(`Running test: ${test}`)
- await run(`cirrus run --lazy-pull integration_individual_test -e FILE_NAME="${test}" -o simple`, debug)
+ await run(`cirrus run --lazy-pull ${image} -e FILE_NAME="${test}" -o simple`, debug)
+}
+
+async function launchPersistentTest(test: string, debug: boolean, operatingSystem: string) {
+ const shell = operatingSystem === 'darwin' ? 'zsh' : 'bash';
+
+ // if (operatingSystem === 'darwin') {
+ const { data: vmList } = await codifySpawn('tart list --format json');
+ console.log(vmList);
+
+ const parsedVmList = JSON.parse(vmList);
+ const runningVm = parsedVmList.find(vm => vm.Name.startsWith('codify-test-vm') && vm.Running === true);
+ if (!runningVm) {
+ throw new Error('No persistent VM found');
+ }
+
+ const vmName = runningVm.Name;
+ const dir = '~/codify-homebrew-plugin';
+ // const dir = '/Volumes/My\\ Shared\\ Files/plugin'
+
+ const debugFlag = debug ? ' -e DEBUG="--inspect-brk=9229"' : ''
+
+ console.log('Refreshing files on VM...');
+ const { data: ipAddr } = await testSpawn(`tart ip ${vmName}`);
+ await testSpawn(`tart exec ${vmName} rm -rf ${dir}/src ${dir}/test`, { throws: true });
+ await testSpawn(`sshpass -p "admin" scp -r -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${path.join(process.cwd(), 'test')} ${path.join(process.cwd(), 'src')} ${path.join(process.cwd(), 'package.json')} ${path.join(process.cwd(), 'package-lock.json')} admin@${ipAddr}:${dir}`, { throws: true });
+
+ console.log('Done refreshing files on VM. Starting tests...');
+ VerbosityLevel.set(3);
+ await codifySpawn(`tart exec ${vmName} ${shell} -c ${operatingSystem === 'darwin' ? '-i' : ''} "cd ${dir} && FORCE_COLOR=true npm run test -- ${test} --disable-console-intercept ${debugFlag} --no-file-parallelism"`, { throws: false });
+ // }
}
+async function launchPersistentVm(operatingSystem: string) {
+ const newVmName = `codify-test-vm-${Date.now()}`;
+ const shell = operatingSystem === 'darwin' ? 'zsh' : 'bash';
+
+ console.log(`Cloning new VM... ${newVmName}`);
+
+ const image = (operatingSystem === 'darwin') ? 'codify-test-vm' : 'codify-test-vm-linux';
+ await testSpawn(`tart clone ${image} ${newVmName}`);
+ testSpawn(`tart run ${newVmName}`)
+ .then(cleanupVm)
+
+ process.on('exit', cleanupVm);
+ process.on('SIGINT', cleanupVm);
+ process.on('SIGHUP', cleanupVm);
+ process.on('SIGTERM', cleanupVm);
+
+ await sleep(5000);
+ await waitUntilVmIsReady(newVmName);
+
+ const { data: ipAddr } = await testSpawn(`tart ip ${newVmName}`);
+ await testSpawn(`sshpass -p "admin" rsync -avz -e 'ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' --exclude 'node_modules' --exclude '.git' --exclude 'dist' --exclude '.fleet' ${process.cwd()} admin@${ipAddr}:~`);
+ await testSpawn(`tart exec ${newVmName} ${shell} -i -c "cd ~/codify-homebrew-plugin && npm ci"`);
+ console.log('Finished installing dependencies. Start tests in a new terminal window.');
+
+ await sleep(1_000_000_000);
+
+ // This is effective the end just without a return
+
+ async function cleanupVm() {
+ console.log('Deleting VM after...')
+ await testSpawn(`tart delete ${newVmName}`);
+ process.exit(0);
+ }
+}
+
+
async function run(cmd: string, debug: boolean, simple = true) {
const messageBuffer: string[] = [];
@@ -61,39 +157,39 @@ async function run(cmd: string, debug: boolean, simple = true) {
});
// If debug then open ssh tunnel
- if (debug) {
- cp.stdout!.on('data', data => {
- if (data.toString().includes('VM was assigned with')) {
- const [_, ip] = data.toString().match(IP_REGEX);
-
- console.log(`Copying ssh keys to ${ip}`)
- // spawnSync(`source $HOME/.zshrc; sshpass -p admin ssh-copy-id -o "StrictHostKeyChecking=no" admin@${ip}`, { stdio: 'inherit', shell: 'zsh' });
-
- console.log('Enabling port forwarding')
- const sshStatement1 = `ssh${ Array.from({ length: 20 }, (i: number) => i + 9000).map((i) => ` -L ${i}:localhost:${i}`)} admin@${ip}`;
- const sshStatement2 = `source $HOME/.zshrc; sshpass -p admin ssh -L 9221:localhost:9221 -L 9229:localhost:9229 -N -o "StrictHostKeyChecking=no" admin@${ip}`
-
- const portForward1 = spawn(sshStatement2, { stdio: 'pipe', shell: 'zsh' });
- portForward1.stderr.pipe(process.stdout)
- portForward1.stdout.on('data', data => {
- console.log(data.toString());
- if (data.toString().includes('Address already in use')) {
- throw new Error('Port 9229 already in use!')
- }
- })
- // console.log('Enabled on port 9229')
-
- // const portForward2 = spawn(`ssh -L 9221:localhost:9221 -Nf admin@${ip}`, { stdio: 'pipe', shell: 'zsh' });
- // portForward2.stdout.on('data', data => {
- // console.log(data.toString());
- // if (data.toString().includes('Address already in use')) {
- // throw new Error('Port 9221 already in use!')
- // }
- // });
- // console.log('Enabled on port 9221')
- }
- })
- }
+ // if (debug) {
+ // cp.stdout!.on('data', data => {
+ // if (data.toString().includes('VM was assigned with')) {
+ // const [_, ip] = data.toString().match(IP_REGEX);
+ //
+ // console.log(`Copying ssh keys to ${ip}`)
+ // // spawnSync(`source $HOME/.zshrc; sshpass -p admin ssh-copy-id -o "StrictHostKeyChecking=no" admin@${ip}`, { stdio: 'inherit', shell: 'zsh' });
+ //
+ // console.log('Enabling port forwarding')
+ // const sshStatement1 = `ssh${ Array.from({ length: 20 }, (i: number) => i + 9000).map((i) => ` -L ${i}:localhost:${i}`)} admin@${ip}`;
+ // const sshStatement2 = `source $HOME/.zshrc; sshpass -p admin ssh -L 9221:localhost:9221 -L 9229:localhost:9229 -N -o "StrictHostKeyChecking=no" admin@${ip}`
+ //
+ // const portForward1 = spawn(sshStatement2, { stdio: 'pipe', shell: 'zsh' });
+ // portForward1.stderr.pipe(process.stdout)
+ // portForward1.stdout.on('data', data => {
+ // console.log(data.toString());
+ // if (data.toString().includes('Address already in use')) {
+ // throw new Error('Port 9229 already in use!')
+ // }
+ // })
+ // // console.log('Enabled on port 9229')
+ //
+ // // const portForward2 = spawn(`ssh -L 9221:localhost:9221 -Nf admin@${ip}`, { stdio: 'pipe', shell: 'zsh' });
+ // // portForward2.stdout.on('data', data => {
+ // // console.log(data.toString());
+ // // if (data.toString().includes('Address already in use')) {
+ // // throw new Error('Port 9221 already in use!')
+ // // }
+ // // });
+ // // console.log('Enabled on port 9221')
+ // }
+ // })
+ // }
return new Promise((resolve) =>
cp.on('exit', () => resolve(messageBuffer.join('\n'))) // We never want to reject here even if the test fails
@@ -101,7 +197,22 @@ async function run(cmd: string, debug: boolean, simple = true) {
}
+function sleep(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
function isInDebugMode() {
return inspector.url() !== undefined;
}
+
+
+async function waitUntilVmIsReady(vmName: string): Promise {
+ while (true) {
+ const result = await testSpawn(`tart exec ${vmName} pwd`, { interactive: true })
+ if (result.status === SpawnStatus.SUCCESS) {
+ return;
+ }
+
+ await sleep(1000);
+ }
+}
diff --git a/src/index.ts b/src/index.ts
index 7ea7e5b..7dda050 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,26 +1,27 @@
-import { Plugin, runPlugin } from 'codify-plugin-lib';
+import { Plugin, runPlugin } from '@codifycli/plugin-core';
import { AndroidStudioResource } from './resources/android/android-studio.js';
+import { AptResource } from './resources/apt/apt.js';
import { AsdfResource } from './resources/asdf/asdf.js';
-import { AsdfGlobalResource } from './resources/asdf/asdf-global.js';
import { AsdfInstallResource } from './resources/asdf/asdf-install.js';
-import { AsdfLocalResource } from './resources/asdf/asdf-local.js';
import { AsdfPluginResource } from './resources/asdf/asdf-plugin.js';
import { AwsCliResource } from './resources/aws-cli/cli/aws-cli.js';
import { AwsProfileResource } from './resources/aws-cli/profile/aws-profile.js';
+import { DnfResource } from './resources/dnf/dnf.js';
import { DockerResource } from './resources/docker/docker.js';
import { FileResource } from './resources/file/file.js';
import { RemoteFileResource } from './resources/file/remote-file.js';
-import { GitCloneResource } from './resources/git/repository/git-repository.js';
import { GitResource } from './resources/git/git/git-resource.js';
import { GitLfsResource } from './resources/git/lfs/git-lfs.js';
+import { GitRepositoryResource } from './resources/git/repository/git-repository.js';
import { WaitGithubSshKey } from './resources/git/wait-github-ssh-key/wait-github-ssh-key.js';
import { HomebrewResource } from './resources/homebrew/homebrew.js';
import { JenvResource } from './resources/java/jenv/jenv.js';
-import { MacportsResource } from './resources/macports/macports.js';
import { Npm } from './resources/javascript/npm/npm.js';
+import { NpmLoginResource } from './resources/javascript/npm/npm-login.js';
import { NvmResource } from './resources/javascript/nvm/nvm.js';
import { Pnpm } from './resources/javascript/pnpm/pnpm.js';
+import { MacportsResource } from './resources/macports/macports.js';
import { PgcliResource } from './resources/pgcli/pgcli.js';
import { Pip } from './resources/python/pip/pip.js';
import { PipSync } from './resources/python/pip-sync/pip-sync.js';
@@ -30,13 +31,18 @@ import { Virtualenv } from './resources/python/virtualenv/virtualenv.js';
import { VirtualenvProject } from './resources/python/virtualenv/virtualenv-project.js';
import { ActionResource } from './resources/scripting/action.js';
import { AliasResource } from './resources/shell/alias/alias-resource.js';
+import { AliasesResource } from './resources/shell/aliases/aliases-resource.js';
import { PathResource } from './resources/shell/path/path-resource.js';
+import { SnapResource } from './resources/snap/snap.js';
import { SshAddResource } from './resources/ssh/ssh-add.js';
import { SshConfigFileResource } from './resources/ssh/ssh-config.js';
import { SshKeyResource } from './resources/ssh/ssh-key.js';
+import { TartResource } from './resources/tart/tart.js';
+import { TartVmResource } from './resources/tart/tart-vm.js';
import { TerraformResource } from './resources/terraform/terraform.js';
import { VscodeResource } from './resources/vscode/vscode.js';
import { XcodeToolsResource } from './resources/xcode-tools/xcode-tools.js';
+import { YumResource } from './resources/yum/yum.js';
runPlugin(Plugin.create(
'default',
@@ -45,6 +51,7 @@ runPlugin(Plugin.create(
new XcodeToolsResource(),
new PathResource(),
new AliasResource(),
+ new AliasesResource(),
new HomebrewResource(),
new PyenvResource(),
new GitLfsResource(),
@@ -55,12 +62,10 @@ runPlugin(Plugin.create(
new JenvResource(),
new PgcliResource(),
new VscodeResource(),
- new GitCloneResource(),
+ new GitRepositoryResource(),
new AndroidStudioResource(),
new AsdfResource(),
new AsdfPluginResource(),
- new AsdfGlobalResource(),
- new AsdfLocalResource(),
new AsdfInstallResource(),
new SshKeyResource(),
new SshConfigFileResource(),
@@ -77,6 +82,13 @@ runPlugin(Plugin.create(
new PipSync(),
new MacportsResource(),
new Npm(),
+ new NpmLoginResource(),
new DockerResource(),
+ new AptResource(),
+ new YumResource(),
+ new DnfResource(),
+ new SnapResource(),
+ new TartResource(),
+ new TartVmResource(),
])
)
diff --git a/src/resources/android/README.md b/src/resources/android/README.md
new file mode 100644
index 0000000..3fa0938
--- /dev/null
+++ b/src/resources/android/README.md
@@ -0,0 +1,52 @@
+---
+title: android-studio
+description: A reference page for the Android Studios resource
+sidebar:
+ label: android-studio
+---
+
+The Android Studio resource installs Android Studios. It supports the current and all previous versions.
+It also allows preview and beta versions to be installed.
+
+## Parameters:
+
+- **version**: *(string)* The version to install. This will default to the latest stable version if it isn't
+specified. For a list of all available versions please see: https://developer.android.com/studio/archive.
+
+- **directory**: *(string)* A custom directory to install Android Studios to. This defaults to `/Applications`
+if left unspecified
+
+## Example usage:
+
+Stable version:
+```json title="codify.jsonc"
+[
+ {
+ "type": "Android Studio"
+ }
+]
+```
+
+Stable and previous version:
+```json title="codify.jsonc"
+[
+ {
+ "type": "Android Studio"
+ },
+ {
+ "type": "Android Studio",
+ "version": "2024.2.1.8"
+ }
+]
+```
+
+Custom directory:
+```json title="codify.jsonc"
+[
+ {
+ "type": "Android Studio",
+ "version": "2024.2.1.7",
+ "directory": "~/programs"
+ }
+]
+```
diff --git a/src/resources/android/android-studio-schema.json b/src/resources/android/android-studio-schema.json
deleted file mode 100644
index 3673bdb..0000000
--- a/src/resources/android/android-studio-schema.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/android-studio.json",
- "$comment": "https://docs.codifycli.com/core-resources/android-studio/",
- "title": "Android studios resource",
- "type": "object",
- "description": "Install Android Studios.",
- "properties": {
- "version": {
- "type": "string",
- "description": "Android studios version. Visit: https://developer.android.com/studio/releases for version info"
- },
- "directory": {
- "type": "string",
- "description": "The directory to install Android Studios into. Defaults to /Applications",
- "default": "/Applications"
- }
- },
- "additionalProperties": false
-}
diff --git a/src/resources/android/android-studio.ts b/src/resources/android/android-studio.ts
index 47e9584..162ee2b 100644
--- a/src/resources/android/android-studio.ts
+++ b/src/resources/android/android-studio.ts
@@ -1,18 +1,28 @@
-import { CreatePlan, Resource, ResourceSettings } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
-import * as fs from 'node:fs';
+import { CreatePlan, Resource, ResourceSettings, getPty, z } from '@codifycli/plugin-core';
+import { OS } from 'codify-schemas';
+import * as fs from 'node:fs/promises';
+import os from 'node:os';
import path from 'node:path';
import plist from 'plist';
-import { codifySpawn } from '../../utils/codify-spawn.js';
import { Utils } from '../../utils/index.js';
-import Schema from './android-studio-schema.json';
import { AndroidStudioPlist, AndroidStudioVersionData } from './types.js';
-export interface AndroidStudioConfig extends ResourceConfig {
- version?: string;
- directory?: string;
-}
+export const schema = z.object({
+ version: z
+ .string()
+ .describe(
+ 'Android studios version. Visit: https://developer.android.com/studio/releases for version info'
+ )
+ .optional(),
+ directory: z
+ .string()
+ .describe(
+ 'The directory to install Android Studios into. Defaults to /Applications'
+ )
+ .optional(),
+})
+export type AndroidStudioConfig = z.infer;
export class AndroidStudioResource extends Resource {
@@ -21,7 +31,8 @@ export class AndroidStudioResource extends Resource {
override getSettings(): ResourceSettings {
return {
id: 'android-studio',
- schema: Schema,
+ operatingSystems: [OS.Darwin],
+ schema,
parameterSettings: {
directory: { type: 'directory', default: '/Applications' },
version: { type: 'version' }
@@ -50,6 +61,8 @@ export class AndroidStudioResource extends Resource {
}
override async create(plan: CreatePlan): Promise {
+ const $ = getPty();
+
if (!this.allAndroidStudioVersions) {
this.allAndroidStudioVersions = await this.fetchAllAndroidStudioVersions()
}
@@ -64,16 +77,14 @@ export class AndroidStudioResource extends Resource {
: versionToDownload.download.find((v) => v.link.includes('mac.dmg'))!
// Create a temporary tmp dir
- const temporaryDirQuery = await codifySpawn('mktemp -d');
- const temporaryDir = temporaryDirQuery.data.trim();
+ const temporaryDir = await fs.mkdtemp(path.join(os.tmpdir(), 'codify-android-'))
try {
// Download and unzip the terraform binary
- await codifySpawn(`curl -fsSL --progress-bar ${downloadLink.link} -o android-studio.dmg`, { cwd: temporaryDir });
+ await $.spawn(`curl -fsSL ${downloadLink.link} -o android-studio.dmg`, { cwd: temporaryDir });
-
- const { data } = await codifySpawn('hdiutil attach android-studio.dmg', { cwd: temporaryDir });
+ const { data } = await $.spawn('hdiutil attach android-studio.dmg', { cwd: temporaryDir });
const mountedDir = data.split(/\n/)
.find((l) => l.includes('/Volumes/'))
?.split(' ')
@@ -85,28 +96,27 @@ export class AndroidStudioResource extends Resource {
}
try {
- const { data: contents } = await codifySpawn('ls', { cwd: mountedDir })
+ const contents = await fs.readdir(mountedDir);
// Depending on it's preview or regular the name is different
- const appName = contents.split(/\n/)
+ const appName = contents
.find((l) => l.includes('Android'))
- await codifySpawn(`rsync -rl "${appName}" Applications/`, { cwd: mountedDir, requiresRoot: true })
-
-
+ // Must rsync because mounted dirs are read-only (can't delete via mv)
+ await $.spawn(`rsync -rl "${appName}" Applications/`, { cwd: mountedDir })
} finally {
// Unmount
- await codifySpawn(`hdiutil detach "${mountedDir}"`)
+ await $.spawnSafe(`hdiutil detach "${mountedDir}"`)
}
} finally {
// Delete the tmp directory
- await codifySpawn(`rm -r ${temporaryDir}`)
+ await fs.rm(temporaryDir, { recursive: true, force: true });
}
}
override async destroy(): Promise {
- await codifySpawn('rm -r "/Applications/Android Studio.app"', { requiresRoot: true })
+ await fs.rm('/Applications/Android Studio.app', { force: true, recursive: true });
}
private async fetchAllAndroidStudioVersions(): Promise {
@@ -121,11 +131,11 @@ export class AndroidStudioResource extends Resource {
private async addPlistData(location: string): Promise<{ location: string, plist: AndroidStudioPlist } | null> {
try {
- const file = fs.readFileSync(path.join(location, '/Contents/Info.plist'), 'utf8');
+ const file = await fs.readFile(path.join(location, '/Contents/Info.plist'), 'utf8');
const plistData = plist.parse(file) as unknown as AndroidStudioPlist;
return { location, plist: plistData };
- } catch(error) {
+ } catch (error) {
console.log(error)
return null;
}
diff --git a/src/resources/apt/apt-schema.json b/src/resources/apt/apt-schema.json
new file mode 100644
index 0000000..cbea82c
--- /dev/null
+++ b/src/resources/apt/apt-schema.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema",
+ "$id": "https://www.codifycli.com/apt.json",
+ "$comment": "https://docs.codifycli.com/core-resources/apt/",
+ "title": "Apt resource",
+ "description": "Manage apt packages on Debian-based systems.",
+ "type": "object",
+ "properties": {
+ "install": {
+ "type": "array",
+ "description": "Installs packages using apt.",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "version": { "type": "string" }
+ },
+ "required": ["name"]
+ }
+ ]
+ }
+ },
+ "update": {
+ "type": "boolean",
+ "description": "Whether to run apt-get update before installing packages. Defaults to true."
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/src/resources/apt/apt.ts b/src/resources/apt/apt.ts
new file mode 100644
index 0000000..0a00626
--- /dev/null
+++ b/src/resources/apt/apt.ts
@@ -0,0 +1,51 @@
+import { CreatePlan, Resource, ResourceSettings, SpawnStatus, getPty } from '@codifycli/plugin-core';
+import { OS, ResourceConfig } from 'codify-schemas';
+
+import schema from './apt-schema.json';
+import { AptInstallParameter, AptPackage } from './install-parameter.js';
+
+export interface AptConfig extends ResourceConfig {
+ install: Array;
+ update?: boolean;
+}
+
+export class AptResource extends Resource {
+
+ override getSettings(): ResourceSettings {
+ return {
+ id: 'apt',
+ operatingSystems: [OS.Linux],
+ schema,
+ removeStatefulParametersBeforeDestroy: true,
+ parameterSettings: {
+ install: { type: 'stateful', definition: new AptInstallParameter() },
+ update: { type: 'boolean', default: true, setting: true }
+ }
+ };
+ }
+
+ override async refresh(parameters: Partial): Promise | null> {
+ const $ = getPty();
+
+ const aptCheck = await $.spawnSafe('which apt-get');
+ if (aptCheck.status === SpawnStatus.ERROR) {
+ return null;
+ }
+
+ return parameters;
+ }
+
+ override async create(_plan: CreatePlan): Promise {
+ const $ = getPty();
+
+ // Update package lists
+ await $.spawn('apt-get update', { requiresRoot: true, interactive: true });
+
+ console.log('apt is already installed on this Debian-based system');
+ }
+
+ override async destroy(): Promise {
+ // apt is a core system component and should not be removed
+ console.warn('apt cannot be destroyed as it is a core system package manager');
+ }
+}
diff --git a/src/resources/apt/install-parameter.ts b/src/resources/apt/install-parameter.ts
new file mode 100644
index 0000000..0bb4a47
--- /dev/null
+++ b/src/resources/apt/install-parameter.ts
@@ -0,0 +1,175 @@
+import { ParameterSetting, Plan, StatefulParameter, getPty } from '@codifycli/plugin-core';
+
+import { AptConfig } from './apt.js';
+
+export interface AptPackage {
+ name: string;
+ version?: string;
+}
+
+export class AptInstallParameter extends StatefulParameter> {
+
+ getSettings(): ParameterSetting {
+ return {
+ type: 'array',
+ filterInStatelessMode: (desired, current) =>
+ current.filter((c) => desired.some((d) => this.isSamePackage(d, c))),
+ isElementEqual: this.isEqual,
+ }
+ }
+
+ async refresh(desired: Array | null, _config: Partial): Promise | null> {
+ const $ = getPty()
+ const { data: installed } = await $.spawnSafe('dpkg-query -W -f=\'${Package} ${Version}\\n\'');
+
+ if (!installed || installed === '') {
+ return null;
+ }
+
+ const r = installed.split(/\n/)
+ .filter(Boolean)
+ .map((l) => {
+ const [name, version] = l.split(/\s+/)
+ .filter(Boolean)
+
+ return { name, version }
+ })
+ .filter((pkg) =>
+ // Only return packages that are in the desired list
+ desired?.some((d) => {
+ if (typeof d === 'string') {
+ return d === pkg.name;
+ }
+
+ return d.name === pkg.name;
+ })
+ )
+ .map((installed) => {
+ if (desired?.find((d) => typeof d === 'string' && d === installed.name)) {
+ return installed.name;
+ }
+
+ if (desired?.find((d) => typeof d === 'object' && d.name === installed.name && !d.version)) {
+ return { name: installed.name }
+ }
+
+ return installed;
+ })
+
+ return r.length > 0 ? r : null;
+ }
+
+ async add(valueToAdd: Array, plan: Plan): Promise {
+ await this.updateIfNeeded(plan);
+ await this.install(valueToAdd);
+ }
+
+ async modify(newValue: (AptPackage | string)[], previousValue: (AptPackage | string)[], plan: Plan): Promise {
+ const valuesToAdd = newValue.filter((n) => !previousValue.some((p) => this.isSamePackage(n, p)));
+ const valuesToRemove = previousValue.filter((p) => !newValue.some((n) => this.isSamePackage(n, p)));
+
+ await this.uninstall(valuesToRemove);
+ await this.updateIfNeeded(plan);
+ await this.install(valuesToAdd);
+ }
+
+ async remove(valueToRemove: (AptPackage | string)[], _plan: Plan): Promise {
+ await this.uninstall(valueToRemove);
+ }
+
+ private async updateIfNeeded(plan: Plan): Promise {
+ if (plan.desiredConfig?.update === false) {
+ return;
+ }
+
+ const $ = getPty();
+ await $.spawn('apt-get update', { requiresRoot: true, interactive: true });
+ }
+
+ private async install(packages: Array): Promise {
+ if (!packages || packages.length === 0) {
+ return;
+ }
+
+ const $ = getPty();
+ const toInstall = packages.map((p) => {
+ if (typeof p === 'string') {
+ return p;
+ }
+
+ if (p.version) {
+ return `${p.name}=${p.version}`;
+ }
+
+ return p.name;
+ }).join(' ');
+
+ await $.spawn(`apt-get install -y ${toInstall}`, {
+ requiresRoot: true,
+ env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' }
+ });
+ }
+
+ private async uninstall(packages: Array): Promise {
+ if (!packages || packages.length === 0) {
+ return;
+ }
+
+ const $ = getPty();
+ const toUninstall = packages.map((p) => {
+ if (typeof p === 'string') {
+ return p;
+ }
+
+ return p.name;
+ }).join(' ');
+
+ await $.spawn(`apt-get auto-remove -y ${toUninstall}`, { requiresRoot: true, env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' }});
+ }
+
+ isSamePackage(a: AptPackage | string, b: AptPackage | string): boolean {
+ if (typeof a === 'string' || typeof b === 'string') {
+ if (typeof a === 'string' && typeof b === 'string') {
+ return a === b;
+ }
+
+ if (typeof a === 'string' && typeof b === 'object') {
+ return a === b.name;
+ }
+
+ if (typeof a === 'object' && typeof b === 'string') {
+ return a.name === b;
+ }
+ }
+
+ if (typeof a === 'object' && typeof b === 'object') {
+ return a.name === b.name;
+ }
+
+ return false;
+ }
+
+ isEqual(desired: AptPackage | string, current: AptPackage | string): boolean {
+ if (typeof desired === 'string' || typeof current === 'string') {
+ if (typeof desired === 'string' && typeof current === 'string') {
+ return desired === current;
+ }
+
+ if (typeof desired === 'string' && typeof current === 'object') {
+ return desired === current.name;
+ }
+
+ if (typeof desired === 'object' && typeof current === 'string') {
+ return desired.name === current;
+ }
+ }
+
+ if (typeof desired === 'object' && typeof current === 'object') {
+ return desired.version
+ ? desired.version === current.version && desired.name === current.name
+ : desired.name === current.name;
+ }
+
+ return false;
+ }
+}
diff --git a/src/resources/asdf/asdf-global-schema.json b/src/resources/asdf/asdf-global-schema.json
deleted file mode 100644
index adb7036..0000000
--- a/src/resources/asdf/asdf-global-schema.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/asdf-global-schema.json",
- "$comment": "https://docs.codifycli.com/core-resources/asdf/asdf-global/",
- "title": "Asdf plugin global resource",
- "type": "object",
- "description": "Manage the asdf global version for a tool. An asdf-global or asdf-local resource must be specified before a tool installed with asdf is active in the shell.",
- "properties": {
- "plugin": {
- "type": "string",
- "description": "Asdf plugin name"
- },
- "version": {
- "type": "string",
- "description": "A version to install"
- }
- },
- "required": ["plugin", "version"],
- "additionalProperties": false
-}
diff --git a/src/resources/asdf/asdf-global.ts b/src/resources/asdf/asdf-global.ts
deleted file mode 100644
index 454fc8f..0000000
--- a/src/resources/asdf/asdf-global.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { CreatePlan, DestroyPlan, Resource, ResourceSettings, getPty, } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
-import os from 'node:os';
-import path from 'node:path';
-
-import { SpawnStatus, codifySpawn } from '../../utils/codify-spawn.js';
-import { FileUtils } from '../../utils/file-utils.js';
-import AsdfGlobalSchema from './asdf-global-schema.json';
-
-export interface AsdfGlobalConfig extends ResourceConfig {
- plugin: string;
- version: string;
-}
-
-export class AsdfGlobalResource extends Resource {
- getSettings(): ResourceSettings {
- return {
- id: 'asdf-global',
- dependencies: ['asdf', 'asdf-plugin'],
- schema: AsdfGlobalSchema,
- importAndDestroy:{
- requiredParameters: ['plugin'],
- refreshKeys: ['plugin', 'version'],
- defaultRefreshValues: {
- version: 'latest'
- }
- }
- }
- }
-
- async refresh(parameters: Partial): Promise | Partial[] | null> {
- const $ = getPty();
-
- const plugins = await $.spawnSafe(`asdf list ${parameters.plugin}`);
- if (plugins.status === SpawnStatus.ERROR) {
- return null;
- }
-
- // Only check for the installed version matches if it's not latest. The latest version could be out of date.
- const installedVersions = new Set(plugins
- .data
- .split(/\n/)
- .filter(Boolean)
- .map((l) => l.trim())
- .map((l) => l.replaceAll('*', '')))
-
- if (parameters.version !== 'latest') {
- if (!installedVersions.has(parameters.version ?? '')) {
- return null;
- }
- } else if (installedVersions.size === 0) {
- return null;
- }
-
- const { status } = await $.spawnSafe(`asdf current ${parameters.plugin}`, { cwd: os.homedir() });
- return status === SpawnStatus.ERROR
- ? null
- : parameters;
- }
-
- async create(plan: CreatePlan): Promise {
- await codifySpawn(`asdf global ${plan.desiredConfig.plugin} ${plan.desiredConfig.version}`);
- }
-
- async destroy(plan: DestroyPlan): Promise {
- await FileUtils.removeLineFromFile(path.join(os.homedir(), '.tool-versions'), plan.currentConfig.plugin);
- }
-}
diff --git a/src/resources/asdf/asdf-install-schema.json b/src/resources/asdf/asdf-install-schema.json
deleted file mode 100644
index ca99153..0000000
--- a/src/resources/asdf/asdf-install-schema.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/asdf-install-schema.json",
- "$comment": "https://docs.codifycli.com/core-resources/asdf/asdf-install/",
- "title": "Asdf plugin resource",
- "type": "object",
- "description": "Install a .tools-version file or directly install an asdf plugin + tool version.",
- "properties": {
- "plugin": {
- "type": "string",
- "description": "Asdf plugin name"
- },
- "versions": {
- "type": "array",
- "description": "A list of versions to install",
- "items": {
- "type": "string"
- }
- },
- "directory": {
- "type": "string",
- "description": "The directory to run the install command"
- }
- },
- "oneOf": [
- {
- "required": ["plugin", "versions"]
- },
- {
- "required": ["directory"]
- }
- ],
- "additionalProperties": false
-}
diff --git a/src/resources/asdf/asdf-install.ts b/src/resources/asdf/asdf-install.ts
index 5efdd3d..940e8c9 100644
--- a/src/resources/asdf/asdf-install.ts
+++ b/src/resources/asdf/asdf-install.ts
@@ -1,20 +1,35 @@
-import { CreatePlan, DestroyPlan, getPty, Resource, ResourceSettings, untildify, } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+import {
+ CreatePlan,
+ DestroyPlan,
+ Resource,
+ ResourceSettings,
+ SpawnStatus,
+ getPty,
+ z,
+} from '@codifycli/plugin-core';
+import { OS } from 'codify-schemas';
import * as fs from 'node:fs/promises';
import path from 'node:path';
-import { SpawnStatus, codifySpawn } from '../../utils/codify-spawn.js';
import { FileUtils } from '../../utils/file-utils.js';
-import AsdfInstallSchema from './asdf-install-schema.json';
-import { AsdfPluginVersionsParameter } from './version-parameter.js';
-export interface AsdfInstallConfig extends ResourceConfig {
- plugin?: string;
- versions?: string[];
- directory?: string;
-}
-
-const CURRENT_VERSION_REGEX = /[^ ]+ +([^ ]+).*/;
+const schema = z.object({
+ plugin: z
+ .string()
+ .describe('Asdf plugin name')
+ .optional(),
+ versions: z
+ .array(z.string())
+ .describe('A list of versions to install')
+ .optional(),
+ directory: z
+ .string()
+ .describe('The directory to run the install command')
+ .optional(),
+});
+
+export type AsdfInstallConfig = z.infer;
+const CURRENT_VERSION_REGEX = /^([^ ]+?)\s+([^ ]+?)\s+.*/;
const TOOL_VERSIONS_REGEX = /^([^ ]+) +([^ ]+)$/;
@@ -22,8 +37,9 @@ export class AsdfInstallResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'asdf-install',
+ operatingSystems: [OS.Darwin, OS.Linux],
dependencies: ['asdf'],
- schema: AsdfInstallSchema,
+ schema,
parameterSettings: {
directory: { type: 'directory' },
versions: { type: 'array' }
@@ -45,7 +61,7 @@ export class AsdfInstallResource extends Resource {
}
if (parameters.directory && !(await FileUtils.checkDirExistsOrThrowIfFile(parameters.directory))) {
- throw new Error(`Directory ${parameters.local} does not exist`);
+ throw new Error(`Directory ${parameters.directory} does not exist`);
}
}
@@ -60,13 +76,13 @@ export class AsdfInstallResource extends Resource {
const desiredTools = await this.getToolVersions(parameters.directory);
for (const { plugin, version } of desiredTools) {
- const { status, data } = await $.spawnSafe(`asdf current ${plugin}`, { cwd: parameters.directory });
+ const { status, data } = await $.spawnSafe(`asdf current ${plugin} --no-header`, { cwd: parameters.directory });
if (status === SpawnStatus.ERROR || data.trim() === '') {
return null;
}
- const [_, currentVersion] = data.match(CURRENT_VERSION_REGEX)!;
- if (currentVersion !== version) {
+ const [_, currentPlugin, currentVersion] = data.match(CURRENT_VERSION_REGEX)!;
+ if (currentPlugin !== plugin || currentVersion !== version) {
return null;
}
}
@@ -78,12 +94,12 @@ export class AsdfInstallResource extends Resource {
// Directly check plugin version
const versionsQuery = await $.spawnSafe(`asdf list ${parameters.plugin}`);
- if (versionsQuery.status === SpawnStatus.ERROR || versionsQuery.data.trim() === 'No versions installed') {
+ if (versionsQuery.status === SpawnStatus.ERROR || versionsQuery.data.trim().includes('No compatible versions installed')) {
return null;
}
const latest = parameters.versions?.includes('latest')
- ? (await codifySpawn(`asdf latest ${parameters.plugin}`)).data.trim()
+ ? (await $.spawnSafe(`asdf latest ${parameters.plugin}`)).data.trim()
: null;
const versions = versionsQuery.data.split(/\n/)
@@ -99,43 +115,48 @@ export class AsdfInstallResource extends Resource {
}
async create(plan: CreatePlan): Promise {
+ const $ = getPty();
+
if (plan.desiredConfig.directory) {
const desiredTools = await this.getToolVersions(plan.desiredConfig.directory);
// Make sure all of the plugins are installed. If not installed, then install them
for (const { plugin } of desiredTools) {
- if ((await codifySpawn(`asdf list ${plugin}`, { throws: false })).status === SpawnStatus.ERROR) {
- await codifySpawn(`asdf plugin add ${plugin}`);
+ if ((await $.spawnSafe(`asdf list ${plugin}`)).status === SpawnStatus.ERROR) {
+ await $.spawn(`asdf plugin add ${plugin}`, { interactive: true });
}
}
- await codifySpawn('asdf install', { cwd: plan.desiredConfig.directory });
+ await $.spawn('asdf install', { cwd: plan.desiredConfig.directory, interactive: true });
return;
}
- await codifySpawn(`asdf install ${plan.desiredConfig?.plugin} ${plan.desiredConfig.versions?.join(' ')}`);
+ await $.spawn(`asdf install ${plan.desiredConfig?.plugin} ${plan.desiredConfig.versions?.join(' ')}`, { interactive: true });
}
async destroy(plan: DestroyPlan): Promise {
+ const $ = getPty();
if (plan.currentConfig.directory) {
const desiredTools = await this.getToolVersions(plan.currentConfig.directory);
// Uninstall plugin versions listed in .tool-versions
for (const { plugin, version } of desiredTools) {
- await codifySpawn(`asdf uninstall ${plugin} ${version}`);
+ await $.spawn(`asdf uninstall ${plugin} ${version === 'latest'? `$(asdf latest ${plugin})` : version}`, { interactive: true });
}
return;
}
// Other path is uninstalled through the stateful parameter
- await codifySpawn(`asdf uninstall ${plan.currentConfig?.plugin} ${plan.currentConfig.versions?.join(' ')}`);
+ await $.spawn(`asdf uninstall ${plan.currentConfig?.plugin} ${plan.currentConfig.versions
+ ?.map(version => version === 'latest'? `$(asdf latest ${plan.currentConfig?.plugin})` : version)
+ .join(' ')}`, { interactive: true });
}
private async getToolVersions(directory: string): Promise> {
const toolsVersions = (await fs.readFile(path.join(directory, '.tool-versions'))).toString();
- return toolsVersions.split(/\n/)
+ return toolsVersions.split(/\n/g)
.filter(Boolean)
.map((l) => {
const matches = l.match(TOOL_VERSIONS_REGEX);
@@ -145,6 +166,6 @@ export class AsdfInstallResource extends Resource {
return matches.slice(1, 3)
})
- .map(([plugin, version, file]) => ({ plugin, version, file }));
+ .map(([plugin, version]) => ({ plugin, version }));
}
}
diff --git a/src/resources/asdf/asdf-local-schema.json b/src/resources/asdf/asdf-local-schema.json
deleted file mode 100644
index e9b40f4..0000000
--- a/src/resources/asdf/asdf-local-schema.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/asdf-local-schema.json",
- "$comment": "https://docs.codifycli.com/core-resources/asdf/asdf-local/",
- "title": "Asdf plugin local resource",
- "description": "Manage the asdf local version for a tool. An asdf-global or asdf-local resource must be specified before a tool installed with asdf is active in the shell.",
- "type": "object",
- "properties": {
- "plugin": {
- "type": "string",
- "description": "Asdf plugin name"
- },
- "version": {
- "type": "string",
- "description": "A version to install"
- },
- "directory": {
- "type": "string",
- "description": "A local install of the version. Provide the location to install the version. For the current directory use '.'"
- },
- "directories": {
- "type": "array",
- "description": "An array of install locations for the specified version. For the current directory use '.",
- "items": {
- "type": "string"
- }
- }
- },
- "required": ["plugin", "version"],
- "additionalProperties": false
-}
diff --git a/src/resources/asdf/asdf-local.ts b/src/resources/asdf/asdf-local.ts
deleted file mode 100644
index c10fef1..0000000
--- a/src/resources/asdf/asdf-local.ts
+++ /dev/null
@@ -1,180 +0,0 @@
-import {
- CreatePlan,
- DestroyPlan,
- getPty,
- ModifyPlan,
- ParameterChange,
- Resource,
- ResourceSettings,
-} from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
-import * as fs from 'node:fs';
-import path from 'node:path';
-
-import { SpawnStatus, codifySpawn } from '../../utils/codify-spawn.js';
-import { FileUtils } from '../../utils/file-utils.js';
-import { untildify } from '../../utils/untildify.js';
-import AsdfLocalSchema from './asdf-local-schema.json';
-
-const CURRENT_VERSION_REGEX = /[^ ]+ +([^ ]+).*/;
-const VARIOUS_VERSIONS = 'various versions';
-
-export interface AsdfLocalConfig extends ResourceConfig {
- plugin: string;
- version: string;
- directory?: string;
- directories?: string[];
-}
-
-export class AsdfLocalResource extends Resource {
- getSettings(): ResourceSettings {
- return {
- id: 'asdf-local',
- dependencies: ['asdf', 'asdf-plugin'],
- schema: AsdfLocalSchema,
- parameterSettings: {
- directory: { type: 'directory' },
- directories: { type: 'array', canModify: true, itemType: 'directory' },
- version: {
- canModify: true,
- }
- },
- importAndDestroy:{
- requiredParameters: ['plugin', 'directory'],
- refreshKeys: ['plugin', 'version', 'directory'],
- defaultRefreshValues: {
- version: 'latest',
- }
- }
- }
- }
-
- async validate(parameters: Partial): Promise {
- if (!parameters.directory && !parameters.directories) {
- throw new Error('Either directory or directories must be specified');
- }
-
- if (parameters.directory && !(await FileUtils.checkDirExistsOrThrowIfFile(parameters.directory))) {
- throw new Error(`Directory ${parameters.local} does not exist`);
- }
-
- if (parameters.directories) {
- for (const dir of parameters.directories) {
- if (!(await FileUtils.checkDirExistsOrThrowIfFile(dir))) {
- throw new Error(`Directory ${dir} in ${parameters.local} does not exist`);
- }
- }
- }
- }
-
- async refresh(parameters: Partial): Promise | Partial[] | null> {
- const $ = getPty();
-
- const plugins = await $.spawnSafe(`asdf list ${parameters.plugin}`);
- if (plugins.status === SpawnStatus.ERROR) {
- return null;
- }
-
- // Only check for the installed version matches if it's not latest. The latest version could be out of date.
- const installedVersions = new Set(plugins
- .data
- .split(/\n/)
- .filter(Boolean)
- .map((l) => l.trim())
- .map((l) => l.replaceAll('*', '')))
-
- if (parameters.version !== 'latest') {
- if (!installedVersions.has(parameters.version ?? '')) {
- return null;
- }
- } else if (installedVersions.size === 0) {
- return null;
- }
-
- if (parameters.directory) {
- const { status, data } = await $.spawnSafe(`asdf current ${parameters.plugin}`, { cwd: parameters.directory });
-
- if (status === SpawnStatus.ERROR || data.trim() === '') {
- return null;
- }
-
- const [_, currentVersion] = data.match(CURRENT_VERSION_REGEX)!;
- return {
- plugin: parameters.plugin,
- version: parameters.version === 'latest' ? parameters.version : currentVersion,
- directory: parameters.directory,
- }
- }
-
- const installedDirectories: string[] = [];
- let versionUpToDate = true;
- let latestVersion = null;
- for (const dir of parameters.directories!) {
- const { status, data } = await $.spawnSafe(`asdf current ${parameters.plugin}`, { cwd: dir });
- if (status === SpawnStatus.ERROR || data.trim() === '') {
- continue;
- }
-
- installedDirectories.push(dir.trim());
-
-
- const [_, currentVersion] = data.match(CURRENT_VERSION_REGEX)!;
- if (parameters.version !== 'latest' && currentVersion !== parameters.version) {
- versionUpToDate = false;
- continue;
- }
-
- if (parameters.version === 'latest' && !latestVersion) {
- latestVersion = currentVersion;
- continue;
- }
-
- if (parameters.version === 'latest' && currentVersion !== latestVersion) {
- versionUpToDate = false;
- }
- }
-
- if (installedDirectories.length === 0) {
- return null;
- }
-
- return {
- plugin: parameters.plugin,
- version: versionUpToDate ? parameters.version : VARIOUS_VERSIONS,
- directories: installedDirectories.filter(Boolean),
- };
- }
-
- async create(plan: CreatePlan): Promise {
- if (plan.desiredConfig.directory) {
- await codifySpawn(`asdf local ${plan.desiredConfig.plugin} ${plan.desiredConfig.version}`, { cwd: plan.desiredConfig.directory });
- return;
- }
-
- for (const dir of plan.desiredConfig.directories!) {
- await codifySpawn(`asdf local ${plan.desiredConfig.plugin} ${plan.desiredConfig.version}`, { cwd: dir });
- }
- }
-
- async modify(pc: ParameterChange, plan: ModifyPlan): Promise {
- if (plan.desiredConfig.directory) {
- await codifySpawn(`asdf local ${plan.desiredConfig.plugin} ${plan.desiredConfig.version}`, { cwd: plan.desiredConfig.directory });
- return;
- }
-
- for (const dir of plan.desiredConfig.directories!) {
- await codifySpawn(`asdf local ${plan.desiredConfig.plugin} ${plan.desiredConfig.version}`, { cwd: dir });
- }
- }
-
- async destroy(plan: DestroyPlan): Promise {
- if (plan.currentConfig.directory) {
- await FileUtils.removeLineFromFile(path.join(plan.currentConfig.directory, '.tool-versions'), plan.currentConfig.plugin);
- return;
- }
-
- for (const dir of plan.currentConfig.directories!) {
- await FileUtils.removeLineFromFile(path.join(dir, '.tool-versions'), plan.currentConfig.plugin);
- }
- }
-}
diff --git a/src/resources/asdf/asdf-plugin-schema.json b/src/resources/asdf/asdf-plugin-schema.json
deleted file mode 100644
index df1f3d3..0000000
--- a/src/resources/asdf/asdf-plugin-schema.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/asdf-plugin-schema.json",
- "$comment": "https://docs.codifycli.com/core-resources/asdf/asdf-plugin/" ,
- "title": "Asdf plugin resource",
- "description": "Installs a plugin and manages specific tool versions.",
- "type": "object",
- "properties": {
- "plugin": {
- "type": "string",
- "description": "Asdf plugin name"
- },
- "versions": {
- "type": "array",
- "description": "A list of versions to install",
- "items": {
- "type": "string"
- }
- },
- "gitUrl": {
- "type": "string",
- "description": "The gitUrl of the plugin"
- }
- },
- "required": ["plugin"],
- "additionalProperties": false
-}
diff --git a/src/resources/asdf/asdf-plugin.ts b/src/resources/asdf/asdf-plugin.ts
index d1ebbcb..c289e8b 100644
--- a/src/resources/asdf/asdf-plugin.ts
+++ b/src/resources/asdf/asdf-plugin.ts
@@ -1,24 +1,39 @@
-import { CreatePlan, DestroyPlan, Resource, ResourceSettings, SpawnStatus, untildify } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+import {
+ CreatePlan,
+ DestroyPlan,
+ Resource,
+ ResourceSettings,
+ SpawnStatus,
+ getPty,
+ z
+} from '@codifycli/plugin-core';
+import { OS } from 'codify-schemas';
-import { codifySpawn } from '../../utils/codify-spawn.js';
-import AsdfPluginSchema from './asdf-plugin-schema.json';
import { AsdfPluginVersionsParameter } from './version-parameter.js';
-export interface AsdfPluginConfig extends ResourceConfig {
- plugin: string;
- gitUrl: string;
- versions: string[];
-}
+const schema = z
+ .object({
+ plugin: z.string().describe('Asdf plugin name'),
+ versions: z
+ .array(z.string())
+ .describe('A list of versions to install')
+ .optional(),
+ gitUrl: z
+ .string()
+ .describe('The gitUrl of the plugin')
+ .optional()
+ })
+export type AsdfPluginConfig = z.infer;
-const PLUGIN_LIST_REGEX = /^([^ ]+) +([^ ]+)$/
+const PLUGIN_LIST_REGEX = /^([^ ]+?)\s+([^ ]+)/
export class AsdfPluginResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'asdf-plugin',
+ operatingSystems: [OS.Darwin, OS.Linux],
dependencies: ['asdf'],
- schema: AsdfPluginSchema,
+ schema,
parameterSettings: {
versions: { type: 'stateful', definition: new AsdfPluginVersionsParameter() }
},
@@ -26,18 +41,20 @@ export class AsdfPluginResource extends Resource {
}
async refresh(parameters: Partial): Promise | Partial[] | null> {
- if ((await codifySpawn('which asdf', { throws: false })).status === SpawnStatus.ERROR) {
+ const $ = getPty();
+ if ((await $.spawnSafe('which asdf')).status === SpawnStatus.ERROR) {
return null;
}
- const installedVersions = (await codifySpawn('asdf plugin list --urls'))
+ const installedVersions = (await $.spawn('asdf plugin list --urls'))
.data
.split(/\n/)
.filter(Boolean)
- .map((l) => l.trim())
- .map((l) => l.replaceAll('*', ''))
.map((l) => {
+ console.log('line', l);
const matches = l.match(PLUGIN_LIST_REGEX)
+ console.log('matches', matches);
+
if (!matches) {
return null;
}
@@ -60,11 +77,13 @@ export class AsdfPluginResource extends Resource {
}
async create(plan: CreatePlan): Promise {
- await codifySpawn(`asdf plugin add ${plan.desiredConfig.plugin} ${plan.desiredConfig.gitUrl ?? ''}`);
+ const $ = getPty();
+ await $.spawn(`asdf plugin add ${plan.desiredConfig.plugin} ${plan.desiredConfig.gitUrl ?? ''}`, { interactive: true });
}
async destroy(plan: DestroyPlan): Promise {
- await codifySpawn(`asdf plugin remove ${plan.currentConfig.plugin} ${plan.currentConfig.gitUrl ?? ''}`)
+ const $ = getPty();
+ await $.spawn(`asdf plugin remove ${plan.currentConfig.plugin} ${plan.currentConfig.gitUrl ?? ''}`, { interactive: true });
}
}
diff --git a/src/resources/asdf/asdf-schema.json b/src/resources/asdf/asdf-schema.json
deleted file mode 100644
index e591485..0000000
--- a/src/resources/asdf/asdf-schema.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/asdf-schema.json",
- "$comment": "https://docs.codifycli.com/core-resources/asdf/asdf/",
- "title": "Asdf resource",
- "type": "object",
- "description": "Installs asdf and manages asdf plugins. Use 'asdf-install' or 'asdf-plugin' to install the actual tool. Use 'asdf-global' or 'asdf-local' to activate the tool in the shell.",
- "properties": {
- "plugins": {
- "type": "array",
- "description": "Asdf plugins to install. See: https://github.com/asdf-community for a full list",
- "items": {
- "type": "string"
- }
- }
- },
- "additionalProperties": false
-}
diff --git a/src/resources/asdf/asdf.ts b/src/resources/asdf/asdf.ts
index 85bb190..29b26a3 100644
--- a/src/resources/asdf/asdf.ts
+++ b/src/resources/asdf/asdf.ts
@@ -1,55 +1,94 @@
-import { CreatePlan, DestroyPlan, getPty, Resource, ResourceSettings } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+import { CreatePlan, FileUtils, Resource, ResourceSettings, SpawnStatus, getPty, z } from '@codifycli/plugin-core';
+import { OS } from 'codify-schemas';
+import fs from 'node:fs/promises';
+import os from 'node:os';
+import path from 'node:path';
-import { SpawnStatus, codifySpawn } from '../../utils/codify-spawn.js';
-import { FileUtils } from '../../utils/file-utils.js';
-import AsdfSchema from './asdf-schema.json';
+import { Utils } from '../../utils/index.js';
import { AsdfPluginsParameter } from './plugins-parameter.js';
-export interface AsdfConfig extends ResourceConfig {
- plugins: string[];
-}
+const schema = z.object({
+ plugins: z
+ .array(z.string())
+ .describe(
+ 'Asdf plugins to install. See: https://github.com/asdf-community for a full list'
+ )
+ .optional()
+})
+
+export type AsdfConfig = z.infer
export class AsdfResource extends Resource {
- getSettings(): ResourceSettings {
- return {
- id: 'asdf',
- schema: AsdfSchema,
- parameterSettings: {
- plugins: { type: 'stateful', definition: new AsdfPluginsParameter() },
- }
+ getSettings(): ResourceSettings {
+ return {
+ id: 'asdf',
+ operatingSystems: [OS.Darwin, OS.Linux],
+ schema,
+ parameterSettings: {
+ plugins: { type: 'stateful', definition: new AsdfPluginsParameter() },
}
}
+ }
+
+ async refresh(parameters: Partial): Promise | Partial[] | null> {
+ const $ = getPty();
+
+ const { status } = await $.spawnSafe('which asdf');
+ return status === SpawnStatus.SUCCESS ? {} : null;
+ }
- async refresh(parameters: Partial): Promise | Partial[] | null> {
- const $ = getPty();
+ async create(plan: CreatePlan): Promise {
+ const $ = getPty();
- const { status } = await $.spawnSafe('which asdf');
- return status === SpawnStatus.SUCCESS ? {} : null;
+ if (Utils.isMacOS()) {
+ if (!(await Utils.isHomebrewInstalled())) {
+ throw new Error('Homebrew is not installed. Please install Homebrew before installing asdf.');
+ }
+
+ await $.spawn('brew install asdf', { interactive: true, env: { HOMEBREW_NO_AUTO_UPDATE: 1 } });
}
- async create(plan: CreatePlan): Promise {
- await codifySpawn('git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.1');
-
- await FileUtils.addAllToStartupFile([
- '# Asdf setup',
- '. "$HOME/.asdf/asdf.sh"',
- '# append completions to fpath',
- 'fpath=(${ASDF_DIR}/completions $fpath)',
- '# initialise completions with ZSH\'s compinit',
- 'autoload -Uz compinit && compinit'
- ]);
+ if (Utils.isLinux()) {
+ const { data: latestVersion } = await $.spawn('curl -s https://api.github.com/repos/asdf-vm/asdf/releases/latest | grep \'"tag_name":\' | sed -E \'s/.*"([^"]+)".*/\\1/\'');
+
+ // Create .asdf directory if it doesn't exist
+ const asdfDir = path.join(os.homedir(), '.local', 'bin');
+ await fs.mkdir(asdfDir, { recursive: true });
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'codify-asdf'));
+ const arch = (await Utils.isArmArch()) ? 'arm64' : 'amd64';
+
+ // Download and extract asdf
+ await $.spawn(`curl -Lo ${tmpDir}/asdf.tar.gz "https://github.com/asdf-vm/asdf/releases/download/${latestVersion}/asdf-${latestVersion}-linux-${arch}.tar.gz"`, { cwd: tmpDir });
+ console.log(await $.spawn('ls -la', { cwd: tmpDir }));
+ await $.spawn(`tar -xzf ${tmpDir}/asdf.tar.gz -C ${asdfDir}`, { cwd: tmpDir });
+ await fs.chmod(path.join(asdfDir, 'asdf'), 0o755);
+
+ await fs.rm(tmpDir, { recursive: true, force: true });
+
+ await FileUtils.addPathToShellRc(path.join(os.homedir(), '.local', 'bin'), true);
}
- async destroy(plan: DestroyPlan): Promise {
- await FileUtils.removeLineFromZshrc('# Asdf setup')
- await FileUtils.removeLineFromZshrc('. "$HOME/.asdf/asdf.sh"');
- await FileUtils.removeLineFromZshrc('# append completions to fpath');
- await FileUtils.removeLineFromZshrc('fpath=(${ASDF_DIR}/completions $fpath)');
- await FileUtils.removeLineFromZshrc('# initialise completions with ZSH\'s compinit');
- await FileUtils.removeLineFromZshrc('autoload -Uz compinit && compinit');
+ // eslint-disable-next-line no-template-curly-in-string
+ await FileUtils.addToShellRc('export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"')
+ }
+
+ async destroy(): Promise {
+ const $ = getPty();
- await codifySpawn('rm -rf ~/.asdf');
+ const asdfDir = (await $.spawn('which asdf', { interactive: true })).data;
+ if (Utils.isMacOS() && asdfDir.includes('homebrew')) {
+ if (!(await Utils.isHomebrewInstalled())) {
+ return;
+ }
+
+ await $.spawn('brew uninstall asdf', { interactive: true, env: { HOMEBREW_NO_AUTO_UPDATE: 1 } });
+ } else {
+ await fs.rm(asdfDir, { recursive: true, force: true });
}
+ // eslint-disable-next-line no-template-curly-in-string
+ await FileUtils.removeLineFromShellRc('export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"')
+ await fs.rm(path.join(os.homedir(), '.asdf'), { recursive: true, force: true });
+ }
+
}
diff --git a/src/resources/asdf/plugins-parameter.ts b/src/resources/asdf/plugins-parameter.ts
index ebff1c3..a7dba0c 100644
--- a/src/resources/asdf/plugins-parameter.ts
+++ b/src/resources/asdf/plugins-parameter.ts
@@ -1,13 +1,12 @@
-import { ArrayStatefulParameter, getPty, Plan, StatefulParameter } from 'codify-plugin-lib';
+import { ArrayStatefulParameter, getPty, Plan, SpawnStatus, StatefulParameter } from '@codifycli/plugin-core';
-import { codifySpawn, SpawnStatus } from '../../utils/codify-spawn.js';
import { AsdfConfig } from './asdf.js';
export class AsdfPluginsParameter extends ArrayStatefulParameter {
async refresh(desired: null | string[]): Promise {
const $ = getPty();
- const plugins = await $.spawnSafe('asdf plugin list ')
+ const plugins = await $.spawnSafe('asdf plugin list')
if (plugins.status === SpawnStatus.ERROR) {
return null;
}
@@ -19,10 +18,12 @@ export class AsdfPluginsParameter extends ArrayStatefulParameter): Promise {
- await codifySpawn(`asdf plugin add ${item}`);
+ const pty = getPty();
+ await pty.spawn(`asdf plugin add ${item}`, { interactive: true });
}
async removeItem(item: string, plan: Plan): Promise {
- await codifySpawn(`asdf plugin remove ${item}`);
+ const pty = getPty();
+ await pty.spawn(`asdf plugin remove ${item}`, { interactive: true });
}
}
diff --git a/src/resources/asdf/version-parameter.ts b/src/resources/asdf/version-parameter.ts
index 3f2de53..9344ee4 100644
--- a/src/resources/asdf/version-parameter.ts
+++ b/src/resources/asdf/version-parameter.ts
@@ -1,6 +1,5 @@
-import { ArrayStatefulParameter, getPty, Plan, SpawnStatus } from 'codify-plugin-lib';
+import { ArrayStatefulParameter, getPty, Plan, SpawnStatus } from '@codifycli/plugin-core';
-import { codifySpawn } from '../../utils/codify-spawn.js';
import { AsdfPluginConfig } from './asdf-plugin.js';
export class AsdfPluginVersionsParameter extends ArrayStatefulParameter {
@@ -14,22 +13,24 @@ export class AsdfPluginVersionsParameter extends ArrayStatefulParameter l.trim())
- .map((l) => l.replaceAll('*', ''))
+ .map((l) => l.replaceAll( '*', ''))
.map((l) => l.trim() === latest ? 'latest' : l)
.filter(Boolean);
}
async addItem(item: string, plan: Plan): Promise {
- await codifySpawn(`asdf install ${plan.desiredConfig?.plugin} ${item}`);
+ const pty = getPty();
+ await pty.spawn(`asdf install ${plan.desiredConfig?.plugin} ${item}`, { interactive: true });
}
async removeItem(item: string, plan: Plan): Promise {
- await codifySpawn(`asdf uninstall ${plan.currentConfig?.plugin} ${item}`);
+ const pty = getPty();
+ await pty.spawn(`asdf uninstall ${plan.currentConfig?.plugin} ${item}`, { interactive: true });
}
}
diff --git a/src/resources/aws-cli/cli/aws-cli.ts b/src/resources/aws-cli/cli/aws-cli.ts
index 6a2fff8..66f978b 100644
--- a/src/resources/aws-cli/cli/aws-cli.ts
+++ b/src/resources/aws-cli/cli/aws-cli.ts
@@ -1,8 +1,9 @@
-import { getPty, Resource, ResourceSettings } from 'codify-plugin-lib';
-import { StringIndexedObject } from 'codify-schemas';
+import { Resource, ResourceSettings, SpawnStatus, Utils, getPty, FileUtils } from '@codifycli/plugin-core';
+import { OS, StringIndexedObject } from 'codify-schemas';
+import fs from 'node:fs/promises';
+import os from 'node:os';
+import path from 'node:path';
-import { SpawnStatus, codifySpawn } from '../../../utils/codify-spawn.js';
-import { Utils } from '../../../utils/index.js';
import Schema from './aws-cli-schema.json';
export interface AwsCliConfig extends StringIndexedObject {
@@ -16,6 +17,7 @@ export class AwsCliResource extends Resource {
getSettings(): ResourceSettings {
return {
schema: Schema,
+ operatingSystems: [OS.Darwin, OS.Linux],
id: 'aws-cli',
};
}
@@ -25,6 +27,8 @@ export class AwsCliResource extends Resource {
const $ = getPty();
const awsCliInfo = await $.spawnSafe('which aws');
+ console.log('Spawn result', awsCliInfo);
+
if (awsCliInfo.status === SpawnStatus.ERROR) {
return null;
}
@@ -33,25 +37,29 @@ export class AwsCliResource extends Resource {
}
override async create(): Promise {
+ const $ = getPty();
+
// Amazon has not released a standalone way to install arm aws-cli. See: https://github.com/aws/aws-cli/issues/7252
// Prefer the homebrew version on M1
const isArmArch = await Utils.isArmArch();
- const isRosettaInstalled = await Utils.isRosetta2Installed()
- const isHomebrewInstalled = await Utils.isHomebrewInstalled();
-
- if (isArmArch && isHomebrewInstalled) {
- console.log('Resource: \'aws-cli\'. Detected that mac is aarch64. Installing AWS-CLI via homebrew')
- await codifySpawn('brew install awscli')
-
- } else if (!isArmArch || isRosettaInstalled) {
- console.log('Resource: \'aws-cli\'. Detected that mac is not ARM or Rosetta is installed. Installing AWS-CLI standalone version')
- await codifySpawn('curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"');
- await codifySpawn('installer -pkg ./AWSCLIV2.pkg -target /', { requiresRoot: true })
- await codifySpawn('rm -rf ./AWSCLIV2.pkg')
-
- } else {
- // This covers arm arch + Homebrew is not installed
- throw new Error(`Resource: 'aws-cli'. This plugin prefers installing AWS-CLI via homebrew for M1 macs.
+
+ if (Utils.isMacOS()) {
+ const isRosettaInstalled = await Utils.isRosetta2Installed()
+ const isHomebrewInstalled = await Utils.isHomebrewInstalled();
+
+ if (isArmArch && isHomebrewInstalled) {
+ console.log('Resource: \'aws-cli\'. Detected that mac is aarch64. Installing AWS-CLI via homebrew')
+ await $.spawn('HOMEBREW_NO_AUTO_UPDATE=1 brew install awscli', { interactive: true })
+
+ } else if (!isArmArch || isRosettaInstalled) {
+ console.log('Resource: \'aws-cli\'. Detected that mac is not ARM or Rosetta is installed. Installing AWS-CLI standalone version')
+ await $.spawn('curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"');
+ await $.spawn('installer -pkg ./AWSCLIV2.pkg -target /', { requiresRoot: true })
+ await fs.rm('./AWSCLIV2.pkg', { recursive: true, force: true });
+
+ } else {
+ // This covers arm arch + Homebrew is not installed
+ throw new Error(`Resource: 'aws-cli'. This plugin prefers installing AWS-CLI via homebrew for M1 macs.
AWS has not updated the standalone installer to support M1 macs. See: https://github.com/aws/aws-cli/issues/7252.
Homebrew can be installed by adding:
@@ -63,28 +71,44 @@ Or enable rosetta 2 using the below command and re-run:
softwareupdate --install-rosetta
`);
+ }
+ } else if (Utils.isLinux()) {
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'codify-aws-cli'));
+
+ // Detect architecture and use appropriate download link
+ const downloadUrl = isArmArch
+ ? 'https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip'
+ : 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip';
+
+ console.log(`Installing AWS CLI for Linux (${isArmArch ? 'ARM64' : 'x86_64'})...`);
+ await FileUtils.downloadFile(downloadUrl, path.join(tmpDir, 'awscliv2.zip'));
+ await $.spawn('unzip -q awscliv2.zip', { cwd: tmpDir });
+ await $.spawn('./aws/install', { cwd: tmpDir, requiresRoot: true });
+ await fs.rm(tmpDir, { recursive: true, force: true });
}
}
override async destroy(): Promise {
+ const $ = getPty();
+
const installLocation = await this.findInstallLocation();
if (!installLocation) {
return;
}
-
+
if (installLocation.includes('homebrew')) {
- await codifySpawn('brew uninstall awscli');
+ await $.spawn('brew uninstall awscli', { interactive: true, env: { HOMEBREW_NO_AUTO_UPDATE: 1 } });
return;
}
-
- await codifySpawn(`rm ${installLocation}`, { requiresRoot: true });
- await codifySpawn(`rm ${installLocation}_completer`, { requiresRoot: true });
- await codifySpawn('rm -rf /usr/local/aws-cli', { requiresRoot: true });
- await codifySpawn('rm -rf $HOME/.aws/', { requiresRoot: true });
+
+ await $.spawnSafe(`rm ${installLocation}`, { requiresRoot: true });
+ await $.spawnSafe(`rm ${installLocation}_completer`, { requiresRoot: true });
+ await $.spawnSafe('rm -rf $HOME/.aws/');
}
-
+
private async findInstallLocation(): Promise {
- const query = await codifySpawn('which aws', { throws: false });
+ const $ = getPty();
+ const query = await $.spawnSafe('which aws', { interactive: true });
if (query.status === SpawnStatus.ERROR) {
return null;
}
diff --git a/src/resources/aws-cli/profile/aws-profile.ts b/src/resources/aws-cli/profile/aws-profile.ts
index 768af85..2a67ac8 100644
--- a/src/resources/aws-cli/profile/aws-profile.ts
+++ b/src/resources/aws-cli/profile/aws-profile.ts
@@ -1,18 +1,18 @@
import {
CreatePlan,
DestroyPlan,
- getPty,
ModifyPlan,
ParameterChange,
Resource,
- ResourceSettings
-} from 'codify-plugin-lib';
-import { StringIndexedObject } from 'codify-schemas';
+ ResourceSettings,
+ SpawnStatus,
+ getPty
+} from '@codifycli/plugin-core';
+import { OS, StringIndexedObject } from 'codify-schemas';
import * as fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
-import { SpawnStatus, codifySpawn } from '../../../utils/codify-spawn.js';
import Schema from './aws-profile-schema.json'
import { CSVCredentialsTransformation } from './csv-credentials-transformation.js';
@@ -32,6 +32,7 @@ export class AwsProfileResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'aws-profile',
+ operatingSystems: [OS.Darwin, OS.Linux],
dependencies: ['aws-cli'],
schema: Schema,
parameterSettings: {
@@ -127,8 +128,10 @@ export class AwsProfileResource extends Resource {
}
override async create(plan: CreatePlan): Promise {
+ const $ = getPty();
+
// Assert that aws-cli is installed
- await codifySpawn('which aws')
+ await $.spawn('which aws', { interactive: true })
const {
awsAccessKeyId,
@@ -190,7 +193,7 @@ export class AwsProfileResource extends Resource {
private async getAwsConfigureValueOrNull(key: string, profile: string): Promise {
const $ = getPty();
- const { data, status } = await $.spawnSafe(`aws configure get ${key} --profile ${profile}`);
+ const { data, status } = await $.spawnSafe(`aws configure get ${key} --profile ${profile}`, { interactive: true });
if (status === SpawnStatus.ERROR) {
return undefined;
}
@@ -199,6 +202,7 @@ export class AwsProfileResource extends Resource {
}
private async setAwsConfigureValue(key: string, value: number | string, profile: string): Promise {
- await codifySpawn(`aws configure set ${key} ${value} --profile ${profile}`);
+ const $ = getPty();
+ await $.spawn(`aws configure set ${key} ${value} --profile ${profile}`, { interactive: true });
}
}
diff --git a/src/resources/aws-cli/profile/csv-credentials-transformation.ts b/src/resources/aws-cli/profile/csv-credentials-transformation.ts
index badaf35..f98cf4e 100644
--- a/src/resources/aws-cli/profile/csv-credentials-transformation.ts
+++ b/src/resources/aws-cli/profile/csv-credentials-transformation.ts
@@ -1,9 +1,8 @@
-import { type InputTransformation } from 'codify-plugin-lib';
+import { type InputTransformation, untildify } from '@codifycli/plugin-core';
import * as fsSync from 'node:fs';
import * as fs from 'node:fs/promises';
import path from 'node:path';
-import { untildify } from '../../../utils/untildify.js';
import { AwsProfileConfig } from './aws-profile.js';
export const CSVCredentialsTransformation: InputTransformation = {
diff --git a/src/resources/dnf/dnf-schema.json b/src/resources/dnf/dnf-schema.json
new file mode 100644
index 0000000..f6ad45e
--- /dev/null
+++ b/src/resources/dnf/dnf-schema.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema",
+ "$id": "https://www.codifycli.com/dnf.json",
+ "$comment": "https://docs.codifycli.com/core-resources/dnf/",
+ "title": "Dnf resource",
+ "description": "Manage dnf packages on modern Red Hat-based systems.",
+ "type": "object",
+ "properties": {
+ "install": {
+ "type": "array",
+ "description": "Installs packages using dnf.",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "version": { "type": "string" }
+ },
+ "required": ["name"]
+ }
+ ]
+ }
+ },
+ "update": {
+ "type": "boolean",
+ "description": "Whether to run dnf check-update before installing packages. Defaults to true."
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/src/resources/dnf/dnf.ts b/src/resources/dnf/dnf.ts
new file mode 100644
index 0000000..155231e
--- /dev/null
+++ b/src/resources/dnf/dnf.ts
@@ -0,0 +1,50 @@
+import { CreatePlan, Resource, ResourceSettings, SpawnStatus, getPty } from '@codifycli/plugin-core';
+import { OS, ResourceConfig } from 'codify-schemas';
+
+import schema from './dnf-schema.json';
+import { DnfInstallParameter, DnfPackage } from './install-parameter.js';
+
+export interface DnfConfig extends ResourceConfig {
+ install: Array;
+ update?: boolean;
+}
+
+export class DnfResource extends Resource {
+
+ override getSettings(): ResourceSettings {
+ return {
+ id: 'dnf',
+ operatingSystems: [OS.Linux],
+ schema,
+ parameterSettings: {
+ install: { type: 'stateful', definition: new DnfInstallParameter() },
+ update: { type: 'boolean', default: true, setting: true }
+ }
+ };
+ }
+
+ override async refresh(parameters: Partial): Promise | null> {
+ const $ = getPty();
+
+ const dnfCheck = await $.spawnSafe('which dnf');
+ if (dnfCheck.status === SpawnStatus.ERROR) {
+ return null;
+ }
+
+ return parameters;
+ }
+
+ override async create(_plan: CreatePlan): Promise {
+ const $ = getPty();
+
+ // Update package lists
+ await $.spawnSafe('dnf check-update', { requiresRoot: true, interactive: true });
+
+ console.log('dnf is already installed on this Red Hat-based system');
+ }
+
+ override async destroy(): Promise {
+ // dnf is a core system component and should not be removed
+ throw new Error('dnf cannot be destroyed as it is a core system package manager');
+ }
+}
diff --git a/src/resources/dnf/install-parameter.ts b/src/resources/dnf/install-parameter.ts
new file mode 100644
index 0000000..7fc25cb
--- /dev/null
+++ b/src/resources/dnf/install-parameter.ts
@@ -0,0 +1,172 @@
+import { ParameterSetting, Plan, StatefulParameter, getPty } from '@codifycli/plugin-core';
+
+import { DnfConfig } from './dnf.js';
+
+export interface DnfPackage {
+ name: string;
+ version?: string;
+}
+
+export class DnfInstallParameter extends StatefulParameter> {
+
+ getSettings(): ParameterSetting {
+ return {
+ type: 'array',
+ filterInStatelessMode: (desired, current) =>
+ current.filter((c) => desired.some((d) => this.isSamePackage(d, c))),
+ isElementEqual: this.isEqual,
+ }
+ }
+
+ async refresh(desired: Array | null, _config: Partial): Promise | null> {
+ const $ = getPty()
+ const { data: installed } = await $.spawnSafe('rpm -qa --queryformat \'%{NAME} %{VERSION}-%{RELEASE}\\n\'');
+
+ if (!installed || installed === '') {
+ return null;
+ }
+
+ const r = installed.split(/\n/)
+ .filter(Boolean)
+ .map((l) => {
+ const [name, version] = l.split(/\s+/)
+ .filter(Boolean)
+
+ return { name, version }
+ })
+ .filter((pkg) =>
+ // Only return packages that are in the desired list
+ desired?.some((d) => {
+ if (typeof d === 'string') {
+ return d === pkg.name;
+ }
+
+ return d.name === pkg.name;
+ })
+ )
+ .map((installed) => {
+ if (desired?.find((d) => typeof d === 'string' && d === installed.name)) {
+ return installed.name;
+ }
+
+ if (desired?.find((d) => typeof d === 'object' && d.name === installed.name && !d.version)) {
+ return { name: installed.name }
+ }
+
+ return installed;
+ })
+
+ return r.length > 0 ? r : null;
+ }
+
+ async add(valueToAdd: Array, plan: Plan): Promise {
+ await this.updateIfNeeded(plan);
+ await this.install(valueToAdd);
+ }
+
+ async modify(newValue: (DnfPackage | string)[], previousValue: (DnfPackage | string)[], plan: Plan): Promise {
+ const valuesToAdd = newValue.filter((n) => !previousValue.some((p) => this.isSamePackage(n, p)));
+ const valuesToRemove = previousValue.filter((p) => !newValue.some((n) => this.isSamePackage(n, p)));
+
+ await this.uninstall(valuesToRemove);
+ await this.updateIfNeeded(plan);
+ await this.install(valuesToAdd);
+ }
+
+ async remove(valueToRemove: (DnfPackage | string)[], _plan: Plan): Promise {
+ await this.uninstall(valueToRemove);
+ }
+
+ private async updateIfNeeded(plan: Plan): Promise {
+ if (plan.desiredConfig?.update === false) {
+ return;
+ }
+
+ const $ = getPty();
+ await $.spawnSafe('dnf check-update', { requiresRoot: true, interactive: true });
+ }
+
+ private async install(packages: Array): Promise {
+ if (!packages || packages.length === 0) {
+ return;
+ }
+
+ const $ = getPty();
+ const toInstall = packages.map((p) => {
+ if (typeof p === 'string') {
+ return p;
+ }
+
+ if (p.version) {
+ return `${p.name}-${p.version}`;
+ }
+
+ return p.name;
+ }).join(' ');
+
+ await $.spawn(`dnf install -y ${toInstall} --allowerasing`, { requiresRoot: true, interactive: true });
+ }
+
+ private async uninstall(packages: Array): Promise {
+ if (!packages || packages.length === 0) {
+ return;
+ }
+
+ const $ = getPty();
+ const toUninstall = packages.map((p) => {
+ if (typeof p === 'string') {
+ return p;
+ }
+
+ return p.name;
+ }).join(' ');
+
+ await $.spawn(`dnf remove -y ${toUninstall}`, { requiresRoot: true, interactive: true });
+ }
+
+ isSamePackage(a: DnfPackage | string, b: DnfPackage | string): boolean {
+ if (typeof a === 'string' || typeof b === 'string') {
+ if (typeof a === 'string' && typeof b === 'string') {
+ return a === b;
+ }
+
+ if (typeof a === 'string' && typeof b === 'object') {
+ return a === b.name;
+ }
+
+ if (typeof a === 'object' && typeof b === 'string') {
+ return a.name === b;
+ }
+ }
+
+ if (typeof a === 'object' && typeof b === 'object') {
+ return a.name === b.name;
+ }
+
+ return false;
+ }
+
+ isEqual(desired: DnfPackage | string, current: DnfPackage | string): boolean {
+ if (typeof desired === 'string' || typeof current === 'string') {
+ if (typeof desired === 'string' && typeof current === 'string') {
+ return desired === current;
+ }
+
+ if (typeof desired === 'string' && typeof current === 'object') {
+ return desired === current.name;
+ }
+
+ if (typeof desired === 'object' && typeof current === 'string') {
+ return desired.name === current;
+ }
+ }
+
+ if (typeof desired === 'object' && typeof current === 'object') {
+ return desired.version
+ ? desired.version === current.version && desired.name === current.name
+ : desired.name === current.name;
+ }
+
+ return false;
+ }
+}
diff --git a/src/resources/docker/docker.ts b/src/resources/docker/docker.ts
index 4c70218..ec26233 100644
--- a/src/resources/docker/docker.ts
+++ b/src/resources/docker/docker.ts
@@ -1,10 +1,10 @@
-import { CreatePlan, DestroyPlan, Resource, ResourceSettings, getPty } from 'codify-plugin-lib';
-import { StringIndexedObject } from 'codify-schemas';
+import { CreatePlan, DestroyPlan, Resource, ResourceSettings, getPty } from '@codifycli/plugin-core';
+import { OS, StringIndexedObject } from 'codify-schemas';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
-import { SpawnStatus, codifySpawn } from '../../utils/codify-spawn.js';
+import { SpawnStatus } from '../../utils/codify-spawn.js';
import { FileUtils } from '../../utils/file-utils.js';
import { Utils } from '../../utils/index.js';
import Schema from './docker-schema.json';
@@ -21,6 +21,7 @@ export class DockerResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'docker',
+ operatingSystems: [OS.Darwin],
schema: Schema,
parameterSettings: {
acceptLicense: {
@@ -67,36 +68,239 @@ export class DockerResource extends Resource {
* @param plan
*/
async create(plan: CreatePlan): Promise {
- const downloadLink = await Utils.isArmArch() ? ARM_DOWNLOAD_LINK : INTEL_DOWNLOAD_LINK;
-
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'codify-docker'))
- await Utils.downloadUrlIntoFile(path.join(tmpDir, 'Docker.dmg'), downloadLink);
- const user = Utils.getUser();
-
- try {
- await codifySpawn('hdiutil attach Docker.dmg', { cwd: tmpDir, requiresRoot: true })
-
- console.log('Running Docker installer. This may take a couple of minutes to complete...')
- await codifySpawn(`/Volumes/Docker/Docker.app/Contents/MacOS/install ${plan.desiredConfig.acceptLicense ? '--accept-license' : ''} ${plan.desiredConfig.useCurrentUser ? `--user ${user}` : ''}`,
- { requiresRoot: true }
- )
- await codifySpawn('hdiutil detach /Volumes/Docker', { cwd: tmpDir, requiresRoot: true })
- } finally {
- await fs.rm(tmpDir, { recursive: true, force: true })
+ const $ = getPty();
+
+ if (Utils.isMacOS()) {
+ const downloadLink = await Utils.isArmArch() ? ARM_DOWNLOAD_LINK : INTEL_DOWNLOAD_LINK;
+
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'codify-docker'))
+ await Utils.downloadUrlIntoFile(path.join(tmpDir, 'Docker.dmg'), downloadLink);
+ const user = Utils.getUser();
+
+ try {
+ await $.spawn('hdiutil attach Docker.dmg', { cwd: tmpDir })
+
+ console.log('Running Docker installer. This may take a couple of minutes to complete...')
+ await $.spawn(`/Volumes/Docker/Docker.app/Contents/MacOS/install ${plan.desiredConfig.acceptLicense ? '--accept-license' : ''} ${plan.desiredConfig.useCurrentUser ? `--user ${user}` : ''}`,
+ { requiresRoot: true }
+ )
+
+ // TODO: Attempt to sleep until Docker is ready
+ await this.sleep(1000);
+ await $.spawn('hdiutil detach /Volumes/Docker', { cwd: tmpDir })
+ } finally {
+ await fs.rm(tmpDir, { recursive: true, force: true })
+ }
+
+ await $.spawn('xattr -r -d com.apple.quarantine /Applications/Docker.app', { requiresRoot: true });
+ await FileUtils.addPathToPrimaryShellRc('/Applications/Docker.app/Contents/Resources/bin', false);
+ } else if (Utils.isLinux()) {
+ // Detect Linux distribution
+ const isDebianBased = await this.isDebianBased($);
+ const isRedHatBased = await this.isRedHatBased($);
+
+ if (isDebianBased) {
+ await this.installDockerDebian($);
+ } else if (isRedHatBased) {
+ await this.installDockerRedHat($);
+ } else {
+ throw new Error('Unsupported Linux distribution. Only Debian-based (Ubuntu, Debian) and RedHat-based (RHEL, CentOS, Fedora) systems are supported.');
+ }
+ } else {
+ throw new Error('Unsupported operating system');
+ }
+ }
+
+ async destroy(_plan: DestroyPlan): Promise {
+ const $ = getPty();
+
+ if (Utils.isMacOS()) {
+ await $.spawnSafe('/Applications/Docker.app/Contents/MacOS/uninstall', { interactive: true, requiresRoot: true })
+ await fs.rm(path.join(os.homedir(), 'Library/Group\\ Containers/group.com.docker'), { recursive: true, force: true });
+ await fs.rm(path.join(os.homedir(), 'Library/Containers/com.docker.docker/Data'), { recursive: true, force: true });
+ await fs.rm(path.join(os.homedir(), '.docker'), { recursive: true, force: true });
+ await $.spawn('rm -rf /Applications/Docker.app')
+
+ await FileUtils.removeLineFromStartupFile('/Applications/Docker.app/Contents/Resources/bin')
+ } else if (Utils.isLinux()) {
+ const isDebianBased = await this.isDebianBased($);
+ const isRedHatBased = await this.isRedHatBased($);
+
+ if (isDebianBased) {
+ await this.uninstallDockerDebian($);
+ } else if (isRedHatBased) {
+ await this.uninstallDockerRedHat($);
+ } else {
+ throw new Error('Unsupported Linux distribution. Only Debian-based (Ubuntu, Debian) and RedHat-based (RHEL, CentOS, Fedora) systems are supported.');
+ }
+
+ // Remove Docker data directories (common across all Linux distributions)
+ await fs.rm(path.join(os.homedir(), '.docker'), { recursive: true, force: true });
+ } else {
+ throw new Error('Unsupported operating system');
+ }
+ }
+
+ async sleep(ms: number): Promise {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+ }
+
+ private async isDebianBased($: ReturnType): Promise {
+ const result = await $.spawnSafe('test -f /etc/debian_version');
+ return result.status === SpawnStatus.SUCCESS;
+ }
+
+ private async isRedHatBased($: ReturnType): Promise {
+ const result = await $.spawnSafe('test -f /etc/redhat-release');
+ return result.status === SpawnStatus.SUCCESS;
+ }
+
+ private async installDockerDebian($: ReturnType): Promise {
+ console.log('Installing Docker on Debian-based system...');
+
+ // Update package index
+ await $.spawn('apt-get update', { requiresRoot: true });
+
+ // Install prerequisites
+ await $.spawn(
+ 'apt-get install -y ca-certificates curl gnupg lsb-release',
+ { requiresRoot: true, env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' } }
+ );
+
+ // Add Docker's official GPG key
+ await $.spawn(
+ 'install -m 0755 -d /etc/apt/keyrings',
+ { requiresRoot: true }
+ );
+ await $.spawn(
+ 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg',
+ { requiresRoot: true }
+ );
+ await $.spawn(
+ 'chmod a+r /etc/apt/keyrings/docker.gpg',
+ { requiresRoot: true }
+ );
+
+ // Set up the repository
+ const archResult = await $.spawn('dpkg --print-architecture');
+ const arch = archResult.data.trim();
+ const distro = await this.getDebianDistro($);
+
+ await $.spawn(
+ `echo "deb [arch=${arch} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${distro} $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null`,
+ { requiresRoot: true }
+ );
+
+ // Install Docker Engine
+ await $.spawn('apt-get update', { requiresRoot: true });
+ await $.spawn(
+ 'apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin',
+ { requiresRoot: true, env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' } }
+ );
+
+ // Start and enable Docker service
+ await $.spawn('systemctl start docker', { requiresRoot: true });
+ await $.spawn('systemctl enable docker', { requiresRoot: true });
+
+ console.log('Docker installed successfully on Debian-based system');
+ }
+
+ private async installDockerRedHat($: ReturnType): Promise {
+ console.log('Installing Docker on RedHat-based system...');
+
+ // Install prerequisites
+ await $.spawn(
+ 'yum install -y yum-utils',
+ { requiresRoot: true }
+ );
+
+ // Add Docker repository
+ await $.spawn(
+ 'yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo',
+ { requiresRoot: true }
+ );
+
+ // Install Docker Engine
+ await $.spawn(
+ 'yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin',
+ { requiresRoot: true }
+ );
+
+ // Start and enable Docker service
+ await $.spawn('systemctl start docker', { requiresRoot: true });
+ await $.spawn('systemctl enable docker', { requiresRoot: true });
+
+ console.log('Docker installed successfully on RedHat-based system');
+ }
+
+ private async getDebianDistro($: ReturnType): Promise {
+ // Check if it's Ubuntu or Debian
+ const result = await $.spawnSafe('lsb_release -is');
+ if (result.status === SpawnStatus.SUCCESS) {
+ const distro = result.data.trim().toLowerCase();
+ if (distro === 'ubuntu') {
+ return 'ubuntu';
+ }
+
+ if (distro === 'debian') {
+ return 'debian';
+ }
}
- await codifySpawn('xattr -r -d com.apple.quarantine /Applications/Docker.app', { requiresRoot: true });
- await FileUtils.addPathToZshrc('/Applications/Docker.app/Contents/Resources/bin', false);
+ // Default to ubuntu if we can't determine
+ return 'ubuntu';
}
- async destroy(plan: DestroyPlan): Promise {
- await codifySpawn('/Applications/Docker.app/Contents/MacOS/uninstall', { throws: false })
- await fs.rm(path.join(os.homedir(), 'Library/Group\\ Containers/group.com.docker'), { recursive: true, force: true });
- await fs.rm(path.join(os.homedir(), 'Library/Containers/com.docker.docker/Data'), { recursive: true, force: true });
- await fs.rm(path.join(os.homedir(), '.docker'), { recursive: true, force: true });
- await codifySpawn('rm -rf /Applications/Docker.app', { requiresRoot: true })
+ private async uninstallDockerDebian($: ReturnType): Promise {
+ console.log('Uninstalling Docker from Debian-based system...');
+
+ // Stop Docker service
+ await $.spawnSafe('systemctl stop docker', { requiresRoot: true });
+ await $.spawnSafe('systemctl stop docker.socket', { requiresRoot: true });
+
+ // Remove Docker packages
+ await $.spawn(
+ 'apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin',
+ { requiresRoot: true }
+ );
+
+ // Remove Docker repository and GPG key
+ await $.spawnSafe('rm -f /etc/apt/sources.list.d/docker.list', { requiresRoot: true });
+ await $.spawnSafe('rm -f /etc/apt/keyrings/docker.gpg', { requiresRoot: true });
+
+ // Remove Docker data directories
+ await $.spawnSafe('rm -rf /var/lib/docker', { requiresRoot: true });
+ await $.spawnSafe('rm -rf /var/lib/containerd', { requiresRoot: true });
+
+ // Clean up unused packages
+ await $.spawnSafe('apt-get autoremove -y', { requiresRoot: true });
+
+ console.log('Docker uninstalled successfully from Debian-based system');
+ }
+
+ private async uninstallDockerRedHat($: ReturnType): Promise {
+ console.log('Uninstalling Docker from RedHat-based system...');
+
+ // Stop Docker service
+ await $.spawnSafe('systemctl stop docker', { requiresRoot: true });
+ await $.spawnSafe('systemctl stop docker.socket', { requiresRoot: true });
+
+ // Remove Docker packages
+ await $.spawn(
+ 'yum remove -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin',
+ { requiresRoot: true }
+ );
+
+ // Remove Docker repository
+ await $.spawnSafe('rm -f /etc/yum.repos.d/docker-ce.repo', { requiresRoot: true });
+
+ // Remove Docker data directories
+ await $.spawnSafe('rm -rf /var/lib/docker', { requiresRoot: true });
+ await $.spawnSafe('rm -rf /var/lib/containerd', { requiresRoot: true });
- await FileUtils.removeLineFromZshrc('/Applications/Docker.app/Contents/Resources/bin')
+ console.log('Docker uninstalled successfully from RedHat-based system');
}
}
diff --git a/src/resources/file/file-schema.json b/src/resources/file/file-schema.json
deleted file mode 100644
index 5257695..0000000
--- a/src/resources/file/file-schema.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "https://www.codifycli.com/file.json",
- "$comment": "https://docs.codifycli.com/core-resources/files/file/",
- "title": "File resource",
- "description": "Manages a file.",
- "type": "object",
- "properties": {
- "path": {
- "type": "string",
- "description": "The location of the file."
- },
- "contents": {
- "type": "string",
- "description": "The contents of the file."
- },
- "onlyCreate": {
- "type": "boolean",
- "description": "Forces the resource to only create the file if it doesn't exist but don't detect any content changes."
- }
- },
- "required": ["path", "contents"],
- "additionalProperties": false
-}
diff --git a/src/resources/file/file.ts b/src/resources/file/file.ts
index ea89435..3039f63 100644
--- a/src/resources/file/file.ts
+++ b/src/resources/file/file.ts
@@ -1,21 +1,28 @@
-import { CreatePlan, DestroyPlan, ModifyPlan, ParameterChange, Resource, ResourceSettings } from 'codify-plugin-lib';
-import { StringIndexedObject } from 'codify-schemas';
+import { CreatePlan, DestroyPlan, ModifyPlan, ParameterChange, Resource, ResourceSettings, z } from '@codifycli/plugin-core';
+import { OS } from 'codify-schemas';
import fs from 'node:fs/promises';
import path from 'node:path';
import { FileUtils } from '../../utils/file-utils.js';
-import schema from './file-schema.json'
-export interface FileConfig extends StringIndexedObject {
- path: string;
- contents: string;
- onlyCreate: boolean;
-}
+const schema = z.object({
+ path: z.string().describe('The location of the file.'),
+ contents: z.string().describe('The contents of the file.'),
+ onlyCreate: z
+ .boolean()
+ .describe(
+ 'Forces the resource to only create the file if it doesn\'t exist but don\'t detect any content changes.'
+ )
+ .optional()
+ })
+
+type FileConfig = z.infer;
export class FileResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'file',
+ operatingSystems: [OS.Darwin, OS.Linux],
schema,
parameterSettings: {
path: { type: 'directory' },
diff --git a/src/resources/file/remote-file.ts b/src/resources/file/remote-file.ts
index a1eecc4..5a896b8 100644
--- a/src/resources/file/remote-file.ts
+++ b/src/resources/file/remote-file.ts
@@ -7,8 +7,8 @@ import {
RefreshContext,
Resource,
ResourceSettings
-} from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+} from '@codifycli/plugin-core';
+import { OS, ResourceConfig } from 'codify-schemas';
import { createHash } from 'node:crypto';
import * as fsSync from 'node:fs';
import fs from 'node:fs/promises';
@@ -31,6 +31,7 @@ export class RemoteFileResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'remote-file',
+ operatingSystems: [OS.Darwin, OS.Linux],
allowMultiple: true,
schema,
parameterSettings: {
diff --git a/src/resources/git/git/git-email-paramater.ts b/src/resources/git/git/git-email-paramater.ts
index e1bb423..0ccdcdc 100644
--- a/src/resources/git/git/git-email-paramater.ts
+++ b/src/resources/git/git/git-email-paramater.ts
@@ -1,6 +1,5 @@
-import { getPty, StatefulParameter } from 'codify-plugin-lib';
+import { getPty, StatefulParameter, SpawnStatus } from '@codifycli/plugin-core';
-import { SpawnStatus, codifySpawn } from '../../../utils/codify-spawn.js';
import { GitConfig } from './git-resource.js';
export class GitEmailParameter extends StatefulParameter {
@@ -13,14 +12,17 @@ export class GitEmailParameter extends StatefulParameter {
}
async add(valueToAdd: string): Promise {
- await codifySpawn(`git config --global user.email "${valueToAdd}"`)
+ const $ = getPty();
+ await $.spawn(`git config --global user.email "${valueToAdd}"`)
}
async modify(newValue: string): Promise {
- await codifySpawn(`git config --global user.email "${newValue}"`)
+ const $ = getPty();
+ await $.spawn(`git config --global user.email "${newValue}"`)
}
async remove(): Promise {
- await codifySpawn('git config --global --unset user.email')
+ const $ = getPty();
+ await $.spawn('git config --global --unset user.email')
}
}
diff --git a/src/resources/git/git/git-name-parameter.ts b/src/resources/git/git/git-name-parameter.ts
index fb42317..ae67aef 100644
--- a/src/resources/git/git/git-name-parameter.ts
+++ b/src/resources/git/git/git-name-parameter.ts
@@ -1,6 +1,5 @@
-import { getPty, StatefulParameter } from 'codify-plugin-lib';
+import { getPty, StatefulParameter, SpawnStatus } from '@codifycli/plugin-core';
-import { SpawnStatus, codifySpawn } from '../../../utils/codify-spawn.js';
import { GitConfig } from './git-resource.js';
export class GitNameParameter extends StatefulParameter {
@@ -13,14 +12,17 @@ export class GitNameParameter extends StatefulParameter {
}
async add(valueToAdd: string): Promise {
- await codifySpawn(`git config --global user.name "${valueToAdd}"`)
+ const $ = getPty();
+ await $.spawn(`git config --global user.name "${valueToAdd}"`)
}
async modify(newValue: string): Promise {
- await codifySpawn(`git config --global user.name "${newValue}"`)
+ const $ = getPty();
+ await $.spawn(`git config --global user.name "${newValue}"`)
}
async remove(): Promise {
- await codifySpawn('git config --global --unset user.name')
+ const $ = getPty();
+ await $.spawn('git config --global --unset user.name')
}
}
diff --git a/src/resources/git/git/git-resource.ts b/src/resources/git/git/git-resource.ts
index 8b4a34a..d5e71ab 100644
--- a/src/resources/git/git/git-resource.ts
+++ b/src/resources/git/git/git-resource.ts
@@ -1,7 +1,6 @@
-import { getPty, Resource, ResourceSettings } from 'codify-plugin-lib';
-import { StringIndexedObject } from 'codify-schemas';
+import { Resource, ResourceSettings, SpawnStatus, Utils, getPty } from '@codifycli/plugin-core';
+import { OS, StringIndexedObject } from 'codify-schemas';
-import { SpawnStatus, codifySpawn } from '../../../utils/codify-spawn.js';
import { GitEmailParameter } from './git-email-paramater.js';
import { GitNameParameter } from './git-name-parameter.js';
import Schema from './git-schema.json';
@@ -16,6 +15,7 @@ export class GitResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'git',
+ operatingSystems: [OS.Darwin, OS.Linux],
schema: Schema,
removeStatefulParametersBeforeDestroy: true,
parameterSettings: {
@@ -33,10 +33,10 @@ export class GitResource extends Resource {
}
async create(): Promise {
- // Git should always be installed with xcode tools. Nothing to do here.
+ await Utils.installViaPkgMgr('git');
}
async destroy(): Promise {
- // Don't uninstall git. It will break things.
+ await Utils.uninstallViaPkgMgr('git');
}
}
diff --git a/src/resources/git/lfs/git-lfs.ts b/src/resources/git/lfs/git-lfs.ts
index b1b196f..7fdad7e 100644
--- a/src/resources/git/lfs/git-lfs.ts
+++ b/src/resources/git/lfs/git-lfs.ts
@@ -1,8 +1,7 @@
-import { getPty, Resource, ResourceSettings, SpawnStatus } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+import { getPty, Resource, ResourceSettings, SpawnStatus, Utils } from '@codifycli/plugin-core';
+import { OS, ResourceConfig } from 'codify-schemas';
import * as os from 'node:os';
-import { codifySpawn } from '../../../utils/codify-spawn.js';
import Schema from './git-lfs-schema.json';
export interface GitLfsConfig extends ResourceConfig {
@@ -13,6 +12,7 @@ export class GitLfsResource extends Resource {
getSettings(): ResourceSettings {
return {
id: 'git-lfs',
+ operatingSystems: [OS.Darwin],
schema: Schema,
dependencies: ['homebrew'],
}
@@ -36,27 +36,23 @@ export class GitLfsResource extends Resource {
// FYI: This create might be called if git-lfs is installed but not initialized.
override async create(): Promise {
- await this.assertBrewInstalled();
-
- const gitLfsCheck = await codifySpawn('git lfs', { throws: false });
- if (gitLfsCheck.status === SpawnStatus.ERROR) {
- await codifySpawn('brew install git-lfs');
- }
+ const $ = getPty();
+ await Utils.installViaPkgMgr('git-lfs');
- await codifySpawn('git lfs install', { cwd: os.homedir() });
+ await $.spawn('git lfs install', { cwd: os.homedir(), interactive: true });
}
override async destroy(): Promise {
- await this.assertBrewInstalled();
+ const $ = getPty();
+ await $.spawn('git lfs uninstall', { cwd: os.homedir(), interactive: true });
- await codifySpawn('git lfs uninstall', { cwd: os.homedir() });
- await codifySpawn('brew uninstall git-lfs');
+ await Utils.uninstallViaPkgMgr('git-lfs');
}
private async checkIfGitLfsIsInstalled(): Promise {
const $ = getPty();
- const gitLfsStatus = await $.spawn('git lfs env', { cwd: os.homedir() });
+ const gitLfsStatus = await $.spawn('git lfs env', { cwd: os.homedir(), interactive: true, disableWrapping: true });
const lines = gitLfsStatus.data.split('\n');
// When git lfs exists but git lfs install hasn't been called then git lfs env returns:
@@ -69,18 +65,4 @@ export class GitLfsResource extends Resource {
return !emptyLfsLines;
}
-
- private async assertBrewInstalled(): Promise {
- const brewCheck = await codifySpawn('which brew', { throws: false });
- if (brewCheck.status === SpawnStatus.ERROR) {
- throw new Error(
- `Homebrew is not installed. Cannot install git-lfs without Homebrew installed.
-
-Brew can be installed using Codify:
-{
- "type": "homebrew",
-}`
- );
- }
- }
}
diff --git a/src/resources/git/repository/git-repository-schema.json b/src/resources/git/repository/git-repository-schema.json
index 9a59b94..e5c0b70 100644
--- a/src/resources/git/repository/git-repository-schema.json
+++ b/src/resources/git/repository/git-repository-schema.json
@@ -10,6 +10,13 @@
"type": "string",
"description": "Remote repository to clone repo from."
},
+ "repositories": {
+ "type": "array",
+ "description": "Remote repositories to clone. This is a convenience property for cloning multiple repositories at once.",
+ "items": {
+ "type": "string"
+ }
+ },
"parentDirectory": {
"type": "string",
"description": "Parent directory to clone into. The folder name will use default git semantics which extracts the last part of the clone url. Only one of parentDirectory or directory can be specified"
@@ -26,6 +33,6 @@
"additionalProperties": false,
"oneOf": [
{ "required": ["repository", "directory"] },
- { "required": ["repository", "parentDirectory"] }
+ { "required": ["repositories", "parentDirectory"] }
]
}
diff --git a/src/resources/git/repository/git-repository.ts b/src/resources/git/repository/git-repository.ts
index 8356ebd..df7da78 100644
--- a/src/resources/git/repository/git-repository.ts
+++ b/src/resources/git/repository/git-repository.ts
@@ -1,57 +1,78 @@
-import { CreatePlan, DestroyPlan, Resource, ResourceSettings, getPty } from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+import { CreatePlan, DestroyPlan, Resource, ResourceSettings, getPty } from '@codifycli/plugin-core';
+import { OS, ResourceConfig } from 'codify-schemas';
import path from 'node:path';
-import { codifySpawn } from '../../../utils/codify-spawn.js';
import { FileUtils } from '../../../utils/file-utils.js';
import Schema from './git-repository-schema.json';
-export interface GitCloneConfig extends ResourceConfig {
+export interface GitRepositoryConfig extends ResourceConfig {
autoVerifySSH: boolean
directory?: string,
parentDirectory?: string,
+ repositories?: string[],
repository: string,
}
-export class GitCloneResource extends Resource {
- getSettings(): ResourceSettings {
+export class GitRepositoryResource extends Resource {
+ getSettings(): ResourceSettings {
return {
id: 'git-repository',
+ operatingSystems: [OS.Darwin, OS.Linux],
schema: Schema,
parameterSettings: {
+ repositories: { type: 'array' },
parentDirectory: { type: 'directory' },
directory: { type: 'directory' },
autoVerifySSH: { type: 'boolean', default: true, setting: true },
},
- importAndDestroy:{
+ importAndDestroy: {
requiredParameters: ['directory']
},
allowMultiple: {
matcher: (desired, current) => {
const desiredPath = desired.parentDirectory
- ? path.resolve(desired.parentDirectory, this.extractBasename(desired.repository!)!)
+ ? desired.repositories?.map((r) => path.resolve(desired.parentDirectory!, this.extractBasename(r)!))
: path.resolve(desired.directory!);
const currentPath = current.parentDirectory
- ? path.resolve(current.parentDirectory, this.extractBasename(current.repository!)!)
+ ? current.repositories?.map((r) => path.resolve(current.parentDirectory!, this.extractBasename(r)!))
: path.resolve(current.directory!);
const isNotCaseSensitive = process.platform === 'darwin';
if (isNotCaseSensitive) {
- return desiredPath.toLowerCase() === currentPath.toLowerCase()
+ if (!Array.isArray(desiredPath) && !Array.isArray(currentPath)) {
+ return desiredPath!.toLowerCase() === currentPath!.toLowerCase()
+ }
+
+ if (Array.isArray(desiredPath) && Array.isArray(currentPath)) {
+ const currentLowered = new Set(currentPath.map((c) => c.toLowerCase()))
+ return desiredPath.some((d) => currentLowered.has(d.toLowerCase()))
+ }
}
-
+
+ if (Array.isArray(desiredPath) && Array.isArray(currentPath)) {
+ return desiredPath.some((d) => currentPath.includes(d))
+ }
+
return desiredPath === currentPath;
},
- findAllParameters: async () => {
+ async findAllParameters() {
const $ = getPty();
const { data } = await $.spawnSafe('find ~ -type d \\( -path $HOME/Library -o -path $HOME/Pictures -o -path $HOME/Utilities -o -path "$HOME/.*" \\) -prune -o -name .git -print')
- return data
+ const directories = data
?.split(/\n/)?.filter(Boolean)
?.map((p) => path.dirname(p))
?.map((directory) => ({ directory }))
?? [];
+
+ const groupedDirectories = Object.groupBy(directories, (d) => path.dirname(d.directory));
+ const multipleRepositories = Object.entries(groupedDirectories).filter(([_, dirs]) => (dirs?.length ?? 0) > 1)
+ .map(([parent]) => ({ parentDirectory: parent }))
+ const singleRepositories = Object.entries(groupedDirectories).filter(([_, dirs]) => (dirs?.length ?? 0) === 1)
+ .map(([directory]) => ({ directory }))
+
+ return [...multipleRepositories, ...singleRepositories];
}
},
dependencies: [
@@ -63,27 +84,41 @@ export class GitCloneResource extends Resource {
}
}
- override async refresh(parameters: Partial): Promise | null> {
+ override async refresh(parameters: Partial): Promise | null> {
const $ = getPty();
if (parameters.parentDirectory) {
- const folderName = this.extractBasename(parameters.repository!);
- if (!folderName) {
- throw new Error('Invalid git repository or remote name. Un-able to parse');
+ // Check if parent directory exists
+ const parentExists = await FileUtils.checkDirExistsOrThrowIfFile(parameters.parentDirectory);
+ if (!parentExists) {
+ return null;
}
- const fullPath = path.join(parameters.parentDirectory, folderName);
+ // Find all git repositories in the parent directory
+ const { data } = await $.spawnSafe(`find "${parameters.parentDirectory}" -maxdepth 2 -type d -name .git`, { cwd: parameters.parentDirectory });
- const exists = await FileUtils.checkDirExistsOrThrowIfFile(fullPath);
- if (!exists) {
+ const gitDirs = data?.split(/\n/)?.filter(Boolean) ?? [];
+ if (gitDirs.length === 0) {
return null;
}
- const { data: url } = await $.spawn('git config --get remote.origin.url', { cwd: fullPath });
+ // Get repository URLs for all found git directories
+ const repositories: string[] = [];
+ for (const gitDir of gitDirs) {
+ const repoPath = path.dirname(gitDir);
+ const { data: url } = await $.spawnSafe('git config --get remote.origin.url', { cwd: repoPath });
+ if (url && url.trim()) {
+ repositories.push(url.trim());
+ }
+ }
+
+ if (repositories.length === 0) {
+ return null;
+ }
return {
parentDirectory: parameters.parentDirectory,
- repository: url.trim(),
+ repositories,
autoVerifySSH: parameters.autoVerifySSH,
}
}
@@ -107,28 +142,34 @@ export class GitCloneResource extends Resource {
}
- override async create(plan: CreatePlan): Promise {
+ override async create(plan: CreatePlan): Promise {
+ const $ = getPty();
const config = plan.desiredConfig;
- if (plan.desiredConfig.autoVerifySSH) {
- await this.autoVerifySSHForFirstAttempt(config.repository)
- }
-
if (config.parentDirectory) {
const parentDirectory = path.resolve(config.parentDirectory);
await FileUtils.createDirIfNotExists(parentDirectory);
- await codifySpawn(`git clone ${config.repository}`, { cwd: parentDirectory });
+
+ // Clone all repositories in the list
+ const repositories = (config as any).repositories || [config.repository];
+ for (const repository of repositories) {
+ if (plan.desiredConfig.autoVerifySSH) {
+ await this.autoVerifySSHForFirstAttempt(repository);
+ }
+
+ await $.spawn(`git clone ${repository}`, { cwd: parentDirectory });
+ }
} else {
const directory = path.resolve(config.directory!);
- await codifySpawn(`git clone ${config.repository} ${directory}`);
+ await $.spawn(`git clone ${config.repository} ${directory}`);
}
}
- override async destroy(plan: DestroyPlan): Promise {
+ override async destroy(plan: DestroyPlan): Promise {
// Do nothing here. We don't want to destroy a user's repository.
// TODO: change this to skip the destroy only if the user's repo has pending changes (check via git)
- throw new Error(`The git-clone resource is not designed to delete folders.
-Please delete ${plan.currentConfig.directory ?? (plan.currentConfig.parentDirectory! + this.extractBasename(plan.currentConfig.repository))} manually and re-apply`);
+ throw new Error(`The git-clone resource is not designed to delete folders.
+Please delete ${plan.currentConfig.directory ?? (plan.currentConfig.repositories?.map((r) => path.resolve(plan.currentConfig.parentDirectory!, this.extractBasename(r)!)).join(', '))} manually and re-apply`);
}
// Converts https://github.com/kevinwang5658/codify-homebrew-plugin.git => codify-homebrew-plugin
@@ -142,6 +183,8 @@ Please delete ${plan.currentConfig.directory ?? (plan.currentConfig.parentDirect
}
private async autoVerifySSHForFirstAttempt(url: string): Promise {
+ const $ = getPty();
+
if (!(url.includes('@') || url.includes('ssh://'))) {
// Not an ssh url
return;
@@ -156,18 +199,18 @@ Please delete ${plan.currentConfig.directory ?? (plan.currentConfig.parentDirect
}
// Create known hosts file it doesn't exist
- await codifySpawn('touch ~/.ssh/known_hosts', { throws: false })
+ await $.spawnSafe('touch ~/.ssh/known_hosts')
const baseUrl = groups!.url!
- const { data: existingKey } = await codifySpawn(`ssh-keygen -F ${baseUrl}`, { throws: false })
- console.log(`Is key blank: ${this.isBlank(existingKey)}`)
+ const { data: existingKey } = await $.spawnSafe(`ssh-keygen -F ${baseUrl}`)
+
if (!this.isBlank(existingKey)) {
// An existing key is already in the file. Skipping..
return;
}
// TODO: Add fingerprint verification here
- await codifySpawn(`ssh-keyscan ${baseUrl} >> ~/.ssh/known_hosts `)
+ await $.spawn(`ssh-keyscan ${baseUrl} >> ~/.ssh/known_hosts `)
}
isBlank(str: string): boolean {
diff --git a/src/resources/git/wait-github-ssh-key/wait-github-ssh-key.ts b/src/resources/git/wait-github-ssh-key/wait-github-ssh-key.ts
index 4931be5..ca61264 100644
--- a/src/resources/git/wait-github-ssh-key/wait-github-ssh-key.ts
+++ b/src/resources/git/wait-github-ssh-key/wait-github-ssh-key.ts
@@ -4,20 +4,19 @@ import {
Resource,
ResourceSettings,
getPty
-} from 'codify-plugin-lib';
-import { ResourceConfig } from 'codify-schemas';
+} from '@codifycli/plugin-core';
+import { OS, ResourceConfig } from 'codify-schemas';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
-import { codifySpawn } from '../../../utils/codify-spawn.js';
-
export interface WaitGithubSshKeyConfig extends ResourceConfig {}
export class WaitGithubSshKey extends Resource {
getSettings(): ResourceSettings {
return {
id: 'wait-github-ssh-key',
+ operatingSystems: [OS.Darwin, OS.Linux],
dependencies: [
'ssh-key',
'ssh-add',
@@ -84,7 +83,8 @@ For additional information visit: ${chalk.underline('https://docs.github.com/en/
}
private async isConnectedToGithub(): Promise {
- const { data: githubConnectionStatus } = await codifySpawn('ssh -o StrictHostKeychecking=no -T git@github.com 2>&1', { throws: false })
+ const $ = getPty();
+ const { data: githubConnectionStatus } = await $.spawnSafe('ssh -o StrictHostKeychecking=no -T git@github.com 2>&1')
return githubConnectionStatus.includes('You\'ve successfully authenticated, but GitHub does not provide shell access.')
}
}
diff --git a/src/resources/homebrew/casks-parameter.ts b/src/resources/homebrew/casks-parameter.ts
index 08c7868..2e53d9c 100644
--- a/src/resources/homebrew/casks-parameter.ts
+++ b/src/resources/homebrew/casks-parameter.ts
@@ -1,12 +1,9 @@
-import { getPty, ParameterSetting, Plan, SpawnStatus, StatefulParameter } from 'codify-plugin-lib';
+import { ParameterSetting, Plan, SpawnStatus, StatefulParameter, Utils, getPty } from '@codifycli/plugin-core';
import path from 'node:path';
-import { codifySpawn } from '../../utils/codify-spawn.js';
import { FileUtils } from '../../utils/file-utils.js';
import { HomebrewConfig } from './homebrew.js';
-const SUDO_ASKPASS_PATH = '~/Library/Caches/codify/homebrew/sudo_prompt.sh'
-
export class CasksParameter extends StatefulParameter {
override getSettings(): ParameterSetting {
@@ -45,7 +42,7 @@ export class CasksParameter extends StatefulParameter
// This serves a secondary purpose as well. It checks that each cask to install is valid (alerting the user
// in the plan instead of in the apply)
- const casksWithConflicts = await this.findConflicts(notInstalledCasks);
+ const casksWithConflicts = await this.findConflictsOnMacOs(notInstalledCasks);
if (casksWithConflicts.length > 0) {
// To avoid errors, we pretend that those programs were already installed by homebrew (even though it was installed outside)
if (config?.skipAlreadyInstalledCasks) {
@@ -84,7 +81,8 @@ export class CasksParameter extends StatefulParameter
return;
}
- const conflicts = await this.findConflicts(casks);
+ const $ = getPty();
+ const conflicts = await this.findConflictsOnMacOs(casks);
const casksToInstall = casks.filter((c) => !conflicts.includes(c))
if (conflicts.length > 0) {
@@ -99,7 +97,11 @@ export class CasksParameter extends StatefulParameter
return;
}
- const result = await codifySpawn(`HOMEBREW_NO_AUTO_UPDATE=1 SUDO_ASKPASS=${SUDO_ASKPASS_PATH} brew install --casks ${casksToInstall.join(' ')}`, { throws: false })
+ const result = await $.spawnSafe(`brew install --casks ${casksToInstall.join(' ')}`, {
+ interactive: true,
+ env: { HOMEBREW_NO_AUTO_UPDATE: 1 }
+ })
+
if (result.status === SpawnStatus.SUCCESS) {
// Casks can't detect if a program was installed by other means. If it returns this message, throw an error
if (result.data.includes('It seems there is already an App at')) {
@@ -117,7 +119,11 @@ export class CasksParameter extends StatefulParameter
return;
}
- const result = await codifySpawn(`SUDO_ASKPASS=${SUDO_ASKPASS_PATH} brew uninstall ${casks.join(' ')}`, { throws: false })
+ const $ = getPty();
+ const result = await $.spawnSafe(`brew uninstall ${casks.join(' ')}`, {
+ interactive: true,
+ env: { HOMEBREW_NO_AUTO_UPDATE: 1 }
+ })
if (result.status === SpawnStatus.SUCCESS) {
console.log(`Uninstalled casks: ${casks.join(' ')}`);
@@ -126,16 +132,22 @@ export class CasksParameter extends StatefulParameter
}
}
- private async findConflicts(casks: string[]): Promise {
- const $ = getPty();
-
- let result: string;
- if ($) {
- result = (await $.spawnSafe(`brew info -q --json=v2 ${casks.map((c) => `"${c}"`).join(' ')}`)).data.replaceAll('\n', '')
- } else {
- result = (await codifySpawn(`brew info -q --json=v2 ${casks.map((c) => `"${c}"`).join(' ')}`)).data.replaceAll('\n', '')
+ private async findConflictsOnMacOs(casks: string[]): Promise {
+ if (!Utils.isMacOS()) {
+ return [];
}
+
+ const $ = getPty();
+ const result = (await $.spawn(
+ `brew info -q --json=v2 ${casks.map((c) => `"${c}"`).join(' ')}`, {
+ interactive: true,
+ env: { HOMEBREW_NO_AUTO_UPDATE: 1 }
+ })
+ )
+ .data
+ .replaceAll('\n', '')
+
const brewInfo = JSON.parse(result);
const casksWithConflicts = new Array();
diff --git a/src/resources/homebrew/formulae-parameter.ts b/src/resources/homebrew/formulae-parameter.ts
index 018f04a..46a29c5 100644
--- a/src/resources/homebrew/formulae-parameter.ts
+++ b/src/resources/homebrew/formulae-parameter.ts
@@ -1,10 +1,7 @@
-import { ArrayParameterSetting, getPty, SpawnStatus, StatefulParameter } from 'codify-plugin-lib';
+import { ArrayParameterSetting, SpawnStatus, StatefulParameter, getPty } from '@codifycli/plugin-core';
-import { codifySpawn } from '../../utils/codify-spawn.js';
import { HomebrewConfig } from './homebrew.js';
-const SUDO_ASKPASS_PATH = '~/Library/Caches/codify/homebrew/sudo_prompt.sh'
-
export class FormulaeParameter extends StatefulParameter {
getSettings(): ArrayParameterSetting {
return {
@@ -59,12 +56,16 @@ export class FormulaeParameter extends StatefulParameter {
override getSettings(): ResourceSettings {
return {
schema: HomebrewSchema,
+ operatingSystems: [OS.Darwin, OS.Linux],
id: 'homebrew',
parameterSettings: {
taps: { type: 'stateful', definition: new TapsParameter(), order: 1 },
@@ -58,54 +61,65 @@ export class HomebrewResource extends Resource {
}
override async create(plan: CreatePlan): Promise {
- await this.saveSudoAskpassIfNotExists();
+ const $ = getPty();
if (plan.desiredConfig.directory) {
return this.installBrewInCustomDir(plan.desiredConfig.directory)
}
- await codifySpawn(`SUDO_ASKPASS=${SUDO_ASKPASS_PATH} NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`, { requestsTTY: true })
- await codifySpawn('(echo; echo \'eval "$(/opt/homebrew/bin/brew shellenv)"\') >> /Users/$USER/.zshrc'); // TODO: may need to support non zsh shells here
+ await $.spawn(
+ '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
+ { stdin: true, env: { NONINTERACTIVE: 1 } }
+ )
+
+ const brewPath = Utils.isLinux() ? '/home/linuxbrew/.linuxbrew/bin/brew' : '/opt/homebrew/bin/brew';
+ await FileUtils.addToShellRc(`eval "$(${brewPath} shellenv)"`);
// TODO: Add a check here to see if homebrew is writable
// Either add a warning or a parameter to edit the permissions on /opt/homebrew
}
override async destroy(): Promise {
- const homebrewInfo = await codifySpawn('brew config');
+ const $ = getPty();
+ const homebrewInfo = await $.spawn('brew config', { interactive: true });
const homebrewDirectory = this.getCurrentLocation(homebrewInfo.data)
if (homebrewDirectory === '/opt/homebrew') {
- await codifySpawn(
- `SUDO_ASKPASS=${SUDO_ASKPASS_PATH} NONINTERACTIVE=1 /bin/bash -c "$(/usr/bin/curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)"`,
- { throws: false, requestsTTY: true }
+ await $.spawnSafe(
+ '/bin/bash -c "$(/usr/bin/curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)"',
+ { stdin: true, env: { NONINTERACTIVE: 1 } }
)
+ } else {
+ await $.spawn(`rm -rf ${homebrewDirectory}`, { requiresRoot: true });
}
- await codifySpawn(`sudo rm -rf ${homebrewDirectory}`);
-
// Delete eval from .zshrc
- await FileUtils.removeLineFromZshrc(`eval "$(${homebrewDirectory}/bin/brew shellenv)"`)
+ await FileUtils.removeLineFromShellRc(`eval "$(${homebrewDirectory}/bin/brew shellenv)"`)
}
private async installBrewInCustomDir(dir: string): Promise {
+ const $ = getPty();
const absoluteDir = path.resolve(untildify(dir))
try {
await fs.access(absoluteDir)
} catch {
- await codifySpawn(`mkdir ${absoluteDir}`)
+ await fs.mkdir(absoluteDir, { recursive: true });
}
// Un-tar brew in a custom dir: https://github.com/Homebrew/brew/blob/664d0c67d5947605c914c4c56ebcfaa80cb6eca0/docs/Installation.md#untar-anywhere
// the location where brew is first activated is where it will be installed
- await codifySpawn('curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1', { cwd: absoluteDir })
+ await $.spawn('curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1', { cwd: absoluteDir })
// Activate brew to install it in the directory
- await codifySpawn(`SUDO_ASKPASS=${SUDO_ASKPASS_PATH} NONINTERACTIVE=1 ./brew config`, { cwd: path.join(absoluteDir, '/bin') })
+ await $.spawn(
+ './brew config', {
+ cwd: path.join(absoluteDir, '/bin'),
+ interactive: true
+ })
// Update shell startup scripts
- await codifySpawn(`(echo; echo 'eval "$(${absoluteDir}/bin/brew shellenv)"') >> /Users/$USER/.zshrc`);
+ await FileUtils.addToShellRc(`eval "$(${absoluteDir}/bin/brew shellenv)"`);
}
// Ex:
@@ -120,21 +134,4 @@ export class HomebrewResource extends Resource {
return homebrewPrefix.split(':')[1].trim()
}
-
- private async saveSudoAskpassIfNotExists(): Promise {
- const fullPath = untildify(SUDO_ASKPASS_PATH);
- const exists = fsSync.existsSync(fullPath)
- if (exists) {
- return;
- }
-
- await mkdir(path.dirname(fullPath), { recursive: true });
- await codifySpawn(`cat << 'EOF' > ${SUDO_ASKPASS_PATH}
-#!/bin/bash
-osascript -e 'display dialog "Enter user password:" default answer "" with hidden answer with icon file "System:Library:Frameworks:SecurityInterface.framework:Versions:A:Resources:Lock_Locked State@2x.png" with title "sudo"'\\
-| grep 'text returned:' \\
-| sed 's/^.*text returned:\\(.*\\).*$/\\1/'
-EOF`);
- await codifySpawn(`chmod +x ${SUDO_ASKPASS_PATH}`);
- }
}
diff --git a/src/resources/homebrew/tap-parameter.ts b/src/resources/homebrew/tap-parameter.ts
index de80173..d6a2a2b 100644
--- a/src/resources/homebrew/tap-parameter.ts
+++ b/src/resources/homebrew/tap-parameter.ts
@@ -1,6 +1,5 @@
-import { getPty, ParameterSetting, SpawnStatus, StatefulParameter } from 'codify-plugin-lib';
+import { ParameterSetting, SpawnStatus, StatefulParameter, getPty } from '@codifycli/plugin-core';
-import { codifySpawn } from '../../utils/codify-spawn.js'
import { HomebrewConfig } from './homebrew.js';
export class TapsParameter extends StatefulParameter