mirror of
https://github.com/opentofu/setup-opentofu.git
synced 2025-12-06 07:50:37 +00:00
Add OpenTofu version file support (#59)
Some checks failed
Continuous Integration / Check dist/ directory (push) Has been cancelled
Continuous Integration / Test (push) Has been cancelled
Setup OpenTofu / OpenTofu Version Files (push) Has been cancelled
Setup OpenTofu / OpenTofu Versions (push) Has been cancelled
Setup OpenTofu / OpenTofu Arguments (push) Has been cancelled
Setup OpenTofu / OpenTofu Run Local (push) Has been cancelled
Setup OpenTofu / OpenTofu Cloud Credentials (push) Has been cancelled
Setup OpenTofu / OpenTofu Enterprise Credentials (push) Has been cancelled
Setup OpenTofu / OpenTofu No Credentials (push) Has been cancelled
Some checks failed
Continuous Integration / Check dist/ directory (push) Has been cancelled
Continuous Integration / Test (push) Has been cancelled
Setup OpenTofu / OpenTofu Version Files (push) Has been cancelled
Setup OpenTofu / OpenTofu Versions (push) Has been cancelled
Setup OpenTofu / OpenTofu Arguments (push) Has been cancelled
Setup OpenTofu / OpenTofu Run Local (push) Has been cancelled
Setup OpenTofu / OpenTofu Cloud Credentials (push) Has been cancelled
Setup OpenTofu / OpenTofu Enterprise Credentials (push) Has been cancelled
Setup OpenTofu / OpenTofu No Credentials (push) Has been cancelled
Signed-off-by: Brendon Smith <bws@bws.bio>
This commit is contained in:
parent
4a98c1282d
commit
000eeb8522
7 changed files with 258 additions and 2 deletions
1
.github/workflows/data/local/.opentofu-version
vendored
Normal file
1
.github/workflows/data/local/.opentofu-version
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
latest
|
||||||
19
.github/workflows/setup-tofu.yml
vendored
19
.github/workflows/setup-tofu.yml
vendored
|
|
@ -14,6 +14,25 @@ env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
tofu-version-files:
|
||||||
|
name: 'OpenTofu Version Files'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
tofu-version-files: ['./.github/workflows/data/local/.opentofu-version']
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup OpenTofu - ${{ matrix['tofu-version-files'] }}
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
tofu_version_file: ${{ matrix['tofu-version-files'] }}
|
||||||
|
tofu_wrapper: false
|
||||||
|
|
||||||
|
- name: Validate that OpenTofu was installed
|
||||||
|
run: tofu version | grep 'OpenTofu v'
|
||||||
tofu-versions:
|
tofu-versions:
|
||||||
name: 'OpenTofu Versions'
|
name: 'OpenTofu Versions'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
|
||||||
13
README.md
13
README.md
|
|
@ -28,6 +28,17 @@ steps:
|
||||||
tofu_version: 1.6.0
|
tofu_version: 1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also specify the version in a file (e.g., `.opentofu-version`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: opentofu/setup-opentofu@v1
|
||||||
|
with:
|
||||||
|
tofu_version_file: .opentofu-version
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported version syntax is the same as for the `tofu_version` input. If both `tofu_version` and `tofu_version_file` are provided, the version number in the file takes precedence.
|
||||||
|
|
||||||
Credentials for Terraform Cloud ([app.terraform.io](https://app.terraform.io/)) can be configured:
|
Credentials for Terraform Cloud ([app.terraform.io](https://app.terraform.io/)) can be configured:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
@ -248,6 +259,8 @@ The action supports the following inputs:
|
||||||
for available range specifications). Examples are: `<1.6.0-beta`, `~1.6.0-alpha`, `1.6.0-alpha2` (all three installing
|
for available range specifications). Examples are: `<1.6.0-beta`, `~1.6.0-alpha`, `1.6.0-alpha2` (all three installing
|
||||||
the latest available `1.6.0-alpha2` version). Prerelease versions can be specified and a range will stay within the
|
the latest available `1.6.0-alpha2` version). Prerelease versions can be specified and a range will stay within the
|
||||||
given tag such as `beta` or `rc`. If no version is given, it will default to `latest`.
|
given tag such as `beta` or `rc`. If no version is given, it will default to `latest`.
|
||||||
|
- `tofu_version_file` - (optional) Path to a file containing the OpenTofu version to install. Supported version syntax
|
||||||
|
is the same as for the `tofu_version` input. Takes precedence over `tofu_version` if both are provided.
|
||||||
- `tofu_wrapper` - (optional) Whether to install a wrapper to wrap subsequent calls of
|
- `tofu_wrapper` - (optional) Whether to install a wrapper to wrap subsequent calls of
|
||||||
the `tofu` binary and expose its STDOUT, STDERR, and exit code as outputs
|
the `tofu` binary and expose its STDOUT, STDERR, and exit code as outputs
|
||||||
named `stdout`, `stderr`, and `exitcode` respectively. Defaults to `true`.
|
named `stdout`, `stderr`, and `exitcode` respectively. Defaults to `true`.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ inputs:
|
||||||
description: 'The version of OpenTofu CLI to install. If no version is given, it will default to `latest`.'
|
description: 'The version of OpenTofu CLI to install. If no version is given, it will default to `latest`.'
|
||||||
default: 'latest'
|
default: 'latest'
|
||||||
required: false
|
required: false
|
||||||
|
tofu_version_file:
|
||||||
|
description: 'Path to a file containing the OpenTofu version to install. Takes precedence over `tofu_version` if both are provided.'
|
||||||
|
required: false
|
||||||
tofu_wrapper:
|
tofu_wrapper:
|
||||||
description: 'Whether or not to install a wrapper to wrap subsequent calls of the `tofu` binary and expose its STDOUT, STDERR, and exit code as outputs named `stdout`, `stderr`, and `exitcode` respectively. Defaults to `true`.'
|
description: 'Whether or not to install a wrapper to wrap subsequent calls of the `tofu` binary and expose its STDOUT, STDERR, and exit code as outputs named `stdout`, `stderr`, and `exitcode` respectively. Defaults to `true`.'
|
||||||
default: 'true'
|
default: 'true'
|
||||||
|
|
|
||||||
24
dist/index.js
vendored
24
dist/index.js
vendored
|
|
@ -247,7 +247,8 @@ credentials "${credentialsHostname}" {
|
||||||
async function run () {
|
async function run () {
|
||||||
try {
|
try {
|
||||||
// Gather GitHub Actions inputs
|
// Gather GitHub Actions inputs
|
||||||
const version = core.getInput('tofu_version');
|
let version = core.getInput('tofu_version');
|
||||||
|
const versionFile = core.getInput('tofu_version_file');
|
||||||
const credentialsHostname = core.getInput('cli_config_credentials_hostname');
|
const credentialsHostname = core.getInput('cli_config_credentials_hostname');
|
||||||
const credentialsToken = core.getInput('cli_config_credentials_token');
|
const credentialsToken = core.getInput('cli_config_credentials_token');
|
||||||
const wrapper = core.getInput('tofu_wrapper') === 'true';
|
const wrapper = core.getInput('tofu_wrapper') === 'true';
|
||||||
|
|
@ -258,6 +259,27 @@ async function run () {
|
||||||
githubToken = process.env.GITHUB_TOKEN;
|
githubToken = process.env.GITHUB_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If tofu_version_file is provided, read the version from the file
|
||||||
|
if (versionFile) {
|
||||||
|
try {
|
||||||
|
core.debug(`Reading OpenTofu version from file: ${versionFile}`);
|
||||||
|
const fileVersion = await fs.readFile(versionFile, 'utf8');
|
||||||
|
const trimmedVersion = fileVersion.trim();
|
||||||
|
if (trimmedVersion) {
|
||||||
|
version = trimmedVersion;
|
||||||
|
core.debug(`Using version from file: ${version}`);
|
||||||
|
} else {
|
||||||
|
core.warning(
|
||||||
|
`Version file ${versionFile} is empty, using tofu_version input: ${version}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(
|
||||||
|
`Failed to read version from file ${versionFile}: ${error.message}. Using tofu_version input: ${version}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Gather OS details
|
// Gather OS details
|
||||||
const osPlatform = os.platform();
|
const osPlatform = os.platform();
|
||||||
const osArch = os.arch();
|
const osArch = os.arch();
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,8 @@ credentials "${credentialsHostname}" {
|
||||||
async function run () {
|
async function run () {
|
||||||
try {
|
try {
|
||||||
// Gather GitHub Actions inputs
|
// Gather GitHub Actions inputs
|
||||||
const version = core.getInput('tofu_version');
|
let version = core.getInput('tofu_version');
|
||||||
|
const versionFile = core.getInput('tofu_version_file');
|
||||||
const credentialsHostname = core.getInput('cli_config_credentials_hostname');
|
const credentialsHostname = core.getInput('cli_config_credentials_hostname');
|
||||||
const credentialsToken = core.getInput('cli_config_credentials_token');
|
const credentialsToken = core.getInput('cli_config_credentials_token');
|
||||||
const wrapper = core.getInput('tofu_wrapper') === 'true';
|
const wrapper = core.getInput('tofu_wrapper') === 'true';
|
||||||
|
|
@ -139,6 +140,27 @@ async function run () {
|
||||||
githubToken = process.env.GITHUB_TOKEN;
|
githubToken = process.env.GITHUB_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If tofu_version_file is provided, read the version from the file
|
||||||
|
if (versionFile) {
|
||||||
|
try {
|
||||||
|
core.debug(`Reading OpenTofu version from file: ${versionFile}`);
|
||||||
|
const fileVersion = await fs.readFile(versionFile, 'utf8');
|
||||||
|
const trimmedVersion = fileVersion.trim();
|
||||||
|
if (trimmedVersion) {
|
||||||
|
version = trimmedVersion;
|
||||||
|
core.debug(`Using version from file: ${version}`);
|
||||||
|
} else {
|
||||||
|
core.warning(
|
||||||
|
`Version file ${versionFile} is empty, using tofu_version input: ${version}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(
|
||||||
|
`Failed to read version from file ${versionFile}: ${error.message}. Using tofu_version input: ${version}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Gather OS details
|
// Gather OS details
|
||||||
const osPlatform = os.platform();
|
const osPlatform = os.platform();
|
||||||
const osArch = os.arch();
|
const osArch = os.arch();
|
||||||
|
|
|
||||||
176
lib/test/setup-tofu.test.js
Normal file
176
lib/test/setup-tofu.test.js
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
// Node.js core
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// External
|
||||||
|
const core = require('@actions/core');
|
||||||
|
|
||||||
|
// First party
|
||||||
|
const releases = require('../releases');
|
||||||
|
const setup = require('../setup-tofu');
|
||||||
|
|
||||||
|
// Mock dependencies
|
||||||
|
jest.mock('@actions/core');
|
||||||
|
jest.mock('@actions/io', () => ({
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn(),
|
||||||
|
mkdirP: jest.fn()
|
||||||
|
}));
|
||||||
|
jest.mock('@actions/tool-cache', () => ({
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractZip: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock releases.js so setup-tofu.js can be tested in isolation
|
||||||
|
jest.mock('../releases');
|
||||||
|
|
||||||
|
// Set up global test fixtures
|
||||||
|
const fallbackVersion = 'latest';
|
||||||
|
let tempDir;
|
||||||
|
let tempDirPath;
|
||||||
|
let version = '1.10.5';
|
||||||
|
let versionFile;
|
||||||
|
let versionFileName = '.opentofu-version';
|
||||||
|
|
||||||
|
describe('setup-tofu', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
// Mock dependencies
|
||||||
|
const tc = require('@actions/tool-cache');
|
||||||
|
tc.downloadTool.mockResolvedValue('/mock/download/path');
|
||||||
|
tc.extractZip.mockResolvedValue('/mock/extract/path');
|
||||||
|
|
||||||
|
const io = require('@actions/io');
|
||||||
|
io.mv.mockResolvedValue();
|
||||||
|
io.cp.mockResolvedValue();
|
||||||
|
io.mkdirP.mockResolvedValue();
|
||||||
|
|
||||||
|
const mockRelease = {
|
||||||
|
getBuild: jest.fn().mockReturnValue({ url: 'mock-url' })
|
||||||
|
};
|
||||||
|
releases.getRelease.mockResolvedValue(mockRelease);
|
||||||
|
|
||||||
|
// Write version file to temporary directory
|
||||||
|
tempDirPath = path.join(os.tmpdir(), 'setup-tofu-');
|
||||||
|
tempDir = await fs.mkdtemp(tempDirPath);
|
||||||
|
versionFile = path.join(tempDir, versionFileName);
|
||||||
|
await fs.writeFile(versionFile, `${version}\n`);
|
||||||
|
|
||||||
|
// Mock action inputs to return default values
|
||||||
|
core.getInput.mockImplementation((name) => {
|
||||||
|
const defaults = {
|
||||||
|
tofu_version: fallbackVersion,
|
||||||
|
tofu_version_file: versionFile,
|
||||||
|
cli_config_credentials_hostname: '',
|
||||||
|
cli_config_credentials_token: '',
|
||||||
|
tofu_wrapper: 'true',
|
||||||
|
github_token: ''
|
||||||
|
};
|
||||||
|
return defaults[name] || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock environment variables
|
||||||
|
process.env.GITHUB_TOKEN = 'mock-github-token';
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
delete process.env.GITHUB_TOKEN;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tofu_version_file functionality', () => {
|
||||||
|
it('should read version from file when tofu_version_file is provided', async () => {
|
||||||
|
jest.spyOn(fs, 'readFile');
|
||||||
|
|
||||||
|
await setup();
|
||||||
|
|
||||||
|
expect(releases.getRelease).toHaveBeenCalledWith(
|
||||||
|
version, process.env.GITHUB_TOKEN
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fs.readFile).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty version file gracefully', async () => {
|
||||||
|
jest.spyOn(fs, 'readFile');
|
||||||
|
|
||||||
|
version = ' ';
|
||||||
|
versionFileName = '.opentofu-version-empty';
|
||||||
|
versionFile = path.join(tempDir, versionFileName);
|
||||||
|
await fs.writeFile(versionFile, `${version}\n`);
|
||||||
|
|
||||||
|
core.getInput.mockImplementation((name) => {
|
||||||
|
if (name === 'tofu_version_file') {
|
||||||
|
return versionFile;
|
||||||
|
}
|
||||||
|
if (name === 'tofu_version') {
|
||||||
|
return fallbackVersion;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
await setup();
|
||||||
|
|
||||||
|
expect(releases.getRelease).toHaveBeenCalledWith(
|
||||||
|
fallbackVersion, process.env.GITHUB_TOKEN
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining(`Version file ${versionFile} is empty`)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fs.readFile).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle file read errors gracefully', async () => {
|
||||||
|
jest.spyOn(fs, 'readFile');
|
||||||
|
|
||||||
|
versionFileName = '.opentofu-version-file-does-not-exist';
|
||||||
|
versionFile = path.join(tempDir, versionFileName);
|
||||||
|
|
||||||
|
core.getInput.mockImplementation((name) => {
|
||||||
|
if (name === 'tofu_version_file') {
|
||||||
|
return versionFile;
|
||||||
|
}
|
||||||
|
if (name === 'tofu_version') {
|
||||||
|
return fallbackVersion;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
await setup();
|
||||||
|
|
||||||
|
expect(releases.getRelease).toHaveBeenCalledWith(
|
||||||
|
fallbackVersion, process.env.GITHUB_TOKEN
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining(`Failed to read version from file ${versionFile}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fs.readFile).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not read file when tofu_version_file is not provided', async () => {
|
||||||
|
jest.spyOn(fs, 'readFile');
|
||||||
|
|
||||||
|
core.getInput.mockImplementation((name) => {
|
||||||
|
if (name === 'tofu_version') {
|
||||||
|
return fallbackVersion;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
await setup();
|
||||||
|
|
||||||
|
expect(releases.getRelease).toHaveBeenCalledWith(
|
||||||
|
fallbackVersion, process.env.GITHUB_TOKEN
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fs.readFile).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue