def git_ignore(root, patterns): classified_patterns = [] with cd(root): for pattern in patterns: if pattern: if '/' in pattern[:-1]: ignored_paths = (Pattern('path', match) for match in glob.glob(pattern)) classified_patterns.extend(ignored_paths) else: classified_patterns.append(Pattern('file', pattern)) def git_ignorer(src, names): relative_src = src[len(root):].lstrip(r"""\/""") ignored = [] for name in names: for pattern in classified_patterns: if pattern.type == 'path': if path.join(relative_src, name) == os.path.normpath(pattern.value): ignored.append(name) elif pattern.type == 'file': ignore_name = pattern.value if pattern.value[-1] in ('/', '\\'): if path.isdir(path.join(src, name)): ignore_name = ignore_name[:-1] if fnmatch.fnmatch(name, ignore_name): ignored.append(name) return set(ignored) return git_ignorer
def _serve(build, target, address, port): LOG.info("Initializing Forge Live..."); _update_path_for_node(build) _setup_livereload(build, target) LOG.info("Serving live updates for '%s' on: http://%s:%s" % (target, address, port)); # call grunt serve if sys.platform.startswith("win"): grunt = path.join("node_modules", ".bin", "grunt.cmd") else: grunt = path.join("node_modules", ".bin", "grunt") grunt_serve_cmd = [grunt, "serve", "--address", address, "--port", "{port}".format(port=port), "--target", target, "--no-color"] def could_not_start_grunt(line): return line.startswith("TODO error condition") with cd(path.join("development", "live")): process_group = utils.ProcessGroup() process_group.spawn( grunt_serve_cmd, fail_if=could_not_start_grunt, command_log_level=logging.DEBUG )
def _setup_livereload(build, target): '''setup livereload service''' template = lib.expand_relative_path(build, path.join('.template', 'lib', 'live')) live = lib.expand_relative_path(build, path.join('development', 'live')) if not path.isdir(live): os.mkdir(live) with cd(live): shutil.copyfile(path.join(template, "package.json"), "package.json") shutil.copyfile(path.join(template, "Gruntfile.js"), "Gruntfile.js") _npm(build, "install")
def run_web(build): """Run an instance of Node locally""" # TODO: port should be a parameter/configuration port = 3000 _update_path_for_node(build) def show_local_server(): LOG.info("Attempting to open browser at http://localhost:%d/" % port) _open_url("http://localhost:%d/" % port) with cd(path.join("development", "web")): timer = None try: # TODO: annoyingly difficult to kill npm processes on windows - npm.cmd actually # launches an instance of node as a subprocess which is the real thing you need to kill! # might be possible to kill npm.cmd in a nicer way, e.g. sending a CTRL_C event, needs # a bit of experimentation _npm(build, "install") attempts = 0 while not _port_available(port): LOG.info('Port still in use, attempting to send a kill signal') #TODO: appropriate timeout and handling requests.post('http://localhost:%d/_forge/kill/' % port) time.sleep(1) attempts += 1 if attempts > 5: raise WebError( "Port %d seems to be in use, you should specify a different port to use" % port) timer = threading.Timer(3, show_local_server).start() _node(build, "./web.js", command_log_level=logging.INFO, check_for_interrupt=True, env=dict(os.environ, PORT=str(port), FORGE_DEBUG='1')) finally: if timer: timer.cancel()
def run_web(build): """Run an instance of Node locally""" # TODO: port should be a parameter/configuration port = 3000 _update_path_for_node(build) def show_local_server(): LOG.info("Attempting to open browser at http://localhost:%d/" % port) _open_url("http://localhost:%d/" % port) with cd(path.join("development", "web")): timer = None try: # TODO: annoyingly difficult to kill npm processes on windows - npm.cmd actually # launches an instance of node as a subprocess which is the real thing you need to kill! # might be possible to kill npm.cmd in a nicer way, e.g. sending a CTRL_C event, needs # a bit of experimentation _npm(build, "install") attempts = 0 while not _port_available(port): LOG.info('Port still in use, attempting to send a kill signal') #TODO: appropriate timeout and handling requests.post('http://localhost:%d/_forge/kill/' % port) time.sleep(1) attempts += 1 if attempts > 5: raise WebError("Port %d seems to be in use, you should specify a different port to use" % port) timer = threading.Timer(3, show_local_server).start() _node(build, "./web.js", command_log_level=logging.INFO, check_for_interrupt=True, env=dict(os.environ, PORT=str(port), FORGE_DEBUG='1')) finally: if timer: timer.cancel()
def package_web(build): interactive = build.tool_config.get('general.interactive', True) development = lib.expand_relative_path(build, 'development/web') output = lib.expand_relative_path(build, 'release/web/heroku') # deploy to Heroku with cd(development): api_key = _get_heroku_api_key(build) chosen_app = _get_app_to_push_to(build, api_key) if not path.isdir(output): os.makedirs(output) with cd(output): if not path.isdir('.git'): LOG.debug('Creating git repo') _git('init') LOG.debug('Create dummy first commit') with open('.forge.txt', 'w') as forge_file: forge_file.write('') _git('add', '.') _git('commit', '-am', 'first commit') # remove all previous files/folders except for .git! with cd(output): for f in os.listdir('.'): if not f == '.git': if path.isfile(f): os.remove(f) elif path.isdir(f): shutil.rmtree(f) # copy code from development to release! with cd(development): for f in os.listdir('.'): if path.isfile(f): shutil.copy2(f, output) elif path.isdir(f) and path.basename(f) != '.git': shutil.copytree(f, path.join(output, f), ignore=shutil.ignore_patterns('.git')) with cd(output): # setup with the specified remote LOG.debug('Setting up git remote for %s' % chosen_app) # remove any previous remote try: _git('remote', 'rm', 'heroku') except WebError: pass _git('remote', 'add', 'heroku', '[email protected]:%s.git' % chosen_app) # commit _git('add', '.') diff = _git('diff', 'HEAD') if not diff.strip(): # not interactive basically means we're using the trigger toolkit, where 'forge build' # doesn't really make sense if interactive: LOG.warning("No app changes detected: did you forget to forge build?") else: LOG.warning("No app changes detected, pushing to heroku anyway") else: _git('commit', '-am', 'forge package web') # push LOG.info('Deploying to %s.herokuapp.com' % chosen_app) # TODO: when running a packaged up toolkit there is no commandline... need to make sure # we can use the ssh key earlier somehow and ask for passphrase if not # also provide docs on how to use ssh-agent/pageant if not interactive: LOG.warning('If the packaging process hangs here, you need to set up ssh-agent or Pageant') # TODO: show our own one line progress bar when running non-verbosely push_output = _git('push', 'heroku', '--all', '--force', command_log_level=logging.INFO) if push_output.startswith('Everything up-to-date'): remote_output = _git('remote', '-v') remote_pattern = re.compile(r'[email protected]:(.*?).git \(fetch\)') remote_match = remote_pattern.search(remote_output) if remote_match: app_url = 'http://%s.herokuapp.com' % remote_match.group(1) _open_url(app_url) LOG.info('Deployed at %s' % app_url) else: deploy_pattern = re.compile(r'(http://[^ ]+) deployed to Heroku') deploy_match = deploy_pattern.search(push_output) if deploy_match: _open_url(deploy_match.group(1)) LOG.info('Deployed at %s' % deploy_match.group(1))
def package_web(build): interactive = build.tool_config.get('general.interactive', True) development = lib.expand_relative_path(build, 'development/web') output = lib.expand_relative_path(build, 'release/web/heroku') # deploy to Heroku with cd(development): api_key = _get_heroku_api_key(build) chosen_app = _get_app_to_push_to(build, api_key) if not path.isdir(output): os.makedirs(output) with cd(output): if not path.isdir('.git'): LOG.debug('Creating git repo') _git('init') LOG.debug('Create dummy first commit') with open('.forge.txt', 'w') as forge_file: forge_file.write('') _git('add', '.') _git('commit', '-am', 'first commit') # remove all previous files/folders except for .git! with cd(output): for f in os.listdir('.'): if not f == '.git': if path.isfile(f): os.remove(f) elif path.isdir(f): shutil.rmtree(f) # copy code from development to release! with cd(development): for f in os.listdir('.'): if path.isfile(f): shutil.copy2(f, output) elif path.isdir(f) and path.basename(f) != '.git': shutil.copytree(f, path.join(output, f), ignore=shutil.ignore_patterns('.git')) with cd(output): # setup with the specified remote LOG.debug('Setting up git remote for %s' % chosen_app) # remove any previous remote try: _git('remote', 'rm', 'heroku') except WebError: pass _git('remote', 'add', 'heroku', '[email protected]:%s.git' % chosen_app) # commit _git('add', '.') diff = _git('diff', 'HEAD') if not diff.strip(): # not interactive basically means we're using the trigger toolkit, where 'forge build' # doesn't really make sense if interactive: LOG.warning( "No app changes detected: did you forget to forge build?" ) else: LOG.warning( "No app changes detected, pushing to heroku anyway") else: _git('commit', '-am', 'forge package web') # push LOG.info('Deploying to %s.herokuapp.com' % chosen_app) # TODO: when running a packaged up toolkit there is no commandline... need to make sure # we can use the ssh key earlier somehow and ask for passphrase if not # also provide docs on how to use ssh-agent/pageant if not interactive: LOG.warning( 'If the packaging process hangs here, you need to set up ssh-agent or Pageant' ) # TODO: show our own one line progress bar when running non-verbosely push_output = _git('push', 'heroku', '--all', '--force', command_log_level=logging.INFO) if push_output.startswith('Everything up-to-date'): remote_output = _git('remote', '-v') remote_pattern = re.compile( r'[email protected]:(.*?).git \(fetch\)') remote_match = remote_pattern.search(remote_output) if remote_match: app_url = 'http://%s.herokuapp.com' % remote_match.group(1) _open_url(app_url) LOG.info('Deployed at %s' % app_url) else: deploy_pattern = re.compile( r'(http://[^ ]+) deployed to Heroku') deploy_match = deploy_pattern.search(push_output) if deploy_match: _open_url(deploy_match.group(1)) LOG.info('Deployed at %s' % deploy_match.group(1))