Preserve GitHub visibility during imports

9b4fa727f63a · AtlantisPleb · · parent f7f831c676da

Preserve GitHub visibility during imports

Deploy story

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

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified docs/openagents-cli/command-reference.md
  • modified docs/openagents-cli/import-github.md
  • modified docs/repository-creation-and-openagents-cli-spec.md
  • modified lib/openagents_web/controllers/repository_import_controller.ex
  • modified lib/openagents_web/live/repository_import_live.ex
  • modified priv/docs/cli-command-reference.md
  • modified priv/docs/import-github.md
  • modified test/openagents_web/controllers/repository_import_controller_test.exs
  • modified test/openagents_web/live/repository_live_test.exs

Diff

9 files changed, +77 -21

docs/openagents-cli/command-reference.md modified +4 -3

@@ -81,11 +81,12 @@ openagents repo import [flags] <github-owner/repository>

81 81
| --- | --- |
82 82
| `--name NAME` | Override the destination repository name. |
83 83
| `--namespace OWNER` | State the matching eligible GitHub owner. |
84
| `--public` | Create a public destination. |
85
| `--private` | Create a private destination, which is the default. |
84
| `--public` | Override the source visibility and create a public destination. |
85
| `--private` | Override the source visibility and create a private destination. |
86 86
| `--wait-timeout SECONDS` | Wait for import. The default is `300`; `0` does not wait. |
87 87
88
This command performs one depth-1 import of every accepted branch and tag. It
88
Without a visibility flag, the destination keeps the source repository's GitHub
89
visibility. This command performs one depth-1 import of every accepted branch and tag. It
89 90
does not copy older history or start synchronization. While create and import
90 91
commands wait, they write state changes, elapsed time, and a five-second
91 92
heartbeat to standard error.
docs/openagents-cli/import-github.md modified +3

@@ -41,6 +41,9 @@ Import a repository into its matching namespace:

41 41
openagents repo import OpenAgentsInc/example
42 42
```
43 43
44
By default, the destination keeps the source repository's GitHub visibility. Use
45
`--public` or `--private` only when you want to override it.
46
44 47
Choose a different destination name:
45 48
46 49
```sh
docs/repository-creation-and-openagents-cli-spec.md modified +2 -2

@@ -876,8 +876,8 @@ Import rules:

876 876
- `--namespace` can state that matching owner explicitly. It cannot copy the
877 877
  source into an unrelated namespace in the first release.
878 878
- `--name` defaults to the source repository name.
879
- The destination defaults to private. `--public` requires an explicit flag,
880
  including for a public source.
879
- The destination keeps the source repository's GitHub visibility unless you
880
  provide `--public` or `--private` as an explicit override.
881 881
- The command submits one idempotent import, polls until `ready` or `failed`,
882 882
  and reports the accepted source head SHA.
883 883
- The command exits after the bounded `--wait-timeout` without canceling a
lib/openagents_web/controllers/repository_import_controller.ex modified +1 -1

@@ -96,7 +96,7 @@ defmodule OpenAgentsWeb.RepositoryImportController do

96 96
  defp source_repository(_params), do: {:error, :invalid_import}
97 97
98 98
  defp import_attrs(params, github_repository) do
99
    private? = Map.get(params, "private", true)
99
    private? = Map.get(params, "private", github_repository["private"])
100 100
    name = params["name"] || github_repository["name"]
101 101
102 102
    if is_boolean(private?) do
lib/openagents_web/live/repository_import_live.ex modified +12 -2

@@ -24,7 +24,17 @@ defmodule OpenAgentsWeb.RepositoryImportLive do

24 24
    repository =
25 25
      Enum.find(socket.assigns.source_repositories, &(&1["full_name"] == params["source"]))
26 26
27
    params = if repository, do: Map.put(params, "name", repository["name"]), else: params
27
    source_changed? = params["source"] != socket.assigns.form.params["source"]
28
29
    params =
30
      if is_map(repository) and source_changed? do
31
        params
32
        |> Map.put("name", repository["name"])
33
        |> Map.put("private", to_string(repository["private"]))
34
      else
35
        params
36
      end
37
28 38
    {:noreply, assign(socket, :form, to_form(params, as: :repository_import))}
29 39
  end
30 40

@@ -192,7 +202,7 @@ defmodule OpenAgentsWeb.RepositoryImportLive do

192 202
      %{
193 203
        "source" => repository["full_name"],
194 204
        "name" => repository["name"],
195
        "private" => "true"
205
        "private" => to_string(repository["private"])
196 206
      },
197 207
      as: :repository_import
198 208
    )
priv/docs/cli-command-reference.md modified +4 -3

@@ -80,11 +80,12 @@ openagents repo import [flags] <github-owner/repository>

80 80
| --- | --- |
81 81
| `--name NAME` | Override the destination repository name. |
82 82
| `--namespace OWNER` | State the matching eligible GitHub owner. |
83
| `--public` | Create a public destination. |
84
| `--private` | Create a private destination, which is the default. |
83
| `--public` | Override the source visibility and create a public destination. |
84
| `--private` | Override the source visibility and create a private destination. |
85 85
| `--wait-timeout SECONDS` | Wait for import. The default is `300`; `0` does not wait. |
86 86
87
This command performs one depth-1 import of every accepted branch and tag. It
87
Without a visibility flag, the destination keeps the source repository's GitHub
88
visibility. This command performs one depth-1 import of every accepted branch and tag. It
88 89
does not copy older history or start synchronization. A client timeout does not
89 90
cancel the accepted server-side import. While create and import commands wait,
90 91
they write state changes, elapsed time, and a five-second heartbeat to standard
priv/docs/import-github.md modified +3

@@ -42,6 +42,9 @@ Import a repository into its matching namespace:

42 42
openagents repo import OpenAgentsInc/example
43 43
```
44 44
45
By default, the destination keeps the source repository's GitHub visibility. Use
46
`--public` or `--private` only when you want to override it.
47
45 48
Choose another destination name:
46 49
47 50
```sh
test/openagents_web/controllers/repository_import_controller_test.exs modified +26 -5

@@ -72,6 +72,24 @@ defmodule OpenAgentsWeb.RepositoryImportControllerTest do

72 72
           |> json_response(404)
73 73
  end
74 74
75
  test "an import inherits the GitHub repository visibility when omitted", %{conn: conn} do
76
    user = github_user("repository-public-import-api", "octavia")
77
    assert {:ok, user} = Accounts.store_github_token(user, "gho_public_import_fixture")
78
    main_sha = String.duplicate("c", 40)
79
80
    expect_import_source(user, main_sha, nil, false)
81
82
    response =
83
      conn
84
      |> authorize(user)
85
      |> put_req_header("idempotency-key", "public-import-key")
86
      |> post(~p"/api/v3/user/repos/imports", %{
87
        source: %{provider: "github", repository: "octavia/source-project"}
88
      })
89
90
    assert %{"private" => false, "visibility" => "public"} = json_response(response, 202)
91
  end
92
75 93
  test "organization creation requires an active GitHub administrator membership", %{conn: conn} do
76 94
    user = github_user("repository-org-api")
77 95
    assert {:ok, user} = Accounts.store_github_token(user, "gho_org_fixture")

@@ -106,7 +124,7 @@ defmodule OpenAgentsWeb.RepositoryImportControllerTest do

106 124
           } = json_response(created, 202)
107 125
  end
108 126
109
  defp expect_import_source(user, main_sha, tag_sha) do
127
  defp expect_import_source(user, main_sha, tag_sha, private? \\ true) do
110 128
    Req.Test.expect(__MODULE__, fn github_conn ->
111 129
      assert github_conn.request_path == "/repos/octavia/source-project"
112 130

@@ -115,7 +133,7 @@ defmodule OpenAgentsWeb.RepositoryImportControllerTest do

115 133
        "node_id" => "R_501",
116 134
        "name" => "source-project",
117 135
        "full_name" => "octavia/source-project",
118
        "private" => true,
136
        "private" => private?,
119 137
        "default_branch" => "main",
120 138
        "owner" => %{
121 139
          "id" => user.github_id,

@@ -141,9 +159,12 @@ defmodule OpenAgentsWeb.RepositoryImportControllerTest do

141 159
      assert github_conn.request_path ==
142 160
               "/repos/octavia/source-project/git/matching-refs/tags/"
143 161
144
      Req.Test.json(github_conn, [
145
        %{"ref" => "refs/tags/v1.0.0", "object" => %{"type" => "tag", "sha" => tag_sha}}
146
      ])
162
      tags =
163
        if tag_sha,
164
          do: [%{"ref" => "refs/tags/v1.0.0", "object" => %{"type" => "tag", "sha" => tag_sha}}],
165
          else: []
166
167
      Req.Test.json(github_conn, tags)
147 168
    end)
148 169
149 170
    Req.Test.expect(__MODULE__, fn github_conn ->
test/openagents_web/live/repository_live_test.exs modified +22 -5

@@ -257,6 +257,23 @@ defmodule OpenAgentsWeb.RepositoryLiveTest do

257 257
    assert repository_import.source_refs == %{"refs/heads/main" => main_sha}
258 258
  end
259 259
260
  test "one-time import picker defaults to the GitHub repository visibility", %{conn: conn} do
261
    user = github_user("repository-live-public-import", "import-owner")
262
    assert {:ok, user} = Accounts.store_github_token(user, "gho_live_public_import")
263
    main_sha = String.duplicate("c", 40)
264
265
    Req.Test.stub(__MODULE__, fn github_conn ->
266
      github_import_response(github_conn, user, main_sha, false)
267
    end)
268
269
    {:ok, view, _html} = live(log_in(conn, user), ~p"/repositories/import/github")
270
271
    assert has_element?(
272
             view,
273
             "#repository-import-form input[name='repository_import[private]']:not([checked])"
274
           )
275
  end
276
260 277
  defp log_in(conn, user), do: Plug.Test.init_test_session(conn, %{"user_id" => user.id})
261 278
262 279
  # A repository copied from GitHub, written through the same receipts the

@@ -298,13 +315,13 @@ defmodule OpenAgentsWeb.RepositoryLiveTest do

298 315
    |> Repo.update!()
299 316
  end
300 317
301
  defp repository_payload(user, main_sha) do
318
  defp repository_payload(user, main_sha, private?) do
302 319
    %{
303 320
      "id" => 901,
304 321
      "node_id" => "R_901",
305 322
      "name" => "source-project",
306 323
      "full_name" => "import-owner/source-project",
307
      "private" => true,
324
      "private" => private?,
308 325
      "fork" => false,
309 326
      "archived" => false,
310 327
      "default_branch" => "main",

@@ -324,16 +341,16 @@ defmodule OpenAgentsWeb.RepositoryLiveTest do

324 341
    }
325 342
  end
326 343
327
  defp github_import_response(github_conn, user, main_sha) do
344
  defp github_import_response(github_conn, user, main_sha, private? \\ true) do
328 345
    case github_conn.request_path do
329 346
      "/user/memberships/orgs" ->
330 347
        Req.Test.json(github_conn, [])
331 348
332 349
      "/user/repos" ->
333
        Req.Test.json(github_conn, [repository_payload(user, main_sha)])
350
        Req.Test.json(github_conn, [repository_payload(user, main_sha, private?)])
334 351
335 352
      "/repos/import-owner/source-project" ->
336
        Req.Test.json(github_conn, repository_payload(user, main_sha))
353
        Req.Test.json(github_conn, repository_payload(user, main_sha, private?))
337 354
338 355
      "/repos/import-owner/source-project/git/matching-refs/heads/" ->
339 356
        Req.Test.json(github_conn, [

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