diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a814719c..ebe36802 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ env: RUBY_VERSION: "3.4" LUA_VERSION: "5.4" LUAROCKS_VERSION: "3.12.2" + DART_VERSION: "3.10.1" jobs: lint: @@ -221,6 +222,11 @@ jobs: with: ruby-version: ${{ env.RUBY_VERSION }} + - name: "Install Dart" + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 # v1.6.5 + with: + sdk: ${{ env.DART_VERSION }} + - name: "Cargo test" run: | cargo llvm-cov nextest \ @@ -301,6 +307,11 @@ jobs: with: ruby-version: ${{ env.RUBY_VERSION }} + - name: "Install Dart" + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 # v1.6.5 + with: + sdk: ${{ env.DART_VERSION }} + - name: "Cargo test" run: | cargo nextest show-config test-groups @@ -392,6 +403,11 @@ jobs: with: ruby-version: ${{ env.RUBY_VERSION }} + - name: "Install Dart" + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 # v1.6.5 + with: + sdk: ${{ env.DART_VERSION }} + - name: "Cargo test" working-directory: ${{ env.PREK_WORKSPACE }} run: | diff --git a/crates/prek-consts/src/env_vars.rs b/crates/prek-consts/src/env_vars.rs index 4b1f273e..982c8e8d 100644 --- a/crates/prek-consts/src/env_vars.rs +++ b/crates/prek-consts/src/env_vars.rs @@ -57,6 +57,9 @@ impl EnvVars { pub const LUA_PATH: &'static str = "LUA_PATH"; pub const LUA_CPATH: &'static str = "LUA_CPATH"; + // Dart related + pub const PUB_CACHE: &'static str = "PUB_CACHE"; + // Ruby related pub const GEM_HOME: &'static str = "GEM_HOME"; pub const GEM_PATH: &'static str = "GEM_PATH"; diff --git a/docs/todo.md b/docs/todo.md index b9b71dcc..35773271 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -16,6 +16,7 @@ The original pre-commit supports hooks written in 10+ languages. The table below | node | ✅ Supported | — | | | golang | ✅ Supported | — | | | lua | ✅ Supported | — | | +| dart | ✅ Supported | — | | | system | ✅ Supported | — | | | script | ✅ Supported | — | | | pygrep | ✅ Supported | — | | @@ -27,7 +28,6 @@ The original pre-commit supports hooks written in 10+ languages. The table below | rust | 🚧 Planned | [#44](https://github.com/j178/prek/issues/44) | | | conda | 🚧 Planned | [#52](https://github.com/j178/prek/issues/52) | | | coursier | 🚧 Planned | [#53](https://github.com/j178/prek/issues/53) | | -| dart | 🚧 Planned | [#51](https://github.com/j178/prek/issues/51) | | | dotnet | 🚧 Planned | [#48](https://github.com/j178/prek/issues/48) | | | haskell | 🚧 Planned | — | | | julia | 🚧 Planned | — | | diff --git a/src/languages/dart.rs b/src/languages/dart.rs new file mode 100644 index 00000000..8559b6eb --- /dev/null +++ b/src/languages/dart.rs @@ -0,0 +1,335 @@ +use std::fmt::Write as _; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use anyhow::{Context, Result}; +use prek_consts::env_vars::EnvVars; +use semver::Version; +use tracing::debug; + +use crate::cli::reporter::HookInstallReporter; +use crate::hook::{Hook, InstallInfo, InstalledHook}; +use crate::languages::LanguageImpl; +use crate::process::Cmd; +use crate::run::{prepend_paths, run_by_batch}; +use crate::store::Store; + +#[derive(Debug, Copy, Clone)] +pub(crate) struct Dart; + +pub(crate) struct DartInfo { + pub(crate) version: Version, + pub(crate) executable: PathBuf, +} + +pub(crate) async fn query_dart_info() -> Result { + let executable = which::which("dart") + .context("Failed to locate dart executable. Is Dart installed and available in PATH?")?; + debug!("Found dart executable at: {}", executable.display()); + + // Use the executable path we found, not just "dart" + let stdout = Cmd::new(&executable, "get dart version") + .arg("--version") + .check(true) + .output() + .await? + .stdout; + + // Parse output like "Dart SDK version: 3.0.0 (stable)" + // Handle Flutter SDK which may output extra lines before the version + let version = str::from_utf8(&stdout) + .context("Failed to parse `dart --version` output as UTF-8")? + .lines() + .find(|line| line.contains("Dart SDK version:")) + .and_then(|line| line.split_whitespace().nth(3)) + .context("Failed to extract Dart version from output")? + .trim(); + + let version = Version::parse(version).context("Failed to parse Dart version")?; + + Ok(DartInfo { + version, + executable, + }) +} + +impl LanguageImpl for Dart { + async fn install( + &self, + hook: Arc, + store: &Store, + reporter: &HookInstallReporter, + ) -> Result { + let progress = reporter.on_install_start(&hook); + + let mut info = InstallInfo::new( + hook.language, + hook.dependencies().clone(), + &store.hooks_dir(), + )?; + + debug!(%hook, target = %info.env_path.display(), "Installing Dart environment"); + + // Check dart is installed. + let dart_info = query_dart_info() + .await + .context("Failed to query Dart info")?; + + // Install dependencies and compile executables from pubspec.yaml. + // For remote hooks: use repo_path (cloned repository) + // For local hooks: use work_dir (user's repository) + let pubspec_source = if let Some(repo_path) = hook.repo_path() { + if Self::has_pubspec(repo_path) { + Some(repo_path) + } else { + None + } + } else if Self::has_pubspec(hook.work_dir()) { + // Local hook with pubspec.yaml in work_dir + Some(hook.work_dir()) + } else { + None + }; + + if let Some(source_path) = pubspec_source { + Self::install_from_pubspec(&dart_info.executable, &info.env_path, source_path).await?; + } + + // Install additional dependencies by creating a pubspec.yaml + if !hook.additional_dependencies.is_empty() { + Self::install_additional_dependencies( + &dart_info.executable, + &info.env_path, + &hook.additional_dependencies, + ) + .await?; + } + + info.with_toolchain(dart_info.executable) + .with_language_version(dart_info.version); + + info.persist_env_path(); + + reporter.on_install_complete(progress); + + Ok(InstalledHook::Installed { + hook, + info: Arc::new(info), + }) + } + + async fn check_health(&self, info: &InstallInfo) -> Result<()> { + let current_dart_info = query_dart_info() + .await + .context("Failed to query current Dart info")?; + + if current_dart_info.version != info.language_version { + anyhow::bail!( + "Dart version mismatch: expected `{}`, found `{}`", + info.language_version, + current_dart_info.version + ); + } + + if current_dart_info.executable != info.toolchain { + anyhow::bail!( + "Dart executable mismatch: expected `{}`, found `{}`", + info.toolchain.display(), + current_dart_info.executable.display() + ); + } + + Ok(()) + } + + async fn run( + &self, + hook: &InstalledHook, + filenames: &[&Path], + _store: &Store, + ) -> Result<(i32, Vec)> { + let env_dir = hook.env_path().expect("Dart must have env path"); + let new_path = prepend_paths(&[&env_dir.join("bin")]).context("Failed to join PATH")?; + let entry = hook.entry.resolve(Some(&new_path))?; + + Self::setup_package_config(env_dir, hook.work_dir())?; + + let run = async |batch: &[&Path]| { + let mut output = Cmd::new(&entry[0], "run dart command") + .current_dir(hook.work_dir()) + .args(&entry[1..]) + .env(EnvVars::PATH, &new_path) + .env(EnvVars::PUB_CACHE, env_dir) + .args(&hook.args) + .args(batch) + .check(false) + .pty_output() + .await?; + + output.stdout.extend(output.stderr); + let code = output.status.code().unwrap_or(1); + anyhow::Ok((code, output.stdout)) + }; + + let results = run_by_batch(hook, filenames, &entry, run).await?; + + let mut combined_status = 0; + let mut combined_output = Vec::new(); + + for (code, output) in results { + combined_status |= code; + combined_output.extend(output); + } + + Ok((combined_status, combined_output)) + } +} + +impl Dart { + fn setup_package_config(env_dir: &Path, work_dir: &Path) -> Result<()> { + let env_package_config = env_dir.join(".dart_tool").join("package_config.json"); + if env_package_config.exists() { + let work_dart_tool = work_dir.join(".dart_tool"); + fs_err::create_dir_all(&work_dart_tool) + .context("Failed to create .dart_tool directory in work_dir")?; + let work_package_config = work_dart_tool.join("package_config.json"); + fs_err::copy(&env_package_config, &work_package_config) + .context("Failed to copy package_config.json to work_dir")?; + } + Ok(()) + } + + async fn compile_executables( + dart: &Path, + pubspec_path: &Path, + bin_src_dir: &Path, + bin_out_dir: &Path, + pub_cache: &Path, + ) -> Result<()> { + // Read pubspec.yaml to get executables + let pubspec_content = + fs_err::read_to_string(pubspec_path).context("Failed to read pubspec.yaml")?; + let pubspec: serde_yaml::Value = + serde_yaml::from_str(&pubspec_content).context("Failed to parse pubspec.yaml")?; + + // Get executables section + let Some(executables) = pubspec.get("executables") else { + // No executables defined, nothing to compile + return Ok(()); + }; + + let Some(executables_map) = executables.as_mapping() else { + anyhow::bail!("pubspec.yaml executables must be a mapping"); + }; + + // Ensure bin output directory exists + fs_err::create_dir_all(bin_out_dir).context("Failed to create bin output directory")?; + + // Compile each executable + for (name, _value) in executables_map { + let Some(exe_name) = name.as_str() else { + continue; + }; + + let source_file = bin_src_dir.join(format!("{exe_name}.dart")); + if !source_file.exists() { + debug!("Skipping executable '{exe_name}': source file not found"); + continue; + } + + // Output path - on Windows this will be .exe, on Unix it's just the name + let output_path = if cfg!(windows) { + bin_out_dir.join(format!("{exe_name}.exe")) + } else { + bin_out_dir.join(exe_name) + }; + + debug!( + "Compiling executable '{exe_name}': {} -> {}", + source_file.display(), + output_path.display() + ); + + Cmd::new(dart, "dart compile exe") + .arg("compile") + .arg("exe") + .arg(&source_file) + .arg("--output") + .arg(&output_path) + .env(EnvVars::PUB_CACHE, pub_cache) + .check(true) + .output() + .await + .context(format!("Failed to compile executable '{exe_name}'"))?; + } + + Ok(()) + } + + async fn install_from_pubspec(dart: &Path, env_path: &Path, repo_path: &Path) -> Result<()> { + // Run `dart pub get` to install dependencies from pubspec.yaml + Cmd::new(dart, "dart pub get") + .current_dir(repo_path) + .env(EnvVars::PUB_CACHE, env_path) + .arg("pub") + .arg("get") + .check(true) + .output() + .await + .context("Failed to run dart pub get")?; + + // Compile executables to env_path/bin + let pubspec_path = repo_path.join("pubspec.yaml"); + let bin_src_dir = repo_path.join("bin"); + let bin_out_dir = env_path.join("bin"); + + Self::compile_executables(dart, &pubspec_path, &bin_src_dir, &bin_out_dir, env_path) + .await?; + + Ok(()) + } + + async fn install_additional_dependencies( + dart: &Path, + env_path: &Path, + dependencies: &rustc_hash::FxHashSet, + ) -> Result<()> { + // Create a minimal pubspec.yaml with the additional dependencies + let mut pubspec_content = indoc::formatdoc! {" + name: prek_dart_env + environment: + sdk: '>=2.12.0 <4.0.0' + dependencies: + "}; + + for dep in dependencies { + // Parse dependency - format is "package" or "package:version" + if let Some((package, version)) = dep.split_once(':') { + writeln!(pubspec_content, " {package}: {version}")?; + } else { + writeln!(pubspec_content, " {dep}: any")?; + } + } + + // Write pubspec.yaml to env_path + let pubspec_path = env_path.join("pubspec.yaml"); + fs_err::tokio::write(&pubspec_path, pubspec_content).await?; + + // Run `dart pub get` to resolve and install dependencies + Cmd::new(dart, "dart pub get") + .current_dir(env_path) + .env(EnvVars::PUB_CACHE, env_path) + .arg("pub") + .arg("get") + .check(true) + .output() + .await + .context("Failed to run dart pub get for additional dependencies")?; + + Ok(()) + } + + fn has_pubspec(repo_path: &Path) -> bool { + repo_path.join("pubspec.yaml").exists() + } +} diff --git a/src/languages/mod.rs b/src/languages/mod.rs index ccaef9aa..52f85185 100644 --- a/src/languages/mod.rs +++ b/src/languages/mod.rs @@ -18,6 +18,7 @@ use crate::identify::parse_shebang; use crate::store::Store; use crate::{archive, hooks, warn_user_once}; +mod dart; mod docker; mod docker_image; mod fail; @@ -31,6 +32,7 @@ mod script; mod system; pub mod version; +static DART: dart::Dart = dart::Dart; static GOLANG: golang::Golang = golang::Golang; static PYTHON: python::Python = python::Python; static NODE: node::Node = node::Node; @@ -118,7 +120,8 @@ impl Language { pub fn supported(lang: Language) -> bool { matches!( lang, - Self::Golang + Self::Dart + | Self::Golang | Self::Python | Self::Node | Self::Ruby @@ -174,6 +177,7 @@ impl Language { reporter: &HookInstallReporter, ) -> Result { match self { + Self::Dart => DART.install(hook, store, reporter).await, Self::Golang => GOLANG.install(hook, store, reporter).await, Self::Python => PYTHON.install(hook, store, reporter).await, Self::Node => NODE.install(hook, store, reporter).await, @@ -191,6 +195,7 @@ impl Language { pub async fn check_health(&self, info: &InstallInfo) -> Result<()> { match self { + Self::Dart => DART.check_health(info).await, Self::Golang => GOLANG.check_health(info).await, Self::Python => PYTHON.check_health(info).await, Self::Node => NODE.check_health(info).await, @@ -236,6 +241,7 @@ impl Language { } match self { + Self::Dart => DART.run(hook, filenames, store).await, Self::Golang => GOLANG.run(hook, filenames, store).await, Self::Python => PYTHON.run(hook, filenames, store).await, Self::Node => NODE.run(hook, filenames, store).await, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 95d3d034..f0b4246d 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -383,6 +383,8 @@ pub const INSTA_FILTERS: &[(&str, &str)] = &[ ), // Time seconds (r"\b(\d+\.)?\d+(ms|s)\b", "[TIME]"), + // Dart SDK version output (handles Flutter SDK too) + (r"Dart SDK version: .*", "Dart SDK version: [VERSION]"), // Windows shebang interpreter (r"#!/bin/sh", "#!/usr/bin/env bash"), ]; diff --git a/tests/languages/dart.rs b/tests/languages/dart.rs new file mode 100644 index 00000000..e18d38b5 --- /dev/null +++ b/tests/languages/dart.rs @@ -0,0 +1,497 @@ +#![allow(clippy::needless_raw_string_hashes)] + +use assert_fs::fixture::{FileWriteStr, PathChild}; + +use crate::common::{TestContext, cmd_snapshot}; + +#[test] +fn health_check() { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r#" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: dart --version + always_run: true + verbose: true + pass_filenames: false + "#}); + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + Dart SDK version: [VERSION] + + ----- stderr ----- + "); + + // Run again to check `health_check` works correctly. + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + Dart SDK version: [VERSION] + + ----- stderr ----- + "); +} + +/// Test specifying `language_version` for Dart hooks which is not supported. +#[test] +fn language_version() { + let context = TestContext::new(); + context.init_project(); + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: local + hooks: + - id: local + name: local + language: dart + entry: dart --version + language_version: '3.0' + always_run: true + verbose: true + pass_filenames: false + "}); + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to init hooks + caused by: Invalid hook `local` + caused by: Hook specified `language_version: 3.0` but the language `dart` does not support toolchain installation for now + "); +} + +/// Test that stderr from hooks is captured and shown to the user. +#[test] +fn hook_stderr() -> anyhow::Result<()> { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: local + hooks: + - id: local + name: local + language: dart + entry: dart ./hook.dart + "}); + + context + .work_dir() + .child("hook.dart") + .write_str(indoc::indoc! {r#" + import 'dart:io'; + void main() { + stderr.writeln('Error from Dart hook'); + exit(1); + } + "#})?; + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: false + exit_code: 1 + ----- stdout ----- + local....................................................................Failed + - hook id: local + - exit code: 1 + + Error from Dart hook + + ----- stderr ----- + "); + + Ok(()) +} + +/// Test Dart script execution with file arguments. +#[test] +fn script_with_files() -> anyhow::Result<()> { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: dart ./script.dart + verbose: true + "}); + + context + .work_dir() + .child("script.dart") + .write_str(indoc::indoc! {r#" + import 'dart:io'; + void main(List args) { + for (var arg in args) { + print('Processing file: $arg'); + } + } + "#})?; + + context + .work_dir() + .child("test1.dart") + .write_str("void main() { print('test1'); }")?; + + context + .work_dir() + .child("test2.dart") + .write_str("void main() { print('test2'); }")?; + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + Processing file: script.dart + Processing file: .pre-commit-config.yaml + Processing file: test2.dart + Processing file: test1.dart + + ----- stderr ----- + "); + + Ok(()) +} + +/// Test Dart hook with pubspec.yaml and dependencies (from pre-commit test suite). +/// Verifies that executables defined in pubspec.yaml are compiled to `env_path/bin`. +#[test] +fn with_pubspec_and_dependencies() -> anyhow::Result<()> { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: hello-world-dart + always_run: true + verbose: true + pass_filenames: false + "}); + + context + .work_dir() + .child("pubspec.yaml") + .write_str(indoc::indoc! {r#" + environment: + sdk: '>=2.17.0 <4.0.0' + + name: hello_world_dart + + executables: + hello-world-dart: + + dependencies: + ansicolor: ^2.0.1 + "#})?; + + std::fs::create_dir(context.work_dir().join("bin"))?; + context + .work_dir() + .child("bin") + .child("hello-world-dart.dart") + .write_str(indoc::indoc! {r#" + import 'package:ansicolor/ansicolor.dart'; + + void main() { + AnsiPen pen = new AnsiPen()..red(); + print("hello hello " + pen("world")); + } + "#})?; + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + hello hello world + + ----- stderr ----- + "); + + Ok(()) +} + +/// Test Dart hook with pubspec.yaml in repository. +#[test] +fn with_pubspec() -> anyhow::Result<()> { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: dart ./bin/hello.dart + always_run: true + verbose: true + pass_filenames: false + "}); + + context + .work_dir() + .child("pubspec.yaml") + .write_str(indoc::indoc! {r#" + name: test_package + description: A test package + version: 1.0.0 + environment: + sdk: '>=2.17.0 <4.0.0' + "#})?; + + std::fs::create_dir(context.work_dir().join("bin"))?; + context + .work_dir() + .child("bin") + .child("hello.dart") + .write_str(indoc::indoc! {r#" + void main() { + print('Hello from Dart package!'); + } + "#})?; + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + Hello from Dart package! + + ----- stderr ----- + "); + + Ok(()) +} + +/// Test Dart hook with additional dependencies. +#[test] +fn additional_dependencies() { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r#" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: dart ./test_path.dart + additional_dependencies: ["path"] + always_run: true + verbose: true + pass_filenames: false + "#}); + + context + .work_dir() + .child("test_path.dart") + .write_str(indoc::indoc! {r#" + import 'package:path/path.dart' as p; + void main() { + var joined = p.join('foo', 'bar', 'baz.txt'); + print('Joined path: $joined'); + } + "#}) + .unwrap(); + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + Joined path: foo/bar/baz.txt + + ----- stderr ----- + "); +} + +/// Test Dart hook with additional dependencies specifying version. +#[test] +fn additional_dependencies_with_version() { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r#" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: dart ./test_path.dart + additional_dependencies: ["path:1.8.0"] + always_run: true + verbose: true + pass_filenames: false + "#}); + + context + .work_dir() + .child("test_path.dart") + .write_str(indoc::indoc! {r#" + import 'package:path/path.dart' as p; + void main() { + print('Using path package'); + } + "#}) + .unwrap(); + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + Using path package + + ----- stderr ----- + "); +} + +/// Test Dart environment variables (PATH and `PUB_CACHE`). +#[test] +fn dart_environment() { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r#" + repos: + - repo: local + hooks: + - id: dart + name: dart + language: dart + entry: dart ./env_test.dart + always_run: true + verbose: true + pass_filenames: false + "#}); + + context + .work_dir() + .child("env_test.dart") + .write_str(indoc::indoc! {r#" + import 'dart:io'; + void main() { + var pubCache = Platform.environment['PUB_CACHE']; + if (pubCache != null) { + print('PUB_CACHE is set: ${pubCache.isNotEmpty}'); + } else { + print('PUB_CACHE is not set'); + } + } + "#}) + .unwrap(); + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart.....................................................................Passed + - hook id: dart + - duration: [TIME] + + PUB_CACHE is set: true + + ----- stderr ----- + "); +} + +/// Test remote Dart hook from GitHub repository. +#[test] +#[ignore = "Requires prek-test-repos/dart-hooks to be created"] +fn remote_hook() { + let context = TestContext::new(); + context.init_project(); + + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: https://github.com/prek-test-repos/dart-hooks + rev: v1.0.0 + hooks: + - id: dart-hooks + always_run: true + verbose: true + "}); + + context.git_add("."); + + cmd_snapshot!(context.filters(), context.run(), @r" + success: true + exit_code: 0 + ----- stdout ----- + dart-hooks...............................................................Passed + - hook id: dart-hooks + - duration: [TIME] + + this is a dart remote hook + + ----- stderr ----- + "); +} diff --git a/tests/languages/main.rs b/tests/languages/main.rs index c2de6f1a..738f97ad 100644 --- a/tests/languages/main.rs +++ b/tests/languages/main.rs @@ -1,6 +1,7 @@ #[path = "../common/mod.rs"] mod common; +mod dart; #[cfg(all(feature = "docker", target_os = "linux"))] mod docker; #[cfg(all(feature = "docker", target_os = "linux"))]