Skip to content

Commit 2a2fe14

Browse files
Merge #485
485: Enforce files in `src` have distinct names after lowercasing and are allowed on all OS's r=ericphanson a=ericphanson closes #441 Co-authored-by: Eric Hanson <[email protected]>
2 parents 0c50828 + 701927f commit 2a2fe14

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "RegistryCI"
22
uuid = "0c95cc5f-2f7e-43fe-82dd-79dbcba86b32"
33
authors = ["Dilum Aluthge <[email protected]>", "Fredrik Ekre <[email protected]>", "contributors"]
4-
version = "7.9.0"
4+
version = "8.0.0"
55

66
[deps]
77
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

src/AutoMerge/guidelines.jl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,69 @@ const guideline_code_can_be_downloaded = Guideline(;
549549
),
550550
)
551551

552+
function _find_lowercase_duplicates(v)
553+
elts = Dict{String, String}()
554+
for x in v
555+
lower_x = lowercase(x)
556+
if haskey(elts, lower_x)
557+
return (elts[lower_x], x)
558+
else
559+
elts[lower_x] = x
560+
end
561+
end
562+
return nothing
563+
end
564+
565+
const DISALLOWED_CHARS = ['/', '<', '>', ':', '"', '/', '\\', '|', '?', '*', Char.(0:31)...]
566+
567+
const DISALLOWED_NAMES = ["CON", "PRN", "AUX", "NUL",
568+
("COM$i" for i in 1:9)...,
569+
("LPT$i" for i in 1:9)...]
570+
571+
function meets_file_dir_name_check(name)
572+
# https://stackoverflow.com/a/31976060
573+
idx = findfirst(n -> occursin(n, name), DISALLOWED_CHARS)
574+
if idx !== nothing
575+
return false, "contains character $(DISALLOWED_CHARS[idx]) which may not be valid as a file or directory name on some platforms"
576+
end
577+
578+
base, ext = splitext(name)
579+
if uppercase(name) in DISALLOWED_NAMES || uppercase(base) in DISALLOWED_NAMES
580+
return false, "is not allowed"
581+
end
582+
if endswith(name, ".") || endswith(name, r"\s")
583+
return false, "ends with `.` or space"
584+
end
585+
return true, ""
586+
end
587+
588+
function meets_src_names_ok(pkg_code_path)
589+
src = joinpath(pkg_code_path, "src/")
590+
isdir(src) || return false, "`src` directory not found"
591+
for (root, dirs, files) in walkdir(src)
592+
files_dirs = Iterators.flatten((files, dirs))
593+
result = _find_lowercase_duplicates(files_dirs)
594+
if result !== nothing
595+
x = joinpath(root, result[1])
596+
y = joinpath(root, result[2])
597+
return false, "Found files or directories in `src` which will cause problems on case insensitive filesystems: `$x` and `$y`"
598+
end
599+
600+
for f in files_dirs
601+
ok, msg = meets_file_dir_name_check(f)
602+
if !ok
603+
return false, "the name of file or directory $(joinpath(root, f)) $(msg). This can cause problems on some operating systems or file systems."
604+
end
605+
end
606+
end
607+
return true, ""
608+
end
609+
610+
const guideline_src_names_OK = Guideline(;
611+
info="`src` files and directories names are OK",
612+
check=data -> meets_src_names_ok(data.pkg_code_path),
613+
)
614+
552615
function meets_code_can_be_downloaded(registry_head, pkg, version, pr; pkg_code_path)
553616
uuid, package_repo, subdir, tree_hash_from_toml = parse_registry_pkg_info(
554617
registry_head, pkg, version
@@ -909,6 +972,7 @@ function get_automerge_guidelines(
909972
# after `guideline_code_can_be_downloaded` so
910973
# that it can use the downloaded code!
911974
(guideline_version_has_osi_license, check_license),
975+
(guideline_src_names_OK, true),
912976
(guideline_version_can_be_imported, true),
913977
(:update_status, true),
914978
(guideline_dependency_confusion, true),
@@ -948,6 +1012,7 @@ function get_automerge_guidelines(
9481012
# after `guideline_code_can_be_downloaded` so
9491013
# that it can use the downloaded code!
9501014
(guideline_version_has_osi_license, check_license),
1015+
(guideline_src_names_OK, true),
9511016
(guideline_version_can_be_imported, true),
9521017
]
9531018
return guidelines

test/automerge-unit.jl

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,67 @@ end
246246
@test nothing == @test_nowarn AutoMerge.always_assert(1 == 1)
247247
@test_throws AutoMerge.AlwaysAssertionError AutoMerge.always_assert(1 == 2)
248248
end
249+
@testset "_find_lowercase_duplicates" begin
250+
@test AutoMerge._find_lowercase_duplicates(("a", "b", "A")) == ("a", "A")
251+
@test AutoMerge._find_lowercase_duplicates(("ab", "bb", "aB")) == ("ab", "aB")
252+
@test AutoMerge._find_lowercase_duplicates(["ab", "bc"]) === nothing
253+
@test AutoMerge._find_lowercase_duplicates(("ab", "bb", "aB", "AB")) == ("ab", "aB")
254+
end
255+
@testset "meets_file_dir_name_check" begin
256+
@test AutoMerge.meets_file_dir_name_check("hi")[1]
257+
@test AutoMerge.meets_file_dir_name_check("hi bye")[1]
258+
@test AutoMerge.meets_file_dir_name_check("hi.txt")[1]
259+
@test AutoMerge.meets_file_dir_name_check("hi.con")[1]
260+
@test !AutoMerge.meets_file_dir_name_check("con")[1]
261+
@test !AutoMerge.meets_file_dir_name_check("lpt5")[1]
262+
@test !AutoMerge.meets_file_dir_name_check("hi.")[1]
263+
@test !AutoMerge.meets_file_dir_name_check("hi.txt.")[1]
264+
@test !AutoMerge.meets_file_dir_name_check("hi ")[1]
265+
@test !AutoMerge.meets_file_dir_name_check("hi:")[1]
266+
@test !AutoMerge.meets_file_dir_name_check("hi:bye")[1]
267+
@test !AutoMerge.meets_file_dir_name_check("hi?bye")[1]
268+
@test !AutoMerge.meets_file_dir_name_check("hi>bye")[1]
269+
end
270+
@testset "meets_src_names_ok: duplicates" begin
271+
@test !AutoMerge.meets_src_names_ok("DOES NOT EXIST")[1]
272+
tmp = mktempdir()
273+
@test !AutoMerge.meets_src_names_ok(tmp)[1]
274+
mkdir(joinpath(tmp, "src"))
275+
@test AutoMerge.meets_src_names_ok(tmp)[1]
276+
touch(joinpath(tmp, "src", "a"))
277+
@test AutoMerge.meets_src_names_ok(tmp)[1]
278+
279+
if !isdir(joinpath(tmp, "SRC"))
280+
mkdir(joinpath(tmp, "src", "A"))
281+
282+
# dir vs file fails
283+
@test !AutoMerge.meets_src_names_ok(tmp)[1]
284+
rm(joinpath(tmp, "src", "a"))
285+
286+
@test AutoMerge.meets_src_names_ok(tmp)[1]
287+
288+
touch(joinpath(tmp, "src", "A", "b"))
289+
@test AutoMerge.meets_src_names_ok(tmp)[1]
290+
touch(joinpath(tmp, "src", "b"))
291+
# repetition at different levels is OK
292+
@test AutoMerge.meets_src_names_ok(tmp)[1]
293+
294+
touch(joinpath(tmp, "src", "A", "B"))
295+
# repetition at the same level is not OK
296+
@test !AutoMerge.meets_src_names_ok(tmp)[1]
297+
else
298+
@warn "Case insensitive filesystem detected, so skipping some `meets_src_files_distinct` checks."
299+
end
300+
end
301+
@testset "meets_src_names_ok: names" begin
302+
tmp = mktempdir()
303+
mkdir(joinpath(tmp, "src"))
304+
@test AutoMerge.meets_src_names_ok(tmp)[1]
305+
mkdir(joinpath(tmp, "src", "B"))
306+
@test AutoMerge.meets_src_names_ok(tmp)[1]
307+
touch(joinpath(tmp, "src", "B", "con"))
308+
@test !AutoMerge.meets_src_names_ok(tmp)[1]
309+
end
249310
@testset "pull-requests.jl" begin
250311
@testset "regexes" begin
251312
@testset "new_package_title_regex" begin

0 commit comments

Comments
 (0)