拼接宿主机的仓库目录
{deploy_from}/{env}/{project}
public static getDeployFromDir ( ) : string | ||
Résultat | string |
/** * 初始化宿主机部署工作空间 * * @return bool */ public function initLocalWorkspace($version) { // svn if ($this->config->repo_type == Project::REPO_SVN) { $cmd[] = 'mkdir -p ' . Project::getDeployWorkspace($version); $cmd[] = sprintf('mkdir -p %s-svn', rtrim(Project::getDeployWorkspace($version), '/')); } else { $cmd[] = sprintf('cp -rf %s %s ', Project::getDeployFromDir(), Project::getDeployWorkspace($version)); } $command = join(' && ', $cmd); return $this->runLocalCommand($command); }
/** * 初始化宿主机部署工作空间 * * @param TaskModel $task * @return bool|int */ public function initLocalWorkspace(TaskModel $task) { $version = $task->link_id; $branch = $task->branch; if ($this->config->repo_type == Project::REPO_SVN) { // svn cp 过来指定分支的目录, 然后 svn up 到指定版本 $cmd[] = sprintf('cp -rf %s %s ', Project::getSvnDeployBranchFromDir($branch), Project::getDeployWorkspace($version)); } else { // git cp 仓库, 然后 checkout 切换分支, up 到指定版本 $cmd[] = sprintf('cp -rf %s %s ', Project::getDeployFromDir(), Project::getDeployWorkspace($version)); } $command = join(' && ', $cmd); return $this->runLocalCommand($command); }
/** * 提交任务 * * @param $projectId * @return string */ public function actionSubmit($projectId = null) { $task = new Task(); if ($projectId) { // svn下无trunk $nonTrunk = false; $conf = Project::find()->where(['id' => $projectId, 'status' => Project::STATUS_VALID])->one(); $conf = Project::getConf($projectId); // 第一次可能会因为更新而耗时,但一般不会,第一次初始化会是在检测里 if ($conf->repo_type == Project::REPO_SVN && !file_exists(Project::getDeployFromDir())) { $version = Repo::getRevision($conf); $version->updateRepo(); } // 为了简化svn无trunk, branches时,不需要做查看分支,直接就是主干 $svnTrunk = sprintf('%s/trunk', Project::getDeployFromDir()); // svn下无trunk目录 if (!file_exists($svnTrunk)) { $nonTrunk = true; } } if (\Yii::$app->request->getIsPost()) { if (!$conf) { throw new \Exception(yii::t('task', 'unknown project')); } $group = Group::find()->where(['user_id' => $this->uid, 'project_id' => $projectId])->count(); if (!$group) { throw new \Exception(yii::t('task', 'you are not the member of project')); } if ($task->load(\Yii::$app->request->post())) { // 是否需要审核 $status = $conf->audit == Project::AUDIT_YES ? Task::STATUS_SUBMIT : Task::STATUS_PASS; $task->user_id = $this->uid; $task->project_id = $projectId; $task->status = $status; if ($task->save()) { return $this->redirect('/task/'); } } } if ($projectId) { $tpl = $conf->repo_type == Project::REPO_GIT ? 'submit-git' : 'submit-svn'; return $this->render($tpl, ['task' => $task, 'conf' => $conf, 'nonTrunk' => $nonTrunk]); } // 成员所属项目 $projects = Project::find()->leftJoin(Group::tableName(), '`group`.project_id=project.id')->where(['project.status' => Project::STATUS_VALID, '`group`.user_id' => $this->uid])->asArray()->all(); return $this->render('select-project', ['projects' => $projects]); }
/** * 获取tag记录 * * @return array */ public function getTagList($count = 20) { // 先更新 $this->updateRepo(); $destination = Project::getDeployFromDir(); $cmd[] = sprintf('cd %s ', $destination); $cmd[] = '/usr/bin/env git tag -l '; $command = join(' && ', $cmd); $result = $this->runLocalCommand($command); if (!$result) { throw new \Exception(\yii::t('walle', 'get tags failed') . $this->getExeLog()); } $history = []; $list = explode(PHP_EOL, $this->getExeLog()); foreach ($list as $item) { $history[] = ['id' => $item, 'message' => $item]; } return $history; }
public static function getBranchDir($branch, $tag = false) { $svnDir = Project::getDeployFromDir(); $branchDir = $branch == 'trunk' && !$tag ? $branch : ($tag ? 'tags/' . $branch : 'branches/' . $branch); return sprintf('%s/%s', $svnDir, $branchDir); }
/** * 获取tag记录 * * @return array */ public function getTagList($count = 20) { // 先更新 $this->updateRepo(); $destination = Project::getDeployFromDir(); $cmd[] = sprintf('cd %s ', $destination); $cmd[] = '/usr/bin/env git tag -l '; $command = join(' && ', $cmd); $result = $this->runLocalCommand($command); $history = []; if (!$result) { return $history; } $list = explode("\n", $this->getExeLog()); foreach ($list as $item) { $history[] = ['id' => $item, 'message' => $item]; } return $history; }
/** * 项目配置检测,提前发现配置不当之处。 * * @return string */ public function actionDetection($projectId) { $project = Project::getConf($projectId); $log = []; $code = 0; // 本地git ssh-key是否加入deploy-keys列表 $revision = Repo::getRevision($project); try { // 1.检测宿主机检出目录是否可读写 $codeBaseDir = Project::getDeployFromDir(); $isWritable = is_dir($codeBaseDir) ? is_writable($codeBaseDir) : @mkdir($codeBaseDir, 0755, true); if (!$isWritable) { $code = -1; $log[] = yii::t('walle', 'hosted server is not writable error', ['user' => getenv("USER"), 'path' => $project->deploy_from]); } // 2.检测宿主机ssh是否加入git信任 $ret = $revision->updateRepo(); if (!$ret) { $code = -1; $error = $project->repo_type == Project::REPO_GIT ? yii::t('walle', 'ssh-key to git', ['user' => getenv("USER")]) : yii::t('walle', 'correct username passwd'); $log[] = yii::t('walle', 'hosted server ssh error', ['error' => $error]); } if ($project->ansible) { $this->ansible = new Ansible($project); // 3.检测 ansible 是否安装 $ret = $this->ansible->test(); if (!$ret) { $code = -1; $log[] = yii::t('walle', 'hosted server ansible error'); } } } catch (\Exception $e) { $code = -1; $log[] = yii::t('walle', 'hosted server sys error', ['error' => $e->getMessage()]); } // 权限与免密码登录检测 $this->walleTask = new WalleTask($project); try { // 4.检测php用户是否加入目标机ssh信任 $command = 'id'; $ret = $this->walleTask->runRemoteTaskCommandPackage([$command]); if (!$ret) { $code = -1; $log[] = yii::t('walle', 'target server ssh error', ['local_user' => getenv("USER"), 'remote_user' => $project->release_user, 'path' => $project->release_to]); } if ($project->ansible) { // 5.检测 ansible 连接目标机是否正常 $ret = $this->ansible->ping(); if (!$ret) { $code = -1; $log[] = yii::t('walle', 'target server ansible ping error'); } } // 6.检测php用户是否具有目标机release目录读写权限 $tmpDir = 'detection' . time(); $command = sprintf('mkdir -p %s', Project::getReleaseVersionDir($tmpDir)); $ret = $this->walleTask->runRemoteTaskCommandPackage([$command]); if (!$ret) { $code = -1; $log[] = yii::t('walle', 'target server is not writable error', ['remote_user' => $project->release_user, 'path' => $project->release_to]); } // 清除 $command = sprintf('rm -rf %s', Project::getReleaseVersionDir($tmpDir)); $this->walleTask->runRemoteTaskCommandPackage([$command]); } catch (\Exception $e) { $code = -1; $log[] = yii::t('walle', 'target server sys error', ['error' => $e->getMessage()]); } // 7.路径必须为绝对路径 $needAbsoluteDir = [Yii::t('conf', 'deploy from') => Project::getConf()->deploy_from, Yii::t('conf', 'webroot') => Project::getConf()->release_to, Yii::t('conf', 'releases') => Project::getConf()->release_library]; foreach ($needAbsoluteDir as $tips => $dir) { if (0 !== strpos($dir, '/')) { $code = -1; $log[] = yii::t('walle', 'config dir must absolute', ['path' => sprintf('%s:%s', $tips, $dir)]); } } // task 检测todo... if ($code === 0) { $log[] = yii::t('walle', 'project configuration works'); } $this->renderJson(join("<br>", $log), $code); }
public function getFiles($path) { $rootPath = Project::getDeployFromDir(); if (!($sourcePath = realpath("{$rootPath}/{$path}"))) { return []; } if (substr($sourcePath, 0, strlen($rootPath)) != $rootPath) { $sourcePath = $rootPath; } $cmd = "find {$sourcePath} -maxdepth 1 ! -path {$sourcePath} ! -path \"*.git*\" ! -path \"*.svn*\""; $excludes = explode("\n", $this->config->excludes); foreach ($excludes as $excludePath) { $excludePath = trim($excludePath); $cmd .= " ! -path \"*{$excludePath}\""; } $result = $this->runLocalCommand($cmd); $output = explode(PHP_EOL, $this->getExeLog()); $files = []; foreach ($output as $line) { $files[$line] = ['file' => $line]; } return $files; }
/** * 获取svn分支目录 * @param $branch * @param Project $project * @return string */ public static function getBranchDir($branch, Project $project) { $svnDir = Project::getDeployFromDir(); if ($project->repo_mode == Project::REPO_MODE_NONTRUNK) { return $svnDir; } elseif ($branch == 'trunk') { return sprintf('%s/trunk', $svnDir); } elseif ($project->repo_mode == Project::REPO_MODE_BRANCH) { return sprintf('%s/branches/%s', $svnDir, $branch); } elseif ($project->repo_mode == Project::REPO_MODE_TAG) { return sprintf('%s/tags/%s', $svnDir, $branch); } else { throw new \InvalidArgumentException('error'); } }
/** * 获取两个版本的差异 * * @ return array */ public function getVersionDiff($old, $new) { $destination = Project::getDeployFromDir(); $cmd[] = sprintf('cd %s ', $destination); $cmd[] = sprintf('/usr/bin/env git reset -q --hard %s', $new); $cmd[] = sprintf('/usr/bin/env git diff %s %s --name-status', $old, $new); $command = implode(' && ', $cmd); $result = $this->runLocalCommand($command); $output = explode(PHP_EOL, $this->getExeLog()); $diff = []; $excludes = GlobalHelper::str2arr($this->config->excludes); array_push($excludes, '*.git*'); array_push($excludes, '.svn*'); array_walk($excludes, function (&$item) { $item = '~^([A-Z])\\s+' . str_replace('\\*', '.*', preg_quote($item)) . '~Ui'; }); foreach ($output as $line) { if (!preg_filter($excludes, 'exclude', $line) && preg_match('~^([A-Z])\\s+(\\S+)$~', $line, $match)) { if ($file = realpath("{$destination}/{$match[2]}")) { $diff[$file] = ['status' => $match[1], 'file' => $file]; } } } return $diff; }