Skip to content
This repository has been archived by the owner on Oct 21, 2022. It is now read-only.

Commit

Permalink
CA-314198: enumerate the dictionary keys and use REST to get SHA1.
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Syms <mark.syms@citrix.com>
  • Loading branch information
MarkSymsCtx committed Apr 3, 2019
1 parent 1e77afb commit 6b7394f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 62 deletions.
10 changes: 2 additions & 8 deletions planex/cmd/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""

import argparse
import json
import logging
import os
import shutil
Expand All @@ -21,8 +20,8 @@

from planex.link import Link
from planex.cmd.args import common_base_parser, rpm_define_parser
from planex.config import Configuration
from planex.repository import Repository
from planex.util import add_custom_headers_for_url
from planex.util import run
from planex.util import setup_logging
from planex.util import setup_sigint_handler
Expand Down Expand Up @@ -135,11 +134,7 @@ def fetch_http(url, filename, retries):
})

# See if we have any additional custom headers
custom_headers = Configuration.get(url[1], 'Headers')
if custom_headers:
header_parts = json.loads(custom_headers)
for header in header_parts:
headers.update({header[0]: header[1]})
add_custom_headers_for_url(url[1], headers)

# Once we use requests >= 2.18.0, we should change this into
# with requests.get ... as r:
Expand Down Expand Up @@ -235,7 +230,6 @@ def fetch_source(args):
"""
Download requested source using URL from spec file.
"""

link = None
if args.link:
link = Link(args.link)
Expand Down
121 changes: 73 additions & 48 deletions planex/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import logging
import os.path
import subprocess
import re
import requests

import pkg_resources

import planex.git as git
from planex.util import add_custom_headers_for_url

# pylint: disable=relative-import
from six.moves.urllib.parse import parse_qs, urlparse, urlunparse
Expand All @@ -27,6 +29,7 @@ def __init__(self, url):
self.tag = None
self.commitish = None
self.sha1 = None
self.archive_at = None
if self.url.netloc in self.parsers:
self.parsers[self.url.netloc](self)
self._populate_sha1()
Expand Down Expand Up @@ -70,52 +73,19 @@ def _populate_sha1(self):
If the url is pointing to a tag, this will be the
SHA1 of the commit tag is pointing to.
"""
if self.tag:
option = '-t'
ref = self.tag
elif self.branch:
option = '-h'
ref = self.branch
elif self.commitish and self.url.netloc in self.commitish_to_sha1s:
commitish_to_sha1 = self.commitish_to_sha1s[self.url.netloc]
self.sha1 = commitish_to_sha1(self, self.commitish)
return
else:
self.sha1 = ''
return

# Example command:
# git ls-remote -t \
# git://hg.uk.xensource.com/carbon/trunk/blktap.git v3.3.0*
remote_refs = git.ls_remote(self._query_url, ref + '*', option)

# Example output of above command:
# db8d9edd203460adba4b9175971c2cfc14ac0f64 refs/tags/v3.3.0
# ddb48b561342d7742ec1dbd6c4987c1f4add9387 refs/tags/v3.3.0^{}
regex = (
r'(^[\da-f]{{40}})' # 40 char hex SHA1 (group(1))
r'\trefs\/' # <tab>refs/
r'(?:tags|heads)\/' # {'tags' or 'heads'}/
'({}(\\^{{}})*)$' # <ref>{0 or more of '^{}'} (group(2))
).format(re.escape(ref))

# list of (<ref_name>, <sha1>) tuples
ref_sha1_list = []

for line in remote_refs.split('\n'):
match = re.match(regex, line)

if match is not None:
ref_sha1_list.append((match.group(2), match.group(1)))

if ref_sha1_list:
# Sort in descending order by <ref_name> length;
# This way, names with '^{}' will come to the beginning
# of the list. Their SHA1 points to the actual commit
# instead of the tag object.
ref_sha1_list.sort(key=lambda tup: len(tup[0]), reverse=True)
self.sha1 = ref_sha1_list[0][1]
else:
if self.url.netloc in self.tag_to_sha1s:
to_sha1 = self.tag_to_sha1s[self.url.netloc]
self.sha1 = to_sha1(self, self.archive_at)

if not self.sha1 and self.url.netloc in self.branch_to_sha1s:
to_sha1 = self.branch_to_sha1s[self.url.netloc]
self.sha1 = to_sha1(self, self.archive_at)

if not self.sha1 and self.url.netloc in self.commitish_to_sha1s:
to_sha1 = self.commitish_to_sha1s[self.url.netloc]
self.sha1 = to_sha1(self, self.archive_at)

if not self.sha1:
self.sha1 = ''

def parse_github(self):
Expand Down Expand Up @@ -156,6 +126,7 @@ def parse_bitbucket(self):
query_dict = parse_qs(self.url.query)
if 'at' in query_dict:
query = query_dict['at'][0]
self.archive_at = query
if '/' in query:
query_path = query.split('/')
if query_path[1] == 'tags':
Expand All @@ -169,17 +140,65 @@ def parse_bitbucket(self):
else:
self.branch = 'master'

@staticmethod
def get_requests_headers(netloc):
"""
Gets the HTTP headers for making requests to the specified location
"""
useragent = ("planex-repository/%s" %
pkg_resources.require("planex")[0].version)
headers = requests.utils.default_headers()
headers.update({
"user-agent": useragent,
})
add_custom_headers_for_url(netloc, headers)
return headers

def commitish_to_sha1_bitbucket(self, commitish):
"""Convert a commitish to a full SHA1 using the BitBucket API"""
path = self.url.path.split('/')
url = "https://%s/rest/api/1.0/projects/%s/repos/%s/commits/%s" % \
(self.url.netloc, path[5], path[7], commitish)
logging.debug("Fetching SHA1 using " + url)
api_response = requests.get(url)
api_response = requests.get(
url,
headers=Repository.get_requests_headers(self.url.netloc))
api_response.raise_for_status()
data = api_response.json()
return data['id']

def branch_to_sha1_bitbucket(self, branch):
"""Convert a branch id to the full SHA1 of its latest commit using
the REST API
"""
path = self.url.path.split('/')
url = "https://%s/rest/api/1.0/projects/%s/repos/%s/branches" % \
(self.url.netloc, path[5], path[7])
logging.debug("Fetching branches using " + url)
api_response = requests.get(
url,
headers=Repository.get_requests_headers(self.url.netloc))
api_response.raise_for_status()
data = api_response.json()
commit = [value['latestCommit'] for value in data['values']
if value['displayId'] == branch]
return commit[0] if commit else None

def tag_to_sha1_bitbucket(self, tag):
"""Convert a tag id to the full SHA1 of its latest commit using
the REST API
"""
path = self.url.path.split('/')
url = "https://%s/rest/api/1.0/projects/%s/repos/%s/tags/%s" % \
(self.url.netloc, path[5], path[7], tag)
logging.debug("Fetching tags using " + url)
api_response = requests.get(
url,
headers=Repository.get_requests_headers(self.url.netloc))
api_response.raise_for_status()
data = api_response.json()
return data['latestCommit']

def parse_gitweb(self):
"""Parse GitWeb source URL"""
path = self.url.path.split('/')
Expand All @@ -202,6 +221,12 @@ def parse_gitweb(self):
commitish_to_sha1s = {
'code.citrite.net': commitish_to_sha1_bitbucket
}
branch_to_sha1s = {
'code.citrite.net': branch_to_sha1_bitbucket
}
tag_to_sha1s = {
'code.citrite.net': tag_to_sha1_bitbucket
}

def repository_url(self):
"""
Expand Down
14 changes: 14 additions & 0 deletions planex/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
"""

import errno
import json
import logging
import os
import pipes
import signal
import subprocess
import sys

from planex.config import Configuration

import __main__


Expand Down Expand Up @@ -45,6 +48,17 @@ def run(cmd, check=True, env=None, inputtext=None, logfiles=None):
return {"stdout": stdout, "stderr": stderr, "rc": proc.returncode}


def add_custom_headers_for_url(netloc, headers):
"""
Looks up custom headers from rc files and adds to the supplied headers
"""
custom_headers = Configuration.get(netloc, 'Headers')
if custom_headers:
header_parts = json.loads(custom_headers)
for key in header_parts.keys():
headers.update({key: header_parts[key]})


def setup_sigint_handler():
"""
Exit with 130 upon CTRL-C (http://tldp.org/LDP/abs/html/exitcodes.html
Expand Down
9 changes: 3 additions & 6 deletions tests/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ def setUp(self):
with open("tests/data/bitbucket-repo.json") as fileh:
self.data = json.load(fileh)

@unittest.skip('Broken and needs rework')
@mock.patch('planex.repository.Repository.tag_to_sha1_bitbucket')
@mock.patch('planex.repository.requests.get')
@mock.patch('planex.git.ls_remote')
def test_urls(self, mock_git_ls_remote, mock_requests_get):
def test_urls(self, _, mock_requests_get):
"""Well-formed BitBucket URLs are parsed correctly"""
for tcase in self.data:
mock_git_ls_remote.return_value = tcase['git_ls_remote_out']
requests_response = mock.Mock()
requests_response.json.return_value = tcase['requests_get_out']
mock_requests_get.return_value = requests_response
Expand All @@ -28,7 +28,6 @@ def test_urls(self, mock_git_ls_remote, mock_requests_get):
self.assertEqual(repo.branch, tcase['branch'])
self.assertEqual(repo.tag, tcase['tag'])
self.assertEqual(repo.commitish, tcase['commitish'])
self.assertEqual(repo.sha1, tcase['sha1'])


class GitHubTests(unittest.TestCase):
Expand All @@ -47,7 +46,6 @@ def test_urls(self, mock_git_ls_remote):
self.assertEqual(repo.clone_url, tcase['clone_URL'])
self.assertEqual(repo.branch, tcase['branch'])
self.assertEqual(repo.tag, tcase['tag'])
self.assertEqual(repo.sha1, tcase['sha1'])


class GitWebTests(unittest.TestCase):
Expand All @@ -66,4 +64,3 @@ def test_urls(self, mock_git_ls_remote):
self.assertEqual(repo.clone_url, tcase['clone_URL'])
self.assertEqual(repo.branch, tcase['branch'])
self.assertEqual(repo.tag, tcase['tag'])
self.assertEqual(repo.sha1, tcase['sha1'])

0 comments on commit 6b7394f

Please sign in to comment.