Skip to content

chore: add preflight job with fast failure #9203

chore: add preflight job with fast failure

chore: add preflight job with fast failure #9203

Workflow file for this run

# cspell:ignore oidc
name: ci
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the main branch
on:
merge_group:
branches: ["main", "devel/*"]
push:
branches: ["main", "devel/*"]
tags:
- "v*.*"
pull_request:
# 'closed' is missing to avoid double triggering on PR merge
# 'edited' is missing to allow us to edit PR title/description without triggering
types: [synchronize, opened, reopened]
branches: ["main", "devel/*"]
schedule:
- cron: "0 0,12 * * *"
workflow_dispatch:
inputs:
publish:
description: "Publish a pre-release"
required: false
default: "false"
concurrency:
group: ${{ github.workflow }}-${{ github.event.ref }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
FORCE_COLOR: "1" # make mocha output colorful
MISE_ENV: ci # removes NODE_OPTIONS to avoid GHA errors about it
PRETTIER_LEGACY_CLI: "1" # https://github.com/prettier/prettier/issues/15832
# https://docs.github.com/en/actions/learn-github-actions/environment-variables
# https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/
WSLENV: HOSTNAME:CI:FORCE_COLOR:GITHUB_ACTION:GITHUB_ACTION_PATH/p:GITHUB_ACTION_REPOSITORY:GITHUB_WORKFLOW:GITHUB_WORKSPACE/p:GITHUB_PATH/p:GITHUB_ENV/p:VIRTUAL_ENV/p:SKIP_PODMAN:SKIP_DOCKER:NODE_OPTIONS:MISE_ENV
# We define a hostname because otherwise the variable might not always be accessible on runners.
HOSTNAME: gha
# NODE_OPTIONS must be kept in sync with one inside .env file
NODE_OPTIONS: --max-old-space-size=8192
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
preflight:
runs-on: ubuntu-24.04
container:
image: ghcr.io/jdx/mise:latest
env:
MISE_TRUSTED_CONFIG_PATHS: /
continue-on-error: false
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0 # we need tags for dynamic versioning
show-progress: false
# - name: Run setup steps (composite action)
# uses: ./.github/actions/setup
- name: task setup
run: |
set -ex
pwd
mise install
mise list
mise cfg
mise exec -- which python3
mise exec -- printenv NODE_OPTIONS || true
task setup
- name: task lint
run: |
task lint
build:
name: ${{ matrix.name }}
needs: preflight
environment: ci
env:
SKIP_DOCKER: ${{ matrix.env.SKIP_DOCKER || 0 }}
SKIP_PODMAN: ${{ matrix.env.SKIP_PODMAN || 0 }}
IS_WSL: ${{ contains(matrix.name, 'wsl') && 1 || 0 }}
TASKFILE_ARGS: --output=group --output-group-begin='::group::{{.TASK}}' --output-group-end='::endgroup::'
defaults:
run:
shell: ${{ matrix.shell || 'bash'}}
# The type of runner that the job will run on
runs-on: ${{ matrix.os || 'ubuntu-24.04' }}
outputs:
can_release_to_npm: ${{ steps.package.outputs.can_release_to_npm }}
permissions:
contents: read
id-token: write # codecov actions
checks: read # codecov actions
strategy:
fail-fast: false
matrix:
# Avoid letting github do the matrix multiplication and use manual
# includes for each job, this gives us fine control over job name.
# Order is important, keep it alphabetical: docs, lint, test*
continue-on-error:
- false
os:
- ubuntu-24.04
task-name:
- docs
name:
- docs
include:
- name: lint
task-name: lint
os: ubuntu-24.04
env:
SKIP_PODMAN: 1
SKIP_DOCKER: 1
- name: test (linux)
task-name: test
- name: test (macos)
task-name: test
os: macos-13-large
env:
SKIP_PODMAN: 1
SKIP_DOCKER: 1
# only until we fix some broken tests, as we need it to pass
# in order to enable the caching
continue-on-error: true
- name: test (wsl)
task-name: test
os: wsl-runner
runs-on: self-hosted
env:
SKIP_PODMAN: 1
SKIP_DOCKER: 1
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0 # we need tags for dynamic versioning
show-progress: false
- name: Run setup steps (composite action)
uses: ./.github/actions/setup
with:
job_name: ${{ matrix.name }}
# https://github.com/marketplace/actions/setup-wsl
- name: Activate WSL
if: contains(matrix.name, 'wsl') && (matrix.runs-on || '') != 'self-hosted'
uses: Vampire/setup-wsl@6a8db447be7ed35f2f499c02c6e60ff77ef11278 # v6.0.0
with:
distribution: Ubuntu-24.04
set-as-default: "true"
# '-i' seems to be the only option that loads .bashrc file that we need
# https://github.com/Vampire/setup-wsl/discussions/54
wsl-shell-command: "bash -i -eo pipefail"
# https://github.com/MicrosoftDocs/WSL/blob/main/WSL/wsl-config.md#L159
wsl-conf: |
[automount]
enabled = true
root = /
options = "metadata,umask=077"
[boot]
command=/etc/init.d/dbus start
[interop]
enabled = false
appendWindowsPath = false
[network]
hostname = wsl
additional-packages: curl
dbus
dirmngr
gawk
gcc
git
gpg
gpg-agent
jq
make
python3-dev
python3-full
python3-venv
qemu-user-static
tar
unzip
xvfb
# asdf nodejs plugin requires: dirmngr gpg curl gawk
- name: Enable caching for vscode resources
if: "${{ contains(matrix.name, 'test') }}"
# left out due to having their own caching: yarn, node_modules, mise
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
.vscode-test
out/ext
out/test-resources*/chromedriver*
out/test-resources*/driverVersion
out/test-resources*/stable.zip
out/test-resources*/manifest.json
key: ${{ env.IMAGE_OS_VERSION }}-${{ matrix.task-name }}-${{ hashFiles('package.json', 'yarn.lock') }}
# - name: Enable caching for podman-machine
# if: "contains(matrix.os, 'macos')"
# uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.0.0
# with:
# path: |
# ~/.local/share/containers
# ~/.config/containers
# key: ${{ runner.os }}-${{ matrix.task-name }}-${{ hashFiles('package.json', 'yarn.lock', '.config/requirements.txt', '**/Taskfile.yml', 'tools/*.*') }}
- name: Setup task
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0
with:
version: 3.37.2
- name: Setup python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: "3.13"
- name: Install ansible-dev-tools (pip for Linux/macOS, pipx for WSL)
run: |
if [[ "${{ matrix.name }}" == *"wsl"* ]]; then
pipx install ansible-dev-tools
else
pip install ansible-dev-tools
fi
- name: Ensure .env file is automatically loaded (mise)
run: |
set -ex
mise doctor || true
test "${VIRTUAL_ENV:-}" = "${HOME}/.local/share/virtualenvs/vsa" || {
echo "VIRTUAL_ENV mismatch"
exit 99
}
test "$(mise exec -- which python3)" = "${HOME}/.local/share/virtualenvs/vsa/bin/python3" || {
echo "::warning::python3 mismatch"
# exit 98
}
# Ensure NODE_OPTIONS config on CI is identical with the one in .env
[[ "${NODE_OPTIONS:-}" == "$(mise exec -- printenv NODE_OPTIONS)" ]] || { echo "NODE_OPTIONS mismatch between .env and ci.yaml"; exit 97; }
- name: Install dependencies
uses: coactions/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # fix/install
# backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: task setup
# starting podman machine can randomly get stuck on macos
timeout-minutes: 25
run: task setup
id: setup
## uncomment to debug on GHA runner
# - name: Setup tmate session
# uses: mxschmitt/action-tmate@v3
- name: task package
id: package
run: |
task package ${{ matrix.env.TASKFILE_ARGS }}
- name: configure podman
if: ${{ matrix.name == 'test (linux)' }}
run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
mkdir -p ~/.config/containers
cat <<EOT >> ~/.config/containers/containers.conf
[engine]
cgroup_manager="cgroupfs"
EOT
podman info
- name: task ${{ matrix.task-name }}
if: "${{ !contains(matrix.name, 'test') }}"
run: task ${{ matrix.task-name }} ${{ matrix.env.TASKFILE_ARGS }}
- name: task mcp
if: contains(matrix.name, 'test')
run: task mcp:test ${{ matrix.env.TASKFILE_ARGS }}
- name: task unit
if: contains(matrix.name, 'test')
run: task unit ${{ matrix.env.TASKFILE_ARGS }}
- name: task ui
# https://github.com/ansible/vscode-ansible/issues/1451
if: ${{ !cancelled() && contains(matrix.name, 'test') && !contains(matrix.name, 'wsl') }}
run: task ui ${{ matrix.env.TASKFILE_ARGS }}
- name: task e2e
# https://github.com/ansible/vscode-ansible/issues/1451
if: ${{ !cancelled() && contains(matrix.name, 'test') }}
run: task e2e ${{ matrix.env.TASKFILE_ARGS }}
- name: task als
# https://github.com/ansible/vscode-ansible/issues/1451
if: ${{ !cancelled() && contains(matrix.name, 'test') }}
run: task als ${{ matrix.env.TASKFILE_ARGS }}
- name: Upload vsix artifact
if: ${{ matrix.name == 'test (linux)' }}
uses: coactions/upload-artifact@ec1957e16e4ecd304d3a115907ccb4ba5f636e9d # v4.0.22
with:
# Do not use github.ref_name as it contains slashes and we cannot sanitize it
name: ansible-extension-build-${{ github.event.number || github.run_id }}.zip
path: ansible-*.vsix
if-no-files-found: error
retention-days: 90
- name: Upload ansible-language-server npm package
if: ${{ matrix.name == 'test (linux)' }}
uses: coactions/upload-artifact@ec1957e16e4ecd304d3a115907ccb4ba5f636e9d # v4.0.22
with:
# Do not use github.ref_name as it contains slashes and we cannot sanitize it
name: "@ansible-ansible-language-server-build-${{ github.event.number || github.run_id }}.tgz"
path: packages/ansible-language-server/*.tgz
if-no-files-found: error
retention-days: 90
- name: Remove invalid files
if: ${{ always() }}
run: |
find out -name '*\?*' -exec rm -r {} \; || true
find out -name '*"*' -exec rm -r {} \; || true
find out -name '*:*' -exec rm -r {} \; || true
- name: Upload test logs and reports as logs-${{ steps.setup.outputs.OS_VERSION }}-${{ matrix.task-name }}.zip
if: ${{ !cancelled() }}
uses: coactions/upload-artifact@ec1957e16e4ecd304d3a115907ccb4ba5f636e9d # v4.0.22
with:
name: logs-${{ steps.setup.outputs.OS_VERSION }}-${{ matrix.task-name }}.zip
path: |
out/coverage
out/junit
out/log
out/test-resources/screenshots
# Not secure to collect due to 'token' being logged by vscode trace logs.
# out/userdata/logs
# out/test-resources/settings/logs
if-no-files-found: ignore
retention-days: 90
# until the WSL/gitleaks failure is resolved
continue-on-error: true
- name: Upload test results to Codecov (als)
if: ${{ !cancelled() && hashFiles('out/junit/als/*.xml') != '' }}
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
fail_ci_if_error: true
directory: out/junit/als
handle_no_reports_found: true
name: ${{ matrix.name }}
# unable to use wildcards yet due to https://github.com/codecov/test-results-action/issues/110
flags: ${{ steps.setup.outputs.OS_VERSION }},${{ steps.setup.outputs.ARCH }},als
use_oidc: ${{ github.event_name == 'merge_group' || github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
- name: Upload test results to Codecov (mcp)
if: ${{ !cancelled() && hashFiles('out/junit/mcp/*.xml') != '' }}
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
fail_ci_if_error: true
directory: out/junit/mcp
handle_no_reports_found: true
name: ${{ matrix.name }}
# unable to use wildcards yet due to https://github.com/codecov/test-results-action/issues/110
flags: ${{ steps.setup.outputs.OS_VERSION }},${{ steps.setup.outputs.ARCH }},mcp
use_oidc: ${{ github.event_name == 'merge_group' || github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
- name: Upload test results to Codecov (e2e)
if: ${{ !cancelled() && hashFiles('out/junit/e2e/*.xml') != '' }}
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
fail_ci_if_error: true
directory: out/junit/e2e
handle_no_reports_found: true
name: ${{ matrix.name }}
# unable to use wildcards yet due to https://github.com/codecov/test-results-action/issues/110
flags: ${{ steps.setup.outputs.OS_VERSION }},${{ steps.setup.outputs.ARCH }},e2e
use_oidc: ${{ github.event_name == 'merge_group' || github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
- name: Upload test results to Codecov (unit)
if: ${{ !cancelled() && hashFiles('out/junit/unit/*.xml') != '' }}
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
fail_ci_if_error: true
directory: out/junit/unit
handle_no_reports_found: true
name: ${{ matrix.name }}
# unable to use wildcards yet due to https://github.com/codecov/test-results-action/issues/110
flags: ${{ steps.setup.outputs.OS_VERSION }},${{ steps.setup.outputs.ARCH }},,unit
use_oidc: ${{ github.event_name == 'merge_group' || github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
- name: Upload test results to Codecov (ui)
if: ${{ !cancelled() && hashFiles('out/junit/ui/*.xml') != '' }}
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
with:
fail_ci_if_error: true
directory: out/junit/ui
handle_no_reports_found: true
name: ${{ matrix.name }}
# unable to use wildcards yet due to https://github.com/codecov/test-results-action/issues/110
flags: ${{ steps.setup.outputs.OS_VERSION }},${{ steps.setup.outputs.ARCH }},,ui
use_oidc: ${{ github.event_name == 'merge_group' || github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) }}
# - name: Stop services
# if: "contains(matrix.os, 'macos')"
# # Stopping podman machine is needed or caching it will fail
# run: |
# command -v podman && {
# podman machine stop
# while [[ "$(podman machine ls --format '{{.Running}}' \
# --noheading || true)" != "false" ]]; do
# sleep 1
# echo -n .
# done
# echo .
# }
# continue-on-error: true
## commented out for future use to debug on the GHA node if required
# - name: Setup tmate session
# if: ${{ always() }}
# uses: mxschmitt/action-tmate@v3
check: # This job does nothing and is only used for the branch protection
needs:
- build
permissions:
checks: read # codecov
contents: write # slack report
id-token: write # codecov
pull-requests: read # slack report
runs-on: ubuntu-24.04
steps:
- name: Checkout Source # needed by codecov uploader
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
- name: Merge logs into a single archive
if: ${{ !failure() }}
uses: actions/upload-artifact/merge@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: logs.zip
pattern: logs-*.zip
separate-directories: true
delete-merged: true
- name: Download artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: .
- name: Remove invalid files
if: ${{ always() }}
run: |
find . -name '*\?*' -exec rm -r {} \; || true
find . -name '*"*' -exec rm -r {} \; || true
find . -name '*:*' -exec rm -r {} \; || true
- name: Upload als test coverage data [1/5]
if: ${{ always() }}
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
name: als
files: ./**/coverage/als/cobertura-coverage.xml
flags: als
disable_search: true
fail_ci_if_error: true
use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }}
- name: Upload unit test coverage data [2/5]
if: ${{ always() }}
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
name: unit
files: ./**/coverage/unit/*cobertura-coverage.xml
flags: unit
disable_search: true
fail_ci_if_error: true
use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }}
- name: Upload ui test coverage data [3/5]
if: ${{ always() }}
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
name: ui
files: ./**/coverage/ui/*cobertura-coverage.xml
flags: ui
disable_search: true
fail_ci_if_error: true
use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }}
- name: Upload e2e test coverage data [4/5]
if: ${{ always() }}
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
name: e2e
files: ./**/coverage/e2e/*cobertura-coverage.xml
flags: e2e
disable_search: true
fail_ci_if_error: true
use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }}
- name: Upload mcp test coverage data [5/5]
if: ${{ always() }}
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
name: mcp
files: ./**/coverage/mcp/*cobertura-coverage.xml
flags: mcp
disable_search: true
fail_ci_if_error: true
use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }}
- name: SonarCloud scan
# Run only for pull requests or push to main
if: >
${{ !cancelled() &&
hashFiles('**/coverage.xml') != '' &&
(github.event_name == 'pull_request' ||
(github.event_name == 'push' && github.ref_name =='main')
)}}
uses: SonarSource/sonarqube-scan-action@v6
env:
SONAR_TOKEN: ${{ secrets.CICD_ORG_SONAR_TOKEN_CICD_BOT || secrets.AAP_ORG_SONAR_TOKEN_ANSIBLE_CICD_BOT }}
with:
args: ${{ env.SONAR_ARGS }}
# Temporarily ignore errors if the pull request is from a fork due to lack of upload secrets access
# See https://issues.redhat.com/browse/AAP-52660
continue-on-error: ${{ github.event_name == 'pull_request' && github.repository != github.event.pull_request.head.repo.full_name }}
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1 # that is a branch, not a tag
id: alls-green
with:
jobs: ${{ toJSON(needs) }}
- name: Send CI failure notification
if: failure() && github.ref == 'refs/heads/main'
env:
SLACK_WEBHOOK_URL: ${{ secrets.DEVTOOLS_CI_SLACK_URL }}
run: |
if [ -n "$SLACK_WEBHOOK_URL" ]; then
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\" Python tests failed in jobs: test for '${GITHUB_REPOSITORY}' (tox.yml). Check logs: '${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}'\"}" \
$SLACK_WEBHOOK_URL
fi
publish:
if: github.ref_type == 'tag' || github.event.inputs.publish == 'true'
runs-on: ubuntu-latest
environment: release
needs:
- check
steps:
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
- name: node post install
run: |
corepack enable
npm config set fund false
- uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
- name: Download the artifact
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: ansible-extension-build-${{ github.event.number || github.run_id }}.zip
- name: Attach vsix to Github release
# cspell: ignore softprops
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
if: github.ref_type == 'tag'
with:
files: "*.vsix"
- run: |
npm exec -- yarn install --immutable
ls -la *.vsix
- name: Setup python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: "3.13"
- name: Publish extension to marketplaces
run: |
./tools/helper --publish
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
publish-npm:
environment: release
if: needs.build.outputs.can_release_to_npm == 'true' && (github.ref_type == 'tag' || github.event.inputs.publish == 'true')
runs-on: ubuntu-latest
needs:
- build
- check
steps:
- name: Download the artifact
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: "@ansible-ansible-language-server-build-${{ github.event.number || github.run_id }}.tgz"
- uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
- run: npm publish --access public @ansible-ansible-language-server-*.tgz
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}