Manifest format update (#33)

This commit is contained in:
Ken Van Hoeylandt
2026-07-03 00:04:29 +02:00
committed by GitHub
parent 8a0f9ef4e4
commit 4ab2377970
19 changed files with 179 additions and 249 deletions
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.brainfuck
versionName=0.3.0
versionCode=3
name=Brainfuck interpreter
description=Brainfuck esoteric language interpreter
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.brainfuck
app.version.name=0.4.0
app.version.code=4
app.name=Brainfuck interpreter
app.description=Brainfuck esoteric language interpreter
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.breakout
versionName=0.3.0
versionCode=3
name=Breakout
description=Classic brick-breaking arcade game
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.breakout
app.version.name=0.4.0
app.version.code=4
app.name=Breakout
app.description=Classic brick-breaking arcade game
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.calculator
versionName=0.4.0
versionCode=4
name=Calculator
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.calculator
app.version.name=0.5.0
app.version.code=5
app.name=Calculator
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.diceware
versionName=0.4.0
versionCode=4
name=Diceware
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.diceware
app.version.name=0.5.0
app.version.code=5
app.name=Diceware
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32s3,esp32p4
[app]
id=one.tactility.epubreader
versionName=0.2.0
versionCode=2
name=Epub Reader
description=Epub and text file reader. Requires PSRAM!
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32s3,esp32p4
app.id=one.tactility.epubreader
app.version.name=0.3.0
app.version.code=3
app.name=Epub Reader
app.description=Epub and text file reader. Requires PSRAM!
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.gpio
versionName=0.5.0
versionCode=5
name=GPIO
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.gpio
app.version.name=0.6.0
app.version.code=6
app.name=GPIO
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.graphicsdemo
versionName=0.4.0
versionCode=4
name=Graphics Demo
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.graphicsdemo
app.version.name=0.5.0
app.version.code=5
app.name=Graphics Demo
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.helloworld
versionName=0.4.0
versionCode=4
name=Hello World
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.helloworld
app.version.name=0.5.0
app.version.code=5
app.name=Hello World
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32s3,esp32p4
[app]
id=one.tactility.m5unittest
versionName=0.2.0
versionCode=2
name=M5 Unit Test
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32s3,esp32p4
app.id=one.tactility.m5unittest
app.version.name=0.3.0
app.version.code=3
app.name=M5 Unit Test
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.magic8ball
versionName=0.3.0
versionCode=3
name=Magic 8-Ball
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.magic8ball
app.version.name=0.4.0
app.version.code=4
app.name=Magic 8-Ball
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32s3,esp32p4
[app]
id=one.tactility.mediakeys
versionName=0.2.0
versionCode=2
name=Media Keys
description=Bluetooth media keys. Touch or Physical Keyboard control\nB - previous, P - play/pause, N - next, M - mute, D - volume down, U - volume up.\nQ or ESC to exit focus.
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32s3,esp32p4
app.id=one.tactility.mediakeys
app.version.name=0.3.0
app.version.code=3
app.name=Media Keys
app.description=Bluetooth media keys. Touch or Physical Keyboard control\nB - previous, P - play/pause, N - next, M - mute, D - volume down, U - volume up.\nQ or ESC to exit focus.
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.mystifydemo
versionName=0.4.0
versionCode=4
name=Mystify Demo
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.mystifydemo
app.version.name=0.5.0
app.version.code=5
app.name=Mystify Demo
+7 -10
View File
@@ -1,10 +1,7 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.serialconsole
versionName=0.5.0
versionCode=5
name=Serial Console
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.serialconsole
app.version.name=0.6.0
app.version.code=6
app.name=Serial Console
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.snake
versionName=0.6.0
versionCode=6
name=Snake
description=Classic Snake game
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.snake
app.version.name=0.7.0
app.version.code=7
app.name=Snake
app.description=Classic Snake game
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.tamatac
versionName=0.2.0
versionCode=2
name=TamaTac
description=Virtual pet inspired by Tamagotchi. Only runs on devices with PSRAM.
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.tamatac
app.version.name=0.3.0
app.version.code=3
app.name=TamaTac
app.description=Virtual pet inspired by Tamagotchi. Only runs on devices with PSRAM.
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.todolist
versionName=0.3.0
versionCode=3
name=Todo List
description=Simple task list manager
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.todolist
app.version.name=0.4.0
app.version.code=4
app.name=Todo List
app.description=Simple task list manager
+8 -11
View File
@@ -1,11 +1,8 @@
[manifest]
version=0.1
[target]
sdk=0.7.0
platforms=esp32,esp32s3,esp32c6,esp32p4
[app]
id=one.tactility.twoeleven
versionName=0.5.0
versionCode=5
name=2048
description=A fun, customizable 2048 sliding tile game for tactility!\nSlide tiles to combine numbers and reach 2048.\nChoose grid sizes: 3x3 (easy), 4x4 (classic), 5x5, or 6x6 (expert).
manifest.version=0.2
target.sdk=0.7.0
target.platforms=esp32,esp32s3,esp32c6,esp32p4
app.id=one.tactility.twoeleven
app.version.name=0.6.0
app.version.code=6
app.name=2048
app.description=A fun, customizable 2048 sliding tile game for tactility!\nSlide tiles to combine numbers and reach 2048.\nChoose grid sizes: 3x3 (easy), 4x4 (classic), 5x5, or 6x6 (expert).
+30 -36
View File
@@ -2,13 +2,20 @@ import json
import tarfile
import os
import tempfile
import configparser
import sys
def read_properties_file(path):
config = configparser.RawConfigParser()
config.read(path)
return config
properties = {}
with open(path, "r") as file:
for line in file:
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
key, sep, value = line.partition("=")
if not sep:
continue
properties[key.strip()] = value.strip()
return properties
def get_manifest(appPath):
"""Extract only the file named 'manifest.properties' from the given tar/tar.gz
@@ -62,12 +69,12 @@ def get_manifest(appPath):
return None
def get_versioned_file_name(manifest):
app_id = manifest["app"]["id"]
version_code = manifest["app"]["versionCode"]
app_id = manifest["app.id"]
version_code = manifest["app.version.code"]
return f"{app_id}-{version_code}.app"
def get_os_version(manifest):
sdk = manifest["target"]["sdk"]
sdk = manifest["target.sdk"]
# Remove trailing hyphen suffix if present
if "-" in sdk:
return sdk.rsplit("-", 1)[0].strip()
@@ -75,38 +82,25 @@ def get_os_version(manifest):
return sdk
def manifest_config_to_flat_json(manifest):
"""Convert a ConfigParser manifest into a flat JSON-like dict.
"""Convert a flat (V2) manifest dict into a flat JSON-like dict.
Expected sections/keys (case-insensitive for keys):
- [app]
id -> appId
versionName -> appVersionName
versionCode -> appVersionCode (int)
name -> appName
description -> appDescription (optional; default "")
- [target]
sdk -> targetSdk
platforms -> targetPlatforms (comma-separated list)
Expected keys:
app.id -> appId
app.version.name -> appVersionName
app.version.code -> appVersionCode (int)
app.name -> appName
app.description -> appDescription (optional; default "")
target.sdk -> targetSdk
target.platforms -> targetPlatforms (comma-separated list)
Unknown/missing values fall back to sensible defaults per requirements.
"""
def get_opt(section, option, default=None):
if not manifest.has_section(section):
return default
# try exact option then lowercase (RawConfigParser lowercases by default)
if manifest.has_option(section, option):
return manifest.get(section, option)
low = option.lower()
if manifest.has_option(section, low):
return manifest.get(section, low)
return default
# Map values
app_id = get_opt("app", "id", "")
app_version_name = get_opt("app", "versionName", "")
app_version_code_raw = get_opt("app", "versionCode", "0")
app_name = get_opt("app", "name", "")
app_description = get_opt("app", "description", "") or ""
app_id = manifest.get("app.id", "")
app_version_name = manifest.get("app.version.name", "")
app_version_code_raw = manifest.get("app.version.code", "0")
app_name = manifest.get("app.name", "")
app_description = manifest.get("app.description", "") or ""
# Coerce version code to int safely
try:
@@ -114,8 +108,8 @@ def manifest_config_to_flat_json(manifest):
except Exception:
app_version_code = 0
target_sdk = get_opt("target", "sdk", "")
platforms_raw = get_opt("target", "platforms", "")
target_sdk = manifest.get("target.sdk", "")
platforms_raw = manifest.get("target.platforms", "")
target_platforms = [p.strip() for p in str(platforms_raw).split(",") if p.strip()] if platforms_raw is not None else []
filename = get_versioned_file_name(manifest)
+22 -35
View File
@@ -1,4 +1,3 @@
import configparser
import json
import os
import re
@@ -13,7 +12,7 @@ import tarfile
from urllib.parse import urlparse
ttbuild_path = ".tactility"
ttbuild_version = "3.5.1"
ttbuild_version = "4.0.0"
ttbuild_cdn = "https://cdn.tactilityproject.org"
ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666
@@ -106,9 +105,17 @@ def get_url(ip, path):
return f"http://{ip}:{ttport}{path}"
def read_properties_file(path):
config = configparser.RawConfigParser()
config.read(path)
return config
properties = {}
with open(path, "r") as file:
for line in file:
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
key, sep, value = line.partition("=")
if not sep:
continue
properties[key.strip()] = value.strip()
return properties
#endregion Core
@@ -231,32 +238,12 @@ def read_manifest():
return read_properties_file("manifest.properties")
def validate_manifest(manifest):
# [manifest]
if not "manifest" in manifest:
exit_with_error("Invalid manifest format: [manifest] not found")
if not "version" in manifest["manifest"]:
exit_with_error("Invalid manifest format: [manifest] version not found")
# [target]
if not "target" in manifest:
exit_with_error("Invalid manifest format: [target] not found")
if not "sdk" in manifest["target"]:
exit_with_error("Invalid manifest format: [target] sdk not found")
if not "platforms" in manifest["target"]:
exit_with_error("Invalid manifest format: [target] platforms not found")
# [app]
if not "app" in manifest:
exit_with_error("Invalid manifest format: [app] not found")
if not "id" in manifest["app"]:
exit_with_error("Invalid manifest format: [app] id not found")
if not "versionName" in manifest["app"]:
exit_with_error("Invalid manifest format: [app] versionName not found")
if not "versionCode" in manifest["app"]:
exit_with_error("Invalid manifest format: [app] versionCode not found")
if not "name" in manifest["app"]:
exit_with_error("Invalid manifest format: [app] name not found")
for key in ("manifest.version", "target.sdk", "target.platforms", "app.id", "app.version.name", "app.version.code", "app.name"):
if key not in manifest:
exit_with_error(f"Invalid manifest format: {key} not found")
def is_valid_manifest_platform(manifest, platform):
manifest_platforms = manifest["target"]["platforms"].split(",")
manifest_platforms = manifest["target.platforms"].split(",")
return platform in manifest_platforms
def validate_manifest_platform(manifest, platform):
@@ -265,7 +252,7 @@ def validate_manifest_platform(manifest, platform):
def get_manifest_target_platforms(manifest, requested_platform):
if requested_platform == "" or requested_platform is None:
return manifest["target"]["platforms"].split(",")
return manifest["target.platforms"].split(",")
else:
validate_manifest_platform(manifest, requested_platform)
return [requested_platform]
@@ -512,7 +499,7 @@ def build_action(manifest, platform_arg, skip_build):
if use_local_sdk:
global local_base_path
local_base_path = os.environ.get("TACTILITY_SDK_PATH")
validate_local_sdks(platforms_to_build, manifest["target"]["sdk"])
validate_local_sdks(platforms_to_build, manifest["target.sdk"])
if should_fetch_sdkconfig_files(platforms_to_build):
fetch_sdkconfig_files(platforms_to_build)
@@ -521,7 +508,7 @@ def build_action(manifest, platform_arg, skip_build):
sdk_json = read_sdk_json()
validate_self(sdk_json)
# Build
sdk_version = manifest["target"]["sdk"]
sdk_version = manifest["target.sdk"]
if not use_local_sdk:
if not sdk_download_all(sdk_version, platforms_to_build):
exit_with_error("Failed to download one or more SDKs")
@@ -570,7 +557,7 @@ def get_device_info(ip):
print_status_error(f"Device info request failed: {e}")
def run_action(manifest, ip):
app_id = manifest["app"]["id"]
app_id = manifest["app.id"]
print_status_busy("Running")
url = get_url(ip, "/app/run")
params = {'id': app_id}
@@ -614,7 +601,7 @@ def install_action(ip, platforms):
return False
def uninstall_action(manifest, ip):
app_id = manifest["app"]["id"]
app_id = manifest["app.id"]
print_status_busy("Uninstalling")
url = get_url(ip, "/app/uninstall")
params = {'id': app_id}
@@ -670,7 +657,7 @@ if __name__ == "__main__":
exit_with_error("manifest.properties not found")
manifest = read_manifest()
validate_manifest(manifest)
all_platform_targets = manifest["target"]["platforms"].split(",")
all_platform_targets = manifest["target.platforms"].split(",")
# Update SDK cache (tool.json)
if not use_local_sdk and should_update_tool_json() and not update_tool_json():
exit_with_error("Failed to retrieve SDK info")