Skip to content

Commit

Permalink
Revert "Revert "feat(scripts/pingcap/community): add script `update-p…
Browse files Browse the repository at this point in the history
…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
wuhuizuo authored Jun 5, 2023
1 parent 878fe45 commit d599f6a
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 0 deletions.
1 change: 1 addition & 0 deletions prow-jobs/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ configMapGenerator:
options:
disableNameSuffixHash: true
files:
- pingcap-community-postsubmits.yaml
- pingcap-inc-enterprise-extensions-presubmits.yaml
- pingcap-qe-ci-presubmits.yaml
- pingcap-tidb-latest-postsubmits.yaml
Expand Down
48 changes: 48 additions & 0 deletions prow-jobs/pingcap-community-postsubmits.yaml
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: {}
171 changes: 171 additions & 0 deletions scripts/pingcap/community/update-prow-owners.ts
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);

0 comments on commit d599f6a

Please sign in to comment.