Skip to content

Commit ce6c5ac

Browse files
authored
Merge pull request SatoshiPortal#2251 from SatoshiPortal/develop
v6.11.0
2 parents d40cad2 + e8a4173 commit ce6c5ac

345 files changed

Lines changed: 42239 additions & 7457 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.devcontainer/devcontainer.json

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
11
{
22
"name": "${localWorkspaceFolderBasename}",
33
"build": {
4-
"dockerfile": "../Containerfile.app",
4+
"dockerfile": "../Containerfile.tools",
55
"args": {
66
"USERNAME": "${localEnv:USER}"
77
}
88
},
99
"remoteUser": "${localEnv:USER}",
1010
"updateRemoteUserUID": true,
1111
"shutdownAction": "stopContainer",
12-
"initializeCommand": ".devcontainer/init-ssh-agent.sh",
12+
"initializeCommand": "sh -c '.devcontainer/init-ssh-agent.sh && .devcontainer/init-adb.sh'",
1313
"runArgs": [
1414
"--name",
1515
"${localWorkspaceFolderBasename}",
1616
"--hostname",
1717
"bull",
1818
"--memory=8g",
19+
"--device=/dev/dri",
20+
"--dns",
21+
"1.1.1.1",
22+
"--dns",
23+
"9.9.9.9",
1924
"--security-opt",
2025
"label=disable"
2126
],
2227
"mounts": [
23-
"source=${localEnv:HOME}/.ssh,target=/home/${localEnv:USER}/.ssh,type=bind,consistency=cached",
24-
"source=/tmp/ssh-agent.sock,target=/ssh-agent,type=bind,consistency=cached",
25-
"source=${localEnv:HOME}/.gitconfig,target=/home/${localEnv:USER}/.gitconfig,type=bind,consistency=cached"
28+
"source=${localEnv:HOME}/.ssh,target=/home/${localEnv:USER}/.ssh,type=bind,consistency=cached,readonly",
29+
"source=${localEnv:HOME}/.ssh-agent-devcontainer.sock,target=/ssh-agent,type=bind,consistency=cached",
30+
"source=${localEnv:HOME}/.gitconfig,target=/home/${localEnv:USER}/.gitconfig,type=bind,consistency=cached",
31+
"source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind,consistency=cached",
32+
"source=/tmp/wayland.sock,target=/tmp/wayland.sock,type=bind,consistency=cached",
33+
"source=${localWorkspaceFolderBasename}-bash-history,target=/home/${localEnv:USER}/.history,type=volume",
34+
"source=${localWorkspaceFolderBasename}-keyrings,target=/home/${localEnv:USER}/.local/share/keyrings,type=volume"
2635
],
2736
"containerEnv": {
28-
"SSH_AUTH_SOCK": "/ssh-agent"
37+
"SSH_AUTH_SOCK": "/ssh-agent",
38+
"ADB_SERVER_SOCKET": "tcp:host.containers.internal:5037",
39+
"DISPLAY": "${localEnv:DISPLAY}",
40+
"XDG_RUNTIME_DIR": "/tmp",
41+
"WAYLAND_DISPLAY": "wayland.sock",
42+
"GDK_BACKEND": "wayland,x11",
43+
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/tmp/dbus-session.sock"
2944
},
30-
"postStartCommand": "sudo chown -R --no-dereference ${localEnv:USER}:${localEnv:USER} /home/${localEnv:USER}/.ssh || true; chmod 700 /home/${localEnv:USER}/.ssh; find /home/${localEnv:USER}/.ssh -maxdepth 1 -type f -exec chmod 600 {} + 2>/dev/null || true"
45+
"postCreateCommand": "sudo chown -R ${localEnv:USER}:${localEnv:USER} /home/${localEnv:USER}/.local /home/${localEnv:USER}/.history && touch /home/${localEnv:USER}/.history/bash_history",
46+
"postStartCommand": ".devcontainer/init-keyring.sh"
3147
}

.devcontainer/init-adb.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/sh
2+
# Host-side script — runs via the devcontainer `initializeCommand`, NOT inside
3+
# the container.
4+
#
5+
# Exposes the host's adb server to the devcontainer so `flutter devices` inside
6+
# the container sees USB devices attached to the host (e.g. an Android phone).
7+
# The container's adb client reaches it via
8+
# ADB_SERVER_SOCKET=tcp:host.containers.internal:5037
9+
# (set in devcontainer.json containerEnv).
10+
#
11+
# Why client-to-host instead of USB passthrough: only one adb server may own a
12+
# USB device at a time. The host already owns it, so the container talks to the
13+
# host's server rather than fighting for the device (which also breaks on every
14+
# replug). adb's server binds to 127.0.0.1 by default — unreachable from the
15+
# container's network namespace — so we (re)start it with -a (all interfaces).
16+
#
17+
# NOTE: host and container adb must be the same version (container ships
18+
# platform-tools 37.0.0); a mismatch makes adb refuse to connect.
19+
set -e
20+
21+
# No adb on the host (or not on PATH) -> nothing to expose; the container falls
22+
# back to its local (desktop) device only.
23+
command -v adb >/dev/null 2>&1 || exit 0
24+
25+
# Restart the server listening on all interfaces. Detach so initializeCommand
26+
# returns; nohup keeps it alive after this script's process group exits.
27+
adb kill-server >/dev/null 2>&1 || true
28+
nohup adb -a -P 5037 server nodaemon >/dev/null 2>&1 &
29+
30+
# Give it a moment to bind before the container starts probing.
31+
sleep 1
32+
exit 0

.devcontainer/init-keyring.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/sh
2+
# Bootstrap a session DBus + gnome-keyring inside the dev container so libsecret
3+
# (used by flutter_secure_storage_linux) has a Secret Service provider to talk
4+
# to. Idempotent — safe to invoke on every container start.
5+
#
6+
# Each step is gated on a *functional* check (can we actually use it?) rather
7+
# than a presence check (does a file exist?). The earlier presence-based
8+
# version passed when a daemon had died leaving a stale socket, or when the
9+
# keyring file existed but was locked with a forgotten password — and the app
10+
# then died at init with libsecret_error: Failed to unlock the keyring.
11+
set -e
12+
DBUS_SOCKET=/tmp/dbus-session.sock
13+
KEYRING_PASSWORD=bull
14+
15+
export DBUS_SESSION_BUS_ADDRESS="unix:path=$DBUS_SOCKET"
16+
17+
# 1. Session DBus on a fixed socket path. Replace the daemon if the socket
18+
# file exists but no daemon is listening on it (stale socket from a crash).
19+
dbus_ok() {
20+
# Peer.Ping on the bus daemon itself succeeds iff the bus is alive and
21+
# accepting connections — a stale socket file fails this.
22+
timeout 2 dbus-send --session --dest=org.freedesktop.DBus \
23+
--print-reply / org.freedesktop.DBus.Peer.Ping >/dev/null 2>&1
24+
}
25+
if ! dbus_ok; then
26+
rm -f "$DBUS_SOCKET"
27+
setsid dbus-daemon \
28+
--session \
29+
--address="unix:path=$DBUS_SOCKET" \
30+
--nofork --syslog-only >/dev/null 2>&1 &
31+
for _ in 1 2 3 4 5 6 7 8 9 10; do
32+
dbus_ok && break
33+
sleep 0.2
34+
done
35+
fi
36+
37+
# 2. gnome-keyring-daemon (Secret Service provider). The probe below tests
38+
# the full path the app uses: bus reachable → daemon registered on it →
39+
# default collection unlocked. A running-but-broken daemon (registered on
40+
# a previous bus, or with a locked collection) fails this and is replaced.
41+
keyring_ok() {
42+
# Store a throwaway secret then clear it. Succeeds only if the daemon is
43+
# registered on org.freedesktop.secrets AND the default collection is
44+
# unlocked. `timeout` guards against the libsecret prompt-for-password
45+
# behaviour that would otherwise hang in a headless container.
46+
printf 'probe' | timeout 3 secret-tool store --label='keyring-probe' \
47+
app keyring-probe >/dev/null 2>&1 || return 1
48+
timeout 2 secret-tool clear app keyring-probe >/dev/null 2>&1 || return 1
49+
}
50+
if ! keyring_ok; then
51+
pkill -x gnome-keyring-d 2>/dev/null || true
52+
sleep 0.3
53+
printf '%s' "$KEYRING_PASSWORD" | gnome-keyring-daemon \
54+
--daemonize --unlock --components=secrets >/dev/null 2>&1 || true
55+
fi
56+
57+
# 3. Bootstrap a default collection if needed. gnome-keyring-daemon doesn't
58+
# create one automatically — libsecret callers get KeyringLocked errors
59+
# until one exists. Run unconditionally: store-then-clear leaves no
60+
# residue, and is the same primitive keyring_ok uses, so it's cheap and
61+
# idempotent.
62+
printf '%s' "$KEYRING_PASSWORD" | secret-tool store --label='bootstrap' \
63+
app bootstrap >/dev/null 2>&1 || true
64+
secret-tool clear app bootstrap >/dev/null 2>&1 || true
65+
66+
# 4. Final assertion. Better to fail postStartCommand here, where the cause
67+
# is obvious, than to die mid-app-init with a stack trace pointing at
68+
# flutter_secure_storage.
69+
if ! keyring_ok; then
70+
echo "init-keyring.sh: keyring still not usable. Try:" >&2
71+
echo " killall -9 gnome-keyring-daemon dbus-daemon" >&2
72+
echo " rm -f /tmp/dbus-session.sock" >&2
73+
echo " rm -rf ~/.local/share/keyrings" >&2
74+
echo " .devcontainer/init-keyring.sh" >&2
75+
exit 1
76+
fi

.devcontainer/init-ssh-agent.sh

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,57 @@
11
#!/bin/sh
2-
# Normalize SSH agent socket path for devcontainer mount
2+
# Normalize host paths for devcontainer mounts.
3+
#
4+
# Every bind mount in devcontainer.json is a hard dependency: if its host
5+
# source is missing at create/start time, the container fails to come up
6+
# (this is what the stale /tmp/ssh-agent.sock mount did after a reboot).
7+
# This script guarantees every bind source exists — real when available,
8+
# harmless placeholder when not — so the container starts on any machine
9+
# regardless of whether SSH, git, an agent, or a display are set up.
10+
# Missing pieces degrade gracefully instead of breaking startup.
11+
#
12+
# The SSH agent socket lives under $HOME (not /tmp) so it's visible inside the
13+
# podman-machine VM on macOS, which bind-mounts /Users from the host but not
14+
# /tmp. Wayland stays in /tmp since it's Linux-only (macOS has no Wayland).
15+
SSH_SOCKET="${HOME}/.ssh-agent-devcontainer.sock"
16+
17+
# Bind sources that must exist on every host, regardless of OS.
18+
# ~/.ssh and ~/.gitconfig are bind-mounted read-only into the container;
19+
# a fresh machine may lack either, which would fail the mount.
20+
mkdir -p "${HOME}/.ssh"
21+
[ -e "${HOME}/.gitconfig" ] || touch "${HOME}/.gitconfig"
22+
323
case "$(uname)" in
424
Linux)
5-
# Symlink real SSH agent socket
6-
ln -sf "$SSH_AUTH_SOCK" /tmp/ssh-agent.sock
25+
# X11 socket dir: bind-mounted at /tmp/.X11-unix. Absent on headless or
26+
# Wayland-only hosts — create it so the mount never fails (an empty dir
27+
# just means no X11 apps, not a broken container).
28+
mkdir -p /tmp/.X11-unix
29+
30+
# SSH agent: only symlink a *real* agent socket. If no agent is running,
31+
# $SSH_AUTH_SOCK is empty/stale — `ln -sf "" ...` would make a broken
32+
# link. Leave a placeholder file instead so the mount succeeds; the
33+
# agent is simply unavailable inside the container.
34+
if [ -n "$SSH_AUTH_SOCK" ] && [ -S "$SSH_AUTH_SOCK" ]; then
35+
ln -sf "$SSH_AUTH_SOCK" "$SSH_SOCKET"
36+
else
37+
rm -f "$SSH_SOCKET"
38+
touch "$SSH_SOCKET"
39+
fi
40+
41+
# Wayland: symlink the real socket if running under Wayland; else dummy file
42+
if [ -n "$WAYLAND_DISPLAY" ] && [ -n "$XDG_RUNTIME_DIR" ] \
43+
&& [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; then
44+
ln -sf "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" /tmp/wayland.sock
45+
else
46+
rm -f /tmp/wayland.sock
47+
touch /tmp/wayland.sock
48+
fi
749
;;
850
Darwin)
9-
# Create dummy so the mount doesn't fail (SSH agent not supported on macOS Podman)
10-
rm -f /tmp/ssh-agent.sock
11-
touch /tmp/ssh-agent.sock
51+
# X11/Wayland/agent forwarding aren't supported on macOS Podman — create
52+
# dummies so the mounts don't fail.
53+
mkdir -p /tmp/.X11-unix
54+
rm -f "$SSH_SOCKET" /tmp/wayland.sock
55+
touch "$SSH_SOCKET" /tmp/wayland.sock
1256
;;
1357
esac

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ ios/
1111
.fvm/
1212

1313
# Local APKs/AABs built previously (host-side artifacts)
14+
BULL-*.apk
15+
BULL-*.aab
1416
app-*.apk
1517
app-*.aab
1618

.git_hooks/pre-commit

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
11
#!/bin/bash
22
echo "Begin pre-commit hook"
33

4+
REPO_ROOT="$(git rev-parse --show-toplevel)"
5+
6+
# Git sets GIT_DIR/etc. for hook subprocesses; Flutter's bin/flutter
7+
# uses `git describe` to detect its own SDK version and these vars
8+
# point it at the wrong repo. Clear them before invoking Flutter.
9+
unset GIT_DIR GIT_INDEX_FILE GIT_WORK_TREE GIT_PREFIX
10+
411
# Prefer FVM Flutter if present; else try `fvm flutter`; else global flutter
512
if [[ -x "$REPO_ROOT/.fvm/flutter_sdk/bin/flutter" ]]; then
613
FLUTTER="$REPO_ROOT/.fvm/flutter_sdk/bin/flutter"
14+
DART="$REPO_ROOT/.fvm/flutter_sdk/bin/dart"
715
elif command -v fvm >/dev/null 2>&1; then
816
FLUTTER="fvm flutter"
17+
DART="fvm dart"
918
else
1019
FLUTTER="flutter"
20+
DART="dart"
1121
fi
1222

1323
echo "Using Flutter: $($FLUTTER --version | head -n1)"
1424

15-
result=$($FLUTTER analyze)
16-
exitCode=$?
17-
18-
if [ $exitCode -ne 0 ]; then
19-
echo "$result"
25+
analyzeStart=$SECONDS
26+
$FLUTTER analyze
27+
analyzeExit=$?
28+
echo "flutter analyze took $((SECONDS - analyzeStart))s"
29+
if [ $analyzeExit -ne 0 ]; then
2030
echo "Flutter analyze found issues, please fix them."
2131
exit 1
2232
fi
23-
echo "Finished running flutter analyze command."
2433

25-
echo "End pre-commit hook"
34+
fixStart=$SECONDS
35+
fixOutput=$($DART fix --dry-run 2>&1)
36+
echo "$fixOutput"
37+
echo "dart fix --dry-run took $((SECONDS - fixStart))s"
38+
if ! echo "$fixOutput" | grep -q "Nothing to fix!"; then
39+
echo "dart fix has suggestions, run \`dart fix --apply\` and commit the result."
40+
exit 1
41+
fi
42+
43+
echo "End pre-commit hook"

0 commit comments

Comments
 (0)