Добавлен новый модуль rules для управления правилами отслеживания файлов в виде записей для gitignore
- create: создание правил из конфигурационного файла:
    - tracking - отслеживаемые файлы/пути
    - ignore - правила gitignore
- update: обновление существующих правил из конфигурационного файла
- reset: сброс изменений в правилах - откат на момент до внесения изменений в конфигурационный файл
- clear: очистить файл с правилами
- show: просмотр правил
- save: сохранить правила без возможности сброса (reset)
			
			
This commit is contained in:
		
							parent
							
								
									db9a6be9f4
								
							
						
					
					
						commit
						5797e83f07
					
				
					 2 changed files with 165 additions and 0 deletions
				
			
		| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										164
									
								
								source/snag/core/rules.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								source/snag/core/rules.d
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,164 @@
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					import std.file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SnagRules {
 | 
				
			||||||
 | 
						private string[] _rules;
 | 
				
			||||||
 | 
						private SnagConfig _config;
 | 
				
			||||||
 | 
						private string[] _baseCommand;
 | 
				
			||||||
 | 
						private string _gitignore;
 | 
				
			||||||
 | 
						private string _gitignoreBak;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private auto git(string[] command, string message, string separator = ":\n\t") {
 | 
				
			||||||
 | 
							auto result = execute(_baseCommand ~ command);
 | 
				
			||||||
 | 
							if (result.status)
 | 
				
			||||||
 | 
								throw new SnagException(
 | 
				
			||||||
 | 
									message ~ separator ~ result.output.split('\n')[0]
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
							_gitignore = config.git.buildPath("info/exclude");
 | 
				
			||||||
 | 
							_gitignoreBak = _gitignore ~ ".bak";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_baseCommand = format(
 | 
				
			||||||
 | 
								"git --git-dir=%s --work-tree=%s",
 | 
				
			||||||
 | 
								config.git, config.project
 | 
				
			||||||
 | 
							).split();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							generate();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void restoreUntracked() {
 | 
				
			||||||
 | 
							git(
 | 
				
			||||||
 | 
								["diff", "--cached", "--name-only"],
 | 
				
			||||||
 | 
								"Failed to retrieve the list of files removed from tracking"
 | 
				
			||||||
 | 
							).output.split('\n')[0..$-1].each!(
 | 
				
			||||||
 | 
								file =>	git(
 | 
				
			||||||
 | 
									["add", "-f", file],
 | 
				
			||||||
 | 
									"Failed to restore the file to tracking"
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void removeUntracked() {
 | 
				
			||||||
 | 
							git(
 | 
				
			||||||
 | 
								["ls-files", "-i", "-c", "--exclude-standard"],
 | 
				
			||||||
 | 
								"Failed to get the list of files to remove from tracking"
 | 
				
			||||||
 | 
							).output.split('\n')[0..$-1].each!(
 | 
				
			||||||
 | 
								file =>	git(
 | 
				
			||||||
 | 
									["rm", "--cached", file],
 | 
				
			||||||
 | 
									"Failed to remove file from tracking"
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void create() {
 | 
				
			||||||
 | 
							auto file = File(_gitignore, "w");
 | 
				
			||||||
 | 
							_rules.each!(rule => file.writeln(rule));
 | 
				
			||||||
 | 
							file.close();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void update(bool remove) {
 | 
				
			||||||
 | 
							if (!_gitignoreBak.exists) {
 | 
				
			||||||
 | 
								readText(_gitignore).splitLines().equal(_rules) &&
 | 
				
			||||||
 | 
									throw new SnagException(
 | 
				
			||||||
 | 
										"Rule configuration is up-to-date and doesn't require updating"
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
								copy(_gitignore, _gitignoreBak);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							restoreUntracked();		
 | 
				
			||||||
 | 
							create();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							remove && removeUntracked();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void reset() {
 | 
				
			||||||
 | 
							!_gitignoreBak.exists &&
 | 
				
			||||||
 | 
								throw new SnagException(
 | 
				
			||||||
 | 
									"No rule changes to reset"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							restoreUntracked();
 | 
				
			||||||
 | 
							rename(_gitignoreBak, _gitignore);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void clear() {
 | 
				
			||||||
 | 
							!readText(_gitignore).splitLines().length &&
 | 
				
			||||||
 | 
								throw new SnagException(
 | 
				
			||||||
 | 
									"The configuration has no rules"
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							!_gitignoreBak.exists && copy(_gitignore, _gitignoreBak);
 | 
				
			||||||
 | 
							restoreUntracked();
 | 
				
			||||||
 | 
							File(_gitignore, "w").close();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void show(bool config) {
 | 
				
			||||||
 | 
							if (config)
 | 
				
			||||||
 | 
								_rules.join('\n').writeln;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								readText(_gitignore).write;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void save() {
 | 
				
			||||||
 | 
							if (!_gitignoreBak.exists) {
 | 
				
			||||||
 | 
								writeln("The rules are up to date");
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							remove(_gitignoreBak);
 | 
				
			||||||
 | 
							writeln("The rules have been saved");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue