| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you 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. | |
| */ | |
| const exec = require("@actions/exec"); | |
| const fs = require("fs"); | |
| const path = require("path"); | |
| const { Pr } = require("./pr"); | |
| const { ReviewersForLabel } = require("./reviewersForLabel"); | |
| const { BOT_NAME } = require("./constants"); | |
| function getPrFileName(prNumber) { | |
| return `pr-${prNumber}.json`.toLowerCase(); | |
| } | |
| function getReviewersForLabelFileName(label) { | |
| return `reviewers-for-label-${label}.json`.toLowerCase(); | |
| } | |
| async function commitStateToRepo() { | |
| try { | |
| await exec.exec("git pull origin pr-bot-state"); | |
| } catch (err) { | |
| console.log( | |
| `Unable to get most recent repo contents, commit may fail: ${err}` | |
| ); | |
| } | |
| // Print changes for observability | |
| await exec.exec("git status", [], { ignoreReturnCode: true }); | |
| await exec.exec("git add state/*"); | |
| const changes = await exec.exec( | |
| "git diff --quiet --cached origin/pr-bot-state state", | |
| [], | |
| { ignoreReturnCode: true } | |
| ); | |
| if (changes == 1) { | |
| await exec.exec(`git commit -m "Updating config from bot" --allow-empty`); | |
| await exec.exec("git push origin pr-bot-state"); | |
| } else { | |
| console.log( | |
| "Skipping updating state branch since there are no changes to commit" | |
| ); | |
| } | |
| } | |
| export class PersistentState { | |
| private switchedBranch = false; | |
| // Returns a Pr object representing the current saved state of the pr. | |
| async getPrState(prNumber: number): Promise<typeof Pr> { | |
| var fileName = getPrFileName(prNumber); | |
| return new Pr(await this.getState(fileName, "state/pr-state")); | |
| } | |
| // Writes a Pr object representing the current saved state of the pr to persistent storage. | |
| async writePrState(prNumber: number, newState: any) { | |
| var fileName = getPrFileName(prNumber); | |
| await this.writeState(fileName, "state/pr-state", new Pr(newState)); | |
| } | |
| // Returns a ReviewersForLabel object representing the current saved state of which reviewers have reviewed recently. | |
| async getReviewersForLabelState( | |
| label: string | |
| ): Promise<typeof ReviewersForLabel> { | |
| var fileName = getReviewersForLabelFileName(label); | |
| return new ReviewersForLabel(label, await this.getState(fileName, "state")); | |
| } | |
| // Writes a ReviewersForLabel object representing the current saved state of which reviewers have reviewed recently. | |
| async writeReviewersForLabelState(label: string, newState: any) { | |
| var fileName = getReviewersForLabelFileName(label); | |
| await this.writeState( | |
| fileName, | |
| "state", | |
| new ReviewersForLabel(label, newState) | |
| ); | |
| } | |
| private async getState(fileName, baseDirectory) { | |
| await this.ensureCorrectBranch(); | |
| fileName = path.join(baseDirectory, fileName); | |
| if (!fs.existsSync(fileName)) { | |
| return null; | |
| } | |
| return JSON.parse(fs.readFileSync(fileName, { encoding: "utf-8" })); | |
| } | |
| private async writeState(fileName, baseDirectory, state) { | |
| await this.ensureCorrectBranch(); | |
| fileName = path.join(baseDirectory, fileName); | |
| if (!fs.existsSync(baseDirectory)) { | |
| fs.mkdirSync(baseDirectory, { recursive: true }); | |
| } | |
| fs.writeFileSync(fileName, JSON.stringify(state, null, 2), { | |
| encoding: "utf-8", | |
| }); | |
| await commitStateToRepo(); | |
| } | |
| private async ensureCorrectBranch() { | |
| if (this.switchedBranch) { | |
| return; | |
| } | |
| console.log( | |
| "Switching to branch pr-bot-state for reading/storing persistent state between runs" | |
| ); | |
| try { | |
| await exec.exec(`git config user.name ${BOT_NAME}`); | |
| await exec.exec(`git config user.email ${BOT_NAME}@github.com`); | |
| await exec.exec("git config pull.rebase false"); | |
| await exec.exec("git fetch origin pr-bot-state"); | |
| await exec.exec("git checkout pr-bot-state"); | |
| } catch { | |
| console.log( | |
| "Couldnt find branch pr-bot-state in origin, trying to create it" | |
| ); | |
| await exec.exec("git checkout -b pr-bot-state"); | |
| // Ensure that if we've created or just checked out the branch that we can also push to it | |
| await exec.exec("git push origin pr-bot-state"); | |
| } | |
| this.switchedBranch = true; | |
| } | |
| } |