| name | fdroid-merge-request-debugging |
| description | Use when preparing, debugging, or responding to F-Droid fdroiddata merge requests, especially app inclusion metadata, reproducible Binaries checks, checkupdates/autoupdate fixes, reviewer discussion resolution, and GitLab pipeline triage. |
| version | 1.0.2 |
| metadata | {"hermes":{"tags":["fdroid","android","reproducible-builds","gitlab","release"]}} |
F-Droid Merge Request Debugging
Use this skill for fdroiddata merge requests where the goal is a reviewer-ready
app inclusion or update. Start from the live GitLab MR and the real pipeline
state, not from stale screenshots.
Preflight
- Identify the fdroiddata worktree, app id, MR iid, source branch, and upstream
app repo/tag/commit.
- Check both worktrees before editing and record whether either tree already
has unrelated local changes:
git -C C:\path\to\fdroiddata status --short --branch
git -C C:\path\to\app status --short --branch
- Pull live reviewer discussions and current MR metadata:
$glab = "C:\Users\Ady\AppData\Local\Programs\glab\glab.exe"
& $glab api 'projects/fdroid%2Ffdroiddata/merge_requests/<iid>/discussions?per_page=100'
& $glab api 'projects/fdroid%2Ffdroiddata/merge_requests/<iid>'
- Record every unresolved resolvable discussion id, comment body, file, line,
and suggested change. Do not resolve threads until the branch is pushed and
CI confirms the fix.
- If screenshots were provided, treat them as hints only. Re-query GitLab
before editing because labels, discussions, pipelines, and suggested changes
can change quickly during F-Droid review.
For Hermes Agent on the known Windows workstation, the usual paths are:
$HermesRepo = "C:\Users\Ady\work\hermes-agent-fdroid-release"
$FdroidData = "C:\Users\Ady\work\fdroiddata"
$AppId = "com.nousresearch.hermesagent"
Metadata Rules
- Keep edits scoped to
metadata/<appid>.yml.
- Prefer reviewer suggestions when they are valid F-Droid metadata.
- Use full commit hashes in
Builds.commit, not tags.
- Set
subdir to the Gradle module that produces the build directory.
- Avoid custom
output unless F-Droid cannot discover the APK.
- Keep
Binaries and AllowedAPKSigningKeys for reproducible builds when the
upstream release publishes a comparable APK.
- Do not run
fdroid rewritemeta casually; it can rewrite formatting and create
unrelated churn. If CI requests it, inspect the diff before committing.
- If CI's
fdroid rewritemeta job fails with a tiny formatter diff, apply the
formatter output exactly even when local fdroid lint reports a cosmetic
warning. The GitLab formatter job is the source of truth for merge readiness.
Hermes Agent hit this with a Binaries: multiline scalar where the formatter
required a trailing space after the colon.
- Use unrestricted
UpdateCheckMode: Tags unless there is a strong reason to
filter tags. If version codes cannot be derived from a single regex capture,
add a small upstream version metadata file and use UpdateCheckData to read
both versionCode and versionName from each tag.
Hermes Agent uses:
UpdateCheckMode: Tags
UpdateCheckData: fdroid/com.nousresearch.hermesagent.version|versionCode=(\d+)|.|versionName=(.*)
Reproducible APK Checks
Before pushing a binary metadata update, verify the release APK independently:
Get-FileHash -Algorithm SHA256 -LiteralPath .\tmp\app.apk
& "$AndroidSdk\build-tools\35.0.0\apksigner.bat" verify --print-certs .\tmp\app.apk
For Chaquopy-based Android apps, also check that generated Python assets are
stable:
- no
*.dist-info/direct_url.json
- inner
requirements-*.imy entries use ZIP_STORED
- inner ZIP timestamps are normalized when the app normalizer requires it
build.json references the actual requirements-*.imy SHA-1
If fdroid build compiles the source successfully but then fails while
retrieving Binaries, check the upstream release asset URL before changing
metadata. A 404 for a URL like
https://github.com/<owner>/<repo>/releases/download/v%v/<apk-name>.apk means
the source build may already be good and the failure is the missing signed
reference APK. Fix the upstream GitHub Release workflow or upload process,
verify the APK URL returns 200 OK, then retry or retrigger the GitLab MR
pipeline. Do not leave the MR red when the only failure is a delayed release
asset upload.
If GitHub Actions API calls are rate-limited, avoid gh run watch loops. Poll
the release asset URL with Invoke-WebRequest -Method Head, then download the
.sha256 sidecar and APK directly and verify Get-FileHash against the
sidecar. This proves the binary is published without burning GitHub API quota.
For GitHub release workflows that remain queued on hosted runners, first check
the job labels and whether a repo variable such as HERMES_ANDROID_RUNNER
overrides runs-on. A temporary self-hosted runner can unblock a release asset
upload only when the repo secrets stay in GitHub Actions and the runner is
ephemeral, non-root on Linux, and deregisters after one job. Remove any temporary
runner override variable immediately after the release job completes.
Local Validation
Run the cheap local gates before commit:
git diff --check
fdroid lint <appid>
fdroid checkupdates --allow-dirty <appid>
On Windows, fdroidserver may fail after successful checkupdates processing
while writing status output if local config expects Unix tools such as rsync.
Treat that as an environment issue only if verbose output already proves the
app version was detected and the GitLab checkupdates job later passes.
Local lint can also be weaker than the MR pipeline. Always monitor GitLab before
claiming the MR is ready.
Commit, Push, and Pipeline
Use noninteractive git commands. If Git opens a browser or credential selector,
configure the repo or host before retrying; do not rely on manual clicks.
git add metadata/<appid>.yml
git commit -m "Address <app> review comments"
git push origin <branch>
Make the Windows host noninteractive before long unattended runs:
git config --global credential.interactive false
git config --global credential.guiPrompt false
git config --global credential.gitLabAuthModes pat
[Environment]::SetEnvironmentVariable("GIT_TERMINAL_PROMPT","0","User")
[Environment]::SetEnvironmentVariable("GCM_INTERACTIVE","never","User")
[Environment]::SetEnvironmentVariable("GCM_GUI_PROMPT","0","User")
$env:GIT_TERMINAL_PROMPT = "0"
$env:GCM_INTERACTIVE = "never"
$env:GCM_GUI_PROMPT = "0"
git push --dry-run origin HEAD:<branch>
For GitHub source repos on this Windows machine, prefer the GitHub CLI
credential helper over Git Credential Manager prompts:
gh auth status
gh auth setup-git --hostname github.com
git config --local --unset-all credential.helper 2>$null
git config --local --add credential.helper ""
git config --local --add credential.helper "!gh auth git-credential"
git config --local credential.interactive never
git config --local credential.useHttpPath true
For GitLab/fdroiddata pushes, do not assume the cached Git credential is also a
REST API token. If git push works but GitLab API calls return 401 Unauthorized, keep the branch and pipeline updated and give the exact reviewer
reply text for the user to paste.
A push with no new commit may not update an MR description even when push
options are supplied. If only the MR description needs correction, prefer the
GitLab UI/API when authenticated. If API auth is unavailable and the correction
is important, use a no-content git commit --amend --no-edit, read the current
remote branch hash with git ls-remote, then push with an explicit
--force-with-lease=refs/heads/<branch>:<remote-hash> and updated push-option
description. Never use an unconditional force push.
Monitor the source-project pipeline, then inspect failed job logs directly:
& $glab api 'projects/<user>%2Ffdroiddata/pipelines?ref=<branch>&per_page=5'
& $glab api 'projects/<user>%2Ffdroiddata/pipelines/<pipeline-id>/jobs?per_page=100'
& $glab api 'projects/<user>%2Ffdroiddata/jobs/<job-id>/trace'
Only declare the MR ready when the relevant jobs pass: fdroid build,
check apk when Binaries is present, checkupdates, fdroid lint,
fdroid rewritemeta, schema validation, source checks, git redirect, and tools
checks.
If the first pipeline after a version bump passes every job except
fdroid rewritemeta, fetch the raw job log and apply the Linux formatter diff
exactly. On Hermes Agent this can intentionally leave Binaries: with a
single trailing space; local Windows git diff --check will complain, but the
GitLab fdroid rewritemeta job is the merge gate.
Reviewer Response
After CI is green:
- Update the MR description with current version, version code, source commit,
metadata commit, binary URL, binary digest, signing key, autoupdate behavior,
and latest passing pipeline.
- Reply directly to reviewer questions with the concrete fix and pipeline URL.
- Resolve only the threads whose requested changes are actually handled.
- Re-query the MR to verify:
unresolved_resolvable = 0
pipeline = success
detailed_merge_status = mergeable
blocking_discussions_resolved = True
If a maintainer-owned label such as waiting-on-response does not change via
API, leave a clear comment and rely on the resolved threads plus green pipeline.
When a reviewer asks whether UpdateCheckMode: Tags is limited to a specific
version line, answer from the metadata: unrestricted Tags is not limited to
0.13.x; any future semver tag can be detected when UpdateCheckData reads
versionName and versionCode from the upstream tag.
If the browser still says "Please register or sign in to reply", do not claim a
thread reply was posted. Put the evidence in the MR description, keep CI green,
and give the user the exact reviewer reply text to paste.
Completion Audit
Before final handoff, map every reviewer note and user request to evidence:
- metadata diff for each reviewer suggestion
- local validation output
- binary digest and signing key evidence when applicable
- GitLab pipeline URL and job statuses
- discussion ids resolved or intentionally left open
- clean worktree status for fdroiddata and the upstream app repo