Complete API documentation for NixActions.
nixactions :: {
# Core constructors
mkWorkflow :: WorkflowConfig -> Derivation,
mkExecutor :: ExecutorConfig -> Executor,
mkMatrixJobs :: MatrixConfig -> AttrSet Job,
# Built-in executors
executors :: {
local :: LocalConfig -> Executor,
oci :: OciConfig -> Executor,
},
# Standard actions
actions :: { ... },
# Environment providers
envProviders :: { ... },
# Pre-built jobs
jobs :: { ... },
# Cross-platform support
linuxPkgs :: Packages, # Linux packages (for Darwin hosts)
}mkWorkflow :: {
name :: String,
jobs :: AttrSet Job,
env :: AttrSet String = {},
envFrom :: [Derivation] = [],
retry :: RetryConfig | Null = null,
timeout :: Int | Null = null,
extensions :: [Workflow -> Workflow] = [], # Workflow transformers
} -> Derivationnixactions.mkWorkflow {
name = "ci";
env = {
CI = "true";
};
envFrom = [
(nixactions.envProviders.file { path = ".env"; })
];
retry = {
max_attempts = 2;
backoff = "exponential";
};
jobs = {
test = { ... };
build = { ... };
};
}Extensions are functions that transform the workflow config before compilation.
They are applied left-to-right via lib.pipe.
# Validation extension
validateNotEmpty = workflow:
assert builtins.length (builtins.attrNames workflow.jobs) > 0;
workflow;
# Transform extension - add default timeout to all jobs
addDefaultTimeout = workflow:
workflow // {
jobs = lib.mapAttrs (name: job:
job // { timeout = job.timeout or "10m"; }
) workflow.jobs;
};
nixactions.mkWorkflow {
name = "ci";
extensions = [
validateNotEmpty
addDefaultTimeout
];
jobs = { ... };
}Job :: {
# Required
executor :: Executor,
steps :: [Step],
# Dependencies
needs :: [String] = [],
# Conditional execution
condition :: Condition = "success()",
# Error handling
continueOnError :: Bool = false,
# Environment
env :: AttrSet String = {},
envFrom :: [Derivation] = [],
# Artifacts
inputs :: [String | { name :: String, path :: String }] = [],
outputs :: AttrSet String = {},
# Retry/timeout
retry :: RetryConfig | Null = null,
timeout :: Int | Null = null,
}{
test = {
executor = nixactions.executors.local;
needs = ["lint"];
condition = "success()";
env = {
NODE_ENV = "test";
};
inputs = ["deps"];
outputs = {
coverage = "coverage/";
};
retry = {
max_attempts = 3;
backoff = "exponential";
};
timeout = 300;
steps = [
{ bash = "npm test"; }
];
};
}Step :: {
name :: String = "step",
bash :: String, # Required: bash script to execute
deps :: [Derivation] = [], # Packages available in PATH
env :: AttrSet String = {},
workdir :: Path | Null = null,
condition :: Condition | Null = null,
retry :: RetryConfig | Null = null,
timeout :: Int | Null = null,
}{
name = "test";
bash = "npm test";
deps = [ pkgs.nodejs ];
env = {
CI = "true";
};
condition = ''[ "$BRANCH" = "main" ]'';
retry = {
max_attempts = 3;
};
timeout = 120;
}
# Running non-bash scripts with lib.getExe:
{
name = "python-check";
bash = ''
${lib.getExe (pkgs.writers.writePython3 "check" {} ''
print("Running Python checks...")
'')}
'';
}Condition ::
| "success()" # Run if all deps succeeded (default)
| "failure()" # Run if any dep failed
| "always()" # Always run
| "cancelled()" # Run if workflow cancelled
| String # Bash script (exit 0 = run, exit 1 = skip)condition = "success()";
condition = "failure()";
condition = "always()";
condition = ''[ "$BRANCH" = "main" ]'';
condition = ''test -f package.json'';
condition = ''git diff --quiet'';RetryConfig :: {
max_attempts :: Int = 1,
backoff :: "exponential" | "linear" | "constant" = "exponential",
min_time :: Int = 1,
max_time :: Int = 60,
}# Exponential backoff
retry = {
max_attempts = 5;
backoff = "exponential";
min_time = 1;
max_time = 60;
};
# Linear backoff
retry = {
max_attempts = 3;
backoff = "linear";
min_time = 2;
max_time = 30;
};
# Constant delay
retry = {
max_attempts = 10;
backoff = "constant";
min_time = 5;
};
# Disable retry
retry = null;local :: {
copyRepo :: Bool = true,
name :: String | Null = null,
} -> ExecutorExamples:
# Default
executor = nixactions.executors.local
# Without repo copy
executor = nixactions.executors.local { copyRepo = false; }
# Custom name (separate workspace)
executor = nixactions.executors.local { name = "isolated"; }oci :: {
name :: String | Null = null,
mode :: "shared" | "isolated" = "shared",
copyRepo :: Bool = true,
extraPackages :: [Derivation] = [],
extraMounts :: [String] = [],
containerEnv :: AttrSet String = {},
} -> ExecutorExamples:
# Default (shared mode)
executor = nixactions.executors.oci {}
# With extra packages
executor = nixactions.executors.oci {
extraPackages = [ nixactions.linuxPkgs.git nixactions.linuxPkgs.curl ];
}
# Isolated mode (container per job)
executor = nixactions.executors.oci {
mode = "isolated";
}
# Custom mounts
executor = nixactions.executors.oci {
extraMounts = [ "/data:/data:ro" ];
}mkExecutor :: {
name :: String,
copyRepo :: Bool = true,
# Workspace level
setupWorkspace :: { actionDerivations :: [Derivation] } -> String,
cleanupWorkspace :: { actionDerivations :: [Derivation] } -> String,
# Job level
setupJob :: { jobName :: String, actionDerivations :: [Derivation] } -> String,
executeJob :: { jobName :: String, actionDerivations :: [Derivation], env :: AttrSet } -> String,
cleanupJob :: { jobName :: String } -> String,
# Artifacts
saveArtifact :: { name :: String, path :: String, jobName :: String } -> String,
restoreArtifact :: { name :: String, path :: String, jobName :: String } -> String,
} -> Executornixactions.actions.checkout :: Action
nixactions.actions.setupNode :: {
version :: String,
} -> Action
nixactions.actions.setupPython :: {
version :: String,
} -> Action
nixactions.actions.setupRust :: Actionnixactions.actions.nixShell :: [String] -> ActionExample:
(nixactions.actions.nixShell [ "curl" "jq" "git" ])nixactions.actions.npmInstall :: Action
nixactions.actions.npmTest :: Action
nixactions.actions.npmBuild :: Action
nixactions.actions.npmLint :: Actionnixactions.actions.sopsLoad :: {
file :: Path,
format :: "yaml" | "json" | "dotenv" = "yaml",
} -> Action
nixactions.actions.vaultLoad :: {
path :: String,
addr :: String | Null = null,
} -> Action
nixactions.actions.opLoad :: {
vault :: String,
item :: String,
} -> Action
nixactions.actions.ageDecrypt :: {
file :: Path,
identity :: Path,
} -> Action
nixactions.actions.bwLoad :: {
itemId :: String,
} -> Action
nixactions.actions.requireEnv :: [String] -> Actionnixactions.envProviders.file :: {
path :: String,
required :: Bool = false,
} -> Derivation
nixactions.envProviders.sops :: {
file :: Path,
format :: "yaml" | "json" | "dotenv" = "yaml",
required :: Bool = true,
} -> Derivation
nixactions.envProviders.static :: AttrSet String -> Derivation
nixactions.envProviders.required :: [String] -> DerivationenvFrom = [
(nixactions.envProviders.file {
path = ".env";
required = false;
})
(nixactions.envProviders.sops {
file = ./secrets.sops.yaml;
format = "yaml";
})
(nixactions.envProviders.static {
CI = "true";
NODE_ENV = "production";
})
(nixactions.envProviders.required [
"API_KEY"
"DATABASE_URL"
])
];mkMatrixJobs :: {
name :: String,
matrix :: AttrSet [Any],
executor :: Executor | (MatrixEntry -> Executor),
steps :: [Step] | (MatrixEntry -> [Action]),
# ... other Job fields
} -> AttrSet Jobnixactions.mkMatrixJobs {
name = "test";
matrix = {
node = [ "18" "20" "22" ];
os = [ "ubuntu" "macos" ];
};
executor = nixactions.executors.local;
actions = entry: [
{
bash = "echo Testing Node ${entry.node} on ${entry.os}";
}
];
}
# Generates:
# {
# test-node-18-os-ubuntu = { ... };
# test-node-18-os-macos = { ... };
# test-node-20-os-ubuntu = { ... };
# ...
# }nixactions.jobs.buildahBuildPush :: {
name :: String,
context :: String = ".",
dockerfile :: String = "Dockerfile",
image :: String,
tags :: [String] = ["latest"],
push :: Bool = true,
registry :: String | Null = null,
} -> Jobjobs = {
build-image = nixactions.jobs.buildahBuildPush {
name = "myapp";
image = "registry.example.com/myapp";
tags = [ "latest" "v1.0.0" ];
};
};- Core Contracts - Type definitions
- Actions - Actions deep dive
- Executors - Executor implementations
- Environment - Environment providers