Skip to content

Commit

Permalink
Merge pull request #15 from michaelmsonne/add-checkforlogwriteaccess
Browse files Browse the repository at this point in the history
Add new feature and bug fixes and more logging and support for the old organization URL format
  • Loading branch information
michaelmsonne authored Sep 22, 2024
2 parents 1912f2f + dee5116 commit d55f294
Show file tree
Hide file tree
Showing 11 changed files with 515 additions and 22 deletions.
4 changes: 2 additions & 2 deletions AdvancedInstaller/AzureDevOpsBackup Installer.aip
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT Type="Advanced Installer" CreateVersion="20.2" version="21.9" PreviousModules="professional" Modules="enterprise" RootPath="." Language="en" Id="{9B0F5C3A-F049-4A7F-A776-DAA7871CB253}">
<DOCUMENT Type="Advanced Installer" CreateVersion="20.2" version="22.0" PreviousModules="professional" Modules="enterprise" RootPath="." Language="en" Id="{9B0F5C3A-F049-4A7F-A776-DAA7871CB253}">
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
<ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
Expand Down Expand Up @@ -41,7 +41,7 @@
<ROW Property="ProductCode" Value="1033:{AC9929FD-4693-47B7-830B-AE484BD4C29B} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="Azure DevOps Backup"/>
<ROW Property="ProductVersion" Value="1.1.0.0" Options="24"/>
<ROW Property="ProductVersion" Value="1.1.1.0" Options="24"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
<ROW Property="UpgradeCode" Value="{B5566068-564D-4227-A015-483DA03A0F0E}"/>
<ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
Expand Down
343 changes: 343 additions & 0 deletions AdvancedInstaller/AzureDevOpsBackup Installer.back(21.9).aip

Large diffs are not rendered by default.

80 changes: 72 additions & 8 deletions AzureDevOpsBackup/Class/FileLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ internal class FileLogger

public static bool WriteOnlyErrorsToEventLog { get; set; } = true;

// Sets the App name for the log function
//public static string AppName { get; set; } = Globals.AppName; // "Unknown",;
// Flag to prevent recursive logging
private static bool _isLogging;

// Set date format short
public static string DateFormat { get; set; } = "dd-MM-yyyy";
Expand Down Expand Up @@ -69,39 +69,99 @@ public static void Message(string logText, EventType type, int id)
// Save message to logfile
private static void AppendMessageToFile(string mess, EventType type, string dtf, string path, int id)
{
if (_isLogging) return; // Prevent recursive logging
_isLogging = true;

try
{
// Check if file exists else create it
if (!Directory.Exists(Files.LogFilePath))
Directory.CreateDirectory(Files.LogFilePath);
try
{
if (!Directory.Exists(Files.LogFilePath))
{
Directory.CreateDirectory(Files.LogFilePath);
//Console.WriteLine("Directory to log files created: " + Files.LogFilePath);
}
}
catch (Exception ex)
{
if (WriteToEventLog)
{
AddMessageToEventLog($"Error creating log directory, {ex.Message}", EventType.Error, dtf, path, id);
AddMessageToEventLog("Writing log file has been disabled.", EventType.Information, dtf, path, id);

Console.WriteLine("No write access to log directory. Writing log file has been disabled.");
}
WriteToFile = false;
return;
}

// Check if we have write access to the directory
if (!HasWriteAccessToDirectory(Files.LogFilePath))
{
if (WriteToEventLog)
{
AddMessageToEventLog("No write access to log directory.", EventType.Error, dtf, path, id);
AddMessageToEventLog("Writing log file has been disabled.", EventType.Information, dtf, path, id);

Console.WriteLine("No write access to log directory. Writing log file has been disabled.");
}
WriteToFile = false;
return;
}
var str = type.ToString().Length > 7 ? "\t" : "\t\t";
if (!File.Exists(path))
{
using (var text = File.CreateText(path))
text.WriteLine(
$"{(object)dtf} - [EventID {(object)id.ToString()}] {(object)type.ToString()}{(object)str}{(object)mess}");
$"{dtf} - [EventID {id}] {type}{str}{mess}");
}
else
{
using (var streamWriter = File.AppendText(path))
streamWriter.WriteLine(
$"{(object)dtf} - [EventID {(object)id.ToString()}] {(object)type.ToString()}{(object)str}{(object)mess}");
$"{dtf} - [EventID {id}] {type}{str}{mess}");
}
}
catch (Exception ex)
{
if (!WriteToEventLog)
return;
AddMessageToEventLog($"Error writing to log file, {ex.Message}", EventType.Error, dtf, path, 0);
AddMessageToEventLog("Writing log file have been disabled.", EventType.Information, dtf, path, 0);
AddMessageToEventLog("Writing log file has been disabled.", EventType.Information, dtf, path, 0);
WriteToFile = false;
}
finally
{
_isLogging = false;
}
}

private static bool HasWriteAccessToDirectory(string path)
{
try
{
// Attempt to get a list of security permissions from the directory.
// This will raise an exception if the path is read-only or do not have access.
Directory.GetAccessControl(path);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
catch (Exception)
{
return false;
}
}

// Save message to Windows event log
private static void AddMessageToEventLog(string mess, EventType type, string dtf, string path, int id)
{
if (_isLogging) return; // Prevent recursive logging
_isLogging = true;

try
{
if (type != EventType.Error && WriteOnlyErrorsToEventLog)
Expand Down Expand Up @@ -145,6 +205,10 @@ private static void AddMessageToEventLog(string mess, EventType type, string dtf
WriteToEventLog = false;
}
}
finally
{
_isLogging = false;
}
}
}
}
}
17 changes: 16 additions & 1 deletion AzureDevOpsBackup/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,22 @@ private static async Task Main(string[] args)

// Base GET API
//const string version = "api-version=7.0";
string baseUrl = "https://dev.azure.com/" + args[Array.IndexOf(args, "--org") + 1] + "/";
string baseUrl;
if (args.Contains("--oldurl"))
{
baseUrl = "https://" + args[Array.IndexOf(args, "--org") + 1] + ".visualstudio.com/";

// Show warning to user about old URL format
Message("Starting connection to Azure DevOps API via the old url format - recommended to update this. Read more here: https://learn.microsoft.com/en-us/azure/devops/release-notes/2018/sep-10-azure-devops-launch#administration", EventType.Information, 1000);
Console.WriteLine("Starting connection to Azure DevOps API via the old url format - recommended to update this.");
}
else
{
// Show warning to user about old URL format
Message("Starting connection to Azure DevOps API via the new url format - all is good!", EventType.Information, 1000);
Console.WriteLine("Starting connection to Azure DevOps API via the new url format - all is good!");
baseUrl = "https://dev.azure.com/" + args[Array.IndexOf(args, "--org") + 1] + "/";
}

// Create a new instance of the SecureArgumentHandler class to handle the encryption and decryption of the token
SecureArgumentHandler handler = new SecureArgumentHandler();
Expand Down
4 changes: 2 additions & 2 deletions AzureDevOpsBackup/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyVersion("1.1.1.0")]
[assembly: AssemblyFileVersion("1.1.1.0")]
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Class\ApplicationInfo.cs" />
<Compile Include="Class\ApplicationStatus.cs" />
<Compile Include="Class\DisplayHelp.cs" />
<Compile Include="Class\FileLogger.cs" />
<Compile Include="Class\Files.cs" />
Expand Down
21 changes: 21 additions & 0 deletions AzureDevOpsBackupUnzipTool/Class/ApplicationStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using static AzureDevOpsBackupUnzipTool.Class.FileLogger;

namespace AzureDevOpsBackupUnzipTool.Class
{
internal class ApplicationStatus
{
public static void ApplicationStartMessage()
{
// Log start of program
Message($"Welcome to {Globals.AppName}, v." + Globals._vData + " by " + Globals._companyName, EventType.Information, 1000);
Console.WriteLine($"\nWelcome to {Globals.AppName}, v." + Globals._vData + " by " + Globals._companyName + "\n");
}
public static void ApplicationEndMessage()
{
// Log end of program
Message($"End of application - {Globals.AppName}, v." + Globals._vData + "\n", EventType.Information, 1000);
Console.WriteLine($"\nEnd of application - {Globals.AppName}, v. {Globals._vData}\n");
}
}
}
46 changes: 39 additions & 7 deletions AzureDevOpsBackupUnzipTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ static void Main(string[] args)
// Get application information
ApplicationInfo.GetExeInfo();

// Log start of program to log
ApplicationStatus.ApplicationStartMessage();

// Set Global Logfile properties for log
DateFormat = "dd-MM-yyyy";
DateTimeFormat = "dd-MM-yyyy HH:mm:ss";
WriteOnlyErrorsToEventLog = false;
WriteToEventLog = false;
WriteToFile = true;

// Create log folder if not exist
LocalFolderTasks.CreateLogFolder();

Expand Down Expand Up @@ -127,42 +137,58 @@ static void Main(string[] args)
{
// Do
UnzipProject(zipFilePath, outputDirectory, jsonFilePath);

// Log
Message("Unzipping completed successfully!", EventType.Information, 1000);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("\n> Unzipping completed successfully!\n");
Console.ResetColor();
}
catch (Exception ex)
{
// Log
Message($"An error occurred: {ex.Message}", EventType.Error, 1001);
Console.WriteLine($"An error occurred: {ex.Message}");
}

// Log end of program to console
ApplicationStatus.ApplicationEndMessage();
}

private static void UnzipProject(string zipFilePath, string outputDirectory, string jsonFilePath)
{
// Log start of the unzipping process
Message("Starting the unzipping process...", EventType.Information, 1000);
Console.WriteLine("Starting the unzipping process...");

// Read JSON data
string jsonData = File.ReadAllText(jsonFilePath);
var rootObject = JsonConvert.DeserializeObject<RootObject>(jsonData);
var items = rootObject.Value;

// Log the number of items to be processed
Message($"Number of items to process: {items.Count}", EventType.Information, 1000);
Console.WriteLine($"Number of items to process: {items.Count}");

// Initialize a counter for processed items
int processedItemCount = 0;

// Open the zip archive
using (var archive = ZipFile.OpenRead(zipFilePath))
{
foreach (var item in items)
{
// Increment the counter
processedItemCount++;

// Get the destination path
string destinationPath = Path.GetFullPath(Path.Combine(outputDirectory, item.Path.TrimStart('/')));

// Log the item being processed with the counter
Message($"Processing item {processedItemCount}/{items.Count}: {item.Path} (Type: {item.GitObjectType})", EventType.Information, 1000);
Console.WriteLine($"Processing item {processedItemCount}/{items.Count}: {item.Path} (Type: {item.GitObjectType})");

// Check if the item is a folder or a file
if (item.GitObjectType == "tree")
{
// If folder data
Console.WriteLine($"Unzipping Git repository folder data: '{destinationPath}'...");

Message($"Unzipping Git repository folder data: '{destinationPath}'...", EventType.Information, 1000);

try
{
// Create backup folder if not exist
Expand Down Expand Up @@ -233,11 +259,17 @@ private static void UnzipProject(string zipFilePath, string outputDirectory, str
// If the entry is null
else
{
// Log
Message($"Entry with ObjectId '{item.ObjectId}' not found in the zip archive.", EventType.Error, 1001);
Console.WriteLine($"Entry with ObjectId '{item.ObjectId}' not found in the zip archive.");
}
}
}
}

// Log end of the unzipping process
Message("Unzipping process completed.", EventType.Information, 1000);
Console.WriteLine("Unzipping process completed.");
}
}
}
4 changes: 2 additions & 2 deletions AzureDevOpsBackupUnzipTool/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.1.1.0")]
[assembly: AssemblyFileVersion("1.1.1.0")]
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## [1.1.1.0] - 22-09-2024

AzureDevOpsBackup:
### Added
- Added support for the old organization URL format (https://organization.visualstudio.com) in the backup tool, so the tool now supports both the new and old URL format if you have not updated your organization URL to the new format (https://dev.azure.com/{organization}).

### Fixed
- Fixed a bug in the backup tool, where the tool would crach if it not could create the log file in the log folder.
- Fixed a bug where the log files folder was not created if it not exists.

AzureDevOpsBackupUnzipTool:
### Added
- Added a bit more logging to the unzip tool to show the progress of the unzip process.

## [1.1.0.0] - 10-08-2024

Major update with new features and bug fixes
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ Note we are also saving the original JSON item list we got from the repository c
(Remember to run --tokenfile <token.data> to create the file first beside the application .exe!)
- <token.data>: Replace this with your Azure DevOps personal access token.
- --org: Replace this with your Azure DevOps organization name.
- --oldurl: Specify this option if you are using the old organization URL format (https://organization.visualstudio.com) if you have not updated your organization URL to the new format (https://dev.azure.com/{organization}).

Read more here: https://learn.microsoft.com/en-us/azure/devops/release-notes/2018/sep-10-azure-devops-launch#administration
- --backup: Specify the local directory where you want to store the Azure DevOps repository backups.
- --server: IP address or domain name of the SMTP server for sending email notifications.
- --port: The SMTP server port
Expand Down

0 comments on commit d55f294

Please sign in to comment.