Skip to content

ci: one release "latest" with stable file names and SHA256SUMS (#60) - #64

Merged
compot-dev merged 2 commits into
masterfrom
ci/release-latest
Oct 17, 2026
Merged

compot-dev merged 2 commits into
masterfrom
ci/release-latest

Conversation

@compot-dev

Copy link
Copy Markdown
Collaborator

Closes #60 (steps 1–3 and the documentation; step 4, removing the old releases, is done by hand after the first latest is checked).

Release job (.github/workflows/build.yml)

  • Runs on a push to master or a manual workflow_dispatch on master, after linux, smoke, android, windows, wine-smoke and macos. The v* tag trigger and the version input are removed, and so is the version-vs-tag check. A concurrency group cancels an older run of the same branch, so latest carries the newest commit.
  • gh release delete latest --cleanup-tag, then gh release create latest --latest --target <built commit> --title "Camex <CAMEX_VERSION>" with short notes (version, commit, pointers to CHANGELOG.md and README.md).
  • Files: camex-aarch64, camex-arm, camex-mipsel, camex-x86_64, camex-android.apk, camex-macos.zip, camex-windows-x86_64.zip, camex-windows-i686.zip, SHA256SUMS (sha256sum *, then sha256sum -c before publishing). Every file is taken by name, so the debug APK, which is now built on every run, cannot slip in. The release APK must pass apksigner verify: an unsigned one fails the job instead of being published.
  • Telegram: the APK is posted only when Camex <version> differs from the title of the current latest, as in OpenIPC/vtesse.
  • The debug APK step lost its !startsWith(github.ref, 'refs/tags/') condition (there are no tags any more).

Checked locally: the YAML parses; the "Collect release files" step was run with stub artifacts: all nine files, sha256sum -c OK, the zip contents are right (camex.exe, LICENSE, README.md, wintun.dll of the right architecture), and new is set only when the title changes.

Documentation

  • README (en/ru/zh-CN, one commit): a Permanent links section (page releases/latest, releases/latest/download/<file>, the table of files, sha256sum -c); the release badge links to releases/latest; "Versioning & Release Process" describes latest instead of tags (PR → merge → latest replaced; Telegram on a version change; manual rebuild).
  • CHANGELOG.md already names this change in 3.0.2 (docs: CHANGELOG.md with the version history (#59) #62).

After the merge

The first push to master publishes latest as Camex 3.0.2 and posts the APK to Telegram. When its files are checked (the Linux binaries on OpenIPC cameras, the APK on the TV box, SHA256SUMS), the v2.3.8, v3.0.0 and v3.0.1 releases and their tags are removed (#60 step 4); their notes are in CHANGELOG.md (#59).

Every push to master (or a manual run on master) replaces the latest
release once all build and test jobs pass: title "Camex <version>",
files camex-{aarch64,arm,mipsel,x86_64}, camex-android.apk (signed,
checked), camex-macos.zip, camex-windows-{x86_64,i686}.zip and
SHA256SUMS, checked with sha256sum -c before publishing. The v* tag
trigger and the workflow_dispatch version input are gone; a newer run
cancels an older one. The APK goes to Telegram only when the version
differs from the title of the previous latest release.

READMEs: a Permanent links section, the release badge links to
releases/latest, the release process describes latest instead of
tags.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Publish one replaceable latest release with stable assets and checksums

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace tag-based releases with a latest release after successful builds on master.
• Publish fixed-name assets and verified SHA256 checksums; reject unsigned release APKs.
• Document permanent downloads and notify Telegram only when the version changes.
Diagram

graph TD
  A["Master push or dispatch"] --> B["Build and test"] --> C["Collect named assets"] --> D["Verify APK and sums"] --> E["Replace latest"] --> F{"Version changed?"}
  F -->|yes| G["Send Telegram APK"]
  F -->|no| H["Skip notification"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Update the existing release in place
  • ➕ Avoids a period when the latest release and its downloads are unavailable.
  • ➖ Requires coordinating asset replacement with moving the latest tag to the built commit and removing obsolete assets.

Recommendation: Delete-and-recreate is straightforward and ensures the tag, notes, and exact asset set match the new build. If uninterrupted downloads become important, consider an in-place update that explicitly handles tag movement and asset cleanup.

Files changed (4) +223 / -211

Documentation (3) +147 / -66
README.mdDocument permanent downloads and the latest release process +50/-22

Document permanent downloads and the latest release process

• Adds fixed download links, an asset list, and a checksum verification example. Replaces tag-based release instructions with the merge, automatic publication, Telegram, and manual rebuild workflow.

README.md

README.ru.mdDocument permanent releases in Russian +49/-22

Document permanent releases in Russian

• Adds the localized asset links and checksum instructions. Updates versioning and release guidance to describe the single latest release instead of version tags.

README.ru.md

README.zh-CN.mdDocument permanent releases in Simplified Chinese +48/-22

Document permanent releases in Simplified Chinese

• Adds the localized asset links and checksum instructions. Updates versioning and release guidance to describe the single latest release instead of version tags.

README.zh-CN.md

Other (1) +76 / -145
build.ymlReplace tag releases with a verified latest release +76/-145

Replace tag releases with a verified latest release

• Releases now run after successful build and test jobs on pushes or manual runs on master, with older runs on the same branch and event canceled. The job selects fixed-name assets, verifies the release APK and checksums, recreates 'latest' on the built commit, and posts to Telegram only for a changed version.

.github/workflows/build.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. An older rebuild can overwrite latest ✓ Resolved
Description
The concurrency group includes github.event_name, so push and workflow_dispatch runs targeting
master neither cancel nor serialize each other. If a manual run builds an older commit but
publishes after a newer push, the release command targets the manual run’s GITHUB_SHA and leaves
latest pointing to that older commit.
Code

.github/workflows/build.yml[R25-27]

+concurrency:
+  group: build-${{ github.event_name }}-${{ github.ref }}
+  cancel-in-progress: true
Evidence
Both push and manual dispatch runs can reach the release job on master, but including the event
name gives them different concurrency keys. The release targets the publishing run’s GITHUB_SHA,
so an older manual run that reaches the create command last can become the final latest release.

Publish a single latest release from successful master builds
.github/workflows/build.yml[25-27]
.github/workflows/build.yml[530-536]
.github/workflows/build.yml[15-27]
.github/workflows/build.yml[464-466]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Manual rebuilds and pushes to `master` use different concurrency groups, allowing an older manual build to publish after a newer push and replace `latest`.
## Fix Focus Areas
- .github/workflows/build.yml[23-27]
- .github/workflows/build.yml[530-536]
## Recommended Fix
Use a concurrency group based on the workflow branch or ref without `github.event_name`, so push and manual runs targeting the same branch share a group and can cancel one another. Keep runs for different branches or refs appropriately separated, and ensure an older commit cannot be published over a newer `master` release.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. A failed replacement removes all downloads 🐞 Bug ☼ Reliability
Description
The publish step deletes the existing release and tag before attempting a single `gh release
create`, with no recovery if creation fails. A cancellation or API failure after deletion leaves the
documented latest downloads unavailable until another run succeeds.
Code

.github/workflows/build.yml[R530-531]

+          gh release delete latest --repo "$GITHUB_REPOSITORY" --cleanup-tag --yes 2>/dev/null || true
+          gh release create latest release/* \
Evidence
The workflow allows in-progress cancellation; the publish step deletes latest with
--cleanup-tag, ignores deletion errors, and has only one subsequent create attempt. There is no
rollback or retry after deletion.

.github/workflows/build.yml[23-27]
.github/workflows/build.yml[525-536]
README.md[82-106]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Deleting the existing release before its replacement succeeds can leave the permanent downloads absent.
## Fix Focus Areas
- .github/workflows/build.yml[530-536]
- .github/workflows/build.yml[25-27]
## Recommended Fix
Publish using an update or staged replacement that preserves the existing release on failure. Do not suppress unexpected deletion errors, and account for cancellation during publication.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Unchanged versions can reach Telegram ✓ Resolved
Description
The gh release view command converts every lookup error into an empty PREV, which makes the
comparison set new=true. If the lookup fails transiently but publication later succeeds, an
unchanged version's APK is posted to Telegram.
Code

.github/workflows/build.yml[R489-491]

+          PREV=$(gh release view latest --repo "$GITHUB_REPOSITORY" --json name -q .name 2>/dev/null || true)
+          echo "Previous release: ${PREV:-none}"
+          [ "$PREV" = "Camex $VER" ] || echo "new=true" >> "$GITHUB_OUTPUT"
Evidence
The suppressed lookup failure produces an empty previous title, the next line marks it new, and that
output alone gates the Telegram step. Release deletion and creation are separate later commands, so
a transient read failure need not stop publication.

.github/workflows/build.yml[485-491]
.github/workflows/build.yml[528-554]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A failed release lookup is treated as proof that the version is new, triggering an unnecessary Telegram post.
## Fix Focus Areas
- .github/workflows/build.yml[489-491]
## Recommended Fix
Set `new=true` for a confirmed missing release or a successfully read, different title. Fail or retry on other lookup errors rather than treating them as an empty title.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. A published version can miss Telegram ✓ Resolved
Description
The Telegram step runs after latest is recreated, while cancel-in-progress can stop the run
between those steps. If the next push retains the same version, its comparison sees that version
already published and does not recover the missed APK post.
Code

.github/workflows/build.yml[R541-542]

    - name: Telegram
-        if: steps.release.outputs.new == 'true'
+        if: steps.vars.outputs.new == 'true'
Evidence
Same-group pushes cancel runs in progress. The workflow publishes before running Telegram, and a
later run decides whether to post solely from the already-published release title, with no record of
whether the earlier post completed.

.github/workflows/build.yml[23-27]
.github/workflows/build.yml[489-491]
.github/workflows/build.yml[530-554]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cancellation after publication but before Telegram can permanently skip a version announcement.
## Fix Focus Areas
- .github/workflows/build.yml[25-27]
- .github/workflows/build.yml[489-491]
- .github/workflows/build.yml[530-554]
## Recommended Fix
Prevent cancellation during the publish-and-notify sequence, or track successful notifications separately so a subsequent run can retry an unannounced version.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can see how risky a PR is at a glance with blast radius labels

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml Outdated
Comment on lines +530 to +531
gh release delete latest --repo "$GITHUB_REPOSITORY" --cleanup-tag --yes 2>/dev/null || true
gh release create latest release/* \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. A failed replacement removes all downloads 🐞 Bug ☼ Reliability

The publish step deletes the existing release and tag before attempting a single `gh release
create`, with no recovery if creation fails. A cancellation or API failure after deletion leaves the
documented latest downloads unavailable until another run succeeds.
Agent Prompt
## Issue description
Deleting the existing release before its replacement succeeds can leave the permanent downloads absent.
## Fix Focus Areas
- .github/workflows/build.yml[530-536]
- .github/workflows/build.yml[25-27]
## Recommended Fix
Publish using an update or staged replacement that preserves the existing release on failure. Do not suppress unexpected deletion errors, and account for cancellation during publication.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml Outdated
…f-way

Review on #64:
- master runs share one concurrency group (pushes and manual runs) and
  are queued instead of cancelled, so a run is never stopped between
  deleting and recreating latest, nor before its Telegram post;
- the release job checks that the built commit is still the head of
  master and skips publishing otherwise (an older manual run);
- gh release create is retried three times, removing a half-created
  release between attempts;
- only 'release not found' counts as no previous release; any other
  lookup error fails the job instead of posting an unchanged version.
@compot-dev

Copy link
Copy Markdown
Collaborator Author

Thanks, @qodo. All four addressed in bcdd010:

  1. An older rebuild can overwrite latest: fixed. The concurrency group is build-<ref>, shared by pushes and manual runs on master. The release job also checks git ls-remote origin refs/heads/master against GITHUB_SHA and does not publish if master has moved on.
  2. A failed replacement removes all downloads: on master, cancel-in-progress is now false, so a run is no longer cancelled between the delete and the create. gh release create is retried 3 times; a half-created release is removed between attempts. If all three fail, the job fails loudly, and the next run (or a manual run) publishes again.
  3. Unchanged versions can reach Telegram: fixed. Only release not found means "no previous release"; any other error from gh release view fails the job before anything is published or posted.
  4. A published version can miss Telegram: with no cancellation on master, a run is no longer stopped between the publish and the post, which was the case described. A Telegram API failure is still only a warning, as in vtesse and before this PR. Re-posting from a later run would need state outside the release and is not worth it for a notification.

Checked with stubbed gh/git: first release (new=true), same title (no post), an API error (job fails), a stale commit (skip), create failing twice and then succeeding (3 attempts, deletes in between), create always failing (exit 1).

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Thanks, @qodo. All four addressed in bcdd010:

  1. An older rebuild can overwrite latest: fixed. The concurrency group is build-<ref>, shared by pushes and manual runs on master. The release job also checks git ls-remote origin refs/heads/master against GITHUB_SHA and does not publish if master has moved on.
  2. A failed replacement removes all downloads: on master, cancel-in-progress is now false, so a run is no longer cancelled between the delete and the create. gh release create is retried 3 times; a half-created release is removed between attempts. If all three fail, the job fails loudly, and the next run (or a manual run) publishes again.
  3. Unchanged versions can reach Telegram: fixed. Only release not found means "no previous release"; any other error from gh release view fails the job before anything is published or posted.
  4. A published version can miss Telegram: with no cancellation on master, a run is no longer stopped between the publish and the post, which was the case described. A Telegram API failure is still only a warning, as in vtesse and before this PR. Re-posting from a later run would need state outside the release and is not worth it for a notification.

Checked with stubbed gh/git: first release (new=true), same title (no post), an API error (job fails), a stale commit (skip), create failing twice and then succeeding (3 attempts, deletes in between), create always failing (exit 1).

Thanks for the detailed verification. Based on the changes described in bcdd010, findings 1–4 are addressed:

  • finding 1: shared ref-based concurrency plus the stale-master guard prevents older builds from publishing.
  • finding 2: master runs are not cancelled during publication, and release creation is retried.
  • finding 3: only a confirmed missing release or a different title triggers the Telegram notification.
  • finding 4: disabling cancellation on master prevents interruption between publication and notification.

The remaining Telegram API failure behavior is an acknowledged best-effort notification policy rather than a release correctness issue.

@compot-dev
compot-dev merged commit 40c5c1c into master Oct 17, 2026
16 checks passed
@compot-dev
compot-dev deleted the ci/release-latest branch October 17, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release: one release "latest" titled "Camex <version>", stable file names, SHA256SUMS

1 participant