| GitHub Actions | Status |
|---|---|
| Workflows |
CommonShell is a thin, typed shell adapter that builds on CommonProcess (>= 0.2.0) to plan and execute commands consistently across hosts and runners.
Add to your Package.swift dependencies:
.package(url: "https://github.com/wrkstrm/common-shell.git", from: "0.1.0")Then import and run a command:
import CommonShell
var shell = CommonShell(executable: .name("git"))
shell.hostKind = .env(options: [])
let out = try await shell.run(arguments: ["status"]) // throws on non-zero exit
print(out) ββββββββββββββββββββΆ β CommandSpec β What to run (ExecutableRef)
β
CommonShell βββΆββββββββββββββββββββΆ β Execution Host β How to wrap the tool
β
CommonProcess βββββββββββββββββββββΆ β Runner (route) β How to execute
β
InstrumentationββββββββββββββββββββΆ β Telemetry β Logging/metrics exposure
ExecutableReferencestays the source-of-truth for identity:.name("git"),.path("/usr/bin/git"),.none(argv-only).- Hosts describe how to wrap the executable before dispatching to CommonProcess:
.direct.shell(options:).env(options:).npm(options:).npx(options:)
- Runners (
ProcessRunnerKind) handle the actual execution surface (Subprocess/Foundation/TSCBasic/Native). - Instrumentation and metrics tag invocations automatically (host kind propagates via
ProcessLogOptions.tags).
public struct CommandSpec: Codable, Sendable {
public var executable: Executable
public var args: [String]
public var env: EnvironmentModel?
public var workingDirectory: String?
public var logOptions: ProcessLogOptions = .init()
public var requestId: String = UUID().uuidString
public var instrumentationKeys: [InstrumentationKey] = []
public var hostKind: ExecutionHostKind? = nil
public var runnerKind: ProcessRunnerKind? = nil
public var timeout: Duration? = nil
public var instrumentation: ProcessInstrumentation? = nil // runtime-only
}.name("git")selects PATH lookup β typicallyhostKind = .env..path("/usr/bin/git")βhostKind = .direct.- Wrapper helpers (e.g.
CommonShell.runShell,runEnv,runDirect,runNpxCommand) sethostKindfor you.
- Built-in keys:
.noop,.metrics(viaProcessMetricsRecorder). ProcessLogOptions.tags["executionHost"]records the host label automatically.
var shell = CommonShell(executable: .name("git"))
shell.hostKind = .env(options: [])
let status = try await shell.execute(arguments: ["status"], runnerKind: .auto)Convenience wrappers:
runDirectβ setshostKind = .directrunShellβ setshostKind = .shell(options:)runEnvβ setshostKind = .env(options:)runNpxCommandβ resolves node/npm CLI and setshostKind = .npx(options:)runForIntervalβ benchmarks a host Γ runner combination
All run APIs accept an optional timeout: parameter (Duration). When supplied, CommonShell cooperatively cancels the underlying runner once the timeout elapses and throws a ProcessError with timedOut = true.
Low-level entry point:
run(host:executable:arguments:runnerKind:)β explicitly supply the host transform and executable identity.
ShellRouteKindstill enumerates runners (.auto,.native,.subprocess(kind)).- Bench helpers (
BenchRoutes,BenchSupport) now operate over(host: ExecutionHostKind, executable: Executable, arguments: [String]) Γ [ShellRouteKind]. - CSV/JSON/Table outputs include both host label (
wrapper) and runner route (route).
- Hosts produce new
CommandSpecinstances with transformedExecutable+args. - CommonProcess runners execute any invocation regardless of host choice.
- Instrumentation/hardware metrics are shared across hosts and routes.
- API guides live under
Sources/CommonShell/Documentation.docc/. - Comparison for CLI authors:
CommonShell-Comparison-TuistCommand.md(when to use CommonShell vs Tuist Command).
MIT β see LICENSE.
- macOS 14+, iOS 17+, Mac Catalyst 17+
- CommonProcess (>= 0.2.0)
- WrkstrmLog/WrkstrmFoundation (>= 2.0.0)
- wrkstrm-performance (>= 0.1.0)
- Linux CI: build + test via swiftβci
- Format lint and DocC workflows mirror CommonProcess
Use CommonProcess/CommonShell runners; avoid Foundation.Process at call sites.
See CONTRIBUTING.md for guidelines.
Report via GitHub Security Advisories (see SECURITY.md).
See CHANGELOG.md.