Skip to content

Commit 54c86c3

Browse files
committed
Add NPM Audit Check
1 parent 882a5ff commit 54c86c3

11 files changed

Lines changed: 177 additions & 41 deletions

File tree

.github/workflows/audit.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,31 @@ jobs:
4545
tool: cargo-audit
4646
- name: Run `cargo audit`
4747
run: cargo audit
48+
49+
npm:
50+
name: NPM - `${{ matrix.package.name }}`
51+
52+
runs-on: ubuntu-slim
53+
54+
timeout-minutes: 20
55+
56+
strategy:
57+
fail-fast: false
58+
matrix:
59+
package:
60+
- { name: js-bindgen-ld, path: host/ld/src/js }
61+
- { name: js-bindgen-runner, path: host/runner/src/js }
62+
63+
defaults:
64+
run:
65+
working-directory: ${{ matrix.package.path }}
66+
67+
steps:
68+
- name: Checkout
69+
uses: actions/checkout@v6
70+
with:
71+
persist-credentials: false
72+
- name: Generate Lockfile
73+
run: npm install --package-lock-only --no-audit --no-fund
74+
- name: Run `npm audit`
75+
run: npm audit

host/dev/src/client/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl Check {
4444
pub fn all() -> Self {
4545
Self {
4646
args: ClientArgs::all(),
47-
tools: vec![Tools::All],
47+
tools: Tools::all(),
4848
}
4949
}
5050

host/dev/src/client/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ impl Default for ClientArgs {
184184
impl ClientArgs {
185185
fn all() -> Self {
186186
Self {
187-
targets: vec![Targets::All],
188-
target_features: vec![TargetFeatures::All],
187+
targets: Targets::all(),
188+
target_features: TargetFeatures::all(),
189189
nightly_toolchain: String::from("nightly"),
190190
}
191191
}

host/dev/src/client/test.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl Test {
4545
pub fn all() -> Self {
4646
Self {
4747
args: ClientArgs::all(),
48-
include: vec![Include::All],
48+
include: Include::all(),
4949
exclude: Vec::new(),
5050
}
5151
}
@@ -169,6 +169,12 @@ enum Include {
169169
WebDriver(WebDriver),
170170
}
171171

172+
impl Include {
173+
fn all() -> Vec<Self> {
174+
vec![Self::All]
175+
}
176+
}
177+
172178
#[derive(Clone, Copy, Eq, PartialEq)]
173179
enum Runner {
174180
Engine(Engine),

host/dev/src/host/audit.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use std::fs;
2+
use std::io::ErrorKind;
3+
use std::path::Path;
4+
use std::process::Command;
5+
use std::time::Duration;
6+
7+
use anyhow::Result;
8+
use clap::{Args, ValueEnum};
9+
use strum::EnumIter;
10+
11+
use crate::command;
12+
13+
#[derive(Args)]
14+
pub struct Audit {
15+
#[arg(long, value_delimiter = ',', default_value = AuditTools::default_arg())]
16+
tools: Vec<AuditTools>,
17+
}
18+
19+
enum_with_all!(pub enum AuditTools, Tool(AuditTool), "tools");
20+
21+
#[derive(Clone, Copy, Default, EnumIter, Eq, PartialEq, ValueEnum)]
22+
pub enum AuditTool {
23+
#[default]
24+
RustSec,
25+
Npm,
26+
}
27+
28+
impl Audit {
29+
pub fn new(tools: Vec<AuditTools>) -> Self {
30+
Self { tools }
31+
}
32+
33+
pub fn execute(self, verbose: bool) -> Result<()> {
34+
let tools = AuditTool::from_tools(self.tools)?;
35+
let mut duration = Duration::ZERO;
36+
37+
for tool in tools {
38+
match tool {
39+
AuditTool::RustSec => {
40+
let mut command = Command::new("cargo");
41+
command.arg("audit");
42+
duration += command::run("RustSec", command, verbose)?;
43+
}
44+
AuditTool::Npm => {
45+
duration += Self::npm("js-bindgen-ld", "ld/src/js", verbose)?;
46+
duration += Self::npm("js-bindgen-runner", "runner/src/js", verbose)?;
47+
}
48+
}
49+
}
50+
51+
println!("-------------------------");
52+
println!("Total Time: {:.2}s", duration.as_secs_f32());
53+
54+
Ok(())
55+
}
56+
57+
fn npm(package: &str, path: &str, verbose: bool) -> Result<Duration> {
58+
let needs_install = match fs::metadata(Path::new(path).join("package-lock.json")) {
59+
Ok(meta) => {
60+
let lock_mtime = meta.modified()?;
61+
let pkg_mtime = fs::metadata(Path::new(path).join("package.json"))?.modified()?;
62+
63+
lock_mtime < pkg_mtime
64+
}
65+
Err(error) if error.kind() == ErrorKind::NotFound => true,
66+
Err(error) => return Err(error.into()),
67+
};
68+
69+
if needs_install {
70+
let mut command = Command::new("npm");
71+
command
72+
.current_dir(path)
73+
.arg("install")
74+
.arg("--package-lock-only")
75+
.arg("--no-audit")
76+
.arg("--no-fund");
77+
78+
command::run(&format!("NPM Install `{package}`"), command, verbose)?;
79+
}
80+
81+
let mut command = Command::new("npm");
82+
command.current_dir(path).arg("audit");
83+
command::run(&format!("NPM Audit `{package}`"), command, verbose)
84+
}
85+
}

host/dev/src/host/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl Build {
2525

2626
pub fn all() -> Self {
2727
Self {
28-
targets: vec![HostTargets::All],
28+
targets: HostTargets::all(),
2929
}
3030
}
3131

host/dev/src/host/check.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ impl Default for Check {
4343
impl Check {
4444
pub fn all() -> Self {
4545
Self {
46-
tools: vec![Tools::All],
47-
targets: vec![HostTargets::All],
46+
tools: Tools::all(),
47+
targets: HostTargets::all(),
4848
}
4949
}
5050

@@ -138,23 +138,30 @@ impl Check {
138138
}
139139

140140
fn npm(package: &str, path: &str, verbose: bool) -> Result<()> {
141-
let needs_update = match fs::metadata(Path::new(path).join("package-lock.json")) {
142-
Ok(meta) => {
141+
let needs_install = match fs::metadata(Path::new(path).join("package-lock.json")) {
142+
Ok(meta) => 'outer: {
143143
let lock_mtime = meta.modified()?;
144144
let pkg_mtime = fs::metadata(Path::new(path).join("package.json"))?.modified()?;
145145

146-
lock_mtime < pkg_mtime
146+
if lock_mtime < pkg_mtime {
147+
break 'outer true;
148+
}
149+
150+
match fs::metadata(Path::new(path).join("node_modules/.package-lock.json")) {
151+
Ok(meta) => meta.modified()? < pkg_mtime,
152+
Err(error) if error.kind() == ErrorKind::NotFound => true,
153+
Err(error) => return Err(error.into()),
154+
}
147155
}
148156
Err(error) if error.kind() == ErrorKind::NotFound => true,
149157
Err(error) => return Err(error.into()),
150158
};
151159

152-
if needs_update {
160+
if needs_install {
153161
let mut command = Command::new("npm");
154162
command
155163
.current_dir(path)
156164
.arg("install")
157-
.arg("-s")
158165
.arg("--no-audit")
159166
.arg("--no-fund");
160167

host/dev/src/host/mod.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod audit;
12
mod build;
23
mod check;
34
mod metadata;
@@ -10,6 +11,7 @@ use anyhow::Result;
1011
use clap::{Subcommand, ValueEnum};
1112
use strum::{Display, EnumIter};
1213

14+
pub use self::audit::{Audit, AuditTool, AuditTools};
1315
use self::build::Build;
1416
use self::check::Check;
1517
use crate::{FmtTool, FmtTools, command};
@@ -29,7 +31,7 @@ pub enum Host {
2931
Build(Build),
3032
Check(Check),
3133
Test,
32-
Audit,
34+
Audit(Audit),
3335
}
3436

3537
impl Host {
@@ -103,16 +105,7 @@ impl Host {
103105
Self::Build(build) => build.execute(verbose),
104106
Self::Check(check) => check.execute(verbose),
105107
Self::Test => test::run(verbose),
106-
Self::Audit => {
107-
let mut command = Command::new("cargo");
108-
command.arg("audit");
109-
let duration = command::run("RustSec", command, verbose)?;
110-
111-
println!("-------------------------");
112-
println!("Total Time: {:.2}s", duration.as_secs_f32());
113-
114-
Ok(())
115-
}
108+
Self::Audit(audit) => audit.execute(verbose),
116109
}
117110
}
118111
}

host/dev/src/main.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use anyhow::Result;
1212
use clap::{Parser, Subcommand, ValueEnum};
1313
use strum::EnumIter;
1414

15-
use crate::client::Client;
16-
use crate::host::Host;
15+
use self::client::Client;
16+
use self::host::{Audit, AuditTool, AuditTools, Host};
1717

1818
#[derive(Parser)]
1919
struct Cli {
@@ -45,7 +45,10 @@ enum CliCommand {
4545
#[arg(long)]
4646
all: bool,
4747
},
48-
Audit,
48+
Audit {
49+
#[arg(long, value_delimiter = ',', default_value = AuditTools::default_arg())]
50+
tools: Vec<AuditTools>,
51+
},
4952
Client {
5053
#[command(subcommand)]
5154
client: Client,
@@ -87,7 +90,12 @@ impl CliCommand {
8790
Self::Test { all }.execute(verbose)?;
8891
println!("-------------------------");
8992
println!();
90-
Self::Audit.execute(verbose)?;
93+
let tools = if all {
94+
AuditTools::all()
95+
} else {
96+
vec![AuditTools::default()]
97+
};
98+
Self::Audit { tools }.execute(verbose)?;
9199

92100
Ok(())
93101
}
@@ -150,11 +158,14 @@ impl CliCommand {
150158

151159
Ok(())
152160
}
153-
Self::Audit => {
154-
Client::Audit.execute(verbose)?;
155-
println!("-------------------------");
156-
println!();
157-
Host::Audit.execute(verbose)?;
161+
Self::Audit { tools } => {
162+
if AuditTool::from_tools(tools.clone())?.contains(&AuditTool::default()) {
163+
Client::Audit.execute(verbose)?;
164+
println!("-------------------------");
165+
println!();
166+
}
167+
168+
Host::Audit(Audit::new(tools)).execute(verbose)?;
158169

159170
Ok(())
160171
}
@@ -173,9 +184,3 @@ enum FmtTool {
173184
Tombi,
174185
Prettier,
175186
}
176-
177-
impl FmtTools {
178-
fn all() -> Vec<Self> {
179-
vec![Self::All]
180-
}
181-
}

host/dev/src/util.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ macro_rules! enum_with_all {
4242
}
4343

4444
impl $name {
45+
$vis fn all() -> Vec<Self> {
46+
vec![Self::All]
47+
}
48+
4549
$vis fn default_arg() -> &'static str {
4650
static DEFAULT: LazyLock<String> = LazyLock::new(|| {
4751
let value = $name::default().to_possible_value().unwrap();
@@ -53,7 +57,7 @@ macro_rules! enum_with_all {
5357
}
5458

5559
impl $type {
56-
fn [<from_ $opt>](cli: Vec<$name>) -> Result<Vec<Self>> {
60+
$vis fn [<from_ $opt>](cli: Vec<$name>) -> Result<Vec<Self>> {
5761
if let [$name::All] = cli.as_slice() {
5862
return Ok(Self::iter().collect());
5963
}

0 commit comments

Comments
 (0)