Addresses all 5 critical gaps identified in review.
| Issue | v1 (broken) | v2 (fixed) |
|---|---|---|
| Per-build tags | Renamed but still created | Deleted after release creation — zero tag accumulation |
| Build index | None | gh-pages JSON index — queryable by device/ROM |
| Reproducibility | Manifest = docs only | Pinned deps + SHA256 checksums |
| VERSION sync | Manual | CI-enforced — build fails on mismatch |
| Latest stable API | Human-only RELEASE.md | Machine-readable JSON endpoint |
- Tag cleanup: Keep existing 3,087 tags, delete them, or archive to a file then delete?
- Android 13: Include in upgrades or treat as frozen/legacy?
- GitHub Pages: Is
gh-pagesbranch acceptable for hosting the build index, or prefer a different hosting?
3.0.0
Reads VERSION file, exports PATCH_ENGINE_VERSION. Sourced by all patcher scripts.
generate_manifest() function that produces build-manifest.json:
{
"schema_version": "1.0",
"patch_engine_version": "3.0.0",
"git_commit": "ebbf4e1",
"device": "vermeer",
"base_rom": "OS3.0.7.0",
"android_version": "15",
"api_level": "35",
"features": ["disable_signature_verification"],
"build_time": "2026-04-26T12:00:00Z",
"checksums": {
"module_zip": "sha256:abc123...",
"framework_patched": "sha256:def456..."
},
"tool_versions": {
"apktool": "2.9.3"
},
"workflow_run_id": "12345678"
}Key additions vs v1: SHA256 checksums of all outputs + tool version pins.
- Import
manifest.sh - Call
generate_manifest()increate_module() - Embed manifest at
META-INF/build-manifest.jsoninside module ZIP - Compute SHA256 of module ZIP after creation, update manifest
- Source
version.shat top - Print engine version in startup banner
- Pass metadata through to
create_module()
{
"apktool": {
"version": "2.9.3",
"url": "https://github.com/iBotPeaches/Apktool/releases/download/v2.9.3/apktool_2.9.3.jar",
"sha256": "<actual hash>"
}
}- Read tool URL + hash from
tools/versions.lock - Verify SHA256 after download (fail build if mismatch)
- Pin
actions/checkout@v4,actions/upload-artifact@v4(already pinned ✓)
- Pin kaorios_toolbox submodule to a specific commit
Pattern: create tag → create release → delete tag.
The release persists after tag deletion. No tag accumulation.
- name: Set Release Info
id: release_info
run: |
RELEASE_TAG="build-tmp-${{ github.run_id }}"
RELEASE_NAME="Android 15 | ${{ steps.set_codename.outputs.codename }} | ${{ github.event.inputs.version_name }}"
echo "tag=${RELEASE_TAG}" >> $GITHUB_OUTPUT
echo "name=${RELEASE_NAME}" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.release_info.outputs.tag }}
name: ${{ steps.release_info.outputs.name }}
files: |
${{ steps.find_zip.outputs.file_path }}
build-manifest.json
- name: Delete temporary tag
if: success()
run: git push --delete origin "${{ steps.release_info.outputs.tag }}" || trueResult: releases exist with assets, zero new tags in the repo.
Triggered after each build workflow completes (via workflow_run). Updates builds/index.json on the gh-pages branch.
Index structure:
{
"schema_version": "1.0",
"last_updated": "2026-04-26T12:00:00Z",
"engine_version": "3.0.0",
"builds": {
"vermeer": {
"OS3.0.7.0": {
"android_version": "15",
"release_url": "https://github.com/.../releases/12345",
"download_url": "https://github.com/.../download/...",
"patch_version": "3.0.0",
"features": ["disable_signature_verification"],
"build_time": "2026-04-26T12:00:00Z",
"checksum": "sha256:abc123..."
}
}
}
}Queryable at: https://frameworksforge.github.io/FrameworkPatcher/builds/index.json
This workflow:
- Checks out
gh-pages - Downloads manifest artifact from the triggering run
- Merges into
index.json(upsert by device+ROM key) - Commits and pushes
{
"engine_version": "3.0.0",
"release_url": "https://github.com/FrameworksForge/FrameworkPatcher/releases/tag/v3.0.0",
"release_date": "2026-04-26",
"supported_android": ["13", "14", "15", "16"]
}Updated by the engine release workflow (Component 7). Machine-readable endpoint at:
https://frameworksforge.github.io/FrameworkPatcher/builds/latest.json
Human-readable pointer to current stable, features, and how to get builds.
- Manual trigger with
versioninput - Validates
VERSIONfile matches input - Creates SemVer Git tag (
v3.0.0) — these are the only tags - Creates GitHub Release marked as "Latest"
- Updates
builds/latest.jsonongh-pages
Add a new job version-check that runs on every push/PR:
version-check:
name: Version consistency check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate VERSION matches CHANGELOG
run: |
VERSION=$(cat VERSION)
if ! grep -q "## \[${VERSION}\]" CHANGELOG.md; then
echo "❌ VERSION file ($VERSION) has no matching entry in CHANGELOG.md"
exit 1
fi
echo "✅ VERSION $VERSION matches CHANGELOG.md"Fails the build if VERSION and CHANGELOG.md drift.
Add [3.0.0] section documenting the platform upgrade.
Remove duplicated properties (lines 16-24 are exact copies of 7-15).
1. VERSION + version.sh (pure additive)
2. tools/versions.lock (pure additive)
3. manifest.sh (pure additive)
4. Update module.sh (backward compatible)
5. Update patcher scripts (backward compatible)
6. Update CI workflows (⚡ behavioral change — tags)
7. Build index workflow (new workflow)
8. Engine release workflow (new workflow)
9. Version check in test suite (CI guard)
10. CHANGELOG + module.prop fixes (cleanup)
Steps 1-5 are safe and non-breaking. Step 6 is the critical behavioral change.
- YAML validation — parse all workflow files
- Local patcher test — run with test JARs, verify manifest in ZIP
- Version check — verify CI catches VERSION/CHANGELOG mismatch
- Workflow dry-run — trigger a test build, verify:
- No new Git tag persists after release
- Release has manifest + ZIP attached
- Build index updates on gh-pages
- Feature test suite — existing tests still pass