address review: guard the release pipeline and harden the metadata test

- fail the release before uploading to Play when the versionCode has no
  changelog
- rely on supply's defaults for release_status and metadata_path
- enforce 8-bit PNGs and reject tRNS chunks in MetadataConstraintsTest
- name the nightly artifact's APKs distinctly
- fix doc drift and the stale libraries uniqueId
This commit is contained in:
austinried
2026-10-08 18:53:00 +00:00
parent b0a0bc1867
commit 44bc3f927e
5 changed files with 42 additions and 7 deletions
+8 -2
View File
@@ -20,9 +20,15 @@ jobs:
- uses: ./.github/actions/build
with:
gradle-args: ":app:assembleDebug :app:assembleNightly --no-daemon"
- name: Collect
run: |
cp app/build/outputs/apk/debug/app-debug.apk subtracks-nightly-debug.apk
cp app/build/outputs/apk/nightly/app-nightly.apk subtracks-nightly.apk
- name: Checksums
run: sha256sum app/build/outputs/apk/debug/app-debug.apk app/build/outputs/apk/nightly/app-nightly.apk
run: sha256sum subtracks-nightly.apk subtracks-nightly-debug.apk
- uses: actions/upload-artifact@v7
with:
name: subtracks-nightly
path: app/build/outputs/apk
path: |
subtracks-nightly.apk
subtracks-nightly-debug.apk
+2 -2
View File
@@ -68,6 +68,8 @@ jobs:
else
echo "value=false" >> "$GITHUB_OUTPUT"
fi
- name: Check release notes exist
run: test -f "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
- name: Upload to Google Play
if: steps.stable.outputs.value == 'true'
env:
@@ -79,9 +81,7 @@ jobs:
--aab dist/bundle/release/app-release.aab \
--package_name com.subtracks \
--track "${{ vars.PLAY_TRACK || 'internal' }}" \
--release_status completed \
--json_key "$RUNNER_TEMP/play.json" \
--metadata_path metadata \
--mapping dist/mapping/release/mapping.txt
- name: Remove Play service account
if: always()
+1 -1
View File
@@ -1,5 +1,5 @@
{
"uniqueId": "com.subtracks:next",
"uniqueId": "com.subtracks",
"name": "Subtracks",
"description": "Subtracks itself, a client for Subsonic-compatible servers.",
"website": "https://github.com/austinried/subtracks",
@@ -66,6 +66,7 @@ class MetadataConstraintsTest {
RGBA_COLOR_TYPE,
image.colorType,
)
assertTrue("${file.path} must be an 8-bit PNG; bitDepth ${image.bitDepth}", image.bitDepth == 8)
assertTrue("${file.path} must be at most 1 MiB", file.length() <= 1024 * 1024)
}
}
@@ -119,6 +120,8 @@ class MetadataConstraintsTest {
RGB_COLOR_TYPE,
image.colorType,
)
assertTrue("${file.path} must be an 8-bit PNG; bitDepth ${image.bitDepth}", image.bitDepth == 8)
assertTrue("${file.path} must not carry a tRNS (transparency) chunk", !image.hasTrns)
}
ImageFormat.JPEG -> {
@@ -156,6 +159,8 @@ class MetadataConstraintsTest {
val width: Int,
val height: Int,
val colorType: Int?,
val bitDepth: Int?,
val hasTrns: Boolean,
val format: ImageFormat,
)
@@ -166,12 +171,33 @@ class MetadataConstraintsTest {
width = bytes.beInt(PNG_WIDTH_OFFSET),
height = bytes.beInt(PNG_HEIGHT_OFFSET),
colorType = bytes[PNG_COLOR_TYPE_OFFSET].toInt() and 0xFF,
bitDepth = bytes[PNG_BIT_DEPTH_OFFSET].toInt() and 0xFF,
hasTrns = bytes.hasTrnsChunk(),
format = ImageFormat.PNG,
)
}
val image = ImageIO.read(file)
assertTrue("unreadable image ${file.path}", image != null)
return ImageInfo(image.width, image.height, colorType = null, format = if (bytes.isJpeg()) ImageFormat.JPEG else ImageFormat.OTHER)
return ImageInfo(
image.width,
image.height,
null,
null,
hasTrns = false,
format = if (bytes.isJpeg()) ImageFormat.JPEG else ImageFormat.OTHER,
)
}
private fun ByteArray.hasTrnsChunk(): Boolean {
var offset = PNG_SIGNATURE_SIZE
while (offset + 8 <= size) {
val length = beInt(offset)
val type = String(this, offset + 4, 4, Charsets.US_ASCII)
if (type == "tRNS") return true
if (type == "IEND" || length < 0) return false
offset += PNG_CHUNK_OVERHEAD + length
}
return false
}
private fun ByteArray.isPng(): Boolean =
@@ -208,6 +234,9 @@ class MetadataConstraintsTest {
const val PNG_WIDTH_OFFSET = 16
const val PNG_HEIGHT_OFFSET = 20
const val PNG_BIT_DEPTH_OFFSET = 24
const val PNG_COLOR_TYPE_OFFSET = 25
const val PNG_SIGNATURE_SIZE = 8
const val PNG_CHUNK_OVERHEAD = 12
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ keytool -printcert -jarfile app-release.aab
## `nightly` builds
The signing key is the committed [`app/debug.keystore`](../app/debug.keystore) (a public key: alias `androiddebugkey`, password `android`). Certificate SHA-256:
The signing key is the committed [`app/debug.keystore`](../app/debug.keystore) (a public key: alias `androiddebugkey`, password `android`). The `subtracks-nightly` artifact contains `subtracks-nightly.apk` (minified) and `subtracks-nightly-debug.apk` (debuggable), both `com.subtracks.nightly`. Certificate SHA-256:
```
# keytool format