Skip to content

Fix repository checkout ownership on Linux when running as non-root #5223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/Agent.Sdk/Util/IOUtil.cs
Original file line number Diff line number Diff line change
@@ -303,6 +303,9 @@ public static void MoveDirectory(string sourceDir, string targetDir, string stag

// move staging to target
Directory.Move(stagingDir, targetDir);

// On Linux, ensure the target directory has the correct ownership
EnsureDirectoryOwnership(targetDir);
}

/// <summary>
@@ -466,6 +469,9 @@ public static void CopyDirectory(string source, string target, CancellationToken
target: Path.Combine(target, subDir.Name),
cancellationToken: cancellationToken);
}

// On Linux, ensure the target directory has the correct ownership
EnsureDirectoryOwnership(target);
}

public static void ValidateExecutePermission(string directory)
@@ -569,6 +575,58 @@ private static void RemoveReadOnly(FileSystemInfo item)
}
}

public static void EnsureDirectoryOwnership(string directoryPath)
{
// Only apply ownership fix on Linux platforms
if (!PlatformUtil.RunningOnLinux)
{
return;
}

try
{
// First get the current user and group ID
System.Diagnostics.Process idProcess = new System.Diagnostics.Process();
idProcess.StartInfo.FileName = "id";
idProcess.StartInfo.Arguments = "-u";
idProcess.StartInfo.UseShellExecute = false;
idProcess.StartInfo.RedirectStandardOutput = true;
idProcess.Start();
string userId = idProcess.StandardOutput.ReadToEnd().Trim();
idProcess.WaitForExit();

idProcess = new System.Diagnostics.Process();
idProcess.StartInfo.FileName = "id";
idProcess.StartInfo.Arguments = "-g";
idProcess.StartInfo.UseShellExecute = false;
idProcess.StartInfo.RedirectStandardOutput = true;
idProcess.Start();
string groupId = idProcess.StandardOutput.ReadToEnd().Trim();
idProcess.WaitForExit();

// Now use chown with the explicit UIDs
System.Diagnostics.Process chownProcess = new System.Diagnostics.Process();
chownProcess.StartInfo.FileName = "chown";
chownProcess.StartInfo.Arguments = $"-R {userId}:{groupId} \"{directoryPath}\"";
chownProcess.StartInfo.UseShellExecute = false;
chownProcess.StartInfo.RedirectStandardOutput = true;
chownProcess.StartInfo.RedirectStandardError = true;
chownProcess.Start();
chownProcess.WaitForExit();

// Log error if the command failed
if (chownProcess.ExitCode != 0)
{
Trace.Error($"Failed to set directory ownership for '{directoryPath}'. Exit code: {chownProcess.ExitCode}");
}
}
catch (Exception ex)
{
// Log but don't fail the operation if ownership change fails
Trace.Error($"Exception while trying to set directory ownership for '{directoryPath}': {ex.Message}");
}
}

public static string GetDirectoryName(string path, PlatformUtil.OS platform)
{
if (string.IsNullOrWhiteSpace(path))
6 changes: 6 additions & 0 deletions src/Agent.Worker/Build/BuildDirectoryManager.cs
Original file line number Diff line number Diff line change
@@ -256,6 +256,12 @@ public void CreateDirectory(IExecutionContext executionContext, string descripti
executionContext.Debug($"Creating {description}: '{path}'");
Trace.Info($"Creating {description}.");
Directory.CreateDirectory(path);

// Ensure correct ownership on Linux platforms
if (PlatformUtil.RunningOnLinux && Directory.Exists(path))
{
IOUtil.EnsureDirectoryOwnership(path);
}
}
}