Hi!
I have changed the code a little bit to reflect the changes we've been
emailing about yesterday.
Code changes introduce:
* moving Quiet property from SvnCheckoutTask to AbstractSvnTask,
* setting Quiet property's value to false as a default.
The code has been tested and works fine for <svn-update>,
<svn-checkout> and generic <svn> task.
Files that need to be patched live in src\Tasks\Svn folder. I am not a
CVS wizard and I attach the whole files as they are not that big. Is
creating a necessary patch as simple as invoking:
cvs diff file > file.patch
from the command-line?
--
Marcin Hoppe
http://hopson.blogspot.com
// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Clayton Harbour ([EMAIL PROTECTED])
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;
using NAnt.SourceControl.Tasks;
namespace NAnt.Contrib.Tasks.Svn {
/// <summary>
/// A base class for creating tasks for executing CVS client commands on a
/// CVS repository.
/// </summary>
public abstract class AbstractSvnTask : AbstractSourceControlTask {
#region Protected Static Fields
/// <summary>
/// An environment variable that holds path information about where
/// svn is located.
/// </summary>
protected const string SVN_HOME = "SVN_HOME";
/// <summary>
/// The prefix used for command arguments.
/// </summary>
protected const string ARG_PREFIX = "--";
/// <summary>
/// Name of the password file that is used to cash password settings.
/// </summary>
protected static readonly String SVN_PASSFILE =
Path.Combine(".subversion", "auth");
/// <summary>
/// The name of the svn executable.
/// </summary>
protected const string SVN_EXE = "svn.exe";
/// <summary>
/// Environment variable that holds the executable name that is used for
/// ssh communication.
/// </summary>
protected const string SVN_RSH = "RSH";
#endregion Protected Static Fields
#region Protected Instance Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractSvnTask" />
/// class.
/// </summary>
protected AbstractSvnTask () : base() {
SetPropertiesDefaults();
}
#endregion Protected Instance Constructors
#region Protected Instance Properties
/// <summary>
/// The name of the executable.
/// </summary>
protected override string VcsExeName {
get { return SVN_EXE; }
}
/// <summary>
/// The name of the password file.
/// </summary>
protected override string PassFileName {
get { return SVN_PASSFILE; }
}
/// <summary>
/// Name of the home environment variable.
/// </summary>
protected override string VcsHomeEnv {
get { return SVN_HOME; }
}
/// <summary>
/// The name of the ssh/ rsh environment variable.
/// </summary>
protected override string SshEnv {
get { return SVN_RSH; }
}
#endregion Protected Instance Properties
#region Public Instance Properties
/// <summary>
/// The full path of the svn executable.
/// </summary>
public override string ExeName {
get {return this.DeriveVcsFromEnvironment().FullName;}
}
/// <summary>
/// <para>
/// TODO: Add more documentation when I understand all svn root
possibilities/
/// protocols.
/// The svn root is usually in the form of a URL from which the server,
protocol
/// and path information can be derived. Although the path to the
repository
/// can also be determined from this the information is more implicit
/// than explicit. For example a subversion root URL of:
///
/// http://svn.collab.net/repos/svn/trunk/doc/book/tools
///
/// would have the following components:
/// protocol: http/ web_dav
/// username: anonymous
/// servername: svn.collab.net
/// repository: /repos/svn
/// server path: /trunk/doc/book/tools
///
/// In addition the revision path or branch can also be determined
as
/// subversion stores this information as a seperate physical
directory.
/// In this example:
///
/// revision: trunk
/// </para>
/// </summary>
[TaskAttribute("uri", Required=true)]
[StringValidator(AllowEmpty=false)]
public override string Root {
get { return base.Root; }
set { base.Root = StringUtils.ConvertEmptyToNull(value); }
}
/// <summary>
/// The user performing the checkout.
/// </summary>
[TaskAttribute("username", Required=false)]
public string UserName {
get {return ((Option)this.CommandOptions["username"]).Value;}
set {this.SetCommandOption("username",
String.Format("username={0}", value), true);}
}
/// <summary>
/// The pasword to use to login to svn.
/// </summary>
[TaskAttribute("password", Required=false)]
public new string Password {
get {return ((Option)this.CommandOptions["password"]).Value;}
set {this.SetCommandOption("password",
String.Format("password={0}", value), true);}
}
/// <summary>
/// Indicates whether the task should be interactive or not. This is
/// set to <see langword="false" /> by default, and I don't see a reason
/// to expose this to the NAnt task.
/// </summary>
public bool Interactive {
get {return ((Option)this.CommandOptions["interactive"]).IfDefined;}
set {this.SetCommandOption("interactive", "non-interactive",
!value);}
}
/// <summary>
/// The executable to use for ssh communication.
/// </summary>
[TaskAttribute("rsh", Required=false)]
public override FileInfo Ssh {
get { return base.Ssh; }
set { base.Ssh = value; }
}
/// <summary>
/// The command to execute.
/// </summary>
[TaskAttribute("command", Required=false)]
public override string CommandName {
get { return base.CommandName; }
set { base.CommandName = value; }
}
/// <summary>
/// Determines if the output should be minimized. The default is
/// <see langword="true" />.
/// </summary>
[TaskAttribute("quiet", Required=false)]
[BooleanValidator()]
public bool Quiet {
get { return
((Option)this.CommandOptions["quiet"]).IfDefined; }
set { this.SetCommandOption("quiet", "quiet", value); }
}
#endregion Public Instance Properties
#region Override implementation of ExternalProgramBase
/// <summary>
/// Build up the command line arguments, determine which executable is
being
/// used and find the path to that executable and set the working
/// directory.
/// </summary>
/// <param name="process">The process to prepare.</param>
protected override void PrepareProcess (Process process) {
Log(Level.Verbose, "Command name: {0}", this.CommandName);
if (null == this.Arguments || 0 == this.Arguments.Count) {
this.AppendGlobalOptions();
this.Arguments.Add(new Argument(this.CommandName));
if (IsRootUsed) {
this.Arguments.Add(new Argument(this.Root));
}
Log(Level.Debug, "Commandline args null: {0}",
((null == this.CommandLineArguments) ? "yes" : "no"));
if (null == this.CommandLineArguments) {
this.AppendCommandOptions();
}
this.AppendFiles();
}
if (!Directory.Exists(this.DestinationDirectory.FullName)) {
Directory.CreateDirectory(this.DestinationDirectory.FullName);
}
base.PrepareProcess(process);
process.StartInfo.FileName = this.ExeName;
process.StartInfo.WorkingDirectory =
this.DestinationDirectory.FullName;
}
#endregion Override implementation of ExternalProgramBase
#region Private Instance Methods
/// <summary>
/// Determines if the root is used for the command based on
/// the command name. Returns <code>true</code> if the root
/// is used, otherwise returns <code>false</code>.
/// </summary>
private bool IsRootUsed {
get {
if (this.CommandName.IndexOf("co") > -1 ||
this.CommandName.IndexOf("checkout") > -1) {
return true;
}
return false;
}
}
private void AppendGlobalOptions () {
foreach (Option option in this.GlobalOptions.Values) {
if (!option.IfDefined || option.UnlessDefined) {
// skip option
continue;
}
this.AddArg(option.Value);
}
}
/// <summary>
/// Append the command line options or commen names for the options
/// to the generic options collection. This is then piped to the
/// command line as a switch.
/// </summary>
private void AppendCommandOptions () {
foreach (Option option in this.CommandOptions.Values) {
if (!option.IfDefined || option.UnlessDefined) {
// skip option
continue;
}
this.AddArg(option.Value);
}
}
private void AddArg (String arg) {
Arguments.Add(new Argument(string.Format("{0}{1}",
ARG_PREFIX, arg)));
}
/// <summary>
/// Set default values for non-requiered parameters.
/// </summary>
private void SetPropertiesDefaults() {
this.Interactive = false;
this.Quiet = false;
}
#endregion Private Instance Methods
}
}
// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Clayton Harbour ([EMAIL PROTECTED])
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;
namespace NAnt.Contrib.Tasks.Svn {
/// <summary>
/// Executes the svn checkout command.
/// </summary>
/// <example>
/// <para>Checkout Gentle.NET.</para>
/// <code>
/// <![CDATA[
/// <svn-update
/// destination="c:/dev/src/gentle.net"
/// uri="http://www.mertner.com/svn/repos/projects/gentle"
/// recursive="true"
/// quiet="true"
/// username="anonymoose"
/// password="Canada"
/// revision="HEAD"
/// cache-auth="false"
/// config-dir="c:\home"
/// />
/// ]]>
/// </code>
/// </example>
[TaskName("svn-checkout")]
public class SvnCheckoutTask : AbstractSvnTask {
#region Private Instance Fields
private string COMMAND_NAME = "checkout";
#endregion Private Instance Fields
#region Public Instance Constructors
/// <summary>
/// Initialize the task, and set the default parameters.
/// </summary>
public SvnCheckoutTask () : base() {}
#endregion Public Instance Constructors
#region Public Instance Properties
/// <summary>
/// Gets the svn command to execute.
/// </summary>
/// <value>
/// The svn command to execute. The default value is "checkout".
/// </value>
public override string CommandName {
get { return this.COMMAND_NAME; }
set { this.COMMAND_NAME = value; }
}
/// <summary>
/// <see langword="true" /> if the command should be executed
recursively.
/// The default is <see langword="true" />.
/// </summary>
[TaskAttribute("recursive", Required=false)]
[BooleanValidator()]
public bool Recursive {
get {return
((Option)this.CommandOptions["non-recursive"]).IfDefined;}
set {this.SetCommandOption("non-recursive", "non-recursive",
!value);}
}
/// <summary>
/// The revision to checkout. If no revision is specified then
subversion
/// will return the <code>HEAD</code>.
/// </summary>
/// <remarks>
/// A revision argument can be one of:
/// NUMBER revision number
/// "{" DATE "}" revision at start of the date
/// "HEAD" latest in repository
/// "BASE" base rev of item's working copy
/// "COMMITTED" last commit at or before BASE
/// "PREV" revision just before COMMITTED
/// </remarks>
[TaskAttribute("revision", Required=false)]
public string Revision {
get {return ((Option)this.CommandOptions["revision"]).Value;}
set {
string number_regex = @"(\+|-)?[0-9][0-9]*(\.[0-9]*)?";
string date_regex =
@"(?<Month>\d{1,2})/(?<Day>\d{1,2})/(?<Year>(?:\d{4}|\d{2}))";
string magic_ref_regex = @"(HEAD)|(BASE)|(COMMITTED)|(PREV)";
if ((new Regex(number_regex)).IsMatch(value) ||
((new Regex(date_regex)).IsMatch(value)) ||
((new Regex(magic_ref_regex)).IsMatch(value))) {
this.SetCommandOption("revision",
string.Format("revision={0}", value), true);
} else {
throw new
BuildException(string.Format(CultureInfo.InvariantCulture,
"Invalid argument specified: {0}.", value), Location);
}
}
}
/// <summary>
/// <see langword="true" /> if the authentiction token should be cached
/// locally.
/// </summary>
[TaskAttribute("cache-auth", Required=false)]
[BooleanValidator()]
public bool CacheAuth {
get {return ((Option)this.CommandOptions["cache-auth"]).IfDefined;}
set {this.SetCommandOption("cache-auth", "no-auth-cache", !value);}
}
/// <summary>
/// The location of the configuration directory.
/// </summary>
[TaskAttribute("config-dir", Required=false)]
public string ConfigDir {
get {return ((Option)this.CommandOptions["config-dir"]).Value;}
set {this.SetCommandOption("config-dir",
String.Format("config-dir={0}", value), true);
}
}
#endregion
#region Override implementation of ExternalProgramBase
protected override void PrepareProcess (Process process) {
base.PrepareProcess(process);
process.StartInfo.Arguments += " . ";
}
#endregion Override implementation of ExternalProgramBase
}
}