Skip to content

Commit 662a943

Browse files
aecsockettdgao
andauthored
fix: tag validator for plugins/datapacks (#7435)
* fix tag validator for plugins/datapacks * Ignore some nags inside code blocks * fix: tests * fmt --------- Co-authored-by: tdgao <mr.trumgao@gmail.com>
1 parent 48d021d commit 662a943

12 files changed

Lines changed: 496 additions & 160 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ postcard = { version = "1.1.3", default-features = false, features = ["alloc"] }
154154
postcard-bindgen = "0.8.0"
155155
proc-macro2 = { version = "1.0" }
156156
prometheus = "0.14.0"
157-
psl = "2.1.226"
157+
psl = "2.1.145"
158+
pulldown-cmark = { version = "0.13.4", default-features = false }
158159
quartz_nbt = "0.2.9"
159160
quick-xml = "0.38.3"
160161
quote = { version = "1.0" }

apps/labrinth/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ path-util = { workspace = true }
8383
postcard = { workspace = true }
8484
prometheus = { workspace = true }
8585
psl = { workspace = true }
86+
pulldown-cmark = { workspace = true }
8687
quick-xml = { workspace = true }
8788
rand = { workspace = true }
8889
rand_chacha = { workspace = true }

apps/labrinth/fixtures/dummy_data.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) V
2828

2929
INSERT INTO loaders (id, loader, metadata) VALUES (7, 'bukkit', '{"platform":false}'::JSONB);
3030
INSERT INTO loaders (id, loader, metadata) VALUES (8, 'waterfall', '{"platform":true}'::JSONB);
31+
INSERT INTO loaders (id, loader) VALUES (9, 'datapack');
32+
33+
INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id)
34+
SELECT l.id, pt.id
35+
FROM loaders l
36+
CROSS JOIN project_types pt
37+
WHERE
38+
(l.loader IN ('bukkit', 'waterfall') AND pt.name = 'plugin')
39+
OR (l.loader = 'datapack' AND pt.name = 'datapack');
40+
41+
INSERT INTO loaders_project_types_games (loader_id, project_type_id, game_id)
42+
SELECT lpt.joining_loader_id, lpt.joining_project_type_id, 1
43+
FROM loaders_project_types lpt
44+
WHERE lpt.joining_loader_id IN (7, 8, 9);
3145

3246
-- Adds dummies to mrpack_loaders
3347
INSERT INTO loader_field_enum_values (enum_id, value)

apps/labrinth/src/test/dummy_data.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use super::{
2121

2222
use super::{database::USER_USER_ID, get_json_val_str};
2323

24-
pub const DUMMY_DATA_UPDATE: i64 = 8;
24+
pub const DUMMY_DATA_UPDATE: i64 = 9;
2525

2626
pub const DUMMY_CATEGORIES: &[&str] = &[
2727
"combat",
@@ -47,6 +47,8 @@ pub enum TestFile {
4747
// and BasicModRandom.bytes() will return a different file each time.
4848
BasicModRandom { filename: String, bytes: Vec<u8> },
4949
BasicModpackRandom { filename: String, bytes: Vec<u8> },
50+
BasicPluginRandom { filename: String, bytes: Vec<u8> },
51+
BasicDatapackRandom { filename: String, bytes: Vec<u8> },
5052
}
5153

5254
impl TestFile {
@@ -163,6 +165,59 @@ impl TestFile {
163165

164166
TestFile::BasicModpackRandom { filename, bytes }
165167
}
168+
169+
pub fn build_random_plugin() -> Self {
170+
let filename = format!("random-plugin-{}.jar", rand::random::<u64>());
171+
let plugin_yml =
172+
"name: TestPlugin\nversion: 1.0.0\nmain: com.example.TestPlugin\n";
173+
174+
let mut cursor = Cursor::new(Vec::new());
175+
{
176+
let mut zip = ZipWriter::new(&mut cursor);
177+
zip.start_file(
178+
"plugin.yml",
179+
FileOptions::<()>::default()
180+
.compression_method(CompressionMethod::Stored),
181+
)
182+
.unwrap();
183+
zip.write_all(plugin_yml.as_bytes()).unwrap();
184+
zip.finish().unwrap();
185+
}
186+
187+
TestFile::BasicPluginRandom {
188+
filename,
189+
bytes: cursor.into_inner(),
190+
}
191+
}
192+
193+
pub fn build_random_datapack() -> Self {
194+
let filename = format!("random-datapack-{}.zip", rand::random::<u64>());
195+
let pack_mcmeta = serde_json::json!({
196+
"pack": {
197+
"pack_format": 15,
198+
"description": "Test datapack"
199+
}
200+
})
201+
.to_string();
202+
203+
let mut cursor = Cursor::new(Vec::new());
204+
{
205+
let mut zip = ZipWriter::new(&mut cursor);
206+
zip.start_file(
207+
"pack.mcmeta",
208+
FileOptions::<()>::default()
209+
.compression_method(CompressionMethod::Stored),
210+
)
211+
.unwrap();
212+
zip.write_all(pack_mcmeta.as_bytes()).unwrap();
213+
zip.finish().unwrap();
214+
}
215+
216+
TestFile::BasicDatapackRandom {
217+
filename,
218+
bytes: cursor.into_inner(),
219+
}
220+
}
166221
}
167222

168223
#[derive(Clone)]
@@ -472,6 +527,8 @@ impl TestFile {
472527
TestFile::BasicModDifferent => "basic-mod-different.jar",
473528
TestFile::BasicModRandom { filename, .. } => filename,
474529
TestFile::BasicModpackRandom { filename, .. } => filename,
530+
TestFile::BasicPluginRandom { filename, .. } => filename,
531+
TestFile::BasicDatapackRandom { filename, .. } => filename,
475532
}
476533
.to_string()
477534
}
@@ -498,6 +555,8 @@ impl TestFile {
498555
}
499556
TestFile::BasicModRandom { bytes, .. } => bytes.clone(),
500557
TestFile::BasicModpackRandom { bytes, .. } => bytes.clone(),
558+
TestFile::BasicPluginRandom { bytes, .. } => bytes.clone(),
559+
TestFile::BasicDatapackRandom { bytes, .. } => bytes.clone(),
501560
}
502561
}
503562

@@ -512,6 +571,8 @@ impl TestFile {
512571
TestFile::BasicZip => "resourcepack",
513572

514573
TestFile::BasicModpackRandom { .. } => "modpack",
574+
TestFile::BasicPluginRandom { .. } => "plugin",
575+
TestFile::BasicDatapackRandom { .. } => "datapack",
515576
}
516577
.to_string()
517578
}
@@ -529,6 +590,10 @@ impl TestFile {
529590
TestFile::BasicModpackRandom { .. } => {
530591
Some("application/x-modrinth-modpack+zip")
531592
}
593+
TestFile::BasicPluginRandom { .. } => {
594+
Some("application/java-archive")
595+
}
596+
TestFile::BasicDatapackRandom { .. } => Some("application/zip"),
532597
}
533598
.map(|s| s.to_string())
534599
}

apps/labrinth/src/validate/project/description.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1+
mod markdown;
2+
13
use serde_json::json;
24

5+
use self::markdown::DescriptionMarkdown;
36
use super::text::{
4-
ProfanityKind, contains_description_spam, description_ends_with_header,
5-
extract_description_blocks, extract_description_text,
6-
find_banned_description_link, has_adjacent_same_level_headers,
7+
ProfanityKind, contains_description_spam, extract_description_blocks,
8+
extract_description_text, find_banned_description_link,
79
has_image_without_alt_text, has_sufficient_english_blocks,
8-
js_string_length, long_header_count, non_standard_text_ratio,
9-
normalize_project_field_text, profanity_matches, project_requires_english,
10+
js_string_length, non_standard_text_ratio, normalize_project_field_text,
11+
profanity_matches, project_requires_english,
1012
};
1113
use super::{ProjectNag, ProjectNagKind, ProjectNagSeverity};
1214

@@ -19,11 +21,15 @@ const NON_STANDARD_TEXT_FAILURE_THRESHOLD: f64 = 0.05;
1921
pub(super) fn validate(project: &Project) -> Vec<ProjectNag> {
2022
let mut nags = Vec::new();
2123
let description = project.description.as_str();
24+
let markdown = DescriptionMarkdown::parse(description);
25+
let description_without_code = markdown.without_code();
2226
let normalized_description = normalize_project_field_text(description);
23-
let text = extract_description_text(description);
24-
let has_spam = has_description_spam(description);
25-
let normalized_text = extract_description_text(&normalized_description);
26-
let blocks = extract_description_blocks(description);
27+
let text = extract_description_text(&description_without_code);
28+
let has_spam = has_description_spam(&description_without_code);
29+
let normalized_text =
30+
normalize_project_field_text(&description_without_code);
31+
let normalized_text = extract_description_text(&normalized_text);
32+
let blocks = extract_description_blocks(&description_without_code);
2733
let profanity = profanity_matches(description);
2834

2935
if let Some(matched) = profanity
@@ -51,7 +57,7 @@ pub(super) fn validate(project: &Project) -> Vec<ProjectNag> {
5157
.with_details(json!({ "value": matched.raw_text })),
5258
);
5359
}
54-
if non_standard_text_ratio(description)
60+
if non_standard_text_ratio(&description_without_code)
5561
>= NON_STANDARD_TEXT_FAILURE_THRESHOLD
5662
{
5763
nags.push(ProjectNag::new(
@@ -91,7 +97,7 @@ pub(super) fn validate(project: &Project) -> Vec<ProjectNag> {
9197
ProjectNagSeverity::Required,
9298
));
9399
}
94-
if let Some(url) = find_banned_description_link(description) {
100+
if let Some(url) = find_banned_description_link(&description_without_code) {
95101
nags.push(
96102
ProjectNag::new(
97103
ProjectNagKind::ProjectDescriptionBannedLink,
@@ -100,7 +106,7 @@ pub(super) fn validate(project: &Project) -> Vec<ProjectNag> {
100106
.with_details(json!({ "full_url": url })),
101107
);
102108
}
103-
let long_headers = long_header_count(description);
109+
let long_headers = markdown.long_header_count();
104110
if long_headers > 0 {
105111
nags.push(
106112
ProjectNag::new(
@@ -110,19 +116,19 @@ pub(super) fn validate(project: &Project) -> Vec<ProjectNag> {
110116
.with_details(json!({ "count": long_headers })),
111117
);
112118
}
113-
if description_ends_with_header(description) {
119+
if markdown.ends_with_header() {
114120
nags.push(ProjectNag::new(
115121
ProjectNagKind::DescriptionEndsWithHeader,
116122
ProjectNagSeverity::Required,
117123
));
118124
}
119-
if has_adjacent_same_level_headers(description) {
125+
if markdown.has_adjacent_same_level_headers() {
120126
nags.push(ProjectNag::new(
121127
ProjectNagKind::AdjacentHeaders,
122128
ProjectNagSeverity::Required,
123129
));
124130
}
125-
if has_image_without_alt_text(description) {
131+
if has_image_without_alt_text(&description_without_code) {
126132
nags.push(ProjectNag::new(
127133
ProjectNagKind::MissingAltText,
128134
ProjectNagSeverity::Warning,
@@ -133,8 +139,10 @@ pub(super) fn validate(project: &Project) -> Vec<ProjectNag> {
133139
}
134140

135141
pub(super) fn is_non_english(project: &Project) -> bool {
136-
let text = extract_description_text(&project.description);
137-
let blocks = extract_description_blocks(&project.description);
142+
let markdown = DescriptionMarkdown::parse(&project.description);
143+
let description_without_code = markdown.without_code();
144+
let text = extract_description_text(&description_without_code);
145+
let blocks = extract_description_blocks(&description_without_code);
138146
is_non_english_text(project, &text, &blocks)
139147
}
140148

0 commit comments

Comments
 (0)