From 2d9d139021464d9eed4fb0add1f5e4720d8e2694 Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Tue, 13 Oct 2026 19:30:53 +0000 Subject: [PATCH 1/5] ci: a release of its own for every 2.xx version, with the APK and the changes since the previous one (#157) Co-Authored-By: Claude --- .github/workflows/build.yml | 32 +++++++++++++++++++++++++++++++- LABORATORY.md | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4e350d3d..1fb29b0e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,14 +119,44 @@ jobs: tag_name: latest files: ${{ steps.vars.outputs.name }}.apk + # v2: every version gets a release of its own (#157), at the commit that was built, with + # the APK and the merged PRs since the previous v2.* release. A push without a version + # bump (docs, CI) only updates "latest" above. Not marked Latest: the README links + # releases/tag/latest + - name: Versioned release + if: github.ref_name == 'v2' + env: + GH_TOKEN: ${{ github.token }} + run: | + VER=$(sed -n 's/.*versionName "\(.*\)".*/\1/p' android/app/build.gradle) + TAG="v${VER}" + if [ -z "$VER" ]; then echo "No versionName found"; exit 1; fi + if gh release view "$TAG" >/dev/null 2>&1; then + echo "Release $TAG exists: only latest was updated" + exit 0 + fi + cp decoder-v2.apk "decoder-v2-${VER}.apk" + PREV=$(gh release list --limit 100 --json tagName \ + -q '[.[].tagName | select(startswith("v2."))][0] // ""') + if [ -n "$PREV" ]; then + NOTES=(--generate-notes --notes-start-tag "$PREV") + else + NOTES=(--notes "First release of the 2.xx line (v2 branch). Earlier changes: https://github.com/${{ github.repository }}/commits/v2") + fi + gh release create "$TAG" "decoder-v2-${VER}.apk" --target "$(git rev-parse HEAD)" \ + --title "OpenIPC Decoder ${VER}" --latest=false "${NOTES[@]}" + - name: Telegram if: github.ref_name == 'basic' || github.ref_name == 'v2' run: | + # basic keeps its Gradle files at the top level + VER=$(sed -n 's/.*versionName "\(.*\)".*/\1/p' android/app/build.gradle app/build.gradle \ + 2>/dev/null | head -1) GIT_HASH=$(git rev-parse --short HEAD) GIT_BRANCH="${{ github.ref_name }}" TAG_NAME="${{ github.ref_type == 'tag' && github.ref_name || '' }}" TIME=$(date -u '+%Y-%m-%d %H:%M:%S UTC') - TG_MSG="Commit: ${GIT_HASH}\nBranch: ${GIT_BRANCH}\nTag: ${TAG_NAME}\nTime: ${TIME}\n\n" + TG_MSG="Version: ${VER}\nCommit: ${GIT_HASH}\nBranch: ${GIT_BRANCH}\nTag: ${TAG_NAME}\nTime: ${TIME}\n\n" TG_ICON="\xE2\x9C\x85 GitHub Actions" TG_HEADER=$(echo -e ${TG_MSG}${TG_ICON}) TG_TOKEN=${{secrets.TELEGRAM_TOKEN_BOT_OPENIPC}} diff --git a/LABORATORY.md b/LABORATORY.md index 60ddd2e0..041633bd 100644 --- a/LABORATORY.md +++ b/LABORATORY.md @@ -205,6 +205,7 @@ $ADB shell getprop ro.product.model # AG-216 ### Builds - `v2` / `basic`: `gh release download latest -R OpenIPC/decoder -p decoder-v2.apk -p decoder.apk` +- A given 2.xx version: `gh release download v2.01 -R OpenIPC/decoder` (`decoder-v2-2.01.apk`). CI creates the release `v` on the first v2 build of each version (#157). - Branches: `build.yml` builds signed APKs only on **push** runs (not `pull_request`); artifact name is `decoder-`: ```sh B=feat/rtsp-multicast From c70d041bc1d86e1ae84f7f309b7f9c4431fd05a1 Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Tue, 13 Oct 2026 19:31:07 +0000 Subject: [PATCH 2/5] docs: versioned releases in the READMEs (#157) Co-Authored-By: Claude --- README.md | 2 ++ README.ru.md | 2 ++ README.zh-CN.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 4700ac87..21cf5b8f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Ready-to-install builds are in the [**latest** release](https://github.com/OpenI | `decoder-v2.apk` | **Current version**, recommended | | `decoder.apk` | Legacy version from the `basic` branch | +Each 2.xx version also has a release of its own — [`v2.01`](https://github.com/OpenIPC/decoder/releases/tag/v2.01) and later, on the [releases page](https://github.com/OpenIPC/decoder/releases) — with that version's APK and the changes since the previous one. + A beta channel is available on [Google Play](https://play.google.com/apps/internaltest/4701231141824898735). To join it, send your e-mail through the [OpenIPC Telegram channels](https://openipc.org/our-channels). ## Features diff --git a/README.ru.md b/README.ru.md index 6921d4f6..9b52f265 100644 --- a/README.ru.md +++ b/README.ru.md @@ -24,6 +24,8 @@ | `decoder-v2.apk` | **Текущая версия**, рекомендуется | | `decoder.apk` | Старая версия из ветки `basic` | +У каждой версии 2.xx есть и свой релиз — [`v2.01`](https://github.com/OpenIPC/decoder/releases/tag/v2.01) и далее, на [странице релизов](https://github.com/OpenIPC/decoder/releases) — с APK этой версии и списком изменений после предыдущей. + Есть бета-канал в [Google Play](https://play.google.com/apps/internaltest/4701231141824898735). Чтобы попасть в него, пришлите свой e-mail через [Telegram-каналы OpenIPC](https://openipc.org/our-channels). ## Возможности diff --git a/README.zh-CN.md b/README.zh-CN.md index 63903723..fe88c83a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -24,6 +24,8 @@ | `decoder-v2.apk` | **当前版本**,推荐使用 | | `decoder.apk` | `basic` 分支的旧版本 | +每个 2.xx 版本另有独立的发布 — [`v2.01`](https://github.com/OpenIPC/decoder/releases/tag/v2.01) 及之后的版本,见[发布页面](https://github.com/OpenIPC/decoder/releases) — 包含该版本的 APK 以及相对上一版本的更改。 + [Google Play](https://play.google.com/apps/internaltest/4701231141824898735) 上提供测试渠道。如需加入,请通过 [OpenIPC Telegram 频道](https://openipc.org/our-channels)发送您的电子邮箱。 ## 功能 From ee30456d47edef31834c8c42951a8e29a2047fe8 Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Tue, 13 Oct 2026 19:34:03 +0000 Subject: [PATCH 3/5] =?UTF-8?q?ci:=20Qodo=20on=20#158=20=E2=80=94=20the=20?= =?UTF-8?q?previous=20version=20by=20number,=20a=20release=20created=20mea?= =?UTF-8?q?nwhile=20by=20another=20build=20is=20fine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- .github/workflows/build.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1fb29b0e..764587a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -136,15 +136,21 @@ jobs: exit 0 fi cp decoder-v2.apk "decoder-v2-${VER}.apk" - PREV=$(gh release list --limit 100 --json tagName \ - -q '[.[].tagName | select(startswith("v2."))][0] // ""') + # The previous version by number, not by date: a manual build of an older commit may + # publish after a newer one (Qodo on #158) + PREV=$(gh release list --limit 200 --json tagName -q '.[].tagName' \ + | grep '^v2\.' | sed 's/^v//' | { cat; echo "$VER"; } | sort -V -u \ + | grep -B1 -x "$VER" | grep -v -x "$VER" || true) if [ -n "$PREV" ]; then - NOTES=(--generate-notes --notes-start-tag "$PREV") + NOTES=(--generate-notes --notes-start-tag "v$PREV") else NOTES=(--notes "First release of the 2.xx line (v2 branch). Earlier changes: https://github.com/${{ github.repository }}/commits/v2") fi - gh release create "$TAG" "decoder-v2-${VER}.apk" --target "$(git rev-parse HEAD)" \ - --title "OpenIPC Decoder ${VER}" --latest=false "${NOTES[@]}" + if ! gh release create "$TAG" "decoder-v2-${VER}.apk" --target "$(git rev-parse HEAD)" \ + --title "OpenIPC Decoder ${VER}" --latest=false "${NOTES[@]}"; then + # Another build of the same version created it meanwhile: fine + gh release view "$TAG" >/dev/null && echo "Release $TAG created by another build" + fi - name: Telegram if: github.ref_name == 'basic' || github.ref_name == 'v2' From ba0b088b4e16bdbf46fd28d08e4b21b65e0bf72b Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Tue, 13 Oct 2026 19:36:40 +0000 Subject: [PATCH 4/5] =?UTF-8?q?ci:=20Qodo=20on=20#158=20=E2=80=94=20Telegr?= =?UTF-8?q?am=20reads=20only=20the=20Gradle=20file=20that=20exists,=20a=20?= =?UTF-8?q?release=20without=20its=20APK=20gets=20it=20uploaded,=20all=20v?= =?UTF-8?q?2=20tags=20count=20for=20the=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- .github/workflows/build.yml | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 764587a4..1f733e73 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -131,33 +131,47 @@ jobs: VER=$(sed -n 's/.*versionName "\(.*\)".*/\1/p' android/app/build.gradle) TAG="v${VER}" if [ -z "$VER" ]; then echo "No versionName found"; exit 1; fi + APK="decoder-v2-${VER}.apk" + cp decoder-v2.apk "$APK" + # An existing release is complete only with its APK (Qodo on #158) + has_apk() { + gh release view "$TAG" --json assets -q '.assets[].name' 2>/dev/null | grep -qx "$APK" + } if gh release view "$TAG" >/dev/null 2>&1; then - echo "Release $TAG exists: only latest was updated" + if has_apk; then + echo "Release $TAG exists: only latest was updated" + else + echo "Release $TAG has no $APK: uploading it" + gh release upload "$TAG" "$APK" --clobber + fi exit 0 fi - cp decoder-v2.apk "decoder-v2-${VER}.apk" # The previous version by number, not by date: a manual build of an older commit may # publish after a newer one (Qodo on #158) - PREV=$(gh release list --limit 200 --json tagName -q '.[].tagName' \ - | grep '^v2\.' | sed 's/^v//' | { cat; echo "$VER"; } | sort -V -u \ + PREV=$(gh api --paginate "repos/${{ github.repository }}/releases?per_page=100" \ + -q '.[].tag_name' | grep '^v2\.' | sed 's/^v//' | { cat; echo "$VER"; } | sort -V -u \ | grep -B1 -x "$VER" | grep -v -x "$VER" || true) if [ -n "$PREV" ]; then NOTES=(--generate-notes --notes-start-tag "v$PREV") else NOTES=(--notes "First release of the 2.xx line (v2 branch). Earlier changes: https://github.com/${{ github.repository }}/commits/v2") fi - if ! gh release create "$TAG" "decoder-v2-${VER}.apk" --target "$(git rev-parse HEAD)" \ + if ! gh release create "$TAG" "$APK" --target "$(git rev-parse HEAD)" \ --title "OpenIPC Decoder ${VER}" --latest=false "${NOTES[@]}"; then - # Another build of the same version created it meanwhile: fine - gh release view "$TAG" >/dev/null && echo "Release $TAG created by another build" + # Another build of the same version created it meanwhile, or the upload failed: + # the release must exist and carry the APK, else the step fails + gh release view "$TAG" >/dev/null + has_apk || gh release upload "$TAG" "$APK" --clobber fi - name: Telegram if: github.ref_name == 'basic' || github.ref_name == 'v2' run: | - # basic keeps its Gradle files at the top level - VER=$(sed -n 's/.*versionName "\(.*\)".*/\1/p' android/app/build.gradle app/build.gradle \ - 2>/dev/null | head -1) + # basic keeps its Gradle files at the top level; read only the one that exists + # (a missing file would fail the step under pipefail, Qodo on #158) + GRADLE=android/app/build.gradle + [ -f "$GRADLE" ] || GRADLE=app/build.gradle + VER=$(sed -n 's/.*versionName "\(.*\)".*/\1/p' "$GRADLE" | head -1) GIT_HASH=$(git rev-parse --short HEAD) GIT_BRANCH="${{ github.ref_name }}" TAG_NAME="${{ github.ref_type == 'tag' && github.ref_name || '' }}" From 9956b174e5b44591644ff550cf567d6ba9938e2f Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Tue, 13 Oct 2026 19:40:15 +0000 Subject: [PATCH 5/5] =?UTF-8?q?ci:=20Qodo=20on=20#158=20=E2=80=94=20only?= =?UTF-8?q?=20the=20build=20of=20the=20tagged=20commit=20adds=20a=20missin?= =?UTF-8?q?g=20APK;=20a=20failed=20release=20lookup=20fails=20the=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- .github/workflows/build.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f733e73..8cc7d9da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -137,20 +137,30 @@ jobs: has_apk() { gh release view "$TAG" --json assets -q '.assets[].name' 2>/dev/null | grep -qx "$APK" } + # Only the build of the tagged commit may add the missing APK (Qodo on #158) + repair_apk() { + TAGGED=$(gh api "repos/${{ github.repository }}/commits/$TAG" -q .sha) + if [ "$TAGGED" = "$(git rev-parse HEAD)" ]; then + gh release upload "$TAG" "$APK" --clobber + else + echo "::warning::$TAG has no $APK and points to ${TAGGED:0:7}, not this build" + fi + } if gh release view "$TAG" >/dev/null 2>&1; then if has_apk; then echo "Release $TAG exists: only latest was updated" else - echo "Release $TAG has no $APK: uploading it" - gh release upload "$TAG" "$APK" --clobber + repair_apk fi exit 0 fi # The previous version by number, not by date: a manual build of an older commit may # publish after a newer one (Qodo on #158) - PREV=$(gh api --paginate "repos/${{ github.repository }}/releases?per_page=100" \ - -q '.[].tag_name' | grep '^v2\.' | sed 's/^v//' | { cat; echo "$VER"; } | sort -V -u \ - | grep -B1 -x "$VER" | grep -v -x "$VER" || true) + # A failed lookup fails the step rather than publishing first-release notes + TAGS=$(gh api --paginate "repos/${{ github.repository }}/releases?per_page=100" \ + -q '.[].tag_name') + PREV=$(printf '%s\n' "$TAGS" | grep '^v2\.' | sed 's/^v//' | { cat; echo "$VER"; } \ + | sort -V -u | grep -B1 -x "$VER" | grep -v -x "$VER" || true) if [ -n "$PREV" ]; then NOTES=(--generate-notes --notes-start-tag "v$PREV") else @@ -161,7 +171,7 @@ jobs: # Another build of the same version created it meanwhile, or the upload failed: # the release must exist and carry the APK, else the step fails gh release view "$TAG" >/dev/null - has_apk || gh release upload "$TAG" "$APK" --clobber + has_apk || repair_apk fi - name: Telegram