Repository navigation
Releases: jhuebel/Automator
Release list
v2.5.1 — Fix stale test suite
Bugfix: stale test suite
No app behavior changes — this release fixes the test suite itself, which had drifted from the app over time. Root-caused all 23 previously-failing tests to two distinct issues rather than treating them as one bug:
What was wrong
UserFactorynever setusername— a required, unique column added when this app moved from Laravel's default email-based login to username-based login. Every test creating a user via the factory failed at the database insert before its actual logic ran.- Several auth tests still targeted pre-customization routes and components. This app replaced Breeze's self-service auth scaffold with admin-provisioned accounts (Settings → Users) and SSO: self-registration, self-service password reset, and self-service email verification were all deliberately removed,
/profilewas renamed to/my-account, and the navigation component was split intolayout.header/layout.sidebar. The leftover scaffold tests were never updated to match.
What changed
- Fixed
UserFactoryto setusername. - Removed
RegistrationTest,PasswordResetTest, andEmailVerificationTestoutright — they exercised routes and components that no longer exist by design, not bugs to fix. - Updated
AuthenticationTestandProfileTestto match the app's actual current routes, component names, and behavior (including that login now authenticates by username, and the profile-email form no longer resets verification status, since that concept doesn't apply to admin/SSO-provisioned accounts).
Test suite: 52 tests / 29 passing / 23 failing → 41 tests / 41 passing.
v2.5.0 — Runner Groups
Runner Groups
Runners can now be grouped into named, many-to-many pools — by region, by proximity to the resources they automate, or any other grouping that makes sense for your fleet — so a script or scheduled job can target "whichever eligible runner in this group is available" instead of pinning to one specific runner.
Why
Routing was previously single-runner only: pin to one exact runner, or auto-pick the least-busy runner fleet-wide by tags and language. There was no way to say "run this against whichever of these particular runners is free" for a set of runners that don't necessarily have identical capabilities individually but collectively cover what a job needs.
What's new
- Runner Groups — create named groups from Settings → Runner Groups and assign runners to them; a runner can belong to more than one group (e.g. a host that's both
us-eastandshared-terraform). - Aggregate eligibility — a group is eligible for a language or a set of required tags if any member runner qualifies, not all of them — since an execution always ends up running on one concrete runner,
RunnerAssignmentServicepicks the least-busy eligible member of the targeted group at assignment time. The Settings tab shows this as aggregate language badges per group. - Group-aware pickers — the Run Script and Scheduled Jobs runner dropdowns now list both runner groups and individual runners (in separate sections), and scheduled jobs can be pinned to a group via
preferred_runner_group_id, mutually exclusive with pinning to a single runner. - Cross-navigation — each runner's expandable detail row in Settings → Runners now shows which groups it belongs to.
- Purely a management-plane concept — the Go runner binary is unchanged; groups are never sent to or evaluated by the runner itself.
Upgrading
Two new tables (runner_groups, runner_group_runner) and one new column
(preferred_runner_group_id on scheduled_jobs) — run php artisan migrate --force as usual.
No existing behavior changes until you create a group and start targeting it.
v2.4.0 — Runner self-update mechanism
Runner self-update mechanism
Runners can now check in with the management plane for newer released builds and update themselves in place, closing the last piece of fleet operations that still required manually rebuilding, copying, and restarting each runner by hand.
Why
As the runner fleet grows, keeping every host's binary current is real toil, and until now there was no visibility into which runners were running stale code beyond the version shown in Settings → Runners. This reuses the existing 15s heartbeat as the discovery channel and the runner's existing Sanctum bearer token for the download — no new polling loop, no new credential type.
What's new
- Self-update over heartbeat — if Settings → Runners' "Automatically offer runner updates" toggle is on, a heartbeat response includes an
updatefield whenever a newer released build exists for that runner's os/arch. The runner downloads it, verifies its sha256 checksum, replaces its own binary in place (atomic rename on Linux; rename-aside-then-write on Windows), and exits cleanly so the process supervisor (systemd/NSSM) relaunches it — never interrupting a script that's currently running. - Two-step publish → release, always —
automator:publish-runner-binarystores a binary as a draft (checksum + size computed, never offered to the fleet). Only a separate, explicitautomator:release-runner-binarymakes it live. A bad build can never reach the fleet automatically and unattended.packaging/build.shnow publishes both cross-compiled binaries on every build, but never auto-releases. - Rollback is just re-releasing — releases are ordered by
released_at, not semver, so shipping an older version again is the entire rollback procedure. - Settings visibility — the global toggle and a per-runner "(update to X.Y.Z available)" hint (shown independent of the toggle, so staleness is visible even with auto-update off) were added to Settings → Runners.
- New
automator-runner versionsubcommand for standalone on-host debugging.
Verified end-to-end against a live runner: idle-gate deferral while a job is running, successful update + supervisor restart once the job finishes, checksum-mismatch refusal leaving the running binary untouched, and rollback via re-release. The Windows replace/restart path is implemented and unit-tested but hasn't been exercised on a live Windows host yet.
Upgrading
One new table (runner_releases) and one new column (runner_auto_update_enabled on app_settings) — run php artisan migrate --force as usual. Auto-update is off by default; nothing changes for existing runners until you publish a build, release it, and turn the toggle on.
v2.3.0 — Runner selection, colored output, and capability reporting
Runner selection, colored output, and runner capability reporting
A cluster of related improvements to how scripts get routed to runners, what
you can see about a runner's actual capabilities, and how output is
displayed.
Why
With multiple runners in a fleet, "which runner will run this?" and "why did
this fail?" were both harder to answer than they should be — assignment was
always automatic, output with color codes rendered as literal escape-sequence
garbage, and there was no visibility into a runner's own health (version,
architecture, disk space) beyond whether it was online.
What's new
- Manual runner selection — Run Script and Scheduled Jobs pages let you
pick a specific runner instead of Auto (least-busy). The picker only lists
runners that report the script's language as installed;RunnerAssignmentService
enforces this server-side too, with a specific failure message
(offline / at capacity / wrong language) instead of a generic one.
Scheduled jobs can be permanently pinned to a runner via
preferred_runner_id. - Real ANSI color rendering — script output now renders actual color and
style (16-color palette, bold, dim, italic, underline, reverse, blink)
instead of showing raw\x1b[...mescape codes as literal text. Applies to
both the live Run Script terminal and the static History detail view. New
resources/js/ansi.js(24 unit tests) plus three sample scripts
(ANSI Color Palette (Bash),ANSI Color Palette (PowerShell),
Colored Status Report (Bash)) to try it out immediately. - Runner capability visibility — History shows which runner ran each
script (with live status), and Settings → Runners now has an expandable
detail row showing per-language support, the runner's own binary version,
CPU architecture, and free/total disk space on its temp directory — so a
stale runner or one running low on disk is visible instead of silently
failing later. - UI polish — Run Script's output terminal now fills the page instead of
stopping at a fixed height, and correctly clears when switching between
scripts instead of leaking the previous script's output underneath the
newly-selected one. - Runner bugfix — fixed a PATH-resolution mismatch where the runner could
report an interpreter (e.g. a Homebrew-installedpwsh) as available in
its heartbeat, but actual script execution still failed to find it because
it only checked the process's inheritedPATH, not the same fallback
search used for detection. Runner binary bumped to 1.2.0.
Upgrading
Two new migrations (preferred_runner_id on scheduled_jobs; version,
arch, disk_free_bytes, disk_total_bytes on runners) — run
php artisan migrate --force as usual. Existing runners will start
reporting the new capability fields on their next heartbeat after being
restarted with the updated binary; until then those fields just show as
unset, nothing breaks.
v2.2.0 — SSO login via Microsoft Entra ID and Google
SSO login via Microsoft Entra ID and Google
Automator now supports Single Sign-On as an additional login method — username/password
login is unchanged and still fully supported. Everything is configured from Settings →
Single Sign-On; no .env changes or redeploys required.
Why
Rolling Automator out to a team usually means plugging into whatever identity provider
the organization already uses, rather than managing a separate set of local credentials.
Entra ID (Azure AD) and Google cover the two most common cases.
What's new
- Settings → Single Sign-On tab: per-provider enable toggle, Client ID/Secret,
Tenant ID (Entra), and an optional comma-separated allowed-email-domains restriction.
Secrets are encrypted at rest and write-only in the UI, the same as the existing
Anthropic API key field. - Admin-configurable account provisioning policy — choose whether a first-time SSO
login automatically creates a new account (with a configurable default role), or is
rejected until an admin has already created a matching-email account in Settings →
Users. - Automatic account linking — if an admin already created an account with a matching
email, the first SSO login for that email links it automatically; no manual linking
step. - Accounts created via auto-provisioning have no local password (they can only sign in
via SSO) — the My Account page now shows a clear message instead of a broken password
form for those accounts. - New
docs/SSO.mdwalks through Entra ID app registration and Google Cloud OAuth client
setup end to end, including the exact redirect URIs to register.
Upgrading
Migrations add nullable entra_object_id/google_id columns to users (existing
accounts are unaffected) and new SSO settings columns to app_settings, all off by
default — SSO is inert until an admin enables a provider in Settings. Run
php artisan migrate --force as usual.
v2.1.0 — Distributed Go runner agents
Distributed Go runner agents
Script execution is no longer tied to the same host as the management plane. Automator
now dispatches every script run to a standalone runner — a small Go agent that can
live on any Linux or Windows host, registers securely, and streams output back in real
time. Multiple runners are supported, so a fleet can offer different capabilities
(Windows-only scripts, specific tool availability, network placement, etc.).
Why
The previous model ran scripts inline in the Laravel queue worker on whatever single
Linux host was hosting the app — cancellation even relied on the web request and the
executing process sharing a PID namespace. That made it impossible to run scripts
anywhere but the one box Automator itself was installed on.
What changed
- New execution model: script runs are assigned to a registered
Runner(least-busy
among online runners, with tag-based routing viarequired_runner_tags) instead of
running inline in a local queue worker. - Secure registration: a one-time enrollment token exchange issues each runner a
Sanctum bearer token; revoking a runner from Settings → Runners immediately cuts it off. - Real-time job delivery: runners receive job assignment/cancellation over a private
Laravel Reverb WebSocket channel — the same broadcasting layer already powering the
browser's live output terminal — and report output/heartbeats back over authenticated
HTTPS REST. - Heartbeat-based offline detection: a runner that misses its heartbeats is marked
offline and any of its in-flight executions are automatically failed with a clear
"runner disconnected" message — no silent hangs. - Full cancellation support:
SIGTERMon Linux;CTRL_BREAK_EVENTwith a
hard-kill fallback on Windows. - No local execution fallback — every run, including on a single-box install, goes
through a registered runner. Install scripts now auto-register a local runner on the
same host during setup, so single-box installs still work out of the box. - Removed:
RunScriptJob, theexecutionsqueue,automator-worker@.service, and
the globalmax_concurrent_executionssetting (replaced by per-runner
max_concurrent_jobs).
See docs/ARCHITECTURE.md, docs/DATA_MODEL.md, and docs/RUNNER_PROTOCOL.md for the
full technical reference.
Upgrading
Existing installs should re-run the packaged install script (or pull the new
packaging/ scripts), which now provisions and registers a local runner automatically
during setup. Additional runners — Linux or Windows, on any host — can be registered at
any time from Settings → Runners.
v2.0.0 — Rewrite from .NET Blazor Server to Laravel
Major rewrite: .NET Blazor Server → Laravel
Automator has been completely rebuilt on a new stack. This is the same tool with the same feature set — script library, live-streamed script execution, cron scheduling, a Claude-powered AI assistant, role-based access, audit logging, and an admin dashboard — but the entire implementation has changed from the ground up.
Why
The previous ASP.NET Core 9 / Blazor Server implementation tied real-time UI updates directly to Blazor's SignalR circuit model and required a self-contained .NET runtime per platform. This release moves to a PHP/Laravel stack that's easier to self-host on standard Linux infrastructure (PHP-FPM + nginx), with an explicit WebSocket layer (Laravel Reverb) replacing Blazor's built-in circuit-based streaming.
What changed
- Framework: ASP.NET Core 9 / Blazor Server → Laravel 13 with Livewire 3 + Volt
- Real-time updates: Blazor Server's SignalR circuits → Laravel Reverb (WebSocket broadcasting) for live script output and AI token streaming
- Auth & roles: ASP.NET Core Identity → spatie/laravel-permission (Admin / Developer / Operator / Viewer), username-based login with lockout
- Script execution: in-process
ScriptRunnerService→ queuedRunScriptJob(Symfony Process) on a dedicatedexecutionsqueue, still supporting Bash, PowerShell, Python, Ansible, and Terraform (init + apply) - Scheduler: 15-second in-process background tick → cron-driven
dispatch-due-jobscommand on a systemd timer (60-second granularity) - AI assistant: Guzzle-based SSE streaming from the Anthropic API, broadcast token-by-token into a CodeMirror 6 editor (previously CodeMirror 5)
- Dashboard: charts now rendered with Chart.js
- Deployment: self-contained .NET binary + systemd + nginx → PHP-FPM + nginx, with new systemd units for queue workers, Reverb, and the scheduler tick
- Windows/IIS packaging has been dropped — this release targets Linux (Ubuntu, RHEL/Rocky/AlmaLinux) only
Upgrading
This is a from-scratch application — there is no automatic data migration path from a v1.x SQLite database. Fresh installs seed default roles, users, and example scripts as before. See docs/INSTALL.md for the updated installation steps.
Default credentials (change immediately)
| Username | Password | Role |
|---|---|---|
admin |
Admin1234! |
Admin |
operator |
Operator1234! |
Operator |
viewer |
Viewer1234! |
Viewer |
v1.7.0
Changes
- Refreshed the Claude model lineup in AI Assistant settings: Haiku 4.5, Sonnet 5 (new recommended default), Opus 4.8, and Fable 5 (frontier reasoning, premium). Removes the retired Sonnet 4.6 / Opus 4.7 entries.
- Added an Effort setting (Low / Medium / High / Max) controlling how much the model "thinks" before responding — trades quality against latency and token cost. Disabled for Haiku, which doesn't support the parameter.
- Existing installs with a stale/retired model string in Settings are automatically rolled forward to the new default (
claude-sonnet-5) on startup.
🤖 Generated with Claude Code
v1.6.0
What's New
Terraform Support
- Execute Terraform scripts directly from Automator — runs
terraform initthenterraform apply -auto-approvein a temp working directory - HCL syntax highlighting in the script editor
- Variables are automatically exposed via
TF_VAR_<name>convention (var.namein your HCL) - Terraform runtime check in Settings → System Status
- Sample "Hello World" Terraform script included
MudBlazor Charts
- Replaced Chart.js with MudBlazor's built-in stacked bar chart on the Dashboard — no external JS dependency
Deployment Packages
- Self-contained archives for Ubuntu, RHEL / Rocky / AlmaLinux, and Windows Server
- Each Linux archive includes a hardened systemd unit, nginx reverse proxy config, and distro-specific install/uninstall scripts
- Windows archive includes PowerShell scripts to install as a Windows Service with optional IIS ARR reverse proxy
Download
| Platform | Archive |
|---|---|
| Ubuntu / RHEL (x64) | automator-1.6.0-linux-x64.tar.gz |
| Windows Server (x64) | automator-1.6.0-win-x64.zip |
See docs/INSTALL.md for installation instructions.