Skip to content
Draft
Show file tree
Hide file tree
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
14 changes: 9 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ Arcade SDK is the core infrastructure tooling used across the .NET ecosystem for
**NEVER CANCEL BUILDS OR TESTS** - they may take 90+ minutes. Always use appropriate timeouts.

```bash

# Full restore, build, and test - TAKES 90+ MINUTES - NEVER CANCEL
timeout 6000 ./build.sh --restore --build
# Set timeout to 100+ minutes (6000 seconds) for build commands
Expand All @@ -35,8 +34,6 @@ timeout 1800 ./build.sh --restore

# Clean build artifacts
./build.sh --clean
```

### Platform-Specific Commands
- **Linux/macOS**: `./build.sh`, `./test.sh`, `./restore.sh`
- **Windows**: `Build.cmd`, `Test.cmd`, `Restore.cmd`
Expand All @@ -62,15 +59,13 @@ timeout 1800 ./build.sh --restore
- **Microsoft.DotNet.PackageTesting**: Automated package validation testing

## Build Artifacts Structure
```
artifacts/
├── bin/ # Compiled binaries by project/configuration
├── packages/ # Generated NuGet packages (Shipping/NonShipping)
├── TestResults/ # Unit and integration test results
├── log/ # Build logs and binary logs (.binlog)
├── tmp/ # Temporary build artifacts
└── toolset/ # Downloaded build tools and dependencies
```

## Validation and Testing

Expand All @@ -88,6 +83,9 @@ timeout 6000 ./build.sh --restore --build --configuration Release --test
- **SDK Tests**: Validate Arcade SDK works in sample projects
- **Packaging Tests**: Ensure generated packages are valid

### Writing Tests
- When writing tests in this repo, **do not implement new mock file system wrappers**; use the existing `Microsoft.Arcade.Test.Common.MockFileSystem`. Update the shared `IFileSystem`/`MockFileSystem` to support binary-safe operations needed for in-place updates, rather than adding separate binary storage dictionaries or per-test filesystem adapters.

## Common Development Tasks

### Adding New Build Tasks
Expand Down Expand Up @@ -166,3 +164,9 @@ timeout 6000 ./build.sh --restore --build --configuration Release --test
- **Discussions**: Use dotnet/arcade discussions for questions
- **Documentation**: See `/Documentation/` folder for detailed guides
- **Contact**: @dotnet/dnceng team for infrastructure issues

## Project-Specific Notes
- Microsoft.DotNet.RecursiveSigning architecture and design references now live in `src/Microsoft.DotNet.RecursiveSigning/docs/`. Consult the markdown files in that directory for component guidance before editing related code.

### Iterative Signing Workflow
- Sign all nodes ready for signing, update the graph, then repack containers whose signable children are signed; proceed round-by-round.
2 changes: 2 additions & 0 deletions Arcade.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<Project Path="src/Microsoft.DotNet.NuGetRepack/tests/Microsoft.DotNet.NuGetRepack.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.PackageTesting.Tests/Microsoft.DotNet.PackageTesting.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.RemoteExecutor/tests/Microsoft.DotNet.RemoteExecutor.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.RecursiveSigning.Tests/Microsoft.DotNet.RecursiveSigning.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.SignTool.Tests/Microsoft.DotNet.SignTool.Tests.csproj" />
<Project Path="src/Microsoft.DotNet.SourceBuild/tests/Microsoft.DotNet.SourceBuild.Tasks.Tests.csproj" />
Expand Down Expand Up @@ -70,6 +71,7 @@
<Project Path="src/Microsoft.DotNet.RemoteExecutor/src/Microsoft.DotNet.RemoteExecutor.csproj" />
<Project Path="src/Microsoft.DotNet.SharedFramework.Sdk/Microsoft.DotNet.SharedFramework.Sdk.csproj" />
<Project Path="src/Microsoft.DotNet.SignTool/Microsoft.DotNet.SignTool.csproj" />
<Project Path="src/Microsoft.DotNet.RecursiveSigning/Microsoft.DotNet.RecursiveSigning.csproj" />
<Project Path="src/Microsoft.DotNet.SourceBuild/tasks/Microsoft.DotNet.SourceBuild.Tasks.csproj" />
<Project Path="src/Microsoft.DotNet.StrongName/Microsoft.DotNet.StrongName.csproj" />
<Project Path="src/Microsoft.DotNet.Tar/Microsoft.DotNet.Tar.csproj" />
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpVersion)" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingVersion)" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsoleVersion)" />
<PackageVersion Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" />
<PackageVersion Include="System.Composition" Version="$(SystemCompositionVersion)" />
Expand Down
1 change: 1 addition & 0 deletions eng/BuildTask.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PackageVersion Update="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
<PackageVersion Update="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Update="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageVersion Update="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
</ItemGroup>

</Project>
2 changes: 2 additions & 0 deletions eng/Version.Details.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ This file should be imported by eng/Versions.props
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>9.0.0</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>9.0.0</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>9.0.0</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>9.0.0</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>9.0.0</MicrosoftExtensionsLoggingConsolePackageVersion>
<SystemCollectionsImmutablePackageVersion>9.0.0</SystemCollectionsImmutablePackageVersion>
<SystemFormatsAsn1PackageVersion>9.0.0</SystemFormatsAsn1PackageVersion>
Expand Down Expand Up @@ -88,6 +89,7 @@ This file should be imported by eng/Versions.props
<MicrosoftExtensionsFileProvidersAbstractionsVersion>$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)</MicrosoftExtensionsFileProvidersAbstractionsVersion>
<MicrosoftExtensionsFileSystemGlobbingVersion>$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)</MicrosoftExtensionsFileSystemGlobbingVersion>
<MicrosoftExtensionsHttpVersion>$(MicrosoftExtensionsHttpPackageVersion)</MicrosoftExtensionsHttpVersion>
<MicrosoftExtensionsLoggingAbstractionsVersion>$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)</MicrosoftExtensionsLoggingAbstractionsVersion>
<MicrosoftExtensionsLoggingConsoleVersion>$(MicrosoftExtensionsLoggingConsolePackageVersion)</MicrosoftExtensionsLoggingConsoleVersion>
<SystemCollectionsImmutableVersion>$(SystemCollectionsImmutablePackageVersion)</SystemCollectionsImmutableVersion>
<SystemFormatsAsn1Version>$(SystemFormatsAsn1PackageVersion)</SystemFormatsAsn1Version>
Expand Down
4 changes: 4 additions & 0 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>d3981726bc8b0e179db50301daf9f22d42393096</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>d3981726bc8b0e179db50301daf9f22d42393096</Sha>
</Dependency>
<Dependency Name="System.Text.Encodings.Web" Version="9.0.0">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>d3981726bc8b0e179db50301daf9f22d42393096</Sha>
Expand Down
14 changes: 14 additions & 0 deletions src/Common/Microsoft.Arcade.Common/FileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,19 @@ public void WriteToFile(string path, string content)
/// <param name="targetPath">Target path that is relative to base path.</param>
/// <exception cref="NotImplementedException"></exception>
public virtual string GetRelativePath(string basePath, string targetPath) => throw new NotImplementedException("Not supported in default FileSystem implementation");

public byte[] ReadAllBytes(string path) => File.ReadAllBytes(path);

public void WriteAllBytes(string path, byte[] bytes)
{
string? dirPath = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(dirPath))
{
Directory.CreateDirectory(dirPath);
}
File.WriteAllBytes(path, bytes);
}

public long GetFileLength(string path) => new FileInfo(path).Length;
}
}
6 changes: 6 additions & 0 deletions src/Common/Microsoft.Arcade.Common/IFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,11 @@ public interface IFileSystem
FileAttributes GetAttributes(string path);

string GetRelativePath(string basePath, string targetPath);

byte[] ReadAllBytes(string path);

void WriteAllBytes(string path, byte[] bytes);

long GetFileLength(string path);
}
}
88 changes: 75 additions & 13 deletions src/Common/Microsoft.Arcade.Test.Common/MockFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Unicode;
using Microsoft.Arcade.Common;

#nullable enable
namespace Microsoft.Arcade.Test.Common
{

public class MockFileSystem : IFileSystem
{
#region File system state

public HashSet<string> Directories { get; }
public HashSet<string> Directories { get; }

public Dictionary<string, string> Files { get; }
public Dictionary<string, byte[]> Files { get; }



public List<string> RemovedFiles { get; } = new();

Expand All @@ -29,7 +34,7 @@ public MockFileSystem(
string directorySeparator = "/")
{
Directories = new(directories ?? new string[0]);
Files = files ?? new();
Files = files?.ToDictionary(f => f.Key, f => System.Text.Encoding.UTF8.GetBytes(f.Value)) ?? new();
DirectorySeparator = directorySeparator;
}

Expand Down Expand Up @@ -59,12 +64,17 @@ public void DeleteFile(string path)

public string PathCombine(string path1, string path2, string path3) => path1 + DirectorySeparator + path2 + DirectorySeparator + path3;

public void WriteToFile(string path, string content) => Files[path] = content;
public void WriteToFile(string path, string content) => Files[path] = System.Text.Encoding.UTF8.GetBytes(content);

public string ReadToString(string path) => System.Text.Encoding.UTF8.GetString(Files[path]);

public void CopyFile(string sourceFileName, string destFileName, bool overwrite = false) => Files[destFileName] = Files[sourceFileName];

public Stream GetFileStream(string path, FileMode mode, FileAccess access)
=> FileExists(path) ? new MemoryStream() : new MockFileStream(this, path);
{
// Always use MockFileStream which handles both read and write correctly
return new MockFileStream(this, path, mode, access);
}

public FileAttributes GetAttributes(string path)
{
Expand All @@ -90,34 +100,86 @@ public string GetRelativePath(string basePath, string targetPath)
return targetPath.Replace(basePath, "").TrimStart('/', '\\');
}

public byte[] ReadAllBytes(string path)
{
if (!Files.ContainsKey(path))
{
throw new FileNotFoundException($"File not found: {path}");
}
return Files[path];
}

public void WriteAllBytes(string path, byte[] bytes)
{
Files[path] = bytes;
}

public long GetFileLength(string path)
{
if (!Files.ContainsKey(path))
{
throw new FileNotFoundException($"File not found: {path}");
}
return Files[path].LongLength;
}

#endregion

/// <summary>
/// Allows to write to a stream that will end up in the MockFileSystem.
/// Allows to read and write to a stream that will end up in the MockFileSystem.
/// </summary>
private class MockFileStream : MemoryStream
{
private readonly MockFileSystem _fileSystem;
private readonly string _path;
private readonly FileAccess _access;
private bool _disposed = false;

public MockFileStream(MockFileSystem fileSystem, string path)
: base(fileSystem.FileExists(path) ? System.Text.Encoding.UTF8.GetBytes(fileSystem.Files[path]) : new byte[2048])
public MockFileStream(MockFileSystem fileSystem, string path, FileMode mode, FileAccess access)
{
_fileSystem = fileSystem;
_path = path;
_access = access;

// Initialize the stream based on mode and existing file content
if (mode == FileMode.Open || mode == FileMode.Append)
{
if (fileSystem.FileExists(path))
{
byte[] existingContent = fileSystem.Files[path];
Write(existingContent, 0, existingContent.Length);

if (mode == FileMode.Open)
{
Seek(0, SeekOrigin.Begin); // Reset to beginning for read
}
// For Append mode, position is already at the end
}
else if (mode == FileMode.Open)
{
throw new FileNotFoundException($"File not found: {path}");
}
}
else if (mode == FileMode.Create || mode == FileMode.CreateNew || mode == FileMode.Truncate)
{
// Start with an empty stream
if (mode == FileMode.CreateNew && fileSystem.FileExists(path))
{
throw new IOException($"File already exists: {path}");
}
}
}

protected override void Dispose(bool disposing)
{
// flush file to our system
if (!_disposed)
// Flush to file system if we have write access
if (!_disposed && (_access == FileAccess.Write || _access == FileAccess.ReadWrite))
{
_disposed = true;
using var sr = new StreamReader(this);
Seek(0, SeekOrigin.Begin);
_fileSystem.WriteToFile(_path, sr.ReadToEnd().Replace("\0", ""));
_fileSystem.WriteAllBytes(_path, ToArray());
}

base.Dispose(disposing);
}
}
}
Expand Down
Loading