mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
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:
@@ -20,9 +20,15 @@ jobs:
|
|||||||
- uses: ./.github/actions/build
|
- uses: ./.github/actions/build
|
||||||
with:
|
with:
|
||||||
gradle-args: ":app:assembleDebug :app:assembleNightly --no-daemon"
|
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
|
- 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
|
- uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: subtracks-nightly
|
name: subtracks-nightly
|
||||||
path: app/build/outputs/apk
|
path: |
|
||||||
|
subtracks-nightly.apk
|
||||||
|
subtracks-nightly-debug.apk
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "value=false" >> "$GITHUB_OUTPUT"
|
echo "value=false" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
- name: Check release notes exist
|
||||||
|
run: test -f "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
|
||||||
- name: Upload to Google Play
|
- name: Upload to Google Play
|
||||||
if: steps.stable.outputs.value == 'true'
|
if: steps.stable.outputs.value == 'true'
|
||||||
env:
|
env:
|
||||||
@@ -79,9 +81,7 @@ jobs:
|
|||||||
--aab dist/bundle/release/app-release.aab \
|
--aab dist/bundle/release/app-release.aab \
|
||||||
--package_name com.subtracks \
|
--package_name com.subtracks \
|
||||||
--track "${{ vars.PLAY_TRACK || 'internal' }}" \
|
--track "${{ vars.PLAY_TRACK || 'internal' }}" \
|
||||||
--release_status completed \
|
|
||||||
--json_key "$RUNNER_TEMP/play.json" \
|
--json_key "$RUNNER_TEMP/play.json" \
|
||||||
--metadata_path metadata \
|
|
||||||
--mapping dist/mapping/release/mapping.txt
|
--mapping dist/mapping/release/mapping.txt
|
||||||
- name: Remove Play service account
|
- name: Remove Play service account
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"uniqueId": "com.subtracks:next",
|
"uniqueId": "com.subtracks",
|
||||||
"name": "Subtracks",
|
"name": "Subtracks",
|
||||||
"description": "Subtracks itself, a client for Subsonic-compatible servers.",
|
"description": "Subtracks itself, a client for Subsonic-compatible servers.",
|
||||||
"website": "https://github.com/austinried/subtracks",
|
"website": "https://github.com/austinried/subtracks",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class MetadataConstraintsTest {
|
|||||||
RGBA_COLOR_TYPE,
|
RGBA_COLOR_TYPE,
|
||||||
image.colorType,
|
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)
|
assertTrue("${file.path} must be at most 1 MiB", file.length() <= 1024 * 1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,6 +120,8 @@ class MetadataConstraintsTest {
|
|||||||
RGB_COLOR_TYPE,
|
RGB_COLOR_TYPE,
|
||||||
image.colorType,
|
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 -> {
|
ImageFormat.JPEG -> {
|
||||||
@@ -156,6 +159,8 @@ class MetadataConstraintsTest {
|
|||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val colorType: Int?,
|
val colorType: Int?,
|
||||||
|
val bitDepth: Int?,
|
||||||
|
val hasTrns: Boolean,
|
||||||
val format: ImageFormat,
|
val format: ImageFormat,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -166,12 +171,33 @@ class MetadataConstraintsTest {
|
|||||||
width = bytes.beInt(PNG_WIDTH_OFFSET),
|
width = bytes.beInt(PNG_WIDTH_OFFSET),
|
||||||
height = bytes.beInt(PNG_HEIGHT_OFFSET),
|
height = bytes.beInt(PNG_HEIGHT_OFFSET),
|
||||||
colorType = bytes[PNG_COLOR_TYPE_OFFSET].toInt() and 0xFF,
|
colorType = bytes[PNG_COLOR_TYPE_OFFSET].toInt() and 0xFF,
|
||||||
|
bitDepth = bytes[PNG_BIT_DEPTH_OFFSET].toInt() and 0xFF,
|
||||||
|
hasTrns = bytes.hasTrnsChunk(),
|
||||||
format = ImageFormat.PNG,
|
format = ImageFormat.PNG,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val image = ImageIO.read(file)
|
val image = ImageIO.read(file)
|
||||||
assertTrue("unreadable image ${file.path}", image != null)
|
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 =
|
private fun ByteArray.isPng(): Boolean =
|
||||||
@@ -208,6 +234,9 @@ class MetadataConstraintsTest {
|
|||||||
|
|
||||||
const val PNG_WIDTH_OFFSET = 16
|
const val PNG_WIDTH_OFFSET = 16
|
||||||
const val PNG_HEIGHT_OFFSET = 20
|
const val PNG_HEIGHT_OFFSET = 20
|
||||||
|
const val PNG_BIT_DEPTH_OFFSET = 24
|
||||||
const val PNG_COLOR_TYPE_OFFSET = 25
|
const val PNG_COLOR_TYPE_OFFSET = 25
|
||||||
|
const val PNG_SIGNATURE_SIZE = 8
|
||||||
|
const val PNG_CHUNK_OVERHEAD = 12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ keytool -printcert -jarfile app-release.aab
|
|||||||
|
|
||||||
## `nightly` builds
|
## `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
|
# keytool format
|
||||||
|
|||||||
Reference in New Issue
Block a user