diff --git a/public/svgs/executor.png b/public/svgs/executor.png new file mode 100644 index 000000000..a7cc57de9 Binary files /dev/null and b/public/svgs/executor.png differ diff --git a/templates/compose/executor.yaml b/templates/compose/executor.yaml new file mode 100644 index 000000000..e6913ae5d --- /dev/null +++ b/templates/compose/executor.yaml @@ -0,0 +1,35 @@ +# documentation: https://executor.sh/docs/hosted/docker +# slogan: Self-hosted AI agent integrations and code execution server. +# category: development +# tags: executor,ai,agents,mcp,automation,development +# logo: svgs/executor.png +# port: 4788 + +services: + executor: + image: 'ghcr.io/rhyssullivan/executor-selfhost:${EXECUTOR_VERSION:-latest}' + environment: + - SERVICE_URL_EXECUTOR_4788 + - EXECUTOR_WEB_BASE_URL=${SERVICE_URL_EXECUTOR_4788} + - EXECUTOR_HOST=${EXECUTOR_HOST:-0.0.0.0} + - PORT=${PORT:-4788} + - EXECUTOR_DATA_DIR=/data + - EXECUTOR_DB_PATH=${EXECUTOR_DB_PATH:-/data/data.db} + - EXECUTOR_ORG_NAME=${EXECUTOR_ORG_NAME:-Default} + - EXECUTOR_ORG_SLUG=${EXECUTOR_ORG_SLUG:-default} + - EXECUTOR_ALLOW_LOCAL_NETWORK=${EXECUTOR_ALLOW_LOCAL_NETWORK:-false} + volumes: + - executor-data:/data + healthcheck: + test: + - CMD + - bun + - -e + - "fetch(`http://127.0.0.1:${process.env.PORT||4788}/api/health`).then(r=>process.exit(r.ok?0:1),()=>process.exit(1))" + interval: 30s + timeout: 5s + retries: 5 + start_period: 20s + +volumes: + executor-data: diff --git a/templates/service-templates-latest.json b/templates/service-templates-latest.json index 00243fff0..b6a452e75 100644 --- a/templates/service-templates-latest.json +++ b/templates/service-templates-latest.json @@ -1400,6 +1400,24 @@ "template_last_updated_at": "2025-08-17T18:23:57+02:00", "port": "80" }, + "executor": { + "documentation": "https://executor.sh/docs/hosted/docker?utm_source=coolify.io", + "slogan": "Self-hosted AI agent integrations and code execution server.", + "compose": "c2VydmljZXM6CiAgZXhlY3V0b3I6CiAgICBpbWFnZTogJ2doY3IuaW8vcmh5c3N1bGxpdmFuL2V4ZWN1dG9yLXNlbGZob3N0OiR7RVhFQ1VUT1JfVkVSU0lPTjotbGF0ZXN0fScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX0VYRUNVVE9SXzQ3ODgKICAgICAgLSAnRVhFQ1VUT1JfV0VCX0JBU0VfVVJMPSR7U0VSVklDRV9VUkxfRVhFQ1VUT1JfNDc4OH0nCiAgICAgIC0gJ0VYRUNVVE9SX0hPU1Q9JHtFWEVDVVRPUl9IT1NUOi0wLjAuMC4wfScKICAgICAgLSAnUE9SVD0ke1BPUlQ6LTQ3ODh9JwogICAgICAtIEVYRUNVVE9SX0RBVEFfRElSPS9kYXRhCiAgICAgIC0gJ0VYRUNVVE9SX0RCX1BBVEg9JHtFWEVDVVRPUl9EQl9QQVRIOi0vZGF0YS9kYXRhLmRifScKICAgICAgLSAnRVhFQ1VUT1JfT1JHX05BTUU9JHtFWEVDVVRPUl9PUkdfTkFNRTotRGVmYXVsdH0nCiAgICAgIC0gJ0VYRUNVVE9SX09SR19TTFVHPSR7RVhFQ1VUT1JfT1JHX1NMVUc6LWRlZmF1bHR9JwogICAgICAtICdFWEVDVVRPUl9BTExPV19MT0NBTF9ORVRXT1JLPSR7RVhFQ1VUT1JfQUxMT1dfTE9DQUxfTkVUV09SSzotZmFsc2V9JwogICAgdm9sdW1lczoKICAgICAgLSAnZXhlY3V0b3ItZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBidW4KICAgICAgICAtICctZScKICAgICAgICAtICdmZXRjaChgaHR0cDovLzEyNy4wLjAuMToke3Byb2Nlc3MuZW52LlBPUlR8fDQ3ODh9L2FwaS9oZWFsdGhgKS50aGVuKHI9PnByb2Nlc3MuZXhpdChyLm9rPzA6MSksKCk9PnByb2Nlc3MuZXhpdCgxKSknCiAgICAgIGludGVydmFsOiAzMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQogICAgICBzdGFydF9wZXJpb2Q6IDIwcwp2b2x1bWVzOgogIGV4ZWN1dG9yLWRhdGE6IG51bGwK", + "tags": [ + "executor", + "ai", + "agents", + "mcp", + "automation", + "development" + ], + "category": "development", + "logo": "svgs/executor.png", + "minversion": "0.0.0", + "template_last_updated_at": null, + "port": "4788" + }, "faraday": { "documentation": "https://faradaysec.com/?utm_source=coolify.io", "slogan": "Faraday is a powerful, open-source, web-based vulnerability management tool.", diff --git a/templates/service-templates.json b/templates/service-templates.json index c193f0934..d279a6a33 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -1400,6 +1400,24 @@ "template_last_updated_at": "2025-08-17T18:23:57+02:00", "port": "80" }, + "executor": { + "documentation": "https://executor.sh/docs/hosted/docker?utm_source=coolify.io", + "slogan": "Self-hosted AI agent integrations and code execution server.", + "compose": "c2VydmljZXM6CiAgZXhlY3V0b3I6CiAgICBpbWFnZTogJ2doY3IuaW8vcmh5c3N1bGxpdmFuL2V4ZWN1dG9yLXNlbGZob3N0OiR7RVhFQ1VUT1JfVkVSU0lPTjotbGF0ZXN0fScKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FWEVDVVRPUl80Nzg4CiAgICAgIC0gJ0VYRUNVVE9SX1dFQl9CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9FWEVDVVRPUl80Nzg4fScKICAgICAgLSAnRVhFQ1VUT1JfSE9TVD0ke0VYRUNVVE9SX0hPU1Q6LTAuMC4wLjB9JwogICAgICAtICdQT1JUPSR7UE9SVDotNDc4OH0nCiAgICAgIC0gRVhFQ1VUT1JfREFUQV9ESVI9L2RhdGEKICAgICAgLSAnRVhFQ1VUT1JfREJfUEFUSD0ke0VYRUNVVE9SX0RCX1BBVEg6LS9kYXRhL2RhdGEuZGJ9JwogICAgICAtICdFWEVDVVRPUl9PUkdfTkFNRT0ke0VYRUNVVE9SX09SR19OQU1FOi1EZWZhdWx0fScKICAgICAgLSAnRVhFQ1VUT1JfT1JHX1NMVUc9JHtFWEVDVVRPUl9PUkdfU0xVRzotZGVmYXVsdH0nCiAgICAgIC0gJ0VYRUNVVE9SX0FMTE9XX0xPQ0FMX05FVFdPUks9JHtFWEVDVVRPUl9BTExPV19MT0NBTF9ORVRXT1JLOi1mYWxzZX0nCiAgICB2b2x1bWVzOgogICAgICAtICdleGVjdXRvci1kYXRhOi9kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGJ1bgogICAgICAgIC0gJy1lJwogICAgICAgIC0gJ2ZldGNoKGBodHRwOi8vMTI3LjAuMC4xOiR7cHJvY2Vzcy5lbnYuUE9SVHx8NDc4OH0vYXBpL2hlYWx0aGApLnRoZW4ocj0+cHJvY2Vzcy5leGl0KHIub2s/MDoxKSwoKT0+cHJvY2Vzcy5leGl0KDEpKScKICAgICAgaW50ZXJ2YWw6IDMwcwogICAgICB0aW1lb3V0OiA1cwogICAgICByZXRyaWVzOiA1CiAgICAgIHN0YXJ0X3BlcmlvZDogMjBzCnZvbHVtZXM6CiAgZXhlY3V0b3ItZGF0YTogbnVsbAo=", + "tags": [ + "executor", + "ai", + "agents", + "mcp", + "automation", + "development" + ], + "category": "development", + "logo": "svgs/executor.png", + "minversion": "0.0.0", + "template_last_updated_at": null, + "port": "4788" + }, "faraday": { "documentation": "https://faradaysec.com/?utm_source=coolify.io", "slogan": "Faraday is a powerful, open-source, web-based vulnerability management tool.", diff --git a/tests/Unit/ExecutorServiceTemplateTest.php b/tests/Unit/ExecutorServiceTemplateTest.php new file mode 100644 index 000000000..7e3d0ce50 --- /dev/null +++ b/tests/Unit/ExecutorServiceTemplateTest.php @@ -0,0 +1,49 @@ +toBeFile(); + + $compose = file_get_contents($templatePath); + + expect($compose) + ->toContain('ghcr.io/rhyssullivan/executor-selfhost:${EXECUTOR_VERSION:-latest}') + ->toContain('SERVICE_URL_EXECUTOR_4788') + ->toContain('EXECUTOR_WEB_BASE_URL=${SERVICE_URL_EXECUTOR_4788}') + ->toContain('executor-data:/data') + ->toContain('http://127.0.0.1:${process.env.PORT||4788}/api/health'); + + foreach (['service-templates.json', 'service-templates-latest.json'] as $templateFile) { + $templateCatalogPath = __DIR__."/../../templates/{$templateFile}"; + $templateCatalog = file_get_contents($templateCatalogPath); + + if ($templateCatalog === false) { + throw new RuntimeException("Unable to read service template catalog at {$templateCatalogPath}."); + } + + $templates = json_decode( + $templateCatalog, + associative: true, + flags: JSON_THROW_ON_ERROR, + ); + + expect($templates)->toHaveKey('executor'); + expect($templates['executor']['port'] ?? null)->toBe('4788'); + expect($templates['executor']['logo'] ?? null)->toBe('svgs/executor.png'); + expect($templates['executor']['category'] ?? null)->toBe('development'); + + $generatedCompose = base64_decode($templates['executor']['compose'], strict: true); + + expect($generatedCompose) + ->toContain('ghcr.io/rhyssullivan/executor-selfhost:${EXECUTOR_VERSION:-latest}') + ->toContain('http://127.0.0.1:${process.env.PORT||4788}/api/health') + ->toContain($templateFile === 'service-templates.json' + ? 'EXECUTOR_WEB_BASE_URL=${SERVICE_FQDN_EXECUTOR_4788}' + : 'EXECUTOR_WEB_BASE_URL=${SERVICE_URL_EXECUTOR_4788}'); + } +}); + +it('ships the Executor service icon from the public path used by the service picker', function () { + expect(__DIR__.'/../../public/svgs/executor.png')->toBeFile(); +});