Skip to content
Open
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
49 changes: 49 additions & 0 deletions launch-templates/linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,51 @@ common-js-init-steps: &common-js-init-steps
- name: Install Browsers (if needed)
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/install-browsers/main.yaml'

common-universal-init-steps: &common-universal-init-steps
- name: Checkout
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/checkout/main.yaml'
- name: Restore Node Modules Cache
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/cache/main.yaml'
inputs:
key: 'package-lock.json|yarn.lock|pnpm-lock.yaml|patches/**|.yarn/patches/**'
paths: 'node_modules'
base-branch: 'main'
- name: Restore Browser Binary Cache
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/cache/main.yaml'
inputs:
key: 'package-lock.json|yarn.lock|pnpm-lock.yaml|"browsers"'
paths: |
'../.cache/Cypress'
base-branch: 'main'
- name: Restore Rust Cache
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/cache/main.yaml'
inputs:
key: '"2" | Cargo.lock | rust-toolchain.toml'
paths: '~/.cargo'
base-branch: 'main'
- name: Restore Gradle Dependencies Cache
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/cache/main.yaml'
inputs:
key: '"1" | gradle/wrapper/gradle-wrapper.properties | gradle/libs.versions.toml | gradle.properties | settings.gradle.kts | build.gradle.kts'
paths: '~/.gradle/caches'
base-branch: 'main'
- name: Restore Gradle Wrapper Cache
uses: 'nrwl/nx-cloud-workflows/v5/workflow-steps/cache/main.yaml'
inputs:
key: '"1" | gradle/wrapper/gradle-wrapper.properties'
paths: '~/.gradle/wrapper'
base-branch: 'main'
- name: Install Mise (if config present)
uses: 'nrwl/nx-cloud-workflows/8997d86c066a752fc445b711e49b083c21816304/workflow-steps/install-mise-if-present/main.yaml'
- name: Install Rust (if Rust project)
uses: 'nrwl/nx-cloud-workflows/8997d86c066a752fc445b711e49b083c21816304/workflow-steps/install-rust-if-present/main.yaml'
- name: Install Node (if JS project)
uses: 'nrwl/nx-cloud-workflows/8997d86c066a752fc445b711e49b083c21816304/workflow-steps/install-node-if-present/main.yaml'
- name: Install Node Modules (if lock file)
uses: 'nrwl/nx-cloud-workflows/8997d86c066a752fc445b711e49b083c21816304/workflow-steps/install-node-modules-if-present/main.yaml'
- name: Install Java (if JVM project)
uses: 'nrwl/nx-cloud-workflows/8997d86c066a752fc445b711e49b083c21816304/workflow-steps/install-java-if-present/main.yaml'

launch-templates:
linux-small-js:
resource-class: 'docker_linux_amd64/small'
Expand Down Expand Up @@ -125,3 +170,7 @@ launch-templates:
resource-class: 'docker_linux_amd64/extra_large+'
image: 'ubuntu22.04-node20.19-v2'
init-steps: *common-dotnet-init-steps
universal-large:
resource-class: 'docker_linux_amd64/large'
image: 'ubuntu22.04-node20.19-v2'
init-steps: *common-universal-init-steps
148 changes: 148 additions & 0 deletions workflow-steps/install-java-if-present/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const { platform, arch, tmpdir } = require('os');
const { execSync } = require('child_process');
const { existsSync, writeFileSync } = require('fs');

const jvmMarkers = [
'build.gradle',
'build.gradle.kts',
'settings.gradle',
'settings.gradle.kts',
'pom.xml',
];

if (!jvmMarkers.some((f) => existsSync(f))) {
console.log(
'No JVM project detected (no build.gradle, build.gradle.kts, settings.gradle*, or pom.xml). Skipping Java install.',
);
return;
}

if (
existsSync('mise.toml') ||
existsSync('.mise.toml') ||
existsSync('.tool-versions')
) {
console.log(
'Mise config detected — Java is expected to be managed via mise. Skipping auto java=<version> install to avoid overwriting the user config.',
);
return;
}

const javaVersion = process.env.NX_CLOUD_INPUT_java_version || '21';
console.log(`JVM project detected. Installing Java ${javaVersion} via mise...`);

// Write a mise.toml so the inlined mise install picks up the tool.
writeFileSync('mise.toml', `[tools]\njava = "${javaVersion}"\n`, {
encoding: 'utf-8',
});

// --- inlined install-mise logic ---

const miseGhVersion =
process.env['NX_CLOUD_INPUT_mise-version'] || 'v2025.12.2';
const installArgs = process.env['NX_CLOUD_INPUT_install-args'] || '';

const MISE_INSTALL_DIR = '$HOME/.local/bin';
const MISE_SHIM_DIR = '$HOME/.local/share/mise/shims';
const TMP_DIR = tmpdir();

process.env.MISE_TRUSTED_CONFIG_PATHS = process.cwd();
process.env.MISE_YES = '1';

function retryWithBackoff(
fn,
maxRetries = 3,
retryDelayMs = 1000,
fnName = 'operation',
) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
fn();
if (attempt > 1)
console.log(`> ${fnName} succeeded on attempt ${attempt}`);
return;
} catch (error) {
lastError = error;
console.error(`> ${fnName} failed on attempt ${attempt}/${maxRetries}`);
console.error(` Error: ${error.message}`);
if (attempt < maxRetries) {
const delay = retryDelayMs * Math.pow(2, attempt - 1);
console.log(`> Retrying in ${delay}ms...`);
execSync(`sleep ${delay / 1000}`, { stdio: 'inherit' });
}
}
}
throw lastError;
}

if (!miseGhVersion.startsWith('v')) {
console.error(`Invalid mise_version: ${miseGhVersion}`);
process.exit(1);
}

function getVersionUrl(_platform, _arch) {
let resolvedPlatform;
let resolvedArch;
if (_platform === 'darwin') resolvedPlatform = 'macos';
else if (_platform === 'win32') resolvedPlatform = 'windows';
else resolvedPlatform = 'linux';

if (_arch === 'x64' || _arch === 'arm64') resolvedArch = _arch;
else {
console.error('Unsupported architecture: ', _arch);
process.exit(1);
}

const version = `${miseGhVersion}-${resolvedPlatform}-${resolvedArch}`;
const url = `https://github.com/jdx/mise/releases/download/${miseGhVersion}/mise-${version}.tar.gz`;
console.log(`> Resolved mise version to download as: ${version}`);
return url;
}

function downloadMise(downloadUrl) {
console.log(`> Downloading mise from ${downloadUrl}`);
execSync(`mkdir -p ${MISE_INSTALL_DIR}`, { stdio: 'inherit' });
retryWithBackoff(
() => {
execSync(`curl -fsSL ${downloadUrl} | tar -xzf - -C ${TMP_DIR}`, {
stdio: 'inherit',
});
},
3,
1000,
'mise download',
);
execSync(`mv ${TMP_DIR}/mise/bin/mise ${MISE_INSTALL_DIR}/mise`, {
stdio: 'inherit',
});
}

function setupMise(installCommandArgs) {
execSync(`chmod +x ${MISE_INSTALL_DIR}/mise`, { stdio: 'inherit' });
execSync(`mkdir -p ${MISE_SHIM_DIR}`, { stdio: 'inherit' });

const newPATH = `${MISE_INSTALL_DIR}:${MISE_SHIM_DIR}:$PATH`;
const setPath = `export PATH="${newPATH}" && echo PATH="${newPATH}" >> $NX_CLOUD_ENV`;
const setMiseEnvVars = `echo "MISE_YES=1" >> $NX_CLOUD_ENV && echo "MISE_TRUSTED_CONFIG_PATHS=$HOME/workspace" >> $NX_CLOUD_ENV`;
const whichMise = `echo "mise is located at $(which mise)"`;
const miseVersion = `echo "mise version is: $(mise version)"`;
const installMiseTools = `mise install ${installCommandArgs}`;

execSync(
[setPath, setMiseEnvVars, whichMise, miseVersion, installMiseTools].join(
' && ',
),
{ stdio: 'inherit' },
);
}

try {
const dlUrl = getVersionUrl(platform(), arch());
downloadMise(dlUrl);
setupMise(installArgs);
} catch (error) {
console.error('> Failed to install Java via mise:');
console.error(error);
process.exit(1);
}
10 changes: 10 additions & 0 deletions workflow-steps/install-java-if-present/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Install Java (if JVM project present)
description: Installs Java via mise when a Gradle or Maven project is detected (build.gradle, build.gradle.kts, settings.gradle*, or pom.xml). Otherwise skips.
inputs:
- name: java_version
description: 'Java version to install (passed to mise as java=<version>).'
default: '21'

definition:
using: 'node'
main: workflow-steps/install-java-if-present/main.js
133 changes: 133 additions & 0 deletions workflow-steps/install-mise-if-present/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
const { platform, arch, tmpdir } = require('os');
const { execSync } = require('child_process');
const { existsSync } = require('fs');

const miseMarkers = ['mise.toml', '.mise.toml', '.tool-versions'];

if (!miseMarkers.some((f) => existsSync(f))) {
console.log(
'No mise config detected (no mise.toml, .mise.toml, or .tool-versions). Skipping mise install.',
);
return;
}

// --- inlined install-mise logic (minus setToolVersions, since we use the
// existing mise config file from the repo) ---

const runInstall = process.env['NX_CLOUD_INPUT_auto-install']
? process.env['NX_CLOUD_INPUT_auto-install'] === 'true'
: true;
const miseGhVersion =
process.env['NX_CLOUD_INPUT_mise-version'] || 'v2025.12.2';
const installArgs = process.env['NX_CLOUD_INPUT_install-args'] || '';

const MISE_INSTALL_DIR = '$HOME/.local/bin';
const MISE_SHIM_DIR = '$HOME/.local/share/mise/shims';
const TMP_DIR = tmpdir();

process.env.MISE_TRUSTED_CONFIG_PATHS = process.cwd();
process.env.MISE_YES = '1';

function retryWithBackoff(
fn,
maxRetries = 3,
retryDelayMs = 1000,
fnName = 'operation',
) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
fn();
if (attempt > 1)
console.log(`> ${fnName} succeeded on attempt ${attempt}`);
return;
} catch (error) {
lastError = error;
console.error(`> ${fnName} failed on attempt ${attempt}/${maxRetries}`);
console.error(` Error: ${error.message}`);
if (attempt < maxRetries) {
const delay = retryDelayMs * Math.pow(2, attempt - 1);
console.log(`> Retrying in ${delay}ms...`);
execSync(`sleep ${delay / 1000}`, { stdio: 'inherit' });
}
}
}
console.error(`> ${fnName} failed after ${maxRetries} attempts`);
throw lastError;
}

if (!miseGhVersion.startsWith('v')) {
console.error(`Invalid mise_version: ${miseGhVersion}`);
process.exit(1);
}

function getVersionUrl(_platform, _arch) {
let resolvedPlatform;
let resolvedArch;
if (_platform === 'darwin') resolvedPlatform = 'macos';
else if (_platform === 'win32') resolvedPlatform = 'windows';
else resolvedPlatform = 'linux';

if (_arch === 'x64' || _arch === 'arm64') resolvedArch = _arch;
else {
console.error('Unsupported architecture: ', _arch);
process.exit(1);
}

const version = `${miseGhVersion}-${resolvedPlatform}-${resolvedArch}`;
const url = `https://github.com/jdx/mise/releases/download/${miseGhVersion}/mise-${version}.tar.gz`;
console.log(`> Resolved mise version to download as: ${version}`);
return url;
}

function downloadMise(downloadUrl) {
console.log(`> Downloading mise from ${downloadUrl}`);
execSync(`mkdir -p ${MISE_INSTALL_DIR}`, { stdio: 'inherit' });
retryWithBackoff(
() => {
execSync(`curl -fsSL ${downloadUrl} | tar -xzf - -C ${TMP_DIR}`, {
stdio: 'inherit',
});
},
3,
1000,
'mise download',
);
console.log(`> Download successful! Moving binary to: ${MISE_INSTALL_DIR}`);
execSync(`mv ${TMP_DIR}/mise/bin/mise ${MISE_INSTALL_DIR}/mise`, {
stdio: 'inherit',
});
}

function setupMise(autoInstall, installCommandArgs) {
console.log('> Setting up mise...\n\n');
execSync(`chmod +x ${MISE_INSTALL_DIR}/mise`, { stdio: 'inherit' });
execSync(`mkdir -p ${MISE_SHIM_DIR}`, { stdio: 'inherit' });

const newPATH = `${MISE_INSTALL_DIR}:${MISE_SHIM_DIR}:$PATH`;
const setPath = `export PATH="${newPATH}" && echo PATH="${newPATH}" >> $NX_CLOUD_ENV`;
const setMiseEnvVars = `echo "MISE_YES=1" >> $NX_CLOUD_ENV && echo "MISE_TRUSTED_CONFIG_PATHS=$HOME/workspace" >> $NX_CLOUD_ENV`;
const whichMise = `echo "mise is located at $(which mise)"`;
const miseVersion = `echo "mise version is: $(mise version)"`;
const installMiseTools = autoInstall
? `mise install ${installCommandArgs}`
: `echo "Skipping auto install. You will need to manually run mise install"`;

execSync(
[setPath, setMiseEnvVars, whichMise, miseVersion, installMiseTools].join(
' && ',
),
{ stdio: 'inherit' },
);
console.log('\n> Finished setting up mise!');
}

try {
const dlUrl = getVersionUrl(platform(), arch());
downloadMise(dlUrl);
setupMise(runInstall, installArgs);
} catch (error) {
console.error('> Failed to install mise: ');
console.error(error);
process.exit(1);
}
6 changes: 6 additions & 0 deletions workflow-steps/install-mise-if-present/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: Install Mise (if mise config present)
description: Installs mise and any tools defined in mise.toml, .mise.toml, or .tool-versions. Skips if no config is detected.

definition:
using: 'node'
main: workflow-steps/install-mise-if-present/main.js
Loading
Loading