diff --git a/.env.dist b/.env.dist index dc90b58..4353c53 100644 --- a/.env.dist +++ b/.env.dist @@ -3,6 +3,5 @@ TYPO3_API_VERSION=v1 TYPO3_API_TOKEN= TYPO3_API_USERNAME= TYPO3_API_PASSWORD= -TYPO3_EXTENSION_KEY= TYPO3_EXCLUDE_FROM_PACKAGING= TYPO3_DISABLE_DOCS_VERSION_UPDATE= diff --git a/README.md b/README.md index 6c976e4..ce8384b 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,13 @@ TYPO3_API_PASSWORD= Example: ```bash -TYPO3_API_TOKEN="someToken" TYPO3_EXTENSION_KEY="ext_key" bin/tailor ter:details +TYPO3_API_TOKEN="someToken" bin/tailor ter:details ext_key ``` This will display the extension details for extension `ext_key` if `someToken` is valid (not expired/revoked and having at least the -`extension:read` scope assigned). +`extension:read` scope assigned). The extension key can also be read +automatically from your `composer.json` if defined there. ## Installation @@ -91,22 +92,23 @@ option. If set, the raw result will be returned. This can be used for further processing e.g. by using some JSON processor. Most of the commands require an extension key to work with. -There are multiple possibilities to provide an extension key. -These are - in the order in which they are checked: +The extension key is determined in the following order: -- As argument, e.g. `./vendor/bin/tailor ter:details my_key` -- As environment variable, `TYPO3_EXTENSION_KEY=my_key` -- In your `composer.json`, `[extra][typo3/cms][extension-key] = 'my_key'` +1. **CLI argument** (highest priority): e.g. `./vendor/bin/tailor ter:details my_key` +2. **composer.json** (recommended): `[extra][typo3/cms][extension-key] = 'my_key'` +3. **Environment variable** (deprecated, for backwards compatibility only): `TYPO3_EXTENSION_KEY=my_key` -This means, even if you have an extension key defined globally, -either as environment variable or in your `composer.json`, you -can still run all commands for different extensions by adding +> [!WARNING] +> Using the `TYPO3_EXTENSION_KEY` environment variable is deprecated and will +> trigger a deprecation warning. Please migrate to using `composer.json` instead. + +This means, even if you have an extension key defined in your `composer.json`, +you can still run commands for different extensions by providing the desired extension key as argument to the command. > [!NOTE] -> If no extension key is defined, neither as an argument, -> as environment variable, nor in your `composer.json`, commands -> which require an extension key to be set, will throw an exception. +> If no extension key is defined, neither as an argument nor in your +> `composer.json`, commands which require an extension key will throw an exception. ### Manage your personal access token @@ -481,15 +483,17 @@ This can either be done using **Step 3** from above example or by creating a new GitHub release which will also add a new tag. -The workflow furthermore requires the GitHub secrets `TYPO3_EXTENSION_KEY` -and `TYPO3_API_TOKEN` to be set. Add them at "Settings -> Secrets -> New -repository secret". +The workflow requires the GitHub secret `TYPO3_API_TOKEN` to be set. +Add it at "Settings -> Secrets -> New repository secret". -> [!NOTE] -> If your `composer.json` file contains the extension key at -> `[extra][typo3/cms][extension-key] = 'my_key'` (this is good practice anyway), -> the `TYPO3_EXTENSION_KEY` secret and assignment in the below GitHub action -> example is not needed, tailor will pick it up. +> [!IMPORTANT] +> The extension key has to be defined in your `composer.json` at +> `[extra][typo3/cms][extension-key]`. +> +> For backwards compatibility, `TYPO3_EXTENSION_KEY` is still supported but +> deprecated. It should not be used as it prevents a single source of truth +> and causes error messages to be masked in CI logs (e.g. +> `"Could not publish extension ***"`), hiding valuable information. The version is automatically fetched from the tag and validated to match the required pattern. @@ -514,7 +518,6 @@ jobs: if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-20.04 env: - TYPO3_EXTENSION_KEY: ${{ secrets.TYPO3_EXTENSION_KEY }} TYPO3_API_TOKEN: ${{ secrets.TYPO3_API_TOKEN }} steps: - name: Checkout repository @@ -539,7 +542,7 @@ jobs: readonly local comment=$(git tag -l ${{ env.version }} --format '%(contents)) if [[ -z "${comment// }" ]]; then - echo "comment=Released version ${{ env.version }} of ${{ env.TYPO3_EXTENSION_KEY }}" >> $GITHUB_ENV + echo "comment=Released version ${{ env.version }}" >> $GITHUB_ENV else { echo 'comment< [!NOTE] -> If your `composer.json` file contains your extension -> key, you can remove the `TYPO3_EXTENSION_KEY` variable, the -> check and the assignment in the GitLab pipeline, since Tailor -> automatically fetches this key then. +> [!IMPORTANT] +> The extension key has to be defined in your `composer.json` at +> `[extra][typo3/cms][extension-key]`. +> +> For backwards compatibility, `TYPO3_EXTENSION_KEY` is still supported but +> deprecated. It should not be used as it prevents a single source of truth +> and causes error messages to be masked in CI logs (e.g. +> `"Could not publish extension ***"`), hiding valuable information. The variable `CI_COMMIT_TAG` is set by GitLab automatically. @@ -616,14 +621,14 @@ The variable `CI_COMMIT_TAG` is set by GitLab automatically. - composer global require typo3/tailor script: - > - if [ -n "$CI_COMMIT_TAG" ] && [ -n "$TYPO3_API_TOKEN" ] && [ -n "$TYPO3_EXTENSION_KEY" ]; then + if [ -n "$CI_COMMIT_TAG" ] && [ -n "$TYPO3_API_TOKEN" ]; then echo -e "Preparing upload of release ${CI_COMMIT_TAG} to TER\n" # Cleanup before we upload git reset --hard HEAD && git clean -fx - # Upload + # Upload (extension key is read from composer.json) TAG_MESSAGE=`git tag -n10 -l $CI_COMMIT_TAG | sed 's/^[0-9.]*[ ]*//g'` echo "Uploading release ${CI_COMMIT_TAG} to TER" - /tmp/vendor/bin/tailor ter:publish --comment "$TAG_MESSAGE" "$CI_COMMIT_TAG" "$TYPO3_EXTENSION_KEY" + /tmp/vendor/bin/tailor ter:publish --comment "$TAG_MESSAGE" "$CI_COMMIT_TAG" fi; ``` diff --git a/src/Helper/CommandHelper.php b/src/Helper/CommandHelper.php index 5777e49..d3a516b 100644 --- a/src/Helper/CommandHelper.php +++ b/src/Helper/CommandHelper.php @@ -24,21 +24,32 @@ final class CommandHelper { public static function getExtensionKeyFromInput(InputInterface $input): string { + // 1. CLI argument has highest priority if ($input->hasArgument('extensionkey') && ($key = ($input->getArgument('extensionkey') ?? '')) !== '' ) { - $extensionKey = $key; - } elseif (Variables::has('TYPO3_EXTENSION_KEY')) { - $extensionKey = Variables::get('TYPO3_EXTENSION_KEY'); - } elseif (($extensionKeyFromComposer = (new ComposerReader())->getExtensionKey()) !== '') { - $extensionKey = $extensionKeyFromComposer; - } else { - throw new ExtensionKeyMissingException( - 'The extension key must either be set as argument, as environment variable or in the composer.json.', - 1605706548 + return $key; + } + + // 2. composer.json is the recommended source + $extensionKeyFromComposer = (new ComposerReader())->getExtensionKey(); + if ($extensionKeyFromComposer !== '') { + return $extensionKeyFromComposer; + } + + // 3. Environment variable only for backwards compatibility + if (Variables::has('TYPO3_EXTENSION_KEY')) { + trigger_error( + 'Using TYPO3_EXTENSION_KEY environment variable is deprecated. ' + . 'Please set the extension key in composer.json at [extra][typo3/cms][extension-key] instead.', + E_USER_DEPRECATED ); + return Variables::get('TYPO3_EXTENSION_KEY'); } - return $extensionKey; + throw new ExtensionKeyMissingException( + 'The extension key must either be set as argument or in composer.json at [extra][typo3/cms][extension-key].', + 1605706548 + ); } } diff --git a/tests/Unit/Helper/CommandHelperTest.php b/tests/Unit/Helper/CommandHelperTest.php index a446e12..f2cd0c6 100644 --- a/tests/Unit/Helper/CommandHelperTest.php +++ b/tests/Unit/Helper/CommandHelperTest.php @@ -37,13 +37,19 @@ protected function setUp(): void $this->input = new ArrayInput([], $this->definition); } + protected function tearDown(): void + { + // Clean up environment variable after each test + putenv('TYPO3_EXTENSION_KEY'); + } + /** * @test */ public function getExtensionKeyFromInputThrowsExceptionIfInputHasNoArgumentDefined(): void { $this->expectException(ExtensionKeyMissingException::class); - $this->expectExceptionMessage('The extension key must either be set as argument, as environment variable or in the composer.json.'); + $this->expectExceptionMessage('The extension key must either be set as argument or in composer.json at [extra][typo3/cms][extension-key].'); $this->expectExceptionCode(1605706548); CommandHelper::getExtensionKeyFromInput($this->input); @@ -66,7 +72,7 @@ public function getExtensionKeyFromInputReturnsExtensionKeyFromInputArgument(): public function getExtensionKeyFromInputIgnoresEmptyInputArgumentValue(): void { $this->expectException(ExtensionKeyMissingException::class); - $this->expectExceptionMessage('The extension key must either be set as argument, as environment variable or in the composer.json.'); + $this->expectExceptionMessage('The extension key must either be set as argument or in composer.json at [extra][typo3/cms][extension-key].'); $this->expectExceptionCode(1605706548); $this->definition->addArgument(new InputArgument('extensionkey', InputArgument::OPTIONAL)); @@ -78,12 +84,25 @@ public function getExtensionKeyFromInputIgnoresEmptyInputArgumentValue(): void /** * @test */ - public function getExtensionKeyFromInputReturnsExtensionKeyFromEnvironmentVariables(): void + public function getExtensionKeyFromInputReturnsExtensionKeyFromEnvironmentVariablesWithDeprecation(): void { putenv('TYPO3_EXTENSION_KEY=foo'); - self::assertSame('foo', CommandHelper::getExtensionKeyFromInput($this->input)); - - putenv('TYPO3_EXTENSION_KEY'); + $deprecationTriggered = false; + set_error_handler(static function (int $errno, string $errstr) use (&$deprecationTriggered): bool { + if ($errno === E_USER_DEPRECATED) { + $deprecationTriggered = true; + self::assertStringContainsString('TYPO3_EXTENSION_KEY environment variable is deprecated', $errstr); + return true; + } + return false; + }); + + try { + self::assertSame('foo', CommandHelper::getExtensionKeyFromInput($this->input)); + self::assertTrue($deprecationTriggered, 'Expected deprecation warning was not triggered'); + } finally { + restore_error_handler(); + } } }