From 7d566e0600b04ac7da4f6c60edebd80df1c19fde Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Fri, 13 Mar 2026 13:40:45 +0100 Subject: [PATCH] extension_ci: Add initial support for extensions in a subdirectory (#51173) This wil help with releases for extensions living this repository, which will become more relevant once agent provider extensions are back on the table. Release Notes: - N/A --- .github/workflows/extension_bump.yml | 47 ++++++++-- .github/workflows/extension_tests.yml | 63 ++++++++++--- .github/workflows/run_tests.yml | 4 +- .../src/tasks/workflows/extension_bump.rs | 82 +++++++++++----- .../src/tasks/workflows/extension_tests.rs | 93 ++++++++++++++----- .../workflows/extension_workflow_rollout.rs | 5 +- .../xtask/src/tasks/workflows/run_tests.rs | 32 +++++-- tooling/xtask/src/tasks/workflows/steps.rs | 9 +- tooling/xtask/src/tasks/workflows/vars.rs | 33 +++++-- 9 files changed, 278 insertions(+), 90 deletions(-) diff --git a/.github/workflows/extension_bump.yml b/.github/workflows/extension_bump.yml index 9cc53741e8007a1b3ddd02ad07b191b3ce171cc8..e61e98f4042826858e54c6f5565c5fd62f280553 100644 --- a/.github/workflows/extension_bump.yml +++ b/.github/workflows/extension_bump.yml @@ -17,6 +17,10 @@ on: description: force-bump required: true type: boolean + working-directory: + description: working-directory + type: string + default: . secrets: app-id: description: The app ID used to create the PR @@ -42,8 +46,6 @@ jobs: if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then PR_FORK_POINT="$(git merge-base origin/main HEAD)" git checkout "$PR_FORK_POINT" - elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then - git checkout "$BRANCH_PARENT_SHA" else git checkout "$(git log -1 --format=%H)"~1 fi @@ -59,6 +61,10 @@ jobs: version_changed: ${{ steps.compare-versions-check.outputs.version_changed }} current_version: ${{ steps.compare-versions-check.outputs.current_version }} timeout-minutes: 1 + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} bump_extension_version: needs: - check_version_changed @@ -98,18 +104,35 @@ jobs: fi NEW_VERSION="$(sed -n 's/^version = \"\(.*\)\"/\1/p' < extension.toml | tr -d '[:space:]')" + EXTENSION_ID="$(sed -n 's/^id = "\(.*\)"/\1/p' < extension.toml | head -1 | tr -d '[:space:]')" + EXTENSION_NAME="$(sed -n 's/^name = "\(.*\)"/\1/p' < extension.toml | head -1 | tr -d '[:space:]')" + + if [[ "$WORKING_DIR" == "." || -z "$WORKING_DIR" ]]; then + { + echo "title=Bump version to ${NEW_VERSION}"; + echo "body=This PR bumps the version of this extension to v${NEW_VERSION}"; + echo "branch_name=zed-zippy-autobump"; + } >> "$GITHUB_OUTPUT" + else + { + echo "title=${EXTENSION_ID}: Bump to v${NEW_VERSION}"; + echo "body=This PR bumps the version of the ${EXTENSION_NAME} extension to v${NEW_VERSION}"; + echo "branch_name=zed-zippy-${EXTENSION_ID}-autobump"; + } >> "$GITHUB_OUTPUT" + fi echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" env: OLD_VERSION: ${{ needs.check_version_changed.outputs.current_version }} BUMP_TYPE: ${{ inputs.bump-type }} + WORKING_DIR: ${{ inputs.working-directory }} - name: extension_bump::create_pull_request uses: peter-evans/create-pull-request@v7 with: - title: Bump version to ${{ steps.bump-version.outputs.new_version }} - body: This PR bumps the version of this extension to v${{ steps.bump-version.outputs.new_version }} - commit-message: Bump version to v${{ steps.bump-version.outputs.new_version }} - branch: zed-zippy-autobump + title: ${{ steps.bump-version.outputs.title }} + body: ${{ steps.bump-version.outputs.body }} + commit-message: ${{ steps.bump-version.outputs.title }} + branch: ${{ steps.bump-version.outputs.branch_name }} committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> base: main delete-branch: true @@ -117,6 +140,10 @@ jobs: sign-commits: true assignees: ${{ github.actor }} timeout-minutes: 3 + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} create_version_label: needs: - check_version_changed @@ -145,6 +172,10 @@ jobs: }) github-token: ${{ steps.generate-token.outputs.token }} timeout-minutes: 1 + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} trigger_release: needs: - check_version_changed @@ -178,6 +209,10 @@ jobs: tag: v${{ needs.check_version_changed.outputs.current_version }} env: COMMITTER_TOKEN: ${{ steps.generate-token.outputs.token }} + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }} cancel-in-progress: true diff --git a/.github/workflows/extension_tests.yml b/.github/workflows/extension_tests.yml index 53de373c1b79dc3ca9a3637642e10998c781580a..de9b4dc047a039c0f6af063c2a95fdecd70e8cba 100644 --- a/.github/workflows/extension_tests.yml +++ b/.github/workflows/extension_tests.yml @@ -9,7 +9,12 @@ env: RUSTUP_TOOLCHAIN: stable CARGO_BUILD_TARGET: wasm32-wasip2 on: - workflow_call: {} + workflow_call: + inputs: + working-directory: + description: working-directory + type: string + default: . jobs: orchestrate: if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') @@ -34,6 +39,14 @@ jobs: fi CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" "$GITHUB_SHA")" + # When running from a subdirectory, git diff returns repo-root-relative paths. + # Filter to only files within the current working directory and strip the prefix. + REPO_SUBDIR="$(git rev-parse --show-prefix)" + REPO_SUBDIR="${REPO_SUBDIR%/}" + if [ -n "$REPO_SUBDIR" ]; then + CHANGED_FILES="$(echo "$CHANGED_FILES" | grep "^${REPO_SUBDIR}/" | sed "s|^${REPO_SUBDIR}/||" || true)" + fi + check_pattern() { local output_name="$1" local pattern="$2" @@ -49,6 +62,10 @@ jobs: outputs: check_rust: ${{ steps.filter.outputs.check_rust }} check_extension: ${{ steps.filter.outputs.check_extension }} + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} check_rust: needs: - orchestrate @@ -66,17 +83,31 @@ jobs: path: ~/.rustup - name: extension_tests::install_rust_target run: rustup target add wasm32-wasip2 - - name: steps::cargo_fmt - run: cargo fmt --all -- --check + - id: get-package-name + name: extension_tests::get_package_name + run: | + PACKAGE_NAME="$(sed -n 's/^name = "\(.*\)"/\1/p' < Cargo.toml | head -1 | tr -d '[:space:]')" + echo "package_name=${PACKAGE_NAME}" >> "$GITHUB_OUTPUT" + - name: extension_tests::cargo_fmt_package + run: cargo fmt -p "$PACKAGE_NAME" -- --check + env: + PACKAGE_NAME: ${{ steps.get-package-name.outputs.package_name }} - name: extension_tests::run_clippy - run: cargo clippy --release --all-features -- --deny warnings + run: cargo clippy -p "$PACKAGE_NAME" --release --all-features -- --deny warnings + env: + PACKAGE_NAME: ${{ steps.get-package-name.outputs.package_name }} - name: steps::cargo_install_nextest uses: taiki-e/install-action@nextest - - name: steps::cargo_nextest - run: 'cargo nextest run --workspace --no-fail-fast --no-tests=warn --target "$(rustc -vV | sed -n ''s|host: ||p'')"' + - name: extension_tests::run_nextest + run: 'cargo nextest run -p "$PACKAGE_NAME" --no-fail-fast --no-tests=warn --target "$(rustc -vV | sed -n ''s|host: ||p'')"' env: + PACKAGE_NAME: ${{ steps.get-package-name.outputs.package_name }} NEXTEST_NO_TESTS: warn timeout-minutes: 6 + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} check_extension: needs: - orchestrate @@ -97,8 +128,8 @@ jobs: - name: extension_tests::download_zed_extension_cli if: steps.cache-zed-extension-cli.outputs.cache-hit != 'true' run: | - wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension" - chmod +x zed-extension + wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension" -O "$GITHUB_WORKSPACE/zed-extension" + chmod +x "$GITHUB_WORKSPACE/zed-extension" - name: steps::cache_rust_dependencies_namespace uses: namespacelabs/nscloud-cache-action@v1 with: @@ -108,7 +139,7 @@ jobs: run: | mkdir -p /tmp/ext-scratch mkdir -p /tmp/ext-output - ./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output + "$GITHUB_WORKSPACE/zed-extension" --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output - name: run_tests::fetch_ts_query_ls uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c with: @@ -117,8 +148,8 @@ jobs: file: ts_query_ls-x86_64-unknown-linux-gnu.tar.gz - name: run_tests::run_ts_query_ls run: |- - tar -xf ts_query_ls-x86_64-unknown-linux-gnu.tar.gz - ./ts_query_ls format --check . || { + tar -xf "$GITHUB_WORKSPACE/ts_query_ls-x86_64-unknown-linux-gnu.tar.gz" -C "$GITHUB_WORKSPACE" + "$GITHUB_WORKSPACE/ts_query_ls" format --check . || { echo "Found unformatted queries, please format them with ts_query_ls." echo "For easy use, install the Tree-sitter query extension:" echo "zed://extension/tree-sitter-query" @@ -132,8 +163,6 @@ jobs: if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then PR_FORK_POINT="$(git merge-base origin/main HEAD)" git checkout "$PR_FORK_POINT" - elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then - git checkout "$BRANCH_PARENT_SHA" else git checkout "$(git log -1 --format=%H)"~1 fi @@ -156,6 +185,10 @@ jobs: VERSION_CHANGED: ${{ steps.compare-versions-check.outputs.version_changed }} PR_USER_LOGIN: ${{ github.event.pull_request.user.login }} timeout-minutes: 6 + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} tests_pass: needs: - orchestrate @@ -183,6 +216,10 @@ jobs: RESULT_ORCHESTRATE: ${{ needs.orchestrate.result }} RESULT_CHECK_RUST: ${{ needs.check_rust.result }} RESULT_CHECK_EXTENSION: ${{ needs.check_extension.result }} + defaults: + run: + shell: bash -euxo pipefail {0} + working-directory: ${{ inputs.working-directory }} concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }} cancel-in-progress: true diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 00d69639a53868386157e67aeab5ce7383d32426..b1d8c1fff3c9f48e62f42fab05473d5f38aad2ce 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -147,8 +147,8 @@ jobs: file: ts_query_ls-x86_64-unknown-linux-gnu.tar.gz - name: run_tests::run_ts_query_ls run: |- - tar -xf ts_query_ls-x86_64-unknown-linux-gnu.tar.gz - ./ts_query_ls format --check . || { + tar -xf "$GITHUB_WORKSPACE/ts_query_ls-x86_64-unknown-linux-gnu.tar.gz" -C "$GITHUB_WORKSPACE" + "$GITHUB_WORKSPACE/ts_query_ls" format --check . || { echo "Found unformatted queries, please format them with ts_query_ls." echo "For easy use, install the Tree-sitter query extension:" echo "zed://extension/tree-sitter-query" diff --git a/tooling/xtask/src/tasks/workflows/extension_bump.rs b/tooling/xtask/src/tasks/workflows/extension_bump.rs index 8c31de202ee7ac81b5f5e95fb26ec89452fd077c..e31800e3ecd4a1039e7a1a191fffa735f64f84f2 100644 --- a/tooling/xtask/src/tasks/workflows/extension_bump.rs +++ b/tooling/xtask/src/tasks/workflows/extension_bump.rs @@ -5,8 +5,8 @@ use crate::tasks::workflows::{ extension_tests::{self}, runners, steps::{ - self, CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, FluentBuilder, NamedJob, - checkout_repo, dependant_job, named, + self, BASH_SHELL, CommonJobConditions, DEFAULT_REPOSITORY_OWNER_GUARD, FluentBuilder, + NamedJob, checkout_repo, dependant_job, named, }, vars::{ JobOutput, StepOutput, WorkflowInput, WorkflowSecret, one_workflow_per_non_main_branch, @@ -22,6 +22,7 @@ pub(crate) fn extension_bump() -> Workflow { // TODO: Ideally, this would have a default of `false`, but this is currently not // supported in gh-workflows let force_bump = WorkflowInput::bool("force-bump", None); + let working_directory = WorkflowInput::string("working-directory", Some(".".to_owned())); let (app_id, app_secret) = extension_workflow_secrets(); let (check_version_changed, version_changed, current_version) = check_version_changed(); @@ -59,6 +60,7 @@ pub(crate) fn extension_bump() -> Workflow { WorkflowCall::default() .add_input(bump_type.name, bump_type.call_input()) .add_input(force_bump.name, force_bump.call_input()) + .add_input(working_directory.name, working_directory.call_input()) .secrets([ (app_id.name.to_owned(), app_id.secret_configuration()), ( @@ -82,10 +84,19 @@ pub(crate) fn extension_bump() -> Workflow { .add_job(trigger_release.name, trigger_release.job) } +fn extension_job_defaults() -> Defaults { + Defaults::default().run( + RunDefaults::default() + .shell(BASH_SHELL) + .working_directory("${{ inputs.working-directory }}"), + ) +} + fn check_version_changed() -> (NamedJob, StepOutput, StepOutput) { let (compare_versions, version_changed, current_version) = compare_versions(); let job = Job::default() + .defaults(extension_job_defaults()) .with_repository_owner_guard() .outputs([ (version_changed.name.to_owned(), version_changed.to_string()), @@ -112,6 +123,7 @@ fn create_version_label( let (generate_token, generated_token) = generate_token(&app_id.to_string(), &app_secret.to_string(), None); let job = steps::dependant_job(dependencies) + .defaults(extension_job_defaults()) .cond(Expression::new(format!( "{DEFAULT_REPOSITORY_OWNER_GUARD} && github.event_name == 'push' && \ github.ref == 'refs/heads/main' && {version_changed} == 'true'", @@ -153,8 +165,6 @@ pub(crate) fn compare_versions() -> (Step, StepOutput, StepOutput) { if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then PR_FORK_POINT="$(git merge-base origin/main HEAD)" git checkout "$PR_FORK_POINT" - elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then - git checkout "$BRANCH_PARENT_SHA" else git checkout "$(git log -1 --format=%H)"~1 fi @@ -187,9 +197,11 @@ fn bump_extension_version( ) -> NamedJob { let (generate_token, generated_token) = generate_token(&app_id.to_string(), &app_secret.to_string(), None); - let (bump_version, new_version) = bump_version(current_version, bump_type); + let (bump_version, _new_version, title, body, branch_name) = + bump_version(current_version, bump_type); let job = steps::dependant_job(dependencies) + .defaults(extension_job_defaults()) .cond(Expression::new(format!( "{DEFAULT_REPOSITORY_OWNER_GUARD} &&\n({force_bump} == true || {version_changed} == 'false')", force_bump = force_bump_output.expr(), @@ -201,7 +213,12 @@ fn bump_extension_version( .add_step(steps::checkout_repo()) .add_step(install_bump_2_version()) .add_step(bump_version) - .add_step(create_pull_request(new_version, generated_token)); + .add_step(create_pull_request( + title, + body, + generated_token, + branch_name, + )); named::job(job) } @@ -256,7 +273,10 @@ fn install_bump_2_version() -> Step { ) } -fn bump_version(current_version: &JobOutput, bump_type: &WorkflowInput) -> (Step, StepOutput) { +fn bump_version( + current_version: &JobOutput, + bump_type: &WorkflowInput, +) -> (Step, StepOutput, StepOutput, StepOutput, StepOutput) { let step = named::bash(formatdoc! {r#" BUMP_FILES=("extension.toml") if [[ -f "Cargo.toml" ]]; then @@ -274,33 +294,50 @@ fn bump_version(current_version: &JobOutput, bump_type: &WorkflowInput) -> (Step fi NEW_VERSION="$({VERSION_CHECK})" + EXTENSION_ID="$(sed -n 's/^id = "\(.*\)"/\1/p' < extension.toml | head -1 | tr -d '[:space:]')" + EXTENSION_NAME="$(sed -n 's/^name = "\(.*\)"/\1/p' < extension.toml | head -1 | tr -d '[:space:]')" + + if [[ "$WORKING_DIR" == "." || -z "$WORKING_DIR" ]]; then + {{ + echo "title=Bump version to ${{NEW_VERSION}}"; + echo "body=This PR bumps the version of this extension to v${{NEW_VERSION}}"; + echo "branch_name=zed-zippy-autobump"; + }} >> "$GITHUB_OUTPUT" + else + {{ + echo "title=${{EXTENSION_ID}}: Bump to v${{NEW_VERSION}}"; + echo "body=This PR bumps the version of the ${{EXTENSION_NAME}} extension to v${{NEW_VERSION}}"; + echo "branch_name=zed-zippy-${{EXTENSION_ID}}-autobump"; + }} >> "$GITHUB_OUTPUT" + fi echo "new_version=${{NEW_VERSION}}" >> "$GITHUB_OUTPUT" "# }) .id("bump-version") .add_env(("OLD_VERSION", current_version.to_string())) - .add_env(("BUMP_TYPE", bump_type.to_string())); + .add_env(("BUMP_TYPE", bump_type.to_string())) + .add_env(("WORKING_DIR", "${{ inputs.working-directory }}")); let new_version = StepOutput::new(&step, "new_version"); - (step, new_version) + let title = StepOutput::new(&step, "title"); + let body = StepOutput::new(&step, "body"); + let branch_name = StepOutput::new(&step, "branch_name"); + (step, new_version, title, body, branch_name) } -fn create_pull_request(new_version: StepOutput, generated_token: StepOutput) -> Step { - let formatted_version = format!("v{new_version}"); - +fn create_pull_request( + title: StepOutput, + body: StepOutput, + generated_token: StepOutput, + branch_name: StepOutput, +) -> Step { named::uses("peter-evans", "create-pull-request", "v7").with( Input::default() - .add("title", format!("Bump version to {new_version}")) - .add( - "body", - format!("This PR bumps the version of this extension to {formatted_version}",), - ) - .add( - "commit-message", - format!("Bump version to {formatted_version}"), - ) - .add("branch", "zed-zippy-autobump") + .add("title", title.to_string()) + .add("body", body.to_string()) + .add("commit-message", title.to_string()) + .add("branch", branch_name.to_string()) .add( "committer", "zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>", @@ -328,6 +365,7 @@ fn trigger_release( let (get_extension_id, extension_id) = get_extension_id(); let job = dependant_job(dependencies) + .defaults(extension_job_defaults()) .with_repository_owner_guard() .runs_on(runners::LINUX_SMALL) .add_step(generate_token) diff --git a/tooling/xtask/src/tasks/workflows/extension_tests.rs b/tooling/xtask/src/tasks/workflows/extension_tests.rs index 09f0cadf1c8731f8eed4ef1197a7edd05e0d1558..a50db3f98bf7bec887ea69f841f547ad717976f9 100644 --- a/tooling/xtask/src/tasks/workflows/extension_tests.rs +++ b/tooling/xtask/src/tasks/workflows/extension_tests.rs @@ -3,15 +3,13 @@ use indoc::indoc; use crate::tasks::workflows::{ extension_bump::compare_versions, - run_tests::{ - fetch_ts_query_ls, orchestrate_without_package_filter, run_ts_query_ls, tests_pass, - }, + run_tests::{fetch_ts_query_ls, orchestrate_for_extension, run_ts_query_ls, tests_pass}, runners, steps::{ - self, CommonJobConditions, FluentBuilder, NamedJob, cache_rust_dependencies_namespace, - named, + self, BASH_SHELL, CommonJobConditions, FluentBuilder, NamedJob, + cache_rust_dependencies_namespace, named, }, - vars::{PathCondition, StepOutput, one_workflow_per_non_main_branch}, + vars::{PathCondition, StepOutput, WorkflowInput, one_workflow_per_non_main_branch}, }; pub(crate) const ZED_EXTENSION_CLI_SHA: &str = "03d8e9aee95ea6117d75a48bcac2e19241f6e667"; @@ -25,8 +23,10 @@ pub(crate) fn extension_tests() -> Workflow { let should_check_extension = PathCondition::new("check_extension", r"^(extension\.toml|.*\.scm)$"); - let orchestrate = - orchestrate_without_package_filter(&[&should_check_rust, &should_check_extension]); + let orchestrate = with_extension_defaults(orchestrate_for_extension(&[ + &should_check_rust, + &should_check_extension, + ])); let jobs = [ orchestrate, @@ -34,10 +34,17 @@ pub(crate) fn extension_tests() -> Workflow { should_check_extension.guard(check_extension()), ]; - let tests_pass = tests_pass(&jobs); + let tests_pass = with_extension_defaults(tests_pass(&jobs)); + + let working_directory = WorkflowInput::string("working-directory", Some(".".to_owned())); named::workflow() - .add_event(Event::default().workflow_call(WorkflowCall::default())) + .add_event( + Event::default().workflow_call( + WorkflowCall::default() + .add_input(working_directory.name, working_directory.call_input()), + ), + ) .concurrency(one_workflow_per_non_main_branch()) .add_env(("CARGO_TERM_COLOR", "always")) .add_env(("RUST_BACKTRACE", 1)) @@ -58,27 +65,66 @@ fn install_rust_target() -> Step { named::bash(format!("rustup target add {EXTENSION_RUST_TARGET}",)) } -fn run_clippy() -> Step { - named::bash("cargo clippy --release --all-features -- --deny warnings") +fn get_package_name() -> (Step, StepOutput) { + let step = named::bash(indoc! {r#" + PACKAGE_NAME="$(sed -n 's/^name = "\(.*\)"/\1/p' < Cargo.toml | head -1 | tr -d '[:space:]')" + echo "package_name=${PACKAGE_NAME}" >> "$GITHUB_OUTPUT" + "#}) + .id("get-package-name"); + + let output = StepOutput::new(&step, "package_name"); + (step, output) +} + +fn cargo_fmt_package(package_name: &StepOutput) -> Step { + named::bash(r#"cargo fmt -p "$PACKAGE_NAME" -- --check"#) + .add_env(("PACKAGE_NAME", package_name.to_string())) +} + +fn run_clippy(package_name: &StepOutput) -> Step { + named::bash(r#"cargo clippy -p "$PACKAGE_NAME" --release --all-features -- --deny warnings"#) + .add_env(("PACKAGE_NAME", package_name.to_string())) +} + +fn run_nextest(package_name: &StepOutput) -> Step { + named::bash( + r#"cargo nextest run -p "$PACKAGE_NAME" --no-fail-fast --no-tests=warn --target "$(rustc -vV | sed -n 's|host: ||p')""#, + ) + .add_env(("PACKAGE_NAME", package_name.to_string())) + .add_env(("NEXTEST_NO_TESTS", "warn")) +} + +fn extension_job_defaults() -> Defaults { + Defaults::default().run( + RunDefaults::default() + .shell(BASH_SHELL) + .working_directory("${{ inputs.working-directory }}"), + ) +} + +fn with_extension_defaults(named_job: NamedJob) -> NamedJob { + NamedJob { + name: named_job.name, + job: named_job.job.defaults(extension_job_defaults()), + } } fn check_rust() -> NamedJob { + let (get_package, package_name) = get_package_name(); + let job = Job::default() + .defaults(extension_job_defaults()) .with_repository_owner_guard() .runs_on(runners::LINUX_LARGE_RAM) .timeout_minutes(6u32) .add_step(steps::checkout_repo()) .add_step(steps::cache_rust_dependencies_namespace()) .add_step(install_rust_target()) - .add_step(steps::cargo_fmt()) - .add_step(run_clippy()) + .add_step(get_package) + .add_step(cargo_fmt_package(&package_name)) + .add_step(run_clippy(&package_name)) .add_step(steps::cargo_install_nextest()) - .add_step( - steps::cargo_nextest(runners::Platform::Linux) - // Set the target to the current platform again - .with_target("$(rustc -vV | sed -n 's|host: ||p')") - .add_env(("NEXTEST_NO_TESTS", "warn")), - ); + .add_step(run_nextest(&package_name)); named::job(job) } @@ -88,6 +134,7 @@ pub(crate) fn check_extension() -> NamedJob { let (check_version_job, version_changed, _) = compare_versions(); let job = Job::default() + .defaults(extension_job_defaults()) .with_repository_owner_guard() .runs_on(runners::LINUX_LARGE_RAM) .timeout_minutes(6u32) @@ -124,8 +171,8 @@ pub fn download_zed_extension_cli(cache_hit: StepOutput) -> Step { named::bash( indoc! { r#" - wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension" - chmod +x zed-extension + wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension" -O "$GITHUB_WORKSPACE/zed-extension" + chmod +x "$GITHUB_WORKSPACE/zed-extension" "#, } ).if_condition(Expression::new(format!("{} != 'true'", cache_hit.expr()))) @@ -136,7 +183,7 @@ pub fn check() -> Step { r#" mkdir -p /tmp/ext-scratch mkdir -p /tmp/ext-output - ./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output + "$GITHUB_WORKSPACE/zed-extension" --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output "# }) } diff --git a/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs b/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs index 4e247fe16ca7b97638488c218684889c39cfcfa8..a62bb107da5228cd3ba620e47ab77dc673974696 100644 --- a/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs +++ b/tooling/xtask/src/tasks/workflows/extension_workflow_rollout.rs @@ -127,8 +127,9 @@ fn fetch_extension_repos(filter_repos_input: &WorkflowInput) -> (NamedJob, JobOu .id("calc-changes") .add_env(("PREV_COMMIT", prev_commit.to_string())); - let removed_ci = StepOutput::new(&step, "removed_ci"); - let removed_shared = StepOutput::new(&step, "removed_shared"); + // These are created in the for-loop above and thus do exist + let removed_ci = StepOutput::new_unchecked(&step, "removed_ci"); + let removed_shared = StepOutput::new_unchecked(&step, "removed_shared"); (step, removed_ci, removed_shared) } diff --git a/tooling/xtask/src/tasks/workflows/run_tests.rs b/tooling/xtask/src/tasks/workflows/run_tests.rs index 38ba1bd32945f9ba8ee1e08ebc994a1132fb07f2..f134fa166d6dfe2ef00e47516e33d658a71badd9 100644 --- a/tooling/xtask/src/tasks/workflows/run_tests.rs +++ b/tooling/xtask/src/tasks/workflows/run_tests.rs @@ -97,14 +97,18 @@ pub(crate) fn run_tests() -> Workflow { // Generates a bash script that checks changed files against regex patterns // and sets GitHub output variables accordingly pub fn orchestrate(rules: &[&PathCondition]) -> NamedJob { - orchestrate_impl(rules, true) + orchestrate_impl(rules, true, false) } -pub fn orchestrate_without_package_filter(rules: &[&PathCondition]) -> NamedJob { - orchestrate_impl(rules, false) +pub fn orchestrate_for_extension(rules: &[&PathCondition]) -> NamedJob { + orchestrate_impl(rules, false, true) } -fn orchestrate_impl(rules: &[&PathCondition], include_package_filter: bool) -> NamedJob { +fn orchestrate_impl( + rules: &[&PathCondition], + include_package_filter: bool, + filter_by_working_directory: bool, +) -> NamedJob { let name = "orchestrate".to_owned(); let step_name = "filter".to_owned(); let mut script = String::new(); @@ -121,6 +125,22 @@ fn orchestrate_impl(rules: &[&PathCondition], include_package_filter: bool) -> N fi CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" "$GITHUB_SHA")" + "#}); + + if filter_by_working_directory { + script.push_str(indoc::indoc! {r#" + # When running from a subdirectory, git diff returns repo-root-relative paths. + # Filter to only files within the current working directory and strip the prefix. + REPO_SUBDIR="$(git rev-parse --show-prefix)" + REPO_SUBDIR="${REPO_SUBDIR%/}" + if [ -n "$REPO_SUBDIR" ]; then + CHANGED_FILES="$(echo "$CHANGED_FILES" | grep "^${REPO_SUBDIR}/" | sed "s|^${REPO_SUBDIR}/||" || true)" + fi + + "#}); + } + + script.push_str(indoc::indoc! {r#" check_pattern() { local output_name="$1" local pattern="$2" @@ -298,8 +318,8 @@ pub(crate) fn fetch_ts_query_ls() -> Step { pub(crate) fn run_ts_query_ls() -> Step { named::bash(formatdoc!( - r#"tar -xf {TS_QUERY_LS_FILE} - ./ts_query_ls format --check . || {{ + r#"tar -xf "$GITHUB_WORKSPACE/{TS_QUERY_LS_FILE}" -C "$GITHUB_WORKSPACE" + "$GITHUB_WORKSPACE/ts_query_ls" format --check . || {{ echo "Found unformatted queries, please format them with ts_query_ls." echo "For easy use, install the Tree-sitter query extension:" echo "zed://extension/tree-sitter-query" diff --git a/tooling/xtask/src/tasks/workflows/steps.rs b/tooling/xtask/src/tasks/workflows/steps.rs index 6bede217b74a1172db712b92ed3d50cd2af603b2..fbe7ef66a331e2e7b84c1b4be7af3482f2b1ce95 100644 --- a/tooling/xtask/src/tasks/workflows/steps.rs +++ b/tooling/xtask/src/tasks/workflows/steps.rs @@ -10,7 +10,7 @@ pub(crate) fn use_clang(job: Job) -> Job { const SCCACHE_R2_BUCKET: &str = "sccache-zed"; -const BASH_SHELL: &str = "bash -euxo pipefail {0}"; +pub(crate) const BASH_SHELL: &str = "bash -euxo pipefail {0}"; // https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsshell pub const PWSH_SHELL: &str = "pwsh"; @@ -24,13 +24,6 @@ pub(crate) fn cargo_nextest(platform: Platform) -> Nextest { } impl Nextest { - pub(crate) fn with_target(mut self, target: &str) -> Step { - if let Some(nextest_command) = self.0.value.run.as_mut() { - nextest_command.push_str(&format!(r#" --target "{target}""#)); - } - self.into() - } - #[allow(dead_code)] pub(crate) fn with_filter_expr(mut self, filter_expr: &str) -> Self { if let Some(nextest_command) = self.0.value.run.as_mut() { diff --git a/tooling/xtask/src/tasks/workflows/vars.rs b/tooling/xtask/src/tasks/workflows/vars.rs index aa8fb0a4056a53807cd4b2f12f331cb9d4d0a235..b3f8bdf56e9bb0f93f81992fbc61dab2b9754e63 100644 --- a/tooling/xtask/src/tasks/workflows/vars.rs +++ b/tooling/xtask/src/tasks/workflows/vars.rs @@ -156,14 +156,31 @@ pub(crate) struct StepOutput { impl StepOutput { pub fn new(step: &Step, name: &'static str) -> Self { - Self { - name, - step_id: step - .value - .id - .clone() - .expect("Steps that produce outputs must have an ID"), - } + let step_id = step + .value + .id + .clone() + .expect("Steps that produce outputs must have an ID"); + + assert!( + step.value + .run + .as_ref() + .is_none_or(|run_command| run_command.contains(name)), + "Step Output name {name} must occur at least once in run command with ID {step_id}!" + ); + + Self { name, step_id } + } + + pub fn new_unchecked(step: &Step, name: &'static str) -> Self { + let step_id = step + .value + .id + .clone() + .expect("Steps that produce outputs must have an ID"); + + Self { name, step_id } } pub fn expr(&self) -> String {