Support -f on service redeploy (C and Python SDKs)

Pass input_files to the redeploy API so un service --redeploy $ID -f
repo.tar.gz works. Files are base64-encoded and sent in the JSON body,
same as service create. C library API signature unchanged (internal
static function extended).
This commit is contained in:
russell@unturf.com 2026-03-01 21:30:55 -05:00
parent de8c816b27
commit ac50544ac3
2 changed files with 46 additions and 4 deletions

View file

@ -4643,7 +4643,7 @@ static char* read_env_stdin(void) {
// Redeploy a service (re-run bootstrap script)
// Bootstrap scripts should be idempotent for proper upgrade behavior
static int redeploy_service(const UnsandboxCredentials *creds, const char *service_id, const char *bootstrap) {
static int redeploy_service(const UnsandboxCredentials *creds, const char *service_id, const char *bootstrap, struct InputFile *input_files, int input_file_count) {
CURL *curl = curl_easy_init();
if (!curl) return 1;
@ -4692,6 +4692,9 @@ static int redeploy_service(const UnsandboxCredentials *creds, const char *servi
} else if (bootstrap_url) {
payload_size += strlen(bootstrap_url) * 2 + 100;
}
for (int i = 0; i < input_file_count; i++) {
payload_size += strlen(input_files[i].content_base64) + 256;
}
// Build JSON payload manually (matching create_service pattern)
char *payload = malloc(payload_size);
@ -4704,15 +4707,31 @@ static int redeploy_service(const UnsandboxCredentials *creds, const char *servi
char *p = payload;
p += sprintf(p, "{");
int has_field = 0;
if (bootstrap_content) {
char *esc_content = escape_json_string(bootstrap_content);
p += sprintf(p, "\"bootstrap_content\":\"%s\"", esc_content);
free(esc_content);
free(bootstrap_content);
has_field = 1;
} else if (bootstrap_url) {
char *esc_url = escape_json_string(bootstrap_url);
p += sprintf(p, "\"bootstrap\":\"%s\"", esc_url);
free(esc_url);
has_field = 1;
}
if (input_file_count > 0) {
if (has_field) p += sprintf(p, ",");
p += sprintf(p, "\"input_files\":[");
for (int i = 0; i < input_file_count; i++) {
if (i > 0) p += sprintf(p, ",");
char *esc_filename = escape_json_string(input_files[i].filename);
p += sprintf(p, "{\"filename\":\"%s\",\"content\":\"%s\"}",
esc_filename, input_files[i].content_base64);
free(esc_filename);
}
p += sprintf(p, "]");
}
p += sprintf(p, "}");
@ -7100,7 +7119,7 @@ int unsandbox_service_redeploy(const char *service_id, const char *bootstrap,
const char *public_key, const char *secret_key) {
UnsandboxCredentials *creds = get_credentials(public_key, secret_key, -1);
if (!creds) return -1;
int result = redeploy_service(creds, service_id, bootstrap);
int result = redeploy_service(creds, service_id, bootstrap, NULL, 0);
free_credentials(creds);
return result;
}
@ -10489,7 +10508,12 @@ int main(int argc, char *argv[]) {
// - If provided via --bootstrap or --bootstrap-file, use it
// - If omitted, API will use the stored encrypted bootstrap
const char *bootstrap_to_use = bootstrap_file ? bootstrap_file : service_bootstrap;
ret = redeploy_service(creds, service_id, bootstrap_to_use);
ret = redeploy_service(creds, service_id, bootstrap_to_use, service_input_files, service_input_file_count);
// Free input file memory
for (int i = 0; i < service_input_file_count; i++) {
free(service_input_files[i].filename);
free(service_input_files[i].content_base64);
}
} else if (do_execute) {
// Pass input files if provided (written to /tmp/input/ before command runs)
ret = execute_service(creds, service_id, execute_command, execute_timeout, service_input_files, service_input_file_count);

View file

@ -1609,6 +1609,7 @@ def redeploy_service(
bootstrap: Optional[str] = None,
public_key: Optional[str] = None,
secret_key: Optional[str] = None,
input_files: Optional[List[Dict[str, str]]] = None,
) -> Dict[str, Any]:
"""
Redeploy a service (re-run bootstrap script).
@ -1620,6 +1621,7 @@ def redeploy_service(
bootstrap: Optional new bootstrap script/URL (uses existing if not provided)
public_key: Optional API key
secret_key: Optional API secret
input_files: Optional list of dicts with "filename" and "content" (base64)
Returns:
Response dict with redeploy confirmation
@ -1636,6 +1638,8 @@ def redeploy_service(
data["bootstrap"] = bootstrap
else:
data["bootstrap_content"] = bootstrap
if input_files:
data["input_files"] = input_files
return _make_request("POST", f"/services/{service_id}/redeploy", public_key, secret_key, data)
@ -2983,7 +2987,21 @@ def _handle_service_command(args, public_key: str, secret_key: str):
bootstrap = f.read()
elif args.bootstrap:
bootstrap = args.bootstrap
result = redeploy_service(args.redeploy, bootstrap=bootstrap, public_key=public_key, secret_key=secret_key)
# Build input_files from -f args
service_input_files = None
if getattr(args, 'files', None):
import base64 as _b64
service_input_files = []
for fpath in args.files:
with open(fpath, "rb") as ff:
encoded = _b64.b64encode(ff.read()).decode("ascii")
service_input_files.append({
"filename": os.path.basename(fpath),
"content": encoded,
})
result = redeploy_service(args.redeploy, bootstrap=bootstrap, input_files=service_input_files, public_key=public_key, secret_key=secret_key)
print(f"Service {args.redeploy} redeployed")
elif args.execute:
service_id, command = args.execute