BATS #23
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: BATS | |
on: | |
workflow_dispatch: | |
inputs: | |
owner: | |
description: Override owner (e.g. rancher-sandbox) | |
type: string | |
repo: | |
description: Override repository (e.g. rancher-desktop) | |
type: string | |
branch: | |
description: Override branch (e.g. main, or PR#) | |
type: string | |
tests: | |
description: 'Tests (in the tests/ directory, e.g. "containers")' | |
default: '*' | |
type: string | |
platforms: | |
description: Platforms to run | |
default: 'linux mac win' | |
type: string | |
engines: | |
description: Container engines to run | |
default: 'containerd moby' | |
type: string | |
package-id: | |
description: Package run ID override; leave empty to use latest. | |
default: '' | |
type: string | |
schedule: | |
- cron: '0 8 * * 1-5' # 8AM UTC weekdays as a baseline | |
permissions: | |
contents: read | |
env: | |
GH_OWNER: ${{ github.repository_owner }} | |
GH_REPOSITORY: ${{ github.repository }} | |
GH_REF_NAME: ${{ github.ref_name }} | |
jobs: | |
get-tests: | |
name: Calculate tests to run | |
runs-on: ubuntu-latest | |
steps: | |
- name: Fetch install script | |
uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
sparse-checkout-cone-mode: false | |
sparse-checkout: | | |
scripts/install-latest-ci.sh | |
.github/workflows/bats/get-tests.py | |
- id: repo | |
name: Calculate short repository | |
run: echo "repo=${GITHUB_REPOSITORY#*/}" >> "$GITHUB_OUTPUT" | |
- name: Fetch tests | |
run: | | |
: ${OWNER:=$GH_OWNER} | |
: ${REPO:=${GH_REPOSITORY#$GH_OWNER/}} | |
: ${BRANCH:=$GH_REF_NAME} | |
# If BRANCH is a number, assume it is supposed to be a PR | |
[[ $BRANCH =~ ^[0-9]+$ ]] && PR=$BRANCH | |
"scripts/install-latest-ci.sh" | |
env: | |
GH_TOKEN: ${{ github.token }} | |
OWNER: ${{ inputs.owner || github.repository_owner }} | |
REPO: ${{ inputs.repo || steps.repo.outputs.repo }} | |
BRANCH: ${{ inputs.branch || github.ref_name }} | |
ID: ${{ inputs.package-id }} | |
BATS_DIR: ${{ github.workspace }}/bats | |
SKIP_INSTALL: true | |
- name: Calculate tests | |
id: calculate | |
# This script is not inline to make local testing easier | |
run: python3 ${{ github.workspace }}/.github/workflows/bats/get-tests.py | |
env: | |
TESTS: ${{ inputs.tests }} | |
PLATFORMS: ${{ inputs.platforms }} | |
ENGINES: ${{ inputs.engines }} | |
working-directory: bats/tests | |
outputs: | |
repo: ${{ steps.repo.outputs.repo }} | |
tests: ${{ steps.calculate.outputs.tests }} | |
bats: | |
needs: get-tests | |
strategy: | |
fail-fast: false | |
matrix: | |
include: ${{ fromJSON(needs.get-tests.outputs.tests )}} | |
runs-on: ${{ matrix.host }} | |
steps: | |
- name: Fetch install script | |
uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
sparse-checkout-cone-mode: false | |
sparse-checkout: | | |
scripts/install-latest-ci.sh | |
.github/workflows/bats/sanitize-artifact-name.sh | |
- name: Install latest CI build | |
run: | | |
: ${OWNER:=$GH_OWNER} | |
: ${REPO:=${GH_REPOSITORY#$GH_OWNER/}} | |
: ${BRANCH:=$GH_REF_NAME} | |
# If BRANCH is a number, assume it is supposed to be a PR | |
[[ $BRANCH =~ ^[0-9]+$ ]] && PR=$BRANCH | |
scripts/install-latest-ci.sh | |
shell: bash | |
env: | |
GH_TOKEN: ${{ github.token }} | |
OWNER: ${{ inputs.owner || github.repository_owner }} | |
REPO: ${{ inputs.repo || needs.get-tests.outputs.repo }} | |
BRANCH: ${{ inputs.branch || github.ref_name }} | |
ID: ${{ inputs.package-id }} | |
BATS_DIR: ${{ github.workspace }}/bats | |
ZIP_NAME: ${{ github.workspace }}/version.txt | |
- name: "Linux: Enable KVM access" | |
if: runner.os == 'Linux' | |
run: sudo chmod a+rwx /dev/kvm | |
- name: "Windows: Stop unwanted services" | |
if: runner.os == 'Windows' | |
run: >- | |
Get-Service -ErrorAction Continue -Name | |
@('W3SVC', 'docker') | |
| Stop-Service | |
- name: "Windows: Update any pre-installed WSL" | |
if: runner.os == 'Windows' | |
run: wsl --update | |
continue-on-error: true | |
- name: "Windows: Install WSL2 Distribution" | |
if: runner.os == 'Windows' | |
run: | | |
# Set the default WSL version (we don't work with WSL1) | |
wsl --set-default-version 2 | |
# Install Debian, but do not launch it | |
wsl --install Debian --no-launch | |
# Initialize Debian, without going through any first-time setup | |
debian.exe install --root | |
shell: pwsh | |
- name: "Windows: Install prerequisites in WSL" | |
if: runner.os == 'Windows' | |
run: >- | |
wsl.exe -d Debian --exec /usr/bin/env DEBIAN_FRONTEND=noninteractive | |
sh -c 'apt-get update && apt-get install --yes curl' | |
- name: "Linux: Initialize pass" | |
if: runner.os == 'Linux' | |
run: | | |
# Configure the agent to allow default passwords | |
HOMEDIR="$(gpgconf --list-dirs homedir)" # spellcheck-ignore-line | |
mkdir -p "${HOMEDIR}" | |
chmod 0700 "${HOMEDIR}" | |
echo "allow-preset-passphrase" >> "${HOMEDIR}/gpg-agent.conf" | |
# Create a GPG key | |
gpg --quick-generate-key --yes --batch --passphrase '' \ | |
[email protected] default \ | |
default never | |
# Get info about the newly created key | |
DATA="$(gpg --batch --with-colons --with-keygrip --list-secret-keys)" | |
FINGERPRINT="$(awk -F: '/^fpr:/ { print $10 ; exit }' <<< "${DATA}")" # spellcheck-ignore-line | |
GRIP="$(awk -F: '/^grp:/ { print $10 ; exit }' <<< "${DATA}")" | |
# Save the password | |
gpg-connect-agent --verbose "PRESET_PASSPHRASE ${GRIP} -1 00" /bye | |
# Initialize pass | |
pass init "${FINGERPRINT}" | |
- name: Set log directory | |
shell: bash | |
run: | | |
echo "LOGS_DIR=$(pwd)/logs" >> "$GITHUB_ENV" | |
mkdir logs | |
- name: "Windows: Override log directory" | |
if: runner.os == 'Windows' | |
shell: powershell | |
run: >- | |
wsl.exe -d Debian -- echo 'LOGS_DIR=$(pwd)' | |
| Out-File -Encoding ASCII -Append "$ENV:GITHUB_ENV" | |
working-directory: logs | |
- name: Normalize test name | |
id: normalize | |
shell: bash | |
run: | | |
t="${{ matrix.name }}" | |
if [[ ! -r "tests/$t" ]] && [[ -r "tests/${t}.bats" ]]; then | |
t="${t}.bats" | |
fi | |
echo "test=$t" >> "$GITHUB_OUTPUT" | |
working-directory: bats | |
- name: "macOS: Set startup command" | |
if: runner.os == 'macOS' | |
run: echo "BATS_COMMAND=$BATS_COMMAND" >> "$GITHUB_ENV" | |
env: | |
BATS_COMMAND: exec | |
- name: "Linux: Set startup command" | |
if: runner.os == 'Linux' | |
run: echo "BATS_COMMAND=$BATS_COMMAND" >> "$GITHUB_ENV" | |
env: | |
BATS_COMMAND: >- | |
exec xvfb-run --auto-servernum | |
--server-args='-screen 0 1280x960x24' | |
- name: "Windows: Set startup command" | |
if: runner.os == 'Windows' | |
shell: bash | |
run: echo "BATS_COMMAND=$BATS_COMMAND" >> "$GITHUB_ENV" | |
env: | |
BATS_COMMAND: wsl.exe -d Debian --exec | |
- name: Run BATS | |
# We use ${{ env.BATS_COMMAND }} instead of ${BATS_COMMAND} to let the | |
# shell parse the command, instead of doing it via expansion which is then | |
# parsed differently (--server-args isn't kept as one word). Also, we | |
# need to use the env.* form because PowerShell uses ${ENV:VAR} instead. | |
run: >- | |
${{ env.BATS_COMMAND }} | |
./bats-core/bin/bats | |
--gather-test-outputs-in '${{ env.LOGS_DIR }}' | |
--print-output-on-failure | |
--filter-tags '!ci-skip' | |
--formatter cat | |
--report-formatter tap | |
'tests/${{ steps.normalize.outputs.test }}' | |
env: | |
BATS_COMMAND: ${{ env.BATS_COMMAND }} | |
GITHUB_TOKEN: ${{ github.token }} | |
LOGS_DIR: ${{ env.LOGS_DIR }} | |
RD_CAPTURE_LOGS: "true" | |
RD_CONTAINER_ENGINE: ${{ matrix.engine }} | |
RD_TAKE_SCREENSHOTS: "true" | |
RD_TRACE: "true" | |
RD_USE_GHCR_IMAGES: "true" | |
RD_USE_RAMDISK: "true" | |
RD_USE_WINDOWS_EXE: "${{ runner.os == 'Windows' }}" | |
WSLENV: "\ | |
GITHUB_TOKEN:\ | |
RD_CAPTURE_LOGS:\ | |
RD_CONTAINER_ENGINE:\ | |
RD_TAKE_SCREENSHOTS:\ | |
RD_TRACE:\ | |
RD_USE_GHCR_IMAGES:\ | |
RD_USE_RAMDISK:\ | |
RD_USE_WINDOWS_EXE:\ | |
" | |
working-directory: bats | |
timeout-minutes: 120 | |
- name: Calculate log name | |
id: log_name | |
if: ${{ !cancelled() }} | |
run: | | |
name="$(.github/workflows/bats/sanitize-artifact-name.sh <<< "$name")" | |
# For the artifact name, backslash and forward slash are also invalid. | |
name=${name//\\/%3C} | |
name=${name//\//%2F} | |
echo "name=$name" >>"$GITHUB_OUTPUT" | |
shell: bash | |
env: | |
name: ${{ matrix.host }}-${{ matrix.engine }}-${{ matrix.name }}.logs | |
- name: Consolidate logs | |
if: ${{ !cancelled() }} | |
run: | | |
# bats/logs may not exist if the workflow is being tested with e.g. tests/helpers/utils.bats | |
if [ -d "bats/logs" ]; then | |
cp -R "bats/logs/" logs | |
fi | |
cp "bats/report.tap" logs | |
.github/workflows/bats/sanitize-artifact-name.sh logs | |
echo "$NAME" > logs/name.txt | |
echo "$OS" > logs/os.txt | |
echo "$ENGINE" > logs/engine.txt | |
echo "$LOG_NAME" > logs/log-name.txt | |
mv version.txt logs/ | |
shell: bash | |
env: | |
NAME: ${{ matrix.name }} | |
OS: ${{ matrix.host }} | |
ENGINE: ${{ matrix.engine }} | |
LOG_NAME: ${{ steps.log_name.outputs.name }} | |
- name: Upload logs | |
if: ${{ !cancelled() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ steps.log_name.outputs.name }} | |
path: logs/ | |
if-no-files-found: error | |
summarize: | |
name: Summarize output | |
needs: bats | |
if: ${{ !cancelled() }} | |
runs-on: ubuntu-latest | |
steps: | |
- name: Fetch summarizer script | |
uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
sparse-checkout-cone-mode: false | |
sparse-checkout: | | |
package.json | |
.github/workflows/bats/summarize.mjs | |
- uses: actions/setup-node@v4 | |
with: | |
node-version-file: package.json | |
- uses: actions/download-artifact@v4 | |
with: | |
pattern: "*.logs" | |
- run: node .github/workflows/bats/summarize.mjs |