Skip to content

Commit

Permalink
feat: add exclude-patterns to cs fixer shared workflow (#153)
Browse files Browse the repository at this point in the history
  • Loading branch information
bshaffer authored Oct 2, 2024
1 parent 692c58e commit 3c3fb00
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 14 deletions.
42 changes: 36 additions & 6 deletions .github/workflows/code-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ on:
add-rules:
type: string
default: "{}"
exclude-patterns:
type: string
default: ""

permissions:
contents: read
Expand All @@ -42,23 +45,50 @@ jobs:
php_code_standards:
runs-on: ubuntu-latest
name: PHP Code Standards
env:
CONFIG: ${{ inputs.config }}
CONFIG_PATH: ${{ inputs.path }}
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
# install Google Cloud Tools if a config is provided which starts with "GoogleCloudPlatform/php-tools/"
- if: ${{ startsWith(inputs.config, 'GoogleCloudPlatform/php-tools/') }}
name: Install Google Cloud Tools
run: |
BRANCH=${CONFIG#GoogleCloudPlatform/php-tools/}
composer global require google/cloud-tools:dev-${BRANCH#*@} -q
echo "CONFIG=$HOME/.composer/vendor/google/cloud-tools/${BRANCH%@*}" >> $GITHUB_ENV
- name: 'Setup jq'
uses: dcarbone/install-jq-action@v2
- name: Install PHP CS Fixer
run: composer global require friendsofphp/php-cs-fixer:${{ inputs.version }}
- name: Run PHP CS Fixer
run: |
composer global require friendsofphp/php-cs-fixer:${{ inputs.version }} -q
CONFIG="${{ inputs.config }}"
RULES=$(echo $'${{ inputs.rules }} ${{ inputs.add-rules }}'|tr -d '\n\t\r '|jq -s '.[0] * .[1]' -crM)
# set environment variables in script
export RULES=$(echo $'${{ inputs.rules }} ${{ inputs.add-rules }}'|tr -d '\n\t\r '|jq -s '.[0] * .[1]' -crM)
export EXCLUDE_PATTERNS=$(echo $'${{ inputs.exclude-patterns }}'|tr -d '\n\t\r ')
set -x
# use config path only if EXCLUDE_PATTERN is empty
CMD_PATH=$([ "$EXCLUDE_PATTERNS" = "" ] && echo "$CONFIG_PATH" || echo "")
CONFIG_OR_RULES=$([ ! -z "$CONFIG" ] && echo "--config=$CONFIG" || echo --rules=$RULES)
# do not fail if php-cs-fixer fails (so we can print debugging info)
set +e
~/.composer/vendor/bin/php-cs-fixer fix \
${{ inputs.path }} \
$(if [ ! -z "$CONFIG" ]; then echo "--config=$CONFIG"; else echo --rules=$RULES; fi) \
$CMD_PATH \
$CONFIG_OR_RULES \
--dry-run --diff
if [ "$?" -ne 0 ]; then
echo "Run this script locally by executing the following command" \
"from the root of your ${{ github.repository }} repo:"
echo ""
echo " composer global require google/cloud-tools"
echo " ~/.composer/vendor/bin/php-tools cs-fixer ${{ github.repository }} --ref ${{ github.ref_name }}"
echo ""
exit 1
fi
7 changes: 7 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ jobs:
with:
path: src

code-standards-with-config:
uses: ./.github/workflows/code-standards.yml
with:
path: .
config: .php-cs-fixer.default.php
exclude-patterns: '["vendor", "test", "examples", "scripts"]'

static-analysis:
uses: ./.github/workflows/static-analysis.yml

Expand Down
19 changes: 14 additions & 5 deletions .php-cs-fixer.default.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
<?php

require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Yaml\Yaml;

$workflow = Yaml::parse(file_get_contents(__DIR__ . '/.github/workflows/code-standards.yml'));
$rules = json_decode($workflow['on']['workflow_call']['inputs']['rules']['default'], true);
$rulesJson = getenv('RULES');
$configPath = getenv('CONFIG_PATH');
$excludePatternsJson = getenv('EXCLUDE_PATTERNS');

if (!$rulesJson) {
// Use default rules
$workflow = Yaml::parse(file_get_contents(__DIR__ . '/.github/workflows/code-standards.yml'));
$rulesJson = $workflow['on']['workflow_call']['inputs']['rules']['default'];
}

$rules = json_decode($rulesJson, true);
$excludePatterns = json_decode($excludePatternsJson ?: '[]', true);

return (new PhpCsFixer\Config())
->setRules($rules)
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__)
->in($configPath ?: __DIR__)
->notPath($excludePatterns)
)
;
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
"symfony/console": "^5.0",
"symfony/filesystem": "^5.0",
"symfony/process": "^5.0",
"symfony/yaml": "^5",
"twig/twig": "~3.0"
},
"bin": [
"src/Utils/Flex/flex_exec",
"src/Utils/WordPress/wp-gae",
"scripts/dump_credentials.php",
"scripts/install_test_deps.sh"
"scripts/install_test_deps.sh",
"scripts/php-tools"
],
"autoload": {
"psr-4": {
Expand All @@ -43,7 +45,6 @@
"friendsofphp/php-cs-fixer": "^3.62",
"google/cloud-dlp": "^1.10",
"google/cloud-storage": "^1.33",
"google/cloud-secret-manager": "^1.12",
"symfony/yaml": "^5"
"google/cloud-secret-manager": "^1.12"
}
}
32 changes: 32 additions & 0 deletions scripts/php-tools
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env php
<?php
/**
* Copyright 2024 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

if (file_exists($autoload = __DIR__ . '/../vendor/autoload.php')) {
// ran in cloned repo (e.g. "git clone https://github.com/GoogleCloudPlatform/php-tools")
require $autoload;
} elseif (file_exists($autoload = __DIR__ . '/../../../autoload.php')) {
// ran in composer installed repo (e.g. "composer install google/cloud-tools")
require $autoload;
}

use Google\Cloud\Utils\Actions\RunCsFixerCommand;
use Symfony\Component\Console\Application;

$app = new Application();
$app->add(new RunCsFixerCommand());
$app->run();
171 changes: 171 additions & 0 deletions src/Utils/Actions/RunCsFixerCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?php
/**
* Copyright 2024 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Utils\Actions;

use GuzzleHttp\Client;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;

/**
* CLI command for running the CS Fixer shared workflow.
*/
class RunCsFixerCommand extends Command
{
protected function configure()
{
$this
->setName('cs-fixer')
->setDescription('Execute a command with the deployed image')
->addArgument(
'repo',
InputArgument::REQUIRED,
'The name of the repo to run the CS fixer for'
)
->addOption(
'workflow-file',
'',
InputOption::VALUE_REQUIRED,
'name of the github workflow file which contains the configuration',
'lint.yaml'
)
->addOption(
'ref',
'',
InputOption::VALUE_REQUIRED,
'The branch of the repo run the CS fixer for',
'main'
)
->addOption(
'flags',
'',
InputOption::VALUE_REQUIRED,
'The flags to pass down to the CS fixer',
'--dry-run --diff'
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$repo = $input->getArgument('repo');
if (false === strpos($repo, '/')) {
throw new \Exception('Invalid repo name. Use the format: owner/repo');
}

$url = sprintf(
'https://raw.githubusercontent.com/%s/%s/.github/workflows/%s',
$repo,
$input->getOption('ref'),
$input->getOption('workflow-file'),
);

$client = new Client(['http_errors' => false]);
$response = $client->request('GET', $url);
if ($response->getStatusCode() === 404) {
throw new \Exception(sprintf(
'Failed to fetch the workflow file at "%s", maybe it doesn\'t exist? '
. 'Try supplying the "--workflow-file" option.',
$url
));
}
$workflow = Yaml::parse($response->getBody());
$job = null;

foreach ($workflow['jobs'] as $id => $workflowJob) {
if (str_contains($workflowJob['uses'] ?? '', '.github/workflows/code-standards.yml')) {
$output->writeln(sprintf('Found job "%s"', $workflowJob['name'] ?? $id));
$job = $workflowJob;
break;
}
}
if (!$job) {
throw new \Exception('No job found for php-tools/code-standards.yaml in the workflow file');
}

// get the default config
$defaultWorkflow = Yaml::parse(file_get_contents(__DIR__ . '/../../../.github/workflows/code-standards.yml'));
$defaults = [];
foreach ($defaultWorkflow['on']['workflow_call']['inputs'] as $name => $inputOptions) {
$defaults[$name] = $inputOptions['default'] ?? '';
}
$options = array_merge($defaults, $job['with']);

if (str_starts_with($options['config'], 'GoogleCloudPlatform/php-tools/')) {
// use local file
$options['config'] = str_replace(
'GoogleCloudPlatform/php-tools/',
__DIR__ . '/../../../',
$options['config']
);
// strip branch (we'll ignore it in favor of the current branch)
if (false !== $i = strpos($options['config'], '@')) {
$options['config'] = substr($options['config'], 0, $i);
}
if (!file_exists($options['config'])) {
throw new \Exception('config file not found: ' . realpath($options['config']));
}
}

// go through config options and set env vars accordingly
$rules = json_encode(array_merge(
json_decode($options['rules'], true),
json_decode($options['add-rules'], true)
));

$excludePatterns = str_replace(["\n", ' '], '', $options['exclude-patterns']);

// use config path only if EXCLUDE_PATTERN is empty
if ($options['config']) {
// set environment variables so they're available in the CONFIG file
$env = sprintf(
'CONFIG_PATH=%s RULES=$\'%s\' EXCLUDE_PATTERNS=$\'%s\'',
$options['path'],
$rules,
$excludePatterns,
);
// Run command using the --config flag
$cmd = sprintf(
'%s ~/.composer/vendor/bin/php-cs-fixer fix --config=%s %s',
$env,
$options['config'],
$input->getOption('flags')
);
} else {
// Run command using the --rules flag
$cmd = sprintf(
'~/.composer/vendor/bin/php-cs-fixer fix %s --rules=$\'%s\' %s',
$options['path'],
$rules,
$input->getOption('flags')
);
}

$output->writeln('Executing the following command: ');
$output->writeln('');
$output->writeln("<info>\t" . $cmd . '</>');
$output->writeln('');

// @TODO use Symfony process component to run this
passthru($cmd, $resultCode);

return $resultCode;
}
}

0 comments on commit 3c3fb00

Please sign in to comment.