Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
"USSec West",
"USSec East"
}
GITHUB_OAUTH_CLIENT_ID = "8d8e1f6000648c575489"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you share where this ID comes from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an oauth app created by AppService CLI team: https://github.com/AzureAppServiceCLI
Is there an official Azure oauth app we can use, or should we stick to this one?

GITHUB_OAUTH_SCOPES = [
"admin:repo_hook",
"repo",
"workflow"
]


class FUNCTIONS_STACKS_API_KEYS():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core.azclierror import (ValidationError, CLIInternalError, UnclassifiedUserFault)
from knack.log import get_logger

from ._constants import (GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_SCOPES)

logger = get_logger(__name__)


'''
Get Github personal access token following Github oauth for command line tools
https://docs.github.com/en/developers/apps/authorizing-oauth-apps#device-flow
'''


def get_github_access_token(cmd, scope_list=None):
if scope_list:
for scope in scope_list:
if scope not in GITHUB_OAUTH_SCOPES:
raise ValidationError("Requested github oauth scope is invalid")
scope_list = ' '.join(scope_list)

authorize_url = 'https://github.com/login/device/code'
authorize_url_data = {
'scope': scope_list,
'client_id': GITHUB_OAUTH_CLIENT_ID
}

import base64
import json
import requests
import time
from urllib.parse import urlparse, parse_qs

try:
response = requests.post(authorize_url, data=authorize_url_data)
parsed_response = parse_qs(response.content.decode('ascii'))

device_code = parsed_response['device_code'][0]
user_code = parsed_response['user_code'][0]
verification_uri = parsed_response['verification_uri'][0]
interval = int(parsed_response['interval'][0])
expires_in_seconds = int(parsed_response['expires_in'][0])
logger.warning('Please navigate to %s and enter the user code %s to activate and '
'retrieve your github personal access token', verification_uri, user_code)

timeout = time.time() + expires_in_seconds
logger.warning("Waiting up to '%s' minutes for activation", str(expires_in_seconds // 60))

confirmation_url = 'https://github.com/login/oauth/access_token'
confirmation_url_data = {
'client_id': GITHUB_OAUTH_CLIENT_ID,
'device_code': device_code,
'grant_type': 'urn:ietf:params:oauth:grant-type:device_code'
}

pending = True
while pending:
time.sleep(interval)

if time.time() > timeout:
raise UnclassifiedUserFault('Activation did not happen in time. Please try again')

confirmation_response = requests.post(confirmation_url, data=confirmation_url_data)
parsed_confirmation_response = parse_qs(confirmation_response.content.decode('ascii'))

if 'error' in parsed_confirmation_response and parsed_confirmation_response['error'][0]:
if parsed_confirmation_response['error'][0] == 'slow_down':
interval += 5 # if slow_down error is received, 5 seconds is added to minimum polling interval
elif parsed_confirmation_response['error'][0] != 'authorization_pending':
pending = False

if 'access_token' in parsed_confirmation_response and parsed_confirmation_response['access_token'][0]:
return parsed_confirmation_response['access_token'][0]
except Exception as e:
raise CLIInternalError(
'Error: {}. Please try again, or retrieve personal access token from the Github website'.format(e))

raise UnclassifiedUserFault('Activation did not happen in time. Please try again')
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def load_arguments(self, _):
c.argument('repository_type', help='repository type',
arg_type=get_enum_type(['git', 'mercurial', 'vsts', 'github', 'externalgit', 'localgit']))
c.argument('git_token', help='Git access token required for auto sync')
c.argument('github_action', options_list=['--github-action'], help='If using github action, default to False')
with self.argument_context(scope + ' identity') as c:
c.argument('scope', help="The scope the managed identity has access to")
c.argument('role', help="Role name or id the managed identity will be assigned")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,7 @@ def config_source_control(cmd, resource_group_name, name, repo_url, repository_t
manual_integration=None, git_token=None, slot=None, cd_app_type=None,
app_working_dir=None, nodejs_task_runner=None, python_framework=None,
python_version=None, cd_account_create=None, cd_project_url=None, test=None,
slot_swap=None, private_repo_username=None, private_repo_password=None):
slot_swap=None, private_repo_username=None, private_repo_password=None, github_action=None):
client = web_client_factory(cmd.cli_ctx)
location = _get_location_from_webapp(client, resource_group_name, name)

Expand Down Expand Up @@ -1602,7 +1602,7 @@ def config_source_control(cmd, resource_group_name, name, repo_url, repository_t

source_control = SiteSourceControl(location=location, repo_url=repo_url, branch=branch,
is_manual_integration=manual_integration,
is_mercurial=(repository_type != 'git'))
is_mercurial=(repository_type != 'git'), is_git_hub_action=bool(github_action))

# SCC config can fail if previous commands caused SCMSite shutdown, so retry here.
for i in range(5):
Expand Down
1 change: 1 addition & 0 deletions src/azure-cli/requirements.py3.Darwin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pbr==5.3.1
portalocker==1.7.1
psutil==5.8.0
pycparser==2.19
PyGithub==1.38
PyJWT==1.7.1
PyNaCl==1.4.0
pyOpenSSL==19.0.0
Expand Down
1 change: 1 addition & 0 deletions src/azure-cli/requirements.py3.Linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pbr==5.3.1
portalocker==1.7.1
psutil==5.8.0
pycparser==2.19
PyGithub==1.38
PyJWT==1.7.1
PyNaCl==1.4.0
pyOpenSSL==19.0.0
Expand Down
1 change: 1 addition & 0 deletions src/azure-cli/requirements.py3.windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pbr==5.3.1
portalocker==1.7.1
psutil==5.8.0
pycparser==2.19
PyGithub==1.38
Copy link
Member

@qwordy qwordy Apr 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add it to setup.py otherwise pip install will fail

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Why are only some of the requirements.txt added to setup.py dependencies?

Copy link
Member

@qwordy qwordy Apr 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pip install reads setup.py. Windows MSI artifact build reads requirements.txt. It makes me confused why two dependency definitions, too.
@fengzhou-msft Could you answer the question by Calvin?

PyJWT==1.7.1
PyNaCl==1.4.0
pyOpenSSL==19.0.0
Expand Down
1 change: 1 addition & 0 deletions src/azure-cli/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
'jsmin~=2.2.2',
'jsondiff==1.2.0',
'packaging~=20.9',
'PyGithub==1.38',
Copy link
Member

@jiasli jiasli Jun 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github module is not imported anywhere. Is this dependency necessary?

The pinned version causes problem when being installed with the latest PyGithub (#18548):

azure-cli 2.24.2 requires PyGithub==1.38, but you'll have pygithub 1.55 which is incompatible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have added it to this PR instead, it will be used in this one #18261. And yes we shouldn't pin, we can use ~= and unpin. Thanks for bringing this up

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change the dependency to use ~= instead?

'pytz==2019.1',
'scp~=0.13.2',
'semver==2.13.0',
Expand Down