diff --git a/.github/workflows/create-docs-pr.yml b/.github/workflows/create-docs-pr.yml new file mode 100644 index 000000000..4feb6b3a7 --- /dev/null +++ b/.github/workflows/create-docs-pr.yml @@ -0,0 +1,189 @@ +# Commits 0 Open PRs 1 Open PR 2 Open PRs +# ------------------------------------------- ---------------------------------------------------- ------------------------------------------------------------------------------------------------------- --------------------------------------------------------- +# Neither ahead or behind Nothing Dismiss PRs Dismiss PRs +# `documentation-gitbook` is behind Open A PR from `main` into `documentation-gitbook` Determine the directionality, if wrong dismiss and open a PR from `main` into `documentation-gitbook` Dismiss the PR from `documentation-gitbook` into `main` +# `documentation-gitbook` is behind Open A PR from `documentation-gitbook` into `main` Determine the directionality, if wrong dismiss and open a PR from `documentation-gitbook` into `main` Dismiss the PR from `main` into `documentation-gitbook` +# `documentation-gitbook` is ahead & behind Open 2 PRs Determine the directionality of the missing one and open that Nothing + +name: create-docs-pr + +on: + workflow_dispatch: + schedule: + - cron: '1 10 * * 1-5' + +jobs: + create-pr: + runs-on: ubuntu-latest + env: + DOCS_BRANCH: documentation-gitbook + PR_LABELS: 'documentation,high priority' + PR_ASSIGNEES: 'emprzy,jcblemai,pearsonca,saraloo,TimothyWillard' + PR_TITLE: 'Sync GitBook' + OWNER: 'HopkinsIDD' + REPO: 'flepiMoP' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + fetch-depth: 0 + - name: Determine Commits Ahead/Behind + run: | + git fetch --all + DOCS_AHEAD_MAIN=$( eval "git rev-list --count origin/main..origin/$DOCS_BRANCH -- documentation/" ) + DOCS_BEHIND_MAIN=$( eval "git rev-list --count origin/$DOCS_BRANCH..origin/main -- documentation/" ) + echo "DOCS_AHEAD_MAIN=$DOCS_AHEAD_MAIN" >> $GITHUB_ENV + echo "DOCS_BEHIND_MAIN=$DOCS_BEHIND_MAIN" >> $GITHUB_ENV + - name: Create PR If Needed + uses: actions/github-script@v7 + id: create-pr + with: + result-encoding: string + retries: 5 + script: | + const { DOCS_AHEAD_MAIN, DOCS_BEHIND_MAIN, DOCS_BRANCH, PR_LABELS, PR_ASSIGNEES, PR_TITLE, OWNER, REPO } = process.env + const prLabels = PR_LABELS.split(",") + const prAssignees = PR_ASSIGNEES.split(",") + const cc = prAssignees.map(x => "@" + x).join(", ") + const docsAheadMain = parseInt(DOCS_AHEAD_MAIN, 10) + const docsBehindMain = parseInt(DOCS_BEHIND_MAIN, 10) + if (isNaN(docsAheadMain) || isNaN(docsBehindMain)) { + throw new Error(`Cannot convert either "${DOCS_AHEAD_MAIN}" or "${DOCS_BEHIND_MAIN}" to integers.`) + } + const initialResults = await github.rest.search.issuesAndPullRequests({ + q: `owner:${OWNER} repo:${REPO} is:open is:pr in:title '${PR_TITLE}'` + }) + const count = initialResults.data.total_count + if (count > 2) { + throw new Error(`There are ${count} open PRs containing '${PR_TITLE}', but this action can only handle 0, 1, or 2 open PRs.`) + } + console.log(`${DOCS_BRANCH} is ${docsAheadMain} ahead, ${docsBehindMain} behind main. The open PR count is ${count}.`) + async function dismissAllPRs() { + initialResults.data.items.forEach((item) => { + github.rest.pulls.update({ + owner: OWNER, + repo: REPO, + pull_number: item.number, + state: "closed" + }) + }) + } + async function createPR({ from, to, body }) { + let prBody = `cc: ${cc}.` + if (body !== null) { + prBody = `${body} ${prBody}` + } + const today = (new Date()).toLocaleDateString() + const pr = await github.rest.pulls.create({ + owner: OWNER, + repo: REPO, + head: from, + base: to, + title: `${today} ${PR_TITLE} From ${from} Into ${to}`, + body: prBody + }) + github.rest.issues.addLabels({ + owner: OWNER, + repo: REPO, + issue_number: pr.data.number, + labels: prLabels + }) + github.rest.issues.addAssignees({ + owner: OWNER, + repo: REPO, + issue_number: pr.data.number, + assignees: prAssignees + }) + return pr.data.number + } + async function handleSingleDirection({ from, to }) { + if (count === 0) { + // There isn't the 1 expected PR, open it + createPR({ + from: from, + to: to, + body: null + }) + } else if (count === 1) { + // There is a PR open, determine direction + let title = initialResults.data.items[0].title + if (!title.includes(`${PR_TITLE} From ${from} Into ${to}`)) { + // Wrong direction, close & recreate + dismissAllPRs() + createPR({ + from: from, + to: to, + body: null + }) + } + } else { + // There are two PRs open, close the wrong direction + initialResults.data.items.forEach((item) => { + if (!title.includes(`${PR_TITLE} From ${from} Into ${to}`)) { + github.rest.pulls.update({ + owner: OWNER, + repo: REPO, + pull_number: item.number, + state: "closed" + }) + } + }) + } + } + if (docsAheadMain > 0 && docsBehindMain > 0) { + // Need PRs both ways + if (count === 0) { + // There are 0 PRs open, open both of them + const docsIntoMainPrNumber = await createPR({ + from: DOCS_BRANCH, + to: "main", + body: null + }) + createPR({ + from: "main", + to: DOCS_BRANCH, + body: `Please merge GH-${docsIntoMainPrNumber} first.` + }) + } else if (count === 1) { + // There is already a PR open in one direction, open the other direction + let title = initialResults.data.items[0].title + let number = initialResults.data.items[0].number + if (title.includes(`${PR_TITLE} From ${DOCS_BRANCH} Into main`)) { + // From docs into main already exists, create main into docs + createPR({ + from: "main", + to: DOCS_BRANCH, + body: `Please merge GH-${number} first.` + }) + } else { + // From main into docs already exists, create docs into main + const docsIntoMainPrNumber = await createPR({ + from: DOCS_BRANCH, + to: "main", + body: null + }) + github.rest.issues.createComment({ + owner: OWNER, + repo: REPO, + issue_number: number, + body: `Please merge GH-${docsIntoMainPrNumber} first.` + }) + } + } + } else if (docsAheadMain > 0) { + // Need a PR from docs to main + handleSingleDirection({ + from: DOCS_BRANCH, + to: "main" + }) + } else if (docsBehindMain > 0) { + // Need a PR from main to docs + handleSingleDirection({ + from: "main", + to: DOCS_BRANCH + }) + } else if (count > 0) { + // Ahead/behind commits is 0 but there are stale PRs to close + dismissAllPRs() + }