From 8ccdd3b2b1370db51d3d08bffad203cdce836f93 Mon Sep 17 00:00:00 2001 From: hotrush Date: Mon, 20 Apr 2026 22:47:46 +0200 Subject: [PATCH 1/3] Smoke testing --- action.yml | 2 +- src/index.js | 20 +++++++++++++++++++- tests/index.test.js | 6 ++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index d183de5..a08ce74 100644 --- a/action.yml +++ b/action.yml @@ -27,5 +27,5 @@ outputs: package-path: description: 'Path to the generated .zip package' runs: - using: 'node20' + using: 'node22' main: dist/index.js diff --git a/src/index.js b/src/index.js index 19f23c2..95e7d26 100644 --- a/src/index.js +++ b/src/index.js @@ -312,13 +312,27 @@ async function extensionDetails() { return { releaseTag: releaseTag, + extName: extName, extSoFile: `${extName}.so`, extPackageName: `php_${extName}-${releaseTag}_php${phpMajorMinor}-${arch}-${os}-${libcFlavour}${zendDebug}${ztsMode}.zip` }; } +async function smokeTest(extName, soPath) { + core.info("Smoke testing extension..."); + const phpBinary = await module.exports.determinePhpBinary(); + const result = await execInDocker(phpBinary, [ + "-d", `extension=${soPath}`, + "-r", `echo extension_loaded('${extName}') ? 'OK' : 'FAIL';`, + ]); + if (!result.stdout.includes("OK")) { + throw new Error(`Smoke test failed: extension '${extName}' did not load`); + } + core.info("Smoke test passed!"); +} + async function main() { - const { releaseTag, extSoFile, extPackageName } = await module.exports.extensionDetails(); + const { releaseTag, extName, extSoFile, extPackageName } = await module.exports.extensionDetails(); await module.exports.buildExtension(); @@ -326,6 +340,9 @@ async function main() { const modulesDir = path.join(buildPath, "modules"); await exec.exec("ls", ["-l", modulesDir]); + const soPath = path.resolve(modulesDir, extSoFile); + await module.exports.smokeTest(extName, soPath); + await exec.exec("zip", ["-j", extPackageName, path.join(modulesDir, extSoFile)]); await module.exports.uploadReleaseAsset(releaseTag, extPackageName); @@ -345,6 +362,7 @@ module.exports = { determinePhpDebugMode, determineZendThreadSafeMode, uploadReleaseAsset, + smokeTest, extensionDetails, main, }; diff --git a/tests/index.test.js b/tests/index.test.js index f482254..f90a6dd 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -552,6 +552,7 @@ describe('extensionDetails', () => { expect(await action.extensionDetails()) .toEqual({ releaseTag: '1.2.3', + extName: 'foo', extSoFile: 'foo.so', extPackageName: 'php_foo-1.2.3_php8.1-x86_64-linux-glibc.zip', }); @@ -572,6 +573,7 @@ describe('extensionDetails', () => { expect(await action.extensionDetails()) .toEqual({ releaseTag: '1.2.3', + extName: 'foo', extSoFile: 'foo.so', extPackageName: 'php_foo-1.2.3_php8.1-x86_64-linux-glibc-debug-zts.zip', }); @@ -582,10 +584,12 @@ describe('main', () => { test('main builds and uploads extension with default build path', async () => { jest.spyOn(action, 'extensionDetails').mockResolvedValue({ releaseTag: '1.2.3', + extName: 'foo', extSoFile: 'foo.so', extPackageName: 'php_foo-1.2.3_php8.1-x86_64-linux-glibc-debug-zts.zip', }); jest.spyOn(action, 'buildExtension').mockResolvedValue(); + jest.spyOn(action, 'smokeTest').mockResolvedValue(); jest.spyOn(action, 'uploadReleaseAsset').mockResolvedValue(); jest.spyOn(exec, 'exec').mockResolvedValue(); core.getInput.mockImplementation((name) => { @@ -605,10 +609,12 @@ describe('main', () => { test('main builds and uploads extension with custom build path', async () => { jest.spyOn(action, 'extensionDetails').mockResolvedValue({ releaseTag: '1.2.3', + extName: 'foo', extSoFile: 'foo.so', extPackageName: 'php_foo-1.2.3_php8.1-x86_64-linux-glibc-debug-zts.zip', }); jest.spyOn(action, 'buildExtension').mockResolvedValue(); + jest.spyOn(action, 'smokeTest').mockResolvedValue(); jest.spyOn(action, 'uploadReleaseAsset').mockResolvedValue(); jest.spyOn(exec, 'exec').mockResolvedValue(); core.getInput.mockImplementation((name) => { From 4845334c95cc55f7c24aaeb759e43b0a49f6403e Mon Sep 17 00:00:00 2001 From: hotrush Date: Mon, 20 Apr 2026 22:52:04 +0200 Subject: [PATCH 2/3] Smoke testing --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index a08ce74..9e5e8e5 100644 --- a/action.yml +++ b/action.yml @@ -27,5 +27,5 @@ outputs: package-path: description: 'Path to the generated .zip package' runs: - using: 'node22' + using: 'node24' main: dist/index.js From daa6d33a73c02fefaf88e1bb5eaf6591ad586f3d Mon Sep 17 00:00:00 2001 From: hotrush Date: Mon, 20 Apr 2026 23:06:04 +0200 Subject: [PATCH 3/3] Smoke testing --- src/index.js | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/index.js b/src/index.js index 95e7d26..aaef377 100644 --- a/src/index.js +++ b/src/index.js @@ -37,28 +37,6 @@ async function execInDocker(command, args = [], opts = {}) { return exec.getExecOutput(command, args, opts); } -/** - * Run a command via exec.exec (no output capture) either on host or in Docker. - */ -async function execRunInDocker(command, args = [], opts = {}) { - const dockerImage = getDockerImage(); - if (dockerImage) { - const workspace = process.env.GITHUB_WORKSPACE || process.cwd(); - const cwd = opts.cwd ? path.resolve(opts.cwd) : workspace; - const workDir = cwd.startsWith(workspace) - ? `/workspace${cwd.substring(workspace.length)}` - : "/workspace"; - - return exec.exec("docker", [ - "run", "--rm", - "-v", `${workspace}:/workspace`, - "-w", workDir, - dockerImage, - command, ...args, - ]); - } - return exec.exec(command, args, opts); -} async function determineExtensionNameFromComposerJson() { core.info("Detecting extension name from composer.json..."); @@ -321,12 +299,25 @@ async function extensionDetails() { async function smokeTest(extName, soPath) { core.info("Smoke testing extension..."); const phpBinary = await module.exports.determinePhpBinary(); - const result = await execInDocker(phpBinary, [ - "-d", `extension=${soPath}`, - "-r", `echo extension_loaded('${extName}') ? 'OK' : 'FAIL';`, - ]); - if (!result.stdout.includes("OK")) { - throw new Error(`Smoke test failed: extension '${extName}' did not load`); + const dockerImage = getDockerImage(); + + if (dockerImage) { + // Docker: fresh container may need runtime deps (libstdc++/libgcc for C++ extensions) + const result = await execInDocker("sh", [ + "-c", + `if command -v apk >/dev/null 2>&1; then apk add --no-cache libstdc++ libgcc; fi && ${phpBinary} -d extension=${soPath} -r "echo extension_loaded('${extName}') ? 'OK' : 'FAIL';"`, + ]); + if (!result.stdout.includes("OK")) { + throw new Error(`Smoke test failed: extension '${extName}' did not load`); + } + } else { + const result = await execInDocker(phpBinary, [ + "-d", `extension=${soPath}`, + "-r", `echo extension_loaded('${extName}') ? 'OK' : 'FAIL';`, + ]); + if (!result.stdout.includes("OK")) { + throw new Error(`Smoke test failed: extension '${extName}' did not load`); + } } core.info("Smoke test passed!"); } @@ -340,7 +331,7 @@ async function main() { const modulesDir = path.join(buildPath, "modules"); await exec.exec("ls", ["-l", modulesDir]); - const soPath = path.resolve(modulesDir, extSoFile); + const soPath = path.join(modulesDir, extSoFile); await module.exports.smokeTest(extName, soPath); await exec.exec("zip", ["-j", extPackageName, path.join(modulesDir, extSoFile)]);