• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

baoyachi / shadow-rs / 11829422363

14 Nov 2024 02:11AM UTC coverage: 24.322% (-3.9%) from 28.209%
11829422363

push

github

web-flow
Merge pull request #190 from baoyachi/hook_ext

feat(HookExt): Add extended hook functionality with custom deny lists

0 of 14 new or added lines in 2 files covered. (0.0%)

795 existing lines in 3 files now uncovered.

610 of 2508 relevant lines covered (24.32%)

0.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.7
/src/git.rs
1
use crate::build::{ConstType, ConstVal, ShadowConst};
2
use crate::ci::CiType;
3
use crate::err::*;
4
use crate::{DateTime, Format};
5
use std::collections::BTreeMap;
6
use std::io::{BufReader, Read};
7
use std::path::Path;
8
use std::process::{Command, Stdio};
9

10
const BRANCH_DOC: &str = r#"
11
The name of the Git branch that this project was built from.
12
This constant will be empty if the branch cannot be determined."#;
13
pub const BRANCH: ShadowConst = "BRANCH";
14
const TAG_DOC: &str = r#"
15
The name of the Git tag that this project was built from.
16
Note that this will be empty if there is no tag for the HEAD at the time of build."#;
17
pub const TAG: ShadowConst = "TAG";
18
const LAST_TAG_DOC: &str = r#"
19
The name of the last Git tag on the branch that this project was built from.
20
As opposed to [`TAG`], this does not require the current commit to be tagged, just one of its parents.
21

22
This constant will be empty if the last tag cannot be determined."#;
23
pub const LAST_TAG: ShadowConst = "LAST_TAG";
24
const SHORT_COMMIT_DOC: &str = r#"
25
The short hash of the Git commit that this project was built from.
26
Note that this will always truncate [`COMMIT_HASH`] to 8 characters if necessary.
27
Depending on the amount of commits in your project, this may not yield a unique Git identifier
28
([see here for more details on hash abbreviation](https://git-scm.com/docs/git-describe#_examples)).
29

30
This constant will be empty if the last commit cannot be determined."#;
31
pub const SHORT_COMMIT: ShadowConst = "SHORT_COMMIT";
32
const COMMIT_HASH_DOC: &str = r#"
33
The full commit hash of the Git commit that this project was built from.
34
An abbreviated, but not necessarily unique, version of this is [`SHORT_COMMIT`].
35

36
This constant will be empty if the last commit cannot be determined."#;
37
pub const COMMIT_HASH: ShadowConst = "COMMIT_HASH";
38
const COMMIT_DATE_DOC: &str = r#"The time of the Git commit that this project was built from.
39
The time is formatted in modified ISO 8601 format (`YYYY-MM-DD HH-MM ±hh-mm` where hh-mm is the offset from UTC).
40

41
This constant will be empty if the last commit cannot be determined."#;
42
pub const COMMIT_DATE: ShadowConst = "COMMIT_DATE";
43
const COMMIT_DATE_2822_DOC: &str = r#"
44
The name of the Git branch that this project was built from.
45
The time is formatted according to [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) (e.g. HTTP Headers).
46

47
This constant will be empty if the last commit cannot be determined."#;
48
pub const COMMIT_DATE_2822: ShadowConst = "COMMIT_DATE_2822";
49
const COMMIT_DATE_3339_DOC: &str = r#"
50
The name of the Git branch that this project was built from.
51
The time is formatted according to [RFC 3339 and ISO 8601](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6).
52

53
This constant will be empty if the last commit cannot be determined."#;
54
pub const COMMIT_DATE_3339: ShadowConst = "COMMIT_DATE_3339";
55
const COMMIT_AUTHOR_DOC: &str = r#"
56
The author of the Git commit that this project was built from.
57

58
This constant will be empty if the last commit cannot be determined."#;
59
pub const COMMIT_AUTHOR: ShadowConst = "COMMIT_AUTHOR";
60
const COMMIT_EMAIL_DOC: &str = r#"
61
The e-mail address of the author of the Git commit that this project was built from.
62

63
This constant will be empty if the last commit cannot be determined."#;
64
pub const COMMIT_EMAIL: ShadowConst = "COMMIT_EMAIL";
65
const GIT_CLEAN_DOC: &str = r#"
66
Whether the Git working tree was clean at the time of project build (`true`), or not (`false`).
67

68
This constant will be `false` if the last commit cannot be determined."#;
69
pub const GIT_CLEAN: ShadowConst = "GIT_CLEAN";
70
const GIT_STATUS_FILE_DOC: &str = r#"
71
The Git working tree status as a list of files with their status, similar to `git status`.
72
Each line of the list is preceded with `  * `, followed by the file name.
73
Files marked `(dirty)` have unstaged changes.
74
Files marked `(staged)` have staged changes.
75

76
This constant will be empty if the working tree status cannot be determined."#;
77
pub const GIT_STATUS_FILE: ShadowConst = "GIT_STATUS_FILE";
78

79
#[derive(Default, Debug)]
80
pub struct Git {
81
    map: BTreeMap<ShadowConst, ConstVal>,
82
    ci_type: CiType,
83
}
84

85
impl Git {
86
    fn update_str(&mut self, c: ShadowConst, v: String) {
1✔
87
        if let Some(val) = self.map.get_mut(c) {
3✔
88
            *val = ConstVal {
1✔
89
                desc: val.desc.clone(),
1✔
90
                v,
1✔
91
                t: ConstType::Str,
1✔
92
            }
93
        }
94
    }
95

96
    fn update_bool(&mut self, c: ShadowConst, v: bool) {
1✔
97
        if let Some(val) = self.map.get_mut(c) {
2✔
98
            *val = ConstVal {
1✔
99
                desc: val.desc.clone(),
1✔
100
                v: v.to_string(),
1✔
101
                t: ConstType::Bool,
1✔
102
            }
103
        }
104
    }
105

106
    fn init(&mut self, path: &Path, std_env: &BTreeMap<String, String>) -> SdResult<()> {
1✔
107
        // First, try executing using the git command.
108
        if let Err(err) = self.init_git() {
1✔
109
            println!("{err}");
×
110
        }
111

112
        // If the git2 feature is enabled, then replace the corresponding values with git2.
113
        self.init_git2(path)?;
2✔
114

115
        // use command branch
116
        if let Some(x) = find_branch_in(path) {
2✔
117
            self.update_str(BRANCH, x)
1✔
118
        };
119

120
        // use command tag
121
        if let Some(x) = command_current_tag() {
2✔
122
            self.update_str(TAG, x)
1✔
123
        }
124

125
        // use command get last tag
126
        if let Some(x) = command_last_tag() {
2✔
127
            self.update_str(LAST_TAG, x)
1✔
128
        }
129

130
        // try use ci branch,tag
131
        self.ci_branch_tag(std_env);
1✔
132
        Ok(())
1✔
133
    }
134

135
    fn init_git(&mut self) -> SdResult<()> {
1✔
136
        // check git status
137
        let x = command_git_clean();
1✔
138
        self.update_bool(GIT_CLEAN, x);
1✔
139

140
        let x = command_git_status_file();
1✔
141
        self.update_str(GIT_STATUS_FILE, x);
1✔
142

143
        let git_info = command_git_head();
1✔
144

145
        self.update_str(COMMIT_EMAIL, git_info.email);
1✔
146
        self.update_str(COMMIT_AUTHOR, git_info.author);
1✔
147
        self.update_str(SHORT_COMMIT, git_info.short_commit);
1✔
148
        self.update_str(COMMIT_HASH, git_info.commit);
1✔
149

150
        let time_stamp = git_info.date.parse::<i64>()?;
1✔
151
        if let Ok(date_time) = DateTime::timestamp_2_utc(time_stamp) {
2✔
152
            self.update_str(COMMIT_DATE, date_time.human_format());
2✔
153
            self.update_str(COMMIT_DATE_2822, date_time.to_rfc2822());
1✔
154
            self.update_str(COMMIT_DATE_3339, date_time.to_rfc3339());
1✔
155
        }
156

157
        Ok(())
1✔
158
    }
159

160
    #[allow(unused_variables)]
161
    fn init_git2(&mut self, path: &Path) -> SdResult<()> {
1✔
162
        #[cfg(feature = "git2")]
163
        {
164
            use crate::date_time::DateTime;
165
            use crate::git::git2_mod::git_repo;
166
            use crate::Format;
167

168
            let repo = git_repo(path).map_err(ShadowError::new)?;
1✔
169
            let reference = repo.head().map_err(ShadowError::new)?;
2✔
170

171
            //get branch
172
            let branch = reference
2✔
173
                .shorthand()
174
                .map(|x| x.trim().to_string())
2✔
175
                .or_else(command_current_branch)
×
176
                .unwrap_or_default();
177

178
            //get HEAD branch
179
            let tag = command_current_tag().unwrap_or_default();
2✔
180
            let last_tag = command_last_tag().unwrap_or_default();
2✔
181
            self.update_str(BRANCH, branch);
1✔
182
            self.update_str(TAG, tag);
1✔
183
            self.update_str(LAST_TAG, last_tag);
1✔
184

185
            if let Some(v) = reference.target() {
1✔
186
                let commit = v.to_string();
1✔
187
                self.update_str(COMMIT_HASH, commit.clone());
2✔
188
                let mut short_commit = commit.as_str();
1✔
189

190
                if commit.len() > 8 {
2✔
191
                    short_commit = short_commit.get(0..8).unwrap();
2✔
192
                }
193
                self.update_str(SHORT_COMMIT, short_commit.to_string());
2✔
194
            }
195

196
            let commit = reference.peel_to_commit().map_err(ShadowError::new)?;
2✔
197

198
            let author = commit.author();
2✔
199
            if let Some(v) = author.email() {
2✔
200
                self.update_str(COMMIT_EMAIL, v.to_string());
2✔
201
            }
202

203
            if let Some(v) = author.name() {
2✔
204
                self.update_str(COMMIT_AUTHOR, v.to_string());
2✔
205
            }
206
            let status_file = Self::git2_dirty_stage(&repo);
2✔
207
            if status_file.trim().is_empty() {
2✔
208
                self.update_bool(GIT_CLEAN, true);
2✔
209
            } else {
210
                self.update_bool(GIT_CLEAN, false);
×
211
            }
212
            self.update_str(GIT_STATUS_FILE, status_file);
1✔
213

214
            let time_stamp = commit.time().seconds().to_string().parse::<i64>()?;
1✔
215
            if let Ok(date_time) = DateTime::timestamp_2_utc(time_stamp) {
1✔
216
                self.update_str(COMMIT_DATE, date_time.human_format());
2✔
217

218
                self.update_str(COMMIT_DATE_2822, date_time.to_rfc2822());
1✔
219

220
                self.update_str(COMMIT_DATE_3339, date_time.to_rfc3339());
1✔
221
            }
222
        }
223
        Ok(())
1✔
224
    }
225

226
    //use git2 crates git repository 'dirty or stage' status files.
227
    #[cfg(feature = "git2")]
228
    pub fn git2_dirty_stage(repo: &git2::Repository) -> String {
1✔
229
        let mut repo_opts = git2::StatusOptions::new();
1✔
230
        repo_opts.include_ignored(false);
1✔
231
        if let Ok(statue) = repo.statuses(Some(&mut repo_opts)) {
1✔
232
            let mut dirty_files = Vec::new();
1✔
233
            let mut staged_files = Vec::new();
1✔
234

235
            for status in statue.iter() {
2✔
236
                if let Some(path) = status.path() {
×
237
                    match status.status() {
×
238
                        git2::Status::CURRENT => (),
×
239
                        git2::Status::INDEX_NEW
×
240
                        | git2::Status::INDEX_MODIFIED
×
241
                        | git2::Status::INDEX_DELETED
×
242
                        | git2::Status::INDEX_RENAMED
×
243
                        | git2::Status::INDEX_TYPECHANGE => staged_files.push(path.to_string()),
×
244
                        _ => dirty_files.push(path.to_string()),
×
245
                    };
246
                }
247
            }
248
            filter_git_dirty_stage(dirty_files, staged_files)
1✔
249
        } else {
250
            "".into()
×
251
        }
252
    }
253

254
    #[allow(clippy::manual_strip)]
255
    fn ci_branch_tag(&mut self, std_env: &BTreeMap<String, String>) {
1✔
256
        let mut branch: Option<String> = None;
1✔
257
        let mut tag: Option<String> = None;
1✔
258
        match self.ci_type {
1✔
259
            CiType::Gitlab => {
×
260
                if let Some(v) = std_env.get("CI_COMMIT_TAG") {
×
261
                    tag = Some(v.to_string());
×
262
                } else if let Some(v) = std_env.get("CI_COMMIT_REF_NAME") {
×
263
                    branch = Some(v.to_string());
×
264
                }
265
            }
266
            CiType::Github => {
×
267
                if let Some(v) = std_env.get("GITHUB_REF") {
2✔
268
                    let ref_branch_prefix: &str = "refs/heads/";
1✔
269
                    let ref_tag_prefix: &str = "refs/tags/";
1✔
270

271
                    if v.starts_with(ref_branch_prefix) {
2✔
272
                        branch = Some(
1✔
273
                            v.get(ref_branch_prefix.len()..)
2✔
274
                                .unwrap_or_default()
×
275
                                .to_string(),
×
276
                        )
UNCOV
277
                    } else if v.starts_with(ref_tag_prefix) {
×
UNCOV
278
                        tag = Some(
×
UNCOV
279
                            v.get(ref_tag_prefix.len()..)
×
280
                                .unwrap_or_default()
×
281
                                .to_string(),
×
282
                        )
283
                    }
284
                }
285
            }
286
            _ => {}
×
287
        }
288
        if let Some(x) = branch {
2✔
289
            self.update_str(BRANCH, x);
2✔
290
        }
291

292
        if let Some(x) = tag {
1✔
UNCOV
293
            self.update_str(TAG, x.clone());
×
UNCOV
294
            self.update_str(LAST_TAG, x);
×
295
        }
296
    }
297
}
298

299
pub(crate) fn new_git(
1✔
300
    path: &Path,
301
    ci: CiType,
302
    std_env: &BTreeMap<String, String>,
303
) -> BTreeMap<ShadowConst, ConstVal> {
304
    let mut git = Git {
305
        map: Default::default(),
1✔
306
        ci_type: ci,
307
    };
308
    git.map.insert(BRANCH, ConstVal::new(BRANCH_DOC));
2✔
309

310
    git.map.insert(TAG, ConstVal::new(TAG_DOC));
1✔
311

312
    git.map.insert(LAST_TAG, ConstVal::new(LAST_TAG_DOC));
1✔
313

314
    git.map.insert(COMMIT_HASH, ConstVal::new(COMMIT_HASH_DOC));
1✔
315

316
    git.map
1✔
317
        .insert(SHORT_COMMIT, ConstVal::new(SHORT_COMMIT_DOC));
2✔
318

319
    git.map
1✔
320
        .insert(COMMIT_AUTHOR, ConstVal::new(COMMIT_AUTHOR_DOC));
2✔
321
    git.map
1✔
322
        .insert(COMMIT_EMAIL, ConstVal::new(COMMIT_EMAIL_DOC));
2✔
323
    git.map.insert(COMMIT_DATE, ConstVal::new(COMMIT_DATE_DOC));
1✔
324

325
    git.map
1✔
326
        .insert(COMMIT_DATE_2822, ConstVal::new(COMMIT_DATE_2822_DOC));
2✔
327

328
    git.map
1✔
329
        .insert(COMMIT_DATE_3339, ConstVal::new(COMMIT_DATE_3339_DOC));
2✔
330

331
    git.map.insert(GIT_CLEAN, ConstVal::new_bool(GIT_CLEAN_DOC));
1✔
332

333
    git.map
1✔
334
        .insert(GIT_STATUS_FILE, ConstVal::new(GIT_STATUS_FILE_DOC));
2✔
335

336
    if let Err(e) = git.init(path, std_env) {
1✔
337
        println!("{e}");
×
338
    }
339

340
    git.map
1✔
341
}
342

343
#[cfg(feature = "git2")]
344
pub mod git2_mod {
345
    use git2::Error as git2Error;
346
    use git2::Repository;
347
    use std::path::Path;
348

349
    pub fn git_repo<P: AsRef<Path>>(path: P) -> Result<Repository, git2Error> {
1✔
350
        Repository::discover(path)
1✔
351
    }
352

353
    pub fn git2_current_branch(repo: &Repository) -> Option<String> {
×
354
        repo.head()
×
355
            .map(|x| x.shorthand().map(|x| x.to_string()))
×
356
            .unwrap_or(None)
×
357
    }
358
}
359

360
/// get current repository git branch.
361
///
362
/// When current repository exists git folder.
363
///
364
/// It's use default feature.This function try use [git2] crates get current branch.
365
/// If not use git2 feature,then try use [Command] to get.
366
pub fn branch() -> String {
×
367
    #[cfg(feature = "git2")]
368
    {
369
        use crate::git::git2_mod::{git2_current_branch, git_repo};
370
        git_repo(".")
371
            .map(|x| git2_current_branch(&x))
×
372
            .unwrap_or_else(|_| command_current_branch())
×
373
            .unwrap_or_default()
374
    }
375
    #[cfg(not(feature = "git2"))]
376
    {
377
        command_current_branch().unwrap_or_default()
378
    }
379
}
380

381
/// get current repository git tag.
382
///
383
/// When current repository exists git folder.
384
/// I's use [Command] to get.
385
pub fn tag() -> String {
×
386
    command_current_tag().unwrap_or_default()
×
387
}
388

389
/// Check current git Repository status without nothing(dirty or stage)
390
///
391
/// if nothing,It means clean:true. On the contrary, it is 'dirty':false
392
pub fn git_clean() -> bool {
×
393
    #[cfg(feature = "git2")]
394
    {
395
        use crate::git::git2_mod::git_repo;
396
        git_repo(".")
397
            .map(|x| Git::git2_dirty_stage(&x))
×
398
            .map(|x| x.trim().is_empty())
×
399
            .unwrap_or(true)
400
    }
401
    #[cfg(not(feature = "git2"))]
402
    {
403
        command_git_clean()
404
    }
405
}
406

407
/// List current git Repository statue(dirty or stage) contain file changed
408
///
409
/// Refer to the 'cargo fix' result output when git statue(dirty or stage) changed.
410
///
411
/// Example output:`   * examples/builtin_fn.rs (dirty)`
412
pub fn git_status_file() -> String {
×
413
    #[cfg(feature = "git2")]
414
    {
415
        use crate::git::git2_mod::git_repo;
416
        git_repo(".")
417
            .map(|x| Git::git2_dirty_stage(&x))
×
418
            .unwrap_or_default()
419
    }
420
    #[cfg(not(feature = "git2"))]
421
    {
422
        command_git_status_file()
423
    }
424
}
425

426
struct GitHeadInfo {
427
    commit: String,
428
    short_commit: String,
429
    email: String,
430
    author: String,
431
    date: String,
432
}
433

434
struct GitCommandExecutor<'a> {
435
    path: &'a Path,
436
}
437

438
impl Default for GitCommandExecutor<'_> {
439
    fn default() -> Self {
1✔
440
        Self::new(Path::new("."))
1✔
441
    }
442
}
443

444
impl<'a> GitCommandExecutor<'a> {
445
    fn new(path: &'a Path) -> Self {
1✔
446
        GitCommandExecutor { path }
447
    }
448

449
    fn exec(&self, args: &[&str]) -> Option<String> {
2✔
450
        Command::new("git")
451
            .current_dir(self.path)
1✔
452
            .args(args)
×
453
            .output()
454
            .map(|x| {
1✔
455
                String::from_utf8(x.stdout)
2✔
456
                    .map(|x| x.trim().to_string())
2✔
457
                    .ok()
×
458
            })
459
            .unwrap_or(None)
1✔
460
    }
461
}
462

463
fn command_git_head() -> GitHeadInfo {
1✔
464
    let cli = |args: &[&str]| GitCommandExecutor::default().exec(args).unwrap_or_default();
2✔
465
    GitHeadInfo {
466
        commit: cli(&["rev-parse", "HEAD"]),
1✔
467
        short_commit: cli(&["rev-parse", "--short", "HEAD"]),
1✔
468
        author: cli(&["log", "-1", "--pretty=format:%an"]),
1✔
469
        email: cli(&["log", "-1", "--pretty=format:%ae"]),
1✔
470
        date: cli(&["show", "--pretty=format:%ct", "--date=raw", "-s"]),
1✔
471
    }
472
}
473

474
/// Command exec git current tag
475
fn command_current_tag() -> Option<String> {
1✔
476
    GitCommandExecutor::default().exec(&["tag", "-l", "--contains", "HEAD"])
1✔
477
}
478

479
/// git describe --tags --abbrev=0 HEAD
480
/// Command exec git last tag
481
fn command_last_tag() -> Option<String> {
1✔
482
    GitCommandExecutor::default().exec(&["describe", "--tags", "--abbrev=0", "HEAD"])
1✔
483
}
484

485
/// git clean:git status --porcelain
486
/// check repository git status is clean
487
fn command_git_clean() -> bool {
1✔
488
    GitCommandExecutor::default()
1✔
489
        .exec(&["status", "--porcelain"])
490
        .map(|x| x.is_empty())
2✔
491
        .unwrap_or(true)
492
}
493

494
/// check git repository 'dirty or stage' status files.
495
/// git dirty:git status  --porcelain | grep '^\sM.' |awk '{print $2}'
496
/// git stage:git status --porcelain --untracked-files=all | grep '^[A|M|D|R]'|awk '{print $2}'
497
fn command_git_status_file() -> String {
1✔
498
    let git_status_files =
2✔
499
        move |args: &[&str], grep: &[&str], awk: &[&str]| -> SdResult<Vec<String>> {
500
            let git_shell = Command::new("git")
3✔
501
                .args(args)
502
                .stdin(Stdio::piped())
1✔
503
                .stdout(Stdio::piped())
1✔
504
                .spawn()?;
1✔
505
            let git_out = git_shell.stdout.ok_or("Failed to exec git stdout")?;
1✔
506

507
            let grep_shell = Command::new("grep")
4✔
508
                .args(grep)
509
                .stdin(Stdio::from(git_out))
1✔
510
                .stdout(Stdio::piped())
1✔
511
                .spawn()?;
1✔
512
            let grep_out = grep_shell.stdout.ok_or("Failed to exec grep stdout")?;
1✔
513

514
            let mut awk_shell = Command::new("awk")
4✔
515
                .args(awk)
516
                .stdin(Stdio::from(grep_out))
1✔
517
                .stdout(Stdio::piped())
1✔
518
                .spawn()?;
1✔
519
            let mut awk_out = BufReader::new(
520
                awk_shell
1✔
521
                    .stdout
522
                    .as_mut()
523
                    .ok_or("Failed to exec awk stdout")?,
×
524
            );
525
            let mut line = String::new();
1✔
526
            awk_out.read_to_string(&mut line)?;
2✔
527
            Ok(line.lines().map(|x| x.into()).collect())
2✔
528
        };
529

530
    let dirty = git_status_files(&["status", "--porcelain"], &[r"^\sM."], &["{print $2}"])
1✔
531
        .unwrap_or_default();
532

533
    let stage = git_status_files(
534
        &["status", "--porcelain", "--untracked-files=all"],
535
        &[r#"^[A|M|D|R]"#],
536
        &["{print $2}"],
537
    )
538
    .unwrap_or_default();
539
    filter_git_dirty_stage(dirty, stage)
1✔
540
}
541

542
/// Command exec git current branch
543
fn command_current_branch() -> Option<String> {
×
544
    find_branch_in(Path::new("."))
×
545
}
546

547
fn find_branch_in(path: &Path) -> Option<String> {
1✔
548
    GitCommandExecutor::new(path).exec(&["symbolic-ref", "--short", "HEAD"])
1✔
549
}
550

551
fn filter_git_dirty_stage(dirty_files: Vec<String>, staged_files: Vec<String>) -> String {
1✔
552
    let mut concat_file = String::new();
1✔
553
    for file in dirty_files {
3✔
554
        concat_file.push_str("  * ");
×
555
        concat_file.push_str(&file);
×
556
        concat_file.push_str(" (dirty)\n");
×
557
    }
558
    for file in staged_files {
2✔
559
        concat_file.push_str("  * ");
×
560
        concat_file.push_str(&file);
×
561
        concat_file.push_str(" (staged)\n");
×
562
    }
563
    concat_file
1✔
564
}
565

566
#[cfg(test)]
567
mod tests {
568
    use super::*;
569
    use crate::get_std_env;
570

571
    #[test]
572
    fn test_git() {
3✔
573
        let env_map = get_std_env();
1✔
574
        let map = new_git(Path::new("./"), CiType::Github, &env_map);
2✔
575
        for (k, v) in map {
3✔
576
            println!("k:{},v:{:?}", k, v);
1✔
577
            assert!(!v.desc.is_empty());
1✔
578
            if !k.eq(TAG) && !k.eq(LAST_TAG) && !k.eq(BRANCH) && !k.eq(GIT_STATUS_FILE) {
3✔
579
                assert!(!v.v.is_empty());
1✔
580
                continue;
581
            }
582

583
            //assert github tag always exist value
584
            if let Some(github_ref) = env_map.get("GITHUB_REF") {
2✔
585
                if github_ref.starts_with("refs/tags/") && k.eq(TAG) {
2✔
UNCOV
586
                    assert!(!v.v.is_empty(), "not empty");
×
587
                } else if github_ref.starts_with("refs/heads/") && k.eq(BRANCH) {
2✔
588
                    assert!(!v.v.is_empty());
1✔
589
                }
590
            }
591
        }
592
    }
593

594
    #[test]
595
    fn test_current_branch() {
3✔
596
        if get_std_env().contains_key("GITHUB_REF") {
2✔
597
            return;
598
        }
599
        #[cfg(feature = "git2")]
600
        {
601
            use crate::git::git2_mod::{git2_current_branch, git_repo};
602
            let git2_branch = git_repo(".")
603
                .map(|x| git2_current_branch(&x))
×
604
                .unwrap_or(None);
×
605
            let command_branch = command_current_branch();
×
606
            assert!(git2_branch.is_some());
×
607
            assert!(command_branch.is_some());
×
608
            assert_eq!(command_branch, git2_branch);
×
609
        }
610

611
        assert_eq!(Some(branch()), command_current_branch());
×
612
    }
613

614
    #[test]
615
    fn test_command_last_tag() {
3✔
616
        let opt_last_tag = command_last_tag();
1✔
617
        assert!(opt_last_tag.is_some())
2✔
618
    }
619
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc