-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "Revert "feat(scripts/pingcap/community): add script `update-p…
…row-oweners.ts`"" (#2116) * Revert "Revert "feat(scripts/pingcap/community): add script `update-prow-oweners.ts` (#2100)" (#2115)" This reverts commit 878fe45. * Update prow-jobs/pingcap-community-postsubmits.yaml * Update prow-jobs/pingcap-community-postsubmits.yaml
- Loading branch information
Showing
3 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# struct ref: https://pkg.go.dev/k8s.io/test-infra/prow/config#Presubmit | ||
postsubmits: | ||
pingcap/community: | ||
- name: create-updating-owners-pr | ||
decorate: true | ||
max_concurrency: 1 | ||
run_if_changed: teams/.*/membership\.json | ||
branches: | ||
- master | ||
spec: | ||
init-containers: | ||
- name: git | ||
image: alpine/git | ||
command: [sh, -c] | ||
args: | ||
- | | ||
git diff --name-only HEAD~1 HEAD | grep membership.json > /pr-changed/files | ||
volumeMounts: | ||
- mountPath: /pr-changed | ||
name: pr-changed | ||
containers: | ||
- name: main | ||
image: denoland/deno:1.32.2 | ||
command: [bash, -c] | ||
args: | ||
- | | ||
cat /pr-changed/files | xargs -I {} deno run --allow-all \ | ||
https://github.com/PingCAP-QE/ci/raw/main/scripts/pingcap/community/update-prow-owners.ts \ | ||
--input {} --owner pingcap --github_private_token $(GITHUB_API_TOKEN) | ||
env: | ||
- name: GITHUB_API_TOKEN | ||
valueFrom: | ||
secretKeyRef: | ||
key: token | ||
name: github-token | ||
volumeMounts: | ||
- mountPath: /pr-changed | ||
name: pr-changed | ||
resources: | ||
limits: | ||
cpu: "1" | ||
memory: 1Gi | ||
requests: | ||
cpu: "1" | ||
memory: 1Gi | ||
volumes: | ||
- name: pr-changed | ||
emptyDir: {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import * as yaml from "https://deno.land/[email protected]/yaml/mod.ts"; | ||
import * as flags from "https://deno.land/[email protected]/flags/mod.ts"; | ||
import { Octokit } from "https://esm.sh/[email protected]"; | ||
|
||
interface CommunityTeamConfig { | ||
_comment?: string; | ||
description?: string; | ||
maintainers?: string[]; | ||
committers?: string[]; | ||
reviewers?: string[]; | ||
repositories: string[]; | ||
} | ||
|
||
interface SimpleProwOwners { | ||
options?: { no_parent_owners?: boolean }; | ||
labels: string[]; | ||
approvers: string[]; | ||
emeritus_approvers?: string[]; | ||
reviewers: string[]; | ||
} | ||
|
||
type ProwOwners = SimpleProwOwners | { | ||
filters: { [pattern: string]: { labels: string[] } }; | ||
}; | ||
|
||
interface cliArgs { | ||
input: string; | ||
owner: string; | ||
github_private_token: string; | ||
} | ||
|
||
async function main({ input, owner, github_private_token }: cliArgs) { | ||
// Read the input JSON file | ||
const inputJson = await Deno.readTextFile(input); | ||
|
||
// Parse the input JSON | ||
const inputData: CommunityTeamConfig = JSON.parse(inputJson); | ||
|
||
// Extract the necessary data from the input | ||
const { reviewers, maintainers, committers, repositories } = inputData; | ||
|
||
const reviewersSet = new Set(reviewers); | ||
|
||
// Combine maintainers and committers into approvers | ||
const approversSet = new Set([...maintainers || [], ...committers || []]); | ||
|
||
// delete maintainers and committers from reviewers | ||
approversSet.forEach((e) => reviewersSet.delete(e)); | ||
|
||
// Construct the output object | ||
const outputData = { | ||
approvers: Array.from(approversSet).sort((a, b) => a.localeCompare(b)), | ||
reviewers: Array.from(reviewersSet).sort((a, b) => a.localeCompare(b)), | ||
}; | ||
|
||
// Create a new Octokit instance using the provided token | ||
const octokit = new Octokit({ auth: github_private_token }); | ||
|
||
// Generate the output YAML data | ||
const yamlContent = `# See the OWNERS docs at https://go.k8s.io/owners\n${ | ||
yaml.stringify(outputData) | ||
}`; | ||
const filePath = "OWNERS"; | ||
|
||
// Create or update the /OWNERS file in each repository | ||
for (const repository of repositories) { | ||
console.debug(`🫧 prepare update for repo: ${owner}/${repository}`); | ||
// Get the default branch of the repository | ||
const { data: repo } = await octokit.rest.repos.get({ | ||
owner, | ||
repo: repository, | ||
}); | ||
|
||
const baseRef = repo?.default_branch || "main"; | ||
const { data } = await octokit.rest.git.getRef({ | ||
owner, | ||
repo: repository, | ||
ref: `heads/${baseRef}`, | ||
}); | ||
const baseSha = data.object.sha; | ||
|
||
await octokit.rest.repos.getContent({ | ||
owner, | ||
repo: repository, | ||
path: filePath, | ||
ref: baseRef, | ||
}).then(async ({ data: { sha: fileSha } }: { data: { sha: string } }) => { | ||
console.debug(`file sha: ${fileSha}`); | ||
const pr = await createUpdateFilePR( | ||
octokit, | ||
owner, | ||
repository, | ||
baseRef, | ||
baseSha, | ||
filePath, | ||
fileSha, | ||
yamlContent, | ||
); | ||
console.debug( | ||
`Created pull request for OWNERS file update in ${owner}/${repository}: ${pr.html_url}.`, | ||
); | ||
}).catch((error: { status?: number }) => { | ||
if (error?.status === 404) { | ||
console.info( | ||
`File "${filePath}" is not existed in repo: ${owner}/${repository}, I will not create pull request to it.`, | ||
); | ||
} else { | ||
// handle all other errors | ||
throw error; | ||
} | ||
}); | ||
|
||
console.debug(`✅ for repo: ${owner}/${repository}`); | ||
} | ||
} | ||
|
||
async function createUpdateFilePR( | ||
octokit: Octokit, | ||
owner: string, | ||
repository: string, | ||
baseRef: string, | ||
baseSha: string, | ||
filePath: string, | ||
fileSha: string, | ||
fileContent: string, | ||
) { | ||
const headRef = `bot/update-owners-${Date.now()}`; | ||
const commitMessage = "Update OWNERS file"; | ||
|
||
// Create a new branch | ||
await octokit.rest.git.createRef({ | ||
owner, | ||
repo: repository, | ||
ref: `refs/heads/${headRef}`, | ||
sha: baseSha, | ||
}); | ||
|
||
// Update the /OWNERS file in the new branch | ||
await octokit.rest.repos.createOrUpdateFileContents({ | ||
owner, | ||
repo: repository, | ||
path: filePath, | ||
message: commitMessage, | ||
content: btoa(fileContent), | ||
branch: headRef, | ||
sha: fileSha, | ||
}); | ||
|
||
// Create a pull request | ||
const { data: pr } = await octokit.rest.pulls.create({ | ||
owner, | ||
repo: repository, | ||
title: commitMessage, | ||
head: headRef, | ||
base: baseRef, | ||
}); | ||
|
||
return pr; | ||
} | ||
|
||
// Execute the main function | ||
/** | ||
* ---------entry---------------- | ||
* ****** CLI args ************** | ||
* --input <team membership.json path> | ||
* --owner <github ORG> | ||
* --github_private_token <github private token> | ||
*/ | ||
const args = flags.parse<cliArgs>(Deno.args); | ||
await main(args); | ||
Deno.exit(0); |