This is an automated email from the ASF dual-hosted git repository. maximebeauchemin pushed a commit to branch pyproject.toml in repository https://gitbox.apache.org/repos/asf/superset.git
commit fbf765ffd36970eae5c67bcec75a4254f0b29af0 Author: Maxime Beauchemin <[email protected]> AuthorDate: Mon Mar 25 13:03:24 2024 -0700 feat: make supersetbot do what dependabot should do --- .github/supersetbot/src/cli.js | 10 ++++++- .github/supersetbot/src/github.js | 52 +++++++++++++++++++++++++++++++++++ .github/supersetbot/src/utils.js | 57 +++++++++++++++++---------------------- .github/workflows/supersetbot.yml | 1 + 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/.github/supersetbot/src/cli.js b/.github/supersetbot/src/cli.js index 4b4612ee97..4cefa3567f 100755 --- a/.github/supersetbot/src/cli.js +++ b/.github/supersetbot/src/cli.js @@ -151,6 +151,14 @@ export default function getCLI(context) { await github.assignOrgLabel(opts.issue, opts.verbose, opts.dryRun); }); + program.command('auto-bump <python-lib>') + .description('Submit a PR to bump the version of a package') + .action(async function (pythonLib) { + const opts = context.processOptions(this, ['repo']); + const github = new Github({ context }); + github.createBumpLibPullRequest(pythonLib, opts.verbose, opts.dryRun); + }); + program.command('docker') .description('Generates/run docker build commands use in CI') @@ -166,7 +174,7 @@ export default function getCLI(context) { const cmd = docker.getDockerCommand({ ...opts }); context.log(cmd); if (!opts.dryRun) { - utils.runShellCommand(cmd, false); + utils.runShellCommand({ cmd, raiseOnError: false }); } }); } diff --git a/.github/supersetbot/src/github.js b/.github/supersetbot/src/github.js index 39d3e42e9f..f94b350de5 100644 --- a/.github/supersetbot/src/github.js +++ b/.github/supersetbot/src/github.js @@ -1,7 +1,12 @@ +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + import { Octokit } from '@octokit/rest'; import { throttling } from '@octokit/plugin-throttling'; import { ORG_LIST, PROTECTED_LABEL_PATTERNS, COMMITTER_TEAM } from './metadata.js'; +import { runShellCommand } from './utils.js'; class Github { #userInTeamCache; @@ -247,6 +252,53 @@ class Github { static isLabelProtected(label) { return PROTECTED_LABEL_PATTERNS.some((pattern) => new RegExp(pattern).test(label)); } + + createBumpLibPullRequest(lib, verbose = false, dryRun = false) { + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'update-')); + console.log("CWD:", cwd); + + // Clone the repo + runShellCommand({ command: `git clone --depth 1 [email protected]:${this.context.repo} .`, cwd, verbose }); + + // Run pip-compile-multi + runShellCommand({ command: `pip-compile-multi -P ${lib}`, cwd }); + + // Check for changes + const status = runShellCommand({ command: 'git status --porcelain', cwd, raiseOnError: false, verbose }); + if (!!status) { + console.log('No changes detected... skipping.'); + } else { + + // Create branch + const branchName = `bump-${lib}-${Date.now()}`; + runShellCommand({ command: `git checkout -b ${branchName}`, cwd, verbose }); + + // Commit changes + runShellCommand({ command: `git add .`, cwd }); + const commitMessage = `chore(supersetbot): bump python library "${lib}"`; + runShellCommand({ command: `git commit -m "${commitMessage}"`, cwd, verbose }); + + // Push changes + runShellCommand({ command: `git push origin ${branchName}`, cwd, verbose }); + + if (!dryRun) { + // Create a PR + this.octokit.pulls.create({ + ...this.unPackRepo(), + title: commitMessage, + head: branchName, + base: 'master', + body: `Updates the python "${lib}" library version. \n\nGenerated by @supersetbot 🤖`, + }) + .then(({ data }) => { + console.log(`Pull request created: ${data.html_url}`); + }) + .catch(console.error); + } + } + // Cleaning up + fs.rmSync(cwd, { recursive: true, force: true }); + } } export default Github; diff --git a/.github/supersetbot/src/utils.js b/.github/supersetbot/src/utils.js index 97c6b2d8f5..07777c378e 100644 --- a/.github/supersetbot/src/utils.js +++ b/.github/supersetbot/src/utils.js @@ -17,7 +17,8 @@ * under the License. */ -import { spawn } from 'child_process'; +import { spawnSync } from 'child_process'; + import { readFile } from 'fs/promises'; import { fileURLToPath } from 'url'; import path from 'path'; @@ -40,39 +41,29 @@ export async function currentPackageVersion() { return data.version; } -export function runShellCommand(command, raiseOnError = true) { - return new Promise((resolve, reject) => { - // Split the command string into an array of arguments - const args = command.split(/\s+/).filter((s) => !!s && s !== '\\'); - const childProcess = spawn(args.shift(), args); - let stdoutData = ''; - let stderrData = ''; +export function runShellCommand({ command, raiseOnError = true, exitOnError = true, cwd = null, verbose = false }) { + const args = command.split(/\s+/).filter((s) => !!s && s !== '\\'); + const spawnOptions = { stdio: 'inherit', shell: true }; + if (verbose) { + console.log(`RUN: ${command}`); + } + if (cwd) { + spawnOptions.cwd = cwd; + } - // Capture stdout data - childProcess.stdout.on('data', (data) => { - stdoutData += data; - console.log(`stdout: ${data}`); - }); + const result = spawnSync(args.shift(), args, spawnOptions); - // Capture stderr data - childProcess.stderr.on('data', (data) => { - stderrData += data; - console.error(`stderr: ${data}`); - }); + if (result.status !== 0) { + const msg = `Command failed with exit code ${result.status}: ${result.stderr?.toString()}`; + console.error(msg); + + if (raiseOnError) { + throw new Error(msg); + } + if (exitOnError) { + process.exit(1); + } + } - // Handle process exit - childProcess.on('close', (code) => { - if (code === 0) { - resolve(stdoutData); - } else { - const msg = `Command failed with code ${code}: ${stderrData}`; - if (raiseOnError) { - reject(new Error(msg)); - } else { - console.error(msg); - process.exit(1); - } - } - }); - }); + return result.stdout?.toString(); } diff --git a/.github/workflows/supersetbot.yml b/.github/workflows/supersetbot.yml index f336629676..cafda9b74b 100644 --- a/.github/workflows/supersetbot.yml +++ b/.github/workflows/supersetbot.yml @@ -51,6 +51,7 @@ jobs: COMMENT_BODY: ${{ github.event.comment.body }} INPUT_COMMENT_BODY: ${{ github.event.inputs.comment_body }} run: | + npm install -g supersetbot cat <<EOF > script.js const run = async () => { const { runCommandFromGithubAction } = await import('supersetbot');
