# @credits https://github.com/c0re100/qBittorrent-Enhanced-Edition
name: matrix multi build and release - artifacts
description: "Distinct id"
description: "Skip rerun?"
description: "Number of rerun retries"
options: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
group: ${{ github.workflow }}-${{ github.ref }}
- uses: actions/checkout@v4
sudo apt-get install jq -y
mapfile -t assets < <(curl -sL "https://api.github.com/repos/userdocs/qbt-workflow-files/releases/latest" | jq -r ".assets | .[].browser_download_url")
for urls in "${assets[@]}"; do
- uses: actions/upload-artifact@v4
# qbt_cross_name: [armel,armhf,armv7,aarch64,x86_64,x86,s390x,powerpc,ppc64el,mips,mipsel,mips64,mips64el,riscv64]
qbt_cross_name: ["armhf", "armv7", "aarch64", "x86_64", "x86"]
qbt_libtorrent_version: ["1.2", "2.0"]
qbt_build_tool: ["", "qmake"]
- qbt_build_tool: "qmake"
qbt_qt_version_name: "qt5-"
name: "${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}libtorrent-v${{ matrix.qbt_libtorrent_version }}"
qbt_build_dir: "qbt-build"
- name: Checkout ${{ github.event.inputs.distinct_id }}
uses: actions/checkout@v4
- name: Host - phased updates ${{ github.event.inputs.distinct_id }}
run: printf '%s\n' 'APT::Get::Always-Include-Phased-Updates "false";' | sudo tee /etc/apt/apt.conf.d/99-phased-updates
- name: Host - update ${{ github.event.inputs.distinct_id }}
- name: Host - upgrade ${{ github.event.inputs.distinct_id }}
run: sudo apt-get -y upgrade
- name: Host - set up qemu-user-static binfmt-support ${{ github.event.inputs.distinct_id }}
run: sudo apt install libpipeline1 qemu-user-static binfmt-support
- name: Host - create build dir ${{ github.event.inputs.distinct_id }}
run: mkdir -p ${{ env.qbt_build_dir }}
- name: Host - Download all artifacts ${{ github.event.inputs.distinct_id }}
uses: actions/download-artifact@v4
- name: Host - organise libtorrent files ${{ github.event.inputs.distinct_id }}
libtorrent_1_2_github_tag="$(git ls-remote -q -t --refs "https://github.com/arvidn/libtorrent.git" | awk '/\/v1/{sub("refs/tags/", "");sub("(.*)(-[^0-9].*)(.*)", ""); print $2 }' | awk '!/^$/' | sort -rV | head -n1)"
libtorrent_2_0_github_tag="$(git ls-remote -q -t --refs "https://github.com/arvidn/libtorrent.git" | awk '/\/v2/{sub("refs/tags/", "");sub("(.*)(-[^0-9].*)(.*)", ""); print $2 }' | awk '!/^$/' | sort -rV | head -n1)"
if [[ "${{ matrix.qbt_libtorrent_version }}" =~ ^1\.2 ]]; then
mv -f artifact/libtorrent.${libtorrent_1_2_github_tag/v/}.tar.xz artifact/libtorrent.tar.xz
rm -f artifact/libtorrent.${libtorrent_2_0_github_tag/v/}.tar.xz
if [[ "${{ matrix.qbt_libtorrent_version }}" =~ ^2\.0 ]]; then
mv -f artifact/libtorrent.${libtorrent_2_0_github_tag/v/}.tar.xz artifact/libtorrent.tar.xz
rm -f artifact/libtorrent.${libtorrent_1_2_github_tag/v/}.tar.xz
- name: Host - organise qt files ${{ github.event.inputs.distinct_id }}
if [[ "${{ matrix.qbt_qt_version }}" =~ ^5 ]]; then
mv -f artifact/qt5base.tar.xz artifact/qtbase.tar.xz
mv -f artifact/qt5tools.tar.xz artifact/qttools.tar.xz
rm -f artifact/qt6base.tar.xz artifact/qt6tools.tar.xz
if [[ "${{ matrix.qbt_qt_version }}" =~ ^6 ]]; then
mv -f artifact/qt6base.tar.xz artifact/qtbase.tar.xz
mv -f artifact/qt6tools.tar.xz artifact/qttools.tar.xz
rm -f artifact/qt5base.tar.xz artifact/qt5tools.tar.xz
- name: Host - Move organised artifacts to ${{ env.qbt_build_dir }} ${{ github.event.inputs.distinct_id }}
run: mv -f artifact/* ${{ env.qbt_build_dir }}
- name: Host - Display structure of downloaded artifacts workspace ${{ github.event.inputs.distinct_id }}
- name: Host - Create Docker template env file ${{ github.event.inputs.distinct_id }}
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=yes" >> env.custom
printf '%s\n' "qbt_boost_tag=" >> env.custom
printf '%s\n' "qbt_libtorrent_tag=" >> env.custom
printf '%s\n' "qbt_qt_tag=" >> env.custom
printf '%s\n' "qbt_qbittorrent_tag=" >> env.custom
printf '%s\n' "qbt_libtorrent_master_jamfile=no" >> env.custom
printf '%s\n' "qbt_workflow_files=no" >> env.custom
printf '%s\n' "qbt_workflow_artifacts=yes" >> env.custom
printf '%s\n' "qbt_cache_dir=" >> env.custom
printf '%s\n' "qbt_optimise_strip=yes" >> env.custom
printf '%s\n' "qbt_build_debug=no" >> env.custom
printf '%s\n' "qbt_revision_url=${{ github.repository }}" >> env.custom
printf '%s\n' "qbt_standard=17" >> env.custom
printf '%s\n' "qbt_static_ish=no" >> env.custom
- name: Host - Create docker multiarch container ${{ github.event.inputs.distinct_id }}
run: docker run --name multiarch -it -d --env-file env.custom -w /root -v ${{ github.workspace }}:/root ${{ matrix.os_id }}:${{ matrix.os_version_id }}
- name: Docker - apk update ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch apk update
- name: Docker - apk install bash ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch apk add bash
- name: Docker - Bootstrap ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh -bs-a
- name: Docker - Copy repo patches to build folder ${{ github.event.inputs.distinct_id }}
run: if [[ -d patches ]]; then docker exec -w /root multiarch cp -r patches/* /root/${{ env.qbt_build_dir }}/patches; fi
- name: Docker - zlib-ng ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh zlib
- name: Docker - iconv ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh iconv
- name: Docker - icu ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh icu
- name: Docker - openssl ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh openssl
- name: Docker - boost ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh boost
- name: Docker - libtorrent ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh libtorrent
- name: Docker - double_conversion ${{ github.event.inputs.distinct_id }}
if: matrix.qbt_build_tool == 'cmake'
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh double_conversion
- name: Docker - qtbase ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh qtbase
- name: Docker - qttools ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh qttools
- name: Docker - qbittorrent ${{ github.event.inputs.distinct_id }}
run: docker exec -w /root multiarch bash qbittorrent-nox-static.sh qbittorrent
- name: Host - qBittorrent v5 transition
# When qBittorrent v5 is released, remove this
if [[ -f ${{ env.qbt_build_dir }}/release_info/disable-qt5 ]]; then
printf '%s\n' "disable_qt5=yes" >> $GITHUB_ENV
- name: Docker - Set release asset name ${{ github.event.inputs.distinct_id }}
if: env.disable_qt5 != 'yes' # When qBittorrent v5 is released, remove this
run: docker exec -w /root/${{ env.qbt_build_dir }}/completed multiarch mv -f qbittorrent-nox ${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox
- name: Generate artifact attestation ${{ github.event.inputs.distinct_id }}
if: env.disable_qt5 != 'yes' # When qBittorrent v5 is released, remove this
uses: actions/attest-build-provenance@v2
subject-path: "${{ env.qbt_build_dir }}/completed/${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox"
- name: Docker - Release Info ${{ github.event.inputs.distinct_id }}
if: env.disable_qt5 != 'yes' # When qBittorrent v5 is released, remove this
run: docker exec -w /root/${{ env.qbt_build_dir }}/release_info multiarch bash -c 'mv *.md *.json '/root/${{ env.qbt_build_dir }}/completed''
# - name: Docker - upx compression ${{ github.event.inputs.distinct_id }}
# docker exec -w /root multiarch apk add upx
# docker exec -w /root/${{ env.qbt_build_dir }}/completed multiarch upx --brute --no-lzma ${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox
- name: Host - Upload libtorrent-v${{ matrix.qbt_libtorrent_version }}-qbittorrent-nox and release info artifact ${{ github.event.inputs.distinct_id }}
if: env.disable_qt5 != 'yes' # When qBittorrent v5 is released, remove this
uses: actions/upload-artifact@v4
name: libtorrent-v${{ matrix.qbt_libtorrent_version }}-${{ matrix.qbt_cross_name }}-${{ matrix.qbt_qt_version_name }}qbittorrent-nox
${{ env.qbt_build_dir }}/completed/*
!${{ env.qbt_build_dir }}/completed/*.png
- name: Host - Upload cmake graphs artifact ${{ github.event.inputs.distinct_id }}
if: matrix.qbt_build_tool == 'cmake' && env.disable_qt5 != 'yes' # When qBittorrent v5 is released, remove this
uses: actions/upload-artifact@v4
name: "${{ matrix.qbt_cross_name }}-libtorrent-v${{ matrix.qbt_libtorrent_version }}-graphs"
path: "${{ env.qbt_build_dir }}/completed/*.png"
if: always() && contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
qbt_libtorrent_version: ["1.2", "2.0"]
- qbt_libtorrent_version: "1.2"
- qbt_libtorrent_version: "2.0"
name: "Publish release libtorrent-v${{ matrix.qbt_libtorrent_version }}"
qbt_build_dir: "qbt-build"
- name: Checkout ${{ github.event.inputs.distinct_id }}
uses: actions/checkout@v4
- name: Pandoc - Bootstrap
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-amd64.tar.gz" | tar xzf - --strip-components 2 -C "$(pwd)" --exclude="share"
- name: Host - Download 1.2 qbittorrent-nox artifacts ${{ github.event.inputs.distinct_id }}
uses: actions/download-artifact@v4
pattern: libtorrent-v1.2-*-qbittorrent-nox
- name: Host - Download 2.0 qbittorrent-nox artifacts ${{ github.event.inputs.distinct_id }}
uses: actions/download-artifact@v4
pattern: libtorrent-v2.0-*-qbittorrent-nox
- name: Host - merge release-info ${{ github.event.inputs.distinct_id }}
if [[ ${{ matrix.qbt_libtorrent_version }} == "1.2" ]]; then
for release in 1\.2/*-release.md; do
[[ -f "${release}" ]] && release+=("${release}")
revision="$(jq -r .revision 1\.2/*-dependency-version.json | head -n1)"
if [[ ${{ matrix.qbt_libtorrent_version }} == "2.0" ]]; then
for release in 2\.0/*-release.md; do
[[ -f "${release}" ]] && release+=("${release}")
revision="$(jq -r .revision 2\.0/*-dependency-version.json | head -n1)"
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/"revision": (.*)/PLACEHOLDER/g' -i "${dependency_version_files}"
dependency_version+=("${dependency_version_files}")
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|PLACEHOLDER|\"revision\": \"${revision}\"|" dependency-version.json
./pandoc --wrap=preserve -f gfm tmp-release.md -t gfm -o release.md
- name: Host - Bootstrap release tag ${{ github.event.inputs.distinct_id }}
run: printf '%s\n' "release_tag=$(cat ${{ matrix.qbt_libtorrent_version }}/tag.md)" >> $GITHUB_ENV
- name: Host - Bootstrap release title ${{ github.event.inputs.distinct_id }}
run: printf '%s\n' "release_title=$(cat ${{ matrix.qbt_libtorrent_version }}/title.md)" >> $GITHUB_ENV
- name: Host- Create release - tag - assets ${{ github.event.inputs.distinct_id }}
uses: ncipollo/release-action@v1
prerelease: "${{ matrix.preview_release }}"
artifacts: "${{ matrix.qbt_libtorrent_version }}/*-qbittorrent-nox,dependency-version.json"
tag: "${{ env.release_tag }}"
name: "${{ env.release_title }}"
token: "${{ github.TOKEN }}"
if: failure() && inputs.skip_rerun == '0'
GH_TOKEN: "${{ github.TOKEN }}"
- uses: actions/checkout@v4
- name: Trigger rerun workflow on job failures
inputs_retries="${{ inputs.retries }}"
gh workflow run rerun.yml -f run_id=${{ github.run_id }} -f attempts=${{ github.run_attempt }} -f retries=${inputs_retries:-1}