Skip to content

Commit

Permalink
feat: add owlbot PRs process check for Node (#5315)
Browse files Browse the repository at this point in the history
See:
https://docs.google.com/document/d/1PxkDU5nZihyxDJ9XvjwX7GvDDdJu8qm4TpoxMUsryz0/edit?tab=t.0#heading=h.689e00isjkgi

Adding owlbot process checks for Node:

```
Auto-approve would approve and tag owl-bot PRs if and only if:
The PR author is ‘gcf-owl-bot[bot]’
The title of the PR, and the commits in the PR, do not include breaking, BREAKING or !
The body of the PR contains a ‘PiperOrigin-RevId’
There are no other PRs in the repository opened by gcf-owl-bot[bot]
Checks that the PR does not have any commits from any other authors other than gcf-owl-bot[bot]
```
  • Loading branch information
sofisl committed Apr 29, 2024
1 parent 759896b commit f6d68ce
Show file tree
Hide file tree
Showing 7 changed files with 390 additions and 107 deletions.
8 changes: 8 additions & 0 deletions packages/auto-approve/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ processes:
- "JavaSampleAppDependency"
- "NodeGeneratorDependency"
- "OwlBotTemplateChangesNode"
- "OwlBotPRsNode"
```

These processes represent different workflows for what auto-approve will approve and merge in a given repository. To see their logic in full, see the corresponding file in /src/process-checks.
Expand Down Expand Up @@ -195,6 +196,13 @@ Below is what each process checks for:
- Checks that the body of the PR does not contain 'PiperOrigin-RevId'
- Checks that the PR is the first of owlbot PRs in the repo (so that they are not merged out of order)
- Checks that there are no other commit authors other than owlbot on the repo
* OwlBotPRsNode:
- Checks that the author is 'gcf-owl-bot[bot]'
- Checks that the title of the PR does NOT include BREAKING, !
- Checks that the title of the PR starts with chore, build, test, refactor, feat, fix,
- Checks that the body of the PR DOES contain 'PiperOrigin-RevId'
- Checks that the PR is the first of owlbot PRs in the repo (so that they are not merged out of order)
- Checks that there are no other commit authors other than owlbot on the repo


This change in configuration permits the following:
Expand Down
212 changes: 106 additions & 106 deletions packages/auto-approve/__snapshots__/auto-approve.test.js
Original file line number Diff line number Diff line change
@@ -1,185 +1,185 @@
exports['auto-approve main auto-approve function config exists on main branch approves and tags a PR if a config exists & is valid & PR is valid 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",

Check warning on line 2 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote

Check warning on line 2 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote
"name": "Auto-approve.yml check",

Check warning on line 3 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote

Check warning on line 3 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote
"conclusion": "success",

Check warning on line 4 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote

Check warning on line 4 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote
"output": {

Check warning on line 5 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote
"title": "Auto-approve.yml check",

Check warning on line 6 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote

Check warning on line 6 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote
"summary": "Successful auto-approve.yml config check",

Check warning on line 7 in packages/auto-approve/__snapshots__/auto-approve.test.js

View workflow job for this annotation

GitHub Actions / test (auto-approve)

Strings must use singlequote
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch approves and tags a PR if a config exists & is valid & PR is valid 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve main auto-approve function config exists on main branch still attempts to add an automerge: exact label if there is an approval 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch approves and tags a PR if everything is valid, and it is coming from a fork 1'] = {
'head_sha': '65f14b92a8135948008c6e26344167a2dac9f066',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "65f14b92a8135948008c6e26344167a2dac9f066",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch approves and tags a PR if everything is valid, and it is coming from a fork 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve main auto-approve function config exists on main branch retries if etag is not current 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch retries if etag is not current 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve main auto-approve function config exists on main branch stops retrying to add the label after 3 attempts, even if it is never successful 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch stops retrying to add the label after 3 attempts, even if it is never successful 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve main auto-approve function config exists on main branch submits a failing check if config exists but is not valid 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'failure',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'auto-approve.yml config check failed',
'text': 'See the following errors in your auto-approve.yml config:\n[{"wrongProperty":"wrongProperty","message":"message"}]\n'
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "failure",
"output": {
"title": "Auto-approve.yml check",
"summary": "auto-approve.yml config check failed",
"text": "See the following errors in your auto-approve.yml config:\n[{\"wrongProperty\":\"wrongProperty\",\"message\":\"message\"}]\n"
}
}

exports['auto-approve main auto-approve function config exists on main branch logs to the console if config is valid but PR is not 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch will not check config on master if the config is modified on PR 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'failure',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'auto-approve.yml config check failed',
'text': 'See the following errors in your auto-approve.yml config:\n\n'
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "failure",
"output": {
"title": "Auto-approve.yml check",
"summary": "auto-approve.yml config check failed",
"text": "See the following errors in your auto-approve.yml config:\n\n"
}
}

exports['auto-approve main auto-approve function config exists on main branch uses the correct function to check the PR if the config is V2 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch uses the correct function to check the PR if the config is V2 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve main auto-approve function config exists on main branch uses the correct function to check the PR if the config is V1 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config exists on main branch uses the correct function to check the PR if the config is V1 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve main auto-approve function config does not exist on main branch attempts to create a passing status check if PR contains correct config 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config does not exist on main branch attempts to create a failing status check if PR contains wrong config, and error messages check out 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'failure',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'auto-approve.yml config check failed',
'text': 'See the following errors in your auto-approve.yml config:\n[{"wrongProperty":"wrongProperty","message":"message"}]\n'
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "failure",
"output": {
"title": "Auto-approve.yml check",
"summary": "auto-approve.yml config check failed",
"text": "See the following errors in your auto-approve.yml config:\n[{\"wrongProperty\":\"wrongProperty\",\"message\":\"message\"}]\n"
}
}

exports['auto-approve main auto-approve function config does not exist on main branch passes PR if auto-approve is on main, not PR 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve main auto-approve function config does not exist on main branch passes PR if auto-approve is on main, not PR 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}

exports['auto-approve gets secrets and authenticates separately for approval creates a separate octokit instance and authenticates with secret in secret manager 1'] = {
'head_sha': 'c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a',
'name': 'Auto-approve.yml check',
'conclusion': 'success',
'output': {
'title': 'Auto-approve.yml check',
'summary': 'Successful auto-approve.yml config check',
'text': ''
"head_sha": "c5b0c82f5d58dd4a87e4e3e5f73cd752e552931a",
"name": "Auto-approve.yml check",
"conclusion": "success",
"output": {
"title": "Auto-approve.yml check",
"summary": "Successful auto-approve.yml config check",
"text": ""
}
}

exports['auto-approve gets secrets and authenticates separately for approval creates a separate octokit instance and authenticates with secret in secret manager 2'] = {
'event': 'APPROVE'
"event": "APPROVE"
}
2 changes: 1 addition & 1 deletion packages/auto-approve/__snapshots__/check-config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ exports['check for config whether YAML file has valid schema should fail if ther
`

exports['check for config whether YAML file has valid schema V2 should fail if YAML has any other properties than the ones specified 1'] = `
[{"wrongProperty":{"allowedValues":["UpdateDiscoveryArtifacts","RegenerateReadme","DiscoveryDocUpdate","PythonDependency","PythonSampleDependency","NodeDependency","NodeRelease","JavaApiaryCodegen","JavaDependency","OwlBotTemplateChanges","OwlBotTemplateChangesNode","OwlBotAPIChanges","PHPApiaryCodegen","PythonSampleAppDependency","JavaSampleAppDependency","DockerDependency","GoDependency","GoApiaryCodegen","NodeGeneratorDependency"]},"message":"must be equal to one of the allowed values"}]
[{"wrongProperty":{"allowedValues":["UpdateDiscoveryArtifacts","RegenerateReadme","DiscoveryDocUpdate","PythonDependency","PythonSampleDependency","NodeDependency","NodeRelease","JavaApiaryCodegen","JavaDependency","OwlBotTemplateChanges","OwlBotTemplateChangesNode","OwlBotPRsNode","OwlBotAPIChanges","PHPApiaryCodegen","PythonSampleAppDependency","JavaSampleAppDependency","DockerDependency","GoDependency","GoApiaryCodegen","NodeGeneratorDependency"]},"message":"must be equal to one of the allowed values"}]
`

exports['check for config whether YAML file has valid schema V2 should fail if the property is wrong 1'] = `
Expand Down
5 changes: 5 additions & 0 deletions packages/auto-approve/src/check-pr-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {NodeDependency} from './process-checks/node/dependency';
import {NodeGeneratorDependency} from './process-checks/node/generator-dependency';
import {NodeRelease} from './process-checks/node/release';
import {OwlBotTemplateChangesNode} from './process-checks/node/owlbot-template-changes';
import {OwlBotNode} from './process-checks/node/owlbot';
import {JavaDependency} from './process-checks/java/dependency';
import {OwlBotTemplateChanges} from './process-checks/owl-bot-template-changes';
import {OwlBotAPIChanges} from './process-checks/owl-bot-api-changes';
Expand Down Expand Up @@ -73,6 +74,10 @@ const typeMap = [
configValue: 'OwlBotTemplateChangesNode',
configType: OwlBotTemplateChangesNode,
},
{
configValue: 'OwlBotPRsNode',
configType: OwlBotNode,
},
{
configValue: 'JavaApiaryCodegen',
configType: JavaApiaryCodegen,
Expand Down
53 changes: 53 additions & 0 deletions packages/auto-approve/src/process-checks/node/owlbot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2021 Google LLC
//
// 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
//
// https://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.

import {PullRequest} from '../../interfaces';
import {
checkAuthor,
checkTitleOrBody,
reportIndividualChecks,
getOpenPRsInRepoFromSameAuthor,
} from '../../utils-for-pr-checking';
import {listCommitsOnAPR} from '../../get-pr-info';
import {Octokit} from '@octokit/rest';
import {OwlBotTemplateChangesNode} from './owlbot-template-changes';

/**
* The OwlBotTemplateChanges class's checkPR function returns
* true if the PR:
- has an author that is 'gcf-owl-bot[bot]'
- has a title that does NOT include BREAKING, or !
- has a PR body that DOES contain 'PiperOrigin-RevId'
- is the first owlbot template PR in a repo (so they are merged in order)
- has no other commit authors on the PR
*/
export class OwlBotNode extends OwlBotTemplateChangesNode {
classRule = {
author: 'gcf-owl-bot[bot]',
// For this particular rule, we want to check a pattern and an antipattern;
// we want it to start with regular commit convention,
// and it should not be breaking
titleRegex: /^(chore|build|tests|refactor|fix|feat)/,
titleRegexExclude: /(breaking|!)/,
bodyRegex: /^((?!PiperOrigin-RevId).)*$/,
};

constructor(octokit: Octokit) {
super(octokit);
}

public async checkPR(incomingPR: PullRequest): Promise<boolean> {
return super.checkPR(incomingPR);
}
}

0 comments on commit f6d68ce

Please sign in to comment.