Skip to content
qbittorrent-nox-static

Github Actions

This project relies heavily on Github Actions to build and release the binaries. The actions are defined in the .github/workflows directory.

Github repo Actions settings

The required permissions for each workflow and job have been configured granularly in the workflows themselves. The permissions are defined in the permissions section of the workflow.

Workflows

These workflows are using a reusable caller workflow that allows for inputs to be passed to the reusable workflows. This allows for a single workflow to be used to call multiple workflows with different inputs.

This is the structure of the workflows:

ci-main-reusable-caller
├─ ci-debian-build
├─ ci-alpine-build
├─ ci-alpine-release
└─ ci-auto-rerun-failed-jobs
ci-main-reusable-caller

The primary reusable caller workflow

# @credits https://github.com/c0re100/qBittorrent-Enhanced-Edition
name: ci - main reusable caller
on:
workflow_dispatch:
inputs:
script_name:
description: "Which script to run?"
required: true
default: "qbt-nox-static.bash"
type: choice
options: ["qbt-nox-static.bash", "qbittorrent-nox-static.sh"]
debian-build:
description: "Debian: build"
required: true
default: false
type: boolean
alpine-build:
description: "Alpine: build"
required: true
default: true
type: boolean
workflow-files:
description: "Workflow files"
required: true
default: true
type: boolean
icu:
description: "Enable icu"
required: true
default: false
type: boolean
debug:
description: "Debug builds (symbols)"
required: true
default: false
type: boolean
release:
description: "Release assets?"
required: true
default: true
type: boolean
distinct_id:
description: "Distinct id"
required: false
type: string
skip_rerun:
description: "Skip rerun?"
required: true
default: false
type: boolean
retries:
description: "Number of rerun retries"
required: true
default: "1"
type: choice
options: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
permissions: {}
jobs:
ci-debian-build:
if: github.event.inputs.debian-build == 'true'
concurrency:
group: ci-debian-build
cancel-in-progress: true
permissions:
contents: read
uses: ./.github/workflows/ci-debian-build.yml
with:
distinct_id: ${{ github.event.inputs.distinct_id }}
workflow-files: ${{ github.event.inputs.workflow-files == 'true' && 'yes' || 'no' }}
icu: ${{ github.event.inputs.icu == 'true' && 'no' || 'yes' }}
debug: ${{ github.event.inputs.debug == 'true' && 'yes' || 'no' }}
script_name: ${{ github.event.inputs.script_name }}
ci-alpine-build:
if: github.event.inputs.alpine-build == 'true'
concurrency:
group: ci-alpine-build
cancel-in-progress: true
permissions:
id-token: write
contents: read
attestations: write
uses: ./.github/workflows/ci-alpine-build.yml
with:
distinct_id: ${{ github.event.inputs.distinct_id }}
workflow-files: ${{ github.event.inputs.workflow-files == 'true' && 'yes' || 'no' }}
icu: ${{ github.event.inputs.icu == 'true' && 'no' || 'yes' }}
debug: ${{ github.event.inputs.debug == 'true' && 'yes' || 'no' }}
script_name: ${{ github.event.inputs.script_name }}
ci-alpine-release:
needs: [ci-alpine-build]
if: github.event.inputs.release == 'true' && contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
concurrency:
group: ci-alpine-release
cancel-in-progress: true
permissions:
contents: write
uses: ./.github/workflows/ci-alpine-release.yml
with:
distinct_id: ${{ github.event.inputs.distinct_id }}
ci-auto-rerun-failed-jobs:
if: failure() && (github.event.inputs.skip_rerun || 'false') == 'false'
needs: [ci-debian-build, ci-alpine-build, ci-alpine-release]
concurrency:
group: ci-auto-rerun-failed-jobs
cancel-in-progress: true
permissions:
actions: write
runs-on: ubuntu-24.04-arm
env:
GH_TOKEN: "${{ secrets.AUTO_RERUN || github.token }}"
github_repo: "" # To use ci-auto-rerun-failed-jobs.yml hosted in a remote repository else default to the current repository. Requires PAT token AUTO_RERUN
retries: ${{ github.event.inputs.retries || '1' }}
distinct_id: ${{ github.event.inputs.distinct_id }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: ci-auto-rerun-failed-jobs via ${{ env.github_repo || github.repository }}
run: >
gh workflow run ci-auto-rerun-failed-jobs-action.yml
--repo "${github_repo:-$GITHUB_REPOSITORY}"
-f github_repo=${GITHUB_REPOSITORY}
-f run_id=${GITHUB_RUN_ID}
-f attempts=${GITHUB_RUN_ATTEMPT}
-f retries=${retries}
-f distinct_id=${distinct_id}
ci-debian-build

The debian build workflow

name: ci - debian build
on:
workflow_call:
inputs:
distinct_id:
description: "Distinct id"
required: false
type: string
workflow-files:
description: "Alpine: workflow-files files"
required: true
type: string
icu:
description: "enable icu"
required: true
type: string
debug:
description: "debug builds"
required: true
type: string
script_name:
description: "script name"
required: true
type: string
jobs:
build:
defaults:
run:
shell: bash
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
container_id: [debian, ubuntu]
container_codename: [bookworm, noble]
qbt_build_tool: ["cmake", "qmake"]
qbt_libtorrent_version: ["1.2", "2.0"]
exclude:
- container_id: debian
container_codename: noble
- container_id: ubuntu
container_codename: bookworm
include:
- qbt_build_tool: "qmake"
qbt_qt_version: "5"
- qbt_build_tool: "cmake"
qbt_qt_version: "6"
name: "${{ matrix.container_id }}-${{ matrix.container_codename }}-${{ matrix.qbt_libtorrent_version }}-qt-${{ matrix.qbt_qt_version }}-${{ matrix.qbt_build_tool }}"
env: # host
qbt_build_dir: "qbt-build"
disable_qt5: ""
artifact_name: "${{ matrix.container_id }}-${{ matrix.container_codename }}-${{ matrix.qbt_libtorrent_version }}-qt-${{ matrix.qbt_qt_version }}-${{ matrix.qbt_build_tool }}-nox"
script_name: ${{ inputs.script_name }}
container:
image: arm64v8/${{ matrix.container_id }}:${{ matrix.container_codename }}
env: # container
LANG: C.UTF-8
LC_ALL: C.UTF-8
DEBIAN_FRONTEND: noninteractive
disable_qt5: ${{ env.disable_qt5 }}
qbt_build_dir: ${{ env.qbt_build_dir }}
qbt_libtorrent_version: ${{ matrix.qbt_libtorrent_version }}
qbt_qt_version: ${{ matrix.qbt_qt_version }}
qbt_build_tool: ${{ matrix.qbt_build_tool }}
qbt_cross_name: ${{ matrix.qbt_cross_name }}
qbt_patches_url: ${{ github.repository }}
qbt_skip_icu: ${{ inputs.icu }}
qbt_boost_tag: ${{ matrix.qbt_boost_tag }}
qbt_libtorrent_tag: ${{ matrix.qbt_libtorrent_tag }}
qbt_qt_tag: ${{ matrix.qbt_qt_tag }}
qbt_qbittorrent_tag: ${{ matrix.qbt_qbittorrent_tag }}
qbt_libtorrent_master_jamfile: ""
qbt_workflow_files: ${{ inputs.workflow-files }}
qbt_workflow_artifacts: ""
qbt_cache_dir: ""
qbt_optimise_strip: ""
qbt_build_debug: ${{ inputs.debug }}
qbt_revision_url: ${{ github.repository }}
qbt_standard: ""
qbt_static_ish: ""
steps:
- name: Checkout ${{ inputs.distinct_id }}
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Bootstrap test tools ${{ inputs.distinct_id }}
run: bash ${script_name} update install_test
- name: qBittorrent v5 transition ${{ inputs.distinct_id }}
run: |
if [[ -f "${qbt_build_dir}/release_info/disable-qt5" || -f "disable-qt5" ]]; then
printf '%s\n' "disable_qt5=yes" >> $GITHUB_ENV
printf '%s\n' "Found file: \`disable-qt5\` -> setting env: \`disable_qt5=yes\`" >> $GITHUB_STEP_SUMMARY
fi
- name: Bootstrap core deps ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} install_core
- name: Bootstrap build ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} -bs-a
- name: glibc ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} glibc
- name: zlib ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} zlib
- name: iconv ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} iconv
- name: icu ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} icu
- name: openssl ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} openssl
- name: boost ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} boost
- name: libtorrent ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} libtorrent
- name: double conversion ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes' && matrix.qbt_build_tool == 'cmake'
run: bash ${script_name} double_conversion
- name: qtbase ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} qtbase
- name: qttools ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} qttools
- name: qbittorrent ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: bash ${script_name} qbittorrent
- name: Upload ${{ env.artifact_name }} artifacts ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
uses: actions/upload-artifact@v4
with:
name: ${{ env.artifact_name }}
path: ${{ env.qbt_build_dir }}/completed/qbittorrent-nox
- name: Host - Upload logs on error
if: failure() && env.disable_qt5 != 'yes'
uses: actions/upload-artifact@v4
with:
name: "${{ env.artifact_name }}-logs"
path: "${{ env.qbt_build_dir }}"
ci-alpine-build

The alpine build workflow

# @credits https://github.com/c0re100/qBittorrent-Enhanced-Edition
name: ci - alpine build
on:
workflow_call:
inputs:
distinct_id:
description: "Distinct id"
required: false
type: string
workflow-files:
description: "Alpine: workflow-files files"
required: true
type: string
icu:
description: "enable icu"
required: true
type: string
debug:
description: "debug builds"
required: true
type: string
script_name:
description: "script name"
required: true
type: string
jobs:
build-alpine:
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
os_id: [alpine]
os_version_id: [edge]
qbt_cross_name: ["armhf", "armv7", "aarch64", "x86_64", "x86"]
qbt_libtorrent_version: ["1.2", "2.0"]
qbt_build_tool: ["", "qmake"]
include:
- qbt_build_tool: "qmake"
qbt_qt_version_name: "qt5-"
qbt_qt_version: "5"
- qbt_build_tool: ""
qbt_qt_version_name: ""
qbt_qt_version: "6"
name: "${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}libtorrent-v${{ matrix.qbt_libtorrent_version }}"
env:
qbt_build_dir: "qbt-build"
script_name: ${{ inputs.script_name }}
container_name: "multiarch"
steps:
- name: Checkout ${{ inputs.distinct_id }}
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Host - Create Docker template env file ${{ inputs.distinct_id }}
env:
set_skip_icu: ${{ inputs.icu }}
set_workflow_files: ${{ inputs.workflow-files }}
set_build_debug: ${{ inputs.debug }}
run: |
printf '%s\n' "qbt_build_dir=${{ env.qbt_build_dir }}" > env.custom
printf '%s\n' "qbt_libtorrent_version=${{ matrix.qbt_libtorrent_version }}" >> env.custom
printf '%s\n' "qbt_qt_version=${{ matrix.qbt_qt_version }}" >> env.custom
printf '%s\n' "qbt_build_tool=${{ matrix.qbt_build_tool }}" >> env.custom
printf '%s\n' "qbt_cross_name=${{ matrix.qbt_cross_name }}" >> env.custom
printf '%s\n' "qbt_patches_url=${{ github.repository }}" >> env.custom
printf '%s\n' "qbt_skip_icu=${set_skip_icu}" >> env.custom
printf '%s\n' "qbt_boost_tag=${{ matrix.qbt_boost_tag }}" >> env.custom
printf '%s\n' "qbt_libtorrent_tag=${{ matrix.qbt_libtorrent_tag }}" >> env.custom
printf '%s\n' "qbt_qt_tag=${{ matrix.qbt_qt_tag }}" >> env.custom
printf '%s\n' "qbt_qbittorrent_tag=${{ matrix.qbt_qbittorrent_tag }}" >> env.custom
printf '%s\n' "qbt_libtorrent_master_jamfile=" >> env.custom
printf '%s\n' "qbt_workflow_files=${set_workflow_files}" >> env.custom
printf '%s\n' "qbt_workflow_artifacts=" >> env.custom
printf '%s\n' "qbt_cache_dir=" >> env.custom
printf '%s\n' "qbt_optimise_strip=" >> env.custom
printf '%s\n' "qbt_build_debug=${set_build_debug}" >> env.custom
printf '%s\n' "qbt_revision_url=${{ github.repository }}" >> env.custom
printf '%s\n' "qbt_standard=" >> env.custom
printf '%s\n' "qbt_static_ish=" >> env.custom
# - name: Host - Github env to container ${{ inputs.distinct_id }}
# run: env >> env.custom
- name: Host - check stuff ${{ inputs.distinct_id }}
run: export $(cat env.custom) && bash ${script_name}
- name: Host - qBittorrent v5 transition ${{ inputs.distinct_id }}
run: |
if [[ -f "${qbt_build_dir}/release_info/disable-qt5" || -f "disable-qt5" ]]; then
printf '%s\n' "disable_qt5=yes" >> $GITHUB_ENV
printf '%s\n' "Found file: \`disable-qt5\` -> setting env: \`disable_qt5=yes\`" >> $GITHUB_STEP_SUMMARY
fi
- name: Host - phased updates ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: printf '%s\n' 'APT::Get::Always-Include-Phased-Updates "false";' | sudo tee /etc/apt/apt.conf.d/99-phased-updates
- name: Host - update ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: sudo apt-get update
# - name: Host - upgrade ${{ inputs.distinct_id }}
# run: sudo apt-get -y upgrade
- name: Host - set up qemu-user-static binfmt-support ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: sudo apt install libpipeline1 qemu-user-static binfmt-support
- name: Host - Create docker ${{ env.multiarch }} container ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: |
# We create an Alpine edge container for cross-compilation with a user named gh which has same id as runner 1001 and provide sudo access
# This way we can run commands as a non-root user, avoiding permission issues on host runner. Switching between user and root as needed.
docker run --name ${container_name} -it -d --env-file env.custom -w /home/gh -v ${{ github.workspace }}:/home/gh ${{ matrix.os_id }}:${{ matrix.os_version_id }}
# Create the user gh with the id 1001:1001 which is the same as the runner user id and group id.
docker exec ${container_name} sh -c 'adduser -h /home/gh -Ds /bin/bash -u 1001 gh && apk add sudo'
# Allow the user gh to run sudo without password prompt: docker exec -u gh:gh ${container_name} sudo ls
docker exec ${container_name} sh -c 'printf "%s" "gh ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/gh'
- name: Docker - apk update ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec ${container_name} apk update
- name: Docker - apk install bash ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec ${container_name} apk add bash
- name: Docker - Bootstrap test tools ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec ${container_name} bash ${script_name} update install_test
- name: Docker - Bootstrap core deps ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec ${container_name} bash ${script_name} install_core
- name: Docker - Bootstrap build ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} -bs-a
- name: Docker - zlib-ng ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} zlib
- name: Docker - iconv ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} iconv
- name: Docker - icu ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} icu
- name: Docker - openssl ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} openssl
- name: Docker - boost ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} boost
- name: Docker - libtorrent ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} libtorrent
- name: Docker - double_conversion ${{ inputs.distinct_id }}
if: matrix.qbt_build_tool == '' && env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} double_conversion
- name: Docker - qtbase ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} qtbase
- name: Docker - qttools ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} qttools
- name: Docker - qbittorrent ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh ${container_name} bash ${script_name} qbittorrent
- name: Docker - Set release asset name ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh -w /home/gh/${{ env.qbt_build_dir }}/completed ${container_name} mv -f qbittorrent-nox ${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox
- name: Generate artifact attestation ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
uses: actions/attest-build-provenance@v2
with:
subject-path: "${{ env.qbt_build_dir }}/completed/${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox"
- name: Docker - Release Info ${{ inputs.distinct_id }}
if: env.disable_qt5 != 'yes'
run: docker exec -u gh:gh -w /home/gh/${{ env.qbt_build_dir }}/release_info ${container_name} bash -c 'mv *.md *.json '/home/gh/${{ env.qbt_build_dir }}/completed''
- name: Host - Upload libtorrent-v${{ matrix.qbt_libtorrent_version }}-qbittorrent-nox and release info artifact ${{ inputs.distinct_id }}
if: success() && env.disable_qt5 != 'yes'
uses: actions/upload-artifact@v4
with:
name: libtorrent-v${{ matrix.qbt_libtorrent_version }}-${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox
path: |
${{ env.qbt_build_dir }}/completed/*
!${{ env.qbt_build_dir }}/completed/*.png
- name: Host - Upload cmake graphs artifact ${{ inputs.distinct_id }}
if: success() && matrix.qbt_build_tool == '' && env.disable_qt5 != 'yes'
uses: actions/upload-artifact@v4
with:
name: "${{ matrix.qbt_cross_name }}-libtorrent-v${{ matrix.qbt_libtorrent_version }}-graphs"
path: "${{ env.qbt_build_dir }}/completed/*.png"
- name: Host - Upload logs on error
if: failure() && env.disable_qt5 != 'yes'
uses: actions/upload-artifact@v4
with:
name: "${{ matrix.qbt_cross_name }}-libtorrent-v${{ matrix.qbt_libtorrent_version }}-logs"
path: "${{ env.qbt_build_dir }}/logs/*"
ci-alpine-release

The alpine release workflow

name: ci - alpine-release
on:
workflow_call:
inputs:
distinct_id:
description: "Distinct ID for the artifacts"
required: true
type: string
jobs:
release:
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
qbt_libtorrent_version: ["1.2", "2.0"]
include:
- qbt_libtorrent_version: "1.2"
preview_release: true
- qbt_libtorrent_version: "2.0"
preview_release: false
name: "Publish release libtorrent-v${{ matrix.qbt_libtorrent_version }}"
env:
qbt_build_dir: "qbt-build"
steps:
- name: Checkout ${{ inputs.distinct_id }}
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Pandoc - Bootstrap
run: |
pandoc_git_tag="$(git ls-remote -q -t --refs https://github.com/jgm/pandoc.git | awk '/tags\/[0-9]/{sub("refs/tags/", ""); print $2 }' | awk '!/^$/' | sort -rV | head -n 1)"
curl -sLo- "https://github.com/jgm/pandoc/releases/latest/download/pandoc-${pandoc_git_tag}-linux-$(dpkg --print-architecture).tar.gz" | tar xzf - --strip-components 2 -C "$(pwd)" --exclude="share"
- name: Host - Download 1.2 qbittorrent-nox artifacts ${{ inputs.distinct_id }}
uses: actions/download-artifact@v4
with:
path: "1.2"
pattern: libtorrent-v1.2-*-qbittorrent-nox
merge-multiple: true
- name: Host - Download 2.0 qbittorrent-nox artifacts ${{ inputs.distinct_id }}
uses: actions/download-artifact@v4
with:
path: "2.0"
pattern: libtorrent-v2.0-*-qbittorrent-nox
merge-multiple: true
- name: Host - merge release-info ${{ inputs.distinct_id }}
run: |
if [[ ${{ matrix.qbt_libtorrent_version }} == "1.2" ]]; then
for release in 1\.2/*-release.md; do
[[ -f "${release}" ]] && release+=("${release}")
done
revision="$(jq -r .revision 1\.2/*-dependency-version.json | head -n1)"
boost="$(jq -r .boost 1\.2/*-dependency-version.json | head -n1)"
fi
if [[ ${{ matrix.qbt_libtorrent_version }} == "2.0" ]]; then
for release in 2\.0/*-release.md; do
[[ -f "${release}" ]] && release+=("${release}")
done
revision="$(jq -r .revision 2\.0/*-dependency-version.json | head -n1)"
boost="$(jq -r .boost 2\.0/*-dependency-version.json | head -n1)"
fi
readarray -t release_sorted < <(printf '%s\n' "${release[@]}" | sort)
for dependency_version_files in 1\.2/*-dependency-version.json 2\.0/*-dependency-version.json; do
if [[ -f "${dependency_version_files}" ]]; then
sed -r 's/"boost": (.*)/BOOST_PLACEHOLDER/g' -i "${dependency_version_files}"
sed -r 's/"revision": (.*)/REVISION_PLACEHOLDER/g' -i "${dependency_version_files}"
dependency_version+=("${dependency_version_files}")
fi
done
readarray -t dependency_version_sorted < <(printf '%s\n' "${dependency_version[@]}" | sort)
paste -d '\n' "${release_sorted[@]}" | uniq | awk '!(NF && seen[$0]++) || /^>/' > "tmp-release.md"
paste -d '\n' "${dependency_version_sorted[@]}" | uniq | awk '!(NF && seen[$0]++)' > "dependency-version.json"
sed -i "s|BOOST_PLACEHOLDER|\"boost\": \"${boost}\",|" dependency-version.json
sed -i "s|REVISION_PLACEHOLDER|\"revision\": \"${revision}\"|" dependency-version.json
./pandoc --wrap=preserve -f gfm tmp-release.md -t gfm -o release.md
- name: Host - Bootstrap release tag ${{ inputs.distinct_id }}
run: printf '%s\n' "release_tag=$(cat ${{ matrix.qbt_libtorrent_version }}/tag.md)" >> $GITHUB_ENV
- name: Host - Bootstrap release title ${{ inputs.distinct_id }}
run: printf '%s\n' "release_title=$(cat ${{ matrix.qbt_libtorrent_version }}/title.md)" >> $GITHUB_ENV
- name: Host- Create release - tag - assets ${{ inputs.distinct_id }}
uses: ncipollo/release-action@v1
with:
prerelease: "${{ matrix.preview_release }}"
artifacts: "${{ matrix.qbt_libtorrent_version }}/*-qbittorrent-nox,dependency-version.json"
replacesArtifacts: true
tag: "${{ env.release_tag }}"
name: "${{ env.release_title }}"
bodyFile: "release.md"
allowUpdates: true
token: "${{ github.TOKEN }}"
ci-auto-rerun-failed-jobs-action

This workflow automatically reruns any failed jobs. Mostly targeted for the release jobs to ensure the release is created.

name: ci - auto rerun failed jobs
on:
workflow_dispatch:
inputs:
run_id:
description: "The run id of the workflow to rerun"
required: true
attempts:
description: "The number of attempts to rerun the workflow"
required: true
retries:
description: "The number of retries to rerun the workflow"
required: true
github_repo:
description: "The repository to rerun the workflow"
required: false
distinct_id:
description: "The distinct id of the workflow to rerun"
required: false
run-name: ci auto rerun failed jobs - attempt ${{ inputs.attempts }}
jobs:
gh-cli-rerun:
name: rerun - attempt ${{ inputs.attempts }}
permissions:
actions: write
runs-on: ubuntu-24.04-arm
env:
GH_TOKEN: "${{ secrets.AUTO_RERUN || github.token }}"
steps:
- name: Host - Checkout action ${{ inputs.distinct_id }}
uses: actions/checkout@v4
with:
persist-credentials: false
- uses: userdocs/gh-cli-workflow-reruns/actions/auto-rerun-failed@main
with:
run_id: ${{ inputs.run_id }}
attempts: ${{ inputs.attempts }}
retries: ${{ inputs.retries }}
github_repo: ${{ inputs.github_repo || github.repository }}
distinct_id: ${{ inputs.distinct_id || github.run_id }}