mirror of
https://github.com/actions/setup-python.git
synced 2024-11-28 20:05:21 +01:00
Add GraalPy support (#694)
* Add support for graalpy * add graalpy test workflow * format, lint and build * symlink graalpy binaries names * fix macos names for graalpy * Don't attempt to update pip for graalpy * Remove test schedule * Extract common getBinaryDirectory function for PyPy and GraalPy * Clean up and format * Pass GitHub token to GraalPy queries * Utilize pagination when querying GraalPy GitHub releases * Build * Fix lint errors * Deal with possible multiple artifacts for a single releases * Skip few GraalPy tests on windows - we don't have a windows release yet * Fix GraalPy test on Mac OS * Build * Skip one more GraalPy test on windows --------- Co-authored-by: Michael Simacek <michael.simacek@oracle.com>
This commit is contained in:
parent
3467d92d48
commit
5f2af211d6
12 changed files with 7429 additions and 27 deletions
116
.github/workflows/test-graalpy.yml
vendored
Normal file
116
.github/workflows/test-graalpy.yml
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
name: Validate GraalPy e2e
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup-graalpy:
|
||||||
|
name: Setup GraalPy ${{ matrix.graalpy }} ${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-20.04, ubuntu-latest]
|
||||||
|
graalpy:
|
||||||
|
- 'graalpy-23.0'
|
||||||
|
- 'graalpy-22.3'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: setup-python ${{ matrix.graalpy }}
|
||||||
|
id: setup-python
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.graalpy }}
|
||||||
|
|
||||||
|
- name: Check python-path
|
||||||
|
run: ./__tests__/check-python-path.sh '${{ steps.setup-python.outputs.python-path }}'
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: GraalPy and Python version
|
||||||
|
run: python --version
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: python -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
- name: Assert GraalPy is running
|
||||||
|
run: |
|
||||||
|
import platform
|
||||||
|
assert platform.python_implementation().lower() == "graalvm"
|
||||||
|
shell: python
|
||||||
|
|
||||||
|
- name: Assert expected binaries (or symlinks) are present
|
||||||
|
run: |
|
||||||
|
EXECUTABLE=${{ matrix.graalpy }}
|
||||||
|
EXECUTABLE=${EXECUTABLE/graalpy-/graalpy} # remove the first '-' in "graalpy-X.Y" -> "graalpyX.Y" to match executable name
|
||||||
|
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
||||||
|
${EXECUTABLE} --version
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
setup-graalpy-noenv:
|
||||||
|
name: Setup GraalPy ${{ matrix.graalpy }} ${{ matrix.os }} (noenv)
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-20.04, ubuntu-latest]
|
||||||
|
graalpy: ['graalpy23.0', 'graalpy22.3']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: setup-python ${{ matrix.graalpy }}
|
||||||
|
id: setup-python
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.graalpy }}
|
||||||
|
update-environment: false
|
||||||
|
|
||||||
|
- name: GraalPy and Python version
|
||||||
|
run: ${{ steps.setup-python.outputs.python-path }} --version
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: ${{ steps.setup-python.outputs.python-path }} -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
check-latest:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup GraalPy and check latest
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
python-version: 'graalpy-23.x'
|
||||||
|
check-latest: true
|
||||||
|
- name: GraalPy and Python version
|
||||||
|
run: python --version
|
||||||
|
|
||||||
|
- name: Run simple code
|
||||||
|
run: python -c 'import math; print(math.factorial(5))'
|
||||||
|
|
||||||
|
- name: Assert GraalPy is running
|
||||||
|
run: |
|
||||||
|
import platform
|
||||||
|
assert platform.python_implementation().lower() == "graalvm"
|
||||||
|
shell: python
|
||||||
|
|
||||||
|
- name: Assert expected binaries (or symlinks) are present
|
||||||
|
run: |
|
||||||
|
EXECUTABLE="graalpy-23.0"
|
||||||
|
EXECUTABLE=${EXECUTABLE/-/} # remove the first '-' in "graalpy-X.Y" -> "graalpyX.Y" to match executable name
|
||||||
|
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
|
||||||
|
${EXECUTABLE} --version
|
||||||
|
shell: bash
|
5798
__tests__/data/graalpy.json
Normal file
5798
__tests__/data/graalpy.json
Normal file
File diff suppressed because it is too large
Load diff
378
__tests__/find-graalpy.test.ts
Normal file
378
__tests__/find-graalpy.test.ts
Normal file
|
@ -0,0 +1,378 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import {HttpClient} from '@actions/http-client';
|
||||||
|
import * as ifm from '@actions/http-client/interfaces';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
|
||||||
|
import * as finder from '../src/find-graalpy';
|
||||||
|
import {IGraalPyManifestRelease, IS_WINDOWS} from '../src/utils';
|
||||||
|
|
||||||
|
import manifestData from './data/graalpy.json';
|
||||||
|
|
||||||
|
const architecture = 'x64';
|
||||||
|
|
||||||
|
const toolDir = path.join(__dirname, 'runner', 'tools');
|
||||||
|
const tempDir = path.join(__dirname, 'runner', 'temp');
|
||||||
|
|
||||||
|
/* GraalPy doesn't have a windows release yet */
|
||||||
|
const describeSkipOnWindows = IS_WINDOWS ? describe.skip : describe;
|
||||||
|
|
||||||
|
describe('parseGraalPyVersion', () => {
|
||||||
|
it.each([
|
||||||
|
['graalpy-23', '23'],
|
||||||
|
['graalpy-23.0', '23.0'],
|
||||||
|
['graalpy23.0', '23.0']
|
||||||
|
])('%s -> %s', (input, expected) => {
|
||||||
|
expect(finder.parseGraalPyVersion(input)).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(['', 'graalpy-', 'graalpy', 'p', 'notgraalpy-'])(
|
||||||
|
'throw on invalid input "%s"',
|
||||||
|
input => {
|
||||||
|
expect(() => finder.parseGraalPyVersion(input)).toThrow(
|
||||||
|
"Invalid 'version' property for GraalPy. GraalPy version should be specified as 'graalpy<python-version>' or 'graalpy-<python-version>'. See README for examples and documentation."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findGraalPyToolCache', () => {
|
||||||
|
const actualGraalPyVersion = '23.0.0';
|
||||||
|
const graalpyPath = path.join('GraalPy', actualGraalPyVersion, architecture);
|
||||||
|
let tcFind: jest.SpyInstance;
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let addPathSpy: jest.SpyInstance;
|
||||||
|
let exportVariableSpy: jest.SpyInstance;
|
||||||
|
let setOutputSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tcFind = jest.spyOn(tc, 'find');
|
||||||
|
tcFind.mockImplementation((toolname: string, pythonVersion: string) => {
|
||||||
|
const semverVersion = new semver.Range(pythonVersion);
|
||||||
|
return semver.satisfies(actualGraalPyVersion, semverVersion)
|
||||||
|
? graalpyPath
|
||||||
|
: '';
|
||||||
|
});
|
||||||
|
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
warningSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
addPathSpy = jest.spyOn(core, 'addPath');
|
||||||
|
addPathSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
exportVariableSpy = jest.spyOn(core, 'exportVariable');
|
||||||
|
exportVariableSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||||
|
setOutputSpy.mockImplementation(() => null);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GraalPy exists on the path and versions are satisfied', () => {
|
||||||
|
expect(finder.findGraalPyToolCache('23.0.0', architecture)).toEqual({
|
||||||
|
installDir: graalpyPath,
|
||||||
|
resolvedGraalPyVersion: actualGraalPyVersion
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GraalPy exists on the path and versions are satisfied with semver', () => {
|
||||||
|
expect(finder.findGraalPyToolCache('23.0', architecture)).toEqual({
|
||||||
|
installDir: graalpyPath,
|
||||||
|
resolvedGraalPyVersion: actualGraalPyVersion
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("GraalPy exists on the path, but version doesn't match", () => {
|
||||||
|
expect(finder.findGraalPyToolCache('22.3', architecture)).toEqual({
|
||||||
|
installDir: '',
|
||||||
|
resolvedGraalPyVersion: ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describeSkipOnWindows('findGraalPyVersion', () => {
|
||||||
|
let getBooleanInputSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
let addPathSpy: jest.SpyInstance;
|
||||||
|
let exportVariableSpy: jest.SpyInstance;
|
||||||
|
let setOutputSpy: jest.SpyInstance;
|
||||||
|
let tcFind: jest.SpyInstance;
|
||||||
|
let spyExtractZip: jest.SpyInstance;
|
||||||
|
let spyExtractTar: jest.SpyInstance;
|
||||||
|
let spyHttpClient: jest.SpyInstance;
|
||||||
|
let spyExistsSync: jest.SpyInstance;
|
||||||
|
let spyExec: jest.SpyInstance;
|
||||||
|
let spySymlinkSync: jest.SpyInstance;
|
||||||
|
let spyDownloadTool: jest.SpyInstance;
|
||||||
|
let spyFsReadDir: jest.SpyInstance;
|
||||||
|
let spyCacheDir: jest.SpyInstance;
|
||||||
|
let spyChmodSync: jest.SpyInstance;
|
||||||
|
let spyCoreAddPath: jest.SpyInstance;
|
||||||
|
let spyCoreExportVariable: jest.SpyInstance;
|
||||||
|
const env = process.env;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getBooleanInputSpy = jest.spyOn(core, 'getBooleanInput');
|
||||||
|
getBooleanInputSpy.mockImplementation(() => false);
|
||||||
|
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
warningSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
addPathSpy = jest.spyOn(core, 'addPath');
|
||||||
|
addPathSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
exportVariableSpy = jest.spyOn(core, 'exportVariable');
|
||||||
|
exportVariableSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||||
|
setOutputSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
jest.resetModules();
|
||||||
|
process.env = {...env};
|
||||||
|
tcFind = jest.spyOn(tc, 'find');
|
||||||
|
tcFind.mockImplementation((tool: string, version: string) => {
|
||||||
|
const semverRange = new semver.Range(version);
|
||||||
|
let graalpyPath = '';
|
||||||
|
if (semver.satisfies('23.0.0', semverRange)) {
|
||||||
|
graalpyPath = path.join(toolDir, 'GraalPy', '23.0.0', architecture);
|
||||||
|
}
|
||||||
|
return graalpyPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
spyDownloadTool = jest.spyOn(tc, 'downloadTool');
|
||||||
|
spyDownloadTool.mockImplementation(() => path.join(tempDir, 'GraalPy'));
|
||||||
|
|
||||||
|
spyExtractZip = jest.spyOn(tc, 'extractZip');
|
||||||
|
spyExtractZip.mockImplementation(() => tempDir);
|
||||||
|
|
||||||
|
spyExtractTar = jest.spyOn(tc, 'extractTar');
|
||||||
|
spyExtractTar.mockImplementation(() => tempDir);
|
||||||
|
|
||||||
|
spyFsReadDir = jest.spyOn(fs, 'readdirSync');
|
||||||
|
spyFsReadDir.mockImplementation((directory: string) => ['GraalPyTest']);
|
||||||
|
|
||||||
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
|
spyHttpClient.mockImplementation(
|
||||||
|
async (): Promise<ifm.ITypedResponse<IGraalPyManifestRelease[]>> => {
|
||||||
|
const result = JSON.stringify(manifestData);
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {},
|
||||||
|
result: JSON.parse(result) as IGraalPyManifestRelease[]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
spyExec = jest.spyOn(exec, 'exec');
|
||||||
|
spyExec.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
spySymlinkSync = jest.spyOn(fs, 'symlinkSync');
|
||||||
|
spySymlinkSync.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
spyExistsSync = jest.spyOn(fs, 'existsSync');
|
||||||
|
spyExistsSync.mockReturnValue(true);
|
||||||
|
|
||||||
|
spyCoreAddPath = jest.spyOn(core, 'addPath');
|
||||||
|
|
||||||
|
spyCoreExportVariable = jest.spyOn(core, 'exportVariable');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
process.env = env;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('found GraalPy in toolcache', async () => {
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-23.0',
|
||||||
|
architecture,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).resolves.toEqual('23.0.0');
|
||||||
|
expect(spyCoreAddPath).toHaveBeenCalled();
|
||||||
|
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||||
|
'pythonLocation',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||||
|
'PKG_CONFIG_PATH',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw on invalid input format', async () => {
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion('graalpy-x23', architecture, true, false, false)
|
||||||
|
).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('found and install successfully', async () => {
|
||||||
|
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||||
|
spyCacheDir.mockImplementation(() =>
|
||||||
|
path.join(toolDir, 'GraalPy', '23.0.0', architecture)
|
||||||
|
);
|
||||||
|
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||||
|
spyChmodSync.mockImplementation(() => undefined);
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-23.0.0',
|
||||||
|
architecture,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).resolves.toEqual('23.0.0');
|
||||||
|
expect(spyCoreAddPath).toHaveBeenCalled();
|
||||||
|
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||||
|
'pythonLocation',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||||
|
'PKG_CONFIG_PATH',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('found and install successfully without environment update', async () => {
|
||||||
|
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||||
|
spyCacheDir.mockImplementation(() =>
|
||||||
|
path.join(toolDir, 'GraalPy', '23.0.0', architecture)
|
||||||
|
);
|
||||||
|
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||||
|
spyChmodSync.mockImplementation(() => undefined);
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-23.0.0',
|
||||||
|
architecture,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).resolves.toEqual('23.0.0');
|
||||||
|
expect(spyCoreAddPath).not.toHaveBeenCalled();
|
||||||
|
expect(spyCoreExportVariable).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw if release is not found', async () => {
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-19.0.0',
|
||||||
|
architecture,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).rejects.toThrow(
|
||||||
|
`GraalPy version 19.0.0 with arch ${architecture} not found`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check-latest enabled version found and used from toolcache', async () => {
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-23.0.0',
|
||||||
|
architecture,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).resolves.toEqual('23.0.0');
|
||||||
|
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith('Resolved as GraalPy 23.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check-latest enabled version found and install successfully', async () => {
|
||||||
|
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||||
|
spyCacheDir.mockImplementation(() =>
|
||||||
|
path.join(toolDir, 'GraalPy', '23.0.0', architecture)
|
||||||
|
);
|
||||||
|
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||||
|
spyChmodSync.mockImplementation(() => undefined);
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-23.0.0',
|
||||||
|
architecture,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).resolves.toEqual('23.0.0');
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith('Resolved as GraalPy 23.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check-latest enabled version is not found and used from toolcache', async () => {
|
||||||
|
tcFind.mockImplementationOnce((tool: string, version: string) => {
|
||||||
|
const semverRange = new semver.Range(version);
|
||||||
|
let graalpyPath = '';
|
||||||
|
if (semver.satisfies('22.3.4', semverRange)) {
|
||||||
|
graalpyPath = path.join(toolDir, 'GraalPy', '22.3.4', architecture);
|
||||||
|
}
|
||||||
|
return graalpyPath;
|
||||||
|
});
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy-22.3.4',
|
||||||
|
architecture,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).resolves.toEqual('22.3.4');
|
||||||
|
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
'Failed to resolve GraalPy 22.3.4 from manifest'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('found and install successfully, pre-release fallback', async () => {
|
||||||
|
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||||
|
spyCacheDir.mockImplementation(() =>
|
||||||
|
path.join(toolDir, 'GraalPy', '23.1', architecture)
|
||||||
|
);
|
||||||
|
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||||
|
spyChmodSync.mockImplementation(() => undefined);
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion(
|
||||||
|
'graalpy23.1',
|
||||||
|
architecture,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
).rejects.toThrow();
|
||||||
|
await expect(
|
||||||
|
finder.findGraalPyVersion('graalpy23.1', architecture, false, false, true)
|
||||||
|
).resolves.toEqual('23.1.0-a.1');
|
||||||
|
});
|
||||||
|
});
|
256
__tests__/install-graalpy.test.ts
Normal file
256
__tests__/install-graalpy.test.ts
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import {HttpClient} from '@actions/http-client';
|
||||||
|
import * as ifm from '@actions/http-client/interfaces';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import * as installer from '../src/install-graalpy';
|
||||||
|
import {
|
||||||
|
IGraalPyManifestRelease,
|
||||||
|
IGraalPyManifestAsset,
|
||||||
|
IS_WINDOWS
|
||||||
|
} from '../src/utils';
|
||||||
|
|
||||||
|
import manifestData from './data/graalpy.json';
|
||||||
|
|
||||||
|
const architecture = 'x64';
|
||||||
|
|
||||||
|
const toolDir = path.join(__dirname, 'runner', 'tools');
|
||||||
|
const tempDir = path.join(__dirname, 'runner', 'temp');
|
||||||
|
|
||||||
|
/* GraalPy doesn't have a windows release yet */
|
||||||
|
const describeSkipOnWindows = IS_WINDOWS ? describe.skip : describe;
|
||||||
|
|
||||||
|
describe('graalpyVersionToSemantic', () => {
|
||||||
|
it.each([
|
||||||
|
['23.0.0a1', '23.0.0a1'],
|
||||||
|
['23.0.0', '23.0.0'],
|
||||||
|
['23.0.x', '23.0.x'],
|
||||||
|
['23.x', '23.x']
|
||||||
|
])('%s -> %s', (input, expected) => {
|
||||||
|
expect(installer.graalPyTagToVersion(input)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describeSkipOnWindows('findRelease', () => {
|
||||||
|
const result = JSON.stringify(manifestData);
|
||||||
|
const releases = JSON.parse(result) as IGraalPyManifestRelease[];
|
||||||
|
const extension = 'tar.gz';
|
||||||
|
const arch = installer.toGraalPyArchitecture(architecture);
|
||||||
|
const platform = installer.toGraalPyPlatform(process.platform);
|
||||||
|
const extensionName = `${platform}-${arch}.${extension}`;
|
||||||
|
const files: IGraalPyManifestAsset = {
|
||||||
|
name: `graalpython-23.0.0-${extensionName}`,
|
||||||
|
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.0.0/graalpython-23.0.0-${extensionName}`
|
||||||
|
};
|
||||||
|
const filesRC1: IGraalPyManifestAsset = {
|
||||||
|
name: `graalpython-23.1.0a1-${extensionName}`,
|
||||||
|
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.1.0a1/graalpython-23.1.0a1-${extensionName}`
|
||||||
|
};
|
||||||
|
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
warningSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(() => null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("GraalPy version doesn't match", () => {
|
||||||
|
const graalpyVersion = '12.0.0';
|
||||||
|
expect(
|
||||||
|
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||||
|
).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GraalPy version matches', () => {
|
||||||
|
const graalpyVersion = '23.0.0';
|
||||||
|
expect(
|
||||||
|
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||||
|
).toMatchObject({
|
||||||
|
foundAsset: files,
|
||||||
|
resolvedGraalPyVersion: graalpyVersion
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Preview version of GraalPy is found', () => {
|
||||||
|
const graalpyVersion = installer.graalPyTagToVersion('vm-23.1.0a1');
|
||||||
|
expect(
|
||||||
|
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||||
|
).toMatchObject({
|
||||||
|
foundAsset: {
|
||||||
|
name: `graalpython-23.1.0a1-${extensionName}`,
|
||||||
|
browser_download_url: `https://github.com/oracle/graalpython/releases/download/graal-23.1.0a1/graalpython-23.1.0a1-${extensionName}`
|
||||||
|
},
|
||||||
|
resolvedGraalPyVersion: '23.1.0-a.1'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Latest GraalPy is found', () => {
|
||||||
|
const graalpyVersion = 'x';
|
||||||
|
expect(
|
||||||
|
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||||
|
).toMatchObject({
|
||||||
|
foundAsset: files,
|
||||||
|
resolvedGraalPyVersion: '23.0.0'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GraalPy version matches semver (pre-release)', () => {
|
||||||
|
const graalpyVersion = '23.1.x';
|
||||||
|
expect(
|
||||||
|
installer.findRelease(releases, graalpyVersion, architecture, false)
|
||||||
|
).toBeNull();
|
||||||
|
expect(
|
||||||
|
installer.findRelease(releases, graalpyVersion, architecture, true)
|
||||||
|
).toMatchObject({
|
||||||
|
foundAsset: filesRC1,
|
||||||
|
resolvedGraalPyVersion: '23.1.0-a.1'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describeSkipOnWindows('installGraalPy', () => {
|
||||||
|
let tcFind: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
let spyExtractZip: jest.SpyInstance;
|
||||||
|
let spyExtractTar: jest.SpyInstance;
|
||||||
|
let spyFsReadDir: jest.SpyInstance;
|
||||||
|
let spyFsWriteFile: jest.SpyInstance;
|
||||||
|
let spyHttpClient: jest.SpyInstance;
|
||||||
|
let spyExistsSync: jest.SpyInstance;
|
||||||
|
let spyExec: jest.SpyInstance;
|
||||||
|
let spySymlinkSync: jest.SpyInstance;
|
||||||
|
let spyDownloadTool: jest.SpyInstance;
|
||||||
|
let spyCacheDir: jest.SpyInstance;
|
||||||
|
let spyChmodSync: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tcFind = jest.spyOn(tc, 'find');
|
||||||
|
tcFind.mockImplementation(() =>
|
||||||
|
path.join('GraalPy', '3.6.12', architecture)
|
||||||
|
);
|
||||||
|
|
||||||
|
spyDownloadTool = jest.spyOn(tc, 'downloadTool');
|
||||||
|
spyDownloadTool.mockImplementation(() => path.join(tempDir, 'GraalPy'));
|
||||||
|
|
||||||
|
spyExtractZip = jest.spyOn(tc, 'extractZip');
|
||||||
|
spyExtractZip.mockImplementation(() => tempDir);
|
||||||
|
|
||||||
|
spyExtractTar = jest.spyOn(tc, 'extractTar');
|
||||||
|
spyExtractTar.mockImplementation(() => tempDir);
|
||||||
|
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
warningSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(() => null);
|
||||||
|
|
||||||
|
spyFsReadDir = jest.spyOn(fs, 'readdirSync');
|
||||||
|
spyFsReadDir.mockImplementation(() => ['GraalPyTest']);
|
||||||
|
|
||||||
|
spyFsWriteFile = jest.spyOn(fs, 'writeFileSync');
|
||||||
|
spyFsWriteFile.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
|
spyHttpClient.mockImplementation(
|
||||||
|
async (): Promise<ifm.ITypedResponse<IGraalPyManifestRelease[]>> => {
|
||||||
|
const result = JSON.stringify(manifestData);
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {},
|
||||||
|
result: JSON.parse(result) as IGraalPyManifestRelease[]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
spyExec = jest.spyOn(exec, 'exec');
|
||||||
|
spyExec.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
spySymlinkSync = jest.spyOn(fs, 'symlinkSync');
|
||||||
|
spySymlinkSync.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
spyExistsSync = jest.spyOn(fs, 'existsSync');
|
||||||
|
spyExistsSync.mockImplementation(() => false);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw if release is not found', async () => {
|
||||||
|
await expect(
|
||||||
|
installer.installGraalPy('7.3.3', architecture, false, undefined)
|
||||||
|
).rejects.toThrow(
|
||||||
|
`GraalPy version 7.3.3 with arch ${architecture} not found`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalled();
|
||||||
|
expect(spyDownloadTool).not.toHaveBeenCalled();
|
||||||
|
expect(spyExec).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('found and install GraalPy', async () => {
|
||||||
|
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||||
|
spyCacheDir.mockImplementation(() =>
|
||||||
|
path.join(toolDir, 'GraalPy', '21.3.0', architecture)
|
||||||
|
);
|
||||||
|
|
||||||
|
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||||
|
spyChmodSync.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
installer.installGraalPy('21.x', architecture, false, undefined)
|
||||||
|
).resolves.toEqual({
|
||||||
|
installDir: path.join(toolDir, 'GraalPy', '21.3.0', architecture),
|
||||||
|
resolvedGraalPyVersion: '21.3.0'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalled();
|
||||||
|
expect(spyDownloadTool).toHaveBeenCalled();
|
||||||
|
expect(spyCacheDir).toHaveBeenCalled();
|
||||||
|
expect(spyExec).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('found and install GraalPy, pre-release fallback', async () => {
|
||||||
|
spyCacheDir = jest.spyOn(tc, 'cacheDir');
|
||||||
|
spyCacheDir.mockImplementation(() =>
|
||||||
|
path.join(toolDir, 'GraalPy', '23.1.0', architecture)
|
||||||
|
);
|
||||||
|
|
||||||
|
spyChmodSync = jest.spyOn(fs, 'chmodSync');
|
||||||
|
spyChmodSync.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
installer.installGraalPy('23.1.x', architecture, false, undefined)
|
||||||
|
).rejects.toThrow();
|
||||||
|
await expect(
|
||||||
|
installer.installGraalPy('23.1.x', architecture, true, undefined)
|
||||||
|
).resolves.toEqual({
|
||||||
|
installDir: path.join(toolDir, 'GraalPy', '23.1.0', architecture),
|
||||||
|
resolvedGraalPyVersion: '23.1.0-a.1'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalled();
|
||||||
|
expect(spyDownloadTool).toHaveBeenCalled();
|
||||||
|
expect(spyCacheDir).toHaveBeenCalled();
|
||||||
|
expect(spyExec).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
|
@ -11,7 +11,8 @@ import {
|
||||||
isCacheFeatureAvailable,
|
isCacheFeatureAvailable,
|
||||||
getVersionInputFromFile,
|
getVersionInputFromFile,
|
||||||
getVersionInputFromPlainFile,
|
getVersionInputFromPlainFile,
|
||||||
getVersionInputFromTomlFile
|
getVersionInputFromTomlFile,
|
||||||
|
getNextPageUrl
|
||||||
} from '../src/utils';
|
} from '../src/utils';
|
||||||
|
|
||||||
jest.mock('@actions/cache');
|
jest.mock('@actions/cache');
|
||||||
|
@ -136,3 +137,25 @@ describe('Version from file test', () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getNextPageUrl', () => {
|
||||||
|
it('GitHub API pagination next page is parsed correctly', () => {
|
||||||
|
function generateResponse(link: string) {
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
result: null,
|
||||||
|
headers: {
|
||||||
|
link: link
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const page1Links =
|
||||||
|
'<https://api.github.com/repositories/129883600/releases?page=2>; rel="next", <https://api.github.com/repositories/129883600/releases?page=3>; rel="last"';
|
||||||
|
expect(getNextPageUrl(generateResponse(page1Links))).toStrictEqual(
|
||||||
|
'https://api.github.com/repositories/129883600/releases?page=2'
|
||||||
|
);
|
||||||
|
const page2Links =
|
||||||
|
'<https://api.github.com/repositories/129883600/releases?page=1>; rel="prev", <https://api.github.com/repositories/129883600/releases?page=1>; rel="first"';
|
||||||
|
expect(getNextPageUrl(generateResponse(page2Links))).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
399
dist/setup/index.js
vendored
399
dist/setup/index.js
vendored
|
@ -69091,6 +69091,132 @@ class PoetryCache extends cache_distributor_1.default {
|
||||||
exports["default"] = PoetryCache;
|
exports["default"] = PoetryCache;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 8040:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.parseGraalPyVersion = exports.findGraalPyToolCache = exports.findGraalPyVersion = void 0;
|
||||||
|
const path = __importStar(__nccwpck_require__(1017));
|
||||||
|
const graalpyInstall = __importStar(__nccwpck_require__(8265));
|
||||||
|
const utils_1 = __nccwpck_require__(1314);
|
||||||
|
const semver = __importStar(__nccwpck_require__(1383));
|
||||||
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
|
const tc = __importStar(__nccwpck_require__(7784));
|
||||||
|
function findGraalPyVersion(versionSpec, architecture, updateEnvironment, checkLatest, allowPreReleases) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let resolvedGraalPyVersion = '';
|
||||||
|
let installDir;
|
||||||
|
let releases;
|
||||||
|
let graalpyVersionSpec = parseGraalPyVersion(versionSpec);
|
||||||
|
if (checkLatest) {
|
||||||
|
releases = yield graalpyInstall.getAvailableGraalPyVersions();
|
||||||
|
if (releases && releases.length > 0) {
|
||||||
|
const releaseData = graalpyInstall.findRelease(releases, graalpyVersionSpec, architecture, false);
|
||||||
|
if (releaseData) {
|
||||||
|
core.info(`Resolved as GraalPy ${releaseData.resolvedGraalPyVersion}`);
|
||||||
|
graalpyVersionSpec = releaseData.resolvedGraalPyVersion;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`Failed to resolve GraalPy ${graalpyVersionSpec} from manifest`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
({ installDir, resolvedGraalPyVersion } = findGraalPyToolCache(graalpyVersionSpec, architecture));
|
||||||
|
if (!installDir) {
|
||||||
|
({ installDir, resolvedGraalPyVersion } = yield graalpyInstall.installGraalPy(graalpyVersionSpec, architecture, allowPreReleases, releases));
|
||||||
|
}
|
||||||
|
const pipDir = utils_1.IS_WINDOWS ? 'Scripts' : 'bin';
|
||||||
|
const _binDir = path.join(installDir, pipDir);
|
||||||
|
const binaryExtension = utils_1.IS_WINDOWS ? '.exe' : '';
|
||||||
|
const pythonPath = path.join(utils_1.IS_WINDOWS ? installDir : _binDir, `python${binaryExtension}`);
|
||||||
|
const pythonLocation = utils_1.getBinaryDirectory(installDir);
|
||||||
|
if (updateEnvironment) {
|
||||||
|
core.exportVariable('pythonLocation', installDir);
|
||||||
|
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
||||||
|
core.exportVariable('Python_ROOT_DIR', installDir);
|
||||||
|
// https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython2
|
||||||
|
core.exportVariable('Python2_ROOT_DIR', installDir);
|
||||||
|
// https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3
|
||||||
|
core.exportVariable('Python3_ROOT_DIR', installDir);
|
||||||
|
core.exportVariable('PKG_CONFIG_PATH', pythonLocation + '/lib/pkgconfig');
|
||||||
|
core.addPath(pythonLocation);
|
||||||
|
core.addPath(_binDir);
|
||||||
|
}
|
||||||
|
core.setOutput('python-version', 'graalpy' + resolvedGraalPyVersion);
|
||||||
|
core.setOutput('python-path', pythonPath);
|
||||||
|
return resolvedGraalPyVersion;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.findGraalPyVersion = findGraalPyVersion;
|
||||||
|
function findGraalPyToolCache(graalpyVersion, architecture) {
|
||||||
|
let resolvedGraalPyVersion = '';
|
||||||
|
let installDir = tc.find('GraalPy', graalpyVersion, architecture);
|
||||||
|
if (installDir) {
|
||||||
|
// 'tc.find' finds tool based on Python version but we also need to check
|
||||||
|
// whether GraalPy version satisfies requested version.
|
||||||
|
resolvedGraalPyVersion = path.basename(path.dirname(installDir));
|
||||||
|
const isGraalPyVersionSatisfies = semver.satisfies(resolvedGraalPyVersion, graalpyVersion);
|
||||||
|
if (!isGraalPyVersionSatisfies) {
|
||||||
|
installDir = null;
|
||||||
|
resolvedGraalPyVersion = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!installDir) {
|
||||||
|
core.info(`GraalPy version ${graalpyVersion} was not found in the local cache`);
|
||||||
|
}
|
||||||
|
return { installDir, resolvedGraalPyVersion };
|
||||||
|
}
|
||||||
|
exports.findGraalPyToolCache = findGraalPyToolCache;
|
||||||
|
function parseGraalPyVersion(versionSpec) {
|
||||||
|
const versions = versionSpec.split('-').filter(item => !!item);
|
||||||
|
if (/^(graalpy)(.+)/.test(versions[0])) {
|
||||||
|
const version = versions[0].replace('graalpy', '');
|
||||||
|
versions.splice(0, 1, 'graalpy', version);
|
||||||
|
}
|
||||||
|
if (versions.length < 2 || versions[0] != 'graalpy') {
|
||||||
|
throw new Error("Invalid 'version' property for GraalPy. GraalPy version should be specified as 'graalpy<python-version>' or 'graalpy-<python-version>'. See README for examples and documentation.");
|
||||||
|
}
|
||||||
|
const pythonVersion = versions[1];
|
||||||
|
if (!utils_1.validateVersion(pythonVersion)) {
|
||||||
|
throw new Error("Invalid 'version' property for GraalPy. GraalPy versions should satisfy SemVer notation. See README for examples and documentation.");
|
||||||
|
}
|
||||||
|
return pythonVersion;
|
||||||
|
}
|
||||||
|
exports.parseGraalPyVersion = parseGraalPyVersion;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 4003:
|
/***/ 4003:
|
||||||
|
@ -69164,7 +69290,7 @@ function findPyPyVersion(versionSpec, architecture, updateEnvironment, checkLate
|
||||||
const _binDir = path.join(installDir, pipDir);
|
const _binDir = path.join(installDir, pipDir);
|
||||||
const binaryExtension = utils_1.IS_WINDOWS ? '.exe' : '';
|
const binaryExtension = utils_1.IS_WINDOWS ? '.exe' : '';
|
||||||
const pythonPath = path.join(utils_1.IS_WINDOWS ? installDir : _binDir, `python${binaryExtension}`);
|
const pythonPath = path.join(utils_1.IS_WINDOWS ? installDir : _binDir, `python${binaryExtension}`);
|
||||||
const pythonLocation = pypyInstall.getPyPyBinaryPath(installDir);
|
const pythonLocation = utils_1.getBinaryDirectory(installDir);
|
||||||
if (updateEnvironment) {
|
if (updateEnvironment) {
|
||||||
core.exportVariable('pythonLocation', installDir);
|
core.exportVariable('pythonLocation', installDir);
|
||||||
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
||||||
|
@ -69419,6 +69545,222 @@ function pythonVersionToSemantic(versionSpec, allowPreReleases) {
|
||||||
exports.pythonVersionToSemantic = pythonVersionToSemantic;
|
exports.pythonVersionToSemantic = pythonVersionToSemantic;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 8265:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.findAsset = exports.toGraalPyArchitecture = exports.toGraalPyPlatform = exports.findRelease = exports.graalPyTagToVersion = exports.getAvailableGraalPyVersions = exports.installGraalPy = void 0;
|
||||||
|
const os = __importStar(__nccwpck_require__(2037));
|
||||||
|
const path = __importStar(__nccwpck_require__(1017));
|
||||||
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
|
const tc = __importStar(__nccwpck_require__(7784));
|
||||||
|
const semver = __importStar(__nccwpck_require__(1383));
|
||||||
|
const httpm = __importStar(__nccwpck_require__(9925));
|
||||||
|
const exec = __importStar(__nccwpck_require__(1514));
|
||||||
|
const fs_1 = __importDefault(__nccwpck_require__(7147));
|
||||||
|
const utils_1 = __nccwpck_require__(1314);
|
||||||
|
const TOKEN = core.getInput('token');
|
||||||
|
const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
|
||||||
|
function installGraalPy(graalpyVersion, architecture, allowPreReleases, releases) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let downloadDir;
|
||||||
|
releases = releases !== null && releases !== void 0 ? releases : (yield getAvailableGraalPyVersions());
|
||||||
|
if (!releases || !releases.length) {
|
||||||
|
throw new Error('No release was found in GraalPy version.json');
|
||||||
|
}
|
||||||
|
let releaseData = findRelease(releases, graalpyVersion, architecture, false);
|
||||||
|
if (allowPreReleases && (!releaseData || !releaseData.foundAsset)) {
|
||||||
|
// check for pre-release
|
||||||
|
core.info([
|
||||||
|
`Stable GraalPy version ${graalpyVersion} with arch ${architecture} not found`,
|
||||||
|
`Trying pre-release versions`
|
||||||
|
].join(os.EOL));
|
||||||
|
releaseData = findRelease(releases, graalpyVersion, architecture, true);
|
||||||
|
}
|
||||||
|
if (!releaseData || !releaseData.foundAsset) {
|
||||||
|
throw new Error(`GraalPy version ${graalpyVersion} with arch ${architecture} not found`);
|
||||||
|
}
|
||||||
|
const { foundAsset, resolvedGraalPyVersion } = releaseData;
|
||||||
|
const downloadUrl = `${foundAsset.browser_download_url}`;
|
||||||
|
core.info(`Downloading GraalPy from "${downloadUrl}" ...`);
|
||||||
|
try {
|
||||||
|
const graalpyPath = yield tc.downloadTool(downloadUrl, undefined, AUTH);
|
||||||
|
core.info('Extracting downloaded archive...');
|
||||||
|
downloadDir = yield tc.extractTar(graalpyPath);
|
||||||
|
// root folder in archive can have unpredictable name so just take the first folder
|
||||||
|
// downloadDir is unique folder under TEMP and can't contain any other folders
|
||||||
|
const archiveName = fs_1.default.readdirSync(downloadDir)[0];
|
||||||
|
const toolDir = path.join(downloadDir, archiveName);
|
||||||
|
let installDir = toolDir;
|
||||||
|
if (!utils_1.isNightlyKeyword(resolvedGraalPyVersion)) {
|
||||||
|
installDir = yield tc.cacheDir(toolDir, 'GraalPy', resolvedGraalPyVersion, architecture);
|
||||||
|
}
|
||||||
|
const binaryPath = utils_1.getBinaryDirectory(installDir);
|
||||||
|
yield createGraalPySymlink(binaryPath, resolvedGraalPyVersion);
|
||||||
|
yield installPip(binaryPath);
|
||||||
|
return { installDir, resolvedGraalPyVersion };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
// Rate limit?
|
||||||
|
if (err instanceof tc.HTTPError &&
|
||||||
|
(err.httpStatusCode === 403 || err.httpStatusCode === 429)) {
|
||||||
|
core.info(`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(err.message);
|
||||||
|
}
|
||||||
|
if (err.stack !== undefined) {
|
||||||
|
core.debug(err.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.installGraalPy = installGraalPy;
|
||||||
|
function getAvailableGraalPyVersions() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const http = new httpm.HttpClient('tool-cache');
|
||||||
|
const headers = {};
|
||||||
|
if (AUTH) {
|
||||||
|
headers.authorization = AUTH;
|
||||||
|
}
|
||||||
|
let url = 'https://api.github.com/repos/oracle/graalpython/releases';
|
||||||
|
const result = [];
|
||||||
|
do {
|
||||||
|
const response = yield http.getJson(url, headers);
|
||||||
|
if (!response.result) {
|
||||||
|
throw new Error(`Unable to retrieve the list of available GraalPy versions from '${url}'`);
|
||||||
|
}
|
||||||
|
result.push(...response.result);
|
||||||
|
url = utils_1.getNextPageUrl(response);
|
||||||
|
} while (url);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.getAvailableGraalPyVersions = getAvailableGraalPyVersions;
|
||||||
|
function createGraalPySymlink(graalpyBinaryPath, graalpyVersion) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const version = semver.coerce(graalpyVersion);
|
||||||
|
const pythonBinaryPostfix = semver.major(version);
|
||||||
|
const pythonMinor = semver.minor(version);
|
||||||
|
const graalpyMajorMinorBinaryPostfix = `${pythonBinaryPostfix}.${pythonMinor}`;
|
||||||
|
const binaryExtension = utils_1.IS_WINDOWS ? '.exe' : '';
|
||||||
|
core.info('Creating symlinks...');
|
||||||
|
utils_1.createSymlinkInFolder(graalpyBinaryPath, `graalpy${binaryExtension}`, `python${pythonBinaryPostfix}${binaryExtension}`, true);
|
||||||
|
utils_1.createSymlinkInFolder(graalpyBinaryPath, `graalpy${binaryExtension}`, `python${binaryExtension}`, true);
|
||||||
|
utils_1.createSymlinkInFolder(graalpyBinaryPath, `graalpy${binaryExtension}`, `graalpy${graalpyMajorMinorBinaryPostfix}${binaryExtension}`, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function installPip(pythonLocation) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
core.info("Installing pip (GraalPy doesn't update pip because it uses a patched version of pip)");
|
||||||
|
const pythonBinary = path.join(pythonLocation, 'python');
|
||||||
|
yield exec.exec(`${pythonBinary} -m ensurepip --default-pip`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function graalPyTagToVersion(tag) {
|
||||||
|
const versionPattern = /.*-(\d+\.\d+\.\d+(?:\.\d+)?)((?:a|b|rc))?(\d*)?/;
|
||||||
|
const match = tag.match(versionPattern);
|
||||||
|
if (match && match[2]) {
|
||||||
|
return `${match[1]}-${match[2]}.${match[3]}`;
|
||||||
|
}
|
||||||
|
else if (match) {
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return tag.replace(/.*-/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.graalPyTagToVersion = graalPyTagToVersion;
|
||||||
|
function findRelease(releases, graalpyVersion, architecture, includePrerelease) {
|
||||||
|
const options = { includePrerelease: includePrerelease };
|
||||||
|
const filterReleases = releases.filter(item => {
|
||||||
|
const isVersionSatisfied = semver.satisfies(graalPyTagToVersion(item.tag_name), graalpyVersion, options);
|
||||||
|
return (isVersionSatisfied && !!findAsset(item, architecture, process.platform));
|
||||||
|
});
|
||||||
|
if (!filterReleases.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const sortedReleases = filterReleases.sort((previous, current) => semver.compare(semver.coerce(graalPyTagToVersion(current.tag_name)), semver.coerce(graalPyTagToVersion(previous.tag_name))));
|
||||||
|
const foundRelease = sortedReleases[0];
|
||||||
|
const foundAsset = findAsset(foundRelease, architecture, process.platform);
|
||||||
|
return {
|
||||||
|
foundAsset,
|
||||||
|
resolvedGraalPyVersion: graalPyTagToVersion(foundRelease.tag_name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports.findRelease = findRelease;
|
||||||
|
function toGraalPyPlatform(platform) {
|
||||||
|
switch (platform) {
|
||||||
|
case 'win32':
|
||||||
|
return 'windows';
|
||||||
|
case 'darwin':
|
||||||
|
return 'macos';
|
||||||
|
}
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
exports.toGraalPyPlatform = toGraalPyPlatform;
|
||||||
|
function toGraalPyArchitecture(architecture) {
|
||||||
|
switch (architecture) {
|
||||||
|
case 'x64':
|
||||||
|
return 'amd64';
|
||||||
|
case 'arm64':
|
||||||
|
return 'aarch64';
|
||||||
|
}
|
||||||
|
return architecture;
|
||||||
|
}
|
||||||
|
exports.toGraalPyArchitecture = toGraalPyArchitecture;
|
||||||
|
function findAsset(item, architecture, platform) {
|
||||||
|
const graalpyArch = toGraalPyArchitecture(architecture);
|
||||||
|
const graalpyPlatform = toGraalPyPlatform(platform);
|
||||||
|
const found = item.assets.filter(file => file.name.startsWith('graalpy') &&
|
||||||
|
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.tar.gz`));
|
||||||
|
/*
|
||||||
|
In the future there could be more variants of GraalPy for a single release. Pick the shortest name, that one is the most likely to be the primary variant.
|
||||||
|
*/
|
||||||
|
found.sort((f1, f2) => f1.name.length - f2.name.length);
|
||||||
|
return found[0];
|
||||||
|
}
|
||||||
|
exports.findAsset = findAsset;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 8168:
|
/***/ 8168:
|
||||||
|
@ -69458,7 +69800,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.findAssetForMacOrLinux = exports.findAssetForWindows = exports.isArchPresentForMacOrLinux = exports.isArchPresentForWindows = exports.pypyVersionToSemantic = exports.getPyPyBinaryPath = exports.findRelease = exports.getAvailablePyPyVersions = exports.installPyPy = void 0;
|
exports.findAssetForMacOrLinux = exports.findAssetForWindows = exports.isArchPresentForMacOrLinux = exports.isArchPresentForWindows = exports.pypyVersionToSemantic = exports.findRelease = exports.getAvailablePyPyVersions = exports.installPyPy = void 0;
|
||||||
const os = __importStar(__nccwpck_require__(2037));
|
const os = __importStar(__nccwpck_require__(2037));
|
||||||
const path = __importStar(__nccwpck_require__(1017));
|
const path = __importStar(__nccwpck_require__(1017));
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
|
@ -69508,7 +69850,7 @@ function installPyPy(pypyVersion, pythonVersion, architecture, allowPreReleases,
|
||||||
installDir = yield tc.cacheDir(toolDir, 'PyPy', resolvedPythonVersion, architecture);
|
installDir = yield tc.cacheDir(toolDir, 'PyPy', resolvedPythonVersion, architecture);
|
||||||
}
|
}
|
||||||
utils_1.writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
utils_1.writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
||||||
const binaryPath = getPyPyBinaryPath(installDir);
|
const binaryPath = utils_1.getBinaryDirectory(installDir);
|
||||||
yield createPyPySymlink(binaryPath, resolvedPythonVersion);
|
yield createPyPySymlink(binaryPath, resolvedPythonVersion);
|
||||||
yield installPip(binaryPath);
|
yield installPip(binaryPath);
|
||||||
return { installDir, resolvedPythonVersion, resolvedPyPyVersion };
|
return { installDir, resolvedPythonVersion, resolvedPyPyVersion };
|
||||||
|
@ -69597,15 +69939,6 @@ function findRelease(releases, pythonVersion, pypyVersion, architecture, include
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
exports.findRelease = findRelease;
|
exports.findRelease = findRelease;
|
||||||
/** Get PyPy binary location from the tool of installation directory
|
|
||||||
* - On Linux and macOS, the Python interpreter is in 'bin'.
|
|
||||||
* - On Windows, it is in the installation root.
|
|
||||||
*/
|
|
||||||
function getPyPyBinaryPath(installDir) {
|
|
||||||
const _binDir = path.join(installDir, 'bin');
|
|
||||||
return utils_1.IS_WINDOWS ? installDir : _binDir;
|
|
||||||
}
|
|
||||||
exports.getPyPyBinaryPath = getPyPyBinaryPath;
|
|
||||||
function pypyVersionToSemantic(versionSpec) {
|
function pypyVersionToSemantic(versionSpec) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
||||||
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
||||||
|
@ -69804,6 +70137,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
const finder = __importStar(__nccwpck_require__(9996));
|
const finder = __importStar(__nccwpck_require__(9996));
|
||||||
const finderPyPy = __importStar(__nccwpck_require__(4003));
|
const finderPyPy = __importStar(__nccwpck_require__(4003));
|
||||||
|
const finderGraalPy = __importStar(__nccwpck_require__(8040));
|
||||||
const path = __importStar(__nccwpck_require__(1017));
|
const path = __importStar(__nccwpck_require__(1017));
|
||||||
const os = __importStar(__nccwpck_require__(2037));
|
const os = __importStar(__nccwpck_require__(2037));
|
||||||
const fs_1 = __importDefault(__nccwpck_require__(7147));
|
const fs_1 = __importDefault(__nccwpck_require__(7147));
|
||||||
|
@ -69812,6 +70146,9 @@ const utils_1 = __nccwpck_require__(1314);
|
||||||
function isPyPyVersion(versionSpec) {
|
function isPyPyVersion(versionSpec) {
|
||||||
return versionSpec.startsWith('pypy');
|
return versionSpec.startsWith('pypy');
|
||||||
}
|
}
|
||||||
|
function isGraalPyVersion(versionSpec) {
|
||||||
|
return versionSpec.startsWith('graalpy');
|
||||||
|
}
|
||||||
function cacheDependencies(cache, pythonVersion) {
|
function cacheDependencies(cache, pythonVersion) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const cacheDependencyPath = core.getInput('cache-dependency-path') || undefined;
|
const cacheDependencyPath = core.getInput('cache-dependency-path') || undefined;
|
||||||
|
@ -69880,6 +70217,11 @@ function run() {
|
||||||
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
|
pythonVersion = `${installed.resolvedPyPyVersion}-${installed.resolvedPythonVersion}`;
|
||||||
core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
|
core.info(`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`);
|
||||||
}
|
}
|
||||||
|
else if (isGraalPyVersion(version)) {
|
||||||
|
const installed = yield finderGraalPy.findGraalPyVersion(version, arch, updateEnvironment, checkLatest, allowPreReleases);
|
||||||
|
pythonVersion = `${installed}`;
|
||||||
|
core.info(`Successfully set up GraalPy ${installed}`);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (version.startsWith('2')) {
|
if (version.startsWith('2')) {
|
||||||
core.warning('The support for python 2.7 will be removed on June 19. Related issue: https://github.com/actions/setup-python/issues/672');
|
core.warning('The support for python 2.7 will be removed on June 19. Related issue: https://github.com/actions/setup-python/issues/672');
|
||||||
|
@ -69948,7 +70290,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.getVersionInputFromFile = exports.getVersionInputFromPlainFile = exports.getVersionInputFromTomlFile = exports.getOSInfo = exports.getLinuxInfo = exports.logWarning = exports.isCacheFeatureAvailable = exports.isGhes = exports.validatePythonVersionFormatForPyPy = exports.writeExactPyPyVersionFile = exports.readExactPyPyVersionFile = exports.getPyPyVersionFromPath = exports.isNightlyKeyword = exports.validateVersion = exports.createSymlinkInFolder = exports.WINDOWS_PLATFORMS = exports.WINDOWS_ARCHS = exports.IS_MAC = exports.IS_LINUX = exports.IS_WINDOWS = void 0;
|
exports.getNextPageUrl = exports.getBinaryDirectory = exports.getVersionInputFromFile = exports.getVersionInputFromPlainFile = exports.getVersionInputFromTomlFile = exports.getOSInfo = exports.getLinuxInfo = exports.logWarning = exports.isCacheFeatureAvailable = exports.isGhes = exports.validatePythonVersionFormatForPyPy = exports.writeExactPyPyVersionFile = exports.readExactPyPyVersionFile = exports.getPyPyVersionFromPath = exports.isNightlyKeyword = exports.validateVersion = exports.createSymlinkInFolder = exports.WINDOWS_PLATFORMS = exports.WINDOWS_ARCHS = exports.IS_MAC = exports.IS_LINUX = exports.IS_WINDOWS = void 0;
|
||||||
/* eslint no-unsafe-finally: "off" */
|
/* eslint no-unsafe-finally: "off" */
|
||||||
const cache = __importStar(__nccwpck_require__(7799));
|
const cache = __importStar(__nccwpck_require__(7799));
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
|
@ -70177,6 +70519,37 @@ function getVersionInputFromFile(versionFile) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.getVersionInputFromFile = getVersionInputFromFile;
|
exports.getVersionInputFromFile = getVersionInputFromFile;
|
||||||
|
/**
|
||||||
|
* Get the directory containing interpreter binary from installation directory of PyPy or GraalPy
|
||||||
|
* - On Linux and macOS, the Python interpreter is in 'bin'.
|
||||||
|
* - On Windows, it is in the installation root.
|
||||||
|
*/
|
||||||
|
function getBinaryDirectory(installDir) {
|
||||||
|
return exports.IS_WINDOWS ? installDir : path.join(installDir, 'bin');
|
||||||
|
}
|
||||||
|
exports.getBinaryDirectory = getBinaryDirectory;
|
||||||
|
/**
|
||||||
|
* Extract next page URL from a HTTP response "link" header. Such headers are used in GitHub APIs.
|
||||||
|
*/
|
||||||
|
function getNextPageUrl(response) {
|
||||||
|
const responseHeaders = response.headers;
|
||||||
|
const linkHeader = responseHeaders.link;
|
||||||
|
if (typeof linkHeader === 'string') {
|
||||||
|
for (const link of linkHeader.split(/\s*,\s*/)) {
|
||||||
|
const match = link.match(/<([^>]+)>(.*)/);
|
||||||
|
if (match) {
|
||||||
|
const url = match[1];
|
||||||
|
for (const param of match[2].split(/\s*;\s*/)) {
|
||||||
|
if (param.match(/rel="?next"?/)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
exports.getNextPageUrl = getNextPageUrl;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
146
src/find-graalpy.ts
Normal file
146
src/find-graalpy.ts
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as graalpyInstall from './install-graalpy';
|
||||||
|
import {
|
||||||
|
IS_WINDOWS,
|
||||||
|
validateVersion,
|
||||||
|
IGraalPyManifestRelease,
|
||||||
|
getBinaryDirectory
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
|
||||||
|
export async function findGraalPyVersion(
|
||||||
|
versionSpec: string,
|
||||||
|
architecture: string,
|
||||||
|
updateEnvironment: boolean,
|
||||||
|
checkLatest: boolean,
|
||||||
|
allowPreReleases: boolean
|
||||||
|
): Promise<string> {
|
||||||
|
let resolvedGraalPyVersion = '';
|
||||||
|
let installDir: string | null;
|
||||||
|
let releases: IGraalPyManifestRelease[] | undefined;
|
||||||
|
|
||||||
|
let graalpyVersionSpec = parseGraalPyVersion(versionSpec);
|
||||||
|
|
||||||
|
if (checkLatest) {
|
||||||
|
releases = await graalpyInstall.getAvailableGraalPyVersions();
|
||||||
|
if (releases && releases.length > 0) {
|
||||||
|
const releaseData = graalpyInstall.findRelease(
|
||||||
|
releases,
|
||||||
|
graalpyVersionSpec,
|
||||||
|
architecture,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (releaseData) {
|
||||||
|
core.info(`Resolved as GraalPy ${releaseData.resolvedGraalPyVersion}`);
|
||||||
|
graalpyVersionSpec = releaseData.resolvedGraalPyVersion;
|
||||||
|
} else {
|
||||||
|
core.info(
|
||||||
|
`Failed to resolve GraalPy ${graalpyVersionSpec} from manifest`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
({installDir, resolvedGraalPyVersion} = findGraalPyToolCache(
|
||||||
|
graalpyVersionSpec,
|
||||||
|
architecture
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!installDir) {
|
||||||
|
({installDir, resolvedGraalPyVersion} = await graalpyInstall.installGraalPy(
|
||||||
|
graalpyVersionSpec,
|
||||||
|
architecture,
|
||||||
|
allowPreReleases,
|
||||||
|
releases
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
const pipDir = IS_WINDOWS ? 'Scripts' : 'bin';
|
||||||
|
const _binDir = path.join(installDir, pipDir);
|
||||||
|
const binaryExtension = IS_WINDOWS ? '.exe' : '';
|
||||||
|
const pythonPath = path.join(
|
||||||
|
IS_WINDOWS ? installDir : _binDir,
|
||||||
|
`python${binaryExtension}`
|
||||||
|
);
|
||||||
|
const pythonLocation = getBinaryDirectory(installDir);
|
||||||
|
if (updateEnvironment) {
|
||||||
|
core.exportVariable('pythonLocation', installDir);
|
||||||
|
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
||||||
|
core.exportVariable('Python_ROOT_DIR', installDir);
|
||||||
|
// https://cmake.org/cmake/help/latest/module/FindPython2.html#module:FindPython2
|
||||||
|
core.exportVariable('Python2_ROOT_DIR', installDir);
|
||||||
|
// https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3
|
||||||
|
core.exportVariable('Python3_ROOT_DIR', installDir);
|
||||||
|
core.exportVariable('PKG_CONFIG_PATH', pythonLocation + '/lib/pkgconfig');
|
||||||
|
core.addPath(pythonLocation);
|
||||||
|
core.addPath(_binDir);
|
||||||
|
}
|
||||||
|
core.setOutput('python-version', 'graalpy' + resolvedGraalPyVersion);
|
||||||
|
core.setOutput('python-path', pythonPath);
|
||||||
|
|
||||||
|
return resolvedGraalPyVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findGraalPyToolCache(
|
||||||
|
graalpyVersion: string,
|
||||||
|
architecture: string
|
||||||
|
) {
|
||||||
|
let resolvedGraalPyVersion = '';
|
||||||
|
let installDir: string | null = tc.find(
|
||||||
|
'GraalPy',
|
||||||
|
graalpyVersion,
|
||||||
|
architecture
|
||||||
|
);
|
||||||
|
|
||||||
|
if (installDir) {
|
||||||
|
// 'tc.find' finds tool based on Python version but we also need to check
|
||||||
|
// whether GraalPy version satisfies requested version.
|
||||||
|
resolvedGraalPyVersion = path.basename(path.dirname(installDir));
|
||||||
|
|
||||||
|
const isGraalPyVersionSatisfies = semver.satisfies(
|
||||||
|
resolvedGraalPyVersion,
|
||||||
|
graalpyVersion
|
||||||
|
);
|
||||||
|
if (!isGraalPyVersionSatisfies) {
|
||||||
|
installDir = null;
|
||||||
|
resolvedGraalPyVersion = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!installDir) {
|
||||||
|
core.info(
|
||||||
|
`GraalPy version ${graalpyVersion} was not found in the local cache`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {installDir, resolvedGraalPyVersion};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseGraalPyVersion(versionSpec: string): string {
|
||||||
|
const versions = versionSpec.split('-').filter(item => !!item);
|
||||||
|
|
||||||
|
if (/^(graalpy)(.+)/.test(versions[0])) {
|
||||||
|
const version = versions[0].replace('graalpy', '');
|
||||||
|
versions.splice(0, 1, 'graalpy', version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versions.length < 2 || versions[0] != 'graalpy') {
|
||||||
|
throw new Error(
|
||||||
|
"Invalid 'version' property for GraalPy. GraalPy version should be specified as 'graalpy<python-version>' or 'graalpy-<python-version>'. See README for examples and documentation."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pythonVersion = versions[1];
|
||||||
|
|
||||||
|
if (!validateVersion(pythonVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
"Invalid 'version' property for GraalPy. GraalPy versions should satisfy SemVer notation. See README for examples and documentation."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pythonVersion;
|
||||||
|
}
|
|
@ -7,7 +7,8 @@ import {
|
||||||
getPyPyVersionFromPath,
|
getPyPyVersionFromPath,
|
||||||
readExactPyPyVersionFile,
|
readExactPyPyVersionFile,
|
||||||
validatePythonVersionFormatForPyPy,
|
validatePythonVersionFormatForPyPy,
|
||||||
IPyPyManifestRelease
|
IPyPyManifestRelease,
|
||||||
|
getBinaryDirectory
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
@ -82,7 +83,7 @@ export async function findPyPyVersion(
|
||||||
IS_WINDOWS ? installDir : _binDir,
|
IS_WINDOWS ? installDir : _binDir,
|
||||||
`python${binaryExtension}`
|
`python${binaryExtension}`
|
||||||
);
|
);
|
||||||
const pythonLocation = pypyInstall.getPyPyBinaryPath(installDir);
|
const pythonLocation = getBinaryDirectory(installDir);
|
||||||
if (updateEnvironment) {
|
if (updateEnvironment) {
|
||||||
core.exportVariable('pythonLocation', installDir);
|
core.exportVariable('pythonLocation', installDir);
|
||||||
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
// https://cmake.org/cmake/help/latest/module/FindPython.html#module:FindPython
|
||||||
|
|
262
src/install-graalpy.ts
Normal file
262
src/install-graalpy.ts
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as httpm from '@actions/http-client';
|
||||||
|
import * as ifm from '@actions/http-client/interfaces';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IS_WINDOWS,
|
||||||
|
IGraalPyManifestRelease,
|
||||||
|
createSymlinkInFolder,
|
||||||
|
isNightlyKeyword,
|
||||||
|
getBinaryDirectory,
|
||||||
|
getNextPageUrl
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
const TOKEN = core.getInput('token');
|
||||||
|
const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
|
||||||
|
|
||||||
|
export async function installGraalPy(
|
||||||
|
graalpyVersion: string,
|
||||||
|
architecture: string,
|
||||||
|
allowPreReleases: boolean,
|
||||||
|
releases: IGraalPyManifestRelease[] | undefined
|
||||||
|
) {
|
||||||
|
let downloadDir;
|
||||||
|
|
||||||
|
releases = releases ?? (await getAvailableGraalPyVersions());
|
||||||
|
|
||||||
|
if (!releases || !releases.length) {
|
||||||
|
throw new Error('No release was found in GraalPy version.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
let releaseData = findRelease(releases, graalpyVersion, architecture, false);
|
||||||
|
|
||||||
|
if (allowPreReleases && (!releaseData || !releaseData.foundAsset)) {
|
||||||
|
// check for pre-release
|
||||||
|
core.info(
|
||||||
|
[
|
||||||
|
`Stable GraalPy version ${graalpyVersion} with arch ${architecture} not found`,
|
||||||
|
`Trying pre-release versions`
|
||||||
|
].join(os.EOL)
|
||||||
|
);
|
||||||
|
releaseData = findRelease(releases, graalpyVersion, architecture, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!releaseData || !releaseData.foundAsset) {
|
||||||
|
throw new Error(
|
||||||
|
`GraalPy version ${graalpyVersion} with arch ${architecture} not found`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {foundAsset, resolvedGraalPyVersion} = releaseData;
|
||||||
|
const downloadUrl = `${foundAsset.browser_download_url}`;
|
||||||
|
|
||||||
|
core.info(`Downloading GraalPy from "${downloadUrl}" ...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const graalpyPath = await tc.downloadTool(downloadUrl, undefined, AUTH);
|
||||||
|
|
||||||
|
core.info('Extracting downloaded archive...');
|
||||||
|
downloadDir = await tc.extractTar(graalpyPath);
|
||||||
|
|
||||||
|
// root folder in archive can have unpredictable name so just take the first folder
|
||||||
|
// downloadDir is unique folder under TEMP and can't contain any other folders
|
||||||
|
const archiveName = fs.readdirSync(downloadDir)[0];
|
||||||
|
|
||||||
|
const toolDir = path.join(downloadDir, archiveName);
|
||||||
|
let installDir = toolDir;
|
||||||
|
if (!isNightlyKeyword(resolvedGraalPyVersion)) {
|
||||||
|
installDir = await tc.cacheDir(
|
||||||
|
toolDir,
|
||||||
|
'GraalPy',
|
||||||
|
resolvedGraalPyVersion,
|
||||||
|
architecture
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const binaryPath = getBinaryDirectory(installDir);
|
||||||
|
await createGraalPySymlink(binaryPath, resolvedGraalPyVersion);
|
||||||
|
await installPip(binaryPath);
|
||||||
|
|
||||||
|
return {installDir, resolvedGraalPyVersion};
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
// Rate limit?
|
||||||
|
if (
|
||||||
|
err instanceof tc.HTTPError &&
|
||||||
|
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
|
||||||
|
) {
|
||||||
|
core.info(
|
||||||
|
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
core.info(err.message);
|
||||||
|
}
|
||||||
|
if (err.stack !== undefined) {
|
||||||
|
core.debug(err.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAvailableGraalPyVersions() {
|
||||||
|
const http: httpm.HttpClient = new httpm.HttpClient('tool-cache');
|
||||||
|
|
||||||
|
const headers: ifm.IHeaders = {};
|
||||||
|
if (AUTH) {
|
||||||
|
headers.authorization = AUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url: string | null =
|
||||||
|
'https://api.github.com/repos/oracle/graalpython/releases';
|
||||||
|
const result: IGraalPyManifestRelease[] = [];
|
||||||
|
do {
|
||||||
|
const response: ifm.ITypedResponse<IGraalPyManifestRelease[]> =
|
||||||
|
await http.getJson(url, headers);
|
||||||
|
if (!response.result) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to retrieve the list of available GraalPy versions from '${url}'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result.push(...response.result);
|
||||||
|
url = getNextPageUrl(response);
|
||||||
|
} while (url);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createGraalPySymlink(
|
||||||
|
graalpyBinaryPath: string,
|
||||||
|
graalpyVersion: string
|
||||||
|
) {
|
||||||
|
const version = semver.coerce(graalpyVersion)!;
|
||||||
|
const pythonBinaryPostfix = semver.major(version);
|
||||||
|
const pythonMinor = semver.minor(version);
|
||||||
|
const graalpyMajorMinorBinaryPostfix = `${pythonBinaryPostfix}.${pythonMinor}`;
|
||||||
|
const binaryExtension = IS_WINDOWS ? '.exe' : '';
|
||||||
|
|
||||||
|
core.info('Creating symlinks...');
|
||||||
|
createSymlinkInFolder(
|
||||||
|
graalpyBinaryPath,
|
||||||
|
`graalpy${binaryExtension}`,
|
||||||
|
`python${pythonBinaryPostfix}${binaryExtension}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
createSymlinkInFolder(
|
||||||
|
graalpyBinaryPath,
|
||||||
|
`graalpy${binaryExtension}`,
|
||||||
|
`python${binaryExtension}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
createSymlinkInFolder(
|
||||||
|
graalpyBinaryPath,
|
||||||
|
`graalpy${binaryExtension}`,
|
||||||
|
`graalpy${graalpyMajorMinorBinaryPostfix}${binaryExtension}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function installPip(pythonLocation: string) {
|
||||||
|
core.info(
|
||||||
|
"Installing pip (GraalPy doesn't update pip because it uses a patched version of pip)"
|
||||||
|
);
|
||||||
|
const pythonBinary = path.join(pythonLocation, 'python');
|
||||||
|
await exec.exec(`${pythonBinary} -m ensurepip --default-pip`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function graalPyTagToVersion(tag: string) {
|
||||||
|
const versionPattern = /.*-(\d+\.\d+\.\d+(?:\.\d+)?)((?:a|b|rc))?(\d*)?/;
|
||||||
|
const match = tag.match(versionPattern);
|
||||||
|
if (match && match[2]) {
|
||||||
|
return `${match[1]}-${match[2]}.${match[3]}`;
|
||||||
|
} else if (match) {
|
||||||
|
return match[1];
|
||||||
|
} else {
|
||||||
|
return tag.replace(/.*-/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findRelease(
|
||||||
|
releases: IGraalPyManifestRelease[],
|
||||||
|
graalpyVersion: string,
|
||||||
|
architecture: string,
|
||||||
|
includePrerelease: boolean
|
||||||
|
) {
|
||||||
|
const options = {includePrerelease: includePrerelease};
|
||||||
|
const filterReleases = releases.filter(item => {
|
||||||
|
const isVersionSatisfied = semver.satisfies(
|
||||||
|
graalPyTagToVersion(item.tag_name),
|
||||||
|
graalpyVersion,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
isVersionSatisfied && !!findAsset(item, architecture, process.platform)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!filterReleases.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortedReleases = filterReleases.sort((previous, current) =>
|
||||||
|
semver.compare(
|
||||||
|
semver.coerce(graalPyTagToVersion(current.tag_name))!,
|
||||||
|
semver.coerce(graalPyTagToVersion(previous.tag_name))!
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const foundRelease = sortedReleases[0];
|
||||||
|
const foundAsset = findAsset(foundRelease, architecture, process.platform);
|
||||||
|
|
||||||
|
return {
|
||||||
|
foundAsset,
|
||||||
|
resolvedGraalPyVersion: graalPyTagToVersion(foundRelease.tag_name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toGraalPyPlatform(platform: string) {
|
||||||
|
switch (platform) {
|
||||||
|
case 'win32':
|
||||||
|
return 'windows';
|
||||||
|
case 'darwin':
|
||||||
|
return 'macos';
|
||||||
|
}
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toGraalPyArchitecture(architecture: string) {
|
||||||
|
switch (architecture) {
|
||||||
|
case 'x64':
|
||||||
|
return 'amd64';
|
||||||
|
case 'arm64':
|
||||||
|
return 'aarch64';
|
||||||
|
}
|
||||||
|
return architecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findAsset(
|
||||||
|
item: IGraalPyManifestRelease,
|
||||||
|
architecture: string,
|
||||||
|
platform: string
|
||||||
|
) {
|
||||||
|
const graalpyArch = toGraalPyArchitecture(architecture);
|
||||||
|
const graalpyPlatform = toGraalPyPlatform(platform);
|
||||||
|
const found = item.assets.filter(
|
||||||
|
file =>
|
||||||
|
file.name.startsWith('graalpy') &&
|
||||||
|
file.name.endsWith(`-${graalpyPlatform}-${graalpyArch}.tar.gz`)
|
||||||
|
);
|
||||||
|
/*
|
||||||
|
In the future there could be more variants of GraalPy for a single release. Pick the shortest name, that one is the most likely to be the primary variant.
|
||||||
|
*/
|
||||||
|
found.sort((f1, f2) => f1.name.length - f2.name.length);
|
||||||
|
return found[0];
|
||||||
|
}
|
|
@ -13,7 +13,8 @@ import {
|
||||||
IPyPyManifestRelease,
|
IPyPyManifestRelease,
|
||||||
createSymlinkInFolder,
|
createSymlinkInFolder,
|
||||||
isNightlyKeyword,
|
isNightlyKeyword,
|
||||||
writeExactPyPyVersionFile
|
writeExactPyPyVersionFile,
|
||||||
|
getBinaryDirectory
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
export async function installPyPy(
|
export async function installPyPy(
|
||||||
|
@ -94,7 +95,7 @@ export async function installPyPy(
|
||||||
|
|
||||||
writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
writeExactPyPyVersionFile(installDir, resolvedPyPyVersion);
|
||||||
|
|
||||||
const binaryPath = getPyPyBinaryPath(installDir);
|
const binaryPath = getBinaryDirectory(installDir);
|
||||||
await createPyPySymlink(binaryPath, resolvedPythonVersion);
|
await createPyPySymlink(binaryPath, resolvedPythonVersion);
|
||||||
await installPip(binaryPath);
|
await installPip(binaryPath);
|
||||||
|
|
||||||
|
@ -237,15 +238,6 @@ export function findRelease(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get PyPy binary location from the tool of installation directory
|
|
||||||
* - On Linux and macOS, the Python interpreter is in 'bin'.
|
|
||||||
* - On Windows, it is in the installation root.
|
|
||||||
*/
|
|
||||||
export function getPyPyBinaryPath(installDir: string) {
|
|
||||||
const _binDir = path.join(installDir, 'bin');
|
|
||||||
return IS_WINDOWS ? installDir : _binDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pypyVersionToSemantic(versionSpec: string) {
|
export function pypyVersionToSemantic(versionSpec: string) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc))(\d*)/g;
|
||||||
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
return versionSpec.replace(prereleaseVersion, '$1-$2.$3');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as finder from './find-python';
|
import * as finder from './find-python';
|
||||||
import * as finderPyPy from './find-pypy';
|
import * as finderPyPy from './find-pypy';
|
||||||
|
import * as finderGraalPy from './find-graalpy';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
@ -17,6 +18,10 @@ function isPyPyVersion(versionSpec: string) {
|
||||||
return versionSpec.startsWith('pypy');
|
return versionSpec.startsWith('pypy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isGraalPyVersion(versionSpec: string) {
|
||||||
|
return versionSpec.startsWith('graalpy');
|
||||||
|
}
|
||||||
|
|
||||||
async function cacheDependencies(cache: string, pythonVersion: string) {
|
async function cacheDependencies(cache: string, pythonVersion: string) {
|
||||||
const cacheDependencyPath =
|
const cacheDependencyPath =
|
||||||
core.getInput('cache-dependency-path') || undefined;
|
core.getInput('cache-dependency-path') || undefined;
|
||||||
|
@ -106,6 +111,16 @@ async function run() {
|
||||||
core.info(
|
core.info(
|
||||||
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
|
`Successfully set up PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
|
||||||
);
|
);
|
||||||
|
} else if (isGraalPyVersion(version)) {
|
||||||
|
const installed = await finderGraalPy.findGraalPyVersion(
|
||||||
|
version,
|
||||||
|
arch,
|
||||||
|
updateEnvironment,
|
||||||
|
checkLatest,
|
||||||
|
allowPreReleases
|
||||||
|
);
|
||||||
|
pythonVersion = `${installed}`;
|
||||||
|
core.info(`Successfully set up GraalPy ${installed}`);
|
||||||
} else {
|
} else {
|
||||||
if (version.startsWith('2')) {
|
if (version.startsWith('2')) {
|
||||||
core.warning(
|
core.warning(
|
||||||
|
|
42
src/utils.ts
42
src/utils.ts
|
@ -6,6 +6,7 @@ import * as path from 'path';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import * as toml from '@iarna/toml';
|
import * as toml from '@iarna/toml';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
|
import * as ifm from '@actions/http-client/interfaces';
|
||||||
|
|
||||||
export const IS_WINDOWS = process.platform === 'win32';
|
export const IS_WINDOWS = process.platform === 'win32';
|
||||||
export const IS_LINUX = process.platform === 'linux';
|
export const IS_LINUX = process.platform === 'linux';
|
||||||
|
@ -29,6 +30,16 @@ export interface IPyPyManifestRelease {
|
||||||
files: IPyPyManifestAsset[];
|
files: IPyPyManifestAsset[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IGraalPyManifestAsset {
|
||||||
|
name: string;
|
||||||
|
browser_download_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGraalPyManifestRelease {
|
||||||
|
tag_name: string;
|
||||||
|
assets: IGraalPyManifestAsset[];
|
||||||
|
}
|
||||||
|
|
||||||
/** create Symlinks for downloaded PyPy
|
/** create Symlinks for downloaded PyPy
|
||||||
* It should be executed only for downloaded versions in runtime, because
|
* It should be executed only for downloaded versions in runtime, because
|
||||||
* toolcache versions have this setup.
|
* toolcache versions have this setup.
|
||||||
|
@ -266,3 +277,34 @@ export function getVersionInputFromFile(versionFile: string): string[] {
|
||||||
return getVersionInputFromPlainFile(versionFile);
|
return getVersionInputFromPlainFile(versionFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the directory containing interpreter binary from installation directory of PyPy or GraalPy
|
||||||
|
* - On Linux and macOS, the Python interpreter is in 'bin'.
|
||||||
|
* - On Windows, it is in the installation root.
|
||||||
|
*/
|
||||||
|
export function getBinaryDirectory(installDir: string) {
|
||||||
|
return IS_WINDOWS ? installDir : path.join(installDir, 'bin');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract next page URL from a HTTP response "link" header. Such headers are used in GitHub APIs.
|
||||||
|
*/
|
||||||
|
export function getNextPageUrl<T>(response: ifm.ITypedResponse<T>) {
|
||||||
|
const responseHeaders = <ifm.IHeaders>response.headers;
|
||||||
|
const linkHeader = responseHeaders.link;
|
||||||
|
if (typeof linkHeader === 'string') {
|
||||||
|
for (const link of linkHeader.split(/\s*,\s*/)) {
|
||||||
|
const match = link.match(/<([^>]+)>(.*)/);
|
||||||
|
if (match) {
|
||||||
|
const url = match[1];
|
||||||
|
for (const param of match[2].split(/\s*;\s*/)) {
|
||||||
|
if (param.match(/rel="?next"?/)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue