Skip to content

Commit

Permalink
Merge pull request #209 from jku/non-maintainer-flow
Browse files Browse the repository at this point in the history
Improve non-maintainer signer flow
  • Loading branch information
jku authored Mar 26, 2024
2 parents c5db842 + 34717e8 commit 3431937
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 43 deletions.
65 changes: 65 additions & 0 deletions signer/tuf_on_ci_sign/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import logging
import os
import subprocess
import webbrowser
from collections.abc import Generator
from contextlib import contextmanager
from datetime import datetime, timedelta
from tempfile import TemporaryDirectory
from urllib import parse
from urllib.request import Request, urlopen

import click
Expand Down Expand Up @@ -154,3 +156,66 @@ def application_update_reminder() -> None:

except Exception as e: # noqa: BLE001
logger.warning(f"Failed to check current tuf-on-ci-sign version: {e}")


def push_changes(user: User, event_name: str, title: str) -> None:
"""Push the event branch to users push remote"""
branch = f"{user.push_remote}/{event_name}"
msg = f"Press enter to push changes to {branch}"
click.prompt(bold(msg), default=True, show_default=False)
if user.push_remote == user.pull_remote:
# maintainer flow: just push to signing event branch
git_echo(
[
"push",
user.push_remote,
f"HEAD:refs/heads/{event_name}",
]
)
else:
# non-maintainer flow: push to fork, make a PR.
# NOTE: we force push: this is safe since any existing fork branches
# have either been merged or are obsoleted by this push
git_echo(
[
"push",
"--force",
user.push_remote,
f"HEAD:refs/heads/{event_name}",
]
)
# Create PR from fork (push remote) to upstream (pull remote)
upstream = get_repo_name(user.pull_remote)
fork = get_repo_name(user.push_remote).replace("/", ":")
query = parse.urlencode(
{
"quick_pull": 1,
"title": title,
"template": "signing_event.md",
}
)
pr_url = f"https://github.com/{upstream}/compare/{event_name}...{fork}:{event_name}?{query}"
if webbrowser.open(pr_url):
click.echo(bold("Please submit the pull request in your browser."))
else:
click.echo(bold(f"Please submit the pull request:\n {pr_url}"))


def get_repo_name(remote: str) -> str:
"""Return 'owner/repo' string for given GitHub remote"""
url = parse.urlparse(git_expect(["config", "--get", f"remote.{remote}.url"]))
owner_repo = url.path[: -len(".git")]
# ssh-urls are relative URLs according to urllib: host is actually part of
# path. We don't want the host part:
_, _, owner_repo = owner_repo.rpartition(":")
# http urls on the other hand are not relative: remove the leading /
owner_repo = owner_repo.lstrip("/")

# sanity check
owner, slash, repo = owner_repo.partition("/")
if not owner or slash != "/" or not repo:
raise RuntimeError(
"Failed to parse GitHub repository from git URL {url} for remote {remote}"
)

return owner_repo
28 changes: 4 additions & 24 deletions signer/tuf_on_ci_sign/delegate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import os
import re
from copy import deepcopy
from urllib import parse

import click
from securesystemslib.signer import (
Expand All @@ -23,9 +22,10 @@
from tuf_on_ci_sign._common import (
application_update_reminder,
bold,
get_repo_name,
get_signing_key_input,
git_echo,
git_expect,
push_changes,
signing_event,
)
from tuf_on_ci_sign._signer_repository import (
Expand Down Expand Up @@ -118,20 +118,10 @@ def verify_signers(response: str) -> list[str]:
return config


def _get_repo_name(remote: str):
url = parse.urlparse(git_expect(["config", "--get", f"remote.{remote}.url"]))
repo = url.path[: -len(".git")]
# ssh-urls are relative URLs according to urllib: host is actually part of
# path. We don't want the host part:
_, _, repo = repo.rpartition(":")
# http urls on the other hand are not relative: remove the leading /
return repo.lstrip("/")


def _sigstore_import(pull_remote: str) -> Key:
# WORKAROUND: build sigstore key and uri here since there is no import yet
issuer = "https://token.actions.githubusercontent.com"
repo = _get_repo_name(pull_remote)
repo = get_repo_name(pull_remote)

id = f"https://github.com/{repo}/.github/workflows/online-sign.yml@refs/heads/main"
key = SigstoreKey(
Expand Down Expand Up @@ -392,17 +382,7 @@ def delegate(verbose: int, push: bool, event_name: str, role: str | None):
)

if push:
branch = f"{user_config.push_remote}/{event_name}"
msg = f"Press enter to push changes to {branch}"
click.prompt(bold(msg), default=True, show_default=False)
git_echo(
[
"push",
"--progress",
user_config.push_remote,
f"HEAD:refs/heads/{event_name}",
]
)
push_changes(user_config, event_name, msg)
else:
# TODO: deal with existing branch?
click.echo(f"Creating local branch {event_name}")
Expand Down
27 changes: 8 additions & 19 deletions signer/tuf_on_ci_sign/sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@

from tuf_on_ci_sign._common import (
application_update_reminder,
bold,
get_signing_key_input,
git_echo,
git_expect,
push_changes,
signing_event,
)
from tuf_on_ci_sign._signer_repository import SignerState
Expand All @@ -39,7 +38,7 @@ def sign(verbose: int, push: bool, event_name: str):
with signing_event(event_name, user_config) as repo:
if repo.state == SignerState.UNINITIALIZED:
click.echo("No metadata repository found")
changed = False
change_status = None
elif repo.state == SignerState.INVITED:
click.echo(
f"You have been invited to become a signer for role(s) {repo.invites}."
Expand All @@ -57,33 +56,23 @@ def sign(verbose: int, push: bool, event_name: str):
for rolename in repo.unsigned:
click.echo(repo.status(rolename))
repo.sign(rolename)
changed = True
change_status = f"{user_config.name} accepted invitation"
elif repo.state == SignerState.SIGNATURE_NEEDED:
click.echo(f"Your signature is requested for role(s) {repo.unsigned}.")
for rolename in repo.unsigned:
click.echo(repo.status(rolename))
repo.sign(rolename)
changed = True
change_status = f"Signature from {user_config.name}"
elif repo.state == SignerState.NO_ACTION:
changed = False
change_status = None
else:
raise NotImplementedError

if changed:
if change_status:
git_expect(["add", "metadata"])
git_expect(["commit", "-m", f"Signed by {user_config.name}", "--signoff"])
git_expect(["commit", "-m", change_status, "--signoff"])
if push:
branch = f"{user_config.push_remote}/{event_name}"
msg = f"Press enter to push signature(s) to {branch}"
click.prompt(bold(msg), default=True, show_default=False)
git_echo(
[
"push",
"--progress",
user_config.push_remote,
f"HEAD:refs/heads/{event_name}",
]
)
push_changes(user_config, event_name, change_status)
else:
# TODO: maybe deal with existing branch?
click.echo(f"Creating local branch {event_name}")
Expand Down

0 comments on commit 3431937

Please sign in to comment.