setName('admin:version') ->addOption('generate-comment', null, Option::VALUE_NONE, '使用git命令生成说明文件') ->addOption('generate-release', null, Option::VALUE_NONE, '使用git命令生成发布说明文件') ->addOption('release-from-tag', null, Option::VALUE_OPTIONAL, '从指定版本开始生成说明文件') ->addOption('push-tag', null, Option::VALUE_NONE, '使用git命令生成tag并推送') ->setDescription('查看当前ulthon_admin的版本号'); } protected function execute(Input $input, Output $output) { // 指令输出 if (!empty(static::PRODUCT_VERSION)) { $output->info('当前版本号为:' . static::PRODUCT_VERSION); } $output->info('当前ulthon_admin版本号为:' . static::VERSION); $output->info('当前Layui版本号为:' . static::LAYUI_VERSION); $output->info('当前ThinkPHP版本号为:' . ThinkApp::VERSION); $output->writeln('当前的修改说明:'); $output->writeln('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'); foreach (static::COMMENT as $comment) { $output->info($comment); } $output->writeln('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'); $output->highlight('代码托管地址:https://gitee.com/ulthon/ulthon_admin'); $output->highlight('开发文档地址:http://doc.ulthon.com/home/read/ulthon_admin/home.html'); $is_push_tag = $input->hasOption('push-tag'); if ($is_push_tag) { $this->pushTag(); } $is_generate_comment = $input->hasOption('generate-comment'); if ($is_generate_comment) { $this->generateComment(); } $is_generate_release = $input->hasOption('generate-release'); if ($is_generate_release) { $fromTag = $input->getOption('release-from-tag'); $this->generateRelease($fromTag); } } protected function pushTag() { $input = $this->input; $output = $this->output; $output->writeln('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'); $version = static::VERSION; if (!empty(static::PRODUCT_VERSION)) { $version = static::PRODUCT_VERSION; } // 将提交信息写入临时文件 $comment = implode("\n", static::COMMENT); $output->info('生成标签:' . $version); $output->info('标签描述:' . $comment); // 使用项目的临时文件路径生成方法 $tempFile = PathTools::tempBuildPath('git_tag_message_' . uniqid() . '.txt'); file_put_contents($tempFile, $comment); // 使用 -F 参数从文件读取提交信息 exec("git tag -a $version -F \"$tempFile\""); // 删除临时文件 if (file_exists($tempFile)) { unlink($tempFile); } $output->info('推送到远程仓库'); exec('git push --tags'); } /** * 生成版本更新说明 * 读取自上次tag到现在所有的提交说明. * @return string */ public function generateComment() { // 获取最新的tag $lastTag = shell_exec('git describe --tags --abbrev=0'); $lastTag = trim($lastTag); // 获取从最新tag到现在的所有提交 $commits = shell_exec("git log {$lastTag}..HEAD --pretty=format:\"%s\""); if (empty($commits)) { return '暂无更新说明'; } $commits = explode("\n", $commits); // 定义提交类型 $types = [ 'feat' => '新功能', 'fix' => '修复', 'docs' => '文档', 'style' => '样式', 'refactor' => '重构', 'perf' => '性能优化', 'test' => '测试', 'build' => '构建', 'ci' => '持续集成', 'chore' => '其他', 'revert' => '回退', ]; // 按类型分组提交 $groupedCommits = []; foreach ($commits as $commit) { $matched = false; foreach ($types as $type => $desc) { if (preg_match("/^{$type}:/i", $commit)) { $message = preg_replace("/^{$type}:/i", '', $commit); $groupedCommits[$desc][] = trim($message); $matched = true; break; } } // 如果没有匹配到任何类型,归类为"其他" if (!$matched) { $groupedCommits['其他'][] = trim($commit); } } // 生成更新说明 $comment = "版本更新说明:\n"; foreach ($groupedCommits as $type => $messages) { $comment .= "\n【{$type}】\n"; foreach ($messages as $message) { $comment .= "- {$message}\n"; } } $this->output->writeln($comment); // 生成数组格式文件 $comment_arr = explode("\n", $comment); $comment_arr = array_filter($comment_arr, function ($item) { return !empty($item); }); $comment_arr = array_map(function ($item) { return "'" . addslashes(trim($item)) . "',"; }, $comment_arr); $comment_arr_code = implode("\n", $comment_arr); $tmp_comment_file = PathTools::tempBuildPath('commit_comment.php'); file_put_contents($tmp_comment_file, "output->info('已生成版本更新代码:' . $tmp_comment_file); $tmp_comment_markdown_file = PathTools::tempBuildPath('commit_comment.md'); file_put_contents($tmp_comment_markdown_file, $comment); $this->output->info('已生成版本更新Markdown:' . $tmp_comment_markdown_file); } /** * 生成发布说明文件 * 从指定版本开始生成发布说明. * @param string|null $fromTag 起始版本号,如果为空则使用最近的tag * @return void */ public function generateRelease($fromTag = null) { $output = $this->output; // 如果没有指定起始版本,则获取最近的tag if (empty($fromTag)) { $fromTag = shell_exec('git describe --tags --abbrev=0'); $fromTag = trim($fromTag); $output->info('未指定起始版本,使用最近的tag: ' . $fromTag); } else { $output->info('从指定版本开始生成发布说明: ' . $fromTag); } // 获取当前版本 $currentVersion = static::VERSION; if (!empty(static::PRODUCT_VERSION)) { $currentVersion = static::PRODUCT_VERSION; } // 获取从指定tag到现在的所有提交,包含提交哈希和提交日期 $commits = shell_exec("git log {$fromTag}..HEAD --pretty=format:\"%h %ad %s\" --date=short"); if (empty($commits)) { $output->warning('从 ' . $fromTag . ' 到现在没有任何提交'); return; } $commits = explode("\n", $commits); // 定义提交类型 $types = [ 'feat' => '新功能', 'fix' => '修复', 'docs' => '文档', 'style' => '样式', 'refactor' => '重构', 'perf' => '性能优化', 'test' => '测试', 'build' => '构建', 'ci' => '持续集成', 'chore' => '其他', 'revert' => '回退', ]; // 按类型分组提交 $groupedCommits = []; foreach ($commits as $commit) { $matched = false; // 提取提交哈希和日期 preg_match('/^([a-f0-9]+)\s(\d{4}-\d{2}-\d{2})\s(.+)$/', $commit, $matches); if (count($matches) >= 4) { $hash = $matches[1]; $date = $matches[2]; $message = $matches[3]; foreach ($types as $type => $desc) { if (preg_match("/^{$type}:/i", $message)) { $cleanMessage = preg_replace("/^{$type}:/i", '', $message); $groupedCommits[$desc][] = [ 'message' => trim($cleanMessage), 'hash' => $hash, 'date' => $date, ]; $matched = true; break; } } // 如果没有匹配到任何类型,归类为"其他" if (!$matched) { $groupedCommits['其他'][] = [ 'message' => trim($message), 'hash' => $hash, 'date' => $date, ]; } } } // 生成发布说明 $releaseTitle = "# {$currentVersion} 发布说明\n\n"; $releaseDate = '发布日期: ' . date('Y-m-d') . "\n\n"; $releaseContent = "本次发布包含了从 {$fromTag} 到 {$currentVersion} 的所有更新。\n\n"; foreach ($groupedCommits as $type => $messages) { $releaseContent .= "## {$type}\n\n"; foreach ($messages as $item) { $releaseContent .= "- [{$item['date']}] {$item['message']} (#{$item['hash']})\n"; } $releaseContent .= "\n"; } $release = $releaseTitle . $releaseDate . $releaseContent; // 生成发布说明文件 $releaseFile = PathTools::tempBuildPath("release_{$currentVersion}.md"); file_put_contents($releaseFile, $release); $output->info('已生成发布说明文件: ' . $releaseFile); } }