Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 41 additions & 9 deletions .github/workflows/fw-lite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -247,60 +247,80 @@
path: backend/FwLite/artifacts/publish/FwLiteWeb/*

publish-android:
name: Publish FW Lite app for Android
name: Publish FW Lite app for Android (${{ matrix.variant }})
needs: [ build-and-test, frontend ]
timeout-minutes: 30
runs-on: macos-latest
# Two bundles for one Play listing: CoreCLR (64-bit) + Mono (armeabi-v7a) — they can't share an AAB
# (a bundle boots a single runtime). 64-bit devices' abilist also includes armeabi-v7a, so they match
# BOTH bundles; the versionCode bands CoreCLR above Mono so they resolve to CoreCLR and only 32-bit-only
# devices get Mono. See create-release for the upload side.
strategy:
fail-fast: false
matrix:
include:
# RIDs come from the csproj, keyed on UseMonoRuntime (see FwLiteMaui.csproj).
- variant: coreclr
runtimeArgs: ''
versionOffset: 100000000
- variant: mono
runtimeArgs: '-p:UseMonoRuntime=true'
versionOffset: 0
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
submodules: true
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: fw-lite-viewer-js
path: ${{ env.VIEWER_BUILD_OUTPUT_DIR }}
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: '10.x'

- name: Setup Maui
run: dotnet workload install maui
- name: Compute Android version code
id: versionCode
shell: bash
run: echo "CODE=$(( ${{ matrix.versionOffset }} + ${{ github.run_number }} ))" >> ${GITHUB_OUTPUT}
- name: Decode Android Keystore
id: decodeKeystore
env:
KEYSTORE_BASE64: ${{ secrets.FW_LITE_KEYSTORE_BASE64 }}
run: |
echo "KEYSTORE_PATH=${RUNNER_TEMP}/keystore.jks" >> ${GITHUB_OUTPUT}
base64 -d <<< "$KEYSTORE_BASE64" > ${RUNNER_TEMP}/keystore.jks

- name: Publish Android
working-directory: backend/FwLite/FwLiteMaui
env:
KEYSTORE_PATH: ${{ steps.decodeKeystore.outputs.KEYSTORE_PATH }}
KEYSTORE_PASS: ${{ secrets.FW_LITE_KEYSTORE_PASS }}
KEYSTORE_ALIAS: ${{ vars.FW_LITE_KEYSTORE_UPLOAD_KEY_ALIAS }}
run: |
dotnet publish -f net10.0-android -p:BuildApple=false --artifacts-path ../artifacts \
dotnet publish -f net10.0-android -p:BuildApple=false --artifacts-path ../artifacts ${{ matrix.runtimeArgs }} \
-p:ApplicationDisplayVersion=${{ needs.build-and-test.outputs.semver-version }} \
-p:ApplicationVersion=${{ github.run_number }} \
-p:ApplicationVersion=${{ steps.versionCode.outputs.CODE }} \
-p:InformationalVersion=${{ needs.build-and-test.outputs.version }} \
-p:AndroidKeyStore=true \
-p:AndroidSigningKeyStore=${KEYSTORE_PATH} \
-p:AndroidSigningKeyPass=env:KEYSTORE_PASS \
-p:AndroidSigningStorePass=env:KEYSTORE_PASS \
-p:AndroidSigningKeyAlias=${KEYSTORE_ALIAS}
rm ${KEYSTORE_PATH}

- name: Upload FWLite App artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: fw-lite-android
name: fw-lite-android-${{ matrix.variant }}
if-no-files-found: error
# path looks like this: backend/FwLite/artifacts/publish/FwLiteMaui/release_net10.0-android/org.sil.fwlitemaui-signed.apk
path: backend/FwLite/artifacts/publish/FwLiteMaui/release_net10.0-android/*
# Single-RID (mono) publishes to release_net10.0-android_android-arm; multi-RID
# (coreclr) to release_net10.0-android. The trailing * tolerates both.
path: backend/FwLite/artifacts/publish/FwLiteMaui/release_net10.0-android*/*

publish-win:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: Publish FW Lite app for Windows
needs: [ build-and-test, frontend ]
timeout-minutes: 30
Expand Down Expand Up @@ -399,17 +419,29 @@
path: fw-lite-web-linux
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: fw-lite-android
path: fw-lite-android
name: fw-lite-android-coreclr
path: fw-lite-android-coreclr
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: fw-lite-android-mono
path: fw-lite-android-mono

- name: Zip artifacts
run: |
zip -r fw-lite-portable.zip fw-lite-portable
chmod +x fw-lite-web-linux/*/FwLiteWeb fw-lite-web-linux/*/*.sh
zip -r fw-lite-web-linux.zip fw-lite-web-linux
- name: Rename Installer
- name: Rename Installer and stage Android bundles
run: |
mv fw-lite-msix/FwLiteMaui.msixbundle fw-lite-msix/FieldWorksLiteInstaller.msixbundle
# Both variants share the org.sil.FwLiteMaui basename, so rename per arch before
# attaching (release asset names must be unique). Upload BOTH .aab to one Play release.
mkdir -p fw-lite-android
for v in coreclr mono; do
case $v in coreclr) arch=64bit ;; mono) arch=arm32 ;; esac
cp fw-lite-android-$v/*.aab fw-lite-android/FieldWorksLite-$arch.aab
cp fw-lite-android-$v/*.apk fw-lite-android/FieldWorksLite-$arch.apk 2>/dev/null || true
done

- name: Create Release
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda #v2.2.1
Expand Down
5 changes: 5 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,8 @@ tasks:
aliases: [android-release-dev]
desc: Build a Release "Dev" APK and adb-install it on the connected USB device
deps: [fw-lite:install-maui-android-release-dev]

fw-lite-android-release-arm32:
aliases: [android-release-arm32]
desc: Build & install a Release Mono arm32 (armeabi-v7a) APK on the connected USB device
deps: [fw-lite:install-maui-android-release-arm32]
6 changes: 5 additions & 1 deletion backend/FwLite/FwLiteMaui/FwLiteMaui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
<RuntimeIdentifiers Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">android-arm;android-arm64;android-x86;android-x64</RuntimeIdentifiers>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you mean to drop android-x86?

<!-- CoreCLR (the .NET 10 default Android runtime) is 64-bit only; UseMonoRuntime=true builds the 32-bit
armeabi-v7a bundle. Keep RIDs here, not on the command line — a global -p:RuntimeIdentifiers breaks
the SDK's per-RID fan-out (dependency projects don't get the singular RID). -->
<RuntimeIdentifiers Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android' And '$(UseMonoRuntime)' != 'true'">android-arm64;android-x64</RuntimeIdentifiers>
<RuntimeIdentifiers Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android' And '$(UseMonoRuntime)' == 'true'">android-arm</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<RootNamespace>FwLiteMaui</RootNamespace>
<UseMaui>true</UseMaui>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public class MauiTroubleshootingService(
[JSInvokable]
public Task<bool> GetCanShare() => Task.FromResult(true);

[JSInvokable]
public Task<string> GetProcessArchitecture() => Task.FromResult(RuntimeInfoHelper.ProcessArchitecture());

[JSInvokable]
public async Task<bool> TryOpenDataDirectory()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace FwLiteShared.Services;
public interface ITroubleshootingService
{
Task<bool> GetCanShare();
Task<string> GetProcessArchitecture();
Task<bool> TryOpenDataDirectory();
Task<string> GetDataDirectory();
Task OpenLogFile();
Expand Down
13 changes: 13 additions & 0 deletions backend/FwLite/FwLiteShared/Services/RuntimeInfoHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Runtime.InteropServices;

namespace FwLiteShared.Services;

public static class RuntimeInfoHelper
{
// On Android the process architecture reveals which bundle a device installed (Mono/arm32 vs CoreCLR/64-bit).
public static string ProcessArchitecture()
{
var bits = Environment.Is64BitProcess ? "64-bit" : "32-bit";
return $"{RuntimeInformation.ProcessArchitecture} ({bits})";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public class WebTroubleshootingService(
[JSInvokable]
public Task<bool> GetCanShare() => Task.FromResult(false);

[JSInvokable]
public Task<string> GetProcessArchitecture() => Task.FromResult(RuntimeInfoHelper.ProcessArchitecture());

[JSInvokable]
public Task<string> GetDataDirectory()
{
Expand Down
8 changes: 8 additions & 0 deletions backend/FwLite/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ tasks:
vars: { FLAVOR: 'Dev' }
- adb -d install -r bin/Release/net10.0-android/org.sil.FwLiteMaui.dev-Signed.apk

install-maui-android-release-arm32:
# Production builds are 64-bit (CoreCLR); this builds & runs the Mono/armeabi-v7a bundle to test
# the 32-bit app on a connected USB device.
desc: Build, install & run a Release Mono arm32 (armeabi-v7a) "Dev" APK on the connected USB device
dir: ./FwLiteMaui
deps: [ ui:build-viewer ]
cmd: dotnet build -f net10.0-android -c Release -t:Run -p:FwLiteFlavor=Dev -p:UseMonoRuntime=true -p:RuntimeIdentifiers=android-arm -p:RuntimeIdentifier=android-arm -p:AndroidPackageFormat=apk -p:AdbTarget=-d

build-mini-lcm-sdk:
desc: Builds the sdk, a zip with the FwLiteWeb server with a project and config to run locally
dir: ./FwLiteWeb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
export interface ITroubleshootingService
{
getCanShare() : Promise<boolean>;
getProcessArchitecture() : Promise<string>;
tryOpenDataDirectory() : Promise<boolean>;
getDataDirectory() : Promise<string>;
openLogFile() : Promise<void>;
Expand Down
12 changes: 10 additions & 2 deletions frontend/viewer/src/lib/troubleshoot/TroubleshootDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
const config = useFwLiteConfig();
let projectCode = $state<string>();
let canShare = resource(() => service, async (s) => await s?.getCanShare());
let architecture = resource(() => service, async (s) => await s?.getProcessArchitecture());
// Mobile platforms keep app data in private storage that no file manager can open.
const canOpenDataDirectory = $derived(config.os !== FwLitePlatform.Android && config.os !== FwLitePlatform.iOS);

Expand All @@ -45,7 +46,7 @@

<ResponsiveDialog bind:open={openQueryParam.current} title={$t`Troubleshoot`} disableBackHandler>
<div class="flex flex-col gap-4 items-start">
<div>
<div class="flex flex-col gap-1">
<p class="flex items-baseline gap-1">
{$t`FieldWorks Lite version`}:
<span class="font-semibold border-b">
Expand All @@ -54,15 +55,22 @@
<CopyButton
variant="ghost"
size="icon-xs"
class="-my-2 self-center"
iconProps={{class: 'size-4'}}
title={$t`Copy version`}
text={`FieldWorks Lite ${config.appVersion} on ${config.os}`}
text={`FieldWorks Lite ${config.appVersion} on ${config.os}${architecture.current ? ` (${architecture.current})` : ''}`}
/>
</p>
<p class="flex items-baseline gap-1">
{$t`Platform`}:
<span class="font-semibold">{config.os}</span>
</p>
{#if architecture.current}
<p class="flex items-baseline gap-1">
{$t`Architecture`}:
<span class="font-semibold">{architecture.current}</span>
</p>
{/if}
</div>
{#if service && (canOpenDataDirectory || $isDev)}
<div class="w-full flex flex-col gap-1.5">
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ msgstr "Any semantic domain"
msgid "Any Ws"
msgstr "Any Ws"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr "Architecture"

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/es.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "Cualquier dominio semántico"
msgid "Any Ws"
msgstr "Cualquier W"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/fr.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "N'importe quel domaine sémantique"
msgid "Any Ws"
msgstr "Tous les Systèmes d'Ecriture"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/id.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "Domain semantik apa pun"
msgid "Any Ws"
msgstr "Setiap Ws"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/ko.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "모든 시맨틱 도메인"
msgid "Any Ws"
msgstr "모든 W"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/ms.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "Mana-mana domain semantik"
msgid "Any Ws"
msgstr "Mana-mana Ws"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/sw.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "Kikoa chochote cha kumkutania"
msgid "Any Ws"
msgstr "Ws yoyote"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
4 changes: 4 additions & 0 deletions frontend/viewer/src/locales/vi.po
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ msgstr "Bất kỳ miền ngữ nghĩa nào"
msgid "Any Ws"
msgstr "Bất kỳ hệ chữ nào"

#: src/lib/troubleshoot/TroubleshootDialog.svelte
msgid "Architecture"
msgstr ""

#. Confirmation prompt
#: src/lib/entry-editor/DeleteDialog.svelte
msgid "Are you sure you want to delete {0}?"
Expand Down
Loading