Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ outputs:
package-path:
description: 'Path to the generated .zip package'
runs:
using: 'node20'
using: 'node24'
main: dist/index.js
55 changes: 32 additions & 23 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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...");
Expand Down Expand Up @@ -312,20 +290,50 @@ 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 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!");
}

async function main() {
const { releaseTag, extSoFile, extPackageName } = await module.exports.extensionDetails();
const { releaseTag, extName, extSoFile, extPackageName } = await module.exports.extensionDetails();

await module.exports.buildExtension();

const buildPath = core.getInput("build-path") || ".";
const modulesDir = path.join(buildPath, "modules");
await exec.exec("ls", ["-l", modulesDir]);

const soPath = path.join(modulesDir, extSoFile);
await module.exports.smokeTest(extName, soPath);

await exec.exec("zip", ["-j", extPackageName, path.join(modulesDir, extSoFile)]);

await module.exports.uploadReleaseAsset(releaseTag, extPackageName);
Expand All @@ -345,6 +353,7 @@ module.exports = {
determinePhpDebugMode,
determineZendThreadSafeMode,
uploadReleaseAsset,
smokeTest,
extensionDetails,
main,
};
Expand Down
6 changes: 6 additions & 0 deletions tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
Expand All @@ -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',
});
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand Down
Loading