Composer脚本不能一键发布,必须拆解为Git操作+包管理+人工确认三步;需定义release:check和release:tag等显式脚本,用PHP而非shell实现精准控制与安全校验。

Composer 脚本本身不参与 Git 标签创建、包发布或 Packagist 同步,它只负责执行命令;真正的“版本发布”必须拆解为 Git 操作 + 包管理操作 + 人工确认三部分,脚本只能串起前两者,且极易因权限、网络或环境差异失败。
怎么用 scripts 定义发布前检查和打标签
发布不是 composer publish 一键搞定的事。先得确保代码干净、测试通过、版本号更新、CHANGELOG 同步——这些都得靠脚本驱动。
-
release:check应包含:git status --porcelain(校验工作区干净)、composer test(或vendor/bin/phpunit)、git diff --quiet HEAD -- composer.json(确认 version 字段没被意外改写) -
release:tag推荐用 PHP 脚本而非 shell 内联:读取composer.json中的version字段,生成形如v1.2.3的 tag,并执行git tag -s -m "Release v1.2.3" v1.2.3;注意-s需提前配置 GPG 密钥,否则会卡住 - 别在脚本里自动
git push --tags:这一步应留给人手动触发,避免误推未验证的 tag
为什么不能直接用 post-update-cmd 自动发布
因为 post-update-cmd 在 composer update 后触发,而发布流程根本不需要运行 composer update——它通常发生在本地开发机上,且必须基于已 commit 的代码。把发布逻辑塞进钩子里,会导致:
- CI 流水线每次
composer install都尝试打 tag(实际不该触发) - 本地
composer update意外创建重复 tag - 钩子执行时当前分支未知,可能从
dev分支误发正式版
正确做法是定义显式脚本名(如 release:prepare),并要求团队统一执行 composer run release:prepare 启动流程。
如何安全地同步到 Packagist 或私有源
Composer 不提供 API 调用能力,Packagist 同步依赖 Webhook 或手动提交。脚本能做的只有两件事:
- 生成并校验
composer.json:确保name、version、autoload、require字段合法,用composer validate --no-check-all快速过一遍 - 触发 webhook(仅限私有源):比如 Satis 或 Private Packagist 支持 POST 请求刷新包索引,可用
curl -X POST https://satis.example.com/update,但需提前配置好 token 和权限 - 绝对不要在脚本中硬编码 API key 或密码;改用
getenv('PACKAGIST_TOKEN')读取环境变量
对 Packagist.org,唯一可靠方式仍是登录后手动点击 “Update” 按钮,或配置 GitHub/GitLab Webhook——脚本无法替代这个动作。
PHP 脚本比 shell 更适合做发布逻辑的关键点
一个典型的 scripts/release.php 开头必须包含:
#!/usr/bin/env php
<?php require_once __DIR__ . '/../vendor/autoload.php';
<p>// 1. 显式限定工作目录为项目根
chdir(dirname(<strong>DIR</strong>));<p>// 2. 确保 Git 用户已配置
exec('git config --global user.name', $out, $ret);
if ($ret !== 0) {
fwrite(STDERR, "Git user.name not set\n");
exit(1);
}</p><p>// 3. 解析 composer.json 版本,拒绝 alpha/beta 格式用于正式发布
$pkg = json_decode(file_get_contents('composer.json'), true);
if (preg_match('/^(dev-|\d+.\d+.\d+-[a-z]+)/i', $pkg['version'])) {
fwrite(STDERR, "Refusing to release unstable version: {$pkg['version']}\n");
exit(1);
}</p>
这种控制力是 shell 命令做不到的:你能精确判断语义化版本阶段、捕获 exec 错误码、统一错误退出(exit(1)),而不是让 && 连接符掩盖中间失败。
最常被忽略的是权限边界——发布脚本必须由人以本人 Git 账户身份运行,不能交给 CI 机器人代劳;否则签名无效、协作追溯断裂、安全审计失效。脚本再稳,也跨不过这道人与机器的分界线。











