From 54d1ee49d9bf9ec91a87b1566cff0999aba3f36d Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Oct 2025 16:38:52 +0100
Subject: [PATCH 1/2] feat(common-utils): add zz_dist script to manage zz_*
utilities distribution (#30)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Initial plan
* feat(common-utils): add zz_dist script to distribute zz_* utilities
Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com>
* fix(common-utils): update zz_dist to require target and verify directory exists
Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com>
* refactor(common-utils): simplify zz_dist by using zz_* utilities directly
Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com>
* fix: 🐛 improve target directory handling and logging
* fix: 🐛 follow suggestions
---
src/common-utils/README.md | 61 +++++++++++++++++++
src/common-utils/_zz_dist.sh | 111 ++++++++++++++++++++++++++++++++++
src/common-utils/package.json | 1 +
3 files changed, 173 insertions(+)
create mode 100755 src/common-utils/_zz_dist.sh
diff --git a/src/common-utils/README.md b/src/common-utils/README.md
index 602d308c..690808d6 100644
--- a/src/common-utils/README.md
+++ b/src/common-utils/README.md
@@ -68,6 +68,67 @@ In addition to the specified utilities, some additional local utilities are also
- `zz_log`: A utility to log messages with color
- `zz_args`: A utility to parse command line arguments and display associated help messages in one go.
+### Distribute zz_\* Utilities
+
+The `zz_dist` utility allows you to copy all `zz_*` utilities from the devcontainer-installed location to a target directory. This is useful for maintaining a local copy of utilities in your project.
+
+#### Usage
+
+```bash
+zz_dist [options]
+```
+
+#### Options
+
+| Option | Description |
+| ---------- | ----------------------------------------------------------------------------------- |
+| `-t
` | Target directory (required unless specified in config). |
+| `-s ` | Source directory (default: `/usr/local/share/common-utils`). |
+| `-q` | Quiet mode: exit silently (code 0) if no target found instead of generating error. |
+
+#### Configuration
+
+The target directory **must** be specified using one of these methods (checked in order):
+
+1. **Command-line option**: Use the `-t` parameter to explicitly specify the target directory.
+
+2. **`.zz_dist` file**: Create a `.zz_dist` file in your project root with the target directory path on the first line.
+
+```bash
+echo "./scripts" > .zz_dist
+```
+
+3. **`package.json`**: Add a `config.zz_dist` entry to your `package.json`:
+
+```json
+{
+ "config": {
+ "zz_dist": "./scripts"
+ }
+}
+```
+
+If no target is found and `-q` (quiet mode) is not specified, the script will exit with an error. The target directory must exist before running the script.
+
+#### Example
+
+```bash
+# Copy using config file
+echo "./scripts" > .zz_dist
+mkdir -p ./scripts
+zz_dist
+
+# Copy to specific directory (must exist)
+mkdir -p ./my-utils
+zz_dist -t ./my-utils
+
+# Copy from custom source to existing directory
+zz_dist -s /custom/path -t ./scripts
+
+# Quiet mode - no error if target not configured
+zz_dist -q
+```
+
### Validate JSON
The `validate-json` utility allows you to validate JSON files against a JSON schema. It supports the following features:
diff --git a/src/common-utils/_zz_dist.sh b/src/common-utils/_zz_dist.sh
new file mode 100755
index 00000000..72225e78
--- /dev/null
+++ b/src/common-utils/_zz_dist.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+
+set -e
+
+# Source colors script
+. zz_colors
+
+# Parse arguments and display help if needed
+eval $(
+ zz_args "Distribute zz_* utilities to target directory" $0 "$@" <<-help
+ t target target Target directory (required unless specified in config)
+ s source source Source directory (default: /usr/local/share/common-utils)
+ q - quiet Quiet mode: exit 0 if no target found instead of error
+ help
+)
+
+# Function to read target from config
+get_target_from_config() {
+ local config_target=""
+
+ # Check for .zz_dist file in project root
+ if [ -f ".zz_dist" ]; then
+ config_target=$(cat .zz_dist | head -n 1 | tr -d '\r\n ')
+ if [ -n "$config_target" ]; then
+ zz_log i "Found target in {U .zz_dist}: {B $config_target}"
+ echo "$config_target"
+ return 0
+ fi
+ fi
+
+ # Check for config.zz_dist in package.json using zz_json
+ if [ -f "package.json" ]; then
+ config_target=$(zz_json package.json 2>/dev/null | jq -r '.config.zz_dist // empty' 2>/dev/null)
+ if [ -n "$config_target" ]; then
+ zz_log i "Found target in {U package.json}: {B $config_target}"
+ echo "$config_target"
+ return 0
+ fi
+ fi
+}
+
+# Determine the target directory
+if [ -z "$target" ]; then
+ # Try to get from config (first .zz_dist file, then package.json)
+ target=$(get_target_from_config);
+ if [ -z "$target" ] && [ -n "$quiet" ]; then
+ # Quiet mode: exit 0 if no target found
+ zz_log w "No target directory specified. Exiting quietly."
+ exit 0
+ elif [ -z "$target" ]; then
+ # No target found: error out
+ zz_log e "No target directory specified. Use -t option, create .zz_dist file, or add config.zz_dist in package.json"
+ exit 1
+ fi
+fi
+
+# Resolve target to absolute path
+target=$(readlink -f "$target")
+
+# Verify target directory exists
+if [ ! -d "$target" ]; then
+ zz_log e "Target directory {U $target} does not exist"
+ exit 1
+fi
+
+zz_log s "Resolved target directory to {U $target}"
+
+# Set default source directory
+if [ -z "$source" ]; then
+ # Try common installation locations
+ if [ -d "/usr/local/share/common-utils" ]; then
+ source="/usr/local/share/common-utils"
+ elif [ -d "/usr/local/bin" ]; then
+ source="/usr/local/bin"
+ else
+ zz_log e "No source directory found. Expected /usr/local/share/common-utils or /usr/local/bin"
+ exit 1
+ fi
+fi
+
+zz_log i "Distributing zz_* utilities"
+zz_log - "From: {U $source}"
+zz_log - "To: {U $target}"
+
+
+# Copy zz_* files from source to target
+for file in $(ls -1 $source/*zz_*); do
+
+ zz_log - "Processing file: {U $file}"
+
+ # _zz_*.sh files are copied as zz_* files without leading underscore and .sh extension
+ if [ -f "$file" ] || [ -L "$file" ]; then
+ basefile=$(basename "$file")
+ if echo "$basefile" | grep -q '^_zz_.*\.sh$'; then
+ targetfile=$target/$(echo "$basefile" | sed -e 's/^_//;s/\.sh$//')
+ else
+ targetfile=$target/$basefile
+ fi
+
+ # Copy file if it exists and is executable
+ if [ -x "$file" ]; then
+ cp -u "$file" "$targetfile"
+ chmod +x "$targetfile"
+ zz_log s "Copied {U $basefile} to {U $targetfile}"
+ else
+ zz_log w "Skipping non-executable file {U $basefile}"
+ fi
+ fi
+done
+
+zz_log s "Successfully distributed utilities to {U $target}"
diff --git a/src/common-utils/package.json b/src/common-utils/package.json
index d2fb08f3..ab72896f 100644
--- a/src/common-utils/package.json
+++ b/src/common-utils/package.json
@@ -20,6 +20,7 @@
"zz-colors": "./_zz_colors.sh",
"zz-context": "./_zz_context.sh",
"zz-dispatch": "./_zz_dispatch.sh",
+ "zz-dist": "./_zz_dist.sh",
"zz-edit": "./_zz_edit.sh",
"zz-input": "./_zz_input.sh",
"zz-json": "./_zz_json.sh",
From 211b1abd3865ea67f3b2c5e86a46526d75d2c8a6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 4 Oct 2025 15:42:17 +0000
Subject: [PATCH 2/2] chore(deps): bump form-data from 4.0.0 to 4.0.4
Bumps [form-data](https://github.com/form-data/form-data) from 4.0.0 to 4.0.4.
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.0...v4.0.4)
---
updated-dependencies:
- dependency-name: form-data
dependency-version: 4.0.4
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
package-lock.json | 166 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 163 insertions(+), 3 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 2ff730a4..58192ed8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -815,6 +815,19 @@
"node": ">=6"
}
},
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2733,6 +2746,20 @@
"node": ">=4"
}
},
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2787,6 +2814,51 @@
"is-arrayish": "^0.2.1"
}
},
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -2964,13 +3036,15 @@
}
},
"node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -3028,6 +3102,30 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/get-pkg-repo": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz",
@@ -3125,6 +3223,19 @@
"node": ">=10"
}
},
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/get-stream": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
@@ -3481,6 +3592,18 @@
"which": "bin/which"
}
},
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3532,6 +3655,33 @@
"node": ">=4"
}
},
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -4592,6 +4742,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/meow": {
"version": "8.1.2",
"resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz",
@@ -6795,6 +6954,7 @@
"zz-colors": "_zz_colors.sh",
"zz-context": "_zz_context.sh",
"zz-dispatch": "_zz_dispatch.sh",
+ "zz-dist": "_zz_dist.sh",
"zz-edit": "_zz_edit.sh",
"zz-input": "_zz_input.sh",
"zz-json": "_zz_json.sh",