Update issue #678
Status: Fixed The CLI now returns error codes. git-svn-id: https://duplicati.googlecode.com/svn/trunk@1420 59da171f-624f-0410-aa54-27559c288bec
This commit is contained in:
@@ -27,7 +27,7 @@ namespace Duplicati.CommandLine
|
||||
{
|
||||
private static readonly string[] COMMANDS_AS_ARGUMENTS = new string[] { "delete-all-but-n-full", "delete-all-but-n", "delete-older-than" };
|
||||
|
||||
static void Main(string[] args)
|
||||
static int Main(string[] args)
|
||||
{
|
||||
bool verboseErrors = false;
|
||||
try
|
||||
@@ -70,7 +70,7 @@ namespace Duplicati.CommandLine
|
||||
|
||||
cargs.RemoveAt(0);
|
||||
UnitTest.RunTest(cargs.ToArray(), options);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace Duplicati.CommandLine
|
||||
if (options.ContainsKey(internaloption))
|
||||
{
|
||||
Console.WriteLine(Strings.Program.InternalOptionUsedError, internaloption);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
//After checking for internal options, we set the filter option
|
||||
@@ -104,7 +104,7 @@ namespace Duplicati.CommandLine
|
||||
{
|
||||
case "purge-signature-cache":
|
||||
Library.Main.Interface.PurgeSignatureCache(options);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Duplicati.CommandLine
|
||||
Help.PrintUsage(cargs[1], options);
|
||||
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
Console.WriteLine(string.Join("\r\n", new List<string>(Duplicati.Library.Main.Interface.ListCurrentFiles(target, options)).ToArray()));
|
||||
@@ -170,7 +170,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
Console.WriteLine(string.Join("\r\n", Duplicati.Library.Main.Interface.ListSourceFolders(target, options) ?? new string[0]));
|
||||
@@ -182,7 +182,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
List<KeyValuePair<Duplicati.Library.Main.RSync.RSyncDir.PatchFileType, string>> files = Duplicati.Library.Main.Interface.ListActualSignatureFiles(cargs[0], options);
|
||||
@@ -248,7 +248,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
List<Duplicati.Library.Main.ManifestEntry> entries = Duplicati.Library.Main.Interface.ParseFileList(cargs[0], options);
|
||||
@@ -281,7 +281,7 @@ namespace Duplicati.CommandLine
|
||||
if (!int.TryParse(target, out n) || n < 0)
|
||||
{
|
||||
Console.WriteLine(string.Format(Strings.Program.IntegerParseError, target));
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
options["delete-all-but-n-full"] = n.ToString();
|
||||
@@ -292,7 +292,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
if (source.Trim().ToLower() == "delete-all-but-n")
|
||||
@@ -309,7 +309,7 @@ namespace Duplicati.CommandLine
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(string.Format(Strings.Program.TimeParseError, target, ex.Message));
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
options["delete-older-than"] = target;
|
||||
@@ -320,7 +320,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
Console.WriteLine(Duplicati.Library.Main.Interface.DeleteOlderThan(cargs[0], options));
|
||||
@@ -332,7 +332,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
Console.WriteLine(Duplicati.Library.Main.Interface.Cleanup(cargs[0], options));
|
||||
@@ -344,7 +344,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
Duplicati.Library.Main.Interface.CreateFolder(cargs[0], options);
|
||||
@@ -357,7 +357,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, DateTime>> results = Duplicati.Library.Main.Interface.FindLastFileVersion(cargs[0], options);
|
||||
@@ -372,7 +372,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 1)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 1);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
List<KeyValuePair<Duplicati.Library.Main.BackupEntryBase, Exception>> results = Duplicati.Library.Main.Interface.VerifyBackup(cargs[0], options);
|
||||
@@ -412,7 +412,7 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 2)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 2);
|
||||
return;
|
||||
return 200;
|
||||
}
|
||||
|
||||
Console.WriteLine(Duplicati.Library.Main.Interface.Restore(source, target.Split(System.IO.Path.PathSeparator), options));
|
||||
@@ -422,11 +422,33 @@ namespace Duplicati.CommandLine
|
||||
if (cargs.Count != 2)
|
||||
{
|
||||
PrintWrongNumberOfArguments(cargs, 2);
|
||||
return;
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
|
||||
Console.WriteLine(Duplicati.Library.Main.Interface.Backup(source.Split(System.IO.Path.PathSeparator), target, options));
|
||||
string result = Duplicati.Library.Main.Interface.Backup(source.Split(System.IO.Path.PathSeparator), target, options);
|
||||
Console.WriteLine(result);
|
||||
|
||||
Dictionary<string, string> tmp = ParseDuplicatiOutput(result);
|
||||
|
||||
//Interrupted = 50
|
||||
if (tmp.ContainsKey("PartialBackup"))
|
||||
return 50;
|
||||
|
||||
//Completed with warnings = 2
|
||||
if (tmp.ContainsKey("NumberOfWarnings"))
|
||||
return 2;
|
||||
|
||||
//Success, but no upload = 1
|
||||
if (tmp.ContainsKey("BytesUploaded"))
|
||||
{
|
||||
long s;
|
||||
if (long.TryParse(tmp["BytesUploaded"], out s) && s == 0)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Normal operation = 0
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -452,9 +474,22 @@ namespace Duplicati.CommandLine
|
||||
Console.Error.WriteLine(Strings.Program.UnhandledInnerException, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
//Error = 100
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> ParseDuplicatiOutput(string output)
|
||||
{
|
||||
System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("(?<key>[^\\:]+)\\:(?<value>[^\\n]*)", System.Text.RegularExpressions.RegexOptions.Singleline);
|
||||
Dictionary<string, string> res = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
|
||||
foreach (System.Text.RegularExpressions.Match m in re.Matches(output))
|
||||
res[m.Groups["key"].Value.Trim()] = m.Groups["value"].Value.Trim();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void PrintWrongNumberOfArguments(List<string> args, int expected)
|
||||
{
|
||||
Console.WriteLine(Strings.Program.WrongNumberOfCommandsError_v2, args.Count, expected, args.Count == 0 ? "" : "\"" + string.Join("\", \"", args.ToArray()) + "\"");
|
||||
|
||||
@@ -23,7 +23,7 @@ See duplicati.commandline.exe help <topic> for more information.
|
||||
|
||||
Formats: date, time, size, encryption, compression
|
||||
|
||||
Advanced options: advanced, filter, <option> (e.g. --auto-cleanup)
|
||||
Advanced options: advanced, returncodes, filter, <option> (e.g. --auto-cleanup)
|
||||
|
||||
http://www.duplicati.com/ Version: %VERSION%
|
||||
|
||||
@@ -383,3 +383,17 @@ Options:
|
||||
Duplicati offers more options than listed in the other topics. Those additional options should only be used with care. For normal operation none of them should ever be required. Here is a complete list of options supported by Duplicati:
|
||||
|
||||
%MAINOPTIONS%
|
||||
|
||||
# =============================================================================
|
||||
> duplicati.commandline.exe help returncodes
|
||||
> duplicati.commandline.exe help exitcodes
|
||||
> duplicati.commandline.exe help statuscodes
|
||||
|
||||
Duplicati reports the following return/exit codes:
|
||||
0 - Success
|
||||
1 - Successful backup, but no files were changed
|
||||
2 - Successful backup, with warnings
|
||||
50 - Backup uploaded some files, but did not finish
|
||||
100 - An error occurred
|
||||
200 - Invalid commandline arguments found
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace Duplicati.Library.Main
|
||||
private long m_tooLargeFiles;
|
||||
private long m_filesWithError;
|
||||
private bool m_full = false;
|
||||
private bool m_partialBackup = false;
|
||||
private string m_typeReason = null;
|
||||
|
||||
public BackupStatistics(DuplicatiOperationMode operationMode)
|
||||
@@ -150,6 +151,12 @@ namespace Duplicati.Library.Main
|
||||
set { m_filesWithError = value; }
|
||||
}
|
||||
|
||||
public bool PartialBackup
|
||||
{
|
||||
get { return m_partialBackup; }
|
||||
set { m_partialBackup = true; }
|
||||
}
|
||||
|
||||
public void SetTypeReason(string reason)
|
||||
{
|
||||
m_typeReason = reason;
|
||||
@@ -178,6 +185,8 @@ namespace Duplicati.Library.Main
|
||||
sb.Append("Unprocessed : " + this.UnprocessedFiles.ToString(System.Globalization.CultureInfo.InvariantCulture) + "\r\n");
|
||||
sb.Append("TooLargeFiles : " + this.FilesTooLarge.ToString(System.Globalization.CultureInfo.InvariantCulture) + "\r\n");
|
||||
sb.Append("FilesWithError : " + this.FilesWithError.ToString(System.Globalization.CultureInfo.InvariantCulture) + "\r\n");
|
||||
if (m_partialBackup)
|
||||
sb.Append("PartialBackup : true" + "\r\n");
|
||||
|
||||
sb.Append(base.ToString());
|
||||
return sb.ToString();
|
||||
|
||||
@@ -861,6 +861,7 @@ namespace Duplicati.Library.Main
|
||||
|
||||
Logging.Log.WriteMessage(string.Format(Strings.Interface.PartialUploadMessage, backend.ManifestUploads, ex.Message), Logging.LogMessageType.Warning);
|
||||
bs.LogError(string.Format(Strings.Interface.PartialUploadMessage, backend.ManifestUploads, ex.Message), ex);
|
||||
bs.PartialBackup = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user