Comment exiger des messages de validation sur le serveur VisualSVN?

Nous avons installé VisualSVN Server comme serveur Subversion sous Windows et nous utilisons Ankhsvn + TortoiseSVN comme clients sur nos postes de travail.

Comment configurer le serveur pour exiger que les messages de validation ne soient pas vides?

SVN utilise un certain nombre de hooks pour accomplir des tâches comme celle-ci.

  • start-commit – s’exécute avant que la transaction de validation ne commence, peut être utilisée pour vérifier les permissions spéciales
  • pre-commit – s’exécute à la fin de la transaction, mais avant la validation. Souvent utilisé pour valider des éléments tels qu’un message de journal de longueur non nulle.
  • post-commit – s’exécute après la post-commit de la transaction. Peut être utilisé pour envoyer des emails ou sauvegarder un référentiel.
  • pre-revprop-change – s’exécute avant une modification de propriété de révision. Peut être utilisé pour vérifier les permissions.
  • post-revprop-change – s’exécute après un changement de propriété de révision. Peut être utilisé pour envoyer par courrier électronique ou sauvegarder ces modifications.

Vous devez utiliser le hook pre-commit . Vous pouvez l’écrire vous-même dans presque toutes les langues sockets en charge par votre plate-forme, mais il existe un certain nombre de scripts sur le Web. Googling “svn precommit hook pour demander un commentaire” J’ai trouvé un couple qui aurait l’air de faire l’affaire:

  • Script Perl
  • Script Python

Je suis content que vous ayez posé cette question. Ceci est notre script de hook pré-commit écrit en commun Windows Batch . Il refuse la validation si le message de journal contient moins de 6 caractères. Il suffit de mettre le pre-commit.bat dans votre répertoire hooks.

pre-commit.bat

 setlocal enabledelayedexpansion set REPOS=%1 set TXN=%2 set SVNLOOK="%VISUALSVN_SERVER%\bin\svnlook.exe" SET M= REM Concatenate all the lines in the commit message FOR /F "usebackq delims==" %%g IN (`%SVNLOOK% log -t %TXN% %REPOS%`) DO SET M=!M!%%g REM Make sure M is defined SET M=0%M% REM Here the 6 is the length we require IF NOT "%M:~6,1%"=="" goto NORMAL_EXIT :ERROR_TOO_SHORT echo "Commit note must be at least 6 letters" >&2 goto ERROR_EXIT :ERROR_EXIT exit /b 1 REM All checks passed, so allow the commit. :NORMAL_EXIT exit 0 

Les réponses techniques à votre question ont déjà été données. Je voudrais append la réponse sociale: “En établissant des normes de message d’engagement avec votre équipe et en les faisant accepter (ou accepter) les raisons pour lesquelles on aurait besoin de messages d’engagement expressifs

J’ai vu tellement de messages de validation indiquant “patch”, “typo”, “fix” ou similaire que j’ai perdu le compte.

Vraiment, expliquez à tout le monde pourquoi vous en avez besoin.

Les exemples pour des raisons sont:

  • Génération de Changenotes (eh bien, cela ferait un bon outil automatique pour appliquer de bons messages si je sais qu’ils seront (avec mon nom) publiquement visibles – ne serait-ce que pour l’équipe)
  • Problèmes de licence : vous devrez peut-être connaître l’origine du code ultérieurement, par exemple si vous souhaitez changer la licence en code (certaines organisations ont même des normes pour le formatage des messages de validation – vous pourriez automatiser la vérification, mais vous pas nécessairement de bons messages de validation avec ceci)
  • Interopérabilité avec d’autres outils , par exemple, les bugtrackers / systèmes de gestion des problèmes qui s’interfacent avec votre contrôle de version et extraient des informations des messages de validation.

J’espère que ça aide, en plus des réponses techniques sur les crochets de pré-engagement.

Voici un exemple de hook de pré-validation en deux parties Batch + PowerShell qui refuse de valider un message de journal contenant moins de 25 caractères.

Placez les deux pre-commit.bat et pre-commit.ps1 dans le dossier des hooks votre référentiel, par exemple C:\Repositories\repository\hooks\

pre-commit.ps1

 # Store hook arguments into variables with mnemonic names $repos = $args[0] $txn = $args[1] # Build path to svnlook.exe $svnlook = "$env:VISUALSVN_SERVER\bin\svnlook.exe" # Get the commit log message $log = (&"$svnlook" log -t $txn $repos) # Check the log message contains non-empty ssortingng $datalines = ($log | where {$_.sortingm() -ne ""}) if ($datalines.length -lt 25) { # Log message is empty. Show the error. [Console]::Error.WriteLine("Commit with empty log message is prohibited.") exit 3 } exit 0 

pre-commit.bat

 @echo off set PWSH=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe %PWSH% -command $input ^| %1\hooks\pre-commit.ps1 %1 %2 if errorlevel 1 exit %errorlevel% 

Note 1: pre-commit.bat est le seul qui puisse être appelé par VisualSVN, puis pre-commit.ps1 est celui qui est appelé par pre-commit.bat .

Note 2: pre-commit.bat peut également être nommé pre-commit.cmd .

Remarque 3: Si vous rencontrez des problèmes d’encodage avec certains caractères accentués et la sortie [Console]::Error.WriteLine , ajoutez par exemple chcp 1252 dans pre-commit.bat , ligne suivante après @echo off .

Ce que VisualSVN vous propose de saisir en tant que hooks sont les “scripts de commande Windows NT”, qui sont essentiellement des fichiers de commandes.

Écrire dans if-then-else dans des fichiers de commandes est très moche et probablement très difficile à déboguer.

Cela ressemblera à ce qui suit (recherche de pre-commit.bat) (non testé):

 SVNLOOK.exe log -t "%2" "%1" | grep.exe "[a-zA-Z0-9]" > nul || GOTO ERROR GOTO OK :ERROR ECHO "Please enter comment and then retry commit!" exit 1 :OK exit 0 

Vous avez besoin d’un grep.exe sur le chemin,% 1 est le chemin d’access à ce référentiel,% 2 le nom du txn sur le point d’être validé. Consultez également le fichier pre-commit.tmpl dans le répertoire hooks de votre référentiel.

Nous utilisons l’excellent outil CS-Script pour nos hooks de pre-commit afin que nous puissions écrire des scripts dans le langage dans lequel nous développons. Voici un exemple qui garantit un message de validation de plus de 10 caractères et garantit que .suo et Les fichiers .user ne sont pas archivés. Vous pouvez également tester les indentations de tabulation / espace, ou appliquer des normes de code réduit à l’archivage, mais faites attention à ce que votre script en fasse trop car vous ne voulez pas ralentir un commit .

 // run from pre-commit.cmd like so: // css.exe /nl /c C:\SVN\Scripts\PreCommit.cs %1 %2 using System; using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; using System.Linq; class PreCommitCS { /// Controls the procedure flow of this script public static int Main(ssortingng[] args) { if (args.Length < 2) { Console.WriteLine("usage: PreCommit.cs repository-path svn-transaction"); Environment.Exit(2); } try { var proc = new PreCommitCS(args[0], args[1]); proc.RunChecks(); if (proc.MessageBuffer.ToString().Length > 0) { throw new CommitException(Ssortingng.Format("Pre-commit hook violation\r\n{0}", proc.MessageBuffer.ToSsortingng())); } } catch (CommitException ex) { Console.WriteLine(ex.Message); Console.Error.WriteLine(ex.Message); throw ex; } catch (Exception ex) { var message = Ssortingng.Format("SCRIPT ERROR! : {1}{0}{2}", "\r\n", ex.Message, ex.StackTrace.ToSsortingng()); Console.WriteLine(message); Console.Error.WriteLine(message); throw ex; } // return success if we didn't throw return 0; } public ssortingng RepoPath { get; set; } public ssortingng SvnTx { get; set; } public SsortingngBuilder MessageBuffer { get; set; } /// Constructor public PreCommitCS(ssortingng repoPath, ssortingng svnTx) { this.RepoPath = repoPath; this.SvnTx = svnTx; this.MessageBuffer = new SsortingngBuilder(); } /// Main logic controller public void RunChecks() { CheckCommitMessageLength(10); // Uncomment for indent checks /* ssortingng[] changedFiles = GetCommitFiles( new ssortingng[] { "A", "U" }, new ssortingng[] { "*.cs", "*.vb", "*.xml", "*.config", "*.vbhtml", "*.cshtml", "*.as?x" }, new ssortingng[] { "*.designer.*", "*.generated.*" } ); EnsureTabIndents(changedFiles); */ CheckForIllegalFileCommits(new ssortingng[] {"*.suo", "*.user"}); } private void CheckForIllegalFileCommits(ssortingng[] filesToExclude) { ssortingng[] illegalFiles = GetCommitFiles( new ssortingng[] { "A", "U" }, filesToExclude, new ssortingng[] {} ); if (illegalFiles.Length > 0) { Echo(Ssortingng.Format("You cannot commit the following files: {0}", Ssortingng.Join(",", illegalFiles))); } } private void EnsureTabIndents(ssortingng[] filesToCheck) { foreach (ssortingng fileName in filesToCheck) { ssortingng contents = GetFileContents(fileName); ssortingng[] lines = contents.Replace("\r\n", "\n").Replace("\r", "\n").Split(new ssortingng[] { "\n" }, SsortingngSplitOptions.None); var linesWithSpaceIndents = Enumerable.Range(0, lines.Length) .Where(i => lines[i].StartsWith(" ")) .Select(i => i + 1) .Take(11) .ToList(); if (linesWithSpaceIndents.Count > 0) { var message = Ssortingng.Format("{0} has spaces for indents on line(s): {1}", fileName, Ssortingng.Join(",", linesWithSpaceIndents)); if (linesWithSpaceIndents.Count > 10) message += "..."; Echo(message); } } } private ssortingng GetFileContents(ssortingng fileName) { ssortingng args = GetSvnLookCommandArgs("cat") + " \"" + fileName + "\""; ssortingng svnlookResults = ExecCmd("svnlook", args); return svnlookResults; } private void CheckCommitMessageLength(int minLength) { ssortingng args = GetSvnLookCommandArgs("log"); ssortingng svnlookResults = ExecCmd("svnlook", args); svnlookResults = (svnlookResults ?? "").Trim(); if (svnlookResults.Length < minLength) { if (svnlookResults.Length > 0) { Echo("Your commit message was too short."); } Echo("Please describe the changes you've made in a commit message in order to successfully commit. Include support ticket number if relevant."); } } private ssortingng[] GetCommitFiles(ssortingng[] changedIds, ssortingng[] includedFiles, ssortingng[] exclusions) { ssortingng args = GetSvnLookCommandArgs("changed"); ssortingng svnlookResults = ExecCmd("svnlook", args); ssortingng[] lines = svnlookResults.Split(new ssortingng[] { "\r", "\n" }, SsortingngSplitOptions.RemoveEmptyEnsortinges); var includedPatterns = (from a in includedFiles select ConvertWildcardPatternToRegex(a)).ToArray(); var excludedPatterns = (from a in exclusions select ConvertWildcardPatternToRegex(a)).ToArray(); var opts = RegexOptions.IgnoreCase; var results = from line in lines let fileName = line.Subssortingng(1).Trim() let changeId = line.Subssortingng(0, 1).ToUpper() where changedIds.Any(x => x.ToUpper() == changeId) && includedPatterns.Any(x => Regex.IsMatch(fileName, x, opts)) && !excludedPatterns.Any(x => Regex.IsMatch(fileName, x, opts)) select fileName; return results.ToArray(); } private ssortingng GetSvnLookCommandArgs(ssortingng cmdType) { ssortingng args = Ssortingng.Format("{0} -t {1} \"{2}\"", cmdType, this.SvnTx, this.RepoPath); return args; } ///  /// Executes a command line call and returns the output from stdout. /// Raises an error is stderr has any output. ///  private ssortingng ExecCmd(ssortingng command, ssortingng args) { Process proc = new Process(); proc.StartInfo.FileName = command; proc.StartInfo.Arguments = args; proc.StartInfo.UseShellExecute = false; proc.StartInfo.CreateNoWindow = true; proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; proc.Start(); var stdOut = proc.StandardOutput.ReadToEnd(); var stdErr = proc.StandardError.ReadToEnd(); proc.WaitForExit(); // Do after ReadToEnd() call per: http://chrfalch.blogspot.com/2008/08/processwaitforexit-never-completes.html if (!ssortingng.IsNullOrWhiteSpace(stdErr)) { throw new Exception(ssortingng.Format("Error: {0}", stdErr)); } return stdOut; } ///  /// Writes the ssortingng provided to the Message Buffer - this fails /// the commit and this message is presented to the comitter. ///  private void Echo(object s) { this.MessageBuffer.AppendLine((s == null ? "" : s.ToSsortingng())); } ///  /// Takes a wildcard pattern (like *.bat) and converts it to the equivalent RegEx pattern ///  /// The wildcard pattern to convert. Syntax similar to VB's Like operator with the addition of pipe ("|") delimited patterns. /// A regex pattern that is equivalent to the wildcard pattern supplied private ssortingng ConvertWildcardPatternToRegex(ssortingng wildcardPattern) { if (ssortingng.IsNullOrEmpty(wildcardPattern)) return ""; // Split on pipe ssortingng[] patternParts = wildcardPattern.Split('|'); // Turn into regex pattern that will match the whole ssortingng with ^$ SsortingngBuilder patternBuilder = new SsortingngBuilder(); bool firstPass = true; patternBuilder.Append("^"); foreach (ssortingng part in patternParts) { ssortingng rePattern = Regex.Escape(part); // add support for ?, #, *, [...], and [!...] rePattern = rePattern.Replace("\\[!", "[^"); rePattern = rePattern.Replace("\\[", "["); rePattern = rePattern.Replace("\\]", "]"); rePattern = rePattern.Replace("\\?", "."); rePattern = rePattern.Replace("\\*", ".*"); rePattern = rePattern.Replace("\\#", "\\d"); if (firstPass) { firstPass = false; } else { patternBuilder.Append("|"); } patternBuilder.Append("("); patternBuilder.Append(rePattern); patternBuilder.Append(")"); } patternBuilder.Append("$"); ssortingng result = patternBuilder.ToSsortingng(); if (!IsValidRegexPattern(result)) { throw new ArgumentException(ssortingng.Format("Invalid pattern: {0}", wildcardPattern)); } return result; } private bool IsValidRegexPattern(ssortingng pattern) { bool result = true; try { new Regex(pattern); } catch { result = false; } return result; } } public class CommitException : Exception { public CommitException(ssortingng message) : base(message) { } } 

Voici un script JScript Windows que vous pouvez utiliser en spécifiant le hook comme:

 %SystemRoot%\System32\CScript.exe //nologo <..path..to..script> %1 %2 

C’est assez facile à lire, alors allez-y, faites une expérience.

BTW, la raison en est que JScript ne repose sur aucun autre outil (Perl, CygWin, etc.) à installer.

 if (WScript.Arguments.Length < 2) { WScript.StdErr.WriteLine("Repository Hook Error: Missing parameters. Should be REPOS_PATH then TXN_NAME, eg %1 %2 in pre-commit hook"); WScript.Quit(-1); } var oShell = new ActiveXObject("WScript.Shell"); var oFSO = new ActiveXObject("Scripting.FileSystemObject"); var preCommitStdOut = oShell.ExpandEnvironmentStrings("%TEMP%\\PRE-COMMIT." + WScript.Arguments(1) + ".stdout"); var preCommitStdErr = oShell.ExpandEnvironmentStrings("%TEMP%\\PRE-COMMIT." + WScript.Arguments(1) + ".stderr"); var commandLine = "%COMSPEC% /C \"C:\\Program Files\\VisualSVN Server\\bin\\SVNLook.exe\" log -t "; commandLine += WScript.Arguments(1); commandLine += " "; commandLine += WScript.Arguments(0); commandLine += "> " + preCommitStdOut + " 2> " + preCommitStdErr; // Run Synchronously, don't show a window // WScript.Echo("About to run: " + commandLine); var exitCode = oShell.Run(commandLine, 0, true); var fsOUT = oFSO.GetFile(preCommitStdOut).OpenAsTextStream(1); var fsERR = oFSO.GetFile(preCommitStdErr).OpenAsTextStream(1); var stdout = fsOUT && !fsOUT.AtEndOfStream ? fsOUT.ReadAll() : ""; var stderr = fsERR && !fsERR.AtEndOfStream ? fsERR.ReadAll() : ""; if (stderr.length > 0) { WScript.StdErr.WriteLine("Error with SVNLook: " + stderr); WScript.Quit(-2); } // To catch naught commiters who write 'blah' as their commit message if (stdout.length < 5) { WScript.StdErr.WriteLine("Please provide a commit message that describes why you've made these changes."); WScript.Quit(-3); } WScript.Quit(0); 

Utilisez ce hook pré-validation sous Windows. Il est écrit dans Windows Batch et utilise l’utilitaire de ligne de commande grep pour vérifier la longueur de la validation.

 svnlook log -t "%2" "%1" | c:\tools\grep -c "[a-zA-z0-9]" > nul if %ERRORLEVEL% NEQ 1 exit 0 echo Please enter a check-in comment 1>&2 exit 1 

Rappelez-vous que vous aurez besoin d’une copie de grep, je recommande la version gnu tools .

Remarque: ceci s’applique uniquement à TortoiseSVN

Cliquez simplement avec le bouton droit sur le niveau supérieur de votre référentiel. Dans le menu contextuel, sélectionnez TortoiseSVN, puis Propriétés pour afficher cette boîte de dialog:

entrer la description de l'image ici

Cliquez sur le bouton Nouveau en bas à droite et sélectionnez Taille des journaux. Entrez le nombre de caractères requirejs pour Commit et Lock (10 dans l’exemple ci-dessous).

entrer la description de l'image ici

Faites un Commit du répertoire de niveau supérieur que vous venez de modifier. Maintenant, votre référentiel exige que tous les utilisateurs affichent des commentaires avant de valider les modifications.

Je pense que vous devrez configurer un hook pré-validation qui vérifiera le message.

En effet, en googlant le premier résultat que j’ai obtenu, il s’agissait d’un script de pré-validation perl pour faire exactement ce que vous vouliez.

Exemple de hook pré-validation Perl (non testé)

Avant d’append des hooks de validation à mon serveur, je viens de dissortingbuer svnprops aux clients TortoiseSVN.

Donc, comme alternative:

Dans TortoiseSVN -> Nom de la propriété Properties – add / set tsvn:logminsize manière appropriée.

Ce n’est bien sûr pas une garantie sur le serveur car les clients / utilisateurs peuvent choisir de ne pas le faire, mais vous pouvez dissortingbuer des fichiers svnprops si vous le souhaitez. De cette façon, les utilisateurs n’ont pas à définir leurs propres valeurs – vous pouvez les fournir à tous les utilisateurs.

Cela fonctionne également pour des choses comme bugtraq: les parameters permettant de lier les éléments de suivi des problèmes dans les journaux.