Skip to content

Commit 43ef99a

Browse files
authored
fix(ci): fix repo check for new components (#9152)
* fix(ci): fix repo check for new components * new packages bypass packagist check * add skipped emoji * revert VERSION file change * Update dev/src/Command/RepoComplianceCommand.php * improve repo execute cmd
1 parent 8875409 commit 43ef99a

5 files changed

Lines changed: 76 additions & 55 deletions

File tree

.github/workflows/release-checks.yaml

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -96,37 +96,6 @@ jobs:
9696
exit 1
9797
fi
9898
99-
# Ensure that if the release PR contains any NEW components, the new component's corresponding
100-
# sub-repo exists
101-
new-component-subrepo-check:
102-
name: New Component Sub-Repo Check
103-
runs-on: ubuntu-latest
104-
if: github.event.pull_request.user.login == 'release-please[bot]'
105-
steps:
106-
- uses: actions/checkout@v6
107-
with:
108-
fetch-depth: 0
109-
- name: "Verify the subrepos exist for new components"
110-
run: |
111-
COMPONENTS=$(git diff origin/main --name-only | grep VERSION | xargs dirname)
112-
FAIL=""
113-
for COMPONENT in ${COMPONENTS}; do {
114-
if [[ "$(cat $COMPONENT/VERSION)" == "0.1.0" ]]; then
115-
SUBREPO=$(cat $COMPONENT/composer.json | jq -r '.extra.component.target')
116-
echo "New component found: $COMPONENT"
117-
if curl --head --silent --fail "https://api.github.com/repos/$SUBREPO" -H 'Authorization: Bearer ${{secrets.SPLIT_TOKEN}}' > /dev/null; then
118-
echo "✅ Target Repo '$SUBREPO' exists"
119-
else
120-
echo "❌ Target Repo '$SUBREPO' DOES NOT EXIST"
121-
FAIL="true"
122-
fi
123-
fi
124-
}; done
125-
if [[ "$FAIL" == "true" ]]; then
126-
echo "Missing repositories for one or more new components"
127-
exit 1
128-
fi
129-
13099
next-release-label-check:
131100
name: Check for "next release" label
132101
uses: GoogleCloudPlatform/php-tools/.github/workflows/release-checks.yml@main

dev/src/Command/ComponentExecuteCommand.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
use Symfony\Component\Console\Input\InputArgument;
2323
use Symfony\Component\Console\Output\OutputInterface;
2424
use Google\Cloud\Dev\Component;
25+
use Google\Cloud\Dev\GitHub;
26+
use Google\Cloud\Dev\Packagist;
27+
use Google\Cloud\Dev\RunShell;
28+
use GuzzleHttp\Client;
29+
use Symfony\Component\Console\Input\InputOption;
2530

2631
/**
2732
* Execute commands for every Component
@@ -37,6 +42,8 @@
3742
*/
3843
class ComponentExecuteCommand extends Command
3944
{
45+
private const PACKAGIST_USERNAME = 'google-cloud';
46+
4047
protected function configure()
4148
{
4249
$this->setName('component:execute')
@@ -52,22 +59,37 @@ protected function configure()
5259
5360
EOF)
5461
->addArgument('code', InputArgument::REQUIRED, 'Path to a file or PHP code to execute')
62+
->addOption('component', 'c', InputOption::VALUE_REQUIRED, 'If specified, display repo info for this component only', '')
63+
->addOption('token', 't', InputOption::VALUE_REQUIRED, 'Github token to use for authentication', '')
64+
->addOption('packagist-token', 'p', InputOption::VALUE_REQUIRED, 'Packagist token for the webhook')
5565
;
5666
}
5767

5868
protected function execute(InputInterface $input, OutputInterface $output): int
5969
{
60-
$code = $input->getArgument('code');
61-
$components = Component::getComponents();
70+
// Create github client wrapper
71+
$http = new Client();
72+
$github = new GitHub(new RunShell(), $http, $input->getOption('token'), $output);
73+
$packagist = new Packagist($http, self::PACKAGIST_USERNAME, $input->getOption('packagist-token') ?? '');
74+
75+
$componentName = $input->getOption('component');
76+
$components = $componentName ? [new Component($componentName)] : Component::getComponents();
6277

63-
$codeToExecute = file_exists($code) ? file_get_contents($code) : $code;
64-
if (0 === strpos($codeToExecute, '<?php')) {
65-
$codeToExecute = substr($codeToExecute, 5);
78+
$code = $input->getArgument('code');
79+
if (file_exists($code)) {
80+
$executeFn = require $code;
81+
} else {
82+
if (0 === strpos($code, '<?php')) {
83+
$code = substr($code, 5);
84+
}
85+
$executeFn = function ($component, $github, $packagist, $output) use ($code) {
86+
eval($code);
87+
};
6688
}
67-
$output->writeln('<info>' . $codeToExecute . '</>');
89+
6890
foreach ($components as $component) {
6991
$output->writeln('Executing for ' . $component->getName());
70-
eval($codeToExecute);
92+
$executeFn($component, $github, $packagist, $output);
7193
}
7294

7395
return 0;

dev/src/Command/RepoComplianceCommand.php

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
6666
}
6767

6868
$table = (new Table($output));
69-
$table->setColumnWidths([55, 20, 20, 25, 50]);
69+
$table->setColumnWidths([55, 20, 22, 33, 50]);
7070
$table->setStyle('compact');
7171
$headers = $format == 'ci' ? ['Name', 'Compliance'] : [
7272
'Name',
@@ -81,38 +81,47 @@ protected function execute(InputInterface $input, OutputInterface $output)
8181
$components = $componentName ? [new Component($componentName)] : Component::getComponents();
8282

8383
$isCompliant = true;
84+
$emoji = fn ($check) => match ($check) { 'skipped' => '', false => '', true => '', null => ''};
8485
foreach ($components as $i => $component) {
86+
$isNewComponent = $component->getPackageVersion() === '0.0.0'
87+
|| $component->getPackageVersion() === '0.1.0' && $format == 'ci';
88+
8589
do {
86-
$details = $this->getRepoDetails($component);
87-
$settingsCheck = $packagistCheck = $webhookCheck = $teamCheck = true;
8890
$refreshDetails = false;
91+
if (!$details = $this->getRepoDetails($component)) {
92+
$isCompliant = $settingsCheck = $packagistCheck = $webhookCheck = $teamCheck = false;
93+
$details = array_fill(0, count($headers) - 1, '**REPO NOT FOUND**');
94+
$details[0] = str_replace('googleapis/', '', $component->getRepoName());
95+
continue;
96+
}
97+
$settingsCheck = $packagistCheck = $webhookCheck = $teamCheck = true;
8998
if (!$this->checkSettingsCompliance($details)) {
9099
$settingsCheck = false;
91100
$refreshDetails |= $this->askFixSettingsCompliance($input, $output, $details);
92101
}
93102
if (!$this->checkWebhookCompliance($details)) {
94-
$webhookCheck = false;
103+
$webhookCheck = $this->github->token ? ($isNewComponent ? 'skipped' : false) : null;
95104
$refreshDetails |= $this->askFixWebhookCompliance($input, $output, $details);
96105
}
97106
if (!$this->checkPackagistCompliance($details)) {
98-
$packagistCheck = false;
99-
$refreshDetails |= $this->askFixPackagistCompliance($input, $output, $component->getRepoName());
107+
// New components don't have packagist config, so bypass for CI.
108+
$packagistCheck = $isNewComponent ? 'skipped' : false;
109+
$refreshDetails |= $this->askFixPackagistCompliance($input, $output, $details);
110+
$details['packagist_config'] ??= '**PACKAGE NOT FOUND**';
100111
}
101112
if (!$this->checkTeamCompliance($details)) {
102113
$teamCheck = $this->github->token ? false : null;
103114
$refreshDetails |= $this->askFixTeamCompliance($input, $output, $component->getRepoName());
104115
}
105116
} while ($refreshDetails);
106117

107-
$emoji = fn (?bool $check) => match ($check) { null => '', true => '', false => ''};
108118
$details['compliant'] = implode("\n", [
109119
sprintf('%s Issues, Projects, Wiki, Pages, and Discussion are disabled', $emoji($settingsCheck)),
110120
sprintf('%s Packagist webhook is configured', $emoji($webhookCheck)),
111121
sprintf('%s Packagist maintainer is "google-cloud"', $emoji($packagistCheck)),
112122
sprintf('%s Github teams permissions are configured correctly', $emoji($teamCheck)),
113123
'',
114124
]);
115-
116125
$isCompliant &= $settingsCheck && $webhookCheck && $packagistCheck && $teamCheck;
117126
if ($format == 'ci') {
118127
unset($details['repo_config'], $details['packagist_config'], $details['teams']);
@@ -169,6 +178,10 @@ private function askFixSettingsCompliance(InputInterface $input, OutputInterface
169178

170179
private function checkWebhookCompliance(array $details): bool
171180
{
181+
if (!$this->github->token) {
182+
return false;
183+
}
184+
172185
$repoName = 'googleapis/' . $details['name'];
173186
$webhookUrl = $this->packagist->getWebhookUrl();
174187

@@ -208,15 +221,15 @@ private function askFixWebhookCompliance(InputInterface $input, OutputInterface
208221
private function checkPackagistCompliance(array $details)
209222
{
210223
return !empty(array_filter(
211-
explode("\n", $details['packagist_config']),
224+
explode("\n", (string) $details['packagist_config']),
212225
fn ($team) => $team === self::PACKAGIST_USERNAME
213226
));
214227
}
215228

216229
private function askFixPackagistCompliance(InputInterface $input, OutputInterface $output, array $details)
217230
{
218-
if (!$this->github->token || $input->getOption('format') == 'ci') {
219-
// without a token, or in CI mode, don't ask to fix compliance
231+
if (!$this->github->token || $input->getOption('format') == 'ci' || $details['packagist_config'] === null) {
232+
// cannot fix compliance without a token, or in CI mode, or without packagist config
220233
return false;
221234
}
222235
throw new \Exception('not implemented');
@@ -239,9 +252,16 @@ private function askFixTeamCompliance(InputInterface $input, OutputInterface $ou
239252
return false;
240253
}
241254

242-
private function getRepoDetails(Component $component): array
255+
private function getRepoDetails(Component $component): array|null
243256
{
244-
$repoDetails = (array) $this->github->getRepoDetails($component->getRepoName());
257+
if (!$repoDetails = $this->github->getRepoDetails($component->getRepoName())) {
258+
return null;
259+
}
260+
261+
if (null !== $packagistDetails = $this->packagist->getMaintainers($component->getPackageName())) {
262+
$packagistDetails = implode("\n", $packagistDetails);
263+
}
264+
245265
// use "array_intersect_key" to filter out fields that were not requested.
246266
$fields = array_map(
247267
fn ($field) => var_export($field, true),
@@ -258,7 +278,7 @@ private function getRepoDetails(Component $component): array
258278
$fields,
259279
array_keys($fields),
260280
)),
261-
'packagist_config' => implode("\n", $this->packagist->getMaintainers($component->getPackageName())),
281+
'packagist_config' => $packagistDetails,
262282
'teams' => $this->getRepoTeamDetails($component),
263283
];
264284
}

dev/src/GitHub.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,9 @@ public function getRepoDetails($target)
274274

275275
return json_decode((string) $res->getBody(), true);
276276
} catch (\Exception $e) {
277-
$this->logException($e);
277+
if ($e->getCode() !== 404) {
278+
$this->logException($e);
279+
}
278280
return null;
279281
}
280282
}

dev/src/Packagist.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,17 @@ public function getDownloads(string $packageName): int
9191
return $data['downloads']['total'] ?? 0;
9292
}
9393

94-
public function getMaintainers(string $packageName): array
94+
public function getMaintainers(string $packageName): array|null
9595
{
96-
$response = $this->client->get("https://packagist.org/packages/$packageName.json");
96+
try {
97+
$response = $this->client->get("https://packagist.org/packages/$packageName.json");
98+
} catch (\Exception $e) {
99+
if ($e->getCode() === 404) {
100+
return null;
101+
}
102+
throw $e;
103+
}
104+
97105
$data = json_decode($response->getBody()->getContents(), true);
98106
return array_map(
99107
fn ($m) => $m['name'],

0 commit comments

Comments
 (0)