Final Class Yiisoft\YiiDevTool\App\Command\Release\MakeCommand
| Inheritance | Yiisoft\YiiDevTool\App\Command\Release\MakeCommand » Yiisoft\YiiDevTool\App\Component\Console\PackageCommand » Symfony\Component\Console\Command\Command |
|---|---|
| Uses Traits | Yiisoft\YiiDevTool\App\Component\GitHubTokenAware |
Public Methods
| Method | Description | Defined By |
|---|---|---|
| getApplication() | Yiisoft\YiiDevTool\App\Component\Console\PackageCommand | |
| getGitHubToken() | Yiisoft\YiiDevTool\App\Component\GitHubTokenAware |
Protected Methods
Constants
| Constant | Value | Description | Defined By |
|---|---|---|---|
| MAIN_BRANCHES | [ 'master', 'main', ] | Yiisoft\YiiDevTool\App\Command\Release\MakeCommand |
Method Details
Defined in: Yiisoft\YiiDevTool\App\Component\Console\PackageCommand::afterProcessingPackages()
Override this method in a subclass if you want to do something after processing the packages.
For example, link the packages with each other.
| protected void afterProcessingPackages ( \Symfony\Component\Console\Input\InputInterface $input ) | ||
| $input | \Symfony\Component\Console\Input\InputInterface | |
protected function afterProcessingPackages(InputInterface $input): void
{
}
Defined in: Yiisoft\YiiDevTool\App\Component\Console\PackageCommand::areTargetPackagesSpecifiedExplicitly()
| protected boolean areTargetPackagesSpecifiedExplicitly ( ) |
protected function areTargetPackagesSpecifiedExplicitly(): bool
{
if ($this->targetPackagesSpecifiedExplicitly === null) {
throw new RuntimeException('Target packages are not initialized.');
}
return $this->targetPackagesSpecifiedExplicitly;
}
| protected void beforeProcessingPackages ( \Symfony\Component\Console\Input\InputInterface $input ) | ||
| $input | \Symfony\Component\Console\Input\InputInterface | |
protected function beforeProcessingPackages(InputInterface $input): void
{
$io = $this->getIO();
$this->tag = $input->getOption('tag');
if ($io->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) {
$io->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
}
}
| protected boolean checkSSHConnection ( ) |
protected function checkSSHConnection(): bool
{
$process = new Process(['ssh', '-T', 'git@github.com']);
$process
->setTimeout(null)
->run();
if ($process->getExitCode() !== 1) {
$this
->getIO()
->error([
'Checking access to github.com ... DENIED',
'Error: ' . $process->getErrorOutput(),
'Seems like you have not installed SSH key to you Github account.',
'Key is required to work with repository via SSH.',
'See here for instructions: https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account',
]);
return false;
}
return true;
}
| protected mixed configure ( ) |
protected function configure()
{
$this
->setName('release/make')
->setDescription('Make a package release')
->addOption('tag', null, InputArgument::OPTIONAL, description: 'Version to tag');
parent::configure();
}
| protected boolean doesPackageContainErrors ( Yiisoft\YiiDevTool\App\Component\Package\Package $package ) | ||
| $package | Yiisoft\YiiDevTool\App\Component\Package\Package | |
protected function doesPackageContainErrors(Package $package): bool
{
return $this->errorList->has($package);
}
| protected integer execute ( \Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output ) | ||
| $input | \Symfony\Component\Console\Input\InputInterface | |
| $output | \Symfony\Component\Console\Output\OutputInterface | |
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->initPackageList();
$this->initTargetPackages($input);
$io = $this->getIO();
$this->beforeProcessingPackages($input);
$packages = $this->getTargetPackages();
sort($packages);
foreach ($packages as $package) {
if ($this->isCurrentInstallationValid($package)) {
$this->processPackage($package);
}
}
$io->clearPreparedPackageHeader();
$this->afterProcessingPackages($input);
$this->showPackageErrors();
if ($io->nothingHasBeenOutput()) {
$message = $this->getMessageWhenNothingHasBeenOutput();
if ($message !== null) {
$io
->important()
->info($message);
}
}
return Command::SUCCESS;
}
Defined in: Yiisoft\YiiDevTool\App\Component\Console\PackageCommand::getAppRootDir()
Use this method to get a root directory of the tool.
Commands and components can be moved as a result of refactoring, so you should not rely on their location in the file system.
| protected string getAppRootDir ( ) | ||
| return | string |
Path to the root directory of the tool WITH a TRAILING SLASH. |
|---|---|---|
protected function getAppRootDir(): string
{
return rtrim($this
->getApplication()
->getRootDir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
| public Yiisoft\YiiDevTool\App\YiiDevToolApplication getApplication ( ) | ||
| return | Yiisoft\YiiDevTool\App\YiiDevToolApplication | |
|---|---|---|
| protected Yiisoft\YiiDevTool\App\Component\Package\PackageErrorList getErrorsList ( ) |
protected function getErrorsList(): PackageErrorList
{
return $this->errorList;
}
| protected string getExampleCommandPrefix ( ) | ||
| return | string |
Console command prefix that works in current environment. |
|---|---|---|
protected function getExampleCommandPrefix(): string
{
$shell = getenv('SHELL');
$isBash = ($shell && stripos($shell, 'bash')) !== false;
return $isBash ? './' : '';
}
| public string getGitHubToken ( ) |
public function getGitHubToken(): string
{
if ($this->gitHubToken !== null) {
return $this->gitHubToken;
}
$io = $this->getIO();
$tokenFile = $this->getAppRootDir() . 'config/github.token';
if (!file_exists($tokenFile)) {
$io->error([
"There's no $tokenFile.",
'<href=https://github.com/settings/tokens>Please create a GitHub token</> and put it there.',
]);
exit(Command::FAILURE);
}
$token = trim(file_get_contents($tokenFile));
if (empty($token)) {
$io->error([
"$tokenFile exists but is empty.",
'<href=https://github.com/settings/tokens>Please create a GitHub token</> and put it there.',
]);
exit(Command::FAILURE);
}
// Test the token by making an authenticated request
$client = new Client();
$client->authenticate($token, null, AuthMethod::ACCESS_TOKEN);
try {
$client->currentUser()->show();
} catch (\Exception $e) {
$io->error([
"Failed to authenticate with GitHub using the provided token from $tokenFile.",
'<href=https://github.com/settings/tokens>Please make sure the token is valid and has the required permissions</>.',
'Error: ' . $e->getMessage(),
]);
exit(Command::FAILURE);
}
$this->gitHubToken = $token;
return $token;
}
| protected Yiisoft\YiiDevTool\App\Component\Console\OutputManager getIO ( ) |
protected function getIO(): OutputManager
{
if ($this->io === null) {
throw new RuntimeException('IO is not initialized.');
}
return $this->io;
}
| protected string|null getMessageWhenNothingHasBeenOutput ( ) |
protected function getMessageWhenNothingHasBeenOutput(): ?string
{
return '<success>✔ Done</success>';
}
| protected Yiisoft\YiiDevTool\App\Component\Package\PackageList getPackageList ( ) |
protected function getPackageList(): PackageList
{
return $this->packageList;
}
| protected Yiisoft\YiiDevTool\App\Component\Package\Package[] getTargetPackages ( ) |
protected function getTargetPackages(): array
{
if ($this->targetPackages === null) {
throw new RuntimeException('Target packages are not initialized.');
}
return $this->targetPackages;
}
| protected void initPackageList ( ) |
protected function initPackageList(): void
{
$io = $this->getIO();
try {
$ownerPackages = require $this->getAppRootDir() . 'owner-packages.php';
if (!preg_match('/^[a-z0-9][a-z0-9-]*[a-z0-9]$/i', $ownerPackages)) {
$io->error([
'The packages owner can only contain the characters [a-z0-9-], and the character \'-\' cannot appear at the beginning or at the end.',
'See <file>owner-packages.php</file> to set the packages owner.',
]);
exit(1);
}
$packagesRootDir = $this->getApplication()->getConfig('packagesRootDir') ?? $this->getAppRootDir() . 'dev';
$this->packageList = new PackageList(
$ownerPackages,
$this->getAppRootDir() . 'packages.php',
packagesRootDir: $packagesRootDir,
);
$this->errorList = new PackageErrorList();
} catch (InvalidArgumentException $e) {
$io->error([
'Invalid local package configuration <file>packages.local.php</file>',
$e->getMessage(),
'See <file>packages.local.php.example</file> for configuration examples.',
]);
exit(1);
}
}
| protected void initTargetPackages ( \Symfony\Component\Console\Input\InputInterface $input ) | ||
| $input | \Symfony\Component\Console\Input\InputInterface | |
protected function initTargetPackages(InputInterface $input): void
{
if ($this->packageList === null) {
throw new RuntimeException('Package list is not initialized.');
}
$io = $this->getIO();
$commaSeparatedPackageIds = $input->getArgument('packages');
if ($commaSeparatedPackageIds === null) {
$this->targetPackagesSpecifiedExplicitly = false;
$this->targetPackages = $this->packageList->getEnabledPackages();
return;
}
$targetPackageIds = array_unique(explode(',', $commaSeparatedPackageIds));
$problemsFound = false;
$targetPackages = [];
foreach ($targetPackageIds as $targetPackageId) {
$package = $this->packageList->getPackage($targetPackageId);
if ($package === null) {
$io->error("Package <package>$targetPackageId</package> not found in <file>packages.php</file>");
$problemsFound = true;
continue;
}
if ($package->disabled()) {
$io->error("Package <package>$targetPackageId</package> disabled in <file>packages.local.php</file>");
$problemsFound = true;
continue;
}
$targetPackages[] = $package;
}
if ($problemsFound) {
exit(1);
}
$this->targetPackagesSpecifiedExplicitly = true;
$this->targetPackages = $targetPackages;
}
| protected mixed initialize ( \Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output ) | ||
| $input | \Symfony\Component\Console\Input\InputInterface | |
| $output | \Symfony\Component\Console\Output\OutputInterface | |
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->io = new OutputManager(new YiiDevToolStyle($input, $output));
}
| protected void processPackage ( Yiisoft\YiiDevTool\App\Component\Package\Package $package ) | ||
| $package | Yiisoft\YiiDevTool\App\Component\Package\Package | |
protected function processPackage(Package $package): void
{
$io = $this->getIO();
$io->preparePackageHeader($package, 'Releasing {package}');
$git = $package->getGitWorkingCopy();
$gitHubToken = $this->getGitHubToken();
if (!$package->composerConfigFileExists()) {
$io->warning([
"No <file>composer.json</file> in package <package>{$package->getName()}</package>.",
'Release cancelled.',
]);
return;
}
$composerPackage = new ComposerPackage($package->getName(), $package->getPath());
$composerConfig = $composerPackage->getComposerConfig();
$unstableFlags = ['dev', 'alpha', 'beta', 'rc'];
$minimumStability = $composerConfig->getSection(ComposerConfig::SECTION_MINIMUM_STABILITY);
if (in_array($minimumStability, $unstableFlags, true)) {
$io->warning([
"Minimum-stability of package <package>{$package->getName()}</package> is <em>$minimumStability</em>.",
'Release is only possible for stable packages.',
'Releasing skipped.',
]);
return;
}
$dependencyList = $composerConfig->getDependencyList(ComposerConfig::SECTION_REQUIRE);
foreach ($dependencyList->getDependencies() as $dependency) {
if ($dependency->constraintContainsAnyOfStabilityFlags($unstableFlags)) {
$io->warning([
"Constraint of dependency <em>{$dependency->getPackageName()}</em> contains an unstable flag.",
"The constraint is <em>{$dependency->getConstraint()}</em>.",
'Release is only possible for packages with stable dependencies.',
'Releasing skipped.',
]);
return;
}
}
$io->info("Hurray, another release is coming!\n");
$currentVersion = $this->getCurrentVersion($git);
if ($currentVersion->asString() === '') {
$io->info('There is currently no release.');
} else {
$io->info("Current version is $currentVersion.");
}
$versionToRelease = $this->getVersionToRelease($currentVersion);
$io->info("Going to release $versionToRelease.");
if ($git->hasChanges()) {
$changes = $git->getStatus();
if ($this->confirm("You have uncommitted changes:\n" . $changes . "\nDiscard these?")) {
$git->reset(['hard' => true]);
$git->clean('-d', '-f');
} else {
$io->error('Can not continue with uncommitted changes.');
return;
}
}
$currentBranch = $this->getCurrentBranch($git);
if (!in_array($currentBranch, self::MAIN_BRANCHES, true)) {
$mainBranch = $this->getMainBranch($git);
if ($mainBranch === null) {
if (!$this->confirm("You are going to release from \"$currentBranch\" branch. OK?")) {
return;
}
} elseif ($this->confirm("You are going to release from \"$currentBranch\" branch. Want to switch to \"$mainBranch\"?")) {
$git->checkout($mainBranch);
}
}
$io->info('Pulling latest changes.');
$git->pull();
$changelogPath = $package->getPath() . '/CHANGELOG.md';
$changelog = new Changelog($changelogPath);
$io->info("Sorting $changelogPath for $versionToRelease.");
$changelog->resort();
$io->info("Closing $changelogPath for $versionToRelease.");
$changelog->close($versionToRelease);
$io->info("Committing changes for $versionToRelease.");
$git->commit([
'S' => true,
'a' => true,
'm' => "Release version $versionToRelease",
]);
$io->info("Adding a tag for $versionToRelease.");
$git->tag([
's' => (string) $versionToRelease,
'm' => (string) $versionToRelease,
]);
$nextVersion = $versionToRelease->getNext(Version::TYPE_PATCH);
$io->info("Opening $changelogPath for $nextVersion.");
$changelog->open($nextVersion);
$io->info('Committing changes.');
$git->commit([
'a' => true,
'm' => 'Prepare for next release',
]);
if ($this->confirm('Push commits and tag, and release on GitHub?')) {
$git->push();
$git->pushTag((string) $versionToRelease);
$this->releaseOnGithub($gitHubToken, $package, $versionToRelease);
$io->done();
$io->info('The following steps are left to do manually:');
$io->info("- Close the $versionToRelease <href=https://github.com/{$package->getName()}/milestones/>milestone on GitHub</> and open new one for $nextVersion.");
$io->info('- Release news and announcement.');
$this->displayReleaseSummary(
package: $package,
composerConfig: $composerConfig,
changelog: $changelog,
versionToRelease: $versionToRelease
);
}
}
| protected void registerPackageError ( Yiisoft\YiiDevTool\App\Component\Package\Package $package, string $message, string $during ) | ||
| $package | Yiisoft\YiiDevTool\App\Component\Package\Package | |
| $message | string | |
| $during | string | |
protected function registerPackageError(Package $package, string $message, string $during): void
{
$this->errorList->set($package, $message, $during);
}
Signup or Login in order to comment.