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
12 changes: 8 additions & 4 deletions .github/workflows/test-workflow.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
name: Test

on: [push]
on: [push, pull_request]

jobs:
Test:
runs-on: ${{ matrix.os }}
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
Expand All @@ -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
Expand Down
176 changes: 154 additions & 22 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down Expand Up @@ -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 {
Expand All @@ -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.`);
}
}
Expand Down Expand Up @@ -120,31 +147,39 @@ async function main(){
//
// <version> is a string of form X.Y.Z corresponding to a release of fpm
// <os> is either 'linux', 'macos', or 'windows'
// <arch> here is always 'x86_64'
// <arch> is 'x86_64' or 'arm64'
// <compiler> 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;
}
Expand All @@ -165,5 +200,102 @@ 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
// 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-<version> (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();
Loading