-
Notifications
You must be signed in to change notification settings - Fork 39
Request Github token from Taskcluster's auth service #825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,9 @@ | |
|
|
||
| from github3 import GitHub | ||
| from github3.exceptions import GitHubException | ||
| from taskcluster.exceptions import TaskclusterFailure | ||
|
|
||
| import taskcluster | ||
| from scriptworker.exceptions import ConfigError | ||
| from scriptworker.utils import get_parts_of_url_path, get_single_item_from_sequence, retry_async_decorator, retry_request, retry_sync | ||
|
|
||
|
|
@@ -23,21 +25,51 @@ | |
| class GitHubRepository: | ||
| """Wrapper around GitHub API. Used to access public data.""" | ||
|
|
||
| def __init__(self, owner, repo_name, token=""): | ||
| GITHUB_APP_NAME = "read" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FTR this would've annoyed me when I was using scriptworker as a third party user :p Don't know if we want to care but maybe there should be a way to configure this that doesn't require monkeypatching the class
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, doesn't hurt! It can definitely go in scriptworker config |
||
| GITHUB_PERMISSIONS = {"contents": "read", "metadata": "read", "pull_requests": "read"} | ||
|
|
||
| def __init__(self, context, owner, repo_name): | ||
| """Build the GitHub API URL which points to the definition of the repository. | ||
|
|
||
| Args: | ||
| owner (str): the owner's GitHub username | ||
| context (scriptworker.context.Context): the scriptworker context | ||
| owner (str): the owner of the repository | ||
| repo_name (str): the name of the repository | ||
| token (str): the GitHub API token | ||
|
|
||
| Returns: | ||
| dict: a representation of the repo definition | ||
|
|
||
| """ | ||
| token = self._get_token(context, owner, repo_name) | ||
| github = retry_sync(GitHub, kwargs={"token": token}, sleeptime_kwargs=_GITHUB_LIBRARY_SLEEP_TIME_KWARGS) | ||
| self._github_repository = retry_sync(github.repository, args=(owner, repo_name), sleeptime_kwargs=_GITHUB_LIBRARY_SLEEP_TIME_KWARGS) | ||
|
|
||
| def _get_token(self, context, owner, repo_name): | ||
| """Get a repository-scoped GitHub token from Taskcluster's auth service. | ||
|
|
||
| Falls back to ``context.config["github_oauth_token"]`` if the auth service call | ||
| fails, e.g. because of missing scopes. | ||
|
|
||
| Args: | ||
| context (scriptworker.context.Context): the scriptworker context | ||
| owner (str): the owner of the repository | ||
| repo_name (str): the name of the repository | ||
|
|
||
| Returns: | ||
| str: the scoped GitHub token, or the fallback token | ||
|
|
||
| """ | ||
| if not context.credentials: | ||
| return context.config.get("github_oauth_token", "") | ||
|
|
||
| try: | ||
| auth = taskcluster.Auth(options={"rootUrl": context.config["taskcluster_root_url"], "credentials": context.credentials}) | ||
| response = auth.githubRepoToken(self.GITHUB_APP_NAME, owner, payload={"repositories": [repo_name], "permissions": self.GITHUB_PERMISSIONS}) | ||
|
Comment on lines
+66
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason to not use the async version here? AFAIK, everything else in scriptworker is, except for this now which would block the event loop (and it'd block on that call which ends with a roundtrip to github, which we know is going to be really fast all the time 🤡 ) |
||
| return response["token"] | ||
| except TaskclusterFailure as e: | ||
| log.warning(f"Could not obtain Github token from Taskcluster for {owner}/{repo_name}, falling back to `github_oauth_token`: {e}") | ||
| return context.config.get("github_oauth_token", "") | ||
|
|
||
| @property | ||
| def definition(self): | ||
| """Fetch the definition of the repository, exposed by the GitHub API. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this going to be a problem? If I open a PR from
github.com/Eijebong/fooforgithub.com/mozilla-releng/foo, the task won't have scopes to get a read token for Eijebong/foo and the tc-auth token request will fail 100% of the time.I'm not sure how we can do that but we probably want to use a read token minted for the parent repo and use that instead? AFAIK that'd work for public repos but not private ones though (although all fork commits are accessible on the parent directly, maybe that's enough to make this whole branch useless?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more about this, I think the private repo part of this is the same anyway since the token passed a secret wouldn't have access to it either