Files
palladum-lightning/.github/workflows/ci.yaml
Rusty Russell 51525efda2 ci: don't run shard 2/12 ubsan without parallel.
3974806e5a added this:
    CI: Try not running group 2/10 UBSAN in parallel.
    
    It's being killed with signal 143, which means docker isn't happy; too much memory consumption?

But since we're now at 12 groups, that probably doesn't apply (it
might not have even before, in the two years since that commit since
so may things have been added).  And it caused this shard to take over
2 hours and timed out.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2026-01-08 22:33:19 +10:30

795 lines
24 KiB
YAML

---
name: Continuous Integration
on:
push:
branches:
- "master"
pull_request:
types: [opened, synchronize, edited]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
# Makes the upload-artifact work more reliably at the cost
# of a bit of compile time.
RUST_PROFILE: release
SLOW_MACHINE: 1
CI_SERVER_URL: "http://35.239.136.52:3170"
PYTEST_OPTS_BASE: "-vvv --junit-xml=report.xml --timeout=1800 --durations=10"
jobs:
prebuild:
name: Pre-build checks
runs-on: ubuntu-24.04
timeout-minutes: 120
if: |
github.event.action != 'edited' ||
contains(github.event.pull_request.body, 'Changelog')
env:
BOLTDIR: bolts
strategy:
fail-fast: true
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Rebase
# We can't rebase if we're on master already.
if: github.ref != 'refs/heads/master'
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git fetch origin ${{ github.base_ref }}
git rebase origin/${{ github.base_ref }}
- name: Check changelog
env:
PR_DESCRIPTION: "${{ github.event.pull_request.body || '' }}"
EVENT_NAME: "${{ github.event_name }}"
BASE_REF: "${{ github.base_ref || 'master' }}"
run: |
echo "Event Name: $EVENT_NAME"
echo "Base Ref: $BASE_REF"
echo "PR DESCRIPTION: $PR_DESCRIPTION"
if [ "$EVENT_NAME" = "pull_request" ]; then
if [[ "$PR_DESCRIPTION" != *"Changelog-None"* && \
-z "$(git log origin/$BASE_REF..HEAD --oneline --grep='Changelog-')" && \
"$(git rev-parse --abbrev-ref HEAD)" != "$BASE_REF" ]]; then
echo "::error::'Changelog' entry is missing in all commits, and 'Changelog-None' not specified in the PR description"
exit 1
else
if [[ "$PR_DESCRIPTION" == *"Changelog-None"* ]]; then
echo "Changelog found in PR description"
else
echo "Changelog found in Commit \"$(git log origin/$BASE_REF..HEAD --oneline --grep='Changelog-')\""
fi
fi
else
echo "Not a PR event."
fi
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
# We're going to check BOLT quotes, so get the latest version
git clone https://github.com/lightning/bolts.git ../${BOLTDIR}
- name: Configure
run: ./configure --enable-debugbuild --enable-rust
- name: Check source
env:
VALGRIND: 0
PYTEST_OPTS: ${{ env.PYTEST_OPTS_BASE }}
run: |
uv run make check-source BASE_REF="origin/${{ github.base_ref }}"
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-prebuild
path: report.xml
if-no-files-found: ignore
- name: Check Generated Files have been updated
run: uv run make check-gen-updated
- name: Check docs
run: uv run make check-doc
compile:
name: Compile CLN ${{ matrix.cfg }}
runs-on: ubuntu-24.04
timeout-minutes: 30
needs:
- prebuild
strategy:
fail-fast: true
matrix:
include:
- CFG: compile-gcc
VALGRIND: 1
COMPILER: gcc
- CFG: compile-gcc-O3
VALGRIND: 1
COMPILER: gcc
COPTFLAGS_VAR: COPTFLAGS="-O3 -Werror"
# While we're at it let's try to compile with clang
- CFG: compile-clang
VALGRIND: 1
COMPILER: clang
- CFG: compile-clang-sanitizers
COMPILER: clang
ASAN: 1
UBSAN: 1
VALGRIND: 0
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Build
env:
COMPILER: ${{ matrix.COMPILER }}
ASAN: ${{ matrix.ASAN }}
UBSAN: ${{ matrix.UBSAN }}
VALGRIND: ${{ matrix.VALGRIND }}
COMPAT: 1
CFG: ${{ matrix.CFG }}
run: |
set -e
./configure --enable-debugbuild CC="$COMPILER" ${{ matrix.COPTFLAGS_VAR }}
uv run make -j $(nproc) testpack.tar.bz2
# Rename now so we don't clash
mv testpack.tar.bz2 cln-${CFG}.tar.bz2
- name: Check rust packages
run: cargo test --all
- uses: actions/upload-artifact@v4
with:
name: cln-${{ matrix.CFG }}.tar.bz2
path: cln-${{ matrix.CFG }}.tar.bz2
check-units:
# The unit test checks are not in the critical path (not dependent
# on the integration tests), so run them with `valgrind`
name: Run unit tests
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
BOLTDIR: bolts
needs:
- compile
strategy:
fail-fast: true
matrix:
include:
- CFG: compile-gcc
VALGRIND: 1
- CFG: compile-clang-sanitizers
VALGRIND: 0
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
sudo apt-get update -qq
# We're going to check BOLT quotes, so get the latest version
git clone https://github.com/lightning/bolts.git ../${BOLTDIR}
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-${{ matrix.CFG }}.tar.bz2
- name: Check
run: |
tar -xaf cln-${{ matrix.CFG }}.tar.bz2
uv run eatmydata make -j $(nproc) check-units installcheck VALGRIND=${{ matrix.VALGRIND }}
check-fuzz:
name: Run fuzz regression tests
runs-on: ubuntu-24.04
needs:
- prebuild
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Build
run: |
./configure --enable-debugbuild --enable-fuzzing --enable-address-sanitizer --enable-ub-sanitizer --disable-valgrind CC=clang
uv run make -j $(nproc) check-fuzz
check-downgrade:
name: Check we can downgrade the node
runs-on: ubuntu-24.04
needs:
- compile
strategy:
fail-fast: false
matrix:
include:
- CFG: compile-gcc
TEST_DB_PROVIDER: sqlite3
TEST_NETWORK: regtest
VALGRIND: 1
- CFG: compile-gcc
TEST_DB_PROVIDER: postgres
TEST_NETWORK: regtest
- CFG: compile-gcc
TEST_DB_PROVIDER: sqlite3
TEST_NETWORK: liquid-regtest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Install bitcoind
env:
TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
run: .github/scripts/install-bitcoind.sh
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-${{ matrix.CFG }}.tar.bz2
- name: Unpack pre-built CLN
env:
CFG: ${{ matrix.CFG }}
run: |
tar -xaf cln-${CFG}.tar.bz2
- name: Fetch and unpack previous CLN
run: |
mkdir /tmp/old-cln
cd /tmp/old-cln
wget https://github.com/ElementsProject/lightning/releases/download/v25.09/clightning-v25.09-ubuntu-24.04-amd64.tar.xz
tar -xaf clightning-v25.09-ubuntu-24.04-amd64.tar.xz
- name: Switch network
if: ${{ matrix.TEST_NETWORK == 'liquid-regtest' }}
run: |
# Loading the network from config.vars rather than the envvar is a terrible idea...
sed -i 's/TEST_NETWORK=regtest/TEST_NETWORK=liquid-regtest/g' config.vars
cat config.vars
- name: Test
env:
SLOW_MACHINE: 1
PYTEST_PAR: 10
TEST_DEBUG: 1
TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }}
TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
LIGHTNINGD_POSTGRES_NO_VACUUM: 1
VALGRIND: ${{ matrix.VALGRIND }}
PREV_LIGHTNINGD: /tmp/old-cln/usr/bin/lightningd
PYTEST_OPTS: ${{ env.PYTEST_OPTS_BASE }}
run: |
env
cat config.vars
uv run eatmydata pytest tests/test_downgrade.py -n ${PYTEST_PAR} ${PYTEST_OPTS}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-check-downgrade-${{ matrix.TEST_DB_PROVIDER }}-${{ matrix.TEST_NETWORK }}
path: report.xml
if-no-files-found: ignore
integration:
name: Test CLN ${{ matrix.name }}
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
RUST_PROFILE: release # Has to match the one in the compile step
PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10
needs:
- compile
strategy:
fail-fast: false
matrix:
include:
- NAME: gcc
CFG: compile-gcc
TEST_DB_PROVIDER: sqlite3
COMPILER: gcc
TEST_NETWORK: regtest
# While we're at it let's try to compile with clang
- NAME: clang
CFG: compile-clang
TEST_DB_PROVIDER: sqlite3
COMPILER: clang
TEST_NETWORK: regtest
# And of course we want to test postgres too
- NAME: postgres
CFG: compile-gcc
COMPILER: gcc
TEST_DB_PROVIDER: postgres
TEST_NETWORK: regtest
# And don't forget about elements (like cdecker did when
# reworking the CI...)
- NAME: liquid
CFG: compile-gcc
COMPILER: gcc
TEST_NETWORK: liquid-regtest
TEST_DB_PROVIDER: sqlite3
# And dual funding!
- NAME: dual-fund
CFG: compile-gcc
TEST_DB_PROVIDER: sqlite3
COMPILER: gcc
TEST_NETWORK: regtest
EXPERIMENTAL_DUAL_FUND: 1
# And splicing!
- NAME: splicing
CFG: compile-gcc
TEST_DB_PROVIDER: sqlite3
COMPILER: gcc
TEST_NETWORK: regtest
EXPERIMENTAL_SPLICING: 1
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Install bitcoind
env:
TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
run: .github/scripts/install-bitcoind.sh
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-${{ matrix.CFG }}.tar.bz2
- name: Unpack pre-built CLN
env:
CFG: ${{ matrix.CFG }}
run: |
tar -xaf cln-${CFG}.tar.bz2
- name: Switch network
if: ${{ matrix.TEST_NETWORK == 'liquid-regtest' }}
run: |
# Loading the network from config.vars rather than the envvar is a terrible idea...
sed -i 's/TEST_NETWORK=regtest/TEST_NETWORK=liquid-regtest/g' config.vars
cat config.vars
- name: Test
env:
COMPILER: ${{ matrix.COMPILER }}
EXPERIMENTAL_DUAL_FUND: ${{ matrix.EXPERIMENTAL_DUAL_FUND }}
EXPERIMENTAL_SPLICING: ${{ matrix.EXPERIMENTAL_SPLICING }}
COMPAT: 1
CFG: ${{ matrix.CFG }}
SLOW_MACHINE: 1
PYTEST_PAR: 10
TEST_DEBUG: 1
TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }}
TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
LIGHTNINGD_POSTGRES_NO_VACUUM: 1
run: |
env
cat config.vars
VALGRIND=0 uv run eatmydata pytest tests/ -n ${PYTEST_PAR} ${PYTEST_OPTS}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-integration-${{ matrix.name }}
path: report.xml
if-no-files-found: ignore
integration-valgrind:
name: Valgrind Test CLN ${{ matrix.name }}
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
RUST_PROFILE: release # Has to match the one in the compile step
CFG: compile-gcc
PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10 --test-group-random-seed=42
needs:
- compile
strategy:
fail-fast: false
matrix:
include:
- NAME: Valgrind (01/10)
GROUP: 1
PYTEST_OPTS: --test-group=1 --test-group-count=10
- NAME: Valgrind (02/10)
GROUP: 2
PYTEST_OPTS: --test-group=2 --test-group-count=10
- NAME: Valgrind (03/10)
GROUP: 3
PYTEST_OPTS: --test-group=3 --test-group-count=10
- NAME: Valgrind (04/10)
GROUP: 4
PYTEST_OPTS: --test-group=4 --test-group-count=10
- NAME: Valgrind (05/10)
GROUP: 5
PYTEST_OPTS: --test-group=5 --test-group-count=10
- NAME: Valgrind (06/10)
GROUP: 6
PYTEST_OPTS: --test-group=6 --test-group-count=10
- NAME: Valgrind (07/10)
GROUP: 7
PYTEST_OPTS: --test-group=7 --test-group-count=10
- NAME: Valgrind (08/10)
GROUP: 8
PYTEST_OPTS: --test-group=8 --test-group-count=10
- NAME: Valgrind (09/10)
GROUP: 9
PYTEST_OPTS: --test-group=9 --test-group-count=10
- NAME: Valgrind (10/10)
GROUP: 10
PYTEST_OPTS: --test-group=10 --test-group-count=10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -yyq valgrind
bash -x .github/scripts/setup.sh
- name: Install bitcoind
run: .github/scripts/install-bitcoind.sh
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-compile-gcc.tar.bz2
- name: Unpack build
run: tar -xvjf cln-compile-gcc.tar.bz2
- name: Test
env:
SLOW_MACHINE: 1
TEST_DEBUG: 1
run: |
VALGRIND=1 uv run eatmydata pytest tests/ -n 3 ${PYTEST_OPTS} ${{ matrix.PYTEST_OPTS }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-integration-valgrind-${{ matrix.GROUP }}
path: report.xml
if-no-files-found: ignore
integration-sanitizers:
name: Sanitizers Test CLN
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
RUST_PROFILE: release
SLOW_MACHINE: 1
TEST_DEBUG: 1
PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10 --test-group-random-seed=42
needs:
- compile
strategy:
fail-fast: false
matrix:
include:
- NAME: ASan/UBSan (01/12)
GROUP: 1
PYTEST_OPTS: --test-group=1 --test-group-count=12
- NAME: ASan/UBSan (02/12)
GROUP: 2
PYTEST_OPTS: --test-group=2 --test-group-count=12
- NAME: ASan/UBSan (03/12)
GROUP: 3
PYTEST_OPTS: --test-group=3 --test-group-count=12
- NAME: ASan/UBSan (04/12)
GROUP: 4
PYTEST_OPTS: --test-group=4 --test-group-count=12
- NAME: ASan/UBSan (05/12)
GROUP: 5
PYTEST_OPTS: --test-group=5 --test-group-count=12
- NAME: ASan/UBSan (06/12)
GROUP: 6
PYTEST_OPTS: --test-group=6 --test-group-count=12
- NAME: ASan/UBSan (07/12)
GROUP: 7
PYTEST_OPTS: --test-group=7 --test-group-count=12
- NAME: ASan/UBSan (08/12)
GROUP: 8
PYTEST_OPTS: --test-group=8 --test-group-count=12
- NAME: ASan/UBSan (09/12)
GROUP: 9
PYTEST_OPTS: --test-group=9 --test-group-count=12
- NAME: ASan/UBSan (10/12)
GROUP: 10
PYTEST_OPTS: --test-group=10 --test-group-count=12
- NAME: ASan/UBSan (11/12)
GROUP: 11
PYTEST_OPTS: --test-group=11 --test-group-count=12
- NAME: ASan/UBSan (12/12)
GROUP: 12
PYTEST_OPTS: --test-group=12 --test-group-count=12
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Install bitcoind
run: .github/scripts/install-bitcoind.sh
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-compile-clang-sanitizers.tar.bz2
- name: Unpack build
run: tar -xvjf cln-compile-clang-sanitizers.tar.bz2
- name: Test
run: |
uv run eatmydata pytest tests/ -n 2 ${PYTEST_OPTS} ${{ matrix.PYTEST_OPTS }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-integration-sanitizers-${{ matrix.GROUP }}
path: report.xml
if-no-files-found: ignore
update-docs-examples:
name: Update examples in doc schemas (disabled temporarily!)
if: false
runs-on: ubuntu-24.04
timeout-minutes: 30
strategy:
fail-fast: false
env:
VALGRIND: 0
GENERATE_EXAMPLES: 1
PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10
TEST_NETWORK: regtest
needs:
- compile
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Install bitcoind
env:
TEST_NETWORK: regtest
run: .github/scripts/install-bitcoind.sh
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-compile-gcc.tar.bz2
- name: Unpack pre-built CLN
run: |
tar -xaf cln-compile-gcc.tar.bz2
- name: Test
run: |
uv run eatmydata make -j $(nproc) check-doc-examples
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-update-docs-examples
path: report.xml
if-no-files-found: ignore
min-btc-support:
name: Test minimum supported BTC v${{ matrix.MIN_BTC_VERSION }} with ${{ matrix.NAME }}
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
RUST_PROFILE: release # Has to match the one in the compile step
PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10
needs:
- compile
strategy:
fail-fast: false
matrix:
include:
- NAME: clang
CFG: compile-clang
TEST_DB_PROVIDER: sqlite3
COMPILER: clang
TEST_NETWORK: regtest
MIN_BTC_VERSION: "25.0"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: |
bash -x .github/scripts/setup.sh
- name: Download Bitcoin Core
run: wget "https://bitcoincore.org/bin/bitcoin-core-${{ matrix.MIN_BTC_VERSION }}/bitcoin-${{ matrix.MIN_BTC_VERSION }}-x86_64-linux-gnu.tar.gz"
- name: Extract Bitcoin Core
run: tar -xf "bitcoin-${{ matrix.MIN_BTC_VERSION }}-x86_64-linux-gnu.tar.gz"
- name: Move Bitcoin Core Binaries
run: sudo mv bitcoin-${{ matrix.MIN_BTC_VERSION }}/bin/* /usr/local/bin/
- name: Clean Up Downloaded Bitcoin
run: rm -rf "bitcoin-${{ matrix.MIN_BTC_VERSION }}-x86_64-linux-gnu.tar.gz" "bitcoin-${{ matrix.MIN_BTC_VERSION }}"
- name: Download build
uses: actions/download-artifact@v4
with:
name: cln-${{ matrix.CFG }}.tar.bz2
- name: Unpack pre-built CLN
env:
CFG: ${{ matrix.CFG }}
run: |
tar -xaf cln-${CFG}.tar.bz2
- name: Test
env:
COMPILER: ${{ matrix.COMPILER }}
COMPAT: 1
CFG: ${{ matrix.CFG }}
SLOW_MACHINE: 1
PYTEST_PAR: 10
TEST_DEBUG: 1
TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }}
TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
LIGHTNINGD_POSTGRES_NO_VACUUM: 1
run: |
env
cat config.vars
VALGRIND=0 uv run eatmydata pytest tests/ -n ${PYTEST_PAR} ${PYTEST_OPTS}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: pytest-results-min-btc-support-${{ matrix.NAME }}
path: report.xml
if-no-files-found: ignore
gather:
# A dummy task that depends on the full matrix of tests, and
# signals successful completion. Used for the PR status to pass
# before merging. Needs to run even if they failed!
name: CI completion
runs-on: ubuntu-24.04
needs:
- integration
- check-units
- integration-valgrind
- integration-sanitizers
- min-btc-support
- check-downgrade
if: ${{ always() }}
steps:
- name: Complete
env:
JOB_NAMES: "INTEGRATION CHECK_UNITS VALGRIND SANITIZERS BTC"
INTEGRATION: ${{ needs.integration.result }}
CHECK_UNITS: ${{ needs['check-units'].result }}
VALGRIND: ${{ needs['integration-valgrind'].result }}
SANITIZERS: ${{ needs['integration-sanitizers'].result }}
DOCS: ${{ needs['update-docs-examples'].result }}
BTC: ${{ needs['min-btc-support'].result }}
CHECK_DOWNGRADE: ${{ needs['check-downgrade'].result }}
run: |
failed=""
for name in $JOB_NAMES; do
result="${!name}"
echo "$name: $result"
if [[ "$result" != "success" ]]; then
failed="yes"
fi
done
if [[ "$failed" == "yes" ]]; then
echo "One or more required jobs failed"
exit 1
fi