Compare commits

..
Author SHA1 Message Date
james.maesandClaude Sonnet 5.5 f2870107e8 ci: run lintRelease and assembleRelease (unsigned) on every push
android / android (push) Failing after 3m25s
build / android (push) Failing after 3m25s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 07:23:37 -06:00
6 changed files with 7 additions and 94 deletions

No files matched your search

+1
View File
@@ -15,5 +15,6 @@ jobs:
tasks: |
testDebugUnitTest :scanner-host:lintDebug :scanner-host:assembleDebug
-PscannerTestBuildType=qa :scanner-host:assembleQa :scanner-host:assembleQaAndroidTest
:scanner-host:lintRelease :scanner-host:assembleRelease -PscannerVersionName=0.0.0-ci -PscannerVersionCode=1
emulator-tasks: connectedDebugAndroidTest
publish-tasks: :qqq:android:core:publishAllPublicationsToGiteaRepository :qqq:android:mobileapp:publishAllPublicationsToGiteaRepository
+2
View File
@@ -23,6 +23,8 @@ Build only after PRs 17 and 18 are merged to `main` and this branch has been reb
./gradlew --no-daemon --console=plain :scanner-host:lintRelease :scanner-host:assembleRelease -PscannerVersionName=1.0.2 -PscannerVersionCode=4
```
CI (`.gitea/workflows/build.yaml`) already runs `:scanner-host:lintRelease :scanner-host:assembleRelease` with throwaway version values on every push, so a release-variant lint or build break fails the pull request. It builds the unsigned release variant only, holds no signing material, and its APK is not a release candidate: the signed APK is still produced by the manual isolated `apksigner` step below.
Then sign `scanner-host/build/outputs/apk/release/scanner-host-release-unsigned.apk` in isolation with `apksigner` using the existing permanent key (certificate SHA-256 `BDDB2D2F...5B17A9FF`), verify v2+v3 with exactly one signer, 16 KiB alignment, `versionCode='4' versionName='1.0.2'` and non-debuggable, and write `QQQ-Mobile-1.0.2.apk`, `SHA256SUMS`, `signature-verification.txt`, `apk-metadata.txt` and `release-receipt.json` next to each other as for 1.0.1. Record the resulting hashes here, in [INSTALLATION](INSTALLATION.md) and in [SESSION-STATE](SESSION-STATE.md) before any tag or release.
## Historical status (November 4, 2026, superseded where noted)
@@ -27,9 +27,8 @@ import kotlinx.serialization.Serializable
*******************************************************************************/
@Serializable
data class QHelpContent(
val content: String? = null, // QQQ core now sends summary-only help entries
val summary: String? = null,
val format: String? = null,
val content: String,
val format: String,
val roles: Set<String> = emptySet(),
val contentAsText: String? = null
) {
@@ -23,7 +23,6 @@
package com.kingsrook.qqq.frontend.android.core.model.metadata
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
/*******************************************************************************
*******************************************************************************/
@@ -31,7 +30,7 @@ import kotlinx.serialization.json.JsonElement
data class QWidgetMetaData(
val name: String,
val label: String? = null,
val icon: JsonElement? = null, // QQQ core serializes a widget icon as a plain string (e.g. "troubleshoot"); tolerate an object too
val icon: QIcon? = null,
val type: String,
) {
}
@@ -1,88 +0,0 @@
/*
* SPDX-License-Identifier: Apache-2.0
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2004-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.kingsrook.qqq.frontend.android.core.model.metadata
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
/*******************************************************************************
** Current QQQ core serializes widget icons as plain strings and sends
** summary-only help entries; the decoder must accept those shapes and the
** older object/content shapes.
*******************************************************************************/
class MetadataDecodingTest {
private val json = Json { ignoreUnknownKeys = true }
/*******************************************************************************
**
*******************************************************************************/
@Test
fun widgetIconAsStringDecodes() {
val widget = json.decodeFromString<QWidgetMetaData>("""{"name":"w","type":"table","icon":"troubleshoot"}""")
assertEquals(JsonPrimitive("troubleshoot"), widget.icon)
}
/*******************************************************************************
**
*******************************************************************************/
@Test
fun widgetIconAsObjectStillDecodes() {
val widget = json.decodeFromString<QWidgetMetaData>("""{"name":"w","type":"table","icon":{"name":"troubleshoot"}}""")
assertTrue(widget.icon is JsonObject)
}
/*******************************************************************************
**
*******************************************************************************/
@Test
fun widgetWithoutIconDecodes() {
assertNull(json.decodeFromString<QWidgetMetaData>("""{"name":"w","type":"table"}""").icon)
}
/*******************************************************************************
**
*******************************************************************************/
@Test
fun summaryOnlyHelpDecodes() {
val help = json.decodeFromString<QHelpContent>("""{"summary":"short help","roles":["ALL_SCREENS"]}""")
assertEquals("short help", help.summary)
assertNull(help.content)
assertNull(help.format)
}
/*******************************************************************************
**
*******************************************************************************/
@Test
fun contentHelpStillDecodes() {
val help = json.decodeFromString<QHelpContent>("""{"content":"body","format":"TEXT"}""")
assertEquals("body", help.content)
assertEquals("TEXT", help.format)
assertNull(help.summary)
}
}
@@ -128,7 +128,7 @@ fun ProcessComponentEditForm(processViewModel: ProcessViewModel, component: QFro
OutlinedButton(onClick = { processViewModel.loadRemoteOptions(field) }) { Text("Retry choices") }
}
semanticFieldHint(field)?.let { Text(it) }
field.helpContents.orEmpty().forEach { Text(it.contentAsText ?: it.content?.let { c -> plainText(c) } ?: it.summary.orEmpty()) }
field.helpContents.orEmpty().forEach { Text(it.contentAsText ?: plainText(it.content)) }
}
}
}