From ec52fc2c477799e5dbe0287814adcf8f0b5b35c0 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Sun, 25 Oct 2026 18:58:16 +0000 Subject: [PATCH 1/5] ci: one reduced build job on draft pull requests and manual runs --- .github/workflows/build.yml | 83 ++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ef3976a5..768910dd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,13 @@ on: tags: - "v*" pull_request: + types: [opened, synchronize, reopened, ready_for_review] + workflow_dispatch: + inputs: + tests: + description: "hspec match patterns, one per line; empty runs all tests" + type: string + default: "" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -24,7 +31,30 @@ jobs: maybe-release: runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.plan.outputs.matrix }} + reduced: ${{ steps.plan.outputs.reduced }} steps: + - name: Choose build matrix + id: plan + shell: bash + env: + REDUCED: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == true }} + GHC8: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} + run: | + ghc8='{"os":"22.04","os_underscore":"22_04","arch":"x86-64","runner":"ubuntu-22.04","ghc":"8.10.7","should_run":'"$GHC8"'}' + x86_2204='{"os":"22.04","os_underscore":"22_04","arch":"x86-64","runner":"ubuntu-22.04","ghc":"9.6.3","should_run":true}' + x86_2404='{"os":"24.04","os_underscore":"24_04","arch":"x86-64","runner":"ubuntu-24.04","ghc":"9.6.3","should_run":true}' + arm_2204='{"os":"22.04","os_underscore":"22_04","arch":"aarch64","runner":"ubuntu-22.04-arm","ghc":"9.6.3","should_run":true}' + arm_2404='{"os":"24.04","os_underscore":"24_04","arch":"aarch64","runner":"ubuntu-24.04-arm","ghc":"9.6.3","should_run":true}' + if [ "$REDUCED" = true ]; then + entries=$x86_2404 + else + entries=$ghc8,$x86_2204,$x86_2404,$arm_2204,$arm_2404 + fi + echo "matrix={\"include\":[$entries]}" >> "$GITHUB_OUTPUT" + echo "reduced=$REDUCED" >> "$GITHUB_OUTPUT" + - name: Clone project if: startsWith(github.ref, 'refs/tags/v') uses: actions/checkout@v3 @@ -71,13 +101,13 @@ jobs: packages: write env: apps: "smp-server xftp-server ntf-server xftp" - opt: ${{ github.event_name == 'pull_request' && 'O0' || 'O2' }} - ghc_options: ${{ github.event_name == 'pull_request' && '--ghc-options=-O0' || '' }} + opt: ${{ github.event_name == 'push' && 'O2' || 'O0' }} + ghc_options: ${{ github.event_name != 'push' && '--ghc-options=-O0' || '' }} release: ${{ startsWith(github.ref, 'refs/tags/v') }} postgres_dist: ${{ startsWith(github.ref, 'refs/tags/v') && 'dist-newstyle' || 'dist-postgres' }} branch: ${{ github.base_ref || github.ref_name }} cache_o0: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/stable') }} - image_cache: ghcr.io/simplex-chat/simplexmq-build-cache:${{ matrix.os }}-${{ matrix.arch }}-ghc${{ matrix.ghc }} + image_cache: ghcr.io/${{ github.repository_owner }}/simplexmq-build-cache:${{ matrix.os }}-${{ matrix.arch }}-ghc${{ matrix.ghc }} source_hashes: dist-newstyle/sources.sha256 runs-on: ${{ matrix.runner }} services: @@ -95,38 +125,7 @@ jobs: - 5432:5432 strategy: fail-fast: false - matrix: - include: - - os: 22.04 - os_underscore: 22_04 - arch: x86-64 - runner: "ubuntu-22.04" - ghc: "8.10.7" - should_run: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} - - os: 22.04 - os_underscore: 22_04 - arch: x86-64 - runner: "ubuntu-22.04" - ghc: "9.6.3" - should_run: true - - os: 24.04 - os_underscore: 24_04 - arch: x86-64 - runner: "ubuntu-24.04" - ghc: "9.6.3" - should_run: true - - os: 22.04 - os_underscore: 22_04 - arch: aarch64 - runner: "ubuntu-22.04-arm" - ghc: "9.6.3" - should_run: true - - os: 24.04 - os_underscore: 24_04 - arch: aarch64 - runner: "ubuntu-24.04-arm" - ghc: "9.6.3" - should_run: true + matrix: ${{ fromJSON(needs.maybe-release.outputs.matrix) }} steps: - name: Clone project if: matrix.should_run == true @@ -291,7 +290,7 @@ jobs: - name: Build everything else (standard) id: build-standard - if: matrix.should_run == true + if: matrix.should_run == true && needs.maybe-release.outputs.reduced != 'true' shell: docker exec -t builder sh -eu {0} run: | cabal build --jobs=$(nproc) ${{ env.ghc_options }} @@ -394,12 +393,22 @@ jobs: shell: bash env: PGHOST: localhost + TESTS: ${{ github.event_name == 'workflow_dispatch' && inputs.tests || '' }} + PR_BODY: ${{ github.event.pull_request.draft == true && github.event.pull_request.body || '' }} run: | + if [ -n "$PR_BODY" ]; then + TESTS=$(printf '%s\n' "$PR_BODY" | tr -d '\r' | awk '/^```ci-tests[ \t]*$/ {f = 1; next} /^```[ \t]*$/ {f = 0} f') + fi + args=() + while read -r p; do + if [ -n "$p" ]; then args+=(-m "$p"); fi + done <<< "$TESTS" + if [ "${#args[@]}" -gt 0 ]; then args+=(--fail-on=empty); fi i=1 attempts=1 ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3 while [ "$i" -le "$attempts" ]; do - if ./simplexmq-test; then + if ./simplexmq-test "${args[@]}"; then break else echo "Attempt $i failed, retrying..." From 40bee2cc0c5304a17f78e900e83e8e86bebd21fc Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Sun, 25 Oct 2026 19:59:15 +0000 Subject: [PATCH 2/5] ci: reject manual runs on tags --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 768910dd7..d47370906 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,12 @@ jobs: matrix: ${{ steps.plan.outputs.matrix }} reduced: ${{ steps.plan.outputs.reduced }} steps: + - name: Reject a manual run on a tag + if: github.event_name == 'workflow_dispatch' && github.ref_type == 'tag' + run: | + echo "::error::A manual run starts on a branch; a tag push builds the release." + exit 1 + - name: Choose build matrix id: plan shell: bash From dba79529a40d97c777c47f897ef3cadd44e5bbbc Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Sun, 25 Oct 2026 21:14:21 +0000 Subject: [PATCH 3/5] ci: reduced job by job condition, tests only when selected --- .github/workflows/build.yml | 96 ++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d47370906..ea4923316 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: inputs: tests: - description: "hspec match patterns, one per line; empty runs all tests" + description: "hspec match patterns, one per line; / runs all tests, empty runs none" type: string default: "" @@ -32,35 +32,14 @@ jobs: maybe-release: runs-on: ubuntu-latest outputs: - matrix: ${{ steps.plan.outputs.matrix }} - reduced: ${{ steps.plan.outputs.reduced }} + reduced: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == true }} steps: - name: Reject a manual run on a tag if: github.event_name == 'workflow_dispatch' && github.ref_type == 'tag' run: | - echo "::error::A manual run starts on a branch; a tag push builds the release." + echo "::error::A manual run starts on a branch; to retry a release, re-run the jobs of its tag push." exit 1 - - name: Choose build matrix - id: plan - shell: bash - env: - REDUCED: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == true }} - GHC8: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} - run: | - ghc8='{"os":"22.04","os_underscore":"22_04","arch":"x86-64","runner":"ubuntu-22.04","ghc":"8.10.7","should_run":'"$GHC8"'}' - x86_2204='{"os":"22.04","os_underscore":"22_04","arch":"x86-64","runner":"ubuntu-22.04","ghc":"9.6.3","should_run":true}' - x86_2404='{"os":"24.04","os_underscore":"24_04","arch":"x86-64","runner":"ubuntu-24.04","ghc":"9.6.3","should_run":true}' - arm_2204='{"os":"22.04","os_underscore":"22_04","arch":"aarch64","runner":"ubuntu-22.04-arm","ghc":"9.6.3","should_run":true}' - arm_2404='{"os":"24.04","os_underscore":"24_04","arch":"aarch64","runner":"ubuntu-24.04-arm","ghc":"9.6.3","should_run":true}' - if [ "$REDUCED" = true ]; then - entries=$x86_2404 - else - entries=$ghc8,$x86_2204,$x86_2404,$arm_2204,$arm_2404 - fi - echo "matrix={\"include\":[$entries]}" >> "$GITHUB_OUTPUT" - echo "reduced=$REDUCED" >> "$GITHUB_OUTPUT" - - name: Clone project if: startsWith(github.ref, 'refs/tags/v') uses: actions/checkout@v3 @@ -102,10 +81,11 @@ jobs: build: name: "ubuntu-${{ matrix.os }}-${{ matrix.arch }}, GHC: ${{ matrix.ghc }}" needs: maybe-release + if: needs.maybe-release.outputs.reduced != 'true' permissions: contents: write packages: write - env: + env: &build-env apps: "smp-server xftp-server ntf-server xftp" opt: ${{ github.event_name == 'push' && 'O2' || 'O0' }} ghc_options: ${{ github.event_name != 'push' && '--ghc-options=-O0' || '' }} @@ -116,7 +96,7 @@ jobs: image_cache: ghcr.io/${{ github.repository_owner }}/simplexmq-build-cache:${{ matrix.os }}-${{ matrix.arch }}-ghc${{ matrix.ghc }} source_hashes: dist-newstyle/sources.sha256 runs-on: ${{ matrix.runner }} - services: + services: &build-services postgres: image: postgres:15 env: @@ -131,8 +111,39 @@ jobs: - 5432:5432 strategy: fail-fast: false - matrix: ${{ fromJSON(needs.maybe-release.outputs.matrix) }} - steps: + matrix: + include: + - os: 22.04 + os_underscore: 22_04 + arch: x86-64 + runner: "ubuntu-22.04" + ghc: "8.10.7" + should_run: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} + - os: 22.04 + os_underscore: 22_04 + arch: x86-64 + runner: "ubuntu-22.04" + ghc: "9.6.3" + should_run: true + - os: 24.04 + os_underscore: 24_04 + arch: x86-64 + runner: "ubuntu-24.04" + ghc: "9.6.3" + should_run: true + - os: 22.04 + os_underscore: 22_04 + arch: aarch64 + runner: "ubuntu-22.04-arm" + ghc: "9.6.3" + should_run: true + - os: 24.04 + os_underscore: 24_04 + arch: aarch64 + runner: "ubuntu-24.04-arm" + ghc: "9.6.3" + should_run: true + steps: &build-steps - name: Clone project if: matrix.should_run == true uses: actions/checkout@v3 @@ -399,6 +410,7 @@ jobs: shell: bash env: PGHOST: localhost + REDUCED: ${{ needs.maybe-release.outputs.reduced }} TESTS: ${{ github.event_name == 'workflow_dispatch' && inputs.tests || '' }} PR_BODY: ${{ github.event.pull_request.draft == true && github.event.pull_request.body || '' }} run: | @@ -409,7 +421,12 @@ jobs: while read -r p; do if [ -n "$p" ]; then args+=(-m "$p"); fi done <<< "$TESTS" - if [ "${#args[@]}" -gt 0 ]; then args+=(--fail-on=empty); fi + if [ "${#args[@]}" -gt 0 ]; then + args+=(--fail-on=empty) + elif [ "$REDUCED" = true ]; then + echo "No tests selected" + exit 0 + fi i=1 attempts=1 ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3 @@ -473,6 +490,27 @@ jobs: dist-postgres key: ${{ steps.keys.outputs.dist }}-O0-${{ env.branch }}-${{ github.sha }} + build-reduced: + name: "ubuntu-${{ matrix.os }}-${{ matrix.arch }}, GHC: ${{ matrix.ghc }}" + needs: maybe-release + if: needs.maybe-release.outputs.reduced == 'true' + permissions: + contents: write + packages: write + env: *build-env + runs-on: ${{ matrix.runner }} + services: *build-services + strategy: + matrix: + include: + - os: 24.04 + os_underscore: 24_04 + arch: x86-64 + runner: "ubuntu-24.04" + ghc: "9.6.3" + should_run: true + steps: *build-steps + # ============================= # Resolver test job # ============================= From 4dcc31c86ac3e6d1093239b2007f2f20ac9e8a98 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Mon, 26 Oct 2026 09:09:19 +0000 Subject: [PATCH 4/5] ci: test selection and retries in scripts/ci/run-tests.sh --- .github/workflows/build.yml | 32 ++----------------------------- scripts/ci/run-tests.sh | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 30 deletions(-) create mode 100755 scripts/ci/run-tests.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea4923316..3a00396bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -413,36 +413,8 @@ jobs: REDUCED: ${{ needs.maybe-release.outputs.reduced }} TESTS: ${{ github.event_name == 'workflow_dispatch' && inputs.tests || '' }} PR_BODY: ${{ github.event.pull_request.draft == true && github.event.pull_request.body || '' }} - run: | - if [ -n "$PR_BODY" ]; then - TESTS=$(printf '%s\n' "$PR_BODY" | tr -d '\r' | awk '/^```ci-tests[ \t]*$/ {f = 1; next} /^```[ \t]*$/ {f = 0} f') - fi - args=() - while read -r p; do - if [ -n "$p" ]; then args+=(-m "$p"); fi - done <<< "$TESTS" - if [ "${#args[@]}" -gt 0 ]; then - args+=(--fail-on=empty) - elif [ "$REDUCED" = true ]; then - echo "No tests selected" - exit 0 - fi - i=1 - attempts=1 - ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3 - while [ "$i" -le "$attempts" ]; do - if ./simplexmq-test "${args[@]}"; then - break - else - echo "Attempt $i failed, retrying..." - i=$((i + 1)) - sleep 1 - fi - done - if [ "$i" -gt "$attempts" ]; then - echo "All "$attempts" attempts failed." - exit 1 - fi + ATTEMPTS: ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) && 3 || 1 }} + run: scripts/ci/run-tests.sh ./simplexmq-test - name: Remove -O2 build directories id: remove-o2 diff --git a/scripts/ci/run-tests.sh b/scripts/ci/run-tests.sh new file mode 100755 index 000000000..327ff667c --- /dev/null +++ b/scripts/ci/run-tests.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -euo pipefail + +if [ "$#" -eq 0 ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +if [ -n "${PR_BODY:-}" ]; then + TESTS=$(printf '%s\n' "$PR_BODY" | tr -d '\r' | awk '/^```ci-tests[ \t]*$/ {f = 1; next} /^```[ \t]*$/ {f = 0} f') +fi +args=() +while read -r p; do + if [ -n "$p" ]; then args+=(-m "$p"); fi +done <<< "${TESTS:-}" +if [ "${#args[@]}" -gt 0 ]; then + args+=(--fail-on=empty) +elif [ "${REDUCED:-}" = true ]; then + echo "No tests selected" + exit 0 +fi + +i=1 +attempts=${ATTEMPTS:-1} +while [ "$i" -le "$attempts" ]; do + if "$@" "${args[@]}"; then + break + else + echo "Attempt $i failed, retrying..." + i=$((i + 1)) + sleep 1 + fi +done +if [ "$i" -gt "$attempts" ]; then + echo "All $attempts attempts failed." + exit 1 +fi From 6134144f3bdc782edb7a9f741021b937e256d4df Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Mon, 26 Oct 2026 09:28:19 +0000 Subject: [PATCH 5/5] ci: guide to reduced builds and test selection --- scripts/ci/README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 scripts/ci/README.md diff --git a/scripts/ci/README.md b/scripts/ci/README.md new file mode 100644 index 000000000..c4064dcdb --- /dev/null +++ b/scripts/ci/README.md @@ -0,0 +1,50 @@ +# CI on GitHub's runners + +## Builds + +`build.yml` runs one of two builds: + +| Trigger | Build | Jobs | +|---|---|---| +| a push to a draft pull request
a manual run (workflow_dispatch) | reduced | build-reduced: ubuntu-24.04 x86-64, GHC 9.6.3, at -O0, without the standard build | +| a pull request marked ready for review, and later pushes to it
a push to master or stable
a push of a v* tag | full | build: 5 entries (GHC 8.10.7 and 9.6.3, x86-64 and aarch64), at -O0 on pull requests and -O2 on pushes | + +Both builds also run maybe-release and resolver (python). Tests run on the x86-64 entries. The reduced build restores the -O0 build caches saved by master pushes; the Docker image cache is tagged by ISO week, so the first run of a week rebuilds the image. + +## Test selection + +- Draft pull request: a fenced block with the info string `ci-tests` in the description, one hspec match pattern per line. The block is read at each push; an edit applies from the next push. +- Manual run: input `tests`, one pattern per line. +- `/` runs all tests. With no pattern, a reduced build runs no tests. A full build runs all tests and ignores the selection. +- Each pattern is passed as `-m `. With at least one pattern, `--fail-on=empty` is added, so a pattern that matches nothing fails the run. +- A pattern matches the item path `/group/subgroup/item/` as a substring. Group names are the `describe` names in tests/Test.hs. + +Example block: + +```` +```ci-tests +/Core tests/Encoding tests/ +SMP proxy +``` +```` + +## Manual run + +```sh +curl -fsS -X POST \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos//simplexmq/actions/workflows/build.yml/dispatches \ + -d '{"ref": "", "inputs": {"tests": "/Core tests/Encoding tests/\nSMP proxy"}}' +``` + +A manual run starts on a branch; a release is retried by re-running the jobs of its tag push. + +## Local run + +scripts/ci/run-tests.sh runs the selection as in CI: it appends the patterns of `TESTS` to the test command. + +```sh +cabal build --enable-tests --ghc-options=-O0 simplexmq-test +TESTS='/Core tests/Encoding tests/' scripts/ci/run-tests.sh "$(cabal list-bin simplexmq-test --ghc-options=-O0)" +```