| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # pull-source - Fetch source for charm, layers, and interfaces | ||
| # | ||
| # Copyright (C) 2016 Canonical Ltd. | ||
| # Author: Tim Van Steenburgh <tvansteenburgh@gmail.com> | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| """download the source code for a charm, layer, or interface. | ||
| The item to download can be specified using any of the following forms: | ||
| - [cs:]charm | ||
| - [cs:]series/charm | ||
| - [cs:]~user/charm | ||
| - [cs:]~user/series/charm | ||
| - layer:layer-name | ||
| - interface:interface-name | ||
| If the item is a layered charm, and the top layer of the charm has a repo | ||
| key in layer.yaml, the top layer repo will be cloned. Otherwise, the charm | ||
| archive will be downloaded and extracted from the charm store. | ||
| If a download directory is not specified, the following environment vars | ||
| will be used to determine the download location: | ||
| - For charms, $JUJU_REPOSITORY | ||
| - For layers, $LAYER_PATH | ||
| - For interfaces, $INTERFACE_PATH | ||
| If a download location can not be determined from environment variables, | ||
| the current working directory will be used. | ||
| The download is aborted if the destination directory already exists. | ||
| """ | ||
|
|
||
| import argparse | ||
| import atexit | ||
| import logging | ||
| import os | ||
| import shutil | ||
| import sys | ||
| import tempfile | ||
| import textwrap | ||
|
|
||
| import yaml | ||
|
|
||
| from . import utils | ||
| from .build import fetchers | ||
| from fetchers import ( | ||
| CharmstoreDownloader, | ||
| FETCHERS, | ||
| get, | ||
| ) | ||
|
|
||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| LAYER_PREFIX = 'layer:' | ||
| INTERFACE_PREFIX = 'interface:' | ||
| CHARM_PREFIX = 'cs:' | ||
|
|
||
| ERR_DIR_EXISTS = "Aborting, destination directory exists" | ||
|
|
||
|
|
||
| class CharmstoreRepoDownloader(CharmstoreDownloader): | ||
| """Clones a charm's bzr repo. | ||
| If the a bzr repo is not set, falls back to | ||
| :class:`fetchers.CharmstoreDownloader`. | ||
| """ | ||
| EXTRA_INFO_URL = CharmstoreDownloader.STORE_URL + '/meta/extra-info' | ||
|
|
||
| def fetch(self, dir_): | ||
| url = self.EXTRA_INFO_URL.format(self.entity) | ||
| repo_url = get(url).json().get('bzr-url') | ||
| if repo_url: | ||
| try: | ||
| fetcher = fetchers.get_fetcher(repo_url) | ||
| except fetchers.FetchError: | ||
| log.debug( | ||
| "No fetcher for %s, downloading from charmstore", | ||
| repo_url) | ||
| return super(CharmstoreRepoDownloader, self).fetch(dir_) | ||
| else: | ||
| return fetcher.fetch(dir_) | ||
| return super(CharmstoreRepoDownloader, self).fetch(dir_) | ||
|
|
||
| FETCHERS.insert(0, CharmstoreRepoDownloader) | ||
|
|
||
|
|
||
| class CharmstoreLayerDownloader(CharmstoreRepoDownloader): | ||
| """Clones the repo containing the top layer of a charm. | ||
| If the charm is not a layered charm, or the repo for the | ||
| top layer can not be determined, falls back to using | ||
| :class:`CharmstoreRepoDownloader`. | ||
| """ | ||
| LAYER_CONFIGS = ['layer.yaml', 'composer.yaml'] | ||
|
|
||
| def fetch(self, dir_): | ||
| for cfg in self.LAYER_CONFIGS: | ||
| url = '{}/{}'.format( | ||
| self.ARCHIVE_URL.format(self.entity), cfg) | ||
| result = get(url) | ||
| if not result.ok: | ||
| continue | ||
| repo_url = yaml.safe_load(result.text).get('repo') | ||
| if not repo_url: | ||
| continue | ||
| try: | ||
| fetcher = fetchers.get_fetcher(repo_url) | ||
| except fetchers.FetchError: | ||
| log.debug( | ||
| 'Charm %s has a repo set in %s, but no fetcher could ' | ||
| 'be found for the repo (%s).', self.entity, cfg, repo_url) | ||
| break | ||
| else: | ||
| return fetcher.fetch(dir_) | ||
| return super(CharmstoreLayerDownloader, self).fetch(dir_) | ||
|
|
||
| FETCHERS.insert(0, CharmstoreLayerDownloader) | ||
|
|
||
|
|
||
| def download_item(item, dir_): | ||
| series_dir = None | ||
|
|
||
| if item.startswith(LAYER_PREFIX): | ||
| dir_ = dir_ or os.environ.get('LAYER_PATH') | ||
| name = item[len(LAYER_PREFIX):] | ||
| elif item.startswith(INTERFACE_PREFIX): | ||
| dir_ = dir_ or os.environ.get('INTERFACE_PATH') | ||
| name = item[len(INTERFACE_PREFIX):] | ||
| else: | ||
| dir_ = dir_ or os.environ.get('JUJU_REPOSITORY') | ||
| if not item.startswith(CHARM_PREFIX): | ||
| item = CHARM_PREFIX + item | ||
|
|
||
| url_parts = item[len(CHARM_PREFIX):].split('/') | ||
| name = url_parts[-1] | ||
| if len(url_parts) == 2 and not url_parts[0].startswith('~'): | ||
| series_dir = url_parts[0] | ||
| elif len(url_parts) == 3: | ||
| series_dir = url_parts[1] | ||
|
|
||
| dir_ = dir_ or os.getcwd() | ||
| dir_ = os.path.abspath(os.path.expanduser(dir_)) | ||
|
|
||
| # Create series dir if we need to | ||
| if series_dir: | ||
| series_path = os.path.join(dir_, series_dir) | ||
| if not os.path.exists(series_path): | ||
| os.mkdir(series_path) | ||
| dir_ = series_path | ||
|
|
||
| # Abort if destination dir already exists | ||
| final_dest_dir = os.path.join(dir_, name) | ||
| if os.path.exists(final_dest_dir): | ||
| return "{}: {}".format(ERR_DIR_EXISTS, final_dest_dir) | ||
|
|
||
| # Create tempdir for initial download | ||
| tempdir = tempfile.mkdtemp() | ||
| atexit.register(shutil.rmtree, tempdir) | ||
| try: | ||
| # Download the item | ||
| fetcher = fetchers.get_fetcher(item) | ||
| download_dir = fetcher.fetch(tempdir) | ||
| except fetchers.FetchError: | ||
| return "Can't find source for {}".format(item) | ||
|
|
||
| # Copy download dir to final destination dir | ||
| shutil.copytree(download_dir, final_dest_dir) | ||
| print('Downloaded {} to {}'.format(item, final_dest_dir)) | ||
|
|
||
|
|
||
| def setup_parser(): | ||
| parser = argparse.ArgumentParser( | ||
| prog='charm pull-source', | ||
| description=textwrap.dedent(__doc__), | ||
| formatter_class=argparse.RawDescriptionHelpFormatter, | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| 'item', | ||
| help='Name of the charm, layer, or interface to download.' | ||
| ) | ||
| parser.add_argument( | ||
| 'dir', nargs='?', | ||
| help='Directory in which to place the downloaded source.', | ||
| ) | ||
| parser.add_argument( | ||
| '-v', '--verbose', | ||
| help='Show verbose output', | ||
| action='store_true', default=False, | ||
| ) | ||
| utils.add_plugin_description(parser) | ||
|
|
||
| return parser | ||
|
|
||
|
|
||
| def main(): | ||
| parser = setup_parser() | ||
| args = parser.parse_args() | ||
|
|
||
| if args.verbose: | ||
| logging.basicConfig( | ||
| format='%(levelname)s %(filename)s: %(message)s', | ||
| level=logging.DEBUG, | ||
| ) | ||
| else: | ||
| logging.basicConfig( | ||
| format='%(levelname)s: %(message)s', | ||
| level=logging.WARN, | ||
| ) | ||
|
|
||
| return download_item(args.item, args.dir) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import re | ||
| import subprocess | ||
|
|
||
| from collections import namedtuple | ||
|
|
||
| from . import utils | ||
|
|
||
|
|
||
| def get_recommended_repo(path): | ||
| """Given vcs directory ``path``, returns the url from which the repo | ||
| can be cloned. | ||
| For git, an 'upstream' remote will be preferred over 'origin'. | ||
| For bzr, the :parent: branch will be preferred. | ||
| For hg, the 'default' alias will be preferred. | ||
| Returns None if the directory is not a repo, or a remote url can not | ||
| be determined. | ||
| :param path: A :class:`path.path` to a directory | ||
| :return: A url string, or None | ||
| """ | ||
|
|
||
| Command = namedtuple("Command", "args parse") | ||
| cmds = [ | ||
| Command(['git', 'remote', '-v'], _parse_git), | ||
| Command(['bzr', 'info'], _parse_bzr), | ||
| Command(['hg', 'paths'], _parse_hg), | ||
| ] | ||
|
|
||
| if not path.exists(): | ||
| return None | ||
|
|
||
| with utils.cd(str(path)): | ||
| for cmd in cmds: | ||
| try: | ||
| output = subprocess.check_output(cmd.args) | ||
| if output: | ||
| repo = cmd.parse(output) | ||
| if repo: | ||
| return repo | ||
| except (subprocess.CalledProcessError, OSError): | ||
| continue | ||
|
|
||
|
|
||
| def _parse_git(txt): | ||
| pat = re.compile( | ||
| r'(?P<name>\S+)\s+(?P<url>\S+)\s+\((?P<type>[^\)]+)\)') | ||
| urls = {} | ||
| for line in txt.split('\n'): | ||
| match = pat.search(line) | ||
| if match: | ||
| d = match.groupdict() | ||
| if d['name'] == 'upstream' and d['type'] == 'fetch': | ||
| return d['url'].strip() | ||
| elif d['type'] == 'fetch': | ||
| urls[d['name']] = d['url'].strip() | ||
|
|
||
| if 'origin' in urls: | ||
| return urls['origin'] | ||
|
|
||
| for url in urls.values(): | ||
| return url | ||
|
|
||
|
|
||
| def _parse_bzr(txt): | ||
| branch_types = ['parent', 'push', 'submit'] | ||
| pat = re.compile( | ||
| r'(?P<branch_type>({})) branch: (?P<url>.*)'.format( | ||
| '|'.join(branch_types))) | ||
| matches = {} | ||
| for line in txt.split('\n'): | ||
| match = pat.search(line) | ||
| if match: | ||
| d = match.groupdict() | ||
| matches[d['branch_type']] = d['url'].strip() | ||
| if not matches: | ||
| return | ||
| for typ in branch_types: | ||
| url = matches.get(typ) | ||
| if url: | ||
| return url | ||
|
|
||
|
|
||
| def _parse_hg(txt): | ||
| pat = re.compile(r'(?P<name>[^\s]+) = (?P<url>.*)') | ||
| urls = [] | ||
| for line in txt.split('\n'): | ||
| match = pat.search(line) | ||
| if match: | ||
| d = match.groupdict() | ||
| if d['name'] == 'default': | ||
| return d['url'].strip() | ||
| else: | ||
| urls.append(d['url'].strip()) | ||
| return urls[0] if urls else None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright (C) 2016 Canonical Ltd. | ||
| # Copyright (C) 2016 Cloudbase Solutions SRL | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| from .template import PowerShellCharmTemplate # noqa |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright (C) 2016 Canonical Ltd. | ||
| # Copyright (C) 2016 Cloudbase Solutions SRL | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| import os | ||
| import os.path as path | ||
| import shutil | ||
| import subprocess | ||
|
|
||
| from charmtools.generators import CharmTemplate | ||
|
|
||
|
|
||
| class PowerShellCharmTemplate(CharmTemplate): | ||
| """ CharmTemplate specific to PowerShell charms. """ | ||
|
|
||
| # _EXTRA_FILES is the list of names of files present in the git repo | ||
| # we don't want transferred over to the charm template: | ||
| _EXTRA_FILES = ["README.md", ".git", ".gitmodules"] | ||
|
|
||
| _TEMPLATE_URL = "https://github.com/cloudbase/windows-charms-boilerplate" | ||
|
|
||
| def __init__(self): | ||
| self.skip_parsing += ["*.ps1", "*.psm1"] | ||
|
|
||
| def create_charm(self, config, output_dir): | ||
| cmd = "git clone --recursive {} {}".format( | ||
| self._TEMPLATE_URL, output_dir | ||
| ) | ||
|
|
||
| try: | ||
| subprocess.check_call(cmd.split()) | ||
| except OSError as e: | ||
| raise Exception( | ||
| "The below error has ocurred whilst attempting to clone" | ||
| "the powershell charm template. Please make sure you have" | ||
| "git installed on your system.\n" + e | ||
| ) | ||
|
|
||
| # iterate and remove all the unwanted files from the git repo: | ||
| for item in [path.join(output_dir, i) for i in self._EXTRA_FILES]: | ||
| if not path.exists(item): | ||
| continue | ||
|
|
||
| if path.isdir(item) and not path.islink(item): | ||
| shutil.rmtree(item) | ||
| else: | ||
| os.remove(item) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright (C) 2014 Canonical Ltd. | ||
| # Author: Clint Byrum <clint.byrum@canonical.com> | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| from .template import ReactiveBashCharmTemplate # noqa |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright (C) 2014 Canonical Ltd. | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| import logging | ||
| import os | ||
| import os.path as path | ||
| import time | ||
| import shutil | ||
| import subprocess | ||
| import tempfile | ||
|
|
||
| from Cheetah.Template import Template | ||
| from stat import ST_MODE | ||
|
|
||
| from charmtools.generators import ( | ||
| CharmTemplate, | ||
| ) | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class ReactiveBashCharmTemplate(CharmTemplate): | ||
| """Creates a reactive, layered bash-based charm""" | ||
|
|
||
| # _EXTRA_FILES is the list of names of files present in the git repo | ||
| # we don't want transferred over to the charm template: | ||
| _EXTRA_FILES = ["README.md", ".git", ".gitmodules"] | ||
|
|
||
| _TEMPLATE_URL = "https://github.com/juju-solutions/template-reactive-bash" | ||
|
|
||
| def create_charm(self, config, output_dir): | ||
| self._clone_template(config, output_dir) | ||
|
|
||
| for root, dirs, files in os.walk(output_dir): | ||
| for outfile in files: | ||
| if self.skip_template(outfile): | ||
| continue | ||
|
|
||
| self._template_file(config, path.join(root, outfile)) | ||
|
|
||
| def _template_file(self, config, outfile): | ||
| if path.islink(outfile): | ||
| return | ||
|
|
||
| mode = os.stat(outfile)[ST_MODE] | ||
| t = Template(file=outfile, searchList=(config)) | ||
| o = tempfile.NamedTemporaryFile( | ||
| dir=path.dirname(outfile), delete=False) | ||
| os.chmod(o.name, mode) | ||
| o.write(str(t)) | ||
| o.close() | ||
| backupname = outfile + str(time.time()) | ||
| os.rename(outfile, backupname) | ||
| os.rename(o.name, outfile) | ||
| os.unlink(backupname) | ||
|
|
||
| def _clone_template(self, config, output_dir): | ||
| cmd = "git clone --recursive {} {}".format( | ||
| self._TEMPLATE_URL, output_dir | ||
| ) | ||
|
|
||
| try: | ||
| subprocess.check_call(cmd.split()) | ||
| except OSError as e: | ||
| raise Exception( | ||
| "The below error has occurred whilst attempting to clone" | ||
| "the charm template. Please make sure you have git" | ||
| "installed on your system.\n" + e | ||
| ) | ||
|
|
||
| # iterate and remove all the unwanted files from the git repo: | ||
| for item in [path.join(output_dir, i) for i in self._EXTRA_FILES]: | ||
| if not path.exists(item): | ||
| continue | ||
|
|
||
| if path.isdir(item) and not path.islink(item): | ||
| shutil.rmtree(item) | ||
| else: | ||
| os.remove(item) | ||
|
|
||
| # rename handlers.sh to <charm-name>.sh | ||
| new_name = '%s.sh' % config['metadata']['package'].replace('-', '_') | ||
| os.rename(os.path.join(output_dir, 'reactive', 'handlers.sh'), | ||
| os.path.join(output_dir, 'reactive', new_name)) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright (C) 2014 Canonical Ltd. | ||
| # Author: Clint Byrum <clint.byrum@canonical.com> | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| from .template import ReactivePythonCharmTemplate # noqa |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| #!/usr/bin/python | ||
| # | ||
| # Copyright (C) 2014 Canonical Ltd. | ||
| # | ||
| # This program is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # This program is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| import logging | ||
| import os | ||
| import os.path as path | ||
| import time | ||
| import shutil | ||
| import subprocess | ||
| import tempfile | ||
|
|
||
| from Cheetah.Template import Template | ||
| from stat import ST_MODE | ||
|
|
||
| from charmtools.generators import ( | ||
| CharmTemplate, | ||
| ) | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| class ReactivePythonCharmTemplate(CharmTemplate): | ||
| """Creates a reactive, layered python-based charm""" | ||
|
|
||
| # _EXTRA_FILES is the list of names of files present in the git repo | ||
| # we don't want transferred over to the charm template: | ||
| _EXTRA_FILES = ["README.md", ".git", ".gitmodules"] | ||
|
|
||
| _TEMPLATE_URL = "https://github.com/juju-solutions/template-reactive-python" | ||
|
|
||
| def create_charm(self, config, output_dir): | ||
| self._clone_template(config, output_dir) | ||
|
|
||
| for root, dirs, files in os.walk(output_dir): | ||
| for outfile in files: | ||
| if self.skip_template(outfile): | ||
| continue | ||
|
|
||
| self._template_file(config, path.join(root, outfile)) | ||
|
|
||
| def _template_file(self, config, outfile): | ||
| if path.islink(outfile): | ||
| return | ||
|
|
||
| mode = os.stat(outfile)[ST_MODE] | ||
| t = Template(file=outfile, searchList=(config)) | ||
| o = tempfile.NamedTemporaryFile( | ||
| dir=path.dirname(outfile), delete=False) | ||
| os.chmod(o.name, mode) | ||
| o.write(str(t)) | ||
| o.close() | ||
| backupname = outfile + str(time.time()) | ||
| os.rename(outfile, backupname) | ||
| os.rename(o.name, outfile) | ||
| os.unlink(backupname) | ||
|
|
||
| def _clone_template(self, config, output_dir): | ||
| cmd = "git clone --recursive {} {}".format( | ||
| self._TEMPLATE_URL, output_dir | ||
| ) | ||
|
|
||
| try: | ||
| subprocess.check_call(cmd.split()) | ||
| except OSError as e: | ||
| raise Exception( | ||
| "The below error has occurred whilst attempting to clone" | ||
| "the charm template. Please make sure you have git" | ||
| "installed on your system.\n" + e | ||
| ) | ||
|
|
||
| # iterate and remove all the unwanted files from the git repo: | ||
| for item in [path.join(output_dir, i) for i in self._EXTRA_FILES]: | ||
| if not path.exists(item): | ||
| continue | ||
|
|
||
| if path.isdir(item) and not path.islink(item): | ||
| shutil.rmtree(item) | ||
| else: | ||
| os.remove(item) | ||
|
|
||
| # rename handlers.py to <charm-name>.py | ||
| new_name = '%s.py' % config['metadata']['package'].replace('-', '_') | ||
| os.rename(os.path.join(output_dir, 'reactive', 'handlers.py'), | ||
| os.path.join(output_dir, 'reactive', new_name)) |