Compare commits

...

3 Commits

Author SHA1 Message Date
Nikolas Grottendieck
e39e1df1b4
Merge b8162e8075 into 2dfa2011c5 2024-09-08 22:40:26 +02:00
aparnajyothi-y
2dfa2011c5
basic validation failure fix (#682) 2024-09-06 14:42:39 -05:00
Nikolas Grottendieck
b8162e8075
fix: Maven Toolchains grows unexpectedly
On self-hosted runners toolchains.xml may survive multiple runs and unexpectedly
grow as a result of the toolchains setup simply appending the JDK definition
even if one with the same `type` and `provides.id` already exists.

Restructuring the parsing step and filtering the potentially existing list of
toolchain definitions prevents this and also fixes toolchain.xml files that
already contain duplicates.

Fixes #530
2023-12-09 11:34:18 +01:00
7 changed files with 668 additions and 143 deletions

View File

@ -1,5 +1,5 @@
import { HttpClient } from '@actions/http-client';
import { SapMachineDistribution } from '../../src/distributions/sapmachine/installer';
import {HttpClient} from '@actions/http-client';
import {SapMachineDistribution} from '../../src/distributions/sapmachine/installer';
import * as utils from '../../src/util';
import manifestData from '../data/sapmachine.json';
@ -43,13 +43,13 @@ describe('getAvailableVersions', () => {
spyHttpClient.mockReturnValueOnce({
statusCode: 404,
headers: {},
result: ""
})
result: ''
});
spyHttpClient.mockReturnValueOnce({
statusCode: 200,
headers: {},
result: manifestData
})
});
const version = '17';
const distribution = new SapMachineDistribution({
@ -65,8 +65,10 @@ describe('getAvailableVersions', () => {
version
);
expect(availableVersion).not.toBeNull();
expect(availableVersion.url).toBe('https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-x64_bin.tar.gz');
})
expect(availableVersion.url).toBe(
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-x64_bin.tar.gz'
);
});
});
describe('getAvailableVersions', () => {
@ -77,7 +79,7 @@ describe('getAvailableVersions', () => {
['16.0.1', 'x64', 'linux', 71],
['23-ea', 'x64', 'linux', 798],
['23-ea', 'aarch64', 'windows', 0],
['23-ea', 'x64', 'windows', 750],
['23-ea', 'x64', 'windows', 750]
])(
'should get right number of available versions from JSON',
async (
@ -209,7 +211,7 @@ describe('getAvailableVersions', () => {
'x64',
'jdk',
'https://github.com/SAP/SapMachine/releases/download/sapmachine-17.0.10/sapmachine-jdk-17.0.10_linux-x64-musl_bin.tar.gz'
],
]
])(
'should return proper link according to the specified java-version, platform and arch',
async (
@ -224,8 +226,7 @@ describe('getAvailableVersions', () => {
version: version,
architecture: arch,
packageType: packageType,
checkLatest: false,
checkLatest: false
});
mockPlatform(distribution, platform);
@ -247,15 +248,20 @@ describe('getAvailableVersions', () => {
['8-ea', 'linux', 'x64', '8'],
['21.0.3+7', 'linux', 'x64', '21.0.3+7'],
['21.0.3+8-ea', 'linux', 'x64', '21.0.3+8'],
['17', 'linux-muse', 'aarch64'],
['17', 'linux-muse', 'aarch64']
])(
'should throw when required version of JDK can not be found in the JSON',
async (version: string, platform: string, arch: string, normalizedVersion: string = version) => {
async (
version: string,
platform: string,
arch: string,
normalizedVersion: string = version
) => {
const distribution = new SapMachineDistribution({
version: version,
architecture: arch,
packageType: 'jdk',
checkLatest: false,
checkLatest: false
});
mockPlatform(distribution, platform);
@ -280,7 +286,9 @@ describe('getAvailableVersions', () => {
mockPlatform(distribution, platform);
await expect(
distribution['findPackageForDownload'](jdkVersion)
).rejects.toThrow('SapMachine provides only the `jdk` and `jre` package type');
).rejects.toThrow(
'SapMachine provides only the `jdk` and `jre` package type'
);
});
});
});

View File

@ -143,7 +143,20 @@ describe('toolchains tests', () => {
</toolchain>
</toolchains>`;
const result = `<?xml version="1.0"?>
<toolchains>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
@ -155,6 +168,442 @@ describe('toolchains tests', () => {
<jdkHome>/opt/jdk/sun/1.6</jdkHome>
</configuration>
</toolchain>
</toolchains>`;
fs.mkdirSync(m2Dir, {recursive: true});
fs.writeFileSync(toolchainsFile, originalFile);
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
await toolchains.createToolchainsSettings({
jdkInfo,
settingsDirectory: m2Dir,
overwriteSettings: true
});
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
);
expect(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
).toEqual(result);
}, 100000);
it('does not discard custom elements in existing toolchain definitions', async () => {
const jdkInfo = {
version: '17',
vendor: 'Eclipse Temurin',
id: 'temurin_17',
jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
};
const originalFile = `<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>1.6</version>
<vendor>Sun</vendor>
<id>sun_1.6</id>
<custom>foo</custom>
</provides>
<configuration>
<jdkHome>/opt/jdk/sun/1.6</jdkHome>
<fooHome>/usr/local/bin/bash</fooHome>
</configuration>
</toolchain>
</toolchains>`;
const result = `<?xml version="1.0"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<version>1.6</version>
<vendor>Sun</vendor>
<id>sun_1.6</id>
<custom>foo</custom>
</provides>
<configuration>
<jdkHome>/opt/jdk/sun/1.6</jdkHome>
<fooHome>/usr/local/bin/bash</fooHome>
</configuration>
</toolchain>
</toolchains>`;
fs.mkdirSync(m2Dir, {recursive: true});
fs.writeFileSync(toolchainsFile, originalFile);
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
await toolchains.createToolchainsSettings({
jdkInfo,
settingsDirectory: m2Dir,
overwriteSettings: true
});
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
);
expect(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
).toEqual(result);
}, 100000);
it('does not discard existing, custom toolchain definitions', async () => {
const jdkInfo = {
version: '17',
vendor: 'Eclipse Temurin',
id: 'temurin_17',
jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
};
const originalFile = `<toolchains>
<toolchain>
<type>foo</type>
<provides>
<custom>baz</custom>
</provides>
<configuration>
<fooHome>/usr/local/bin/foo</fooHome>
</configuration>
</toolchain>
</toolchains>`;
const result = `<?xml version="1.0"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>foo</type>
<provides>
<custom>baz</custom>
</provides>
<configuration>
<fooHome>/usr/local/bin/foo</fooHome>
</configuration>
</toolchain>
</toolchains>`;
fs.mkdirSync(m2Dir, {recursive: true});
fs.writeFileSync(toolchainsFile, originalFile);
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
await toolchains.createToolchainsSettings({
jdkInfo,
settingsDirectory: m2Dir,
overwriteSettings: true
});
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
);
expect(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
).toEqual(result);
}, 100000);
it('does not duplicate existing toolchain definitions', async () => {
const jdkInfo = {
version: '17',
vendor: 'Eclipse Temurin',
id: 'temurin_17',
jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
};
const originalFile = `<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
</toolchains>`;
const result = `<?xml version="1.0"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
</toolchains>`;
fs.mkdirSync(m2Dir, {recursive: true});
fs.writeFileSync(toolchainsFile, originalFile);
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
await toolchains.createToolchainsSettings({
jdkInfo,
settingsDirectory: m2Dir,
overwriteSettings: true
});
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
);
expect(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
).toEqual(result);
}, 100000);
it('does not duplicate existing toolchain definitions if multiple exist', async () => {
const jdkInfo = {
version: '17',
vendor: 'Eclipse Temurin',
id: 'temurin_17',
jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
};
const originalFile = `<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>1.6</version>
<vendor>Sun</vendor>
<id>sun_1.6</id>
</provides>
<configuration>
<jdkHome>/opt/jdk/sun/1.6</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
</toolchains>`;
const result = `<?xml version="1.0"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
<toolchain>
<type>jdk</type>
<provides>
<version>1.6</version>
<vendor>Sun</vendor>
<id>sun_1.6</id>
</provides>
<configuration>
<jdkHome>/opt/jdk/sun/1.6</jdkHome>
</configuration>
</toolchain>
</toolchains>`;
fs.mkdirSync(m2Dir, {recursive: true});
fs.writeFileSync(toolchainsFile, originalFile);
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
await toolchains.createToolchainsSettings({
jdkInfo,
settingsDirectory: m2Dir,
overwriteSettings: true
});
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
);
expect(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
).toEqual(result);
}, 100000);
it('handles an empty list of existing toolchains correctly', async () => {
const jdkInfo = {
version: '17',
vendor: 'Eclipse Temurin',
id: 'temurin_17',
jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
};
const originalFile = `<toolchains>
</toolchains>`;
const result = `<?xml version="1.0"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>
<version>17</version>
<vendor>Eclipse Temurin</vendor>
<id>temurin_17</id>
</provides>
<configuration>
<jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
</configuration>
</toolchain>
</toolchains>`;
fs.mkdirSync(m2Dir, {recursive: true});
fs.writeFileSync(toolchainsFile, originalFile);
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
await toolchains.createToolchainsSettings({
jdkInfo,
settingsDirectory: m2Dir,
overwriteSettings: true
});
expect(fs.existsSync(m2Dir)).toBe(true);
expect(fs.existsSync(toolchainsFile)).toBe(true);
expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
);
expect(
toolchains.generateToolchainDefinition(
originalFile,
jdkInfo.version,
jdkInfo.vendor,
jdkInfo.id,
jdkInfo.jdkHome
)
).toEqual(result);
}, 100000);
it('handles an empty existing toolchains.xml correctly', async () => {
const jdkInfo = {
version: '17',
vendor: 'Eclipse Temurin',
id: 'temurin_17',
jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
};
const originalFile = ``;
const result = `<?xml version="1.0"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<toolchain>
<type>jdk</type>
<provides>

83
dist/setup/index.js vendored
View File

@ -125012,7 +125012,7 @@ class SapMachineDistribution extends base_installer_1.JavaBase {
continue;
}
// skip earlyAccessVersions if stable version requested
if (this.stable && buildVersionMap.ea === "true") {
if (this.stable && buildVersionMap.ea === 'true') {
continue;
}
for (const [edition, editionAssets] of Object.entries(buildVersionMap.assets)) {
@ -125038,7 +125038,7 @@ class SapMachineDistribution extends base_installer_1.JavaBase {
version: buildVersionWithoutPrefix,
checksum: contentTypeAssets.checksum,
downloadLink: contentTypeAssets.url,
packageType: edition,
packageType: edition
});
}
}
@ -125938,46 +125938,53 @@ function createToolchainsSettings({ jdkInfo, settingsDirectory, overwriteSetting
exports.createToolchainsSettings = createToolchainsSettings;
// only exported for testing purposes
function generateToolchainDefinition(original, version, vendor, id, jdkHome) {
let xmlObj;
let jsToolchains = [
{
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
}
];
if (original === null || original === void 0 ? void 0 : original.length) {
xmlObj = (0, xmlbuilder2_1.create)(original)
// convert existing toolchains into TS native objects for better handling
// xmlbuilder2 will convert the document into a `{toolchains: { toolchain: [] | {} }}` structure
// instead of the desired `toolchains: [{}]` one or simply `[{}]`
const jsObj = (0, xmlbuilder2_1.create)(original)
.root()
.ele({
toolchain: {
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
.toObject();
if (jsObj.toolchains && jsObj.toolchains.toolchain) {
// in case only a single child exists xmlbuilder2 will not create an array and using verbose = true equally doesn't work here
// See https://oozcitak.github.io/xmlbuilder2/serialization.html#js-object-and-map-serializers for details
if (Array.isArray(jsObj.toolchains.toolchain)) {
jsToolchains.push(...jsObj.toolchains.toolchain);
}
});
else {
jsToolchains.push(jsObj.toolchains.toolchain);
}
}
// remove potential duplicates based on type & id (which should be a unique combination);
// self.findIndex will only return the first occurrence, ensuring duplicates are skipped
jsToolchains = jsToolchains.filter((value, index, self) =>
// ensure non-jdk toolchains are kept in the results, we must not touch them because they belong to the user
value.type !== 'jdk' ||
index ===
self.findIndex(t => t.type === value.type && t.provides.id === value.provides.id));
}
else
xmlObj = (0, xmlbuilder2_1.create)({
toolchains: {
'@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'@xsi:schemaLocation': 'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
toolchain: [
{
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
}
]
}
});
return xmlObj.end({
// TODO: technically bad because we shouldn't re-create the toolchains root node (with possibly different schema values) if it already exists, however, just overriding the toolchain array with xmlbuilder2 is … uh non-trivial
return (0, xmlbuilder2_1.create)({
toolchains: {
'@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'@xsi:schemaLocation': 'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
toolchain: jsToolchains
}
}).end({
format: 'xml',
wellFormed: false,
headless: false,

View File

@ -25,7 +25,7 @@ enum JavaDistribution {
Corretto = 'corretto',
Oracle = 'oracle',
Dragonwell = 'dragonwell',
SapMachine = 'sapmachine',
SapMachine = 'sapmachine'
}
export function getJavaDistribution(

View File

@ -2,7 +2,7 @@ import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import semver from 'semver';
import fs from 'fs';
import { OutgoingHttpHeaders } from 'http';
import {OutgoingHttpHeaders} from 'http';
import path from 'path';
import {
convertVersionToSemver,
@ -11,13 +11,13 @@ import {
getGitHubHttpHeaders,
isVersionSatisfies
} from '../../util';
import { JavaBase } from '../base-installer';
import {JavaBase} from '../base-installer';
import {
JavaDownloadRelease,
JavaInstallerOptions,
JavaInstallerResults
} from '../base-models';
import { ISapMachineAllVersions, ISapMachineVersions } from './models';
import {ISapMachineAllVersions, ISapMachineVersions} from './models';
export class SapMachineDistribution extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) {
@ -27,10 +27,12 @@ export class SapMachineDistribution extends JavaBase {
protected async findPackageForDownload(
version: string
): Promise<JavaDownloadRelease> {
core.debug(`Only stable versions: ${this.stable}`)
core.debug(`Only stable versions: ${this.stable}`);
if (!['jdk', 'jre'].includes(this.packageType)) {
throw new Error('SapMachine provides only the `jdk` and `jre` package type');
throw new Error(
'SapMachine provides only the `jdk` and `jre` package type'
);
}
const availableVersions = await this.getAvailableVersions();
@ -60,10 +62,15 @@ export class SapMachineDistribution extends JavaBase {
const platform = this.getPlatformOption();
const arch = this.distributionArchitecture();
let fetchedReleasesJson = await this.fetchReleasesFromUrl('https://sap.github.io/SapMachine/assets/data/sapmachine-releases-all.json')
let fetchedReleasesJson = await this.fetchReleasesFromUrl(
'https://sap.github.io/SapMachine/assets/data/sapmachine-releases-all.json'
);
if (!fetchedReleasesJson) {
fetchedReleasesJson = await this.fetchReleasesFromUrl('https://api.github.com/repos/SAP/SapMachine/contents/assets/data/sapmachine-releases-all.json?ref=gh-pages', getGitHubHttpHeaders());
fetchedReleasesJson = await this.fetchReleasesFromUrl(
'https://api.github.com/repos/SAP/SapMachine/contents/assets/data/sapmachine-releases-all.json?ref=gh-pages',
getGitHubHttpHeaders()
);
}
if (!fetchedReleasesJson) {
@ -117,29 +124,42 @@ export class SapMachineDistribution extends JavaBase {
this.architecture
);
return { version: javaRelease.version, path: javaPath };
return {version: javaRelease.version, path: javaPath};
}
private parseVersions(
platform: string,
arch: string,
versions: ISapMachineAllVersions,
versions: ISapMachineAllVersions
): ISapMachineVersions[] {
const eligibleVersions: ISapMachineVersions[] = [];
for (const [, majorVersionMap] of Object.entries(versions)) {
for (const [, jdkVersionMap] of Object.entries(majorVersionMap.updates)) {
for (const [buildVersion, buildVersionMap] of Object.entries(jdkVersionMap)) {
let buildVersionWithoutPrefix = buildVersion.replace('sapmachine-', '');
for (const [buildVersion, buildVersionMap] of Object.entries(
jdkVersionMap
)) {
let buildVersionWithoutPrefix = buildVersion.replace(
'sapmachine-',
''
);
if (!buildVersionWithoutPrefix.includes('.')) {
// replace major version with major.minor.patch and keep the remaining build identifier after the + as is with regex
buildVersionWithoutPrefix = buildVersionWithoutPrefix.replace(/(\d+)(\+.*)?/, '$1.0.0$2');
buildVersionWithoutPrefix = buildVersionWithoutPrefix.replace(
/(\d+)(\+.*)?/,
'$1.0.0$2'
);
}
// replace + with . to convert to semver format if we have more than 3 version digits
if (buildVersionWithoutPrefix.split('.').length > 3) {
buildVersionWithoutPrefix = buildVersionWithoutPrefix.replace('+', '.');
buildVersionWithoutPrefix = buildVersionWithoutPrefix.replace(
'+',
'.'
);
}
buildVersionWithoutPrefix = convertVersionToSemver(buildVersionWithoutPrefix);
buildVersionWithoutPrefix = convertVersionToSemver(
buildVersionWithoutPrefix
);
// ignore invalid version
if (!semver.valid(buildVersionWithoutPrefix)) {
@ -148,23 +168,29 @@ export class SapMachineDistribution extends JavaBase {
}
// skip earlyAccessVersions if stable version requested
if (this.stable && buildVersionMap.ea === "true") {
if (this.stable && buildVersionMap.ea === 'true') {
continue;
}
for (const [edition, editionAssets] of Object.entries(buildVersionMap.assets)) {
for (const [edition, editionAssets] of Object.entries(
buildVersionMap.assets
)) {
if (this.packageType !== edition) {
continue;
}
for (const [archAndPlatForm, archAssets] of Object.entries(editionAssets)) {
let expectedArchAndPlatform = `${platform}-${arch}`
for (const [archAndPlatForm, archAssets] of Object.entries(
editionAssets
)) {
let expectedArchAndPlatform = `${platform}-${arch}`;
if (platform === 'linux-musl') {
expectedArchAndPlatform = `linux-${arch}-musl`
expectedArchAndPlatform = `linux-${arch}-musl`;
}
if (archAndPlatForm !== expectedArchAndPlatform) {
continue
continue;
}
for (const [contentType, contentTypeAssets] of Object.entries(archAssets)) {
for (const [contentType, contentTypeAssets] of Object.entries(
archAssets
)) {
// skip if not tar.gz and zip files
if (contentType !== 'tar.gz' && contentType !== 'zip') {
continue;
@ -175,7 +201,7 @@ export class SapMachineDistribution extends JavaBase {
version: buildVersionWithoutPrefix,
checksum: contentTypeAssets.checksum,
downloadLink: contentTypeAssets.url,
packageType: edition,
packageType: edition
});
}
}
@ -206,7 +232,7 @@ export class SapMachineDistribution extends JavaBase {
case 'win32':
return 'windows';
case 'darwin':
return 'macos'
return 'macos';
case 'linux':
// figure out if alpine/musl
if (fs.existsSync('/etc/alpine-release')) {
@ -218,7 +244,10 @@ export class SapMachineDistribution extends JavaBase {
}
}
private async fetchReleasesFromUrl(url: string, headers: OutgoingHttpHeaders = {}): Promise<ISapMachineAllVersions | null> {
private async fetchReleasesFromUrl(
url: string,
headers: OutgoingHttpHeaders = {}
): Promise<ISapMachineAllVersions | null> {
try {
core.debug(
`Trying to fetch available SapMachine versions info from the primary url: ${url}`
@ -229,7 +258,8 @@ export class SapMachineDistribution extends JavaBase {
return releases;
} catch (err) {
core.debug(
`Fetching SapMachine versions info from the link: ${url} ended up with the error: ${(err as Error).message
`Fetching SapMachine versions info from the link: ${url} ended up with the error: ${
(err as Error).message
}`
);
return null;

View File

@ -1,33 +1,33 @@
export interface ISapMachineAllVersions {
[major: string]: {
lts: string,
updates: {
[full_version: string]: {
[sapmachineBuild: string]: {
release_url: string,
ea: string,
assets: {
[packageType: string]: {
[arch: string]: {
[content_type: string]: {
name: string,
checksum: string,
url: string
};
};
};
};
[major: string]: {
lts: string;
updates: {
[full_version: string]: {
[sapmachineBuild: string]: {
release_url: string;
ea: string;
assets: {
[packageType: string]: {
[arch: string]: {
[content_type: string]: {
name: string;
checksum: string;
url: string;
};
};
};
};
};
};
};
};
}
export interface ISapMachineVersions {
os: string;
architecture: string;
version: string;
checksum: string;
downloadLink: string;
packageType: string;
os: string;
architecture: string;
version: string;
checksum: string;
downloadLink: string;
packageType: string;
}

View File

@ -84,47 +84,59 @@ export function generateToolchainDefinition(
id: string,
jdkHome: string
) {
let xmlObj;
if (original?.length) {
xmlObj = xmlCreate(original)
.root()
.ele({
toolchain: {
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
}
});
} else
xmlObj = xmlCreate({
toolchains: {
'@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'@xsi:schemaLocation':
'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
toolchain: [
{
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
}
]
let jsToolchains: Toolchain[] = [
{
type: 'jdk',
provides: {
version: `${version}`,
vendor: `${vendor}`,
id: `${id}`
},
configuration: {
jdkHome: `${jdkHome}`
}
});
}
];
if (original?.length) {
// convert existing toolchains into TS native objects for better handling
// xmlbuilder2 will convert the document into a `{toolchains: { toolchain: [] | {} }}` structure
// instead of the desired `toolchains: [{}]` one or simply `[{}]`
const jsObj = xmlCreate(original)
.root()
.toObject() as unknown as ExtractedToolchains;
if (jsObj.toolchains && jsObj.toolchains.toolchain) {
// in case only a single child exists xmlbuilder2 will not create an array and using verbose = true equally doesn't work here
// See https://oozcitak.github.io/xmlbuilder2/serialization.html#js-object-and-map-serializers for details
if (Array.isArray(jsObj.toolchains.toolchain)) {
jsToolchains.push(...jsObj.toolchains.toolchain);
} else {
jsToolchains.push(jsObj.toolchains.toolchain);
}
}
return xmlObj.end({
// remove potential duplicates based on type & id (which should be a unique combination);
// self.findIndex will only return the first occurrence, ensuring duplicates are skipped
jsToolchains = jsToolchains.filter(
(value, index, self) =>
// ensure non-jdk toolchains are kept in the results, we must not touch them because they belong to the user
value.type !== 'jdk' ||
index ===
self.findIndex(
t => t.type === value.type && t.provides.id === value.provides.id
)
);
}
// TODO: technically bad because we shouldn't re-create the toolchains root node (with possibly different schema values) if it already exists, however, just overriding the toolchain array with xmlbuilder2 is … uh non-trivial
return xmlCreate({
toolchains: {
'@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'@xsi:schemaLocation':
'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
toolchain: jsToolchains
}
}).end({
format: 'xml',
wellFormed: false,
headless: false,
@ -167,3 +179,22 @@ async function writeToolchainsFileToDisk(
flag: 'w'
});
}
interface ExtractedToolchains {
toolchains: {
toolchain: Toolchain[] | Toolchain;
};
}
// Toolchain type definition according to Maven Toolchains XSD 1.1.0
interface Toolchain {
type: string;
provides:
| {
version: string;
vendor: string;
id: string;
}
| any;
configuration: any;
}