Compare commits

...
2 Commits
Author SHA1 Message Date
Thomas SharedInboxandClaude Sonnet 4.6 99b6027faf feat: inject GIT_HASH into Dagger Android/Linux builds so About page shows git hash (#249)
BuildAndroidRelease, BuildAndroidApk, and BuildLinuxRelease in ci/main.go
now accept an optional commitHash parameter and pass it as
--dart-define=GIT_HASH=<hash> to flutter, matching what the local fvm build
tasks already do. PublishAndroid and DeployApk/DeployLinux thread the hash
through. Taskfile publish-android and build-android-bundle tasks are updated
to capture and forward the current HEAD hash.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:08:24 +02:00
Thomas SharedInboxandClaude Sonnet 4.6 004aa9e837 fix: disable Save button when no password available, fix changelog fetch-depth (#246, #229)
- Disable Save button (alongside Try connection) when no stored password
  and password field is empty, making both buttons consistent (#246)
- Update deploy-apk and build-linux CI jobs to use fetch-depth: 100 so
  generate-changelog produces a full 50-entry log, matching deploy-playstore (#229)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 14:42:46 +02:00
5 changed files with 73 additions and 14 deletions
+2 -2
View File
@@ -136,7 +136,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 1 fetch-depth: 100
- name: Check runner tools - name: Check runner tools
run: | run: |
@@ -178,7 +178,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 1 fetch-depth: 100
- name: Check runner tools - name: Check runner tools
run: | run: |
+2 -2
View File
@@ -224,7 +224,7 @@ tasks:
desc: Build AAB via Dagger (cached, versionCode=1 placeholder) and export locally desc: Build AAB via Dagger (cached, versionCode=1 placeholder) and export locally
cmds: cmds:
- mkdir -p build/app/outputs/bundle/release - mkdir -p build/app/outputs/bundle/release
- dagger call --progress=plain -q -m ci --source=. build-android-release -o build/app/outputs/bundle/release/app-release.aab - HASH=$(git rev-parse --short HEAD) && dagger call --progress=plain -q -m ci --source=. build-android-release --commit-hash "$HASH" -o build/app/outputs/bundle/release/app-release.aab
upload-android-bundle: upload-android-bundle:
desc: Upload AAB from build/ to Play Store via Dagger desc: Upload AAB from build/ to Play Store via Dagger
@@ -247,7 +247,7 @@ tasks:
- sh: test -n "$ANDROID_KEYSTORE_PASSWORD" - sh: test -n "$ANDROID_KEYSTORE_PASSWORD"
msg: "ANDROID_KEYSTORE_PASSWORD is not set" msg: "ANDROID_KEYSTORE_PASSWORD is not set"
cmds: cmds:
- dagger call --progress=plain -q -m ci --source=. publish-android --play-store-config env:PLAY_STORE_CONFIG_JSON --keystore-base64 env:ANDROID_KEYSTORE_BASE64 --keystore-password env:ANDROID_KEYSTORE_PASSWORD - HASH=$(git rev-parse --short HEAD) && dagger call --progress=plain -q -m ci --source=. publish-android --play-store-config env:PLAY_STORE_CONFIG_JSON --keystore-base64 env:ANDROID_KEYSTORE_BASE64 --keystore-password env:ANDROID_KEYSTORE_PASSWORD --commit-hash "$HASH"
deploy-apk: deploy-apk:
desc: Build and deploy Android APK via Dagger desc: Build and deploy Android APK via Dagger
+39 -9
View File
@@ -584,9 +584,17 @@ func (m *Ci) BuildLinux() *dagger.Directory {
} }
// BuildLinuxRelease builds the Linux release bundle. // BuildLinuxRelease builds the Linux release bundle.
func (m *Ci) BuildLinuxRelease() *dagger.Directory { func (m *Ci) BuildLinuxRelease(
// Git commit hash injected as GIT_HASH dart-define so the About page can display it.
// +optional
commitHash string,
) *dagger.Directory {
args := []string{"flutter", "build", "linux", "--release"}
if commitHash != "" {
args = append(args, "--dart-define=GIT_HASH="+commitHash)
}
return m.setup(m.linuxSrc()). return m.setup(m.linuxSrc()).
WithExec([]string{"flutter", "build", "linux", "--release"}). WithExec(args).
Directory("build/linux/x64/release/bundle") Directory("build/linux/x64/release/bundle")
} }
@@ -599,7 +607,7 @@ func (m *Ci) DeployLinux(
sshHost string, sshHost string,
commitHash string, commitHash string,
) (string, error) { ) (string, error) {
bundle := m.BuildLinuxRelease() bundle := m.BuildLinuxRelease(commitHash)
datePath := time.Now().Format("2006/01/02") datePath := time.Now().Format("2006/01/02")
remoteDir := fmt.Sprintf("public_html/builds/%s", datePath) remoteDir := fmt.Sprintf("public_html/builds/%s", datePath)
@@ -622,9 +630,20 @@ func (m *Ci) setupKeystore(keystoreBase64 *dagger.Secret, keystorePassword *dagg
} }
// BuildAndroidApk builds a release APK signed with the upload key. // BuildAndroidApk builds a release APK signed with the upload key.
func (m *Ci) BuildAndroidApk(keystoreBase64 *dagger.Secret, keystorePassword *dagger.Secret, buildNumber string) *dagger.File { func (m *Ci) BuildAndroidApk(
keystoreBase64 *dagger.Secret,
keystorePassword *dagger.Secret,
buildNumber string,
// Git commit hash injected as GIT_HASH dart-define so the About page can display it.
// +optional
commitHash string,
) *dagger.File {
args := []string{"flutter", "build", "apk", "--release", "--no-pub", "--build-number", buildNumber}
if commitHash != "" {
args = append(args, "--dart-define=GIT_HASH="+commitHash)
}
return m.setupKeystore(keystoreBase64, keystorePassword). return m.setupKeystore(keystoreBase64, keystorePassword).
WithExec([]string{"flutter", "build", "apk", "--release", "--no-pub", "--build-number", buildNumber}). WithExec(args).
File("build/app/outputs/flutter-apk/app-release.apk") File("build/app/outputs/flutter-apk/app-release.apk")
} }
@@ -640,7 +659,7 @@ func (m *Ci) DeployApk(
keystorePassword *dagger.Secret, keystorePassword *dagger.Secret,
buildNumber string, buildNumber string,
) (string, error) { ) (string, error) {
apk := m.BuildAndroidApk(keystoreBase64, keystorePassword, buildNumber) apk := m.BuildAndroidApk(keystoreBase64, keystorePassword, buildNumber, commitHash)
datePath := time.Now().Format("2006/01/02") datePath := time.Now().Format("2006/01/02")
remoteDir := fmt.Sprintf("public_html/builds/%s", datePath) remoteDir := fmt.Sprintf("public_html/builds/%s", datePath)
@@ -716,9 +735,17 @@ func (m *Ci) TestAndroidFirebase(
// BuildAndroidRelease builds the AAB with a fixed build-number so Dagger can cache it. // BuildAndroidRelease builds the AAB with a fixed build-number so Dagger can cache it.
// versionCode and signing are applied separately via StampAndroidVersionCode + SignAndroidBundle. // versionCode and signing are applied separately via StampAndroidVersionCode + SignAndroidBundle.
func (m *Ci) BuildAndroidRelease() *dagger.File { func (m *Ci) BuildAndroidRelease(
// Git commit hash injected as GIT_HASH dart-define so the About page can display it.
// +optional
commitHash string,
) *dagger.File {
args := []string{"flutter", "build", "appbundle", "--release", "--no-pub", "--build-number", "1"}
if commitHash != "" {
args = append(args, "--dart-define=GIT_HASH="+commitHash)
}
return m.setup(m.androidSrc()). return m.setup(m.androidSrc()).
WithExec([]string{"flutter", "build", "appbundle", "--release", "--no-pub", "--build-number", "1"}). WithExec(args).
File("build/app/outputs/bundle/release/app-release.aab") File("build/app/outputs/bundle/release/app-release.aab")
} }
@@ -790,9 +817,12 @@ func (m *Ci) PublishAndroid(
playStoreConfig *dagger.Secret, playStoreConfig *dagger.Secret,
keystoreBase64 *dagger.Secret, keystoreBase64 *dagger.Secret,
keystorePassword *dagger.Secret, keystorePassword *dagger.Secret,
// Git commit hash injected as GIT_HASH dart-define so the About page can display it.
// +optional
commitHash string,
) (string, error) { ) (string, error) {
versionCode := int(time.Now().Unix()) versionCode := int(time.Now().Unix())
aab := m.BuildAndroidRelease() aab := m.BuildAndroidRelease(commitHash)
stamped := m.StampAndroidVersionCode(aab, versionCode) stamped := m.StampAndroidVersionCode(aab, versionCode)
signed := m.SignAndroidBundle(stamped, keystoreBase64, keystorePassword) signed := m.SignAndroidBundle(stamped, keystoreBase64, keystorePassword)
return m.UploadToPlayStore(ctx, signed, playStoreConfig) return m.UploadToPlayStore(ctx, signed, playStoreConfig)
+6 -1
View File
@@ -360,7 +360,12 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
: null, : null,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
FilledButton(onPressed: _save, child: const Text('Save')), FilledButton(
onPressed: _hasStoredPassword || _passwordCtrl.text.isNotEmpty
? _save
: null,
child: const Text('Save'),
),
], ],
), ),
), ),
+24
View File
@@ -163,6 +163,30 @@ void main() {
expect(button.onPressed, isNotNull); expect(button.onPressed, isNotNull);
}); });
testWidgets('save button is disabled when no password stored or entered', (
tester,
) async {
tester.view.physicalSize = const Size(800, 1400);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(
buildApp(
initialLocation: '/accounts/acc-1/edit',
overrides: baseOverrides(
accounts: [kTestAccount],
hasStoredPassword: false,
),
),
);
await tester.pumpAndSettle();
final button = tester
.widget<FilledButton>(find.widgetWithText(FilledButton, 'Save'));
expect(button.onPressed, isNull);
});
testWidgets('connection error shows error message', (tester) async { testWidgets('connection error shows error message', (tester) async {
tester.view.physicalSize = const Size(800, 1400); tester.view.physicalSize = const Size(800, 1400);
tester.view.devicePixelRatio = 1.0; tester.view.devicePixelRatio = 1.0;