Промежуточный набросок для управления правилами

This commit is contained in:
Alexander Zhirov 2025-05-27 03:53:59 +03:00
parent db9a6be9f4
commit 573f1d7d51
Signed by: alexander
GPG key ID: C8D8BE544A27C511
6 changed files with 135 additions and 2 deletions

2
.vscode/launch.json vendored
View file

@ -11,7 +11,7 @@
"name": "Build & Debug DUB project", "name": "Build & Debug DUB project",
"cwd": "${command:dubWorkingDirectory}", "cwd": "${command:dubWorkingDirectory}",
"program": "bin/${command:dubTarget}", "program": "bin/${command:dubTarget}",
"args": ["list"] "args": ["create"]
} }
] ]
} }

View file

@ -10,5 +10,15 @@
"postsnag": [ "postsnag": [
"/usr/bin/ls", "/usr/bin/ls",
"/usr/local/bin/script.sh" "/usr/local/bin/script.sh"
] ],
"rules": {
"tracking": [
"/etc/systemd/*.conf",
"/usr/exit"
],
"ignore": [
"/usr/exit/.gitignore",
"/usr/exit/dd"
]
}
} }

View file

@ -21,6 +21,7 @@ int main(string[] args)
.optional .optional
) )
) )
.add(new Command("retracking", "Tracking rules update"))
.add(new Command("status", "Checking the status of tracked files")) .add(new Command("status", "Checking the status of tracked files"))
.add(new Command("diff", "Show changed data")) .add(new Command("diff", "Show changed data"))
.add(new Command("import", "Import snapshot from a tar.gz archive") .add(new Command("import", "Import snapshot from a tar.gz archive")
@ -160,6 +161,9 @@ int main(string[] args)
i.option("author", ""), i.option("author", ""),
i.option("email", "") i.option("email", "")
) )
)
.on("retracking", e =>
(new SnagRules(config)).create()
); );
} catch (SnagException e) { } catch (SnagException e) {
e.print(); e.print();

View file

@ -5,6 +5,8 @@ import std.file;
import std.path; import std.path;
import std.string; import std.string;
import snag.lib; import snag.lib;
import std.algorithm;
import std.array;
import snag.config.exception; import snag.config.exception;
@ -13,6 +15,8 @@ class SnagConfig {
private string _project; private string _project;
private string _email; private string _email;
private string _author; private string _author;
private string[] _tracking;
private string[] _ignore;
this(string configFile) { this(string configFile) {
string jsonText; string jsonText;
@ -93,10 +97,34 @@ class SnagConfig {
throw new SnagConfigException( throw new SnagConfigException(
"The \"author\" parameter must contain an author name" "The \"author\" parameter must contain an author name"
); );
if ("rules" in jsonData) {
if (jsonData["rules"].type != JSONType.object)
throw new SnagConfigException(
"The \"rules\" parameter must be an object"
);
auto rules = jsonData["rules"];
if ("tracking" in rules) {
if (rules["tracking"].type != JSONType.array)
throw new SnagConfigException(
"The \"tracking\" parameter must be an array containing a set of paths to tracked files"
);
_tracking = rules["tracking"].array.map!(item => item.str).array;
}
if ("ignore" in rules) {
if (rules["ignore"].type != JSONType.array)
throw new SnagConfigException(
"The \"ignore\" parameter must contain a gitignore rule"
);
_ignore = rules["ignore"].array.map!(item => item.str).array;
}
}
} }
@property string git() const { return _git; } @property string git() const { return _git; }
@property string project() const { return _project; } @property string project() const { return _project; }
@property string email() const { return _email; } @property string email() const { return _email; }
@property string author() const { return _author; } @property string author() const { return _author; }
@property const(string[]) tracking() const { return _tracking; }
@property const(string[]) ignore() const { return _ignore; }
} }

View file

@ -2,3 +2,4 @@ module snag.core;
public import snag.core.core; public import snag.core.core;
public import snag.core.exception; public import snag.core.exception;
public import snag.core.rules;

90
source/snag/core/rules.d Normal file
View file

@ -0,0 +1,90 @@
module snag.core.rules;
import snag.config;
import snag.core.exception;
import std.algorithm;
import std.array;
import std.path;
import std.stdio;
import std.string;
import std.conv;
import std.container;
import std.process;
class SnagRules {
private string[] _rules;
private SnagConfig _config;
private string[] _baseCommand;
private string[] generateGitignoreRules(string finalPath) {
string[] rules;
string[] parts = finalPath.split("/").filter!(p => !p.empty).array;
if (parts.length == 0) return rules;
rules ~= "/*";
rules ~= "!/" ~ parts[0];
rules ~= "/" ~ parts[0] ~ "/*";
if (parts.length > 1) {
string currentPath = "/" ~ parts[0];
foreach (i; 1 .. parts.length) {
currentPath ~= "/" ~ parts[i];
rules ~= "!" ~ currentPath;
if (i < parts.length.to!int - 1) {
rules ~= currentPath ~ "/*";
}
}
}
return rules;
}
private void generate() {
string[] rules;
auto tempRules = new RedBlackTree!string;
_config.tracking.each!(
track => rules ~= generateGitignoreRules(track)
);
rules.each!((rule) {
if (rule in tempRules) return;
tempRules.insert(rule);
_rules ~= rule;
});
_rules ~= _config.ignore;
}
this(SnagConfig config) {
_config = config;
_baseCommand = format(
"git --git-dir=%s --work-tree=%s",
config.git, config.project
).split();
}
void create() {
auto result = execute(_baseCommand ~ ["rev-parse", "--git-dir"]);
result.status &&
throw new SnagException(
"A problem occurred while checking the repository: "
~ result.output.strip('\n')
);
generate();
string gitignore = _config.git.buildPath("info/exclude");
File file = File(gitignore, "w");
_rules.each!(rule => file.writeln(rule));
}
// git ls-files -i -c --exclude-standard -z | xargs -0 git rm --cached
// git ls-files -i -c --exclude-standard
// git rm --cached <path-to-file>
}