Skip to content

Commit

Permalink
feature: add no-environment-update input
Browse files Browse the repository at this point in the history
This option allows to specify if the action shall update environment variables (default) or not.
This allows to use the setup-python action in a composite action without side effect (except downloading/installing python if version is missing).
  • Loading branch information
mayeut committed May 26, 2022
1 parent 081a3cf commit df47808
Show file tree
Hide file tree
Showing 16 changed files with 2,360 additions and 1,830 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/test-pypy.yml
Expand Up @@ -65,3 +65,33 @@ jobs:
EXECUTABLE=${EXECUTABLE%%-*} # remove any -* suffixe
${EXECUTABLE} --version
shell: bash

setup-pypy-noenv:
name: Setup PyPy ${{ matrix.pypy }} ${{ matrix.os }} (noenv)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-latest]
pypy:
- 'pypy2.7'
- 'pypy3.7'
- 'pypy3.8'
- 'pypy3.9-nightly'

steps:
- name: Checkout
uses: actions/checkout@v3

- name: setup-python ${{ matrix.pypy }}
id: setup-python
uses: ./
with:
python-version: ${{ matrix.pypy }}
no-environment-update: true

- name: PyPy 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))'
26 changes: 25 additions & 1 deletion .github/workflows/test-python.yml
@@ -1,5 +1,5 @@
name: Validate Python e2e
on:
on:
push:
branches:
- main
Expand Down Expand Up @@ -106,3 +106,27 @@ jobs:
- name: Run simple code
run: python -c 'import math; print(math.factorial(5))'

setup-versions-noenv:
name: Setup ${{ matrix.python }} ${{ matrix.os }} (noenv)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-18.04, ubuntu-20.04]
python: ["3.7", "3.8", "3.9", "3.10"]
steps:
- name: Checkout
uses: actions/checkout@v3

- name: setup-python ${{ matrix.python }}
id: setup-python
uses: ./
with:
python-version: ${{ matrix.python }}
no-environment-update: true

- name: 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))'
2 changes: 1 addition & 1 deletion .licenses/npm/@actions/core.dep.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 0 additions & 32 deletions .licenses/npm/@actions/http-client-1.0.8.dep.yml

This file was deleted.

File renamed without changes.
20 changes: 20 additions & 0 deletions README.md
Expand Up @@ -305,6 +305,26 @@ steps:
- run: pipenv install
```

# No environment update

The `no-environment-update` flag defaults to `false`.
With this setting, the action will add/update environment variables (e.g. `PATH`, `PKG_CONFIG_PATH`, `pythonLocation`) for `python` to just work out of the box.

If `no-environment-update` is set to `true`, the action will not add/update environment variables.
This can prove useful if you want the only side-effect to be to ensure python is installed and rely on the `python-path` output to run python.
Such a requirement on side-effect could be because you don't want your composite action messing with your user's workflows.

```yaml
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
id: cp310
with:
python-version: '3.10'
no-environment-update: true
- run: ${{ steps.cp310.outputs.python-path }} my_script.py
```

# Using `setup-python` with a self hosted runner

Python distributions are only available for the same [environments](https://github.com/actions/virtual-environments#available-environments) that GitHub Actions hosted environments are available for. If you are using an unsupported version of Ubuntu such as `19.04` or another Linux distribution such as Fedora, `setup-python` will not work. If you have a supported self-hosted runner and you would like to use `setup-python`, there are a few extra things you need to make sure are set up so that new versions of Python can be downloaded and configured on your runner.
Expand Down
52 changes: 47 additions & 5 deletions __tests__/find-pypy.test.ts
Expand Up @@ -5,6 +5,7 @@ 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';
Expand Down Expand Up @@ -148,6 +149,8 @@ describe('findPyPyVersion', () => {
let spyWriteExactPyPyVersionFile: jest.SpyInstance;
let spyCacheDir: jest.SpyInstance;
let spyChmodSync: jest.SpyInstance;
let spyCoreAddPath: jest.SpyInstance;
let spyCoreExportVariable: jest.SpyInstance;

beforeEach(() => {
tcFind = jest.spyOn(tc, 'find');
Expand Down Expand Up @@ -201,6 +204,10 @@ describe('findPyPyVersion', () => {

spyExistsSync = jest.spyOn(fs, 'existsSync');
spyExistsSync.mockReturnValue(true);

spyCoreAddPath = jest.spyOn(core, 'addPath');

spyCoreExportVariable = jest.spyOn(core, 'exportVariable');
});

afterEach(() => {
Expand All @@ -211,22 +218,31 @@ describe('findPyPyVersion', () => {

it('found PyPy in toolcache', async () => {
await expect(
finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture)
finder.findPyPyVersion('pypy-3.6-v7.3.x', architecture, true)
).resolves.toEqual({
resolvedPythonVersion: '3.6.12',
resolvedPyPyVersion: '7.3.3'
});
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.findPyPyVersion('pypy3.7-v7.3.x', architecture)
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true)
).rejects.toThrow();
});

it('throw on invalid input format pypy3.7-7.3.x', async () => {
await expect(
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture)
finder.findPyPyVersion('pypy3.7-v7.3.x', architecture, true)
).rejects.toThrow();
});

Expand All @@ -238,16 +254,42 @@ describe('findPyPyVersion', () => {
spyChmodSync = jest.spyOn(fs, 'chmodSync');
spyChmodSync.mockImplementation(() => undefined);
await expect(
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture)
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, true)
).resolves.toEqual({
resolvedPythonVersion: '3.7.9',
resolvedPyPyVersion: '7.3.3'
});
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, 'PyPy', '3.7.7', architecture)
);
spyChmodSync = jest.spyOn(fs, 'chmodSync');
spyChmodSync.mockImplementation(() => undefined);
await expect(
finder.findPyPyVersion('pypy-3.7-v7.3.x', architecture, false)
).resolves.toEqual({
resolvedPythonVersion: '3.7.9',
resolvedPyPyVersion: '7.3.3'
});
expect(spyCoreAddPath).not.toHaveBeenCalled();
expect(spyCoreExportVariable).not.toHaveBeenCalled();
});

it('throw if release is not found', async () => {
await expect(
finder.findPyPyVersion('pypy-3.7-v7.5.x', architecture)
finder.findPyPyVersion('pypy-3.7-v7.5.x', architecture, true)
).rejects.toThrowError(
`PyPy version 3.7 (v7.5.x) with arch ${architecture} not found`
);
Expand Down
59 changes: 55 additions & 4 deletions __tests__/finder.test.ts
Expand Up @@ -19,23 +19,54 @@ process.env['RUNNER_TOOL_CACHE'] = toolDir;
process.env['RUNNER_TEMP'] = tempDir;

import * as tc from '@actions/tool-cache';
import * as core from '@actions/core';
import * as finder from '../src/find-python';
import * as installer from '../src/install-python';

const manifestData = require('./data/versions-manifest.json');

describe('Finder tests', () => {
let spyCoreAddPath: jest.SpyInstance;
let spyCoreExportVariable: jest.SpyInstance;

beforeEach(() => {
spyCoreAddPath = jest.spyOn(core, 'addPath');

spyCoreExportVariable = jest.spyOn(core, 'exportVariable');
});

afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
jest.restoreAllMocks();
});

it('Finds Python if it is installed', async () => {
const pythonDir: string = path.join(toolDir, 'Python', '3.0.0', 'x64');
await io.mkdirP(pythonDir);
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
await finder.useCpythonVersion('3.x', 'x64');
await finder.useCpythonVersion('3.x', 'x64', true);
expect(spyCoreAddPath).toHaveBeenCalled();
expect(spyCoreExportVariable).toHaveBeenCalledWith(
'pythonLocation',
expect.anything()
);
expect(spyCoreExportVariable).toHaveBeenCalledWith(
'PKG_CONFIG_PATH',
expect.anything()
);
});

it('Finds Python if it is installed without environment update', async () => {
const pythonDir: string = path.join(toolDir, 'Python', '3.0.0', 'x64');
await io.mkdirP(pythonDir);
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
await finder.useCpythonVersion('3.x', 'x64', false);
expect(spyCoreAddPath).not.toHaveBeenCalled();
expect(spyCoreExportVariable).not.toHaveBeenCalled();
expect(spyCoreExportVariable).not.toHaveBeenCalled();
});

it('Finds stable Python version if it is not installed, but exists in the manifest', async () => {
Expand All @@ -52,7 +83,16 @@ describe('Finder tests', () => {
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
});
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
await finder.useCpythonVersion('1.2.3', 'x64');
await finder.useCpythonVersion('1.2.3', 'x64', true);
expect(spyCoreAddPath).toHaveBeenCalled();
expect(spyCoreExportVariable).toHaveBeenCalledWith(
'pythonLocation',
expect.anything()
);
expect(spyCoreExportVariable).toHaveBeenCalledWith(
'PKG_CONFIG_PATH',
expect.anything()
);
});

it('Finds pre-release Python version in the manifest', async () => {
Expand All @@ -74,17 +114,28 @@ describe('Finder tests', () => {
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
});
// This will throw if it doesn't find it in the manifest (because no such version exists)
await finder.useCpythonVersion('1.2.3-beta.2', 'x64');
await finder.useCpythonVersion('1.2.3-beta.2', 'x64', true);
expect(spyCoreAddPath).toHaveBeenCalled();
expect(spyCoreExportVariable).toHaveBeenCalledWith(
'pythonLocation',
expect.anything()
);
expect(spyCoreExportVariable).toHaveBeenCalledWith(
'PKG_CONFIG_PATH',
expect.anything()
);
});

it('Errors if Python is not installed', async () => {
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
let thrown = false;
try {
await finder.useCpythonVersion('3.300000', 'x64');
await finder.useCpythonVersion('3.300000', 'x64', true);
} catch {
thrown = true;
}
expect(thrown).toBeTruthy();
expect(spyCoreAddPath).not.toHaveBeenCalled();
expect(spyCoreExportVariable).not.toHaveBeenCalled();
});
});
3 changes: 3 additions & 0 deletions action.yml
Expand Up @@ -16,6 +16,9 @@ inputs:
default: ${{ github.token }}
cache-dependency-path:
description: 'Used to specify the path to dependency files. Supports wildcards or a list of file names for caching multiple dependencies.'
no-environment-update:
description: 'Set this option if you want the action not to update environment variables.'
default: false
outputs:
python-version:
description: "The installed python version. Useful when given a version range as input."
Expand Down

0 comments on commit df47808

Please sign in to comment.