chore: initial workflow implementation
Some checks failed
Test Module Installation Action / Node.js Module with NPM (pull_request) Failing after 22s
Test Module Installation Action / Linear Dependencies (A → B → C) (pull_request) Failing after 1m2s
Test Module Installation Action / Single Module (No Dependencies) (pull_request) Failing after 1m16s
Test Module Installation Action / PHP Module with Composer (pull_request) Successful in 1m26s
Test Module Installation Action / Mixed PHP and Node Modules (pull_request) Failing after 1m25s
Test Module Installation Action / Error Handling (pull_request) Successful in 1m24s
Test Module Installation Action / Test Summary (pull_request) Failing after 3s

Signed-off-by: Sebastian Krupinski <root@LAPTOP-7DVOR6NC>
This commit is contained in:
Sebastian Krupinski
2026-02-15 16:13:08 -05:00
committed by Sebastian Krupinski
parent cbeb999b18
commit 9fd503d45b
3 changed files with 1450 additions and 1 deletions

635
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,635 @@
name: Test Module Installation Action
on:
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
# Test basic single module installation
test-single-module:
name: Single Module (No Dependencies)
runs-on: ubuntu-latest
steps:
- name: Checkout action-module-install
uses: actions/checkout@v4
with:
path: action-module-install
- name: Clone action-server-install
run: |
git clone https://git.ktrix.dev/Nodarx/action-server-install action-server-install
cd action-server-install
git checkout main
- name: Setup server environment
uses: ./action-server-install
with:
install-php: 'true'
install-node: 'true'
php-version: '8.5'
node-version: '24'
server-path: './server'
- name: Create test module repository
run: |
mkdir -p /tmp/test-repos/simple-module
cd /tmp/test-repos/simple-module
git init
git config user.email "test@example.com"
git config user.name "Test User"
# Create a simple Node.js module
cat > package.json << 'EOF'
{
"name": "simple-module",
"version": "1.0.0",
"description": "Test module",
"main": "index.js"
}
EOF
echo "console.log('Simple module loaded');" > index.js
git add .
git commit -m "Initial commit"
- name: Install single module
id: install
uses: ./action-module-install
with:
modules: |
[
{
"name": "simple-module",
"repo": "file:///tmp/test-repos/simple-module"
}
]
- name: Verify installation
run: |
echo "Checking installation..."
# Check module directory exists
if [ ! -d "./modules/simple-module" ]; then
echo "::error::Module directory not found"
exit 1
fi
# Check files were cloned
if [ ! -f "./modules/simple-module/package.json" ]; then
echo "::error::package.json not found"
exit 1
fi
# Check npm install ran
if [ ! -d "./modules/simple-module/node_modules" ]; then
echo "::error::node_modules not found - npm install didn't run"
exit 1
fi
# Check outputs
if [ "${{ steps.install.outputs.status }}" != "success" ]; then
echo "::error::Installation status is not success"
exit 1
fi
if [ "${{ steps.install.outputs.installed-modules }}" != "simple-module" ]; then
echo "::error::Installed modules output is incorrect"
exit 1
fi
echo "✓ Single module installation verified"
# Test modules with linear dependencies
test-linear-dependencies:
name: Linear Dependencies (A → B → C)
runs-on: ubuntu-latest
steps:
- name: Checkout action-module-install
uses: actions/checkout@v4
with:
path: action-module-install
- name: Clone action-server-install
run: |
git clone https://git.ktrix.dev/Nodarx/action-server-install action-server-install
cd action-server-install
git checkout main
- name: Setup server environment
uses: ./action-server-install
with:
install-php: 'true'
php-version: '8.5'
server-path: './server'
- name: Create test repositories
run: |
# Create module-a (no dependencies)
mkdir -p /tmp/test-repos/module-a
cd /tmp/test-repos/module-a
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > composer.json << 'EOF'
{
"name": "test/module-a",
"description": "Module A",
"require": {}
}
EOF
git add . && git commit -m "Initial commit"
# Create module-b (depends on module-a)
mkdir -p /tmp/test-repos/module-b
cd /tmp/test-repos/module-b
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > composer.json << 'EOF'
{
"name": "test/module-b",
"description": "Module B",
"require": {}
}
EOF
git add . && git commit -m "Initial commit"
# Create module-c (depends on module-b)
mkdir -p /tmp/test-repos/module-c
cd /tmp/test-repos/module-c
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > composer.json << 'EOF'
{
"name": "test/module-c",
"description": "Module C",
"require": {}
}
EOF
git add . && git commit -m "Initial commit"
- name: Install modules with dependencies
id: install
uses: ./action-module-install
with:
modules: |
[
{
"name": "module-c",
"repo": "file:///tmp/test-repos/module-c",
"dependencies": ["module-b"]
},
{
"name": "module-a",
"repo": "file:///tmp/test-repos/module-a"
},
{
"name": "module-b",
"repo": "file:///tmp/test-repos/module-b",
"dependencies": ["module-a"]
}
]
- name: Verify dependency order
run: |
INSTALLED="${{ steps.install.outputs.installed-modules }}"
echo "Installation order: $INSTALLED"
# Verify all modules installed
for module in module-a module-b module-c; do
if [ ! -d "./modules/$module" ]; then
echo "::error::Module $module not found"
exit 1
fi
if [ ! -d "./modules/$module/vendor" ]; then
echo "::error::Composer dependencies not installed for $module"
exit 1
fi
done
# Verify order: module-a must come before module-b, module-b before module-c
echo "$INSTALLED" | grep -E "(module-a.*module-b.*module-c|module-a.*module-c)" || {
echo "::error::Installation order is incorrect: $INSTALLED"
echo "Expected: module-a installed before module-b and module-c"
exit 1
}
echo "✓ Linear dependency installation verified"
# Test PHP module
test-php-module:
name: PHP Module with Composer
runs-on: ubuntu-latest
steps:
- name: Checkout action-module-install
uses: actions/checkout@v4
with:
path: action-module-install
- name: Clone action-server-install
run: |
git clone https://git.ktrix.dev/Nodarx/action-server-install action-server-install
cd action-server-install
git checkout main
- name: Setup PHP environment
uses: ./action-server-install
with:
install-php: 'true'
php-version: '8.5'
server-path: './server'
- name: Create PHP test module
run: |
mkdir -p /tmp/test-repos/php-module
cd /tmp/test-repos/php-module
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > composer.json << 'EOF'
{
"name": "test/php-module",
"description": "PHP test module",
"require": {
"php": ">=8.0"
},
"autoload": {
"psr-4": {
"Test\\PhpModule\\": "src/"
}
}
}
EOF
mkdir -p src
cat > src/TestClass.php << 'EOF'
<?php
namespace Test\PhpModule;
class TestClass {
public function getMessage() {
return "PHP Module loaded";
}
}
EOF
git add . && git commit -m "Initial commit"
- name: Install PHP module
uses: ./action-module-install
with:
modules: |
[
{
"name": "php-module",
"repo": "file:///tmp/test-repos/php-module"
}
]
- name: Verify PHP installation
run: |
# Check composer.json exists
if [ ! -f "./modules/php-module/composer.json" ]; then
echo "::error::composer.json not found"
exit 1
fi
# Check vendor directory created
if [ ! -d "./modules/php-module/vendor" ]; then
echo "::error::vendor directory not found - composer install failed"
exit 1
fi
# Check autoload file exists
if [ ! -f "./modules/php-module/vendor/autoload.php" ]; then
echo "::error::autoload.php not found"
exit 1
fi
echo "✓ PHP module installation verified"
# Test Node.js module
test-node-module:
name: Node.js Module with NPM
runs-on: ubuntu-latest
steps:
- name: Checkout action-module-install
uses: actions/checkout@v4
with:
path: action-module-install
- name: Clone action-server-install
run: |
git clone https://git.ktrix.dev/Nodarx/action-server-install action-server-install
cd action-server-install
git checkout main
- name: Setup Node.js environment
uses: ./action-server-install
with:
install-node: 'true'
node-version: '24'
server-path: './server'
- name: Create Node.js test module
run: |
mkdir -p /tmp/test-repos/vue-module
cd /tmp/test-repos/vue-module
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > package.json << 'EOF'
{
"name": "vue-module",
"version": "1.0.0",
"description": "Vue test module",
"main": "index.js",
"dependencies": {}
}
EOF
cat > index.js << 'EOF'
export default {
name: 'VueModule',
getMessage() {
return 'Vue module loaded';
}
};
EOF
git add . && git commit -m "Initial commit"
- name: Install Node.js module
uses: ./action-module-install
with:
modules: |
[
{
"name": "vue-module",
"repo": "file:///tmp/test-repos/vue-module"
}
]
- name: Verify Node.js installation
run: |
# Check package.json exists
if [ ! -f "./modules/vue-module/package.json" ]; then
echo "::error::package.json not found"
exit 1
fi
# Check node_modules directory created
if [ ! -d "./modules/vue-module/node_modules" ]; then
echo "::error::node_modules directory not found - npm install failed"
exit 1
fi
echo "✓ Node.js module installation verified"
# Test mixed PHP and Node.js modules
test-mixed-modules:
name: Mixed PHP and Node Modules
runs-on: ubuntu-latest
steps:
- name: Checkout action-module-install
uses: actions/checkout@v4
with:
path: action-module-install
- name: Clone action-server-install
run: |
git clone https://git.ktrix.dev/Nodarx/action-server-install action-server-install
cd action-server-install
git checkout main
- name: Setup both PHP and Node.js
uses: ./action-server-install
with:
install-php: 'true'
install-node: 'true'
php-version: '8.5'
node-version: '24'
server-path: './server'
- name: Create mixed test modules
run: |
# PHP backend module
mkdir -p /tmp/test-repos/backend
cd /tmp/test-repos/backend
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > composer.json << 'EOF'
{
"name": "test/backend",
"require": {}
}
EOF
git add . && git commit -m "Initial commit"
# Node.js frontend module
mkdir -p /tmp/test-repos/frontend
cd /tmp/test-repos/frontend
git init
git config user.email "test@example.com"
git config user.name "Test User"
cat > package.json << 'EOF'
{
"name": "frontend",
"version": "1.0.0"
}
EOF
git add . && git commit -m "Initial commit"
- name: Install mixed modules
uses: ./action-module-install
with:
modules: |
[
{
"name": "backend",
"repo": "file:///tmp/test-repos/backend"
},
{
"name": "frontend",
"repo": "file:///tmp/test-repos/frontend"
}
]
- name: Verify mixed installation
run: |
# Verify PHP module
if [ ! -d "./modules/backend/vendor" ]; then
echo "::error::Backend composer dependencies not installed"
exit 1
fi
# Verify Node module
if [ ! -d "./modules/frontend/node_modules" ]; then
echo "::error::Frontend npm dependencies not installed"
exit 1
fi
echo "✓ Mixed PHP and Node.js modules verified"
# Test error handling for invalid inputs
test-error-handling:
name: Error Handling
runs-on: ubuntu-latest
steps:
- name: Checkout action-module-install
uses: actions/checkout@v4
with:
path: action-module-install
- name: Clone action-server-install
run: |
git clone https://git.ktrix.dev/Nodarx/action-server-install action-server-install
cd action-server-install
git checkout main
- name: Setup environment
uses: ./action-server-install
with:
install-php: 'true'
install-node: 'true'
server-path: './server'
- name: Test invalid JSON
id: test-invalid-json
continue-on-error: true
uses: ./action-module-install
with:
modules: 'invalid json string'
- name: Verify invalid JSON was caught
run: |
if [ "${{ steps.test-invalid-json.outcome }}" = "success" ]; then
echo "::error::Action should have failed with invalid JSON"
exit 1
fi
echo "✓ Invalid JSON properly rejected"
- name: Test missing dependency
id: test-missing-dep
continue-on-error: true
uses: ./action-module-install
with:
modules: |
[
{
"name": "module-a",
"repo": "file:///tmp/fake",
"dependencies": ["non-existent-module"]
}
]
- name: Verify missing dependency was caught
run: |
if [ "${{ steps.test-missing-dep.outcome }}" = "success" ]; then
echo "::error::Action should have failed with missing dependency"
exit 1
fi
echo "✓ Missing dependency properly detected"
- name: Test circular dependency
id: test-circular
continue-on-error: true
run: |
# Create repos for circular dependency test
mkdir -p /tmp/test-repos/circle-a /tmp/test-repos/circle-b
cd /tmp/test-repos/circle-a
git init
git config user.email "test@example.com"
git config user.name "Test User"
echo '{"name": "circle-a"}' > package.json
git add . && git commit -m "Initial"
cd /tmp/test-repos/circle-b
git init
git config user.email "test@example.com"
git config user.name "Test User"
echo '{"name": "circle-b"}' > package.json
git add . && git commit -m "Initial"
- name: Run circular dependency test
id: test-circular-run
continue-on-error: true
uses: ./action-module-install
with:
modules: |
[
{
"name": "circle-a",
"repo": "file:///tmp/test-repos/circle-a",
"dependencies": ["circle-b"]
},
{
"name": "circle-b",
"repo": "file:///tmp/test-repos/circle-b",
"dependencies": ["circle-a"]
}
]
- name: Verify circular dependency was caught
run: |
if [ "${{ steps.test-circular-run.outcome }}" = "success" ]; then
echo "::error::Action should have failed with circular dependency"
exit 1
fi
echo "✓ Circular dependency properly detected"
# Test summary
test-summary:
name: Test Summary
runs-on: ubuntu-latest
needs:
- test-single-module
- test-linear-dependencies
- test-php-module
- test-node-module
- test-mixed-modules
- test-error-handling
if: always()
steps:
- name: Check test results
run: |
echo "## Test Results Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Test | Result |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Single Module | ${{ needs.test-single-module.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Linear Dependencies | ${{ needs.test-linear-dependencies.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| PHP Module | ${{ needs.test-php-module.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Node Module | ${{ needs.test-node-module.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Mixed Modules | ${{ needs.test-mixed-modules.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Error Handling | ${{ needs.test-error-handling.result }} |" >> $GITHUB_STEP_SUMMARY
# Check if any test failed
if [ "${{ needs.test-single-module.result }}" != "success" ] || \
[ "${{ needs.test-linear-dependencies.result }}" != "success" ] || \
[ "${{ needs.test-php-module.result }}" != "success" ] || \
[ "${{ needs.test-node-module.result }}" != "success" ] || \
[ "${{ needs.test-mixed-modules.result }}" != "success" ] || \
[ "${{ needs.test-error-handling.result }}" != "success" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "❌ One or more tests failed" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ All tests passed!" >> $GITHUB_STEP_SUMMARY

407
README.md
View File

@@ -1,2 +1,407 @@
# action-module-install
# Nodarx Module Install Action
Install custom PHP/Vue modules from git repositories with automatic dependency resolution.
This GitHub Action clones module repositories, resolves dependencies, and installs both PHP (Composer) and Node.js (npm) dependencies in the correct order.
## Features
- 🔗 **Dependency Resolution**: Automatically orders modules based on their dependencies
- 📦 **Multi-Language Support**: Handles both PHP (Composer) and Node.js (npm) projects
- 🔄 **Flexible**: Support for custom branches, tags, or commits
-**Validation**: Detects circular dependencies and validates module configuration
- 🎯 **Simple**: Just provide a JSON array of modules and their dependencies
## Quick Start
```yaml
name: Deploy with Modules
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Install server environment (PHP + Node.js)
- uses: Nodarx/action-server-install@v1
with:
install-php: 'true'
install-node: 'true'
php-version: '8.5'
node-version: '24'
server-path: './server'
# Install modules with dependencies
- uses: Nodarx/action-module-install@v1
with:
modules: |
[
{"name": "mail_manager", "repo": "https://git.ktrix.dev/Nodarx/mail_manager"},
{"name": "provider_jmapc", "repo": "https://git.ktrix.dev/Nodarx/provider_jmapc"},
{"name": "mail", "repo": "https://git.ktrix.dev/Nodarx/mail", "dependencies": ["mail_manager", "provider_jmapc"]}
]
```
## Inputs
| Input | Description | Required | Default |
|-------|-------------|----------|---------|
| `modules` | JSON array of modules to install (see [Module Schema](#module-schema)) | Yes | - |
| `install-path` | Base directory where modules will be installed | No | `./modules` |
| `skip-dependencies-check` | Skip dependency validation (not recommended) | No | `false` |
## Outputs
| Output | Description | Example |
|--------|-------------|---------|
| `installed-modules` | Comma-separated list of installed module names in installation order | `mail_manager,provider_jmapc,mail` |
| `install-path` | Path where modules were installed | `./modules` |
| `status` | Installation status | `success` |
## Module Schema
Each module in the `modules` array should follow this structure:
```json
{
"name": "module-name", // Required: Unique module identifier
"repo": "https://...", // Required: Git repository URL
"branch": "main", // Optional: Branch, tag, or commit (default: main)
"dependencies": ["module1"] // Optional: Array of module names this module depends on
}
```
### Module Schema Examples
**Minimal Module (No Dependencies)**
```json
{
"name": "standalone-module",
"repo": "https://git.ktrix.dev/Nodarx/standalone"
}
```
**Module with Branch Specification**
```json
{
"name": "beta-module",
"repo": "https://git.ktrix.dev/Nodarx/beta",
"branch": "develop"
}
```
**Module with Dependencies**
```json
{
"name": "mail",
"repo": "https://git.ktrix.dev/Nodarx/mail",
"branch": "main",
"dependencies": ["mail_manager", "provider_jmapc"]
}
```
## Usage Examples
### Single Module
```yaml
- uses: Nodarx/action-module-install@v1
with:
modules: |
[
{"name": "auth", "repo": "https://git.ktrix.dev/Nodarx/auth"}
]
```
### Linear Dependencies (A → B → C)
```yaml
- uses: Nodarx/action-module-install@v1
with:
modules: |
[
{"name": "core", "repo": "https://git.ktrix.dev/Nodarx/core"},
{"name": "api", "repo": "https://git.ktrix.dev/Nodarx/api", "dependencies": ["core"]},
{"name": "web", "repo": "https://git.ktrix.dev/Nodarx/web", "dependencies": ["api"]}
]
```
Installation order: `core``api``web`
### Diamond Dependencies (A,B → C; C → D)
```yaml
- uses: Nodarx/action-module-install@v1
with:
modules: |
[
{"name": "utils", "repo": "https://git.ktrix.dev/Nodarx/utils"},
{"name": "storage", "repo": "https://git.ktrix.dev/Nodarx/storage"},
{"name": "database", "repo": "https://git.ktrix.dev/Nodarx/database", "dependencies": ["utils", "storage"]},
{"name": "api", "repo": "https://git.ktrix.dev/Nodarx/api", "dependencies": ["database"]}
]
```
Installation order: `utils,storage``database``api`
### Mail System Example (Real-World)
```yaml
- uses: Nodarx/action-server-install@v1
with:
install-php: 'true'
install-node: 'true'
php-version: '8.5'
node-version: '24'
- uses: Nodarx/action-module-install@v1
id: modules
with:
modules: |
[
{
"name": "mail_manager",
"repo": "https://git.ktrix.dev/Nodarx/mail_manager",
"branch": "main"
},
{
"name": "provider_jmapc",
"repo": "https://git.ktrix.dev/Nodarx/provider_jmapc",
"branch": "stable1"
},
{
"name": "mail",
"repo": "https://git.ktrix.dev/Nodarx/mail",
"branch": "main",
"dependencies": ["mail_manager", "provider_jmapc"]
}
]
install-path: './modules'
- name: Verify installation
run: |
echo "Installed: ${{ steps.modules.outputs.installed-modules }}"
ls -la ./modules/
```
### Different Branches per Module
```yaml
- uses: Nodarx/action-module-install@v1
with:
modules: |
[
{"name": "core", "repo": "https://git.ktrix.dev/Nodarx/core", "branch": "v2.0"},
{"name": "plugin", "repo": "https://git.ktrix.dev/Nodarx/plugin", "branch": "develop"},
{"name": "theme", "repo": "https://git.ktrix.dev/Nodarx/theme", "branch": "stable1"}
]
```
### Custom Install Path
```yaml
- uses: Nodarx/action-module-install@v1
with:
install-path: './custom/modules/directory'
modules: |
[
{"name": "module1", "repo": "https://git.ktrix.dev/Nodarx/module1"}
]
```
## How It Works
1. **Validation**: Validates JSON structure and checks that all dependencies are defined
2. **Dependency Resolution**: Uses topological sort to determine correct installation order
3. **Cloning**: Clones each module repository in dependency order
4. **Installation**: Auto-detects and runs:
- `composer install` for modules with `composer.json`
- `npm install` or `npm ci` for modules with `package.json`
5. **Verification**: Verifies all modules installed successfully
## Dependency Resolution
The action automatically determines the correct installation order:
```
Input modules:
- mail (depends on: mail_manager, provider_jmapc)
- mail_manager (no dependencies)
- provider_jmapc (no dependencies)
Installation order:
1. mail_manager
2. provider_jmapc
3. mail
```
Dependencies are installed **before** their dependents, ensuring all required modules are available when needed.
## Error Handling
The action will fail with descriptive errors for:
-**Invalid JSON**: Syntax errors in modules input
-**Missing Fields**: Modules without `name` or `repo`
-**Circular Dependencies**: A → B → A
-**Missing Dependencies**: Module depends on undefined module
-**Clone Failures**: Repository not accessible or branch doesn't exist
-**Install Failures**: Composer or npm installation errors
-**Missing Prerequisites**: PHP/Composer or Node/npm not installed
## Prerequisites
Before using this action, ensure PHP and/or Node.js are installed:
```yaml
- uses: Nodarx/action-server-install@v1
with:
install-php: 'true' # If modules use Composer
install-node: 'true' # If modules use npm
```
Or use standard setup actions:
```yaml
- uses: actions/setup-node@v4
with:
node-version: '24'
- uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
tools: composer:v2
```
## Troubleshooting
### Circular Dependency Error
```
Error: Circular dependency detected. Cannot determine installation order.
```
**Solution**: Review your dependencies to remove circular references. Example:
```
❌ moduleA depends on moduleB, moduleB depends on moduleA
✅ moduleA depends on moduleB, moduleB has no dependencies
```
### Missing Dependency Error
```
Error: The following dependencies are referenced but not defined: module_x
```
**Solution**: Add the missing module to your modules array:
```json
[
{"name": "module_x", "repo": "https://..."},
{"name": "dependent", "repo": "https://...", "dependencies": ["module_x"]}
]
```
### Composer/npm Not Found
```
Error: composer not found. Please ensure PHP and Composer are installed
```
**Solution**: Install PHP/Node.js before this action:
```yaml
- uses: Nodarx/action-server-install@v1
with:
install-php: 'true'
install-node: 'true'
```
### Clone Failed (Branch Not Found)
```
Error: Failed to clone module_name from https://... (branch: xyz)
```
**Solution**: Verify the branch exists in the repository. Use `main`, `master`, or a valid branch/tag name.
## Complete Workflow Example
```yaml
name: Deploy Application with Modules
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup server environment
uses: Nodarx/action-server-install@v1
with:
install-php: 'true'
install-node: 'true'
php-version: '8.5'
node-version: '24'
server-path: './server'
- name: Install application modules
id: install-modules
uses: Nodarx/action-module-install@v1
with:
modules: |
[
{
"name": "mail_manager",
"repo": "https://git.ktrix.dev/Nodarx/mail_manager",
"branch": "main"
},
{
"name": "provider_jmapc",
"repo": "https://git.ktrix.dev/Nodarx/provider_jmapc",
"branch": "stable1"
},
{
"name": "mail",
"repo": "https://git.ktrix.dev/Nodarx/mail",
"branch": "main",
"dependencies": ["mail_manager", "provider_jmapc"]
},
{
"name": "calendar",
"repo": "https://git.ktrix.dev/Nodarx/calendar",
"branch": "main",
"dependencies": ["mail_manager"]
}
]
install-path: './server/modules'
- name: Display installation results
run: |
echo "Installation Status: ${{ steps.install-modules.outputs.status }}"
echo "Installed Modules: ${{ steps.install-modules.outputs.installed-modules }}"
echo "Install Path: ${{ steps.install-modules.outputs.install-path }}"
echo ""
echo "Module directories:"
ls -la ${{ steps.install-modules.outputs.install-path }}
- name: Run application tests
run: |
cd ./server
npm test
```
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Author
Created by [Nodarx](https://github.com/Nodarx)

409
action.yml Normal file
View File

@@ -0,0 +1,409 @@
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)
# For each module with dependencies, create "module dep" pairs
echo "$MODULES_JSON" | jq -r '
.[] |
select(.dependencies != null and (.dependencies | length) > 0) |
. as $item |
.dependencies[] |
($item.name + " " + .)
' > "$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 // empty')
MODULE_PATH="$INSTALL_PATH/$MODULE_NAME"
echo "Repository: $REPO_URL"
if [ -n "$BRANCH" ]; then
echo "Branch: $BRANCH"
else
echo "Branch: (repository default)"
fi
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"
if [ -n "$BRANCH" ]; then
git pull origin "$BRANCH" || echo "::warning::Pull failed, continuing with existing code"
else
git pull || echo "::warning::Pull failed, continuing with existing code"
fi
cd - > /dev/null
else
if [ -n "$BRANCH" ]; then
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
else
if git clone --depth 1 "$REPO_URL" "$MODULE_PATH"; then
echo "✓ Cloned $MODULE_NAME successfully"
else
echo "::error::Failed to clone $MODULE_NAME from $REPO_URL"
exit 1
fi
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::"