Skip to content

feat(CI): Keep local and Github CI test in sync with cargo xtask#23414

Open
2010YOUY01 wants to merge 1 commit into
apache:mainfrom
2010YOUY01:ci-xtask
Open

feat(CI): Keep local and Github CI test in sync with cargo xtask#23414
2010YOUY01 wants to merge 1 commit into
apache:mainfrom
2010YOUY01:ci-xtask

Conversation

@2010YOUY01

@2010YOUY01 2010YOUY01 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Part of #21048

For easier review, start with this PR writeup, then read the top comments in .github/workflows/rust.yml, and then follow along with the code.

This PR introduces an easy way to reproduce CI checks locally.

To reproduce .github/workflows/rust.yml:

cargo xtask ci workflow rust

Example output:

yongting@Yongtings-MacBook-Pro-2 ~/C/datafusion (ci-xtask)> cargo xtask ci workflow rust

ci workflow `rust` total execution time: 565.131385s
    ci job `linux-rustdoc`: 210.992471s
    ci job `linux-test-doc`: 117.395768s
    ci job `linux-test`: 85.890313s
    ci job `linux-test-example`: 39.367084s
    ci job `linux-test-datafusion-cli`: 37.235765s
    ci job `clippy`: 24.750692s
    ci job `msrv`: 16.421963s
    ci job `macos-aarch64`: 12.399443s
    ci job `config-docs-check`: 6.801263s
    ci job `check-fmt`: 3.139787s
    ci job `linux-cargo-check-datafusion`: 2.715153s
    ci job `cargo-toml-formatting-checks`: 1.94445s
    ci job `examples-docs-check`: 1.518475s
    ci job `linux-cargo-check-datafusion-functions`: 1.121708s
    ci job `sqllogictest-substrait`: 991.08ms
    ci job `linux-build-lib`: 600.417ms
    ci job `linux-datafusion-proto-features`: 596.001ms
    ci job `linux-datafusion-substrait-features`: 528.707ms
    ci job `vendor`: 346.94ms
    ci job `linux-datafusion-common-features`: 208.197ms
    ci job `verify-clean`: 83.283ms
    ci job `verify-clean`: 82.425ms

To reproduce single job or step similarly,

yongting@Yongtings-MacBook-Pro-2 ~/C/datafusion (ci-xtask)> cargo xtask ci job check-fmt
+ /Users/yongting/Code/datafusion/ci/scripts/rust_fmt.sh
[rust_fmt.sh] `cargo fmt --all -- --check`

ci job `check-fmt` total execution time: 2.817591s
    ci step `fmt`: 2.817591s
yongting@Yongtings-MacBook-Pro-2 ~/C/datafusion (ci-xtask)> cargo xtask ci step fmt
+ /Users/yongting/Code/datafusion/ci/scripts/rust_fmt.sh
[rust_fmt.sh] `cargo fmt --all -- --check`

ci step `fmt` completed in 2.793302s

Background

The existing GitHub CI follows a 3-layer structure:

  • step: an atomic check, for example cargo fmt
  • job: one or more steps, for example check-fmt
  • workflow: a list of jobs, for example .github/workflows/rust.yml

For example:

workflow: rust
  job: check-fmt
    step: fmt
      command: cargo fmt --all -- --check

  job: clippy
    step: clippy
      command: cargo clippy ...

  job: linux-test
    step: rust-test
      command: cargo test ...
    step: verify-clean
      command: git diff --exit-code

This PR mirrors that structure in xtask, so the same CI units can be used by both GitHub Actions and local runs.

Motivation

Making CI easy to reproduce locally improves developer experience.

  • If a specific job fails on GitHub, we can reproduce it locally with cargo xtask ci job <job>.
  • After a small change, we can run a subset of the full CI locally. This shortens the development cycle while still giving good confidence that the full CI will pass.

Possible future extensions:

cargo xtask ci workflow changed

Detect changed Rust files, then only run unit tests and clippy on crates affected by the change, plus relevant integration tests.

cargo xtask ci workflow doc

For documentation-only changes, only run formatting, typo checks, docs checks, and other lightweight checks.

Implementation

The implementation adds reusable units for atomic checks, such as cargo fmt. In GitHub Actions terminology, these are "steps".

The key idea is:

  • GitHub CI orchestrates these atomic checks remotely.
  • cargo xtask ci ... orchestrates the same checks locally.

There are two viable ways to organize these atomic checks:

  • cargo xtask, for example cargo xtask ci step fmt
  • shell scripts, for example ./check_fmt.sh

I previously tried the shell script approach for dev.yml:

https://github.com/apache/datafusion/blob/main/dev/rust_lint.sh

For the current CI complexity, I found cargo xtask easier to keep organized. I suggest we continue with this approach and later migrate the existing scripts to use xtask as well.

Scope of this PR

This PR:

  • Introduces the xtask framework.
  • Migrates the steps in rust.yml to xtask runners. The commands are unchanged, except for the small difference noted below.
  • Supports local orchestration with cargo xtask ci workflow rust.

Notes:

  • This PR only achieves CI check command equivalence. Setup steps and tool installation are not fully reproduced yet, and are expected to be done manually for now.
  • Three jobs are skipped in the local run because they need slightly different handling on macOS. I would like to add them in follow-up PRs to keep this PR easier to review.

Testing

Individual 'step's will be tested by CI.

For workflow/jobs, I have tested locally on MacOS. I think we can let CI to test some fast workflows in the future.

@github-actions github-actions Bot added the development-process Related to development process of DataFusion label Jul 9, 2026
Comment thread xtask/src/step_runners.rs
Comment on lines +667 to +668
"--release-source",
"rust-dist",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines added should be the only changed command (I found the default option fails on my local setting)

@2010YOUY01

Copy link
Copy Markdown
Contributor Author

cc who might be interested, @alamb @comphead @kumarUjjawal @blaginin

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @2010YOUY01 does that mean CI changes should be reflected in xtask and vice versa?

WDYT about https://github.com/nektos/act to try run DataFusion CI locally, although I heard not all things possible with this tool

.iter()
.find(|job| job.name == VERIFY_CLEAN_JOB_NAME)
.unwrap_or_else(|| panic!("missing `{VERIFY_CLEAN_JOB_NAME}` job"));
let mut jobs = vec![verify_clean];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fails immediately if we have there are any uncommitted changes before it runs any CI check. Was this intentional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is intentional, now the whole workflow requires a clean local git repo to run (no uncommitted changes), though I think there is room to improve it later.

To better simulate GitHub CI, we should try to reproduce the actual commands it runs as closely as possible.

Currently, GitHub CI runs several verify-clean checks between tests to ensure that no generated or modified files are left behind after each test, using git diff.

So for the local reproducer to behave the same way, we need to ensure there are no uncommitted changes before running the full workflow.

Comment thread xtask/src/step_runners.rs
format!("failed to create {}: {error}", tpch_data.display())
})?;

self.command("git")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the step fails partway through and I re-run, git clone immediately fails with "destination path already exists". I'd have to manually rm -rf tpch-dbgen before retrying.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It keeps the existing command, we could fix it later. See #23414 (comment)

Comment thread xtask/src/step_runners.rs
.env("RUSTFLAGS", rustflags)
.run()?;

let chrome_driver = format!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in wasm-pack being called with --chromedriver /chromedriver , which fails with a confusing "not found" error. The run_rust_sqllogictest function handles the missing-env-var case much better by returning an explicit error, we can use the same here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, #23414 (comment)

I found this job also a bit tricky to setup locally on my Mac

Comment thread xtask/src/main.rs
let result = runner(self, &args);
job_timing.add_step_timing(step, start.elapsed().unwrap_or_default());

if let Err(error) = result

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should consider building the full message in a single map_err rather than chaining, there are several instances

@2010YOUY01

2010YOUY01 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @2010YOUY01 does that mean CI changes should be reflected in xtask and vice versa?

Yes, exactly. That's the end goal, and this PR is the initial step with a limited scope.

WDYT about https://github.com/nektos/act to try run DataFusion CI locally, although I heard not all things possible with this tool

Good to know about act! I went to do some investigation, its limitations are

  • Not mature enough (lots of unsupported features for github .yml, bugs)
  • Expensive to run locally (Docker based, pulls large images)

Overall I think xtask is a better choice.

use crate::ci_workflows::WorkflowInfo;

const VERIFY_CLEAN_JOB_NAME: &str = "verify-clean";
const SKIPPED_JOBS: &[&str] = &[

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These jobs are not supported by the local runner yet. I found that they need some modifications to run on macOS.

This PR does not change any commands, to make the review easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

development-process Related to development process of DataFusion

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants