Implement app uninstall via tactility.py

This commit is contained in:
Ken Van Hoeylandt 2025-09-14 13:20:57 +02:00
parent d303500b7c
commit d082ce6d07
9 changed files with 143 additions and 9 deletions

View File

@ -14,7 +14,7 @@ import shutil
import configparser import configparser
ttbuild_path = ".tactility" ttbuild_path = ".tactility"
ttbuild_version = "2.0.0" ttbuild_version = "2.1.0"
ttbuild_cdn = "https://cdn.tactility.one" ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666 ttport = 6666
@ -60,8 +60,9 @@ def print_help():
print(" clean Clean the build folders") print(" clean Clean the build folders")
print(" clearcache Clear the SDK cache") print(" clearcache Clear the SDK cache")
print(" updateself Update this tool") print(" updateself Update this tool")
print(" run [ip] Run an application") print(" run [ip] Run the application")
print(" install [ip] Install an application") print(" install [ip] Install the application")
print(" uninstall [ip] Uninstall the application")
print(" bir [ip] [esp32,esp32s3] Build, install then run. Optionally specify a platform.") print(" bir [ip] [esp32,esp32s3] Build, install then run. Optionally specify a platform.")
print(" brrr [ip] [esp32,esp32s3] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.") print(" brrr [ip] [esp32,esp32s3] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.")
print("") print("")
@ -544,6 +545,19 @@ def install_action(ip, platforms):
except IOError as e: except IOError as e:
print_error(f"File error: {e}") print_error(f"File error: {e}")
def uninstall_action(manifest, ip):
app_id = manifest["app"]["id"]
print(f"Uninstalling {app_id} on {ip}")
url = get_url(ip, "/app/uninstall")
params = {'id': app_id}
try:
response = requests.put(url, params=params)
if response.status_code != 200:
print_error("Uninstall failed")
else:
print(f"{shell_color_green}Uninstall successful ✅{shell_color_reset}")
except requests.RequestException as e:
print(f"Request failed: {e}")
#region Main #region Main
if __name__ == "__main__": if __name__ == "__main__":
@ -599,6 +613,11 @@ if __name__ == "__main__":
platform = sys.argv[3] platform = sys.argv[3]
platforms_to_install = [platform] platforms_to_install = [platform]
install_action(sys.argv[2], platforms_to_install) install_action(sys.argv[2], platforms_to_install)
elif action_arg == "uninstall":
if len(sys.argv) < 3:
print_help()
exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir": elif action_arg == "bir":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print_help() print_help()

View File

@ -14,7 +14,7 @@ import shutil
import configparser import configparser
ttbuild_path = ".tactility" ttbuild_path = ".tactility"
ttbuild_version = "2.0.0" ttbuild_version = "2.1.0"
ttbuild_cdn = "https://cdn.tactility.one" ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666 ttport = 6666
@ -60,8 +60,9 @@ def print_help():
print(" clean Clean the build folders") print(" clean Clean the build folders")
print(" clearcache Clear the SDK cache") print(" clearcache Clear the SDK cache")
print(" updateself Update this tool") print(" updateself Update this tool")
print(" run [ip] Run an application") print(" run [ip] Run the application")
print(" install [ip] Install an application") print(" install [ip] Install the application")
print(" uninstall [ip] Uninstall the application")
print(" bir [ip] [esp32,esp32s3] Build, install then run. Optionally specify a platform.") print(" bir [ip] [esp32,esp32s3] Build, install then run. Optionally specify a platform.")
print(" brrr [ip] [esp32,esp32s3] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.") print(" brrr [ip] [esp32,esp32s3] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.")
print("") print("")
@ -544,6 +545,19 @@ def install_action(ip, platforms):
except IOError as e: except IOError as e:
print_error(f"File error: {e}") print_error(f"File error: {e}")
def uninstall_action(manifest, ip):
app_id = manifest["app"]["id"]
print(f"Uninstalling {app_id} on {ip}")
url = get_url(ip, "/app/uninstall")
params = {'id': app_id}
try:
response = requests.put(url, params=params)
if response.status_code != 200:
print_error("Uninstall failed")
else:
print(f"{shell_color_green}Uninstall successful ✅{shell_color_reset}")
except requests.RequestException as e:
print(f"Request failed: {e}")
#region Main #region Main
if __name__ == "__main__": if __name__ == "__main__":
@ -599,6 +613,11 @@ if __name__ == "__main__":
platform = sys.argv[3] platform = sys.argv[3]
platforms_to_install = [platform] platforms_to_install = [platform]
install_action(sys.argv[2], platforms_to_install) install_action(sys.argv[2], platforms_to_install)
elif action_arg == "uninstall":
if len(sys.argv) < 3:
print_help()
exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir": elif action_arg == "bir":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print_help() print_help()

View File

@ -14,7 +14,7 @@ import shutil
import configparser import configparser
ttbuild_path = ".tactility" ttbuild_path = ".tactility"
ttbuild_version = "2.0.0" ttbuild_version = "2.1.0"
ttbuild_cdn = "https://cdn.tactility.one" ttbuild_cdn = "https://cdn.tactility.one"
ttbuild_sdk_json_validity = 3600 # seconds ttbuild_sdk_json_validity = 3600 # seconds
ttport = 6666 ttport = 6666
@ -60,8 +60,9 @@ def print_help():
print(" clean Clean the build folders") print(" clean Clean the build folders")
print(" clearcache Clear the SDK cache") print(" clearcache Clear the SDK cache")
print(" updateself Update this tool") print(" updateself Update this tool")
print(" run [ip] Run an application") print(" run [ip] Run the application")
print(" install [ip] Install an application") print(" install [ip] Install the application")
print(" uninstall [ip] Uninstall the application")
print(" bir [ip] [esp32,esp32s3] Build, install then run. Optionally specify a platform.") print(" bir [ip] [esp32,esp32s3] Build, install then run. Optionally specify a platform.")
print(" brrr [ip] [esp32,esp32s3] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.") print(" brrr [ip] [esp32,esp32s3] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.")
print("") print("")
@ -544,6 +545,19 @@ def install_action(ip, platforms):
except IOError as e: except IOError as e:
print_error(f"File error: {e}") print_error(f"File error: {e}")
def uninstall_action(manifest, ip):
app_id = manifest["app"]["id"]
print(f"Uninstalling {app_id} on {ip}")
url = get_url(ip, "/app/uninstall")
params = {'id': app_id}
try:
response = requests.put(url, params=params)
if response.status_code != 200:
print_error("Uninstall failed")
else:
print(f"{shell_color_green}Uninstall successful ✅{shell_color_reset}")
except requests.RequestException as e:
print(f"Request failed: {e}")
#region Main #region Main
if __name__ == "__main__": if __name__ == "__main__":
@ -599,6 +613,11 @@ if __name__ == "__main__":
platform = sys.argv[3] platform = sys.argv[3]
platforms_to_install = [platform] platforms_to_install = [platform]
install_action(sys.argv[2], platforms_to_install) install_action(sys.argv[2], platforms_to_install)
elif action_arg == "uninstall":
if len(sys.argv) < 3:
print_help()
exit_with_error("Commandline parameter missing")
uninstall_action(manifest, sys.argv[2])
elif action_arg == "bir": elif action_arg == "bir":
if len(sys.argv) < 3: if len(sys.argv) < 3:
print_help() print_help()

View File

@ -100,4 +100,6 @@ std::string getInstallPath();
bool install(const std::string& path); bool install(const std::string& path);
bool uninstall(const std::string& appId);
} }

View File

@ -11,6 +11,9 @@ struct AppManifest;
/** Register an application with its manifest */ /** Register an application with its manifest */
void addApp(const AppManifest& manifest); void addApp(const AppManifest& manifest);
/** Remove an app from the registry */
bool removeApp(const std::string& id);
/** Find an application manifest by its id /** Find an application manifest by its id
* @param[in] id the manifest id * @param[in] id the manifest id
* @return the application manifest if it was found * @return the application manifest if it was found

View File

@ -41,6 +41,13 @@ class DevelopmentService final : public Service {
.user_ctx = this .user_ctx = this
}; };
httpd_uri_t appUninstallEndpoint = {
.uri = "/app/uninstall",
.method = HTTP_PUT,
.handler = handleAppUninstall,
.user_ctx = this
};
void onNetworkConnected(); void onNetworkConnected();
void onNetworkDisconnected(); void onNetworkDisconnected();
@ -50,6 +57,7 @@ class DevelopmentService final : public Service {
static esp_err_t handleGetInfo(httpd_req_t* request); static esp_err_t handleGetInfo(httpd_req_t* request);
static esp_err_t handleAppRun(httpd_req_t* request); static esp_err_t handleAppRun(httpd_req_t* request);
static esp_err_t handleAppInstall(httpd_req_t* request); static esp_err_t handleAppInstall(httpd_req_t* request);
static esp_err_t handleAppUninstall(httpd_req_t* request);
public: public:

View File

@ -223,4 +223,25 @@ bool install(const std::string& path) {
return true; return true;
} }
bool uninstall(const std::string& appId) {
TT_LOG_I(TAG, "Uninstalling app %s", appId.c_str());
auto app_path = getInstallPath() + "/" + appId;
return file::withLock<bool>(app_path, [&app_path, &appId]() {
if (!file::isDirectory(app_path)) {
TT_LOG_E(TAG, "App %s not found at ", app_path.c_str());
return false;
}
if (!file::deleteRecursively(app_path)) {
return false;
}
if (!removeApp(appId)) {
TT_LOG_W(TAG, "Failed to remove app %d from registry", appId.c_str());
}
return true;
});
}
} // namespace } // namespace

View File

@ -29,6 +29,15 @@ void addApp(const AppManifest& manifest) {
hash_mutex.unlock(); hash_mutex.unlock();
} }
bool removeApp(const std::string& id) {
TT_LOG_I(TAG, "Removing manifest for %s", id.c_str());
auto lock = hash_mutex.asScopedLock();
lock.lock();
return app_manifest_map.erase(id) == 1;
}
_Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) { _Nullable std::shared_ptr<AppManifest> findAppById(const std::string& id) {
hash_mutex.lock(); hash_mutex.lock();
auto result = app_manifest_map.find(id); auto result = app_manifest_map.find(id);

View File

@ -110,6 +110,7 @@ void DevelopmentService::startServer() {
httpd_register_uri_handler(server, &handleGetInfoEndpoint); httpd_register_uri_handler(server, &handleGetInfoEndpoint);
httpd_register_uri_handler(server, &appRunEndpoint); httpd_register_uri_handler(server, &appRunEndpoint);
httpd_register_uri_handler(server, &appInstallEndpoint); httpd_register_uri_handler(server, &appInstallEndpoint);
httpd_register_uri_handler(server, &appUninstallEndpoint);
TT_LOG_I(TAG, "Started on port %d", config.server_port); TT_LOG_I(TAG, "Started on port %d", config.server_port);
} else { } else {
TT_LOG_E(TAG, "Failed to start"); TT_LOG_E(TAG, "Failed to start");
@ -297,6 +298,39 @@ esp_err_t DevelopmentService::handleAppInstall(httpd_req_t* request) {
return ESP_OK; return ESP_OK;
} }
esp_err_t DevelopmentService::handleAppUninstall(httpd_req_t* request) {
TT_LOG_I(TAG, "PUT /app/uninstall");
std::string query;
if (!network::getQueryOrSendError(request, query)) {
return ESP_FAIL;
}
auto parameters = network::parseUrlQuery(query);
auto id_key_pos = parameters.find("id");
if (id_key_pos == parameters.end()) {
TT_LOG_W(TAG, "[400] /app/uninstall id not specified");
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "id not specified");
return ESP_FAIL;
}
if (!app::findAppById(id_key_pos->second)) {
TT_LOG_I(TAG, "[200] /app/uninstall %s (app wasn't installed)", id_key_pos->second.c_str());
httpd_resp_send(request, nullptr, 0);
return ESP_OK;
}
if (app::uninstall(id_key_pos->second)) {
TT_LOG_I(TAG, "[200] /app/uninstall %s", id_key_pos->second.c_str());
httpd_resp_send(request, nullptr, 0);
return ESP_OK;
} else {
TT_LOG_W(TAG, "[500] /app/uninstall %s", id_key_pos->second.c_str());
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to uninstall");
return ESP_FAIL;
}
}
// endregion // endregion
std::shared_ptr<DevelopmentService> findService() { std::shared_ptr<DevelopmentService> findService() {