From 4376a9b96714b02b55cb727518d6be13a426b23b Mon Sep 17 00:00:00 2001 From: ciripel Date: Mon, 2 Feb 2026 15:23:27 +0200 Subject: [PATCH] add `asctl cat` option --- readme.md | 7 ++++++- src/ascend/server.lua | 23 ++++++++++++++++++++++ src/ascend/services.lua | 32 +++++++++++++++++++++++++++++++ src/asctl.lua | 42 ++++++++++++++++++++++++++++++++++++++++- tests/asctl.lua | 42 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 143 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index c3ac0b1..4347e9f 100644 --- a/readme.md +++ b/readme.md @@ -82,6 +82,11 @@ asctl status ```bash asctl show ``` + +* #### Show Service Definition File: +```bash +asctl cat +``` Replace `` with the name of your service. ## Logging @@ -116,4 +121,4 @@ ascend --log-level=trace #### 4. Ascend control commands: -In another terminal u can run the `asctl` commands to check the running service. \ No newline at end of file +In another terminal u can run the `asctl` commands to check the running service. diff --git a/src/ascend/server.lua b/src/ascend/server.lua index 6ff6815..085e0cf 100644 --- a/src/ascend/server.lua +++ b/src/ascend/server.lua @@ -252,6 +252,29 @@ local methodHandlers = { return end + respond({ + success = true, + data = result + }) + end, + cat = function(request, respond) + if not check_params(request.params, check_is_array_of_strings, respond) then + return + end + + if not check_params(request.params, check_manages_just_managed_services, respond) then + return + end + + local result, err = services.cat(request.params) + if not result then + respond(nil, { + code = jsonrpc.error_codes.INTERNAL_ERROR, + message = err or "unknown error" + }) + return + end + respond({ success = true, data = result diff --git a/src/ascend/services.lua b/src/ascend/services.lua index 0ea2c8e..645076a 100644 --- a/src/ascend/services.lua +++ b/src/ascend/services.lua @@ -194,6 +194,38 @@ function services.show(names) return result end +---@param names string[] +---@return table?, string? +function services.cat(names) + local result = {} + for _, name in ipairs(names) do + local service_name = name_to_service_module(name) + local service = managed_services[service_name] + if not service then + local msg = string.interpolate("service ${name} not found", { name = service_name }) + return nil, msg + end + + local source = service.source + if type(source) ~= "string" then + local msg = string.interpolate("service ${name} source not found", { name = service_name }) + return nil, msg + end + + local content, err = fs.read_file(source) + if not content then + local msg = string.interpolate("failed to read ${source}: ${error}", { source = source, error = err }) + return nil, msg + end + + result[service_name] = { + source = source, + content = content + } + end + return result +end + ---@class StartOptions ---@field manual boolean? ---@field is_boot boolean? diff --git a/src/asctl.lua b/src/asctl.lua index 4045ebb..c6c9fdd 100644 --- a/src/asctl.lua +++ b/src/asctl.lua @@ -101,7 +101,7 @@ local commands = { end print(format.status(response.data)) end, - show = function(parameters, options) + show = function(parameters, _) local response, err = client.execute("show", parameters) if not response then log_error(err --[[ @as string ]]) @@ -109,6 +109,46 @@ local commands = { end print(format.show(response.data)) end, + cat = function(parameters, _) + local response, err = client.execute("cat", parameters) + if not response then + log_error(err --[[ @as string ]]) + os.exit(1) + end + + local output = response.data + local printed = false + local function emit(service_name) + local entry = output[service_name] + if not entry then + return + end + if printed then + io.write("\n") + end + if type(entry.source) == "string" then + print("# " .. entry.source) + end + if type(entry.content) == "string" then + io.write(entry.content) + if entry.content:sub(-1) ~= "\n" then + io.write("\n") + end + end + printed = true + end + + if type(parameters) == "table" and #parameters > 0 then + for _, name in ipairs(parameters) do + local service_name = name:match("^(.+):") or name + emit(service_name) + end + else + for service_name, _ in pairs(output) do + emit(service_name) + end + end + end, logs = function(parameters, options) local response, err = client.execute("logs", parameters) if not response then diff --git a/tests/asctl.lua b/tests/asctl.lua index 08c1cc2..d336937 100644 --- a/tests/asctl.lua +++ b/tests/asctl.lua @@ -1,6 +1,8 @@ local test = TEST or require "u-test" local new_test_env = require "common.test-env" +DISABLE_CLEANUP = false --- disable to see the tmp directory + test["asctl - list"] = function() ---@type AscendTestEnvOptions local options = { @@ -360,7 +362,7 @@ test["asctl - reload"] = function() test.assert(result, err) end -test["asctl - ascend-healh"] = function() +test["asctl - ascend-health"] = function() ---@type AscendTestEnvOptions local options = { services = { @@ -479,6 +481,44 @@ test["asctl - show"] = function() test.assert(result, err) end +test["asctl - cat"] = function() + ---@type AscendTestEnvOptions + local options = { + services = { + ["date"] = { + source_path = "assets/services/simple-date.hjson", + } + }, + assets = { + ["scripts/date.lua"] = "assets/scripts/date.lua" + } + } + + local result, err = new_test_env(options):run(function(env, ascendOutput) + local startTime = os.time() + while true do -- wait for service started + local line = ascendOutput:read("l", 2) + if line and line:match("date:default started") then + break + end + if os.time() > startTime + 10 then + return false, "Service did not start in time" + end + end + + local ok, outputOrError = env:asctl({ "cat", "date" }) + if not ok then + return false, outputOrError + end + + local output = outputOrError + return output:match("# .+date%.hjson") and + output:match("executable") and + output:match("date%.lua") + end):result() + test.assert(result, err) +end + test["asctl - logs"] = function() ---@type AscendTestEnvOptions local options = {