Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* [`pup info`](/docs/commands.md#pup-info)
* [`pup package`](/docs/commands.md#pup-package)
* [`pup replace-tbd`](/docs/commands.md#pup-replace-tbd)
* [`pup replace-version`](/docs/commands.md#pup-replace-version)
* [`pup workflow`](/docs/commands.md#pup-workflow)
* [`pup zip`](/docs/commands.md#pup-zip)
* [`pup zip-name`](/docs/commands.md#pup-zip-name)
Expand Down Expand Up @@ -299,6 +300,28 @@ composer -- pup replace-tbd <version> [--dry-run]
| `--root` | **Optional.** Run the command from a different directory from the current. |


## `pup replace-version`
Replaces the version number in all of your project's [version files](https://github.com/stellarwp/pup/blob/main/docs/configuration.md#paths-versions) with the version you provide.

This command iterates over every entry in your `.puprc` file's [`paths.versions`](/docs/configuration.md#pathsversions) array and rewrites the matched version number using the supplied `version` argument. It is handy when preparing a release or staging a zip, where you want to bump the version in place without running a full `pup package`.
Comment thread
d4mation marked this conversation as resolved.

Unlike `pup package`, this command writes the changes directly to your working files and does **not** restore them afterward. If you want to undo the changes, use your version control system (e.g. `git checkout`).

### Usage
```bash
pup replace-version <version> [--dev]
# or
composer -- pup replace-version <version> [--dev]
```

### Arguments
| Argument | Description |
|-----------|----------------------------------------------------------------------------------------------------------|
| `version` | **Required.** The version number to write into the version files. |
| `--dev` | **Optional.** Append the dev suffix (e.g. `-dev-<timestamp>-<hash>`) to the provided version. |
| `--root` | **Optional.** Run the command from a different directory from the current. |


## `pup workflow`
Run a command workflow.

Expand Down
1 change: 1 addition & 0 deletions src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public function __construct( string $version ) {
$this->add( new Commands\Info() );
$this->add( new Commands\Package() );
$this->add( new Commands\ReplaceTbd() );
$this->add( new Commands\ReplaceVersion() );
$this->add( new Commands\Workflow() );
$this->add( new Commands\Zip() );
$this->add( new Commands\ZipName() );
Expand Down
13 changes: 3 additions & 10 deletions src/Commands/GetVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
use StellarWP\Pup\App;
use StellarWP\Pup\Exceptions\BaseException;
use StellarWP\Pup\Command\Command;
use StellarWP\Pup\Commands\Traits\DevSuffix;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class GetVersion extends Command {
use DevSuffix;

/**
* @inheritDoc
*
Expand Down Expand Up @@ -69,14 +72,4 @@ protected function execute( InputInterface $input, OutputInterface $output ) {
$output->writeln( $version );
return 0;
}

/**
* @return string
*/
protected function getDevSuffix(): string {
$timestamp = exec( 'git show -s --format=%ct HEAD' );
$hash = exec( 'git rev-parse --short=8 HEAD' );

return "-dev-{$timestamp}-{$hash}";
}
}
52 changes: 31 additions & 21 deletions src/Commands/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,12 @@ protected function execute( InputInterface $input, OutputInterface $output ) {
$zip_filename = "{$full_zip_name}.zip";

$output->writeln( '<fg=gray>- Updating version files...</>' );
if ( $version !== 'unknown' ) {
$this->updateVersionsInFiles( $version );
if ( $version !== 'unknown' && ! empty( $config->getVersionFiles() ) ) {
$results = $this->updateVersionsInFiles( $version );
if ( $results !== 0 ) {
$this->undoChanges();
return $results;
}
}
$output->writeln( '<fg=green>✓</> Updating version files...Complete.' );

Expand Down Expand Up @@ -490,31 +494,37 @@ protected function undoChanges() {
}

/**
* @param string $version
* Update the version in the configured version files by delegating to the
* ReplaceVersion command.
*
* @return bool
* The provided version is written as-is (the dev suffix, if any, is already
* baked in by the caller via `get-version`), so `--dev` is intentionally
* not passed through.
*
* @param string $version The version to write into the version files.
*
* @throws \Symfony\Component\Console\Exception\ExceptionInterface
*
* @return int
*/
protected function updateVersionsInFiles( string $version ): bool {
$root = $this->input->getOption( 'root' );
$root = $root ? DirectoryUtils::trailingSlashIt( $root ) : '';
$config = App::getConfig();
$version_files = $config->getVersionFiles();

foreach ( $version_files as $file ) {
$contents = file_get_contents( $root . $file->getPath() );
protected function updateVersionsInFiles( string $version ): int {
$arguments = [
'version' => $version,
];

if ( ! $contents ) {
throw new Exceptions\BaseException( 'Could not read file: ' . $file->getPath() );
}
$root = $this->input->getOption( 'root' );
if ( $root ) {
$arguments['--root'] = $root;
}

$contents = preg_replace( '/' . $file->getRegex() . '/', '${1}' . $version, $contents, 1 );
$results = file_put_contents( $root . $file->getPath(), $contents );
$buffer = new BufferedOutput();
$command = new ReplaceVersion();
$results = $command->run( new ArrayInput( $arguments ), $buffer );

if ( false === $results ) {
return false;
}
if ( $results !== 0 ) {
$this->output->write( $buffer->fetch() );
}

return true;
return $results;
}
}
84 changes: 84 additions & 0 deletions src/Commands/ReplaceVersion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace StellarWP\Pup\Commands;

use StellarWP\Pup\App;
use StellarWP\Pup\Exceptions\BaseException;
use StellarWP\Pup\Command\Command;
use StellarWP\Pup\Commands\Traits\DevSuffix;
use StellarWP\Pup\Utils\Directory as DirectoryUtils;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ReplaceVersion extends Command {
use DevSuffix;

/**
* @inheritDoc
*
* @return void
*/
protected function configure() {
$this->setName( 'replace-version' )
->addArgument( 'version', InputArgument::REQUIRED, 'The version to write into the version files.' )
->addOption( 'dev', null, InputOption::VALUE_NONE, 'Append the dev suffix to the version.' )
->addOption( 'root', null, InputOption::VALUE_REQUIRED, 'Set the root directory for running commands.' )
->setDescription( 'Replaces the version in the files defined in .puprc paths.versions.' )
->setHelp( 'Replaces the version in the files defined in .puprc paths.versions with the provided version.' );
}

/**
* @inheritDoc
*/
protected function execute( InputInterface $input, OutputInterface $output ) {
parent::execute( $input, $output );

$config = App::getConfig();
$version = $input->getArgument( 'version' );
$version_files = $config->getVersionFiles();

if ( empty( $version_files ) ) {
$output->writeln( '<fg=yellow>No version files found in .puprc paths.versions.</>' );
return 1;
}

if ( $input->getOption( 'dev' ) ) {
$version .= $this->getDevSuffix();
}

$root = $input->getOption( 'root' );
$root = $root ? DirectoryUtils::trailingSlashIt( $root ) : '';

foreach ( $version_files as $file ) {
$contents = file_get_contents( $root . $file->getPath() );

if ( ! $contents ) {
throw new BaseException( 'Could not read file: ' . $file->getPath() );
}

$count = 0;
$replaced = preg_replace( '/' . $file->getRegex() . '/', '${1}' . $version, $contents, 1, $count );

if ( null === $replaced ) {
throw new BaseException( 'Could not replace version in file (check the regex): ' . $file->getPath() );
}

if ( $count === 0 ) {
$output->writeln( "<fg=yellow>!</> No version found in <fg=cyan>{$file->getPath()}</> matching its regex. Skipping." );
continue;
}

$results = file_put_contents( $root . $file->getPath(), $replaced );

if ( false === $results ) {
throw new BaseException( 'Could not write to file: ' . $file->getPath() );
}

$output->writeln( "<fg=green>✓</> Updated version in <fg=cyan>{$file->getPath()}</> to <fg=cyan>{$version}</>." );
}

return 0;
}
}
20 changes: 20 additions & 0 deletions src/Commands/Traits/DevSuffix.php
Comment thread
d4mation marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace StellarWP\Pup\Commands\Traits;

/**
* Provides the dev version suffix used by version-related commands.
*/
trait DevSuffix {
/**
* Builds the dev suffix (e.g. -dev-<timestamp>-<hash>) from the current git HEAD.
*
* @return string
*/
protected function getDevSuffix(): string {
$timestamp = exec( 'git show -s --format=%ct HEAD' );
$hash = exec( 'git rev-parse --short=8 HEAD' );

return "-dev-{$timestamp}-{$hash}";
}
}
13 changes: 3 additions & 10 deletions src/Commands/ZipName.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
use StellarWP\Pup\App;
use StellarWP\Pup\Exceptions\BaseException;
use StellarWP\Pup\Command\Command;
use StellarWP\Pup\Commands\Traits\DevSuffix;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;

class ZipName extends Command {
use DevSuffix;

/**
* @inheritDoc
*
Expand Down Expand Up @@ -65,14 +68,4 @@ protected function execute( InputInterface $input, OutputInterface $output ) {

return 0;
}

/**
* @return string
*/
protected function getDevSuffix(): string {
$timestamp = exec( 'git show -s --format=%ct HEAD' );
$hash = exec( 'git rev-parse --short=8 HEAD' );

return "-dev-{$timestamp}-{$hash}";
}
}
93 changes: 93 additions & 0 deletions tests/cli/Commands/ReplaceVersionCest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace StellarWP\Pup\Tests\Cli\Commands;

use StellarWP\Pup\Tests\Cli\AbstractBase;
use StellarWP\Pup\Tests\CliTester;

class ReplaceVersionCest extends AbstractBase {

/**
* The version files modified by the replace-version command. These are git-tracked
* fixtures, so they must be restored after each test.
*
* @var string[]
*/
protected $version_files = [
'bootstrap.php',
'package.json',
'src/Plugin.php',
];

/**
* @inheritDoc
*/
public function _after( CliTester $I ) {
$this->restore_version_files();
parent::_after( $I );
}

/**
* Restores the git-tracked version fixture files modified during a test.
*
* @return void
*/
protected function restore_version_files(): void {
foreach ( $this->version_files as $file ) {
$path = $this->tests_root . '/_data/fake-project/' . $file;
system( 'git checkout -- ' . escapeshellarg( $path ) );
}
}

/**
* @test
*/
public function it_should_replace_the_version_in_version_files( CliTester $I ) {
$this->write_default_puprc();

chdir( $this->tests_root . '/_data/fake-project' );

$I->runShellCommand( "php {$this->pup} replace-version 2.5.0" );
$I->seeResultCodeIs( 0 );
$I->seeInShellOutput( 'bootstrap.php' );
$I->seeInShellOutput( '2.5.0' );

$project = $this->tests_root . '/_data/fake-project';
$I->assertStringContainsString( "define( 'FAKE_PROJECT_VERSION', '2.5.0' );", (string) file_get_contents( $project . '/bootstrap.php' ) );
$I->assertStringContainsString( '"version": "2.5.0"', (string) file_get_contents( $project . '/package.json' ) );
$I->assertStringContainsString( "const VERSION = '2.5.0';", (string) file_get_contents( $project . '/src/Plugin.php' ) );
}

/**
* @test
*/
public function it_should_append_the_dev_suffix_with_the_dev_option( CliTester $I ) {
$this->write_default_puprc();

chdir( $this->tests_root . '/_data/fake-project' );

$I->runShellCommand( "php {$this->pup} replace-version 2.5.0 --dev" );
$I->seeResultCodeIs( 0 );

$project = $this->tests_root . '/_data/fake-project';
$contents = (string) file_get_contents( $project . '/bootstrap.php' );

// The suffix is -dev-<timestamp>-<hash>, so assert the prefix landed in the file.
$I->assertMatchesRegularExpression( "/FAKE_PROJECT_VERSION', '2\.5\.0-dev-\d+-[0-9a-f]+'/", $contents );
}

/**
* @test
*/
public function it_should_fail_when_no_version_files_are_configured( CliTester $I ) {
$puprc = $this->get_puprc();
$puprc['paths']['versions'] = [];
$this->write_puprc( $puprc );

chdir( $this->tests_root . '/_data/fake-project' );

$I->runShellCommand( "php {$this->pup} replace-version 2.5.0", false );
$I->seeResultCodeIs( 1 );
$I->seeInShellOutput( 'No version files found' );
}
}
Loading