From ce559cda9be9ca181a5b2406c9a2dd4f5518608a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Va=C5=A1ek?= Date: Wed, 10 Jul 2024 15:06:50 +0200 Subject: [PATCH] Cron GH Action to update CA bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Matej VaĊĦek --- .github/workflows/update-ca-bundle.yaml | 26 ++++++ hack/update-ca-bundle.js | 110 ++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 .github/workflows/update-ca-bundle.yaml create mode 100644 hack/update-ca-bundle.js diff --git a/.github/workflows/update-ca-bundle.yaml b/.github/workflows/update-ca-bundle.yaml new file mode 100644 index 000000000..ad8bd75a6 --- /dev/null +++ b/.github/workflows/update-ca-bundle.yaml @@ -0,0 +1,26 @@ +name: Update CA bundle in embedded templates + +permissions: + contents: write + pull-requests: write + +on: + schedule: + - cron: '0 */4 * * *' + +jobs: + update: + name: Update CA bundle + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install NPM deps. + run: npm install octokit@3.2.1 + - name: Create PR + env: + GITHUB_TOKEN: ${{ github.token }} + run: node ./hack/update-ca-bundle.js + diff --git a/hack/update-ca-bundle.js b/hack/update-ca-bundle.js new file mode 100644 index 000000000..dd7b5eee5 --- /dev/null +++ b/hack/update-ca-bundle.js @@ -0,0 +1,110 @@ +// const xml2js = require('xml2js'); +const {Octokit} = require("octokit"); +// const {readFile,writeFile} = require('fs/promises'); +const {spawn} = require('node:child_process'); + +const octokit = new Octokit({auth: process.env.GITHUB_TOKEN}); +const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/') + +const prExists = async (pred) => { + let page = 1 + const perPage = 10; + + while (true) { + const resp = await octokit.rest.pulls.list({ + owner: owner, + repo: repo, + state: 'open', + per_page: perPage, + page: page + }) + + for (const e of resp.data) { + if (pred(e)) { + return true + } + } + if (resp.data.length < perPage) { + return false + } + page++ + } +} + +/** + * @param script + * @return {Promise} + */ +const runScript = async (script) => { + const subproc = spawn("sh", ["-c", script], {stdio: ['inherit', 'inherit', 'inherit']}) + return new Promise((resolve, reject) => { + subproc.on('exit', code => { + resolve(code) + }) + if (typeof subproc.exitCode === 'number') { + resolve(subproc.exitCode) + } + }) +} + +/** + * @return {Promise} + */ +const updateCA = async () => { + let ec = await runScript('make templates/certs/ca-certificates.crt') + if (ec !== 0) { + throw new Error('cannot update CA bundle') + } + return (await runScript('git diff --exit-code -- templates/certs/ca-certificates.crt')) !== 0 +} + +const prepareBranch = async (branchName, prTitle) => { + const script = `git config user.email "automation@knative.team" && \\ + git config user.name "Knative Automation" && \\ + git checkout -b "${branchName}" && \\ + make generate/zz_filesystem_generated.go && \\ + git add generate/zz_filesystem_generated.go templates/certs/ca-certificates.crt && \\ + git commit -m "${prTitle}" && \\ + git push --set-upstream origin "${branchName}" +` + const ec = await runScript(script) + if (ec !== 0) { + throw new Error("cannot prepare branch: non-zero exit code") + } +} + +const main = async () => { + const prTitle = `chore: update CA bundle` + if (await prExists(({title}) => title === prTitle)) { + console.log("The PR already exists!") + return + } + + const hasUpdated = await updateCA() + if (!hasUpdated) { + console.log('The CA bundle is up to date. Nothing to be done.') + return + } + + const branchName = `update-ca-bundle-${(new Date()).toISOString().split('T')[0]}` + + await prepareBranch(branchName, prTitle) + + await octokit.rest.pulls.create({ + owner: owner, + repo: repo, + title: prTitle, + body: prTitle, + base: 'main', + head: `${owner}:${branchName}`, + }) + console.log("The PR has been created!") + +} + +main().then(value => { + console.log("OK!") +}).catch(reason => { + console.log("ERROR: ", reason) + process.exit(1) +})