def f2h(self, args): """ 本迭代的特性分支转修复分支 :return: """ branch = None while args: branch = args.pop(0) if not branch: raise exception.FlowException(u'未指定分支名称') old_branch = igit.real_branch( branch, iconfig.read_config('system', 'branch')['feature_prefix']) new_branch = igit.real_branch( branch.split('/')[-1], iconfig.read_config('system', 'branch')['hotfix_prefix']) if not old_branch or not new_branch: raise exception.FlowException(u'分支名称不合法') if old_branch not in igit.local_branches(): raise exception.FlowException(u'分支不存在:%s' % old_branch) if ihelper.confirm(u'确定将特性分支%s转为修复分支%s吗?' % (old_branch, new_branch)) == 'y': git.Git('rename', [old_branch, new_branch]).execute() ok() else: ok(u'取消操作')
def delete(self, args): """ 删除分支 :return: """ if not args: raise exception.FlowException(u'指令格式错误,请输入h %s查看使用说明' % self.cmd) branch = None delete_remote = True auto_delete = False while args: c = args.pop(0) if c == '--np' or c == '--no-push': delete_remote = False elif c == '-y': auto_delete = True else: branch = igit.real_branch(c, self.cmd) if not branch: raise exception.FlowException(u'请输入分支名称') if auto_delete or ihelper.confirm(u'确定删除分支 %s 吗?' % branch, default='n') == 'y': igit.delete_branch(branch, del_remote=delete_remote)
def rename(self): """ 分支重命名。old和new需要时绝对分支名 """ if len(self.args) < 2: raise exception.FlowException('指令格式错误,请输入help查看使用说明') old = self.args[0] new = self.args[1] local_branches = igit.local_branches() if old not in local_branches: raise exception.FlowException(u'分支名称不存在:%s' % old) remote_branches = igit.remote_branches() # 名称重复性检测 if new in local_branches or new in remote_branches: raise exception.FlowException(u'该分支已经存在:%s' % new) info(u'重命名分支:%s -> %s...' % (old, new)) # 重命名本地分支 ihelper.execute('git branch -m ' + old + ' ' + new, raise_err=True) # 删除远程分支(如果有的话) if old in remote_branches: ihelper.execute('git push --delete origin ' + old) # 上传新分支到远程 ihelper.execute('git push -u origin ' + new + ':' + new) #防止重命名后检查master更新情况 igit.set_last_sync_master_date(new)
def f2f(self, args): """ 修复分支转修复分支,默认转到下个迭代 :param list args: :return: """ if '-n' not in args and '-s' not in args: args.append('-n') b_info = self.__branch_and_sprint(args) if not b_info['branch']: raise exception.FlowException(u'未指定分支名称') old_branch = igit.real_branch( b_info['branch'], iconfig.read_config('system', 'branch')['feature_prefix']) new_branch = igit.real_branch( '%s/%s' % (b_info['sprint'], b_info['branch'].split('/')[-1]), iconfig.read_config('system', 'branch')['feature_prefix']) if not old_branch or not new_branch: raise exception.FlowException(u'分支名称不合法') if old_branch not in igit.local_branches(): raise exception.FlowException(u'分支不存在:%s' % old_branch) if ihelper.confirm(u'确定将特性分支%s转为特性分支%s吗?' % (old_branch, new_branch)) == 'y': git.Git('rename', [old_branch, new_branch]).execute() ok() else: ok(u'取消操作')
def required_check(): """ 必须进行的检查 """ iprint.info(u'正在进行工作环境检查...') #git版本检测 git_version = igit.git_version() if int(git_version[0]) < 2: raise exception.FlowException(u'git版本过低,请安装2.0.0以上版本') # 检查project.json有没有配置以及路径是否正确 proj_cfg = iconfig.read_config('project', use_cache=False) if not proj_cfg: raise exception.FlowException( u'请配置项目信息(config/project.json文件,具体格式参见readme.md文件)') for proj_name, info in proj_cfg.items(): if proj_name == 'global': raise exception.FlowException(u'项目名称不能叫global,请使用别的名称') if not info['dir'] or not os.path.exists( info['dir']) or not os.path.isdir(info['dir']): raise exception.FlowException(u'项目' + proj_name + u'的目录配置不正确') # 检测目录是否有效的git仓库 if not igit.dir_is_repository(info['dir']): raise exception.FlowException(u'目录' + info['dir'] + u'不是有效的git仓库') # sprint版本号检测 return isprint.check_sprint()
def tag(self): """ 在生产分支上打标签 :return: """ if not self.args: return ihelper.execute('git tag') #将要在该分支上打标签 tag_branch = igit.product_branch() # 当前工作空间是否干净 if igit.current_branch() != tag_branch and not igit.workspace_is_clean( ): raise exception.FlowException(u'工作区中尚有未保存的内容') tag_name = None comment = '' while self.args: c = self.args.pop(0) if c == '-a': tag_name = self.args.pop(0) elif c == '-m': while self.args: if self.args[0].startswith('-'): break comment += self.args.pop(0) + ' ' if not comment: raise exception.FlowException(u'请输入标签注释') if not tag_name: tag_name = igit.tag_name() if not tag_name: raise exception.FlowException(u'未设置tag name') if ihelper.confirm(u'将在分支 %s 上打tag:%s, ok?' % (tag_branch, tag_name)) != 'y': warn(u'取消操作') return c_branch = igit.current_branch() try: #切换分支 if c_branch != tag_branch: info(u'切换到分支 %s:' % tag_branch) ihelper.execute('git checkout %s' % tag_branch) #打tag print igit.tag(tag_name, comment) except exception.FlowException, e: error(u'操作失败:') raise e
def test(self, args): """ 发布到测试分支,只允许单个分支发布 :param args: :return: """ # 当前工作空间是否干净 if not igit.workspace_is_clean(): raise exception.FlowException(u'工作区中尚有未保存的内容') if not args: branch = igit.current_branch() else: branch = args.pop(0) branch = igit.real_branch(branch, self.cmd) if branch != igit.current_branch(): # 切换分支 info(u'切换到分支%s' % branch) ihelper.execute('git checkout ' + branch) # 当前工作空间是否干净 if not igit.workspace_is_clean(): raise exception.FlowException(u'工作区中尚有未保存的内容') igit.sync_branch() # 切换到test分支 test_branch = igit.test_branch() info(u'切换到分支%s' % test_branch) ihelper.execute('git checkout ' + test_branch) # 当前工作空间是否干净 if not igit.workspace_is_clean(): raise exception.FlowException(u'工作区中尚有未保存的内容') # 合并 info(u'正在将%s合并到%s上...' % (branch, test_branch)) igit.merge(branch) # 正常执行后,工作空间应该是干净的 if not igit.workspace_is_clean(): raise exception.FlowException(u'合并失败,请用git status查看工作空间详情') # 将test推到远程 igit.push() # 切换到原来的分支 info(u'切换回%s' % branch) ihelper.execute('git checkout ' + branch) ok(u'合并到' + test_branch + u'成功!')
def delete(self): """ 删除本地匹配模式的多个分支,并删除远程相应分支 :return: """ if not self.args: raise exception.FlowException(u'指令格式错误,请输入help查看使用说明') tag_pattern = None del_remote = True del_branches = [] while self.args: c = self.args.pop(0) if c == '--no-remote': del_remote = False else: tag_pattern = c if not tag_pattern: raise exception.FlowException(u'请指定需要删除的分支匹配模式') for branch in igit.local_branches(): if branch == igit.product_branch(): continue if re.match(tag_pattern, branch): del_branches.append(branch) if not del_branches: warn(u'没有符合条件的分支') return #打印出将要删除的分支列表 warn(u'将要删除以下分支%s:' % ('(以及其对应的远程分支)' if del_remote else '')) for del_branch in del_branches: ok(del_branch) print if (ihelper.confirm( u'确定删除吗%s?' % ('(同时删除远程分支,删除后不可恢复)' if del_remote else ''), 'n') != 'y'): return #删除分支 for del_branch in del_branches: try: igit.delete_branch(del_branch, del_remote) except exception.FlowException, e: warn(str(e))
def tag(tag_name, comment): """ 打标签 :return: """ out = ihelper.execute('git tag -a %s -m "%s"' % (tag_name, comment), print_out=False) if (out.find('already exists') > -1): raise exception.FlowException(u'已存在该标签') out = ihelper.execute('git push origin %s' % tag_name, print_out=False) if (out.find('already exists') > -1): raise exception.FlowException(u'远程已存在该标签') return out
def sync_branch(): if not workspace_is_clean(): raise exception.FlowException(u'当前工作空间不是clean状态') # 先pull再push pull() push(need_pull=False)
def __post_publish(self, proj, tag_list): """ 项目发布完成的后续工作 :return: """ # 执行后续钩子 self.exec_hook("product", "post", proj) # 钩子执行后需检查工作空间是否干净 if igit.workspace_at_status(iglobal.GIT_CONFLICT): raise exception.FlowException( u'出现冲突,请手工解决冲突后执行 %s p --continue 继续。或执行 %s p --abort 结束' % (self.cmd, self.cmd)) if igit.workspace_at_status( iglobal.GIT_UNCOMMITED) or igit.workspace_at_status( iglobal.GIT_UNSTAGED): git.Git('commit', ['-p', '发布生产:执行自定义钩子后提交']).execute() # 标签 info(u'项目 %s 发布完成,打标签:' % proj) proj_tag = self.__tag() if proj_tag: tag_list.append((proj, proj_tag))
def real_cmd(cmd, raise_err=True, valid=True, top_cmd=None): """ valid=True时,如果不指定top_cmd,则认为cmd是一级指令,否则认为是top_cmd的二级指令 :param str top_cmd: :param cmd: :param raise_err: :param valid: :return: """ config = iconfig.read_config('system') alias = config['alias'] cmd = alias[cmd] if alias.has_key(cmd) else cmd if valid: error = False if top_cmd: top_cmd = alias[top_cmd] if top_cmd in alias else top_cmd cls = config['cmd_cls'][top_cmd] if top_cmd in config['cmd_cls'] else None if not cls: error = True else: if cmd not in dir(eval('%s.%s' % (cls.lower(), cls))): error = True elif not config['cmd_cls'].has_key(cmd): error = True if error: if raise_err: raise exception.FlowException(u'无效指令') else: return None return cmd
def real_branch( branch, prefix): """ 获取完整的分支名 :param str prefix: :param str branch: :return: str """ if not branch: raise exception.FlowException(u'参数错误') if branch.find(':') != -1: branch = branch.split(':')[1] original_branch = branch branch = filter(lambda x:x != '', branch.split('/')) config = iconfig.read_config('system', 'branch') if len(branch) > 2 and branch[0] != config['feature_prefix']: raise exception.FlowException(u'分支格式不合法') if len(branch) == 3: branch[1] = isprint.get_sprint(branch[1]) if not branch[1]: raise exception.FlowException(u'分支格式不合法') return '/'.join(branch) if len(branch) == 2: if branch[0] == 'hotfix': return '/'.join(branch) elif isprint.check_sprint_format(branch[0], True): # 迭代号开始的,此时prefix只能是feature if prefix != config['feature_prefix']: raise exception.FlowException(u'分支格式不合法') branch[0] = isprint.get_sprint(branch[0]) branch.insert(0, config[prefix + '_prefix']) else: # 看是否以feature或hotfix开头 if branch[0] != config['feature_prefix'] and branch[0] != config['hotfix_prefix']: raise exception.FlowException(u'分支格式不合法') return '/'.join(branch) if original_branch.find('/') == 0: return config[prefix + '_prefix'] + original_branch else: return config[prefix + '_prefix'] + ('/' + iglobal.SPRINT if prefix == 'feature' else '') + '/' + branch[0]
def check_dir(): runtime_dir = iglobal.BASE_DIR + '/runtime/' config_dir = iglobal.BASE_DIR + '/config/' log_dir = iglobal.BASE_DIR + '/log/' config_file = config_dir + 'system.json' project_file = config_dir + 'project.json' if not os.path.exists(config_dir): raise exception.FlowException(u'目录缺失:%s' % config_dir) if not os.path.exists(config_file): raise exception.FlowException(u'文件缺失:%s' % config_file) if not os.path.exists(project_file): raise exception.FlowException(u'文件缺失:%s' % project_file) if not os.path.isdir(runtime_dir): os.mkdir(runtime_dir) if not os.path.isdir(log_dir): os.mkdir(log_dir) return True
def execute(cmd, print_out=True, raise_err=False, return_result=False): if not print_out: return_result = True # 不关心异常且需要输出且不需要返回时,直接调用os.system if print_out and not raise_err and not return_result: if iglobal.SILENCE: p = os.popen(cmd) out = p.read() p.close() return out else: sys.stdout.flush() return os.system(cmd) else: # git指令需要特殊处理 is_git_cmd = True if cmd.startswith('git ') else False p = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True) out, err = p.communicate() del p # 致命git错误必须抛出 if is_git_cmd and igit.is_fatal_git_error(out + err): raise exception.FlowException(u'发生致命git错误:\n%s' % (out + err)) if err: if raise_err: # 将错误抛出由外界处理 raise exception.FlowException(err) else: out = err + out if print_out and not iglobal.SILENCE: iprint.info(out) if return_result: return out.rstrip('\n')
def sprint(args): """ 切换到某个迭代 :param args: """ sprint = isprint.get_sprint(None if not args else args[0]) if not sprint: raise exception.FlowException(u'版本号格式错误') iglobal.SPRINT = sprint ihelper.write_runtime('sprint', sprint)
def cd(args): """ 进入项目目录 :param args: """ if not args: raise exception.FlowException(u'指令格式不正确,请键入help查看该指令使用方式') proj_name = args[0] proj = iconfig.read_config('project', proj_name) if not proj or not proj.has_key('dir'): raise exception.FlowException(u'未配置项目' + proj_name + u'信息,请进入config/project.json中正确配置') if not os.path.isdir(proj['dir']): raise exception.FlowException(u'该项目git根目录配置不正确,请进入config/project.json中正确配置') iglobal.PROJECT = proj_name ihelper.write_runtime('project', proj_name) os.chdir(proj['dir'])
def check_sprint(): """ 迭代版本号检查 """ sprint = ihelper.read_runtime('sprint') if not sprint: raise exception.FlowException(u'尚未设置迭代版本号,请使用sp指令设置正确的迭代版本号') else: sp_date = get_date_from_sprint(sprint).split('-') now = time.strftime('%Y-%m-' + '01').split('-') d1 = datetime.datetime(int(sp_date[0]), int(sp_date[1]), int(sp_date[2])) d2 = datetime.datetime(int(now[0]), int(now[1]), int(now[2])) diff = abs((d1 - d2).days) if diff >= 90: raise exception.FlowException(u'迭代版本号(' + sprint + u')和当前日期不符,请使用sp指令重新设置正确的迭代版本号') return True
def simple_branch(branch): """ 获取分支精简名称 :param str branch: :return: str """ if not branch: raise exception.FlowException(u'请输入分支名称') if branch.startswith('/') and branch.count('/') == 1: return branch return branch.split('/').pop()
def pull(): """ 拉取当前分支 :return: """ fetch(branch=current_branch()) if not workspace_at_status(iglobal.GIT_BEHIND) and not workspace_at_status(iglobal.GIT_DIVERGED): return ihelper.execute('git rebase') if workspace_at_status(iglobal.GIT_CONFLICT): raise exception.FlowException(u'拉取远程分支出错:冲突。请手工解决冲突后执行git add . && git rebase --continue,然后再重新执行命令')
def commit(self): """ 提交 :return: """ comment = '' push = False if not self.args: raise exception.FlowException(u'指令格式错误,请输入h ft查看使用说明') while self.args: c = self.args.pop(0) if c == '-p': push = True else: comment += ' %s' % c comment = comment.strip() if not comment: raise exception.FlowException(u'请填写提交说明') curr_branch = igit.current_branch() # 提交 ihelper.execute('git add .') ihelper.execute('git commit -m "' + igit.comment(comment, curr_branch.split('/')[0]). decode('utf-8').encode(iglobal.FROM_ENCODING) + '"') if push: igit.push(curr_branch) ok(u'提交' + (u'并推送到远程仓库' if push else '') + u'成功!')
def sync(project, prefix=None, only_this_sprint=True, deep=False): """ 同步本地和远程分支 :param bool deep: 是否深层合并,只有深层合并才会同步本地和远程都存在的分支(暂未实现),否则只是创建本地没有的分支和推远程没有的分支 :param project: :param prefix: :param only_this_sprint: :return: """ iglobal.SILENCE = True old_project = iglobal.PROJECT try: # 进入该项目目录 if project != iglobal.PROJECT: command.Extra('cd', [project]).execute() if prefix and only_this_sprint: prefix = '%s/%s' % (prefix.rstrip('/'), iglobal.SPRINT) # remote_branches里面已经执行了git fetch了,此处就不再执行了 l_brs = local_branches() r_brs = remote_branches() if prefix: l_brs = filter(lambda x:str(x).startswith(prefix), l_brs) r_brs = filter(lambda x:str(x).startswith(prefix), r_brs) local_only = list(set(l_brs).difference(set(r_brs))) remote_only = list(set(r_brs).difference(set(l_brs))) the_same = list(set(l_brs).intersection(set(r_brs))) # 仅在本地存在的,推到远程去 for l_o_br in local_only: ihelper.execute('git push -u origin %s:%s' % (l_o_br, l_o_br)) # 仅远程存在的,创建本地相关分支 for r_o_br in remote_only: ihelper.execute('git branch %s origin/%s' % (r_o_br, r_o_br)) # 两边都存在的,同步 if the_same and deep: raise exception.FlowException(u'暂未实现') if old_project != iglobal.PROJECT: command.Extra('cd', [old_project]).execute() finally: iglobal.SILENCE = False
def checkout(self, args): """ 切换到某个特性/修复分支并拉取最新代码(如果工作空间是干净的) :return: """ branch = None sync_remote = False while args: c = args.pop(0) if c == '-r' or c == '--remote': sync_remote = True else: branch = igit.real_branch(c, self.cmd) current_branch = igit.current_branch() if not branch: branch = current_branch if branch == current_branch: sync_remote = True __error = False if branch != current_branch: # 检查当前分支下工作区状态 if not igit.workspace_is_clean(): raise exception.FlowException( u'工作区中尚有未提交的内容,请先用commit提交或用git stash保存到Git栈中') out = ihelper.execute('git checkout ' + branch, return_result=True) # git的checkout指令输出在stderr中 if 'Switched to branch' not in out: __error = True if __error: warn(u'checkout失败') return if sync_remote: info('fetch from remote...') igit.fetch(branch=branch) if igit.workspace_at_status( iglobal.GIT_BEHIND) or igit.workspace_at_status( iglobal.GIT_BEHIND): warn(u'远程仓库已有更新,请执行 git rebase 获取最新代码')
def delete_branch(branchName, del_remote=False): """ 删除分支 :param branchName: :param del_remote: :return: """ if branchName == current_branch(): raise exception.FlowException('can not delete current branch') info(u'删除本地分支 %s ...' % branchName) ihelper.execute('git branch -D %s' % branchName, raise_err=True) #删除远程分支 if del_remote: info(u'删除远程分支 origin/%s ...' % branchName) ihelper.execute('git push --delete origin %s' % branchName) ok(u'删除成功!')
def merge(branch, need_push=True, need_pull=True): """ 将branch合并到当前分支 :param need_pull: :param need_push: :param branch: :return: """ # 从远程仓库拉最新代码 if need_pull: pull() # 合并(git合并冲突信息在stdout中) ihelper.execute('git merge --no-ff ' + branch) if workspace_at_status(iglobal.GIT_CONFLICT): ihelper.execute('git status -s') raise exception.FlowException(u'合并失败:发生冲突。请手工解决冲突后执行git add . && git commit,然后重新执行命令') # 推送到远程仓库 if need_push: ihelper.execute('git push')
def execute(self): raise exception.FlowException(u"指令尚未实现")
def create(self, args): """ 创建特性/修复分支。一次只能创建一个,会推到远端,且切换到此分支 :return: """ if not args: raise exception.FlowException(u'指令格式错误,请输入h %s查看使用说明' % self.cmd) branch = None auto_create_from_remote = False push_to_remote = True while args: c = args.pop(0) if c == '-y': auto_create_from_remote = True elif c == '--np' or c == '--no-push': push_to_remote = False else: branch = c if not branch: raise exception.FlowException(u'请输入分支名称') # 分支简称不可与项目、迭代名称相同(防止一些指令出现歧义) simple_branch = igit.simple_branch(branch) if simple_branch == iglobal.SPRINT or simple_branch in ihelper.projects( ): raise exception.FlowException(u'分支简称不可与项目或迭代名同名') branch = igit.real_branch(branch, self.cmd) # 检查当前分支下工作区状态 if not igit.workspace_is_clean(): raise exception.FlowException( u'工作区中尚有未提交的内容,请先用git commit提交或用git stash保存到Git栈中') # 分支名称重复性检查 info(u'检查本地分支...') if branch in igit.local_branches(): raise exception.FlowException(u'该分支名称已经存在') # 本地没有但远程有 create_from_remote = False info(u'检查远程分支...') if branch in igit.remote_branches(): if not auto_create_from_remote and ihelper.confirm( u'远程仓库已存在%s,是否基于该远程分支创建本地分支?' % branch) != 'y': return else: create_from_remote = True say(('white', u'正在创建分支'), ('sky_blue', branch), ('white', '...')) if create_from_remote: # 基于远程分支创建本地分支,会自动追踪该远程分支 ihelper.execute('git checkout -b ' + branch + ' origin/' + branch) else: # 切换到生产分支 p_branch = igit.product_branch() ihelper.execute('git checkout ' + p_branch) igit.pull() # 基于本地生产分支创建新分支 ihelper.execute('git checkout -b ' + branch) # 推送到远程 if push_to_remote: ihelper.execute('git push -u origin ' + branch + ':' + branch) if igit.workspace_is_clean(): #处理master更新检验,防止创建分支后执行master更新检查操作 igit.set_last_sync_master_date(branch) ok(u'创建成功!已进入分支:' + branch) else: raise exception.FlowException(u'创建分支失败')
def sql(args=None): """ 获取项目下面的所有sql :param args: :return: """ dirs = [] old_proj = iglobal.PROJECT for proj, info in iconfig.read_config('project').items(): if 'ignore_sql_file' in info and info['ignore_sql_file']: continue # 需要先将项目切换到相应的分支 sql_branch = info['branch']['sql_branch'] \ if info.has_key('branch') and 'sql_branch' in info['branch'] \ else iconfig.read_config('system', 'branch')['sql_branch'] # 进入项目 if iglobal.PROJECT != proj: iprint.info(u'进入项目%s' % proj) Extra.cd([proj]) curr_branch = igit.current_branch() if curr_branch != sql_branch: if not igit.workspace_is_clean(): raise exception.FlowException(u'项目的工作空间有尚未保存的修改,请先执行git commit提交或git clean -fd丢弃。处理后请再次执行sql指令') # 切换分支 ihelper.system('git checkout %s' % sql_branch) # 拉取 igit.pull() base_dir = info['dir'] if 'sql_dir' in info: rel_dir = info['sql_dir'] else: rel_dir = iconfig.read_config('system', 'sql_dir') dirs.append((proj, ihelper.real_path(str(base_dir).rstrip('/') + '/' + rel_dir))) if iglobal.PROJECT != old_proj: Extra.cd([old_proj]) sql_file_suffixes = [ele.lstrip('.') for ele in str(iconfig.read_config('system', 'sql_file_suffix')).split('|')] if not dirs: return files = [] for proj, sql_path in dirs: if not sql_path or not os.path.exists(sql_path): continue # 获取文件夹下所有的sql文件 for f in os.listdir(sql_path): f = sql_path + f if not os.path.isfile(f): continue if f.split('.')[-1] in sql_file_suffixes: files.append(f) if not files: iprint.info(u'本次迭代没有sql需要执行') return # 排序 def __isort(x, y): """ :type x: str :type y: str :return: """ sp_date = isprint.get_date_from_sprint(iglobal.SPRINT).split('-') year = sp_date[0] month = sp_date[1] x = os.path.basename(x) y = os.path.basename(y) if month == '12' and x.startswith('01'): x = str(int(year) + 1) + x else: x = year + x if month == '12' and y.startswith('01'): y = str(int(year) + 1) + y else: y = year + y if x < y: return -1 return 1 if x > y else 0 files.sort(__isort) print iprint.warn(u'以下sql需要发布:') for f in files: iprint.info(' ' + os.path.basename(f)) print out_file = iglobal.BASE_DIR + '/runtime/' + iglobal.SPRINT + '.sql' if ihelper.confirm(u'是否导出sql?') == 'y': out_handler = open(out_file, 'w') out_handler.write('set names utf8;\n') for f_name in files: f_handler = open(f_name, 'r') out_handler.write('\n\n-- ----------------------------------- %s -----------------------------------\n' % os.path.basename(f_name)) out_handler.write(f_handler.read()) f_handler.close() out_handler.close() print iprint.ok(u'已写入到%s中' % out_file) else: return
def publish_to_master(self, branches=None): """ 发布分支到生产环境 :param branches: 待发布分支列表:[(proj_name, branch)] """ curr_p_branch = None tag_list = [] if not branches: #接着上次的继续发布 branches = ihelper.read_runtime('publish_branches') tag_list = ihelper.read_runtime('publish_tags') or [] if not branches: info(u'没有需要发布的分支') return orig_branches = list(branches) try: curr_proj = None for index, item in enumerate(branches): curr_p_branch = item proj, branch = tuple(item) if iglobal.PROJECT != proj: info(u'进入项目%s' % proj) extra.Extra('cd', [proj]).execute() if not igit.workspace_is_clean(): raise exception.FlowException( u'项目%s工作空间有未提交的更改,请先提交(或丢弃)后执行 %s p --continue 继续' % (proj, self.cmd)) # 首次进入项目执行fetch获取本地和远程分支差异 if curr_proj != proj: info('fetch...') igit.fetch(useCache=False) # 切换到将要合并的分支(如果不存在本地分支则会自动创建) ihelper.execute('git checkout %s' % branch) # 同步当前本地和远程分支(此处可能会出现冲突) igit.sync_branch() # 切换到master分支 ihelper.execute('git checkout %s' % igit.product_branch()) is_last_branch = index >= len( branches) - 1 or proj != branches[index + 1][0] # 合并 info(u'合并%s...' % branch) igit.merge(branch, need_pull=proj != curr_proj, need_push=is_last_branch) info(u'合并完成:%s' % branch) # 完成 orig_branches.remove(curr_p_branch) if proj != curr_proj: curr_proj = proj # 本项目发布完成后的一些操作 if is_last_branch: self.__post_publish(proj, tag_list) ok(u'发布完成!tag:') # 打印tag信息 for (proj_name, tag_name) in tag_list: info(' %s %s' % (proj_name, tag_name)) # 清空tag list tag_list = [] except Exception, e: error(e.message) warn(u'处理完成后执行 %s p --continue 继续。或执行 %s p --abort 结束' % (self.cmd, self.cmd))
def __init__(self, cmd, args): # 检查当前目录是否git仓库 if not igit.dir_is_repository(): raise exception.FlowException(u'当前目录不是有效的Git仓库') Command.__init__(self, cmd, args)