Skip to content

fix workflow fail when no test files #8

fix workflow fail when no test files

fix workflow fail when no test files #8

name: Module Container Tests
permissions:
contents: read
on:
push:
branches:
- main
paths:
- "src/modules/**"
- "src/server/core/**"
- ".github/workflows/test-module-container.yml"
workflow_dispatch:
jobs:
discover-modules:
name: Discover modules to test
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.discover.outputs.matrix }}
has_modules: ${{ steps.discover.outputs.has_modules }}
selected_modules: ${{ steps.discover.outputs.selected_modules }}
steps:
- name: checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: set commit range
id: commit-range
shell: bash
run: |
base_sha="${{ github.event.before }}"
head_sha="${{ github.sha }}"
if [[ "$base_sha" =~ ^0+$ ]]; then
base_sha="$(git rev-parse "$head_sha^")"
fi
echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT"
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
- name: discover changed and testable modules
id: discover
env:
BASE_SHA: ${{ steps.commit-range.outputs.base_sha }}
HEAD_SHA: ${{ steps.commit-range.outputs.head_sha }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
run: |
node <<'NODE'
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const baseSha = process.env.BASE_SHA;
const headSha = process.env.HEAD_SHA;
const eventName = process.env.GITHUB_EVENT_NAME;
let changedFiles = [];
try {
const diff = execSync(`git diff --name-only ${baseSha} ${headSha}`, { encoding: "utf8" });
changedFiles = diff.split("\n").map((s) => s.trim()).filter(Boolean);
} catch {
changedFiles = [];
}
const changedModules = Array.from(
new Set(
changedFiles
.map((file) => {
const match = file.match(/^src\/modules\/([^/]+)\//);
return match ? match[1] : null;
})
.filter(Boolean)
)
);
const coreChanged =
eventName === "workflow_dispatch" ||
changedFiles.some((file) => file.startsWith("src/server/core/"));
const modulesRoot = path.join("src", "modules");
const testableModules = [];
function hasTestFiles(dirPath) {
const stack = [dirPath];
while (stack.length > 0) {
const current = stack.pop();
const entries = fs.readdirSync(current, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
continue;
}
const fullPath = path.join(current, entry.name);
if (entry.isDirectory()) {
stack.push(fullPath);
continue;
}
if (/\.(test|spec)\.[cm]?[jt]sx?$/.test(entry.name)) {
return true;
}
}
}
return false;
}
for (const moduleName of fs.readdirSync(modulesRoot)) {
const containerPath = path.join(modulesRoot, moduleName, "container");
const packagePath = path.join(modulesRoot, moduleName, "container", "package.json");
if (!fs.existsSync(packagePath)) {
continue;
}
try {
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf8"));
if (pkg?.scripts?.test && hasTestFiles(containerPath)) {
testableModules.push(moduleName);
}
} catch {
// Skip invalid package.json files.
}
}
const selected = coreChanged
? testableModules
: testableModules.filter((moduleName) => changedModules.includes(moduleName));
const matrix = selected.map((moduleName) => ({
module: moduleName,
}));
const out = process.env.GITHUB_OUTPUT;
fs.appendFileSync(out, `matrix=${JSON.stringify(matrix)}\n`);
fs.appendFileSync(out, `has_modules=${matrix.length > 0}\n`);
fs.appendFileSync(out, `selected_modules=${selected.join(",")}\n`);
NODE
module-tests:
name: Module Tests
needs: discover-modules
if: needs.discover-modules.outputs.has_modules == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.discover-modules.outputs.matrix) }}
steps:
- name: checkout repository
uses: actions/checkout@v6
- name: module under test
run: |
echo "Testing module: ${{ matrix.module }}"
- name: setup node
uses: actions/setup-node@v6
with:
node-version: 22
- name: ensure module test Dockerfile exists
run: |
dockerfile_path="src/modules/${{ matrix.module }}/container/Dockerfile.test"
if [[ -f "$dockerfile_path" ]]; then
echo "Using existing test Dockerfile at $dockerfile_path"
exit 0
fi
echo "No test Dockerfile found at $dockerfile_path, creating CI test Dockerfile"
cat > "$dockerfile_path" <<EOF_DOCKER
FROM node:22-alpine
WORKDIR /home/node/module
COPY src/server/core ./core
COPY src/modules/${{ matrix.module }}/container ./
RUN npm install
CMD ["npm", "run", "development"]
EOF_DOCKER
- name: run tests for module
run: |
cd "src/modules/${{ matrix.module }}/container"
npm ci
npm run test
no-modules-changed:
name: No module tests required
needs: discover-modules
if: needs.discover-modules.outputs.has_modules != 'true'
runs-on: ubuntu-latest
steps:
- name: print skip reason
run: echo "No changed modules with both a test script and test files were detected."