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
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ PROJECT_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
# The Dockerfile to build
DOCKERFILE ?= "${_MAKEFILE_DIR}Dockerfile"

# The directory where standalone tooling is installed
TOOLS_DIR := $(PROJECT_DIR)tools
# Whether the tools directory exists
HAS_TOOLS := $(strip $(shell if [ -d $(TOOLS_DIR) ]; then echo TRUE; fi))

# Formatting Macros
MK_BLUE = echo -e "\033[34m"$(1)"\033[0m"
MK_GREEN = echo -e "\033[32m"$(1)"\033[0m"
Expand All @@ -39,6 +44,8 @@ MK_ERROR = @$(call MK_RED, $1)
# Variables for collecting targets
QA_TARGETS :=
CLEAN_TARGETS :=
BUMP_TOOLS_TARGETS :=
UPDATE_TOOLS_TARGETS :=

# The $(HELP) variable collects help text for the `help` target
HELP := ""
Expand Down Expand Up @@ -68,6 +75,11 @@ include $(_MAKEFILE_DIR)makefiles/Mago.mk
include $(_MAKEFILE_DIR)makefiles/PHPCodeSniffer.mk
include $(_MAKEFILE_DIR)makefiles/PHPUnit.mk
include $(_MAKEFILE_DIR)makefiles/Psalm.mk
include $(_MAKEFILE_DIR)makefiles/Rector.mk

# Include the Tools fragment after all other tooling so that bump and update of stand-alone tools collects all necessary targets
include $(_MAKEFILE_DIR)makefiles/Tools.mk

# Run docs checks later during QA runs by moving them to the end of the inclusion list
include $(_MAKEFILE_DIR)makefiles/MarkdownLint.mk
include $(_MAKEFILE_DIR)makefiles/LinkChecker.mk
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ There are a number of `make` targets available for running Mago's suite of tools

- `make test`

### Rector Targets

When rector is installed to `./tools/rector`

- `make rector` (Run rector with --dry-run)
- `make rector-fix`

## Migrations

Migrations represent small units of automation for common or one-off maintenance tasks for Laminas Repos.
Expand All @@ -100,3 +107,13 @@ make migrate-to-php86
```

For more information about this migration [see the README](migrations/PHP-86/README.md).

### Rector Installation

Install rector into `./tools/rector`

```bash
make install-rector
```

For more information about this migration [see the README](migrations/InstallStandaloneRector/README.md).
4 changes: 3 additions & 1 deletion mago.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ extends = "mago/defaults.toml"
[source]
workspace = "."
paths = ["migrations/"]

excludes = [
"migrations/*/templates/rector.php",
]
[analyzer]

ignore = [
Expand Down
35 changes: 35 additions & 0 deletions makefiles/Rector.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
RECTOR_DIRECTORY ?= $(PROJECT_DIR)tools/rector

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is $(PROJECT_DIR) guaranteed to end with a /?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In local testing, $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) yields directories with trailing slashes, so I've adopted that convention for these types of variables, which is where most of them come from.

HAS_RECTOR := $(strip $(shell if [ -d $(RECTOR_DIRECTORY) ]; then echo TRUE; fi))


ifeq ("$(HAS_RECTOR)", "TRUE")

BUMP_TOOLS_TARGETS += rector-bump
UPDATE_TOOLS_TARGETS += rector-update

HELP += $(call MK_HELP,'rector','Check the codebase with Rector')
rector:
@$(DOCKER_RUN) $(DOCKER_IMAGE_NAME) $(RECTOR_DIRECTORY)/vendor/bin/rector process --dry-run -c $(RECTOR_DIRECTORY)/rector.php
.PHONY: rector

HELP += $(call MK_HELP,'rector-fix','Fix code style inconsistencies with Rector')
rector-fix:
@$(DOCKER_RUN) $(DOCKER_IMAGE_NAME) $(RECTOR_DIRECTORY)/vendor/bin/rector process -c $(RECTOR_DIRECTORY)/rector.php
.PHONY: rector-fix

rector-update:
$(DOCKER_RUN) ${DOCKER_IMAGE_NAME} composer \
--working-dir=$(RECTOR_DIRECTORY) \
update
.PHONY: rector-update

rector-bump: rector-update _do-rector-bump rector-update
.PHONY: rector-bump

_do-rector-bump:
$(DOCKER_RUN) ${DOCKER_IMAGE_NAME} composer \
--working-dir=$(RECTOR_DIRECTORY) \
bump -D
.PHONY: _do-rector-bump

endif
20 changes: 20 additions & 0 deletions makefiles/Tools.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# If _any_ tools are installed, and the targets for updating or bumping those tools are non-empty, then
# add targets for bumping or updating _all_ stand-alone tools
#

ifeq ("$(HAS_TOOLS)", "TRUE")

ifneq ($(strip $(UPDATE_TOOLS_TARGETS)), "")
HELP += $(call MK_HELP,'update-tools','Update all standalone tools')
update-tools: $(UPDATE_TOOLS_TARGETS)
.PHONY: update-tools
endif

ifneq ($(strip $(BUMP_TOOLS_TARGETS)), "")
HELP += $(call MK_HELP,'bump-tools','Bump and update the versions for all standalone tools')
bump-tools: $(BUMP_TOOLS_TARGETS)
.PHONY: bump-tools
endif

endif
36 changes: 36 additions & 0 deletions migrations/InstallStandaloneRector/InstallStandaloneRector.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CURRENT_DIRECTORY := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
RECTOR_DIRECTORY := $(PROJECT_DIR)tools/rector

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for Rector.mk file - can we guarantee $(PROJECT_DIR) has a trailing slash?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it seems like the trailing slash is a documented "standard":

https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html#index-dir

HAS_RECTOR_DIR := $(strip $(shell if [ -d $(RECTOR_DIRECTORY) ]; then echo TRUE; fi))

ifneq ("$(HAS_RECTOR_DIR)","TRUE")

PROJECT_COMPOSER_PLATFORM := $(shell $(DOCKER_RUN) ${DOCKER_IMAGE_NAME} composer config platform.php)
HELP += $(call MK_HELP,'install-rector','Install a standalone copy of rector into tools/rector')

install-rector: _do-install-rector $(RECTOR_DIRECTORY)
.PHONY: install-rector

_do-install-rector:
@$(call MK_INFO,"Installing Rector")
@$(DOCKER_RUN) ${DOCKER_IMAGE_NAME} php $(CURRENT_DIRECTORY)migrate $(PROJECT_DIR)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will $(CURRENT_DIRECTORY) always have a trailing /?

.PHONY: _do-install-rector


# Installation routine for rector:
$(RECTOR_DIRECTORY):
mkdir -p $(RECTOR_DIRECTORY)
echo '{}' > $(RECTOR_DIRECTORY)/composer.json
$(DOCKER_RUN) ${DOCKER_IMAGE_NAME} composer \
--working-dir=$(RECTOR_DIRECTORY) \
config platform.php $(PROJECT_COMPOSER_PLATFORM)
$(DOCKER_RUN) ${DOCKER_IMAGE_NAME} composer \
--working-dir=$(RECTOR_DIRECTORY) \
require --dev rector/rector
cp $(CURRENT_DIRECTORY)templates/rector.php $(RECTOR_DIRECTORY)/
echo vendor > $(RECTOR_DIRECTORY)/.gitignore
$(DOCKER_RUN) ${DOCKER_IMAGE_NAME} composer \
--working-dir=$(RECTOR_DIRECTORY) \
install
# end

endif
3 changes: 3 additions & 0 deletions migrations/InstallStandaloneRector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Install Stand-Alone Rector

This migration installs rector to `./tools/rector` in the project and ensures that the tools directory is `export-ignore`d.
24 changes: 24 additions & 0 deletions migrations/InstallStandaloneRector/migrate
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php

declare(strict_types=1);

use Laminas\Internal\Migrations\GitAttributes;
use Laminas\Internal\Migrations\ProjectInformation;

require __DIR__ . '/../autoloader.php';

/** @var mixed $rootDirectory */
$rootDirectory = $argv[1] ?? null;
if (
! is_string($rootDirectory)
|| $rootDirectory === ''
|| ! is_dir($rootDirectory)
) {
throw new RuntimeException('Pass a single argument representing the root directory of the target library');
}

$project = ProjectInformation::fromDirectory($rootDirectory);

$attributes = new GitAttributes($project->gitAttributes);
$attributes->ignore('tools');
14 changes: 14 additions & 0 deletions migrations/InstallStandaloneRector/templates/rector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

require_once 'vendor/autoload.php';
Comment thread
froschdesign marked this conversation as resolved.

use Rector\Config\RectorConfig;

return RectorConfig::configure()
->withPhpSets()
->withPaths([
__DIR__ . '/../../src',
__DIR__ . '/../../test',
]);
1 change: 1 addition & 0 deletions migrations/Migrations.mk
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

include $(_MAKEFILE_DIR)migrations/PHP-86/PHP86.mk
include $(_MAKEFILE_DIR)migrations/InstallStandaloneRector/InstallStandaloneRector.mk
18 changes: 1 addition & 17 deletions migrations/PHP-86/migrate
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,7 @@ use Laminas\Internal\Migrations\Makefile;
use Laminas\Internal\Migrations\PatchComposer;
use Laminas\Internal\Migrations\ProjectInformation;

$paths = [
__DIR__ . '/../../../../autoload.php',
__DIR__ . '/../../vendor/autoload.php',
];

$autoloader = null;
foreach ($paths as $file) {
if (! file_exists($file)) {
continue;
}

$autoloader = require $file;
}

if ($autoloader === null) {
throw new RuntimeException('Cannot determine autoloader');
}
require __DIR__ . '/../autoloader.php';

/** @var mixed $rootDirectory */
$rootDirectory = $argv[1] ?? null;
Expand Down
22 changes: 22 additions & 0 deletions migrations/autoloader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

$paths = [
__DIR__ . '/../../../autoload.php',
__DIR__ . '/../vendor/autoload.php',
];

$autoloader = null;
foreach ($paths as $file) {
if (! file_exists($file)) {
continue;
}

/** @var mixed $autoloader */
$autoloader = require $file;
}

if ($autoloader === null) {
throw new RuntimeException('Cannot determine autoloader');
}
88 changes: 88 additions & 0 deletions migrations/src/GitAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

declare(strict_types=1);

namespace Laminas\Internal\Migrations;

use function assert;
use function explode;
use function file_get_contents;
use function file_put_contents;
use function ltrim;
use function sprintf;
use function str_starts_with;
use function trim;

use const DIRECTORY_SEPARATOR;
use const PHP_EOL;

final readonly class GitAttributes
{
public function __construct(
private string $file,
) {}

/** @return list<string> */
private function getLines(): array
{
return explode(PHP_EOL, $this->read());
}

public function ignore(string $path): void
{
if ($this->isExportIgnored($path)) {
return;
}

$content = sprintf(
"%s\n%s export-ignore\n",
trim($this->read()),
$path,
);

$this->write($content);
}

private function isExportIgnored(string $path): bool
{
foreach ($this->getLines() as $line) {
if (! $this->isExportIgnoreLine($line)) {
continue;
}

$result = str_starts_with(
ltrim(trim($line), DIRECTORY_SEPARATOR),
ltrim(trim($path), DIRECTORY_SEPARATOR),
);

if ($result) {
return true;
}
}

return false;
}

private function isExportIgnoreLine(string $line): bool
{
return (bool) preg_match('/\s+export-ignore\s*$/i', $line);
}

/** @return non-empty-string */
private function read(): string
{
$contents = file_get_contents($this->file);
assert(
$contents !== false && $contents !== '',
'The file could not be read or it is empty',
);

return $contents;
}

/** @param non-empty-string $data */
private function write(string $data): void
{
file_put_contents($this->file, $data);
}
}
2 changes: 2 additions & 0 deletions migrations/src/ProjectInformation.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function __construct(
public string|null $laminasCiWorkflow,
public string|null $magoConfiguration,
public string|null $makefile,
public string $gitAttributes,
) {}

/** @throws Throwable */
Expand All @@ -63,6 +64,7 @@ public static function fromDirectory(string $directory): self
self::findLaminasCIWorkflowInDirectory($directory . '/.github/workflows'),
self::fileExistsOrNull($directory . '/mago.toml'),
self::fileExistsOrNull($directory . '/Makefile'),
self::assertFileExists($directory . '/.gitattributes'),
);
}

Expand Down
Loading
Loading