1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use anyhow::anyhow;
use clap::Parser;
use nexlint::{prelude::*, NexLintContext};
use nexlint_lints::{
    content::*,
    package::*,
    project::{
        BannedDepConfig, BannedDepType, BannedDeps, BannedDepsConfig, DirectDepDups,
        DirectDepDupsConfig, DirectDuplicateGitDependencies,
    },
};
static EXTERNAL_CRATE_DIR: &str = "external-crates/";
static CREATE_DAPP_TEMPLATE_DIR: &str = "sdk/create-dapp/templates";
static LICENSE_HEADER: &str = "Copyright (c) Mysten Labs, Inc.\n\
                               SPDX-License-Identifier: Apache-2.0\n\
                               ";
#[derive(Debug, Parser)]
pub struct Args {
    #[clap(long)]
    fail_fast: bool,
}

pub fn run(args: Args) -> crate::Result<()> {
    let banned_deps_config = BannedDepsConfig(
        vec![
            (
                "lazy_static".to_owned(),
                BannedDepConfig {
                    message: "use once_cell::sync::Lazy instead".to_owned(),
                    type_: BannedDepType::Direct,
                },
            ),
            (
                "tracing-test".to_owned(),
                BannedDepConfig {
                    message: "you should not be testing against log lines".to_owned(),
                    type_: BannedDepType::Always,
                },
            ),
            (
                "openssl-sys".to_owned(),
                BannedDepConfig {
                    message: "use rustls for TLS".to_owned(),
                    type_: BannedDepType::Always,
                },
            ),
            (
                "actix-web".to_owned(),
                BannedDepConfig {
                    message: "use axum for a webframework instead".to_owned(),
                    type_: BannedDepType::Always,
                },
            ),
            (
                "warp".to_owned(),
                BannedDepConfig {
                    message: "use axum for a webframework instead".to_owned(),
                    type_: BannedDepType::Always,
                },
            ),
        ]
        .into_iter()
        .collect(),
    );

    let direct_dep_dups_config = DirectDepDupsConfig {
        allow: vec![
            // TODO spend the time to de-dup these direct dependencies
            "serde_yaml".to_owned(),
            "syn".to_owned(),
            // Our opentelemetry integration requires that we use the same version of these packages
            // as the opentelemetry crates.
            "prost".to_owned(),
            "tonic".to_owned(),
        ],
    };

    let project_linters: &[&dyn ProjectLinter] = &[
        &BannedDeps::new(&banned_deps_config),
        &DirectDepDups::new(&direct_dep_dups_config),
        &DirectDuplicateGitDependencies,
    ];

    let package_linters: &[&dyn PackageLinter] = &[
        &CrateNamesPaths,
        &IrrelevantBuildDeps,
        // This one seems to be broken
        // &UnpublishedPackagesOnlyUsePathDependencies::new(),
        &PublishedPackagesDontDependOnUnpublishedPackages,
        &OnlyPublishToCratesIo,
        &CratesInCratesDirectory,
        // TODO: re-enable after moving Narwhal crates to crates/, or back to Narwhal repo.
        // &CratesOnlyInCratesDirectory,
    ];

    let file_path_linters: &[&dyn FilePathLinter] = &[
        // &AllowedPaths::new(DEFAULT_ALLOWED_PATHS_REGEX)?
        ];

    // allow whitespace exceptions for markdown files
    // let whitespace_exceptions = build_exceptions(&["*.md".to_owned()])?;
    let content_linters: &[&dyn ContentLinter] = &[
        &LicenseHeader::new(LICENSE_HEADER),
        &RootToml,
        // &EofNewline::new(&whitespace_exceptions),
        // &TrailingWhitespace::new(&whitespace_exceptions),
    ];

    let nexlint_context = NexLintContext::from_current_dir()?;
    let engine = LintEngineConfig::new(&nexlint_context)
        .with_project_linters(project_linters)
        .with_package_linters(package_linters)
        .with_file_path_linters(file_path_linters)
        .with_content_linters(content_linters)
        .fail_fast(args.fail_fast)
        .build();

    let results = engine.run()?;

    handle_lint_results_exclude_external_crate_checks(results)
}

/// Define custom handler so we can skip certain lints on certain files. This is a temporary till we upstream this logic
pub fn handle_lint_results_exclude_external_crate_checks(
    results: LintResults,
) -> crate::Result<()> {
    // TODO: handle skipped results
    let mut errs = false;
    for (source, message) in &results.messages {
        if let LintKind::Content(path) = source.kind() {
            if (path.starts_with(EXTERNAL_CRATE_DIR)
                || path.starts_with(CREATE_DAPP_TEMPLATE_DIR)
                || path.to_string().contains("/generated/"))
                && source.name() == "license-header"
            {
                continue;
            }
        }
        println!(
            "[{}] [{}] [{}]: {}\n",
            message.level(),
            source.name(),
            source.kind(),
            message.message()
        );
        errs = true;
    }

    if errs {
        Err(anyhow!("there were lint errors"))
    } else {
        Ok(())
    }
}