Skip to content

Commit d9c1b1f

Browse files
committed
ci: read Flutter version from .fvmrc and update version.py
1 parent 2ddc8e8 commit d9c1b1f

File tree

8 files changed

+142
-38
lines changed

8 files changed

+142
-38
lines changed

.github/scripts/common.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ function patch_python_package_versions() {
1010
uv version --package "$pkg" "$PYPI_VER"
1111
echo "Patched version for $pkg to $PYPI_VER"
1212
done
13+
14+
# Get Flutter version from .fvmrc and set it in version.py
15+
FLUTTER_VERSION="$(
16+
uv run --no-default-groups "$SCRIPTS/read_fvmrc.py" "${ROOT}/.fvmrc" 2>/dev/null || true
17+
)"
18+
sed -i -e "s/FLUTTER_VERSION = \"\"/FLUTTER_VERSION = \"$FLUTTER_VERSION\"/g" packages/flet/src/flet/version.py
19+
echo "Patched Flutter SDK version to $FLUTTER_VERSION"
1320
}
1421

1522

.github/scripts/read_fvmrc.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
Reads the Flutter version from a .fvmrc file and prints it.
3+
4+
Usage:
5+
uv run read_fvmrc.py <fvmrc_file>
6+
"""
7+
8+
import json
9+
import sys
10+
11+
12+
def main() -> None:
13+
try:
14+
with open(sys.argv[1], encoding="utf-8") as f:
15+
v = json.load(f)["flutter"].strip()
16+
if not v:
17+
raise ValueError("Empty 'flutter' value in .fvmrc")
18+
print(v)
19+
except Exception as e:
20+
print(f"Error parsing {sys.argv[1]}: {e}", file=sys.stderr)
21+
sys.exit(1)
22+
23+
24+
if __name__ == "__main__":
25+
main()

.github/workflows/macos-integration-tests.yml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
name: macOS Integration Tests
22

33
on:
4-
push:
5-
paths:
6-
- '.github/workflows/macos-integration-tests.yml'
7-
- 'sdk/python/packages/flet/src/**'
8-
- 'sdk/python/packages/flet/integration_tests/**'
9-
- 'packages/flet/**'
10-
- 'client/**'
11-
- '.fvmrc'
12-
pull_request:
13-
paths:
14-
- '.github/workflows/macos-integration-tests.yml'
15-
- 'sdk/python/packages/flet/src/**'
16-
- 'sdk/python/packages/flet/integration_tests/**'
17-
- 'packages/flet/**'
18-
- 'client/**'
19-
- '.fvmrc'
4+
# push:
5+
# paths:
6+
# - '.github/workflows/macos-integration-tests.yml'
7+
# - 'sdk/python/packages/flet/src/**'
8+
# - 'sdk/python/packages/flet/integration_tests/**'
9+
# - 'packages/flet/**'
10+
# - 'client/**'
11+
# - '.fvmrc'
12+
# pull_request:
13+
# paths:
14+
# - '.github/workflows/macos-integration-tests.yml'
15+
# - 'sdk/python/packages/flet/src/**'
16+
# - 'sdk/python/packages/flet/integration_tests/**'
17+
# - 'packages/flet/**'
18+
# - 'client/**'
19+
# - '.fvmrc'
2020
workflow_dispatch:
2121

2222
# Ensure only one run per branch (PR or push), cancel older ones

sdk/python/packages/flet-cli/src/flet_cli/commands/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
2222
parser.add_argument(
2323
"--flutter-version",
2424
action="version",
25-
version=flet.version.FLUTTER_VERSION,
25+
version=flet.version.get_flutter_version() or "Unknown",
2626
help="Print the required Flutter SDK version and exit.",
2727
)
2828
parser.add_argument(

sdk/python/packages/flet-cli/src/flet_cli/commands/devices.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
3434
type=int,
3535
default=10,
3636
dest="device_timeout",
37-
help="Time (in seconds) to wait for devices to attach (default: 10).",
37+
help="Time (in seconds) to wait for devices to attach (default: 10)",
3838
)
3939
parser.add_argument(
4040
"--device-connection",
@@ -43,7 +43,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
4343
default="both",
4444
dest="device_connection",
4545
help="Filter devices by connection type: attached (USB) or wireless "
46-
"(default: both).",
46+
"(default: both)",
4747
)
4848
super().add_arguments(parser)
4949

sdk/python/packages/flet-cli/src/flet_cli/commands/flutter_base.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@
2121
from flet_cli.commands.base import BaseCommand
2222
from flet_cli.utils.flutter import get_flutter_dir, install_flutter
2323

24-
FLUTTER_VERSION = version.Version(flet_version.FLUTTER_VERSION)
25-
print(f"Flutter version: {FLUTTER_VERSION}")
24+
25+
def get_flutter_version() -> Optional[version.Version]:
26+
flutter_version_str = flet_version.get_flutter_version()
27+
if not flutter_version_str:
28+
return None
29+
return version.Version(flutter_version_str)
30+
2631

2732
no_rich_output = get_bool_env_var("FLET_CLI_NO_RICH_OUTPUT")
2833

@@ -50,6 +55,7 @@ def __init__(self, parser: argparse.ArgumentParser) -> None:
5055
self.emojis = {}
5156
self.dart_exe = None
5257
self.flutter_exe = None
58+
self.required_flutter_version: Optional[version.Version] = None
5359
self.verbose = False
5460
self.require_android_sdk = False
5561
self.skip_flutter_doctor = get_bool_env_var("FLET_CLI_SKIP_FLUTTER_DOCTOR")
@@ -98,6 +104,14 @@ def handle(self, options: argparse.Namespace) -> None:
98104

99105
def initialize_command(self):
100106
assert self.options
107+
self.required_flutter_version = get_flutter_version()
108+
if not self.required_flutter_version:
109+
self.cleanup(
110+
1,
111+
"Cannot determine required Flutter SDK version. "
112+
"In a development checkout ensure `.fvmrc` exists.",
113+
)
114+
101115
self.emojis = {
102116
"checkmark": "[green]OK[/]" if self.no_rich_output else "✅",
103117
"loading": "" if self.no_rich_output else "⏳",
@@ -125,7 +139,7 @@ def initialize_command(self):
125139
)
126140
prompt = (
127141
"Flutter SDK "
128-
f"{FLUTTER_VERSION} is required. It will be installed now. "
142+
f"{self.required_flutter_version} is required. It will be installed now. "
129143
"Proceed? [y/n] "
130144
)
131145

@@ -154,6 +168,7 @@ def initialize_command(self):
154168
self.install_android_sdk()
155169

156170
def flutter_version_valid(self):
171+
assert self.required_flutter_version
157172
version_results = self.run(
158173
[
159174
self.flutter_exe,
@@ -171,18 +186,21 @@ def flutter_version_valid(self):
171186

172187
# validate installed Flutter version
173188
return (
174-
flutter_version.major == FLUTTER_VERSION.major
175-
and flutter_version.minor == FLUTTER_VERSION.minor
189+
flutter_version.major == self.required_flutter_version.major
190+
and flutter_version.minor == self.required_flutter_version.minor
176191
)
177192
else:
178193
console.log(1, "Failed to validate Flutter version.")
179194
return False
180195

181196
def install_flutter(self):
182-
self.update_status(f"[bold blue]Installing Flutter {FLUTTER_VERSION}...")
197+
assert self.required_flutter_version
198+
self.update_status(
199+
f"[bold blue]Installing Flutter {self.required_flutter_version}..."
200+
)
183201

184202
flutter_dir = install_flutter(
185-
str(FLUTTER_VERSION), self.log_stdout, progress=self.progress
203+
str(self.required_flutter_version), self.log_stdout, progress=self.progress
186204
)
187205
ext = ".bat" if platform.system() == "Windows" else ""
188206
self.flutter_exe = os.path.join(flutter_dir, "bin", f"flutter{ext}")
@@ -222,7 +240,7 @@ def install_flutter(self):
222240

223241
if self.verbose > 0:
224242
console.log(
225-
f"Flutter {FLUTTER_VERSION} installed {self.emojis['checkmark']}"
243+
f"Flutter {self.required_flutter_version} installed {self.emojis['checkmark']}"
226244
)
227245

228246
def install_jdk(self):
@@ -300,7 +318,8 @@ def _prompt_input(self, prompt: str) -> bool:
300318
self.live.start()
301319

302320
def find_flutter_batch(self, exe_filename: str):
303-
install_dir = get_flutter_dir(str(FLUTTER_VERSION))
321+
assert self.required_flutter_version
322+
install_dir = get_flutter_dir(str(self.required_flutter_version))
304323
ext = ".bat" if is_windows() else ""
305324
batch_path = os.path.join(install_dir, "bin", f"{exe_filename}{ext}")
306325
if os.path.exists(batch_path):

sdk/python/packages/flet-cli/src/flet_cli/commands/serve.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ def handler(*args, **kwargs):
6565
try:
6666
with socketserver.TCPServer(("", options.port), handler) as httpd:
6767
console.print(
68-
f"Serving [green]{directory}[/green] at [cyan]http://localhost:{options.port}[/cyan] (Press Ctrl+C to stop)\n"
68+
f"Serving [green]{directory}[/green] at [cyan]"
69+
f"http://localhost:{options.port}[/cyan] (Press Ctrl+C to stop)\n"
6970
)
7071
httpd.serve_forever()
7172
except KeyboardInterrupt:

sdk/python/packages/flet/src/flet/version.py

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
"""Provide the current Flet version."""
22

3+
import json
34
import os
45
import subprocess as sp
56
from pathlib import Path
67

7-
import flet
88
from flet.utils import is_mobile, is_windows, which
99

1010
__all__ = [
11-
"DEFAULT_VERSION",
1211
"FLUTTER_VERSION",
1312
"PYODIDE_VERSION",
1413
"find_repo_root",
1514
"from_git",
15+
"get_flutter_version",
1616
"version",
1717
]
1818

19-
DEFAULT_VERSION = "0.1.0"
19+
DEFAULT_FLET_VERSION = "0.1.0"
2020

21-
# will be replaced by CI
21+
# set by CI
2222
version = ""
2323

2424

25-
FLUTTER_VERSION = "3.38.3"
25+
# set by CI
26+
FLUTTER_VERSION = ""
2627
"""
2728
The Flutter SDK version used when building the flet client or packaging
2829
apps with [`flet build`](https://docs.flet.dev/cli/flet-build/).
@@ -35,12 +36,63 @@
3536
"""
3637

3738

39+
def _find_upwards(start_dir: Path, file_name: str) -> Path | None:
40+
current_dir = start_dir.resolve()
41+
while current_dir != current_dir.parent:
42+
candidate = current_dir / file_name
43+
if candidate.is_file():
44+
return candidate
45+
current_dir = current_dir.parent
46+
return None
47+
48+
49+
def _flutter_version_from_fvmrc(fvmrc_path: Path) -> str | None:
50+
try:
51+
raw = fvmrc_path.read_text(encoding="utf-8").strip()
52+
except OSError:
53+
return None
54+
if not raw:
55+
return None
56+
try:
57+
data = json.loads(raw)
58+
except json.JSONDecodeError:
59+
return raw.strip().strip('"')
60+
if isinstance(data, dict):
61+
value = data.get("flutter")
62+
if isinstance(value, str) and value.strip():
63+
return value.strip()
64+
return None
65+
66+
67+
def get_flutter_version() -> str:
68+
"""
69+
Return the required Flutter SDK version.
70+
71+
In CI/release builds the value is stamped into `FLUTTER_VERSION`.
72+
In a local development checkout (where `FLUTTER_VERSION == ""`), it is
73+
resolved from the repository's `.fvmrc`.
74+
"""
75+
76+
if FLUTTER_VERSION:
77+
return FLUTTER_VERSION
78+
if is_mobile():
79+
return ""
80+
81+
start_dirs = [Path.cwd(), Path(__file__).resolve().parent]
82+
for start_dir in start_dirs:
83+
fvmrc_path = _find_upwards(start_dir, ".fvmrc")
84+
if fvmrc_path:
85+
v = _flutter_version_from_fvmrc(fvmrc_path)
86+
if v:
87+
return v
88+
return ""
89+
90+
3891
def from_git():
3992
"""Try to get the version from Git tags."""
4093
working = Path().absolute()
4194
try:
42-
version_file_path = Path(flet.__file__).absolute().parent / "version.py"
43-
repo_root = find_repo_root(version_file_path.parent)
95+
repo_root = find_repo_root(Path(__file__).resolve().parent)
4496

4597
if repo_root:
4698
os.chdir(repo_root)
@@ -86,11 +138,11 @@ def find_repo_root(start_path: Path) -> Path | None:
86138
if not version and not is_mobile():
87139
# Only try to get the version from Git if the pre-set version is empty
88140
# This is more likely to happen in a development/source environment
89-
version = from_git() or DEFAULT_VERSION # Fallback to a default if Git fails
141+
version = from_git() or DEFAULT_FLET_VERSION # Fallback to a default if Git fails
90142

91143
# If 'version' is still empty after the above (e.g., in a built package
92144
# where CI didn't replace it), it might be appropriate to have another
93145
# default or a way to set it during the build process. However, the
94146
# CI replacement is the standard way for packaged versions.
95147
if not version:
96-
version = DEFAULT_VERSION # Final fallback
148+
version = DEFAULT_FLET_VERSION # Final fallback

0 commit comments

Comments
 (0)