Grcov report - container_run.rs

1

    use std::io::{

2

      BufRead as _,

3

      BufReader,

4

    };

5

    use std::process::Command as ProcessCommand;

6

    use std::{

7

      env,

8

      thread,

9

    };

10

11

    use anyhow::Context as _;

12

    use serde::Deserialize;

13

    use which::which;

14

15

    use crate::defaults::{

16

      default_ignore_errors,

17

      default_verbose,

18

    };

19

    use crate::file::ToUtf8 as _;

20

    use crate::handle_output;

21

    use crate::schema::{

22

      get_output_handler,

23

      TaskContext,

24

    };

25

26

    #[derive(Debug, Deserialize)]

27

    pub struct ContainerRun {

28

      /// The command to run in the container

29

      pub container_command: Vec<String>,

30

31

      /// The container image to use

32

      pub image: String,

33

34

      /// The mounted paths to bind mount into the container

35

      #[serde(default)]

36

      pub mounted_paths: Vec<String>,

37

38

      /// Ignore errors if the command fails

39

      #[serde(default)]

40

      pub ignore_errors: Option<bool>,

41

42

      /// Show verbose output

43

      #[serde(default)]

44

      pub verbose: Option<bool>,

45

    }

46

47

    impl ContainerRun {

48

      pub fn execute(&self, context: &TaskContext) -> anyhow::Result<()> {

49

        assert!(!self.image.is_empty());

50

        assert!(!self.container_command.is_empty());

51

52

        let ignore_errors = self.ignore_errors(context);

53

        let verbose = self.verbose(context);

54

55

        let stdout = get_output_handler(verbose);

56

        let stderr = get_output_handler(verbose);

57

58

        let container_runtime = which("docker")

59

          .or_else(|_| which("podman"))

60

          .with_context(|| "Failed to find docker or podman")?;

61

62

        let mut cmd = ProcessCommand::new(container_runtime);

63

        cmd.arg("run").arg("--rm").arg("-i").stdout(stdout).stderr(stderr);

64

65

        let current_dir = env::current_dir()?;

66

        cmd

67

          .arg("-v")

68

          .arg(format!("{}:/workdir:z", current_dir.to_utf8()?));

69

        cmd.arg("-w").arg("/workdir");

70

71

        for mounted_path in self.mounted_paths.clone() {

72

          cmd.arg("-v").arg(mounted_path);

73

        }

74

75

        // Inject environment variables in both container and command

76

        for (key, value) in context.env_vars.iter() {

77

          cmd.env(key, value);

78

          cmd.arg("-e").arg(format!("{}={}", key, value));

79

        }

80

81

        cmd.arg(&self.image).args(&self.container_command);

82

83

        log::trace!("Running command: {:?}", cmd);

84

85

        let mut cmd = cmd.spawn()?;

86

        if verbose {

87

          handle_output!(cmd.stdout, context);

88

          handle_output!(cmd.stderr, context);

89

        }

90

91

        let status = cmd.wait()?;

92

        if !status.success() && !ignore_errors {

93

          anyhow::bail!("Command failed - {}", self.container_command.join(" "));

94

        }

95

96

        Ok(())

97

      }

98

99

      fn ignore_errors(&self, context: &TaskContext) -> bool {

100

        self

101

          .ignore_errors

102

          .or(context.ignore_errors)

103

          .unwrap_or(default_ignore_errors())

104

      }

105

106

      fn verbose(&self, context: &TaskContext) -> bool {

107

        self.verbose.or(context.verbose).unwrap_or(default_verbose())

108

      }

109

    }