mirror of
https://github.com/JuliusBrussee/caveman.git
synced 2026-08-11 13:21:09 +02:00
This commit is contained in:
@@ -17,6 +17,16 @@ SKIP_EXTENSIONS = {
|
||||
".dockerfile", ".makefile", ".csv", ".ini", ".cfg",
|
||||
}
|
||||
|
||||
# Well-known build/config files that carry no (or a misleading) extension —
|
||||
# `Dockerfile` has no suffix so `.dockerfile` above never matches it, and
|
||||
# `CMakeLists.txt` would ride the compressible `.txt` rule. Checked by
|
||||
# basename before any extension rule.
|
||||
KNOWN_CODE_FILENAMES = {
|
||||
"dockerfile", "makefile", "gnumakefile", "jenkinsfile", "vagrantfile",
|
||||
"rakefile", "gemfile", "justfile", "procfile", "brewfile",
|
||||
"cmakelists.txt",
|
||||
}
|
||||
|
||||
# Patterns that indicate a line is code
|
||||
CODE_PATTERNS = [
|
||||
re.compile(r"^\s*(import |from .+ import |require\(|const |let |var )"),
|
||||
@@ -67,6 +77,10 @@ def detect_file_type(filepath: Path) -> str:
|
||||
"""
|
||||
ext = filepath.suffix.lower()
|
||||
|
||||
# Known code filenames win over any extension rule
|
||||
if filepath.name.lower() in KNOWN_CODE_FILENAMES:
|
||||
return "code"
|
||||
|
||||
# Extension-based classification
|
||||
if ext in COMPRESSIBLE_EXTENSIONS:
|
||||
return "natural_language"
|
||||
@@ -82,6 +96,10 @@ def detect_file_type(filepath: Path) -> str:
|
||||
|
||||
lines = text.splitlines()[:50]
|
||||
|
||||
# Shebang means executable script, never prose
|
||||
if text.startswith("#!"):
|
||||
return "code"
|
||||
|
||||
if _is_json_content(text[:10000]):
|
||||
return "config"
|
||||
if _is_yaml_content(lines):
|
||||
|
||||
@@ -17,6 +17,16 @@ SKIP_EXTENSIONS = {
|
||||
".dockerfile", ".makefile", ".csv", ".ini", ".cfg",
|
||||
}
|
||||
|
||||
# Well-known build/config files that carry no (or a misleading) extension —
|
||||
# `Dockerfile` has no suffix so `.dockerfile` above never matches it, and
|
||||
# `CMakeLists.txt` would ride the compressible `.txt` rule. Checked by
|
||||
# basename before any extension rule.
|
||||
KNOWN_CODE_FILENAMES = {
|
||||
"dockerfile", "makefile", "gnumakefile", "jenkinsfile", "vagrantfile",
|
||||
"rakefile", "gemfile", "justfile", "procfile", "brewfile",
|
||||
"cmakelists.txt",
|
||||
}
|
||||
|
||||
# Patterns that indicate a line is code
|
||||
CODE_PATTERNS = [
|
||||
re.compile(r"^\s*(import |from .+ import |require\(|const |let |var )"),
|
||||
@@ -67,6 +77,10 @@ def detect_file_type(filepath: Path) -> str:
|
||||
"""
|
||||
ext = filepath.suffix.lower()
|
||||
|
||||
# Known code filenames win over any extension rule
|
||||
if filepath.name.lower() in KNOWN_CODE_FILENAMES:
|
||||
return "code"
|
||||
|
||||
# Extension-based classification
|
||||
if ext in COMPRESSIBLE_EXTENSIONS:
|
||||
return "natural_language"
|
||||
@@ -82,6 +96,10 @@ def detect_file_type(filepath: Path) -> str:
|
||||
|
||||
lines = text.splitlines()[:50]
|
||||
|
||||
# Shebang means executable script, never prose
|
||||
if text.startswith("#!"):
|
||||
return "code"
|
||||
|
||||
if _is_json_content(text[:10000]):
|
||||
return "config"
|
||||
if _is_yaml_content(lines):
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
"""Tests for detect.py file-type classification (issue #600).
|
||||
|
||||
Extensionless build files (`Dockerfile`, `Makefile`) and shebang scripts
|
||||
used to fall through to the content heuristic and come back as
|
||||
compressible natural language — so `/caveman-compress Dockerfile` would
|
||||
overwrite a Dockerfile with caveman prose. These tests pin the basename
|
||||
and shebang guards.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "skills" / "caveman-compress"))
|
||||
|
||||
from scripts.detect import detect_file_type, should_compress # noqa: E402
|
||||
|
||||
|
||||
DOCKERFILE_BODY = """FROM node:20-bookworm
|
||||
WORKDIR /app
|
||||
COPY package.json .
|
||||
RUN npm install
|
||||
CMD ["node", "index.js"]
|
||||
"""
|
||||
|
||||
MAKEFILE_BODY = """all: build
|
||||
|
||||
build:
|
||||
\tgo build -o bin/app ./cmd/app
|
||||
|
||||
clean:
|
||||
\trm -rf bin
|
||||
"""
|
||||
|
||||
SHEBANG_BODY = """#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "deploying"
|
||||
"""
|
||||
|
||||
PROSE_BODY = """This project collects notes about our deployment process.
|
||||
|
||||
The main goal is to keep the steps simple enough that anyone on the
|
||||
team can run a release without asking for help. Start by reading the
|
||||
overview, then follow the checklist in order.
|
||||
"""
|
||||
|
||||
|
||||
class DetectFileTypeTests(unittest.TestCase):
|
||||
def _write(self, dirpath, name, body):
|
||||
p = Path(dirpath) / name
|
||||
p.write_text(body, encoding="utf-8")
|
||||
return p
|
||||
|
||||
def test_dockerfile_is_code(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
p = self._write(tmp, "Dockerfile", DOCKERFILE_BODY)
|
||||
self.assertEqual(detect_file_type(p), "code")
|
||||
self.assertFalse(should_compress(p))
|
||||
|
||||
def test_makefile_is_code(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
p = self._write(tmp, "Makefile", MAKEFILE_BODY)
|
||||
self.assertEqual(detect_file_type(p), "code")
|
||||
self.assertFalse(should_compress(p))
|
||||
|
||||
def test_known_names_case_insensitive(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
for name in ("dockerfile", "MAKEFILE", "Jenkinsfile", "Vagrantfile"):
|
||||
p = self._write(tmp, name, "irrelevant body\n")
|
||||
self.assertEqual(detect_file_type(p), "code", name)
|
||||
|
||||
def test_cmakelists_txt_not_compressible_despite_txt_extension(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
p = self._write(tmp, "CMakeLists.txt", "add_executable(app main.c)\n")
|
||||
self.assertEqual(detect_file_type(p), "code")
|
||||
self.assertFalse(should_compress(p))
|
||||
|
||||
def test_shebang_script_is_code(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
p = self._write(tmp, "deploy", SHEBANG_BODY)
|
||||
self.assertEqual(detect_file_type(p), "code")
|
||||
self.assertFalse(should_compress(p))
|
||||
|
||||
def test_extensionless_prose_still_compressible(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
p = self._write(tmp, "NOTES", PROSE_BODY)
|
||||
self.assertEqual(detect_file_type(p), "natural_language")
|
||||
self.assertTrue(should_compress(p))
|
||||
|
||||
def test_markdown_still_compressible(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
p = self._write(tmp, "README.md", PROSE_BODY)
|
||||
self.assertEqual(detect_file_type(p), "natural_language")
|
||||
self.assertTrue(should_compress(p))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user