Skip to content
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

added support to 'download' sources from git #2555

Merged
merged 23 commits into from
Sep 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0f9ce62
added support to 'download' sources from git
mboisson Aug 30, 2018
c657745
cosmetic changes
mboisson Aug 30, 2018
372b3c6
trying to appease the hound
mboisson Aug 30, 2018
7baa951
is the hound happy yet ?
mboisson Aug 30, 2018
75ef62c
Added my name in the list of authors... and hound: good boy
mboisson Aug 30, 2018
5abd3b7
fixed reference before initialization for git_config
mboisson Aug 30, 2018
ed59ede
moved code to get_source_from_git in filetools
mboisson Aug 31, 2018
4ce646f
fixed run_cmd => run.run_cmd when within filetools
mboisson Aug 31, 2018
8ff3559
fixed missing return
mboisson Aug 31, 2018
70c17b3
appeasing hound
mboisson Aug 31, 2018
b8d9a4e
Cosmetic changes.
mboisson Sep 5, 2018
e2f07a0
Switched back to rm -rf instead of remove_file. Changed parametrize to
mboisson Sep 5, 2018
ed74a75
keep imports sorted in easyblock.py
boegel Sep 19, 2018
8dbc954
minor cleanup in get_source_tarball_from_git + add & use remove_dir/r…
boegel Sep 19, 2018
51a4db3
fix merge conflicts with develop
boegel Sep 19, 2018
e205640
minor cleanup in get_source_tarball_from_git + add & use remove_dir/r…
boegel Sep 19, 2018
b1eec8b
fixing merge conflict
mboisson Sep 19, 2018
fa0389b
cherry-picked commit to resolve merge conflict
boegel Aug 31, 2018
6af5a0c
fixed one too many blank line
mboisson Sep 19, 2018
9c578cd
Merge branch 'sources_from_git' into sources_from_git
mboisson Sep 19, 2018
e3ab8df
Merge pull request #5 from boegel/sources_from_git
mboisson Sep 19, 2018
55f03be
use HTTPS url for test repo in tests to avoid cloning failures in Travis
boegel Sep 20, 2018
a0ed138
Merge pull request #6 from boegel/sources_from_git
mboisson Sep 20, 2018
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
24 changes: 16 additions & 8 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
:author: Ward Poelmans (Ghent University)
:author: Fotis Georgatos (Uni.Lu, NTUA)
:author: Damian Alvarez (Forschungszentrum Juelich GmbH)
:author: Maxime Boissonneault (Compute Canada)
"""

import copy
Expand Down Expand Up @@ -71,9 +72,9 @@
from easybuild.tools.filetools import CHECKSUM_TYPE_MD5, CHECKSUM_TYPE_SHA256
from easybuild.tools.filetools import adjust_permissions, apply_patch, back_up_file, change_dir, convert_name
from easybuild.tools.filetools import compute_checksum, copy_file, derive_alt_pypi_url, diff_files, download_file
from easybuild.tools.filetools import encode_class_name, extract_file, is_alt_pypi_url, is_sha256_checksum, mkdir
from easybuild.tools.filetools import move_logs, read_file, remove_file, rmtree2, verify_checksum, weld_paths
from easybuild.tools.filetools import write_file
from easybuild.tools.filetools import encode_class_name, extract_file, get_source_tarball_from_git, is_alt_pypi_url
from easybuild.tools.filetools import is_sha256_checksum, mkdir, move_logs, read_file, remove_file, rmtree2
from easybuild.tools.filetools import verify_checksum, weld_paths, write_file
from easybuild.tools.hooks import BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EXTENSIONS_STEP, FETCH_STEP, INSTALL_STEP
from easybuild.tools.hooks import MODULE_STEP, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTPROC_STEP, PREPARE_STEP
from easybuild.tools.hooks import READY_STEP, SANITYCHECK_STEP, SOURCE_STEP, TEST_STEP, TESTCASES_STEP
Expand Down Expand Up @@ -339,17 +340,19 @@ def fetch_sources(self, sources=None, checksums=None):
checksums = self.cfg['checksums']

for index, source in enumerate(sources):
extract_cmd, download_filename, source_urls = None, None, None
extract_cmd, download_filename, source_urls, git_config = None, None, None, None

if isinstance(source, basestring):
filename = source

elif isinstance(source, dict):
# Making a copy to avoid modifying the object with pops
source = source.copy()
filename = source.pop('filename', None)
extract_cmd = source.pop('extract_cmd', None)
download_filename = source.pop('download_filename', None)
source_urls = source.pop('source_urls', None)
git_config = source.pop('git_config', None)
if source:
raise EasyBuildError("Found one or more unexpected keys in 'sources' specification: %s", source)

Expand All @@ -363,7 +366,7 @@ def fetch_sources(self, sources=None, checksums=None):
# check if the sources can be located
force_download = build_option('force_download') in [FORCE_DOWNLOAD_ALL, FORCE_DOWNLOAD_SOURCES]
path = self.obtain_file(filename, download_filename=download_filename, force_download=force_download,
urls=source_urls)
urls=source_urls, git_config=git_config)
if path:
self.log.debug('File %s found for source %s' % (path, filename))
self.src.append({
Expand Down Expand Up @@ -562,7 +565,8 @@ def fetch_extension_sources(self, skip_checksums=False):

return exts_sources

def obtain_file(self, filename, extension=False, urls=None, download_filename=None, force_download=False):
def obtain_file(self, filename, extension=False, urls=None, download_filename=None, force_download=False,
git_config=None):
"""
Locate the file with the given name
- searches in different subdirectories of source path
Expand All @@ -571,7 +575,8 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
:param extension: indicates whether locations for extension sources should also be considered
:param urls: list of source URLs where this file may be available
:param download_filename: filename with which the file should be downloaded, and then renamed to <filename>
:force_download: always try to download file, even if it's already available in source path
:param force_download: always try to download file, even if it's already available in source path
:param git_config: dictionary to define how to download a git repository
"""
srcpaths = source_paths()

Expand Down Expand Up @@ -662,10 +667,14 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No

break # no need to try other source paths

targetdir = os.path.join(srcpaths[0], self.name.lower()[0], self.name)

if foundfile:
if self.dry_run:
self.dry_run_msg(" * %s found at %s", filename, foundfile)
return foundfile
elif git_config:
return get_source_tarball_from_git(filename, targetdir, git_config)
else:
# try and download source files from specified source URLs
if urls:
Expand All @@ -674,7 +683,6 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
source_urls = []
source_urls.extend(self.cfg['source_urls'])

targetdir = os.path.join(srcpaths[0], self.name.lower()[0], self.name)
mkdir(targetdir, parents=True)

for url in source_urls:
Expand Down
114 changes: 113 additions & 1 deletion easybuild/tools/filetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
:author: Sotiris Fragkiskos (NTUA, CERN)
:author: Davide Vanzo (ACCRE, Vanderbilt University)
:author: Damian Alvarez (Forschungszentrum Juelich GmbH)
:author: Maxime Boissonneault (Compute Canada)
"""
import datetime
import difflib
Expand Down Expand Up @@ -266,7 +267,41 @@ def remove_file(path):
if os.path.exists(path) or os.path.islink(path):
os.remove(path)
except OSError, err:
raise EasyBuildError("Failed to remove %s: %s", path, err)
raise EasyBuildError("Failed to remove file %s: %s", path, err)


def remove_dir(path):
"""Remove directory at specified path."""
# early exit in 'dry run' mode
if build_option('extended_dry_run'):
dry_run_msg("directory %s removed" % path, silent=build_option('silent'))
return

try:
if os.path.exists(path):
rmtree2(path)
except OSError, err:
raise EasyBuildError("Failed to remove directory %s: %s", path, err)


def remove(paths):
"""
Remove single file/directory or list of files and directories

:param paths: path(s) to remove
"""
if isinstance(paths, basestring):
paths = [paths]

_log.info("Removing %d files & directories", len(paths))

for path in paths:
if os.path.isfile(path):
remove_file(path)
elif os.path.isdir(path):
remove_dir(path)
else:
raise EasyBuildError("Specified path to remove is not an existing file or directory: %s", path)


def change_dir(path):
Expand Down Expand Up @@ -1669,6 +1704,83 @@ def copy(paths, target_path, force_in_dry_run=False):
raise EasyBuildError("Specified path to copy is not an existing file or directory: %s", path)


def get_source_tarball_from_git(filename, targetdir, git_config):
"""
Downloads a git repository, at a specific tag or commit, recursively or not, and make an archive with it

:param filename: name of the archive to save the code to (must be .tar.gz)
:param targetdir: target directory where to save the archive to
:param git_config: dictionary containing url, repo_name, recursive, and one of tag or commit
"""
# sanity check on git_config value being passed
if not isinstance(git_config, dict):
raise EasyBuildError("Found unexpected type of value for 'git_config' argument: %s" % type(git_config))

# Making a copy to avoid modifying the object with pops
git_config = git_config.copy()
tag = git_config.pop('tag', None)
url = git_config.pop('url', None)
repo_name = git_config.pop('repo_name', None)
commit = git_config.pop('commit', None)
recursive = git_config.pop('recursive', False)

# input validation of git_config dict
if git_config:
raise EasyBuildError("Found one or more unexpected keys in 'git_config' specification: %s", git_config)

if not repo_name:
raise EasyBuildError("repo_name not specified in git_config parameter")

if not tag and not commit:
raise EasyBuildError("Neither tag nor commit found in git_config parameter")

if tag and commit:
raise EasyBuildError("Tag and commit are mutually exclusive in git_config parameter")

if not url:
raise EasyBuildError("url not specified in git_config parameter")

if not filename.endswith('.tar.gz'):
raise EasyBuildError("git_config currently only supports filename ending in .tar.gz")

# prepare target directory and clone repository
mkdir(targetdir, parents=True)
targetpath = os.path.join(targetdir, filename)

# compose 'git clone' command, and run it
clone_cmd = ['git', 'clone']

if tag:
clone_cmd.extend(['--branch', tag])

if recursive:
clone_cmd.append('--recursive')

clone_cmd.append('%s/%s.git' % (url, repo_name))

tmpdir = tempfile.mkdtemp()
cwd = change_dir(tmpdir)
run.run_cmd(' '.join(clone_cmd), log_all=True, log_ok=False, simple=False, regexp=False)

# if a specific commit is asked for, check it out
if commit:
checkout_cmd = ['git', 'checkout', commit]
if recursive:
checkout_cmd.extend(['&&', 'git', 'submodule', 'update'])

run.run_cmd(' '.join(checkout_cmd), log_all=True, log_ok=False, simple=False, regexp=False, path=repo_name)

# create an archive and delete the git repo directory
tar_cmd = ['tar', 'cfvz', targetpath, '--exclude', '.git', repo_name]
run.run_cmd(' '.join(tar_cmd), log_all=True, log_ok=False, simple=False, regexp=False)

# cleanup (repo_name dir does not exist in dry run mode)
change_dir(cwd)
remove(tmpdir)

return targetpath


def move_file(path, target_path, force_in_dry_run=False):
"""
Move a file from path to target_path
Expand Down
Loading