Prove the installer leaves the `openagents` name alone

f3743e1a96e6 · AtlantisPleb · · parent 467a98801873

Prove the installer leaves the `openagents` name alone

openagents#90 asks for `which -a openagents` after a fresh install as evidence.
The release artifacts are withdrawn, so there is no install to run — and an
acceptance that can only be checked by hand during a release is one nobody
checks. These assert the same property from the script itself.

Two of the three run the installer's own shell rather than grepping it:
`reclaim_openagents_name` is extracted and executed against a real tree, and
must remove the link a previous install left while leaving an `openagents` we
did not install untouched; the shadow-detection ERE is pulled out of the script
and run against a fixture rc file, where it must catch the three ways `oa` gets
defined and leave `oat`, `openagents`, and a comment alone.

Checked by reintroducing the defect: relinking the `openagents` name and
weakening the alias pattern fails these tests, and restoring the script passes
them. The reclaim test also asserts the function is called, because a function
that works and is never invoked passes a test written any other way.

Refs openagents#90.

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

pushed
by user · WAL seq 437 · 2026-08-26T06:45:53.592060Z

Changed files

  • modified test/openagents_web/install_script_test.exs

Diff

1 file changed, +116 -0

test/openagents_web/install_script_test.exs modified +116

@@ -197,4 +197,120 @@ defmodule OpenAgentsWeb.InstallScriptTest do

197 197
    assert script =~ "${BASE_URL_PRIMARY}/${CHANNEL}",
198 198
           "the installer never reads the channel pointer"
199 199
  end
200
201
  # The Rust binary implements a strict subset of the TypeScript CLI's
202
  # commands, so taking the `openagents` name swaps a fraction of the CLI in
203
  # underneath every script, agent tool call, and `openagents` tool that
204
  # invokes it by name — and the failure surfaces far from the cause, as
205
  # `unrecognized subcommand` from something that always worked. See
206
  # openagents#90.
207
  #
208
  # These run the installer's own shell, rather than grepping it. A test that
209
  # only greps passes the moment someone writes the same bug a different way.
210
  describe "the `openagents` name" do
211
    test "is never written by an install" do
212
      script = File.read!(@script)
213
214
      refute script =~ ~s(ln -sf "$link_target" "$BIN_DIR/openagents"),
215
             "the installer links the openagents name at the Rust binary"
216
217
      refute script =~ ~s($candidate/openagents),
218
             "the installer puts the openagents name on PATH"
219
    end
220
221
    test "is reclaimed when an earlier install took it, and only then" do
222
      tmp = Path.join(System.tmp_dir!(), "oa-reclaim-#{System.unique_integer([:positive])}")
223
      bin = Path.join(tmp, "bin")
224
      downloads = Path.join(tmp, "downloads")
225
      File.mkdir_p!(bin)
226
      File.mkdir_p!(downloads)
227
228
      binary = Path.join(downloads, "openagents-macos-aarch64")
229
      File.write!(binary, "#!/bin/sh\n")
230
231
      # What a previous install left behind, and what must go.
232
      File.ln_s!("../downloads/openagents-macos-aarch64", Path.join(bin, "openagents"))
233
234
      # Someone else's `openagents`, which must survive untouched.
235
      other = Path.join(tmp, "real-openagents")
236
      File.write!(other, "#!/bin/sh\n")
237
      File.ln_s!(other, Path.join(bin, "openagents-real"))
238
239
      script = File.read!(@script)
240
241
      # The function working proves nothing if the install path never calls it.
242
      assert script =~ ~r/^\s+reclaim_openagents_name$/m,
243
             "reclaim_openagents_name is defined and never called"
244
245
      body = extract_function(script, "reclaim_openagents_name")
246
247
      runner = Path.join(tmp, "run.sh")
248
249
      File.write!(runner, """
250
      BIN_DIR="#{bin}"
251
      DOWNLOAD_DIR="#{downloads}"
252
      HOME="#{tmp}"
253
      #{body}
254
      reclaim_openagents_name
255
      """)
256
257
      assert {_out, 0} = System.cmd("sh", [runner], stderr_to_stdout: true)
258
259
      refute File.exists?(Path.join(bin, "openagents")),
260
             "the link an earlier install left is still shadowing the CLI"
261
262
      assert File.exists?(Path.join(bin, "openagents-real")),
263
             "an openagents we did not install was removed"
264
265
      assert File.exists?(binary), "the binary itself was removed with the link"
266
267
      File.rm_rf!(tmp)
268
    end
269
270
    test "a shell alias named oa is reported, since it beats PATH and is invisible to `command -v`" do
271
      pattern = shadow_pattern(File.read!(@script))
272
273
      tmp = Path.join(System.tmp_dir!(), "oa-rc-#{System.unique_integer([:positive])}")
274
      File.mkdir_p!(tmp)
275
      rc = Path.join(tmp, "rc")
276
277
      File.write!(rc, """
278
      export PATH="/usr/bin:$PATH"
279
      alias oa="node ~/old/cli.js"
280
      alias oat="a different command"
281
      oa() { echo hi; }
282
      function oa { echo hi; }
283
      alias openagents="not this one"
284
      # alias oa=commented out
285
      """)
286
287
      {out, _} = System.cmd("sh", ["-c", "grep -Ens '#{pattern}' '#{rc}' || true"])
288
289
      lines =
290
        out |> String.split("\n", trim: true) |> Enum.map(&List.first(String.split(&1, ":")))
291
292
      assert lines == ["2", "4", "5"],
293
             "the shadow check found #{inspect(lines)}; it must catch the three ways `oa` is " <>
294
               "defined and leave `oat`, `openagents`, and a comment alone"
295
296
      File.rm_rf!(tmp)
297
    end
298
  end
299
300
  # Pull one shell function out of the installer so a test can run the real
301
  # thing rather than a restatement of it.
302
  defp extract_function(script, name) do
303
    script
304
    |> String.split("\n")
305
    |> Enum.drop_while(&(!String.starts_with?(&1, "#{name}() {")))
306
    |> Enum.take_while(&(&1 != "}"))
307
    |> Kernel.++(["}"])
308
    |> Enum.join("\n")
309
  end
310
311
  # The ERE the installer greps the shell rc file with.
312
  defp shadow_pattern(script) do
313
    [_, pattern] = Regex.run(~r/grep -Eqs '([^']+)'/, script)
314
    pattern
315
  end
200 316
end

This page updates live while a promote is in flight · changelog