Skip to content

Commit

Permalink
Handling vscode insiders devcontainers (dotnet#7111)
Browse files Browse the repository at this point in the history
* Handling vscode insiders devcontainers

Fixes dotnet#7110

* handling the potential for multiple settings file locations
  • Loading branch information
aaronpowell authored Jan 15, 2025
1 parent 2780cdc commit d7e27b1
Showing 1 changed file with 54 additions and 41 deletions.
95 changes: 54 additions & 41 deletions src/Aspire.Hosting/Devcontainers/DevcontainerSettingsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ namespace Aspire.Hosting.Devcontainers;
internal class DevcontainerSettingsWriter(ILogger<DevcontainerSettingsWriter> logger, IOptions<CodespacesOptions> codespaceOptions, IOptions<DevcontainersOptions> devcontainerOptions)
{
private const string CodespaceSettingsPath = "/home/vscode/.vscode-remote/data/Machine/settings.json";
private const string LocalDevcontainerSettingsPath = "/home/vscode/.vscode-server/data/Machine/settings.json";
private const string VSCodeServerPath = "/home/vscode/.vscode-server";
private const string VSCodeInsidersServerPath = "/home/vscode/.vscode-server-insiders";
private const string LocalDevcontainerSettingsPath = "data/Machine/settings.json";
private const string PortAttributesFieldName = "remote.portsAttributes";
private const int WriteLockTimeoutMs = 2000;
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1);
Expand All @@ -22,64 +24,75 @@ public async Task SetPortAttributesAsync(int port, string protocol, string label
ArgumentNullException.ThrowIfNullOrEmpty(protocol);
ArgumentNullException.ThrowIfNullOrEmpty(label);

var settingsPath = GetSettingsPath();
var settingsPaths = GetSettingsPaths();

var acquired = await _writeLock.WaitAsync(WriteLockTimeoutMs, cancellationToken).ConfigureAwait(false);

if (!acquired)
foreach (var settingsPath in settingsPaths)
{
throw new DistributedApplicationException($"Failed to acquire semaphore for settings file: {settingsPath}");
}
var acquired = await _writeLock.WaitAsync(WriteLockTimeoutMs, cancellationToken).ConfigureAwait(false);

await EnsureSettingsFileExists(settingsPath, cancellationToken).ConfigureAwait(false);
if (!acquired)
{
throw new DistributedApplicationException($"Failed to acquire semaphore for settings file: {settingsPath}");
}

var settingsContent = await File.ReadAllTextAsync(settingsPath, cancellationToken).ConfigureAwait(false);
var settings = (JsonObject)JsonObject.Parse(settingsContent)!;
await EnsureSettingsFileExists(settingsPath, cancellationToken).ConfigureAwait(false);

JsonObject? portsAttributes;
if (!settings.TryGetPropertyValue(PortAttributesFieldName, out var portsAttributesNode))
{
portsAttributes = new JsonObject();
settings.Add(PortAttributesFieldName, portsAttributes);
}
else
{
portsAttributes = (JsonObject)portsAttributesNode!;
}
var settingsContent = await File.ReadAllTextAsync(settingsPath, cancellationToken).ConfigureAwait(false);
var settings = (JsonObject)JsonObject.Parse(settingsContent)!;

var portAsString = port.ToString(CultureInfo.InvariantCulture);
JsonObject? portsAttributes;
if (!settings.TryGetPropertyValue(PortAttributesFieldName, out var portsAttributesNode))
{
portsAttributes = new JsonObject();
settings.Add(PortAttributesFieldName, portsAttributes);
}
else
{
portsAttributes = (JsonObject)portsAttributesNode!;
}

JsonObject? portAttributes;
if (!portsAttributes.TryGetPropertyValue(portAsString, out var portAttributeNode))
{
portAttributes = new JsonObject();
portsAttributes.Add(portAsString, portAttributes);
}
else
{
portAttributes = (JsonObject)portAttributeNode!;
}
var portAsString = port.ToString(CultureInfo.InvariantCulture);

JsonObject? portAttributes;
if (!portsAttributes.TryGetPropertyValue(portAsString, out var portAttributeNode))
{
portAttributes = new JsonObject();
portsAttributes.Add(portAsString, portAttributes);
}
else
{
portAttributes = (JsonObject)portAttributeNode!;
}

portAttributes["label"] = label;
portAttributes["protocol"] = protocol;
portAttributes["onAutoForward"] = "notify";
portAttributes["label"] = label;
portAttributes["protocol"] = protocol;
portAttributes["onAutoForward"] = "notify";

settingsContent = settings.ToString();
await File.WriteAllTextAsync(settingsPath, settingsContent, cancellationToken).ConfigureAwait(false);
settingsContent = settings.ToString();
await File.WriteAllTextAsync(settingsPath, settingsContent, cancellationToken).ConfigureAwait(false);

_writeLock.Release();
_writeLock.Release();
}

string GetSettingsPath()
IEnumerable<string> GetSettingsPaths()
{
// For some reason the machine settings path is different between Codespaces and local Devcontainers
// so we decide which one to use here based on the options.
if (codespaceOptions.Value.IsCodespace)
{
return CodespaceSettingsPath;
yield return CodespaceSettingsPath;
}
else if (devcontainerOptions.Value.IsDevcontainer)
{
return LocalDevcontainerSettingsPath;
if (Directory.Exists(VSCodeServerPath))
{
yield return Path.Combine(VSCodeServerPath, LocalDevcontainerSettingsPath);
}

if (Directory.Exists(VSCodeInsidersServerPath))
{
yield return Path.Combine(VSCodeInsidersServerPath, LocalDevcontainerSettingsPath);
}
}
else
{
Expand Down Expand Up @@ -110,4 +123,4 @@ async Task EnsureSettingsFileExists(string path, CancellationToken cancellationT
}
}
}
}
}

0 comments on commit d7e27b1

Please sign in to comment.