Skip to content

Decouple workspace install lifecycle from root npm install#63

Open
Copilot wants to merge 9 commits into
developfrom
copilot/manage-dependencies-installation
Open

Decouple workspace install lifecycle from root npm install#63
Copilot wants to merge 9 commits into
developfrom
copilot/manage-dependencies-installation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 24, 2026

This PR makes each workspace installable on its own while preserving root-level stub management. It aligns npm lifecycle behavior so install-time setup and post-install configuration run in the right scope without cascading unintended workspace execution.

  • Root lifecycle behavior

    • Added root install script in package.json to keep root stub deployment on npm install:
      • sh ./install.sh -s
    • Root installs continue to manage root stubs only.
  • Workspace-independent install/config lifecycle

    • Added install + postinstall scripts to workspace package.json files (common-utils, gitutils, githooks, gitversion, act, larasets, gateway, minikube).
    • Each lifecycle is guarded with INIT_CWD == PWD, so scripts run when the workspace is explicitly installed, not when npm traverses from root.
  • Common-utils bin/script execution behavior

    • Kept lifecycle/helper scripts using commands deployed to a PATH-available folder (zz_*, merge-json, normalize-json, etc.).
    • Updated bin installation to select a writable global/script path with fallback order:
      • /usr/local/bin~/.local/bin → first writable directory in PATH (excluding node_modules/.bin) → $target/bin
    • Bin-directory selection logic is externalized into _select-bin.sh using a candidate list + two-pass loop: first pass finds an existing writable+executable directory; second pass only creates known/safe directories, never PATH-sourced entries.
    • The selected bin directory is added to PATH for the current install session when needed, so installed common scripts remain executable during lifecycle execution.
  • Devcontainer-aware feature discovery in install.sh

    • Added devcontainer_features() helper to extract tomgrv/devcontainer-features entries from a given devcontainer.json.
    • Added find_devcontainer_features() to discover devcontainer files across standard VS Code locations in priority order: .devcontainer/devcontainer.jsondevcontainer.json.devcontainer.json.devcontainer/*/devcontainer.json.
    • The -a (all) and -u (update) modes now use find_devcontainer_features instead of hardcoded paths.
    • When no flags or explicit features are provided, install.sh automatically detects features to install from devcontainer files in the current directory.
  • Manifest-based transitive dependency resolution via dedicated helpers

    • Extracted dependency resolution into src/common-utils/_install-deps.sh: accepts <source_dir> and a feature list, reads each feature's devcontainer-feature.json, performs BFS to collect all transitive tomgrv/devcontainer-features dependencies, then outputs a topologically-sorted list (deps before dependents). Example: install-deps $source githookscommon-utils → gitversion → gitutils → githooks.
    • Extracted feature installation into src/common-utils/_install-feat.sh: installs a single feature and its dependencies recursively. Uses INSTALL_FEAT_TRACKER to prevent re-installation within the same session. Calls install.sh for each dependency (making install.sh the recursive orchestrator), then installs the feature itself — handling container, stubs, and no-container cases.
    • install.sh is the orchestrator: uses install-deps to expand the feature list and install-feat for each resolved feature. A INSTALL_ROOT_CALL guard ensures symlink setup/cleanup only runs at the outermost invocation, not in recursive calls.
{
  "scripts": {
    "install": "if [ \"$INIT_CWD\" = \"$PWD\" ]; then sh ./install.sh; fi",
    "postinstall": "if [ \"$INIT_CWD\" = \"$PWD\" ]; then configure-feature -s . gitutils; fi"
  }
}

[!WARNING]

Copilot AI linked an issue May 24, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Manage dependencies during workspace installation Decouple workspace install lifecycle from root npm install May 24, 2026
Copilot finished work on behalf of tomgrv May 24, 2026 18:33
Copilot AI requested a review from tomgrv May 24, 2026 18:33
Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During common-utils install, common scripts should be made available in path as they are installed in a bin folder. If not, find a fallback way to make them available globally.

Verify, confirm and update accordingly if needed.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 24, 2026

During common-utils install, common scripts should be made available in path as they are installed in a bin folder. If not, find a fallback way to make them available globally.

Verify, confirm and up...

Implemented in 655514c. common-utils install now resolves a writable bin destination with fallback order: /usr/local/bin~/.local/bin → first writable PATH directory (excluding node_modules/.bin) → $target/bin, and adds the selected directory to PATH for the install session. I also verified with INSTALL_BIN_DIR=/proc/1 npm install that links are created in the fallback global path (~/.local/bin).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/apt/methods/https /usr/lib/apt/methods/https (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of tomgrv May 24, 2026 19:27
Copilot AI requested a review from tomgrv May 24, 2026 19:27
Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot

Simplify and rationalize bin directory selection (by using a loop and a list for example)

Revert all scripts to use scripts deployed in a path available folder

Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot

Simplify and rationalize bin directory selection (by using a loop and a list for example)

Revert all scripts to use scripts deployed in a path available folder

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 24, 2026

@copilot

Simplify and rationalize bin directory selection (by using a loop and a list for example)...

Addressed in 37b4361.

  • Simplified bin directory resolution in _install-bin.sh using a candidate list + loop.
  • Reverted common-utils scripts to use PATH-deployed commands (zz_*, merge-json, normalize-json, etc.) instead of script-directory absolute invocations.
  • Verified fallback behavior with INSTALL_BIN_DIR=/proc/1, which links into ~/.local/bin when needed.

No UI changes in this update.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/apt/methods/https /usr/lib/apt/methods/https (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of tomgrv May 24, 2026 20:12
Copilot AI requested a review from tomgrv May 24, 2026 20:12
Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Externalise bin folder selection to a _select-bin.sh script

Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Externalise bin folder selection to a _select-bin.sh script

@tomgrv
Copy link
Copy Markdown
Owner

tomgrv commented May 25, 2026

@copilot update documentation accordingly.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot update documentation accordingly.

Updated in 78df9ff: /home/runner/work/devcontainer-features/devcontainer-features/src/common-utils/README.md now documents _select-bin.sh and the fallback bin selection order used by _install-bin.sh. I’m currently hitting a GitHub remote internal error while pushing from the agent; once push succeeds this commit will be visible on the branch.

Copilot finished work on behalf of tomgrv May 25, 2026 09:09
Copilot AI requested a review from tomgrv May 25, 2026 09:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts npm lifecycle scripts so each workspace under src/* can be installed/configured independently, while keeping root-level stub deployment triggered from the root npm install. It also updates common-utils bin-link installation to choose a writable bin directory more flexibly.

Changes:

  • Added root install lifecycle to deploy stubs on root npm install.
  • Added guarded install/postinstall scripts to multiple workspace package.json files so feature install/config only runs when installing that workspace directly.
  • Refactored common-utils bin linking to select a writable bin directory via a new _select-bin.sh, and temporarily amends PATH during install to keep helper commands runnable.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
package.json Adds root install script to run stub deployment on npm install.
src/act/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/common-utils/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/common-utils/_install-bin.sh Uses _select-bin.sh to choose a writable bin dir and prepends it to PATH for the install session.
src/common-utils/_select-bin.sh New helper to resolve a writable bin directory using a fallback order.
src/common-utils/README.md Documents the bin-directory selection behavior and fallback order.
src/gateway/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/githooks/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/gitutils/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/gitversion/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/larasets/package.json Adds guarded workspace install/postinstall lifecycle scripts.
src/minikube/package.json Adds guarded workspace install/postinstall lifecycle scripts.

Comment thread package.json
Comment thread src/common-utils/_select-bin.sh Outdated
Comment on lines +29 to +34
for dir in $(echo "$PATH" | tr ':' '\n'); do
case "$dir" in
"" | "." | "$PWD" | */node_modules/.bin) continue ;;
esac
add_candidate "$dir"
done
Comment thread src/common-utils/_select-bin.sh Outdated
Comment on lines +42 to +43
[ -d "$candidate" ] || mkdir -p "$candidate" 2>/dev/null || true
if [ -d "$candidate" ] && [ -w "$candidate" ]; then
Comment thread src/common-utils/_install-bin.sh Outdated
Comment on lines 29 to 31
# Find all shell scripts in the target directory, make them executable, and create symbolic links in /usr/local/bin
find $target -type f -name "_*.sh" -exec echo {} \; -exec chmod +x {} \; | while read file; do
# Create a symbolic link in /usr/local/bin with the script name (without the leading underscore and .sh extension)
Comment thread src/common-utils/_install-bin.sh Outdated
Comment on lines 30 to 33
find $target -type f -name "_*.sh" -exec echo {} \; -exec chmod +x {} \; | while read file; do
# Create a symbolic link in /usr/local/bin with the script name (without the leading underscore and .sh extension)
link=/usr/local/bin/$(basename $file | sed 's/^_//;s/.sh$//')
link=$link_dir/$(basename $file | sed 's/^_//;s/.sh$//')
ln -sf $file $link && zz_log s "Linked {U $file} to {U $link}"
@tomgrv
Copy link
Copy Markdown
Owner

tomgrv commented May 25, 2026

@copilot install.sh, handle dependencies based on decontainer files

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot install.sh, handle dependencies based on decontainer files

Implemented in 78e5a23. install.sh now discovers and reads devcontainer feature dependencies from standard devcontainer file locations in priority order: .devcontainer/devcontainer.jsondevcontainer.json.devcontainer.json.devcontainer/*/devcontainer.json.

Two new helper functions were added:

  • devcontainer_features() — extracts tomgrv/devcontainer-features entries from a given file
  • find_devcontainer_features() — searches standard locations and delegates to the above

The -a and -u modes now use these helpers, and when no flags or features are explicitly provided, the script auto-detects features from devcontainer files in the current directory.

Copilot finished work on behalf of tomgrv May 25, 2026 18:24
Copilot AI requested a review from tomgrv May 25, 2026 18:24
Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Rely on devcontainer-feature.json files to deduct dependencies

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot Rely on devcontainer-feature.json files to deduct dependencies

Implemented in 481827c. Two new helpers are added to install.sh:

  • feature_deps() — reads a feature's devcontainer-feature.json and extracts its tomgrv/devcontainer-features dependsOn entries
  • resolve_feature_deps() — BFS-collects all transitive dependencies, then topological-sorts the full set so dependencies always appear before the features that need them

After features are determined from any source (devcontainer.json, package.json, positional args, or auto-detection), the list is expanded through resolve_feature_deps. For example, requesting githooks automatically resolves to common-utils → gitversion → gitutils → githooks.

Copilot finished work on behalf of tomgrv May 25, 2026 18:31
Copilot AI requested a review from tomgrv May 25, 2026 18:31
Copy link
Copy Markdown
Owner

@tomgrv tomgrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Extract dependencies installation to install-feat.sh and extract resolution to dedicated install-deps.sh. use them in a recursive manner with install.sh as orchestrator and folder being the current context

…d feature install to _install-feat.sh

Agent-Logs-Url: https://github.com/tomgrv/devcontainer-features/sessions/e5fc286c-1e46-438a-a07d-ef0ff70f873b

Co-authored-by: tomgrv <1809566+tomgrv@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 25, 2026

@copilot Extract dependencies installation to install-feat.sh and extract resolution to dedicated install-deps.sh. use them in a recursive manner with install.sh as orchestrator and folder being the c...

Implemented in f72aac6.

src/common-utils/_install-deps.sh — standalone dependency resolver. Takes <source_dir> and a feature list, reads each feature's devcontainer-feature.json, performs BFS to collect all transitive tomgrv/devcontainer-features dependencies, then outputs a topologically-sorted list (deps before dependents). Example: install-deps $source githookscommon-utils → gitversion → gitutils → githooks.

src/common-utils/_install-feat.sh — feature installer. Takes <source_dir> and <feature>. It:

  1. Checks INSTALL_FEAT_TRACKER to skip already-installed features
  2. Calls install-deps for the feature's deps, then calls sh "$source/install.sh" recursively for each dep (making install.sh the orchestrator in the recursive chain)
  3. Installs the feature itself, handling container/stubs/no-container cases

install.sh (simplified orchestrator):

  • Removes feature_deps() and resolve_feature_deps() — replaced by install-deps
  • Replaces the per-feature install loop with install-feat "$source" "$feature" [--stubs]
  • Uses INSTALL_ROOT_CALL env var to ensure symlink setup/cleanup only happens at the root invocation, not in recursive calls from _install-feat.sh

Copilot finished work on behalf of tomgrv May 25, 2026 19:05
Copilot AI requested a review from tomgrv May 25, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dependencies management.

3 participants