mirror of
https://github.com/opentofu/setup-opentofu.git
synced 2025-12-05 23:34:45 +00:00
feat: ported action definition from setup-terraform
Signed-off-by: Dmitry Kisler <admin@dkisler.com>
This commit is contained in:
parent
c37e0c575a
commit
01bef202d2
19 changed files with 18728 additions and 3 deletions
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
15
.github/workflows/continuous-integration.yml
vendored
Normal file
15
.github/workflows/continuous-integration.yml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: 'Continuous Integration'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-dist:
|
||||||
|
name: Check dist/ directory
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@a8533f184b279cfc1b2dd6a96ed2f097ccf81189
|
||||||
|
test:
|
||||||
|
name: Test
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@a8533f184b279cfc1b2dd6a96ed2f097ccf81189
|
||||||
5
.github/workflows/data/local/main.tf
vendored
Normal file
5
.github/workflows/data/local/main.tf
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
resource "null_resource" "null" {
|
||||||
|
triggers = {
|
||||||
|
value = timestamp()
|
||||||
|
}
|
||||||
|
}
|
||||||
316
.github/workflows/setup-terraform.yml
vendored
Normal file
316
.github/workflows/setup-terraform.yml
vendored
Normal file
|
|
@ -0,0 +1,316 @@
|
||||||
|
name: 'Setup OpenTofu'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
# TODO: remove after manual tests
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tofu-versions:
|
||||||
|
name: 'OpenTofu Versions'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
# TODO: add move versions to test when the feature is added
|
||||||
|
tofu-versions: [latest]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu - ${{ matrix['tofu-versions'] }}
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
tofu_version: ${{ matrix['tofu-versions'] }}
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Version - ${{ matrix['tofu-versions'] }}
|
||||||
|
if: ${{ matrix['tofu-versions'] != 'latest' }}
|
||||||
|
run: tofu version | grep ${{ matrix['tofu-versions']}}
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Version - ${{ matrix['tofu-versions'] }}
|
||||||
|
if: ${{ matrix['tofu-versions'] == 'latest' }}
|
||||||
|
run: tofu version | grep 'OpenTofu v'
|
||||||
|
|
||||||
|
tofu-versions-no-wrapper:
|
||||||
|
name: 'OpenTofu Versions No Wrapper'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
# TODO: add more versions to test when the semver feature is implemented
|
||||||
|
tofu-versions: [latest]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu (no wrapper) - ${{ matrix['tofu-versions'] }}
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
tofu_version: ${{ matrix['tofu-versions'] }}
|
||||||
|
tofu_wrapper: false
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Version - ${{ matrix['tofu-versions'] }}
|
||||||
|
if: ${{ matrix['tofu-versions'] != 'latest' }}
|
||||||
|
run: tofu version | grep ${{ matrix['tofu-versions']}}
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Version - ${{ matrix['tofu-versions'] }}
|
||||||
|
if: ${{ matrix['tofu-versions'] == 'latest' }}
|
||||||
|
run: tofu version | grep 'OpenTofu v'
|
||||||
|
|
||||||
|
# TODO: uncomment when the semver feature is implemented
|
||||||
|
# tofu-versions-constraints:
|
||||||
|
# name: 'OpenTofu Versions Constraints'
|
||||||
|
# runs-on: ${{ matrix.os }}
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
# tofu-versions: [~0.12, 0.12.x, <0.13.0]
|
||||||
|
# steps:
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
#
|
||||||
|
# - name: Setup OpenTofu - ${{ matrix['tofu-versions'] }}
|
||||||
|
# uses: ./
|
||||||
|
# with:
|
||||||
|
# tofu_version: ${{ matrix['tofu-versions'] }}
|
||||||
|
#
|
||||||
|
# - name: Validate OpenTofu Version - ${{ matrix['tofu-versions'] }}
|
||||||
|
# run: tofu version | grep 'OpenTofu v0\.12'
|
||||||
|
|
||||||
|
# TODO: uncomment when the semver feature is implemented
|
||||||
|
# tofu-versions-constraints-no-wrapper:
|
||||||
|
# name: 'OpenTofu Versions Constraints No Wrapper'
|
||||||
|
# runs-on: ${{ matrix.os }}
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
# tofu-versions: [~0.12, 0.12.x, <0.13.0]
|
||||||
|
# steps:
|
||||||
|
# - name: Checkout
|
||||||
|
# uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
#
|
||||||
|
# - name: Setup OpenTofu (no wrapper) - ${{ matrix['tofu-versions'] }}
|
||||||
|
# uses: ./
|
||||||
|
# with:
|
||||||
|
# tofu_version: ${{ matrix['tofu-versions'] }}
|
||||||
|
# tofu_wrapper: false
|
||||||
|
#
|
||||||
|
# - name: Validate OpenTofu Version - ${{ matrix['tofu-versions'] }}
|
||||||
|
# run: tofu version | grep 'OpenTofu v0\.12'
|
||||||
|
|
||||||
|
tofu-credentials-cloud:
|
||||||
|
name: 'OpenTofu Cloud Credentials'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
env:
|
||||||
|
TF_CLOUD_API_TOKEN: 'XXXXXXXXXXXXXX.atlasv1.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
cli_config_credentials_token: ${{ env.TF_CLOUD_API_TOKEN }}
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Credentials (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
cat ${APPDATA}/terraform.rc | grep 'credentials "app.terraform.io"'
|
||||||
|
cat ${APPDATA}/terraform.rc | grep 'token = "${{ env.TF_CLOUD_API_TOKEN }}"'
|
||||||
|
|
||||||
|
- name: Validate Teraform Credentials (Linux & macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
|
run: |
|
||||||
|
cat ${HOME}/.terraformrc | grep 'credentials "app.terraform.io"'
|
||||||
|
cat ${HOME}/.terraformrc | grep 'token = "${{ env.TF_CLOUD_API_TOKEN }}"'
|
||||||
|
|
||||||
|
tofu-credentials-enterprise:
|
||||||
|
name: 'OpenTofu Enterprise Credentials'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
env:
|
||||||
|
TF_CLOUD_API_TOKEN: 'XXXXXXXXXXXXXX.atlasv1.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
cli_config_credentials_hostname: 'tofu.example.com'
|
||||||
|
cli_config_credentials_token: ${{ env.TF_CLOUD_API_TOKEN }}
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Credentials (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
cat ${APPDATA}/terraform.rc | grep 'credentials "tofu.example.com"'
|
||||||
|
cat ${APPDATA}/terraform.rc | grep 'token = "${{ env.TF_CLOUD_API_TOKEN }}"'
|
||||||
|
|
||||||
|
- name: Validate Teraform Credentials (Linux & macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
|
run: |
|
||||||
|
cat ${HOME}/.terraformrc | grep 'credentials "tofu.example.com"'
|
||||||
|
cat ${HOME}/.terraformrc | grep 'token = "${{ env.TF_CLOUD_API_TOKEN }}"'
|
||||||
|
|
||||||
|
tofu-credentials-none:
|
||||||
|
name: 'OpenTofu No Credentials'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
|
||||||
|
- name: Validate OpenTofu Credentials (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
[[ -f ${APPDATA}/terraform.rc ]] || exit 0
|
||||||
|
|
||||||
|
- name: Validate Teraform Credentials (Linux & macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
|
run: |
|
||||||
|
[[ -f ${HOME}/.terraformrc ]] || exit 0
|
||||||
|
|
||||||
|
tofu-arguments:
|
||||||
|
name: 'OpenTofu Arguments'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
|
||||||
|
- name: Check No Arguments
|
||||||
|
run: tofu || exit 0
|
||||||
|
|
||||||
|
- name: Check Single Argument
|
||||||
|
run: tofu help || exit 0
|
||||||
|
|
||||||
|
- name: Check Single Argument Hyphen
|
||||||
|
run: tofu -help
|
||||||
|
|
||||||
|
- name: Check Single Argument Double Hyphen
|
||||||
|
run: tofu --help
|
||||||
|
|
||||||
|
- name: Check Single Argument Subcommand
|
||||||
|
run: tofu fmt -check
|
||||||
|
|
||||||
|
- name: Check Multiple Arguments Subcommand
|
||||||
|
run: tofu fmt -check -list=true -no-color
|
||||||
|
|
||||||
|
tofu-arguments-no-wrapper:
|
||||||
|
name: 'OpenTofu Arguments No Wrapper'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
tofu_wrapper: false
|
||||||
|
|
||||||
|
- name: Check No Arguments
|
||||||
|
run: tofu || exit 0
|
||||||
|
|
||||||
|
- name: Check Single Argument
|
||||||
|
run: tofu help || exit 0
|
||||||
|
|
||||||
|
- name: Check Single Argument Hyphen
|
||||||
|
run: tofu -help
|
||||||
|
|
||||||
|
- name: Check Single Argument Double Hyphen
|
||||||
|
run: tofu --help
|
||||||
|
|
||||||
|
- name: Check Single Argument Subcommand
|
||||||
|
run: tofu fmt -check
|
||||||
|
|
||||||
|
- name: Check Multiple Arguments Subcommand
|
||||||
|
run: tofu fmt -check -list=true -no-color
|
||||||
|
|
||||||
|
tofu-run-local:
|
||||||
|
name: 'OpenTofu Run Local'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./.github/workflows/data/local
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
|
||||||
|
- name: OpenTofu Init
|
||||||
|
shell: bash
|
||||||
|
run: tofu init
|
||||||
|
|
||||||
|
- name: OpenTofu Format
|
||||||
|
shell: bash
|
||||||
|
run: tofu fmt -check
|
||||||
|
|
||||||
|
- name: OpenTofu Plan
|
||||||
|
id: plan
|
||||||
|
shell: bash
|
||||||
|
run: tofu plan
|
||||||
|
|
||||||
|
- name: Print OpenTofu Plan
|
||||||
|
shell: bash
|
||||||
|
run: echo "${{ steps.plan.outputs.stdout }}"
|
||||||
|
|
||||||
|
tofu-run-local-no-wrapper:
|
||||||
|
name: 'OpenTofu Run Local No Wrapper'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./.github/workflows/data/local
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||||
|
|
||||||
|
- name: Setup OpenTofu
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
tofu_wrapper: false
|
||||||
|
|
||||||
|
- name: OpenTofu Init
|
||||||
|
shell: bash
|
||||||
|
run: tofu init
|
||||||
|
|
||||||
|
- name: OpenTofu Format
|
||||||
|
shell: bash
|
||||||
|
run: tofu fmt -check
|
||||||
|
|
||||||
|
- name: OpenTofu Plan
|
||||||
|
id: plan
|
||||||
|
shell: bash
|
||||||
|
run: tofu plan
|
||||||
72
.gitignore
vendored
Normal file
72
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Editors
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Other Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# ./wrapper/dist gets included in top-level ./dist
|
||||||
|
wrapper/dist
|
||||||
|
|
||||||
|
# Jetbrains IDEs
|
||||||
|
.idea/
|
||||||
|
.iml
|
||||||
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
_
|
||||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npm run build && git add dist/
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
The `opentofu/setup-opentofu` action sets up OpenTofu CLI in your GitHub Actions workflow by:
|
The `opentofu/setup-opentofu` action sets up OpenTofu CLI in your GitHub Actions workflow by:
|
||||||
|
|
||||||
- [ ] Downloading the latest version of OpenTofu CLI and adding it to the `PATH`.
|
- Downloading the latest version of OpenTofu CLI and adding it to the `PATH`.
|
||||||
- [ ] Configuring the [CLI configuration file](https://www.terraform.io/docs/commands/cli-config.html) with a Terraform Cloud/Enterprise hostname and API token.
|
- Configuring the [CLI configuration file](https://www.terraform.io/docs/commands/cli-config.html) with a Terraform Cloud/Enterprise hostname and API token.
|
||||||
- [ ] Installing a wrapper script to wrap subsequent calls of the `tofu` binary and expose its STDOUT, STDERR, and exit code as outputs named `stdout`, `stderr`, and `exitcode` respectively. (This can be optionally skipped if subsequent steps in the same job do not need to access the results of
|
- Installing a wrapper script to wrap subsequent calls of the `tofu` binary and expose its STDOUT, STDERR, and exit code as outputs named `stdout`, `stderr`, and `exitcode` respectively. (This can be optionally skipped if subsequent steps in the same job do not need to access the results of
|
||||||
OpenTofu commands.)
|
OpenTofu commands.)
|
||||||
|
|
||||||
After you've used the action, subsequent steps in the same job can run arbitrary OpenTofu commands using [the GitHub Actions `run` syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun). This allows most OpenTofu commands to work exactly
|
After you've used the action, subsequent steps in the same job can run arbitrary OpenTofu commands using [the GitHub Actions `run` syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun). This allows most OpenTofu commands to work exactly
|
||||||
|
|
|
||||||
7015
dist/index.js
vendored
Normal file
7015
dist/index.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
4242
dist/index1.js
vendored
Executable file
4242
dist/index1.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
16
index.js
Normal file
16
index.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
const core = require('@actions/core');
|
||||||
|
|
||||||
|
const setup = require('./lib/setup-tofu');
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await setup();
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
|
})();
|
||||||
62
lib/releases.js
Normal file
62
lib/releases.js
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) OpenTofu
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Build {
|
||||||
|
constructor (name, url) {
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Release {
|
||||||
|
constructor (releaseMeta) {
|
||||||
|
this.version = releaseMeta.tag_name.replace('v', '');
|
||||||
|
this.builds = releaseMeta.assets.map(asset => new Build(asset.name, asset.browser_download_url));
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuild (platform, arch) {
|
||||||
|
const requiredName = `tofu_${this.version}_${platform}_${arch}.zip`;
|
||||||
|
return this.builds.find(build => build.name === requiredName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the top 30 releases sorted in desc order.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async function fetchReleases () {
|
||||||
|
const url = 'https://api.github.com/repos/opentofu/opentofu/releases';
|
||||||
|
|
||||||
|
const resp = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/vnd.github+json',
|
||||||
|
'X-GitHub-Api-Version': '2022-11-28'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resp.ok) {
|
||||||
|
throw Error('failed fetching releases');
|
||||||
|
}
|
||||||
|
|
||||||
|
const releasesMeta = await resp.json();
|
||||||
|
|
||||||
|
return releasesMeta.map(releaseMeta => new Release(releaseMeta));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the release given the version.
|
||||||
|
*
|
||||||
|
* @param {string} version: Release version.
|
||||||
|
*/
|
||||||
|
async function getRelease (version) {
|
||||||
|
const releases = await fetchReleases();
|
||||||
|
return releases.find(release => release.version === version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the export is defined as adaptor to replace hashicorp/js-releases
|
||||||
|
// See: https://github.com/hashicorp/setup-terraform/blob/e192cfcbae6c6ed207c277ed7624131996c9bf13/lib/setup-terraform.js#L15
|
||||||
|
module.exports = {
|
||||||
|
getRelease
|
||||||
|
};
|
||||||
170
lib/setup-tofu.js
Normal file
170
lib/setup-tofu.js
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* Copyright (c) OpenTofu
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Node.js core
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// External
|
||||||
|
const core = require('@actions/core');
|
||||||
|
const tc = require('@actions/tool-cache');
|
||||||
|
const io = require('@actions/io');
|
||||||
|
const releases = require('./releases');
|
||||||
|
|
||||||
|
// arch in [arm, x32, x64...] (https://nodejs.org/api/os.html#os_os_arch)
|
||||||
|
// return value in [amd64, 386, arm]
|
||||||
|
function mapArch (arch) {
|
||||||
|
const mappings = {
|
||||||
|
x32: '386',
|
||||||
|
x64: 'amd64'
|
||||||
|
};
|
||||||
|
return mappings[arch] || arch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// os in [darwin, linux, win32...] (https://nodejs.org/api/os.html#os_os_platform)
|
||||||
|
// return value in [darwin, linux, windows]
|
||||||
|
function mapOS (os) {
|
||||||
|
const mappings = {
|
||||||
|
win32: 'windows'
|
||||||
|
};
|
||||||
|
return mappings[os] || os;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadCLI (url) {
|
||||||
|
core.debug(`Downloading OpenTofu CLI from ${url}`);
|
||||||
|
const pathToCLIZip = await tc.downloadTool(url);
|
||||||
|
|
||||||
|
let pathToCLI = '';
|
||||||
|
|
||||||
|
core.debug('Extracting OpenTofu CLI zip file');
|
||||||
|
if (os.platform().startsWith('win')) {
|
||||||
|
core.debug(`OpenTofu CLI Download Path is ${pathToCLIZip}`);
|
||||||
|
const fixedPathToCLIZip = `${pathToCLIZip}.zip`;
|
||||||
|
io.mv(pathToCLIZip, fixedPathToCLIZip);
|
||||||
|
core.debug(`Moved download to ${fixedPathToCLIZip}`);
|
||||||
|
pathToCLI = await tc.extractZip(fixedPathToCLIZip);
|
||||||
|
} else {
|
||||||
|
pathToCLI = await tc.extractZip(pathToCLIZip);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(`OpenTofu CLI path is ${pathToCLI}.`);
|
||||||
|
|
||||||
|
if (!pathToCLIZip || !pathToCLI) {
|
||||||
|
throw new Error(`Unable to download OpenTofu from ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathToCLI;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function installWrapper (pathToCLI) {
|
||||||
|
let source, target;
|
||||||
|
|
||||||
|
// If we're on Windows, then the executable ends with .exe
|
||||||
|
const exeSuffix = os.platform().startsWith('win') ? '.exe' : '';
|
||||||
|
|
||||||
|
// Rename tofu(.exe) to tofu-bin(.exe)
|
||||||
|
try {
|
||||||
|
source = [pathToCLI, `tofu${exeSuffix}`].join(path.sep);
|
||||||
|
target = [pathToCLI, `tofu-bin${exeSuffix}`].join(path.sep);
|
||||||
|
core.debug(`Moving ${source} to ${target}.`);
|
||||||
|
await io.mv(source, target);
|
||||||
|
} catch (e) {
|
||||||
|
core.error(`Unable to move ${source} to ${target}.`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install our wrapper as tofu
|
||||||
|
try {
|
||||||
|
source = path.resolve([__dirname, '..', 'wrapper', 'dist', 'index.js'].join(path.sep));
|
||||||
|
target = [pathToCLI, 'tofu'].join(path.sep);
|
||||||
|
core.debug(`Copying ${source} to ${target}.`);
|
||||||
|
await io.cp(source, target);
|
||||||
|
} catch (e) {
|
||||||
|
core.error(`Unable to copy ${source} to ${target}.`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export a new environment variable, so our wrapper can locate the binary
|
||||||
|
core.exportVariable('TOFU_CLI_PATH', pathToCLI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add credentials to CLI Configuration File
|
||||||
|
// https://www.tofu.io/docs/commands/cli-config.html
|
||||||
|
async function addCredentials (credentialsHostname, credentialsToken, osPlat) {
|
||||||
|
// format HCL block
|
||||||
|
// eslint-disable
|
||||||
|
const creds = `
|
||||||
|
credentials "${credentialsHostname}" {
|
||||||
|
token = "${credentialsToken}"
|
||||||
|
}`.trim();
|
||||||
|
// eslint-enable
|
||||||
|
|
||||||
|
// default to OS-specific path
|
||||||
|
let credsFile = osPlat === 'win32'
|
||||||
|
? `${process.env.APPDATA}/terraform.rc`
|
||||||
|
: `${process.env.HOME}/.terraformrc`;
|
||||||
|
|
||||||
|
// override with TF_CLI_CONFIG_FILE environment variable
|
||||||
|
credsFile = process.env.TF_CLI_CONFIG_FILE ? process.env.TF_CLI_CONFIG_FILE : credsFile;
|
||||||
|
|
||||||
|
// get containing folder
|
||||||
|
const credsFolder = path.dirname(credsFile);
|
||||||
|
|
||||||
|
core.debug(`Creating ${credsFolder}`);
|
||||||
|
await io.mkdirP(credsFolder);
|
||||||
|
|
||||||
|
core.debug(`Adding credentials to ${credsFile}`);
|
||||||
|
await fs.writeFile(credsFile, creds);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run () {
|
||||||
|
try {
|
||||||
|
// Gather GitHub Actions inputs
|
||||||
|
const version = '1.6.0-alpha1';
|
||||||
|
// TODO: allow dynamic version selection once logic is ready
|
||||||
|
// const version = core.getInput('tofu_version');
|
||||||
|
const credentialsHostname = core.getInput('cli_config_credentials_hostname');
|
||||||
|
const credentialsToken = core.getInput('cli_config_credentials_token');
|
||||||
|
const wrapper = core.getInput('tofu_wrapper') === 'true';
|
||||||
|
|
||||||
|
// Gather OS details
|
||||||
|
const osPlatform = os.platform();
|
||||||
|
const osArch = os.arch();
|
||||||
|
|
||||||
|
core.debug(`Finding releases for OpenTofu version ${version}`);
|
||||||
|
const release = await releases.getRelease(version);
|
||||||
|
const platform = mapOS(osPlatform);
|
||||||
|
const arch = mapArch(osArch);
|
||||||
|
core.debug(`Getting build for OpenTofu version ${release.version}: ${platform} ${arch}`);
|
||||||
|
const build = release.getBuild(platform, arch);
|
||||||
|
if (!build) {
|
||||||
|
throw new Error(`OpenTofu version ${version} not available for ${platform} and ${arch}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download requested version
|
||||||
|
const pathToCLI = await downloadCLI(build.url);
|
||||||
|
|
||||||
|
// Install our wrapper
|
||||||
|
if (wrapper) {
|
||||||
|
await installWrapper(pathToCLI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to path
|
||||||
|
core.addPath(pathToCLI);
|
||||||
|
|
||||||
|
// Add credentials to file if they are provided
|
||||||
|
if (credentialsHostname && credentialsToken) {
|
||||||
|
await addCredentials(credentialsHostname, credentialsToken, osPlatform);
|
||||||
|
}
|
||||||
|
return release;
|
||||||
|
} catch (error) {
|
||||||
|
core.error(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = run;
|
||||||
6630
package-lock.json
generated
Normal file
6630
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
40
package.json
Normal file
40
package.json
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "setup-opentofu",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Setup OpenTofu CLI for GitHub Actions",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"publisher": "OpenTofu",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/opentofu/setup-opentofu.git"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "semistandard --env jest && jest --coverage",
|
||||||
|
"lint": "semistandard --env jest --fix",
|
||||||
|
"build": "ncc build wrapper/tofu.js --out wrapper/dist && ncc build index.js --out dist",
|
||||||
|
"prepare": "husky install",
|
||||||
|
"format-check": "echo \"unimplemented for actions/reusable-workflows basic-validation\""
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.10.1",
|
||||||
|
"@actions/exec": "^1.1.1",
|
||||||
|
"@actions/github": "^5.1.1",
|
||||||
|
"@actions/io": "^1.1.3",
|
||||||
|
"@actions/tool-cache": "^2.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vercel/ncc": "^0.38.0",
|
||||||
|
"husky": "^8.0.3",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"nock": "^13.3.3",
|
||||||
|
"semistandard": "^17.0.0"
|
||||||
|
},
|
||||||
|
"semistandard": {
|
||||||
|
"ignore": [
|
||||||
|
"**/dist/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
41
wrapper/lib/output-listener.js
Normal file
41
wrapper/lib/output-listener.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acts as a listener for @actions/exec, by capturing STDOUT and STDERR
|
||||||
|
* streams, and exposing them via a contents attribute.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Instantiate a new listener
|
||||||
|
* const listener = new OutputListener();
|
||||||
|
* // Register listener against STDOUT stream
|
||||||
|
* await exec.exec('ls', ['-ltr'], {
|
||||||
|
* listeners: {
|
||||||
|
* stdout: listener.listener
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* // Log out STDOUT contents
|
||||||
|
* console.log(listener.contents);
|
||||||
|
*/
|
||||||
|
class OutputListener {
|
||||||
|
constructor () {
|
||||||
|
this._buff = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get listener () {
|
||||||
|
const listen = function listen (data) {
|
||||||
|
this._buff.push(data);
|
||||||
|
};
|
||||||
|
return listen.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get contents () {
|
||||||
|
return this._buff
|
||||||
|
.map(chunk => chunk.toString())
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = OutputListener;
|
||||||
14
wrapper/lib/tofu-bin.js
Normal file
14
wrapper/lib/tofu-bin.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = (() => {
|
||||||
|
// If we're on Windows, then the executable ends with .exe
|
||||||
|
const exeSuffix = os.platform().startsWith('win') ? '.exe' : '';
|
||||||
|
|
||||||
|
return [process.env.TOFU_CLI_PATH, `tofu-bin${exeSuffix}`].join(path.sep);
|
||||||
|
})();
|
||||||
17
wrapper/test/output-listener.test.js
Normal file
17
wrapper/test/output-listener.test.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
const OutputListener = require('../lib/output-listener');
|
||||||
|
|
||||||
|
describe('output-listener', () => {
|
||||||
|
it('receives and exposes data', () => {
|
||||||
|
const listener = new OutputListener();
|
||||||
|
const listen = listener.listener;
|
||||||
|
listen(Buffer.from('foo'));
|
||||||
|
listen(Buffer.from('bar'));
|
||||||
|
listen(Buffer.from('baz'));
|
||||||
|
expect(listener.contents).toEqual('foobarbaz');
|
||||||
|
});
|
||||||
|
});
|
||||||
59
wrapper/tofu.js
Executable file
59
wrapper/tofu.js
Executable file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
const io = require('@actions/io');
|
||||||
|
const core = require('@actions/core');
|
||||||
|
const { exec } = require('@actions/exec');
|
||||||
|
|
||||||
|
const OutputListener = require('./lib/output-listener');
|
||||||
|
const pathToCLI = require('./lib/tofu-bin');
|
||||||
|
|
||||||
|
async function checkTofu () {
|
||||||
|
// Setting check to `true` will cause `which` to throw if tofu isn't found
|
||||||
|
const check = true;
|
||||||
|
return io.which(pathToCLI, check);
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// This will fail if tofu isn't found, which is what we want
|
||||||
|
await checkTofu();
|
||||||
|
|
||||||
|
// Create listeners to receive output (in memory) as well
|
||||||
|
const stdout = new OutputListener();
|
||||||
|
const stderr = new OutputListener();
|
||||||
|
const listeners = {
|
||||||
|
stdout: stdout.listener,
|
||||||
|
stderr: stderr.listener
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute tofu and capture output
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const options = {
|
||||||
|
listeners,
|
||||||
|
ignoreReturnCode: true
|
||||||
|
};
|
||||||
|
const exitCode = await exec(pathToCLI, args, options);
|
||||||
|
core.debug(`OpenTofu exited with code ${exitCode}.`);
|
||||||
|
core.debug(`stdout: ${stdout.contents}`);
|
||||||
|
core.debug(`stderr: ${stderr.contents}`);
|
||||||
|
core.debug(`exitcode: ${exitCode}`);
|
||||||
|
|
||||||
|
// Set outputs, result, exitcode, and stderr
|
||||||
|
core.setOutput('stdout', stdout.contents);
|
||||||
|
core.setOutput('stderr', stderr.contents);
|
||||||
|
core.setOutput('exitcode', exitCode.toString(10));
|
||||||
|
|
||||||
|
if (exitCode === 0 || exitCode === 2) {
|
||||||
|
// A exitCode of 0 is considered a success
|
||||||
|
// An exitCode of 2 may be returned when the '-detailed-exitcode' option
|
||||||
|
// is passed to plan. This denotes Success with non-empty
|
||||||
|
// diff (changes present).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A non-zero exitCode is considered an error
|
||||||
|
core.setFailed(`OpenTofu exited with code ${exitCode}.`);
|
||||||
|
})();
|
||||||
Loading…
Add table
Add a link
Reference in a new issue