Skip to content

Specify shell in all GitHub Actions workflows when needed #19716

Specify shell in all GitHub Actions workflows when needed

Specify shell in all GitHub Actions workflows when needed #19716

Workflow file for this run

name: ci
on:
workflow_dispatch:
merge_group:
pull_request:
push:
branches:
- master
- releases/*
# We set `concurrency` to prevent having this workflow being run on code that is not up-to-date on a PR (a user make multiple push in a quick manner).
# But on the main branch, we don't want that behavior.
# Having the workflow run on each merge commit is something we would like, that could help us where a regression was made and missed by previous checks.
#
# For that we use `head_ref` that is only defined on `pull-request` and fallback to `run_id` (this is a counter, so it's value is unique between workflow call).
concurrency:
group: ci-${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
packages: read
jobs:
dispatch:
runs-on: ubuntu-22.04
outputs:
rust: ${{ steps.need-check.outputs.rust }}
python: ${{ steps.need-check.outputs.python }}
python-style-only: ${{ steps.need-check.outputs.python_style_only }}
rust-test-wasm: ${{ steps.need-check.outputs.rust_test_wasm }}
rust-run-cargo-deny: ${{ steps.need-check.outputs.run_cargo_deny }}
web: ${{ steps.need-check.outputs.web }}
docs: ${{ steps.need-check.outputs.docs }}
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # pin v4.2.0
timeout-minutes: 5
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin v3.0.2
id: changes
with:
list-files: shell
filters: .github/filters/ci.yml
timeout-minutes: 5
- name: Determine which workflows need to be run
id: need-check
shell: bash -eux -o pipefail {0}
run: |
env | grep NEED_CHECK_
for check in $(env | grep -o 'NEED_CHECK_[a-z_]\+' ); do
env_value=$(eval "echo \${$check}")
echo "${check#NEED_CHECK_}=$env_value"
done | tee -a $GITHUB_OUTPUT
env:
NEED_CHECK_rust: ${{ github.ref == 'refs/heads/master' || steps.changes.outputs.rust-jobs == 'true' }}
NEED_CHECK_web: ${{ github.ref == 'refs/heads/master' || steps.changes.outputs.web-jobs == 'true' }}
NEED_CHECK_python: ${{ github.ref == 'refs/heads/master' || steps.changes.outputs.python-jobs == 'true' }}
# Basically we want to only check the python code style when we modify a file that ins't related to the parsec server (e.g.: bindings/generator/generate.py)
# Thus don't require to run the python test.
NEED_CHECK_python_style_only: ${{ steps.changes.outputs.python-jobs == 'false' && steps.changes.outputs.any-python-files == 'true' }}
NEED_CHECK_rust_test_wasm: ${{ steps.changes.outputs.rust-test-wasm == 'true' }}
NEED_CHECK_run_cargo_deny: ${{ steps.changes.outputs.rust-cargo-deny == 'true' }}
NEED_CHECK_docs: ${{ steps.changes.outputs.docs-jobs == 'true' }}
# Github PR merging is configured to only require this job to pass
ci-is-happy:
name: ⭐ CI is happy ⭐
needs:
- dispatch
- quality-assurance
- python
- rust
- web
- spelling
- docs
runs-on: ubuntu-latest
if: always()
# Just a fail-safe timeout, see the fine grain per-task timeout instead
timeout-minutes: 2
steps:
- name: Requirement fulfilled
run: |
#!python3
import os
import json
NOT_A_JOB = ('python-style-only', 'rust-test-wasm')
needs = json.loads(os.environ["NEEDS"])
print("NEEDS:", json.dumps(needs, indent=4), sep="\n")
# Check jobs aren't failed or cancelled.
for name, job in needs.items():
if job["result"] in ("cancelled", "failure"):
raise ValueError(f"The job `{name}` is in a invalid state: {job['result']}")
# Check required jobs, here we just need to check that the state is `success` and not `skipped`.
for job_name, required in needs["dispatch"]["outputs"].items():
if job_name in NOT_A_JOB:
continue
if job_name not in needs:
print(f"Job: `{job_name}` isn't required!")
continue
assert required in ("true", "false")
job_result = needs[job_name]["result"]
if required == "true" and job_result != "success":
raise ValueError(
f"The required job `{job_name}` should be in a success state but is not (state={job_result})"
)
env:
NEEDS: ${{ toJSON(needs) }}
shell: python
spelling:
uses: ./.github/workflows/cspell.yml
##############################################################################
# 📊 Q&A #
##############################################################################
quality-assurance:
name: 📊 Q&A
# All linux jobs must run the same ubuntu version to avoid Rust caching issues !
runs-on: ubuntu-22.04
# Just a fail-safe timeout, see the fine grain per-task timeout instead
timeout-minutes: 10
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # pin v4.2.0
timeout-minutes: 5
- name: Ensure the PR head ref is not a perennial branch
if: github.event_name == 'pull_request'
run: |
if [[ "${{ github.head_ref }}" =~ ^releases/.*$ ]]; then
(
echo 'You are using a perennial branch as a PR head ref, this is forbidden because once merged the branch will be deleted by GitHub.';
echo 'If you want to create a PR with a change from a perennial branch, create a feature branch from it (git switch --create my-feature-branch $PERENNIAL_BRANCH)' and use the feature branch as the PR head ref;
echo;
echo 'If you want to acknowledge a release use `python misc/releaser.py acknowledge $VERSION`';
) >&2
exit 0
fi
timeout-minutes: 1
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin v3.0.2
id: newsfragment-have-changed
with:
filters: |
newsfragments:
- newsfragments/**
- name: Install python
uses: actions/setup-python@19dfb7b659fa9e60c2f89c33335ab5f6f1792b6e # pin v5.2.0
id: setup-python
with:
python-version: 3.12
- name: Check tools version
run: |
echo "::add-matcher::.github/custom-problem-matchers/version-updater.json"
python --version
python misc/version_updater.py --check
echo "::remove-matcher owner=version-updater::"
timeout-minutes: 2
- name: Check Commit Signature
run: python .github/scripts/check_commit_signature.py
- name: Check News fragments
if: |
github.event_name == 'pull_request'
&& !(
startsWith(github.head_ref, 'releases/')
|| startsWith(github.head_ref, 'revert')
|| startsWith(github.head_ref, 'acknowledge')
)
&& steps.newsfragment-have-changed.outputs.newsfragments == 'true'
run: |
python .github/scripts/check_newsfragments.py
timeout-minutes: 5
- name: Patch pre-commit for line-ending
id: patched-pre-commit-config
run: |
TEMP_FILE=$(mktemp)
sed '/id: mixed-line-ending/a\ args: [ --fix=lf ]' .pre-commit-config.yaml > $TEMP_FILE
diff --unified .pre-commit-config.yaml $TEMP_FILE || true
echo "path=$TEMP_FILE" >> $GITHUB_OUTPUT
- uses: taiki-e/install-action@b1acf153d459cd533e9e0d25a07042be1dd2ed71 # pin v2.44.25
with:
tool: [email protected]
# Why some checks are skipped:
# - `clippy`: Clippy basically compile the project, hence it's faster to run
# it in the `test-rust-matrix` job where compilation cache is reused !
# - `fmt`: Requires Rust toolchain, so run instead in `test-rust-matrix`.
# - `cspell`: Run in `spelling`.
# - `ruff`/`black`/`pyright`/`deptry`: Requires to have the Python server
# project installed, so run instead in `test-python-server`.
# - `sqlfluff`: Same as above, but on top of that it checks SQL code in both
# server (PostgreSQL) and libparsec (Sqlite).
# So we run the server checks in `test-python-server`, and install a standalone
# sqlfluff in `test-rust-linux` to do the libparsec checks.
# - `eslint`: Requires to have the Javascript GUI project installed, so run
# instead in `test-web-app`.
- uses: ./.github/actions/use-pre-commit
with:
config-file: ${{ steps.patched-pre-commit-config.outputs.path }}
env:
SKIP: clippy,fmt,cspell,ruff,black,pyright,eslint,deptry,sqlfluff
timeout-minutes: 5
python:
needs:
- dispatch
if: needs.dispatch.outputs.python == 'true' || needs.dispatch.outputs.python-style-only == 'true'
uses: ./.github/workflows/ci-python.yml
with:
style-only: ${{ needs.dispatch.outputs.python-style-only == 'true' }}
rust:
needs:
- dispatch
if: needs.dispatch.outputs.rust == 'true'
uses: ./.github/workflows/ci-rust.yml
with:
run-wasm-tests: ${{ needs.dispatch.outputs.rust-test-wasm == 'true' }}
check-cargo-deny: ${{ needs.dispatch.outputs.rust-run-cargo-deny == 'true' }}
web:
needs:
- dispatch
if: needs.dispatch.outputs.web == 'true'
uses: ./.github/workflows/ci-web.yml
docs:
needs:
- dispatch
if: needs.dispatch.outputs.docs == 'true'
uses: ./.github/workflows/ci-docs.yml