Files
action-module-install/action.yml
Sebastian Krupinski 3fc872439a
Some checks failed
Test Module Installation Action / Single Module (No Dependencies) (pull_request) Failing after 4s
Test Module Installation Action / Linear Dependencies (A → B → C) (pull_request) Failing after 4s
Test Module Installation Action / PHP Module with Composer (pull_request) Failing after 4s
Test Module Installation Action / Node.js Module with NPM (pull_request) Failing after 4s
Test Module Installation Action / Mixed PHP and Node Modules (pull_request) Failing after 4s
Test Module Installation Action / Error Handling (pull_request) Failing after 4s
Test Module Installation Action / Test Summary (pull_request) Failing after 3s
chore: initial workflow implementation
Signed-off-by: Sebastian Krupinski <root@LAPTOP-7DVOR6NC>
2026-02-15 16:34:02 -05:00

392 lines
13 KiB
YAML

name: 'Install Nodarx Modules'
description: 'Installs custom PHP/Vue modules from git repositories with dependency resolution'
author: 'Nodarx'
branding:
icon: 'package'
color: 'purple'
inputs:
modules:
description: 'JSON array of modules to install. Example: [{"name":"mail","repo":"https://git.ktrix.dev/Nodarx/mail","branch":"main","dependencies":["mail_manager"]}]'
required: true
install-path:
description: 'Base directory where modules will be installed'
required: false
default: './modules'
skip-dependencies-check:
description: 'Skip dependency validation (not recommended)'
required: false
default: 'false'
outputs:
installed-modules:
description: 'Comma-separated list of installed module names in installation order'
value: ${{ steps.install.outputs.installed_modules }}
install-path:
description: 'Path where modules were installed'
value: ${{ steps.install.outputs.install_path }}
status:
description: 'Installation status (success/failure)'
value: ${{ steps.install.outputs.status }}
runs:
using: 'composite'
steps:
- name: Check prerequisites
shell: bash
run: |
echo "::group::Checking prerequisites"
# Check for required commands
MISSING_CMDS=()
for cmd in git jq; do
if ! command -v $cmd &> /dev/null; then
MISSING_CMDS+=($cmd)
fi
done
# Install missing commands
if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
echo "Installing missing commands: ${MISSING_CMDS[*]}"
sudo apt-get update -qq
for cmd in "${MISSING_CMDS[@]}"; do
sudo apt-get install -y $cmd
done
fi
# Verify all commands are now available
for cmd in git jq; do
if ! command -v $cmd &> /dev/null; then
echo "::error::Required command '$cmd' not found after installation attempt."
exit 1
fi
done
echo "✓ All prerequisites met"
echo "::endgroup::"
- name: Validate and parse modules JSON
id: parse
shell: bash
env:
MODULES_JSON: ${{ inputs.modules }}
SKIP_DEPS_CHECK: ${{ inputs.skip-dependencies-check }}
run: |
echo "::group::Validating modules configuration"
# Validate JSON syntax
if ! echo "$MODULES_JSON" | jq empty 2>/dev/null; then
echo "::error::Invalid JSON in modules input"
exit 1
fi
# Validate JSON is an array
if [ "$(echo "$MODULES_JSON" | jq 'type')" != '"array"' ]; then
echo "::error::Modules input must be a JSON array"
exit 1
fi
# Check if array is empty
MODULE_COUNT=$(echo "$MODULES_JSON" | jq 'length')
if [ "$MODULE_COUNT" -eq 0 ]; then
echo "::error::Modules array is empty. Please specify at least one module."
exit 1
fi
echo "Found $MODULE_COUNT module(s) to install"
# Validate each module has required fields
INVALID_MODULES=$(echo "$MODULES_JSON" | jq -r '
to_entries[] |
select(.value.name == null or .value.name == "" or .value.repo == null or .value.repo == "") |
.key
')
if [ -n "$INVALID_MODULES" ]; then
echo "::error::Modules at indices $INVALID_MODULES are missing required fields 'name' or 'repo'"
exit 1
fi
# Extract all module names
ALL_MODULES=$(echo "$MODULES_JSON" | jq -r '.[].name' | tr '\n' ' ')
echo "Modules to install: $ALL_MODULES"
# Validate dependencies exist (if not skipped)
if [ "$SKIP_DEPS_CHECK" != "true" ]; then
MISSING_DEPS=$(echo "$MODULES_JSON" | jq -r --arg all_modules "$ALL_MODULES" '
.[] |
select(.dependencies != null) |
.dependencies[] as $dep |
select(($all_modules | contains($dep)) | not) |
$dep
' | sort -u)
if [ -n "$MISSING_DEPS" ]; then
echo "::error::The following dependencies are referenced but not defined: $MISSING_DEPS"
exit 1
fi
fi
echo "✓ Modules configuration is valid"
echo "::endgroup::"
- name: Order modules by dependencies
id: order
shell: bash
env:
MODULES_JSON: ${{ inputs.modules }}
SKIP_DEPS_CHECK: ${{ inputs.skip-dependencies-check }}
run: |
echo "::group::Calculating installation order"
# Create temporary files for topological sort
TEMP_DIR=$(mktemp -d)
EDGES_FILE="$TEMP_DIR/edges"
NODES_FILE="$TEMP_DIR/nodes"
# Extract all module names
echo "$MODULES_JSON" | jq -r '.[].name' > "$NODES_FILE"
# Create dependency edges (dependent -> dependency)
echo "$MODULES_JSON" | jq -r '
.[] |
select(.dependencies != null and (.dependencies | length) > 0) |
.name as $module |
.dependencies[] |
"\($module) \(.)"
' > "$EDGES_FILE"
# Perform topological sort
if [ "$SKIP_DEPS_CHECK" != "true" ] && [ -s "$EDGES_FILE" ]; then
# Check for circular dependencies
if ! tsort "$EDGES_FILE" &>/dev/null; then
echo "::error::Circular dependency detected. Cannot determine installation order."
cat "$EDGES_FILE"
rm -rf "$TEMP_DIR"
exit 1
fi
# Get sorted order (reverse tsort output since it gives reverse topological order)
INSTALL_ORDER=$(cat "$NODES_FILE" "$EDGES_FILE" | tsort | tac | tr '\n' ' ')
else
# No dependencies or skip check - install in order given
INSTALL_ORDER=$(cat "$NODES_FILE" | tr '\n' ' ')
fi
# Clean up
rm -rf "$TEMP_DIR"
echo "Installation order: $INSTALL_ORDER"
echo "install_order=$INSTALL_ORDER" >> $GITHUB_OUTPUT
echo "✓ Installation order determined"
echo "::endgroup::"
- name: Clone module repositories
id: clone
shell: bash
env:
MODULES_JSON: ${{ inputs.modules }}
INSTALL_PATH: ${{ inputs.install-path }}
INSTALL_ORDER: ${{ steps.order.outputs.install_order }}
run: |
echo "::group::Cloning module repositories"
# Create base install directory
mkdir -p "$INSTALL_PATH"
# Clone each module in order
for MODULE_NAME in $INSTALL_ORDER; do
echo ""
echo "=== Cloning module: $MODULE_NAME ==="
# Extract module info from JSON
MODULE_INFO=$(echo "$MODULES_JSON" | jq -r --arg name "$MODULE_NAME" '
.[] | select(.name == $name)
')
REPO_URL=$(echo "$MODULE_INFO" | jq -r '.repo')
BRANCH=$(echo "$MODULE_INFO" | jq -r '.branch // "main"')
MODULE_PATH="$INSTALL_PATH/$MODULE_NAME"
echo "Repository: $REPO_URL"
echo "Branch: $BRANCH"
echo "Path: $MODULE_PATH"
# Clone the repository
if [ -d "$MODULE_PATH" ]; then
echo "::warning::Directory $MODULE_PATH already exists, pulling latest changes..."
cd "$MODULE_PATH"
git pull origin "$BRANCH" || echo "::warning::Pull failed, continuing with existing code"
cd - > /dev/null
else
if git clone --branch "$BRANCH" --depth 1 "$REPO_URL" "$MODULE_PATH"; then
echo "✓ Cloned $MODULE_NAME successfully"
else
echo "::error::Failed to clone $MODULE_NAME from $REPO_URL (branch: $BRANCH)"
exit 1
fi
fi
done
echo ""
echo "✓ All modules cloned successfully"
echo "::endgroup::"
- name: Install module dependencies
id: install
shell: bash
env:
INSTALL_PATH: ${{ inputs.install-path }}
INSTALL_ORDER: ${{ steps.order.outputs.install_order }}
run: |
echo "::group::Installing module dependencies"
INSTALLED_MODULES=()
for MODULE_NAME in $INSTALL_ORDER; do
MODULE_PATH="$INSTALL_PATH/$MODULE_NAME"
echo ""
echo "=== Installing dependencies for: $MODULE_NAME ==="
echo "Path: $MODULE_PATH"
if [ ! -d "$MODULE_PATH" ]; then
echo "::error::Module directory not found: $MODULE_PATH"
exit 1
fi
cd "$MODULE_PATH"
# Detect and install PHP dependencies
if [ -f "composer.json" ]; then
echo "Found composer.json - installing PHP dependencies..."
# Check if composer is available
if ! command -v composer &> /dev/null; then
echo "::error::composer not found. Please ensure PHP and Composer are installed (use action-server-install with install-php: true)"
exit 1
fi
# Install with composer
if composer install --no-dev --optimize-autoloader --no-interaction; then
echo "✓ Composer dependencies installed"
else
echo "::error::Failed to install composer dependencies for $MODULE_NAME"
exit 1
fi
else
echo "No composer.json found, skipping PHP dependencies"
fi
# Detect and install Node dependencies
if [ -f "package.json" ]; then
echo "Found package.json - installing Node dependencies..."
# Check if npm is available
if ! command -v npm &> /dev/null; then
echo "::error::npm not found. Please ensure Node.js is installed (use action-server-install with install-node: true)"
exit 1
fi
# Use npm ci if lockfile exists, otherwise npm install
if [ -f "package-lock.json" ]; then
echo "Using npm ci (lockfile found)..."
if npm ci --production; then
echo "✓ npm dependencies installed (with lockfile)"
else
echo "::warning::npm ci failed, trying npm install..."
if npm install --production; then
echo "✓ npm dependencies installed"
else
echo "::error::Failed to install npm dependencies for $MODULE_NAME"
exit 1
fi
fi
else
echo "Using npm install (no lockfile)..."
if npm install --production; then
echo "✓ npm dependencies installed"
else
echo "::error::Failed to install npm dependencies for $MODULE_NAME"
exit 1
fi
fi
else
echo "No package.json found, skipping Node dependencies"
fi
# Return to previous directory
cd - > /dev/null
INSTALLED_MODULES+=("$MODULE_NAME")
echo "✓ $MODULE_NAME installation complete"
done
# Set outputs
INSTALLED_LIST=$(IFS=,; echo "${INSTALLED_MODULES[*]}")
echo "installed_modules=$INSTALLED_LIST" >> $GITHUB_OUTPUT
echo "install_path=$INSTALL_PATH" >> $GITHUB_OUTPUT
echo "status=success" >> $GITHUB_OUTPUT
echo ""
echo "::notice::✓ Successfully installed ${#INSTALLED_MODULES[@]} module(s): $INSTALLED_LIST"
echo "::endgroup::"
- name: Verify installations
shell: bash
env:
INSTALL_PATH: ${{ inputs.install-path }}
INSTALL_ORDER: ${{ steps.order.outputs.install_order }}
run: |
echo "::group::Verifying module installations"
for MODULE_NAME in $INSTALL_ORDER; do
MODULE_PATH="$INSTALL_PATH/$MODULE_NAME"
echo ""
echo "Verifying: $MODULE_NAME"
# Check module directory exists
if [ ! -d "$MODULE_PATH" ]; then
echo "::error::Module directory not found: $MODULE_PATH"
exit 1
fi
# Check .git directory exists
if [ ! -d "$MODULE_PATH/.git" ]; then
echo "::warning::No .git directory found in $MODULE_NAME"
fi
# Verify composer installation
if [ -f "$MODULE_PATH/composer.json" ]; then
if [ -d "$MODULE_PATH/vendor" ]; then
echo " ✓ PHP dependencies installed (vendor/ exists)"
else
echo "::error::composer.json exists but vendor/ directory not found in $MODULE_NAME"
exit 1
fi
fi
# Verify npm installation
if [ -f "$MODULE_PATH/package.json" ]; then
if [ -d "$MODULE_PATH/node_modules" ]; then
echo " ✓ Node dependencies installed (node_modules/ exists)"
else
echo "::error::package.json exists but node_modules/ directory not found in $MODULE_NAME"
exit 1
fi
fi
echo " ✓ $MODULE_NAME verified"
done
echo ""
echo "✓ All modules verified successfully"
echo "::endgroup::"