Question

Le serveur VisualSVN est configuré en tant que serveur Subversion sous Windows et nous utilisons Ankhsvn + TortoiseSVN en tant que clients sur nos postes de travail.

Comment pouvez-vous configurer le serveur pour exiger que les messages de validation soient non vides?

Était-ce utile?

La solution

VisualSVN Server 3.9 fournit le crochet de pré-validation de vérification / logSage VisualSVNServerHooks.exe qui vous aide à rejeter les validations avec des messages de journal vides ou courts. Consultez l'article KB140: validation des messages de journal de validation dans VisualSVN Server pour obtenir des instructions.

Outre le VisualSVNServerHooks.exe intégré, VisualSVN Server et SVN utilisent généralement un nombre de points d'ancrage pour accomplir de telles tâches.

Vous devez utiliser le hook Pre-commit . Vous pouvez l'écrire vous-même dans pratiquement toutes les langues prises en charge par votre plate-forme, mais il existe un certain nombre de scripts sur le Web. Googling & svn crochet prédéfini pour demander un commentaire " J'ai trouvé un couple qui semblait vouloir faire l'affaire:

Autres conseils

Je suis content que vous ayez posé cette question. Il s'agit de notre script de hook pré-validation écrit dans le lot Windows commun. Il refuse la validation si le message de journal contient moins de 6 caractères. Il suffit de placer le pre-commit.bat dans votre répertoire de 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. J'aimerais ajouter la réponse sociale, qui est: "En établissant des normes de message de validation avec votre équipe et en leur demandant de donner leur accord (ou d'accepter) les raisons pour lesquelles on aurait besoin de messages expressifs de validation"

J'ai vu tellement de messages de validation portant la mention "correctif", "typo", "correctif" et "correctif". ou similaire que j'ai perdu le compte.

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

Des exemples de raisons sont:

  • Changenotes générées (bon, cela constituerait en fait un outil automatique pratique pour appliquer de bons messages si je sais qu'ils seront (avec mon nom) visibles publiquement - si seulement pour l'équipe).
  • Problèmes de licence : vous devrez peut-être connaître l'origine du code ultérieurement, par exemple. si vous souhaitez modifier la licence de votre code (certaines organisations ont même des normes pour la mise en forme des messages de validation; vous pouvez automatiser la vérification, mais vous n'obtiendrez pas nécessairement des bons validations avec cette commande. )
  • Interopérabilité avec d'autres outils , par exemple. des systèmes de gestion des bogues / problèmes qui s’interfacent avec votre contrôle de version et extraient des informations des messages de validation.

J'espère que cela vous aidera, en plus des réponses techniques concernant les crochets précommis.

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

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

pré-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 string
$datalines = ($log | where {

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

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

pré-commit.ps1

@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%

pre-commit.bat

<*>

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

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

Remarque 3: Si vous rencontrez des problèmes d'encodage avec des 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 .

.trim() -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

<*>

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

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

Remarque 3: Si vous rencontrez des problèmes d'encodage avec des 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 d'entrer comme points d'ancrage est un "script de commande Windows NT", qui est essentiellement un fichier de commandes.

L'écriture if-then-else dans des fichiers de commandes est très laide 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 fichier grep.exe sur le chemin,% 1 est le chemin d'accès à 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 des hooks de votre référentiel.

Nous utilisons l'excellent outil CS-Script pour nos hooks de pré-validation afin de pouvoir écrire des scripts dans la langue dans laquelle nous développons. Voici un exemple qui garantit qu’un message de validation compte plus de 10 caractères et que les fichiers .suo et .user ne sont pas archivés. Vous pouvez également tester les retraits de tabulation / espace ou Respectez peu les normes de code à l’enregistrement, mais veillez à ce que votre script en fasse trop, car vous ne voulez pas ralentir la validation.

// 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 {

  /// <summary>Controls the procedure flow of this script</summary>
  public static int Main(string[] 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(String.Format("Pre-commit hook violation\r\n{0}", proc.MessageBuffer.ToString()));
      }
    }
    catch (CommitException ex) {
      Console.WriteLine(ex.Message);
      Console.Error.WriteLine(ex.Message);
      throw ex;
    }
    catch (Exception ex) {
      var message = String.Format("SCRIPT ERROR! : {1}{0}{2}", "\r\n", ex.Message, ex.StackTrace.ToString());
      Console.WriteLine(message);
      Console.Error.WriteLine(message);
      throw ex;
    }

    // return success if we didn't throw
    return 0;
  }

  public string RepoPath { get; set; }
  public string SvnTx { get; set; }
  public StringBuilder MessageBuffer { get; set; }

  /// <summary>Constructor</summary>
  public PreCommitCS(string repoPath, string svnTx) {
    this.RepoPath = repoPath;
    this.SvnTx = svnTx;
    this.MessageBuffer = new StringBuilder();
  }

  /// <summary>Main logic controller</summary>
  public void RunChecks() {
    CheckCommitMessageLength(10);

    // Uncomment for indent checks
    /*
    string[] changedFiles = GetCommitFiles(
      new string[] { "A", "U" },
      new string[] { "*.cs", "*.vb", "*.xml", "*.config", "*.vbhtml", "*.cshtml", "*.as?x" },
      new string[] { "*.designer.*", "*.generated.*" }
    );
    EnsureTabIndents(changedFiles);
    */

    CheckForIllegalFileCommits(new string[] {"*.suo", "*.user"});
  }

  private void CheckForIllegalFileCommits(string[] filesToExclude) {
    string[] illegalFiles = GetCommitFiles(
      new string[] { "A", "U" },
      filesToExclude,
      new string[] {}
    );
    if (illegalFiles.Length > 0) {
      Echo(String.Format("You cannot commit the following files: {0}", String.Join(",", illegalFiles)));
    }
  }

  private void EnsureTabIndents(string[] filesToCheck) {
    foreach (string fileName in filesToCheck) {
      string contents = GetFileContents(fileName);
      string[] lines = contents.Replace("\r\n", "\n").Replace("\r", "\n").Split(new string[] { "\n" }, StringSplitOptions.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 = String.Format("{0} has spaces for indents on line(s): {1}", fileName, String.Join(",", linesWithSpaceIndents));
        if (linesWithSpaceIndents.Count > 10) message += "...";
        Echo(message);
      }
    }
  }

  private string GetFileContents(string fileName) {
    string args = GetSvnLookCommandArgs("cat") + " \"" + fileName + "\"";
    string svnlookResults = ExecCmd("svnlook", args);
    return svnlookResults;
  }

  private void CheckCommitMessageLength(int minLength) {
    string args = GetSvnLookCommandArgs("log");
    string 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 string[] GetCommitFiles(string[] changedIds, string[] includedFiles, string[] exclusions) {
    string args = GetSvnLookCommandArgs("changed");
    string svnlookResults = ExecCmd("svnlook", args);
    string[] lines = svnlookResults.Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
    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.Substring(1).Trim()
      let changeId = line.Substring(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 string GetSvnLookCommandArgs(string cmdType) {
    string args = String.Format("{0} -t {1} \"{2}\"", cmdType, this.SvnTx, this.RepoPath);
    return args;
  }

  /// <summary>
  /// Executes a command line call and returns the output from stdout.
  /// Raises an error is stderr has any output.
  /// </summary>
  private string ExecCmd(string command, string 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 (!string.IsNullOrWhiteSpace(stdErr)) {
      throw new Exception(string.Format("Error: {0}", stdErr));
    }

    return stdOut;
  }

  /// <summary>
  /// Writes the string provided to the Message Buffer - this fails
  /// the commit and this message is presented to the comitter.
  /// </summary>
  private void Echo(object s) {
    this.MessageBuffer.AppendLine((s == null ? "" : s.ToString()));
  }

  /// <summary>
  /// Takes a wildcard pattern (like *.bat) and converts it to the equivalent RegEx pattern
  /// </summary>
  /// <param name="wildcardPattern">The wildcard pattern to convert.  Syntax similar to VB's Like operator with the addition of pipe ("|") delimited patterns.</param>
  /// <returns>A regex pattern that is equivalent to the wildcard pattern supplied</returns>
  private string ConvertWildcardPatternToRegex(string wildcardPattern) {
    if (string.IsNullOrEmpty(wildcardPattern)) return "";

    // Split on pipe
    string[] patternParts = wildcardPattern.Split('|');

    // Turn into regex pattern that will match the whole string with ^$
    StringBuilder patternBuilder = new StringBuilder();
    bool firstPass = true;
    patternBuilder.Append("^");
    foreach (string part in patternParts) {
      string 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("<*>quot;);

    string result = patternBuilder.ToString();
    if (!IsValidRegexPattern(result)) {
      throw new ArgumentException(string.Format("Invalid pattern: {0}", wildcardPattern));
    }
    return result;
  }

  private bool IsValidRegexPattern(string pattern) {
    bool result = true;
    try {
      new Regex(pattern);
    }
    catch {
      result = false;
    }
    return result;
  }
}

public class CommitException : Exception {
  public CommitException(string message) : base(message) {
  }
}

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

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

C'est assez facile à lire, alors faites une expérience.

BTW, la raison de le faire dans JScript est qu’elle 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, e.g. %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 de pré-validation sous Windows. Il est écrit dans Windows Batch et utilise un utilitaire de ligne de commande grep pour vérifier la longueur de 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

N'oubliez pas que vous aurez besoin d'une copie de grep, je recommande la version de l'outil GNU .

Remarque: cela ne s'applique qu'à TortoiseSVN

.

Cliquez simplement avec le bouton droit de la souris 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 dialogue:

entrer la description de l

Cliquez sur le bouton Nouveau en bas à droite, puis sélectionnez Taille du journal. Entrez le nombre de caractères que vous souhaitez exiger pour la validation et le verrouillage (10 dans l'exemple ci-dessous).

entrer la description de l

Faites une validation à partir du répertoire de niveau supérieur que vous venez de modifier. Désormais, votre référentiel nécessite que tous les utilisateurs commentent avant de valider les modifications.

Avant d'ajouter des points de connexion à mon serveur, je venais de distribuer svnprops aux clients TortoiseSVN.

Donc, au lieu de cela:

Dans TortoiseSVN - > Propriétés nom de la propriété - ajouter / définir tsvn: logminsize de manière appropriée.

Ceci n’est bien sûr pas garanti sur le serveur car les clients / utilisateurs peuvent choisir de ne pas le faire, mais vous pouvez distribuer les fichiers svnprops si vous le souhaitez. Ainsi, les utilisateurs ne doivent pas définir leurs propres valeurs - vous pouvez les fournir à tous les utilisateurs.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top