From cf5cd90b4c0c92560f92a2587cf7fccc5757d4dc Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Wed, 6 Jul 2022 12:19:55 +0530 Subject: [PATCH] Improve support for composer authenticating private respositories --- README.md | 50 +++++++++++++++++++++++++++++---- __tests__/tools.test.ts | 10 +++++++ dist/index.js | 8 ++++-- src/scripts/tools/add_tools.ps1 | 33 ++++++++++++++++++---- src/scripts/tools/add_tools.sh | 23 +++++++++++++-- src/tools.ts | 8 ++++-- 6 files changed, 115 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7dad0e51..26d0e589 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,9 @@ Setup PHP with required extensions, php.ini configuration, code-coverage support - [JIT Configuration](#jit-configuration) - [Cache Extensions](#cache-extensions) - [Cache Composer Dependencies](#cache-composer-dependencies) - - [Composer GitHub OAuth](#composer-github-oauth) + - [GitHub Composer Authentication](#github-composer-authentication) + - [Private Packagist Authentication](#private-packagist-authentication) + - [Manual Composer Authentication](#manual-composer-authentication) - [Inline PHP Scripts](#inline-php-scripts) - [Problem Matchers](#problem-matchers) - [Examples](#examples) @@ -252,7 +254,7 @@ These tools can be set up globally using the `tools` input. It accepts a string When you specify just the major version or the version in `major.minor` format, the latest patch version matching the input will be setup. - Except for major versions of `composer`, For other tools when you specify only the `major` version or the version in `major.minor` format for any tool you can get rate limited by GitHub's API. To avoid this, it is recommended to provide a [`GitHub` OAuth token](https://github.com/shivammathur/setup-php#composer-github-oauth "Composer GitHub OAuth"). You can do that by setting `COMPOSER_TOKEN` environment variable. + Except for major versions of `composer`, For other tools when you specify only the `major` version or the version in `major.minor` format for any tool you can get rate limited by GitHub's API. To avoid this, it is recommended to provide a [`GitHub` OAuth token](https://github.com/shivammathur/setup-php#composer-github-oauth "Composer GitHub OAuth"). You can do that by setting `GITHUB_TOKEN` environment variable. The `COMPOSER_TOKEN` environment variable has been deprecated in favor of `GITHUB_TOKEN` and will be removed in a future release. ```yaml - name: Setup PHP with tools @@ -261,7 +263,7 @@ These tools can be set up globally using the `tools` input. It accepts a string php-version: '8.1' tools: php-cs-fixer:3.5, phpunit:9.5 env: - COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` - The latest stable version of `composer` is set up by default. You can set up the required `composer` version by specifying the major version `v1` or `v2`, or the version in `major.minor` or `semver` format. Additionally for composer `snapshot` and `preview` can also be specified to set up the respective releases. @@ -736,9 +738,10 @@ key: ${{ runner.os }}-composer-${{ matrix.prefer }}-${{ hashFiles('**/composer.l restore-keys: ${{ runner.os }}-composer-${{ matrix.prefer }}- ``` -### Composer GitHub OAuth +### GitHub Composer Authentication -If you have a number of workflows which set up multiple tools or have many composer dependencies, you might hit the GitHub's rate limit for composer. Also, if you specify only the major version or the version in `major.minor` format, you can hit the rate limit. To avoid this you can specify an `OAuth` token by setting `COMPOSER_TOKEN` environment variable. You can use [`GITHUB_TOKEN`](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token "GITHUB_TOKEN documentation") secret for this purpose. +If you have a number of workflows which set up multiple tools or have many composer dependencies, you might hit the GitHub's rate limit for composer. Also, if you specify only the major version or the version in `major.minor` format, you can hit the rate limit. To avoid this you can specify an `OAuth` token by setting `GITHUB_TOKEN` environment variable. You can use [`GITHUB_TOKEN`](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token "GITHUB_TOKEN documentation") secret for this purpose. +The `COMPOSER_TOKEN` key has been deprecated in favor of `GITHUB_TOKEN` and will be removed in the next major version. ```yaml - name: Setup PHP @@ -746,7 +749,42 @@ If you have a number of workflows which set up multiple tools or have many compo with: php-version: '8.1' env: - COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +### Private Packagist Authentication + +If you use Private Packagist for your private composer dependencies, you can set the `PACKAGIST_TOKEN` environment variable to authenticate. + +```yaml +- name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + env: + PACKAGIST_TOKEN: ${{ secrets.PACKAGIST_TOKEN }} +``` + +### Manual Composer Authentication + +In addition to GitHub or Private Packagist, if you want to authenticate private repositories hosted elsewhere, you can set the `COMPOSER_AUTH_JSON` environment variable with the authentication methods and the credentials in json format. +Please refer to the authentication section in [`composer documentation`](https://getcomposer.org/doc/articles/authentication-for-private-packages.md "composer documentation") for more details. + +```yaml +- name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + env: + COMPOSER_AUTH_JSON: | + { + "http-basic": { + "example.org": { + "username": "${{ secrets.EXAMPLE_ORG_USERNAME }}", + "password": "${{ secrets.EXAMPLE_ORG_PASSWORD }}" + } + } + } ``` ### Inline PHP Scripts diff --git a/__tests__/tools.test.ts b/__tests__/tools.test.ts index 374f777a..98807ac0 100644 --- a/__tests__/tools.test.ts +++ b/__tests__/tools.test.ts @@ -527,4 +527,14 @@ describe('Tools tests', () => { process.env['COMPOSER_TOKEN'] = token; expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script); }); + + it.each` + tools_csv | token | script + ${'cs2pr:1.2'} | ${'invalid_token'} | ${'add_log "$cross" "cs2pr" "Invalid token"'} + ${'phpunit:1.2'} | ${'invalid_token'} | ${'add_log "$cross" "phpunit" "Invalid token"'} + ${'phpunit:0.1'} | ${'no_data'} | ${'add_log "$cross" "phpunit" "No version found with prefix 0.1."'} + `('checking error: $tools_csv', async ({tools_csv, token, script}) => { + process.env['GITHUB_TOKEN'] = token; + expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script); + }); }); diff --git a/dist/index.js b/dist/index.js index d142be1d..aeba2ec2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -622,8 +622,12 @@ const utils = __importStar(__nccwpck_require__(918)); async function getSemverVersion(data) { const search = data['version_prefix'] + data['version']; const url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${search}.`; - const token = await utils.readEnv('COMPOSER_TOKEN'); - const response = await fetch.fetch(url, token); + let github_token = await utils.readEnv('GITHUB_TOKEN'); + const composer_token = await utils.readEnv('COMPOSER_TOKEN'); + if (composer_token && !github_token) { + github_token = composer_token; + } + const response = await fetch.fetch(url, github_token); if (response.error || response.data === '[]') { data['error'] = response.error ?? `No version found with prefix ${search}.`; return data['version']; diff --git a/src/scripts/tools/add_tools.ps1 b/src/scripts/tools/add_tools.ps1 index 1daa1dad..1d6e77bf 100644 --- a/src/scripts/tools/add_tools.ps1 +++ b/src/scripts/tools/add_tools.ps1 @@ -1,7 +1,8 @@ # Variables -$composer_bin = "$env:APPDATA\Composer\vendor\bin" -$composer_json = "$env:APPDATA\Composer\composer.json" -$composer_lock = "$env:APPDATA\Composer\composer.lock" +$composer_home = "$env:APPDATA\Composer" +$composer_bin = "$composer_home\vendor\bin" +$composer_json = "$composer_home\composer.json" +$composer_lock = "$composer_home\composer.lock" # Function to configure composer. Function Edit-ComposerConfig() { @@ -24,8 +25,30 @@ Function Edit-ComposerConfig() { } Add-EnvPATH $src\configs\composer.env Add-Path $composer_bin - if (Test-Path env:COMPOSER_TOKEN) { - Add-Env COMPOSER_AUTH ('{"github-oauth": {"github.com": "' + $env:COMPOSER_TOKEN + '"}}') + Set-ComposerAuth +} + +# Function to setup authentication in composer. +Function Set-ComposerAuth() { + if(Test-Path env:COMPOSER_AUTH_JSON) { + if(Test-Json -JSON $env:COMPOSER_AUTH_JSON) { + Set-Content -Path $composer_home\auth.json -Value $env:COMPOSER_AUTH_JSON + } else { + Add-Log "$cross" "composer" "Could not parse COMPOSER_AUTH_JSON as valid JSON" + } + } + $composer_auth = @() + if(Test-Path env:PACKAGIST_TOKEN) { + $composer_auth += '"http-basic": {"repo.packagist.com": { "username": "token", "password": "' + $env:PACKAGIST_TOKEN + '"}}' + } + if(-not(Test-Path env:GITHUB_TOKEN) -and (Test-Path env:COMPOSER_TOKEN)) { + $env:GITHUB_TOKEN = $env:COMPOSER_TOKEN + } + if (Test-Path env:GITHUB_TOKEN) { + $composer_auth += '"github-oauth": {"github.com": "' + $env:GITHUB_TOKEN + '"}' + } + if($composer_auth.length) { + Add-Env COMPOSER_AUTH ('{' + ($composer_auth -join ',') + '}') } } diff --git a/src/scripts/tools/add_tools.sh b/src/scripts/tools/add_tools.sh index fd1d1a14..4e0af8c1 100644 --- a/src/scripts/tools/add_tools.sh +++ b/src/scripts/tools/add_tools.sh @@ -44,8 +44,27 @@ configure_composer() { fi add_env_path "${src:?}"/configs/composer.env add_path "$composer_bin" - if [ -n "$COMPOSER_TOKEN" ]; then - add_env COMPOSER_AUTH '{"github-oauth": {"github.com": "'"$COMPOSER_TOKEN"'"}}' + set_composer_auth +} + +# Function to setup authentication in composer. +set_composer_auth() { + if [ -n "$COMPOSER_AUTH_JSON" ]; then + if php -r "json_decode('$COMPOSER_AUTH_JSON'); if(json_last_error() !== JSON_ERROR_NONE) { throw new Exception('invalid json'); }"; then + echo "$COMPOSER_AUTH_JSON" | tee "$composer_home/auth.json" >/dev/null + else + add_log "${cross:?}" "composer" "Could not parse COMPOSER_AUTH_JSON as valid JSON" + fi + fi + composer_auth=() + if [ -n "$PACKAGIST_TOKEN" ]; then + composer_auth+=( '"http-basic": {"repo.packagist.com": { "username": "token", "password": "'"$PACKAGIST_TOKEN"'"}}' ) + fi + if [ -n "${GITHUB_TOKEN:-$COMPOSER_TOKEN}" ]; then + composer_auth+=( '"github-oauth": {"github.com": "'"${GITHUB_TOKEN:-$COMPOSER_TOKEN}"'"}' ) + fi + if ((${#composer_auth[@]})); then + add_env COMPOSER_AUTH "{$(IFS=$','; echo "${composer_auth[*]}")}" fi } diff --git a/src/tools.ts b/src/tools.ts index 067363a4..a2b7d7e4 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -21,8 +21,12 @@ interface IRef { export async function getSemverVersion(data: RS): Promise { const search: string = data['version_prefix'] + data['version']; const url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${search}.`; - const token: string = await utils.readEnv('COMPOSER_TOKEN'); - const response: RS = await fetch.fetch(url, token); + let github_token: string = await utils.readEnv('GITHUB_TOKEN'); + const composer_token: string = await utils.readEnv('COMPOSER_TOKEN'); + if (composer_token && !github_token) { + github_token = composer_token; + } + const response: RS = await fetch.fetch(url, github_token); if (response.error || response.data === '[]') { data['error'] = response.error ?? `No version found with prefix ${search}.`; return data['version'];