Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to Wurk are recorded here. Format: [Keep a Changelog](https:

## [Unreleased]

### Changed

- **`bin/check` probes the Redis at `REDIS_URL`, not a pinned `127.0.0.1:6379`.** The guard now checks the server the suite actually connects to (`REDIS_URL`, which `test_helper` and `RedisPool` already read), and falls back to the suite's own default, `redis://localhost:6379/0`, when it is unset, so nothing changes for a local run. `bin/test-ecosystem` uses DB 15 of the same server. A developerz.ai box can now run this gate against a Redis on a private port, which leaves 6379 free for another repo's own store on the same box.

## [1.7.6] - 2026-09-07

One runtime fix that matters to anyone using encrypted jobs — on json 3.0.0, every one of them failed — plus the repo hygiene that had accumulated since 1.7.5: the gate a contributor runs before opening a PR now checks what CI checks, the docs stopped claiming things the tree does not do, and three timing-sensitive tests stopped failing for reasons that were never the code. No Redis key, command sequence, job-JSON field, or public API changed: a 1.7.5 worker and a 1.7.6 worker can drain the same queue.
Expand Down
14 changes: 7 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ treating it as unknown:

| Exit | Trigger | Source |
|---|---|---|
| `0` | Every stage passed, OR `-h` / `--help` / `help` printed the help block without running any stage. | `bin/check:201-204`, `bin/check:23-26` |
| `1` | At least one stage reported failure. | `bin/check:206-207` |
| `0` | Every stage passed, OR `-h` / `--help` / `help` printed the help block without running any stage. | `bin/check:210-213`, `bin/check:23-26` |
| `1` | At least one stage reported failure. | `bin/check:215-216` |
| `64` | Unrecognised mode argument — anything other than `''`, `pr`, `fast`, `full`, `-h`, `--help`, or `help`. | `bin/check:27-30` |
| `75` | No bundler on `PATH` — the environment cannot run the Ruby gate at all. | `bin/check:62-66` |
| `75` | Bundler is on `PATH` but the gems are not installed — `bin/setup` has never run here. | `bin/check:80-84` |
| `75` | No Redis on `127.0.0.1:6379`. | `bin/check:92-96` |
| `75` | No `bun` on `PATH` — frontend gate cannot execute. | `bin/check:104-109` |
| `75` | No Redis at `REDIS_URL` (default `redis://localhost:6379/0`). | `bin/check:101-105` |
| `75` | No `bun` on `PATH` — frontend gate cannot execute. | `bin/check:113-118` |

`75` is the platform's "preconditions unmet" code, and all four triggers share it
on purpose: none of them says anything about the diff. A gate that cannot reach its Redis,
Expand All @@ -86,9 +86,9 @@ nobody wrote.

### Env knobs

- `SKIP_LINT=1` — drop the rubocop stage (`bin/check:125`).
- `SKIP_PARITY=1` — drop the parity oracles stage (`bin/check:153`).
- `SKIP_FRONTEND=1` — drop the frontend stage (typecheck + oxlint + vitest in `frontend/`; `bin/check:144-145`).
- `SKIP_LINT=1` — drop the rubocop stage (`bin/check:134`).
- `SKIP_PARITY=1` — drop the parity oracles stage (`bin/check:162`).
- `SKIP_FRONTEND=1` — drop the frontend stage (typecheck + oxlint + vitest in `frontend/`; `bin/check:153-154`).
- `NCPU=<n>` — see [Worker count](#worker-count) below for the trade-off (ceiling 14).

The individual tasks, when you want one:
Expand Down
19 changes: 14 additions & 5 deletions bin/check
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,20 @@ fi
# Redis is not optional — integration and parity tests use a real server, never
# a mock (CLAUDE.md). Refuse with the platform's preconditions-unmet contract
# code (apps/runner/src/modes/setup/base.ts, CHECK_EXIT_PRECONDITIONS_UNMET =
# 75). Local developers who hit this need a Redis on 127.0.0.1:6379 — the two
# lines on stderr above tell them how. `docker run --rm -p 6379:6379 redis:7.4`
# is the supported path.
if ! (exec 3<>/dev/tcp/127.0.0.1/6379) 2>/dev/null; then
echo "bin/check: no Redis on 127.0.0.1:6379." >&2
# 75). The guard probes the server the suite will actually use: REDIS_URL, which
# test_helper and RedisPool read, else their default. A developerz.ai box hands
# the gate a REDIS_URL on a private port, because 6379 belongs to whatever repo
# stack the box also runs. Locally, `docker run --rm -p 6379:6379 redis:7.4` is
# the supported path.
redis_addr="${REDIS_URL:-redis://localhost:6379/0}"
redis_addr="${redis_addr#*://}"
redis_addr="${redis_addr##*@}"
redis_addr="${redis_addr%%/*}"
redis_host="${redis_addr%:*}"
redis_port="${redis_addr##*:}"
[ "$redis_host" != "$redis_addr" ] || redis_port=6379
if ! (exec 3<>"/dev/tcp/${redis_host}/${redis_port}") 2>/dev/null; then
echo "bin/check: no Redis on ${redis_host}:${redis_port} (REDIS_URL, default redis://localhost:6379/0)." >&2
echo " start one with: docker run --rm -p 6379:6379 redis:7.4" >&2
exit 75
fi
Expand Down
6 changes: 4 additions & 2 deletions bin/test-ecosystem
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ set -euo pipefail
# suite installs NO real sidekiq and runs entirely on wurk.
#
# Checkouts are cloned (cached) into test/ecosystem/.checkouts/ (gitignored).
# The gem's suite hits real Redis; default DB 15 — override with
# The gem's suite hits real Redis; DB 15 of REDIS_URL's server — override with
# ECOSYSTEM_REDIS_URL. The DB is the suite's to trash; don't point it at
# data you care about.

Expand All @@ -19,7 +19,9 @@ ROOT=$(pwd)

ECOSYSTEM_DIR="test/ecosystem"
CHECKOUTS="$ROOT/$ECOSYSTEM_DIR/.checkouts"
REDIS_URL="${ECOSYSTEM_REDIS_URL:-redis://127.0.0.1:6379/15}"
# DB 15 on the same server the rest of the gate uses (REDIS_URL), not a pinned port.
redis_base="${REDIS_URL:-redis://localhost:6379/0}"
REDIS_URL="${ECOSYSTEM_REDIS_URL:-$(printf '%s' "$redis_base" | sed -E 's#/[0-9]*$##')/15}"
Comment thread
sebyx07 marked this conversation as resolved.

ran=0
failures=0
Expand Down
73 changes: 73 additions & 0 deletions test/unit/bin_check_redis_url_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

require 'minitest/autorun'
require 'open3'
require 'socket'
require 'tmpdir'

# bin/check's Redis guard must look where the suite will connect: REDIS_URL,
# which lib/wurk and test_helper already read, falling back to the default.
# A box whose Redis is not on 127.0.0.1:6379 (the developerz.ai fleet moves its
# sidecar off the well-known port so a repo's own store can bind it) must not
# be refused for a server the suite was never going to use.
#
# Drives the real script in `fast` mode with a stub `bundle` on PATH, so every
# stage past the guard is a no-op and the exit code is the guard's verdict.
class BinCheckRedisUrlTest < Minitest::Test
parallelize_me!

CHECK = File.expand_path('../../bin/check', __dir__)

def run_check(redis_url)
Dir.mktmpdir do |dir|
stub = File.join(dir, 'bundle')
File.write(stub, "#!/usr/bin/env bash\nexit 0\n")
File.chmod(0o755, stub)
env = { 'PATH' => "#{dir}:#{ENV.fetch('PATH')}", 'REDIS_URL' => redis_url }
Open3.capture3(env, CHECK, 'fast')
end
end

def closed_port
server = TCPServer.new('127.0.0.1', 0)
port = server.addr[1]
server.close
port
end

def test_refuses_when_the_redis_url_server_is_down_and_names_it
port = closed_port
_out, err, status = run_check("redis://127.0.0.1:#{port}/0")

assert_equal 75, status.exitstatus, err
assert_includes err, "127.0.0.1:#{port}"
end

# The unset-REDIS_URL fallback must be the suite's own default, byte for byte: a
# guard that probes `127.0.0.1` while RedisPool connects to `localhost` can pass
# or refuse over a server the suite never uses. Read, not run — the real 6379
# on the box would otherwise decide the answer.
def test_the_unset_fallback_is_the_suites_own_default
suite_default = File.read(File.expand_path('../../lib/wurk/redis_pool.rb', __dir__))[
/DEFAULT_URL\s*=\s*ENV\.fetch\('REDIS_URL', '([^']+)'\)/, 1
]

refute_nil suite_default, 'RedisPool::DEFAULT_URL must still read REDIS_URL with a literal fallback'
%w[bin/check bin/test-ecosystem].each do |script|
fallback = File.read(File.expand_path("../../#{script}", __dir__))[/\$\{REDIS_URL:-([^}]+)\}/, 1]

assert_equal suite_default, fallback, "#{script} must fall back to the suite's own default"
end
end

def test_runs_the_gate_when_the_redis_url_server_answers
server = TCPServer.new('127.0.0.1', 0)
begin
_out, err, status = run_check("redis://127.0.0.1:#{server.addr[1]}/3")

assert_equal 0, status.exitstatus, err
ensure
server.close
end
end
end
Loading