From cf9937878875aa46c3c551e94049d92f44d1f48e Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 9 Oct 2025 09:34:28 +0200 Subject: [PATCH 1/3] support macos-14 --- .github/workflows/test-workflow.yml | 10 +- index.js | 177 ++++++++++++++++++++++++---- 2 files changed, 162 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test-workflow.yml b/.github/workflows/test-workflow.yml index 745986a..8fc3f1d 100644 --- a/.github/workflows/test-workflow.yml +++ b/.github/workflows/test-workflow.yml @@ -8,9 +8,13 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-13, windows-latest] - fpm-version: ["v0.8.0", "v0.9.0", "latest"] + os: [ubuntu-latest, macos-13, macos-14, windows-latest] + fpm-version: ["v0.8.0", "v0.9.0", "v0.10.1", "latest"] node-version: ["20.x"] + exclude: + # v0.8.0 install.sh is broken, can't be built from source on macOS ARM64 + - os: macos-14 + fpm-version: "v0.8.0" steps: - name: Checkout @@ -24,7 +28,7 @@ jobs: - name: MacOS system setup if: runner.os == 'macOS' run: | - brew install gcc@10 + brew install gcc@13 - name: Install dependencies run: npm ci diff --git a/index.js b/index.js index b7d4371..eed78bc 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ const io = require('@actions/io'); const exec = require('@actions/exec'); const path = require('path'); const github = require('@actions/github'); +const os = require('os'); // Main entry function // @@ -39,32 +40,36 @@ async function main(){ } + // Detect architecture + const arch = os.arch(); + console.log(`System architecture: ${arch}`); + console.log(`This platform is ${process.platform}`); + // Build download path const fetchPath = fpmRepo + '/releases/download/' + fpmVersion + '/'; - const filename = getFPMFilename(fpmVersion,process.platform); + const filename = getFPMFilename(fpmVersion, process.platform, arch); - console.log(`This platform is ${process.platform}`); console.log(`Fetching fpm from ${fetchPath}${filename}`); // Download release var fpmPath; try { - - // Try downloading the file without the compiler suffix - const filename = getFPMFilename(fpmVersion, process.platform); + + // Try downloading the file without the compiler suffix + const filename = getFPMFilename(fpmVersion, process.platform, arch); fpmPath = await tc.downloadTool(fetchPath + filename); } catch (error) { - + // If download fails, try adding compiler suffixes const compilers = ['gcc-10', 'gcc-11', 'gcc-12', 'gcc-13', 'gcc-14']; - + let success = false; - + for (const compiler of compilers) { - + // Generate the filename with the compiler suffix - const filenameWithSuffix = getFPMFilename(fpmVersion, process.platform, compiler); + const filenameWithSuffix = getFPMFilename(fpmVersion, process.platform, arch, compiler); console.log(`Trying to fetch compiler-built fpm: ${filenameWithSuffix}`); try { @@ -74,10 +79,32 @@ async function main(){ } catch (error) { console.log(` -> Failed to download ${filenameWithSuffix}`); } - + } if (!success) { + // On macOS ARM64, fall back to building from source + if (process.platform === 'darwin' && arch === 'arm64') { + console.log('No pre-built ARM64 binary found, falling back to building from source'); + + // For older versions without working install.sh, we can't build from source + const versionNum = fpmVersion.replace('v', ''); + const versionParts = versionNum.split('.').map(Number); + const isOldVersion = versionParts[0] === 0 && versionParts[1] < 9; + + if (isOldVersion) { + core.setFailed( + `Building fpm ${fpmVersion} from source is not supported on macOS ARM64.\n` + + 'Please use fpm v0.9.0 or later, which has a working install.sh script.\n' + + 'For example, set fpm-version to "v0.9.0", "v0.10.1" or "latest".' + ); + return; + } + + await installFromSource(fpmVersion, fpmRepo); + return; + } + core.setFailed(`Error while trying to fetch fpm - please check that a version exists at the above release url.`); } } @@ -120,31 +147,39 @@ async function main(){ // // is a string of form X.Y.Z corresponding to a release of fpm // is either 'linux', 'macos', or 'windows' -// here is always 'x86_64' +// is 'x86_64' or 'arm64' // is an optional string like '-gcc-12' // -function getFPMFilename(fpmVersion, platform, compiler = '') { +function getFPMFilename(fpmVersion, platform, arch, compiler = '') { var filename = 'fpm-'; - + // Remove the leading 'v' if it exists filename += fpmVersion.replace('v', '') + '-'; + // Map Node.js arch to FPM arch naming + let fpmArch = 'x86_64'; + if (arch === 'arm64') { + fpmArch = 'arm64'; + } else if (arch === 'x64') { + fpmArch = 'x86_64'; + } + // Add the platform and architecture if (platform === 'linux') { - filename += 'linux-x86_64'; + filename += `linux-${fpmArch}`; } else if (platform === 'darwin') { - filename += 'macos-x86_64'; + filename += `macos-${fpmArch}`; } else if (platform === 'win32') { - filename += 'windows-x86_64'; + filename += `windows-${fpmArch}`; } else { core.setFailed('Unknown platform'); } - // If a compiler is provided, append it as a suffix - if (compiler) filename += `-${compiler}`; - - // Add the '.exe' suffix for Windows - if (platform === 'win32') filename += '.exe'; + // If a compiler is provided, append it as a suffix + if (compiler) filename += `-${compiler}`; + + // Add the '.exe' suffix for Windows + if (platform === 'win32') filename += '.exe'; return filename; } @@ -165,5 +200,103 @@ async function getLatestReleaseVersion(token){ } +// Install fpm from source using the install.sh script +// This is used on macOS ARM64 where pre-built binaries are not available +// +async function installFromSource(fpmVersion, fpmRepo){ + + try { + + // Find gfortran - it may be versioned (e.g., gfortran-13) + // Use gcc <= 13 for compatibility with older fpm versions + let gfortranCmd = 'gfortran'; + let foundGfortran = false; + + try { + await exec.exec('which', ['gfortran'], { silent: true }); + foundGfortran = true; + } catch (error) { + // gfortran not found, try versioned + } + + // If we found unversioned gfortran, check if it's gcc >= 14 + // If so, or if we didn't find unversioned gfortran, look for a versioned one <= 13 + if (!foundGfortran || true) { // Always prefer versioned <= 13 for compatibility + let foundVersioned = false; + for (const ver of [13, 12, 11, 10]) { + try { + await exec.exec('which', [`gfortran-${ver}`], { silent: true }); + gfortranCmd = `gfortran-${ver}`; + foundVersioned = true; + console.log(`Found ${gfortranCmd}`); + break; + } catch (e) { + // Continue searching + } + } + + if (!foundVersioned && !foundGfortran) { + core.setFailed( + 'gfortran is required to build fpm from source on macOS ARM64.\n' + + 'Please install gcc version 10-13 before running this action, for example:\n' + + ' - name: Install gfortran\n' + + ' run: brew install gcc@13\n' + + 'Or use fortran-lang/setup-fortran to install a Fortran compiler.' + ); + return; + } + } + + const versionNumber = fpmVersion.replace('v', ''); + + // Download the full source tarball for the requested version + const tarballUrl = `${fpmRepo}/archive/refs/tags/${fpmVersion}.tar.gz`; + console.log(`Downloading fpm source from: ${tarballUrl}`); + const tarballPath = await tc.downloadTool(tarballUrl); + + // Create a temporary directory for building + const buildDir = path.join(process.env.RUNNER_TEMP || '/tmp', 'fpm-build'); + await io.mkdirP(buildDir); + + // Extract the tarball + const extractDir = await tc.extractTar(tarballPath, buildDir); + console.log(`Extracted to: ${extractDir}`); + + // The extracted directory will be named fpm- (without the 'v') + const fpmSourceDir = path.join(buildDir, `fpm-${versionNumber}`); + + console.log('Installing fpm from source using install.sh...'); + + // Create installation directory + const installPrefix = path.join(process.env.HOME || process.env.USERPROFILE || '/tmp', '.local'); + const installDir = path.join(installPrefix, 'bin'); + await io.mkdirP(installDir); + + // Run the install.sh script with FC environment variable + const installScript = path.join(fpmSourceDir, 'install.sh'); + await exec.exec('bash', [installScript, `--prefix=${installPrefix}`], { + cwd: fpmSourceDir, + env: { + ...process.env, + FC: gfortranCmd + } + }); + + // Add to path + core.addPath(installDir); + console.log(`fpm installed and added to path at ${installDir}`); + + // Clean up build directory + await io.rmRF(buildDir); + + } catch (error) { + + core.setFailed(`Failed to install fpm from source: ${error.message}`); + + } + +} + + // Call entry function main(); From 7e438b045c2fa3c28d6ce29fbdbb7d542d8a2d66 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 9 Oct 2025 10:03:12 +0200 Subject: [PATCH 2/3] test on pull request --- .github/workflows/test-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-workflow.yml b/.github/workflows/test-workflow.yml index 8fc3f1d..3a5954f 100644 --- a/.github/workflows/test-workflow.yml +++ b/.github/workflows/test-workflow.yml @@ -1,6 +1,6 @@ name: Test -on: [push] +on: [push, pull_request] jobs: Test: From 2315d557fc19ee4150678bad1316398f923b5a33 Mon Sep 17 00:00:00 2001 From: Federico Perini Date: Thu, 9 Oct 2025 11:07:04 +0200 Subject: [PATCH 3/3] Update index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- index.js | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index eed78bc..5986544 100644 --- a/index.js +++ b/index.js @@ -221,30 +221,29 @@ async function installFromSource(fpmVersion, fpmRepo){ // If we found unversioned gfortran, check if it's gcc >= 14 // If so, or if we didn't find unversioned gfortran, look for a versioned one <= 13 - if (!foundGfortran || true) { // Always prefer versioned <= 13 for compatibility - let foundVersioned = false; - for (const ver of [13, 12, 11, 10]) { - try { - await exec.exec('which', [`gfortran-${ver}`], { silent: true }); - gfortranCmd = `gfortran-${ver}`; - foundVersioned = true; - console.log(`Found ${gfortranCmd}`); - break; - } catch (e) { - // Continue searching - } + // Always prefer versioned <= 13 for compatibility + let foundVersioned = false; + for (const ver of [13, 12, 11, 10]) { + try { + await exec.exec('which', [`gfortran-${ver}`], { silent: true }); + gfortranCmd = `gfortran-${ver}`; + foundVersioned = true; + console.log(`Found ${gfortranCmd}`); + break; + } catch (e) { + // Continue searching } + } - if (!foundVersioned && !foundGfortran) { - core.setFailed( - 'gfortran is required to build fpm from source on macOS ARM64.\n' + - 'Please install gcc version 10-13 before running this action, for example:\n' + - ' - name: Install gfortran\n' + - ' run: brew install gcc@13\n' + - 'Or use fortran-lang/setup-fortran to install a Fortran compiler.' - ); - return; - } + if (!foundVersioned && !foundGfortran) { + core.setFailed( + 'gfortran is required to build fpm from source on macOS ARM64.\n' + + 'Please install gcc version 10-13 before running this action, for example:\n' + + ' - name: Install gfortran\n' + + ' run: brew install gcc@13\n' + + 'Or use fortran-lang/setup-fortran to install a Fortran compiler.' + ); + return; } const versionNumber = fpmVersion.replace('v', '');