feat: guess whether command was invoked non-interactively

Previously, it was made possible to control the interactivity status of the
progress bar by setting `_G.interactive`. However, this was not
automatically inferred from the context the process was started from,
and was expected to be manually set by the library invoker.

Non-interactivity status is now inferred automatically based on the
context the command was called from.
This commit is contained in:
Erica Marigold 2025-04-19 08:44:41 +01:00
parent 369f6bb714
commit 90386f1ec9
Signed by: DevComp
SSH key fingerprint: SHA256:jD3oMT4WL3WHPJQbrjC3l5feNCnkv7ndW8nYaHX5wFw
2 changed files with 58 additions and 1 deletions

View file

@ -30,6 +30,7 @@ local ProgressBar = require("./utils/progress")
local compression = require("./compression") local compression = require("./compression")
local eq = require("./utils/eq") local eq = require("./utils/eq")
local manifest = require("./manifest") local manifest = require("./manifest")
local sys = require("./utils/sys")
export type ToolId = { export type ToolId = {
alias: Option<string>, alias: Option<string>,
@ -165,8 +166,12 @@ local function getGithubToken(): Option<string>
end) end)
end end
local function isInteractive(): boolean
return sys.isTTY("stdout") and process.env.TERM ~= "dumb" and process.env.CI == nil
end
-- Initialize the shared global progress bar state -- Initialize the shared global progress bar state
_G.interactive = nil _G.interactive = isInteractive() or nil
local barFns local barFns
function installTool(tool: ToolId, installPath: pathfs.Path): number function installTool(tool: ToolId, installPath: pathfs.Path): number

View file

@ -0,0 +1,52 @@
--> Non-exhaustive set of util functions for Linux systems
local process = require("@lune/process")
local ResultExt = require("./ext/result")
local fs = require("../../lune_packages/pathfs").fs
local Result = require("../../lune_packages/result")
local Option = require("../../lune_packages/option")
local sys = {}
sys.consts = {
S_IFMT = 0xF000,
S_IFCHR = 0x2000,
}
export type StdioFd = "stdin" | "stdout" | "stderr"
local STDIO_FD_LOOKUP: { [StdioFd]: number } = {
stdin = 0,
stdout = 1,
stderr = 2,
}
function sys.isCharacterDevice(mode: number)
return bit32.band(mode, sys.consts.S_IFMT) == sys.consts.S_IFCHR
end
function sys.isTTY(fd: number | StdioFd): boolean
if process.os ~= "linux" then
-- idk about windows and macOS :p
return true
end
local inputFd: number = if typeof(fd) == "string" then STDIO_FD_LOOKUP[fd] else fd
local statFd = fs.readFile("/proc/self/stat")
local pid = assert(string.match(statFd, "^%S+%s+%S+%s+%S+%s+(%S+)"), "Could not get current process PID")
local stdoutFdMode = ResultExt.ok(
Result.try(process.spawn, "stat", { "-L", "-c", "%f", `/proc/{pid}/fd/{inputFd}` })
)
:andThen(function(child: process.SpawnResult)
if not child.ok then
return Option.None
end
return Option.from(tonumber(child.stdout, 16))
end)
return stdoutFdMode:isSomeAnd(sys.isCharacterDevice)
end
return sys