···1111 Uses the session cookies from `Atvouch.Tangled.Session` for authentication.
1212 If a 401 is returned, invalidates the session and retries once.
1313 """
1414- def post_comment(repo_handle, repo_rkey, pull_number, body, opts \\ []) do
1414+ def post_comment(repo_handle, repo_name, pull_number, body, opts \\ []) do
1515 session = Keyword.get(opts, :session, Atvouch.Tangled.Session)
1616 retry = Keyword.get(opts, :retry, true)
1717 tangled_url = Atvouch.Tangled.Session.get_url(session)
18181919 case Atvouch.Tangled.Session.get_cookies(session) do
2020 {:ok, cookies} ->
2121- url = "#{tangled_url}/#{repo_handle}/#{repo_rkey}/pulls/#{pull_number}/round/0/comment"
2121+ url = "#{tangled_url}/#{repo_handle}/#{repo_name}/pulls/#{pull_number}/round/0/comment"
2222 form_body = URI.encode_query(%{"body" => body})
23232424 headers = [
···3434 {:ok, 401} when retry ->
3535 Logger.info("Tangled returned 401, invalidating session and retrying")
3636 Atvouch.Tangled.Session.invalidate(session)
3737- post_comment(repo_handle, repo_rkey, pull_number, body, Keyword.put(opts, :retry, false))
3737+ post_comment(repo_handle, repo_name, pull_number, body, Keyword.put(opts, :retry, false))
38383939 {:ok, status} ->
4040 {:error, {:http_error, status}}
+2-2
appview/lib/atvouch/tangled/scraper.ex
···7676 Returns `{:ok, pulls}` where pulls is the result of `parse_pulls_page/1`,
7777 or `{:error, reason}`.
7878 """
7979- def fetch_pulls(tangled_url, repo_handle, repo_rkey) do
8080- url = "#{tangled_url}/#{repo_handle}/#{repo_rkey}/pulls"
7979+ def fetch_pulls(tangled_url, repo_handle, repo_name) do
8080+ url = "#{tangled_url}/#{repo_handle}/#{repo_name}/pulls"
81818282 case Tesla.get(url) do
8383 {:ok, %Tesla.Env{status: 200, body: body}} ->
+71-33
appview/lib/atvouch/tap_handler.ex
···167167 defp create_membership(event) do
168168 repo_at_uri = event.record["repo"]
169169 repo_did = extract_did_from_at_uri(repo_at_uri)
170170+ repo_rkey = extract_rkey_from_at_uri(repo_at_uri)
170171 maintainers = event.record["maintainers"] || []
171172 now = DateTime.utc_now() |> DateTime.to_iso8601()
172173173173- case Atvouch.Membership.create(
174174- %{
175175- repo_at_uri: repo_at_uri,
176176- source_did: event.did,
177177- repo_did: repo_did,
178178- remote_created_at: now,
179179- received_at: now
180180- },
181181- maintainers
182182- ) do
183183- {:ok, _membership} -> :ok
184184- {:error, reason} -> {:error, reason}
174174+ case resolve_repo_name(repo_did, repo_rkey) do
175175+ {:ok, repo_name} ->
176176+ case Atvouch.Membership.create(
177177+ %{
178178+ repo_at_uri: repo_at_uri,
179179+ source_did: event.did,
180180+ repo_did: repo_did,
181181+ repo_name: repo_name,
182182+ remote_created_at: now,
183183+ received_at: now
184184+ },
185185+ maintainers
186186+ ) do
187187+ {:ok, _membership} -> :ok
188188+ {:error, reason} -> {:error, reason}
189189+ end
190190+191191+ {:error, reason} ->
192192+ Logger.warning("Skipping membership for #{repo_at_uri}: repo record not found (#{inspect(reason)})")
193193+ :skip
185194 end
186195 end
187196···225234226235 defp validate_rkey_matches_repo(_rkey, _repo), do: {:error, "repo field is missing"}
227236237237+ defp resolve_repo_name(repo_did, repo_rkey, retries \\ 3) do
238238+ atproto = Application.get_env(:atvouch, :atproto_module, Atvouch.Atproto)
239239+240240+ case atproto.get_record(repo_did, "sh.tangled.repo", repo_rkey) do
241241+ {:ok, %{"value" => %{"name" => name}}} when is_binary(name) ->
242242+ {:ok, name}
243243+244244+ {:ok, _} ->
245245+ {:error, :missing_name_field}
246246+247247+ # 404 from getRecord means the repo record definitively doesn't exist
248248+ {:error, {:get_record_failed, 404}} ->
249249+ {:error, :repo_not_found}
250250+251251+ {:error, reason} when retries > 0 ->
252252+ Logger.debug("Retrying repo name resolution for #{repo_did}/#{repo_rkey} (#{retries} left): #{inspect(reason)}")
253253+ Process.sleep(500)
254254+ resolve_repo_name(repo_did, repo_rkey, retries - 1)
255255+256256+ {:error, reason} ->
257257+ {:error, reason}
258258+ end
259259+ end
260260+228261 defp extract_did_from_at_uri("at://" <> rest) do
229262 rest |> String.split("/") |> List.first()
230263 end
···249282250283 defp process_pull_for_membership(membership, author_did) do
251284 repo_did = membership.repo_did
252252- repo_rkey = extract_rkey_from_at_uri(membership.repo_at_uri)
285285+ repo_name = membership.repo_name
253286254254- # Resolve repo owner handle for Tangled URL construction
255255- case Atvouch.Identity.one(repo_did) do
256256- nil ->
257257- Logger.warning("Cannot resolve handle for repo DID #{repo_did}, skipping pull")
258258- :ok
287287+ if is_nil(repo_name) do
288288+ Logger.warning("No repo name for membership #{membership.repo_at_uri}, skipping pull")
289289+ :ok
290290+ else
291291+ # Resolve repo owner handle for Tangled URL construction
292292+ case Atvouch.Identity.one(repo_did) do
293293+ nil ->
294294+ Logger.warning("Cannot resolve handle for repo DID #{repo_did}, skipping pull")
295295+ :ok
259296260260- %{handle: nil} ->
261261- Logger.warning("No handle for repo DID #{repo_did}, skipping pull")
262262- :ok
297297+ %{handle: nil} ->
298298+ Logger.warning("No handle for repo DID #{repo_did}, skipping pull")
299299+ :ok
263300264264- identity ->
265265- process_pull_with_identity(membership, identity.handle, repo_rkey, author_did)
301301+ identity ->
302302+ process_pull_with_identity(membership, identity.handle, repo_name, author_did)
303303+ end
266304 end
267305 end
268306269269- defp process_pull_with_identity(membership, repo_handle, repo_rkey, author_did) do
307307+ defp process_pull_with_identity(membership, repo_handle, repo_name, author_did) do
270308 tangled_url = Atvouch.Tangled.Session.get_url()
271309272272- case Atvouch.Tangled.Scraper.fetch_pulls(tangled_url, repo_handle, repo_rkey) do
310310+ case Atvouch.Tangled.Scraper.fetch_pulls(tangled_url, repo_handle, repo_name) do
273311 {:ok, pulls} ->
274274- process_new_pulls(membership, repo_handle, repo_rkey, pulls, author_did)
312312+ process_new_pulls(membership, repo_handle, repo_name, pulls, author_did)
275313276314 {:error, reason} ->
277277- Logger.warning("Failed to fetch pulls for #{repo_handle}/#{repo_rkey}: #{inspect(reason)}")
315315+ Logger.warning("Failed to fetch pulls for #{repo_handle}/#{repo_name}: #{inspect(reason)}")
278316 :ok
279317 end
280318 end
281319282282- defp process_new_pulls(membership, repo_handle, repo_rkey, pulls, author_did) do
320320+ defp process_new_pulls(membership, repo_handle, repo_name, pulls, author_did) do
283321 maintainer_dids = Atvouch.Membership.maintainers(membership.repo_at_uri)
284322 membership_received_at = membership.received_at
285323···296334 comment_on_pull(
297335 membership.repo_at_uri,
298336 repo_handle,
299299- repo_rkey,
337337+ repo_name,
300338 pull_number,
301339 author_did,
302340 maintainer_dids
···322360 # If the scraper couldn't extract a timestamp, skip to be safe
323361 defp pull_newer_than_membership?(_pull_created_at, _membership_received_at), do: false
324362325325- defp comment_on_pull(repo_at_uri, repo_handle, repo_rkey, pull_number, author_did, maintainer_dids) do
363363+ defp comment_on_pull(repo_at_uri, repo_handle, repo_name, pull_number, author_did, maintainer_dids) do
326364 # Resolve author handle
327365 author_handle =
328366 case Atvouch.Identity.one(author_did) do
···346384347385 comment = Atvouch.Tangled.CommentBuilder.build_comment(author_did, author_handle, maintainer_routes)
348386349349- case Atvouch.Tangled.Client.post_comment(repo_handle, repo_rkey, pull_number, comment) do
387387+ case Atvouch.Tangled.Client.post_comment(repo_handle, repo_name, pull_number, comment) do
350388 :ok ->
351389 now = DateTime.utc_now() |> DateTime.to_iso8601()
352390···357395 commented_at: now
358396 })
359397360360- Logger.info("Posted vouch comment on #{repo_handle}/#{repo_rkey}##{pull_number}")
398398+ Logger.info("Posted vouch comment on #{repo_handle}/#{repo_name}##{pull_number}")
361399 :ok
362400363401 {:error, reason} ->
364364- Logger.warning("Failed to post comment on #{repo_handle}/#{repo_rkey}##{pull_number}: #{inspect(reason)}")
402402+ Logger.warning("Failed to post comment on #{repo_handle}/#{repo_name}##{pull_number}: #{inspect(reason)}")
365403 :ok
366404 end
367405 end