PHP 怎么自动化语义发布

wen PHP项目 2

本文目录导读:

PHP 怎么自动化语义发布

  1. 语义化版本(SemVer)基础
  2. 自动化版本管理工具
  3. 实现自动化发布脚本
  4. 提交信息规范化
  5. 集成GitHub Actions
  6. 自动化发布辅助函数
  7. 使用PHP库简化流程
  8. 完整示例配置文件

我来详细介绍PHP自动化语义发布的实现方法:

语义化版本(SemVer)基础

// 语义化版本格式:MAJOR.MINOR.PATCH
// MAJOR: 不兼容的API修改
// MINOR: 向后兼容的功能性新增
// PATCH: 向后兼容的问题修正

自动化版本管理工具

1 使用 semantic-release

// package.json (在PHP项目中使用)
{
  "scripts": {
    "semantic-release": "semantic-release"
  },
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    "@semantic-release/git"
  ]
}

2 PHP专用工具:Conventional Commits

{
    "require-dev": {
        "symplify/monorepo-builder": "^10.0",
        "semver/semver": "^3.0"
    }
}

实现自动化发布脚本

1 基础发布脚本

<?php
// publish.php
class SemanticPublisher
{
    private $versionFile;
    private $changelogFile;
    public function __construct($versionFile = 'VERSION', $changelogFile = 'CHANGELOG.md')
    {
        $this->versionFile = $versionFile;
        $this->changelogFile = $changelogFile;
    }
    public function publish()
    {
        // 获取当前版本
        $currentVersion = $this->getCurrentVersion();
        // 分析提交信息
        $newVersion = $this->analyzeCommits($currentVersion);
        // 更新版本号
        $this->updateVersion($newVersion);
        // 生成变更日志
        $this->generateChangelog($newVersion);
        // 创建Git标签
        $this->createGitTag($newVersion);
        echo "Published version: {$newVersion}\n";
    }
    private function getCurrentVersion()
    {
        if (file_exists($this->versionFile)) {
            return trim(file_get_contents($this->versionFile));
        }
        return '0.0.0';
    }
    private function analyzeCommits($currentVersion)
    {
        // 获取最新提交
        $commits = $this->getGitLog();
        $major = $minor = $patch = 0;
        foreach ($commits as $commit) {
            if (preg_match('/^BREAKING CHANGE:/', $commit)) {
                $major++;
            } elseif (preg_match('/^feat\(/', $commit)) {
                $minor++;
            } elseif (preg_match('/^fix\(/', $commit)) {
                $patch++;
            }
        }
        list($curMajor, $curMinor, $curPatch) = explode('.', $currentVersion);
        return ($curMajor + $major) . '.' . 
               ($curMinor + $minor) . '.' . 
               ($curPatch + $patch);
    }
    private function getGitLog($from = null, $to = 'HEAD')
    {
        $cmd = "git log --oneline";
        if ($from) {
            $cmd .= " $from..$to";
        }
        return explode("\n", shell_exec($cmd));
    }
    private function updateVersion($newVersion)
    {
        file_put_contents($this->versionFile, $newVersion);
        // 更新composer.json版本
        $composer = json_decode(file_get_contents('composer.json'), true);
        $composer['version'] = $newVersion;
        file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT));
    }
    private function generateChangelog($newVersion)
    {
        $changelog = "# Changelog\n\n";
        $changelog .= "## {$newVersion} ({date('Y-m-d')})\n\n";
        $commits = $this->getGitLog($this->getLastTag());
        foreach ($commits as $commit) {
            $changelog .= "- {$commit}\n";
        }
        file_put_contents($this->changelogFile, $changelog);
    }
    private function createGitTag($version)
    {
        $commands = [
            "git add {$this->versionFile} {$this->changelogFile} composer.json",
            "git commit -m \"chore: release {$version}\"",
            "git tag v{$version}",
            "git push origin main --tags"
        ];
        foreach ($commands as $cmd) {
            shell_exec($cmd);
        }
    }
    private function getLastTag()
    {
        return trim(shell_exec('git describe --tags --abbrev=0'));
    }
}
// 执行发布
$publisher = new SemanticPublisher();
$publisher->publish();

2 使用Composer脚本自动化

// composer.json
{
    "scripts": {
        "pre-release": "php scripts/pre-release.php",
        "post-release": "php scripts/post-release.php",
        "release": [
            "@pre-release",
            "php scripts/publish.php",
            "@post-release"
        ]
    }
}

提交信息规范化

1 创建Git提交钩子

<?php
// .git/hooks/commit-msg
class CommitValidator
{
    public function validate($message)
    {
        $patterns = [
            'feat' => '/^feat(\(.+\))?: /',
            'fix' => '/^fix(\(.+\))?: /',
            'docs' => '/^docs(\(.+\))?: /',
            'style' => '/^style(\(.+\))?: /',
            'refactor' => '/^refactor(\(.+\))?: /',
            'test' => '/^test(\(.+\))?: /',
            'chore' => '/^chore(\(.+\))?: /',
            'BREAKING CHANGE' => '/^BREAKING CHANGE: /'
        ];
        foreach ($patterns as $type => $pattern) {
            if (preg_match($pattern, $message)) {
                return true;
            }
        }
        return false;
    }
}
// 检查提交信息
$message = file_get_contents($argv[1]);
$validator = new CommitValidator();
if (!$validator->validate($message)) {
    echo "Error: Commit message must follow conventional format\n";
    echo "Example: feat(api): add new endpoint\n";
    exit(1);
}

集成GitHub Actions

# .github/workflows/release.yml
name: Release
on:
  push:
    branches: [ main ]
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.0'
      - name: Install dependencies
        run: composer install
      - name: Run Semantic Release
        run: |
          composer config version 
          php scripts/semantic-release.php
      - name: Create GitHub Release
        uses: softprops/action-gh-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ env.RELEASE_TAG }}
          body_path: CHANGELOG.md

自动化发布辅助函数

<?php
// SemanticHelper.php
class SemanticHelper
{
    // 分析提交类型
    public function analyzeCommitType($commitMessage)
    {
        if (preg_match('/breaking change/i', $commitMessage)) {
            return 'major';
        }
        if (preg_match('/^feat/i', $commitMessage)) {
            return 'minor';
        }
        if (preg_match('/^fix|^bug|^hotfix/i', $commitMessage)) {
            return 'patch';
        }
        return null;
    }
    // 生成变更日志
    public function generateChangelog($commits, $version, $date)
    {
        $changelog = "\n## $version ($date)\n\n";
        $categories = [
            'Features' => 'feat',
            'Bug Fixes' => 'fix',
            'Documents' => 'docs',
            'Refactoring' => 'refactor',
            'Tests' => 'test',
            'Style' => 'style',
            'Chores' => 'chore'
        ];
        foreach ($categories as $title => $prefix) {
            $items = array_filter($commits, function($commit) use ($prefix) {
                return strpos($commit, $prefix . ':') === 0 || 
                       strpos($commit, $prefix . '(') === 0;
            });
            if (!empty($items)) {
                $changelog .= "### $title\n";
                foreach ($items as $item) {
                    $changelog .= "- $item\n";
                }
                $changelog .= "\n";
            }
        }
        return $changelog;
    }
    // 更新版本配置文件
    public function updateVersionConfig($filePath, $newVersion)
    {
        $config = json_decode(file_get_contents($filePath), true);
        $config['version'] = $newVersion;
        file_put_contents($filePath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
    }
    // 推送通知
    public function sendReleaseNotification($version, $changelog)
    {
        // 可以集成Slack、Telegram、邮件等通知
        $message = "🚀 New Release v$version\n\n" . substr($changelog, 0, 500);
        // 发送到Slack
        if (getenv('SLACK_WEBHOOK')) {
            file_get_contents(getenv('SLACK_WEBHOOK'), false, stream_context_create([
                'http' => [
                    'method' => 'POST',
                    'header' => 'Content-Type: application/json',
                    'content' => json_encode(['text' => $message])
                ]
            ]));
        }
    }
}

使用PHP库简化流程

<?php
// 使用semver库
require 'vendor/autoload.php';
use SemVer\SemVer;
class AutomatedReleaser
{
    public function determineNextVersion($currentVersion, $commits)
    {
        $semver = new SemVer($currentVersion);
        $type = 'patch'; // 默认patch
        foreach ($commits as $commit) {
            if (preg_match('/breaking/i', $commit)) {
                $type = 'major';
                break;
            } elseif (preg_match('/^feat/i', $commit)) {
                $type = 'minor';
            }
        }
        switch ($type) {
            case 'major':
                return $semver->major()->value;
            case 'minor':
                return $semver->minor()->value;
            default:
                return $semver->patch()->value;
        }
    }
}

完整示例配置文件

// semantic-release.php
<?php
return [
    'project' => 'my-php-app',
    'version_file' => 'VERSION',
    'changelog_file' => 'CHANGELOG.md',
    'git' => [
        'branch' => 'main',
        'commit_prefix' => 'chore: release'
    ],
    'releases' => [
        'auto_version' => true,
        'generate_changelog' => true,
        'create_git_tag' => true,
        'push_to_remote' => true
    ],
    'notifications' => [
        'slack_webhook' => env('SLACK_WEBHOOK'),
        'telegram_bot' => env('TELEGRAM_BOT'),
        'email' => env('RELEASE_EMAIL')
    ],
    'plugins' => [
        'post_release' => [
            'php scripts/deploy.php',
            'composer dump-autoload --optimize'
        ]
    ]
];

通过这些工具和方法,你可以实现自动化的语义化发布流程,核心要点包括:规范化的提交信息、自动版本计算、变更日志生成、Git标签和发布管理。

抱歉,评论功能暂时关闭!