diff --git a/.github/workflows/autofix_pr.yml b/.github/workflows/autofix_pr.yml new file mode 100644 index 0000000000000000000000000000000000000000..8c7786f6e1b91879a8b5a6f26f685570cd9cb2d3 --- /dev/null +++ b/.github/workflows/autofix_pr.yml @@ -0,0 +1,53 @@ +# Generated from xtask::workflows::autofix_pr +# Rebuild with `cargo xtask workflows`. +name: autofix_pr +run-name: 'autofix PR #${{ inputs.pr_number }}' +on: + workflow_dispatch: + inputs: + pr_number: + description: pr_number + required: true + type: string +jobs: + run_autofix: + runs-on: namespace-profile-2x4-ubuntu-2404 + steps: + - id: get-app-token + name: autofix_pr::run_autofix::authenticate_as_zippy + uses: actions/create-github-app-token@bef1eaf1c0ac2b148ee2a0a74c65fbe6db0631f1 + with: + app-id: ${{ secrets.ZED_ZIPPY_APP_ID }} + private-key: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }} + - name: steps::checkout_repo_with_token + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + clean: false + token: ${{ steps.get-app-token.outputs.token }} + - name: autofix_pr::run_autofix::checkout_pr + run: gh pr checkout ${{ inputs.pr_number }} + shell: bash -euxo pipefail {0} + env: + GITHUB_TOKEN: ${{ steps.get-app-token.outputs.token }} + - name: autofix_pr::run_autofix::run_cargo_fmt + run: cargo fmt --all + shell: bash -euxo pipefail {0} + - name: autofix_pr::run_autofix::run_clippy_fix + run: cargo clippy --workspace --release --all-targets --all-features --fix --allow-dirty --allow-staged + shell: bash -euxo pipefail {0} + - name: autofix_pr::run_autofix::commit_and_push + run: | + if git diff --quiet; then + echo "No changes to commit" + else + git add -A + git commit -m "Apply cargo fmt and clippy --fix" + git push + fi + shell: bash -euxo pipefail {0} + env: + GIT_COMMITTER_NAME: Zed Zippy + GIT_COMMITTER_EMAIL: hi@zed.dev + GIT_AUTHOR_NAME: Zed Zippy + GIT_AUTHOR_EMAIL: hi@zed.dev + GITHUB_TOKEN: ${{ steps.get-app-token.outputs.token }} diff --git a/tooling/xtask/src/tasks/workflows.rs b/tooling/xtask/src/tasks/workflows.rs index 717517402d619e54d30a502fcfe26418910aac35..fe476355203a69c962081c36fe350460b9df6f6b 100644 --- a/tooling/xtask/src/tasks/workflows.rs +++ b/tooling/xtask/src/tasks/workflows.rs @@ -5,6 +5,7 @@ use std::fs; use std::path::{Path, PathBuf}; mod after_release; +mod autofix_pr; mod cherry_pick; mod compare_perf; mod danger; @@ -111,6 +112,7 @@ pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> { WorkflowFile::zed(run_tests::run_tests), WorkflowFile::zed(release::release), WorkflowFile::zed(cherry_pick::cherry_pick), + WorkflowFile::zed(autofix_pr::autofix_pr), WorkflowFile::zed(compare_perf::compare_perf), WorkflowFile::zed(run_agent_evals::run_unit_evals), WorkflowFile::zed(run_agent_evals::run_cron_unit_evals), diff --git a/tooling/xtask/src/tasks/workflows/autofix_pr.rs b/tooling/xtask/src/tasks/workflows/autofix_pr.rs new file mode 100644 index 0000000000000000000000000000000000000000..7e00a8bcbdbd9cd367221d2d90413fb59428d560 --- /dev/null +++ b/tooling/xtask/src/tasks/workflows/autofix_pr.rs @@ -0,0 +1,77 @@ +use gh_workflow::*; + +use crate::tasks::workflows::{ + runners, + steps::{self, NamedJob, named}, + vars::{self, StepOutput, WorkflowInput}, +}; + +pub fn autofix_pr() -> Workflow { + let pr_number = WorkflowInput::string("pr_number", None); + let autofix = run_autofix(&pr_number); + named::workflow() + .run_name(format!("autofix PR #{pr_number}")) + .on(Event::default().workflow_dispatch( + WorkflowDispatch::default().add_input(pr_number.name, pr_number.input()), + )) + .add_job(autofix.name, autofix.job) +} + +fn run_autofix(pr_number: &WorkflowInput) -> NamedJob { + fn authenticate_as_zippy() -> (Step, StepOutput) { + let step = named::uses( + "actions", + "create-github-app-token", + "bef1eaf1c0ac2b148ee2a0a74c65fbe6db0631f1", + ) + .add_with(("app-id", vars::ZED_ZIPPY_APP_ID)) + .add_with(("private-key", vars::ZED_ZIPPY_APP_PRIVATE_KEY)) + .id("get-app-token"); + let output = StepOutput::new(&step, "token"); + (step, output) + } + + fn checkout_pr(pr_number: &WorkflowInput, token: &StepOutput) -> Step { + named::bash(&format!("gh pr checkout {pr_number}")).add_env(("GITHUB_TOKEN", token)) + } + + fn run_cargo_fmt() -> Step { + named::bash("cargo fmt --all") + } + + fn run_clippy_fix() -> Step { + named::bash( + "cargo clippy --workspace --release --all-targets --all-features --fix --allow-dirty --allow-staged", + ) + } + + fn commit_and_push(token: &StepOutput) -> Step { + named::bash(indoc::indoc! {r#" + if git diff --quiet; then + echo "No changes to commit" + else + git add -A + git commit -m "Apply cargo fmt and clippy --fix" + git push + fi + "#}) + .add_env(("GIT_COMMITTER_NAME", "Zed Zippy")) + .add_env(("GIT_COMMITTER_EMAIL", "hi@zed.dev")) + .add_env(("GIT_AUTHOR_NAME", "Zed Zippy")) + .add_env(("GIT_AUTHOR_EMAIL", "hi@zed.dev")) + .add_env(("GITHUB_TOKEN", token)) + } + + let (authenticate, token) = authenticate_as_zippy(); + + named::job( + Job::default() + .runs_on(runners::LINUX_SMALL) + .add_step(authenticate) + .add_step(steps::checkout_repo_with_token(&token)) + .add_step(checkout_pr(pr_number, &token)) + .add_step(run_cargo_fmt()) + .add_step(run_clippy_fix()) + .add_step(commit_and_push(&token)), + ) +} diff --git a/tooling/xtask/src/tasks/workflows/steps.rs b/tooling/xtask/src/tasks/workflows/steps.rs index 722a5f0704542889703fdbb42c691d01bc50ace6..7d55df2db433d6e6eae96a5ae62a0c033689d904 100644 --- a/tooling/xtask/src/tasks/workflows/steps.rs +++ b/tooling/xtask/src/tasks/workflows/steps.rs @@ -1,6 +1,6 @@ use gh_workflow::*; -use crate::tasks::workflows::{runners::Platform, vars}; +use crate::tasks::workflows::{runners::Platform, vars, vars::StepOutput}; pub const BASH_SHELL: &str = "bash -euxo pipefail {0}"; // https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsshell @@ -17,6 +17,16 @@ pub fn checkout_repo() -> Step { .add_with(("clean", false)) } +pub fn checkout_repo_with_token(token: &StepOutput) -> Step { + named::uses( + "actions", + "checkout", + "11bd71901bbe5b1630ceea73d27597364c9af683", // v4 + ) + .add_with(("clean", false)) + .add_with(("token", token.to_string())) +} + pub fn setup_pnpm() -> Step { named::uses( "pnpm",